2021SC@SDUSC

目录

  • 一、XXEncoder
    • 1.1 ASCIIEncoder
    • 1.2 C40Encoder
    • 1.3 TextEncoder
    • 1.4 X12Encoder
    • 1.5 EdifactEncoder
    • 1.6 Base256Encoder

前言:本篇博客继续上篇内容上篇内容,来分析Zxing中Data Matrix的代码,解释Data Matrix码encode是如何实现的。


一、XXEncoder

在上一篇博客中提到,DataMatrix编码的第一步骤是生成码字,需要将原始信息转换成DataMatrix的码字,生成的码字范围(0,255)即unsigned char。通常的编码方式为ASCII编码,将原始字符+1即生成码字;同时为了压缩码长,若其中含有连续的两位数字,则将其+130后,生成一个unsigned char。如果要进一步压缩码长,还可以混合其他的编码方式:

在介绍各个编码方式之前,先说明一下,它们都继承于同一个接口Encoder:

interface Encoder {int getEncodingMode(); void encode(EncoderContext context);
}

它们都有使用同一个final类EncoderContext:(相关set、get等函数已省略)

final class EncoderContext {private final String msg;private SymbolShapeHint shape;private Dimension minSize;private Dimension maxSize;private final StringBuilder codewords;int pos;private int newEncoding;private SymbolInfo symbolInfo;private int skipAtEnd;EncoderContext(String msg) {//  从这一点上讲,字符串不再是Unicode了byte[] msgBinary = msg.getBytes(StandardCharsets.ISO_8859_1);StringBuilder sb = new StringBuilder(msgBinary.length);for (int i = 0, c = msgBinary.length; i < c; i++) {char ch = (char) (msgBinary[i] & 0xff);if (ch == '?' && msg.charAt(i) != '?') {throw new IllegalArgumentException("Message contains characters outside ISO-8859-1 encoding.");}sb.append(ch);}this.msg = sb.toString(); // 这里不是Unicodeshape = SymbolShapeHint.FORCE_NONE;this.codewords = new StringBuilder(msg.length());newEncoding = -1;}public void updateSymbolInfo(int len) {if (this.symbolInfo == null || len > this.symbolInfo.getDataCapacity()) {this.symbolInfo = SymbolInfo.lookup(len, shape, minSize, maxSize, true);}}public void resetSymbolInfo() {this.symbolInfo = null;}
}

1.1 ASCIIEncoder

ASCIIEncoder以ASCII编码方式编码Data Matrix二维码。
其中,encode里的switch部分用来切换编码方式,当编码不是本类的方式时,每种编码方式都会转到ASCIIEncoder来进行切换。切换代码如下(在后面的介绍中将省略此部分)

if (newMode != getEncodingMode()) {// 返回到ASCII编码,进行切换到新模式context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);break;}

ASCIIEncoder详细代码如下:

final class ASCIIEncoder implements Encoder {@Overridepublic void encode(EncoderContext context) {//step Bint n = HighLevelEncoder.determineConsecutiveDigitCount(context.getMessage(), context.pos);if (n >= 2) {context.writeCodeword(encodeASCIIDigits(context.getMessage().charAt(context.pos),context.getMessage().charAt(context.pos + 1)));context.pos += 2;} else {char c = context.getCurrentChar();int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());if (newMode != getEncodingMode()) {switch (newMode) {case HighLevelEncoder.BASE256_ENCODATION:context.writeCodeword(HighLevelEncoder.LATCH_TO_BASE256);context.signalEncoderChange(HighLevelEncoder.BASE256_ENCODATION);return;case HighLevelEncoder.C40_ENCODATION:context.writeCodeword(HighLevelEncoder.LATCH_TO_C40);context.signalEncoderChange(HighLevelEncoder.C40_ENCODATION);return;case HighLevelEncoder.X12_ENCODATION:context.writeCodeword(HighLevelEncoder.LATCH_TO_ANSIX12);context.signalEncoderChange(HighLevelEncoder.X12_ENCODATION);break;case HighLevelEncoder.TEXT_ENCODATION:context.writeCodeword(HighLevelEncoder.LATCH_TO_TEXT);context.signalEncoderChange(HighLevelEncoder.TEXT_ENCODATION);break;case HighLevelEncoder.EDIFACT_ENCODATION:context.writeCodeword(HighLevelEncoder.LATCH_TO_EDIFACT);context.signalEncoderChange(HighLevelEncoder.EDIFACT_ENCODATION);break;default:throw new IllegalStateException("Illegal mode: " + newMode);}} else if (HighLevelEncoder.isExtendedASCII(c)) {context.writeCodeword(HighLevelEncoder.UPPER_SHIFT);context.writeCodeword((char) (c - 128 + 1));context.pos++;} else {context.writeCodeword((char) (c + 1));context.pos++;}}}private static char encodeASCIIDigits(char digit1, char digit2) {if (HighLevelEncoder.isDigit(digit1) && HighLevelEncoder.isDigit(digit2)) {int num = (digit1 - 48) * 10 + (digit2 - 48);return (char) (num + 130);}throw new IllegalArgumentException("not digits: " + digit1 + digit2);}}

1.2 C40Encoder

C40Encoder以C40编码方式编码Data Matrix二维码。

class C40Encoder implements Encoder {@Overridepublic void encode(EncoderContext context) {//step CStringBuilder buffer = new StringBuilder();while (context.hasMoreCharacters()) {char c = context.getCurrentChar();context.pos++;int lastCharSize = encodeChar(c, buffer);int unwritten = (buffer.length() / 3) * 2;int curCodewordCount = context.getCodewordCount() + unwritten;context.updateSymbolInfo(curCodewordCount);int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;if (!context.hasMoreCharacters()) {// 避免在最后三元组中使用单个C40值StringBuilder removed = new StringBuilder();if ((buffer.length() % 3) == 2 && available != 2) {lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);}while ((buffer.length() % 3) == 1 && (lastCharSize > 3 || available != 1)) {lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);}break;}int count = buffer.length();if ((count % 3) == 0) {int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());}}handleEOD(context, buffer);}private int backtrackOneCharacter(EncoderContext context,StringBuilder buffer, StringBuilder removed, int lastCharSize) {int count = buffer.length();buffer.delete(count - lastCharSize, count);context.pos--;char c = context.getCurrentChar();lastCharSize = encodeChar(c, removed);context.resetSymbolInfo(); // 处理符号大小可能减少的问题return lastCharSize;}static void writeNextTriplet(EncoderContext context, StringBuilder buffer) {context.writeCodewords(encodeToCodewords(buffer));buffer.delete(0, 3);}private static String encodeToCodewords(CharSequence sb) {int v = (1600 * sb.charAt(0)) + (40 * sb.charAt(1)) + sb.charAt(2) + 1;char cw1 = (char) (v / 256);char cw2 = (char) (v % 256);return new String(new char[] {cw1, cw2});}}

其中,有encodeChar和handleEOD方法,子类会对其进行重写。

int encodeChar(char c, StringBuilder sb) {if (c == ' ') {sb.append('\3');return 1;}if (c >= '0' && c <= '9') {sb.append((char) (c - 48 + 4));return 1;}if (c >= 'A' && c <= 'Z') {sb.append((char) (c - 65 + 14));return 1;}if (c < ' ') {sb.append('\0'); //Shift 1 Setsb.append(c);return 2;}if (c <= '/') {sb.append('\1'); //Shift 2 Setsb.append((char) (c - 33));return 2;}if (c <= '@') {sb.append('\1'); //Shift 2 Setsb.append((char) (c - 58 + 15));return 2;}if (c <= '_') {sb.append('\1'); //Shift 2 Setsb.append((char) (c - 91 + 22));return 2;}if (c <= 127) {sb.append('\2'); //Shift 3 Setsb.append((char) (c - 96));return 2;}sb.append("\1\u001e"); //Shift 2, Upper Shiftint len = 2;len += encodeChar((char) (c - 128), sb);return len;}

handleEOD用来处理“数据结束”情况。
其中,context为编码器上下文,
buffer为使用剩余的编码字符创建缓冲区。

  void handleEOD(EncoderContext context, StringBuilder buffer) {int unwritten = (buffer.length() / 3) * 2;int rest = buffer.length() % 3;int curCodewordCount = context.getCodewordCount() + unwritten;context.updateSymbolInfo(curCodewordCount);int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;if (rest == 2) {buffer.append('\0'); //Shift 1while (buffer.length() >= 3) {writeNextTriplet(context, buffer);}if (context.hasMoreCharacters()) {context.writeCodeword(HighLevelEncoder.C40_UNLATCH);}} else if (available == 1 && rest == 1) {while (buffer.length() >= 3) {writeNextTriplet(context, buffer);}if (context.hasMoreCharacters()) {context.writeCodeword(HighLevelEncoder.C40_UNLATCH);}// else no unlatch 否则就没有解锁context.pos--;} else if (rest == 0) {while (buffer.length() >= 3) {writeNextTriplet(context, buffer);}if (available > 0 || context.hasMoreCharacters()) {context.writeCodeword(HighLevelEncoder.C40_UNLATCH);}} else {throw new IllegalStateException("Unexpected case. Please report!");}context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);}

1.3 TextEncoder

TextEncoder以Text编码方式编码Data Matrix二维码。与其他编码不同的是,它继承自C40Encoder,重写了encodeChar方法。

final class TextEncoder extends C40Encoder {@Overrideint encodeChar(char c, StringBuilder sb) {if (c == ' ') {sb.append('\3');return 1;}if (c >= '0' && c <= '9') {sb.append((char) (c - 48 + 4));return 1;}if (c >= 'a' && c <= 'z') {sb.append((char) (c - 97 + 14));return 1;}if (c < ' ') {sb.append('\0'); //Shift 1 Setsb.append(c);return 2;}if (c <= '/') {sb.append('\1'); //Shift 2 Setsb.append((char) (c - 33));return 2;}if (c <= '@') {sb.append('\1'); //Shift 2 Setsb.append((char) (c - 58 + 15));return 2;}if (c >= '[' && c <= '_') {sb.append('\1'); //Shift 2 Setsb.append((char) (c - 91 + 22));return 2;}if (c == '`') {sb.append('\2'); //Shift 3 Setsb.append((char) 0); // '`' - 96 == 0return 2;}if (c <= 'Z') {sb.append('\2'); //Shift 3 Setsb.append((char) (c - 65 + 1));return 2;}if (c <= 127) {sb.append('\2'); //Shift 3 Setsb.append((char) (c - 123 + 27));return 2;}sb.append("\1\u001e"); //Shift 2, Upper Shiftint len = 2;len += encodeChar((char) (c - 128), sb);return len;}}

1.4 X12Encoder

X12Encoder以X12编码方式编码Data Matrix二维码。与TextEncoder相同,它继承自C40Encoder。

final class X12Encoder extends C40Encoder {@Overridepublic void encode(EncoderContext context) {//step CStringBuilder buffer = new StringBuilder();while (context.hasMoreCharacters()) {char c = context.getCurrentChar();context.pos++;encodeChar(c, buffer);int count = buffer.length();if ((count % 3) == 0) {writeNextTriplet(context, buffer);int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());}}handleEOD(context, buffer);}@Overrideint encodeChar(char c, StringBuilder sb) {switch (c) {case '\r':sb.append('\0');break;case '*':sb.append('\1');break;case '>':sb.append('\2');break;case ' ':sb.append('\3');break;default:if (c >= '0' && c <= '9') {sb.append((char) (c - 48 + 4));} else if (c >= 'A' && c <= 'Z') {sb.append((char) (c - 65 + 14));} else {HighLevelEncoder.illegalCharacter(c);}break;}return 1;}@Overridevoid handleEOD(EncoderContext context, StringBuilder buffer) {context.updateSymbolInfo();int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();int count = buffer.length();context.pos -= count;if (context.getRemainingCharacters() > 1 || available > 1 ||context.getRemainingCharacters() != available) {context.writeCodeword(HighLevelEncoder.X12_UNLATCH);}if (context.getNewEncoding() < 0) {context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);}}
}

1.5 EdifactEncoder

EdifactEncoder以Edifact编码方式编码Data Matrix二维码。

final class EdifactEncoder implements Encoder {@Overridepublic void encode(EncoderContext context) {//step FStringBuilder buffer = new StringBuilder();while (context.hasMoreCharacters()) {char c = context.getCurrentChar();encodeChar(c, buffer);context.pos++;int count = buffer.length();if (count >= 4) {context.writeCodewords(encodeToCodewords(buffer));buffer.delete(0, 4);int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());}}buffer.append((char) 31); //解锁handleEOD(context, buffer);}// 处理“数据结束”情况// context:编码器上下文// buffer: 使用剩余的编码字符创建缓冲区private static void handleEOD(EncoderContext context, CharSequence buffer) {try {int count = buffer.length();if (count == 0) {return; // 完成}if (count == 1) {// 最后只有一个解锁context.updateSymbolInfo();int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();int remaining = context.getRemainingCharacters();// 以下两行代码是一个灵感来源于https://sourceforge.net/p/barcode4j/svn/221/if (remaining > available) {context.updateSymbolInfo(context.getCodewordCount() + 1);available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();}if (remaining <= available && available <= 2) {return; // 不解锁}}if (count > 4) {throw new IllegalStateException("Count must not exceed 4");}int restChars = count - 1;String encoded = encodeToCodewords(buffer);boolean endOfSymbolReached = !context.hasMoreCharacters();boolean restInAscii = endOfSymbolReached && restChars <= 2;if (restChars <= 2) {context.updateSymbolInfo(context.getCodewordCount() + restChars);int available = context.getSymbolInfo().getDataCapacity() - context.getCodewordCount();if (available >= 3) {restInAscii = false;context.updateSymbolInfo(context.getCodewordCount() + encoded.length());}}if (restInAscii) {context.resetSymbolInfo();context.pos -= restChars;} else {context.writeCodewords(encoded);}} finally {context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);}}private static void encodeChar(char c, StringBuilder sb) {if (c >= ' ' && c <= '?') {sb.append(c);} else if (c >= '@' && c <= '^') {sb.append((char) (c - 64));} else {HighLevelEncoder.illegalCharacter(c);}}private static String encodeToCodewords(CharSequence sb) {int len = sb.length();if (len == 0) {throw new IllegalStateException("StringBuilder must not be empty");}char c1 = sb.charAt(0);char c2 = len >= 2 ? sb.charAt(1) : 0;char c3 = len >= 3 ? sb.charAt(2) : 0;char c4 = len >= 4 ? sb.charAt(3) : 0;int v = (c1 << 18) + (c2 << 12) + (c3 << 6) + c4;char cw1 = (char) ((v >> 16) & 255);char cw2 = (char) ((v >> 8) & 255);char cw3 = (char) (v & 255);StringBuilder res = new StringBuilder(3);res.append(cw1);if (len >= 2) {res.append(cw2);}if (len >= 3) {res.append(cw3);}return res.toString();}}

1.6 Base256Encoder

Base256Encoder以Base256编码方式编码Data Matrix二维码。

final class Base256Encoder implements Encoder {@Overridepublic void encode(EncoderContext context) {StringBuilder buffer = new StringBuilder();buffer.append('\0'); //Initialize length fieldwhile (context.hasMoreCharacters()) {char c = context.getCurrentChar();buffer.append(c);context.pos++;int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());}int dataCount = buffer.length() - 1;int lengthFieldSize = 1;int currentSize = context.getCodewordCount() + dataCount + lengthFieldSize;context.updateSymbolInfo(currentSize);boolean mustPad = (context.getSymbolInfo().getDataCapacity() - currentSize) > 0;if (context.hasMoreCharacters() || mustPad) {if (dataCount <= 249) {buffer.setCharAt(0, (char) dataCount);} else if (dataCount <= 1555) {buffer.setCharAt(0, (char) ((dataCount / 250) + 249));buffer.insert(1, (char) (dataCount % 250));} else {throw new IllegalStateException("Message length not in valid ranges: " + dataCount);}}for (int i = 0, c = buffer.length(); i < c; i++) {context.writeCodeword(randomize255State(buffer.charAt(i), context.getCodewordCount() + 1));}}private static char randomize255State(char ch, int codewordPosition) {int pseudoRandom = ((149 * codewordPosition) % 255) + 1;int tempVariable = ch + pseudoRandom;if (tempVariable <= 255) {return (char) tempVariable;} else {return (char) (tempVariable - 256);}}}

2021SC@SDUSC Zxing开源代码(十一)Data Matrix二维码(四)相关推荐

  1. Data Matrix 二维码解码库 libdmtx 编译方法

    Data Matrix 二维码解码库 libdmtx 编译方法 libdmtx 是一个开源的 Data Matrix 编解码库.项目主页在:https://github.com/dmtx 这里简单记录 ...

  2. 基于机器视觉的Data Matrix二维码识别

    基于机器视觉的Data Matrix二维码识别 二维码识别,这个在视觉应用中占有很重要的比例,各种各样的二维码都有可能需要识别.常见的QR码.Data Matrix码.本方案是识别Data Matri ...

  3. 2021SC@SDUSC Zxing开源代码(二)CaptureActivity代码分析-生命周期函数

    文章目录 前言 一.Android基础知识 1. Android 基本组件 2. SurfaceView .Surface与SurfaceHolder 3. Activity 生命周期 4. Andr ...

  4. 2021SC@SDUSC Zxing开源代码(十七)Zxing代码解析——一维码

    2021SC@SDUSC 目录 Code 39 Code 93 Code 128 Codabar ITF 参考资料 前言:本篇博客主要介绍一维码. Code 39 Code39是条形码的一种,也被称为 ...

  5. Zxing图片识别 从相册选二维码图片解析总结

    Zxing图片识别 从相册选取二维码图片进行解析总结 在Zxing扫描识别和图片识别的解析对象是相同的 本文分三个步骤: 1 获取相册的照片 2 解析二维码图片 3 返回结果 1) 获取相册照片 go ...

  6. C# 利用ZXing.Net来生成条形码和二维码

    本文是利用ZXing.Net在WinForm中生成条形码,二维码的小例子,仅供学习分享使用,如有不足之处,还请指正. 什么是ZXing.Net? ZXing是一个开放源码的,用Java实现的多种格式的 ...

  7. Android 基于google Zxing实现对手机中的二维码进行扫描

    转载请注明出处:http://blog.csdn.net/xiaanming/article/details/14450809 我之前写了一篇关于google Zxing扫描二维码的文章,效果是仿微信 ...

  8. 开发一个基于ZXing库以及安卓Studio的二维码扫描小程序(二)

    开发一个基于ZXing库以及安卓Studio的二维码扫描小程序(二) 下面我们做一个ZXing扫描二维码的例子,是通过安卓库的方式引用ZXing应用代码. 开发步骤 建立一个安卓工程(Project) ...

  9. 【QR Code Generator】开源免费响应式QRcdr二维码生成网站源码

    介绍: 开源免费响应式QRcdr二维码,一款基于PHP编写的二维码在线生成系统,只需点击几下就可以生成您的个人二维码,上传您的徽标或水印,选择自定义颜色,生成多种类型,选择一个图案并下载最终的二维码图 ...

  10. 用11行Python代码,实现动态二维码制作

    用11行Python代码,实现动态二维码制作 效果展示 Python实现 素材 其他工具 效果展示 Python实现 要预装myqr模块才行. windows键+R 打开cmd,输入pip insta ...

最新文章

  1. Linux运维人员成长之路学习书籍推荐
  2. 神策数据荣登 2020 IDC 中国 Fintech 50 强榜单
  3. 字符数据类型转换代码
  4. 关于多线程编程您不知道的 5 件事 有关高性能线程处理的微妙之处
  5. pandas后几行_天秀!Pandas还能用来写爬虫?
  6. balenaEtcher-1.5.70可能是最好用的镜像写U盘工具 img to usb dmg to usb支持多种格式内附截图介绍多平台均有
  7. font-family:中文字体的英文名称
  8. 根域名服务器作用,域名系统的主要功能是什么?域名系统中的本地域名服务器、根域名服务器、顶级域名 - 问答库...
  9. 百练_1664:放苹果_递归
  10. Mac突然连不上WiFi解决步骤
  11. SDN控制器Floodlight源码学习(五)--控制器和交换机交互(3)
  12. 开机提示:one of your disks needs to be checked解决方法
  13. 单例模式、适配器模式
  14. python 对excel的函数操作_自动化报表(3)
  15. 《我不是药神》——生如夏花
  16. 形状类似小于等于号的符号是啥
  17. 微信小程序图片验证组件封装
  18. 极兔快递单号查询API
  19. Centos开机启动项
  20. 操作系统原理学习-概述

热门文章

  1. gcc: libc: __ASSEMBLER__; Kernel: __ASSEMBLY__
  2. OFDM通信链路仿真加噪声方法总结
  3. oracle如何判断节假日,oracle function 用于判断是否为节假日
  4. java gc 监控_JAVA网站full GC监控脚本
  5. selenium实例自动登陆知乎
  6. C语言 · 求arccos值
  7. 一本通 1194:移动路线
  8. 洛谷 P1194 买礼物 题解
  9. python中如何判断词性_python进行词性分析
  10. CRM客户管理系统-SSM框架项目实战教程