From b414036d9859f5d664249d4f8f4841985aaa4a2e Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Sat, 22 Aug 2020 15:29:41 +0200 Subject: [PATCH 1/8] JavaScript improvements * Custom colours * PNG/WEBP/JPEG output on the sample page via HTML5 canvas * Sample page: More options, better layout, download and info * SVG output: Fixed background rect size * Canvas output: Added margin * ASCII output: Added optional inverted display (was already inverted, switched it back) * ASCII output: Fixed size (was square value of requested size) * HTML table output: Optimised HTML size --- js/qrcode.js | 134 +++++++++++++++++++++++++++---------------------- js/sample.css | 49 ++++++++++++++++++ js/sample.html | 95 +++++++++++++++++++++++++---------- js/sample.js | 118 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 296 insertions(+), 100 deletions(-) create mode 100644 js/sample.css diff --git a/js/qrcode.js b/js/qrcode.js index 76889b5..5879f78 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -37,6 +37,8 @@ var qrcode = function() { var _moduleCount = 0; var _dataCache = null; var _dataList = []; + var _foreground = "black"; + var _background = "white"; var _this = {}; @@ -452,6 +454,17 @@ var qrcode = function() { makeImpl(false, getBestMaskPattern() ); }; + _this.setColors = function(foreground, background) { + _foreground = simplifyColor(foreground); + _background = simplifyColor(background); + + function simplifyColor(c) { + if (c.match(/^#[0-9A-Fa-f]{6}$/) && c[1] === c[2] && c[3] === c[4] && c[5] === c[6]) + c = "#" + c[1] + c[3] + c[5]; + return c; + } + }; + _this.createTableTag = function(cellSize, margin) { cellSize = cellSize || 2; @@ -459,34 +472,28 @@ var qrcode = function() { var qrHtml = ''; - qrHtml += '' : ''; qrSvg += (alt.text) ? '' + escapeXml(alt.text) + '' : ''; - qrSvg += ''; + qrSvg += ''; qrSvg += ''; + qrSvg += '" stroke="transparent" fill="' + _foreground + '"/>'; qrSvg += ''; return qrSvg; @@ -563,7 +570,7 @@ var qrcode = function() { var min = margin; var max = size - margin; - return createDataURL(size, size, function(x, y) { + return createDataURL(size, size, _foreground, _background, function(x, y) { if (min <= x && x < max && min <= y && y < max) { var c = Math.floor( (x - min) / cellSize); var r = Math.floor( (y - min) / cellSize); @@ -582,21 +589,11 @@ var qrcode = function() { var size = _this.getModuleCount() * cellSize + margin * 2; var img = ''; - img += ' 0) { - return ascii.substring(0, ascii.length - size - 1) + Array(size+1).join('▀'); + return ascii.substring(0, ascii.length - size - 1) + Array(size+1).join(inverted ? '▀' : '\xa0'); } return ascii.substring(0, ascii.length-1); }; - _this.createASCII = function(cellSize, margin) { + _this.createASCII = function(cellSize, margin, inverted) { cellSize = cellSize || 1; if (cellSize < 2) { - return _createHalfASCII(margin); + return _createHalfASCII(margin, inverted); } cellSize -= 1; - margin = (typeof margin == 'undefined')? cellSize * 2 : margin; + margin = (typeof margin == 'undefined')? cellSize * 2 : margin / (cellSize + 1) * cellSize; var size = _this.getModuleCount() * cellSize + margin * 2; var min = margin; @@ -689,8 +686,13 @@ var qrcode = function() { var y, x, r, p; - var white = Array(cellSize+1).join('██'); - var black = Array(cellSize+1).join(' '); + var white = '\xa0\xa0'; + var black = '██'; + if (inverted) { + var b = black; + black = white; + white = b; + } var ascii = ''; var line = ''; @@ -708,21 +710,24 @@ var qrcode = function() { line += p ? white : black; } - for (r = 0; r < cellSize; r += 1) { - ascii += line + '\n'; - } + ascii += line + '\n'; } return ascii.substring(0, ascii.length-1); }; - _this.renderTo2dContext = function(context, cellSize) { + _this.renderTo2dContext = function(context, cellSize, margin) { cellSize = cellSize || 2; + margin = (typeof margin == 'undefined')? cellSize * 4 : margin; var length = _this.getModuleCount(); + if (margin > 0) { + context.fillStyle = _background; + context.fillRect(0, 0, length * cellSize + margin * 2, length * cellSize + margin * 2); + } for (var row = 0; row < length; row++) { for (var col = 0; col < length; col++) { - context.fillStyle = _this.isDark(row, col) ? 'black' : 'white'; - context.fillRect(row * cellSize, col * cellSize, cellSize, cellSize); + context.fillStyle = _this.isDark(row, col) ? _foreground : _background; + context.fillRect(margin + col * cellSize, margin + row * cellSize, cellSize, cellSize); } } } @@ -2009,7 +2014,7 @@ var qrcode = function() { // gifImage (B/W) //--------------------------------------------------------------------- - var gifImage = function(width, height) { + var gifImage = function(width, height, foreground, background) { var _width = width; var _height = height; @@ -2041,15 +2046,17 @@ var qrcode = function() { //--------------------------------- // Global Color Map - // black - out.writeByte(0x00); - out.writeByte(0x00); - out.writeByte(0x00); + // foreground + var fg = parseColor(foreground, "#000"); + out.writeByte(fg[0]); + out.writeByte(fg[1]); + out.writeByte(fg[2]); - // white - out.writeByte(0xff); - out.writeByte(0xff); - out.writeByte(0xff); + // background + var bg = parseColor(background, "#fff"); + out.writeByte(bg[0]); + out.writeByte(bg[1]); + out.writeByte(bg[2]); //--------------------------------- // Image Descriptor @@ -2089,6 +2096,15 @@ var qrcode = function() { out.writeString(';'); }; + function parseColor(c, fallback) { + c = c.toLowerCase(); + if (c.match(/^#[0-9a-f]{3}$/)) + return [parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16)]; + if (c.match(/^#[0-9a-f]{6}$/)) + return [parseInt(c[1] + c[2], 16), parseInt(c[3] + c[4], 16), parseInt(c[5] + c[6], 16)]; + return parseColor(fallback); + } + var bitOutputStream = function(out) { var _out = out; @@ -2219,8 +2235,8 @@ var qrcode = function() { return _this; }; - var createDataURL = function(width, height, getPixel) { - var gif = gifImage(width, height); + var createDataURL = function(width, height, foreground, background, getPixel) { + var gif = gifImage(width, height, foreground, background); for (var y = 0; y < height; y += 1) { for (var x = 0; x < width; x += 1) { gif.setPixel(x, y, getPixel(x, y) ); diff --git a/js/sample.css b/js/sample.css new file mode 100644 index 0000000..179e64a --- /dev/null +++ b/js/sample.css @@ -0,0 +1,49 @@ +html +{ + background: #f0f0f0; +} +body, +input, +select, +button +{ + font: 16px sans-serif; + line-height: 133%; +} +h1 +{ + text-align: center; +} +textarea +{ + width: 95%; + height: 100px; + resize: vertical; + font-size: 16px; +} +p +{ + margin: 1em auto; + text-align: center; +} +#qr +{ + margin: 3em auto; + text-align: center; +} +#qr > * +{ + outline: solid 1px #e0e0e0; +} +#qr pre +{ + display: inline-block; + background: white; + color: black; + font-size: 8px; + line-height: 100%; +} +#qr table +{ + margin: 0 auto; +} diff --git a/js/sample.html b/js/sample.html index 11bd2a8..19515ad 100644 --- a/js/sample.html +++ b/js/sample.html @@ -1,7 +1,7 @@ - + @@ -14,36 +14,77 @@ --> QR Code Generator for JavaScript + +

QR Code Generator for JavaScript

- TypeNumber: - - ErrorCorrectionLevel: - - Mode: - - Multibyte: - -
- -
- +

+
+ +

+

+ Cell size: + +   + Padding: + +   + Error correction level: + +

+

+ Image type: + +   + Foreground: + +   + Background: + +

+

+ Content mode: + +   + Multibyte text: + +   + Size type: + +

+

+ +

+

+ +

diff --git a/js/sample.js b/js/sample.js index d351b05..8f4e006 100644 --- a/js/sample.js +++ b/js/sample.js @@ -1,3 +1,4 @@ +var downloadFileName, downloadType, downloadData; var body_loadHander = function() { @@ -9,41 +10,130 @@ var body_loadHander = function() { }; var t = document.forms['qrForm'].elements['t']; - t.appendChild(crtOpt('' + 0, 'Auto Detect') ); + t.appendChild(crtOpt('' + 0, 'Auto') ); for (var i = 1; i <= 40; i += 1) { t.appendChild(crtOpt('' + i, '' + i) ); } t.value = '0'; + var form = document.forms['qrForm']; + form.elements['msg'].addEventListener("input", update_qrcode); + form.elements['t'].addEventListener("change", update_qrcode); + form.elements['cellsize'].addEventListener("input", update_qrcode); + form.elements['padding'].addEventListener("input", update_qrcode); + form.elements['e'].addEventListener("change", update_qrcode); + form.elements['m'].addEventListener("change", update_qrcode); + form.elements['mb'].addEventListener("change", update_qrcode); + form.elements['format'].addEventListener("change", update_qrcode); + form.elements['foreground'].addEventListener("change", update_qrcode); + form.elements['background'].addEventListener("change", update_qrcode); update_qrcode(); + + document.getElementById("download").addEventListener("click", function () { + download(downloadFileName, downloadType, downloadData); + }); }; var draw_qrcode = function(text, typeNumber, errorCorrectionLevel) { document.write(create_qrcode(text, typeNumber, errorCorrectionLevel) ); }; -var create_qrcode = function(text, typeNumber, - errorCorrectionLevel, mode, mb) { +var create_qrcode = function(text, typeNumber, cellsize, padding, errorCorrectionLevel, mode, mb, format, fg, bg) { + + if (!mode) { + if (text.match(/^[0-9]+$/)) + mode = "Numeric"; + else if (text.match(/^[0-9A-Z $%*+/.:-]+$/)) + mode = "Alphanumeric"; + else + mode = "Byte"; + } - qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; + try { + qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; + + var qr = qrcode(typeNumber || 4, errorCorrectionLevel || 'M'); + qr.addData(text, mode); + qr.make(); + qr.setColors(fg, bg); - var qr = qrcode(typeNumber || 4, errorCorrectionLevel || 'M'); - qr.addData(text, mode); - qr.make(); + var size = qr.getModuleCount() * cellsize + padding * 2; + document.getElementById("qr-info").innerHTML = "Image size: " + size + "x" + size + " px"; -// return qr.createTableTag(); -// return qr.createSvgTag(); - return qr.createImgTag(); + switch (format) { + case "svg": + downloadFileName = "qr.svg"; + downloadType = "image/svg"; + downloadData = qr.createSvgTag(cellsize, padding); + return downloadData; + case "gif": + case "png": + case "webp": + case "jpeg": + var canvas = document.createElement("canvas"); + canvas.width = canvas.height = qr.getModuleCount() * cellsize + padding * 2; + var context = canvas.getContext("2d"); + qr.renderTo2dContext(context, cellsize, padding); + downloadFileName = "qr." + format; + downloadType = ""; // raw download data URL + downloadData = canvas.toDataURL("image/" + format); + var warning = ""; + if (format === "webp" && downloadData.match(/^data:image\/png/)) + warning = "

Attention: This browser cannot generate WEBP images and made a PNG instead."; + return '' + warning; + case "gif-direct": + downloadFileName = "qr.gif"; + downloadType = ""; // raw download data URL + downloadData = qr.createDataURL(cellsize, padding); + return qr.createImgTag(cellsize, padding); + case "ascii": + downloadFileName = "qr.txt"; + downloadType = "text/plain"; + downloadData = qr.createASCII(cellsize, padding); + return "
" + downloadData + "
"; + case "ascii-inv": + downloadFileName = "qr.txt"; + downloadType = "text/plain"; + downloadData = qr.createASCII(cellsize, padding, true); + return "
" + downloadData + "
"; + case "table": + downloadFileName = "qr.html"; + downloadType = "text/html"; + downloadData = qr.createTableTag(cellsize, padding); + return downloadData; + } + } + catch (error) { + return "Error: " + error; + } }; var update_qrcode = function() { var form = document.forms['qrForm']; - var text = form.elements['msg'].value. - replace(/^[\s\u3000]+|[\s\u3000]+$/g, ''); + var text = form.elements['msg'].value.replace(/^[\s\u3000]+|[\s\u3000]+$/g, ''); var t = form.elements['t'].value; + var cellsize = +form.elements['cellsize'].value; + var padding = +form.elements['padding'].value * cellsize; var e = form.elements['e'].value; var m = form.elements['m'].value; var mb = form.elements['mb'].value; - document.getElementById('qr').innerHTML = - create_qrcode(text, t, e, m, mb); + var format = form.elements['format'].value; + var fg = form.elements['foreground'].value; + var bg = form.elements['background'].value; + document.getElementById("length").innerHTML = "(" + text.length + " characters)"; + document.getElementById('qr').innerHTML = create_qrcode(text, t, cellsize, padding, e, m, mb, format, fg, bg); }; + +// Source: https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server +function download(filename, mimeType, text) { + var element = document.createElement("a"); + if (downloadType) + element.setAttribute("href", "data:" + mimeType + ";charset=utf-8," + encodeURIComponent(text)); + else + element.setAttribute("href", text); + element.setAttribute("download", filename); + element.style.display = "none"; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +} From 22c85661189a633c925af3c91f6480a5c203d951 Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Sat, 22 Aug 2020 16:13:24 +0200 Subject: [PATCH 2/8] Updated documentation --- js/README.md | 15 ++++++++++----- js/qrcode.d.ts | 17 ++++++++--------- js/qrcode.js | 2 +- js/sample.js | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/js/README.md b/js/README.md index a85af12..f68c416 100644 --- a/js/README.md +++ b/js/README.md @@ -68,11 +68,15 @@ _[Note] call make() before this function._ | row | number | 0 ~ moduleCount - 1 | | col | number | 0 ~ moduleCount - 1 | +#### setColors(foreground, background) => void +Sets the foreground and background colors for the output formats. Default to black and white, respectively. +_[Note] This has no effect on the ASCII output._ + #### createDataURL(cellSize, margin) => string #### createImgTag(cellSize, margin, alt) => string #### createSvgTag(cellSize, margin) => string #### createTableTag(cellSize, margin) => string -#### createASCII(cellSize, margin) => string +#### createASCII(cellSize, margin, inverted) => string Helper functions for HTML. _[Note] call make() before these functions._ @@ -81,6 +85,7 @@ Helper functions for HTML. | cellSize | number | default: 2 | | margin | number | default: cellSize * 4 | | alt | string | (optional) | +| inverted | bool | (optional) | #### createSvgTag(opts) => string @@ -91,11 +96,11 @@ Helper functions for HTML. | opts.margin | number | default: cellSize * 4 | | opts.scalable | boolean | default: false | -#### renderTo2dContext(context, cellSize) => void +#### renderTo2dContext(context, cellSize, margin) => void --- +---- This implementation is based on JIS X 0510:1999. -The word 'QR Code' is registered trademark of DENSO WAVE INCORPORATED -
http://www.denso-wave.com/qrcode/faqpatent-e.html +The word 'QR Code' is registered trademark of DENSO WAVE INCORPORATED.
+https://www.qrcode.com/en/faq.html diff --git a/js/qrcode.d.ts b/js/qrcode.d.ts index b34952e..c5b90be 100644 --- a/js/qrcode.d.ts +++ b/js/qrcode.d.ts @@ -11,7 +11,7 @@ // // The word 'QR Code' is registered trademark of // DENSO WAVE INCORPORATED -// http://www.denso-wave.com/qrcode/faqpatent-e.html +// https://www.qrcode.com/en/faq.html // //--------------------------------------------------------------------- @@ -31,8 +31,7 @@ interface QRCodeFactory { (typeNumber: TypeNumber, errorCorrectionLevel: ErrorCorrectionLevel) : QRCode; stringToBytes(s: string) : number[]; stringToBytesFuncs : { [encoding : string] : (s: string) => number[] }; - createStringToBytes(unicodeData: string, numChars: number) : - (s : string) => number[]; + createStringToBytes(unicodeData: string, numChars: number) : (s : string) => number[]; } interface QRCode { @@ -40,14 +39,14 @@ interface QRCode { make() : void; getModuleCount() : number; isDark(row: number, col: number) : boolean; - createImgTag(cellSize?: number, margin?: number) : string; - createSvgTag(cellSize?: number, margin?: number) : string; - createSvgTag(opts? : { cellSize?: number, margin?: number, - scalable?: boolean }) : string; + setColors(foreground: string, background: string) : void; + createImgTag(cellSize?: number, margin?: number, alt?: string) : string; + createSvgTag(cellSize?: number, margin?: number, alt?: string, title?: string) : string; + createSvgTag(opts? : { cellSize?: number, margin?: number, scalable?: boolean }) : string; createDataURL(cellSize?: number, margin?: number) : string; createTableTag(cellSize?: number, margin?: number) : string; - createASCII(cellSize?: number, margin?: number) : string; - renderTo2dContext(context: CanvasRenderingContext2D, cellSize?: number): void; + createASCII(cellSize?: number, margin?: number, inverted?: boolean) : string; + renderTo2dContext(context: CanvasRenderingContext2D, cellSize?: number, margin?: number): void; } declare var qrcode : QRCodeFactory; diff --git a/js/qrcode.js b/js/qrcode.js index 5879f78..7af3c3e 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -11,7 +11,7 @@ // // The word 'QR Code' is registered trademark of // DENSO WAVE INCORPORATED -// http://www.denso-wave.com/qrcode/faqpatent-e.html +// https://www.qrcode.com/en/faq.html // //--------------------------------------------------------------------- diff --git a/js/sample.js b/js/sample.js index 8f4e006..c547565 100644 --- a/js/sample.js +++ b/js/sample.js @@ -52,7 +52,7 @@ var create_qrcode = function(text, typeNumber, cellsize, padding, errorCorrectio try { qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; - var qr = qrcode(typeNumber || 4, errorCorrectionLevel || 'M'); + var qr = qrcode(typeNumber || 0, errorCorrectionLevel || 'M'); qr.addData(text, mode); qr.make(); qr.setColors(fg, bg); From ad543381fc5b91e518f3d0e8bb5904a790552021 Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Sat, 22 Aug 2020 17:21:51 +0200 Subject: [PATCH 3/8] Decreased SVG size by about half by combining adjacent rects in a row --- js/qrcode.js | 16 +++++++++------- js/sample.css | 2 +- js/sample.js | 15 ++++++++++++--- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/js/qrcode.js b/js/qrcode.js index 7af3c3e..dff4f69 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -526,10 +526,7 @@ var qrcode = function() { title.id = (title.text) ? title.id || 'qrcode-title' : null; var size = _this.getModuleCount() * cellSize + margin * 2; - var c, mc, r, mr, qrSvg='', rect; - - rect = 'l' + cellSize + ',0 0,' + cellSize + - ' -' + cellSize + ',0 0,-' + cellSize + 'z '; + var c, c2, mc, r, mr, qrSvg='', rect; qrSvg += ''; qrSvg += '' + warning; case "gif-direct": downloadFileName = "qr.gif"; downloadType = ""; // raw download data URL downloadData = qr.createDataURL(cellsize, padding); + info.innerHTML += ", file size: " + (downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); return qr.createImgTag(cellsize, padding); case "ascii": downloadFileName = "qr.txt"; downloadType = "text/plain"; downloadData = qr.createASCII(cellsize, padding); + info.innerHTML += ", file size: " + downloadData.length; return "
" + downloadData + "
"; case "ascii-inv": downloadFileName = "qr.txt"; downloadType = "text/plain"; downloadData = qr.createASCII(cellsize, padding, true); + info.innerHTML += ", file size: " + downloadData.length; return "
" + downloadData + "
"; case "table": downloadFileName = "qr.html"; downloadType = "text/html"; downloadData = qr.createTableTag(cellsize, padding); + info.innerHTML += ", file size: " + downloadData.length; return downloadData; } } From 5859c3bbb143fdb2e706abb14aa8c8688b43eb8e Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Sun, 23 Aug 2020 11:41:12 +0200 Subject: [PATCH 4/8] Sample page: Show determined code size type, better format file size, improved error handling --- js/qrcode.js | 4 ++++ js/sample.js | 38 +++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/js/qrcode.js b/js/qrcode.js index dff4f69..d143a9d 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -454,6 +454,10 @@ var qrcode = function() { makeImpl(false, getBestMaskPattern() ); }; + _this.getTypeNumber = function() { + return _typeNumber; + }; + _this.setColors = function(foreground, background) { _foreground = simplifyColor(foreground); _background = simplifyColor(background); diff --git a/js/sample.js b/js/sample.js index a21e91b..9a46273 100644 --- a/js/sample.js +++ b/js/sample.js @@ -30,7 +30,8 @@ var body_loadHander = function() { update_qrcode(); document.getElementById("download").addEventListener("click", function () { - download(downloadFileName, downloadType, downloadData); + if (downloadData) + download(downloadFileName, downloadType, downloadData); }); }; @@ -49,24 +50,38 @@ var create_qrcode = function(text, typeNumber, cellsize, padding, errorCorrectio mode = "Byte"; } + var info = document.getElementById("qr-info"); + downloadData = undefined; + + var showLength = function (len) { + if (len < 1024) + len += " B"; + else if (len < 10240) + len = Math.round(len * 100 / 1024) / 100 + " KiB"; + else + len = Math.round(len * 10 / 1024) / 10 + " KiB"; + info.innerHTML += ", file size: " + len + ""; + }; + try { qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; - + var qr = qrcode(typeNumber || 0, errorCorrectionLevel || 'M'); qr.addData(text, mode); qr.make(); qr.setColors(fg, bg); var size = qr.getModuleCount() * cellsize + padding * 2; - var info = document.getElementById("qr-info"); - info.innerHTML = "Image size: " + size + "x" + size + " px"; + info.innerHTML = "Size type: " + qr.getTypeNumber() + ", " + + "code columns: " + qr.getModuleCount() + ", " + + "image size: " + size + "x" + size + " px"; switch (format) { case "svg": downloadFileName = "qr.svg"; downloadType = "image/svg"; downloadData = qr.createSvgTag(cellsize, padding); - info.innerHTML += ", file size: " + downloadData.length; + showLength(downloadData.length); return downloadData; case "gif": case "png": @@ -79,7 +94,7 @@ var create_qrcode = function(text, typeNumber, cellsize, padding, errorCorrectio downloadFileName = "qr." + format; downloadType = ""; // raw download data URL downloadData = canvas.toDataURL("image/" + format); - info.innerHTML += ", file size: " + (downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); + showLength(downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); var warning = ""; var m = downloadData.match(/^data:image\/([a-z0-9]+)/); if (m && m[1] !== format) @@ -90,30 +105,31 @@ var create_qrcode = function(text, typeNumber, cellsize, padding, errorCorrectio downloadFileName = "qr.gif"; downloadType = ""; // raw download data URL downloadData = qr.createDataURL(cellsize, padding); - info.innerHTML += ", file size: " + (downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); + showLength(downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); return qr.createImgTag(cellsize, padding); case "ascii": downloadFileName = "qr.txt"; downloadType = "text/plain"; downloadData = qr.createASCII(cellsize, padding); - info.innerHTML += ", file size: " + downloadData.length; + showLength(downloadData.length); return "
" + downloadData + "
"; case "ascii-inv": downloadFileName = "qr.txt"; downloadType = "text/plain"; downloadData = qr.createASCII(cellsize, padding, true); - info.innerHTML += ", file size: " + downloadData.length; + showLength(downloadData.length); return "
" + downloadData + "
"; case "table": downloadFileName = "qr.html"; downloadType = "text/html"; downloadData = qr.createTableTag(cellsize, padding); - info.innerHTML += ", file size: " + downloadData.length; + showLength(downloadData.length); return downloadData; } } catch (error) { - return "Error: " + error; + info.innerHTML = "Error: " + error; + return ""; } }; From e5ea16364e965972e5f25e42bfcfbe1fd188240e Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Wed, 2 Jun 2021 20:17:45 +0200 Subject: [PATCH 5/8] Decreased SVG size by ~30% with SVG path abbreviations h/v instead of l * Simple rectangles with a fixed geometry can be expressed shorter * Removed spaces between path commands and two extra spaces from the SVG --- js/qrcode.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/js/qrcode.js b/js/qrcode.js index d143a9d..0a4c1ca 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -534,7 +534,7 @@ var qrcode = function() { qrSvg += ''; qrSvg += ''; + qrSvg += d.trim() + '" stroke="transparent" fill="' + _foreground + '"/>'; qrSvg += ''; return qrSvg; From 6e02b9899782e2ab8e6a7a4c707a9eb1e3a61c17 Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Wed, 2 Jun 2021 21:14:24 +0200 Subject: [PATCH 6/8] Code formatting (needed for more extensive optimisations), removed most ESLint warnings --- js/qrcode.js | 4432 +++++++++++++++++++++++------------------------- js/sample.html | 175 +- js/sample.js | 300 ++-- 3 files changed, 2381 insertions(+), 2526 deletions(-) diff --git a/js/qrcode.js b/js/qrcode.js index 0a4c1ca..3fe276a 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -15,2306 +15,2162 @@ // //--------------------------------------------------------------------- -var qrcode = function() { +var qrcode = function () { + + //--------------------------------------------------------------------- + // qrcode + //--------------------------------------------------------------------- + + /** + * qrcode + * @param typeNumber 1 to 40 + * @param errorCorrectionLevel 'L','M','Q','H' + */ + var qrcode = function (typeNumber, errorCorrectionLevel) { + let PAD0 = 0xEC; + let PAD1 = 0x11; + + let _typeNumber = typeNumber; + let _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel]; + let _modules = null; + let _moduleCount = 0; + let _dataCache = null; + let _dataList = []; + let _foreground = "black"; + let _background = "white"; + let _this = {}; + + var makeImpl = function (test, maskPattern) { + _moduleCount = _typeNumber * 4 + 17; + _modules = function (moduleCount) { + let modules = new Array(moduleCount); + for (let row = 0; row < moduleCount; row++) { + modules[row] = new Array(moduleCount); + for (let col = 0; col < moduleCount; col++) { + modules[row][col] = null; + } + } + return modules; + }(_moduleCount); + + setupPositionProbePattern(0, 0); + setupPositionProbePattern(_moduleCount - 7, 0); + setupPositionProbePattern(0, _moduleCount - 7); + setupPositionAdjustPattern(); + setupTimingPattern(); + setupTypeInfo(test, maskPattern); + + if (_typeNumber >= 7) { + setupTypeNumber(test); + } + if (_dataCache === null) { + _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList); + } + + mapData(_dataCache, maskPattern); + }; + + var setupPositionProbePattern = function (row, col) { + for (let r = -1; r <= 7; r++) { + if (row + r <= -1 || row + r >= _moduleCount) + continue; + for (let c = -1; c <= 7; c++) { + if (col + c <= -1 || col + c >= _moduleCount) + continue; + if ((0 <= r && r <= 6 && (c === 0 || c === 6)) || + (0 <= c && c <= 6 && (r === 0 || r === 6)) || + (2 <= r && r <= 4 && 2 <= c && c <= 4)) { + _modules[row + r][col + c] = true; + } + else { + _modules[row + r][col + c] = false; + } + } + } + }; + + var getBestMaskPattern = function () { + let minLostPoint = 0; + let pattern = 0; + + for (let i = 0; i < 8; i++) { + makeImpl(true, i); + + let lostPoint = QRUtil.getLostPoint(_this); + if (i === 0 || minLostPoint > lostPoint) { + minLostPoint = lostPoint; + pattern = i; + } + } + return pattern; + }; + + var setupTimingPattern = function () { + for (let r = 8; r < _moduleCount - 8; r++) { + if (_modules[r][6] !== null) + continue; + _modules[r][6] = (r % 2 === 0); + } + for (let c = 8; c < _moduleCount - 8; c++) { + if (_modules[6][c] !== null) + continue; + _modules[6][c] = (c % 2 === 0); + } + }; + + var setupPositionAdjustPattern = function () { + let pos = QRUtil.getPatternPosition(_typeNumber); + + for (let i = 0; i < pos.length; i++) { + for (let j = 0; j < pos.length; j++) { + let row = pos[i]; + let col = pos[j]; + + if (_modules[row][col] !== null) + continue; + + for (let r = -2; r <= 2; r++) { + for (let c = -2; c <= 2; c++) { + if (r === -2 || r === 2 || c === -2 || c === 2 || + (r === 0 && c === 0)) { + _modules[row + r][col + c] = true; + } + else { + _modules[row + r][col + c] = false; + } + } + } + } + } + }; + + var setupTypeNumber = function (test) { + let bits = QRUtil.getBCHTypeNumber(_typeNumber); + + for (let i = 0; i < 18; i++) { + let mod = (!test && ((bits >> i) & 1) === 1); + _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; + } + for (let i = 0; i < 18; i++) { + let mod = (!test && ((bits >> i) & 1) === 1); + _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; + } + }; + + var setupTypeInfo = function (test, maskPattern) { + let data = (_errorCorrectionLevel << 3) | maskPattern; + let bits = QRUtil.getBCHTypeInfo(data); + + // vertical + for (let i = 0; i < 15; i++) { + let mod = (!test && ((bits >> i) & 1) === 1); + + if (i < 6) { + _modules[i][8] = mod; + } + else if (i < 8) { + _modules[i + 1][8] = mod; + } + else { + _modules[_moduleCount - 15 + i][8] = mod; + } + } + + // horizontal + for (let i = 0; i < 15; i++) { + let mod = (!test && ((bits >> i) & 1) === 1); + + if (i < 8) { + _modules[8][_moduleCount - i - 1] = mod; + } + else if (i < 9) { + _modules[8][15 - i - 1 + 1] = mod; + } + else { + _modules[8][15 - i - 1] = mod; + } + } + + // fixed module + _modules[_moduleCount - 8][8] = (!test); + }; + + var mapData = function (data, maskPattern) { + let inc = -1; + let row = _moduleCount - 1; + let bitIndex = 7; + let byteIndex = 0; + let maskFunc = QRUtil.getMaskFunction(maskPattern); + + for (let col = _moduleCount - 1; col > 0; col -= 2) { + if (col === 6) + col -= 1; + + while (true) { + for (let c = 0; c < 2; c++) { + if (_modules[row][col - c] === null) { + let dark = false; + if (byteIndex < data.length) + dark = (((data[byteIndex] >>> bitIndex) & 1) === 1); + + let mask = maskFunc(row, col - c); + if (mask) + dark = !dark; + + _modules[row][col - c] = dark; + bitIndex -= 1; + + if (bitIndex === -1) { + byteIndex++; + bitIndex = 7; + } + } + } + + row += inc; + if (row < 0 || row >= _moduleCount) { + row -= inc; + inc = -inc; + break; + } + } + } + }; + + var createBytes = function (buffer, rsBlocks) { + let offset = 0; + let maxDcCount = 0; + let maxEcCount = 0; + let dcdata = new Array(rsBlocks.length); + let ecdata = new Array(rsBlocks.length); + + for (let r = 0; r < rsBlocks.length; r++) { + let dcCount = rsBlocks[r].dataCount; + let ecCount = rsBlocks[r].totalCount - dcCount; + + maxDcCount = Math.max(maxDcCount, dcCount); + maxEcCount = Math.max(maxEcCount, ecCount); + + dcdata[r] = new Array(dcCount); + + for (let i = 0; i < dcdata[r].length; i++) { + dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; + } + offset += dcCount; + + let rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); + let rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1); + + let modPoly = rawPoly.mod(rsPoly); + ecdata[r] = new Array(rsPoly.getLength() - 1); + for (let i = 0; i < ecdata[r].length; i++) { + let modIndex = i + modPoly.getLength() - ecdata[r].length; + ecdata[r][i] = (modIndex >= 0) ? modPoly.getAt(modIndex) : 0; + } + } + + let totalCodeCount = 0; + for (let i = 0; i < rsBlocks.length; i++) { + totalCodeCount += rsBlocks[i].totalCount; + } + + let data = new Array(totalCodeCount); + let index = 0; + + for (let i = 0; i < maxDcCount; i++) { + for (let r = 0; r < rsBlocks.length; r++) { + if (i < dcdata[r].length) { + data[index] = dcdata[r][i]; + index++; + } + } + } + for (let i = 0; i < maxEcCount; i++) { + for (let r = 0; r < rsBlocks.length; r++) { + if (i < ecdata[r].length) { + data[index] = ecdata[r][i]; + index++; + } + } + } + return data; + }; + + var createData = function (typeNumber, errorCorrectionLevel, dataList) { + let rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel); + let buffer = qrBitBuffer(); + + for (let i = 0; i < dataList.length; i++) { + let data = dataList[i]; + buffer.put(data.getMode(), 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber)); + data.write(buffer); + } + + // calc num max data. + let totalDataCount = 0; + for (let i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + if (buffer.getLengthInBits() > totalDataCount * 8) { + throw 'code length overflow. (' + buffer.getLengthInBits() + '>' + totalDataCount * 8 + ')'; + } + + // end code + if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { + buffer.put(0, 4); + } + + // padding + while (buffer.getLengthInBits() % 8 !== 0) { + buffer.putBit(false); + } + + // padding + while (true) { + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(PAD0, 8); + + if (buffer.getLengthInBits() >= totalDataCount * 8) { + break; + } + buffer.put(PAD1, 8); + } + return createBytes(buffer, rsBlocks); + }; + + _this.addData = function (data, mode) { + mode = mode || 'Byte'; + + let newData = null; + + switch (mode) { + case 'Numeric': + newData = qrNumber(data); + break; + case 'Alphanumeric': + newData = qrAlphaNum(data); + break; + case 'Byte': + newData = qr8BitByte(data); + break; + case 'Kanji': + newData = qrKanji(data); + break; + default: + throw 'mode:' + mode; + } + + _dataList.push(newData); + _dataCache = null; + }; + + _this.isDark = function (row, col) { + if (row < 0 || row >= _moduleCount || col < 0 || col >= _moduleCount) { + throw row + ',' + col; + } + return _modules[row][col]; + }; + + _this.getModuleCount = function () { + return _moduleCount; + }; + + _this.make = function () { + if (_typeNumber < 1) { + let typeNumber = 1; + + for (; typeNumber < 40; typeNumber++) { + let rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel); + let buffer = qrBitBuffer(); + + for (let i = 0; i < _dataList.length; i++) { + let data = _dataList[i]; + buffer.put(data.getMode(), 4); + buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber)); + data.write(buffer); + } + + let totalDataCount = 0; + for (let i = 0; i < rsBlocks.length; i++) { + totalDataCount += rsBlocks[i].dataCount; + } + + if (buffer.getLengthInBits() <= totalDataCount * 8) { + break; + } + } + + _typeNumber = typeNumber; + } + + makeImpl(false, getBestMaskPattern()); + }; + + _this.getTypeNumber = function () { + return _typeNumber; + }; + + _this.setColors = function (foreground, background) { + _foreground = simplifyColor(foreground); + _background = simplifyColor(background); + + function simplifyColor(c) { + if (c.match(/^#[0-9A-Fa-f]{6}$/) && c[1] === c[2] && c[3] === c[4] && c[5] === c[6]) + c = "#" + c[1] + c[3] + c[5]; + return c; + } + }; + + _this.createTableTag = function (cellSize, margin) { + + cellSize = cellSize || 2; + margin = (typeof margin === 'undefined') ? cellSize * 4 : margin; + + let qrHtml = ''; + qrHtml += '
'; + + for (let r = 0; r < _this.getModuleCount(); r++) { + qrHtml += ''; + for (let c = 0; c < _this.getModuleCount(); c++) { + qrHtml += ''; + } + qrHtml += '
'; + } + qrHtml += '
'; + return qrHtml; + }; + + _this.createSvgTag = function (cellSize, margin, alt, title) { + let opts = {}; + if (typeof arguments[0] === 'object') { + // Called by options. + opts = arguments[0]; + // overwrite cellSize and margin. + cellSize = opts.cellSize; + margin = opts.margin; + alt = opts.alt; + title = opts.title; + } + + cellSize = cellSize || 2; + margin = (typeof margin === 'undefined') ? cellSize * 4 : margin; + + // Compose alt property surrogate + alt = (typeof alt === 'string') ? { text: alt } : alt || {}; + alt.text = alt.text || null; + alt.id = (alt.text) ? alt.id || 'qrcode-description' : null; + + // Compose title property surrogate + title = (typeof title === 'string') ? { text: title } : title || {}; + title.text = title.text || null; + title.id = (title.text) ? title.id || 'qrcode-title' : null; + + let size = _this.getModuleCount() * cellSize + margin * 2; + let c, c2, mc, r, mr, qrSvg = '', rect; + + qrSvg += '' + + escapeXml(title.text) + '' : ''; + qrSvg += (alt.text) ? '' + + escapeXml(alt.text) + '' : ''; + qrSvg += ''; + qrSvg += ''; + qrSvg += ''; + + return qrSvg; + }; + + _this.createDataURL = function (cellSize, margin) { + cellSize = cellSize || 2; + margin = (typeof margin === 'undefined') ? cellSize * 4 : margin; + + let size = _this.getModuleCount() * cellSize + margin * 2; + let min = margin; + let max = size - margin; + + return createDataURL(size, size, _foreground, _background, function (x, y) { + if (min <= x && x < max && min <= y && y < max) { + let c = Math.floor((x - min) / cellSize); + let r = Math.floor((y - min) / cellSize); + return _this.isDark(r, c) ? 0 : 1; + } + else { + return 1; + } + }); + }; + + _this.createImgTag = function (cellSize, margin, alt) { + cellSize = cellSize || 2; + margin = (typeof margin === 'undefined') ? cellSize * 4 : margin; + + let size = _this.getModuleCount() * cellSize + margin * 2; + + let img = ''; + img += '': escaped += '>'; break; + case '&': escaped += '&'; break; + case '"': escaped += '"'; break; + default: escaped += c; break; + } + } + return escaped; + }; + + var _createHalfASCII = function (margin, inverted) { + let cellSize = 1; + margin = (typeof margin === 'undefined') ? cellSize * 2 : margin; + + let size = _this.getModuleCount() * cellSize + margin * 2; + let min = margin; + let max = size - margin; + let y, x, r1, r2, p; + + let blocks = { + '██': '█', + '█ ': '▀', + ' █': '▄', + ' ': '\xa0' + }; + let blocksLastLineNoMargin = { + '██': '▀', + '█ ': '▀', + ' █': '\xa0', + ' ': '\xa0' + }; + + let ascii = ''; + for (y = 0; y < size; y += 2) { + r1 = Math.floor((y - min) / cellSize); + r2 = Math.floor((y + 1 - min) / cellSize); + for (x = 0; x < size; x++) { + p = inverted ? '█' : ' '; + + if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) { + p = inverted ? ' ' : '█'; + } + + if (min <= x && x < max && min <= y + 1 && y + 1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) { + p += inverted ? ' ' : '█'; + } + else { + p += inverted ? '█' : ' '; + } + + // Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square. + ascii += (margin < 1 && y + 1 >= max) ? blocksLastLineNoMargin[p] : blocks[p]; + } + ascii += '\n'; + } + + if (size % 2 && margin > 0) { + return ascii.substring(0, ascii.length - size - 1) + Array(size + 1).join(inverted ? '▀' : '\xa0'); + } + return ascii.substring(0, ascii.length - 1); + }; + + _this.createASCII = function (cellSize, margin, inverted) { + cellSize = cellSize || 1; + if (cellSize < 2) + return _createHalfASCII(margin, inverted); + + cellSize -= 1; + margin = (typeof margin === 'undefined') ? cellSize * 2 : margin / (cellSize + 1) * cellSize; + + let size = _this.getModuleCount() * cellSize + margin * 2; + let min = margin; + let max = size - margin; + let y, x, r, p; + + let white = '\xa0\xa0'; + let black = '██'; + if (inverted) { + let b = black; + black = white; + white = b; + } + + let ascii = ''; + let line = ''; + for (y = 0; y < size; y++) { + r = Math.floor((y - min) / cellSize); + line = ''; + for (x = 0; x < size; x++) { + p = 1; + if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) { + p = 0; + } + + // Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square. + line += p ? white : black; + } + ascii += line + '\n'; + } + return ascii.substring(0, ascii.length - 1); + }; + + _this.renderTo2dContext = function (context, cellSize, margin) { + cellSize = cellSize || 2; + margin = (typeof margin === 'undefined') ? cellSize * 4 : margin; + let length = _this.getModuleCount(); + if (margin > 0) { + context.fillStyle = _background; + context.fillRect(0, 0, length * cellSize + margin * 2, length * cellSize + margin * 2); + } + for (let row = 0; row < length; row++) { + for (let col = 0; col < length; col++) { + context.fillStyle = _this.isDark(row, col) ? _foreground : _background; + context.fillRect(margin + col * cellSize, margin + row * cellSize, cellSize, cellSize); + } + } + } + + return _this; + }; + + //--------------------------------------------------------------------- + // qrcode.stringToBytes + //--------------------------------------------------------------------- + + qrcode.stringToBytesFuncs = { + 'default': function (s) { + let bytes = []; + for (let i = 0; i < s.length; i++) { + let c = s.charCodeAt(i); + bytes.push(c & 0xff); + } + return bytes; + } + }; + + qrcode.stringToBytes = qrcode.stringToBytesFuncs['default']; + + //--------------------------------------------------------------------- + // qrcode.createStringToBytes + //--------------------------------------------------------------------- + + /** + * @param unicodeData base64 string of byte array. + * [16bit Unicode],[16bit Bytes], ... + * @param numChars + */ + qrcode.createStringToBytes = function (unicodeData, numChars) { + // create conversion map. + + var unicodeMap = function () { + let bin = base64DecodeInputStream(unicodeData); + let read = function () { + let b = bin.read(); + if (b === -1) throw 'eof'; + return b; + }; + + let count = 0; + let unicodeMap = {}; + while (true) { + let b0 = bin.read(); + if (b0 === -1) break; + let b1 = read(); + let b2 = read(); + let b3 = read(); + let k = String.fromCharCode((b0 << 8) | b1); + let v = (b2 << 8) | b3; + unicodeMap[k] = v; + count++; + } + if (count !== numChars) { + throw count + ' != ' + numChars; + } + return unicodeMap; + }(); + + let unknownChar = '?'.charCodeAt(0); + + return function (s) { + let bytes = []; + for (let i = 0; i < s.length; i++) { + let c = s.charCodeAt(i); + if (c < 128) { + bytes.push(c); + } + else { + let b = unicodeMap[s.charAt(i)]; + if (typeof b === 'number') { + if ((b & 0xff) === b) { + // 1byte + bytes.push(b); + } + else { + // 2bytes + bytes.push(b >>> 8); + bytes.push(b & 0xff); + } + } + else { + bytes.push(unknownChar); + } + } + } + return bytes; + }; + }; + + //--------------------------------------------------------------------- + // QRMode + //--------------------------------------------------------------------- + + var QRMode = { + MODE_NUMBER: 1 << 0, + MODE_ALPHA_NUM: 1 << 1, + MODE_8BIT_BYTE: 1 << 2, + MODE_KANJI: 1 << 3 + }; + + //--------------------------------------------------------------------- + // QRErrorCorrectionLevel + //--------------------------------------------------------------------- + + var QRErrorCorrectionLevel = { + L: 1, + M: 0, + Q: 3, + H: 2 + }; + + //--------------------------------------------------------------------- + // QRMaskPattern + //--------------------------------------------------------------------- + + var QRMaskPattern = { + PATTERN000: 0, + PATTERN001: 1, + PATTERN010: 2, + PATTERN011: 3, + PATTERN100: 4, + PATTERN101: 5, + PATTERN110: 6, + PATTERN111: 7 + }; + + //--------------------------------------------------------------------- + // QRUtil + //--------------------------------------------------------------------- + + var QRUtil = function () { + let PATTERN_POSITION_TABLE = [ + [], + [6, 18], + [6, 22], + [6, 26], + [6, 30], + [6, 34], + [6, 22, 38], + [6, 24, 42], + [6, 26, 46], + [6, 28, 50], + [6, 30, 54], + [6, 32, 58], + [6, 34, 62], + [6, 26, 46, 66], + [6, 26, 48, 70], + [6, 26, 50, 74], + [6, 30, 54, 78], + [6, 30, 56, 82], + [6, 30, 58, 86], + [6, 34, 62, 90], + [6, 28, 50, 72, 94], + [6, 26, 50, 74, 98], + [6, 30, 54, 78, 102], + [6, 28, 54, 80, 106], + [6, 32, 58, 84, 110], + [6, 30, 58, 86, 114], + [6, 34, 62, 90, 118], + [6, 26, 50, 74, 98, 122], + [6, 30, 54, 78, 102, 126], + [6, 26, 52, 78, 104, 130], + [6, 30, 56, 82, 108, 134], + [6, 34, 60, 86, 112, 138], + [6, 30, 58, 86, 114, 142], + [6, 34, 62, 90, 118, 146], + [6, 30, 54, 78, 102, 126, 150], + [6, 24, 50, 76, 102, 128, 154], + [6, 28, 54, 80, 106, 132, 158], + [6, 32, 58, 84, 110, 136, 162], + [6, 26, 54, 82, 110, 138, 166], + [6, 30, 58, 86, 114, 142, 170] + ]; + let G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); + let G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); + let G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); + + let _this = {}; + + var getBCHDigit = function (data) { + let digit = 0; + while (data !== 0) { + digit++; + data >>>= 1; + } + return digit; + }; + + _this.getBCHTypeInfo = function (data) { + let d = data << 10; + while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { + d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15))); + } + return ((data << 10) | d) ^ G15_MASK; + }; + + _this.getBCHTypeNumber = function (data) { + let d = data << 12; + while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { + d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18))); + } + return (data << 12) | d; + }; + + _this.getPatternPosition = function (typeNumber) { + return PATTERN_POSITION_TABLE[typeNumber - 1]; + }; + + _this.getMaskFunction = function (maskPattern) { + switch (maskPattern) { + case QRMaskPattern.PATTERN000: + return function (i, j) { return (i + j) % 2 === 0; }; + case QRMaskPattern.PATTERN001: + return function (i, j) { return i % 2 === 0; }; + case QRMaskPattern.PATTERN010: + return function (i, j) { return j % 3 === 0; }; + case QRMaskPattern.PATTERN011: + return function (i, j) { return (i + j) % 3 === 0; }; + case QRMaskPattern.PATTERN100: + return function (i, j) { return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 === 0; }; + case QRMaskPattern.PATTERN101: + return function (i, j) { return (i * j) % 2 + (i * j) % 3 === 0; }; + case QRMaskPattern.PATTERN110: + return function (i, j) { return ((i * j) % 2 + (i * j) % 3) % 2 === 0; }; + case QRMaskPattern.PATTERN111: + return function (i, j) { return ((i * j) % 3 + (i + j) % 2) % 2 === 0; }; + default: + throw 'bad maskPattern:' + maskPattern; + } + }; + + _this.getErrorCorrectPolynomial = function (errorCorrectLength) { + let a = qrPolynomial([1], 0); + for (let i = 0; i < errorCorrectLength; i++) { + a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0)); + } + return a; + }; + + _this.getLengthInBits = function (mode, type) { + if (1 <= type && type < 10) { + // 1 - 9 + switch (mode) { + case QRMode.MODE_NUMBER: return 10; + case QRMode.MODE_ALPHA_NUM: return 9; + case QRMode.MODE_8BIT_BYTE: return 8; + case QRMode.MODE_KANJI: return 8; + default: + throw 'mode:' + mode; + } + } + else if (type < 27) { + // 10 - 26 + switch (mode) { + case QRMode.MODE_NUMBER: return 12; + case QRMode.MODE_ALPHA_NUM: return 11; + case QRMode.MODE_8BIT_BYTE: return 16; + case QRMode.MODE_KANJI: return 10; + default: + throw 'mode:' + mode; + } + } + else if (type < 41) { + // 27 - 40 + switch (mode) { + case QRMode.MODE_NUMBER: return 14; + case QRMode.MODE_ALPHA_NUM: return 13; + case QRMode.MODE_8BIT_BYTE: return 16; + case QRMode.MODE_KANJI: return 12; + default: + throw 'mode:' + mode; + } + } + else { + throw 'type:' + type; + } + }; + + _this.getLostPoint = function (qrcode) { + let moduleCount = qrcode.getModuleCount(); + let lostPoint = 0; + + // LEVEL1 + + for (let row = 0; row < moduleCount; row++) { + for (let col = 0; col < moduleCount; col++) { + let sameCount = 0; + let dark = qrcode.isDark(row, col); + + for (let r = -1; r <= 1; r++) { + if (row + r < 0 || moduleCount <= row + r) + continue; + + for (let c = -1; c <= 1; c++) { + if (col + c < 0 || moduleCount <= col + c) + continue; + if (r === 0 && c === 0) + continue; + + if (dark === qrcode.isDark(row + r, col + c)) + sameCount++; + } + } + + if (sameCount > 5) { + lostPoint += (3 + sameCount - 5); + } + } + }; + + // LEVEL2 + + for (let row = 0; row < moduleCount - 1; row++) { + for (let col = 0; col < moduleCount - 1; col++) { + let count = 0; + if (qrcode.isDark(row, col)) count++; + if (qrcode.isDark(row + 1, col)) count++; + if (qrcode.isDark(row, col + 1)) count++; + if (qrcode.isDark(row + 1, col + 1)) count++; + if (count === 0 || count === 4) { + lostPoint += 3; + } + } + } + + // LEVEL3 + + for (let row = 0; row < moduleCount; row++) { + for (let col = 0; col < moduleCount - 6; col++) { + if (qrcode.isDark(row, col) && + !qrcode.isDark(row, col + 1) && + qrcode.isDark(row, col + 2) && + qrcode.isDark(row, col + 3) && + qrcode.isDark(row, col + 4) && + !qrcode.isDark(row, col + 5) && + qrcode.isDark(row, col + 6)) { + lostPoint += 40; + } + } + } + + for (let col = 0; col < moduleCount; col++) { + for (let row = 0; row < moduleCount - 6; row++) { + if (qrcode.isDark(row, col) && + !qrcode.isDark(row + 1, col) && + qrcode.isDark(row + 2, col) && + qrcode.isDark(row + 3, col) && + qrcode.isDark(row + 4, col) && + !qrcode.isDark(row + 5, col) && + qrcode.isDark(row + 6, col)) { + lostPoint += 40; + } + } + } + + // LEVEL4 + + let darkCount = 0; + + for (let col = 0; col < moduleCount; col++) { + for (let row = 0; row < moduleCount; row++) { + if (qrcode.isDark(row, col)) { + darkCount++; + } + } + } + + let ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; + lostPoint += ratio * 10; + return lostPoint; + }; + + return _this; + }(); + + //--------------------------------------------------------------------- + // QRMath + //--------------------------------------------------------------------- + + var QRMath = function () { + let EXP_TABLE = new Array(256); + let LOG_TABLE = new Array(256); + + // initialize tables + for (let i = 0; i < 8; i++) { + EXP_TABLE[i] = 1 << i; + } + for (let i = 8; i < 256; i++) { + EXP_TABLE[i] = EXP_TABLE[i - 4] ^ + EXP_TABLE[i - 5] ^ + EXP_TABLE[i - 6] ^ + EXP_TABLE[i - 8]; + } + for (let i = 0; i < 255; i++) { + LOG_TABLE[EXP_TABLE[i]] = i; + } + + let _this = {}; + + _this.glog = function (n) { + if (n < 1) + throw 'glog(' + n + ')'; + + return LOG_TABLE[n]; + }; + + _this.gexp = function (n) { + while (n < 0) { + n += 255; + } + while (n >= 256) { + n -= 255; + } + return EXP_TABLE[n]; + }; + + return _this; + }(); + + //--------------------------------------------------------------------- + // qrPolynomial + //--------------------------------------------------------------------- + + function qrPolynomial(num, shift) { + if (typeof num.length === 'undefined') + throw num.length + '/' + shift; + + let _num = function () { + let offset = 0; + while (offset < num.length && num[offset] === 0) { + offset++; + } + let _num = new Array(num.length - offset + shift); + for (let i = 0; i < num.length - offset; i++) { + _num[i] = num[i + offset]; + } + return _num; + }(); + + let _this = {}; + + _this.getAt = function (index) { + return _num[index]; + }; + + _this.getLength = function () { + return _num.length; + }; + + _this.multiply = function (e) { + let num = new Array(_this.getLength() + e.getLength() - 1); + for (let i = 0; i < _this.getLength(); i++) { + for (let j = 0; j < e.getLength(); j++) { + num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i)) + QRMath.glog(e.getAt(j))); + } + } + return qrPolynomial(num, 0); + }; + + _this.mod = function (e) { + if (_this.getLength() - e.getLength() < 0) + return _this; + + let ratio = QRMath.glog(_this.getAt(0)) - QRMath.glog(e.getAt(0)); + let num = new Array(_this.getLength()); + for (let i = 0; i < _this.getLength(); i++) { + num[i] = _this.getAt(i); + } + for (let i = 0; i < e.getLength(); i++) { + num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i)) + ratio); + } + + // recursive call + return qrPolynomial(num, 0).mod(e); + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // QRRSBlock + //--------------------------------------------------------------------- + + var QRRSBlock = function () { + let RS_BLOCK_TABLE = [ + // L + // M + // Q + // H + + // 1 + [1, 26, 19], + [1, 26, 16], + [1, 26, 13], + [1, 26, 9], + + // 2 + [1, 44, 34], + [1, 44, 28], + [1, 44, 22], + [1, 44, 16], + + // 3 + [1, 70, 55], + [1, 70, 44], + [2, 35, 17], + [2, 35, 13], + + // 4 + [1, 100, 80], + [2, 50, 32], + [2, 50, 24], + [4, 25, 9], + + // 5 + [1, 134, 108], + [2, 67, 43], + [2, 33, 15, 2, 34, 16], + [2, 33, 11, 2, 34, 12], + + // 6 + [2, 86, 68], + [4, 43, 27], + [4, 43, 19], + [4, 43, 15], + + // 7 + [2, 98, 78], + [4, 49, 31], + [2, 32, 14, 4, 33, 15], + [4, 39, 13, 1, 40, 14], + + // 8 + [2, 121, 97], + [2, 60, 38, 2, 61, 39], + [4, 40, 18, 2, 41, 19], + [4, 40, 14, 2, 41, 15], + + // 9 + [2, 146, 116], + [3, 58, 36, 2, 59, 37], + [4, 36, 16, 4, 37, 17], + [4, 36, 12, 4, 37, 13], + + // 10 + [2, 86, 68, 2, 87, 69], + [4, 69, 43, 1, 70, 44], + [6, 43, 19, 2, 44, 20], + [6, 43, 15, 2, 44, 16], + + // 11 + [4, 101, 81], + [1, 80, 50, 4, 81, 51], + [4, 50, 22, 4, 51, 23], + [3, 36, 12, 8, 37, 13], + + // 12 + [2, 116, 92, 2, 117, 93], + [6, 58, 36, 2, 59, 37], + [4, 46, 20, 6, 47, 21], + [7, 42, 14, 4, 43, 15], + + // 13 + [4, 133, 107], + [8, 59, 37, 1, 60, 38], + [8, 44, 20, 4, 45, 21], + [12, 33, 11, 4, 34, 12], + + // 14 + [3, 145, 115, 1, 146, 116], + [4, 64, 40, 5, 65, 41], + [11, 36, 16, 5, 37, 17], + [11, 36, 12, 5, 37, 13], + + // 15 + [5, 109, 87, 1, 110, 88], + [5, 65, 41, 5, 66, 42], + [5, 54, 24, 7, 55, 25], + [11, 36, 12, 7, 37, 13], + + // 16 + [5, 122, 98, 1, 123, 99], + [7, 73, 45, 3, 74, 46], + [15, 43, 19, 2, 44, 20], + [3, 45, 15, 13, 46, 16], + + // 17 + [1, 135, 107, 5, 136, 108], + [10, 74, 46, 1, 75, 47], + [1, 50, 22, 15, 51, 23], + [2, 42, 14, 17, 43, 15], + + // 18 + [5, 150, 120, 1, 151, 121], + [9, 69, 43, 4, 70, 44], + [17, 50, 22, 1, 51, 23], + [2, 42, 14, 19, 43, 15], + + // 19 + [3, 141, 113, 4, 142, 114], + [3, 70, 44, 11, 71, 45], + [17, 47, 21, 4, 48, 22], + [9, 39, 13, 16, 40, 14], + + // 20 + [3, 135, 107, 5, 136, 108], + [3, 67, 41, 13, 68, 42], + [15, 54, 24, 5, 55, 25], + [15, 43, 15, 10, 44, 16], + + // 21 + [4, 144, 116, 4, 145, 117], + [17, 68, 42], + [17, 50, 22, 6, 51, 23], + [19, 46, 16, 6, 47, 17], + + // 22 + [2, 139, 111, 7, 140, 112], + [17, 74, 46], + [7, 54, 24, 16, 55, 25], + [34, 37, 13], + + // 23 + [4, 151, 121, 5, 152, 122], + [4, 75, 47, 14, 76, 48], + [11, 54, 24, 14, 55, 25], + [16, 45, 15, 14, 46, 16], + + // 24 + [6, 147, 117, 4, 148, 118], + [6, 73, 45, 14, 74, 46], + [11, 54, 24, 16, 55, 25], + [30, 46, 16, 2, 47, 17], + + // 25 + [8, 132, 106, 4, 133, 107], + [8, 75, 47, 13, 76, 48], + [7, 54, 24, 22, 55, 25], + [22, 45, 15, 13, 46, 16], + + // 26 + [10, 142, 114, 2, 143, 115], + [19, 74, 46, 4, 75, 47], + [28, 50, 22, 6, 51, 23], + [33, 46, 16, 4, 47, 17], + + // 27 + [8, 152, 122, 4, 153, 123], + [22, 73, 45, 3, 74, 46], + [8, 53, 23, 26, 54, 24], + [12, 45, 15, 28, 46, 16], + + // 28 + [3, 147, 117, 10, 148, 118], + [3, 73, 45, 23, 74, 46], + [4, 54, 24, 31, 55, 25], + [11, 45, 15, 31, 46, 16], + + // 29 + [7, 146, 116, 7, 147, 117], + [21, 73, 45, 7, 74, 46], + [1, 53, 23, 37, 54, 24], + [19, 45, 15, 26, 46, 16], + + // 30 + [5, 145, 115, 10, 146, 116], + [19, 75, 47, 10, 76, 48], + [15, 54, 24, 25, 55, 25], + [23, 45, 15, 25, 46, 16], + + // 31 + [13, 145, 115, 3, 146, 116], + [2, 74, 46, 29, 75, 47], + [42, 54, 24, 1, 55, 25], + [23, 45, 15, 28, 46, 16], + + // 32 + [17, 145, 115], + [10, 74, 46, 23, 75, 47], + [10, 54, 24, 35, 55, 25], + [19, 45, 15, 35, 46, 16], + + // 33 + [17, 145, 115, 1, 146, 116], + [14, 74, 46, 21, 75, 47], + [29, 54, 24, 19, 55, 25], + [11, 45, 15, 46, 46, 16], + + // 34 + [13, 145, 115, 6, 146, 116], + [14, 74, 46, 23, 75, 47], + [44, 54, 24, 7, 55, 25], + [59, 46, 16, 1, 47, 17], + + // 35 + [12, 151, 121, 7, 152, 122], + [12, 75, 47, 26, 76, 48], + [39, 54, 24, 14, 55, 25], + [22, 45, 15, 41, 46, 16], + + // 36 + [6, 151, 121, 14, 152, 122], + [6, 75, 47, 34, 76, 48], + [46, 54, 24, 10, 55, 25], + [2, 45, 15, 64, 46, 16], + + // 37 + [17, 152, 122, 4, 153, 123], + [29, 74, 46, 14, 75, 47], + [49, 54, 24, 10, 55, 25], + [24, 45, 15, 46, 46, 16], + + // 38 + [4, 152, 122, 18, 153, 123], + [13, 74, 46, 32, 75, 47], + [48, 54, 24, 14, 55, 25], + [42, 45, 15, 32, 46, 16], + + // 39 + [20, 147, 117, 4, 148, 118], + [40, 75, 47, 7, 76, 48], + [43, 54, 24, 22, 55, 25], + [10, 45, 15, 67, 46, 16], + + // 40 + [19, 148, 118, 6, 149, 119], + [18, 75, 47, 31, 76, 48], + [34, 54, 24, 34, 55, 25], + [20, 45, 15, 61, 46, 16] + ]; + + var qrRSBlock = function (totalCount, dataCount) { + let _this = {}; + _this.totalCount = totalCount; + _this.dataCount = dataCount; + return _this; + }; + + let _this = {}; + + var getRsBlockTable = function (typeNumber, errorCorrectionLevel) { + switch (errorCorrectionLevel) { + case QRErrorCorrectionLevel.L: + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; + case QRErrorCorrectionLevel.M: + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; + case QRErrorCorrectionLevel.Q: + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; + case QRErrorCorrectionLevel.H: + return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; + default: + return undefined; + } + }; + + _this.getRSBlocks = function (typeNumber, errorCorrectionLevel) { + let rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel); + if (typeof rsBlock === 'undefined') + throw 'bad rs block @ typeNumber:' + typeNumber + '/errorCorrectionLevel:' + errorCorrectionLevel; + + let length = rsBlock.length / 3; + let list = []; + + for (let i = 0; i < length; i++) { + let count = rsBlock[i * 3 + 0]; + let totalCount = rsBlock[i * 3 + 1]; + let dataCount = rsBlock[i * 3 + 2]; + + for (let j = 0; j < count; j++) { + list.push(qrRSBlock(totalCount, dataCount)); + } + } + return list; + }; + + return _this; + }(); + + //--------------------------------------------------------------------- + // qrBitBuffer + //--------------------------------------------------------------------- + + var qrBitBuffer = function () { + let _buffer = []; + let _length = 0; + let _this = {}; + + _this.getBuffer = function () { + return _buffer; + }; + + _this.getAt = function (index) { + let bufIndex = Math.floor(index / 8); + return ((_buffer[bufIndex] >>> (7 - index % 8)) & 1) === 1; + }; + + _this.put = function (num, length) { + for (let i = 0; i < length; i++) { + _this.putBit(((num >>> (length - i - 1)) & 1) === 1); + } + }; + + _this.getLengthInBits = function () { + return _length; + }; + + _this.putBit = function (bit) { + let bufIndex = Math.floor(_length / 8); + if (_buffer.length <= bufIndex) { + _buffer.push(0); + } + if (bit) { + _buffer[bufIndex] |= (0x80 >>> (_length % 8)); + } + _length++; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qrNumber + //--------------------------------------------------------------------- + + var qrNumber = function (data) { + let _mode = QRMode.MODE_NUMBER; + let _data = data; + let _this = {}; + + _this.getMode = function () { + return _mode; + }; + + _this.getLength = function (buffer) { + return _data.length; + }; + + _this.write = function (buffer) { + let data = _data; + let i = 0; + + while (i + 2 < data.length) { + buffer.put(strToNum(data.substring(i, i + 3)), 10); + i += 3; + } + + if (i < data.length) { + if (data.length - i === 1) { + buffer.put(strToNum(data.substring(i, i + 1)), 4); + } + else if (data.length - i === 2) { + buffer.put(strToNum(data.substring(i, i + 2)), 7); + } + } + }; + + var strToNum = function (s) { + let num = 0; + for (let i = 0; i < s.length; i++) { + num = num * 10 + charToNum(s.charAt(i)); + } + return num; + }; + + var charToNum = function (c) { + if ('0' <= c && c <= '9') { + return c.charCodeAt(0) - '0'.charCodeAt(0); + } + throw 'illegal char :' + c; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qrAlphaNum + //--------------------------------------------------------------------- + + var qrAlphaNum = function (data) { + let _mode = QRMode.MODE_ALPHA_NUM; + let _data = data; + let _this = {}; + + _this.getMode = function () { + return _mode; + }; + + _this.getLength = function (buffer) { + return _data.length; + }; + + _this.write = function (buffer) { + let s = _data; + let i = 0; + + while (i + 1 < s.length) { + buffer.put( + getCode(s.charAt(i)) * 45 + + getCode(s.charAt(i + 1)), 11); + i += 2; + } + + if (i < s.length) { + buffer.put(getCode(s.charAt(i)), 6); + } + }; + + var getCode = function (c) { + if ('0' <= c && c <= '9') { + return c.charCodeAt(0) - '0'.charCodeAt(0); + } + else if ('A' <= c && c <= 'Z') { + return c.charCodeAt(0) - 'A'.charCodeAt(0) + 10; + } + else { + switch (c) { + case ' ': return 36; + case '$': return 37; + case '%': return 38; + case '*': return 39; + case '+': return 40; + case '-': return 41; + case '.': return 42; + case '/': return 43; + case ':': return 44; + default: + throw 'illegal char :' + c; + } + } + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qr8BitByte + //--------------------------------------------------------------------- + + var qr8BitByte = function (data) { + let _mode = QRMode.MODE_8BIT_BYTE; + let _data = data; + let _bytes = qrcode.stringToBytes(data); + let _this = {}; + + _this.getMode = function () { + return _mode; + }; + + _this.getLength = function (buffer) { + return _bytes.length; + }; + + _this.write = function (buffer) { + for (let i = 0; i < _bytes.length; i++) { + buffer.put(_bytes[i], 8); + } + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // qrKanji + //--------------------------------------------------------------------- + + var qrKanji = function (data) { + let _mode = QRMode.MODE_KANJI; + let _data = data; + + let stringToBytes = qrcode.stringToBytesFuncs['SJIS']; + if (!stringToBytes) + throw 'sjis not supported.'; + + !function (c, code) { + // self test for sjis support. + let test = stringToBytes(c); + if (test.length !== 2 || ((test[0] << 8) | test[1]) !== code) { + throw 'sjis not supported.'; + } + }('\u53cb', 0x9746); + + let _bytes = stringToBytes(data); + + let _this = {}; + + _this.getMode = function () { + return _mode; + }; + + _this.getLength = function (buffer) { + return ~~(_bytes.length / 2); + }; + + _this.write = function (buffer) { + let data = _bytes; + let i = 0; + + while (i + 1 < data.length) { + let c = ((0xff & data[i]) << 8) | (0xff & data[i + 1]); + + if (0x8140 <= c && c <= 0x9FFC) { + c -= 0x8140; + } + else if (0xE040 <= c && c <= 0xEBBF) { + c -= 0xC140; + } + else { + throw 'illegal char at ' + (i + 1) + '/' + c; + } + + c = ((c >>> 8) & 0xff) * 0xC0 + (c & 0xff); + buffer.put(c, 13); + i += 2; + } + + if (i < data.length) { + throw 'illegal char at ' + (i + 1); + } + }; + + return _this; + }; + + //===================================================================== + // GIF Support etc. + // + + //--------------------------------------------------------------------- + // byteArrayOutputStream + //--------------------------------------------------------------------- + + var byteArrayOutputStream = function () { + let _bytes = []; + let _this = {}; + + _this.writeByte = function (b) { + _bytes.push(b & 0xff); + }; + + _this.writeShort = function (i) { + _this.writeByte(i); + _this.writeByte(i >>> 8); + }; + + _this.writeBytes = function (b, off, len) { + off = off || 0; + len = len || b.length; + for (let i = 0; i < len; i++) { + _this.writeByte(b[i + off]); + } + }; + + _this.writeString = function (s) { + for (let i = 0; i < s.length; i++) { + _this.writeByte(s.charCodeAt(i)); + } + }; + + _this.toByteArray = function () { + return _bytes; + }; + + _this.toString = function () { + let s = ''; + s += '['; + for (let i = 0; i < _bytes.length; i++) { + if (i > 0) + s += ','; + s += _bytes[i]; + } + s += ']'; + return s; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // base64EncodeOutputStream + //--------------------------------------------------------------------- + + var base64EncodeOutputStream = function () { + let _buffer = 0; + let _buflen = 0; + let _length = 0; + let _base64 = ''; + let _this = {}; + + var writeEncoded = function (b) { + _base64 += String.fromCharCode(encode(b & 0x3f)); + }; + + var encode = function (n) { + if (n < 0) { + // error. + } + else if (n < 26) { + return 0x41 + n; + } + else if (n < 52) { + return 0x61 + (n - 26); + } + else if (n < 62) { + return 0x30 + (n - 52); + } + else if (n === 62) { + return 0x2b; + } + else if (n === 63) { + return 0x2f; + } + throw 'n:' + n; + }; + + _this.writeByte = function (n) { + _buffer = (_buffer << 8) | (n & 0xff); + _buflen += 8; + _length++; + + while (_buflen >= 6) { + writeEncoded(_buffer >>> (_buflen - 6)); + _buflen -= 6; + } + }; + + _this.flush = function () { + if (_buflen > 0) { + writeEncoded(_buffer << (6 - _buflen)); + _buffer = 0; + _buflen = 0; + } + + if (_length % 3 !== 0) { + // padding + let padlen = 3 - _length % 3; + for (let i = 0; i < padlen; i++) { + _base64 += '='; + } + } + }; + + _this.toString = function () { + return _base64; + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // base64DecodeInputStream + //--------------------------------------------------------------------- + + var base64DecodeInputStream = function (str) { + let _str = str; + let _pos = 0; + let _buffer = 0; + let _buflen = 0; + let _this = {}; + + _this.read = function () { + while (_buflen < 8) { + if (_pos >= _str.length) { + if (_buflen === 0) + return -1; + throw 'unexpected end of file./' + _buflen; + } + + let c = _str.charAt(_pos); + _pos++; + + if (c === '=') { + _buflen = 0; + return -1; + } + else if (c.match(/^\s$/)) { + // ignore if whitespace. + continue; + } + + _buffer = (_buffer << 6) | decode(c.charCodeAt(0)); + _buflen += 6; + } + + let n = (_buffer >>> (_buflen - 8)) & 0xff; + _buflen -= 8; + return n; + }; + + var decode = function (c) { + if (0x41 <= c && c <= 0x5a) { + return c - 0x41; + } + else if (0x61 <= c && c <= 0x7a) { + return c - 0x61 + 26; + } + else if (0x30 <= c && c <= 0x39) { + return c - 0x30 + 52; + } + else if (c === 0x2b) { + return 62; + } + else if (c === 0x2f) { + return 63; + } + else { + throw 'c:' + c; + } + }; + + return _this; + }; + + //--------------------------------------------------------------------- + // gifImage (B/W) + //--------------------------------------------------------------------- + + var gifImage = function (width, height, foreground, background) { + let _width = width; + let _height = height; + let _data = new Array(width * height); + let _this = {}; + + _this.setPixel = function (x, y, pixel) { + _data[y * _width + x] = pixel; + }; + + _this.write = function (out) { + //--------------------------------- + // GIF Signature + + out.writeString('GIF87a'); + + //--------------------------------- + // Screen Descriptor + + out.writeShort(_width); + out.writeShort(_height); + + out.writeByte(0x80); // 2bit + out.writeByte(0); + out.writeByte(0); + + //--------------------------------- + // Global Color Map + + // foreground + let fg = parseColor(foreground, "#000"); + out.writeByte(fg[0]); + out.writeByte(fg[1]); + out.writeByte(fg[2]); + + // background + let bg = parseColor(background, "#fff"); + out.writeByte(bg[0]); + out.writeByte(bg[1]); + out.writeByte(bg[2]); + + //--------------------------------- + // Image Descriptor + + out.writeString(','); + out.writeShort(0); + out.writeShort(0); + out.writeShort(_width); + out.writeShort(_height); + out.writeByte(0); + + //--------------------------------- + // Local Color Map + + //--------------------------------- + // Raster Data + + let lzwMinCodeSize = 2; + let raster = getLZWRaster(lzwMinCodeSize); + + out.writeByte(lzwMinCodeSize); + + let offset = 0; + + while (raster.length - offset > 255) { + out.writeByte(255); + out.writeBytes(raster, offset, 255); + offset += 255; + } + + out.writeByte(raster.length - offset); + out.writeBytes(raster, offset, raster.length - offset); + out.writeByte(0x00); + + //--------------------------------- + // GIF Terminator + out.writeString(';'); + }; + + function parseColor(c, fallback) { + c = c.toLowerCase(); + if (c.match(/^#[0-9a-f]{3}$/)) + return [parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16)]; + if (c.match(/^#[0-9a-f]{6}$/)) + return [parseInt(c[1] + c[2], 16), parseInt(c[3] + c[4], 16), parseInt(c[5] + c[6], 16)]; + return parseColor(fallback); + } + + var bitOutputStream = function (out) { + let _out = out; + let _bitLength = 0; + let _bitBuffer = 0; + let _this = {}; + + _this.write = function (data, length) { + if ((data >>> length) !== 0) + throw 'length over'; + + while (_bitLength + length >= 8) { + _out.writeByte(0xff & ((data << _bitLength) | _bitBuffer)); + length -= (8 - _bitLength); + data >>>= (8 - _bitLength); + _bitBuffer = 0; + _bitLength = 0; + } + + _bitBuffer = (data << _bitLength) | _bitBuffer; + _bitLength = _bitLength + length; + }; + + _this.flush = function () { + if (_bitLength > 0) { + _out.writeByte(_bitBuffer); + } + }; + + return _this; + }; + + var getLZWRaster = function (lzwMinCodeSize) { + let clearCode = 1 << lzwMinCodeSize; + let endCode = (1 << lzwMinCodeSize) + 1; + let bitLength = lzwMinCodeSize + 1; + + // Setup LZWTable + let table = lzwTable(); + + for (let i = 0; i < clearCode; i++) { + table.add(String.fromCharCode(i)); + } + table.add(String.fromCharCode(clearCode)); + table.add(String.fromCharCode(endCode)); + + let byteOut = byteArrayOutputStream(); + let bitOut = bitOutputStream(byteOut); + + // clear code + bitOut.write(clearCode, bitLength); + + let dataIndex = 0; + + let s = String.fromCharCode(_data[dataIndex]); + dataIndex++; + + while (dataIndex < _data.length) { + let c = String.fromCharCode(_data[dataIndex]); + dataIndex++; + + if (table.contains(s + c)) { + s = s + c; + } + else { + bitOut.write(table.indexOf(s), bitLength); + + if (table.size() < 0xfff) { + if (table.size() === (1 << bitLength)) { + bitLength++; + } + table.add(s + c); + } + s = c; + } + } + + bitOut.write(table.indexOf(s), bitLength); + + // end code + bitOut.write(endCode, bitLength); + + bitOut.flush(); + + return byteOut.toByteArray(); + }; + + var lzwTable = function () { + let _map = {}; + let _size = 0; + let _this = {}; + + _this.add = function (key) { + if (_this.contains(key)) + throw 'dup key:' + key; + + _map[key] = _size; + _size++; + }; + + _this.size = function () { + return _size; + }; + + _this.indexOf = function (key) { + return _map[key]; + }; + + _this.contains = function (key) { + return typeof _map[key] !== 'undefined'; + }; + + return _this; + }; + + return _this; + }; - //--------------------------------------------------------------------- - // qrcode - //--------------------------------------------------------------------- + var createDataURL = function (width, height, foreground, background, getPixel) { + let gif = gifImage(width, height, foreground, background); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + gif.setPixel(x, y, getPixel(x, y)); + } + } - /** - * qrcode - * @param typeNumber 1 to 40 - * @param errorCorrectionLevel 'L','M','Q','H' - */ - var qrcode = function(typeNumber, errorCorrectionLevel) { - - var PAD0 = 0xEC; - var PAD1 = 0x11; - - var _typeNumber = typeNumber; - var _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel]; - var _modules = null; - var _moduleCount = 0; - var _dataCache = null; - var _dataList = []; - var _foreground = "black"; - var _background = "white"; + let b = byteArrayOutputStream(); + gif.write(b); - var _this = {}; + let base64 = base64EncodeOutputStream(); + let bytes = b.toByteArray(); + for (let i = 0; i < bytes.length; i++) { + base64.writeByte(bytes[i]); + } + base64.flush(); - var makeImpl = function(test, maskPattern) { + return 'data:image/gif;base64,' + base64; + }; - _moduleCount = _typeNumber * 4 + 17; - _modules = function(moduleCount) { - var modules = new Array(moduleCount); - for (var row = 0; row < moduleCount; row += 1) { - modules[row] = new Array(moduleCount); - for (var col = 0; col < moduleCount; col += 1) { - modules[row][col] = null; - } - } - return modules; - }(_moduleCount); + //--------------------------------------------------------------------- + // returns qrcode function. - setupPositionProbePattern(0, 0); - setupPositionProbePattern(_moduleCount - 7, 0); - setupPositionProbePattern(0, _moduleCount - 7); - setupPositionAdjustPattern(); - setupTimingPattern(); - setupTypeInfo(test, maskPattern); - - if (_typeNumber >= 7) { - setupTypeNumber(test); - } - - if (_dataCache == null) { - _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList); - } - - mapData(_dataCache, maskPattern); - }; - - var setupPositionProbePattern = function(row, col) { - - for (var r = -1; r <= 7; r += 1) { - - if (row + r <= -1 || _moduleCount <= row + r) continue; - - for (var c = -1; c <= 7; c += 1) { - - if (col + c <= -1 || _moduleCount <= col + c) continue; - - if ( (0 <= r && r <= 6 && (c == 0 || c == 6) ) - || (0 <= c && c <= 6 && (r == 0 || r == 6) ) - || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { - _modules[row + r][col + c] = true; - } else { - _modules[row + r][col + c] = false; - } - } - } - }; - - var getBestMaskPattern = function() { - - var minLostPoint = 0; - var pattern = 0; - - for (var i = 0; i < 8; i += 1) { - - makeImpl(true, i); - - var lostPoint = QRUtil.getLostPoint(_this); - - if (i == 0 || minLostPoint > lostPoint) { - minLostPoint = lostPoint; - pattern = i; - } - } - - return pattern; - }; - - var setupTimingPattern = function() { - - for (var r = 8; r < _moduleCount - 8; r += 1) { - if (_modules[r][6] != null) { - continue; - } - _modules[r][6] = (r % 2 == 0); - } - - for (var c = 8; c < _moduleCount - 8; c += 1) { - if (_modules[6][c] != null) { - continue; - } - _modules[6][c] = (c % 2 == 0); - } - }; - - var setupPositionAdjustPattern = function() { - - var pos = QRUtil.getPatternPosition(_typeNumber); - - for (var i = 0; i < pos.length; i += 1) { - - for (var j = 0; j < pos.length; j += 1) { - - var row = pos[i]; - var col = pos[j]; - - if (_modules[row][col] != null) { - continue; - } - - for (var r = -2; r <= 2; r += 1) { - - for (var c = -2; c <= 2; c += 1) { - - if (r == -2 || r == 2 || c == -2 || c == 2 - || (r == 0 && c == 0) ) { - _modules[row + r][col + c] = true; - } else { - _modules[row + r][col + c] = false; - } - } - } - } - } - }; - - var setupTypeNumber = function(test) { - - var bits = QRUtil.getBCHTypeNumber(_typeNumber); - - for (var i = 0; i < 18; i += 1) { - var mod = (!test && ( (bits >> i) & 1) == 1); - _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; - } - - for (var i = 0; i < 18; i += 1) { - var mod = (!test && ( (bits >> i) & 1) == 1); - _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; - } - }; - - var setupTypeInfo = function(test, maskPattern) { - - var data = (_errorCorrectionLevel << 3) | maskPattern; - var bits = QRUtil.getBCHTypeInfo(data); - - // vertical - for (var i = 0; i < 15; i += 1) { - - var mod = (!test && ( (bits >> i) & 1) == 1); - - if (i < 6) { - _modules[i][8] = mod; - } else if (i < 8) { - _modules[i + 1][8] = mod; - } else { - _modules[_moduleCount - 15 + i][8] = mod; - } - } - - // horizontal - for (var i = 0; i < 15; i += 1) { - - var mod = (!test && ( (bits >> i) & 1) == 1); - - if (i < 8) { - _modules[8][_moduleCount - i - 1] = mod; - } else if (i < 9) { - _modules[8][15 - i - 1 + 1] = mod; - } else { - _modules[8][15 - i - 1] = mod; - } - } - - // fixed module - _modules[_moduleCount - 8][8] = (!test); - }; - - var mapData = function(data, maskPattern) { - - var inc = -1; - var row = _moduleCount - 1; - var bitIndex = 7; - var byteIndex = 0; - var maskFunc = QRUtil.getMaskFunction(maskPattern); - - for (var col = _moduleCount - 1; col > 0; col -= 2) { - - if (col == 6) col -= 1; - - while (true) { - - for (var c = 0; c < 2; c += 1) { - - if (_modules[row][col - c] == null) { - - var dark = false; - - if (byteIndex < data.length) { - dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1); - } - - var mask = maskFunc(row, col - c); - - if (mask) { - dark = !dark; - } - - _modules[row][col - c] = dark; - bitIndex -= 1; - - if (bitIndex == -1) { - byteIndex += 1; - bitIndex = 7; - } - } - } - - row += inc; - - if (row < 0 || _moduleCount <= row) { - row -= inc; - inc = -inc; - break; - } - } - } - }; - - var createBytes = function(buffer, rsBlocks) { - - var offset = 0; - - var maxDcCount = 0; - var maxEcCount = 0; - - var dcdata = new Array(rsBlocks.length); - var ecdata = new Array(rsBlocks.length); - - for (var r = 0; r < rsBlocks.length; r += 1) { - - var dcCount = rsBlocks[r].dataCount; - var ecCount = rsBlocks[r].totalCount - dcCount; - - maxDcCount = Math.max(maxDcCount, dcCount); - maxEcCount = Math.max(maxEcCount, ecCount); - - dcdata[r] = new Array(dcCount); - - for (var i = 0; i < dcdata[r].length; i += 1) { - dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; - } - offset += dcCount; - - var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); - var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1); - - var modPoly = rawPoly.mod(rsPoly); - ecdata[r] = new Array(rsPoly.getLength() - 1); - for (var i = 0; i < ecdata[r].length; i += 1) { - var modIndex = i + modPoly.getLength() - ecdata[r].length; - ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0; - } - } - - var totalCodeCount = 0; - for (var i = 0; i < rsBlocks.length; i += 1) { - totalCodeCount += rsBlocks[i].totalCount; - } - - var data = new Array(totalCodeCount); - var index = 0; - - for (var i = 0; i < maxDcCount; i += 1) { - for (var r = 0; r < rsBlocks.length; r += 1) { - if (i < dcdata[r].length) { - data[index] = dcdata[r][i]; - index += 1; - } - } - } - - for (var i = 0; i < maxEcCount; i += 1) { - for (var r = 0; r < rsBlocks.length; r += 1) { - if (i < ecdata[r].length) { - data[index] = ecdata[r][i]; - index += 1; - } - } - } - - return data; - }; - - var createData = function(typeNumber, errorCorrectionLevel, dataList) { - - var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel); - - var buffer = qrBitBuffer(); - - for (var i = 0; i < dataList.length; i += 1) { - var data = dataList[i]; - buffer.put(data.getMode(), 4); - buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); - data.write(buffer); - } - - // calc num max data. - var totalDataCount = 0; - for (var i = 0; i < rsBlocks.length; i += 1) { - totalDataCount += rsBlocks[i].dataCount; - } - - if (buffer.getLengthInBits() > totalDataCount * 8) { - throw 'code length overflow. (' - + buffer.getLengthInBits() - + '>' - + totalDataCount * 8 - + ')'; - } - - // end code - if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { - buffer.put(0, 4); - } - - // padding - while (buffer.getLengthInBits() % 8 != 0) { - buffer.putBit(false); - } - - // padding - while (true) { - - if (buffer.getLengthInBits() >= totalDataCount * 8) { - break; - } - buffer.put(PAD0, 8); - - if (buffer.getLengthInBits() >= totalDataCount * 8) { - break; - } - buffer.put(PAD1, 8); - } - - return createBytes(buffer, rsBlocks); - }; - - _this.addData = function(data, mode) { - - mode = mode || 'Byte'; - - var newData = null; - - switch(mode) { - case 'Numeric' : - newData = qrNumber(data); - break; - case 'Alphanumeric' : - newData = qrAlphaNum(data); - break; - case 'Byte' : - newData = qr8BitByte(data); - break; - case 'Kanji' : - newData = qrKanji(data); - break; - default : - throw 'mode:' + mode; - } - - _dataList.push(newData); - _dataCache = null; - }; - - _this.isDark = function(row, col) { - if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) { - throw row + ',' + col; - } - return _modules[row][col]; - }; - - _this.getModuleCount = function() { - return _moduleCount; - }; - - _this.make = function() { - if (_typeNumber < 1) { - var typeNumber = 1; - - for (; typeNumber < 40; typeNumber++) { - var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel); - var buffer = qrBitBuffer(); - - for (var i = 0; i < _dataList.length; i++) { - var data = _dataList[i]; - buffer.put(data.getMode(), 4); - buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); - data.write(buffer); - } - - var totalDataCount = 0; - for (var i = 0; i < rsBlocks.length; i++) { - totalDataCount += rsBlocks[i].dataCount; - } - - if (buffer.getLengthInBits() <= totalDataCount * 8) { - break; - } - } - - _typeNumber = typeNumber; - } - - makeImpl(false, getBestMaskPattern() ); - }; - - _this.getTypeNumber = function() { - return _typeNumber; - }; - - _this.setColors = function(foreground, background) { - _foreground = simplifyColor(foreground); - _background = simplifyColor(background); - - function simplifyColor(c) { - if (c.match(/^#[0-9A-Fa-f]{6}$/) && c[1] === c[2] && c[3] === c[4] && c[5] === c[6]) - c = "#" + c[1] + c[3] + c[5]; - return c; - } - }; - - _this.createTableTag = function(cellSize, margin) { - - cellSize = cellSize || 2; - margin = (typeof margin == 'undefined')? cellSize * 4 : margin; - - var qrHtml = ''; - - qrHtml += ''; - - for (var r = 0; r < _this.getModuleCount(); r += 1) { - - qrHtml += ''; - - for (var c = 0; c < _this.getModuleCount(); c += 1) { - qrHtml += ''; - } - - qrHtml += '
'; - } - - qrHtml += '
'; - - return qrHtml; - }; - - _this.createSvgTag = function(cellSize, margin, alt, title) { - - var opts = {}; - if (typeof arguments[0] == 'object') { - // Called by options. - opts = arguments[0]; - // overwrite cellSize and margin. - cellSize = opts.cellSize; - margin = opts.margin; - alt = opts.alt; - title = opts.title; - } - - cellSize = cellSize || 2; - margin = (typeof margin == 'undefined')? cellSize * 4 : margin; - - // Compose alt property surrogate - alt = (typeof alt === 'string') ? {text: alt} : alt || {}; - alt.text = alt.text || null; - alt.id = (alt.text) ? alt.id || 'qrcode-description' : null; - - // Compose title property surrogate - title = (typeof title === 'string') ? {text: title} : title || {}; - title.text = title.text || null; - title.id = (title.text) ? title.id || 'qrcode-title' : null; - - var size = _this.getModuleCount() * cellSize + margin * 2; - var c, c2, mc, r, mr, qrSvg='', rect; - - qrSvg += '' + - escapeXml(title.text) + '' : ''; - qrSvg += (alt.text) ? '' + - escapeXml(alt.text) + '' : ''; - qrSvg += ''; - qrSvg += ''; - qrSvg += ''; - - return qrSvg; - }; - - _this.createDataURL = function(cellSize, margin) { - - cellSize = cellSize || 2; - margin = (typeof margin == 'undefined')? cellSize * 4 : margin; - - var size = _this.getModuleCount() * cellSize + margin * 2; - var min = margin; - var max = size - margin; - - return createDataURL(size, size, _foreground, _background, function(x, y) { - if (min <= x && x < max && min <= y && y < max) { - var c = Math.floor( (x - min) / cellSize); - var r = Math.floor( (y - min) / cellSize); - return _this.isDark(r, c)? 0 : 1; - } else { - return 1; - } - } ); - }; - - _this.createImgTag = function(cellSize, margin, alt) { - - cellSize = cellSize || 2; - margin = (typeof margin == 'undefined')? cellSize * 4 : margin; - - var size = _this.getModuleCount() * cellSize + margin * 2; - - var img = ''; - img += '': escaped += '>'; break; - case '&': escaped += '&'; break; - case '"': escaped += '"'; break; - default : escaped += c; break; - } - } - return escaped; - }; - - var _createHalfASCII = function(margin, inverted) { - var cellSize = 1; - margin = (typeof margin == 'undefined')? cellSize * 2 : margin; - - var size = _this.getModuleCount() * cellSize + margin * 2; - var min = margin; - var max = size - margin; - - var y, x, r1, r2, p; - - var blocks = { - '██': '█', - '█ ': '▀', - ' █': '▄', - ' ': '\xa0' - }; - - var blocksLastLineNoMargin = { - '██': '▀', - '█ ': '▀', - ' █': '\xa0', - ' ': '\xa0' - }; - - var ascii = ''; - for (y = 0; y < size; y += 2) { - r1 = Math.floor((y - min) / cellSize); - r2 = Math.floor((y + 1 - min) / cellSize); - for (x = 0; x < size; x += 1) { - p = inverted ? '█' : ' '; - - if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) { - p = inverted ? ' ' : '█'; - } - - if (min <= x && x < max && min <= y+1 && y+1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) { - p += inverted ? ' ' : '█'; - } - else { - p += inverted ? '█' : ' '; - } - - // Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square. - ascii += (margin < 1 && y+1 >= max) ? blocksLastLineNoMargin[p] : blocks[p]; - } - - ascii += '\n'; - } - - if (size % 2 && margin > 0) { - return ascii.substring(0, ascii.length - size - 1) + Array(size+1).join(inverted ? '▀' : '\xa0'); - } - - return ascii.substring(0, ascii.length-1); - }; - - _this.createASCII = function(cellSize, margin, inverted) { - cellSize = cellSize || 1; - - if (cellSize < 2) { - return _createHalfASCII(margin, inverted); - } - - cellSize -= 1; - margin = (typeof margin == 'undefined')? cellSize * 2 : margin / (cellSize + 1) * cellSize; - - var size = _this.getModuleCount() * cellSize + margin * 2; - var min = margin; - var max = size - margin; - - var y, x, r, p; - - var white = '\xa0\xa0'; - var black = '██'; - if (inverted) { - var b = black; - black = white; - white = b; - } - - var ascii = ''; - var line = ''; - for (y = 0; y < size; y += 1) { - r = Math.floor( (y - min) / cellSize); - line = ''; - for (x = 0; x < size; x += 1) { - p = 1; - - if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) { - p = 0; - } - - // Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square. - line += p ? white : black; - } - - ascii += line + '\n'; - } - - return ascii.substring(0, ascii.length-1); - }; - - _this.renderTo2dContext = function(context, cellSize, margin) { - cellSize = cellSize || 2; - margin = (typeof margin == 'undefined')? cellSize * 4 : margin; - var length = _this.getModuleCount(); - if (margin > 0) { - context.fillStyle = _background; - context.fillRect(0, 0, length * cellSize + margin * 2, length * cellSize + margin * 2); - } - for (var row = 0; row < length; row++) { - for (var col = 0; col < length; col++) { - context.fillStyle = _this.isDark(row, col) ? _foreground : _background; - context.fillRect(margin + col * cellSize, margin + row * cellSize, cellSize, cellSize); - } - } - } - - return _this; - }; - - //--------------------------------------------------------------------- - // qrcode.stringToBytes - //--------------------------------------------------------------------- - - qrcode.stringToBytesFuncs = { - 'default' : function(s) { - var bytes = []; - for (var i = 0; i < s.length; i += 1) { - var c = s.charCodeAt(i); - bytes.push(c & 0xff); - } - return bytes; - } - }; - - qrcode.stringToBytes = qrcode.stringToBytesFuncs['default']; - - //--------------------------------------------------------------------- - // qrcode.createStringToBytes - //--------------------------------------------------------------------- - - /** - * @param unicodeData base64 string of byte array. - * [16bit Unicode],[16bit Bytes], ... - * @param numChars - */ - qrcode.createStringToBytes = function(unicodeData, numChars) { - - // create conversion map. - - var unicodeMap = function() { - - var bin = base64DecodeInputStream(unicodeData); - var read = function() { - var b = bin.read(); - if (b == -1) throw 'eof'; - return b; - }; - - var count = 0; - var unicodeMap = {}; - while (true) { - var b0 = bin.read(); - if (b0 == -1) break; - var b1 = read(); - var b2 = read(); - var b3 = read(); - var k = String.fromCharCode( (b0 << 8) | b1); - var v = (b2 << 8) | b3; - unicodeMap[k] = v; - count += 1; - } - if (count != numChars) { - throw count + ' != ' + numChars; - } - - return unicodeMap; - }(); - - var unknownChar = '?'.charCodeAt(0); - - return function(s) { - var bytes = []; - for (var i = 0; i < s.length; i += 1) { - var c = s.charCodeAt(i); - if (c < 128) { - bytes.push(c); - } else { - var b = unicodeMap[s.charAt(i)]; - if (typeof b == 'number') { - if ( (b & 0xff) == b) { - // 1byte - bytes.push(b); - } else { - // 2bytes - bytes.push(b >>> 8); - bytes.push(b & 0xff); - } - } else { - bytes.push(unknownChar); - } - } - } - return bytes; - }; - }; - - //--------------------------------------------------------------------- - // QRMode - //--------------------------------------------------------------------- - - var QRMode = { - MODE_NUMBER : 1 << 0, - MODE_ALPHA_NUM : 1 << 1, - MODE_8BIT_BYTE : 1 << 2, - MODE_KANJI : 1 << 3 - }; - - //--------------------------------------------------------------------- - // QRErrorCorrectionLevel - //--------------------------------------------------------------------- - - var QRErrorCorrectionLevel = { - L : 1, - M : 0, - Q : 3, - H : 2 - }; - - //--------------------------------------------------------------------- - // QRMaskPattern - //--------------------------------------------------------------------- - - var QRMaskPattern = { - PATTERN000 : 0, - PATTERN001 : 1, - PATTERN010 : 2, - PATTERN011 : 3, - PATTERN100 : 4, - PATTERN101 : 5, - PATTERN110 : 6, - PATTERN111 : 7 - }; - - //--------------------------------------------------------------------- - // QRUtil - //--------------------------------------------------------------------- - - var QRUtil = function() { - - var PATTERN_POSITION_TABLE = [ - [], - [6, 18], - [6, 22], - [6, 26], - [6, 30], - [6, 34], - [6, 22, 38], - [6, 24, 42], - [6, 26, 46], - [6, 28, 50], - [6, 30, 54], - [6, 32, 58], - [6, 34, 62], - [6, 26, 46, 66], - [6, 26, 48, 70], - [6, 26, 50, 74], - [6, 30, 54, 78], - [6, 30, 56, 82], - [6, 30, 58, 86], - [6, 34, 62, 90], - [6, 28, 50, 72, 94], - [6, 26, 50, 74, 98], - [6, 30, 54, 78, 102], - [6, 28, 54, 80, 106], - [6, 32, 58, 84, 110], - [6, 30, 58, 86, 114], - [6, 34, 62, 90, 118], - [6, 26, 50, 74, 98, 122], - [6, 30, 54, 78, 102, 126], - [6, 26, 52, 78, 104, 130], - [6, 30, 56, 82, 108, 134], - [6, 34, 60, 86, 112, 138], - [6, 30, 58, 86, 114, 142], - [6, 34, 62, 90, 118, 146], - [6, 30, 54, 78, 102, 126, 150], - [6, 24, 50, 76, 102, 128, 154], - [6, 28, 54, 80, 106, 132, 158], - [6, 32, 58, 84, 110, 136, 162], - [6, 26, 54, 82, 110, 138, 166], - [6, 30, 58, 86, 114, 142, 170] - ]; - var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); - var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); - var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); - - var _this = {}; - - var getBCHDigit = function(data) { - var digit = 0; - while (data != 0) { - digit += 1; - data >>>= 1; - } - return digit; - }; - - _this.getBCHTypeInfo = function(data) { - var d = data << 10; - while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { - d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) ); - } - return ( (data << 10) | d) ^ G15_MASK; - }; - - _this.getBCHTypeNumber = function(data) { - var d = data << 12; - while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { - d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) ); - } - return (data << 12) | d; - }; - - _this.getPatternPosition = function(typeNumber) { - return PATTERN_POSITION_TABLE[typeNumber - 1]; - }; - - _this.getMaskFunction = function(maskPattern) { - - switch (maskPattern) { - - case QRMaskPattern.PATTERN000 : - return function(i, j) { return (i + j) % 2 == 0; }; - case QRMaskPattern.PATTERN001 : - return function(i, j) { return i % 2 == 0; }; - case QRMaskPattern.PATTERN010 : - return function(i, j) { return j % 3 == 0; }; - case QRMaskPattern.PATTERN011 : - return function(i, j) { return (i + j) % 3 == 0; }; - case QRMaskPattern.PATTERN100 : - return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; }; - case QRMaskPattern.PATTERN101 : - return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; }; - case QRMaskPattern.PATTERN110 : - return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; }; - case QRMaskPattern.PATTERN111 : - return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; }; - - default : - throw 'bad maskPattern:' + maskPattern; - } - }; - - _this.getErrorCorrectPolynomial = function(errorCorrectLength) { - var a = qrPolynomial([1], 0); - for (var i = 0; i < errorCorrectLength; i += 1) { - a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) ); - } - return a; - }; - - _this.getLengthInBits = function(mode, type) { - - if (1 <= type && type < 10) { - - // 1 - 9 - - switch(mode) { - case QRMode.MODE_NUMBER : return 10; - case QRMode.MODE_ALPHA_NUM : return 9; - case QRMode.MODE_8BIT_BYTE : return 8; - case QRMode.MODE_KANJI : return 8; - default : - throw 'mode:' + mode; - } - - } else if (type < 27) { - - // 10 - 26 - - switch(mode) { - case QRMode.MODE_NUMBER : return 12; - case QRMode.MODE_ALPHA_NUM : return 11; - case QRMode.MODE_8BIT_BYTE : return 16; - case QRMode.MODE_KANJI : return 10; - default : - throw 'mode:' + mode; - } - - } else if (type < 41) { - - // 27 - 40 - - switch(mode) { - case QRMode.MODE_NUMBER : return 14; - case QRMode.MODE_ALPHA_NUM : return 13; - case QRMode.MODE_8BIT_BYTE : return 16; - case QRMode.MODE_KANJI : return 12; - default : - throw 'mode:' + mode; - } - - } else { - throw 'type:' + type; - } - }; - - _this.getLostPoint = function(qrcode) { - - var moduleCount = qrcode.getModuleCount(); - - var lostPoint = 0; - - // LEVEL1 - - for (var row = 0; row < moduleCount; row += 1) { - for (var col = 0; col < moduleCount; col += 1) { - - var sameCount = 0; - var dark = qrcode.isDark(row, col); - - for (var r = -1; r <= 1; r += 1) { - - if (row + r < 0 || moduleCount <= row + r) { - continue; - } - - for (var c = -1; c <= 1; c += 1) { - - if (col + c < 0 || moduleCount <= col + c) { - continue; - } - - if (r == 0 && c == 0) { - continue; - } - - if (dark == qrcode.isDark(row + r, col + c) ) { - sameCount += 1; - } - } - } - - if (sameCount > 5) { - lostPoint += (3 + sameCount - 5); - } - } - }; - - // LEVEL2 - - for (var row = 0; row < moduleCount - 1; row += 1) { - for (var col = 0; col < moduleCount - 1; col += 1) { - var count = 0; - if (qrcode.isDark(row, col) ) count += 1; - if (qrcode.isDark(row + 1, col) ) count += 1; - if (qrcode.isDark(row, col + 1) ) count += 1; - if (qrcode.isDark(row + 1, col + 1) ) count += 1; - if (count == 0 || count == 4) { - lostPoint += 3; - } - } - } - - // LEVEL3 - - for (var row = 0; row < moduleCount; row += 1) { - for (var col = 0; col < moduleCount - 6; col += 1) { - if (qrcode.isDark(row, col) - && !qrcode.isDark(row, col + 1) - && qrcode.isDark(row, col + 2) - && qrcode.isDark(row, col + 3) - && qrcode.isDark(row, col + 4) - && !qrcode.isDark(row, col + 5) - && qrcode.isDark(row, col + 6) ) { - lostPoint += 40; - } - } - } - - for (var col = 0; col < moduleCount; col += 1) { - for (var row = 0; row < moduleCount - 6; row += 1) { - if (qrcode.isDark(row, col) - && !qrcode.isDark(row + 1, col) - && qrcode.isDark(row + 2, col) - && qrcode.isDark(row + 3, col) - && qrcode.isDark(row + 4, col) - && !qrcode.isDark(row + 5, col) - && qrcode.isDark(row + 6, col) ) { - lostPoint += 40; - } - } - } - - // LEVEL4 - - var darkCount = 0; - - for (var col = 0; col < moduleCount; col += 1) { - for (var row = 0; row < moduleCount; row += 1) { - if (qrcode.isDark(row, col) ) { - darkCount += 1; - } - } - } - - var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; - lostPoint += ratio * 10; - - return lostPoint; - }; - - return _this; - }(); - - //--------------------------------------------------------------------- - // QRMath - //--------------------------------------------------------------------- - - var QRMath = function() { - - var EXP_TABLE = new Array(256); - var LOG_TABLE = new Array(256); - - // initialize tables - for (var i = 0; i < 8; i += 1) { - EXP_TABLE[i] = 1 << i; - } - for (var i = 8; i < 256; i += 1) { - EXP_TABLE[i] = EXP_TABLE[i - 4] - ^ EXP_TABLE[i - 5] - ^ EXP_TABLE[i - 6] - ^ EXP_TABLE[i - 8]; - } - for (var i = 0; i < 255; i += 1) { - LOG_TABLE[EXP_TABLE[i] ] = i; - } - - var _this = {}; - - _this.glog = function(n) { - - if (n < 1) { - throw 'glog(' + n + ')'; - } - - return LOG_TABLE[n]; - }; - - _this.gexp = function(n) { - - while (n < 0) { - n += 255; - } - - while (n >= 256) { - n -= 255; - } - - return EXP_TABLE[n]; - }; - - return _this; - }(); - - //--------------------------------------------------------------------- - // qrPolynomial - //--------------------------------------------------------------------- - - function qrPolynomial(num, shift) { - - if (typeof num.length == 'undefined') { - throw num.length + '/' + shift; - } - - var _num = function() { - var offset = 0; - while (offset < num.length && num[offset] == 0) { - offset += 1; - } - var _num = new Array(num.length - offset + shift); - for (var i = 0; i < num.length - offset; i += 1) { - _num[i] = num[i + offset]; - } - return _num; - }(); - - var _this = {}; - - _this.getAt = function(index) { - return _num[index]; - }; - - _this.getLength = function() { - return _num.length; - }; - - _this.multiply = function(e) { - - var num = new Array(_this.getLength() + e.getLength() - 1); - - for (var i = 0; i < _this.getLength(); i += 1) { - for (var j = 0; j < e.getLength(); j += 1) { - num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) ); - } - } - - return qrPolynomial(num, 0); - }; - - _this.mod = function(e) { - - if (_this.getLength() - e.getLength() < 0) { - return _this; - } - - var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) ); - - var num = new Array(_this.getLength() ); - for (var i = 0; i < _this.getLength(); i += 1) { - num[i] = _this.getAt(i); - } - - for (var i = 0; i < e.getLength(); i += 1) { - num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio); - } - - // recursive call - return qrPolynomial(num, 0).mod(e); - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // QRRSBlock - //--------------------------------------------------------------------- - - var QRRSBlock = function() { - - var RS_BLOCK_TABLE = [ - - // L - // M - // Q - // H - - // 1 - [1, 26, 19], - [1, 26, 16], - [1, 26, 13], - [1, 26, 9], - - // 2 - [1, 44, 34], - [1, 44, 28], - [1, 44, 22], - [1, 44, 16], - - // 3 - [1, 70, 55], - [1, 70, 44], - [2, 35, 17], - [2, 35, 13], - - // 4 - [1, 100, 80], - [2, 50, 32], - [2, 50, 24], - [4, 25, 9], - - // 5 - [1, 134, 108], - [2, 67, 43], - [2, 33, 15, 2, 34, 16], - [2, 33, 11, 2, 34, 12], - - // 6 - [2, 86, 68], - [4, 43, 27], - [4, 43, 19], - [4, 43, 15], - - // 7 - [2, 98, 78], - [4, 49, 31], - [2, 32, 14, 4, 33, 15], - [4, 39, 13, 1, 40, 14], - - // 8 - [2, 121, 97], - [2, 60, 38, 2, 61, 39], - [4, 40, 18, 2, 41, 19], - [4, 40, 14, 2, 41, 15], - - // 9 - [2, 146, 116], - [3, 58, 36, 2, 59, 37], - [4, 36, 16, 4, 37, 17], - [4, 36, 12, 4, 37, 13], - - // 10 - [2, 86, 68, 2, 87, 69], - [4, 69, 43, 1, 70, 44], - [6, 43, 19, 2, 44, 20], - [6, 43, 15, 2, 44, 16], - - // 11 - [4, 101, 81], - [1, 80, 50, 4, 81, 51], - [4, 50, 22, 4, 51, 23], - [3, 36, 12, 8, 37, 13], - - // 12 - [2, 116, 92, 2, 117, 93], - [6, 58, 36, 2, 59, 37], - [4, 46, 20, 6, 47, 21], - [7, 42, 14, 4, 43, 15], - - // 13 - [4, 133, 107], - [8, 59, 37, 1, 60, 38], - [8, 44, 20, 4, 45, 21], - [12, 33, 11, 4, 34, 12], - - // 14 - [3, 145, 115, 1, 146, 116], - [4, 64, 40, 5, 65, 41], - [11, 36, 16, 5, 37, 17], - [11, 36, 12, 5, 37, 13], - - // 15 - [5, 109, 87, 1, 110, 88], - [5, 65, 41, 5, 66, 42], - [5, 54, 24, 7, 55, 25], - [11, 36, 12, 7, 37, 13], - - // 16 - [5, 122, 98, 1, 123, 99], - [7, 73, 45, 3, 74, 46], - [15, 43, 19, 2, 44, 20], - [3, 45, 15, 13, 46, 16], - - // 17 - [1, 135, 107, 5, 136, 108], - [10, 74, 46, 1, 75, 47], - [1, 50, 22, 15, 51, 23], - [2, 42, 14, 17, 43, 15], - - // 18 - [5, 150, 120, 1, 151, 121], - [9, 69, 43, 4, 70, 44], - [17, 50, 22, 1, 51, 23], - [2, 42, 14, 19, 43, 15], - - // 19 - [3, 141, 113, 4, 142, 114], - [3, 70, 44, 11, 71, 45], - [17, 47, 21, 4, 48, 22], - [9, 39, 13, 16, 40, 14], - - // 20 - [3, 135, 107, 5, 136, 108], - [3, 67, 41, 13, 68, 42], - [15, 54, 24, 5, 55, 25], - [15, 43, 15, 10, 44, 16], - - // 21 - [4, 144, 116, 4, 145, 117], - [17, 68, 42], - [17, 50, 22, 6, 51, 23], - [19, 46, 16, 6, 47, 17], - - // 22 - [2, 139, 111, 7, 140, 112], - [17, 74, 46], - [7, 54, 24, 16, 55, 25], - [34, 37, 13], - - // 23 - [4, 151, 121, 5, 152, 122], - [4, 75, 47, 14, 76, 48], - [11, 54, 24, 14, 55, 25], - [16, 45, 15, 14, 46, 16], - - // 24 - [6, 147, 117, 4, 148, 118], - [6, 73, 45, 14, 74, 46], - [11, 54, 24, 16, 55, 25], - [30, 46, 16, 2, 47, 17], - - // 25 - [8, 132, 106, 4, 133, 107], - [8, 75, 47, 13, 76, 48], - [7, 54, 24, 22, 55, 25], - [22, 45, 15, 13, 46, 16], - - // 26 - [10, 142, 114, 2, 143, 115], - [19, 74, 46, 4, 75, 47], - [28, 50, 22, 6, 51, 23], - [33, 46, 16, 4, 47, 17], - - // 27 - [8, 152, 122, 4, 153, 123], - [22, 73, 45, 3, 74, 46], - [8, 53, 23, 26, 54, 24], - [12, 45, 15, 28, 46, 16], - - // 28 - [3, 147, 117, 10, 148, 118], - [3, 73, 45, 23, 74, 46], - [4, 54, 24, 31, 55, 25], - [11, 45, 15, 31, 46, 16], - - // 29 - [7, 146, 116, 7, 147, 117], - [21, 73, 45, 7, 74, 46], - [1, 53, 23, 37, 54, 24], - [19, 45, 15, 26, 46, 16], - - // 30 - [5, 145, 115, 10, 146, 116], - [19, 75, 47, 10, 76, 48], - [15, 54, 24, 25, 55, 25], - [23, 45, 15, 25, 46, 16], - - // 31 - [13, 145, 115, 3, 146, 116], - [2, 74, 46, 29, 75, 47], - [42, 54, 24, 1, 55, 25], - [23, 45, 15, 28, 46, 16], - - // 32 - [17, 145, 115], - [10, 74, 46, 23, 75, 47], - [10, 54, 24, 35, 55, 25], - [19, 45, 15, 35, 46, 16], - - // 33 - [17, 145, 115, 1, 146, 116], - [14, 74, 46, 21, 75, 47], - [29, 54, 24, 19, 55, 25], - [11, 45, 15, 46, 46, 16], - - // 34 - [13, 145, 115, 6, 146, 116], - [14, 74, 46, 23, 75, 47], - [44, 54, 24, 7, 55, 25], - [59, 46, 16, 1, 47, 17], - - // 35 - [12, 151, 121, 7, 152, 122], - [12, 75, 47, 26, 76, 48], - [39, 54, 24, 14, 55, 25], - [22, 45, 15, 41, 46, 16], - - // 36 - [6, 151, 121, 14, 152, 122], - [6, 75, 47, 34, 76, 48], - [46, 54, 24, 10, 55, 25], - [2, 45, 15, 64, 46, 16], - - // 37 - [17, 152, 122, 4, 153, 123], - [29, 74, 46, 14, 75, 47], - [49, 54, 24, 10, 55, 25], - [24, 45, 15, 46, 46, 16], - - // 38 - [4, 152, 122, 18, 153, 123], - [13, 74, 46, 32, 75, 47], - [48, 54, 24, 14, 55, 25], - [42, 45, 15, 32, 46, 16], - - // 39 - [20, 147, 117, 4, 148, 118], - [40, 75, 47, 7, 76, 48], - [43, 54, 24, 22, 55, 25], - [10, 45, 15, 67, 46, 16], - - // 40 - [19, 148, 118, 6, 149, 119], - [18, 75, 47, 31, 76, 48], - [34, 54, 24, 34, 55, 25], - [20, 45, 15, 61, 46, 16] - ]; - - var qrRSBlock = function(totalCount, dataCount) { - var _this = {}; - _this.totalCount = totalCount; - _this.dataCount = dataCount; - return _this; - }; - - var _this = {}; - - var getRsBlockTable = function(typeNumber, errorCorrectionLevel) { - - switch(errorCorrectionLevel) { - case QRErrorCorrectionLevel.L : - return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; - case QRErrorCorrectionLevel.M : - return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; - case QRErrorCorrectionLevel.Q : - return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; - case QRErrorCorrectionLevel.H : - return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; - default : - return undefined; - } - }; - - _this.getRSBlocks = function(typeNumber, errorCorrectionLevel) { - - var rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel); - - if (typeof rsBlock == 'undefined') { - throw 'bad rs block @ typeNumber:' + typeNumber + - '/errorCorrectionLevel:' + errorCorrectionLevel; - } - - var length = rsBlock.length / 3; - - var list = []; - - for (var i = 0; i < length; i += 1) { - - var count = rsBlock[i * 3 + 0]; - var totalCount = rsBlock[i * 3 + 1]; - var dataCount = rsBlock[i * 3 + 2]; - - for (var j = 0; j < count; j += 1) { - list.push(qrRSBlock(totalCount, dataCount) ); - } - } - - return list; - }; - - return _this; - }(); - - //--------------------------------------------------------------------- - // qrBitBuffer - //--------------------------------------------------------------------- - - var qrBitBuffer = function() { - - var _buffer = []; - var _length = 0; - - var _this = {}; - - _this.getBuffer = function() { - return _buffer; - }; - - _this.getAt = function(index) { - var bufIndex = Math.floor(index / 8); - return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1; - }; - - _this.put = function(num, length) { - for (var i = 0; i < length; i += 1) { - _this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1); - } - }; - - _this.getLengthInBits = function() { - return _length; - }; - - _this.putBit = function(bit) { - - var bufIndex = Math.floor(_length / 8); - if (_buffer.length <= bufIndex) { - _buffer.push(0); - } - - if (bit) { - _buffer[bufIndex] |= (0x80 >>> (_length % 8) ); - } - - _length += 1; - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // qrNumber - //--------------------------------------------------------------------- - - var qrNumber = function(data) { - - var _mode = QRMode.MODE_NUMBER; - var _data = data; - - var _this = {}; - - _this.getMode = function() { - return _mode; - }; - - _this.getLength = function(buffer) { - return _data.length; - }; - - _this.write = function(buffer) { - - var data = _data; - - var i = 0; - - while (i + 2 < data.length) { - buffer.put(strToNum(data.substring(i, i + 3) ), 10); - i += 3; - } - - if (i < data.length) { - if (data.length - i == 1) { - buffer.put(strToNum(data.substring(i, i + 1) ), 4); - } else if (data.length - i == 2) { - buffer.put(strToNum(data.substring(i, i + 2) ), 7); - } - } - }; - - var strToNum = function(s) { - var num = 0; - for (var i = 0; i < s.length; i += 1) { - num = num * 10 + chatToNum(s.charAt(i) ); - } - return num; - }; - - var chatToNum = function(c) { - if ('0' <= c && c <= '9') { - return c.charCodeAt(0) - '0'.charCodeAt(0); - } - throw 'illegal char :' + c; - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // qrAlphaNum - //--------------------------------------------------------------------- - - var qrAlphaNum = function(data) { - - var _mode = QRMode.MODE_ALPHA_NUM; - var _data = data; - - var _this = {}; - - _this.getMode = function() { - return _mode; - }; - - _this.getLength = function(buffer) { - return _data.length; - }; - - _this.write = function(buffer) { - - var s = _data; - - var i = 0; - - while (i + 1 < s.length) { - buffer.put( - getCode(s.charAt(i) ) * 45 + - getCode(s.charAt(i + 1) ), 11); - i += 2; - } - - if (i < s.length) { - buffer.put(getCode(s.charAt(i) ), 6); - } - }; - - var getCode = function(c) { - - if ('0' <= c && c <= '9') { - return c.charCodeAt(0) - '0'.charCodeAt(0); - } else if ('A' <= c && c <= 'Z') { - return c.charCodeAt(0) - 'A'.charCodeAt(0) + 10; - } else { - switch (c) { - case ' ' : return 36; - case '$' : return 37; - case '%' : return 38; - case '*' : return 39; - case '+' : return 40; - case '-' : return 41; - case '.' : return 42; - case '/' : return 43; - case ':' : return 44; - default : - throw 'illegal char :' + c; - } - } - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // qr8BitByte - //--------------------------------------------------------------------- - - var qr8BitByte = function(data) { - - var _mode = QRMode.MODE_8BIT_BYTE; - var _data = data; - var _bytes = qrcode.stringToBytes(data); - - var _this = {}; - - _this.getMode = function() { - return _mode; - }; - - _this.getLength = function(buffer) { - return _bytes.length; - }; - - _this.write = function(buffer) { - for (var i = 0; i < _bytes.length; i += 1) { - buffer.put(_bytes[i], 8); - } - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // qrKanji - //--------------------------------------------------------------------- - - var qrKanji = function(data) { - - var _mode = QRMode.MODE_KANJI; - var _data = data; - - var stringToBytes = qrcode.stringToBytesFuncs['SJIS']; - if (!stringToBytes) { - throw 'sjis not supported.'; - } - !function(c, code) { - // self test for sjis support. - var test = stringToBytes(c); - if (test.length != 2 || ( (test[0] << 8) | test[1]) != code) { - throw 'sjis not supported.'; - } - }('\u53cb', 0x9746); - - var _bytes = stringToBytes(data); - - var _this = {}; - - _this.getMode = function() { - return _mode; - }; - - _this.getLength = function(buffer) { - return ~~(_bytes.length / 2); - }; - - _this.write = function(buffer) { - - var data = _bytes; - - var i = 0; - - while (i + 1 < data.length) { - - var c = ( (0xff & data[i]) << 8) | (0xff & data[i + 1]); - - if (0x8140 <= c && c <= 0x9FFC) { - c -= 0x8140; - } else if (0xE040 <= c && c <= 0xEBBF) { - c -= 0xC140; - } else { - throw 'illegal char at ' + (i + 1) + '/' + c; - } - - c = ( (c >>> 8) & 0xff) * 0xC0 + (c & 0xff); - - buffer.put(c, 13); - - i += 2; - } - - if (i < data.length) { - throw 'illegal char at ' + (i + 1); - } - }; - - return _this; - }; - - //===================================================================== - // GIF Support etc. - // - - //--------------------------------------------------------------------- - // byteArrayOutputStream - //--------------------------------------------------------------------- - - var byteArrayOutputStream = function() { - - var _bytes = []; - - var _this = {}; - - _this.writeByte = function(b) { - _bytes.push(b & 0xff); - }; - - _this.writeShort = function(i) { - _this.writeByte(i); - _this.writeByte(i >>> 8); - }; - - _this.writeBytes = function(b, off, len) { - off = off || 0; - len = len || b.length; - for (var i = 0; i < len; i += 1) { - _this.writeByte(b[i + off]); - } - }; - - _this.writeString = function(s) { - for (var i = 0; i < s.length; i += 1) { - _this.writeByte(s.charCodeAt(i) ); - } - }; - - _this.toByteArray = function() { - return _bytes; - }; - - _this.toString = function() { - var s = ''; - s += '['; - for (var i = 0; i < _bytes.length; i += 1) { - if (i > 0) { - s += ','; - } - s += _bytes[i]; - } - s += ']'; - return s; - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // base64EncodeOutputStream - //--------------------------------------------------------------------- - - var base64EncodeOutputStream = function() { - - var _buffer = 0; - var _buflen = 0; - var _length = 0; - var _base64 = ''; - - var _this = {}; - - var writeEncoded = function(b) { - _base64 += String.fromCharCode(encode(b & 0x3f) ); - }; - - var encode = function(n) { - if (n < 0) { - // error. - } else if (n < 26) { - return 0x41 + n; - } else if (n < 52) { - return 0x61 + (n - 26); - } else if (n < 62) { - return 0x30 + (n - 52); - } else if (n == 62) { - return 0x2b; - } else if (n == 63) { - return 0x2f; - } - throw 'n:' + n; - }; - - _this.writeByte = function(n) { - - _buffer = (_buffer << 8) | (n & 0xff); - _buflen += 8; - _length += 1; - - while (_buflen >= 6) { - writeEncoded(_buffer >>> (_buflen - 6) ); - _buflen -= 6; - } - }; - - _this.flush = function() { - - if (_buflen > 0) { - writeEncoded(_buffer << (6 - _buflen) ); - _buffer = 0; - _buflen = 0; - } - - if (_length % 3 != 0) { - // padding - var padlen = 3 - _length % 3; - for (var i = 0; i < padlen; i += 1) { - _base64 += '='; - } - } - }; - - _this.toString = function() { - return _base64; - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // base64DecodeInputStream - //--------------------------------------------------------------------- - - var base64DecodeInputStream = function(str) { - - var _str = str; - var _pos = 0; - var _buffer = 0; - var _buflen = 0; - - var _this = {}; - - _this.read = function() { - - while (_buflen < 8) { - - if (_pos >= _str.length) { - if (_buflen == 0) { - return -1; - } - throw 'unexpected end of file./' + _buflen; - } - - var c = _str.charAt(_pos); - _pos += 1; - - if (c == '=') { - _buflen = 0; - return -1; - } else if (c.match(/^\s$/) ) { - // ignore if whitespace. - continue; - } - - _buffer = (_buffer << 6) | decode(c.charCodeAt(0) ); - _buflen += 6; - } - - var n = (_buffer >>> (_buflen - 8) ) & 0xff; - _buflen -= 8; - return n; - }; - - var decode = function(c) { - if (0x41 <= c && c <= 0x5a) { - return c - 0x41; - } else if (0x61 <= c && c <= 0x7a) { - return c - 0x61 + 26; - } else if (0x30 <= c && c <= 0x39) { - return c - 0x30 + 52; - } else if (c == 0x2b) { - return 62; - } else if (c == 0x2f) { - return 63; - } else { - throw 'c:' + c; - } - }; - - return _this; - }; - - //--------------------------------------------------------------------- - // gifImage (B/W) - //--------------------------------------------------------------------- - - var gifImage = function(width, height, foreground, background) { - - var _width = width; - var _height = height; - var _data = new Array(width * height); - - var _this = {}; - - _this.setPixel = function(x, y, pixel) { - _data[y * _width + x] = pixel; - }; - - _this.write = function(out) { - - //--------------------------------- - // GIF Signature - - out.writeString('GIF87a'); - - //--------------------------------- - // Screen Descriptor - - out.writeShort(_width); - out.writeShort(_height); - - out.writeByte(0x80); // 2bit - out.writeByte(0); - out.writeByte(0); - - //--------------------------------- - // Global Color Map - - // foreground - var fg = parseColor(foreground, "#000"); - out.writeByte(fg[0]); - out.writeByte(fg[1]); - out.writeByte(fg[2]); - - // background - var bg = parseColor(background, "#fff"); - out.writeByte(bg[0]); - out.writeByte(bg[1]); - out.writeByte(bg[2]); - - //--------------------------------- - // Image Descriptor - - out.writeString(','); - out.writeShort(0); - out.writeShort(0); - out.writeShort(_width); - out.writeShort(_height); - out.writeByte(0); - - //--------------------------------- - // Local Color Map - - //--------------------------------- - // Raster Data - - var lzwMinCodeSize = 2; - var raster = getLZWRaster(lzwMinCodeSize); - - out.writeByte(lzwMinCodeSize); - - var offset = 0; - - while (raster.length - offset > 255) { - out.writeByte(255); - out.writeBytes(raster, offset, 255); - offset += 255; - } - - out.writeByte(raster.length - offset); - out.writeBytes(raster, offset, raster.length - offset); - out.writeByte(0x00); - - //--------------------------------- - // GIF Terminator - out.writeString(';'); - }; - - function parseColor(c, fallback) { - c = c.toLowerCase(); - if (c.match(/^#[0-9a-f]{3}$/)) - return [parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16)]; - if (c.match(/^#[0-9a-f]{6}$/)) - return [parseInt(c[1] + c[2], 16), parseInt(c[3] + c[4], 16), parseInt(c[5] + c[6], 16)]; - return parseColor(fallback); - } - - var bitOutputStream = function(out) { - - var _out = out; - var _bitLength = 0; - var _bitBuffer = 0; - - var _this = {}; - - _this.write = function(data, length) { - - if ( (data >>> length) != 0) { - throw 'length over'; - } - - while (_bitLength + length >= 8) { - _out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) ); - length -= (8 - _bitLength); - data >>>= (8 - _bitLength); - _bitBuffer = 0; - _bitLength = 0; - } - - _bitBuffer = (data << _bitLength) | _bitBuffer; - _bitLength = _bitLength + length; - }; - - _this.flush = function() { - if (_bitLength > 0) { - _out.writeByte(_bitBuffer); - } - }; - - return _this; - }; - - var getLZWRaster = function(lzwMinCodeSize) { - - var clearCode = 1 << lzwMinCodeSize; - var endCode = (1 << lzwMinCodeSize) + 1; - var bitLength = lzwMinCodeSize + 1; - - // Setup LZWTable - var table = 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 = byteArrayOutputStream(); - var bitOut = bitOutputStream(byteOut); - - // clear code - bitOut.write(clearCode, bitLength); - - var dataIndex = 0; - - var s = String.fromCharCode(_data[dataIndex]); - dataIndex += 1; - - while (dataIndex < _data.length) { - - var c = String.fromCharCode(_data[dataIndex]); - dataIndex += 1; - - if (table.contains(s + c) ) { - - s = s + c; - - } else { - - bitOut.write(table.indexOf(s), bitLength); - - if (table.size() < 0xfff) { - - if (table.size() == (1 << bitLength) ) { - bitLength += 1; - } - - table.add(s + c); - } - - s = c; - } - } - - bitOut.write(table.indexOf(s), bitLength); - - // end code - bitOut.write(endCode, bitLength); - - bitOut.flush(); - - return byteOut.toByteArray(); - }; - - var lzwTable = function() { - - var _map = {}; - var _size = 0; - - var _this = {}; - - _this.add = function(key) { - if (_this.contains(key) ) { - throw 'dup key:' + key; - } - _map[key] = _size; - _size += 1; - }; - - _this.size = function() { - return _size; - }; - - _this.indexOf = function(key) { - return _map[key]; - }; - - _this.contains = function(key) { - return typeof _map[key] != 'undefined'; - }; - - return _this; - }; - - return _this; - }; - - var createDataURL = function(width, height, foreground, background, getPixel) { - var gif = gifImage(width, height, foreground, background); - for (var y = 0; y < height; y += 1) { - for (var x = 0; x < width; x += 1) { - gif.setPixel(x, y, getPixel(x, y) ); - } - } - - var b = byteArrayOutputStream(); - gif.write(b); - - var base64 = base64EncodeOutputStream(); - var bytes = b.toByteArray(); - for (var i = 0; i < bytes.length; i += 1) { - base64.writeByte(bytes[i]); - } - base64.flush(); - - return 'data:image/gif;base64,' + base64; - }; - - //--------------------------------------------------------------------- - // returns qrcode function. - - return qrcode; + return qrcode; }(); // multibyte support -!function() { - - qrcode.stringToBytesFuncs['UTF-8'] = function(s) { - // http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array - function toUTF8Array(str) { - var utf8 = []; - for (var i=0; i < str.length; i++) { - var charcode = str.charCodeAt(i); - if (charcode < 0x80) utf8.push(charcode); - else if (charcode < 0x800) { - utf8.push(0xc0 | (charcode >> 6), - 0x80 | (charcode & 0x3f)); - } - else if (charcode < 0xd800 || charcode >= 0xe000) { - utf8.push(0xe0 | (charcode >> 12), - 0x80 | ((charcode>>6) & 0x3f), - 0x80 | (charcode & 0x3f)); - } - // surrogate pair - else { - i++; - // UTF-16 encodes 0x10000-0x10FFFF by - // subtracting 0x10000 and splitting the - // 20 bits of 0x0-0xFFFFF into two halves - charcode = 0x10000 + (((charcode & 0x3ff)<<10) - | (str.charCodeAt(i) & 0x3ff)); - utf8.push(0xf0 | (charcode >>18), - 0x80 | ((charcode>>12) & 0x3f), - 0x80 | ((charcode>>6) & 0x3f), - 0x80 | (charcode & 0x3f)); - } - } - return utf8; - } - return toUTF8Array(s); - }; +!function () { + qrcode.stringToBytesFuncs['UTF-8'] = function (s) { + // http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array + function toUTF8Array(str) { + let utf8 = []; + for (let i = 0; i < str.length; i++) { + let charcode = str.charCodeAt(i); + if (charcode < 0x80) utf8.push(charcode); + else if (charcode < 0x800) { + utf8.push(0xc0 | (charcode >> 6), + 0x80 | (charcode & 0x3f)); + } + else if (charcode < 0xd800 || charcode >= 0xe000) { + utf8.push(0xe0 | (charcode >> 12), + 0x80 | ((charcode >> 6) & 0x3f), + 0x80 | (charcode & 0x3f)); + } + // surrogate pair + else { + i++; + // UTF-16 encodes 0x10000-0x10FFFF by + // subtracting 0x10000 and splitting the + // 20 bits of 0x0-0xFFFFF into two halves + charcode = 0x10000 + (((charcode & 0x3ff) << 10) | + (str.charCodeAt(i) & 0x3ff)); + utf8.push(0xf0 | (charcode >> 18), + 0x80 | ((charcode >> 12) & 0x3f), + 0x80 | ((charcode >> 6) & 0x3f), + 0x80 | (charcode & 0x3f)); + } + } + return utf8; + } + return toUTF8Array(s); + }; }(); (function (factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof exports === 'object') { - module.exports = factory(); - } + if (typeof define === 'function' && define.amd) { + define([], factory); + } + else if (typeof exports === 'object') { + module.exports = factory(); + } }(function () { - return qrcode; + return qrcode; })); diff --git a/js/sample.html b/js/sample.html index 19515ad..90ec81e 100644 --- a/js/sample.html +++ b/js/sample.html @@ -1,90 +1,91 @@ - - - - - - - - - - QR Code Generator for JavaScript - - - -

QR Code Generator for JavaScript

-
-

-
- -

-

- Cell size: - -   - Padding: - -   - Error correction level: - -

-

- Image type: - -   - Foreground: - -   - Background: - -

-

- Content mode: - -   - Multibyte text: - -   - Size type: - -

-
-

- -

-

- -

-
- + + + + + + + + + + QR Code Generator for JavaScript + + + +

QR Code Generator for JavaScript

+ +
+

+
+ +

+

+ Cell size: + +   + Padding: + +   + Error correction level: + +

+

+ Image type: + +   + Foreground: + +   + Background: + +

+

+ Content mode: + +   + Multibyte text: + +   + Size type: + +

+
+

+ +

+

+ +

+
+ diff --git a/js/sample.js b/js/sample.js index 9a46273..111f5b4 100644 --- a/js/sample.js +++ b/js/sample.js @@ -1,164 +1,162 @@ var downloadFileName, downloadType, downloadData; -var body_loadHander = function() { - - var crtOpt = function(value, label) { - var opt = document.createElement('option'); - opt.appendChild(document.createTextNode(label) ); - opt.value = value; - return opt; - }; - - var t = document.forms['qrForm'].elements['t']; - t.appendChild(crtOpt('' + 0, 'Auto') ); - for (var i = 1; i <= 40; i += 1) { - t.appendChild(crtOpt('' + i, '' + i) ); - } - t.value = '0'; - - var form = document.forms['qrForm']; - form.elements['msg'].addEventListener("input", update_qrcode); - form.elements['t'].addEventListener("change", update_qrcode); - form.elements['cellsize'].addEventListener("input", update_qrcode); - form.elements['padding'].addEventListener("input", update_qrcode); - form.elements['e'].addEventListener("change", update_qrcode); - form.elements['m'].addEventListener("change", update_qrcode); - form.elements['mb'].addEventListener("change", update_qrcode); - form.elements['format'].addEventListener("change", update_qrcode); - form.elements['foreground'].addEventListener("change", update_qrcode); - form.elements['background'].addEventListener("change", update_qrcode); - update_qrcode(); - - document.getElementById("download").addEventListener("click", function () { - if (downloadData) - download(downloadFileName, downloadType, downloadData); - }); +var body_loadHander = function () { + var crtOpt = function (value, label) { + let opt = document.createElement('option'); + opt.appendChild(document.createTextNode(label)); + opt.value = value; + return opt; + }; + + let t = document.forms['qrForm'].elements['t']; + t.appendChild(crtOpt('' + 0, 'Auto')); + for (let i = 1; i <= 40; i += 1) { + t.appendChild(crtOpt('' + i, '' + i)); + } + t.value = '0'; + + let form = document.forms['qrForm']; + form.elements['msg'].addEventListener("input", update_qrcode); + form.elements['t'].addEventListener("change", update_qrcode); + form.elements['cellsize'].addEventListener("input", update_qrcode); + form.elements['padding'].addEventListener("input", update_qrcode); + form.elements['e'].addEventListener("change", update_qrcode); + form.elements['m'].addEventListener("change", update_qrcode); + form.elements['mb'].addEventListener("change", update_qrcode); + form.elements['format'].addEventListener("change", update_qrcode); + form.elements['foreground'].addEventListener("change", update_qrcode); + form.elements['background'].addEventListener("change", update_qrcode); + update_qrcode(); + + document.getElementById("download").addEventListener("click", function () { + if (downloadData) + download(downloadFileName, downloadType, downloadData); + }); }; -var draw_qrcode = function(text, typeNumber, errorCorrectionLevel) { - document.write(create_qrcode(text, typeNumber, errorCorrectionLevel) ); +var draw_qrcode = function (text, typeNumber, errorCorrectionLevel) { + document.write(create_qrcode(text, typeNumber, errorCorrectionLevel)); }; -var create_qrcode = function(text, typeNumber, cellsize, padding, errorCorrectionLevel, mode, mb, format, fg, bg) { - - if (!mode) { - if (text.match(/^[0-9]+$/)) - mode = "Numeric"; - else if (text.match(/^[0-9A-Z $%*+/.:-]+$/)) - mode = "Alphanumeric"; - else - mode = "Byte"; - } - - var info = document.getElementById("qr-info"); - downloadData = undefined; - - var showLength = function (len) { - if (len < 1024) - len += " B"; - else if (len < 10240) - len = Math.round(len * 100 / 1024) / 100 + " KiB"; - else - len = Math.round(len * 10 / 1024) / 10 + " KiB"; - info.innerHTML += ", file size: " + len + ""; - }; - - try { - qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; - - var qr = qrcode(typeNumber || 0, errorCorrectionLevel || 'M'); - qr.addData(text, mode); - qr.make(); - qr.setColors(fg, bg); - - var size = qr.getModuleCount() * cellsize + padding * 2; - info.innerHTML = "Size type: " + qr.getTypeNumber() + ", " + - "code columns: " + qr.getModuleCount() + ", " + - "image size: " + size + "x" + size + " px"; - - switch (format) { - case "svg": - downloadFileName = "qr.svg"; - downloadType = "image/svg"; - downloadData = qr.createSvgTag(cellsize, padding); - showLength(downloadData.length); - return downloadData; - case "gif": - case "png": - case "webp": - case "jpeg": - var canvas = document.createElement("canvas"); - canvas.width = canvas.height = qr.getModuleCount() * cellsize + padding * 2; - var context = canvas.getContext("2d"); - qr.renderTo2dContext(context, cellsize, padding); - downloadFileName = "qr." + format; - downloadType = ""; // raw download data URL - downloadData = canvas.toDataURL("image/" + format); - showLength(downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); - var warning = ""; - var m = downloadData.match(/^data:image\/([a-z0-9]+)/); - if (m && m[1] !== format) - warning = "

Attention: This browser cannot generate " + - format.toUpperCase() + " images and made a " + m[1].toUpperCase() + " instead."; - return '' + warning; - case "gif-direct": - downloadFileName = "qr.gif"; - downloadType = ""; // raw download data URL - downloadData = qr.createDataURL(cellsize, padding); - showLength(downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); - return qr.createImgTag(cellsize, padding); - case "ascii": - downloadFileName = "qr.txt"; - downloadType = "text/plain"; - downloadData = qr.createASCII(cellsize, padding); - showLength(downloadData.length); - return "
" + downloadData + "
"; - case "ascii-inv": - downloadFileName = "qr.txt"; - downloadType = "text/plain"; - downloadData = qr.createASCII(cellsize, padding, true); - showLength(downloadData.length); - return "
" + downloadData + "
"; - case "table": - downloadFileName = "qr.html"; - downloadType = "text/html"; - downloadData = qr.createTableTag(cellsize, padding); - showLength(downloadData.length); - return downloadData; - } - } - catch (error) { - info.innerHTML = "Error: " + error; - return ""; - } +var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrectionLevel, mode, mb, format, fg, bg) { + if (!mode) { + if (text.match(/^[0-9]+$/)) + mode = "Numeric"; + else if (text.match(/^[0-9A-Z $%*+/.:-]+$/)) + mode = "Alphanumeric"; + else + mode = "Byte"; + } + + let info = document.getElementById("qr-info"); + downloadData = undefined; + + var showLength = function (len) { + if (len < 1024) + len += " B"; + else if (len < 10240) + len = Math.round(len * 100 / 1024) / 100 + " KiB"; + else + len = Math.round(len * 10 / 1024) / 10 + " KiB"; + info.innerHTML += ", file size: " + len + ""; + }; + + try { + qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; + + let qr = qrcode(typeNumber || 0, errorCorrectionLevel || 'M'); + qr.addData(text, mode); + qr.make(); + qr.setColors(fg, bg); + + let size = qr.getModuleCount() * cellsize + padding * 2; + info.innerHTML = "Size type: " + qr.getTypeNumber() + ", " + + "code columns: " + qr.getModuleCount() + ", " + + "image size: " + size + "x" + size + " px"; + + switch (format) { + case "svg": + downloadFileName = "qr.svg"; + downloadType = "image/svg"; + downloadData = qr.createSvgTag(cellsize, padding); + showLength(downloadData.length); + return downloadData; + case "gif": + case "png": + case "webp": + case "jpeg": + var canvas = document.createElement("canvas"); + canvas.width = canvas.height = qr.getModuleCount() * cellsize + padding * 2; + var context = canvas.getContext("2d"); + qr.renderTo2dContext(context, cellsize, padding); + downloadFileName = "qr." + format; + downloadType = ""; // raw download data URL + downloadData = canvas.toDataURL("image/" + format); + showLength(downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); + var warning = ""; + var m = downloadData.match(/^data:image\/([a-z0-9]+)/); + if (m && m[1] !== format) + warning = "

Attention: This browser cannot generate " + + format.toUpperCase() + " images and made a " + m[1].toUpperCase() + " instead."; + return '' + warning; + case "gif-direct": + downloadFileName = "qr.gif"; + downloadType = ""; // raw download data URL + downloadData = qr.createDataURL(cellsize, padding); + showLength(downloadData.replace(/^data:image\/[a-z]+;base64,/, "").length * 3 / 4); + return qr.createImgTag(cellsize, padding); + case "ascii": + downloadFileName = "qr.txt"; + downloadType = "text/plain"; + downloadData = qr.createASCII(cellsize, padding); + showLength(downloadData.length); + return "
" + downloadData + "
"; + case "ascii-inv": + downloadFileName = "qr.txt"; + downloadType = "text/plain"; + downloadData = qr.createASCII(cellsize, padding, true); + showLength(downloadData.length); + return "
" + downloadData + "
"; + case "table": + downloadFileName = "qr.html"; + downloadType = "text/html"; + downloadData = qr.createTableTag(cellsize, padding); + showLength(downloadData.length); + return downloadData; + } + } + catch (error) { + info.innerHTML = "Error: " + error; + return ""; + } }; -var update_qrcode = function() { - var form = document.forms['qrForm']; - var text = form.elements['msg'].value.replace(/^[\s\u3000]+|[\s\u3000]+$/g, ''); - var t = form.elements['t'].value; - var cellsize = +form.elements['cellsize'].value; - var padding = +form.elements['padding'].value * cellsize; - var e = form.elements['e'].value; - var m = form.elements['m'].value; - var mb = form.elements['mb'].value; - var format = form.elements['format'].value; - var fg = form.elements['foreground'].value; - var bg = form.elements['background'].value; - document.getElementById("length").innerHTML = "(" + text.length + " characters)"; - document.getElementById('qr').innerHTML = create_qrcode(text, t, cellsize, padding, e, m, mb, format, fg, bg); +var update_qrcode = function () { + let form = document.forms['qrForm']; + let text = form.elements['msg'].value.replace(/^[\s\u3000]+|[\s\u3000]+$/g, ''); + let t = form.elements['t'].value; + let cellsize = +form.elements['cellsize'].value; + let padding = +form.elements['padding'].value * cellsize; + let e = form.elements['e'].value; + let m = form.elements['m'].value; + let mb = form.elements['mb'].value; + let format = form.elements['format'].value; + let fg = form.elements['foreground'].value; + let bg = form.elements['background'].value; + document.getElementById("length").innerHTML = "(" + text.length + " characters)"; + document.getElementById('qr').innerHTML = create_qrcode(text, t, cellsize, padding, e, m, mb, format, fg, bg); }; // Source: https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server function download(filename, mimeType, text) { - var element = document.createElement("a"); - if (downloadType) - element.setAttribute("href", "data:" + mimeType + ";charset=utf-8," + encodeURIComponent(text)); - else - element.setAttribute("href", text); - element.setAttribute("download", filename); - element.style.display = "none"; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); + let element = document.createElement("a"); + if (downloadType) + element.setAttribute("href", "data:" + mimeType + ";charset=utf-8," + encodeURIComponent(text)); + else + element.setAttribute("href", text); + element.setAttribute("download", filename); + element.style.display = "none"; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); } From 5bb2d93e10beacb919a1fff242012360e92256ca Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Thu, 3 Jun 2021 11:42:37 +0200 Subject: [PATCH 7/8] Optimised SVG shape by reducing points (another 60% size reduction) * The number of points in the SVG is minimised by connecting all squares in the most efficient way * Added minified source file with source map * New sample form options: trim text, select mask pattern * Mask patterns can be selected and evaluated for file size and visual "beauty" * Sample form is now fixed, list selections survive page reload in the browser --- README.md | 7 ++ js/README.md | 24 +++++ js/miniwebcompiler.json | 8 ++ js/qrcode.js | 222 +++++++++++++++++++++++++++++++++------- js/qrcode.min.js | 2 + js/qrcode.min.js.map | 1 + js/sample.html | 87 +++++++++++++--- js/sample.js | 50 +++++---- 8 files changed, 330 insertions(+), 71 deletions(-) create mode 100644 js/miniwebcompiler.json create mode 100644 js/qrcode.min.js create mode 100644 js/qrcode.min.js.map diff --git a/README.md b/README.md index 407cd69..73fa8e3 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,10 @@ All the implementations are based on JIS X 0510:1999. The word "QR Code" is registered trademark of DENSO WAVE INCORPORATED
http://www.denso-wave.com/qrcode/faqpatent-e.html + +---- + +This repository is a fork of the original with all programming languages. +It contains **only improvements for the JavaScript implementation**. +Please select the "improvements" branch to see the changes. +Other files have not been altered or regarded in any way. diff --git a/js/README.md b/js/README.md index f68c416..6b9cdd1 100644 --- a/js/README.md +++ b/js/README.md @@ -59,6 +59,10 @@ Make a QR Code. The number of modules(cells) for each orientation. _[Note] call make() before this function._ +#### getDarkCount() => number +The total number of modules(cells) that are dark in the final QR code. +_[Note] call make() before this function._ + #### isDark(row, col) => boolean The module at row and col is dark or not. _[Note] call make() before this function._ @@ -68,6 +72,26 @@ _[Note] call make() before this function._ | row | number | 0 ~ moduleCount - 1 | | col | number | 0 ~ moduleCount - 1 | +#### getTypeNumber() => number +Gets the code size type of the final QR code. +_[Note] call make() before this function._ + +#### getMaskPattern() => number +Gets the index of the mask pattern (0 to 7) that was used in the final QR code. +_[Note] call make() before this function._ + +#### getBestMaskPattern() => number +Gets the index of the mask pattern (0 to 7) that would be chosen for the QR code, based on its minimal penalty score. +_[Note] call make() before this function._ + +#### getMaskPatternPenaltyScores() => number[] +Gets the calculated penalty score for each mask pattern. Lower values make better QR codes. +_[Note] call make() before this function._ + +#### setMaskPattern(index) => void +Sets the index of the mask pattern (0 to 7) to use in the final QR code, regardless of which the best pattern would be. +Default to null, which selects the best pattern automatically. + #### setColors(foreground, background) => void Sets the foreground and background colors for the output formats. Default to black and white, respectively. _[Note] This has no effect on the ASCII output._ diff --git a/js/miniwebcompiler.json b/js/miniwebcompiler.json new file mode 100644 index 0000000..5312337 --- /dev/null +++ b/js/miniwebcompiler.json @@ -0,0 +1,8 @@ +{ + "projectName": "qrcode-generator", + "files": [ + { + "name": "qrcode.js" + } + ] +} diff --git a/js/qrcode.js b/js/qrcode.js index 3fe276a..5755d4d 100644 --- a/js/qrcode.js +++ b/js/qrcode.js @@ -3,6 +3,7 @@ // QR Code Generator for JavaScript // // Copyright (c) 2009 Kazuhiko Arase +// Improved 2020-2021 by Yves Goergen, ygoe.de // // URL: http://www.d-project.com/ // @@ -38,6 +39,10 @@ var qrcode = function () { let _dataList = []; let _foreground = "black"; let _background = "white"; + let _maskPatternOverride = null; + let _maskPattern; + let _bestMaskPattern; + let _maskPatternPenaltyScores = []; let _this = {}; var makeImpl = function (test, maskPattern) { @@ -90,19 +95,21 @@ var qrcode = function () { }; var getBestMaskPattern = function () { - let minLostPoint = 0; - let pattern = 0; + let minPenaltyScore = 0; + let bestPattern = 0; + _maskPatternPenaltyScores = []; for (let i = 0; i < 8; i++) { makeImpl(true, i); - let lostPoint = QRUtil.getLostPoint(_this); - if (i === 0 || minLostPoint > lostPoint) { - minLostPoint = lostPoint; - pattern = i; + let penaltyScore = QRUtil.getPenaltyScore(_this); + _maskPatternPenaltyScores.push(penaltyScore); + if (i === 0 || penaltyScore < minPenaltyScore) { + minPenaltyScore = penaltyScore; + bestPattern = i; } } - return pattern; + return bestPattern; }; var setupTimingPattern = function () { @@ -276,7 +283,6 @@ var qrcode = function () { let data = new Array(totalCodeCount); let index = 0; - for (let i = 0; i < maxDcCount; i++) { for (let r = 0; r < rsBlocks.length; r++) { if (i < dcdata[r].length) { @@ -346,7 +352,6 @@ var qrcode = function () { mode = mode || 'Byte'; let newData = null; - switch (mode) { case 'Numeric': newData = qrNumber(data); @@ -379,6 +384,17 @@ var qrcode = function () { return _moduleCount; }; + _this.getDarkCount = function () { + let count = 0; + for (let row = 0; row < _moduleCount; row++) { + for (let col = 0; col < _moduleCount; col++) { + if (_modules[row][col]) + count++; + } + } + return count; + }; + _this.make = function () { if (_typeNumber < 1) { let typeNumber = 1; @@ -407,13 +423,33 @@ var qrcode = function () { _typeNumber = typeNumber; } - makeImpl(false, getBestMaskPattern()); + _maskPattern = getBestMaskPattern(); + _bestMaskPattern = _maskPattern; + if (_maskPatternOverride !== null) + _maskPattern = _maskPatternOverride; + makeImpl(false, _maskPattern); }; _this.getTypeNumber = function () { return _typeNumber; }; + _this.getMaskPattern = function () { + return _maskPattern; + }; + + _this.getBestMaskPattern = function () { + return _bestMaskPattern; + }; + + _this.getMaskPatternPenaltyScores = function () { + return _maskPatternPenaltyScores; + }; + + _this.setMaskPattern = function (maskPattern) { + _maskPatternOverride = maskPattern; + }; + _this.setColors = function (foreground, background) { _foreground = simplifyColor(foreground); _background = simplifyColor(background); @@ -426,7 +462,6 @@ var qrcode = function () { }; _this.createTableTag = function (cellSize, margin) { - cellSize = cellSize || 2; margin = (typeof margin === 'undefined') ? cellSize * 4 : margin; @@ -479,8 +514,7 @@ var qrcode = function () { title.id = (title.text) ? title.id || 'qrcode-title' : null; let size = _this.getModuleCount() * cellSize + margin * 2; - let c, c2, mc, r, mr, qrSvg = '', rect; - + let qrSvg = ''; qrSvg += '' + escapeXml(alt.text) + '' : ''; qrSvg += ''; - qrSvg += ''; + let polygons = []; + while (edges.length > 0) { + // Pick a random edge and follow its connected edges to form a path (remove used edges) + // If there are multiple connected edges, pick the first + // Stop when the starting point of this path is reached + let polygon = []; + polygons.push(polygon); + let edge = edges.splice(0, 1)[0]; + polygon.push(edge[0]); + polygon.push(edge[1]); + do { + let foundEdge = false; + for (let i = 0; i < edges.length; i++) { + if (pointEquals(edges[i][0], edge[1])) { + // Found an edge that starts at the last edge's end + foundEdge = true; + edge = edges.splice(i, 1)[0]; + let p1 = polygon[polygon.length - 2]; // polygon's second-last point + let p2 = polygon[polygon.length - 1]; // polygon's current end + let p3 = edge[1]; // new point + // Extend polygon end if it's continuing in the same direction + if (p1[0] === p2[0] && // polygon ends vertical + p2[0] === p3[0]) { // new point is vertical, too + polygon[polygon.length - 1][1] = p3[1]; + } + else if (p1[1] === p2[1] && // polygon ends horizontal + p2[1] === p3[1]) { // new point is horizontal, too + polygon[polygon.length - 1][0] = p3[0]; + } + else { + polygon.push(p3); // new direction + } + break; + } + } + if (!foundEdge) + throw new Error("no next edge found at", edge[1]); + } + while (!pointEquals(polygon[polygon.length - 1], polygon[0])); + + // Move polygon's start and end point into a corner + if (polygon[0][0] === polygon[1][0] && + polygon[polygon.length - 2][0] === polygon[polygon.length - 1][0]) { + // start/end is along a vertical line + polygon.length--; + polygon[0][1] = polygon[polygon.length - 1][1]; + } + else if (polygon[0][1] === polygon[1][1] && + polygon[polygon.length - 2][1] === polygon[polygon.length - 1][1]) { + // start/end is along a horizontal line + polygon.length--; + polygon[0][0] = polygon[polygon.length - 1][0]; + } + } + // Repeat until there are no more unused edges + + // If two paths touch in at least one point, pick such a point and include one path in the other's sequence of points + for (let i = 0; i < polygons.length; i++) { + const polygon = polygons[i]; + for (let j = 0; j < polygon.length; j++) { + const point = polygon[j]; + for (let k = i + 1; k < polygons.length; k++) { + const polygon2 = polygons[k]; + for (let l = 0; l < polygon2.length - 1; l++) { // exclude end point (same as start) + const point2 = polygon2[l]; + if (pointEquals(point, point2)) { + // Embed polygon2 into polygon + if (l > 0) { + // Touching point is not other polygon's start/end + polygon.splice.apply(polygon, [j + 1, 0].concat( + polygon2.slice(1, l + 1))); + } + polygon.splice.apply(polygon, [j + 1, 0].concat( + polygon2.slice(l + 1))); + polygons.splice(k, 1); + k--; + break; + } + } + } + } + } + + // Generate SVG path data + let d = ""; + for (let i = 0; i < polygons.length; i++) { + const polygon = polygons[i]; + d += "M" + polygon[0][0] + "," + polygon[0][1]; + for (let j = 1; j < polygon.length; j++) { + if (polygon[j][0] === polygon[j - 1][0]) + d += "v" + (polygon[j][1] - polygon[j - 1][1]); + else + d += "h" + (polygon[j][0] - polygon[j - 1][0]); + } + d += "z"; + } + + qrSvg += ''; qrSvg += ''; return qrSvg; @@ -955,9 +1105,9 @@ var qrcode = function () { } }; - _this.getLostPoint = function (qrcode) { + _this.getPenaltyScore = function (qrcode) { let moduleCount = qrcode.getModuleCount(); - let lostPoint = 0; + let penaltyScore = 0; // LEVEL1 @@ -982,7 +1132,7 @@ var qrcode = function () { } if (sameCount > 5) { - lostPoint += (3 + sameCount - 5); + penaltyScore += (3 + sameCount - 5); } } }; @@ -997,7 +1147,7 @@ var qrcode = function () { if (qrcode.isDark(row, col + 1)) count++; if (qrcode.isDark(row + 1, col + 1)) count++; if (count === 0 || count === 4) { - lostPoint += 3; + penaltyScore += 3; } } } @@ -1013,7 +1163,7 @@ var qrcode = function () { qrcode.isDark(row, col + 4) && !qrcode.isDark(row, col + 5) && qrcode.isDark(row, col + 6)) { - lostPoint += 40; + penaltyScore += 40; } } } @@ -1027,7 +1177,7 @@ var qrcode = function () { qrcode.isDark(row + 4, col) && !qrcode.isDark(row + 5, col) && qrcode.isDark(row + 6, col)) { - lostPoint += 40; + penaltyScore += 40; } } } @@ -1045,8 +1195,8 @@ var qrcode = function () { } let ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; - lostPoint += ratio * 10; - return lostPoint; + penaltyScore += ratio * 10; + return penaltyScore; }; return _this; diff --git a/js/qrcode.min.js b/js/qrcode.min.js new file mode 100644 index 0000000..38dcf90 --- /dev/null +++ b/js/qrcode.min.js @@ -0,0 +1,2 @@ +var qrcode=function(){var t=function(t,e){let r,n,o=t,i=l[e],u=null,f=0,a=null,c=[],g="black",h="white",s=null,w=[],m={};var b=function(t,e){u=function(t){let e=new Array(t);for(let r=0;r=7&&D(t),null===a&&(a=P(o,i,c)),T(a,e)},L=function(t,e){for(let r=-1;r<=7;r++)if(!(t+r<=-1||t+r>=f))for(let n=-1;n<=7;n++)e+n<=-1||e+n>=f||(u[t+r][e+n]=0<=r&&r<=6&&(0===n||6===n)||0<=n&&n<=6&&(0===r||6===r)||2<=r&&r<=4&&2<=n&&n<=4)},v=function(){for(let t=8;t>r&1);u[Math.floor(r/3)][r%3+f-8-3]=n}for(let r=0;r<18;r++){let n=!t&&1==(e>>r&1);u[r%3+f-8-3][Math.floor(r/3)]=n}},I=function(t,e){let r=i<<3|e,n=d.getBCHTypeInfo(r);for(let e=0;e<15;e++){let r=!t&&1==(n>>e&1);e<6?u[e][8]=r:e<8?u[e+1][8]=r:u[f-15+e][8]=r}for(let e=0;e<15;e++){let r=!t&&1==(n>>e&1);e<8?u[8][f-e-1]=r:e<9?u[8][15-e-1+1]=r:u[8][15-e-1]=r}u[f-8][8]=!t},T=function(t,e){let r=-1,n=f-1,o=7,l=0,i=d.getMaskFunction(e);for(let e=f-1;e>0;e-=2)for(6===e&&(e-=1);;){for(let r=0;r<2;r++)if(null===u[n][e-r]){let f=!1;l>>o&1)),i(n,e-r)&&(f=!f),u[n][e-r]=f,-1===(o-=1)&&(l++,o=7)}if((n+=r)<0||n>=f){n-=r,r=-r;break}}},P=function(t,e,r){let n=y.getRSBlocks(t,e),o=B();for(let e=0;e8*l)throw"code length overflow. ("+o.getLengthInBits()+">"+8*l+")";for(o.getLengthInBits()+4<=8*l&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=8*l||(o.put(236,8),o.getLengthInBits()>=8*l));)o.put(17,8);return function(t,e){let r=0,n=0,o=0,l=new Array(e.length),i=new Array(e.length);for(let u=0;u=0?g.getAt(e):0}}let u=0;for(let t=0;t=f||e<0||e>=f)throw t+","+e;return u[t][e]},m.getModuleCount=function(){return f},m.getDarkCount=function(){let t=0;for(let e=0;e";for(let n=0;n'+F(n.text)+"":"",i+=r.text?''+F(r.text)+"":"",i+='';const u=m.getModuleCount(),f=function(t,e){return t[0]===e[0]&&t[1]===e[1]},a=[];for(let r=0;r=0;t--)for(let e=t-1;e>=0;e--)if(f(a[t][0],a[e][1])&&f(a[t][1],a[e][0])){a.splice(t,1),a.splice(e,1),t--;break}let c=[];for(;a.length>0;){let t=[];c.push(t);let e=a.splice(0,1)[0];t.push(e[0]),t.push(e[1]);do{let r=!1;for(let n=0;n0&&e.splice.apply(e,[r+1,0].concat(t.slice(1,l+1))),e.splice.apply(e,[r+1,0].concat(t.slice(l+1))),c.splice(o,1),o--;break}}}}}let s="";for(let t=0;t',i+=""},m.createDataURL=function(t,e){t=t||2,e=void 0===e?4*t:e;let r=m.getModuleCount()*t+2*e,n=e,o=r-e;return x(r,r,g,h,function(e,r){if(n<=e&&e":e+=">";break;case"&":e+="&";break;case'"':e+=""";break;default:e+=n}}return e};return m.createASCII=function(t,e,r){if((t=t||1)<2)return function(t,e){t=void 0===t?2:t;let r,n,o,l,i,u=1*m.getModuleCount()+2*t,f=t,a=u-t,c={"██":"█","█ ":"▀"," █":"▄"," ":" "},g={"██":"▀","█ ":"▀"," █":" "," ":" "},h="";for(r=0;r=a?g[i]:c[i];h+="\n"}return u%2&&t>0?h.substring(0,h.length-u-1)+Array(u+1).join(e?"▀":" "):h.substring(0,h.length-1)}(e,r);t-=1,e=void 0===e?2*t:e/(t+1)*t;let n,o,l,i,u=m.getModuleCount()*t+2*e,f=e,a=u-e,c="  ",g="██";if(r){let t=g;g=c,c=t}let h="",s="";for(n=0;n0&&(t.fillStyle=h,t.fillRect(0,0,n*e+2*r,n*e+2*r));for(let o=0;o>>8),e.push(255&l)):e.push(n)}}return e}};var e=1,r=2,n=4,o=8,l={L:1,M:0,Q:3,H:2},i=0,u=1,f=2,a=3,c=4,g=5,h=6,s=7,d=function(){let t=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],l={};var d=function(t){let e=0;for(;0!==t;)e++,t>>>=1;return e};return l.getBCHTypeInfo=function(t){let e=t<<10;for(;d(e)-d(1335)>=0;)e^=1335<=0;)e^=7973<5&&(r+=3+l-5)}for(let n=0;n=256;)e-=255;return t[e]}};return r}();function p(t,e){if(void 0===t.length)throw t.length+"/"+e;let r=function(){let r=0;for(;r>>7-e%8&1)},put:function(t,e){for(let n=0;n>>e-n-1&1))},getLengthInBits:function(){return e},putBit:function(r){let n=Math.floor(e/8);t.length<=n&&t.push(0),r&&(t[n]|=128>>>e%8),e++}};return r},k=function(t){let r=e,n=t,o={getMode:function(){return r},getLength:function(t){return n.length},write:function(t){let e=n,r=0;for(;r+2>>8&255)+(255&n),t.put(n,13),r+=2}if(r>>8)},writeBytes:function(t,r,n){r=r||0,n=n||t.length;for(let o=0;o0&&(e+=","),e+=t[r];return e+="]"}};return e},b=function(t){let e=t,r=0,n=0,o=0,l={read:function(){for(;o<8;){if(r>=e.length){if(0===o)return-1;throw"unexpected end of file./"+o}let t=e.charAt(r);if(r++,"="===t)return o=0,-1;t.match(/^\s$/)||(n=n<<6|i(t.charCodeAt(0)),o+=6)}let t=n>>>o-8&255;return o-=8,t}};var i=function(t){if(65<=t&&t<=90)return t-65;if(97<=t&&t<=122)return t-97+26;if(48<=t&&t<=57)return t-48+52;if(43===t)return 62;if(47===t)return 63;throw"c:"+t};return l},x=function(t,e,r,n,o){let l=function(t,e,r,n){let o=t,l=e,i=new Array(t*e),u={};function f(t,e){return(t=t.toLowerCase()).match(/^#[0-9a-f]{3}$/)?[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]:t.match(/^#[0-9a-f]{6}$/)?[parseInt(t[1]+t[2],16),parseInt(t[3]+t[4],16),parseInt(t[5]+t[6],16)]:f(e)}u.setPixel=function(t,e,r){i[e*o+t]=r},u.write=function(t){t.writeString("GIF87a"),t.writeShort(o),t.writeShort(l),t.writeByte(128),t.writeByte(0),t.writeByte(0);let e=f(r,"#000");t.writeByte(e[0]),t.writeByte(e[1]),t.writeByte(e[2]);let i=f(n,"#fff");t.writeByte(i[0]),t.writeByte(i[1]),t.writeByte(i[2]),t.writeString(","),t.writeShort(0),t.writeShort(0),t.writeShort(o),t.writeShort(l),t.writeByte(0);let u=a(2);t.writeByte(2);let c=0;for(;u.length-c>255;)t.writeByte(255),t.writeBytes(u,c,255),c+=255;t.writeByte(u.length-c),t.writeBytes(u,c,u.length-c),t.writeByte(0),t.writeString(";")};var a=function(t){let e=1<>>o!=0)throw"length over";for(;r+o>=8;)e.writeByte(255&(t<>>=8-r,n=0,r=0;n|=t<0&&e.writeByte(n)}};return o}(l);u.write(e,n);let f=0,a=String.fromCharCode(i[f]);for(f++;f=6;)l(t>>>e-6),e-=6},o.flush=function(){if(e>0&&(l(t<<6-e),t=0,e=0),r%3!=0){let t=3-r%3;for(let e=0;e>6,128|63&n):n<55296||n>=57344?e.push(224|n>>12,128|n>>6&63,128|63&n):(r++,n=65536+((1023&n)<<10|1023&t.charCodeAt(r)),e.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return e}(t)},function(t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports&&(module.exports=t())}(function(){return qrcode}); +//# sourceMappingURL=qrcode.min.js.map \ No newline at end of file diff --git a/js/qrcode.min.js.map b/js/qrcode.min.js.map new file mode 100644 index 0000000..26a9252 --- /dev/null +++ b/js/qrcode.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["qrcode.js"],"names":["qrcode","typeNumber","errorCorrectionLevel","_maskPattern","_bestMaskPattern","_typeNumber","_errorCorrectionLevel","QRErrorCorrectionLevel","_modules","_moduleCount","_dataCache","_dataList","_foreground","_background","_maskPatternOverride","_maskPatternPenaltyScores","_this","makeImpl","test","maskPattern","moduleCount","modules","Array","row","col","setupPositionProbePattern","setupPositionAdjustPattern","setupTimingPattern","setupTypeInfo","setupTypeNumber","createData","mapData","r","c","pos","QRUtil","getPatternPosition","i","length","j","bits","getBCHTypeNumber","mod","Math","floor","data","getBCHTypeInfo","inc","bitIndex","byteIndex","maskFunc","getMaskFunction","dark","dataList","rsBlocks","QRRSBlock","getRSBlocks","buffer","qrBitBuffer","put","getMode","getLength","getLengthInBits","write","totalDataCount","dataCount","putBit","offset","maxDcCount","maxEcCount","dcdata","ecdata","dcCount","ecCount","totalCount","max","getBuffer","rsPoly","getErrorCorrectPolynomial","modPoly","qrPolynomial","modIndex","getAt","totalCodeCount","index","createBytes","addData","mode","newData","qrNumber","qrAlphaNum","qr8BitByte","qrKanji","push","isDark","getModuleCount","getDarkCount","count","make","minPenaltyScore","bestPattern","penaltyScore","getPenaltyScore","getBestMaskPattern","getTypeNumber","getMaskPattern","getMaskPatternPenaltyScores","setMaskPattern","setColors","foreground","background","simplifyColor","match","createTableTag","cellSize","margin","qrHtml","createSvgTag","alt","title","opts","arguments","text","id","size","qrSvg","scalable","escapeXml","join","trim","pointEquals","a","b","edges","x0","y0","x1","y1","splice","polygons","polygon","edge","foundEdge","p1","p2","p3","Error","point","k","polygon2","l","apply","concat","slice","d","createDataURL","min","x","y","createImgTag","img","s","escaped","charAt","createASCII","inverted","r1","r2","p","blocks","\u2588\u2588","\u2588 "," \u2588"," ","blocksLastLineNoMargin","ascii","substring","_createHalfASCII","white","black","line","renderTo2dContext","context","fillStyle","fillRect","stringToBytes","stringToBytesFuncs","default","bytes","charCodeAt","createStringToBytes","unicodeData","numChars","unicodeMap","bin","base64DecodeInputStream","read","b0","b1","v","String","fromCharCode","unknownChar","QRMode","L","M","Q","H","QRMaskPattern","PATTERN_POSITION_TABLE","getBCHDigit","digit","errorCorrectLength","multiply","QRMath","gexp","type","sameCount","darkCount","abs","EXP_TABLE","LOG_TABLE","glog","n","num","shift","_num","e","ratio","RS_BLOCK_TABLE","qrRSBlock","rsBlock","getRsBlockTable","list","_buffer","_length","bufIndex","bit","_mode","_data","strToNum","charToNum","getCode","_bytes","code","byteArrayOutputStream","writeByte","writeShort","writeBytes","off","len","writeString","toByteArray","toString","str","_str","_pos","_buflen","decode","width","height","getPixel","gif","_width","_height","parseColor","fallback","toLowerCase","parseInt","setPixel","pixel","out","fg","bg","raster","getLZWRaster","lzwMinCodeSize","clearCode","endCode","bitLength","table","lzwTable","add","byteOut","bitOut","_out","_bitLength","_bitBuffer","flush","bitOutputStream","dataIndex","contains","indexOf","_map","_size","key","gifImage","base64","_base64","writeEncoded","encode","padlen","base64EncodeOutputStream","utf8","charcode","toUTF8Array","factory","define","amd","exports","module"],"mappings":"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"} \ No newline at end of file diff --git a/js/sample.html b/js/sample.html index 90ec81e..5c6a484 100644 --- a/js/sample.html +++ b/js/sample.html @@ -3,15 +3,10 @@ - + - - - + + QR Code Generator for JavaScript @@ -22,6 +17,8 @@

QR Code Generator for JavaScript


+ +  

@@ -30,14 +27,6 @@

QR Code Generator for JavaScript

  Padding: -   - Error correction level: -

Image type: @@ -77,7 +66,71 @@

QR Code Generator for JavaScript

  Size type: - + +

+

+ Error correction level: + +   + Mask pattern: +

diff --git a/js/sample.js b/js/sample.js index 111f5b4..7a71869 100644 --- a/js/sample.js +++ b/js/sample.js @@ -1,21 +1,8 @@ var downloadFileName, downloadType, downloadData; var body_loadHander = function () { - var crtOpt = function (value, label) { - let opt = document.createElement('option'); - opt.appendChild(document.createTextNode(label)); - opt.value = value; - return opt; - }; - - let t = document.forms['qrForm'].elements['t']; - t.appendChild(crtOpt('' + 0, 'Auto')); - for (let i = 1; i <= 40; i += 1) { - t.appendChild(crtOpt('' + i, '' + i)); - } - t.value = '0'; - let form = document.forms['qrForm']; + form.elements['trim'].addEventListener("click", update_qrcode); form.elements['msg'].addEventListener("input", update_qrcode); form.elements['t'].addEventListener("change", update_qrcode); form.elements['cellsize'].addEventListener("input", update_qrcode); @@ -26,6 +13,7 @@ var body_loadHander = function () { form.elements['format'].addEventListener("change", update_qrcode); form.elements['foreground'].addEventListener("change", update_qrcode); form.elements['background'].addEventListener("change", update_qrcode); + form.elements['mask'].addEventListener("change", update_qrcode); update_qrcode(); document.getElementById("download").addEventListener("click", function () { @@ -38,7 +26,7 @@ var draw_qrcode = function (text, typeNumber, errorCorrectionLevel) { document.write(create_qrcode(text, typeNumber, errorCorrectionLevel)); }; -var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrectionLevel, mode, mb, format, fg, bg) { +var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrectionLevel, mode, mb, format, fg, bg, mask) { if (!mode) { if (text.match(/^[0-9]+$/)) mode = "Numeric"; @@ -65,14 +53,35 @@ var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrecti qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb]; let qr = qrcode(typeNumber || 0, errorCorrectionLevel || 'M'); + if (mask > 0) + qr.setMaskPattern(mask - 1); qr.addData(text, mode); qr.make(); qr.setColors(fg, bg); + let maskPattern = qr.getMaskPattern(); + let bestMaskPattern = qr.getBestMaskPattern(); + let scores = qr.getMaskPatternPenaltyScores(); + let minScore = Math.min.apply(null, scores); + let maskField = document.forms['qrForm'].elements['mask']; + for (let i = 0; i <= 8; i++) { + if (i === 0) { + maskField.children[i].textContent = "Best (" + (bestMaskPattern + 1) + ")"; + } + else if (scores[i - 1] === minScore) { + maskField.children[i].textContent = i + " (best)"; + } + else { + let worse = scores[i - 1] / minScore - 1; + maskField.children[i].textContent = i + " (" + (Math.round(worse * 1000) / 10) + "% worse)"; + } + } + let size = qr.getModuleCount() * cellsize + padding * 2; info.innerHTML = "Size type: " + qr.getTypeNumber() + ", " + "code columns: " + qr.getModuleCount() + ", " + - "image size: " + size + "x" + size + " px"; + "filled blocks: " + qr.getDarkCount() + " (" + Math.round(qr.getDarkCount() / (qr.getModuleCount() * qr.getModuleCount()) * 100) + "%)
" + + "Image size: " + size + "x" + size + " px"; switch (format) { case "svg": @@ -126,6 +135,7 @@ var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrecti } } catch (error) { + console.error(error); info.innerHTML = "Error: " + error; return ""; } @@ -133,7 +143,10 @@ var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrecti var update_qrcode = function () { let form = document.forms['qrForm']; - let text = form.elements['msg'].value.replace(/^[\s\u3000]+|[\s\u3000]+$/g, ''); + let trim = form.elements['trim'].checked; + let text = form.elements['msg'].value; + if (trim) + text = text.replace(/^[\s\u3000]+|[\s\u3000]+$/g, ''); // U+3000 Ideographic Space let t = form.elements['t'].value; let cellsize = +form.elements['cellsize'].value; let padding = +form.elements['padding'].value * cellsize; @@ -143,8 +156,9 @@ var update_qrcode = function () { let format = form.elements['format'].value; let fg = form.elements['foreground'].value; let bg = form.elements['background'].value; + let mask = form.elements['mask'].value; document.getElementById("length").innerHTML = "(" + text.length + " characters)"; - document.getElementById('qr').innerHTML = create_qrcode(text, t, cellsize, padding, e, m, mb, format, fg, bg); + document.getElementById('qr').innerHTML = create_qrcode(text, t, cellsize, padding, e, m, mb, format, fg, bg, mask); }; // Source: https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server From 985860d3e6c42b5d174132a4ecce4a8c0c88f88f Mon Sep 17 00:00:00 2001 From: Yves Goergen Date: Mon, 13 Nov 2023 21:03:42 +0100 Subject: [PATCH 8/8] Always use default monospace font for ASCII output to prevent misalignment from styled fonts set by CSS --- js/sample.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/sample.js b/js/sample.js index 7a71869..a46c974 100644 --- a/js/sample.js +++ b/js/sample.js @@ -119,13 +119,13 @@ var create_qrcode = function (text, typeNumber, cellsize, padding, errorCorrecti downloadType = "text/plain"; downloadData = qr.createASCII(cellsize, padding); showLength(downloadData.length); - return "

" + downloadData + "
"; + return "
" + downloadData + "
"; case "ascii-inv": downloadFileName = "qr.txt"; downloadType = "text/plain"; downloadData = qr.createASCII(cellsize, padding, true); showLength(downloadData.length); - return "
" + downloadData + "
"; + return "
" + downloadData + "
"; case "table": downloadFileName = "qr.html"; downloadType = "text/html";