【Qt】条形码制作器(Code39编码)

环境:Qt5.3 MSVC2010

编译器:Qt Creator


描述:

        最近公司项目卡在了POS机打印小票上,因为涉及打印机太多,很多打印机对ESC/POS指令集支持又不是特别好,所以直接用ESC指令集打不出需要的条形码。唯一的方式只有先生成需要的条形码图片,再解析图片把数据发送给POS小票机打印。

主要问题就是生成条形码算法了。

        现在最常见的条形码有两种编码格式,一种是39码(code39),一种是128码(code128)。

        最初打算使用的是39码,代码做完之后,发现39码的编码规则虽然简单,但是不适合太长的条形码,一般码数超过8位图片就显得很长,再长一些甚至就会超出POS打印机的最大宽度。相对于39码,128码的编码规则虽然复杂,字符集庞大,但是能把纯数字的条形码长度压缩近一倍(相对于39码),所以后来又重做了128码图片生成。

        由于128码支持的字符集太多,但是公司项目目前只针对纯数字的条形码,所以128码的代码只写了极小部分的字符集支持,就不贴上来。

        只贴39码的算法。


解析:

       关于39码,遵循以下规则(不少资料中会总结为5条规则,但是并不好理解,我总结为一下几条)

1.支持0~9 A~Z(大写字母) + - * / % ¥[空格]其中*只作为起始终止符,不出现在条码中)

        2.每个字符对应一个12位的二进制逻辑码(对照表见后)

        3.对应逻辑码中,1代表条形码的黑线,0代表条形码白线(不少资料也会约束11为粗黑线,00为粗白线,1为细黑线,0为细白线)

        4.条形码的开头与结尾必须使用*做标识符


PS:

        第一次做出代码生成图片时,扫描不成功,这个问题纠结了很长时间,后来把合法的条形码和自己生成的条形码挨个对比,才发现问题。

       实际上,每个字符对应的条形码之间,还有一个单位的白线。

       也就是说,在QString Code39::CodeBinary(QString)中,每转换一个二进制逻辑码后,都要加一个“0”来分割两个字符的二进制逻辑码。这点非常重要!


Code39.cpp(头文件略)

#include "Code39.h"
#include <QDebug>
#include <QDateTime>
#include <QMessageBox>
#include <QFont>

Code39::Code39(QString BarCode,QString SavePath)
{
    barcode=BarCode;
    path=SavePath;
    InitMap();
    start(barcode);
}

void Code39::start(QString barcode)
{
    //校验字符是否合法
    QString com="0123456789ABCDEFGHIJKLMNOPQISTUVWXWZ+-*/%$. ";//合法字符模版
    for(int i=0;i<barcode.size();i++)
    {
        if(!com.contains(barcode.at(i)))
        {
            QMessageBox msg;
            msg.setText(QStringLiteral("条形码字符不合法!\n合法字符:A~Z 0~9 +-/%$[空格]"));
            msg.exec();
            return;
        }
    }
    draw(CodeBinary(barcode),barcode);
}

void Code39::draw(QString BinaryNum,QString barcode)
{
    int MAXWIDTH;
    int MAXHEIGHT=100;
    int FONTHEIGHT=30;

    //设定条码线条宽度px
    int LineWidth=3;
    //预留10px的左右空白;
    MAXWIDTH=LineWidth*BinaryNum.size()+10;

    //计算条线宽度时,精度丢失造成条码右边空白,进行右移居中
    int move=(MAXWIDTH-LineWidth*BinaryNum.size())/2;

    QBitmap bmp(MAXWIDTH,MAXHEIGHT+FONTHEIGHT);
    QPainter painter(&bmp);
    QPen white,black;
//    painter.setRenderHint(QPainter::Antialiasing,true);//弧线圆润
    white.setColor(QColor(255,255,255));
    black.setColor(QColor(0,0,0));
    white.setWidth(LineWidth);
    black.setWidth(LineWidth);

    //背景充填白色
    painter.setPen(white);
    painter.drawRect(0,0,MAXWIDTH,MAXHEIGHT+FONTHEIGHT);

    //画线
    for(int i=0;i<BinaryNum.size();i++)
    {
        if(BinaryNum.at(i)=="1") painter.setPen(black);
        else painter.setPen(white);
        painter.drawLine(i*LineWidth+LineWidth/2+move,0,i*LineWidth+LineWidth/2+move,MAXHEIGHT);
    }

    //添加底部条形码字符
    painter.setPen(black);
    painter.setRenderHint(QPainter::Antialiasing,true);
    painter.setRenderHint(QPainter::TextAntialiasing,true);
    painter.setRenderHint(QPainter::HighQualityAntialiasing,true);
    QFont font;
    font.setFamily("Impact");
    font.setPixelSize(20);
    font.setWeight(50);
    painter.setFont(font);
    painter.drawText(10,100,MAXWIDTH,30,Qt::AlignCenter,barcode);
    bmp.save(path+QDateTime::currentDateTime().toString("MM-dd-hh-mm-ss-zz")+".bmp");
}


QString Code39::CodeBinary(QString barcode)
{
    barcode="*"+barcode+"*";
    QString str="";
    for(int i=0;i<barcode.size();i++)
    {
        str+=map[barcode.at(i)]+"0";//每个字符的二进制逻辑码之间,用“0”隔开(加一条白线)
    }
    return str;
}

void Code39::InitMap()
{
    map.insert("A","110101001011");
    map.insert("B","101101001011");
    map.insert("C","110110100101");
    map.insert("D","101011001011");
    map.insert("E","110101100101");
    map.insert("F","101101100101");
    map.insert("G","101010011011");
    map.insert("H","110101001101");
    map.insert("I","101101001101");
    map.insert("J","101011001101");
    map.insert("K","110101010011");
    map.insert("L","101101010011");
    map.insert("M","110110101001");
    map.insert("N","101011010011");
    map.insert("O","110101101001");
    map.insert("P","101101101001");
    map.insert("Q","101010110011");
    map.insert("R","110101011001");
    map.insert("S","101101011001");
    map.insert("T","101011011001");
    map.insert("U","110010101011");
    map.insert("V","100110101011");
    map.insert("W","110011010101");
    map.insert("X","100101101011");
    map.insert("Y","110010110101");
    map.insert("Z","100110110101");
    map.insert("0","101001101101");
    map.insert("1","110100101011");
    map.insert("2","101100101011");
    map.insert("3","110110010101");
    map.insert("4","101001101011");
    map.insert("5","110100110101");
    map.insert("6","101100110101");
    map.insert("7","101001011011");
    map.insert("8","110100101101");
    map.insert("9","101100101101");
    map.insert("+","100101001001");
    map.insert("-","100101011011");
    map.insert("*","100101101101");
    map.insert("/","100100101001");
    map.insert("%","101001001001");
    map.insert("$","100100100101");
    map.insert(".","110010101101");
    map.insert(" ","100110101101");
}


对照表

39码的字符编码方式

(一) 英文字母部分

字符

逻辑型态

字符

逻辑型态

A

110101001011

N

101011010011

B

101101001011

O

110101101001

C

110110100101

P

101101101001

D

101011001011

Q

101010110011

E

110101100101

R

110101011001

F

101101100101

S

101101011001

G

101010011011

T

101011011001

H

110101001101

U

110010101011

I

101101001101

V

100110101011

J

101011001101

W

110011010101

K

110101010011

X

100101101011

L

101101010011

Y

110010110101

M

110110101001

Z

100110110101

(二) 数字与特殊符号部分

字符

逻辑型态

字符

逻辑型态

0

101001101101

100101001001

1

110100101011

100101011011

2

101100101011

100101101101

3

110110010101

100100101001

4

101001101011

101001001001

5

110100110101

100100100101

6

101100110101

110010101101

7

101001011011

空白

100110101101

8

110100101101

9

101100101101


猜你喜欢

转载自blog.csdn.net/shihoongbo/article/details/49330331