├── .clang-format ├── .gitignore ├── LICENSE ├── Qt-QrCodeGenerator.pri ├── Qt-QrCodeGenerator.pro ├── README.md ├── example ├── Example.pro ├── MainWindow.cpp ├── MainWindow.h ├── demo.gif └── main.cpp └── src ├── QrCodeGenerator.cpp ├── QrCodeGenerator.h └── qrcodegen ├── qrcodegen.cpp └── qrcodegen.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | Standard: Cpp11 3 | CommentPragmas: "^!|^:" 4 | PointerBindsToType: false 5 | SpaceAfterTemplateKeyword: false 6 | BreakBeforeBinaryOperators: All 7 | BreakBeforeBraces: Custom 8 | BreakBeforeTernaryOperators: true 9 | BreakConstructorInitializers: BeforeComma 10 | ConstructorInitializerIndentWidth: 2 11 | NamespaceIndentation: None 12 | AlignAfterOpenBracket: true 13 | AlwaysBreakTemplateDeclarations: true 14 | AllowShortFunctionsOnASingleLine: Inline 15 | SortIncludes: false 16 | IndentCaseLabels: true 17 | IndentPPDirectives: AfterHash 18 | AccessModifierOffset: -2 19 | IndentWidth: 2 20 | ColumnLimit: 80 21 | 22 | BraceWrapping: 23 | AfterClass: true 24 | AfterControlStatement: true 25 | AfterEnum: true 26 | AfterFunction: true 27 | AfterNamespace: true 28 | AfterObjCDeclaration: true 29 | AfterStruct: true 30 | AfterUnion: true 31 | BeforeCatch: true 32 | BeforeElse: true 33 | IndentBraces: false 34 | 35 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] 36 | #StatementMacros ['Q_OBJECT', 'Q_UNUSED'] 37 | 38 | 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # General 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | .cache 7 | 8 | # Binary builds 9 | udp_function_generator 10 | udp_function_generator.exe 11 | 12 | # Build & IDEs 13 | *.kdev* 14 | build/* 15 | example/build/* 16 | *.vscode 17 | *.layout 18 | *.qmlls.ini 19 | 20 | # Icon must end with two \r 21 | Icon 22 | 23 | # Thumbnails 24 | ._* 25 | 26 | # Files that might appear in the root of a volume 27 | .DocumentRevisions-V100 28 | .fseventsd 29 | .Spotlight-V100 30 | .TemporaryItems 31 | .Trashes 32 | .VolumeIcon.icns 33 | .com.apple.timemachine.donotpresent 34 | 35 | # Directories potentially created on remote AFP share 36 | .AppleDB 37 | .AppleDesktop 38 | Network Trash Folder 39 | Temporary Items 40 | .apdisk 41 | 42 | # Qt project file 43 | *.pro.user 44 | *.user 45 | 46 | # Clion 47 | .idea/* 48 | cmake-build-* 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Alex Spataru 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Qt-QrCodeGenerator.pri: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Alex Spataru 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | QT += core gui svg 24 | 25 | INCLUDEPATH += $$PWD/src 26 | 27 | HEADERS += \ 28 | $$PWD/src/QrCodeGenerator.h \ 29 | $$PWD/src/qrcodegen/qrcodegen.h 30 | 31 | SOURCES += \ 32 | $$PWD/src/QrCodeGenerator.cpp \ 33 | $$PWD/src/qrcodegen/qrcodegen.cpp 34 | -------------------------------------------------------------------------------- /Qt-QrCodeGenerator.pro: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Alex Spataru 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | TARGET = Qt-QrCodeGenerator 24 | TEMPLATE = lib 25 | 26 | DEFINES += QR_CODE_GENERATOR_LIBRARY 27 | 28 | include($$PWD/Qt-QrCodeGenerator.pri) 29 | 30 | unix { 31 | target.path = /usr/lib 32 | INSTALLS += target 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qt QR Code Generator Library 2 | 3 | Qt QR Code Generator is a simple C++ class that uses the [qrcodegen](https://github.com/nayuki/QR-Code-generator) library to generate QR codes from QStrings in Qt applications. 4 | 5 | [![Demo](example/demo.gif)](example/demo.gif) 6 | 7 | ## Usage 8 | 9 | 1. Copy the *Qt-QrCodeGenerator* folder in your `lib` folder. 10 | 2. Include the *Qt-QrCodeGenerator* project include (pri) file using the `include()` qmake function. 11 | 3. Use the `QrCodeGenerator` class in your code: 12 | 13 | ```cpp 14 | #include 15 | 16 | QrCodeGenerator generator; 17 | QString data = "https://www.example.com"; 18 | QImage qrCodeImage = generator.generateQr(data); 19 | ``` 20 | 21 | 4. That's all! Check the [example](example) project as a reference for your project if needed. 22 | 23 | ## License 24 | 25 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 26 | -------------------------------------------------------------------------------- /example/Example.pro: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Alex Spataru 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | TEMPLATE = app 24 | TARGET = QR-Example 25 | 26 | QT += core gui svg widgets 27 | 28 | include(../Qt-QrCodeGenerator.pri) 29 | 30 | HEADERS += \ 31 | MainWindow.h 32 | 33 | SOURCES += \ 34 | MainWindow.cpp \ 35 | main.cpp 36 | -------------------------------------------------------------------------------- /example/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Alex Spataru 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include "MainWindow.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | /** 30 | * Constructor function, initializes the user interface & setups signals/slots. 31 | */ 32 | MainWindow::MainWindow() 33 | { 34 | // Initialize layout-related widgets 35 | m_centralWidget = new QWidget(this); 36 | m_header = new QWidget(m_centralWidget); 37 | 38 | // Initialize horizontal layout widgets 39 | m_label = new QLabel(tr("Message") + ": ", m_header); 40 | m_saveButton = new QPushButton(tr("Export QR Code"), m_header); 41 | m_lineEdit = new QLineEdit(m_header); 42 | 43 | // Initialize QR code image label 44 | m_qrCodeDisplay = new QLabel(m_centralWidget); 45 | m_qrCodeDisplay->setMinimumSize(QSize(500, 500)); 46 | m_qrCodeDisplay->setMaximumSize(QSize(500, 500)); 47 | 48 | // Setup horizontal layout for line edit & push button 49 | m_verticalLayout = new QVBoxLayout(m_centralWidget); 50 | m_headerLayout = new QHBoxLayout(m_header); 51 | m_headerLayout->addWidget(m_label); 52 | m_headerLayout->addWidget(m_lineEdit); 53 | m_headerLayout->addWidget(m_saveButton); 54 | m_headerLayout->setContentsMargins(0, 0, 0, 0); 55 | m_headerLayout->setStretch(1, 1); 56 | 57 | // Setup vertical layout with QR code label & QR code generation controls 58 | m_verticalLayout->addWidget(m_header); 59 | m_verticalLayout->addWidget(m_qrCodeDisplay); 60 | m_verticalLayout->setStretch(0, 1); 61 | 62 | // Set central widget & resize window to minimum size possible 63 | setCentralWidget(m_centralWidget); 64 | setFixedSize(minimumSize()); 65 | 66 | // Set window title 67 | setWindowTitle(tr("QR Code Generator")); 68 | 69 | // Update the QR code when the text changes 70 | connect(m_lineEdit, &QLineEdit::textChanged, this, 71 | [this](const QString &text) { 72 | m_saveButton->setEnabled(!text.isEmpty()); 73 | if (!text.isEmpty()) 74 | generateQrCode(); 75 | }); 76 | 77 | // Export QR code as when user clicks on the save button 78 | connect(m_saveButton, &QPushButton::clicked, this, &MainWindow::saveQrCode); 79 | 80 | // Set "Hello World" text & generate QR code 81 | m_lineEdit->setText(tr("Hello World!")); 82 | generateQrCode(); 83 | } 84 | 85 | /** 86 | * Destructor function. 87 | */ 88 | MainWindow::~MainWindow() 89 | { 90 | delete m_label; 91 | delete m_lineEdit; 92 | delete m_saveButton; 93 | delete m_qrCodeDisplay; 94 | delete m_headerLayout; 95 | delete m_verticalLayout; 96 | delete m_header; 97 | delete m_centralWidget; 98 | } 99 | 100 | /** 101 | * Generates a QR code from text input & saves it as a SVG file. 102 | */ 103 | void MainWindow::saveQrCode() 104 | { 105 | // Obtain SVG data for QR code 106 | auto svg = m_generator.generateSvgQr(m_lineEdit->text()); 107 | if (svg.isEmpty()) 108 | return; 109 | 110 | // Ask user where to save the SVG file 111 | auto path = QFileDialog::getSaveFileName(this, tr("Save QR Code"), 112 | QDir::homePath(), tr("*.svg")); 113 | 114 | // Write SVG data into the file selected by the user 115 | if (!path.isEmpty()) 116 | { 117 | QFile file(path); 118 | if (file.open(QFile::WriteOnly)) 119 | { 120 | file.write(svg.toUtf8()); 121 | file.close(); 122 | } 123 | } 124 | } 125 | 126 | /** 127 | * Generates a QR code from text input & displays it in the UI. 128 | */ 129 | void MainWindow::generateQrCode() 130 | { 131 | // Get text from line edit 132 | auto text = m_lineEdit->text(); 133 | 134 | // Generate QR code from text 135 | QImage unscaledImage = m_generator.generateQr(text); 136 | QImage image = unscaledImage.scaled(500, 500); 137 | 138 | // Display generated image 139 | m_qrCodeDisplay->setPixmap(QPixmap::fromImage(image)); 140 | m_qrCodeDisplay->setToolTip(text); 141 | } 142 | -------------------------------------------------------------------------------- /example/MainWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Alex Spataru 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | class MainWindow : public QMainWindow 34 | { 35 | Q_OBJECT 36 | 37 | public: 38 | explicit MainWindow(); 39 | ~MainWindow(); 40 | 41 | private Q_SLOTS: 42 | void saveQrCode(); 43 | void generateQrCode(); 44 | 45 | private: 46 | QrCodeGenerator m_generator; 47 | 48 | QWidget *m_header; 49 | QWidget *m_centralWidget; 50 | 51 | QLabel *m_label; 52 | QLineEdit *m_lineEdit; 53 | QLabel *m_qrCodeDisplay; 54 | QPushButton *m_saveButton; 55 | 56 | QHBoxLayout *m_headerLayout; 57 | QVBoxLayout *m_verticalLayout; 58 | }; 59 | -------------------------------------------------------------------------------- /example/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-spataru/Qt-QrCodeGenerator/0183960a41ea425c48098c9879fb44413f8f768e/example/demo.gif -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Alex Spataru 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include "MainWindow.h" 24 | 25 | /** 26 | * Entry-point function of the application. 27 | */ 28 | int main(int argc, char **argv) 29 | { 30 | QApplication app(argc, argv); 31 | MainWindow window; 32 | window.show(); 33 | return app.exec(); 34 | } 35 | -------------------------------------------------------------------------------- /src/QrCodeGenerator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Alex Spataru 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "QrCodeGenerator.h" 30 | 31 | /** 32 | * @brief Default constructor for the QrCodeGenerator class. 33 | * @param parent QObject parent 34 | */ 35 | QrCodeGenerator::QrCodeGenerator(QObject *parent) 36 | : QObject(parent) 37 | { 38 | // No implementation needed for default constructor. 39 | } 40 | 41 | /** 42 | * @brief Generates a QR code image from a given text string. 43 | * @param data The data to encode in the QR code. 44 | * @param size The image size for the generated QR code. 45 | * @param borderSize The size of the border around the QR code. 46 | * @param errorCorrection The level of error correction to apply. 47 | * @return QImage representing the generated QR code. 48 | */ 49 | QImage QrCodeGenerator::generateQr(const QString &data, quint16 size, 50 | quint16 borderSize, 51 | qrcodegen::QrCode::Ecc errorCorrection) 52 | { 53 | auto b = data.toUtf8(); 54 | const auto qrCode 55 | = qrcodegen::QrCode::encodeText(b.constData(), errorCorrection); 56 | return qrCodeToImage(qrCode, borderSize, size); 57 | } 58 | 59 | /** 60 | * @brief Generates a QR code image from a given text string. 61 | * @param data The data to encode in the QR code. 62 | * @param size The image size for the generated QR code. 63 | * @param borderSize The size of the border around the QR code. 64 | * @param errorCorrection The level of error correction to apply. 65 | * @return QImage representing the generated QR code. 66 | */ 67 | QImage QrCodeGenerator::generateQr(const QByteArray &data, quint16 size, 68 | quint16 borderSize, 69 | qrcodegen::QrCode::Ecc errorCorrection) 70 | { 71 | std::vector c(data.constData(), data.constData() + data.size()); 72 | const auto qrCode 73 | = qrcodegen::QrCode::encodeBinary(c, errorCorrection); 74 | return qrCodeToImage(qrCode, borderSize, size); 75 | } 76 | 77 | /** 78 | * @brief Generates an SVG string representing a QR code. 79 | * @param data The data to encode in the QR code. 80 | * @param borderSize The size of the border around the QR code. 81 | * @param errorCorrection The level of error correction to apply. 82 | * @return QString containing the SVG representation of the QR code. 83 | */ 84 | QString QrCodeGenerator::generateSvgQr(const QString &data, quint16 borderSize, 85 | qrcodegen::QrCode::Ecc errorCorrection) 86 | { 87 | auto b = data.toUtf8(); 88 | const auto qrCode 89 | = qrcodegen::QrCode::encodeText(b.constData(), errorCorrection); 90 | return toSvgString(qrCode, borderSize); 91 | } 92 | 93 | /** 94 | * @brief Converts a QR code to its SVG representation as a string. 95 | * @param qr The QR code to convert. 96 | * @param border The border size to use. 97 | * @return QString containing the SVG representation of the QR code. 98 | */ 99 | QString QrCodeGenerator::toSvgString(const qrcodegen::QrCode &qr, 100 | quint16 border) const 101 | { 102 | QString str; 103 | QTextStream sb(&str); 104 | 105 | sb << R"()" 106 | << R"()" 107 | << R"()"; 124 | return str; 125 | } 126 | 127 | /** 128 | * @brief Converts a QR code to a QImage. 129 | * @param qrCode The QR code to convert. 130 | * @param border The border size to use. 131 | * @param size The image size to generate. 132 | * @return QImage representing the QR code. 133 | */ 134 | // QImage QrCodeGenerator::qrCodeToImage(const qrcodegen::QrCode &qrCode, 135 | // quint16 border, quint16 size) const 136 | // { 137 | // QString svg = toSvgString(qrCode, border); 138 | // QSvgRenderer render(svg.toUtf8()); 139 | // QImage image(size, size, QImage::Format_Mono); 140 | // image.fill(Qt::white); 141 | // QPainter painter(&image); 142 | // painter.setRenderHint(QPainter::Antialiasing); 143 | // render.render(&painter); 144 | // return image; 145 | // } 146 | 147 | // QR code image is being rendered from SVG output, not directly from bitmaps or 148 | // matrices QSvgRenderer has internal limits on path complexity or buffer size — 149 | // hitting those leads to the truncation warning. 150 | // If the generated SVG string is too large or malformed, the QSvgRenderer will 151 | // throw warnings. 152 | // like: W : qt.svg: Invalid path data; path truncated. 153 | QImage QrCodeGenerator::qrCodeToImage(const qrcodegen::QrCode &qrCode, 154 | quint16 border, quint16 size) const 155 | { 156 | const int qrSize = qrCode.getSize(); 157 | const int totalSize = qrSize + 2 * border; 158 | // Calculate scaling factor to fit requested size 159 | const int pixelSize = size / totalSize; 160 | const int imageSize = pixelSize * totalSize; 161 | // Create the output image 162 | QImage image(imageSize, imageSize, QImage::Format_RGB32); 163 | image.fill(Qt::white); 164 | 165 | QPainter painter(&image); 166 | painter.setBrush(Qt::black); 167 | painter.setPen(Qt::NoPen); 168 | 169 | // Draw each QR module (black square) 170 | for (int y = 0; y < qrSize; ++y) 171 | { 172 | for (int x = 0; x < qrSize; ++x) 173 | { 174 | if (qrCode.getModule(x, y)) 175 | { 176 | int xPos = (x + border) * pixelSize; 177 | int yPos = (y + border) * pixelSize; 178 | painter.drawRect(xPos, yPos, pixelSize, pixelSize); 179 | } 180 | } 181 | } 182 | 183 | return image; 184 | } 185 | -------------------------------------------------------------------------------- /src/QrCodeGenerator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Alex Spataru 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "qrcodegen/qrcodegen.h" 30 | 31 | /** 32 | * @class QrCodeGenerator 33 | * @brief The QrCodeGenerator class is a simple C++ class that uses the 34 | * qrcodegen library to generate QR codes from QStrings in Qt applications. 35 | */ 36 | class QrCodeGenerator : public QObject 37 | { 38 | public: 39 | /** 40 | * @brief Constructs a QrCodeGenerator object. 41 | * @param parent The parent QObject. 42 | */ 43 | explicit QrCodeGenerator(QObject *parent = nullptr); 44 | 45 | /** 46 | * @brief Generates a QR code from the given data and error correction level. 47 | * @param data The QString containing the data to encode in the QR code. 48 | * @param size The desired width/height of the generated image (default: 500). 49 | * @param borderSize The desired border width of the generated image (default: 50 | * 1). 51 | * @param errorCorrection The desired error correction level (default: 52 | * qrcodegen::QrCode::Ecc::MEDIUM). 53 | * 54 | * @return QImage containing the generated QR code. 55 | */ 56 | QImage generateQr(const QString &data, const quint16 size = 1000, 57 | const quint16 borderSize = 1, 58 | qrcodegen::QrCode::Ecc errorCorrection 59 | = qrcodegen::QrCode::Ecc::MEDIUM); 60 | 61 | /** 62 | * @brief Generates a QR code from the given data and error correction level. 63 | * @param data The QByteArray containing the data to encode in the QR code. 64 | * @param size The desired width/height of the generated image (default: 500). 65 | * @param borderSize The desired border width of the generated image (default: 66 | * 1). 67 | * @param errorCorrection The desired error correction level (default: 68 | * qrcodegen::QrCode::Ecc::MEDIUM). 69 | * 70 | * @return QImage containing the generated QR code. 71 | */ 72 | QImage generateQr(const QByteArray &data, const quint16 size = 1000, 73 | const quint16 borderSize = 1, 74 | qrcodegen::QrCode::Ecc errorCorrection 75 | = qrcodegen::QrCode::Ecc::MEDIUM); 76 | 77 | /** 78 | * @brief Generates a QR code from the given data and error correction level. 79 | * @param data The QString containing the data to encode in the QR code. 80 | * @param borderSize The desired border width of the generated image (default: 81 | * 1). 82 | * @param errorCorrection The desired error correction level (default: 83 | * qrcodegen::QrCode::Ecc::MEDIUM). 84 | * 85 | * @return QString string containing the generated QR code in SVG format. 86 | */ 87 | QString generateSvgQr(const QString &data, const quint16 borderSize = 1, 88 | qrcodegen::QrCode::Ecc errorCorrection 89 | = qrcodegen::QrCode::Ecc::MEDIUM); 90 | 91 | private: 92 | /** 93 | * @brief Converts a qrcodegen::QrCode object to a SVG image. 94 | * @param qrCode The qrcodegen::QrCode object to convert. 95 | * @param border The desired border width of the generated image (default: 1). 96 | * 97 | * @return SVG containing the QR code. 98 | */ 99 | QString toSvgString(const qrcodegen::QrCode &qr, quint16 border) const; 100 | 101 | /** 102 | * @brief Converts a qrcodegen::QrCode object to a QImage. 103 | * @param qrCode The qrcodegen::QrCode object to convert. 104 | * @param size The desired width/height of the generated image. 105 | * @param borderSize The desired border width of the generated image. 106 | * 107 | * @return QImage containing the QR code. 108 | */ 109 | QImage qrCodeToImage(const qrcodegen::QrCode &qrCode, quint16 border, 110 | const quint16 size) const; 111 | }; 112 | -------------------------------------------------------------------------------- /src/qrcodegen/qrcodegen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki. (MIT License) 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * - The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * - The Software is provided "as is", without warranty of any kind, express or 16 | * implied, including but not limited to the warranties of merchantability, 17 | * fitness for a particular purpose and noninfringement. In no event shall the 18 | * authors or copyright holders be liable for any claim, damages or other 19 | * liability, whether in an action of contract, tort or otherwise, arising 20 | * from, out of or in connection with the Software or the use or other dealings 21 | * in the Software. 22 | */ 23 | 24 | #include "qrcodegen.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | using std::int8_t; 36 | using std::size_t; 37 | using std::uint8_t; 38 | using std::vector; 39 | 40 | namespace qrcodegen 41 | { 42 | 43 | /*---- Class QrSegment ----*/ 44 | 45 | QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) 46 | : modeBits(mode) 47 | { 48 | numBitsCharCount[0] = cc0; 49 | numBitsCharCount[1] = cc1; 50 | numBitsCharCount[2] = cc2; 51 | } 52 | 53 | int QrSegment::Mode::getModeBits() const 54 | { 55 | return modeBits; 56 | } 57 | 58 | int QrSegment::Mode::numCharCountBits(int ver) const 59 | { 60 | return numBitsCharCount[(ver + 7) / 17]; 61 | } 62 | 63 | const QrSegment::Mode QrSegment::Mode::NUMERIC(0x1, 10, 12, 14); 64 | const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); 65 | const QrSegment::Mode QrSegment::Mode::BYTE(0x4, 8, 16, 16); 66 | const QrSegment::Mode QrSegment::Mode::KANJI(0x8, 8, 10, 12); 67 | const QrSegment::Mode QrSegment::Mode::ECI(0x7, 0, 0, 0); 68 | 69 | QrSegment QrSegment::makeBytes(const vector &data) 70 | { 71 | if (data.size() > static_cast(INT_MAX)) 72 | throw std::length_error("Data too long"); 73 | BitBuffer bb; 74 | for (uint8_t b : data) 75 | bb.appendBits(b, 8); 76 | return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); 77 | } 78 | 79 | QrSegment QrSegment::makeNumeric(const char *digits) 80 | { 81 | BitBuffer bb; 82 | int accumData = 0; 83 | int accumCount = 0; 84 | int charCount = 0; 85 | for (; *digits != '\0'; digits++, charCount++) 86 | { 87 | char c = *digits; 88 | if (c < '0' || c > '9') 89 | throw std::domain_error("String contains non-numeric characters"); 90 | accumData = accumData * 10 + (c - '0'); 91 | accumCount++; 92 | if (accumCount == 3) 93 | { 94 | bb.appendBits(static_cast(accumData), 10); 95 | accumData = 0; 96 | accumCount = 0; 97 | } 98 | } 99 | if (accumCount > 0) // 1 or 2 digits remaining 100 | bb.appendBits(static_cast(accumData), accumCount * 3 + 1); 101 | return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); 102 | } 103 | 104 | QrSegment QrSegment::makeAlphanumeric(const char *text) 105 | { 106 | BitBuffer bb; 107 | int accumData = 0; 108 | int accumCount = 0; 109 | int charCount = 0; 110 | for (; *text != '\0'; text++, charCount++) 111 | { 112 | const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); 113 | if (temp == nullptr) 114 | throw std::domain_error( 115 | "String contains unencodable characters in alphanumeric mode"); 116 | accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); 117 | accumCount++; 118 | if (accumCount == 2) 119 | { 120 | bb.appendBits(static_cast(accumData), 11); 121 | accumData = 0; 122 | accumCount = 0; 123 | } 124 | } 125 | if (accumCount > 0) // 1 character remaining 126 | bb.appendBits(static_cast(accumData), 6); 127 | return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); 128 | } 129 | 130 | vector QrSegment::makeSegments(const char *text) 131 | { 132 | // Select the most efficient segment encoding automatically 133 | vector result; 134 | if (*text == '\0') 135 | ; // Leave result empty 136 | else if (isNumeric(text)) 137 | result.push_back(makeNumeric(text)); 138 | else if (isAlphanumeric(text)) 139 | result.push_back(makeAlphanumeric(text)); 140 | else 141 | { 142 | vector bytes; 143 | for (; *text != '\0'; text++) 144 | bytes.push_back(static_cast(*text)); 145 | result.push_back(makeBytes(bytes)); 146 | } 147 | return result; 148 | } 149 | 150 | QrSegment QrSegment::makeEci(long assignVal) 151 | { 152 | BitBuffer bb; 153 | if (assignVal < 0) 154 | throw std::domain_error("ECI assignment value out of range"); 155 | else if (assignVal < (1 << 7)) 156 | bb.appendBits(static_cast(assignVal), 8); 157 | else if (assignVal < (1 << 14)) 158 | { 159 | bb.appendBits(2, 2); 160 | bb.appendBits(static_cast(assignVal), 14); 161 | } 162 | else if (assignVal < 1000000L) 163 | { 164 | bb.appendBits(6, 3); 165 | bb.appendBits(static_cast(assignVal), 21); 166 | } 167 | else 168 | throw std::domain_error("ECI assignment value out of range"); 169 | return QrSegment(Mode::ECI, 0, std::move(bb)); 170 | } 171 | 172 | QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) 173 | : mode(&md) 174 | , numChars(numCh) 175 | , data(dt) 176 | { 177 | if (numCh < 0) 178 | throw std::domain_error("Invalid value"); 179 | } 180 | 181 | QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) 182 | : mode(&md) 183 | , numChars(numCh) 184 | , data(std::move(dt)) 185 | { 186 | if (numCh < 0) 187 | throw std::domain_error("Invalid value"); 188 | } 189 | 190 | int QrSegment::getTotalBits(const vector &segs, int version) 191 | { 192 | int result = 0; 193 | for (const QrSegment &seg : segs) 194 | { 195 | int ccbits = seg.mode->numCharCountBits(version); 196 | if (seg.numChars >= (1L << ccbits)) 197 | return -1; // The segment's length doesn't fit the field's bit width 198 | if (4 + ccbits > INT_MAX - result) 199 | return -1; // The sum will overflow an int type 200 | result += 4 + ccbits; 201 | if (seg.data.size() > static_cast(INT_MAX - result)) 202 | return -1; // The sum will overflow an int type 203 | result += static_cast(seg.data.size()); 204 | } 205 | return result; 206 | } 207 | 208 | bool QrSegment::isNumeric(const char *text) 209 | { 210 | for (; *text != '\0'; text++) 211 | { 212 | char c = *text; 213 | if (c < '0' || c > '9') 214 | return false; 215 | } 216 | return true; 217 | } 218 | 219 | bool QrSegment::isAlphanumeric(const char *text) 220 | { 221 | for (; *text != '\0'; text++) 222 | { 223 | if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) 224 | return false; 225 | } 226 | return true; 227 | } 228 | 229 | const QrSegment::Mode &QrSegment::getMode() const 230 | { 231 | return *mode; 232 | } 233 | 234 | int QrSegment::getNumChars() const 235 | { 236 | return numChars; 237 | } 238 | 239 | const std::vector &QrSegment::getData() const 240 | { 241 | return data; 242 | } 243 | 244 | const char *QrSegment::ALPHANUMERIC_CHARSET 245 | = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; 246 | 247 | /*---- Class QrCode ----*/ 248 | 249 | int QrCode::getFormatBits(Ecc ecl) 250 | { 251 | switch (ecl) 252 | { 253 | case Ecc::LOW: 254 | return 1; 255 | case Ecc::MEDIUM: 256 | return 0; 257 | case Ecc::QUARTILE: 258 | return 3; 259 | case Ecc::HIGH: 260 | return 2; 261 | default: 262 | throw std::logic_error("Unreachable"); 263 | } 264 | } 265 | 266 | QrCode QrCode::encodeText(const char *text, Ecc ecl) 267 | { 268 | vector segs = QrSegment::makeSegments(text); 269 | return encodeSegments(segs, ecl); 270 | } 271 | 272 | QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) 273 | { 274 | vector segs{QrSegment::makeBytes(data)}; 275 | return encodeSegments(segs, ecl); 276 | } 277 | 278 | QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, 279 | int minVersion, int maxVersion, int mask, 280 | bool boostEcl) 281 | { 282 | if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion 283 | && maxVersion <= MAX_VERSION) 284 | || mask < -1 || mask > 7) 285 | throw std::invalid_argument("Invalid value"); 286 | 287 | // Find the minimal version number to use 288 | int version, dataUsedBits; 289 | for (version = minVersion;; version++) 290 | { 291 | int dataCapacityBits = getNumDataCodewords(version, ecl) 292 | * 8; // Number of data bits available 293 | dataUsedBits = QrSegment::getTotalBits(segs, version); 294 | if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) 295 | break; // This version number is found to be suitable 296 | if (version >= maxVersion) 297 | { // All versions in the range could not fit the given data 298 | std::ostringstream sb; 299 | if (dataUsedBits == -1) 300 | sb << "Segment too long"; 301 | else 302 | { 303 | sb << "Data length = " << dataUsedBits << " bits, "; 304 | sb << "Max capacity = " << dataCapacityBits << " bits"; 305 | } 306 | throw data_too_long(sb.str()); 307 | } 308 | } 309 | assert(dataUsedBits != -1); 310 | 311 | // Increase the error correction level while the data still fits in the 312 | // current version number 313 | for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) 314 | { // From low to high 315 | if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) 316 | ecl = newEcl; 317 | } 318 | 319 | // Concatenate all segments to create the data bit string 320 | BitBuffer bb; 321 | for (const QrSegment &seg : segs) 322 | { 323 | bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); 324 | bb.appendBits(static_cast(seg.getNumChars()), 325 | seg.getMode().numCharCountBits(version)); 326 | bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); 327 | } 328 | assert(bb.size() == static_cast(dataUsedBits)); 329 | 330 | // Add terminator and pad up to a byte if applicable 331 | size_t dataCapacityBits 332 | = static_cast(getNumDataCodewords(version, ecl)) * 8; 333 | assert(bb.size() <= dataCapacityBits); 334 | bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); 335 | bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); 336 | assert(bb.size() % 8 == 0); 337 | 338 | // Pad with alternating bytes until data capacity is reached 339 | for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; 340 | padByte ^= 0xEC ^ 0x11) 341 | bb.appendBits(padByte, 8); 342 | 343 | // Pack bits into bytes in big endian 344 | vector dataCodewords(bb.size() / 8); 345 | for (size_t i = 0; i < bb.size(); i++) 346 | dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); 347 | 348 | // Create the QR Code object 349 | return QrCode(version, ecl, dataCodewords, mask); 350 | } 351 | 352 | QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) 353 | : // Initialize fields and check arguments 354 | version(ver) 355 | , errorCorrectionLevel(ecl) 356 | { 357 | if (ver < MIN_VERSION || ver > MAX_VERSION) 358 | throw std::domain_error("Version value out of range"); 359 | if (msk < -1 || msk > 7) 360 | throw std::domain_error("Mask value out of range"); 361 | size = ver * 4 + 17; 362 | size_t sz = static_cast(size); 363 | modules = vector>(sz, vector(sz)); // Initially all light 364 | isFunction = vector>(sz, vector(sz)); 365 | 366 | // Compute ECC, draw modules 367 | drawFunctionPatterns(); 368 | const vector allCodewords = addEccAndInterleave(dataCodewords); 369 | drawCodewords(allCodewords); 370 | 371 | // Do masking 372 | if (msk == -1) 373 | { // Automatically choose best mask 374 | long minPenalty = LONG_MAX; 375 | for (int i = 0; i < 8; i++) 376 | { 377 | applyMask(i); 378 | drawFormatBits(i); 379 | long penalty = getPenaltyScore(); 380 | if (penalty < minPenalty) 381 | { 382 | msk = i; 383 | minPenalty = penalty; 384 | } 385 | applyMask(i); // Undoes the mask due to XOR 386 | } 387 | } 388 | assert(0 <= msk && msk <= 7); 389 | mask = msk; 390 | applyMask(msk); // Apply the final choice of mask 391 | drawFormatBits(msk); // Overwrite old format bits 392 | 393 | isFunction.clear(); 394 | isFunction.shrink_to_fit(); 395 | } 396 | 397 | int QrCode::getVersion() const 398 | { 399 | return version; 400 | } 401 | 402 | int QrCode::getSize() const 403 | { 404 | return size; 405 | } 406 | 407 | QrCode::Ecc QrCode::getErrorCorrectionLevel() const 408 | { 409 | return errorCorrectionLevel; 410 | } 411 | 412 | int QrCode::getMask() const 413 | { 414 | return mask; 415 | } 416 | 417 | bool QrCode::getModule(int x, int y) const 418 | { 419 | return 0 <= x && x < size && 0 <= y && y < size && module(x, y); 420 | } 421 | 422 | void QrCode::drawFunctionPatterns() 423 | { 424 | // Draw horizontal and vertical timing patterns 425 | for (int i = 0; i < size; i++) 426 | { 427 | setFunctionModule(6, i, i % 2 == 0); 428 | setFunctionModule(i, 6, i % 2 == 0); 429 | } 430 | 431 | // Draw 3 finder patterns (all corners except bottom right; overwrites some 432 | // timing modules) 433 | drawFinderPattern(3, 3); 434 | drawFinderPattern(size - 4, 3); 435 | drawFinderPattern(3, size - 4); 436 | 437 | // Draw numerous alignment patterns 438 | const vector alignPatPos = getAlignmentPatternPositions(); 439 | size_t numAlign = alignPatPos.size(); 440 | for (size_t i = 0; i < numAlign; i++) 441 | { 442 | for (size_t j = 0; j < numAlign; j++) 443 | { 444 | // Don't draw on the three finder corners 445 | if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) 446 | || (i == numAlign - 1 && j == 0))) 447 | drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); 448 | } 449 | } 450 | 451 | // Draw configuration data 452 | drawFormatBits(0); // Dummy mask value; overwritten later in the constructor 453 | drawVersion(); 454 | } 455 | 456 | void QrCode::drawFormatBits(int msk) 457 | { 458 | // Calculate error correction code and pack bits 459 | int data = getFormatBits(errorCorrectionLevel) << 3 460 | | msk; // errCorrLvl is uint2, msk is uint3 461 | int rem = data; 462 | for (int i = 0; i < 10; i++) 463 | rem = (rem << 1) ^ ((rem >> 9) * 0x537); 464 | int bits = (data << 10 | rem) ^ 0x5412; // uint15 465 | assert(bits >> 15 == 0); 466 | 467 | // Draw first copy 468 | for (int i = 0; i <= 5; i++) 469 | setFunctionModule(8, i, getBit(bits, i)); 470 | setFunctionModule(8, 7, getBit(bits, 6)); 471 | setFunctionModule(8, 8, getBit(bits, 7)); 472 | setFunctionModule(7, 8, getBit(bits, 8)); 473 | for (int i = 9; i < 15; i++) 474 | setFunctionModule(14 - i, 8, getBit(bits, i)); 475 | 476 | // Draw second copy 477 | for (int i = 0; i < 8; i++) 478 | setFunctionModule(size - 1 - i, 8, getBit(bits, i)); 479 | for (int i = 8; i < 15; i++) 480 | setFunctionModule(8, size - 15 + i, getBit(bits, i)); 481 | setFunctionModule(8, size - 8, true); // Always dark 482 | } 483 | 484 | void QrCode::drawVersion() 485 | { 486 | if (version < 7) 487 | return; 488 | 489 | // Calculate error correction code and pack bits 490 | int rem = version; // version is uint6, in the range [7, 40] 491 | for (int i = 0; i < 12; i++) 492 | rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); 493 | long bits = static_cast(version) << 12 | rem; // uint18 494 | assert(bits >> 18 == 0); 495 | 496 | // Draw two copies 497 | for (int i = 0; i < 18; i++) 498 | { 499 | bool bit = getBit(bits, i); 500 | int a = size - 11 + i % 3; 501 | int b = i / 3; 502 | setFunctionModule(a, b, bit); 503 | setFunctionModule(b, a, bit); 504 | } 505 | } 506 | 507 | void QrCode::drawFinderPattern(int x, int y) 508 | { 509 | for (int dy = -4; dy <= 4; dy++) 510 | { 511 | for (int dx = -4; dx <= 4; dx++) 512 | { 513 | int dist 514 | = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm 515 | int xx = x + dx, yy = y + dy; 516 | if (0 <= xx && xx < size && 0 <= yy && yy < size) 517 | setFunctionModule(xx, yy, dist != 2 && dist != 4); 518 | } 519 | } 520 | } 521 | 522 | void QrCode::drawAlignmentPattern(int x, int y) 523 | { 524 | for (int dy = -2; dy <= 2; dy++) 525 | { 526 | for (int dx = -2; dx <= 2; dx++) 527 | setFunctionModule(x + dx, y + dy, 528 | std::max(std::abs(dx), std::abs(dy)) != 1); 529 | } 530 | } 531 | 532 | void QrCode::setFunctionModule(int x, int y, bool isDark) 533 | { 534 | size_t ux = static_cast(x); 535 | size_t uy = static_cast(y); 536 | modules.at(uy).at(ux) = isDark; 537 | isFunction.at(uy).at(ux) = true; 538 | } 539 | 540 | bool QrCode::module(int x, int y) const 541 | { 542 | return modules.at(static_cast(y)).at(static_cast(x)); 543 | } 544 | 545 | vector QrCode::addEccAndInterleave(const vector &data) const 546 | { 547 | if (data.size() 548 | != static_cast( 549 | getNumDataCodewords(version, errorCorrectionLevel))) 550 | throw std::invalid_argument("Invalid argument"); 551 | 552 | // Calculate parameter numbers 553 | int numBlocks 554 | = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)] 555 | [version]; 556 | int blockEccLen 557 | = ECC_CODEWORDS_PER_BLOCK[static_cast(errorCorrectionLevel)] 558 | [version]; 559 | int rawCodewords = getNumRawDataModules(version) / 8; 560 | int numShortBlocks = numBlocks - rawCodewords % numBlocks; 561 | int shortBlockLen = rawCodewords / numBlocks; 562 | 563 | // Split data into blocks and append ECC to each block 564 | vector> blocks; 565 | const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); 566 | for (int i = 0, k = 0; i < numBlocks; i++) 567 | { 568 | vector dat( 569 | data.cbegin() + k, 570 | data.cbegin() 571 | + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); 572 | k += static_cast(dat.size()); 573 | const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); 574 | if (i < numShortBlocks) 575 | dat.push_back(0); 576 | dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); 577 | blocks.push_back(std::move(dat)); 578 | } 579 | 580 | // Interleave (not concatenate) the bytes from every block into a single 581 | // sequence 582 | vector result; 583 | for (size_t i = 0; i < blocks.at(0).size(); i++) 584 | { 585 | for (size_t j = 0; j < blocks.size(); j++) 586 | { 587 | // Skip the padding byte in short blocks 588 | if (i != static_cast(shortBlockLen - blockEccLen) 589 | || j >= static_cast(numShortBlocks)) 590 | result.push_back(blocks.at(j).at(i)); 591 | } 592 | } 593 | assert(result.size() == static_cast(rawCodewords)); 594 | return result; 595 | } 596 | 597 | void QrCode::drawCodewords(const vector &data) 598 | { 599 | if (data.size() 600 | != static_cast(getNumRawDataModules(version) / 8)) 601 | throw std::invalid_argument("Invalid argument"); 602 | 603 | size_t i = 0; // Bit index into the data 604 | // Do the funny zigzag scan 605 | for (int right = size - 1; right >= 1; right -= 2) 606 | { // Index of right column in each column pair 607 | if (right == 6) 608 | right = 5; 609 | for (int vert = 0; vert < size; vert++) 610 | { // Vertical counter 611 | for (int j = 0; j < 2; j++) 612 | { 613 | size_t x = static_cast(right - j); // Actual x coordinate 614 | bool upward = ((right + 1) & 2) == 0; 615 | size_t y = static_cast(upward ? size - 1 - vert 616 | : vert); // Actual y coordinate 617 | if (!isFunction.at(y).at(x) && i < data.size() * 8) 618 | { 619 | modules.at(y).at(x) 620 | = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); 621 | i++; 622 | } 623 | // If this QR Code has any remainder bits (0 to 7), they were assigned 624 | // as 0/false/light by the constructor and are left unchanged by this 625 | // method 626 | } 627 | } 628 | } 629 | assert(i == data.size() * 8); 630 | } 631 | 632 | void QrCode::applyMask(int msk) 633 | { 634 | if (msk < 0 || msk > 7) 635 | throw std::domain_error("Mask value out of range"); 636 | size_t sz = static_cast(size); 637 | for (size_t y = 0; y < sz; y++) 638 | { 639 | for (size_t x = 0; x < sz; x++) 640 | { 641 | bool invert; 642 | switch (msk) 643 | { 644 | case 0: 645 | invert = (x + y) % 2 == 0; 646 | break; 647 | case 1: 648 | invert = y % 2 == 0; 649 | break; 650 | case 2: 651 | invert = x % 3 == 0; 652 | break; 653 | case 3: 654 | invert = (x + y) % 3 == 0; 655 | break; 656 | case 4: 657 | invert = (x / 3 + y / 2) % 2 == 0; 658 | break; 659 | case 5: 660 | invert = x * y % 2 + x * y % 3 == 0; 661 | break; 662 | case 6: 663 | invert = (x * y % 2 + x * y % 3) % 2 == 0; 664 | break; 665 | case 7: 666 | invert = ((x + y) % 2 + x * y % 3) % 2 == 0; 667 | break; 668 | default: 669 | throw std::logic_error("Unreachable"); 670 | } 671 | modules.at(y).at(x) 672 | = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); 673 | } 674 | } 675 | } 676 | 677 | long QrCode::getPenaltyScore() const 678 | { 679 | long result = 0; 680 | 681 | // Adjacent modules in row having same color, and finder-like patterns 682 | for (int y = 0; y < size; y++) 683 | { 684 | bool runColor = false; 685 | int runX = 0; 686 | std::array runHistory = {}; 687 | for (int x = 0; x < size; x++) 688 | { 689 | if (module(x, y) == runColor) 690 | { 691 | runX++; 692 | if (runX == 5) 693 | result += PENALTY_N1; 694 | else if (runX > 5) 695 | result++; 696 | } 697 | else 698 | { 699 | finderPenaltyAddHistory(runX, runHistory); 700 | if (!runColor) 701 | result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; 702 | runColor = module(x, y); 703 | runX = 1; 704 | } 705 | } 706 | result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) 707 | * PENALTY_N3; 708 | } 709 | // Adjacent modules in column having same color, and finder-like patterns 710 | for (int x = 0; x < size; x++) 711 | { 712 | bool runColor = false; 713 | int runY = 0; 714 | std::array runHistory = {}; 715 | for (int y = 0; y < size; y++) 716 | { 717 | if (module(x, y) == runColor) 718 | { 719 | runY++; 720 | if (runY == 5) 721 | result += PENALTY_N1; 722 | else if (runY > 5) 723 | result++; 724 | } 725 | else 726 | { 727 | finderPenaltyAddHistory(runY, runHistory); 728 | if (!runColor) 729 | result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; 730 | runColor = module(x, y); 731 | runY = 1; 732 | } 733 | } 734 | result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) 735 | * PENALTY_N3; 736 | } 737 | 738 | // 2*2 blocks of modules having same color 739 | for (int y = 0; y < size - 1; y++) 740 | { 741 | for (int x = 0; x < size - 1; x++) 742 | { 743 | bool color = module(x, y); 744 | if (color == module(x + 1, y) && color == module(x, y + 1) 745 | && color == module(x + 1, y + 1)) 746 | result += PENALTY_N2; 747 | } 748 | } 749 | 750 | // Balance of dark and light modules 751 | int dark = 0; 752 | for (const vector &row : modules) 753 | { 754 | for (bool color : row) 755 | { 756 | if (color) 757 | dark++; 758 | } 759 | } 760 | int total = size * size; // Note that size is odd, so dark/total != 1/2 761 | // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= 762 | // (55+5k)% 763 | int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) 764 | / total) 765 | - 1; 766 | assert(0 <= k && k <= 9); 767 | result += k * PENALTY_N4; 768 | assert(0 <= result 769 | && result <= 2568888L); // Non-tight upper bound based on default 770 | // values of PENALTY_N1, ..., N4 771 | return result; 772 | } 773 | 774 | vector QrCode::getAlignmentPatternPositions() const 775 | { 776 | if (version == 1) 777 | return vector(); 778 | else 779 | { 780 | int numAlign = version / 7 + 2; 781 | int step = (version == 32) 782 | ? 26 783 | : (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; 784 | vector result; 785 | for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) 786 | result.insert(result.begin(), pos); 787 | result.insert(result.begin(), 6); 788 | return result; 789 | } 790 | } 791 | 792 | int QrCode::getNumRawDataModules(int ver) 793 | { 794 | if (ver < MIN_VERSION || ver > MAX_VERSION) 795 | throw std::domain_error("Version number out of range"); 796 | int result = (16 * ver + 128) * ver + 64; 797 | if (ver >= 2) 798 | { 799 | int numAlign = ver / 7 + 2; 800 | result -= (25 * numAlign - 10) * numAlign - 55; 801 | if (ver >= 7) 802 | result -= 36; 803 | } 804 | assert(208 <= result && result <= 29648); 805 | return result; 806 | } 807 | 808 | int QrCode::getNumDataCodewords(int ver, Ecc ecl) 809 | { 810 | return getNumRawDataModules(ver) / 8 811 | - ECC_CODEWORDS_PER_BLOCK[static_cast(ecl)][ver] 812 | * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; 813 | } 814 | 815 | vector QrCode::reedSolomonComputeDivisor(int degree) 816 | { 817 | if (degree < 1 || degree > 255) 818 | throw std::domain_error("Degree out of range"); 819 | // Polynomial coefficients are stored from highest to lowest power, excluding 820 | // the leading term which is always 1. For example the polynomial x^3 + 255x^2 821 | // + 8x + 93 is stored as the uint8 array {255, 8, 93}. 822 | vector result(static_cast(degree)); 823 | result.at(result.size() - 1) = 1; // Start off with the monomial x^0 824 | 825 | // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x 826 | // - r^{degree-1}), and drop the highest monomial term which is always 827 | // 1x^degree. Note that r = 0x02, which is a generator element of this field 828 | // GF(2^8/0x11D). 829 | uint8_t root = 1; 830 | for (int i = 0; i < degree; i++) 831 | { 832 | // Multiply the current product by (x - r^i) 833 | for (size_t j = 0; j < result.size(); j++) 834 | { 835 | result.at(j) = reedSolomonMultiply(result.at(j), root); 836 | if (j + 1 < result.size()) 837 | result.at(j) ^= result.at(j + 1); 838 | } 839 | root = reedSolomonMultiply(root, 0x02); 840 | } 841 | return result; 842 | } 843 | 844 | vector 845 | QrCode::reedSolomonComputeRemainder(const vector &data, 846 | const vector &divisor) 847 | { 848 | vector result(divisor.size()); 849 | for (uint8_t b : data) 850 | { // Polynomial division 851 | uint8_t factor = b ^ result.at(0); 852 | result.erase(result.begin()); 853 | result.push_back(0); 854 | for (size_t i = 0; i < result.size(); i++) 855 | result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); 856 | } 857 | return result; 858 | } 859 | 860 | uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) 861 | { 862 | // Russian peasant multiplication 863 | int z = 0; 864 | for (int i = 7; i >= 0; i--) 865 | { 866 | z = (z << 1) ^ ((z >> 7) * 0x11D); 867 | z ^= ((y >> i) & 1) * x; 868 | } 869 | assert(z >> 8 == 0); 870 | return static_cast(z); 871 | } 872 | 873 | int QrCode::finderPenaltyCountPatterns( 874 | const std::array &runHistory) const 875 | { 876 | int n = runHistory.at(1); 877 | assert(n <= size * 3); 878 | bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 879 | && runHistory.at(4) == n && runHistory.at(5) == n; 880 | return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) 881 | + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); 882 | } 883 | 884 | int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, 885 | int currentRunLength, 886 | std::array &runHistory) const 887 | { 888 | if (currentRunColor) 889 | { // Terminate dark run 890 | finderPenaltyAddHistory(currentRunLength, runHistory); 891 | currentRunLength = 0; 892 | } 893 | currentRunLength += size; // Add light border to final run 894 | finderPenaltyAddHistory(currentRunLength, runHistory); 895 | return finderPenaltyCountPatterns(runHistory); 896 | } 897 | 898 | void QrCode::finderPenaltyAddHistory(int currentRunLength, 899 | std::array &runHistory) const 900 | { 901 | if (runHistory.at(0) == 0) 902 | currentRunLength += size; // Add light border to initial run 903 | std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, 904 | runHistory.end()); 905 | runHistory.at(0) = currentRunLength; 906 | } 907 | 908 | bool QrCode::getBit(long x, int i) 909 | { 910 | return ((x >> i) & 1) != 0; 911 | } 912 | 913 | /*---- Tables of constants ----*/ 914 | 915 | const int QrCode::PENALTY_N1 = 3; 916 | const int QrCode::PENALTY_N2 = 3; 917 | const int QrCode::PENALTY_N3 = 40; 918 | const int QrCode::PENALTY_N4 = 10; 919 | 920 | const int8_t QrCode::ECC_CODEWORDS_PER_BLOCK[4][41] = { 921 | // Version: (note that index 0 is for padding, and is set to an illegal 922 | // value) 923 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 924 | // 18, 19, 20, 21, 22, 23, 925 | // 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error 926 | // correction level 927 | {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 928 | 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 929 | 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low 930 | {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 931 | 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 932 | 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium 933 | {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 934 | 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 935 | 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile 936 | {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 937 | 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 938 | 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High 939 | }; 940 | 941 | const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { 942 | // Version: (note that index 0 is for padding, and is set to an illegal 943 | // value) 944 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 945 | // 21, 22, 23, 24, 25, 946 | // 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error 947 | // correction level 948 | {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 949 | 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 950 | 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low 951 | {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 952 | 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 953 | 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium 954 | {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 955 | 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 956 | 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile 957 | {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 958 | 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 959 | 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High 960 | }; 961 | 962 | data_too_long::data_too_long(const std::string &msg) 963 | : std::length_error(msg) 964 | { 965 | } 966 | 967 | /*---- Class BitBuffer ----*/ 968 | 969 | BitBuffer::BitBuffer() 970 | : std::vector() 971 | { 972 | } 973 | 974 | void BitBuffer::appendBits(std::uint32_t val, int len) 975 | { 976 | if (len < 0 || len > 31 || val >> len != 0) 977 | throw std::domain_error("Value out of range"); 978 | for (int i = len - 1; i >= 0; i--) // Append bit by bit 979 | this->push_back(((val >> i) & 1) != 0); 980 | } 981 | 982 | } // namespace qrcodegen 983 | -------------------------------------------------------------------------------- /src/qrcodegen/qrcodegen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QR Code generator library (C++) 3 | * 4 | * Copyright (c) Project Nayuki. (MIT License) 5 | * https://www.nayuki.io/page/qr-code-generator-library 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * - The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * - The Software is provided "as is", without warranty of any kind, express or 16 | * implied, including but not limited to the warranties of merchantability, 17 | * fitness for a particular purpose and noninfringement. In no event shall the 18 | * authors or copyright holders be liable for any claim, damages or other 19 | * liability, whether in an action of contract, tort or otherwise, arising 20 | * from, out of or in connection with the Software or the use or other dealings 21 | * in the Software. 22 | */ 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace qrcodegen 33 | { 34 | 35 | /* 36 | * A segment of character/binary/control data in a QR Code symbol. 37 | * Instances of this class are immutable. 38 | * The mid-level way to create a segment is to take the payload data 39 | * and call a static factory function such as QrSegment::makeNumeric(). 40 | * The low-level way to create a segment is to custom-make the bit buffer 41 | * and call the QrSegment() constructor with appropriate values. 42 | * This segment class imposes no length restrictions, but QR Codes have 43 | * restrictions. Even in the most favorable conditions, a QR Code can only hold 44 | * 7089 characters of data. Any segment longer than this is meaningless for the 45 | * purpose of generating QR Codes. 46 | */ 47 | class QrSegment final 48 | { 49 | 50 | /*---- Public helper enumeration ----*/ 51 | 52 | /* 53 | * Describes how a segment's data bits are interpreted. Immutable. 54 | */ 55 | public: 56 | class Mode final 57 | { 58 | 59 | /*-- Constants --*/ 60 | 61 | public: 62 | static const Mode NUMERIC; 63 | 64 | public: 65 | static const Mode ALPHANUMERIC; 66 | 67 | public: 68 | static const Mode BYTE; 69 | 70 | public: 71 | static const Mode KANJI; 72 | 73 | public: 74 | static const Mode ECI; 75 | 76 | /*-- Fields --*/ 77 | 78 | // The mode indicator bits, which is a uint4 value (range 0 to 15). 79 | private: 80 | int modeBits; 81 | 82 | // Number of character count bits for three different version ranges. 83 | private: 84 | int numBitsCharCount[3]; 85 | 86 | /*-- Constructor --*/ 87 | 88 | private: 89 | Mode(int mode, int cc0, int cc1, int cc2); 90 | 91 | /*-- Methods --*/ 92 | 93 | /* 94 | * (Package-private) Returns the mode indicator bits, which is an unsigned 95 | * 4-bit value (range 0 to 15). 96 | */ 97 | public: 98 | int getModeBits() const; 99 | 100 | /* 101 | * (Package-private) Returns the bit width of the character count field for 102 | * a segment in this mode in a QR Code at the given version number. The 103 | * result is in the range [0, 16]. 104 | */ 105 | public: 106 | int numCharCountBits(int ver) const; 107 | }; 108 | 109 | /*---- Static factory functions (mid level) ----*/ 110 | 111 | /* 112 | * Returns a segment representing the given binary data encoded in 113 | * byte mode. All input byte vectors are acceptable. Any text string 114 | * can be converted to UTF-8 bytes and encoded as a byte mode segment. 115 | */ 116 | public: 117 | static QrSegment makeBytes(const std::vector &data); 118 | 119 | /* 120 | * Returns a segment representing the given string of decimal digits encoded 121 | * in numeric mode. 122 | */ 123 | public: 124 | static QrSegment makeNumeric(const char *digits); 125 | 126 | /* 127 | * Returns a segment representing the given text string encoded in 128 | * alphanumeric mode. The characters allowed are: 0 to 9, A to Z (uppercase 129 | * only), space, dollar, percent, asterisk, plus, hyphen, period, slash, 130 | * colon. 131 | */ 132 | public: 133 | static QrSegment makeAlphanumeric(const char *text); 134 | 135 | /* 136 | * Returns a list of zero or more segments to represent the given text string. 137 | * The result may use various segment modes and switch modes to optimize the 138 | * length of the bit stream. 139 | */ 140 | public: 141 | static std::vector makeSegments(const char *text); 142 | 143 | /* 144 | * Returns a segment representing an Extended Channel Interpretation 145 | * (ECI) designator with the given assignment value. 146 | */ 147 | public: 148 | static QrSegment makeEci(long assignVal); 149 | 150 | /*---- Public static helper functions ----*/ 151 | 152 | /* 153 | * Tests whether the given string can be encoded as a segment in numeric mode. 154 | * A string is encodable iff each character is in the range 0 to 9. 155 | */ 156 | public: 157 | static bool isNumeric(const char *text); 158 | 159 | /* 160 | * Tests whether the given string can be encoded as a segment in alphanumeric 161 | * mode. A string is encodable iff each character is in the following set: 0 162 | * to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, 163 | * hyphen, period, slash, colon. 164 | */ 165 | public: 166 | static bool isAlphanumeric(const char *text); 167 | 168 | /*---- Instance fields ----*/ 169 | 170 | /* The mode indicator of this segment. Accessed through getMode(). */ 171 | private: 172 | const Mode *mode; 173 | 174 | /* The length of this segment's unencoded data. Measured in characters for 175 | * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. 176 | * Always zero or positive. Not the same as the data's bit length. 177 | * Accessed through getNumChars(). */ 178 | private: 179 | int numChars; 180 | 181 | /* The data bits of this segment. Accessed through getData(). */ 182 | private: 183 | std::vector data; 184 | 185 | /*---- Constructors (low level) ----*/ 186 | 187 | /* 188 | * Creates a new QR Code segment with the given attributes and data. 189 | * The character count (numCh) must agree with the mode and the bit buffer 190 | * length, but the constraint isn't checked. The given bit buffer is copied 191 | * and stored. 192 | */ 193 | public: 194 | QrSegment(const Mode &md, int numCh, const std::vector &dt); 195 | 196 | /* 197 | * Creates a new QR Code segment with the given parameters and data. 198 | * The character count (numCh) must agree with the mode and the bit buffer 199 | * length, but the constraint isn't checked. The given bit buffer is moved and 200 | * stored. 201 | */ 202 | public: 203 | QrSegment(const Mode &md, int numCh, std::vector &&dt); 204 | 205 | /*---- Methods ----*/ 206 | 207 | /* 208 | * Returns the mode field of this segment. 209 | */ 210 | public: 211 | const Mode &getMode() const; 212 | 213 | /* 214 | * Returns the character count field of this segment. 215 | */ 216 | public: 217 | int getNumChars() const; 218 | 219 | /* 220 | * Returns the data bits of this segment. 221 | */ 222 | public: 223 | const std::vector &getData() const; 224 | 225 | // (Package-private) Calculates the number of bits needed to encode the given 226 | // segments at the given version. Returns a non-negative number if successful. 227 | // Otherwise returns -1 if a segment has too many characters to fit its length 228 | // field, or the total bits exceeds INT_MAX. 229 | public: 230 | static int getTotalBits(const std::vector &segs, int version); 231 | 232 | /*---- Private constant ----*/ 233 | 234 | /* The set of all legal characters in alphanumeric mode, where 235 | * each character value maps to the index in the string. */ 236 | private: 237 | static const char *ALPHANUMERIC_CHARSET; 238 | }; 239 | 240 | /* 241 | * A QR Code symbol, which is a type of two-dimension barcode. 242 | * Invented by Denso Wave and described in the ISO/IEC 18004 standard. 243 | * Instances of this class represent an immutable square grid of dark and light 244 | * cells. The class provides static factory functions to create a QR Code from 245 | * text or binary data. The class covers the QR Code Model 2 specification, 246 | * supporting all versions (sizes) from 1 to 40, all 4 error correction levels, 247 | * and 4 character encoding modes. 248 | * 249 | * Ways to create a QR Code object: 250 | * - High level: Take the payload data and call QrCode::encodeText() or 251 | * QrCode::encodeBinary(). 252 | * - Mid level: Custom-make the list of segments and call 253 | * QrCode::encodeSegments(). 254 | * - Low level: Custom-make the array of data codeword bytes (including 255 | * segment headers and final padding, excluding error correction codewords), 256 | * supply the appropriate version number, and call the QrCode() constructor. 257 | * (Note that all ways require supplying the desired error correction level.) 258 | */ 259 | class QrCode final 260 | { 261 | 262 | /*---- Public helper enumeration ----*/ 263 | 264 | /* 265 | * The error correction level in a QR Code symbol. 266 | */ 267 | public: 268 | enum class Ecc 269 | { 270 | LOW = 0, // The QR Code can tolerate about 7% erroneous codewords 271 | MEDIUM, // The QR Code can tolerate about 15% erroneous codewords 272 | QUARTILE, // The QR Code can tolerate about 25% erroneous codewords 273 | HIGH, // The QR Code can tolerate about 30% erroneous codewords 274 | }; 275 | 276 | // Returns a value in the range 0 to 3 (unsigned 2-bit integer). 277 | private: 278 | static int getFormatBits(Ecc ecl); 279 | 280 | /*---- Static factory functions (high level) ----*/ 281 | 282 | /* 283 | * Returns a QR Code representing the given Unicode text string at the given 284 | * error correction level. As a conservative upper bound, this function is 285 | * guaranteed to succeed for strings that have 2953 or fewer UTF-8 code units 286 | * (not Unicode code points) if the low error correction level is used. The 287 | * smallest possible QR Code version is automatically chosen for the output. 288 | * The ECC level of the result may be higher than the ecl argument if it can 289 | * be done without increasing the version. 290 | */ 291 | public: 292 | static QrCode encodeText(const char *text, Ecc ecl); 293 | 294 | /* 295 | * Returns a QR Code representing the given binary data at the given error 296 | * correction level. This function always encodes using the binary segment 297 | * mode, not any text mode. The maximum number of bytes allowed is 2953. The 298 | * smallest possible QR Code version is automatically chosen for the output. 299 | * The ECC level of the result may be higher than the ecl argument if it can 300 | * be done without increasing the version. 301 | */ 302 | public: 303 | static QrCode encodeBinary(const std::vector &data, Ecc ecl); 304 | 305 | /*---- Static factory functions (mid level) ----*/ 306 | 307 | /* 308 | * Returns a QR Code representing the given segments with the given encoding 309 | * parameters. The smallest possible QR Code version within the given range is 310 | * automatically chosen for the output. Iff boostEcl is true, then the ECC 311 | * level of the result may be higher than the ecl argument if it can be done 312 | * without increasing the version. The mask number is either between 0 to 7 313 | * (inclusive) to force that mask, or -1 to automatically choose an 314 | * appropriate mask (which may be slow). This function allows the user to 315 | * create a custom sequence of segments that switches between modes (such as 316 | * alphanumeric and byte) to encode text in less space. This is a mid-level 317 | * API; the high-level API is encodeText() and encodeBinary(). 318 | */ 319 | public: 320 | static QrCode encodeSegments(const std::vector &segs, Ecc ecl, 321 | int minVersion = 1, int maxVersion = 40, 322 | int mask = -1, 323 | bool boostEcl = true); // All optional parameters 324 | 325 | /*---- Instance fields ----*/ 326 | 327 | // Immutable scalar parameters: 328 | 329 | /* The version number of this QR Code, which is between 1 and 40 (inclusive). 330 | * This determines the size of this barcode. */ 331 | private: 332 | int version; 333 | 334 | /* The width and height of this QR Code, measured in modules, between 335 | * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ 336 | private: 337 | int size; 338 | 339 | /* The error correction level used in this QR Code. */ 340 | private: 341 | Ecc errorCorrectionLevel; 342 | 343 | /* The index of the mask pattern used in this QR Code, which is between 0 and 344 | * 7 (inclusive). Even if a QR Code is created with automatic masking 345 | * requested (mask = -1), the resulting object still has a mask value between 346 | * 0 and 7. */ 347 | private: 348 | int mask; 349 | 350 | // Private grids of modules/pixels, with dimensions of size*size: 351 | 352 | // The modules of this QR Code (false = light, true = dark). 353 | // Immutable after constructor finishes. Accessed through getModule(). 354 | private: 355 | std::vector> modules; 356 | 357 | // Indicates function modules that are not subjected to masking. Discarded 358 | // when constructor finishes. 359 | private: 360 | std::vector> isFunction; 361 | 362 | /*---- Constructor (low level) ----*/ 363 | 364 | /* 365 | * Creates a new QR Code with the given version number, 366 | * error correction level, data codeword bytes, and mask number. 367 | * This is a low-level API that most users should not use directly. 368 | * A mid-level API is the encodeSegments() function. 369 | */ 370 | public: 371 | QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, 372 | int msk); 373 | 374 | /*---- Public instance methods ----*/ 375 | 376 | /* 377 | * Returns this QR Code's version, in the range [1, 40]. 378 | */ 379 | public: 380 | int getVersion() const; 381 | 382 | /* 383 | * Returns this QR Code's size, in the range [21, 177]. 384 | */ 385 | public: 386 | int getSize() const; 387 | 388 | /* 389 | * Returns this QR Code's error correction level. 390 | */ 391 | public: 392 | Ecc getErrorCorrectionLevel() const; 393 | 394 | /* 395 | * Returns this QR Code's mask, in the range [0, 7]. 396 | */ 397 | public: 398 | int getMask() const; 399 | 400 | /* 401 | * Returns the color of the module (pixel) at the given coordinates, which is 402 | * false for light or true for dark. The top left corner has the coordinates 403 | * (x=0, y=0). If the given coordinates are out of bounds, then false (light) 404 | * is returned. 405 | */ 406 | public: 407 | bool getModule(int x, int y) const; 408 | 409 | /*---- Private helper methods for constructor: Drawing function modules ----*/ 410 | 411 | // Reads this object's version field, and draws and marks all function 412 | // modules. 413 | private: 414 | void drawFunctionPatterns(); 415 | 416 | // Draws two copies of the format bits (with its own error correction code) 417 | // based on the given mask and this object's error correction level field. 418 | private: 419 | void drawFormatBits(int msk); 420 | 421 | // Draws two copies of the version bits (with its own error correction code), 422 | // based on this object's version field, iff 7 <= version <= 40. 423 | private: 424 | void drawVersion(); 425 | 426 | // Draws a 9*9 finder pattern including the border separator, 427 | // with the center module at (x, y). Modules can be out of bounds. 428 | private: 429 | void drawFinderPattern(int x, int y); 430 | 431 | // Draws a 5*5 alignment pattern, with the center module 432 | // at (x, y). All modules must be in bounds. 433 | private: 434 | void drawAlignmentPattern(int x, int y); 435 | 436 | // Sets the color of a module and marks it as a function module. 437 | // Only used by the constructor. Coordinates must be in bounds. 438 | private: 439 | void setFunctionModule(int x, int y, bool isDark); 440 | 441 | // Returns the color of the module at the given coordinates, which must be in 442 | // range. 443 | private: 444 | bool module(int x, int y) const; 445 | 446 | /*---- Private helper methods for constructor: Codewords and masking ----*/ 447 | 448 | // Returns a new byte string representing the given data with the appropriate 449 | // error correction codewords appended to it, based on this object's version 450 | // and error correction level. 451 | private: 452 | std::vector 453 | addEccAndInterleave(const std::vector &data) const; 454 | 455 | // Draws the given sequence of 8-bit codewords (data and error correction) 456 | // onto the entire data area of this QR Code. Function modules need to be 457 | // marked off before this is called. 458 | private: 459 | void drawCodewords(const std::vector &data); 460 | 461 | // XORs the codeword modules in this QR Code with the given mask pattern. 462 | // The function modules must be marked and the codeword bits must be drawn 463 | // before masking. Due to the arithmetic of XOR, calling applyMask() with 464 | // the same mask value a second time will undo the mask. A final well-formed 465 | // QR Code needs exactly one (not zero, two, etc.) mask applied. 466 | private: 467 | void applyMask(int msk); 468 | 469 | // Calculates and returns the penalty score based on state of this QR Code's 470 | // current modules. This is used by the automatic mask choice algorithm to 471 | // find the mask pattern that yields the lowest score. 472 | private: 473 | long getPenaltyScore() const; 474 | 475 | /*---- Private helper functions ----*/ 476 | 477 | // Returns an ascending list of positions of alignment patterns for this 478 | // version number. Each position is in the range [0,177), and are used on both 479 | // the x and y axes. This could be implemented as lookup table of 40 480 | // variable-length lists of unsigned bytes. 481 | private: 482 | std::vector getAlignmentPatternPositions() const; 483 | 484 | // Returns the number of data bits that can be stored in a QR Code of the 485 | // given version number, after all function modules are excluded. This 486 | // includes remainder bits, so it might not be a multiple of 8. The result is 487 | // in the range [208, 29648]. This could be implemented as a 40-entry lookup 488 | // table. 489 | private: 490 | static int getNumRawDataModules(int ver); 491 | 492 | // Returns the number of 8-bit data (i.e. not error correction) codewords 493 | // contained in any QR Code of the given version number and error correction 494 | // level, with remainder bits discarded. This stateless pure function could be 495 | // implemented as a (40*4)-cell lookup table. 496 | private: 497 | static int getNumDataCodewords(int ver, Ecc ecl); 498 | 499 | // Returns a Reed-Solomon ECC generator polynomial for the given degree. This 500 | // could be implemented as a lookup table over all possible parameter values, 501 | // instead of as an algorithm. 502 | private: 503 | static std::vector reedSolomonComputeDivisor(int degree); 504 | 505 | // Returns the Reed-Solomon error correction codeword for the given data and 506 | // divisor polynomials. 507 | private: 508 | static std::vector 509 | reedSolomonComputeRemainder(const std::vector &data, 510 | const std::vector &divisor); 511 | 512 | // Returns the product of the two given field elements modulo GF(2^8/0x11D). 513 | // All inputs are valid. This could be implemented as a 256*256 lookup table. 514 | private: 515 | static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); 516 | 517 | // Can only be called immediately after a light run is added, and 518 | // returns either 0, 1, or 2. A helper function for getPenaltyScore(). 519 | private: 520 | int finderPenaltyCountPatterns(const std::array &runHistory) const; 521 | 522 | // Must be called at the end of a line (row or column) of modules. A helper 523 | // function for getPenaltyScore(). 524 | private: 525 | int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, 526 | std::array &runHistory) const; 527 | 528 | // Pushes the given value to the front and drops the last value. A helper 529 | // function for getPenaltyScore(). 530 | private: 531 | void finderPenaltyAddHistory(int currentRunLength, 532 | std::array &runHistory) const; 533 | 534 | // Returns true iff the i'th bit of x is set to 1. 535 | private: 536 | static bool getBit(long x, int i); 537 | 538 | /*---- Constants and tables ----*/ 539 | 540 | // The minimum version number supported in the QR Code Model 2 standard. 541 | public: 542 | static constexpr int MIN_VERSION = 1; 543 | 544 | // The maximum version number supported in the QR Code Model 2 standard. 545 | public: 546 | static constexpr int MAX_VERSION = 40; 547 | 548 | // For use in getPenaltyScore(), when evaluating which mask is best. 549 | private: 550 | static const int PENALTY_N1; 551 | 552 | private: 553 | static const int PENALTY_N2; 554 | 555 | private: 556 | static const int PENALTY_N3; 557 | 558 | private: 559 | static const int PENALTY_N4; 560 | 561 | private: 562 | static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; 563 | 564 | private: 565 | static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; 566 | }; 567 | 568 | /*---- Public exception class ----*/ 569 | 570 | /* 571 | * Thrown when the supplied data does not fit any QR Code version. Ways to 572 | * handle this exception include: 573 | * - Decrease the error correction level if it was greater than Ecc::LOW. 574 | * - If the encodeSegments() function was called with a maxVersion argument, 575 | * then increase it if it was less than QrCode::MAX_VERSION. (This advice does 576 | * not apply to the other factory functions because they search all versions up 577 | * to QrCode::MAX_VERSION.) 578 | * - Split the text data into better or optimal segments in order to reduce the 579 | * number of bits required. 580 | * - Change the text or binary data to be shorter. 581 | * - Change the text to fit the character set of a particular segment mode (e.g. 582 | * alphanumeric). 583 | * - Propagate the error upward to the caller/user. 584 | */ 585 | class data_too_long : public std::length_error 586 | { 587 | 588 | public: 589 | explicit data_too_long(const std::string &msg); 590 | }; 591 | 592 | /* 593 | * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. 594 | */ 595 | class BitBuffer final : public std::vector 596 | { 597 | 598 | /*---- Constructor ----*/ 599 | 600 | // Creates an empty bit buffer (length 0). 601 | public: 602 | BitBuffer(); 603 | 604 | /*---- Method ----*/ 605 | 606 | // Appends the given number of low-order bits of the given value 607 | // to this buffer. Requires 0 <= len <= 31 and val < 2^len. 608 | public: 609 | void appendBits(std::uint32_t val, int len); 610 | }; 611 | 612 | } // namespace qrcodegen 613 | --------------------------------------------------------------------------------