UNPKG

qrcode-generator-ts

Version:

Typescript QR Code generator based on https://github.com/kazuhikoarase/qrcode-generator

266 lines (208 loc) 6.07 kB
import { Base64 } from '../io/Base64'; import { OutputStream } from '../io/OutputStream'; import { ByteArrayOutputStream } from '../io/ByteArrayOutputStream'; 'use strict'; /** * GIF Image (B/W) * @author Kazuhiko Arase */ export class GIFImage { private width: number; private height: number; private data: number[]; constructor(width: number, height: number) { this.width = width; this.height = height; var size = width * height; this.data = []; for (var i = 0; i < size; i += 1) { this.data.push(0); } } public setPixel(x: number, y: number, pixel: number): void { if (x < 0 || this.width <= x) throw '!' + x; if (y < 0 || this.height <= y) throw '!' + y; this.data[y * this.width + x] = pixel; } public getPixel(x: number, y: number): number { if (x < 0 || this.width <= x) throw '!' + x; if (y < 0 || this.height <= y) throw '!' + y; return this.data[y * this.width + x]; } public write(out: OutputStream): void { //--------------------------------- // GIF Signature out.writeByte('G'.charCodeAt(0)); out.writeByte('I'.charCodeAt(0)); out.writeByte('F'.charCodeAt(0)); out.writeByte('8'.charCodeAt(0)); out.writeByte('7'.charCodeAt(0)); out.writeByte('a'.charCodeAt(0)); //--------------------------------- // Screen Descriptor this.writeWord(out, this.width); this.writeWord(out, this.height); out.writeByte(0x80); // 2bit out.writeByte(0); out.writeByte(0); //--------------------------------- // Global Color Map // black out.writeByte(0x00); out.writeByte(0x00); out.writeByte(0x00); // white out.writeByte(0xff); out.writeByte(0xff); out.writeByte(0xff); //--------------------------------- // Image Descriptor out.writeByte(','.charCodeAt(0)); this.writeWord(out, 0); this.writeWord(out, 0); this.writeWord(out, this.width); this.writeWord(out, this.height); out.writeByte(0); //--------------------------------- // Local Color Map //--------------------------------- // Raster Data var lzwMinCodeSize = 2; var raster = this.getLZWRaster(lzwMinCodeSize); out.writeByte(lzwMinCodeSize); var offset: number = 0; while (raster.length - offset > 255) { out.writeByte(255); this.writeBytes(out, raster, offset, 255); offset += 255; } out.writeByte(raster.length - offset); this.writeBytes(out, raster, offset, raster.length - offset); out.writeByte(0x00); //--------------------------------- // GIF Terminator out.writeByte(';'.charCodeAt(0)); } private getLZWRaster(lzwMinCodeSize: number): number[] { var clearCode = 1 << lzwMinCodeSize; var endCode = (1 << lzwMinCodeSize) + 1; var bitLength = lzwMinCodeSize + 1; // Setup LZWTable var table = new LZWTable(); for (var i = 0; i < clearCode; i += 1) { table.add(String.fromCharCode(i)); } table.add(String.fromCharCode(clearCode)); table.add(String.fromCharCode(endCode)); var byteOut = new ByteArrayOutputStream(); var bitOut = new BitOutputStream(byteOut); try { // clear code bitOut.write(clearCode, bitLength); var dataIndex = 0; var s = String.fromCharCode(this.data[dataIndex]); dataIndex += 1; while (dataIndex < this.data.length) { var c = String.fromCharCode(this.data[dataIndex]); dataIndex += 1; if (table.contains(s + c)) { s = s + c; } else { bitOut.write(table.indexOf(s), bitLength); if (table.getSize() < 0xfff) { if (table.getSize() == (1 << bitLength)) { bitLength += 1; } table.add(s + c); } s = c; } } bitOut.write(table.indexOf(s), bitLength); // end code bitOut.write(endCode, bitLength); } finally { bitOut.close(); } return byteOut.toByteArray(); } private writeWord(out: OutputStream, i: number) { out.writeByte(i & 0xff); out.writeByte((i >>> 8) & 0xff); } private writeBytes( out: OutputStream, bytes: number[], off: number, len: number ) { for (var i = 0; i < len; i += 1) { out.writeByte(bytes[i + off]); } } public toDataURL(): string { var bout = new ByteArrayOutputStream(); this.write(bout); bout.close(); var s = ''; var bytes = Base64.encode(bout.toByteArray()); for (var i = 0; i < bytes.length; i += 1) { s += String.fromCharCode(bytes[i]); } return 'data:image/gif;base64,' + s; } } class LZWTable { private map: { [key: string]: number; } = {}; private size: number = 0; constructor() { } public add(key: string): void { if (this.contains(key)) { throw 'dup key:' + key; } this.map[key] = this.size; this.size += 1; } public getSize(): number { return this.size; } public indexOf(key: string): number { return this.map[key]; } public contains(key: string): boolean { return typeof this.map[key] != 'undefined'; } } class BitOutputStream { private out: OutputStream; private bitLength: number; private bitBuffer: number; constructor(out: OutputStream) { this.out = out; this.bitLength = 0; } public write(data: number, length: number): void { if ((data >>> length) != 0) { throw 'length over'; } while (this.bitLength + length >= 8) { this.out.writeByte(0xff & ((data << this.bitLength) | this.bitBuffer)); length -= (8 - this.bitLength); data >>>= (8 - this.bitLength); this.bitBuffer = 0; this.bitLength = 0; } this.bitBuffer = (data << this.bitLength) | this.bitBuffer; this.bitLength = this.bitLength + length; } public flush(): void { if (this.bitLength > 0) { this.out.writeByte(this.bitBuffer); } this.out.flush(); } public close(): void { this.flush(); this.out.close(); } }