tdf#139778 Switch to ZXing for generating QR code
Change-Id: Ief944266d5183bb862afe99ec6b0bdaca4956938
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112534
Tested-by: Jenkins
Tested-by: René Engelhard <rene@debian.org>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index ff221a9..f2df06c 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -73,7 +73,7 @@ $(eval $(call gb_Library_use_externals,cui,\
libxml2 \
orcus-parser \
orcus \
qrcodegen \
zxing \
))
ifeq ($(DISABLE_GUI),)
$(eval $(call gb_Library_use_externals,cui,\
diff --git a/cui/source/dialogs/QrCodeGenDialog.cxx b/cui/source/dialogs/QrCodeGenDialog.cxx
index 3de87ab..3a82a11 100644
--- a/cui/source/dialogs/QrCodeGenDialog.cxx
+++ b/cui/source/dialogs/QrCodeGenDialog.cxx
@@ -17,14 +17,26 @@
#include <utility>
#include <vcl/svapp.hxx>
#if ENABLE_QRCODEGEN
#if defined(SYSTEM_QRCODEGEN)
#include <qrcodegen/QrCode.hpp>
#else
#include <QrCode.hpp>
#if ENABLE_ZXING
#include <rtl/ustrbuf.hxx>
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#include <BarcodeFormat.h>
#include <BitArray.h>
#include <BitMatrix.h>
#include <MultiFormatWriter.h>
#include <TextUtfEncoding.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif // ENABLE_ZXING
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/drawing/XShape.hpp>
@@ -56,9 +68,37 @@ using namespace css::sheet;
using namespace css::text;
using namespace css::drawing;
using namespace css::graphic;
#if ENABLE_QRCODEGEN
using namespace qrcodegen;
namespace
{
#if ENABLE_ZXING
// Implementation adapted from the answer: https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350
OUString ConvertToSVGFormat(const ZXing::BitMatrix& bitmatrix)
{
OUStringBuffer sb;
const int width = bitmatrix.width();
const int height = bitmatrix.height();
ZXing::BitArray row(width);
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 "
+ OUString::number(width) + " " + OUString::number(height)
+ "\" stroke=\"none\">\n"
"<path d=\"");
for (int i = 0; i < height; ++i)
{
bitmatrix.getRow(i, row);
for (int j = 0; j < width; ++j)
{
if (row.get(j))
{
sb.append("M" + OUString::number(j) + "," + OUString::number(i) + "h1v1h-1z");
}
}
}
sb.append("\"/>\n</svg>");
return sb.toString();
}
#endif
}
QrCodeGenDialog::QrCodeGenDialog(weld::Widget* pParent, Reference<XModel> xModel,
bool bEditExisting)
@@ -70,7 +110,7 @@ QrCodeGenDialog::QrCodeGenDialog(weld::Widget* pParent, Reference<XModel> xModel
m_xBuilder->weld_radio_button("button_quartile"),
m_xBuilder->weld_radio_button("button_high") }
, m_xSpinBorder(m_xBuilder->weld_spin_button("edit_border"))
#if ENABLE_QRCODEGEN
#if ENABLE_ZXING
, mpParent(pParent)
#endif
{
@@ -108,7 +148,7 @@ QrCodeGenDialog::QrCodeGenDialog(weld::Widget* pParent, Reference<XModel> xModel
short QrCodeGenDialog::run()
{
#if ENABLE_QRCODEGEN
#if ENABLE_ZXING
short nRet;
while (true)
{
@@ -120,7 +160,7 @@ short QrCodeGenDialog::run()
Apply();
break;
}
catch (const qrcodegen::data_too_long&)
catch (const std::exception&)
{
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
mpParent, VclMessageType::Warning, VclButtonsType::Ok,
@@ -139,7 +179,7 @@ short QrCodeGenDialog::run()
void QrCodeGenDialog::Apply()
{
#if ENABLE_QRCODEGEN
#if ENABLE_ZXING
css::drawing::QRCode aQRCode;
aQRCode.Payload = m_xEdittext->get_text();
@@ -264,43 +304,41 @@ void QrCodeGenDialog::Apply()
OUString QrCodeGenDialog::GenerateQRCode(OUString aQRText, tools::Long aQRECC, int aQRBorder)
{
#if ENABLE_QRCODEGEN
//Select ECC:: value from aQrECC
qrcodegen::QrCode::Ecc bqrEcc = qrcodegen::QrCode::Ecc::LOW;
#if ENABLE_ZXING
// Associated ZXing error correction levels (0-8) to our constants arbitrarily.
int bqrEcc = 1;
switch (aQRECC)
{
case 1:
case css::drawing::QRCodeErrorCorrection::LOW:
{
bqrEcc = qrcodegen::QrCode::Ecc::LOW;
bqrEcc = 1;
break;
}
case 2:
case css::drawing::QRCodeErrorCorrection::MEDIUM:
{
bqrEcc = qrcodegen::QrCode::Ecc::MEDIUM;
bqrEcc = 3;
break;
}
case 3:
case css::drawing::QRCodeErrorCorrection::QUARTILE:
{
bqrEcc = qrcodegen::QrCode::Ecc::QUARTILE;
bqrEcc = 5;
break;
}
case 4:
case css::drawing::QRCodeErrorCorrection::HIGH:
{
bqrEcc = qrcodegen::QrCode::Ecc::HIGH;
bqrEcc = 7;
break;
}
}
//OuString to char* qrtext
OString o = OUStringToOString(aQRText, RTL_TEXTENCODING_UTF8);
const char* qrtext = o.pData->buffer;
// From QR Code library
qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText(qrtext, bqrEcc);
std::string svg = qr0.toSvgString(aQRBorder);
//cstring to OUString
return OUString::createFromAscii(svg.c_str());
std::string QRText(o.getStr(), o.getLength());
ZXing::BarcodeFormat format = ZXing::BarcodeFormatFromString("QR_CODE");
auto writer = ZXing::MultiFormatWriter(format).setMargin(aQRBorder).setEccLevel(bqrEcc);
writer.setEncoding(ZXing::CharacterSet::UTF8);
ZXing::BitMatrix bitmatrix = writer.encode(ZXing::TextUtfEncoding::FromUtf8(QRText), 0, 0);
return ConvertToSVGFormat(bitmatrix);
#else
(void)aQRText;
(void)aQRECC;
diff --git a/cui/source/inc/QrCodeGenDialog.hxx b/cui/source/inc/QrCodeGenDialog.hxx
index 7c39fe0..c3f7a9a 100644
--- a/cui/source/inc/QrCodeGenDialog.hxx
+++ b/cui/source/inc/QrCodeGenDialog.hxx
@@ -8,7 +8,7 @@
*/
#pragma once
#include <config_qrcodegen.h>
#include <config_zxing.h>
#include <vcl/weld.hxx>
@@ -33,7 +33,7 @@ private:
std::unique_ptr<weld::Entry> m_xEdittext;
std::unique_ptr<weld::RadioButton> m_xECC[4];
std::unique_ptr<weld::SpinButton> m_xSpinBorder;
#if ENABLE_QRCODEGEN
#if ENABLE_ZXING
weld::Widget* mpParent;
#endif