1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <QrCodeGenDialog.hxx>
12 #include <comphelper/processfactory.hxx>
13 #include <comphelper/propertyvalue.hxx>
14 #include <tools/stream.hxx>
15 #include <dialmgr.hxx>
16 #include <strings.hrc>
17 #include <unotools/streamwrap.hxx>
19 #include <vcl/svapp.hxx>
22 #include <rtl/strbuf.hxx>
25 #pragma GCC diagnostic push
26 #pragma GCC diagnostic ignored "-Wshadow"
29 #include <BarcodeFormat.h>
30 #include <BitMatrix.h>
31 #include <MultiFormatWriter.h>
32 #include <TextUtfEncoding.h>
35 #pragma GCC diagnostic pop
38 #endif // ENABLE_ZXING
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
42 #include <com/sun/star/drawing/XShape.hpp>
43 #include <com/sun/star/graphic/GraphicProvider.hpp>
44 #include <com/sun/star/graphic/XGraphic.hpp>
45 #include <com/sun/star/drawing/BarCode.hpp>
46 #include <com/sun/star/drawing/BarCodeErrorCorrection.hpp>
47 #include <com/sun/star/graphic/XGraphicProvider.hpp>
48 #include <com/sun/star/io/XInputStream.hpp>
49 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
50 #include <com/sun/star/lang/XServiceInfo.hpp>
51 #include <com/sun/star/sheet/XSpreadsheet.hpp>
52 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
53 #include <com/sun/star/text/TextContentAnchorType.hpp>
54 #include <com/sun/star/text/XTextContent.hpp>
55 #include <com/sun/star/text/XTextViewCursor.hpp>
56 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
57 #include <com/sun/star/drawing/XDrawView.hpp>
58 #include <com/sun/star/drawing/XDrawPage.hpp>
61 using namespace css::uno
;
62 using namespace css::beans
;
63 using namespace css::container
;
64 using namespace css::frame
;
65 using namespace css::io
;
66 using namespace css::lang
;
67 using namespace css::sheet
;
68 using namespace css::text
;
69 using namespace css::drawing
;
70 using namespace css::graphic
;
75 // Implementation adapted from the answer: https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350
76 OString
ConvertToSVGFormat(const ZXing::BitMatrix
& bitmatrix
)
79 const int width
= bitmatrix
.width();
80 const int height
= bitmatrix
.height();
81 sb
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
82 "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "
83 + OString::number(width
) + " " + OString::number(height
)
84 + "\" stroke=\"none\">\n"
86 for (int i
= 0; i
< height
; ++i
)
88 for (int j
= 0; j
< width
; ++j
)
90 if (bitmatrix
.get(j
, i
))
92 sb
.append("M" + OString::number(j
) + "," + OString::number(i
) + "h1v1h-1z");
96 sb
.append("\"/>\n</svg>");
100 std::string
GetBarCodeType(int type
)
111 OString
GenerateQRCode(std::u16string_view aQRText
, tools::Long aQRECC
, int aQRBorder
, int aQRType
)
113 // Associated ZXing error correction levels (0-8) to our constants arbitrarily.
118 case css::drawing::BarCodeErrorCorrection::LOW
:
123 case css::drawing::BarCodeErrorCorrection::MEDIUM
:
128 case css::drawing::BarCodeErrorCorrection::QUARTILE
:
133 case css::drawing::BarCodeErrorCorrection::HIGH
:
140 OString o
= OUStringToOString(aQRText
, RTL_TEXTENCODING_UTF8
);
141 std::string
QRText(o
);
142 ZXing::BarcodeFormat format
= ZXing::BarcodeFormatFromString(GetBarCodeType(aQRType
));
143 auto writer
= ZXing::MultiFormatWriter(format
).setMargin(aQRBorder
).setEccLevel(bqrEcc
);
144 writer
.setEncoding(ZXing::CharacterSet::UTF8
);
145 ZXing::BitMatrix bitmatrix
= writer
.encode(ZXing::TextUtfEncoding::FromUtf8(QRText
), 0, 0);
146 return ConvertToSVGFormat(bitmatrix
);
150 } // anonymous namespace
152 QrCodeGenDialog::QrCodeGenDialog(weld::Widget
* pParent
, Reference
<XModel
> xModel
,
154 : GenericDialogController(pParent
, "cui/ui/qrcodegen.ui", "QrCodeGenDialog")
155 , m_xModel(std::move(xModel
))
156 , m_xEdittext(m_xBuilder
->weld_text_view("edit_text"))
157 , m_xECC
{ m_xBuilder
->weld_radio_button("button_low"),
158 m_xBuilder
->weld_radio_button("button_medium"),
159 m_xBuilder
->weld_radio_button("button_quartile"),
160 m_xBuilder
->weld_radio_button("button_high") }
161 , m_xSpinBorder(m_xBuilder
->weld_spin_button("edit_margin"))
162 , m_xComboType(m_xBuilder
->weld_combo_box("choose_type"))
167 m_xEdittext
->set_size_request(m_xEdittext
->get_approximate_digit_width() * 28,
168 m_xEdittext
->get_height_rows(6));
171 // TODO: This only works in Writer doc. Should also work in shapes
172 Reference
<XIndexAccess
> xSelections(m_xModel
->getCurrentSelection(), UNO_QUERY
);
173 if (xSelections
.is())
175 Reference
<XTextRange
> xSelection(xSelections
->getByIndex(0), UNO_QUERY
);
177 m_xEdittext
->set_text(xSelection
->getString());
182 Reference
<container::XIndexAccess
> xIndexAccess(m_xModel
->getCurrentSelection(),
184 Reference
<XPropertySet
> xProps(xIndexAccess
->getByIndex(0), UNO_QUERY_THROW
);
186 // Read properties from selected QR Code
187 css::drawing::BarCode aBarCode
;
188 xProps
->getPropertyValue("BarCodeProperties") >>= aBarCode
;
190 m_xEdittext
->set_text(aBarCode
.Payload
);
192 //Get Error Correction Constant from selected QR Code
193 GetErrorCorrection(aBarCode
.ErrorCorrection
);
195 m_xSpinBorder
->set_value(aBarCode
.Border
);
197 m_xComboType
->set_active(aBarCode
.Type
);
199 // Mark this as existing shape
200 m_xExistingShapeProperties
= xProps
;
203 short QrCodeGenDialog::run()
209 nRet
= GenericDialogController::run();
217 catch (const std::exception
&)
219 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(
220 mpParent
, VclMessageType::Warning
, VclButtonsType::Ok
,
221 CuiResId(RID_CUISTR_QRCODEDATALONG
)));
234 void QrCodeGenDialog::Apply()
237 css::drawing::BarCode aBarCode
;
238 aBarCode
.Payload
= m_xEdittext
->get_text();
239 aBarCode
.Type
= m_xComboType
->get_active();
241 bool bLowECCActive(m_xECC
[0]->get_active());
242 bool bMediumECCActive(m_xECC
[1]->get_active());
243 bool bQuartileECCActive(m_xECC
[2]->get_active());
247 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::LOW
;
249 else if (bMediumECCActive
)
251 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::MEDIUM
;
253 else if (bQuartileECCActive
)
255 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::QUARTILE
;
259 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::HIGH
;
262 aBarCode
.Border
= m_xSpinBorder
->get_value();
264 // Read svg and replace placeholder texts
265 OString aSvgImage
= GenerateQRCode(aBarCode
.Payload
, aBarCode
.ErrorCorrection
, aBarCode
.Border
,
268 // Insert/Update graphic
269 SvMemoryStream
aSvgStream(4096, 4096);
270 aSvgStream
.WriteOString(aSvgImage
);
271 Reference
<XInputStream
> xInputStream(new utl::OSeekableInputStreamWrapper(aSvgStream
));
272 Reference
<XComponentContext
> xContext(comphelper::getProcessComponentContext());
273 Reference
<XGraphicProvider
> xProvider
= css::graphic::GraphicProvider::create(xContext
);
275 Sequence
<PropertyValue
> aMediaProperties
{ comphelper::makePropertyValue("InputStream",
277 Reference
<XGraphic
> xGraphic(xProvider
->queryGraphic(aMediaProperties
));
279 bool bIsExistingQRCode
= m_xExistingShapeProperties
.is();
280 Reference
<XPropertySet
> xShapeProps
;
281 if (bIsExistingQRCode
)
282 xShapeProps
= m_xExistingShapeProperties
;
284 xShapeProps
.set(Reference
<lang::XMultiServiceFactory
>(m_xModel
, UNO_QUERY_THROW
)
285 ->createInstance("com.sun.star.drawing.GraphicObjectShape"),
288 xShapeProps
->setPropertyValue("Graphic", Any(xGraphic
));
290 // Set QRCode properties
291 xShapeProps
->setPropertyValue("BarCodeProperties", Any(aBarCode
));
293 if (bIsExistingQRCode
)
297 Reference
<XShape
> xShape(xShapeProps
, UNO_QUERY
);
298 awt::Size aShapeSize
;
299 aShapeSize
.Height
= 4000;
300 aShapeSize
.Width
= 4000;
301 xShape
->setSize(aShapeSize
);
304 xShapeProps
->setPropertyValue("AnchorType", Any(TextContentAnchorType_AT_PARAGRAPH
));
306 const Reference
<XServiceInfo
> xServiceInfo(m_xModel
, UNO_QUERY_THROW
);
309 if (xServiceInfo
->supportsService("com.sun.star.text.TextDocument"))
311 Reference
<XTextContent
> xTextContent(xShape
, UNO_QUERY_THROW
);
312 Reference
<XTextViewCursorSupplier
> xViewCursorSupplier(m_xModel
->getCurrentController(),
314 Reference
<XTextViewCursor
> xCursor
= xViewCursorSupplier
->getViewCursor();
315 // use cursor's XText - it might be in table cell, frame, ...
316 Reference
<XText
> const xText(xCursor
->getText());
318 xText
->insertTextContent(xCursor
, xTextContent
, true);
323 else if (xServiceInfo
->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
325 Reference
<XPropertySet
> xSheetCell(m_xModel
->getCurrentSelection(), UNO_QUERY_THROW
);
326 awt::Point aCellPosition
;
327 xSheetCell
->getPropertyValue("Position") >>= aCellPosition
;
328 xShape
->setPosition(aCellPosition
);
330 Reference
<XSpreadsheetView
> xView(m_xModel
->getCurrentController(), UNO_QUERY_THROW
);
331 Reference
<XSpreadsheet
> xSheet(xView
->getActiveSheet(), UNO_SET_THROW
);
332 Reference
<XDrawPageSupplier
> xDrawPageSupplier(xSheet
, UNO_QUERY_THROW
);
333 Reference
<XDrawPage
> xDrawPage(xDrawPageSupplier
->getDrawPage(), UNO_SET_THROW
);
334 Reference
<XShapes
> xShapes(xDrawPage
, UNO_QUERY_THROW
);
336 xShapes
->add(xShape
);
341 else if (xServiceInfo
->supportsService("com.sun.star.presentation.PresentationDocument")
342 || xServiceInfo
->supportsService("com.sun.star.drawing.DrawingDocument"))
344 Reference
<XDrawView
> xView(m_xModel
->getCurrentController(), UNO_QUERY_THROW
);
345 Reference
<XDrawPage
> xPage(xView
->getCurrentPage(), UNO_SET_THROW
);
346 Reference
<XShapes
> xShapes(xPage
, UNO_QUERY_THROW
);
348 xShapes
->add(xShape
);
354 //Not implemented for math,base and other apps.
355 throw uno::RuntimeException("Not implemented");
360 void QrCodeGenDialog::GetErrorCorrection(tools::Long ErrorCorrection
)
362 switch (ErrorCorrection
)
364 case css::drawing::BarCodeErrorCorrection::LOW
:
366 m_xECC
[0]->set_active(true);
369 case css::drawing::BarCodeErrorCorrection::MEDIUM
:
371 m_xECC
[1]->set_active(true);
374 case css::drawing::BarCodeErrorCorrection::QUARTILE
:
376 m_xECC
[2]->set_active(true);
379 case css::drawing::BarCodeErrorCorrection::HIGH
:
381 m_xECC
[3]->set_active(true);
387 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */