简介
Ø Code39介绍
Ø Code Table
Ø 示例
Code39介绍
1. Code 39是一种条形码的编码标准。它的码表构成由26个大写英文字母(A-Z)和10个数字符号(0-9)以及7个特殊字符(-.sp*/+%)组成。
2. 一个Code39条码由5个条码区4个空白区成共9个区域组成,每个区域只能是宽或窄,并且9个区域中总有3个宽的,这就是Code39名字的由来。
3. 通常使用*作为编码的起始与结束。并且编码的起始与终止前后需要一段干净的区域(通常比长度大0.1英寸)。
4. 通常使读码枪能顺利识别,其中“窄”条码的最小宽度为7.5mils(1 mil=1/1000英寸)或0.19毫米。
5. 如果“窄”条码的宽度大于20mils,则“宽“条码的宽度为“窄”条码的2-3倍;如果“窄”条码的宽度小于20mils,则“宽”条码的跨度为“窄”条码的2-2.2倍。
6. 条码的高度至少为条码总宽度的0.15倍或0.25英寸
条码总宽度计算公式
L = (C + 2)(3N + 6)X + (C + 1)I
L条码的总宽度,不包含“干净区域“
C条码中符号的个数
X其中“窄”条码的宽度
N“宽“条码与“窄”条码的比例
I分隔符号的宽度,通常为黑条间所夹的白色区域
Code39 Table
Char. | Pattern b s b s b s b s b | Char. | Pattern b s b s b s b s b | Char. | Pattern b s b s b s b s b | Char. | Pattern b s b s b s b s b |
0 | n n n w w n w n n | C | w n w n n w n n n | O | w n n n w n n w n | - | n w n n n n w n w |
1 | w n n w n n n n w | D | n n n n w w n n w | P | n n w n w n n w n | . | w w n n n n w n n |
2 | n n w w n n n n w | E | w n n n w w n n n | Q | n n n n n n w w w | SP | n w w n n n w n n |
3 | w n w w n n n n n | F | n n w n w w n n n | R | w n n n n n w w n | * | n w n n w n w n n |
4 | n n n w w n n n w | G | n n n n n w w n w | S | n n w n n n w w n | $ | n w n w n w n n n |
5 | w n n w w n n n n | H | w n n n n w w n n | T | n n n n w n w w n | / | n w n w n n n w n |
6 | n n w w w n n n n | I | n n w n n w w n n | U | w w n n n n n n w | + | n w n n n w n w n |
7 | n n n w n n w n w | J | n n n n w w w n n | V | n w w n n n n n w | % | n n n w n w n w n |
8 | w n n w n n w n n | K | w n n n n n n w w | W | w w w n n n n n n | | |
9 | n n w w n n w n n | L | n n w n n n n w w | X | n w n n w n n n w | | |
A | w n n n n w n n w | M | w n w n n n n w n | Y | w w n n w n n n n | | |
B | n n w n n w n n w | N | n n n n w n n w w | Z | n w w n w n n n n | | |
(b黑色,s白色,n“窄“,w宽) SP为空格
示例
说明:
当前条码打印基本都是用条码字库实现的。在实际操作中需要依赖字库,并且发现有的条码识别枪对其生成的条码识别率不高。
示例采用java语言编码实现,其中“宽“与“窄”采用GDI中的像素为单位且已黑白为条码颜色。其中生成一张底色为白色的图片,然后在上面化黑色的条码,然后进行偏移画下一个黑色的条码。生成示例*JOYCODES.COM* ![](http://www.joycodes.com/code39.jpg)
联系方式:joycsharp@hotmail.com 转载请注明出处。
package
com.css.aicmap.util.barcode;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
import
java.awt.image.BufferedImage;
import
java.awt.Graphics;
import
java.awt.Color;
import
java.awt.geom.AffineTransform;
import
java.awt.image.AffineTransformOp;
import
com.sun.image.codec.jpeg.JPEGImageEncoder;
import
com.sun.image.codec.jpeg.JPEGCodec;
import
java.io.OutputStream;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
import
com.sun.image.codec.jpeg.ImageFormatException;
import
java.io.IOException;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/** */
/**
* 符合BarCode 39规范的条码图像生成器
* @author ChenLiang & LiGuang
*/
public
class
BarCode39ImageBuilder
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
private static final int rate = 3; //条码宽条与窄条宽度之比
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private int m_nNarrowWidth; //窄条的宽度像素数
private int m_nImageHeight; //条码的高度像素数
private boolean m_bRotato; //输出的图像是否需要先旋转
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 根据strCodes传入的字符串,生成符合BarCode 39规范的JPEG输出流;
* @param nNarrowWidth
* @param nImageHeight
*/
public BarCode39ImageBuilder(int nNarrowWidth, int nImageHeight)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
m_nNarrowWidth = nNarrowWidth;
m_nImageHeight = nImageHeight;
m_bRotato = false;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 构造函数,默认窄条宽为4像素,条码高度是100像素;
*/
public BarCode39ImageBuilder()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this(4, 100);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 设置是否需要将结果图像在输出之前进行旋转;系统默认是不旋转
* @param b true:旋转,false:不旋转
*/
public void setRotato(boolean b)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
m_bRotato = b;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 生成相应的Bar Code图像,格式以jpeg格式的输出流;
* @param strCodes 要生成条码的字符串,注意该字符串需要包含首尾的两个星号
* @param out 接结果的输出流
* @throws IOException
* @throws ImageFormatException
* @throws IOException
*/
public void genImage(String strCodes, OutputStream out) throws ImageFormatException, IOException
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (null==strCodes || null==out || 0==strCodes.length())
return;
int nImageWidth = (strCodes.length() * (3 * rate + 7) * m_nNarrowWidth);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
BufferedImage bi = new BufferedImage(nImageWidth, m_nImageHeight, BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
g.setColor(Color.WHITE);
g.fillRect(0, 0, nImageWidth, m_nImageHeight);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
g.setColor(Color.BLACK);
int startx = 0;
for (int i = 0; i < strCodes.length(); i++)
startx = drawOneChar(g, startx, strCodes.charAt(i));
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (m_bRotato)
bi = flipX2Y(bi);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(bi);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 辅助方法,绘制一个字符
* @param g 画布
* @param x 起始位置
* @param ch 要绘制的字符
* @return 下一个要绘制字符的位置
*/
private int drawOneChar(Graphics g, int x, char ch)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
short sCode = getCharCode(ch);
for (int i = 0; i < 9; i++)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int width = m_nNarrowWidth;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (((0x100 >>> i) & sCode) != 0)
width *= rate;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if ((i & 0x1) == 0)
g.fillRect(x, 0, width, m_nImageHeight);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
x += width;
}
return x + m_nNarrowWidth;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 图像翻转,即X与Y方向互换
* @param in 源图像
* @return 翻转后的图像
*/
private BufferedImage flipX2Y(BufferedImage in)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
BufferedImage out = new BufferedImage(in.getHeight(), in.getWidth(), in.getType());
//请查看JDK
AffineTransform affineTransform = new AffineTransform(0, 1, 1, 0, 0, 0);
AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
return affineTransformOp.filter(in, out);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 原来旋转的版本,仅供参考
// private BufferedImage rotate(BufferedImage in)
// {
// int width = in.getWidth();
// int height = in.getHeight();
// BufferedImage out = new BufferedImage(height, width, in.getType());
//
// AffineTransform affineTransform = AffineTransform.getRotateInstance(Math.toRadians(90));
// affineTransform.translate(0, -height);
//
// AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform,
// AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
// affineTransformOp.filter(in, out);
// return out;
// }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
//Code39符号表
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private static final char[] m_chars =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '-', '.', ' ', '*', '$', '/', '+', '%' };
//Code39符号所对应的码表,16进制编码 n为0 w为1
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
private static final short[] m_codes =
{ 0x34, 0x121, 0x61, 0x160, 0x31, 0x130, 0x70, 0x25, 0x124, 0x64,
0x109, 0x49, 0x148, 0x19, 0x118, 0x58, 0xd, 0x10c, 0x4c, 0x1c, 0x103, 0x43, 0x142, 0x13, 0x112, 0x52,
0x7, 0x106, 0x46, 0x16, 0x181, 0xc1, 0x1c0, 0x91, 0x190, 0xd0, 0x85, 0x184, 0xc4, 0x94, 0xa8, 0xa2,
0x8a, 0x2a };
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/** *//**
* 通过码表符号获得码表字符
* @param ch 码表符号
* @return 码表值
*/
private static short getCharCode(char ch)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
for (int i = 0; i < m_chars.length; i++)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (ch == m_chars[i])
return m_codes[i];
}
return 0;
}
}