mirror of
https://github.com/cnwhy/QRCode-decode.git
synced 2026-08-12 20:31:40 +08:00
v0.0.5
This commit is contained in:
+25
-22
@@ -16,7 +16,7 @@
|
|||||||
<div></div>
|
<div></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
<script type="text/javascript" src="../dist/qr-decode.js"></script>
|
<script type="text/javascript" src="../dist/qr-decode.umd.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('file').onchange = function (event) {
|
document.getElementById('file').onchange = function (event) {
|
||||||
var el = event.target;
|
var el = event.target;
|
||||||
@@ -49,47 +49,50 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var video =document.getElementById('video');
|
var video = document.getElementById('video');
|
||||||
var videoBut =document.getElementById('videoBut');
|
var videoBut = document.getElementById('videoBut');
|
||||||
var xc;
|
var xc;
|
||||||
videoBut.onclick = videoEnable;
|
videoBut.onclick = videoEnable;
|
||||||
|
|
||||||
function videoEnable() {
|
function videoEnable() {
|
||||||
var URL = window.URL || window.webkitURL;
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||||
navigator.getUserMedia({
|
navigator.mediaDevices.getUserMedia({
|
||||||
video: true
|
video: true
|
||||||
}, function (stream) {
|
}).then(function (stream) {
|
||||||
video.src = URL.createObjectURL(stream);// 将获取到的视频流对象转换为地址
|
video.srcObject = stream; // 用 srcObject 替代已废弃的 URL.createObjectURL(stream)
|
||||||
video.oncanplay = function(){
|
video.oncanplay = function () {
|
||||||
videoPlay();
|
videoPlay();
|
||||||
video.width = video.videoWidth;
|
video.width = video.videoWidth;
|
||||||
video.height = video.videoHeight;
|
video.height = video.videoHeight;
|
||||||
}
|
}
|
||||||
}, function (error) {
|
}).catch(function (error) {
|
||||||
alert(error.name || error);
|
alert(error.name || error);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
alert('当前浏览器不支持摄像头访问');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoStop(){
|
function videoStop() {
|
||||||
clearInterval(xc);
|
clearInterval(xc);
|
||||||
video.pause();
|
video.pause();
|
||||||
videoBut.innerText = '启动';
|
videoBut.innerText = '启动';
|
||||||
videoBut.onclick = videoPlay;
|
videoBut.onclick = videoPlay;
|
||||||
}
|
}
|
||||||
function videoPlay(){
|
function videoPlay() {
|
||||||
video.play();
|
video.play();
|
||||||
videoBut.innerText = '停止';
|
videoBut.innerText = '停止';
|
||||||
xc = setInterval(function(){
|
xc = setInterval(function () {
|
||||||
try{
|
try {
|
||||||
var txt = qrDecode.decodeByDom(video);
|
var txt = qrDecode.decodeByDom(video);
|
||||||
var msg = document.createElement("div")
|
var msg = document.createElement("div")
|
||||||
msg.innerHTML = "识别到二维码: " + txt;
|
msg.innerHTML = "识别到二维码: " + txt;
|
||||||
document.body.appendChild(msg);
|
document.body.appendChild(msg);
|
||||||
videoStop();
|
videoStop();
|
||||||
}catch(err){
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
},300)
|
}, 300)
|
||||||
videoBut.onclick = videoStop;
|
videoBut.onclick = videoStop;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Vendored
+162
-5
@@ -1,5 +1,5 @@
|
|||||||
/*!
|
/*!
|
||||||
* qr-decode v0.0.4
|
* qr-decode v0.0.5
|
||||||
* (c) cnwhy <w.why@163.com>
|
* (c) cnwhy <w.why@163.com>
|
||||||
* Released under the ISC License.
|
* Released under the ISC License.
|
||||||
*/
|
*/
|
||||||
@@ -2432,6 +2432,130 @@ function decode(imageDate, debugfn) {
|
|||||||
};
|
};
|
||||||
return process(imageDate.data, base);
|
return process(imageDate.data, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ECI 声明中常见的字符集(QR 规范:ECI 编号 -> 字符集)
|
||||||
|
var ECI_CHARSET = {
|
||||||
|
3: 'iso-8859-1',
|
||||||
|
20: 'shift_jis',
|
||||||
|
25: 'utf-16be',
|
||||||
|
26: 'utf-8',
|
||||||
|
27: 'us-ascii'
|
||||||
|
};
|
||||||
|
|
||||||
|
// UTF-8 字节数组转字符串
|
||||||
|
var decodeUTF8 = function decodeUTF8(bytes) {
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
return new TextDecoder('utf-8').decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
var str = "";
|
||||||
|
var i = 0;
|
||||||
|
while (i < bytes.length) {
|
||||||
|
var code = bytes[i++] & 0xFF;
|
||||||
|
if (code < 0x80) {
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
} else if (code < 0xE0) {
|
||||||
|
str += String.fromCharCode((code & 0x1F) << 6 | bytes[i++] & 0x3F);
|
||||||
|
} else if (code < 0xF0) {
|
||||||
|
str += String.fromCharCode((code & 0x0F) << 12 | (bytes[i++] & 0x3F) << 6 | bytes[i++] & 0x3F);
|
||||||
|
} else {
|
||||||
|
var cp = (code & 0x07) << 18 | (bytes[i++] & 0x3F) << 12 | (bytes[i++] & 0x3F) << 6 | bytes[i++] & 0x3F;
|
||||||
|
cp -= 0x10000;
|
||||||
|
str += String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
// UTF-16 字节数组转字符串(isLE=false 时先把 BE 转成 LE 统一处理)
|
||||||
|
var decodeUTF16 = function decodeUTF16(bytes, isLE) {
|
||||||
|
if (!isLE) {
|
||||||
|
// UTF-16BE -> 交换字节序
|
||||||
|
for (var i = 0; i + 1 < bytes.length; i += 2) {
|
||||||
|
var t = bytes[i];
|
||||||
|
bytes[i] = bytes[i + 1];
|
||||||
|
bytes[i + 1] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
return new TextDecoder('utf-16le').decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
// 兜底:手写 UTF-16LE 解码(含代理对)
|
||||||
|
var str = "";
|
||||||
|
for (var i = 0; i + 1 < bytes.length; i += 2) {
|
||||||
|
var code = bytes[i] | bytes[i + 1] << 8;
|
||||||
|
if (code >= 0xD800 && code <= 0xDBFF && i + 3 < bytes.length) {
|
||||||
|
var low = bytes[i + 2] | bytes[i + 3] << 8;
|
||||||
|
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||||
|
str += String.fromCharCode(code, low);
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 8bit 字节段解码:按 ECI 声明 -> BOM -> 启发式检测 -> UTF-8 的顺序判断编码方式
|
||||||
|
var bytesToString = function bytesToString(bytes, eci) {
|
||||||
|
var len = bytes.length;
|
||||||
|
if (len == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 优先按 ECI 声明的字符集解码
|
||||||
|
if (eci && ECI_CHARSET[eci]) {
|
||||||
|
var charset = ECI_CHARSET[eci];
|
||||||
|
if (charset == 'utf-16be') {
|
||||||
|
return decodeUTF16(bytes, false);
|
||||||
|
}
|
||||||
|
if (charset == 'utf-8') {
|
||||||
|
return decodeUTF8(bytes);
|
||||||
|
}
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
try {
|
||||||
|
return new TextDecoder(charset).decode(new Uint8Array(bytes));
|
||||||
|
} catch (e) {
|
||||||
|
// 环境不支持该字符集时继续走下面的检测
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. BOM 检测
|
||||||
|
if (len >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
|
||||||
|
return decodeUTF16(bytes.slice(2), true); // UTF-16LE
|
||||||
|
}
|
||||||
|
if (len >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
|
||||||
|
return decodeUTF16(bytes.slice(2), false); // UTF-16BE
|
||||||
|
}
|
||||||
|
if (len >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
|
||||||
|
return decodeUTF8(bytes.slice(3)); // UTF-8 with BOM
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 无 BOM 时启发式检测 UTF-16(偶数长度且奇/偶位存在成片 0x00 字节)
|
||||||
|
if (len >= 4 && len % 2 == 0) {
|
||||||
|
var evenZeros = 0;
|
||||||
|
var oddZeros = 0;
|
||||||
|
for (var i = 0; i < len; i += 2) {
|
||||||
|
if (bytes[i] == 0) {
|
||||||
|
evenZeros++;
|
||||||
|
}
|
||||||
|
if (bytes[i + 1] == 0) {
|
||||||
|
oddZeros++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var half = len / 2;
|
||||||
|
if (oddZeros > half * 0.7) {
|
||||||
|
return decodeUTF16(bytes, true); // 奇数位大量 0x00 -> UTF-16LE
|
||||||
|
}
|
||||||
|
if (evenZeros > half * 0.7) {
|
||||||
|
return decodeUTF16(bytes, false); // 偶数位大量 0x00 -> UTF-16BE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 兜底默认 UTF-8
|
||||||
|
return decodeUTF8(bytes);
|
||||||
|
};
|
||||||
var process = function process(data, base) {
|
var process = function process(data, base) {
|
||||||
// var start = new Date().getTime();
|
// var start = new Date().getTime();
|
||||||
// var image = grayScaleToBitmap(grayscale(),base);
|
// var image = grayScaleToBitmap(grayscale(),base);
|
||||||
@@ -2453,11 +2577,44 @@ var process = function process(data, base) {
|
|||||||
|
|
||||||
// 解析QR矩阵
|
// 解析QR矩阵
|
||||||
var reader = Decoder_1.decode(qRCodeMatrix.bits);
|
var reader = Decoder_1.decode(qRCodeMatrix.bits);
|
||||||
// console.log(reader);
|
|
||||||
var data = reader.DataByte;
|
// 先读取编码方式(mode indicator),按编码方式将数据直接转成字符串
|
||||||
|
var MODE_NUMBER = 1;
|
||||||
|
var MODE_ROMAN_AND_NUMBER = 2;
|
||||||
|
var MODE_8BIT_BYTE = 4;
|
||||||
|
var MODE_ECI = 7;
|
||||||
|
var MODE_KANJI = 8;
|
||||||
var str = "";
|
var str = "";
|
||||||
for (var i = 0; i < data.length; i++) {
|
var eci = 0; // ECI 声明的字符集编号,作用于后续字节段
|
||||||
for (var j = 0; j < data[i].length; j++) str += String.fromCharCode(data[i][j]);
|
while (true) {
|
||||||
|
var mode = reader.NextMode();
|
||||||
|
if (mode == 0) break;
|
||||||
|
if (mode == MODE_ECI) {
|
||||||
|
eci = reader.parseECIValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var dataLength = reader.getDataLength(mode);
|
||||||
|
if (dataLength < 1) throw "Invalid data length: " + dataLength;
|
||||||
|
switch (mode) {
|
||||||
|
case MODE_NUMBER:
|
||||||
|
// 数字模式
|
||||||
|
str += reader.getFigureString(dataLength);
|
||||||
|
break;
|
||||||
|
case MODE_ROMAN_AND_NUMBER:
|
||||||
|
// 字母数字模式
|
||||||
|
str += reader.getRomanAndFigureString(dataLength);
|
||||||
|
break;
|
||||||
|
case MODE_8BIT_BYTE:
|
||||||
|
// 8bit 字节模式,先判断编码方式(ECI/BOM/启发式)再解码
|
||||||
|
str += bytesToString(reader.get8bitByteArray(dataLength), eci);
|
||||||
|
break;
|
||||||
|
case MODE_KANJI:
|
||||||
|
// 汉字(日文)模式
|
||||||
|
str += reader.getKanjiString(dataLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw "Invalid mode: " + mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// var end = new Date().getTime();
|
// var end = new Date().getTime();
|
||||||
|
|||||||
Vendored
+162
-5
@@ -1,5 +1,5 @@
|
|||||||
/*!
|
/*!
|
||||||
* qr-decode v0.0.4
|
* qr-decode v0.0.5
|
||||||
* (c) cnwhy <w.why@163.com>
|
* (c) cnwhy <w.why@163.com>
|
||||||
* Released under the ISC License.
|
* Released under the ISC License.
|
||||||
*/
|
*/
|
||||||
@@ -2438,6 +2438,130 @@
|
|||||||
};
|
};
|
||||||
return process(imageDate.data, base);
|
return process(imageDate.data, base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ECI 声明中常见的字符集(QR 规范:ECI 编号 -> 字符集)
|
||||||
|
var ECI_CHARSET = {
|
||||||
|
3: 'iso-8859-1',
|
||||||
|
20: 'shift_jis',
|
||||||
|
25: 'utf-16be',
|
||||||
|
26: 'utf-8',
|
||||||
|
27: 'us-ascii'
|
||||||
|
};
|
||||||
|
|
||||||
|
// UTF-8 字节数组转字符串
|
||||||
|
var decodeUTF8 = function decodeUTF8(bytes) {
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
return new TextDecoder('utf-8').decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
var str = "";
|
||||||
|
var i = 0;
|
||||||
|
while (i < bytes.length) {
|
||||||
|
var code = bytes[i++] & 0xFF;
|
||||||
|
if (code < 0x80) {
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
} else if (code < 0xE0) {
|
||||||
|
str += String.fromCharCode((code & 0x1F) << 6 | bytes[i++] & 0x3F);
|
||||||
|
} else if (code < 0xF0) {
|
||||||
|
str += String.fromCharCode((code & 0x0F) << 12 | (bytes[i++] & 0x3F) << 6 | bytes[i++] & 0x3F);
|
||||||
|
} else {
|
||||||
|
var cp = (code & 0x07) << 18 | (bytes[i++] & 0x3F) << 12 | (bytes[i++] & 0x3F) << 6 | bytes[i++] & 0x3F;
|
||||||
|
cp -= 0x10000;
|
||||||
|
str += String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
// UTF-16 字节数组转字符串(isLE=false 时先把 BE 转成 LE 统一处理)
|
||||||
|
var decodeUTF16 = function decodeUTF16(bytes, isLE) {
|
||||||
|
if (!isLE) {
|
||||||
|
// UTF-16BE -> 交换字节序
|
||||||
|
for (var i = 0; i + 1 < bytes.length; i += 2) {
|
||||||
|
var t = bytes[i];
|
||||||
|
bytes[i] = bytes[i + 1];
|
||||||
|
bytes[i + 1] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
return new TextDecoder('utf-16le').decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
// 兜底:手写 UTF-16LE 解码(含代理对)
|
||||||
|
var str = "";
|
||||||
|
for (var i = 0; i + 1 < bytes.length; i += 2) {
|
||||||
|
var code = bytes[i] | bytes[i + 1] << 8;
|
||||||
|
if (code >= 0xD800 && code <= 0xDBFF && i + 3 < bytes.length) {
|
||||||
|
var low = bytes[i + 2] | bytes[i + 3] << 8;
|
||||||
|
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||||
|
str += String.fromCharCode(code, low);
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 8bit 字节段解码:按 ECI 声明 -> BOM -> 启发式检测 -> UTF-8 的顺序判断编码方式
|
||||||
|
var bytesToString = function bytesToString(bytes, eci) {
|
||||||
|
var len = bytes.length;
|
||||||
|
if (len == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 优先按 ECI 声明的字符集解码
|
||||||
|
if (eci && ECI_CHARSET[eci]) {
|
||||||
|
var charset = ECI_CHARSET[eci];
|
||||||
|
if (charset == 'utf-16be') {
|
||||||
|
return decodeUTF16(bytes, false);
|
||||||
|
}
|
||||||
|
if (charset == 'utf-8') {
|
||||||
|
return decodeUTF8(bytes);
|
||||||
|
}
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
try {
|
||||||
|
return new TextDecoder(charset).decode(new Uint8Array(bytes));
|
||||||
|
} catch (e) {
|
||||||
|
// 环境不支持该字符集时继续走下面的检测
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. BOM 检测
|
||||||
|
if (len >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
|
||||||
|
return decodeUTF16(bytes.slice(2), true); // UTF-16LE
|
||||||
|
}
|
||||||
|
if (len >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
|
||||||
|
return decodeUTF16(bytes.slice(2), false); // UTF-16BE
|
||||||
|
}
|
||||||
|
if (len >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
|
||||||
|
return decodeUTF8(bytes.slice(3)); // UTF-8 with BOM
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 无 BOM 时启发式检测 UTF-16(偶数长度且奇/偶位存在成片 0x00 字节)
|
||||||
|
if (len >= 4 && len % 2 == 0) {
|
||||||
|
var evenZeros = 0;
|
||||||
|
var oddZeros = 0;
|
||||||
|
for (var i = 0; i < len; i += 2) {
|
||||||
|
if (bytes[i] == 0) {
|
||||||
|
evenZeros++;
|
||||||
|
}
|
||||||
|
if (bytes[i + 1] == 0) {
|
||||||
|
oddZeros++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var half = len / 2;
|
||||||
|
if (oddZeros > half * 0.7) {
|
||||||
|
return decodeUTF16(bytes, true); // 奇数位大量 0x00 -> UTF-16LE
|
||||||
|
}
|
||||||
|
if (evenZeros > half * 0.7) {
|
||||||
|
return decodeUTF16(bytes, false); // 偶数位大量 0x00 -> UTF-16BE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 兜底默认 UTF-8
|
||||||
|
return decodeUTF8(bytes);
|
||||||
|
};
|
||||||
var process = function process(data, base) {
|
var process = function process(data, base) {
|
||||||
// var start = new Date().getTime();
|
// var start = new Date().getTime();
|
||||||
// var image = grayScaleToBitmap(grayscale(),base);
|
// var image = grayScaleToBitmap(grayscale(),base);
|
||||||
@@ -2459,11 +2583,44 @@
|
|||||||
|
|
||||||
// 解析QR矩阵
|
// 解析QR矩阵
|
||||||
var reader = Decoder_1.decode(qRCodeMatrix.bits);
|
var reader = Decoder_1.decode(qRCodeMatrix.bits);
|
||||||
// console.log(reader);
|
|
||||||
var data = reader.DataByte;
|
// 先读取编码方式(mode indicator),按编码方式将数据直接转成字符串
|
||||||
|
var MODE_NUMBER = 1;
|
||||||
|
var MODE_ROMAN_AND_NUMBER = 2;
|
||||||
|
var MODE_8BIT_BYTE = 4;
|
||||||
|
var MODE_ECI = 7;
|
||||||
|
var MODE_KANJI = 8;
|
||||||
var str = "";
|
var str = "";
|
||||||
for (var i = 0; i < data.length; i++) {
|
var eci = 0; // ECI 声明的字符集编号,作用于后续字节段
|
||||||
for (var j = 0; j < data[i].length; j++) str += String.fromCharCode(data[i][j]);
|
while (true) {
|
||||||
|
var mode = reader.NextMode();
|
||||||
|
if (mode == 0) break;
|
||||||
|
if (mode == MODE_ECI) {
|
||||||
|
eci = reader.parseECIValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var dataLength = reader.getDataLength(mode);
|
||||||
|
if (dataLength < 1) throw "Invalid data length: " + dataLength;
|
||||||
|
switch (mode) {
|
||||||
|
case MODE_NUMBER:
|
||||||
|
// 数字模式
|
||||||
|
str += reader.getFigureString(dataLength);
|
||||||
|
break;
|
||||||
|
case MODE_ROMAN_AND_NUMBER:
|
||||||
|
// 字母数字模式
|
||||||
|
str += reader.getRomanAndFigureString(dataLength);
|
||||||
|
break;
|
||||||
|
case MODE_8BIT_BYTE:
|
||||||
|
// 8bit 字节模式,先判断编码方式(ECI/BOM/启发式)再解码
|
||||||
|
str += bytesToString(reader.get8bitByteArray(dataLength), eci);
|
||||||
|
break;
|
||||||
|
case MODE_KANJI:
|
||||||
|
// 汉字(日文)模式
|
||||||
|
str += reader.getKanjiString(dataLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw "Invalid mode: " + mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// var end = new Date().getTime();
|
// var end = new Date().getTime();
|
||||||
|
|||||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Generated
+12420
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "qr-decode",
|
"name": "qr-decode",
|
||||||
"version": "0.0.4",
|
"version": "0.0.5",
|
||||||
"description": "QRCode parser/decode",
|
"description": "QRCode parser/decode",
|
||||||
"main": "src/QRDecode.js",
|
"main": "src/QRDecode.js",
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
+164
-5
@@ -119,6 +119,134 @@ function decode(imageDate,debugfn){
|
|||||||
return process(imageDate.data,base)
|
return process(imageDate.data,base)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ECI 声明中常见的字符集(QR 规范:ECI 编号 -> 字符集)
|
||||||
|
var ECI_CHARSET = {
|
||||||
|
3: 'iso-8859-1',
|
||||||
|
20: 'shift_jis',
|
||||||
|
25: 'utf-16be',
|
||||||
|
26: 'utf-8',
|
||||||
|
27: 'us-ascii'
|
||||||
|
};
|
||||||
|
|
||||||
|
// UTF-8 字节数组转字符串
|
||||||
|
var decodeUTF8 = function (bytes) {
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
return new TextDecoder('utf-8').decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
var str = "";
|
||||||
|
var i = 0;
|
||||||
|
while (i < bytes.length) {
|
||||||
|
var code = bytes[i++] & 0xFF;
|
||||||
|
if (code < 0x80) {
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
else if (code < 0xE0) {
|
||||||
|
str += String.fromCharCode(((code & 0x1F) << 6) | (bytes[i++] & 0x3F));
|
||||||
|
}
|
||||||
|
else if (code < 0xF0) {
|
||||||
|
str += String.fromCharCode(((code & 0x0F) << 12) | ((bytes[i++] & 0x3F) << 6) | (bytes[i++] & 0x3F));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var cp = ((code & 0x07) << 18) | ((bytes[i++] & 0x3F) << 12) | ((bytes[i++] & 0x3F) << 6) | (bytes[i++] & 0x3F);
|
||||||
|
cp -= 0x10000;
|
||||||
|
str += String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTF-16 字节数组转字符串(isLE=false 时先把 BE 转成 LE 统一处理)
|
||||||
|
var decodeUTF16 = function (bytes, isLE) {
|
||||||
|
if (!isLE) {
|
||||||
|
// UTF-16BE -> 交换字节序
|
||||||
|
for (var i = 0; i + 1 < bytes.length; i += 2) {
|
||||||
|
var t = bytes[i];
|
||||||
|
bytes[i] = bytes[i + 1];
|
||||||
|
bytes[i + 1] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
return new TextDecoder('utf-16le').decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
// 兜底:手写 UTF-16LE 解码(含代理对)
|
||||||
|
var str = "";
|
||||||
|
for (var i = 0; i + 1 < bytes.length; i += 2) {
|
||||||
|
var code = bytes[i] | (bytes[i + 1] << 8);
|
||||||
|
if (code >= 0xD800 && code <= 0xDBFF && i + 3 < bytes.length) {
|
||||||
|
var low = bytes[i + 2] | (bytes[i + 3] << 8);
|
||||||
|
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||||
|
str += String.fromCharCode(code, low);
|
||||||
|
i += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8bit 字节段解码:按 ECI 声明 -> BOM -> 启发式检测 -> UTF-8 的顺序判断编码方式
|
||||||
|
var bytesToString = function (bytes, eci) {
|
||||||
|
var len = bytes.length;
|
||||||
|
if (len == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 优先按 ECI 声明的字符集解码
|
||||||
|
if (eci && ECI_CHARSET[eci]) {
|
||||||
|
var charset = ECI_CHARSET[eci];
|
||||||
|
if (charset == 'utf-16be') {
|
||||||
|
return decodeUTF16(bytes, false);
|
||||||
|
}
|
||||||
|
if (charset == 'utf-8') {
|
||||||
|
return decodeUTF8(bytes);
|
||||||
|
}
|
||||||
|
if (typeof TextDecoder !== 'undefined') {
|
||||||
|
try {
|
||||||
|
return new TextDecoder(charset).decode(new Uint8Array(bytes));
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// 环境不支持该字符集时继续走下面的检测
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. BOM 检测
|
||||||
|
if (len >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
|
||||||
|
return decodeUTF16(bytes.slice(2), true); // UTF-16LE
|
||||||
|
}
|
||||||
|
if (len >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
|
||||||
|
return decodeUTF16(bytes.slice(2), false); // UTF-16BE
|
||||||
|
}
|
||||||
|
if (len >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
|
||||||
|
return decodeUTF8(bytes.slice(3)); // UTF-8 with BOM
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 无 BOM 时启发式检测 UTF-16(偶数长度且奇/偶位存在成片 0x00 字节)
|
||||||
|
if (len >= 4 && len % 2 == 0) {
|
||||||
|
var evenZeros = 0;
|
||||||
|
var oddZeros = 0;
|
||||||
|
for (var i = 0; i < len; i += 2) {
|
||||||
|
if (bytes[i] == 0) {
|
||||||
|
evenZeros++;
|
||||||
|
}
|
||||||
|
if (bytes[i + 1] == 0) {
|
||||||
|
oddZeros++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var half = len / 2;
|
||||||
|
if (oddZeros > half * 0.7) {
|
||||||
|
return decodeUTF16(bytes, true); // 奇数位大量 0x00 -> UTF-16LE
|
||||||
|
}
|
||||||
|
if (evenZeros > half * 0.7) {
|
||||||
|
return decodeUTF16(bytes, false); // 偶数位大量 0x00 -> UTF-16BE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 兜底默认 UTF-8
|
||||||
|
return decodeUTF8(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
var process = function (data,base) {
|
var process = function (data,base) {
|
||||||
// var start = new Date().getTime();
|
// var start = new Date().getTime();
|
||||||
// var image = grayScaleToBitmap(grayscale(),base);
|
// var image = grayScaleToBitmap(grayscale(),base);
|
||||||
@@ -143,12 +271,43 @@ var process = function (data,base) {
|
|||||||
|
|
||||||
// 解析QR矩阵
|
// 解析QR矩阵
|
||||||
var reader = Decoder.decode(qRCodeMatrix.bits);
|
var reader = Decoder.decode(qRCodeMatrix.bits);
|
||||||
// console.log(reader);
|
|
||||||
var data = reader.DataByte;
|
// 先读取编码方式(mode indicator),按编码方式将数据直接转成字符串
|
||||||
|
var MODE_NUMBER = 1;
|
||||||
|
var MODE_ROMAN_AND_NUMBER = 2;
|
||||||
|
var MODE_8BIT_BYTE = 4;
|
||||||
|
var MODE_ECI = 7;
|
||||||
|
var MODE_KANJI = 8;
|
||||||
|
|
||||||
var str = "";
|
var str = "";
|
||||||
for (var i = 0; i < data.length; i++) {
|
var eci = 0; // ECI 声明的字符集编号,作用于后续字节段
|
||||||
for (var j = 0; j < data[i].length; j++)
|
while (true) {
|
||||||
str += String.fromCharCode(data[i][j]);
|
var mode = reader.NextMode();
|
||||||
|
if (mode == 0)
|
||||||
|
break;
|
||||||
|
if (mode == MODE_ECI) {
|
||||||
|
eci = reader.parseECIValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var dataLength = reader.getDataLength(mode);
|
||||||
|
if (dataLength < 1)
|
||||||
|
throw "Invalid data length: " + dataLength;
|
||||||
|
switch (mode) {
|
||||||
|
case MODE_NUMBER: // 数字模式
|
||||||
|
str += reader.getFigureString(dataLength);
|
||||||
|
break;
|
||||||
|
case MODE_ROMAN_AND_NUMBER: // 字母数字模式
|
||||||
|
str += reader.getRomanAndFigureString(dataLength);
|
||||||
|
break;
|
||||||
|
case MODE_8BIT_BYTE: // 8bit 字节模式,先判断编码方式(ECI/BOM/启发式)再解码
|
||||||
|
str += bytesToString(reader.get8bitByteArray(dataLength), eci);
|
||||||
|
break;
|
||||||
|
case MODE_KANJI: // 汉字(日文)模式
|
||||||
|
str += reader.getKanjiString(dataLength);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw "Invalid mode: " + mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// var end = new Date().getTime();
|
// var end = new Date().getTime();
|
||||||
|
|||||||
Reference in New Issue
Block a user