Add multiple segments support for AZTEC, CODEONE, DATAMATRIX, DOTCODE,

GRIDMATRIX, HANXIN, MAXICODE, MICROPDF417, PDF417, QRCODE, RMQR, ULTRA
RMQR: fix ECI encoding (wrong bit length for indicator)
MICROQR: check versions M1 and M2 for allowed characters so as to give
  better error messages
DOTCODE: some small optimizations
common.c: add is_chr(), segs_length(), segs_cpy()
CODEONE/CODE128/DOTCODE/GRIDMATRIX/HANXIN/MAXICODE/QRCODE/ULTRA: add
  namespace prefixes to static funcs/data
includes: use Z_ prefix, unuse double underscore prefixes (guard defines)
manual.txt: compress some tables using double/treble column sets
This commit is contained in:
gitlost 2022-05-09 19:50:50 +01:00
parent 3b9d989894
commit f58c80e290
81 changed files with 12026 additions and 4701 deletions

View file

@ -34,10 +34,36 @@
#define QSL QStringLiteral
namespace Zint {
static const char *fontStyle = "Helvetica";
static const char *fontStyleError = "Helvetica";
static const char fontStyle[] = "Helvetica";
static const char fontStyleError[] = "Helvetica";
static const int fontSizeError = 14; /* Point size */
static const int maxSegs = 256;
static const int maxCLISegs = 10; /* CLI restricted to 10 segments (including main data) */
/* Helper to convert ECI combo index to ECI value */
static int ECIIndexToECI(const int ECIIndex) {
int ret;
if (ECIIndex >= 1 && ECIIndex <= 11) {
ret = ECIIndex + 2;
} else if (ECIIndex >= 12 && ECIIndex <= 15) {
ret = ECIIndex + 3;
} else if (ECIIndex >= 16 && ECIIndex <= 31) {
ret = ECIIndex + 4;
} else if (ECIIndex == 32) {
ret = 170; /* ISO 646 Invariant */
} else if (ECIIndex == 33) {
ret = 899; /* 8-bit binary data */
} else {
ret = 0;
}
return ret;
}
/* Segment constructors */
QZintSeg::QZintSeg() : m_eci(0) {}
QZintSeg::QZintSeg(const QString& text, const int ECIIndex) : m_text(text), m_eci(ECIIndexToECI(ECIIndex)) {}
QZint::QZint()
: m_zintSymbol(NULL), m_symbol(BARCODE_CODE128), m_input_mode(UNICODE_MODE),
m_height(0.0f),
@ -139,9 +165,17 @@ namespace Zint {
void QZint::encode() {
resetSymbol();
QByteArray bstr = m_text.toUtf8();
/* Note do our own rotation */
m_error = ZBarcode_Encode_and_Buffer_Vector(m_zintSymbol, (unsigned char *) bstr.data(), bstr.length(), 0);
if (m_segs.empty()) {
QByteArray bstr = m_text.toUtf8();
/* Note do our own rotation */
m_error = ZBarcode_Encode_and_Buffer_Vector(m_zintSymbol, (unsigned char *) bstr.data(), bstr.length(), 0);
} else {
struct zint_seg segs[maxSegs];
std::vector<QByteArray> bstrs;
int seg_count = convertSegs(segs, bstrs);
/* Note do our own rotation */
m_error = ZBarcode_Encode_Segs_and_Buffer_Vector(m_zintSymbol, segs, seg_count, 0);
}
m_lastError = m_zintSymbol->errtxt;
if (m_error < ZINT_ERROR) {
@ -182,6 +216,19 @@ namespace Zint {
void QZint::setText(const QString& text) {
m_text = text;
m_segs.clear();
}
std::vector<QZintSeg> QZint::segs() const {
return m_segs;
}
void QZint::setSegs(const std::vector<QZintSeg>& segs) {
m_segs = segs;
m_text.clear();
if (m_segs.size()) { /* Make sure `symbol->eci` synced */
m_eci = m_segs[0].m_eci;
}
}
QString QZint::primaryMessage() const {
@ -454,19 +501,7 @@ namespace Zint {
}
void QZint::setECI(int ECIIndex) { // Sets from comboBox index
if (ECIIndex >= 1 && ECIIndex <= 11) {
m_eci = ECIIndex + 2;
} else if (ECIIndex >= 12 && ECIIndex <= 15) {
m_eci = ECIIndex + 3;
} else if (ECIIndex >= 16 && ECIIndex <= 31) {
m_eci = ECIIndex + 4;
} else if (ECIIndex == 32) {
m_eci = 170; /* ISO 646 Invariant */
} else if (ECIIndex == 33) {
m_eci = 899; /* 8-bit binary data */
} else {
m_eci = 0;
}
m_eci = ECIIndexToECI(ECIIndex);
}
void QZint::setECIValue(int eci) { // Sets literal value
@ -611,12 +646,19 @@ namespace Zint {
return ZBarcode_Version();
}
bool QZint::save_to_file(const QString &filename) {
bool QZint::save_to_file(const QString& filename) {
resetSymbol();
strcpy(m_zintSymbol->outfile, filename.toLatin1().left(255));
QByteArray bstr = m_text.toUtf8();
m_error = ZBarcode_Encode_and_Print(m_zintSymbol, (unsigned char *) bstr.data(), bstr.length(),
m_rotate_angle);
if (m_segs.empty()) {
QByteArray bstr = m_text.toUtf8();
m_error = ZBarcode_Encode_and_Print(m_zintSymbol, (unsigned char *) bstr.data(), bstr.length(),
m_rotate_angle);
} else {
struct zint_seg segs[maxSegs];
std::vector<QByteArray> bstrs;
int seg_count = convertSegs(segs, bstrs);
m_error = ZBarcode_Encode_Segs_and_Print(m_zintSymbol, segs, seg_count, m_rotate_angle);
}
if (m_error >= ZINT_ERROR) {
m_lastError = m_zintSymbol->errtxt;
m_encodedWidth = 0;
@ -657,6 +699,19 @@ namespace Zint {
}
}
/* Helper to convert `m_segs` to `struct zint_seg[]` */
int QZint::convertSegs(struct zint_seg segs[], std::vector<QByteArray>& bstrs) {
bstrs.reserve(m_segs.size());
int i;
for (i = 0; i < (int) m_segs.size() && i < maxSegs && !m_segs[i].m_text.isEmpty(); i++) {
segs[i].eci = m_segs[i].m_eci;
bstrs.push_back(m_segs[i].m_text.toUtf8());
segs[i].source = (unsigned char *) bstrs.back().data();
segs[i].length = bstrs.back().length();
}
return i;
}
/* Note: legacy argument `mode` is not used */
void QZint::render(QPainter& painter, const QRectF& paintRect, AspectRatioMode /*mode*/) {
struct zint_vector_rect *rect;
@ -824,7 +879,7 @@ namespace Zint {
If HEIGHTPERROW_MODE set and non-zero `heightPerRow` given then use that for height instead of internal
height */
QString QZint::getAsCLI(const bool win, const bool longOptOnly, const bool barcodeNames,
const bool autoHeight, const float heightPerRow, const QString &outfile) const {
const bool autoHeight, const float heightPerRow, const QString& outfile) const {
QString cmd(win ? QSL("zint.exe") : QSL("zint"));
char name_buf[32];
@ -859,7 +914,18 @@ namespace Zint {
arg_bool(cmd, "--compliantheight", hasCompliantHeight() && compliantHeight());
arg_data(cmd, longOptOnly ? "--data=" : "-d ", text(), win);
if (m_segs.empty()) {
if (supportsECI()) {
arg_int(cmd, "--eci=", eci());
}
arg_data(cmd, longOptOnly ? "--data=" : "-d ", m_text, win);
} else {
arg_int(cmd, "--eci=", m_segs.front().m_eci);
arg_data(cmd, longOptOnly ? "--data=" : "-d ", m_segs.front().m_text, win);
for (int i = 1; i < (int) m_segs.size() && i < maxCLISegs && !m_segs[i].m_text.isEmpty(); i++) {
arg_seg(cmd, i, m_segs[i], win);
}
}
if (m_symbol == BARCODE_DATAMATRIX || m_symbol == BARCODE_HIBC_DM) {
arg_bool(cmd, "--dmre", option3() == DM_DMRE);
@ -872,10 +938,6 @@ namespace Zint {
arg_bool(cmd, "--dotty", dotty());
}
if (supportsECI()) {
arg_int(cmd, "--eci=", eci());
}
arg_bool(cmd, "--esc", inputMode() & ESCAPE_MODE);
arg_bool(cmd, "--fast", inputMode() & FAST_MODE);
@ -992,26 +1054,26 @@ namespace Zint {
}
/* `getAsCLI()` helpers */
void QZint::arg_str(QString &cmd, const char *const opt, const QString &val) {
void QZint::arg_str(QString& cmd, const char *const opt, const QString& val) {
if (!val.isEmpty()) {
QByteArray bstr = val.toUtf8();
cmd += QString::asprintf(" %s%.*s", opt, bstr.length(), bstr.data());
}
}
void QZint::arg_int(QString &cmd, const char *const opt, const int val, const bool allowZero) {
void QZint::arg_int(QString& cmd, const char *const opt, const int val, const bool allowZero) {
if (val > 0 || (val == 0 && allowZero)) {
cmd += QString::asprintf(" %s%d", opt, val);
}
}
void QZint::arg_bool(QString &cmd, const char *const opt, const bool val) {
void QZint::arg_bool(QString& cmd, const char *const opt, const bool val) {
if (val) {
cmd += QString::asprintf(" %s", opt);
}
}
void QZint::arg_color(QString &cmd, const char *const opt, const QColor val) {
void QZint::arg_color(QString& cmd, const char *const opt, const QColor val) {
if (val.alpha() != 0xFF) {
cmd += QString::asprintf(" %s%02X%02X%02X%02X", opt, val.red(), val.green(), val.blue(), val.alpha());
} else {
@ -1019,33 +1081,43 @@ namespace Zint {
}
}
void QZint::arg_data(QString &cmd, const char *const opt, const QString &val, const bool win) {
void QZint::arg_data(QString& cmd, const char *const opt, const QString& val, const bool win) {
if (!val.isEmpty()) {
QString text(val);
const char delim = win ? '"' : '\'';
if (win) {
// Difficult (impossible?) to fully escape strings on Windows, e.g. "blah%PATH%" will substitute
// env var PATH, so just doing basic escaping here
text.replace("\\\\", "\\\\\\\\"); // Double-up backslashed backslash `\\` -> `\\\\`
text.replace("\"", "\\\""); // Backslash quote `"` -> `\"`
QByteArray bstr = text.toUtf8();
cmd += QString::asprintf(" %s%c%.*s%c", opt, delim, bstr.length(), bstr.data(), delim);
} else {
text.replace("'", "'\\''"); // Single quote `'` -> `'\''`
QByteArray bstr = text.toUtf8();
cmd += QString::asprintf(" %s%c%.*s%c", opt, delim, bstr.length(), bstr.data(), delim);
}
arg_data_esc(cmd, opt, text, win);
}
}
void QZint::arg_float(QString &cmd, const char *const opt, const float val, const bool allowZero) {
void QZint::arg_seg(QString& cmd, const int seg_no, const QZintSeg& val, const bool win) {
QString text(val.m_text);
QString opt = QString::asprintf("--seg%d=%d,", seg_no, val.m_eci);
arg_data_esc(cmd, opt.toUtf8(), text, win);
}
void QZint::arg_data_esc(QString& cmd, const char *const opt, QString& text, const bool win) {
const char delim = win ? '"' : '\'';
if (win) {
// Difficult (impossible?) to fully escape strings on Windows, e.g. "blah%PATH%" will substitute
// env var PATH, so just doing basic escaping here
text.replace("\\\\", "\\\\\\\\"); // Double-up backslashed backslash `\\` -> `\\\\`
text.replace("\"", "\\\""); // Backslash quote `"` -> `\"`
QByteArray bstr = text.toUtf8();
cmd += QString::asprintf(" %s%c%.*s%c", opt, delim, bstr.length(), bstr.data(), delim);
} else {
text.replace("'", "'\\''"); // Single quote `'` -> `'\''`
QByteArray bstr = text.toUtf8();
cmd += QString::asprintf(" %s%c%.*s%c", opt, delim, bstr.length(), bstr.data(), delim);
}
}
void QZint::arg_float(QString& cmd, const char *const opt, const float val, const bool allowZero) {
if (val > 0 || (val == 0 && allowZero)) {
cmd += QString::asprintf(" %s%g", opt, val);
}
}
void QZint::arg_structapp(QString &cmd, const char *const opt, const int count, const int index,
const QString &id, const bool win) {
void QZint::arg_structapp(QString& cmd, const char *const opt, const int count, const int index,
const QString& id, const bool win) {
if (count >= 2 && index >= 1) {
if (id.isEmpty()) {
cmd += QString::asprintf(" %s%d,%d", opt, index, count);

View file

@ -1,7 +1,7 @@
/***************************************************************************
* Copyright (C) 2008 by BogDan Vatra *
* bogdan@licentia.eu *
* Copyright (C) 2010-2021 Robin Stuart *
* Copyright (C) 2010-2022 Robin Stuart *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -14,7 +14,6 @@
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/* vim: set ts=4 sw=4 et : */
#ifndef QZINT_H
#define QZINT_H
@ -26,6 +25,17 @@
namespace Zint
{
/* QString version of `struct zint_seg` */
class QZintSeg {
public:
QString m_text;
int m_eci;
QZintSeg();
QZintSeg(const QString& text, const int ECIIndex = 0); // `ECIIndex` is comboBox index (not ECI value)
};
/* Interface */
class QZint : public QObject
{
Q_OBJECT
@ -44,7 +54,10 @@ public:
void setInputMode(int input_mode);
QString text() const;
void setText(const QString& text);
void setText(const QString& text); // Clears segs
std::vector<QZintSeg> segs() const;
void setSegs(const std::vector<QZintSeg>& segs); // Clears text and sets eci
QString primaryMessage() const;
void setPrimaryMessage(const QString& primaryMessage);
@ -177,7 +190,7 @@ public:
const QString& lastError() const;
bool hasErrors() const;
bool save_to_file(const QString &filename);
bool save_to_file(const QString& filename);
/* Note: legacy argument `mode` is not used */
void render(QPainter& painter, const QRectF& paintRect, AspectRatioMode mode = IgnoreAspectRatio);
@ -189,7 +202,7 @@ public:
If HEIGHTPERROW_MODE set and non-zero `heightPerRow` given then use that for height instead of internal
height */
QString getAsCLI(const bool win, const bool longOptOnly = false, const bool barcodeNames = false,
const bool autoHeight = false, const float heightPerRow = 0.0f, const QString &outfile = "") const;
const bool autoHeight = false, const float heightPerRow = 0.0f, const QString& outfile = "") const;
signals:
void encoded();
@ -198,17 +211,22 @@ signals:
private:
void resetSymbol();
void encode();
int convertSegs(struct zint_seg segs[], std::vector<QByteArray>& bstrs);
static Qt::GlobalColor colourToQtColor(int colour);
/* `getAsCLI()` helpers */
static void arg_str(QString &cmd, const char *const opt, const QString &val);
static void arg_int(QString &cmd, const char *const opt, const int val, const bool allowZero = false);
static void arg_bool(QString &cmd, const char *const opt, const bool val);
static void arg_color(QString &cmd, const char *const opt, const QColor val);
static void arg_data(QString &cmd, const char *const opt, const QString &val, const bool win);
static void arg_float(QString &cmd, const char *const opt, const float val, const bool allowZero = false);
static void arg_structapp(QString &cmd, const char *const opt, const int count, const int index,
const QString &id, const bool win);
static void arg_str(QString& cmd, const char *const opt, const QString& val);
static void arg_int(QString& cmd, const char *const opt, const int val, const bool allowZero = false);
static void arg_bool(QString& cmd, const char *const opt, const bool val);
static void arg_color(QString& cmd, const char *const opt, const QColor val);
static void arg_data(QString& cmd, const char *const opt, const QString& val, const bool win);
static void arg_seg(QString& cmd, const int seg_no, const QZintSeg& val, const bool win);
static void arg_data_esc(QString& cmd, const char *const opt, QString& text, const bool win);
static void arg_float(QString& cmd, const char *const opt, const float val, const bool allowZero = false);
static void arg_structapp(QString& cmd, const char *const opt, const int count, const int index,
const QString& id, const bool win);
private:
zint_symbol *m_zintSymbol;
@ -216,6 +234,7 @@ private:
int m_input_mode;
QString m_text;
QString m_primaryMessage;
std::vector<QZintSeg> m_segs;
float m_height;
int m_option_1;
int m_option_2;
@ -256,4 +275,5 @@ private:
} /* namespace Zint */
/* vim: set ts=4 sw=4 et : */
#endif /* QZINT_H */

View file

@ -60,6 +60,35 @@ private slots:
QString text("text");
bc.setText(text);
QCOMPARE(bc.text(), text);
QCOMPARE(bc.segs().empty(), true);
std::vector<QString> segTexts;
std::vector<int> segECIs;
segTexts.push_back(QString("Τεχτ"));
segECIs.push_back(9);
segTexts.push_back(QString("貫やぐ禁"));
segECIs.push_back(20);
segTexts.push_back(QString("กขฯ"));
segECIs.push_back(13);
std::vector<Zint::QZintSeg> segs;
for (int i = 0; i < (int) segTexts.size(); i++) {
segs.push_back(Zint::QZintSeg(segTexts[i]));
segs.back().m_eci = segECIs[i];
}
bc.setSegs(segs);
QCOMPARE(bc.segs().size(), segs.size());
for (int i = 0; i < (int) segs.size(); i++) {
QCOMPARE(bc.segs()[i].m_text, segTexts[i]);
QCOMPARE(bc.segs()[i].m_eci, segECIs[i]);
}
QCOMPARE(bc.text().isEmpty(), true);
QCOMPARE(bc.eci(), segECIs[0]);
bc.setText(text);
QCOMPARE(bc.text(), text);
QCOMPARE(bc.segs().empty(), true);
QString primaryMessage("primary message");
bc.setPrimaryMessage(primaryMessage);
@ -343,7 +372,7 @@ private slots:
QTest::newRow("BARCODE_QRCODE") << BARCODE_QRCODE << "1234" << 0 << "" << 21 << 21;
if (!m_skipIfFontUsed) {
QTest::newRow("BARCODE_QRCODE no text") << BARCODE_QRCODE << "" << ZINT_ERROR_INVALID_DATA << "Error 205: No input data" << 0 << 0;
QTest::newRow("BARCODE_QRCODE no text") << BARCODE_QRCODE << "" << ZINT_ERROR_INVALID_DATA << "Error 773: Input segment 0 length zero" << 0 << 0;
}
}
@ -499,9 +528,9 @@ private slots:
<< true << 0 << 0 << 2 << 3 << 0 // cmyk-fontSetting
<< true << false << false << false << false << 0 // showText-rotateAngle
<< 7 << false << false << false << WARN_DEFAULT << false // eci-debug
<< "zint -b 92 --cmyk -d '12345678Ж0%var%' --dotsize=0.9 --dotty --eci=7 --fg=0000FF --scale=4"
<< "zint -b 92 --cmyk --eci=7 -d '12345678Ж0%var%' --dotsize=0.9 --dotty --fg=0000FF --scale=4"
" --secure=1 --structapp='1,2,as\"dfa'\\''sdf' --vwhitesp=3 -w 2"
<< "zint.exe -b 92 --cmyk -d \"12345678Ж0%var%\" --dotsize=0.9 --dotty --eci=7 --fg=0000FF --scale=4"
<< "zint.exe -b 92 --cmyk --eci=7 -d \"12345678Ж0%var%\" --dotsize=0.9 --dotty --fg=0000FF --scale=4"
" --secure=1 --structapp=\"1,2,as\\\"dfa'sdf\" --vwhitesp=3 -w 2"
<< "" << "";
@ -679,8 +708,8 @@ private slots:
<< false << 0 << 0 << 0 << 0 << 0 // cmyk-fontSetting
<< true << false << false << false << true << 0 // showText-rotateAngle
<< 29 << false << false << false << WARN_DEFAULT << false // eci-debug
<< "zint -b 116 -d 'éβÿ啊\\e\"'\\''' --eci=29 --esc --mask=0 --secure=2 --vers=5"
<< "zint.exe -b 116 -d \"éβÿ啊\\e\\\"'\" --eci=29 --esc --mask=0 --secure=2 --vers=5"
<< "zint -b 116 --eci=29 -d 'éβÿ啊\\e\"'\\''' --esc --mask=0 --secure=2 --vers=5"
<< "zint.exe -b 116 --eci=29 -d \"éβÿ啊\\e\\\"'\" --esc --mask=0 --secure=2 --vers=5"
<< "" << "";
QTest::newRow("BARCODE_HIBC_DM") << false << 10.0f << ""
@ -770,8 +799,8 @@ private slots:
<< false << 0 << 0 << 0 << 0 << 0 // cmyk-fontSetting
<< true << false << false << false << true << 180 // showText-rotateAngle
<< 20 << false << false << false << WARN_DEFAULT << false // eci-debug
<< "zint -b 145 -d 'テ' --eci=20 --rotate=180 --vers=8"
<< "zint.exe -b 145 -d \"\" --eci=20 --rotate=180 --vers=8"
<< "zint -b 145 --eci=20 -d 'テ' --rotate=180 --vers=8"
<< "zint.exe -b 145 --eci=20 -d \"\" --rotate=180 --vers=8"
<< "" << "";
QTest::newRow("BARCODE_ULTRA") << false << 0.0f << ""
@ -926,6 +955,44 @@ private slots:
}
}
void getAsCLISegsTest()
{
Zint::QZint bc;
QString cmd;
QString expected_cmd;
QString expected_win;
std::vector<QString> segTexts;
std::vector<int> segECIs;
segTexts.push_back(QString("Τεχτ"));
segECIs.push_back(9);
segTexts.push_back(QString("Téxt"));
segECIs.push_back(3);
segTexts.push_back(QString("กขฯ"));
segECIs.push_back(13);
segTexts.push_back(QString("貫やぐ禁"));
segECIs.push_back(20);
std::vector<Zint::QZintSeg> segs;
for (int i = 0; i < (int) segTexts.size(); i++) {
segs.push_back(Zint::QZintSeg(segTexts[i]));
segs.back().m_eci = segECIs[i];
}
bc.setSymbol(BARCODE_QRCODE);
bc.setSegs(segs);
bc.setDotty(true);
expected_cmd = "zint -b 58 --eci=9 -d 'Τεχτ' --seg1=3,'Téxt' --seg2=13,'กขฯ' --seg3=20,'貫やぐ禁' --dotty";
cmd = bc.getAsCLI(false /*win*/);
QCOMPARE(cmd, expected_cmd);
expected_win = "zint.exe -b 58 --eci=9 -d \"Τεχτ\" --seg1=3,\"Téxt\" --seg2=13,\"กขฯ\" --seg3=20,\"貫やぐ禁\" --dotty";
cmd = bc.getAsCLI(true /*win*/);
QCOMPARE(cmd, expected_win);
}
void qZintAndLibZintEqual_data()
{
QTest::addColumn<int>("symbology");