条码是一种以机器可读的可视形式表示数据的方法。条码种类繁多,主要分为一维码和二维码。
我们可以从条码获取二进制数据,并通过不同方法去读码。例如,在ean-13中,位于其右边的代码1110010代表数字0。而qr码,它是二维的,可以存储更多的数据,有几种表示数据的模式:
| 输入模式 | 模式标识 | 最大字符数 | 允许的字符,默认编码 |
|---|---|---|---|
| 仅数字 | 1 | 7,089 | 0、1、2、3、4、5、6、7、8、9 |
| 字母与数字 | 2 | 4,296 | 0-9、a-z(仅限大写)、空格、$、%、*、+、-、..、/、: |
| 二进制/字节 | 4 | 2,953 | iso/iec 8859-1 |
| 日语汉字/假名 | 8 | 1,817 | shift jis x 0208 |
| 结构化追加 | 3 | 无限 | 没有限定 |
ps:结构化追加(structured append)是一种将数据分到多个条码的模式。
在本文中,我们将创建一个用于读取条码二进制数据的javascript库,并重点处理qr码,因为这种码型有多种模式。
dynamsoft barcode reader会被用于读取条码。
新建项目
使用vite创建一个新的typescript项目:
npm create vite@latest barcodedatareader -- --template vanilla-ts
编写定义
为读取条码的二进制数据定义几个接口和一个枚举。
定义barcodedetail接口:
export interface barcodedetail {
mode?:number; //the data encoding mode
model?:number; //number of models
errorcorrectionlevel?:number;
columns?:number; //the column count
rows?:number; //the row count
page?:number; //position of the particular code in structured append codes
totalpage?:number; //total number of structured append codes
paritydata?:number; //a value obtained by xoring byte by byte the ascii/jis values of all the original input data before division into symbol blocks.
version?:number; //the version of the code.
}
定义barcode接口:
export interface barcode {
bytes:uint8array;
details:barcodedetail;
barcodetype?:string;
}
二进制数据存储为uint8array。
定义readingresult接口:
export interface readingresult {
text?:string;
img?:htmlimageelement;
blob?:blob;
}
我们可以得到纯文本、html图像元素或blob这三种类型的结果。
类型需要在datatype中指定。
export enum datatype {
text = 0,
image = 1,
unknown = 2
}
创建一个读取二进制数据的类
使用以下模板创建一个新的barcodedatareader.ts文件:
export class barcodedatareader{
async read(barcodes:barcode[],datatype:datatype):promise<readingresult[]>{
if (barcodes.length == 0) {
throw new error("no barcodes given");
}
let results:readingresult[] = [];
return results;
}
}
根据不同模式读取二进制数据。
async read(barcodes:barcode[],datatype:datatype):promise<readingresult[]>{
if (barcodes.length == 0) {
throw new error("no barcodes given");
}
let results:readingresult[] = [];
const mode = barcodes[0].details.mode;
if (mode == 1) {
results = this.readnumericbarcodes(barcodes);
}else if (mode == 2) {
results = this.readalphanumericbarcodes(barcodes);
}else if (mode == 4) {
results = await this.readbyteencodingbarcodes(barcodes,datatype);
}else if (mode == 8) {
results = this.readkanjibarcodes(barcodes);
}else if (mode == 3) {
results = await this.readstructuredappendbarcodes(barcodes,datatype);
}else {
results = await this.readbyteencodingbarcodes(barcodes,datatype.text);
}
return results;
}
实现对数字、字母与数字和日文汉字模式的读取。这三种模式的数据都是纯文本,它们可以共享类似的逻辑。
private readalphanumericbarcodes(barcodes:barcode[]):readingresult[]{
return this.decodetext(barcodes,"ascii");
}
private readnumericbarcodes(barcodes:barcode[]):readingresult[]{
return this.decodetext(barcodes,"ascii");
}
private readkanjibarcodes(barcodes:barcode[]):readingresult[]{
return this.decodetext(barcodes,"shift-jis");
}
private decodetext(barcodes:barcode[],encoding:string){
let results:readingresult[] = [];
for (let index = 0; index < barcodes.length; index++) {
const barcode = barcodes[index];
const decoder = new textdecoder(encoding);
const text = decoder.decode(barcode.bytes);
let result = {
text:text
}
results.push(result);
}
return results;
}
以字节模式读取数据。
由于字节模式下的数据类型未知,我们需要根据用户指定的数据类型获取读取结果。
如果数据类型是文本,我们需要检测编码。这里使用chardet库进行检测。
private async readbyteencodingbarcodes(barcodes:barcode[],datatype:datatype):promise<readingresult[]>{
let results:readingresult[] = [];
for (let index = 0; index < barcodes.length; index++) {
const barcode = barcodes[index];
let result:readingresult = await this.getresultbasedondatatype(barcode.bytes,datatype);
results.push(result);
}
return results;
}
async getresultbasedondatatype(data:uint8array,datatype:datatype) {
let result:readingresult;
if (datatype == datatype.text) {
const charset = chardet.analyse(data);
const decoder = new textdecoder(charset[0].name);
const text = decoder.decode(data);
result = {
text:text
}
}else if (datatype == datatype.image) {
const img = await this.getimagefromuint8array(data);
result = {
img:img
}
}else{
result = {
blob:this.getblobfromuint8array(data)
}
}
return result;
}
getblobfromuint8array(data:uint8array) {
return new blob([data]);
}
getimagefromuint8array(data:uint8array):promise<htmlimageelement>{
return new promise<htmlimageelement>((resolve, reject) => {
const img = document.createelement("img");
const blob = this.getblobfromuint8array(data);
img.onload = function(){
resolve(img);
}
img.onerror = function(error) {
console.error(error);
reject(error);
}
img.src = url.createobjecturl(blob);
console.log(img.src)
})
}
以结构化追加模式读取数据。
需要根据页码对条码进行排序,将多个码的数据合并成一个unit8array,然后得到结果。
private async readstructuredappendbarcodes(barcodes:barcode[],datatype:datatype):promise<readingresult[]>{
let results:readingresult[] = [];
barcodes.sort((a, b) => (a.details.page ?? 0) - (b.details.page ?? 0))
let concateddata:uint8array = new uint8array();
for (let index = 0; index < barcodes.length; index++) {
const barcode = barcodes[index];
let merged = new uint8array(barcode.bytes.length + concateddata.length);
merged.set(concateddata);
merged.set(barcode.bytes, concateddata.length);
concateddata = merged;
}
let result = await this.getresultbasedondatatype(concateddata,datatype);
results.push(result);
return results;
}
读取测试
然后,我们可以更新index.html,使用dynamsoft barcode reader读取qr码,并使用我们编写的库读取二进制数据。
以下是基本代码:
let router = await dynamsoft.cvr.capturevisionrouter.createinstance();
//read barcodes from an image
let result = await router.capture(document.getelementbyid("image"),"readbarcodes_balance");
let datatype = document.getelementbyid("datatypeselect").selectedindex;
let barcodes = [];
for (let index = 0; index < result.items.length; index++) {
const item = result.items[index];
if (item.type === dynamsoft.core.enumcapturedresultitemtype.crit_barcode) {
let data = new uint8array(item.bytes.length);
data.set(item.bytes);
let barcode = {
bytes:data,
details:item.details
}
barcodes.push(barcode);
}
}
let results = await datareader.read(barcodes,datatype)
console.log(results);
可以访问在线演示进行试用。
如果手头没有二维码,可以使用此文中的二维码和二维码生成器。
下面是读取两个二维码的测试截图,一张图片被编码在这两个二维码中:

打包为库
为了便于使用,我们可以将其作为库发布到npm上。
安装devdependencies :
npm install -d @types/node vite-plugin-dts
创建一个新的vite.config.ts文件:
// vite.config.ts
import { resolve } from 'path';
import { defineconfig } from 'vite';
import dts from 'vite-plugin-dts';
// https://vitejs.dev/guide/build.html#library-mode
export default defineconfig({
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'barcode-data-reader',
filename: 'barcode-data-reader',
},
},
plugins: [dts()],
});
将我们的包的入口点添加到package.json。
{
"main": "./dist/barcode-data-reader.umd.cjs",
"module": "./dist/barcode-data-reader.js",
"types": "./dist/index.d.ts",
"exports": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/barcode-data-reader.js"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/barcode-data-reader.umd.cjs"
}
},
"files": [
"dist/*.css",
"dist/*.js",
"dist/*.cjs",
"dist/*.d.ts"
]
}
运行npm run build。然后,我们可以在dist文件夹中看到打包好的文件。
源代码
欢迎下载源代码并尝试使用:
github.com/tony-xlh/barcode-data-reader
以上就是javascript实现读取条码中的二进制数据的详细内容,更多关于javascript读取条码二进制数据的资料请关注代码网其它相关文章!
发表评论