zxing 设置条码两边空白(EncodeHintType.MARGIN)无效的分析,有需要的朋友可以参考下。
最新项目需要使用到 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 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 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,代表空,此处测试内容为 901001C005,生成的一维数组长度为 134,
*/
}
}
看到 encode(String contents,BarcodeFormat......)调用了其父类的 encode 函数,继续追踪。
4、
@Override
public BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map 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 会采用这种策略,就是为了保持条码中的比例,以免失真出现无法识别的条码矩阵。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于