Qt 中利用 GNU barcode 生成一维条形码

Qt 中利用 GNU barcode 生成一维条形码

最近有个项目,需要用到条形码。上网找了一圈,网上很少有介绍如何用 C++ 或者 C代码生成条形码的。偶尔有那么一两篇,也是针对某一种条形码给出的。而且一般都是 Code 39 码或者 Code 128 码这种比较简单的条码。

我调研了一番,感觉开源的条码库里面还是GNU barcode 实现的比较全。基本常见的条形码全都支持。不过 GNU barcode 直接在项目里用不是很方便。因为它封装的太简单了。缺少把条码画出来的功能代码。因此,我花了几天时间,对 GNU barcode 进行了二次封装,封装之后方便在 Qt 项目中使用。

关于如何获取和编译 GNU barcode 已经有了一篇博客来介绍:

https://blog.csdn.net/liyuanbhu/article/details/106300298

这里就只写写我的封装代码。

我把相关功能封装到了一个类中:QlyGNUBarcode

这个类使用起来很简单,下面是个简单的样例代码:

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_I25 | BARCODE_NO_CHECKSUM);
bc.setNO_ASCII(false);
bc.setMargin(5);
bc.encodeText("1234567890");
QImage img = bc.painterImage(2, 80);

输出的图片如下:

在这里插入图片描述

如果不想要条码下面的文字,代码可以改为:

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_I25 | BARCODE_NO_CHECKSUM);
bc.setNO_ASCII(true);
bc.setMargin(5);
bc.encodeText("1234567890");
QImage img = bc.painterImage(2, 80);

输出的图片如下:

在这里插入图片描述

上面代码中 setMargin() 设置的是条码四周的留白。不同的条码要求的最小留白是不同的。不过一般设5-10个像素就够了。

setBarcodeType() 是设置条码的编码类型。 GNU barcode 支持的条码类型还是比较全的。在 v0.99 中支持一下几种。

  • UPC

  • EAN

  • ISBN

  • code 128-B

  • code 128-C

  • code 39

  • interleaved 2 of 5

  • code 128

  • Codabar

  • Plessey

  • MSI

  • code 93

UPC

其中 UPC 要求字符串长度为 6、7、8(UPC-E)或者 11、12 (UPC-A)。并且字符只能是数字。

下面是一个代码片段:

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_UPC);
bc.encodeText("1234567");
bc.encodeText("12345678901");

在这里插入图片描述

EAN

EAN 和 UPC 要求差不多,只支持数字。要求数字是 7位或者12位。通常用于商品的编码。

下面是例子:

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_EAN);
bc.encodeText("1234567");
bc.encodeText("123456789012");

在这里插入图片描述

ISBN

ISBN 是图书上用的一种编码,除了图书上,其他地方很少用。这里就不介绍了。

CODE 128

code 128 可以编码任意可以打印出来的 ASCII 字符。

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_128);
bc.encodeText("1234567890ABCDEFGH");
bc.setBarcodeType(BARCODE_128B);
bc.encodeText("3200930012ABC");

在这里插入图片描述

CODE 39

CODE 39 这种编码支持数字和大写字母,还支持”+“、”-“、”*“ 等少数几个特殊符号。但是这个编码比较浪费空间。比如下面这个码,本身没有几个字符,却要占这么长的地方。

在这里插入图片描述

interleaved 2 of 5

交叉25码。只支持数字。带校验的时候只支持奇数位数,如果输入字符是偶数,则编码是前面会加个 0。 如果不想要这个 0 可以把校验去掉。比如下面的例子:

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_I25);
bc.encodeText("123456");
bc.setBarcodeType(BARCODE_I25 | BARCODE_NO_CHECKSUM);

在这里插入图片描述

Plessey

支持 0123456789ABCDE 。这15个字符。输出结果中的字母永远是大写。

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_PLS);
bc.encodeText("12345ABCDE");

在这里插入图片描述

MSI

MSI 只支持数字。位数不限。

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_MSI);
bc.encodeText("1234567890");

在这里插入图片描述

CODE 93

CODE 93 支持10个数字,26个英文字母,空格等共 48 个字符。

QlyGNUBarcode bc;
bc.setBarcodeType(BARCODE_93);
bc.encodeText("1234567890ABCXYZ+-*");

在这里插入图片描述

下面是我的代码:

#ifndef QTGNUBARCODE_H
#define QTGNUBARCODE_H

/**
 * @file QlyGNUBarcode.h
 * @author LiYuan
 * @email [email protected]
 * @version v1.0
 * @license GPL2.0
 * @brief GNU Barcode 库的一个简单封装,用于在 Qt 程序中生成条形码。
 * @details 因为用到了 QPainter 和 QImage, 所以需要 QtGui 模块。
 *          可用于 Qt4 和 Qt5 代码中。Qt6 还没有测试。
 * @copyright 2015-2021
 */

#include <QImage>
#include <QPainter>

#include "barcode.h"

/**
 * @brief The QlyGNUBarcode class 对 GNU Barcode 库的一个封装。
 */
class QlyGNUBarcode
{
public:
    QlyGNUBarcode();
    ~QlyGNUBarcode() {if( bc ) Barcode_Delete(bc);}

    /**
     * @brief encodeText 对文字进行编码。
     * @param text
     * @return
     */
    bool encodeText(const QString & text);

    /**
     * @brief setBarcodeType 设置条码的类型。条形码类型很多,如果不设置则选择能编码数据的第一种条码类型。
     * @param barcode_type BARCODE_EAN 等,具体参照 barcode.h
     */
    void setBarcodeType(int barcode_type) {m_barcode_type = barcode_type;}

    /**
     * @brief setMargin 条码四周留白区域的大小。
     * @param whitespace
     */
    void setMargin(int whitespace);

    /**
     * @brief setNO_ASCII 设置是否在条码上/下面显示对应文字
     * @param on
     */
    void setNO_ASCII(bool on) {m_NO_ASCII = on;}

    QImage paintImage(double width_scale = 2, int height = 60, QImage::Format format = QImage::Format_RGB32);
    QImage paintImage(QSize size, QImage::Format format = QImage::Format_RGB32);
    bool render(QPainter &painter);
    bool render(QPainter &painter, QRect rect);

    /**
     * @brief minSize 返回条码的尺寸,不包括四周的 margin。
     * @return
     */
    QSize minSize();

    /**
     * @brief size 返回条码的尺寸,包括四周的 margin。
     * @return
     */
    QSize size() {return QSize(m_global_width, m_global_height);}

    /**
     * @brief foregroundColor 返回条码的前景色。
     * @return
     */
    QColor foregroundColor() const;
    void setForegroundColor(const QColor & fgColor);

    /**
     * @brief backgroundColor 返回条码的背景色。
     * @return
     */
    QColor backgroundColor() const;
    void setBackgroundColor(const QColor & bgColor);


private:
    bool updateSizeInfo();
    bool drawBar(QPainter &painter);
    bool drawText(QPainter &painter);

    Barcode_Item * bc;

    int m_barcode_type;
    bool m_NO_ASCII;

    int m_minWidth;
    int m_minHeight;
    int m_margin;
    int m_global_width; // 整体的宽度,包含 margin
    int m_global_height; // 整体的高度,包含 margin

    QString m_text;
    QColor m_fgColor;
    QColor m_bgColor;
};

#endif // QTGNUBARCODE_H

/**
 * @file QlyGNUBarcode.cpp
 * @author LiYuan
 * @email [email protected]
 * @version v1.0
 * @license GPL2.0
 * @brief GNU Barcode 库的一个简单封装,用于在 Qt 程序中生成条形码。
 * @details 因为用到了 QPainter 和 QImage, 所以需要 QtGui 模块。
 *          可用于 Qt4 和 Qt5 代码中。Qt6 还没有测试。
 * @copyright 2015-2021
 */

#include "QlyGNUBarcode.h"
#include <QDebug>
#include <string.h>

QlyGNUBarcode::QlyGNUBarcode()
    :bc(nullptr),
      m_barcode_type(BARCODE_128),
      m_NO_ASCII(false), 
      m_margin(0),
      m_fgColor(Qt::black),
      m_bgColor(Qt::white)
{

}

void QlyGNUBarcode::setMargin(int whitespace)
{
    m_margin = whitespace;
}

QColor QlyGNUBarcode::foregroundColor() const
{
    return m_fgColor;
}

void QlyGNUBarcode::setForegroundColor(const QColor & fgColor)
{
    m_fgColor = fgColor;
}

QColor QlyGNUBarcode::backgroundColor() const
{
    return  m_bgColor;
}

void QlyGNUBarcode::setBackgroundColor(const QColor & bgColor)
{
    m_bgColor = bgColor;
}

QSize QlyGNUBarcode::minSize()
{
    if( bc ) return QSize();
    return QSize(bc->width, bc->height);
}

bool QlyGNUBarcode::encodeText(const QString &text)
{
    if( bc ) Barcode_Delete(bc);
    bc = Barcode_Create((char *) text.toLocal8Bit().data());
    //Barcode_Position(bc, 0, 0, 0, 0, 1.0);
    bc->margin = m_margin;
    int flags = m_barcode_type;
    if(m_NO_ASCII)
    {
        flags = m_barcode_type | BARCODE_NO_ASCII;
    }
    else
    {
        flags = m_barcode_type;
    }
    bc->flags = flags;

    Barcode_Encode(bc, flags);
    updateSizeInfo();
    return (bc);
}

bool QlyGNUBarcode::updateSizeInfo()
{
    if( !bc ) return false;

    if ( !bc->partial || !bc->textinfo )
    {
        bc->error = EINVAL;
        return false;
    }
    /* First, calculate barlen */
    int barlen = bc->partial[0] - '0';
    for (char * ptr = bc->partial + 1; *ptr; ptr++)
    {
        if ( isdigit(*ptr) )
        {
            barlen += (*ptr - '0');
        }
        else if ( islower(*ptr) )
        {
            barlen += (*ptr - 'a' + 1);
        }
    }

    m_minWidth = barlen; // 这个宽度是计算出的最小宽度
    m_minHeight = 80; // 默认的高度

    qDebug() << "m_minWidth = " << m_minWidth;
    qDebug() << "m_minHeight = " << m_minHeight;

    /* The scale factor depends on bar length */
    if ( (fabs(bc->scalef) < 1e-6) )
    {
        if ( !bc->width )
        {
            bc->width = barlen; /* default */
        }
        bc->scalef = (double) bc->width / (double)barlen;

        //qDebug() << "fabs(bc->scalef) < 1e-6 , bc->width = " << bc->width << "bc->scalef = " << bc->scalef;
    }

    /* The width defaults to "just enough" */
    if ( !bc->width )
    {
        bc->width = barlen * bc->scalef + 1;
    }

    /* But it can be too small, in this case enlarge and center the area */
    if (bc->width < barlen * bc->scalef)
    {
        int wid = barlen * bc->scalef + 1;
        bc->xoff -= (wid - bc->width) / 2 ;
        bc->width = wid;
        /* Can't extend too far on the left */
        if (bc->xoff < 0)
        {
            bc->width += -bc->xoff;
            bc->xoff = 0;
        } 
    }

    /* The height defaults to 80 points (rescaled) */
    if ( !bc->height )
    {
        bc->height = 80 * bc->scalef;
    }

    /* If too small (5 + text), reduce the scale factor and center */
    int i = 5 + 10 * (( bc->flags & BARCODE_NO_ASCII) == 0 );
    if (bc->height < i * bc->scalef )
    {
        double scaleg = ((double) bc->height) / i;
        int wid = bc->width * scaleg / bc->scalef;
        bc->xoff += ( bc->width - wid ) / 2;
        bc->width = wid;
        bc->scalef = scaleg;
    }
    m_margin = bc->margin;

    m_global_width  = bc->xoff + bc->width  + 2 * bc->margin;
    m_global_height = bc->yoff + bc->height + 2 * bc->margin;

    return true;
}

bool QlyGNUBarcode::render(QPainter &painter, QRect rect)
{
    int xoffset = bc->xoff;
    int yoffset = bc->yoff;
    int width = bc->width;
    int height = bc->height;
    double scalef = bc->scalef;

    bc->xoff = rect.left();
    bc->xoff = rect.top();
    bc->width = rect.width() - 2 * m_margin;
    bc->height = rect.height() - 2 * m_margin;
    bc->scalef = 0.0;

    updateSizeInfo();
    bool ret = render(painter);

    bc->xoff = xoffset;
    bc->yoff = yoffset;
    bc->width = width;
    bc->height = height;
    bc->scalef = scalef;

    return ret;
}

bool QlyGNUBarcode::drawBar(QPainter &painter)
{
    int mode = '-';
    int i; /* text below bars */
    char * ptr;
    double xpos = bc->margin + (bc->partial[0] - '0') * bc->scalef;
    for (ptr = bc->partial + 1, i = 1; *ptr; ptr++, i++)
    {
        /* special cases: '+' and '-' */
        if (*ptr == '+' || *ptr == '-')
        {
            mode = *ptr; /* don't count it */
            i++;
            continue;
        }
        /* j is the width of this bar/space */
        int j;
        if (isdigit (*ptr))   j = *ptr - '0';
        else                  j = *ptr - 'a' + 1;

        double x0, y0, yr;
        if (i % 2) /* bar */
        {

            //qDebug() << "bc->xoff = " << bc->xoff << ", xpos = " << xpos;
            x0 = bc->xoff + xpos;// + (j * scalef) / 2;
            y0 = bc->yoff + bc->margin;
            yr = bc->height;
            if ( !(bc->flags & BARCODE_NO_ASCII) )
            { /* leave space for text */
                if (mode == '-')
                {
                    /* text below bars: 10 points or five points */
                    //y0 += (isdigit(*ptr) ? 10 : 5) * scalef;
                    yr -= (isdigit(*ptr) ? 10 : 5) * bc->scalef;
                }
                else
                { /* '+' */
                    /* text above bars: 10 or 0 from bottom, and 10 from top */
                    y0 += (isdigit(*ptr) ? 10 : 0) * bc->scalef;
                    yr -= (isdigit(*ptr) ? 20 : 10) * bc->scalef;
                }
            }
            painter.fillRect(QRect(x0, y0, (j * bc->scalef), yr ), m_fgColor);
            //qDebug() << "fillRect: " <<  QRect(x0, y0, (j * m_scalef), yr );
        }
        xpos += j * bc->scalef;
    }
    return true;
}

bool QlyGNUBarcode::drawText(QPainter &painter)
{

    int mode = '-'; /* reinstantiate default */
    if (!(bc->flags & BARCODE_NO_ASCII))
    {
        painter.save();
        painter.setPen(m_fgColor);

        for (char * ptr = bc->textinfo; ptr; ptr = strchr(ptr, ' '))
        {
            //qDebug() << "*";
            while (*ptr == ' ') ptr++;

            if (!*ptr) break;
            if (*ptr == '+' || *ptr == '-')
            {
                mode = *ptr; continue;
            }
            double f1, f2;
            char c;
            if (sscanf(ptr, "%lf:%lf:%c", &f1, &f2, &c) != 3)
            {
                //fprintf(stderr, "barcode: impossible data: %s\n", ptr);
                continue;
            }
            painter.setFont(QFont("Helvetica", (int)(0.8 * f2 * bc->scalef)));
            int x_pos = bc->xoff + f1 * bc->scalef + bc->margin;

            int y_pos = 0;
            if(mode == '-')
            {
                y_pos = (double)bc->yoff + bc->margin + bc->height ;//- 8 * bc->scalef;
            }
            else
            {
                y_pos =  (double)bc->yoff + bc->margin;
            }
            painter.drawText(QPoint(x_pos, y_pos ), QString(QChar(c)));
        }
        painter.restore();
    }
    return true;
}
QImage QlyGNUBarcode::paintImage(QSize size, QImage::Format format)
{
    if(!bc) return QImage();

    int w = size.width();
    double h = size.height();
    return paintImage(w / bc->width, h, format);
}

QImage QlyGNUBarcode::paintImage(double width_scale, int height, QImage::Format format)
{
    if(!bc) return QImage();

    int bcWidth = bc->width; // 保存现场
    int bcHeight = bc->height;
    float bcScalef = bc->scalef;

    bc->width = bc->width * width_scale;
    bc->scalef = width_scale;
    bc->height = height;
    //qDebug() << "bc->margin = " << bc->margin;

    int w = bc->width + 2 * bc->margin;
    int h = bc->height + 2 * bc->margin;

    //qDebug() << "in QtGnuBarcode::toImage(), w = " << w << ", h = " << h;
    QImage img(w, h, format);
    QPainter painter(&img);
    img.fill(m_bgColor);
    painter.setBrush(m_fgColor);
    render(painter);

    bc->width = bcWidth; // 恢复原状
    bc->height = bcHeight;
    bc->scalef = bcScalef;
    return img;
}

bool QlyGNUBarcode::render(QPainter &painter)
{
    if( !bc ) return false;

    int w = bc->width + 2 * bc->margin;
    int h = bc->height + 2 * bc->margin;
    painter.fillRect(QRect(0, 0, w, h), m_bgColor);

    if ( !bc->partial || !bc->textinfo )
    {
        bc->error = EINVAL;
        return false;
    }
    drawBar(painter);

    qDebug() << "bc->flags & BARCODE_NO_ASCII = " <<( bc->flags & BARCODE_NO_ASCII);
    if(! (bc->flags & BARCODE_NO_ASCII) )
    {
        drawText(painter);
    }
    return true;
}

猜你喜欢

转载自blog.csdn.net/liyuanbhu/article/details/121404792