位置: IT常识 - 正文

uniapp微信小程序实现连接低功耗蓝牙打印功能(uniapp微信小程序支付流程)

发布时间:2023-12-23
uniapp微信小程序实现连接低功耗蓝牙打印功能

推荐整理分享uniapp微信小程序实现连接低功耗蓝牙打印功能(uniapp微信小程序支付流程),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:uniapp微信小程序获取手机号,uniapp微信小程序头像获取与服务器对接,uniapp微信小程序获取手机号,uniapp微信小程序获取手机号,uniapp微信小程序获取手机号,uniapp微信小程序兼容,uniapp微信小程序获取手机号,uniapp微信小程序获取手机号,内容如对您有帮助,希望把文章链接给更多的朋友!

微信小程序项目中有使用到蓝牙连接打印,参考官方文档做了一个参考笔记,这样使用的时候就按着步骤查看。

uni-app蓝牙连接蓝牙:1、初始化蓝牙

uni.openBluetoothAdapter(OBJECT)

uni.openBluetoothAdapter({ success(res) { console.log(res) // 接口调用成功的回调函数 }, fail:(res)=>{ // 接口调用失败的回调函数 }, complete:()=>{// 接口调用结束的回调函数(调用成功、失败都会执行) }})

错误 :res.errCode

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

注意

其他蓝牙相关 API 必须在 uni.openBluetoothAdapter 调用之后使用。否则 API 会返回错误(errCode=10000)。在用户蓝牙开关未开启或者手机不支持蓝牙功能的情况下,调用 uni.openBluetoothAdapter 会返回错误(errCode=10001),表示手机蓝牙功能不可用。此时APP蓝牙模块已经初始化完成,可通过 uni.onBluetoothAdapterStateChange 监听手机蓝牙状态的改变,也可以调用蓝牙模块的所有API。2、监听蓝牙适配器状态变化事件

uni.onBluetoothAdapterStateChange(CALLBACK)

CALLBACK 返回参数

属性类型说明availableboolean蓝牙适配器是否可用discoveringboolean蓝牙适配器是否处于搜索状态

示例代码

uni.onBluetoothAdapterStateChange((res) => { console.log('onBluetoothAdapterStateChange', res) // available:蓝牙适配器是否可用 if (res.available) { // 取消监听,否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange, // 导致再次调用startBluetoothDevicesDiscovery uni.onBluetoothAdapterStateChange(() => {}); // 开始搜寻附近的蓝牙外围设备 uni.startBluetoothDevicesDiscovery(OBJECT) }})3、开始搜寻附近的蓝牙外围设备

uni.startBluetoothDevicesDiscovery(OBJECT)

此操作比较耗费系统资源,请在搜索并连接到设备后调用 uni.stopBluetoothDevicesDiscovery 方法停止搜索。

OBJECT 参数说明

属性类型默认值必填说明servicesArray否要搜索但蓝牙设备主 service 的 uuid 列表。某些蓝牙设备会广播自己的主 service 的 uuid。如果设置此参数,则只搜索广播包有对应 uuid 的主服务的蓝牙设备。建议主要通过该参数过滤掉周边不需要处理的其他蓝牙设备。allowDuplicatesKeybooleanfalse否是否允许重复上报同一设备。如果允许重复上报,则 uni.onBlueToothDeviceFound 方法会多次上报同一设备,但是 RSSI 值会有不同。intervalnumber0否上报设备的间隔。0 表示找到新设备立即上报,其他数值根据传入的间隔上报。successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

注意:

App 端目前仅支持发现ble蓝牙设备,更多蓝牙设备发现,可以使用 Native.js,参考:https://ask.dcloud.net.cn/article/114。也可以在插件市场获取原生插件

示例代码

// 以微信硬件平台的蓝牙智能灯为例,主服务的 UUID 是 FEE7。传入这个参数,只搜索主服务 UUID 为 FEE7 的设备uni.startBluetoothDevicesDiscovery({ services: ['FEE7'], success(res) { console.log(res) // 监听寻找到新设备的事件 uni.onBluetoothDeviceFound(OBJECT) }})4、监听寻找到新设备的事件

uni.onBluetoothDeviceFound(CALLBACK)

CALLBACK 返回参数

属性类型说明devicesArray新搜索到的设备列表

devices 的结构

属性类型说明namestring蓝牙设备名称,某些设备可能没有deviceIdstring用于区分设备的 idRSSInumber当前蓝牙设备的信号强度advertisDataArrayBuffer当前蓝牙设备的广播数据段中的 ManufacturerData 数据段。advertisServiceUUIDsArray当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段localNamestring当前蓝牙设备的广播数据段中的 LocalName 数据段serviceDataObject当前蓝牙设备的广播数据段中的 ServiceData 数据段

注意

若在 uni.onBluetoothDeviceFound 回调了某个设备,则此设备会添加到 uni.getBluetoothDevices 接口获取到的数组中。

示例代码

// ArrayBuffer转16进度字符串示例function ab2hex(buffer) { const hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join('')}uni.onBluetoothDeviceFound(function (devices) { console.log('new device list has founded') console.dir(devices) console.log(ab2hex(devices[0].advertisData))})5、停止搜寻附近的蓝牙外围设备

uni.stopBluetoothDevicesDiscovery(OBJECT)

若已经找到需要的蓝牙设备并不需要继续搜索时,建议调用该接口停止蓝牙搜索。

OBJECT 参数说明

属性类型默认值必填说明successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.stopBluetoothDevicesDiscovery({ success(res) { console.log(res) }})6、根据 uuid 获取处于已连接状态的设备

uni.getConnectedBluetoothDevices(OBJECT)

OBJECT 参数说明

属性类型默认值必填说明servicesArray是蓝牙设备主 service 的 uuid 列表successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明:

属性类型说明devicesArray搜索到的设备列表

res.devices 的结构

属性类型说明namestring蓝牙设备名称,某些设备可能没有deviceIdstring用于区分设备的 id

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.getConnectedBluetoothDevices({ success(res) { console.log(res) }})7、获取在蓝牙模块生效期间所有已发现的蓝牙设备

uni.getBluetoothDevices(OBJECT)

包括已经和本机处于连接状态的设备。

OBJECT 参数说明

属性类型默认值必填说明successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明:

属性类型说明devicesArrayuuid 对应的的已连接设备列表

res.devices 的结构

属性类型说明namestring蓝牙设备名称,某些设备可能没有deviceIdstring用于区分设备的 idRSSInumber当前蓝牙设备的信号强度advertisDataArrayBuffer当前蓝牙设备的广播数据段中的 ManufacturerData 数据段。advertisServiceUUIDsArray当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段localNamestring当前蓝牙设备的广播数据段中的 LocalName 数据段serviceDataObject当前蓝牙设备的广播数据段中的 ServiceData 数据段

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

// ArrayBuffer转16进度字符串示例function ab2hex(buffer) { const hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join('')}uni.getBluetoothDevices({ success(res) { console.log(res) if (res.devices[0]) { console.log(ab2hex(res.devices[0].advertisData)) } }})

注意

该接口获取到的设备列表为蓝牙模块生效期间所有搜索到的蓝牙设备,若在蓝牙模块使用流程结束后未及时调用 uni.closeBluetoothAdapter 释放资源,会存在调用该接口会返回之前的蓝牙使用流程中搜索到的蓝牙设备,可能设备已经不在用户身边,无法连接。蓝牙设备在被搜索到时,系统返回的 name 字段一般为广播包中的 LocalName 字段中的设备名称,而如果与蓝牙设备建立连接,系统返回的 name 字段会改为从蓝牙设备上获取到的 GattName。若需要动态改变设备名称并展示,建议使用 localName 字段。8、获取本机蓝牙适配器状态

uni.getBluetoothAdapterState(OBJECT)

OBJECT 参数说明

属性类型默认值必填说明successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明:

属性类型说明discoveringboolean是否正在搜索设备availableboolean蓝牙适配器是否可用

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.getBluetoothAdapterState({ success(res) { console.log(res) }})9、关闭蓝牙模块

uni.closeBluetoothAdapter(OBJECT)

调用该方法将断开所有已建立的连接并释放系统资源。建议在使用蓝牙流程后,与 uni.openBluetoothAdapter 成对调用。

OBJECT 参数说明

属性类型默认值必填说明successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.closeBluetoothAdapter({ success(res) { console.log(res) }})低功耗蓝牙:1、设置蓝牙最大传输单元

[uni.setBLEMTU(OBJECT)

需在 uni.createBLEConnection调用成功后调用,mtu 设置范围 (22,512)。安卓5.1以上有效。

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是用于区分设备的 idmtunumber是最大传输单元(22,512) 区间内,单位 bytessuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)2、向低功耗蓝牙设备特征值中写入二进制数据

uni.writeBLECharacteristicValue(OBJECT)

注意:必须设备的特征值支持 write 才可以成功调用。

uniapp微信小程序实现连接低功耗蓝牙打印功能(uniapp微信小程序支付流程)

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是蓝牙设备 idserviceIdstring是蓝牙特征值对应服务的 uuidcharacteristicIdstring是蓝牙特征值的 uuidvalueArrayBuffer是蓝牙设备特征值对应的二进制值successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

注意

并行调用多次会存在写失败的可能性。APP不会对写入数据包大小做限制,但系统与蓝牙设备会限制蓝牙4.0单次传输的数据大小,超过最大字节数后会发生写入错误,建议每次写入不超过20字节。若单次写入数据过长,iOS 上存在系统不会有任何回调的情况(包括错误回调)。安卓平台上,在调用 notifyBLECharacteristicValueChange 成功后立即调用 writeBLECharacteristicValue 接口,在部分机型上会发生 10008 系统错误

示例代码

// 向蓝牙设备发送一个0x00的16进制数据const buffer = new ArrayBuffer(1)const dataView = new DataView(buffer)dataView.setUint8(0, 0)uni.writeBLECharacteristicValue({ // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取 deviceId, // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取 serviceId, // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取 characteristicId, // 这里的value是ArrayBuffer类型 value: buffer, success(res) { console.log('writeBLECharacteristicValue success', res.errMsg) }})3、读取低功耗蓝牙设备的特征值的二进制数据值

uni.readBLECharacteristicValue(OBJECT)

注意:必须设备的特征值支持 read 才可以成功调用。

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是蓝牙设备 idserviceIdstring是蓝牙特征值对应服务的 uuidcharacteristicIdstring是蓝牙特征值的 uuidsuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

注意

并行调用多次会存在读失败的可能性。接口读取到的信息需要在 onBLECharacteristicValueChange 方法注册的回调中获取。

示例代码

// 必须在这里的回调才能获取uni.onBLECharacteristicValueChange(function (characteristic) { console.log('characteristic value comed:', characteristic)})uni.readBLECharacteristicValue({ // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接 deviceId, // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取 serviceId, // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取 characteristicId, success(res) { console.log('readBLECharacteristicValue:', res.errCode) }})4、监听低功耗蓝牙连接状态的改变事件

uni.onBLEConnectionStateChange(CALLBACK)

包括开发者主动连接或断开连接,设备丢失,连接异常断开等等

CALLBACK 返回参数

属性类型说明deviceIdstring蓝牙设备IDconnectedboolean是否处于已连接状态

示例代码

uni.onBLEConnectionStateChange(function (res) { // 该方法回调中可以用于处理连接意外断开等异常情况 console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)})5、监听低功耗蓝牙设备的特征值变化事件

uni.onBLECharacteristicValueChange(CALLBACK)

必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。

CALLBACK 返回参数

属性类型说明deviceIdstring蓝牙设备 idserviceIdstring蓝牙特征值对应服务的 uuidcharacteristicIdstring蓝牙特征值的 uuidvalueArrayBuffer特征值最新的值

示例代码

// ArrayBuffer转16进度字符串示例function ab2hex(buffer) { const hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join('')}uni.onBLECharacteristicValueChange(function (res) { console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`) console.log(ab2hex(res.value))})6、订阅特征值

uni.notifyBLECharacteristicValueChange(OBJECT)

启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。注意:必须设备的特征值支持 notify 或者 indicate 才可以成功调用。 另外,必须先启用 notifyBLECharacteristicValueChange 才能监听到设备 characteristicValueChange 事件

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是蓝牙设备 idserviceIdstring是蓝牙特征值对应服务的 uuidcharacteristicIdstring是蓝牙特征值的 uuidstateboolean是是否启用 notifysuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

注意

订阅操作成功后需要设备主动更新特征值的 value,才会触发 uni.onBLECharacteristicValueChange 回调。安卓平台上,在调用 notifyBLECharacteristicValueChange 成功后立即调用 writeBLECharacteristicValue 接口,在部分机型上会发生 10008 系统错误

示例代码

uni.notifyBLECharacteristicValueChange({ state: true, // 启用 notify 功能 // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接 deviceId, // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取 serviceId, // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取 characteristicId, success(res) { console.log('notifyBLECharacteristicValueChange success', res.errMsg) }})7、获取蓝牙设备所有服务(service)

uni.getBLEDeviceServices(OBJECT)

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是蓝牙设备 idsuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明:

属性类型说明servicesArray设备服务列表

res.services 的结构

属性类型说明uuidstring蓝牙设备服务的 uuidisPrimaryboolean该服务是否为主服务

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.getBLEDeviceServices({ // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接 deviceId, success(res) { console.log('device services:', res.services) }})8、获取蓝牙设备的信号强度

uni.getBLEDeviceRSSI(OBJECT)

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是蓝牙设备 idsuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)9、获取蓝牙设备某个服务中所有特征值(characteristic)。

uni.getBLEDeviceCharacteristics(OBJECT)

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是蓝牙设备 idserviceIdstring是蓝牙服务 uuid,需要使用 getBLEDeviceServices 获取successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

success 返回参数说明:

属性类型说明characteristicsArray设备服务列表

res.characteristics 的结构

属性类型说明uuidstring蓝牙设备特征值的 uuidpropertiesObject该特征值支持的操作类型

properties 的结构

属性类型说明readboolean该特征值是否支持 read 操作writeboolean该特征值是否支持 write 操作notifyboolean该特征值是否支持 notify 操作indicateboolean该特征值是否支持 indicate 操作

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.getBLEDeviceCharacteristics({ // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接 deviceId, // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取 serviceId, success(res) { console.log('device getBLEDeviceCharacteristics:', res.characteristics) }})10、连接低功耗蓝牙设备

uni.createBLEConnection(OBJECT)

若APP在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需进行搜索操作。

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是用于区分设备的 idtimeoutnumber否超时时间,单位ms,不填表示不会超时successfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

注意

请保证尽量成对的调用 createBLEConnection 和 closeBLEConnection 接口。安卓如果多次调用 createBLEConnection 创建连接,有可能导致系统持有同一设备多个连接的实例,导致调用 closeBLEConnection 的时候并不能真正的断开与设备的连接。蓝牙连接随时可能断开,建议监听 uni.onBLEConnectionStateChange 回调事件,当蓝牙设备断开时按需执行重连操作若对未连接的设备或已断开连接的设备调用数据读写操作的接口,会返回 10006 错误,建议进行重连操作。

示例代码

uni.createBLEConnection({ // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接 deviceId, success(res) { console.log(res) }})11、断开与低功耗蓝牙设备的连接。

uni.closeBLEConnection(OBJECT)

OBJECT 参数说明

属性类型默认值必填说明deviceIdstring是用于区分设备的 idsuccessfunction否接口调用成功的回调函数failfunction否接口调用失败的回调函数completefunction否接口调用结束的回调函数(调用成功、失败都会执行)

错误

错误码错误信息说明0ok正常10000not init未初始化蓝牙适配器10001not available当前蓝牙适配器不可用10002no device没有找到指定设备10003connection fail连接失败10004no service没有找到指定服务10005no characteristic没有找到指定特征值10006no connection当前连接已断开10007property not support当前特征值不支持此操作10008system error其余所有系统上报的异常10009system not supportAndroid 系统特有,系统版本低于 4.3 不支持 BLE

示例代码

uni.closeBLEConnection({ deviceId, success(res) { console.log(res) }})实现代码:<template><!-- 蓝牙打印页面 --><view class="page"><button @click="openBluetoothAdapter">重新查询</button><view class="title">可连接的蓝牙设备列表:<text style="color: red;font-size:22rpx;">(部分机型需要打开GPS定位服务)</text></view><view class="list"><view class="item" v-for="(item, i) in devices" :key="i"><!-- 设备名称 --><text>{{ item.name }}</text><!-- 连接状态 --><view class="right"><view class="font-color-3" @click="createBLEConnection(item)" v-show="!item.isShowConnect">连接设备</view><view class="font-color-3" v-show="item.isShowConnect">已连接</view></view></view><!-- v-if="devices.length" --><button :class="isDisabled || isConnected ? 'submit disabled' : 'submit'" @click="writeBLECharacteristicValue" :disabled="isDisabled || isConnected">开始打印</button><view class="no-devices" v-if="!devices.length">未搜索到蓝牙设备</view></view></view></template><script>const LAST_CONNECTED_DEVICE = 'last_connected_device';import PrinterJobs from '../../common/printer/printerjobs';import printerUtil from '../../common/printer/printerutil';function inArray(arr, key, val) {for (let i = 0; i < arr.length; i++) {if (arr[i][key] === val) {return i;}}return -1;}// ArrayBuffer转16进度字符串示例function ab2hex(buffer) {const hexArr = Array.prototype.map.call(new Uint8Array(buffer), function(bit) {return ('00' + bit.toString(16)).slice(-2);});return hexArr.join(',');}function str2ab(str) {// Convert str to ArrayBuff and write to printerlet buffer = new ArrayBuffer(str.length);let dataView = new DataView(buffer);for (let i = 0; i < str.length; i++) {dataView.setUint8(i, str.charAt(i).charCodeAt(0));}return buffer;}export default {name: 'print',components: {},props: {},data() {return {devices: [],connected: false,isConnected: true,name: '',deviceId: null};},onLoad() {},onShow() {},created() {},mounted() {this.openBluetoothAdapter();},methods: {// 初始化蓝牙openBluetoothAdapter() {console.log('初始化蓝牙模块 openBluetoothAdapter');if (!uni.openBluetoothAdapter) {console.log('微信版本过低');uni.showModal({title: '提示',content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'});return;}uni.showLoading({title: '开始搜索蓝牙设备'});// uni.hideLoading();uni.openBluetoothAdapter({success: res => {console.log('初始化成功openBluetoothAdapter success', res);uni.hideLoading();// 搜寻附近的蓝牙this.startBluetoothDevicesDiscovery();},fail: res => {console.log('初始化失败openBluetoothAdapter fail', res);// uni.showModal({// content: res.errMsg,// showCancel: false// });uni.hideLoading();if (res.errCode === 10001) {// 当前蓝牙适配器不可用uni.showModal({title: '错误',content: '未找到蓝牙设备, 请打开蓝牙后重试。',showCancel: false});// 监听蓝牙适配器状态变化事件uni.onBluetoothAdapterStateChange(res => {console.log('监听蓝牙适配器状态 onBluetoothAdapterStateChange', res);// available:蓝牙适配器是否可用if (res.available) {// 取消监听,否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange,// 导致再次调用startBluetoothDevicesDiscovery// uni.onBluetoothAdapterStateChange(() => {});// 开始搜寻附近的蓝牙外围设备this.startBluetoothDevicesDiscovery();}});}}});},// 开始搜寻附近的蓝牙外围设备startBluetoothDevicesDiscovery() {console.log('开始搜寻附近的蓝牙设备');uni.startBluetoothDevicesDiscovery({allowDuplicatesKey: false,interval: 0,success: res => {console.log('搜寻附近的蓝牙外围设备 startBluetoothDevicesDiscovery success111', res);// 监听寻找到新设备的事件this.onBluetoothDeviceFound();},fail: res => {console.log('搜寻附近的蓝牙外围设备 startBluetoothDevicesDiscovery fail', res);uni.hideLoading();}});},// 监听寻找到新设备的事件onBluetoothDeviceFound() {console.log('进入查询设备');uni.onBluetoothDeviceFound(res => {console.log('寻找设备', res.devices);res.devices.forEach(device => {if (!device.name && !device.localName) {return;}const foundDevices = this.devices;// 在数组中查找指定值,并返回它的索引值(如果没有找到,则返回-1)const idx = inArray(foundDevices, 'deviceId', device.deviceId);const data = {};if (idx === -1) {this.$set(this.devices, `${foundDevices.length}`, device);} else {this.$set(this.devices, `${idx}`, device);}console.log('搜索结果', this.devices);uni.hideLoading();});});},// this.devices是蓝牙设备列表,渲染到页面显示点击执行蓝牙连接// 点击链接蓝牙createBLEConnection(e) {console.log('点击连接蓝牙', e);const deviceId = e.deviceId;const name = e.name;this._createBLEConnection(deviceId, name);},_createBLEConnection(deviceId, name) {this.$myToast('连接设备中', 'loading');// 连接低功耗蓝牙设备uni.createBLEConnection({deviceId, // 用于区分设备的 idsuccess: () => {console.log('连接蓝牙接口调用成功 createBLEConnection success', this.devices);this.devices.forEach((item, index) => {this.$set(this.devices[index], 'isShowConnect', false);if (item.deviceId === deviceId) {this.$set(this.devices[index], 'isShowConnect', true);}});this.$myToast('设备连接成功', 'success');this.connected = true;this.isConnected = false;this.name = name;this.deviceId = deviceId;// 获取蓝牙设备所有服务(service)this.getBLEDeviceServices(deviceId);// 最后连接设备uni.setStorage({key: LAST_CONNECTED_DEVICE,data: name + ':' + deviceId});},complete() {uni.hideLoading();},fail: res => {// 连接蓝牙接口调用失败console.log('连接蓝牙接口调用失败 createBLEConnection fail', res);uni.showModal({title: this.$t('wechat.w227'),content: '蓝牙连接失败',showCancel: false});}});// 已经找到需要的蓝牙设备,停止搜寻附近的蓝牙外围设备this.stopBluetoothDevicesDiscovery();},// 获取蓝牙设备所有服务(service)getBLEDeviceServices(deviceId) {uni.getBLEDeviceServices({deviceId,success: res => {console.log('获取蓝牙设备所有服务 getBLEDeviceServices', res);for (let i = 0; i < res.services.length; i++) {if (res.services[i].isPrimary) {this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);return;}}}});},stopBluetoothDevicesDiscovery() {uni.stopBluetoothDevicesDiscovery({complete: () => {// console.log('stopBluetoothDevicesDiscovery')this._discoveryStarted = false;}});},/*获取蓝牙设备某个服务中所有特征值(characteristic)characteristic:uuid:蓝牙设备特征值的 uuidproperties:该特征值支持的操作类型*/getBLEDeviceCharacteristics(deviceId, serviceId) {uni.getBLEDeviceCharacteristics({// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接deviceId,// 这里的 serviceId(蓝牙服务 uuid) 需要在 getBLEDeviceServices 接口中获取serviceId,success: res => {console.log('特征值变化 getBLEDeviceCharacteristics success', res.characteristics);// 这里会存在特征值是支持write,写入成功但是没有任何反应的情况// 只能一个个去试// characteristics:设备服务列表for (let i = 0; i < res.characteristics.length; i++) {const item = res.characteristics[i];// if (item.properties.read) {// uni.readBLECharacteristicValue({// deviceId,// serviceId,// characteristicId: item.uuid// })// }if (item.properties.write) {this.canWrite = true;this._deviceId = deviceId;this._serviceId = serviceId;this._characteristicId = item.uuid;}if (item.properties.notify || item.properties.indicate) {uni.notifyBLECharacteristicValueChange({deviceId,serviceId,characteristicId: item.uuid,state: true});}if (item.properties.write) {this.canWrite = true;this._deviceId = deviceId;this._serviceId = serviceId;this._characteristicId = item.uuid;}if (item.properties.notify || item.properties.indicate) {uni.notifyBLECharacteristicValueChange({deviceId,serviceId,characteristicId: item.uuid,state: true});}}},fail(res) {console.error('特征值变化 getBLEDeviceCharacteristics', res);}});},// 蓝牙连接成功后点击打印,打印数据// 点击打印:写入数据(根据项目需要打印内容来实现)writeBLECharacteristicValue() {console.log('写数据');let printerJobs = new PrinterJobs();// 要打印的信息printerJobs.setAlign('ct').setSize(2, 2).print('记录报告').setSize(0, 0).print().setAlign('lt');// 打印printerJobs.print(printerUtil.fillLine());// 结尾printerJobs.println().print('签名').println().println();let buffer = printerJobs.buffer();// console.log('ArrayBuffer', 'length: ' + buffer.byteLength, ' hex: ' + ab2hex(buffer));// 1.并行调用多次会存在写失败的可能性// 2.建议每次写入不超过20字节// 分包处理,延时调用const maxChunk = 20;const delay = 40;console.log(111111);for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {let subPackage = buffer.slice(i, i + maxChunk <= length ? i + maxChunk : length);// subPackage:参数setTimeout(this._writeBLECharacteristicValue, j * delay, subPackage);}console.log(22222);},// 向低功耗蓝牙设备特征值中写入二进制数据。注意:必须设备的特征值支持 write 才可以成功调用。_writeBLECharacteristicValue(buffer) {console.log('写入数据');uni.writeBLECharacteristicValue({deviceId: this._deviceId, // 蓝牙设备 idserviceId: this._serviceId, // 蓝牙特征值对应服务的 uuidcharacteristicId: this._characteristicId, // 蓝牙特征值的 uuidvalue: buffer, // 蓝牙设备特征值对应的二进制值success(res) {console.log('writeBLECharacteristicValue success', res);},fail(res) {console.log('writeBLECharacteristicValue fail', res);}});}}};</script><style scoped lang="scss">.page {margin: 20rpx;color: #323232;background-color: #f5f9ff;height: 100vh;}.title {font-weight: 600;margin: 20rpx 0rpx;}.list {.item {display: flex;justify-content: space-between;padding: 10rpx 20rpx;height: 60rpx;line-height: 60rpx;background-color: #ffffff;margin-bottom: 4rpx;}.right {}.no-devices {height: 400rpx;font-size: 32rpx;line-height: 400rpx;text-align: center;color: #969696;}}.font-color-3 {color: #1a8cff;}.submit {background-color: #4d88ff !important;color: #f5f9ff !important;}.disabled {background-color: #66b1ff !important;}</style>

页面布局效果:

可根据项目具体需求对打印数据做处理,打印效果:

其他文件:

printerutil.js// 打印机纸宽58mm,页的宽度384,字符宽度为1,每行最多盛放32个字符const PAGE_WIDTH = 384;const MAX_CHAR_COUNT_EACH_LINE = 32;/** * @param str * @returns {boolean} str是否全是中文 */function isChinese(str) { return /^[\u4e00-\u9fa5]$/.test(str);}/** * 返回字符串宽度(1个中文=2个英文字符) * @param str * @returns {number} */function getStringWidth(str) { let width = 0; for (let i = 0, len = str.length; i < len; i++) { width += isChinese(str.charAt(i)) ? 2 : 1; } return width;}/** * 同一行输出str1, str2,str1居左, str2居右 * @param {string} str1 内容1 * @param {string} str2 内容2 * @param {number} fontWidth 字符宽度 1/2 * @param {string} fillWith str1 str2之间的填充字符 * */function inline(str1, str2, fillWith = ' ', fontWidth = 1) { const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth; // 需要填充的字符数量 let fillCount = lineWidth - (getStringWidth(str1) + getStringWidth(str2)) % lineWidth; let fillStr = new Array(fillCount).fill(fillWith.charAt(0)).join(''); return str1 + fillStr + str2;}/** * 用字符填充一整行 * @param {string} fillWith 填充字符 * @param {number} fontWidth 字符宽度 1/2 */function fillLine(fillWith = '-', fontWidth = 1) { const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth; return new Array(lineWidth).fill(fillWith.charAt(0)).join('');}/** * 文字内容居中,左右用字符填充 * @param {string} str 文字内容 * @param {number} fontWidth 字符宽度 1/2 * @param {string} fillWith str1 str2之间的填充字符 */function fillAround(str, fillWith = '-', fontWidth = 1) { const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth; let strWidth = getStringWidth(str); // 内容已经超过一行了,没必要填充 if (strWidth >= lineWidth) { return str; } // 需要填充的字符数量 let fillCount = lineWidth - strWidth; // 左侧填充的字符数量 let leftCount = Math.round(fillCount / 2); // 两侧的填充字符,需要考虑左边需要填充,右边不需要填充的情况 let fillStr = new Array(leftCount).fill(fillWith.charAt(0)).join(''); return fillStr + str + fillStr.substr(0, fillCount - leftCount);}module.exports = { inline: inline, fillLine: fillLine, fillAround: fillAround,};printerjobs.jsconst commands = require('./commands.js');const TextEncoder = require('../text-encoding/index').TextEncoder;const printerJobs = function () { this._queue = Array.from(commands.HARDWARE.HW_INIT); this._encoder = new TextEncoder("gb2312", {NONSTANDARD_allowLegacyEncoding: true}); this._enqueue = function (cmd) { this._queue.push.apply(this._queue, cmd); }};/** * 增加打印内容 * @param {string} content 文字内容 */printerJobs.prototype.text = function (content) { if (content) { let uint8Array = this._encoder.encode(content); let encoded = Array.from(uint8Array); this._enqueue(encoded); } return this;};/** * 打印文字 * @param {string} content 文字内容 */printerJobs.prototype.print = function (content) { this.text(content); this._enqueue(commands.LF); return this;};/** * 打印文字并换行 * @param {string} content 文字内容 */printerJobs.prototype.println = function (content = '') { return this.print(content + commands.EOL);};/** * 设置对齐方式 * @param {string} align 对齐方式 LT/CT/RT */printerJobs.prototype.setAlign = function (align) { this._enqueue(commands.TEXT_FORMAT['TXT_ALIGN_' + align.toUpperCase()]); return this;};/** * 设置字体 * @param {string} family A/B/C */printerJobs.prototype.setFont = function (family) { this._enqueue(commands.TEXT_FORMAT['TXT_FONT_' + family.toUpperCase()]); return this;};/** * 设定字体尺寸 * @param {number} width 字体宽度 1~2 * @param {number} height 字体高度 1~2 */printerJobs.prototype.setSize = function (width, height) { if (2 >= width && 2 >= height) { this._enqueue(commands.TEXT_FORMAT.TXT_NORMAL); if (2 === width && 2 === height) { this._enqueue(commands.TEXT_FORMAT.TXT_4SQUARE); } else if (1 === width && 2 === height) { this._enqueue(commands.TEXT_FORMAT.TXT_2HEIGHT); } else if (2 === width && 1 === height) { this._enqueue(commands.TEXT_FORMAT.TXT_2WIDTH); } } return this;};/** * 设定字体是否加粗 * @param {boolean} bold */printerJobs.prototype.setBold = function (bold) { if (typeof bold !== 'boolean') { bold = true; } this._enqueue(bold ? commands.TEXT_FORMAT.TXT_BOLD_ON : commands.TEXT_FORMAT.TXT_BOLD_OFF); return this;};/** * 设定是否开启下划线 * @param {boolean} underline */printerJobs.prototype.setUnderline = function (underline) { if (typeof underline !== 'boolean') { underline = true; } this._enqueue(underline ? commands.TEXT_FORMAT.TXT_UNDERL_ON : commands.TEXT_FORMAT.TXT_UNDERL_OFF); return this;};/** * 设置行间距为 n 点行,默认值行间距是 30 点 * @param {number} n 0≤n≤255 */printerJobs.prototype.setLineSpacing = function (n) { if (n === undefined || n === null) { this._enqueue(commands.LINE_SPACING.LS_DEFAULT); } else { this._enqueue(commands.LINE_SPACING.LS_SET); this._enqueue([n]); } return this;};/** * 打印空行 * @param {number} n */printerJobs.prototype.lineFeed = function (n = 1) { return this.print(new Array(n).fill(commands.EOL).join(''));};/** * 设置字体颜色,需要打印机支持 * @param {number} color - 0 默认颜色黑色 1 红色 */printerJobs.prototype.setColor = function (color) { this._enqueue(commands.COLOR[color === 1 ? 1 : 0]); return this;};/** * https://support.loyverse.com/hardware/printers/use-the-beeper-in-a-escpos-printers * 蜂鸣警报,需要打印机支持 * @param {number} n 蜂鸣次数,1-9 * @param {number} t 蜂鸣长短,1-9 */printerJobs.prototype.beep = function (n, t) { this._enqueue(commands.BEEP); this._enqueue([n, t]); return this;};/** * 清空任务 */printerJobs.prototype.clear = function () { this._queue = Array.from(commands.HARDWARE.HW_INIT); return this;};/** * 返回ArrayBuffer */printerJobs.prototype.buffer = function () { return new Uint8Array(this._queue).buffer;};module.exports = printerJobs;commands.js/** * 修改自https://github.com/song940/node-escpos/blob/master/commands.js * ESC/POS _ (Constants) */var _ = { LF: [0x0a], FS: [0x1c], FF: [0x0c], GS: [0x1d], DLE: [0x10], EOT: [0x04], NUL: [0x00], ESC: [0x1b], EOL: '\n',};/** * [FEED_CONTROL_SEQUENCES Feed control sequences] * @type {Object} */_.FEED_CONTROL_SEQUENCES = { CTL_LF: [0x0a], // Print and line feed 打印换行 CTL_GLF: [0x4a, 0x00], // Print and feed paper (without spaces between lines) 打印和送纸(行间无空格) CTL_FF: [0x0c], // Form feed 换页 CTL_CR: [0x0d], // Carriage return CTL_HT: [0x09], // Horizontal tab 回车 CTL_VT: [0x0b], // Vertical tab 垂直制表符};_.CHARACTER_SPACING = { CS_DEFAULT: [0x1b, 0x20, 0x00], CS_SET: [0x1b, 0x20]};_.LINE_SPACING = { LS_DEFAULT: [0x1b, 0x32], LS_SET: [0x1b, 0x33]};/** * [HARDWARE Printer hardware] * @type {Object} */_.HARDWARE = { HW_INIT: [0x1b, 0x40], // Clear data in buffer and reset modes HW_SELECT: [0x1b, 0x3d, 0x01], // Printer select HW_RESET: [0x1b, 0x3f, 0x0a, 0x00], // Reset printer hardware};/** * [CASH_DRAWER Cash Drawer] * @type {Object} */_.CASH_DRAWER = { CD_KICK_2: [0x1b, 0x70, 0x00], // Sends a pulse to pin 2 [] CD_KICK_5: [0x1b, 0x70, 0x01], // Sends a pulse to pin 5 []};/** * [MARGINS Margins sizes] * @type {Object} */_.MARGINS = { BOTTOM: [0x1b, 0x4f], // Fix bottom size LEFT: [0x1b, 0x6c], // Fix left size RIGHT: [0x1b, 0x51], // Fix right size};/** * [PAPER Paper] * @type {Object} */_.PAPER = { PAPER_FULL_CUT: [0x1d, 0x56, 0x00], // Full cut paper PAPER_PART_CUT: [0x1d, 0x56, 0x01], // Partial cut paper PAPER_CUT_A: [0x1d, 0x56, 0x41], // Partial cut paper PAPER_CUT_B: [0x1d, 0x56, 0x42], // Partial cut paper};/** * [TEXT_FORMAT Text format] * @type {Object} */_.TEXT_FORMAT = { TXT_NORMAL: [0x1b, 0x21, 0x00], // Normal text TXT_2HEIGHT: [0x1b, 0x21, 0x10], // Double height text 两倍高度的文本 TXT_2WIDTH: [0x1b, 0x21, 0x20], // Double width text 双宽度的文本 TXT_4SQUARE: [0x1b, 0x21, 0x30], // Double width & height text 双宽度和高度文本 TXT_UNDERL_OFF: [0x1b, 0x2d, 0x00], // Underline font OFF TXT_UNDERL_ON: [0x1b, 0x2d, 0x01], // Underline font 1-dot ON TXT_UNDERL2_ON: [0x1b, 0x2d, 0x02], // Underline font 2-dot ON TXT_BOLD_OFF: [0x1b, 0x45, 0x00], // Bold font OFF TXT_BOLD_ON: [0x1b, 0x45, 0x01], // Bold font ON TXT_ITALIC_OFF: [0x1b, 0x35], // Italic font ON TXT_ITALIC_ON: [0x1b, 0x34], // Italic font ON TXT_FONT_A: [0x1b, 0x4d, 0x00], // Font type A TXT_FONT_B: [0x1b, 0x4d, 0x01], // Font type B TXT_FONT_C: [0x1b, 0x4d, 0x02], // Font type C TXT_ALIGN_LT: [0x1b, 0x61, 0x00], // Left justification TXT_ALIGN_CT: [0x1b, 0x61, 0x01], // Centering TXT_ALIGN_RT: [0x1b, 0x61, 0x02], // Right justification};/** * [BARCODE_FORMAT Barcode format] * @type {Object} */_.BARCODE_FORMAT = { BARCODE_TXT_OFF: [0x1d, 0x48, 0x00], // HRI barcode chars OFF BARCODE_TXT_ABV: [0x1d, 0x48, 0x01], // HRI barcode chars above BARCODE_TXT_BLW: [0x1d, 0x48, 0x02], // HRI barcode chars below BARCODE_TXT_BTH: [0x1d, 0x48, 0x03], // HRI barcode chars both above and below BARCODE_FONT_A: [0x1d, 0x66, 0x00], // Font type A for HRI barcode chars BARCODE_FONT_B: [0x1d, 0x66, 0x01], // Font type B for HRI barcode chars BARCODE_HEIGHT: function (height) { // Barcode Height [1-255] return [0x1d, 0x68, height]; }, BARCODE_WIDTH: function (width) { // Barcode Width [2-6] return [0x1d, 0x77, width]; }, BARCODE_HEIGHT_DEFAULT: [0x1d, 0x68, 0x64], // Barcode height default:100 BARCODE_WIDTH_DEFAULT: [0x1d, 0x77, 0x01], // Barcode width default:1 BARCODE_UPC_A: [0x1d, 0x6b, 0x00], // Barcode type UPC-A BARCODE_UPC_E: [0x1d, 0x6b, 0x01], // Barcode type UPC-E BARCODE_EAN13: [0x1d, 0x6b, 0x02], // Barcode type EAN13 BARCODE_EAN8: [0x1d, 0x6b, 0x03], // Barcode type EAN8 BARCODE_CODE39: [0x1d, 0x6b, 0x04], // Barcode type CODE39 BARCODE_ITF: [0x1d, 0x6b, 0x05], // Barcode type ITF BARCODE_NW7: [0x1d, 0x6b, 0x06], // Barcode type NW7 BARCODE_CODE93: [0x1d, 0x6b, 0x48], // Barcode type CODE93 BARCODE_CODE128: [0x1d, 0x6b, 0x49], // Barcode type CODE128};/** * [IMAGE_FORMAT Image format] * @type {Object} */_.IMAGE_FORMAT = { S_RASTER_N: [0x1d, 0x76, 0x30, 0x00], // Set raster image normal size S_RASTER_2W: [0x1d, 0x76, 0x30, 0x01], // Set raster image double width S_RASTER_2H: [0x1d, 0x76, 0x30, 0x02], // Set raster image double height S_RASTER_Q: [0x1d, 0x76, 0x30, 0x03], // Set raster image quadruple};/** * [BITMAP_FORMAT description] * @type {Object} */_.BITMAP_FORMAT = { BITMAP_S8: [0x1b, 0x2a, 0x00], BITMAP_D8: [0x1b, 0x2a, 0x01], BITMAP_S24: [0x1b, 0x2a, 0x20], BITMAP_D24: [0x1b, 0x2a, 0x21]};/** * [GSV0_FORMAT description] * @type {Object} */_.GSV0_FORMAT = { GSV0_NORMAL: [0x1d, 0x76, 0x30, 0x00], GSV0_DW: [0x1d, 0x76, 0x30, 0x01], GSV0_DH: [0x1d, 0x76, 0x30, 0x02], GSV0_DWDH: [0x1d, 0x76, 0x30, 0x03]};/** * [BEEP description] * @type {string} */_.BEEP = [0x1b, 0x42]; // Printer Buzzer pre hex/** * [COLOR description] * @type {Object} */_.COLOR = { 0: [0x1b, 0x72, 0x00], // black 1: [0x1b, 0x72, 0x01] // red};/** * [exports description] * @type {[type]} */module.exports = _;

text-encoding/index

// This is free and unencumbered software released into the public domain.// See LICENSE.md for more information./** * @fileoverview Global |this| required for resolving indexes in node. * @suppress {globalThis} */(function(global) { 'use strict'; // If we're in node require encoding-indexes and attach it to the global. if (typeof module !== "undefined" && module.exports && !global["encoding-indexes"]) { global["encoding-indexes"] = require("./encoding-indexes.js")["encoding-indexes"]; } // // Utilities // /** * @param {number} a The number to test. * @param {number} min The minimum value in the range, inclusive. * @param {number} max The maximum value in the range, inclusive. * @return {boolean} True if a >= min and a <= max. */ function inRange(a, min, max) { return min <= a && a <= max; } /** * @param {!Array.<*>} array The array to check. * @param {*} item The item to look for in the array. * @return {boolean} True if the item appears in the array. */ function includes(array, item) { return array.indexOf(item) !== -1; } var floor = Math.floor; /** * @param {*} o * @return {Object} */ function ToDictionary(o) { if (o === undefined) return {}; if (o === Object(o)) return o; throw TypeError('Could not convert argument to dictionary'); } /** * @param {string} string Input string of UTF-16 code units. * @return {!Array.<number>} Code points. */ function stringToCodePoints(string) { // https://heycam.github.io/webidl/#dfn-obtain-unicode // 1. Let S be the DOMString value. var s = String(string); // 2. Let n be the length of S. var n = s.length; // 3. Initialize i to 0. var i = 0; // 4. Initialize U to be an empty sequence of Unicode characters. var u = []; // 5. While i < n: while (i < n) { // 1. Let c be the code unit in S at index i. var c = s.charCodeAt(i); // 2. Depending on the value of c: // c < 0xD800 or c > 0xDFFF if (c < 0xD800 || c > 0xDFFF) { // Append to U the Unicode character with code point c. u.push(c); } // 0xDC00 ≤ c ≤ 0xDFFF else if (0xDC00 <= c && c <= 0xDFFF) { // Append to U a U+FFFD REPLACEMENT CHARACTER. u.push(0xFFFD); } // 0xD800 ≤ c ≤ 0xDBFF else if (0xD800 <= c && c <= 0xDBFF) { // 1. If i = n−1, then append to U a U+FFFD REPLACEMENT // CHARACTER. if (i === n - 1) { u.push(0xFFFD); } // 2. Otherwise, i < n−1: else { // 1. Let d be the code unit in S at index i+1. var d = s.charCodeAt(i + 1); // 2. If 0xDC00 ≤ d ≤ 0xDFFF, then: if (0xDC00 <= d && d <= 0xDFFF) { // 1. Let a be c & 0x3FF. var a = c & 0x3FF; // 2. Let b be d & 0x3FF. var b = d & 0x3FF; // 3. Append to U the Unicode character with code point // 2^16+2^10*a+b. u.push(0x10000 + (a << 10) + b); // 4. Set i to i+1. i += 1; } // 3. Otherwise, d < 0xDC00 or d > 0xDFFF. Append to U a // U+FFFD REPLACEMENT CHARACTER. else { u.push(0xFFFD); } } } // 3. Set i to i+1. i += 1; } // 6. Return U. return u; } /** * @param {!Array.<number>} code_points Array of code points. * @return {string} string String of UTF-16 code units. */ function codePointsToString(code_points) { var s = ''; for (var i = 0; i < code_points.length; ++i) { var cp = code_points[i]; if (cp <= 0xFFFF) { s += String.fromCharCode(cp); } else { cp -= 0x10000; s += String.fromCharCode((cp >> 10) + 0xD800, (cp & 0x3FF) + 0xDC00); } } return s; } // // Implementation of Encoding specification // https://encoding.spec.whatwg.org/ // // // 4. Terminology // /** * An ASCII byte is a byte in the range 0x00 to 0x7F, inclusive. * @param {number} a The number to test. * @return {boolean} True if a is in the range 0x00 to 0x7F, inclusive. */ function isASCIIByte(a) { return 0x00 <= a && a <= 0x7F; } /** * An ASCII code point is a code point in the range U+0000 to * U+007F, inclusive. */ var isASCIICodePoint = isASCIIByte; /** * End-of-stream is a special token that signifies no more tokens * are in the stream. * @const */ var end_of_stream = -1; /** * A stream represents an ordered sequence of tokens. * * @constructor * @param {!(Array.<number>|Uint8Array)} tokens Array of tokens that provide * the stream. */ function Stream(tokens) { /** @type {!Array.<number>} */ this.tokens = [].slice.call(tokens); // Reversed as push/pop is more efficient than shift/unshift. this.tokens.reverse(); } Stream.prototype = { /** * @return {boolean} True if end-of-stream has been hit. */ endOfStream: function() { return !this.tokens.length; }, /** * When a token is read from a stream, the first token in the * stream must be returned and subsequently removed, and * end-of-stream must be returned otherwise. * * @return {number} Get the next token from the stream, or * end_of_stream. */ read: function() { if (!this.tokens.length) return end_of_stream; return this.tokens.pop(); }, /** * When one or more tokens are prepended to a stream, those tokens * must be inserted, in given order, before the first token in the * stream. * * @param {(number|!Array.<number>)} token The token(s) to prepend to the * stream. */ prepend: function(token) { if (Array.isArray(token)) { var tokens = /**@type {!Array.<number>}*/(token); while (tokens.length) this.tokens.push(tokens.pop()); } else { this.tokens.push(token); } }, /** * When one or more tokens are pushed to a stream, those tokens * must be inserted, in given order, after the last token in the * stream. * * @param {(number|!Array.<number>)} token The tokens(s) to push to the * stream. */ push: function(token) { if (Array.isArray(token)) { var tokens = /**@type {!Array.<number>}*/(token); while (tokens.length) this.tokens.unshift(tokens.shift()); } else { this.tokens.unshift(token); } } }; // // 5. Encodings // // 5.1 Encoders and decoders /** @const */ var finished = -1; /** * @param {boolean} fatal If true, decoding errors raise an exception. * @param {number=} opt_code_point Override the standard fallback code point. * @return {number} The code point to insert on a decoding error. */ function decoderError(fatal, opt_code_point) { if (fatal) throw TypeError('Decoder error'); return opt_code_point || 0xFFFD; } /** * @param {number} code_point The code point that could not be encoded. * @return {number} Always throws, no value is actually returned. */ function encoderError(code_point) { throw TypeError('The code point ' + code_point + ' could not be encoded.'); } /** @interface */ function Decoder() {} Decoder.prototype = { /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point, or |finished|. */ handler: function(stream, bite) {} }; /** @interface */ function Encoder() {} Encoder.prototype = { /** * @param {Stream} stream The stream of code points being encoded. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit, or |finished|. */ handler: function(stream, code_point) {} }; // 5.2 Names and labels // TODO: Define @typedef for Encoding: {name:string,labels:Array.<string>} // https://github.com/google/closure-compiler/issues/247 /** * @param {string} label The encoding label. * @return {?{name:string,labels:Array.<string>}} */ function getEncoding(label) { // 1. Remove any leading and trailing ASCII whitespace from label. label = String(label).trim().toLowerCase(); // 2. If label is an ASCII case-insensitive match for any of the // labels listed in the table below, return the corresponding // encoding, and failure otherwise. if (Object.prototype.hasOwnProperty.call(label_to_encoding, label)) { return label_to_encoding[label]; } return null; } /** * Encodings table: https://encoding.spec.whatwg.org/encodings.json * @const * @type {!Array.<{ * heading: string, * encodings: Array.<{name:string,labels:Array.<string>}> * }>} */ var encodings = [ { "encodings": [ { "labels": [ "unicode-1-1-utf-8", "utf-8", "utf8" ], "name": "UTF-8" } ], "heading": "The Encoding" }, { "encodings": [ { "labels": [ "866", "cp866", "csibm866", "ibm866" ], "name": "IBM866" }, { "labels": [ "csisolatin2", "iso-8859-2", "iso-ir-101", "iso8859-2", "iso88592", "iso_8859-2", "iso_8859-2:1987", "l2", "latin2" ], "name": "ISO-8859-2" }, { "labels": [ "csisolatin3", "iso-8859-3", "iso-ir-109", "iso8859-3", "iso88593", "iso_8859-3", "iso_8859-3:1988", "l3", "latin3" ], "name": "ISO-8859-3" }, { "labels": [ "csisolatin4", "iso-8859-4", "iso-ir-110", "iso8859-4", "iso88594", "iso_8859-4", "iso_8859-4:1988", "l4", "latin4" ], "name": "ISO-8859-4" }, { "labels": [ "csisolatincyrillic", "cyrillic", "iso-8859-5", "iso-ir-144", "iso8859-5", "iso88595", "iso_8859-5", "iso_8859-5:1988" ], "name": "ISO-8859-5" }, { "labels": [ "arabic", "asmo-708", "csiso88596e", "csiso88596i", "csisolatinarabic", "ecma-114", "iso-8859-6", "iso-8859-6-e", "iso-8859-6-i", "iso-ir-127", "iso8859-6", "iso88596", "iso_8859-6", "iso_8859-6:1987" ], "name": "ISO-8859-6" }, { "labels": [ "csisolatingreek", "ecma-118", "elot_928", "greek", "greek8", "iso-8859-7", "iso-ir-126", "iso8859-7", "iso88597", "iso_8859-7", "iso_8859-7:1987", "sun_eu_greek" ], "name": "ISO-8859-7" }, { "labels": [ "csiso88598e", "csisolatinhebrew", "hebrew", "iso-8859-8", "iso-8859-8-e", "iso-ir-138", "iso8859-8", "iso88598", "iso_8859-8", "iso_8859-8:1988", "visual" ], "name": "ISO-8859-8" }, { "labels": [ "csiso88598i", "iso-8859-8-i", "logical" ], "name": "ISO-8859-8-I" }, { "labels": [ "csisolatin6", "iso-8859-10", "iso-ir-157", "iso8859-10", "iso885910", "l6", "latin6" ], "name": "ISO-8859-10" }, { "labels": [ "iso-8859-13", "iso8859-13", "iso885913" ], "name": "ISO-8859-13" }, { "labels": [ "iso-8859-14", "iso8859-14", "iso885914" ], "name": "ISO-8859-14" }, { "labels": [ "csisolatin9", "iso-8859-15", "iso8859-15", "iso885915", "iso_8859-15", "l9" ], "name": "ISO-8859-15" }, { "labels": [ "iso-8859-16" ], "name": "ISO-8859-16" }, { "labels": [ "cskoi8r", "koi", "koi8", "koi8-r", "koi8_r" ], "name": "KOI8-R" }, { "labels": [ "koi8-ru", "koi8-u" ], "name": "KOI8-U" }, { "labels": [ "csmacintosh", "mac", "macintosh", "x-mac-roman" ], "name": "macintosh" }, { "labels": [ "dos-874", "iso-8859-11", "iso8859-11", "iso885911", "tis-620", "windows-874" ], "name": "windows-874" }, { "labels": [ "cp1250", "windows-1250", "x-cp1250" ], "name": "windows-1250" }, { "labels": [ "cp1251", "windows-1251", "x-cp1251" ], "name": "windows-1251" }, { "labels": [ "ansi_x3.4-1968", "ascii", "cp1252", "cp819", "csisolatin1", "ibm819", "iso-8859-1", "iso-ir-100", "iso8859-1", "iso88591", "iso_8859-1", "iso_8859-1:1987", "l1", "latin1", "us-ascii", "windows-1252", "x-cp1252" ], "name": "windows-1252" }, { "labels": [ "cp1253", "windows-1253", "x-cp1253" ], "name": "windows-1253" }, { "labels": [ "cp1254", "csisolatin5", "iso-8859-9", "iso-ir-148", "iso8859-9", "iso88599", "iso_8859-9", "iso_8859-9:1989", "l5", "latin5", "windows-1254", "x-cp1254" ], "name": "windows-1254" }, { "labels": [ "cp1255", "windows-1255", "x-cp1255" ], "name": "windows-1255" }, { "labels": [ "cp1256", "windows-1256", "x-cp1256" ], "name": "windows-1256" }, { "labels": [ "cp1257", "windows-1257", "x-cp1257" ], "name": "windows-1257" }, { "labels": [ "cp1258", "windows-1258", "x-cp1258" ], "name": "windows-1258" }, { "labels": [ "x-mac-cyrillic", "x-mac-ukrainian" ], "name": "x-mac-cyrillic" } ], "heading": "Legacy single-byte encodings" }, { "encodings": [ { "labels": [ "chinese", "csgb2312", "csiso58gb231280", "gb2312", "gb_2312", "gb_2312-80", "gbk", "iso-ir-58", "x-gbk" ], "name": "GBK" }, { "labels": [ "gb18030" ], "name": "gb18030" } ], "heading": "Legacy multi-byte Chinese (simplified) encodings" }, { "encodings": [ { "labels": [ "big5", "big5-hkscs", "cn-big5", "csbig5", "x-x-big5" ], "name": "Big5" } ], "heading": "Legacy multi-byte Chinese (traditional) encodings" }, { "encodings": [ { "labels": [ "cseucpkdfmtjapanese", "euc-jp", "x-euc-jp" ], "name": "EUC-JP" }, { "labels": [ "csiso2022jp", "iso-2022-jp" ], "name": "ISO-2022-JP" }, { "labels": [ "csshiftjis", "ms932", "ms_kanji", "shift-jis", "shift_jis", "sjis", "windows-31j", "x-sjis" ], "name": "Shift_JIS" } ], "heading": "Legacy multi-byte Japanese encodings" }, { "encodings": [ { "labels": [ "cseuckr", "csksc56011987", "euc-kr", "iso-ir-149", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601", "ksc_5601", "windows-949" ], "name": "EUC-KR" } ], "heading": "Legacy multi-byte Korean encodings" }, { "encodings": [ { "labels": [ "csiso2022kr", "hz-gb-2312", "iso-2022-cn", "iso-2022-cn-ext", "iso-2022-kr" ], "name": "replacement" }, { "labels": [ "utf-16be" ], "name": "UTF-16BE" }, { "labels": [ "utf-16", "utf-16le" ], "name": "UTF-16LE" }, { "labels": [ "x-user-defined" ], "name": "x-user-defined" } ], "heading": "Legacy miscellaneous encodings" } ]; // Label to encoding registry. /** @type {Object.<string,{name:string,labels:Array.<string>}>} */ var label_to_encoding = {}; encodings.forEach(function(category) { category.encodings.forEach(function(encoding) { encoding.labels.forEach(function(label) { label_to_encoding[label] = encoding; }); }); }); // Registry of of encoder/decoder factories, by encoding name. /** @type {Object.<string, function({fatal:boolean}): Encoder>} */ var encoders = {}; /** @type {Object.<string, function({fatal:boolean}): Decoder>} */ var decoders = {}; // // 6. Indexes // /** * @param {number} pointer The |pointer| to search for. * @param {(!Array.<?number>|undefined)} index The |index| to search within. * @return {?number} The code point corresponding to |pointer| in |index|, * or null if |code point| is not in |index|. */ function indexCodePointFor(pointer, index) { if (!index) return null; return index[pointer] || null; } /** * @param {number} code_point The |code point| to search for. * @param {!Array.<?number>} index The |index| to search within. * @return {?number} The first pointer corresponding to |code point| in * |index|, or null if |code point| is not in |index|. */ function indexPointerFor(code_point, index) { var pointer = index.indexOf(code_point); return pointer === -1 ? null : pointer; } /** * @param {string} name Name of the index. * @return {(!Array.<number>|!Array.<Array.<number>>)} * */ function index(name) { if (!('encoding-indexes' in global)) { throw Error("Indexes missing." + " Did you forget to include encoding-indexes.js first?"); } return global['encoding-indexes'][name]; } /** * @param {number} pointer The |pointer| to search for in the gb18030 index. * @return {?number} The code point corresponding to |pointer| in |index|, * or null if |code point| is not in the gb18030 index. */ function indexGB18030RangesCodePointFor(pointer) { // 1. If pointer is greater than 39419 and less than 189000, or // pointer is greater than 1237575, return null. if ((pointer > 39419 && pointer < 189000) || (pointer > 1237575)) return null; // 2. If pointer is 7457, return code point U+E7C7. if (pointer === 7457) return 0xE7C7; // 3. Let offset be the last pointer in index gb18030 ranges that // is equal to or less than pointer and let code point offset be // its corresponding code point. var offset = 0; var code_point_offset = 0; var idx = index('gb18030-ranges'); var i; for (i = 0; i < idx.length; ++i) { /** @type {!Array.<number>} */ var entry = idx[i]; if (entry[0] <= pointer) { offset = entry[0]; code_point_offset = entry[1]; } else { break; } } // 4. Return a code point whose value is code point offset + // pointer − offset. return code_point_offset + pointer - offset; } /** * @param {number} code_point The |code point| to locate in the gb18030 index. * @return {number} The first pointer corresponding to |code point| in the * gb18030 index. */ function indexGB18030RangesPointerFor(code_point) { // 1. If code point is U+E7C7, return pointer 7457. if (code_point === 0xE7C7) return 7457; // 2. Let offset be the last code point in index gb18030 ranges // that is equal to or less than code point and let pointer offset // be its corresponding pointer. var offset = 0; var pointer_offset = 0; var idx = index('gb18030-ranges'); var i; for (i = 0; i < idx.length; ++i) { /** @type {!Array.<number>} */ var entry = idx[i]; if (entry[1] <= code_point) { offset = entry[1]; pointer_offset = entry[0]; } else { break; } } // 3. Return a pointer whose value is pointer offset + code point // − offset. return pointer_offset + code_point - offset; } /** * @param {number} code_point The |code_point| to search for in the Shift_JIS * index. * @return {?number} The code point corresponding to |pointer| in |index|, * or null if |code point| is not in the Shift_JIS index. */ function indexShiftJISPointerFor(code_point) { // 1. Let index be index jis0208 excluding all entries whose // pointer is in the range 8272 to 8835, inclusive. shift_jis_index = shift_jis_index || index('jis0208').map(function(code_point, pointer) { return inRange(pointer, 8272, 8835) ? null : code_point; }); var index_ = shift_jis_index; // 2. Return the index pointer for code point in index. return index_.indexOf(code_point); } var shift_jis_index; /** * @param {number} code_point The |code_point| to search for in the big5 * index. * @return {?number} The code point corresponding to |pointer| in |index|, * or null if |code point| is not in the big5 index. */ function indexBig5PointerFor(code_point) { // 1. Let index be index Big5 excluding all entries whose pointer big5_index_no_hkscs = big5_index_no_hkscs || index('big5').map(function(code_point, pointer) { return (pointer < (0xA1 - 0x81) * 157) ? null : code_point; }); var index_ = big5_index_no_hkscs; // 2. If code point is U+2550, U+255E, U+2561, U+256A, U+5341, or // U+5345, return the last pointer corresponding to code point in // index. if (code_point === 0x2550 || code_point === 0x255E || code_point === 0x2561 || code_point === 0x256A || code_point === 0x5341 || code_point === 0x5345) { return index_.lastIndexOf(code_point); } // 3. Return the index pointer for code point in index. return indexPointerFor(code_point, index_); } var big5_index_no_hkscs; // // 8. API // /** @const */ var DEFAULT_ENCODING = 'utf-8'; // 8.1 Interface TextDecoder /** * @constructor * @param {string=} label The label of the encoding; * defaults to 'utf-8'. * @param {Object=} options */ function TextDecoder(label, options) { // Web IDL conventions if (!(this instanceof TextDecoder)) throw TypeError('Called as a function. Did you forget \'new\'?'); label = label !== undefined ? String(label) : DEFAULT_ENCODING; options = ToDictionary(options); // A TextDecoder object has an associated encoding, decoder, // stream, ignore BOM flag (initially unset), BOM seen flag // (initially unset), error mode (initially replacement), and do // not flush flag (initially unset). /** @private */ this._encoding = null; /** @private @type {?Decoder} */ this._decoder = null; /** @private @type {boolean} */ this._ignoreBOM = false; /** @private @type {boolean} */ this._BOMseen = false; /** @private @type {string} */ this._error_mode = 'replacement'; /** @private @type {boolean} */ this._do_not_flush = false; // 1. Let encoding be the result of getting an encoding from // label. var encoding = getEncoding(label); // 2. If encoding is failure or replacement, throw a RangeError. if (encoding === null || encoding.name === 'replacement') throw RangeError('Unknown encoding: ' + label); if (!decoders[encoding.name]) { throw Error('Decoder not present.' + ' Did you forget to include encoding-indexes.js first?'); } // 3. Let dec be a new TextDecoder object. var dec = this; // 4. Set dec's encoding to encoding. dec._encoding = encoding; // 5. If options's fatal member is true, set dec's error mode to // fatal. if (Boolean(options['fatal'])) dec._error_mode = 'fatal'; // 6. If options's ignoreBOM member is true, set dec's ignore BOM // flag. if (Boolean(options['ignoreBOM'])) dec._ignoreBOM = true; // For pre-ES5 runtimes: if (!Object.defineProperty) { this.encoding = dec._encoding.name.toLowerCase(); this.fatal = dec._error_mode === 'fatal'; this.ignoreBOM = dec._ignoreBOM; } // 7. Return dec. return dec; } if (Object.defineProperty) { // The encoding attribute's getter must return encoding's name. Object.defineProperty(TextDecoder.prototype, 'encoding', { /** @this {TextDecoder} */ get: function() { return this._encoding.name.toLowerCase(); } }); // The fatal attribute's getter must return true if error mode // is fatal, and false otherwise. Object.defineProperty(TextDecoder.prototype, 'fatal', { /** @this {TextDecoder} */ get: function() { return this._error_mode === 'fatal'; } }); // The ignoreBOM attribute's getter must return true if ignore // BOM flag is set, and false otherwise. Object.defineProperty(TextDecoder.prototype, 'ignoreBOM', { /** @this {TextDecoder} */ get: function() { return this._ignoreBOM; } }); } /** * @param {BufferSource=} input The buffer of bytes to decode. * @param {Object=} options * @return {string} The decoded string. */ TextDecoder.prototype.decode = function decode(input, options) { var bytes; if (typeof input === 'object' && input instanceof ArrayBuffer) { bytes = new Uint8Array(input); } else if (typeof input === 'object' && 'buffer' in input && input.buffer instanceof ArrayBuffer) { bytes = new Uint8Array(input.buffer, input.byteOffset, input.byteLength); } else { bytes = new Uint8Array(0); } options = ToDictionary(options); // 1. If the do not flush flag is unset, set decoder to a new // encoding's decoder, set stream to a new stream, and unset the // BOM seen flag. if (!this._do_not_flush) { this._decoder = decoders[this._encoding.name]({ fatal: this._error_mode === 'fatal'}); this._BOMseen = false; } // 2. If options's stream is true, set the do not flush flag, and // unset the do not flush flag otherwise. this._do_not_flush = Boolean(options['stream']); // 3. If input is given, push a copy of input to stream. // TODO: Align with spec algorithm - maintain stream on instance. var input_stream = new Stream(bytes); // 4. Let output be a new stream. var output = []; /** @type {?(number|!Array.<number>)} */ var result; // 5. While true: while (true) { // 1. Let token be the result of reading from stream. var token = input_stream.read(); // 2. If token is end-of-stream and the do not flush flag is // set, return output, serialized. // TODO: Align with spec algorithm. if (token === end_of_stream) break; // 3. Otherwise, run these subsubsteps: // 1. Let result be the result of processing token for decoder, // stream, output, and error mode. result = this._decoder.handler(input_stream, token); // 2. If result is finished, return output, serialized. if (result === finished) break; if (result !== null) { if (Array.isArray(result)) output.push.apply(output, /**@type {!Array.<number>}*/(result)); else output.push(result); } // 3. Otherwise, if result is error, throw a TypeError. // (Thrown in handler) // 4. Otherwise, do nothing. } // TODO: Align with spec algorithm. if (!this._do_not_flush) { do { result = this._decoder.handler(input_stream, input_stream.read()); if (result === finished) break; if (result === null) continue; if (Array.isArray(result)) output.push.apply(output, /**@type {!Array.<number>}*/(result)); else output.push(result); } while (!input_stream.endOfStream()); this._decoder = null; } // A TextDecoder object also has an associated serialize stream // algorithm... /** * @param {!Array.<number>} stream * @return {string} * @this {TextDecoder} */ function serializeStream(stream) { // 1. Let token be the result of reading from stream. // (Done in-place on array, rather than as a stream) // 2. If encoding is UTF-8, UTF-16BE, or UTF-16LE, and ignore // BOM flag and BOM seen flag are unset, run these subsubsteps: if (includes(['UTF-8', 'UTF-16LE', 'UTF-16BE'], this._encoding.name) && !this._ignoreBOM && !this._BOMseen) { if (stream.length > 0 && stream[0] === 0xFEFF) { // 1. If token is U+FEFF, set BOM seen flag. this._BOMseen = true; stream.shift(); } else if (stream.length > 0) { // 2. Otherwise, if token is not end-of-stream, set BOM seen // flag and append token to stream. this._BOMseen = true; } else { // 3. Otherwise, if token is not end-of-stream, append token // to output. // (no-op) } } // 4. Otherwise, return output. return codePointsToString(stream); } return serializeStream.call(this, output); }; // 8.2 Interface TextEncoder /** * @constructor * @param {string=} label The label of the encoding. NONSTANDARD. * @param {Object=} options NONSTANDARD. */ function TextEncoder(label, options) { // Web IDL conventions if (!(this instanceof TextEncoder)) throw TypeError('Called as a function. Did you forget \'new\'?'); options = ToDictionary(options); // A TextEncoder object has an associated encoding and encoder. /** @private */ this._encoding = null; /** @private @type {?Encoder} */ this._encoder = null; // Non-standard /** @private @type {boolean} */ this._do_not_flush = false; /** @private @type {string} */ this._fatal = Boolean(options['fatal']) ? 'fatal' : 'replacement'; // 1. Let enc be a new TextEncoder object. var enc = this; // 2. Set enc's encoding to UTF-8's encoder. if (Boolean(options['NONSTANDARD_allowLegacyEncoding'])) { // NONSTANDARD behavior. label = label !== undefined ? String(label) : DEFAULT_ENCODING; var encoding = getEncoding(label); if (encoding === null || encoding.name === 'replacement') throw RangeError('Unknown encoding: ' + label); if (!encoders[encoding.name]) { throw Error('Encoder not present.' + ' Did you forget to include encoding-indexes.js first?'); } enc._encoding = encoding; } else { // Standard behavior. enc._encoding = getEncoding('utf-8'); if (label !== undefined && 'console' in global) { console.warn('TextEncoder constructor called with encoding label, ' + 'which is ignored.'); } } // For pre-ES5 runtimes: if (!Object.defineProperty) this.encoding = enc._encoding.name.toLowerCase(); // 3. Return enc. return enc; } if (Object.defineProperty) { // The encoding attribute's getter must return encoding's name. Object.defineProperty(TextEncoder.prototype, 'encoding', { /** @this {TextEncoder} */ get: function() { return this._encoding.name.toLowerCase(); } }); } /** * @param {string=} opt_string The string to encode. * @param {Object=} options * @return {!Uint8Array} Encoded bytes, as a Uint8Array. */ TextEncoder.prototype.encode = function encode(opt_string, options) { opt_string = opt_string === undefined ? '' : String(opt_string); options = ToDictionary(options); // NOTE: This option is nonstandard. None of the encodings // permitted for encoding (i.e. UTF-8, UTF-16) are stateful when // the input is a USVString so streaming is not necessary. if (!this._do_not_flush) this._encoder = encoders[this._encoding.name]({ fatal: this._fatal === 'fatal'}); this._do_not_flush = Boolean(options['stream']); // 1. Convert input to a stream. var input = new Stream(stringToCodePoints(opt_string)); // 2. Let output be a new stream var output = []; /** @type {?(number|!Array.<number>)} */ var result; // 3. While true, run these substeps: while (true) { // 1. Let token be the result of reading from input. var token = input.read(); if (token === end_of_stream) break; // 2. Let result be the result of processing token for encoder, // input, output. result = this._encoder.handler(input, token); if (result === finished) break; if (Array.isArray(result)) output.push.apply(output, /**@type {!Array.<number>}*/(result)); else output.push(result); } // TODO: Align with spec algorithm. if (!this._do_not_flush) { while (true) { result = this._encoder.handler(input, input.read()); if (result === finished) break; if (Array.isArray(result)) output.push.apply(output, /**@type {!Array.<number>}*/(result)); else output.push(result); } this._encoder = null; } // 3. If result is finished, convert output into a byte sequence, // and then return a Uint8Array object wrapping an ArrayBuffer // containing output. return new Uint8Array(output); }; // // 9. The encoding // // 9.1 utf-8 // 9.1.1 utf-8 decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function UTF8Decoder(options) { var fatal = options.fatal; // utf-8's decoder's has an associated utf-8 code point, utf-8 // bytes seen, and utf-8 bytes needed (all initially 0), a utf-8 // lower boundary (initially 0x80), and a utf-8 upper boundary // (initially 0xBF). var /** @type {number} */ utf8_code_point = 0, /** @type {number} */ utf8_bytes_seen = 0, /** @type {number} */ utf8_bytes_needed = 0, /** @type {number} */ utf8_lower_boundary = 0x80, /** @type {number} */ utf8_upper_boundary = 0xBF; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and utf-8 bytes needed is not 0, // set utf-8 bytes needed to 0 and return error. if (bite === end_of_stream && utf8_bytes_needed !== 0) { utf8_bytes_needed = 0; return decoderError(fatal); } // 2. If byte is end-of-stream, return finished. if (bite === end_of_stream) return finished; // 3. If utf-8 bytes needed is 0, based on byte: if (utf8_bytes_needed === 0) { // 0x00 to 0x7F if (inRange(bite, 0x00, 0x7F)) { // Return a code point whose value is byte. return bite; } // 0xC2 to 0xDF else if (inRange(bite, 0xC2, 0xDF)) { // 1. Set utf-8 bytes needed to 1. utf8_bytes_needed = 1; // 2. Set UTF-8 code point to byte & 0x1F. utf8_code_point = bite & 0x1F; } // 0xE0 to 0xEF else if (inRange(bite, 0xE0, 0xEF)) { // 1. If byte is 0xE0, set utf-8 lower boundary to 0xA0. if (bite === 0xE0) utf8_lower_boundary = 0xA0; // 2. If byte is 0xED, set utf-8 upper boundary to 0x9F. if (bite === 0xED) utf8_upper_boundary = 0x9F; // 3. Set utf-8 bytes needed to 2. utf8_bytes_needed = 2; // 4. Set UTF-8 code point to byte & 0xF. utf8_code_point = bite & 0xF; } // 0xF0 to 0xF4 else if (inRange(bite, 0xF0, 0xF4)) { // 1. If byte is 0xF0, set utf-8 lower boundary to 0x90. if (bite === 0xF0) utf8_lower_boundary = 0x90; // 2. If byte is 0xF4, set utf-8 upper boundary to 0x8F. if (bite === 0xF4) utf8_upper_boundary = 0x8F; // 3. Set utf-8 bytes needed to 3. utf8_bytes_needed = 3; // 4. Set UTF-8 code point to byte & 0x7. utf8_code_point = bite & 0x7; } // Otherwise else { // Return error. return decoderError(fatal); } // Return continue. return null; } // 4. If byte is not in the range utf-8 lower boundary to utf-8 // upper boundary, inclusive, run these substeps: if (!inRange(bite, utf8_lower_boundary, utf8_upper_boundary)) { // 1. Set utf-8 code point, utf-8 bytes needed, and utf-8 // bytes seen to 0, set utf-8 lower boundary to 0x80, and set // utf-8 upper boundary to 0xBF. utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0; utf8_lower_boundary = 0x80; utf8_upper_boundary = 0xBF; // 2. Prepend byte to stream. stream.prepend(bite); // 3. Return error. return decoderError(fatal); } // 5. Set utf-8 lower boundary to 0x80 and utf-8 upper boundary // to 0xBF. utf8_lower_boundary = 0x80; utf8_upper_boundary = 0xBF; // 6. Set UTF-8 code point to (UTF-8 code point << 6) | (byte & // 0x3F) utf8_code_point = (utf8_code_point << 6) | (bite & 0x3F); // 7. Increase utf-8 bytes seen by one. utf8_bytes_seen += 1; // 8. If utf-8 bytes seen is not equal to utf-8 bytes needed, // continue. if (utf8_bytes_seen !== utf8_bytes_needed) return null; // 9. Let code point be utf-8 code point. var code_point = utf8_code_point; // 10. Set utf-8 code point, utf-8 bytes needed, and utf-8 bytes // seen to 0. utf8_code_point = utf8_bytes_needed = utf8_bytes_seen = 0; // 11. Return a code point whose value is code point. return code_point; }; } // 9.1.2 utf-8 encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function UTF8Encoder(options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. Set count and offset based on the range code point is in: var count, offset; // U+0080 to U+07FF, inclusive: if (inRange(code_point, 0x0080, 0x07FF)) { // 1 and 0xC0 count = 1; offset = 0xC0; } // U+0800 to U+FFFF, inclusive: else if (inRange(code_point, 0x0800, 0xFFFF)) { // 2 and 0xE0 count = 2; offset = 0xE0; } // U+10000 to U+10FFFF, inclusive: else if (inRange(code_point, 0x10000, 0x10FFFF)) { // 3 and 0xF0 count = 3; offset = 0xF0; } // 4. Let bytes be a byte sequence whose first byte is (code // point >> (6 × count)) + offset. var bytes = [(code_point >> (6 * count)) + offset]; // 5. Run these substeps while count is greater than 0: while (count > 0) { // 1. Set temp to code point >> (6 × (count − 1)). var temp = code_point >> (6 * (count - 1)); // 2. Append to bytes 0x80 | (temp & 0x3F). bytes.push(0x80 | (temp & 0x3F)); // 3. Decrease count by one. count -= 1; } // 6. Return bytes bytes, in order. return bytes; }; } /** @param {{fatal: boolean}} options */ encoders['UTF-8'] = function(options) { return new UTF8Encoder(options); }; /** @param {{fatal: boolean}} options */ decoders['UTF-8'] = function(options) { return new UTF8Decoder(options); }; // // 10. Legacy single-byte encodings // // 10.1 single-byte decoder /** * @constructor * @implements {Decoder} * @param {!Array.<number>} index The encoding index. * @param {{fatal: boolean}} options */ function SingleByteDecoder(index, options) { var fatal = options.fatal; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream, return finished. if (bite === end_of_stream) return finished; // 2. If byte is an ASCII byte, return a code point whose value // is byte. if (isASCIIByte(bite)) return bite; // 3. Let code point be the index code point for byte − 0x80 in // index single-byte. var code_point = index[bite - 0x80]; // 4. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 5. Return a code point whose value is code point. return code_point; }; } // 10.2 single-byte encoder /** * @constructor * @implements {Encoder} * @param {!Array.<?number>} index The encoding index. * @param {{fatal: boolean}} options */ function SingleByteEncoder(index, options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. Let pointer be the index pointer for code point in index // single-byte. var pointer = indexPointerFor(code_point, index); // 4. If pointer is null, return error with code point. if (pointer === null) encoderError(code_point); // 5. Return a byte whose value is pointer + 0x80. return pointer + 0x80; }; } (function() { if (!('encoding-indexes' in global)) return; encodings.forEach(function(category) { if (category.heading !== 'Legacy single-byte encodings') return; category.encodings.forEach(function(encoding) { var name = encoding.name; var idx = index(name.toLowerCase()); /** @param {{fatal: boolean}} options */ decoders[name] = function(options) { return new SingleByteDecoder(idx, options); }; /** @param {{fatal: boolean}} options */ encoders[name] = function(options) { return new SingleByteEncoder(idx, options); }; }); }); }()); // // 11. Legacy multi-byte Chinese (simplified) encodings // // 11.1 gbk // 11.1.1 gbk decoder // gbk's decoder is gb18030's decoder. /** @param {{fatal: boolean}} options */ decoders['GBK'] = function(options) { return new GB18030Decoder(options); }; // 11.1.2 gbk encoder // gbk's encoder is gb18030's encoder with its gbk flag set. /** @param {{fatal: boolean}} options */ encoders['GBK'] = function(options) { return new GB18030Encoder(options, true); }; // 11.2 gb18030 // 11.2.1 gb18030 decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function GB18030Decoder(options) { var fatal = options.fatal; // gb18030's decoder has an associated gb18030 first, gb18030 // second, and gb18030 third (all initially 0x00). var /** @type {number} */ gb18030_first = 0x00, /** @type {number} */ gb18030_second = 0x00, /** @type {number} */ gb18030_third = 0x00; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and gb18030 first, gb18030 // second, and gb18030 third are 0x00, return finished. if (bite === end_of_stream && gb18030_first === 0x00 && gb18030_second === 0x00 && gb18030_third === 0x00) { return finished; } // 2. If byte is end-of-stream, and gb18030 first, gb18030 // second, or gb18030 third is not 0x00, set gb18030 first, // gb18030 second, and gb18030 third to 0x00, and return error. if (bite === end_of_stream && (gb18030_first !== 0x00 || gb18030_second !== 0x00 || gb18030_third !== 0x00)) { gb18030_first = 0x00; gb18030_second = 0x00; gb18030_third = 0x00; decoderError(fatal); } var code_point; // 3. If gb18030 third is not 0x00, run these substeps: if (gb18030_third !== 0x00) { // 1. Let code point be null. code_point = null; // 2. If byte is in the range 0x30 to 0x39, inclusive, set // code point to the index gb18030 ranges code point for // (((gb18030 first − 0x81) × 10 + gb18030 second − 0x30) × // 126 + gb18030 third − 0x81) × 10 + byte − 0x30. if (inRange(bite, 0x30, 0x39)) { code_point = indexGB18030RangesCodePointFor( (((gb18030_first - 0x81) * 10 + gb18030_second - 0x30) * 126 + gb18030_third - 0x81) * 10 + bite - 0x30); } // 3. Let buffer be a byte sequence consisting of gb18030 // second, gb18030 third, and byte, in order. var buffer = [gb18030_second, gb18030_third, bite]; // 4. Set gb18030 first, gb18030 second, and gb18030 third to // 0x00. gb18030_first = 0x00; gb18030_second = 0x00; gb18030_third = 0x00; // 5. If code point is null, prepend buffer to stream and // return error. if (code_point === null) { stream.prepend(buffer); return decoderError(fatal); } // 6. Return a code point whose value is code point. return code_point; } // 4. If gb18030 second is not 0x00, run these substeps: if (gb18030_second !== 0x00) { // 1. If byte is in the range 0x81 to 0xFE, inclusive, set // gb18030 third to byte and return continue. if (inRange(bite, 0x81, 0xFE)) { gb18030_third = bite; return null; } // 2. Prepend gb18030 second followed by byte to stream, set // gb18030 first and gb18030 second to 0x00, and return error. stream.prepend([gb18030_second, bite]); gb18030_first = 0x00; gb18030_second = 0x00; return decoderError(fatal); } // 5. If gb18030 first is not 0x00, run these substeps: if (gb18030_first !== 0x00) { // 1. If byte is in the range 0x30 to 0x39, inclusive, set // gb18030 second to byte and return continue. if (inRange(bite, 0x30, 0x39)) { gb18030_second = bite; return null; } // 2. Let lead be gb18030 first, let pointer be null, and set // gb18030 first to 0x00. var lead = gb18030_first; var pointer = null; gb18030_first = 0x00; // 3. Let offset be 0x40 if byte is less than 0x7F and 0x41 // otherwise. var offset = bite < 0x7F ? 0x40 : 0x41; // 4. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80 // to 0xFE, inclusive, set pointer to (lead − 0x81) × 190 + // (byte − offset). if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFE)) pointer = (lead - 0x81) * 190 + (bite - offset); // 5. Let code point be null if pointer is null and the index // code point for pointer in index gb18030 otherwise. code_point = pointer === null ? null : indexCodePointFor(pointer, index('gb18030')); // 6. If code point is null and byte is an ASCII byte, prepend // byte to stream. if (code_point === null && isASCIIByte(bite)) stream.prepend(bite); // 7. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 8. Return a code point whose value is code point. return code_point; } // 6. If byte is an ASCII byte, return a code point whose value // is byte. if (isASCIIByte(bite)) return bite; // 7. If byte is 0x80, return code point U+20AC. if (bite === 0x80) return 0x20AC; // 8. If byte is in the range 0x81 to 0xFE, inclusive, set // gb18030 first to byte and return continue. if (inRange(bite, 0x81, 0xFE)) { gb18030_first = bite; return null; } // 9. Return error. return decoderError(fatal); }; } // 11.2.2 gb18030 encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options * @param {boolean=} gbk_flag */ function GB18030Encoder(options, gbk_flag) { var fatal = options.fatal; // gb18030's decoder has an associated gbk flag (initially unset). /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. If code point is U+E5E5, return error with code point. if (code_point === 0xE5E5) return encoderError(code_point); // 4. If the gbk flag is set and code point is U+20AC, return // byte 0x80. if (gbk_flag && code_point === 0x20AC) return 0x80; // 5. Let pointer be the index pointer for code point in index // gb18030. var pointer = indexPointerFor(code_point, index('gb18030')); // 6. If pointer is not null, run these substeps: if (pointer !== null) { // 1. Let lead be floor(pointer / 190) + 0x81. var lead = floor(pointer / 190) + 0x81; // 2. Let trail be pointer % 190. var trail = pointer % 190; // 3. Let offset be 0x40 if trail is less than 0x3F and 0x41 otherwise. var offset = trail < 0x3F ? 0x40 : 0x41; // 4. Return two bytes whose values are lead and trail + offset. return [lead, trail + offset]; } // 7. If gbk flag is set, return error with code point. if (gbk_flag) return encoderError(code_point); // 8. Set pointer to the index gb18030 ranges pointer for code // point. pointer = indexGB18030RangesPointerFor(code_point); // 9. Let byte1 be floor(pointer / 10 / 126 / 10). var byte1 = floor(pointer / 10 / 126 / 10); // 10. Set pointer to pointer − byte1 × 10 × 126 × 10. pointer = pointer - byte1 * 10 * 126 * 10; // 11. Let byte2 be floor(pointer / 10 / 126). var byte2 = floor(pointer / 10 / 126); // 12. Set pointer to pointer − byte2 × 10 × 126. pointer = pointer - byte2 * 10 * 126; // 13. Let byte3 be floor(pointer / 10). var byte3 = floor(pointer / 10); // 14. Let byte4 be pointer − byte3 × 10. var byte4 = pointer - byte3 * 10; // 15. Return four bytes whose values are byte1 + 0x81, byte2 + // 0x30, byte3 + 0x81, byte4 + 0x30. return [byte1 + 0x81, byte2 + 0x30, byte3 + 0x81, byte4 + 0x30]; }; } /** @param {{fatal: boolean}} options */ encoders['gb18030'] = function(options) { return new GB18030Encoder(options); }; /** @param {{fatal: boolean}} options */ decoders['gb18030'] = function(options) { return new GB18030Decoder(options); }; // // 12. Legacy multi-byte Chinese (traditional) encodings // // 12.1 Big5 // 12.1.1 Big5 decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function Big5Decoder(options) { var fatal = options.fatal; // Big5's decoder has an associated Big5 lead (initially 0x00). var /** @type {number} */ Big5_lead = 0x00; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and Big5 lead is not 0x00, set // Big5 lead to 0x00 and return error. if (bite === end_of_stream && Big5_lead !== 0x00) { Big5_lead = 0x00; return decoderError(fatal); } // 2. If byte is end-of-stream and Big5 lead is 0x00, return // finished. if (bite === end_of_stream && Big5_lead === 0x00) return finished; // 3. If Big5 lead is not 0x00, let lead be Big5 lead, let // pointer be null, set Big5 lead to 0x00, and then run these // substeps: if (Big5_lead !== 0x00) { var lead = Big5_lead; var pointer = null; Big5_lead = 0x00; // 1. Let offset be 0x40 if byte is less than 0x7F and 0x62 // otherwise. var offset = bite < 0x7F ? 0x40 : 0x62; // 2. If byte is in the range 0x40 to 0x7E, inclusive, or 0xA1 // to 0xFE, inclusive, set pointer to (lead − 0x81) × 157 + // (byte − offset). if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0xA1, 0xFE)) pointer = (lead - 0x81) * 157 + (bite - offset); // 3. If there is a row in the table below whose first column // is pointer, return the two code points listed in its second // column // Pointer | Code points // --------+-------------- // 1133 | U+00CA U+0304 // 1135 | U+00CA U+030C // 1164 | U+00EA U+0304 // 1166 | U+00EA U+030C switch (pointer) { case 1133: return [0x00CA, 0x0304]; case 1135: return [0x00CA, 0x030C]; case 1164: return [0x00EA, 0x0304]; case 1166: return [0x00EA, 0x030C]; } // 4. Let code point be null if pointer is null and the index // code point for pointer in index Big5 otherwise. var code_point = (pointer === null) ? null : indexCodePointFor(pointer, index('big5')); // 5. If code point is null and byte is an ASCII byte, prepend // byte to stream. if (code_point === null && isASCIIByte(bite)) stream.prepend(bite); // 6. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 7. Return a code point whose value is code point. return code_point; } // 4. If byte is an ASCII byte, return a code point whose value // is byte. if (isASCIIByte(bite)) return bite; // 5. If byte is in the range 0x81 to 0xFE, inclusive, set Big5 // lead to byte and return continue. if (inRange(bite, 0x81, 0xFE)) { Big5_lead = bite; return null; } // 6. Return error. return decoderError(fatal); }; } // 12.1.2 Big5 encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function Big5Encoder(options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. Let pointer be the index Big5 pointer for code point. var pointer = indexBig5PointerFor(code_point); // 4. If pointer is null, return error with code point. if (pointer === null) return encoderError(code_point); // 5. Let lead be floor(pointer / 157) + 0x81. var lead = floor(pointer / 157) + 0x81; // 6. If lead is less than 0xA1, return error with code point. if (lead < 0xA1) return encoderError(code_point); // 7. Let trail be pointer % 157. var trail = pointer % 157; // 8. Let offset be 0x40 if trail is less than 0x3F and 0x62 // otherwise. var offset = trail < 0x3F ? 0x40 : 0x62; // Return two bytes whose values are lead and trail + offset. return [lead, trail + offset]; }; } /** @param {{fatal: boolean}} options */ encoders['Big5'] = function(options) { return new Big5Encoder(options); }; /** @param {{fatal: boolean}} options */ decoders['Big5'] = function(options) { return new Big5Decoder(options); }; // // 13. Legacy multi-byte Japanese encodings // // 13.1 euc-jp // 13.1.1 euc-jp decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function EUCJPDecoder(options) { var fatal = options.fatal; // euc-jp's decoder has an associated euc-jp jis0212 flag // (initially unset) and euc-jp lead (initially 0x00). var /** @type {boolean} */ eucjp_jis0212_flag = false, /** @type {number} */ eucjp_lead = 0x00; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and euc-jp lead is not 0x00, set // euc-jp lead to 0x00, and return error. if (bite === end_of_stream && eucjp_lead !== 0x00) { eucjp_lead = 0x00; return decoderError(fatal); } // 2. If byte is end-of-stream and euc-jp lead is 0x00, return // finished. if (bite === end_of_stream && eucjp_lead === 0x00) return finished; // 3. If euc-jp lead is 0x8E and byte is in the range 0xA1 to // 0xDF, inclusive, set euc-jp lead to 0x00 and return a code // point whose value is 0xFF61 − 0xA1 + byte. if (eucjp_lead === 0x8E && inRange(bite, 0xA1, 0xDF)) { eucjp_lead = 0x00; return 0xFF61 - 0xA1 + bite; } // 4. If euc-jp lead is 0x8F and byte is in the range 0xA1 to // 0xFE, inclusive, set the euc-jp jis0212 flag, set euc-jp lead // to byte, and return continue. if (eucjp_lead === 0x8F && inRange(bite, 0xA1, 0xFE)) { eucjp_jis0212_flag = true; eucjp_lead = bite; return null; } // 5. If euc-jp lead is not 0x00, let lead be euc-jp lead, set // euc-jp lead to 0x00, and run these substeps: if (eucjp_lead !== 0x00) { var lead = eucjp_lead; eucjp_lead = 0x00; // 1. Let code point be null. var code_point = null; // 2. If lead and byte are both in the range 0xA1 to 0xFE, // inclusive, set code point to the index code point for (lead // − 0xA1) × 94 + byte − 0xA1 in index jis0208 if the euc-jp // jis0212 flag is unset and in index jis0212 otherwise. if (inRange(lead, 0xA1, 0xFE) && inRange(bite, 0xA1, 0xFE)) { code_point = indexCodePointFor( (lead - 0xA1) * 94 + (bite - 0xA1), index(!eucjp_jis0212_flag ? 'jis0208' : 'jis0212')); } // 3. Unset the euc-jp jis0212 flag. eucjp_jis0212_flag = false; // 4. If byte is not in the range 0xA1 to 0xFE, inclusive, // prepend byte to stream. if (!inRange(bite, 0xA1, 0xFE)) stream.prepend(bite); // 5. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 6. Return a code point whose value is code point. return code_point; } // 6. If byte is an ASCII byte, return a code point whose value // is byte. if (isASCIIByte(bite)) return bite; // 7. If byte is 0x8E, 0x8F, or in the range 0xA1 to 0xFE, // inclusive, set euc-jp lead to byte and return continue. if (bite === 0x8E || bite === 0x8F || inRange(bite, 0xA1, 0xFE)) { eucjp_lead = bite; return null; } // 8. Return error. return decoderError(fatal); }; } // 13.1.2 euc-jp encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function EUCJPEncoder(options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. If code point is U+00A5, return byte 0x5C. if (code_point === 0x00A5) return 0x5C; // 4. If code point is U+203E, return byte 0x7E. if (code_point === 0x203E) return 0x7E; // 5. If code point is in the range U+FF61 to U+FF9F, inclusive, // return two bytes whose values are 0x8E and code point − // 0xFF61 + 0xA1. if (inRange(code_point, 0xFF61, 0xFF9F)) return [0x8E, code_point - 0xFF61 + 0xA1]; // 6. If code point is U+2212, set it to U+FF0D. if (code_point === 0x2212) code_point = 0xFF0D; // 7. Let pointer be the index pointer for code point in index // jis0208. var pointer = indexPointerFor(code_point, index('jis0208')); // 8. If pointer is null, return error with code point. if (pointer === null) return encoderError(code_point); // 9. Let lead be floor(pointer / 94) + 0xA1. var lead = floor(pointer / 94) + 0xA1; // 10. Let trail be pointer % 94 + 0xA1. var trail = pointer % 94 + 0xA1; // 11. Return two bytes whose values are lead and trail. return [lead, trail]; }; } /** @param {{fatal: boolean}} options */ encoders['EUC-JP'] = function(options) { return new EUCJPEncoder(options); }; /** @param {{fatal: boolean}} options */ decoders['EUC-JP'] = function(options) { return new EUCJPDecoder(options); }; // 13.2 iso-2022-jp // 13.2.1 iso-2022-jp decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function ISO2022JPDecoder(options) { var fatal = options.fatal; /** @enum */ var states = { ASCII: 0, Roman: 1, Katakana: 2, LeadByte: 3, TrailByte: 4, EscapeStart: 5, Escape: 6 }; // iso-2022-jp's decoder has an associated iso-2022-jp decoder // state (initially ASCII), iso-2022-jp decoder output state // (initially ASCII), iso-2022-jp lead (initially 0x00), and // iso-2022-jp output flag (initially unset). var /** @type {number} */ iso2022jp_decoder_state = states.ASCII, /** @type {number} */ iso2022jp_decoder_output_state = states.ASCII, /** @type {number} */ iso2022jp_lead = 0x00, /** @type {boolean} */ iso2022jp_output_flag = false; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // switching on iso-2022-jp decoder state: switch (iso2022jp_decoder_state) { default: case states.ASCII: // ASCII // Based on byte: // 0x1B if (bite === 0x1B) { // Set iso-2022-jp decoder state to escape start and return // continue. iso2022jp_decoder_state = states.EscapeStart; return null; } // 0x00 to 0x7F, excluding 0x0E, 0x0F, and 0x1B if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F && bite !== 0x1B) { // Unset the iso-2022-jp output flag and return a code point // whose value is byte. iso2022jp_output_flag = false; return bite; } // end-of-stream if (bite === end_of_stream) { // Return finished. return finished; } // Otherwise // Unset the iso-2022-jp output flag and return error. iso2022jp_output_flag = false; return decoderError(fatal); case states.Roman: // Roman // Based on byte: // 0x1B if (bite === 0x1B) { // Set iso-2022-jp decoder state to escape start and return // continue. iso2022jp_decoder_state = states.EscapeStart; return null; } // 0x5C if (bite === 0x5C) { // Unset the iso-2022-jp output flag and return code point // U+00A5. iso2022jp_output_flag = false; return 0x00A5; } // 0x7E if (bite === 0x7E) { // Unset the iso-2022-jp output flag and return code point // U+203E. iso2022jp_output_flag = false; return 0x203E; } // 0x00 to 0x7F, excluding 0x0E, 0x0F, 0x1B, 0x5C, and 0x7E if (inRange(bite, 0x00, 0x7F) && bite !== 0x0E && bite !== 0x0F && bite !== 0x1B && bite !== 0x5C && bite !== 0x7E) { // Unset the iso-2022-jp output flag and return a code point // whose value is byte. iso2022jp_output_flag = false; return bite; } // end-of-stream if (bite === end_of_stream) { // Return finished. return finished; } // Otherwise // Unset the iso-2022-jp output flag and return error. iso2022jp_output_flag = false; return decoderError(fatal); case states.Katakana: // Katakana // Based on byte: // 0x1B if (bite === 0x1B) { // Set iso-2022-jp decoder state to escape start and return // continue. iso2022jp_decoder_state = states.EscapeStart; return null; } // 0x21 to 0x5F if (inRange(bite, 0x21, 0x5F)) { // Unset the iso-2022-jp output flag and return a code point // whose value is 0xFF61 − 0x21 + byte. iso2022jp_output_flag = false; return 0xFF61 - 0x21 + bite; } // end-of-stream if (bite === end_of_stream) { // Return finished. return finished; } // Otherwise // Unset the iso-2022-jp output flag and return error. iso2022jp_output_flag = false; return decoderError(fatal); case states.LeadByte: // Lead byte // Based on byte: // 0x1B if (bite === 0x1B) { // Set iso-2022-jp decoder state to escape start and return // continue. iso2022jp_decoder_state = states.EscapeStart; return null; } // 0x21 to 0x7E if (inRange(bite, 0x21, 0x7E)) { // Unset the iso-2022-jp output flag, set iso-2022-jp lead // to byte, iso-2022-jp decoder state to trail byte, and // return continue. iso2022jp_output_flag = false; iso2022jp_lead = bite; iso2022jp_decoder_state = states.TrailByte; return null; } // end-of-stream if (bite === end_of_stream) { // Return finished. return finished; } // Otherwise // Unset the iso-2022-jp output flag and return error. iso2022jp_output_flag = false; return decoderError(fatal); case states.TrailByte: // Trail byte // Based on byte: // 0x1B if (bite === 0x1B) { // Set iso-2022-jp decoder state to escape start and return // continue. iso2022jp_decoder_state = states.EscapeStart; return decoderError(fatal); } // 0x21 to 0x7E if (inRange(bite, 0x21, 0x7E)) { // 1. Set the iso-2022-jp decoder state to lead byte. iso2022jp_decoder_state = states.LeadByte; // 2. Let pointer be (iso-2022-jp lead − 0x21) × 94 + byte − 0x21. var pointer = (iso2022jp_lead - 0x21) * 94 + bite - 0x21; // 3. Let code point be the index code point for pointer in // index jis0208. var code_point = indexCodePointFor(pointer, index('jis0208')); // 4. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 5. Return a code point whose value is code point. return code_point; } // end-of-stream if (bite === end_of_stream) { // Set the iso-2022-jp decoder state to lead byte, prepend // byte to stream, and return error. iso2022jp_decoder_state = states.LeadByte; stream.prepend(bite); return decoderError(fatal); } // Otherwise // Set iso-2022-jp decoder state to lead byte and return // error. iso2022jp_decoder_state = states.LeadByte; return decoderError(fatal); case states.EscapeStart: // Escape start // 1. If byte is either 0x24 or 0x28, set iso-2022-jp lead to // byte, iso-2022-jp decoder state to escape, and return // continue. if (bite === 0x24 || bite === 0x28) { iso2022jp_lead = bite; iso2022jp_decoder_state = states.Escape; return null; } // 2. Prepend byte to stream. stream.prepend(bite); // 3. Unset the iso-2022-jp output flag, set iso-2022-jp // decoder state to iso-2022-jp decoder output state, and // return error. iso2022jp_output_flag = false; iso2022jp_decoder_state = iso2022jp_decoder_output_state; return decoderError(fatal); case states.Escape: // Escape // 1. Let lead be iso-2022-jp lead and set iso-2022-jp lead to // 0x00. var lead = iso2022jp_lead; iso2022jp_lead = 0x00; // 2. Let state be null. var state = null; // 3. If lead is 0x28 and byte is 0x42, set state to ASCII. if (lead === 0x28 && bite === 0x42) state = states.ASCII; // 4. If lead is 0x28 and byte is 0x4A, set state to Roman. if (lead === 0x28 && bite === 0x4A) state = states.Roman; // 5. If lead is 0x28 and byte is 0x49, set state to Katakana. if (lead === 0x28 && bite === 0x49) state = states.Katakana; // 6. If lead is 0x24 and byte is either 0x40 or 0x42, set // state to lead byte. if (lead === 0x24 && (bite === 0x40 || bite === 0x42)) state = states.LeadByte; // 7. If state is non-null, run these substeps: if (state !== null) { // 1. Set iso-2022-jp decoder state and iso-2022-jp decoder // output state to states. iso2022jp_decoder_state = iso2022jp_decoder_state = state; // 2. Let output flag be the iso-2022-jp output flag. var output_flag = iso2022jp_output_flag; // 3. Set the iso-2022-jp output flag. iso2022jp_output_flag = true; // 4. Return continue, if output flag is unset, and error // otherwise. return !output_flag ? null : decoderError(fatal); } // 8. Prepend lead and byte to stream. stream.prepend([lead, bite]); // 9. Unset the iso-2022-jp output flag, set iso-2022-jp // decoder state to iso-2022-jp decoder output state and // return error. iso2022jp_output_flag = false; iso2022jp_decoder_state = iso2022jp_decoder_output_state; return decoderError(fatal); } }; } // 13.2.2 iso-2022-jp encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function ISO2022JPEncoder(options) { var fatal = options.fatal; // iso-2022-jp's encoder has an associated iso-2022-jp encoder // state which is one of ASCII, Roman, and jis0208 (initially // ASCII). /** @enum */ var states = { ASCII: 0, Roman: 1, jis0208: 2 }; var /** @type {number} */ iso2022jp_state = states.ASCII; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream and iso-2022-jp encoder // state is not ASCII, prepend code point to stream, set // iso-2022-jp encoder state to ASCII, and return three bytes // 0x1B 0x28 0x42. if (code_point === end_of_stream && iso2022jp_state !== states.ASCII) { stream.prepend(code_point); iso2022jp_state = states.ASCII; return [0x1B, 0x28, 0x42]; } // 2. If code point is end-of-stream and iso-2022-jp encoder // state is ASCII, return finished. if (code_point === end_of_stream && iso2022jp_state === states.ASCII) return finished; // 3. If ISO-2022-JP encoder state is ASCII or Roman, and code // point is U+000E, U+000F, or U+001B, return error with U+FFFD. if ((iso2022jp_state === states.ASCII || iso2022jp_state === states.Roman) && (code_point === 0x000E || code_point === 0x000F || code_point === 0x001B)) { return encoderError(0xFFFD); } // 4. If iso-2022-jp encoder state is ASCII and code point is an // ASCII code point, return a byte whose value is code point. if (iso2022jp_state === states.ASCII && isASCIICodePoint(code_point)) return code_point; // 5. If iso-2022-jp encoder state is Roman and code point is an // ASCII code point, excluding U+005C and U+007E, or is U+00A5 // or U+203E, run these substeps: if (iso2022jp_state === states.Roman && ((isASCIICodePoint(code_point) && code_point !== 0x005C && code_point !== 0x007E) || (code_point == 0x00A5 || code_point == 0x203E))) { // 1. If code point is an ASCII code point, return a byte // whose value is code point. if (isASCIICodePoint(code_point)) return code_point; // 2. If code point is U+00A5, return byte 0x5C. if (code_point === 0x00A5) return 0x5C; // 3. If code point is U+203E, return byte 0x7E. if (code_point === 0x203E) return 0x7E; } // 6. If code point is an ASCII code point, and iso-2022-jp // encoder state is not ASCII, prepend code point to stream, set // iso-2022-jp encoder state to ASCII, and return three bytes // 0x1B 0x28 0x42. if (isASCIICodePoint(code_point) && iso2022jp_state !== states.ASCII) { stream.prepend(code_point); iso2022jp_state = states.ASCII; return [0x1B, 0x28, 0x42]; } // 7. If code point is either U+00A5 or U+203E, and iso-2022-jp // encoder state is not Roman, prepend code point to stream, set // iso-2022-jp encoder state to Roman, and return three bytes // 0x1B 0x28 0x4A. if ((code_point === 0x00A5 || code_point === 0x203E) && iso2022jp_state !== states.Roman) { stream.prepend(code_point); iso2022jp_state = states.Roman; return [0x1B, 0x28, 0x4A]; } // 8. If code point is U+2212, set it to U+FF0D. if (code_point === 0x2212) code_point = 0xFF0D; // 9. Let pointer be the index pointer for code point in index // jis0208. var pointer = indexPointerFor(code_point, index('jis0208')); // 10. If pointer is null, return error with code point. if (pointer === null) return encoderError(code_point); // 11. If iso-2022-jp encoder state is not jis0208, prepend code // point to stream, set iso-2022-jp encoder state to jis0208, // and return three bytes 0x1B 0x24 0x42. if (iso2022jp_state !== states.jis0208) { stream.prepend(code_point); iso2022jp_state = states.jis0208; return [0x1B, 0x24, 0x42]; } // 12. Let lead be floor(pointer / 94) + 0x21. var lead = floor(pointer / 94) + 0x21; // 13. Let trail be pointer % 94 + 0x21. var trail = pointer % 94 + 0x21; // 14. Return two bytes whose values are lead and trail. return [lead, trail]; }; } /** @param {{fatal: boolean}} options */ encoders['ISO-2022-JP'] = function(options) { return new ISO2022JPEncoder(options); }; /** @param {{fatal: boolean}} options */ decoders['ISO-2022-JP'] = function(options) { return new ISO2022JPDecoder(options); }; // 13.3 Shift_JIS // 13.3.1 Shift_JIS decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function ShiftJISDecoder(options) { var fatal = options.fatal; // Shift_JIS's decoder has an associated Shift_JIS lead (initially // 0x00). var /** @type {number} */ Shift_JIS_lead = 0x00; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and Shift_JIS lead is not 0x00, // set Shift_JIS lead to 0x00 and return error. if (bite === end_of_stream && Shift_JIS_lead !== 0x00) { Shift_JIS_lead = 0x00; return decoderError(fatal); } // 2. If byte is end-of-stream and Shift_JIS lead is 0x00, // return finished. if (bite === end_of_stream && Shift_JIS_lead === 0x00) return finished; // 3. If Shift_JIS lead is not 0x00, let lead be Shift_JIS lead, // let pointer be null, set Shift_JIS lead to 0x00, and then run // these substeps: if (Shift_JIS_lead !== 0x00) { var lead = Shift_JIS_lead; var pointer = null; Shift_JIS_lead = 0x00; // 1. Let offset be 0x40, if byte is less than 0x7F, and 0x41 // otherwise. var offset = (bite < 0x7F) ? 0x40 : 0x41; // 2. Let lead offset be 0x81, if lead is less than 0xA0, and // 0xC1 otherwise. var lead_offset = (lead < 0xA0) ? 0x81 : 0xC1; // 3. If byte is in the range 0x40 to 0x7E, inclusive, or 0x80 // to 0xFC, inclusive, set pointer to (lead − lead offset) × // 188 + byte − offset. if (inRange(bite, 0x40, 0x7E) || inRange(bite, 0x80, 0xFC)) pointer = (lead - lead_offset) * 188 + bite - offset; // 4. If pointer is in the range 8836 to 10715, inclusive, // return a code point whose value is 0xE000 − 8836 + pointer. if (inRange(pointer, 8836, 10715)) return 0xE000 - 8836 + pointer; // 5. Let code point be null, if pointer is null, and the // index code point for pointer in index jis0208 otherwise. var code_point = (pointer === null) ? null : indexCodePointFor(pointer, index('jis0208')); // 6. If code point is null and byte is an ASCII byte, prepend // byte to stream. if (code_point === null && isASCIIByte(bite)) stream.prepend(bite); // 7. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 8. Return a code point whose value is code point. return code_point; } // 4. If byte is an ASCII byte or 0x80, return a code point // whose value is byte. if (isASCIIByte(bite) || bite === 0x80) return bite; // 5. If byte is in the range 0xA1 to 0xDF, inclusive, return a // code point whose value is 0xFF61 − 0xA1 + byte. if (inRange(bite, 0xA1, 0xDF)) return 0xFF61 - 0xA1 + bite; // 6. If byte is in the range 0x81 to 0x9F, inclusive, or 0xE0 // to 0xFC, inclusive, set Shift_JIS lead to byte and return // continue. if (inRange(bite, 0x81, 0x9F) || inRange(bite, 0xE0, 0xFC)) { Shift_JIS_lead = bite; return null; } // 7. Return error. return decoderError(fatal); }; } // 13.3.2 Shift_JIS encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function ShiftJISEncoder(options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point or U+0080, return a // byte whose value is code point. if (isASCIICodePoint(code_point) || code_point === 0x0080) return code_point; // 3. If code point is U+00A5, return byte 0x5C. if (code_point === 0x00A5) return 0x5C; // 4. If code point is U+203E, return byte 0x7E. if (code_point === 0x203E) return 0x7E; // 5. If code point is in the range U+FF61 to U+FF9F, inclusive, // return a byte whose value is code point − 0xFF61 + 0xA1. if (inRange(code_point, 0xFF61, 0xFF9F)) return code_point - 0xFF61 + 0xA1; // 6. If code point is U+2212, set it to U+FF0D. if (code_point === 0x2212) code_point = 0xFF0D; // 7. Let pointer be the index Shift_JIS pointer for code point. var pointer = indexShiftJISPointerFor(code_point); // 8. If pointer is null, return error with code point. if (pointer === null) return encoderError(code_point); // 9. Let lead be floor(pointer / 188). var lead = floor(pointer / 188); // 10. Let lead offset be 0x81, if lead is less than 0x1F, and // 0xC1 otherwise. var lead_offset = (lead < 0x1F) ? 0x81 : 0xC1; // 11. Let trail be pointer % 188. var trail = pointer % 188; // 12. Let offset be 0x40, if trail is less than 0x3F, and 0x41 // otherwise. var offset = (trail < 0x3F) ? 0x40 : 0x41; // 13. Return two bytes whose values are lead + lead offset and // trail + offset. return [lead + lead_offset, trail + offset]; }; } /** @param {{fatal: boolean}} options */ encoders['Shift_JIS'] = function(options) { return new ShiftJISEncoder(options); }; /** @param {{fatal: boolean}} options */ decoders['Shift_JIS'] = function(options) { return new ShiftJISDecoder(options); }; // // 14. Legacy multi-byte Korean encodings // // 14.1 euc-kr // 14.1.1 euc-kr decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function EUCKRDecoder(options) { var fatal = options.fatal; // euc-kr's decoder has an associated euc-kr lead (initially 0x00). var /** @type {number} */ euckr_lead = 0x00; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and euc-kr lead is not 0x00, set // euc-kr lead to 0x00 and return error. if (bite === end_of_stream && euckr_lead !== 0) { euckr_lead = 0x00; return decoderError(fatal); } // 2. If byte is end-of-stream and euc-kr lead is 0x00, return // finished. if (bite === end_of_stream && euckr_lead === 0) return finished; // 3. If euc-kr lead is not 0x00, let lead be euc-kr lead, let // pointer be null, set euc-kr lead to 0x00, and then run these // substeps: if (euckr_lead !== 0x00) { var lead = euckr_lead; var pointer = null; euckr_lead = 0x00; // 1. If byte is in the range 0x41 to 0xFE, inclusive, set // pointer to (lead − 0x81) × 190 + (byte − 0x41). if (inRange(bite, 0x41, 0xFE)) pointer = (lead - 0x81) * 190 + (bite - 0x41); // 2. Let code point be null, if pointer is null, and the // index code point for pointer in index euc-kr otherwise. var code_point = (pointer === null) ? null : indexCodePointFor(pointer, index('euc-kr')); // 3. If code point is null and byte is an ASCII byte, prepend // byte to stream. if (pointer === null && isASCIIByte(bite)) stream.prepend(bite); // 4. If code point is null, return error. if (code_point === null) return decoderError(fatal); // 5. Return a code point whose value is code point. return code_point; } // 4. If byte is an ASCII byte, return a code point whose value // is byte. if (isASCIIByte(bite)) return bite; // 5. If byte is in the range 0x81 to 0xFE, inclusive, set // euc-kr lead to byte and return continue. if (inRange(bite, 0x81, 0xFE)) { euckr_lead = bite; return null; } // 6. Return error. return decoderError(fatal); }; } // 14.1.2 euc-kr encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function EUCKREncoder(options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. Let pointer be the index pointer for code point in index // euc-kr. var pointer = indexPointerFor(code_point, index('euc-kr')); // 4. If pointer is null, return error with code point. if (pointer === null) return encoderError(code_point); // 5. Let lead be floor(pointer / 190) + 0x81. var lead = floor(pointer / 190) + 0x81; // 6. Let trail be pointer % 190 + 0x41. var trail = (pointer % 190) + 0x41; // 7. Return two bytes whose values are lead and trail. return [lead, trail]; }; } /** @param {{fatal: boolean}} options */ encoders['EUC-KR'] = function(options) { return new EUCKREncoder(options); }; /** @param {{fatal: boolean}} options */ decoders['EUC-KR'] = function(options) { return new EUCKRDecoder(options); }; // // 15. Legacy miscellaneous encodings // // 15.1 replacement // Not needed - API throws RangeError // 15.2 Common infrastructure for utf-16be and utf-16le /** * @param {number} code_unit * @param {boolean} utf16be * @return {!Array.<number>} bytes */ function convertCodeUnitToBytes(code_unit, utf16be) { // 1. Let byte1 be code unit >> 8. var byte1 = code_unit >> 8; // 2. Let byte2 be code unit & 0x00FF. var byte2 = code_unit & 0x00FF; // 3. Then return the bytes in order: // utf-16be flag is set: byte1, then byte2. if (utf16be) return [byte1, byte2]; // utf-16be flag is unset: byte2, then byte1. return [byte2, byte1]; } // 15.2.1 shared utf-16 decoder /** * @constructor * @implements {Decoder} * @param {boolean} utf16_be True if big-endian, false if little-endian. * @param {{fatal: boolean}} options */ function UTF16Decoder(utf16_be, options) { var fatal = options.fatal; var /** @type {?number} */ utf16_lead_byte = null, /** @type {?number} */ utf16_lead_surrogate = null; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream and either utf-16 lead byte or // utf-16 lead surrogate is not null, set utf-16 lead byte and // utf-16 lead surrogate to null, and return error. if (bite === end_of_stream && (utf16_lead_byte !== null || utf16_lead_surrogate !== null)) { return decoderError(fatal); } // 2. If byte is end-of-stream and utf-16 lead byte and utf-16 // lead surrogate are null, return finished. if (bite === end_of_stream && utf16_lead_byte === null && utf16_lead_surrogate === null) { return finished; } // 3. If utf-16 lead byte is null, set utf-16 lead byte to byte // and return continue. if (utf16_lead_byte === null) { utf16_lead_byte = bite; return null; } // 4. Let code unit be the result of: var code_unit; if (utf16_be) { // utf-16be decoder flag is set // (utf-16 lead byte << 8) + byte. code_unit = (utf16_lead_byte << 8) + bite; } else { // utf-16be decoder flag is unset // (byte << 8) + utf-16 lead byte. code_unit = (bite << 8) + utf16_lead_byte; } // Then set utf-16 lead byte to null. utf16_lead_byte = null; // 5. If utf-16 lead surrogate is not null, let lead surrogate // be utf-16 lead surrogate, set utf-16 lead surrogate to null, // and then run these substeps: if (utf16_lead_surrogate !== null) { var lead_surrogate = utf16_lead_surrogate; utf16_lead_surrogate = null; // 1. If code unit is in the range U+DC00 to U+DFFF, // inclusive, return a code point whose value is 0x10000 + // ((lead surrogate − 0xD800) << 10) + (code unit − 0xDC00). if (inRange(code_unit, 0xDC00, 0xDFFF)) { return 0x10000 + (lead_surrogate - 0xD800) * 0x400 + (code_unit - 0xDC00); } // 2. Prepend the sequence resulting of converting code unit // to bytes using utf-16be decoder flag to stream and return // error. stream.prepend(convertCodeUnitToBytes(code_unit, utf16_be)); return decoderError(fatal); } // 6. If code unit is in the range U+D800 to U+DBFF, inclusive, // set utf-16 lead surrogate to code unit and return continue. if (inRange(code_unit, 0xD800, 0xDBFF)) { utf16_lead_surrogate = code_unit; return null; } // 7. If code unit is in the range U+DC00 to U+DFFF, inclusive, // return error. if (inRange(code_unit, 0xDC00, 0xDFFF)) return decoderError(fatal); // 8. Return code point code unit. return code_unit; }; } // 15.2.2 shared utf-16 encoder /** * @constructor * @implements {Encoder} * @param {boolean} utf16_be True if big-endian, false if little-endian. * @param {{fatal: boolean}} options */ function UTF16Encoder(utf16_be, options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1. If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is in the range U+0000 to U+FFFF, inclusive, // return the sequence resulting of converting code point to // bytes using utf-16be encoder flag. if (inRange(code_point, 0x0000, 0xFFFF)) return convertCodeUnitToBytes(code_point, utf16_be); // 3. Let lead be ((code point − 0x10000) >> 10) + 0xD800, // converted to bytes using utf-16be encoder flag. var lead = convertCodeUnitToBytes( ((code_point - 0x10000) >> 10) + 0xD800, utf16_be); // 4. Let trail be ((code point − 0x10000) & 0x3FF) + 0xDC00, // converted to bytes using utf-16be encoder flag. var trail = convertCodeUnitToBytes( ((code_point - 0x10000) & 0x3FF) + 0xDC00, utf16_be); // 5. Return a byte sequence of lead followed by trail. return lead.concat(trail); }; } // 15.3 utf-16be // 15.3.1 utf-16be decoder /** @param {{fatal: boolean}} options */ encoders['UTF-16BE'] = function(options) { return new UTF16Encoder(true, options); }; // 15.3.2 utf-16be encoder /** @param {{fatal: boolean}} options */ decoders['UTF-16BE'] = function(options) { return new UTF16Decoder(true, options); }; // 15.4 utf-16le // 15.4.1 utf-16le decoder /** @param {{fatal: boolean}} options */ encoders['UTF-16LE'] = function(options) { return new UTF16Encoder(false, options); }; // 15.4.2 utf-16le encoder /** @param {{fatal: boolean}} options */ decoders['UTF-16LE'] = function(options) { return new UTF16Decoder(false, options); }; // 15.5 x-user-defined // 15.5.1 x-user-defined decoder /** * @constructor * @implements {Decoder} * @param {{fatal: boolean}} options */ function XUserDefinedDecoder(options) { var fatal = options.fatal; /** * @param {Stream} stream The stream of bytes being decoded. * @param {number} bite The next byte read from the stream. * @return {?(number|!Array.<number>)} The next code point(s) * decoded, or null if not enough data exists in the input * stream to decode a complete code point. */ this.handler = function(stream, bite) { // 1. If byte is end-of-stream, return finished. if (bite === end_of_stream) return finished; // 2. If byte is an ASCII byte, return a code point whose value // is byte. if (isASCIIByte(bite)) return bite; // 3. Return a code point whose value is 0xF780 + byte − 0x80. return 0xF780 + bite - 0x80; }; } // 15.5.2 x-user-defined encoder /** * @constructor * @implements {Encoder} * @param {{fatal: boolean}} options */ function XUserDefinedEncoder(options) { var fatal = options.fatal; /** * @param {Stream} stream Input stream. * @param {number} code_point Next code point read from the stream. * @return {(number|!Array.<number>)} Byte(s) to emit. */ this.handler = function(stream, code_point) { // 1.If code point is end-of-stream, return finished. if (code_point === end_of_stream) return finished; // 2. If code point is an ASCII code point, return a byte whose // value is code point. if (isASCIICodePoint(code_point)) return code_point; // 3. If code point is in the range U+F780 to U+F7FF, inclusive, // return a byte whose value is code point − 0xF780 + 0x80. if (inRange(code_point, 0xF780, 0xF7FF)) return code_point - 0xF780 + 0x80; // 4. Return error with code point. return encoderError(code_point); }; } /** @param {{fatal: boolean}} options */ encoders['x-user-defined'] = function(options) { return new XUserDefinedEncoder(options); }; /** @param {{fatal: boolean}} options */ decoders['x-user-defined'] = function(options) { return new XUserDefinedDecoder(options); }; if (!global['TextEncoder']) global['TextEncoder'] = TextEncoder; if (!global['TextDecoder']) global['TextDecoder'] = TextDecoder; if (typeof module !== "undefined" && module.exports) { module.exports = { TextEncoder: global['TextEncoder'], TextDecoder: global['TextDecoder'], EncodingIndexes: global["encoding-indexes"] }; }// For strict environments where `this` inside the global scope// is `undefined`, take a pure object instead}(this || {}));

text-encoding/encoding-indexes:文件内容太多放不下了

(function(global) { 'use strict'; if (typeof module !== "undefined" && module.exports) { module.exports = global; } global["encoding-indexes"] ={ "gb18030":[]} }(this || {}));

gb18030:是一个汉字编码集

注: 1、连接的蓝牙必须是低功耗的蓝牙,经典蓝牙无法搜索到(因为不能执行uni.onBluetoothDeviceFound) 2、部分手机需要将定位打开才能搜索到蓝牙(开启定位才能执行uni.onBluetoothDeviceFound)

本文链接地址:https://www.jiuchutong.com/zhishi/277060.html 转载请保留说明!

上一篇:腾讯地图api使用——地图选点自动定位到当前位置(腾讯地图js api)

下一篇:腾讯电脑管家中腾讯新闻是什么?(腾讯电脑管家中的软件搬家)

  • 你餐厅的营销顾客不买账?试试这10招!(你餐厅的营销顾客有哪些)

    你餐厅的营销顾客不买账?试试这10招!(你餐厅的营销顾客有哪些)

  • 微信防误触怎么关闭(微信防误触怎么开)

    微信防误触怎么关闭(微信防误触怎么开)

  • 京东退款后京贴返还吗(退货后京东金贴)

    京东退款后京贴返还吗(退货后京东金贴)

  • 手机电池突然从50到1什么原因(手机电池突然从40%降到3%)

    手机电池突然从50到1什么原因(手机电池突然从40%降到3%)

  • 苹果原装数据线多长(苹果原装数据线怎么辨别真假)

    苹果原装数据线多长(苹果原装数据线怎么辨别真假)

  • vivo手机系统修复模式什么意思(vivo手机系统修复后需要密码吗)

    vivo手机系统修复模式什么意思(vivo手机系统修复后需要密码吗)

  • 快手私信删除了还能看到内容么(快手私信删除了还有记录吗)

    快手私信删除了还能看到内容么(快手私信删除了还有记录吗)

  • 虎牙直播一个藏宝图等于多少人民币(虎牙直播一个藏宝图多少钱)

    虎牙直播一个藏宝图等于多少人民币(虎牙直播一个藏宝图多少钱)

  • 电脑桌面隐藏了怎么弄出来(电脑桌面隐藏了一个图标,怎么恢复)

    电脑桌面隐藏了怎么弄出来(电脑桌面隐藏了一个图标,怎么恢复)

  • 小米cc9快充多少w(小米cc9快充多少功率)

    小米cc9快充多少w(小米cc9快充多少功率)

  • 把通讯录导到新手机上(通讯录怎么拷贝到新手机)

    把通讯录导到新手机上(通讯录怎么拷贝到新手机)

  • 魅族16th有快充吗(魅族16th快充多少w)

    魅族16th有快充吗(魅族16th快充多少w)

  • 台式电脑进不了系统怎么办(台式电脑进不了bios怎么办)

    台式电脑进不了系统怎么办(台式电脑进不了bios怎么办)

  • 移动卡显示hd怎么消除(移动卡显示hd啥意思)

    移动卡显示hd怎么消除(移动卡显示hd啥意思)

  • iphone11预购什么时候发货(苹果11预售)

    iphone11预购什么时候发货(苹果11预售)

  • vivo如何导出手机通讯录(vivo手机怎样导出手机号码)

    vivo如何导出手机通讯录(vivo手机怎样导出手机号码)

  • 谷歌地图怎么调语言(谷歌地图怎么调比例)

    谷歌地图怎么调语言(谷歌地图怎么调比例)

  • 红米k20pro有人脸识别吗(红米k40有人脸)

    红米k20pro有人脸识别吗(红米k40有人脸)

  • 表格属性在哪里(excel2007表格属性在哪里)

    表格属性在哪里(excel2007表格属性在哪里)

  • 安卓手机图标大小设置(安卓手机图标大小尺寸)

    安卓手机图标大小设置(安卓手机图标大小尺寸)

  • 淘宝怎么投诉卖家电话(淘宝怎么投诉卖家怎么找到客服小蜜人工服务)

    淘宝怎么投诉卖家电话(淘宝怎么投诉卖家怎么找到客服小蜜人工服务)

  • 千库网如何下载图片(千库网如何下载无水印)

    千库网如何下载图片(千库网如何下载无水印)

  • 最新 Award Bios 设置全程图解教程(biospwds最新版)

    最新 Award Bios 设置全程图解教程(biospwds最新版)

  • vue中如何使用vue-pdf及相应报错解决(vue中如何使用weboffice)

    vue中如何使用vue-pdf及相应报错解决(vue中如何使用weboffice)

  • qttask.exe是什么进程?qttask.exe是不是病毒?

    qttask.exe是什么进程?qttask.exe是不是病毒?

  • 损失函数 | BCE Loss(Binary CrossEntropy Loss)(损失函数是什么)

    损失函数 | BCE Loss(Binary CrossEntropy Loss)(损失函数是什么)

  • 关于Vue-Router 底层运行逻辑浅析(vue$router)

    关于Vue-Router 底层运行逻辑浅析(vue$router)

  • 收客户刷卡手续费时客户不愿意缴费,如何处理
  • 购入安装设备的专用材料分录
  • 过了汇算清缴期后发现错误
  • 稽查查补的税款用什么时候的税率表示
  • 电子税务局怎么添加购票人
  • 货代企业都需要交什么税
  • 土地使用权出让金多少钱一平米
  • 一般纳税人旧货
  • 一个企业只有收入没有支出合理吗
  • 减少实收资本会引起资产和所有者权益发生变化吗
  • 什么是法?法的本质特征是什么
  • 提供部分原料的英文
  • 小规模季度销售超过30万怎么纳税
  • 处于汇总期
  • 什么叫统借统还业务
  • 有形动产经营租赁服务税率
  • 合同税率16%调整到13%怎么算
  • 资产处置损益借贷方向表示什么
  • 消费税什么时候用最高售价
  • 车辆保险车船税怎么计算
  • 劳务费个人所得税税率表2023最新
  • 认证成功次月何时补发
  • 浏览器标签栏
  • 公司收到个人的款怎么做账务处理
  • WIN10怎么禁止始终处于活动状态
  • 说一下键盘
  • php string函数
  • php的数组函数
  • 土增税清算扣除项目分摊公式
  • 记账凭证填制的依据
  • php二维数组查询指定值
  • 免税怎么开
  • 已领待用物资
  • MySQL高级查询
  • vue错误提示
  • vue中的$el
  • day29--Java泛型02
  • 待处理财产损溢借方是增还是减
  • 材料暂估入库的依据有哪些
  • 织梦怎么改文字
  • 个税显示退税成功但是没有收到钱
  • 发票跨月还可以作废吗
  • 房地产开发间接费用和开发费用的区别
  • sqlserver触发器写法
  • 提高资产利用率的例子
  • 高铁票丢失了可以补打吗
  • 差旅费误餐补助
  • 如何处理库存货
  • 工业企业分为哪几类
  • 递延税款科目怎样使用
  • 停产工人工资计入哪个科目
  • 其他应付款贷方正数表示什么意思
  • 收到合同后的整个流程
  • 企业销售退回的论文
  • 原材料采购未入库会计分录
  • 民非企业附加税计提和缴纳分录
  • 申报表弥补以前年度亏损是链接哪个数据
  • 租写字楼水电费自己付吗
  • 废品损失明细账应选择三栏式
  • 政府部门有税务和审计审查吗
  • WIN10开始菜单点击鼠标右键没反应的处理方法
  • Windows Server 2008下的自助安全防御
  • freebsd忘记root密码
  • explore是什么文件
  • WIN10系统安装教程
  • win7如何显示文件扩展
  • 自定义ui界面
  • edit apps
  • 黑客需要学哪些
  • perl随机数
  • 笔记本电脑没有鼠标怎么多选文件
  • 批处理 leq
  • android 侧滑页面
  • js控制浏览器返回按钮
  • 阿里云服务器使用教程
  • 县税务局可以去市里吗
  • 曲靖市税务局领导班子
  • 河北省国税局发展前景
  • 江西省国家税务局总局官网
  • 烟叶税进项抵扣
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号