Browse Source

打印功能

hella_vue3
zhang_li 7 months ago
parent
commit
00e5d71199
  1. BIN
      src/components/kk-printer/empty-icon.png
  2. 416
      src/components/kk-printer/index.vue
  3. 402
      src/components/kk-printer/utils/barcode.js
  4. 723
      src/components/kk-printer/utils/bluetoolth.js
  5. 23
      src/components/kk-printer/utils/index.js
  6. 14633
      src/components/kk-printer/utils/mqtt.js
  7. 1
      src/components/kk-printer/utils/mqtt.min.js
  8. 285
      src/components/kk-printer/utils/printUtil-GBK.js
  9. 778
      src/components/kk-printer/utils/qrcode.js
  10. 171
      src/components/kk-printer/utils/util.js
  11. 6
      src/pages.json
  12. 70
      src/pages/point/index.vue

BIN
src/components/kk-printer/empty-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

416
src/components/kk-printer/index.vue

@ -0,0 +1,416 @@
<template>
<view class="kk-printer">
<view class="kk-printer-btn" @tap="handlePrintTap">
{{isPrinting?printingText:defaultText}}
</view>
<view class="kk-shadow" :class="isShowSearch?'show':''" @tap="handleSearchClose">
<view class="kk-modal" @tap.stop="doNothing">
<view class="kk-search-device">
<view class="kk-filter-wrap">
<view class="filter-title">根据SRRI过滤设备</view>
<slider @change="handleSRRIChange" max='-20' min='-100' value="-95" show-value/>
</view>
<view class="kk-filter-wrap">
<view class="filter-title">根据蓝牙名过滤</view>
<input type="text" placeholder-class="kk-placeholder-class" placeholder="请输入蓝牙名字或设备ID搜索" v-model="filterName" />
</view>
<view class="kk-btn-wrap">
<view class="kk-btn-item confirm-btn" @tap="searchBtnTap" v-if="!isSearching">
搜索设备
</view>
<view class="kk-btn-item confirm-btn" v-else>
搜索中...
</view>
<view class="kk-btn-item" @tap="stopSearchBtnTap">
停止搜索
</view>
</view>
<view class="kk-devices-wrap">
<view class="empty-wrap" v-if="filterDeviceList.length <= 0">
<view class="empty-icon"></view>
<view class="empty-text">~ 无可搜索到的设备 ~</view>
</view>
<view class="" v-else>
<view class="kk-devices-item" v-for="(item,index) in filterDeviceList" :key="index" @tap="handleConnectDevice(item)">
<view class="name">
<text>设备名称</text>
<text>{{item.name?item.name:'未命名'}}</text>
</view>
<view class="rssi">
<text>信号强度</text>
<text>({{Math.max(0, item.RSSI + 100)}}%)</text>
</view>
<view class="deviceid">
<text>设备ID</text>
<text>{{item.deviceId}}</text>
</view>
<view class="advmac" v-if="item.advMac">
<text>advMac</text>
<text>{{item.advMac}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import * as gbk from '@/components/kk-printer/utils/printUtil-GBK.js';
import * as blesdk from './utils/bluetoolth';
import * as util from './utils/util.js';
export default {
data(){
return{
//
isPrinting:false,
//
isSearching:false,
//
isShowSearch:false,
//
filterName:'',
//
filterRSSI:-95,
//
devicesList:[],
//ID
deviceId:'',
//ID
services:'',
//
serviceId: '',
writeId: '',
readId: ''
}
},
props:{
//
defaultText:{
type:String,
default:'打印'
},
//
printingText:{
type:String,
default:'打印中...'
},
bufferData:{
type:String,
require:true
}
},
computed:{
mapFilterRSSI(){
return (0 - this.filterRSSI)
},
filterDeviceList(){
let devices = this.devicesList;
let name = this.filterName;
let rssi = this.filterRSSI;
console.log(devices,name,rssi)
//RSSI
let filterDevices1 = devices.filter((item)=>{
return item.RSSI > rssi
})
console.log(filterDevices1)
//
let filterDevices2
if(name!=''){
filterDevices2 = filterDevices1.filter((item)=>{
return (item.name.indexOf(name) >= 0 || item.deviceId.indexOf(name) >= 0)
})
}else{
filterDevices2 = filterDevices1
}
// 广MAC
for (let i = 0; i < filterDevices2.length;i++) {
if (filterDevices2[i].hasOwnProperty('advertisData')){
if (filterDevices2[i].advertisData.byteLength == 8) {
filterDevices2[i].advMac = util.buf2hex(filterDevices2[i].advertisData.slice(2, 7));
}
}
}
return filterDevices2
}
},
mounted() {
},
beforeDestroy(){
this.stopSearchBtnTap();
},
methods:{
doNothing(){
return;
},
//
handlePrintTap(){
console.log(11)
//
blesdk.openBlue().then((res)=>{
console.log(99,res)
//
blesdk.getConnectedBluetoothDevices().then((res)=>{
//
console.log(66,res,this.deviceId,this.serviceId,this.writeId,this.bufferData,this.onPrintSuccess)
if(res.devices.length == 0){
this.isShowSearch = true
}else{
let datalen=20;
if (plus.os.name != 'Android')
{
datalen=180;
}
this.isPrinting = true;
this.$emit('onPrint');
this.$nextTick(()=>{
console.log(1,this.bufferData)
if(this.bufferData!=''){
let buffer = gbk.strToGBKByte(this.bufferData)
console.log(2,buffer)
let opt = {
deviceId: this.deviceId,
serviceId: this.serviceId,
characteristicId: this.writeId,
value:buffer,
lasterSuccess: this.onPrintSuccess,
onceLength:datalen
}
console.log(3,opt)
blesdk.sendDataToDevice(opt);
this.isPrinting = false;
}
})
}
}).catch((err)=>{
console.log(88,err)
blesdk.catchToast(err);
})
}).catch((err)=>{
console.log(77,err)
blesdk.catchToast(err);
})
},
onGetDevice(res){
this.devicesList = res;
},
handleSearchClose(){
this.isShowSearch = false
},
handleSRRIChange(e){
this.filterRSSI = e.detail.value
},
//
searchBtnTap(){
blesdk.startBluetoothDevicesDiscovery();
this.isSearching = true;
blesdk.onfindBlueDevices(this.onGetDevice)
},
//
stopSearchBtnTap(){
blesdk.stopBlueDevicesDiscovery();
this.isSearching = false;
},
//
handleConnectDevice(device){
let deviceId = device.deviceId;
let name = device.name;
this.deviceId = deviceId;
console.log('deviceId',this.deviceId)
// uni.setStorageSync('k_curDeviceID',deviceId);
// uni.setStorageSync('k_curDeviceName',name);
uni.onBLEConnectionStateChange((res)=>{
console.log('连接',res)
if(res.connected){
plus.nativeUI.toast('设备'+ res.deviceId + '已连接',{
verticalAlign:'center'
})
}else{
plus.nativeUI.toast('设备'+ res.deviceId + '已断开连接',{
verticalAlign:'center'
})
}
})
blesdk.createBLEConnection(deviceId, this.onConnectSuccess, this.onConnectFail);
},
onConnectSuccess(res){
this.stopSearchBtnTap()
blesdk.getBLEDeviceServices(this.deviceId, this.onGetServicesSuccess, this.onGetServicesFail);
},
onConnectFail(err){
console.log('链接失败',err)
},
onGetServicesSuccess(res){
console.log('获取服务',res)
this.services = res.serviceId;
blesdk.getDeviceCharacteristics(this.deviceId, this.services, this.onGetCharacterSuccess, this.onGetCharacterFail);
},
onGetServicesFail(err){
console.log('获取服务失败')
},
onGetCharacterSuccess(res){
console.log('获取特征值成功',res)
this.serviceId = res.serviceId;
this.writeId = res.writeId;
this.readId = res.readId;
this.isShowSearch = false;
},
onGetCharacterFail(err){
console.log('特征值获取失败')
},
onPrintSuccess(){
this.isPrinting = false;
console.log('打印成功')
this.$emit('onPrintSuccess')
},
onPrintFail(){
console.log('打印失败')
this.isPrinting = false;
}
}
}
</script>
<style lang="scss" scoped>
.kk-printer{
width:100%;
height:100%;
&-btn{
width:100%;
height:100%;
text-align: center;
line-height: 50px;
}
.kk-shadow{
display: none;
&.show{
display: block;
width:100vw;
height:100vh;
background: rgba(0,0,0,0.4);
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
.kk-modal{
width:680upx;
height: 80%;
padding:24upx;
box-sizing: border-box;
overflow-y: auto;
border-radius: 20upx;
background: #ffffff;
display: flex;
justify-content: center;
align-items: center;
.kk-search-device{
width:100%;
height: 100%;
.kk-filter-wrap{
width:100%;
height: 160upx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
.filter-title{
line-height: 70upx;
font-size: 30upx;
color: #999999;
}
&>slider{
width:90%;
height: 90upx;
}
&>input{
padding:0 20upx;
box-sizing: border-box;
border-radius: 10upx;
height: 90upx;
width:100%;
border: 1upx solid #ebebeb;
}
}
.kk-btn-wrap{
width:100%;
height: 140upx;
display: flex;
justify-content: space-between;
align-items: center;
&>view{
flex:1 1 auto;
height: 100upx;
line-height: 100upx;
border-radius: 16upx;
text-align: center;
color:#ffffff;
&.confirm-btn{
background: #007AFF;
margin-right:30upx;
}
&:nth-last-child(1){
background: #DD524D;
}
}
}
.kk-devices-wrap{
height: calc(100% - 460upx);
overflow-y:auto;
padding:10upx 20upx;
box-sizing: border-box;
border: 1upx solid #ebebeb;
box-sizing: border-box;
border-radius: 20upx;
.empty-wrap{
width:100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.empty-icon{
width:268upx;
height: 240upx;
background: url('./empty-icon.png') no-repeat;
background-size:100% 100%;
margin-bottom: 26upx;
}
.empty-text{
width: 100%;
line-height: 50upx;
font-size: 30upx;
text-align: center;
color: #999999;
}
}
.kk-devices-item{
width:100%;
border-bottom: 1upx solid #ebebeb;
padding:10upx 0;
box-sizing: border-box;
&:nth-last-child(1){
border-bottom: none;
}
&>view{
width:100%;
font-size: 30upx;
}
}
}
}
}
}
}
}
.kk-placeholder-class{
font-size: 30upx;
color:#999999;
}
</style>

402
src/components/kk-printer/utils/barcode.js

@ -0,0 +1,402 @@
var CHAR_TILDE = 126;
var CODE_FNC1 = 102;
var SET_STARTA = 103;
var SET_STARTB = 104;
var SET_STARTC = 105;
var SET_SHIFT = 98;
var SET_CODEA = 101;
var SET_CODEB = 100;
var SET_STOP = 106;
var REPLACE_CODES = {
CHAR_TILDE: CODE_FNC1 //~ corresponds to FNC1 in GS1-128 standard
}
var CODESET = {
ANY: 1,
AB: 2,
A: 3,
B: 4,
C: 5
};
function getBytes(str) {
var bytes = [];
for (var i = 0; i < str.length; i++) {
bytes.push(str.charCodeAt(i));
}
return bytes;
}
exports.code128 = function (ctx, text, width, height) {
width = parseInt(width);
height = parseInt(height);
var codes = stringToCode128(text);
var g = new Graphics(ctx, width, height);
var barWeight = g.area.width / ((codes.length - 3) * 11 + 35);
var x = g.area.left;
var y = g.area.top;
for (var i = 0; i < codes.length; i++) {
var c = codes[i];
//two bars at a time: 1 black and 1 white
for (var bar = 0; bar < 8; bar += 2) {
var barW = PATTERNS[c][bar] * barWeight;
// var barH = height - y - this.border;
var barH = height - y;
var spcW = PATTERNS[c][bar + 1] * barWeight;
//no need to draw if 0 width
if (barW > 0) {
g.fillFgRect(x, y, barW, barH);
}
x += barW + spcW;
}
}
ctx.draw();
}
function stringToCode128(text) {
var barc = {
currcs: CODESET.C
};
var bytes = getBytes(text);
//decide starting codeset
var index = bytes[0] == CHAR_TILDE ? 1 : 0;
var csa1 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
var csa2 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
barc.currcs = getBestStartSet(csa1, csa2);
barc.currcs = perhapsCodeC(bytes, barc.currcs);
//if no codeset changes this will end up with bytes.length+3
//start, checksum and stop
var codes = new Array();
switch (barc.currcs) {
case CODESET.A:
codes.push(SET_STARTA);
break;
case CODESET.B:
codes.push(SET_STARTB);
break;
default:
codes.push(SET_STARTC);
break;
}
for (var i = 0; i < bytes.length; i++) {
var b1 = bytes[i]; //get the first of a pair
//should we translate/replace
if (b1 in REPLACE_CODES) {
codes.push(REPLACE_CODES[b1]);
i++ //jump to next
b1 = bytes[i];
}
//get the next in the pair if possible
var b2 = bytes.length > (i + 1) ? bytes[i + 1] : -1;
codes = codes.concat(codesForChar(b1, b2, barc.currcs));
//code C takes 2 chars each time
if (barc.currcs == CODESET.C) i++;
}
//calculate checksum according to Code 128 standards
var checksum = codes[0];
for (var weight = 1; weight < codes.length; weight++) {
checksum += (weight * codes[weight]);
}
codes.push(checksum % 103);
codes.push(SET_STOP);
//encoding should now be complete
return codes;
function getBestStartSet(csa1, csa2) {
//tries to figure out the best codeset
//to start with to get the most compact code
var vote = 0;
vote += csa1 == CODESET.A ? 1 : 0;
vote += csa1 == CODESET.B ? -1 : 0;
vote += csa2 == CODESET.A ? 1 : 0;
vote += csa2 == CODESET.B ? -1 : 0;
//tie goes to B due to my own predudices
return vote > 0 ? CODESET.A : CODESET.B;
}
function perhapsCodeC(bytes, codeset) {
for (var i = 0; i < bytes.length; i++) {
var b = bytes[i]
if ((b < 48 || b > 57) && b != CHAR_TILDE)
return codeset;
}
return CODESET.C;
}
//chr1 is current byte
//chr2 is the next byte to process. looks ahead.
function codesForChar(chr1, chr2, currcs) {
var result = [];
var shifter = -1;
if (charCompatible(chr1, currcs)) {
if (currcs == CODESET.C) {
if (chr2 == -1) {
shifter = SET_CODEB;
currcs = CODESET.B;
}
else if ((chr2 != -1) && !charCompatible(chr2, currcs)) {
//need to check ahead as well
if (charCompatible(chr2, CODESET.A)) {
shifter = SET_CODEA;
currcs = CODESET.A;
}
else {
shifter = SET_CODEB;
currcs = CODESET.B;
}
}
}
}
else {
//if there is a next char AND that next char is also not compatible
if ((chr2 != -1) && !charCompatible(chr2, currcs)) {
//need to switch code sets
switch (currcs) {
case CODESET.A:
shifter = SET_CODEB;
currcs = CODESET.B;
break;
case CODESET.B:
shifter = SET_CODEA;
currcs = CODESET.A;
break;
}
}
else {
//no need to shift code sets, a temporary SHIFT will suffice
shifter = SET_SHIFT;
}
}
//ok some type of shift is nessecary
if (shifter != -1) {
result.push(shifter);
result.push(codeValue(chr2));
}
else {
if (currcs == CODESET.C) {
//include next as well
result.push(codeValue(chr1, chr2));
}
else {
result.push(codeValue(chr1));
}
}
barc.currcs = currcs;
return result;
}
}
//reduce the ascii code to fit into the Code128 char table
function codeValue(chr1, chr2) {
if (typeof chr2 == "undefined") {
return chr1 >= 32 ? chr1 - 32 : chr1 + 64;
}
else {
return parseInt(String.fromCharCode(chr1) + String.fromCharCode(chr2));
}
}
function charCompatible(chr, codeset) {
var csa = codeSetAllowedFor(chr);
if (csa == CODESET.ANY) return true;
//if we need to change from current
if (csa == CODESET.AB) return true;
if (csa == CODESET.A && codeset == CODESET.A) return true;
if (csa == CODESET.B && codeset == CODESET.B) return true;
return false;
}
function codeSetAllowedFor(chr) {
if (chr >= 48 && chr <= 57) {
//0-9
return CODESET.ANY;
}
else if (chr >= 32 && chr <= 95) {
//0-9 A-Z
return CODESET.AB;
}
else {
//if non printable
return chr < 32 ? CODESET.A : CODESET.B;
}
}
var Graphics = function(ctx, width, height) {
this.width = width;
this.height = height;
this.quiet = Math.round(this.width / 40);
this.border_size = 0;
this.padding_width = 0;
this.area = {
width : width - this.padding_width * 2 - this.quiet * 2,
height: height - this.border_size * 2,
top : this.border_size - 4,
left : this.padding_width + this.quiet
};
this.ctx = ctx;
this.fg = "#000000";
this.bg = "#ffffff";
// fill background
this.fillBgRect(0,0, width, height);
// fill center to create border
this.fillBgRect(0, this.border_size, width, height - this.border_size * 2);
}
//use native color
Graphics.prototype._fillRect = function(x, y, width, height, color) {
this.ctx.setFillStyle(color)
this.ctx.fillRect(x, y, width, height)
}
Graphics.prototype.fillFgRect = function(x,y, width, height) {
this._fillRect(x, y, width, height, this.fg);
}
Graphics.prototype.fillBgRect = function(x,y, width, height) {
this._fillRect(x, y, width, height, this.bg);
}
var PATTERNS = [
[2, 1, 2, 2, 2, 2, 0, 0], // 0
[2, 2, 2, 1, 2, 2, 0, 0], // 1
[2, 2, 2, 2, 2, 1, 0, 0], // 2
[1, 2, 1, 2, 2, 3, 0, 0], // 3
[1, 2, 1, 3, 2, 2, 0, 0], // 4
[1, 3, 1, 2, 2, 2, 0, 0], // 5
[1, 2, 2, 2, 1, 3, 0, 0], // 6
[1, 2, 2, 3, 1, 2, 0, 0], // 7
[1, 3, 2, 2, 1, 2, 0, 0], // 8
[2, 2, 1, 2, 1, 3, 0, 0], // 9
[2, 2, 1, 3, 1, 2, 0, 0], // 10
[2, 3, 1, 2, 1, 2, 0, 0], // 11
[1, 1, 2, 2, 3, 2, 0, 0], // 12
[1, 2, 2, 1, 3, 2, 0, 0], // 13
[1, 2, 2, 2, 3, 1, 0, 0], // 14
[1, 1, 3, 2, 2, 2, 0, 0], // 15
[1, 2, 3, 1, 2, 2, 0, 0], // 16
[1, 2, 3, 2, 2, 1, 0, 0], // 17
[2, 2, 3, 2, 1, 1, 0, 0], // 18
[2, 2, 1, 1, 3, 2, 0, 0], // 19
[2, 2, 1, 2, 3, 1, 0, 0], // 20
[2, 1, 3, 2, 1, 2, 0, 0], // 21
[2, 2, 3, 1, 1, 2, 0, 0], // 22
[3, 1, 2, 1, 3, 1, 0, 0], // 23
[3, 1, 1, 2, 2, 2, 0, 0], // 24
[3, 2, 1, 1, 2, 2, 0, 0], // 25
[3, 2, 1, 2, 2, 1, 0, 0], // 26
[3, 1, 2, 2, 1, 2, 0, 0], // 27
[3, 2, 2, 1, 1, 2, 0, 0], // 28
[3, 2, 2, 2, 1, 1, 0, 0], // 29
[2, 1, 2, 1, 2, 3, 0, 0], // 30
[2, 1, 2, 3, 2, 1, 0, 0], // 31
[2, 3, 2, 1, 2, 1, 0, 0], // 32
[1, 1, 1, 3, 2, 3, 0, 0], // 33
[1, 3, 1, 1, 2, 3, 0, 0], // 34
[1, 3, 1, 3, 2, 1, 0, 0], // 35
[1, 1, 2, 3, 1, 3, 0, 0], // 36
[1, 3, 2, 1, 1, 3, 0, 0], // 37
[1, 3, 2, 3, 1, 1, 0, 0], // 38
[2, 1, 1, 3, 1, 3, 0, 0], // 39
[2, 3, 1, 1, 1, 3, 0, 0], // 40
[2, 3, 1, 3, 1, 1, 0, 0], // 41
[1, 1, 2, 1, 3, 3, 0, 0], // 42
[1, 1, 2, 3, 3, 1, 0, 0], // 43
[1, 3, 2, 1, 3, 1, 0, 0], // 44
[1, 1, 3, 1, 2, 3, 0, 0], // 45
[1, 1, 3, 3, 2, 1, 0, 0], // 46
[1, 3, 3, 1, 2, 1, 0, 0], // 47
[3, 1, 3, 1, 2, 1, 0, 0], // 48
[2, 1, 1, 3, 3, 1, 0, 0], // 49
[2, 3, 1, 1, 3, 1, 0, 0], // 50
[2, 1, 3, 1, 1, 3, 0, 0], // 51
[2, 1, 3, 3, 1, 1, 0, 0], // 52
[2, 1, 3, 1, 3, 1, 0, 0], // 53
[3, 1, 1, 1, 2, 3, 0, 0], // 54
[3, 1, 1, 3, 2, 1, 0, 0], // 55
[3, 3, 1, 1, 2, 1, 0, 0], // 56
[3, 1, 2, 1, 1, 3, 0, 0], // 57
[3, 1, 2, 3, 1, 1, 0, 0], // 58
[3, 3, 2, 1, 1, 1, 0, 0], // 59
[3, 1, 4, 1, 1, 1, 0, 0], // 60
[2, 2, 1, 4, 1, 1, 0, 0], // 61
[4, 3, 1, 1, 1, 1, 0, 0], // 62
[1, 1, 1, 2, 2, 4, 0, 0], // 63
[1, 1, 1, 4, 2, 2, 0, 0], // 64
[1, 2, 1, 1, 2, 4, 0, 0], // 65
[1, 2, 1, 4, 2, 1, 0, 0], // 66
[1, 4, 1, 1, 2, 2, 0, 0], // 67
[1, 4, 1, 2, 2, 1, 0, 0], // 68
[1, 1, 2, 2, 1, 4, 0, 0], // 69
[1, 1, 2, 4, 1, 2, 0, 0], // 70
[1, 2, 2, 1, 1, 4, 0, 0], // 71
[1, 2, 2, 4, 1, 1, 0, 0], // 72
[1, 4, 2, 1, 1, 2, 0, 0], // 73
[1, 4, 2, 2, 1, 1, 0, 0], // 74
[2, 4, 1, 2, 1, 1, 0, 0], // 75
[2, 2, 1, 1, 1, 4, 0, 0], // 76
[4, 1, 3, 1, 1, 1, 0, 0], // 77
[2, 4, 1, 1, 1, 2, 0, 0], // 78
[1, 3, 4, 1, 1, 1, 0, 0], // 79
[1, 1, 1, 2, 4, 2, 0, 0], // 80
[1, 2, 1, 1, 4, 2, 0, 0], // 81
[1, 2, 1, 2, 4, 1, 0, 0], // 82
[1, 1, 4, 2, 1, 2, 0, 0], // 83
[1, 2, 4, 1, 1, 2, 0, 0], // 84
[1, 2, 4, 2, 1, 1, 0, 0], // 85
[4, 1, 1, 2, 1, 2, 0, 0], // 86
[4, 2, 1, 1, 1, 2, 0, 0], // 87
[4, 2, 1, 2, 1, 1, 0, 0], // 88
[2, 1, 2, 1, 4, 1, 0, 0], // 89
[2, 1, 4, 1, 2, 1, 0, 0], // 90
[4, 1, 2, 1, 2, 1, 0, 0], // 91
[1, 1, 1, 1, 4, 3, 0, 0], // 92
[1, 1, 1, 3, 4, 1, 0, 0], // 93
[1, 3, 1, 1, 4, 1, 0, 0], // 94
[1, 1, 4, 1, 1, 3, 0, 0], // 95
[1, 1, 4, 3, 1, 1, 0, 0], // 96
[4, 1, 1, 1, 1, 3, 0, 0], // 97
[4, 1, 1, 3, 1, 1, 0, 0], // 98
[1, 1, 3, 1, 4, 1, 0, 0], // 99
[1, 1, 4, 1, 3, 1, 0, 0], // 100
[3, 1, 1, 1, 4, 1, 0, 0], // 101
[4, 1, 1, 1, 3, 1, 0, 0], // 102
[2, 1, 1, 4, 1, 2, 0, 0], // 103
[2, 1, 1, 2, 1, 4, 0, 0], // 104
[2, 1, 1, 2, 3, 2, 0, 0], // 105
[2, 3, 3, 1, 1, 1, 2, 0] // 106
]

723
src/components/kk-printer/utils/bluetoolth.js

@ -0,0 +1,723 @@
/**
* @export
* @param {string} name 微信api的名称 uniAsyncPromise("getSystemInfo",options)
* @param {object} options 除了success fail 的其他参数
* @returns
*/
export function uniAsyncPromise(name, options) {
return new Promise((resolve, reject) => {
uni[name]({
...(options || {}),
// ...options,
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
}
});
});
}
//微信小程序向蓝牙打印机发送数据进行打印的坑:
//小程序api向蓝牙打印机发送数据打印,发送的任何内容都应该要转成二进制数据,而且蓝牙打印的文本编码是GBK的,发送中文需转成GBK编码再转成二进制数据发送
//发送打印机指令也要转成二进制数据发送
//蓝牙打印机一次接收的二级制数据有限制,不同的系统不同的蓝牙设备限制可能不同,微信建议一次20个字节,需做递归分包发送
//发送完要打印的内容后,一定要发送一个打印的指令才能顺利打印 (有些指令就不需要)
//一、初始化蓝牙、开始检索蓝牙设备
// { allowDuplicatesKey: true, interval: 500}
export function openBlue() {
return uniAsyncPromise('openBluetoothAdapter')
}
export function startBluetoothDevicesDiscovery(option) {
console.log('开始蓝牙扫描');
uniAsyncPromise('startBluetoothDevicesDiscovery', option).then((res) => {
console.log('正在搜寻蓝牙设备', res);
});
}
export function getConnectedBluetoothDevices(option) {
console.log('开始获取已连接设备');
return uniAsyncPromise('getConnectedBluetoothDevices', option)
}
//二、
/**
*
*
* @export
* @param {function} getDevices uni.getBluetoothDevices的监听回调函数
*/
export function onfindBlueDevices(getDevices) {
//监听寻找到新设备的事件
uni.onBluetoothDeviceFound((devices)=>{
//获取在蓝牙模块生效期间所有已发现的蓝牙设备
uniAsyncPromise('getBluetoothDevices').then((res) => {
getDevices && getDevices(res.devices);
});
});
}
/**
* @export
* @param {function} stopBlueDevicesDiscovery 关闭蓝牙扫描
*/
export function stopBlueDevicesDiscovery() {
//监听寻找到新设备的事件
console.log('停止蓝牙扫描');
return uniAsyncPromise('stopBluetoothDevicesDiscovery').then((res) => {
console.log('停止搜寻蓝牙设备', res);
});
}
//三、连接蓝牙设备
/**
* @export
* @param {function} createBLEConnection
* @param {number} deviceId 蓝牙设备id
*/
export function createBLEConnection(deviceId, sucess, fail) {
//连接蓝牙设备
console.log('连接蓝牙设备', deviceId);
uniAsyncPromise("createBLEConnection", {
deviceId
})
.then(res => {
//连接成功可选择停止搜索蓝牙
//stopBlueDevicesDiscovery();
console.log('连接成功');
sucess && sucess({
res: res,
});
})
.catch(res => {
console.log('连接设备异常' + res);
fail && fail({
res: res,
});
})
/*.finally(res=>{
console.log('连接成功');
sucess && sucess({
res: res,
});
});*/
}
export function closeBLEConnection(deviceId) {
console.log('断开蓝牙设备', deviceId);
uniAsyncPromise("closeBLEConnection", {
deviceId
})
.then(res => {
console.log('BLEDisconnect complete', res);
})
.catch(res => {
console.log('断开设备异常' + res);
})
/*.finally(res=>{
console.log('BLEDisconnect complete', res);
}); */
}
//四、连接成功后, 获取蓝牙设备的service服务
// uniAsyncPromise("getBLEDeviceServices",{deviceId:""}).then(res=>{})
export function getBLEDeviceServices(deviceId, success, fail) {
console.log('获取ServiceId', deviceId);
//加延迟避免取不到service
setTimeout(()=>{
uniAsyncPromise("getBLEDeviceServices", {
deviceId:deviceId
})
.then(res => {
console.log('服务', res);
success && success({
serviceId: res.services,
});
})
.catch((res) => {
//getBLEDeviceServices(deviceId, success, fail);
console.log('获取ServiceId异常' + res);
fail && fail({
res: res,
});
});
},1000)
}
//五、获取的service服务可能有多个,递归获取特征值(最后要用的是能读,能写,能监听的那个值的uuid作为特征值id)
/**
*
*
* @export
* @param {number} deviceId 蓝牙设备id
* @param {array} services uniAsyncPromise("getBLEDeviceServices",{deviceId:""}).then(res=>{})获取的res.services
* @param {function} success 成功取得有用特征值uuid的回调函数
*/
export function getDeviceCharacteristics(deviceId, services, success, fail) {
//services = services.slice(0);
console.log('获取Characteristics', deviceId, services);
if (services.length) {
const serviceId = services.shift().uuid;
console.log('ServceID ', serviceId);
uniAsyncPromise('getBLEDeviceCharacteristics', {
deviceId,
serviceId,
})
.then((res) => {
console.log('getBLEDeviceCharacteristics', deviceId, serviceId, res);
let finished = false;
let write = false;
let notify = false;
let indicate = false;
var readId;
var writeId;
//有斑马品牌的一款打印机中res.characteristics的所有uuid都是相同的,找所有的properties存在(notify || indicate) && write这种情况就说明这个uuid是可用的(不确保所有的打印机都能用这种方式取得uuid,在主要测试得凯盛诺打印机res.characteristic只有一个uuid,所以也能用这个方式)
for (var i = 0; i < res.characteristics.length; i++) {
if (!notify) {
notify = res.characteristics[i].properties.notify;
if (notify) readId = res.characteristics[i].uuid;
}
if (!indicate) {
indicate = res.characteristics[i].properties.indicate;
if (indicate) readId = res.characteristics[i].uuid;
}
if (!write) {
write = res.characteristics[i].properties.write;
writeId = res.characteristics[i].uuid;
}
if ((notify || indicate) && write) {
/* 获取蓝牙特征值uuid */
success &&
success({
serviceId,
writeId: writeId,
readId: readId,
});
finished = true;
break;
}
}
if (!finished) {
getDeviceCharacteristics(deviceId, services, success, fail);
}
})
.catch((res) => {
getDeviceCharacteristics(deviceId, services, success, fail);
});
} else {
fail && fail();
}
}
//六、启动notify 蓝牙监听功能 然后使用 uni.onBLECharacteristicValueChange用来监听蓝牙设备传递数据
/**
* @export
* @param {object} options
* {
deviceId,//蓝牙设备id
serviceId,//服务id
characteristicId,//可用特征值uuid
}
* @param {function} onChange 监听蓝牙设备传递数据回调函数
*/
export function onGetBLECharacteristicValueChange(options, onChange = function() {}) {
console.log('deviceId ', options.deviceId);
console.log('serviceId ', options.serviceId);
console.log('characteristicId ', options.characteristicId);
uniAsyncPromise('notifyBLECharacteristicValueChange', {
state: true,
...options,
}).then((res) => {
console.log('onBLECharacteristicValueChange ');
uni.onBLECharacteristicValueChange(onChange);
});
}
//七、发送数据(递归分包发送)
/**
* @export
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
value [ArrayBuffer],
lasterSuccess,
onceLength
}
*/
export function sendDataToDevice(options) {
let byteLength = options.value.byteLength;
//这里默认一次20个字节发送
const speed = options.onceLength; //20;
console.log("send data 20");
console.log(options);
if (byteLength > 0) {
uniAsyncPromise('writeBLECharacteristicValue', {
...options,
value: options.value.slice(0, byteLength > speed ? speed : byteLength),
})
.then((res) => {
if (byteLength > speed) {
sendDataToDevice({
...options,
value: options.value.slice(speed, byteLength),
});
} else {
options.lasterSuccess && options.lasterSuccess();
}
})
.catch((res) => {
console.log(res);
});
}
}
export function charToArrayBuffer(str) {
var out = new ArrayBuffer(str.length);
var uint8 = new Uint8Array(out);
var strs = str.split('');
for (var i = 0; i < strs.length; i++) {
uint8[i] = strs[i].charCodeAt();
}
return uint8;
}
export function charToArray(str) {
var arr = [];
var strs = str.split('');
for (var i = 0; i < strs.length; i++) {
arr[i] = strs[i].charCodeAt();
}
return arr;
}
//打印二维码
/**
* @export
* @param {object} options
* {
deviceId,
serviceId,
characteristicId,
value,//ArrayBuffer:二维码的数据
}
*/
export function printQR(options) {
//打印二维码的十进制指令data:
let data = [29, 107, 97, 7, 4, options.value.byteLength, 0];
sendDataToDevice({
...options,
value: new Uint8Array(data).buffer,
lasterSuccess: () => {
//指令发送成功后,发送二维码的数据
sendDataToDevice(options);
},
});
}
function grayPixle(pix) {
return pix[0] * 0.299 + pix[1] * 0.587 + pix[2] * 0.114;
}
export function overwriteImageData(data) {
let sendWidth = data.width,
sendHeight = data.height;
const threshold = data.threshold || 180;
let sendImageData = new ArrayBuffer((sendWidth * sendHeight) / 8);
sendImageData = new Uint8Array(sendImageData);
let pix = data.imageData;
const part = [];
let index = 0;
for (let i = 0; i < pix.length; i += 32) {
//横向每8个像素点组成一个字节(8位二进制数)。
for (let k = 0; k < 8; k++) {
const grayPixle1 = grayPixle(pix.slice(i + k * 4, i + k * 4 + (4 - 1)));
//阈值调整
if (grayPixle1 > threshold) {
//灰度值大于threshold位 白色 为第k位0不打印
part[k] = 0;
} else {
part[k] = 1;
}
}
let temp = 0;
for (let a = 0; a < part.length; a++) {
temp += part[a] * Math.pow(2, part.length - 1 - a);
}
//开始不明白以下算法什么意思,了解了字节才知道,一个字节是8位的二进制数,part这个数组存的0和1就是二进制的0和1,传输到打印的位图数据的一个字节是0-255之间的十进制数,以下是用权相加法转十进制数,理解了这个就用上面的for循环替代了
// const temp =
// part[0] * 128 +
// part[1] * 64 +
// part[2] * 32 +
// part[3] * 16 +
// part[4] * 8 +
// part[5] * 4 +
// part[6] * 2 +
// part[7] * 1;
sendImageData[index++] = temp;
}
return {
array: Array.from(sendImageData),
width: sendWidth / 8,
height: sendHeight,
};
}
/**
* printImage
* @param {object} opt
* {
deviceId,//蓝牙设备id
serviceId,//服务id
characteristicId,//可用特征值uuid
lasterSuccess , //最后完成的回调
}
*/
export function printImage(opt = {}, imageInfo = {}) {
let arr = imageInfo.array,
width = imageInfo.width;
const writeArray = [];
const xl = width % 256;
const xh = width / 256;
//分行发送图片数据,用的十进制指令
const command = [29, 118, 48, 0, xl, xh, 1, 0]; //1D 76 30 00 w h
const enter = [13, 10];
for (let i = 0; i < arr.length / width; i++) {
const subArr = arr.slice(i * width, i * width + width);
const tempArr = command.concat(subArr);
writeArray.push(new Uint8Array(tempArr));
}
writeArray.push(new Uint8Array(enter));
//console.log(writeArray);
const print = (options, writeArray) => {
if (writeArray.length) {
console.log("send");
sendDataToDevice({
...options,
value: writeArray.shift().buffer,
lasterSuccess: () => {
if (writeArray.length) {
print(options, writeArray);
} else {
options.lasterSuccess && options.lasterSuccess();
}
},
});
}
};
console.log("start print");
print(opt, writeArray);
}
/* 16hex insert 0 */
function Hex2Str(num) {
if (num.toString(16).length < 2) return "0" + num.toString(16);
else
return num.toString(16);
}
/*****CPCL指令接口****/
/**
* 配置项如下
*
* width: 标签纸的宽度单位像素點
* height: 标签纸的高度单位像素點
* 8像素=1mm
* printNum: 打印张数默认为1
* rotation页面整体旋转 1-90 2-180 3-270 其他-不旋转
*/
export function CreatCPCLPage(width, height, printNum, rotation = 0, offset = 0) {
var strCmd = '! ' + offset + ' 200 200 ' + height + ' ' + printNum + '\n';
strCmd += "PAGE-WIDTH " + width + '\n';
if (rotation == 1)
strCmd += "ZPROTATE90\n";
else if (rotation == 2)
strCmd += "ZPROTATE180\n";
else if (rotation == 3)
strCmd += "ZPROTATE270\n";
else
strCmd += "ZPROTATE0\n";
return strCmd;
}
/**
* 打印文字
* x: 文字方块左上角X座标单位dot
* y: 文字方块左上角Y座标单位dot
* fontName,fontSize: 字体取值 參考文檔
* rotation: 旋转 1-90 2-180 3-270 其他-不旋转
* content: 文字内容
*/
export function addCPCLText(x, y, fontName, fontSize, rotation, content) {
//console.log(fontName,fontSize,rotation, content);
var strCmd = '';
if (rotation == 1) {
strCmd += 'T90 ';
}
if (rotation == 2) {
strCmd += 'T180 ';
}
if (rotation == 3) {
strCmd += 'T270 ';
} else {
strCmd += 'T ';
}
strCmd += fontName + ' ' + fontSize + ' ' + x + ' ' + y + ' ' + content + '\n';
return strCmd;
};
/**
* 打印一维码
*
* x: 文字方块左上角X座标单位dot
* y: 文字方块左上角Y座标单位dot
* codeType: 条码类型取值为128UPCAUPCA2UPCA5UPCEUPCE2UPC5EAN13EAN13+2EAN13+5
* EAN8EAN8+2EAN8+53939CF39F39C93CODABARCODABAR16ITFI2OF5
* h: 条码高度单位dot
* rotation: 顺时针旋转角度取值如下
* - 0 不旋转
* - 1 顺时针旋转90度
*
* narrow: 窄条码比例因子(dot) 取值 參考文檔
* wide: 宽条码比例因子(dot) 取值 參考文檔
* content: 文字内容
*
*/
export function addCPCLBarCode(x, y, codeType, h, rotation, narrow, wide, content) {
var strCmd = '';
if (rotation == 0)
strCmd += 'B ';
else
strCmd += 'VB ';
strCmd += codeType + ' ' + narrow + ' ' + wide + ' ' + h + ' ' + x + ' ' + y + ' ' + content + '\n'
return strCmd;
};
/**
* 打印二维码
*
* x: 文字方块左上角X座标单位dot
* y: 文字方块左上角Y座标单位dot
* level: 错误纠正能力等级取值为L(7%)M(15%)Q(25%)H(30%)
* ver: 1-10 版本根据内容调整以获取合适容量
* scale: 1-10 放大倍数
* content: 文字内容
*
*/
export function addCPCLQRCode(x, y, level, ver, scale, content) {
var strCmd = 'B QR ' + x + ' ' + y + ' M ' + ver + ' U ' + scale + '\n' + level + 'A,' + content + '\n';
strCmd += 'ENDQR\n';
return strCmd;
};
/**
* 放大指令
* scaleX: 横向放大倍数 123等整数
* scaleY: 纵向放大倍数 123等整数
*/
export function addCPCLSETMAG(scaleX, scaleY) {
var strCmd = 'SETMAG ' + scaleX + ' ' + scaleY + '\n';
return strCmd;
};
/**
* 对齐指令 0-左对齐 1-右对齐 2-居中
*/
export function addCPCLLocation(set) {
var strCmd = '';
if (set == 1) {
strCmd += 'RIGHT\n';
} else if (set == 2) {
strCmd += 'CENTER\n';
} else {
strCmd += 'LEFT\n';
}
return strCmd;
};
/**
* 反白线 x0,y0,x1,y1,width
*/
export function addCPCLInverseLine(x0, y0, x1, y1, width) {
var strCmd = 'IL ' + x0 + ' ' + y0 + ' ' + x1 + ' ' + y1 + ' ' + width + '\n';
return strCmd;
};
/**
* 画线 x0,y0,x1,y1,width
*/
export function addCPCLLine(x0, y0, x1, y1, width) {
var strCmd = 'L ' + x0 + ' ' + y0 + ' ' + x1 + ' ' + y1 + ' ' + width + '\n';
return strCmd;
};
/**
* 画框 x0,y0,x1,y1,width
*/
export function addCPCLBox(x0, y0, x1, y1, width) {
var strCmd = 'BOX ' + x0 + ' ' + y0 + ' ' + x1 + ' ' + y1 + ' ' + width + '\n';
return strCmd;
};
/**
* 字体加粗
*/
export function addCPCLSETBOLD(bold) {
var strCmd = 'SETBOLD ' + bold + '\n';
return strCmd;
};
/**
* 字体下划线
*/
export function addCPCLUNDERLINE(c) {
var strCmd = 'UNDERLINE ';
if (c) strCmd += 'ON\n';
else if (c) strCmd += 'OFF\n';
return strCmd;
};
/**
* 水印打印灰度等级 0-255
*/
export function addCPCLBACKGROUND(level) {
var strCmd = 'BACKGROUND ';
if (level > 255 || level < 0) level = 255;
strCmd += level + '\n';
return strCmd;
};
/**
* 打印水印文字
* x: 文字方块左上角X座标单位dot
* y: 文字方块左上角Y座标单位dot
* fontName,fontSize: 字体取值 參考文檔
* rotation: 旋转 1-90 2-180 3-270 其他-不旋转
* content: 文字内容
*/
export function addCPCLBKVText(x, y, fontName, fontSize, rotation, content) {
//console.log(fontName,fontSize,rotation, content);
var strCmd = '';
if (rotation == 1) {
strCmd += 'BKT90 ';
}
if (rotation == 2) {
strCmd += 'BKT180 ';
}
if (rotation == 3) {
strCmd += 'BKT270 ';
} else {
strCmd += 'BKT ';
}
strCmd += fontName + ' ' + fontSize + ' ' + x + ' ' + y + ' ' + content + '\n';
return strCmd;
};
/**
* 标签缝隙定位指令
*/
export function addCPCLGAP() {
var strCmd = 'GAP-SENSE\nFORM\n';
return strCmd;
};
/**
* 标签右黑标检测指令
*/
export function addCPCLSENSE() {
var strCmd = 'BAR-SENSE\nFORM\n';
return strCmd;
};
/**
* 标签左黑标检测指令
*/
export function addCPCLSENSELEFT() {
var strCmd = 'BAR-SENSE LEFT\nFORM\n';
return strCmd;
};
/**
* 打印指令
*/
export function addCPCLPrint() {
var strCmd = 'PRINT\n';
return strCmd;
};
/**
* 图片打印指令
* x: 文字方块左上角X座标单位dot
* y: 文字方块左上角Y座标单位dot
* data{
threshold,//0/1提取的灰度级
width,//图像宽度
height,//图像高度
imageData , //图像数据
}
*/
export function addCPCLImageCmd(x, y, data) {
var strImgCmd = '';
const threshold = data.threshold || 180;
let myBitmapWidth = data.width,
myBitmapHeight = data.height;
let len = parseInt((myBitmapWidth + 7) / 8); //一行的数据长度
//console.log('len=',len);
//console.log('myBitmapWidth=',myBitmapWidth);
//console.log('myBitmapHeight=',myBitmapHeight);
let ndata = 0;
let i = 0;
let j = 0;
let sendImageData = new ArrayBuffer(len * myBitmapHeight);
sendImageData = new Uint8Array(sendImageData);
let pix = data.imageData;
console.log('pix=', pix);
for (i = 0; i < myBitmapHeight; i++) {
for (j = 0; j < len; j++) {
sendImageData[ndata + j] = 0;
}
for (j = 0; j < myBitmapWidth; j++) {
const grayPixle1 = grayPixle(pix.slice((i * myBitmapWidth + j) * 4, (i * myBitmapWidth + j) * 4 + 3));
if (grayPixle1 < threshold)
sendImageData[ndata + parseInt(j / 8)] |= (0x80 >> (j % 8));
}
ndata += len;
}
//console.log('sendImageData=',sendImageData);
//CPCL指令图片数据
strImgCmd += 'EG ' + len + ' ' + myBitmapHeight + ' ' + x + ' ' + y + ' ';
for (i = 0; i < sendImageData.length; i++) {
strImgCmd += Hex2Str(sendImageData[i]);
}
strImgCmd += '\n';
//console.log(strImgCmd);
return strImgCmd;
}
/**
* toast显示捕获的蓝牙异常
*/
export function catchToast(err) {
const errMsg = {
10000: '未初始化蓝牙模块',
10001: '蓝牙未打开',
10002: '没有找到指定设备',
10003: '连接失败',
10004: '没有找到指定服务',
10005: '没有找到指定特征值',
10006: '当前连接已断开',
10007: '当前特征值不支持此操作',
10008: '系统上报异常',
10009: '系统版本低于 4.3 不支持BLE'
};
let coode = err.errCode ? err.errCode.toString() : '';
let msg = errMsg[coode];
plus.nativeUI.toast(msg || coode, {
align: 'center',
verticalAlign: 'center'
});
}

23
src/components/kk-printer/utils/index.js

@ -0,0 +1,23 @@
var barcode = require('./barcode');
var qrcode = require('./qrcode');
function convert_length(length) {
return Math.round(wx.getSystemInfoSync().windowWidth * length / 750);
}
function barc(id, code, width, height) {
barcode.code128(wx.createCanvasContext(id), code, convert_length(width), convert_length(height))
}
function qrc(id, code, width, height) {
qrcode.api.draw(code, {
ctx: wx.createCanvasContext(id),
width: convert_length(width),
height: convert_length(height)
})
}
module.exports = {
barcode: barc,
qrcode: qrc
}

14633
src/components/kk-printer/utils/mqtt.js

File diff suppressed because it is too large

1
src/components/kk-printer/utils/mqtt.min.js

File diff suppressed because one or more lines are too long

285
src/components/kk-printer/utils/printUtil-GBK.js

File diff suppressed because one or more lines are too long

778
src/components/kk-printer/utils/qrcode.js

@ -0,0 +1,778 @@
var QR = (function () {
// alignment pattern
var adelta = [
0, 11, 15, 19, 23, 27, 31, // force 1 pat
16, 18, 20, 22, 24, 26, 28, 20, 22, 24, 24, 26, 28, 28, 22, 24, 24,
26, 26, 28, 28, 24, 24, 26, 26, 26, 28, 28, 24, 26, 26, 26, 28, 28
];
// version block
var vpat = [
0xc94, 0x5bc, 0xa99, 0x4d3, 0xbf6, 0x762, 0x847, 0x60d,
0x928, 0xb78, 0x45d, 0xa17, 0x532, 0x9a6, 0x683, 0x8c9,
0x7ec, 0xec4, 0x1e1, 0xfab, 0x08e, 0xc1a, 0x33f, 0xd75,
0x250, 0x9d5, 0x6f0, 0x8ba, 0x79f, 0xb0b, 0x42e, 0xa64,
0x541, 0xc69
];
// final format bits with mask: level << 3 | mask
var fmtword = [
0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, //L
0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, //M
0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed, //Q
0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b //H
];
// 4 per version: number of blocks 1,2; data width; ecc width
var eccblocks = [
1, 0, 19, 7, 1, 0, 16, 10, 1, 0, 13, 13, 1, 0, 9, 17,
1, 0, 34, 10, 1, 0, 28, 16, 1, 0, 22, 22, 1, 0, 16, 28,
1, 0, 55, 15, 1, 0, 44, 26, 2, 0, 17, 18, 2, 0, 13, 22,
1, 0, 80, 20, 2, 0, 32, 18, 2, 0, 24, 26, 4, 0, 9, 16,
1, 0, 108, 26, 2, 0, 43, 24, 2, 2, 15, 18, 2, 2, 11, 22,
2, 0, 68, 18, 4, 0, 27, 16, 4, 0, 19, 24, 4, 0, 15, 28,
2, 0, 78, 20, 4, 0, 31, 18, 2, 4, 14, 18, 4, 1, 13, 26,
2, 0, 97, 24, 2, 2, 38, 22, 4, 2, 18, 22, 4, 2, 14, 26,
2, 0, 116, 30, 3, 2, 36, 22, 4, 4, 16, 20, 4, 4, 12, 24,
2, 2, 68, 18, 4, 1, 43, 26, 6, 2, 19, 24, 6, 2, 15, 28,
4, 0, 81, 20, 1, 4, 50, 30, 4, 4, 22, 28, 3, 8, 12, 24,
2, 2, 92, 24, 6, 2, 36, 22, 4, 6, 20, 26, 7, 4, 14, 28,
4, 0, 107, 26, 8, 1, 37, 22, 8, 4, 20, 24, 12, 4, 11, 22,
3, 1, 115, 30, 4, 5, 40, 24, 11, 5, 16, 20, 11, 5, 12, 24,
5, 1, 87, 22, 5, 5, 41, 24, 5, 7, 24, 30, 11, 7, 12, 24,
5, 1, 98, 24, 7, 3, 45, 28, 15, 2, 19, 24, 3, 13, 15, 30,
1, 5, 107, 28, 10, 1, 46, 28, 1, 15, 22, 28, 2, 17, 14, 28,
5, 1, 120, 30, 9, 4, 43, 26, 17, 1, 22, 28, 2, 19, 14, 28,
3, 4, 113, 28, 3, 11, 44, 26, 17, 4, 21, 26, 9, 16, 13, 26,
3, 5, 107, 28, 3, 13, 41, 26, 15, 5, 24, 30, 15, 10, 15, 28,
4, 4, 116, 28, 17, 0, 42, 26, 17, 6, 22, 28, 19, 6, 16, 30,
2, 7, 111, 28, 17, 0, 46, 28, 7, 16, 24, 30, 34, 0, 13, 24,
4, 5, 121, 30, 4, 14, 47, 28, 11, 14, 24, 30, 16, 14, 15, 30,
6, 4, 117, 30, 6, 14, 45, 28, 11, 16, 24, 30, 30, 2, 16, 30,
8, 4, 106, 26, 8, 13, 47, 28, 7, 22, 24, 30, 22, 13, 15, 30,
10, 2, 114, 28, 19, 4, 46, 28, 28, 6, 22, 28, 33, 4, 16, 30,
8, 4, 122, 30, 22, 3, 45, 28, 8, 26, 23, 30, 12, 28, 15, 30,
3, 10, 117, 30, 3, 23, 45, 28, 4, 31, 24, 30, 11, 31, 15, 30,
7, 7, 116, 30, 21, 7, 45, 28, 1, 37, 23, 30, 19, 26, 15, 30,
5, 10, 115, 30, 19, 10, 47, 28, 15, 25, 24, 30, 23, 25, 15, 30,
13, 3, 115, 30, 2, 29, 46, 28, 42, 1, 24, 30, 23, 28, 15, 30,
17, 0, 115, 30, 10, 23, 46, 28, 10, 35, 24, 30, 19, 35, 15, 30,
17, 1, 115, 30, 14, 21, 46, 28, 29, 19, 24, 30, 11, 46, 15, 30,
13, 6, 115, 30, 14, 23, 46, 28, 44, 7, 24, 30, 59, 1, 16, 30,
12, 7, 121, 30, 12, 26, 47, 28, 39, 14, 24, 30, 22, 41, 15, 30,
6, 14, 121, 30, 6, 34, 47, 28, 46, 10, 24, 30, 2, 64, 15, 30,
17, 4, 122, 30, 29, 14, 46, 28, 49, 10, 24, 30, 24, 46, 15, 30,
4, 18, 122, 30, 13, 32, 46, 28, 48, 14, 24, 30, 42, 32, 15, 30,
20, 4, 117, 30, 40, 7, 47, 28, 43, 22, 24, 30, 10, 67, 15, 30,
19, 6, 118, 30, 18, 31, 47, 28, 34, 34, 24, 30, 20, 61, 15, 30
];
// Galois field log table
var glog = [
0xff, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
];
// Galios field exponent table
var gexp = [
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x00
];
// Working buffers:
// data input and ecc append, image working buffer, fixed part of image, run lengths for badness
var strinbuf=[], eccbuf=[], qrframe=[], framask=[], rlens=[];
// Control values - width is based on version, last 4 are from table.
var version, width, neccblk1, neccblk2, datablkw, eccblkwid;
var ecclevel = 2;
// set bit to indicate cell in qrframe is immutable. symmetric around diagonal
function setmask(x, y)
{
var bt;
if (x > y) {
bt = x;
x = y;
y = bt;
}
// y*y = 1+3+5...
bt = y;
bt *= y;
bt += y;
bt >>= 1;
bt += x;
framask[bt] = 1;
}
// enter alignment pattern - black to qrframe, white to mask (later black frame merged to mask)
function putalign(x, y)
{
var j;
qrframe[x + width * y] = 1;
for (j = -2; j < 2; j++) {
qrframe[(x + j) + width * (y - 2)] = 1;
qrframe[(x - 2) + width * (y + j + 1)] = 1;
qrframe[(x + 2) + width * (y + j)] = 1;
qrframe[(x + j + 1) + width * (y + 2)] = 1;
}
for (j = 0; j < 2; j++) {
setmask(x - 1, y + j);
setmask(x + 1, y - j);
setmask(x - j, y - 1);
setmask(x + j, y + 1);
}
}
//========================================================================
// Reed Solomon error correction
// exponentiation mod N
function modnn(x)
{
while (x >= 255) {
x -= 255;
x = (x >> 8) + (x & 255);
}
return x;
}
var genpoly = [];
// Calculate and append ECC data to data block. Block is in strinbuf, indexes to buffers given.
function appendrs(data, dlen, ecbuf, eclen)
{
var i, j, fb;
for (i = 0; i < eclen; i++)
strinbuf[ecbuf + i] = 0;
for (i = 0; i < dlen; i++) {
fb = glog[strinbuf[data + i] ^ strinbuf[ecbuf]];
if (fb != 255) /* fb term is non-zero */
for (j = 1; j < eclen; j++)
strinbuf[ecbuf + j - 1] = strinbuf[ecbuf + j] ^ gexp[modnn(fb + genpoly[eclen - j])];
else
for( j = ecbuf ; j < ecbuf + eclen; j++ )
strinbuf[j] = strinbuf[j + 1];
strinbuf[ ecbuf + eclen - 1] = fb == 255 ? 0 : gexp[modnn(fb + genpoly[0])];
}
}
//========================================================================
// Frame data insert following the path rules
// check mask - since symmetrical use half.
function ismasked(x, y)
{
var bt;
if (x > y) {
bt = x;
x = y;
y = bt;
}
bt = y;
bt += y * y;
bt >>= 1;
bt += x;
return framask[bt];
}
//========================================================================
// Apply the selected mask out of the 8.
function applymask(m)
{
var x, y, r3x, r3y;
switch (m) {
case 0:
for (y = 0; y < width; y++)
for (x = 0; x < width; x++)
if (!((x + y) & 1) && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
break;
case 1:
for (y = 0; y < width; y++)
for (x = 0; x < width; x++)
if (!(y & 1) && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
break;
case 2:
for (y = 0; y < width; y++)
for (r3x = 0, x = 0; x < width; x++, r3x++) {
if (r3x == 3)
r3x = 0;
if (!r3x && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
}
break;
case 3:
for (r3y = 0, y = 0; y < width; y++, r3y++) {
if (r3y == 3)
r3y = 0;
for (r3x = r3y, x = 0; x < width; x++, r3x++) {
if (r3x == 3)
r3x = 0;
if (!r3x && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
}
}
break;
case 4:
for (y = 0; y < width; y++)
for (r3x = 0, r3y = ((y >> 1) & 1), x = 0; x < width; x++, r3x++) {
if (r3x == 3) {
r3x = 0;
r3y = !r3y;
}
if (!r3y && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
}
break;
case 5:
for (r3y = 0, y = 0; y < width; y++, r3y++) {
if (r3y == 3)
r3y = 0;
for (r3x = 0, x = 0; x < width; x++, r3x++) {
if (r3x == 3)
r3x = 0;
if (!((x & y & 1) + !(!r3x | !r3y)) && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
}
}
break;
case 6:
for (r3y = 0, y = 0; y < width; y++, r3y++) {
if (r3y == 3)
r3y = 0;
for (r3x = 0, x = 0; x < width; x++, r3x++) {
if (r3x == 3)
r3x = 0;
if (!(((x & y & 1) + (r3x && (r3x == r3y))) & 1) && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
}
}
break;
case 7:
for (r3y = 0, y = 0; y < width; y++, r3y++) {
if (r3y == 3)
r3y = 0;
for (r3x = 0, x = 0; x < width; x++, r3x++) {
if (r3x == 3)
r3x = 0;
if (!(((r3x && (r3x == r3y)) + ((x + y) & 1)) & 1) && !ismasked(x, y))
qrframe[x + y * width] ^= 1;
}
}
break;
}
return;
}
// Badness coefficients.
var N1 = 3, N2 = 3, N3 = 40, N4 = 10;
// Using the table of the length of each run, calculate the amount of bad image
// - long runs or those that look like finders; called twice, once each for X and Y
function badruns(length)
{
var i;
var runsbad = 0;
for (i = 0; i <= length; i++)
if (rlens[i] >= 5)
runsbad += N1 + rlens[i] - 5;
// BwBBBwB as in finder
for (i = 3; i < length - 1; i += 2)
if (rlens[i - 2] == rlens[i + 2]
&& rlens[i + 2] == rlens[i - 1]
&& rlens[i - 1] == rlens[i + 1]
&& rlens[i - 1] * 3 == rlens[i]
// white around the black pattern? Not part of spec
&& (rlens[i - 3] == 0 // beginning
|| i + 3 > length // end
|| rlens[i - 3] * 3 >= rlens[i] * 4 || rlens[i + 3] * 3 >= rlens[i] * 4)
)
runsbad += N3;
return runsbad;
}
// Calculate how bad the masked image is - blocks, imbalance, runs, or finders.
function badcheck()
{
var x, y, h, b, b1;
var thisbad = 0;
var bw = 0;
// blocks of same color.
for (y = 0; y < width - 1; y++)
for (x = 0; x < width - 1; x++)
if ((qrframe[x + width * y] && qrframe[(x + 1) + width * y]
&& qrframe[x + width * (y + 1)] && qrframe[(x + 1) + width * (y + 1)]) // all black
|| !(qrframe[x + width * y] || qrframe[(x + 1) + width * y]
|| qrframe[x + width * (y + 1)] || qrframe[(x + 1) + width * (y + 1)])) // all white
thisbad += N2;
// X runs
for (y = 0; y < width; y++) {
rlens[0] = 0;
for (h = b = x = 0; x < width; x++) {
if ((b1 = qrframe[x + width * y]) == b)
rlens[h]++;
else
rlens[++h] = 1;
b = b1;
bw += b ? 1 : -1;
}
thisbad += badruns(h);
}
// black/white imbalance
if (bw < 0)
bw = -bw;
var big = bw;
var count = 0;
big += big << 2;
big <<= 1;
while (big > width * width)
big -= width * width, count++;
thisbad += count * N4;
// Y runs
for (x = 0; x < width; x++) {
rlens[0] = 0;
for (h = b = y = 0; y < width; y++) {
if ((b1 = qrframe[x + width * y]) == b)
rlens[h]++;
else
rlens[++h] = 1;
b = b1;
}
thisbad += badruns(h);
}
return thisbad;
}
function genframe(instring)
{
var x, y, k, t, v, i, j, m;
// find the smallest version that fits the string
t = instring.length;
version = 0;
do {
version++;
k = (ecclevel - 1) * 4 + (version - 1) * 16;
neccblk1 = eccblocks[k++];
neccblk2 = eccblocks[k++];
datablkw = eccblocks[k++];
eccblkwid = eccblocks[k];
k = datablkw * (neccblk1 + neccblk2) + neccblk2 - 3 + (version <= 9);
if (t <= k)
break;
} while (version < 40);
// FIXME - insure that it fits insted of being truncated
width = 17 + 4 * version;
// allocate, clear and setup data structures
v = datablkw + (datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2;
for( t = 0; t < v; t++ )
eccbuf[t] = 0;
strinbuf = instring.slice(0);
for( t = 0; t < width * width; t++ )
qrframe[t] = 0;
for( t = 0 ; t < (width * (width + 1) + 1) / 2; t++)
framask[t] = 0;
// insert finders - black to frame, white to mask
for (t = 0; t < 3; t++) {
k = 0;
y = 0;
if (t == 1)
k = (width - 7);
if (t == 2)
y = (width - 7);
qrframe[(y + 3) + width * (k + 3)] = 1;
for (x = 0; x < 6; x++) {
qrframe[(y + x) + width * k] = 1;
qrframe[y + width * (k + x + 1)] = 1;
qrframe[(y + 6) + width * (k + x)] = 1;
qrframe[(y + x + 1) + width * (k + 6)] = 1;
}
for (x = 1; x < 5; x++) {
setmask(y + x, k + 1);
setmask(y + 1, k + x + 1);
setmask(y + 5, k + x);
setmask(y + x + 1, k + 5);
}
for (x = 2; x < 4; x++) {
qrframe[(y + x) + width * (k + 2)] = 1;
qrframe[(y + 2) + width * (k + x + 1)] = 1;
qrframe[(y + 4) + width * (k + x)] = 1;
qrframe[(y + x + 1) + width * (k + 4)] = 1;
}
}
// alignment blocks
if (version > 1) {
t = adelta[version];
y = width - 7;
for (;;) {
x = width - 7;
while (x > t - 3) {
putalign(x, y);
if (x < t)
break;
x -= t;
}
if (y <= t + 9)
break;
y -= t;
putalign(6, y);
putalign(y, 6);
}
}
// single black
qrframe[8 + width * (width - 8)] = 1;
// timing gap - mask only
for (y = 0; y < 7; y++) {
setmask(7, y);
setmask(width - 8, y);
setmask(7, y + width - 7);
}
for (x = 0; x < 8; x++) {
setmask(x, 7);
setmask(x + width - 8, 7);
setmask(x, width - 8);
}
// reserve mask-format area
for (x = 0; x < 9; x++)
setmask(x, 8);
for (x = 0; x < 8; x++) {
setmask(x + width - 8, 8);
setmask(8, x);
}
for (y = 0; y < 7; y++)
setmask(8, y + width - 7);
// timing row/col
for (x = 0; x < width - 14; x++)
if (x & 1) {
setmask(8 + x, 6);
setmask(6, 8 + x);
}
else {
qrframe[(8 + x) + width * 6] = 1;
qrframe[6 + width * (8 + x)] = 1;
}
// version block
if (version > 6) {
t = vpat[version - 7];
k = 17;
for (x = 0; x < 6; x++)
for (y = 0; y < 3; y++, k--)
if (1 & (k > 11 ? version >> (k - 12) : t >> k)) {
qrframe[(5 - x) + width * (2 - y + width - 11)] = 1;
qrframe[(2 - y + width - 11) + width * (5 - x)] = 1;
}
else {
setmask(5 - x, 2 - y + width - 11);
setmask(2 - y + width - 11, 5 - x);
}
}
// sync mask bits - only set above for white spaces, so add in black bits
for (y = 0; y < width; y++)
for (x = 0; x <= y; x++)
if (qrframe[x + width * y])
setmask(x, y);
// convert string to bitstream
// 8 bit data to QR-coded 8 bit data (numeric or alphanum, or kanji not supported)
v = strinbuf.length;
// string to array
for( i = 0 ; i < v; i++ )
eccbuf[i] = strinbuf.charCodeAt(i);
strinbuf = eccbuf.slice(0);
// calculate max string length
x = datablkw * (neccblk1 + neccblk2) + neccblk2;
if (v >= x - 2) {
v = x - 2;
if (version > 9)
v--;
}
// shift and repack to insert length prefix
i = v;
if (version > 9) {
strinbuf[i + 2] = 0;
strinbuf[i + 3] = 0;
while (i--) {
t = strinbuf[i];
strinbuf[i + 3] |= 255 & (t << 4);
strinbuf[i + 2] = t >> 4;
}
strinbuf[2] |= 255 & (v << 4);
strinbuf[1] = v >> 4;
strinbuf[0] = 0x40 | (v >> 12);
}
else {
strinbuf[i + 1] = 0;
strinbuf[i + 2] = 0;
while (i--) {
t = strinbuf[i];
strinbuf[i + 2] |= 255 & (t << 4);
strinbuf[i + 1] = t >> 4;
}
strinbuf[1] |= 255 & (v << 4);
strinbuf[0] = 0x40 | (v >> 4);
}
// fill to end with pad pattern
i = v + 3 - (version < 10);
while (i < x) {
strinbuf[i++] = 0xec;
// buffer has room if (i == x) break;
strinbuf[i++] = 0x11;
}
// calculate and append ECC
// calculate generator polynomial
genpoly[0] = 1;
for (i = 0; i < eccblkwid; i++) {
genpoly[i + 1] = 1;
for (j = i; j > 0; j--)
genpoly[j] = genpoly[j]
? genpoly[j - 1] ^ gexp[modnn(glog[genpoly[j]] + i)] : genpoly[j - 1];
genpoly[0] = gexp[modnn(glog[genpoly[0]] + i)];
}
for (i = 0; i <= eccblkwid; i++)
genpoly[i] = glog[genpoly[i]]; // use logs for genpoly[] to save calc step
// append ecc to data buffer
k = x;
y = 0;
for (i = 0; i < neccblk1; i++) {
appendrs(y, datablkw, k, eccblkwid);
y += datablkw;
k += eccblkwid;
}
for (i = 0; i < neccblk2; i++) {
appendrs(y, datablkw + 1, k, eccblkwid);
y += datablkw + 1;
k += eccblkwid;
}
// interleave blocks
y = 0;
for (i = 0; i < datablkw; i++) {
for (j = 0; j < neccblk1; j++)
eccbuf[y++] = strinbuf[i + j * datablkw];
for (j = 0; j < neccblk2; j++)
eccbuf[y++] = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))];
}
for (j = 0; j < neccblk2; j++)
eccbuf[y++] = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))];
for (i = 0; i < eccblkwid; i++)
for (j = 0; j < neccblk1 + neccblk2; j++)
eccbuf[y++] = strinbuf[x + i + j * eccblkwid];
strinbuf = eccbuf;
// pack bits into frame avoiding masked area.
x = y = width - 1;
k = v = 1; // up, minus
/* inteleaved data and ecc codes */
m = (datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2;
for (i = 0; i < m; i++) {
t = strinbuf[i];
for (j = 0; j < 8; j++, t <<= 1) {
if (0x80 & t)
qrframe[x + width * y] = 1;
do { // find next fill position
if (v)
x--;
else {
x++;
if (k) {
if (y != 0)
y--;
else {
x -= 2;
k = !k;
if (x == 6) {
x--;
y = 9;
}
}
}
else {
if (y != width - 1)
y++;
else {
x -= 2;
k = !k;
if (x == 6) {
x--;
y -= 8;
}
}
}
}
v = !v;
} while (ismasked(x, y));
}
}
// save pre-mask copy of frame
strinbuf = qrframe.slice(0);
t = 0; // best
y = 30000; // demerit
// for instead of while since in original arduino code
// if an early mask was "good enough" it wouldn't try for a better one
// since they get more complex and take longer.
for (k = 0; k < 8; k++) {
applymask(k); // returns black-white imbalance
x = badcheck();
if (x < y) { // current mask better than previous best?
y = x;
t = k;
}
if (t == 7)
break; // don't increment i to a void redoing mask
qrframe = strinbuf.slice(0); // reset for next pass
}
if (t != k) // redo best mask - none good enough, last wasn't t
applymask(t);
// add in final mask/ecclevel bytes
y = fmtword[t + ((ecclevel - 1) << 3)];
// low byte
for (k = 0; k < 8; k++, y >>= 1)
if (y & 1) {
qrframe[(width - 1 - k) + width * 8] = 1;
if (k < 6)
qrframe[8 + width * k] = 1;
else
qrframe[8 + width * (k + 1)] = 1;
}
// high byte
for (k = 0; k < 7; k++, y >>= 1)
if (y & 1) {
qrframe[8 + width * (width - 7 + k)] = 1;
if (k)
qrframe[(6 - k) + width * 8] = 1;
else
qrframe[7 + width * 8] = 1;
}
// return image
return qrframe;
}
var _canvas = null,
_size = null;
var api = {
get ecclevel () {
return ecclevel;
},
set ecclevel (val) {
ecclevel = val;
},
get size () {
return _size;
},
set size (val) {
_size = val
},
get canvas () {
return _canvas;
},
set canvas (el) {
_canvas = el;
},
getFrame: function (string) {
return genframe(string);
},
draw: function (string, canvas, size, ecc) {
ecclevel = ecc || ecclevel;
canvas = canvas || _canvas;
if (!canvas) {
console.warn('No canvas provided to draw QR code in!')
return;
}
size = size || _size || Math.min(canvas.width, canvas.height);
var frame = genframe(string),
ctx = canvas.ctx,
px = Math.round(size / (width + 8));
var roundedSize = px * (width + 8),
offset = Math.floor((size - roundedSize) / 2);
size = roundedSize;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setFillStyle('#000000');
for (var i = 0; i < width; i++) {
for (var j = 0; j < width; j++) {
if (frame[j * width + i]) {
ctx.fillRect(px * (4 + i) + offset, px * (4 + j) + offset, px, px);
}
}
}
ctx.draw();
}
}
module.exports = {
api: api
}
})()

171
src/components/kk-printer/utils/util.js

@ -0,0 +1,171 @@
//const gbk = require('./gbk.js');
//console.log("sasas" + gbk);
const formatTime = date => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : '0' + n
}
/*
const hexStringToBuff = str => { //str='中国:WXHSH'
const buffer = new ArrayBuffer((sumStrLength(str)) * 4)
const dataView = new DataView(buffer)
var data = str.toString();
var p = 0; //ArrayBuffer 偏移量
for (var i = 0; i < data.length; i++) {
if (isCN(data[i])) { //是中文
//调用GBK 转码
var t = gbk.encode(data[i]);
for (var j = 0; j < 2; j++) {
//var code = t[j * 2] + t[j * 2 + 1];
var code = t[j * 3 + 1] + t[j * 3 + 2];
var temp = parseInt(code, 16)
//var temp = strToHexCharCode(code);
dataView.setUint8(p++, temp)
}
} else {
var temp = data.charCodeAt(i);
dataView.setUint8(p++, temp)
}
}
return buffer;
}
*/
function toUnicode(s) {
var str = "";
for (var i = 0; i < s.length; i++) {
str += "\\u" + s.charCodeAt(i).toString(16) + "\t";
}
return str;
}
function strToHexCharCode(str) {
if (str === "")
return "";
var hexCharCode = [];
hexCharCode.push("0x");
for (var i = 0; i < str.length; i++) {
hexCharCode.push((str.charCodeAt(i)).toString(16));
}
return hexCharCode.join("");
}
function sumStrLength(str) {
var length = 0;
var data = str.toString();
for (var i = 0; i < data.length; i++) {
if (isCN(data[i])) { //是中文
length += 2;
} else {
length += 1;
}
}
return length;
}
function isCN(str) {
if (/^[\u3220-\uFA29]+$/.test(str)) {
return true;
} else {
return false;
}
}
//汉字转码
export function hexStringToArrayBuffer(str) {
const buffer = new ArrayBuffer((str.length / 2) + 1)
const dataView = new DataView(buffer)
for (var i = 0; i < str.length / 2; i++) {
var temp = parseInt(str[i * 2] + str[i * 2 + 1], 16)
dataView.setUint8(i, temp)
}
dataView.setUint8((str.length / 2), 0x0a)
return buffer;
}
//返回八位数组
function subString(str) {
var arr = [];
if (str.length > 8) { //大于8
for (var i = 0;
(i * 8) < str.length; i++) {
var temp = str.substring(i * 8, 8 * i + 8);
arr.push(temp)
}
return arr;
} else {
return str
}
}
//不带有汉字
function hexStringToArrayBufferstr(str) {
let val = ""
for (let i = 0; i < str.length; i++) {
if (val === '') {
val = str.charCodeAt(i).toString(16)
} else {
val += ',' + str.charCodeAt(i).toString(16)
}
}
val += "," + "0x0a";
console.log(val)
// 将16进制转化为ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function(h) {
return parseInt(h, 16)
})).buffer
}
export function ab2hex(buffer) {
let hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
})
return hexArr.join('');
}
// ArrayBuffer转为字符串,参数为ArrayBuffer对象
export function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
// 字符串转为ArrayBuffer对象,参数为字符串
export function str2ab(str) {
var buf = new ArrayBuffer(str.length+1); // 补充/0
var bufView = new Uint8Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
export function send0X0A() {
const buffer = new ArrayBuffer(1)
const dataView = new DataView(buffer)
dataView.setUint8(0, 0x0a)
return buffer;
}
export function buf2hex(buffer) {
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
// module.exports = {
// hexStringToArrayBuffer: hexStringToArrayBuffer,
// send0X0A: send0X0A,
// ab2hex: ab2hex,
// str2ab: str2ab,
// ab2str: ab2str,
// buf2hex: buf2hex
// }

6
src/pages.json

@ -2021,6 +2021,12 @@
"navigationBarTitleText": "翻包任务详情", "navigationBarTitleText": "翻包任务详情",
"enablePullDownRefresh": false "enablePullDownRefresh": false
} }
},{
"path": "pages/point/index",
"style": {
"navigationBarTitleText": "打印",
"enablePullDownRefresh": false
}
} }

70
src/pages/point/index.vue

@ -0,0 +1,70 @@
<template>
<view class="content">
<view class="btn-wrap">
<kk-printer ref="kkprinter" :bufferData="bufferData" @onPrint="onPrint"></kk-printer>
</view>
</view>
</template>
<script>
import util from '@/components/kk-printer/utils/util.js';
import * as blesdk from '@/components/kk-printer/utils/bluetoolth.js';
import kkPrinter from '@/components/kk-printer/index.vue';
export default {
data() {
return {
bufferData:''
}
},
components:{
kkPrinter
},
mounted() {
},
methods: {
onPrint(opt){
let arr = ['2','3']
let y = 450
let strCmd =blesdk.CreatCPCLPage(400,y*arr.length,1,0);
arr.forEach((item,index)=>{
strCmd += blesdk.addCPCLLine(0,210 + (y*index),400,210+ (y*index),3);
strCmd += blesdk.addCPCLText(10,0 + (y*index),'4','3',0,'8.14');
strCmd += blesdk.addCPCLBarCode(270,0 + (y*index),'128',80,0,1,1,'00051');
strCmd += blesdk.addCPCLText(290,80 + (y*index),'7','2',0,'00051');
strCmd += blesdk.addCPCLText(40,110 + (y*index),'3','0',0,'CHICKEN FEET (BONELESS)');
// strCmd += blesdk.addCPCLSETMAG(2,2);
strCmd += blesdk.addCPCLText(40,150 + (y*index),'55','0',0,'无骨鸡爪 一盒(约1.5磅)');
// strCmd += blesdk.addCPCLSETMAG(0,0);
strCmd += blesdk.addCPCLText(0,180 + (y*index),'7','2',0,'2019-08-12');
strCmd += blesdk.addCPCLLocation(2);//
strCmd += blesdk.addCPCLQRCode(0,220 + (y*index),'M', 2, 6, 'qr code test');
})
console.log(strCmd)
strCmd += blesdk.addCPCLPrint();
this.bufferData = strCmd;
}
}
}
</script>
<style>
.content {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.btn-wrap{
width:180upx;
height: 100upx;
border-radius: 16upx;
border: 2upx solid #333333;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
</style>
Loading…
Cancel
Save