最新项目需要使用到zxing生成条码,条码格式为CODE128,CODE128的规则可参考:点击打开链接。
当调用
MultiFormatWriter().encode(str,BarcodeFormat.CODE_128, mwidth, mHeight, hints)
后生成的矩阵数据转换为Bitmap图时,会产生很大的左右两边的空白,zxing提供了 EncodeHintType.MARGIN选项来设置左右空白,很多时候无效,但偶然会成功,很困惑,所以追踪了一下代码,明白了其生成的原理,现以源码解析的方式整理如下。
此处,以类似“901001*C005”格式的内容来分析,编码格式为CODE128。
1、
BitMatrix matrix = new MultiFormatWriter().encode(str,BarcodeFormat.CODE_128, mwidth, mHeight, hints);
此处没什么讲的生成条码/二维码的核心代码,zxing使用方法可以去百度/谷歌。
2、追踪encode函数,其原型MultiFormatWriter.java中,代码如下:
public BitMatrix encode(String contents,
BarcodeFormat format,
int width, int height,
Map<EncodeHintType,?> hints) throws WriterException {
Writer writer;
switch (format) {
case EAN_8:
writer = new EAN8Writer();
break;
case EAN_13:
writer = new EAN13Writer();
break;
case UPC_A:
writer = new UPCAWriter();
break;
case QR_CODE:
writer = new QRCodeWriter();
break;
case CODE_39:
writer = new Code39Writer();
break;
case CODE_128:
writer = new Code128Writer();
break;
case ITF:
writer = new ITFWriter();
break;
case PDF_417:
writer = new PDF417Writer();
break;
case CODABAR:
writer = new CodaBarWriter();
break;
case DATA_MATRIX:
writer = new DataMatrixWriter();
break;
case AZTEC:
writer = new AztecWriter();
break;
default:
throw new IllegalArgumentException("No encoder available for format " + format);
}
return writer.encode(contents, format, width, height, hints);
}
可以看到其采用工厂模式,将任务分发到了各个实现类中,以本测试CODE128格式为例,其使用的是Code128Writer中的encode函数,接着追踪到该单元中。
3、
public final class Code128Writer extends OneDimensionalCodeWriter {
@Override
public BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map<EncodeHintType,?> hints) throws WriterException {
if (format != BarcodeFormat.CODE_128) {
throw new IllegalArgumentException("Can only encode CODE_128, but got " + format);
}
return super.encode(contents, format, width, height, hints);
}
@Override
public boolean[] encode(String contents) {
int length = contents.length();
/*
*省略,就是根据CODE128的规则,生成一个一维的数组,每个元素代表有效内容的条码表示,true代表“条”,false,代表空,此处测试内容为901001*C005,生成的一维数组长度为134,
*/
}
}
看到encode(String contents,BarcodeFormat......)调用了其父类的encode函数,继续追踪。
4、
@Override
public BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map<EncodeHintType,?> hints) throws WriterException {
if (contents.isEmpty()) {
throw new IllegalArgumentException("Found empty contents");
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Negative size is not allowed. Input: "
+ width + 'x' + height);
}
int sidesMargin = getDefaultMargin();
if (hints != null) {
Integer sidesMarginInt = (Integer) hints.get(EncodeHintType.MARGIN);
if (sidesMarginInt != null) {
sidesMargin = sidesMarginInt;
}
}
boolean[] code = encode(contents);
return renderResult(code, width, height, sidesMargin);
}
此处可以看到,我们设置的EncodeHintsType.MARGIN出现了,代码的具体含义就是,你要是设置了该值就取你设置的值,如果没有设置,去默认值(10),代码为:
public int getDefaultMargin() {
// CodaBar spec requires a side margin to be more than ten times wider than narrow space.
// This seems like a decent idea for a default for all formats.
return 10;
}
然后其其调用了encode(String contents),该函数在OneDimensionalCodeWriter类中声明为抽象方法,下放到子类中实现,此处就是标题3处Code128Writer类中实现的encode(String content)生成一个一维数组。
接下来关键的内容来,函数renderResult。在该函数中计算了生成的矩阵的大小,其有你传入的宽、生成的一维数组长度、你设置的MARGIN三个参数共同决定,对于一维条码,高(需大于1)并无大用。
5、
private static BitMatrix renderResult(boolean[] code, int width, int height, int sidesMargin) {
int inputWidth = code.length;
// Add quiet zone on both sides.
int fullWidth = inputWidth + sidesMargin;
int outputWidth = Math.max(width, fullWidth);
int outputHeight = Math.max(1, height);
int multiple = outputWidth / fullWidth;
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (code[inputX]) {
output.setRegion(outputX, 0, multiple, outputHeight);
}
}
return output;
}
首先,inputWidth即根据CODE128规则生成的一维数组的长度;此处为134
接着,fullWidth即实际内容宽度和你设置的空白宽度;假设,我设置MARGIN为1,此处fullwidth为135;
接下来,是获取输出矩阵的宽和高,用了取较大值的数学函数max;假设我们设置的需要的条码宽和高分别为:300和100,此处outputWidth为300,outputHeigth为100;
接着,关键代码,计算实际内容缩放的比例:multiple,此处可得300/135=2,;
然后,可计算真实的左右空白:leftPadding,此处为:(300 - 135*2)/2 = 15;
然后,就是根据计算好的宽高及空白,生成矩阵。
我们可以在拿另一个例子解析下,宽设置为270,MARGIN设置为0,故multiple=2,leftPading = 0;然后生成的条码没有空白,但我们其他参数不变,宽设置为260,得multiple = 1;leftPading = (260 - 134*1)/2 = 62,两边有很大的空白。
结束语,之所以,zxing会采用这种策略,就是为了保持条码中的比例,以免失真出现无法识别的条码矩阵。