From e0992db6b6a05f09c593fb9be8936ac375f668af Mon Sep 17 00:00:00 2001 From: Jim Evins Date: Mon, 29 May 2017 20:43:10 -0400 Subject: [PATCH] Initial implementation of GNU Barcode backend. --- glabels/BarcodeBackends.cpp | 13 + glabels/BarcodeBackends/CMakeLists.txt | 1 + glabels/BarcodeBackends/GnuBarcode.cpp | 374 +++++++++++++++++++++++++ glabels/BarcodeBackends/GnuBarcode.h | 85 ++++++ glabels/BarcodeBackends/QrEncode.cpp | 2 +- glabels/CMakeLists.txt | 2 + glabels/LabelModelBarcodeObject.cpp | 2 +- glabels/PrintView.cpp | 8 +- glbarcode/QtRenderer.cpp | 28 +- translations/glabels_C.ts | 29 +- 10 files changed, 517 insertions(+), 27 deletions(-) create mode 100644 glabels/BarcodeBackends/GnuBarcode.cpp create mode 100644 glabels/BarcodeBackends/GnuBarcode.h diff --git a/glabels/BarcodeBackends.cpp b/glabels/BarcodeBackends.cpp index bef51ca..d279841 100644 --- a/glabels/BarcodeBackends.cpp +++ b/glabels/BarcodeBackends.cpp @@ -22,6 +22,7 @@ #include "glbarcode/Factory.h" +#include "BarcodeBackends/GnuBarcode.h" #include "BarcodeBackends/QrEncode.h" @@ -72,6 +73,18 @@ namespace glabels registerStyle( "datamatrix", "", tr("IEC16022 (DataMatrix)"), false, false, true, false, "1234567890AB", false, 12 ); +#if HAVE_GNU_BARCODE + // + // Libqrencode backend + // + registerBackend( "gnu-barcode", "GNU Barcode" ); + + glbarcode::Factory::registerType( "gnu-barcode::ean", GnuBarcode::Ean::create ); + + registerStyle( "ean", "gnu-barcode", tr("EAN (any)"), + true, true, true, false, "000000000000 00000", false, 17 ); +#endif // HAVE_GNU_BARCODE + #if HAVE_QRENCODE // // Libqrencode backend diff --git a/glabels/BarcodeBackends/CMakeLists.txt b/glabels/BarcodeBackends/CMakeLists.txt index 933123a..fe87034 100644 --- a/glabels/BarcodeBackends/CMakeLists.txt +++ b/glabels/BarcodeBackends/CMakeLists.txt @@ -2,6 +2,7 @@ # Sources #======================================= set (barcode_sources + GnuBarcode.cpp QrEncode.cpp ) diff --git a/glabels/BarcodeBackends/GnuBarcode.cpp b/glabels/BarcodeBackends/GnuBarcode.cpp new file mode 100644 index 0000000..8be3761 --- /dev/null +++ b/glabels/BarcodeBackends/GnuBarcode.cpp @@ -0,0 +1,374 @@ +/* GnuBarcode.cpp + * + * Copyright (C) 2017 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gLabels-qt. If not, see . + */ + +#if HAVE_GNU_BARCODE + +#include "GnuBarcode.h" + +#include +#include +#include + + +namespace +{ + const double INK_BLEED = 0.1; /* Shrink bars to account for ink bleed. */ + const double FONT_SCALE = 0.75; /* Shrink font slightly, otherwise too crowded. */ +} + + +namespace glabels +{ + namespace GnuBarcode + { + + bool Base::isAscii( const std::string& data ) const + { + for ( unsigned int i = 0; i < data.size(); i++ ) + { + if ( (data[i] & 0x80) != 0 ) + { + return false; + } + } + + return true; + } + + + bool Base::isNumericLengthValid( const std::string& data, + unsigned int nMin, + unsigned int nMax ) const + { + unsigned int n = 0; + + for ( unsigned int i = 0; i < data.size(); i++ ) + { + if ( (data[i] & 0x80) == 0 ) + { + n++; + } + else + { + return false; + } + } + + return (n >= nMin) && (n <= nMax); + } + + + bool Base::isNumericLength1Valid( const std::string& data, + unsigned int nMin, + unsigned int nMax ) const + { + unsigned int n = 0; + + for ( unsigned int i = 0; !isspace(data[i]) && (i < data.size()); i++ ) + { + if ( isdigit(data[i]) ) + { + n++; + } + else if ( !isspace(data[i]) ) + { + return false; + } + } + + return (n >= nMin) && (n <= nMax); + } + + + bool Base::isNumericLength2Valid( const std::string& data, + unsigned int nMin, + unsigned int nMax ) const + { + unsigned int i; + unsigned int n = 0; + + for ( i = 0; !isspace(data[i]) && (i < data.size()); i++ ) + { + /* Skip over 1st string */ + } + + for ( i++ /* skip space */ ; i < data.size(); i++ ) + { + if ( isdigit(data[i]) ) + { + n++; + } + else + { + return false; + } + } + + return (n >= nMin) && (n <= nMax); + } + + + void Base::vectorize( const std::string& encodedData, + const std::string& displayText, + const std::string& cookedData, + double& w, + double& h ) + { + /* + * First encode using GNU Barcode library. + */ + Barcode_Item* bci = Barcode_Create( (char*)cookedData.c_str() ); + + bci->scalef = 0; + bci->width = (int)w; + bci->height = (int)h; + + bci->flags = flags; + if ( !showText() ) + { + bci->flags |= BARCODE_NO_ASCII; + } + if ( !checksum() ) + { + bci->flags |= BARCODE_NO_CHECKSUM; + } + + Barcode_Encode( bci, flags ); + + if ( (bci->partial == nullptr) || (bci->textinfo == nullptr) ) + { + Barcode_Delete( bci ); + setIsDataValid( false ); + return; + } + + + /* + * Now do the actual vectorization. + * + * This code is based on the postscript renderer (ps.c) from the GNU barcode library: + * + * Copyright (C) 1999 Alessaandro Rubini (rubini@gnu.org) + * Copyright (C) 1999 Prosa Srl. (prosa@prosa.it) + */ + if (bci->width > (2*bci->margin)) + { + bci->width -= 2*bci->margin; + } + if (bci->height > (2*bci->margin)) + { + bci->height -= 2*bci->margin; + } + + /* First calculate barlen */ + int barlen = bci->partial[0] - '0'; + for ( int i = 1; bci->partial[i] != 0; i++ ) + { + if ( isdigit(bci->partial[i]) ) + { + barlen += bci->partial[i] - '0'; + } + else + { + if ( (bci->partial[i] != '+') && (bci->partial[i] != '-') ) + { + barlen += bci->partial[i] - 'a' + 1; + } + } + } + + /* The scale factor depends on bar length */ + double scalef = 1; + if ( bci->scalef == 0 ) + { + if ( bci->width == 0 ) + { + bci->width = barlen; /* default */ + } + scalef = bci->scalef = (double)bci->width / (double)barlen; + if ( scalef < 0.5 ) + { + scalef = 0.5; + } + } + + /* The width defaults to "just enough" */ + bci->width = (int)( barlen * scalef + 1 ); + + /* But it can be too small, in this case enlarge and center the area */ + if ( bci->width < (int)(barlen * scalef) ) + { + int wid = (int)( barlen * scalef + 1); + bci->xoff -= (wid - bci->width)/2 ; + bci->width = wid; + /* Can't extend too far on the left */ + if (bci->xoff < 0) + { + bci->width += -bci->xoff; + bci->xoff = 0; + } + } + + /* The height defaults to 80 points (rescaled) */ + if ( bci->height == 0 ) + { + bci->height = (int)( 80 * scalef ); + } + + /* If too small (5 + text), reduce the scale factor and center */ + int i = 5 + 10 * ( ((bci->flags & BARCODE_NO_ASCII)==0) ? 1 : 0 ); + if ( bci->height < (int)(i * scalef) ) + { + bci->height = (int)( i * scalef ); + } + + /* Now traverse the code string and create a list of lines */ + char mode = '-'; /* text below bars */ + double x = bci->margin + (bci->partial[0] - '0') * scalef; + i = 1; + for ( int ip = 1; bci->partial[ip] != 0; ip++, i++) + { + /* special cases: '+' and '-' */ + if ( bci->partial[ip] == '+' || bci->partial[ip] == '-' ) + { + mode = bci->partial[ip]; /* don't count it */ + i++; + continue; + } + /* j is the width of this bar/space */ + int j; + if ( isdigit(bci->partial[ip]) ) + { + j = bci->partial[ip] - '0'; + } + else + { + j = bci->partial[ip] - 'a' + 1; + } + if ( (i % 2) != 0 ) + { + /* bar */ + double x0 = x; + double y0 = bci->margin; + double yr = bci->height; + if ( (bci->flags & BARCODE_NO_ASCII) == 0 ) + { + /* leave space for text */ + if (mode == '-') + { + /* text below bars: 10 or 5 points */ + yr -= (isdigit(bci->partial[ip]) ? 10 : 5) * scalef; + } + else + { + /* '+' */ + /* above bars: 10 or 0 from bottom, + and 10 from top */ + y0 += 10 * scalef; + yr -= (isdigit(bci->partial[ip]) ? 20 : 10) * scalef; + } + } + addBox( x0, y0, (j * scalef) - INK_BLEED, yr ); + } + x += j * scalef; + + } + + /* Now the text */ + mode = '-'; /* reinstantiate default */ + if ( (bci->flags & BARCODE_NO_ASCII) == 0 ) + { + int len = strlen( bci->textinfo ); + for ( int i = 0; i < len; i++ ) + { + if ( bci->textinfo[i] == ' ' ) + { + continue; + } + if ( (bci->textinfo[i] == '+') || (bci->textinfo[i] == '-') ) + { + mode = bci->textinfo[i]; + continue; + } + double f1, f2; + char c; + if ( sscanf( &bci->textinfo[i], "%lf:%lf:%c", &f1, &f2, &c) != 3 ) + { + qDebug() << "Impossible data:" << QString(&bci->textinfo[i]); + continue; + } + f2 *= FONT_SCALE; + double x0 = f1*scalef + bci->margin + 0.4*f2*scalef; + double y0; + if (mode == '-') + { + y0 = bci->margin + bci->height - 8*scalef + 0.75*f2*scalef; + } + else + { + y0 = bci->margin + 0.75*f2*scalef; + } + addText( x0, y0, f2*scalef, QString(c).toStdString() ); + + /* skip past the substring we just read. */ + while ( (bci->textinfo[i] != ' ') && (bci->textinfo[i] != 0) ) + { + i++; + } + } + } + + /* Fill in other info */ + w = bci->width + 2.0*bci->margin; + h = bci->height + 2.0*bci->margin; + + /* Cleanup */ + Barcode_Delete( bci ); + } + + + + glbarcode::Barcode* Ean::create() + { + return new Ean(); + } + + + bool Ean::validate( const std::string& rawData ) + { + return ( isNumericLengthValid( rawData, 7, 8 ) + || isNumericLengthValid( rawData, 12, 13 ) + || ( isNumericLength1Valid( rawData, 7, 8 ) && isNumericLength2Valid( rawData, 2, 2 ) ) + || ( isNumericLength1Valid( rawData, 7, 8 ) && isNumericLength2Valid( rawData, 5, 5 ) ) + || ( isNumericLength1Valid( rawData, 12, 13 ) && isNumericLength2Valid( rawData, 2, 2 ) ) + || ( isNumericLength1Valid( rawData, 12, 13 ) && isNumericLength2Valid( rawData, 5, 5 ) ) ); + } + + + std::string Ean::encode( const std::string& cookedData ) + { + flags = BARCODE_EAN; + return ""; // Actual encoding is done in vectorize + } + + } +} + +#endif // HAVE_GNU_BARCODE diff --git a/glabels/BarcodeBackends/GnuBarcode.h b/glabels/BarcodeBackends/GnuBarcode.h new file mode 100644 index 0000000..fd6783a --- /dev/null +++ b/glabels/BarcodeBackends/GnuBarcode.h @@ -0,0 +1,85 @@ +/* GnuBarcode.h + * + * Copyright (C) 2017 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gLabels-qt. If not, see . + */ + +#ifndef barcode_GnuBarcode_h +#define barcode_GnuBarcode_h + +#if HAVE_GNU_BARCODE + +#include "glbarcode/Barcode1dBase.h" + + +namespace glabels +{ + namespace GnuBarcode + { + + /** + * GnuBarcode::Base base class for all GNU Barcodes + * + * Implements glbarcode::Barcode1dBase. + */ + class Base : public glbarcode::Barcode1dBase + { + protected: + int flags; + + bool isAscii( const std::string& data ) const; + + bool isNumericLengthValid( const std::string& data, + unsigned int nMin, + unsigned int nMax ) const; + + bool isNumericLength1Valid( const std::string& data, + unsigned int nMin, + unsigned int nMax ) const; + + bool isNumericLength2Valid( const std::string& data, + unsigned int nMin, + unsigned int nMax ) const; + + void vectorize( const std::string& encodedData, + const std::string& displayText, + const std::string& cookedData, + double& w, + double& h ) override; + }; + + + /** + * EAN Barcode (Any) + */ + class Ean : public Base + { + public: + static Barcode* create(); + + protected: + bool validate( const std::string& rawData ) override; + std::string encode( const std::string& cookedData ) override; + }; + + } +} + + +#endif // HAVE_GNU_BARCODE + +#endif // barcode_GnuBarcode_h diff --git a/glabels/BarcodeBackends/QrEncode.cpp b/glabels/BarcodeBackends/QrEncode.cpp index aa41e87..1949068 100644 --- a/glabels/BarcodeBackends/QrEncode.cpp +++ b/glabels/BarcodeBackends/QrEncode.cpp @@ -22,7 +22,7 @@ #include "QrEncode.h" -#include "qrencode.h" +#include namespace glabels diff --git a/glabels/CMakeLists.txt b/glabels/CMakeLists.txt index bc237bf..900e393 100644 --- a/glabels/CMakeLists.txt +++ b/glabels/CMakeLists.txt @@ -199,6 +199,7 @@ target_link_libraries (glabels-qt ${Qt5Xml_LIBRARIES} ${Qt5Svg_LIBRARIES} ${ZLIB_LIBRARIES} + ${GNUBARCODE_LIBRARIES} ${LIBQRENCODE_LIBRARIES} ) @@ -213,6 +214,7 @@ include_directories ( ${Qt5PrintSupport_INCLUDE_DIRS} ${Qt5Xml_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS} + ${GNUBARCODE_INCLUDE_DIR} ${LIBQRENCODE_INCLUDE_DIR} ) diff --git a/glabels/LabelModelBarcodeObject.cpp b/glabels/LabelModelBarcodeObject.cpp index c48fa82..753d783 100644 --- a/glabels/LabelModelBarcodeObject.cpp +++ b/glabels/LabelModelBarcodeObject.cpp @@ -450,7 +450,7 @@ namespace glabels { painter->setPen( QPen( color ) ); - glbarcode::Barcode* bc = glbarcode::Factory::createBarcode( mBcStyle.id().toStdString() ); + glbarcode::Barcode* bc = glbarcode::Factory::createBarcode( mBcStyle.fullId().toStdString() ); bc->setChecksum(mBcChecksumFlag); bc->setShowText(mBcTextFlag); diff --git a/glabels/PrintView.cpp b/glabels/PrintView.cpp index cdde000..f6e98b0 100644 --- a/glabels/PrintView.cpp +++ b/glabels/PrintView.cpp @@ -122,7 +122,7 @@ namespace glabels void PrintView::onPrintButtonClicked() { QSizeF pageSize( mModel->tmplate()->pageWidth().pt(), mModel->tmplate()->pageHeight().pt() ); - mPrinter->setPaperSize( pageSize, QPrinter::Point ); + mPrinter->setPageSize( QPageSize(pageSize, QPageSize::Point) ); mPrinter->setFullPage( true ); mPrinter->setPageMargins( 0, 0, 0, 0, QPrinter::Point ); @@ -139,9 +139,9 @@ namespace glabels { QPainter painter( mPrinter ); - QSizeF sizePx = mPrinter->paperSize( QPrinter::DevicePixel ); - QSizeF sizePts = mPrinter->paperSize( QPrinter::Point ); - painter.scale( sizePx.width()/sizePts.width(), sizePx.height()/sizePts.height() ); + QRectF rectPx = mPrinter->paperRect( QPrinter::DevicePixel ); + QRectF rectPts = mPrinter->paperRect( QPrinter::Point ); + painter.scale( rectPx.width()/rectPts.width(), rectPx.height()/rectPts.height() ); for ( int iPage = 0; iPage < mRenderer.nPages(); iPage++ ) { diff --git a/glbarcode/QtRenderer.cpp b/glbarcode/QtRenderer.cpp index 04265a7..85ad0c7 100644 --- a/glbarcode/QtRenderer.cpp +++ b/glbarcode/QtRenderer.cpp @@ -23,6 +23,14 @@ #include #include #include +#include +#include + + +namespace +{ + const double FONT_SCALE = 0.75; +} namespace glbarcode @@ -136,18 +144,20 @@ namespace glbarcode { d->painter->setPen( QPen( d->color ) ); - QFont font = d->painter->font(); + QFont font; font.setStyleHint( QFont::Monospace ); - font.setPointSizeF( size ); - d->painter->setFont( font ); + font.setFamily( "monospace" ); + font.setPointSizeF( FONT_SCALE*size ); - QFontMetrics fm( font ); - QRect rect = fm.boundingRect( QString::fromStdString(text) ); - - double xCorner = x - rect.width()/2.0; - double yCorner = y + fm.descent(); + QFontMetricsF fm( font ); + double xCorner = x - fm.width( QString::fromStdString(text) )/2.0; + double yCorner = y - fm.ascent(); - d->painter->drawText( QPointF(xCorner, yCorner), QString::fromStdString(text) ); + QTextLayout layout( QString::fromStdString(text), font ); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + layout.draw( d->painter, QPointF(xCorner, yCorner) ); } } diff --git a/translations/glabels_C.ts b/translations/glabels_C.ts index fc227c4..68bf213 100644 --- a/translations/glabels_C.ts +++ b/translations/glabels_C.ts @@ -744,62 +744,67 @@ glabels::BarcodeBackends - + POSTNET (any) - + POSTNET-5 (ZIP only) - + POSTNET-9 (ZIP+4) - + POSTNET-11 (DPBC) - + CEPNET - + USPS Intelligent Mail - + IEC16022 (DataMatrix) - + + EAN (any) + + + + IEC18004 (QRCode) - + Code 39 - + Code 39 Extended - + UPC-A - + EAN-13