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>
23 #include <rtl/strbuf.hxx>
26 #pragma GCC diagnostic push
27 #pragma GCC diagnostic ignored "-Wshadow"
30 #include <BarcodeFormat.h>
31 #include <BitMatrix.h>
32 #include <MultiFormatWriter.h>
35 #pragma GCC diagnostic pop
39 #include <BitMatrixIO.h>
42 #if ZXING_VERSION_MAJOR < 2
43 #include <TextUtfEncoding.h>
46 #endif // ENABLE_ZXING
48 #include <com/sun/star/beans/XPropertySet.hpp>
49 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
50 #include <com/sun/star/drawing/XShape.hpp>
51 #include <com/sun/star/graphic/GraphicProvider.hpp>
52 #include <com/sun/star/graphic/XGraphic.hpp>
53 #include <com/sun/star/drawing/BarCode.hpp>
54 #include <com/sun/star/drawing/BarCodeErrorCorrection.hpp>
55 #include <com/sun/star/graphic/XGraphicProvider.hpp>
56 #include <com/sun/star/io/XInputStream.hpp>
57 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
58 #include <com/sun/star/lang/XServiceInfo.hpp>
59 #include <com/sun/star/sheet/XSpreadsheet.hpp>
60 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
61 #include <com/sun/star/text/TextContentAnchorType.hpp>
62 #include <com/sun/star/text/XTextContent.hpp>
63 #include <com/sun/star/text/XTextViewCursor.hpp>
64 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
65 #include <com/sun/star/drawing/XDrawView.hpp>
66 #include <com/sun/star/drawing/XDrawPage.hpp>
69 using namespace css::uno
;
70 using namespace css::beans
;
71 using namespace css::container
;
72 using namespace css::frame
;
73 using namespace css::io
;
74 using namespace css::lang
;
75 using namespace css::sheet
;
76 using namespace css::text
;
77 using namespace css::drawing
;
78 using namespace css::graphic
;
83 // Implementation adapted from the answer: https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350
85 OString
ConvertToSVGFormat(const ZXing::BitMatrix
& bitmatrix
)
88 const int width
= bitmatrix
.width();
89 const int height
= bitmatrix
.height();
90 sb
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
91 "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "
92 + OString::number(width
) + " " + OString::number(height
)
93 + "\" stroke=\"none\">\n"
95 for (int i
= 0; i
< height
; ++i
)
97 for (int j
= 0; j
< width
; ++j
)
99 if (bitmatrix
.get(j
, i
))
101 sb
.append("M" + OString::number(j
) + "," + OString::number(i
) + "h1v1h-1z");
105 sb
.append("\"/>\n</svg>");
106 return sb
.toString();
110 std::string
GetBarCodeType(int type
)
121 OString
GenerateQRCode(std::u16string_view aQRText
, tools::Long aQRECC
, int aQRBorder
, int aQRType
)
123 // Associated ZXing error correction levels (0-8) to our constants arbitrarily.
128 case css::drawing::BarCodeErrorCorrection::LOW
:
133 case css::drawing::BarCodeErrorCorrection::MEDIUM
:
138 case css::drawing::BarCodeErrorCorrection::QUARTILE
:
143 case css::drawing::BarCodeErrorCorrection::HIGH
:
150 OString o
= OUStringToOString(aQRText
, RTL_TEXTENCODING_UTF8
);
151 std::string
QRText(o
);
152 ZXing::BarcodeFormat format
= ZXing::BarcodeFormatFromString(GetBarCodeType(aQRType
));
153 auto writer
= ZXing::MultiFormatWriter(format
).setMargin(aQRBorder
).setEccLevel(bqrEcc
);
154 writer
.setEncoding(ZXing::CharacterSet::UTF8
);
155 #if ZXING_VERSION_MAJOR >= 2
156 ZXing::BitMatrix bitmatrix
= writer
.encode(QRText
, 0, 0);
158 ZXing::BitMatrix bitmatrix
= writer
.encode(ZXing::TextUtfEncoding::FromUtf8(QRText
), 0, 0);
161 return OString(ZXing::ToSVG(bitmatrix
));
163 return ConvertToSVGFormat(bitmatrix
);
168 } // anonymous namespace
170 QrCodeGenDialog::QrCodeGenDialog(weld::Widget
* pParent
, Reference
<XModel
> xModel
,
172 : GenericDialogController(pParent
, u
"cui/ui/qrcodegen.ui"_ustr
, u
"QrCodeGenDialog"_ustr
)
173 , m_xModel(std::move(xModel
))
174 , m_xEdittext(m_xBuilder
->weld_text_view(u
"edit_text"_ustr
))
175 , m_xECC
{ m_xBuilder
->weld_radio_button(u
"button_low"_ustr
),
176 m_xBuilder
->weld_radio_button(u
"button_medium"_ustr
),
177 m_xBuilder
->weld_radio_button(u
"button_quartile"_ustr
),
178 m_xBuilder
->weld_radio_button(u
"button_high"_ustr
) }
179 , m_xSpinBorder(m_xBuilder
->weld_spin_button(u
"edit_margin"_ustr
))
180 , m_xComboType(m_xBuilder
->weld_combo_box(u
"choose_type"_ustr
))
185 m_xEdittext
->set_size_request(m_xEdittext
->get_approximate_digit_width() * 28,
186 m_xEdittext
->get_height_rows(6));
189 // TODO: This only works in Writer doc. Should also work in shapes
190 Reference
<XIndexAccess
> xSelections(m_xModel
->getCurrentSelection(), UNO_QUERY
);
191 if (xSelections
.is())
193 Reference
<XTextRange
> xSelection(xSelections
->getByIndex(0), UNO_QUERY
);
195 m_xEdittext
->set_text(xSelection
->getString());
200 Reference
<container::XIndexAccess
> xIndexAccess(m_xModel
->getCurrentSelection(),
202 Reference
<XPropertySet
> xProps(xIndexAccess
->getByIndex(0), UNO_QUERY_THROW
);
204 // Read properties from selected QR Code
205 css::drawing::BarCode aBarCode
;
206 xProps
->getPropertyValue(u
"BarCodeProperties"_ustr
) >>= aBarCode
;
208 m_xEdittext
->set_text(aBarCode
.Payload
);
210 //Get Error Correction Constant from selected QR Code
211 GetErrorCorrection(aBarCode
.ErrorCorrection
);
213 m_xSpinBorder
->set_value(aBarCode
.Border
);
215 m_xComboType
->set_active(aBarCode
.Type
);
217 // Mark this as existing shape
218 m_xExistingShapeProperties
= std::move(xProps
);
221 short QrCodeGenDialog::run()
227 nRet
= GenericDialogController::run();
235 catch (const std::exception
&)
237 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(
238 mpParent
, VclMessageType::Warning
, VclButtonsType::Ok
,
239 CuiResId(RID_CUISTR_QRCODEDATALONG
)));
252 bool QrCodeGenDialog::runAsync(const std::shared_ptr
<QrCodeGenDialog
>& rController
,
253 const std::function
<void(sal_Int32
)>& rFunc
)
257 weld::GenericDialogController::runAsync(rController
, [rController
, rFunc
](sal_Int32 nResult
) {
258 if (nResult
== RET_OK
)
262 rController
->Apply();
264 catch (const std::exception
&)
266 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(
267 rController
->GetParent(), VclMessageType::Warning
, VclButtonsType::Ok
,
268 CuiResId(RID_CUISTR_QRCODEDATALONG
)));
282 void QrCodeGenDialog::Apply()
285 css::drawing::BarCode aBarCode
;
286 aBarCode
.Payload
= m_xEdittext
->get_text();
287 aBarCode
.Type
= m_xComboType
->get_active();
289 bool bLowECCActive(m_xECC
[0]->get_active());
290 bool bMediumECCActive(m_xECC
[1]->get_active());
291 bool bQuartileECCActive(m_xECC
[2]->get_active());
295 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::LOW
;
297 else if (bMediumECCActive
)
299 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::MEDIUM
;
301 else if (bQuartileECCActive
)
303 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::QUARTILE
;
307 aBarCode
.ErrorCorrection
= css::drawing::BarCodeErrorCorrection::HIGH
;
310 aBarCode
.Border
= m_xSpinBorder
->get_value();
312 // Read svg and replace placeholder texts
313 OString aSvgImage
= GenerateQRCode(aBarCode
.Payload
, aBarCode
.ErrorCorrection
, aBarCode
.Border
,
316 // Insert/Update graphic
317 SvMemoryStream
aSvgStream(4096, 4096);
318 aSvgStream
.WriteOString(aSvgImage
);
319 Reference
<XInputStream
> xInputStream(new utl::OSeekableInputStreamWrapper(aSvgStream
));
320 const Reference
<XComponentContext
>& xContext(comphelper::getProcessComponentContext());
321 Reference
<XGraphicProvider
> xProvider
= css::graphic::GraphicProvider::create(xContext
);
323 Sequence
<PropertyValue
> aMediaProperties
{ comphelper::makePropertyValue(u
"InputStream"_ustr
,
325 Reference
<XGraphic
> xGraphic(xProvider
->queryGraphic(aMediaProperties
));
327 bool bIsExistingQRCode
= m_xExistingShapeProperties
.is();
328 Reference
<XPropertySet
> xShapeProps
;
329 if (bIsExistingQRCode
)
330 xShapeProps
= m_xExistingShapeProperties
;
332 xShapeProps
.set(Reference
<lang::XMultiServiceFactory
>(m_xModel
, UNO_QUERY_THROW
)
333 ->createInstance(u
"com.sun.star.drawing.GraphicObjectShape"_ustr
),
336 xShapeProps
->setPropertyValue(u
"Graphic"_ustr
, Any(xGraphic
));
338 // Set QRCode properties
339 xShapeProps
->setPropertyValue(u
"BarCodeProperties"_ustr
, Any(aBarCode
));
341 if (bIsExistingQRCode
)
345 Reference
<XShape
> xShape(xShapeProps
, UNO_QUERY
);
346 awt::Size aShapeSize
;
347 aShapeSize
.Height
= 4000;
348 aShapeSize
.Width
= 4000;
349 xShape
->setSize(aShapeSize
);
352 xShapeProps
->setPropertyValue(u
"AnchorType"_ustr
, Any(TextContentAnchorType_AT_PARAGRAPH
));
354 const Reference
<XServiceInfo
> xServiceInfo(m_xModel
, UNO_QUERY_THROW
);
357 if (xServiceInfo
->supportsService(u
"com.sun.star.text.TextDocument"_ustr
))
359 Reference
<XTextContent
> xTextContent(xShape
, UNO_QUERY_THROW
);
360 Reference
<XTextViewCursorSupplier
> xViewCursorSupplier(m_xModel
->getCurrentController(),
362 Reference
<XTextViewCursor
> xCursor
= xViewCursorSupplier
->getViewCursor();
363 // use cursor's XText - it might be in table cell, frame, ...
364 Reference
<XText
> const xText(xCursor
->getText());
366 xText
->insertTextContent(xCursor
, xTextContent
, true);
371 else if (xServiceInfo
->supportsService(u
"com.sun.star.sheet.SpreadsheetDocument"_ustr
))
373 Reference
<XPropertySet
> xSheetCell(m_xModel
->getCurrentSelection(), UNO_QUERY_THROW
);
374 awt::Point aCellPosition
;
375 xSheetCell
->getPropertyValue(u
"Position"_ustr
) >>= aCellPosition
;
376 xShape
->setPosition(aCellPosition
);
378 Reference
<XSpreadsheetView
> xView(m_xModel
->getCurrentController(), UNO_QUERY_THROW
);
379 Reference
<XSpreadsheet
> xSheet(xView
->getActiveSheet(), UNO_SET_THROW
);
380 Reference
<XDrawPageSupplier
> xDrawPageSupplier(xSheet
, UNO_QUERY_THROW
);
381 Reference
<XDrawPage
> xDrawPage(xDrawPageSupplier
->getDrawPage(), UNO_SET_THROW
);
382 Reference
<XShapes
> xShapes(xDrawPage
, UNO_QUERY_THROW
);
384 xShapes
->add(xShape
);
389 else if (xServiceInfo
->supportsService(u
"com.sun.star.presentation.PresentationDocument"_ustr
)
390 || xServiceInfo
->supportsService(u
"com.sun.star.drawing.DrawingDocument"_ustr
))
392 Reference
<XDrawView
> xView(m_xModel
->getCurrentController(), UNO_QUERY_THROW
);
393 Reference
<XDrawPage
> xPage(xView
->getCurrentPage(), UNO_SET_THROW
);
394 Reference
<XShapes
> xShapes(xPage
, UNO_QUERY_THROW
);
396 xShapes
->add(xShape
);
402 //Not implemented for math,base and other apps.
403 throw uno::RuntimeException(u
"Not implemented"_ustr
);
408 void QrCodeGenDialog::GetErrorCorrection(tools::Long ErrorCorrection
)
410 switch (ErrorCorrection
)
412 case css::drawing::BarCodeErrorCorrection::LOW
:
414 m_xECC
[0]->set_active(true);
417 case css::drawing::BarCodeErrorCorrection::MEDIUM
:
419 m_xECC
[1]->set_active(true);
422 case css::drawing::BarCodeErrorCorrection::QUARTILE
:
424 m_xECC
[2]->set_active(true);
427 case css::drawing::BarCodeErrorCorrection::HIGH
:
429 m_xECC
[3]->set_active(true);
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */