diff --git a/.gitignore b/.gitignore index 49afe0c..f87d8c4 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ Thumbs.db *.pro.user* *.txt.user* build-* +build # xemacs temporary files *.flc diff --git a/examples/QmlBarcodeGenerator/qml/GeneratorPage.qml b/examples/QmlBarcodeGenerator/qml/GeneratorPage.qml index 851ab54..f93449b 100644 --- a/examples/QmlBarcodeGenerator/qml/GeneratorPage.qml +++ b/examples/QmlBarcodeGenerator/qml/GeneratorPage.qml @@ -58,15 +58,6 @@ ApplicationWindow { right: parent.right } - // TextField { - // id: textField - - // anchors.fill: parent - - // selectByMouse: true - - // placeholderText: qsTr("Input") - // } CTextField { id: textField @@ -226,7 +217,7 @@ ApplicationWindow { id: widthField implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 placeholderText: "Current width: " + barcodeGenerator.width @@ -244,7 +235,7 @@ ApplicationWindow { id: heightField implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 placeholderText: "Current height: " + barcodeGenerator.height @@ -262,7 +253,7 @@ ApplicationWindow { id: marginField implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 placeholderText: "Current margin: " + barcodeGenerator.margin @@ -280,7 +271,7 @@ ApplicationWindow { id: eccLevelField implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 placeholderText: "Current ECC Level: " + barcodeGenerator.eccLevel @@ -293,7 +284,7 @@ ApplicationWindow { id: formatDropDown implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 model: ListModel { id: formats @@ -350,7 +341,7 @@ ApplicationWindow { id: imageFormat implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 model: ListModel { id: extensions @@ -374,12 +365,42 @@ ApplicationWindow { text: qsTr(barcodeGenerator.fileName) implicitWidth: parent.width - implicitHeight: parent.height / 8 + implicitHeight: parent.height / 10 onEditingFinished: { barcodeGenerator.fileName = text } } + + CTextField { + id: imagePathField + + text: qsTr(barcodeGenerator.imagePath) + + implicitWidth: parent.width + implicitHeight: parent.height / 10 + + placeholderText: "Current image path: " + barcodeGenerator.imagePath + + onEditingFinished: { + barcodeGenerator.imagePath = text + } + } + + CTextField { + id: centerImageRatioField + + text: qsTr(barcodeGenerator.centerImageRatio.toString()) + + implicitWidth: parent.width + implicitHeight: parent.height / 10 + + placeholderText: "Current center image ratio: " + barcodeGenerator.centerImageRatio + + onEditingFinished: { + barcodeGenerator.centerImageRatio = parseInt(text) + } + } } onClosed: { diff --git a/src/SBarcodeGenerator.cpp b/src/SBarcodeGenerator.cpp index 198894f..a18502d 100644 --- a/src/SBarcodeGenerator.cpp +++ b/src/SBarcodeGenerator.cpp @@ -1,5 +1,6 @@ #include "SBarcodeGenerator.h" #include +#include #ifdef Q_OS_ANDROID #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) @@ -21,19 +22,46 @@ bool SBarcodeGenerator::generate(const QString &inputString) if (inputString.isEmpty()) { return false; } else { + // Change ecc level to max to generate image on QR code. + if (m_format == SCodes::SBarcodeFormat::QRCode && !m_imagePath.isEmpty()) { + if (m_eccLevel < 8) { + qDebug() << "To draw image on QR Code use maximum level of ecc. Setting it to 8."; + + setEccLvel(8); + } + } + ZXing::MultiFormatWriter writer = ZXing::MultiFormatWriter(SCodes::toZXingFormat(m_format)).setMargin( m_margin).setEccLevel(m_eccLevel); - _bitmap = ZXing::ToMatrix(writer.encode(inputString.toStdString(), m_width, m_height)); + auto qrCodeMatrix = writer.encode(inputString.toStdString(), m_width, m_height); + _bitmap = ZXing::ToMatrix(qrCodeMatrix); + + QImage image(_bitmap.data(), m_width, m_height, QImage::Format_Grayscale8); + + // Center images works only on QR codes. + if (m_format == SCodes::SBarcodeFormat::QRCode) { + if (!m_imagePath.isEmpty()) { + QSize centerImageSize(m_width / m_centerImageRatio, m_height / m_centerImageRatio); + drawCenterImage(&image, m_imagePath, centerImageSize, + (image.width() - centerImageSize.width()) / 2, + (image.height() - centerImageSize.height()) / 2); + } else { + qDebug() << "Center Image path is empty. Skip drawing center image."; + } + } else { + qDebug() << "Center images works only on QR codes."; + } m_filePath = QDir::tempPath() + "/" + m_fileName + "." + m_extension; - auto image = QImage(_bitmap.data(), m_width, m_height, QImage::Format::Format_Grayscale8); - - QFile file{m_filePath}; - - if(file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + // Save the final image with the QR code and center image + QFile file(m_filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { image.save(&file); + } else { + qWarning() << "Could not open file for writing!"; + return false; } emit generationFinished(); @@ -82,6 +110,47 @@ bool SBarcodeGenerator::saveImage() return true; } +void SBarcodeGenerator::drawCenterImage(QImage *parentImage, const QString &imagePath, QSize imageSize, int x, int y) +{ + QImage centerImage(imageSize, QImage::Format_RGB32); + centerImage.load(imagePath); + + if (centerImage.isNull()) { + qWarning() << "Center image could not be loaded!"; + return; + } + + // Create a painter to overlay the center image on the parentImage. + QPainter painter(parentImage); + + // Draw background rectangle. + painter.setBrush(Qt::white); + painter.setPen(Qt::NoPen); + painter.drawRect(x, y, imageSize.width(), imageSize.height()); + + // Scale the center image to be smaller than background rectangle. + float imageRatio = 0.8; + centerImage = centerImage.scaled(imageSize.width() * imageRatio, + imageSize.height() * imageRatio, + Qt::KeepAspectRatio, Qt::SmoothTransformation); + + // Draw image. + painter.drawImage(x + (imageSize.width() - centerImage.width()) / 2, + y + (imageSize.height() - centerImage.height()) / 2, + centerImage); + painter.end(); +} + +void SBarcodeGenerator::setEccLvel(int eccLevel) +{ + if (m_eccLevel == eccLevel) { + return; + } + + m_eccLevel = eccLevel; + emit eccLevelChanged(m_eccLevel); +} + SCodes::SBarcodeFormat SBarcodeGenerator::format() const { return m_format; @@ -112,3 +181,33 @@ void SBarcodeGenerator::setFormat(const QString &formatName) { setFormat(SCodes::fromString(formatName)); } + +QString SBarcodeGenerator::imagePath() const +{ + return m_imagePath; +} + +void SBarcodeGenerator::setImagePath(const QString &imagePath) +{ + if (m_imagePath == imagePath) { + return; + } + + m_imagePath = imagePath; + emit imagePathChanged(); +} + +int SBarcodeGenerator::centerImageRatio() const +{ + return m_centerImageRatio; +} + +void SBarcodeGenerator::setCenterImageRatio(int centerImageRatio) +{ + if (m_centerImageRatio == centerImageRatio) { + return; + } + + m_centerImageRatio = centerImageRatio; + emit centerImageRatioChanged(); +} diff --git a/src/SBarcodeGenerator.h b/src/SBarcodeGenerator.h index 2579681..8a4957d 100644 --- a/src/SBarcodeGenerator.h +++ b/src/SBarcodeGenerator.h @@ -27,6 +27,8 @@ class SBarcodeGenerator : public QQuickItem Q_PROPERTY(QString filePath MEMBER m_filePath) Q_PROPERTY(QString inputText MEMBER m_inputText) Q_PROPERTY(SCodes::SBarcodeFormat format READ format WRITE setFormat NOTIFY formatChanged) + Q_PROPERTY(QString imagePath READ imagePath WRITE setImagePath NOTIFY imagePathChanged) + Q_PROPERTY(int centerImageRatio READ centerImageRatio WRITE setCenterImageRatio NOTIFY centerImageRatioChanged) public: @@ -56,6 +58,32 @@ class SBarcodeGenerator : public QQuickItem */ void setFormat(SCodes::SBarcodeFormat format); + /*! + * \fn QString imagePath() const + * \brief Returns the center image path. + */ + QString imagePath() const; + + /*! + * \fn void setImagePath(const QString &imagePath) + * \brief Sets the center image path. + * \param const QString &imagePath - new image path. + */ + void setImagePath(const QString &imagePath); + + /*! + * \fn int centerImageRatio() const + * \brief Returns the center image ratio. + */ + int centerImageRatio() const; + + /*! + * \fn void setCenterImageRatio(int centerImageRatio) + * \brief Sets the center image ratio. + * \param const int ¢erImageRatio - new image ratio. + */ + void setCenterImageRatio(int centerImageRatio); + public slots: /*! @@ -122,20 +150,51 @@ public slots: */ void formatChanged(SCodes::SBarcodeFormat format); + /*! + * \brief This signal is emitted to send image path to QML. + */ + void imagePathChanged(); + + /*! + * \brief This signal is emitted to send center image ratio to QML. + */ + void centerImageRatioChanged(); + private: int m_width = 500; int m_height = 500; int m_margin = 10; int m_eccLevel = -1; + // centerImageRatio defines the ratio by which the center image is smaller than the QR code. + int m_centerImageRatio = 5; + QString m_extension = "png"; QString m_fileName = "code"; QString m_filePath = ""; QString m_inputText = ""; + QString m_imagePath = ""; SCodes::SBarcodeFormat m_format = SCodes::SBarcodeFormat::Code128; ZXing::Matrix _bitmap = ZXing::Matrix(); + + /*! + * \brief This method draws Rectangle and `imageRatio` smaller Image in the center of that Rectangle. + * \param QImage *parentImage - Image parent. It is used for Painter constructor. + * \param QString imagePath - Image path. + * \param QSize imageSize - Image size. + * \param int x - X coordinate where Image should be painted. + * \param Qint y - Y coordinate where Image should be painted. + */ + void drawCenterImage(QImage *parentImage, const QString &imagePath, QSize imageSize, int x, int y); + + /*! + * \fn void setEccLvel(int eccLevel) + * \brief Sets the QR code ecc level. + * \param int eccLevel - QR code ecc level. + */ + void setEccLvel(int eccLevel); }; #endif // SBARCODEGENERATOR_H