├── .gitignore ├── .spmignore ├── .travis.yml ├── HISTORY.md ├── Makefile ├── README.md ├── dist └── arale-qrcode │ └── 3.0.5 │ └── index.js ├── examples └── index.md ├── index.js ├── lib ├── index.js └── qrcodealg.js ├── package.json ├── src ├── index.js └── qrcodealg.js └── tests └── qrcode-spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/ 3 | .ipr 4 | .iws 5 | *~ 6 | ~* 7 | *.diff 8 | *.patch 9 | *.bak 10 | .DS_Store 11 | Thumbs.db 12 | .project 13 | .*proj 14 | *.swp 15 | *.swo 16 | *.pyc 17 | *.pyo 18 | build 19 | node_modules 20 | spm_modules/ 21 | _site 22 | sea-modules 23 | .cache 24 | npm-debug.log 25 | -------------------------------------------------------------------------------- /.spmignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | spm_modules/ 4 | _site/ 5 | examples/ 6 | tests/ 7 | .gitignore 8 | .git 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.10" 5 | 6 | install: 7 | - npm install spm@ninja coveralls 8 | 9 | before_script: 10 | - node_modules/spm/bin/spm-install 11 | 12 | script: 13 | - node_modules/spm/bin/spm-test 14 | 15 | after_success: 16 | - node_modules/spm/bin/spm-test --coveralls | node_modules/.bin/coveralls 17 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | --- 4 | ## 3.0.5 5 | 6 | `tag:fixed` canvas 模式在 Retina 下显示模糊 7 | 8 | ## 3.0.4 9 | 10 | `tag:new` 支付script引用的变量挂载 11 | 12 | ## 3.0.1 13 | 14 | `tag:fixed` 预编译es6语法,解决被require时打包问题 15 | 16 | ## 3.0.0 17 | 18 | `tag:fixed` svg不能修改大小bug 19 | 20 | `tag:improved` 配置将width、height简化为size 21 | 22 | ## 2.1.0 23 | 24 | `tag:improved` 去掉对jQuery的依赖 25 | 26 | ## 2.0.0 27 | 28 | `tag:new` 增加绘制图片功能,增加支持三角颜色配置,升到spm3.6.9规范 29 | 30 | ## 1.1.0 31 | 32 | `tag:improved` 升级到 spm@3.x 规范。 33 | 34 | ## 1.0.3 35 | 36 | `tag:improved` 性能优化 37 | 38 | ## 1.0.2 39 | 40 | `tag:improved` 性能优化 41 | 42 | ## 1.0.1 43 | 44 | `tag:improved` 优化代码 45 | 46 | ## 1.0.0 47 | 48 | 第一个版本 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = spec 2 | 3 | install: 4 | rm -fr _theme 5 | git clone git://github.com/aralejs/nico-arale.git _theme 6 | ./node_modules/.bin/nico build --theme _theme -C _theme/nico.js 7 | 8 | test: install 9 | ./node_modules/.bin/mocha-browser _site/tests/runner.html -S --reporter $(REPORTER) 10 | 11 | test-cov: 12 | ./node_modules/.bin/jscoverage src _site/src-cov 13 | ./node_modules/.bin/mocha-browser _site/tests/runner.html?cov -S -R html-cov > coverage.html 14 | 15 | .PHONY: install test test-cov 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qrcode 2 | 3 | --- 4 | 5 | [![spm package](http://spmjs.io/badge/arale-qrcode)](http://spmjs.io/package/arale-qrcode) 6 | [![Build Status](https://travis-ci.org/aralejs/qrcode.png)](https://travis-ci.org/aralejs/qrcode) 7 | [![Coverage Status](https://coveralls.io/repos/aralejs/qrcode/badge.png?branch=master)](https://coveralls.io/r/aralejs/qrcode) 8 | 9 | 二维码组件,用于绘制二维码。 10 | 11 | 已添加UTF-8编码,中文生成的二维码扫描不会出现乱码。 12 | 13 | [二维码编码原理](http://www.thonky.com/qr-code-tutorial/) 14 | 15 | --- 16 | 17 | ## 配置说明 18 | 19 | ### render `string` 20 | 21 | 配置用哪个节点元素画二维码,选项有`table`、`svg`和`canvas` 22 | 23 | 默认的选择顺序为 `canvas` -> `svg` -> `table` 24 | 25 | ### text `string` 26 | 27 | 要编码的字符串 28 | 29 | 默认:`""` 30 | 31 | ### size `number` 32 | 33 | 二维码的宽和高,单位是px,只允许生成正方形二维码 34 | 35 | 需要注意的是,当使用table绘制二维码时,会适当减小,使得能够整除二维码矩阵的维度。 36 | 37 | 默认:`256` 38 | 39 | ### correctLevel `number` 40 | 41 | 纠错级别,可取0、1、2、3,数字越大说明所需纠错级别越大 42 | 43 | 默认:`3` 44 | 45 | ### background `color` 46 | 47 | 背景色 48 | 49 | 默认:`#FFFFFF` 50 | 51 | ### foreground `color` 52 | 53 | 前景色 54 | 55 | 默认:`#000000` 56 | 57 | ### pdground `color` 58 | 59 | 三个角的颜色 60 | 61 | 默认:foreground 62 | 63 | ### image `string` 64 | 65 | 码正中间图片的url,只支持配置正方形图片 66 | 67 | 默认:`''` 68 | 69 | ### imageSize `number` 70 | 71 | image的宽和高,单位px 72 | 73 | 默认:`30` 74 | 75 | 76 | ## other 77 | 78 | 支持script标签引用,暴露全局变量为 AraleQRCode 79 | 80 | ``` 81 | 82 | 85 | ``` -------------------------------------------------------------------------------- /dist/arale-qrcode/3.0.5/index.js: -------------------------------------------------------------------------------- 1 | this.AraleQRCode=function(t){function e(o){if(r[o])return r[o].exports;var i=r[o]={exports:{},id:o,loaded:!1};return t[o].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){t.exports=r(1)},function(t,e,r){"use strict";t.exports=r(2)},function(t,e,r){"use strict";var o=r(4),i=[],n=r(3),s=function(t){var e=t.options;return e.pdground&&(t.row>1&&t.row<5&&t.col>1&&t.col<5||t.row>t.count-6&&t.row1&&t.col<5||t.row>1&&t.row<5&&t.col>t.count-6&&t.colr;r++)if(i[r].text==this.options.text&&i[r].text.correctLevel==this.options.correctLevel){e=i[r].obj;break}if(r==s&&(e=new n(this.options.text,this.options.correctLevel),i.push({text:this.options.text,correctLevel:this.options.correctLevel,obj:e})),this.options.render)switch(this.options.render){case"canvas":return this.createCanvas(e);case"table":return this.createTable(e);case"svg":return this.createSVG(e);default:return this.createDefault(e)}return this.createDefault(e)};o(a.prototype,{createDefault:function(t){var e=document.createElement("canvas");if(e.getContext)return this.createCanvas(t);var r="http://www.w3.org/2000/svg";return document.createElementNS&&document.createElementNS(r,"svg").createSVGRect?this.createSVG(t):this.createTable(t)},createCanvas:function(t){var e=this.options,r=document.createElement("canvas"),o=r.getContext("2d"),i=t.getModuleCount(),n=u(o),a=e.size,l=a*n,h=e.imageSize*n,g=function(t,e){var r=new Image;r.src=t,r.onload=function(){e(this),r.onload=null}},c=(l/i).toPrecision(4),f=(l/i).toPrecision(4);r.width=l,r.height=l;for(var d=0;i>d;d++)for(var m=0;i>m;m++){var p=Math.ceil((m+1)*c)-Math.floor(m*c),v=Math.ceil((d+1)*c)-Math.floor(d*c),b=s({row:d,col:m,count:i,options:e});o.fillStyle=t.modules[d][m]?b:e.background,o.fillRect(Math.round(m*c),Math.round(d*f),p,v)}return e.image&&g(e.image,function(t){var e=((l-h)/2).toFixed(2),r=((l-h)/2).toFixed(2);o.drawImage(t,e,r,h,h)}),r.style.width=a+"px",r.style.height=a+"px",r},createTable:function(t){var e=this.options,r=t.getModuleCount(),o=Math.floor(e.size/r),i=Math.floor(e.size/r);0>=o&&(o=80>r?2:1),0>=i&&(i=80>r?2:1);var n=[];n.push('');for(var u=0;r>u;u++){n.push('');for(var a=0;r>a;a++){var l=s({row:u,col:a,count:r,options:e});t.modules[u][a]?n.push(''):n.push('')}n.push("")}if(n.push("
"),e.image){var h=o*r,g=i*r,c=((h-e.imageSize)/2).toFixed(2),f=((g-e.imageSize)/2).toFixed(2);n.unshift("
"),n.push(""),n.push("
")}var d=document.createElement("span");return d.innerHTML=n.join(""),d.firstChild},createSVG:function(t){var e=this.options,r=t.getModuleCount(),o=r/e.size,i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.setAttribute("width",e.size),i.setAttribute("height",e.size),i.setAttribute("viewBox","0 0 "+r+" "+r);for(var n=0;r>n;n++)for(var u=0;r>u;u++){var a=document.createElementNS("http://www.w3.org/2000/svg","rect"),l=s({row:n,col:u,count:r,options:e});a.setAttribute("x",u),a.setAttribute("y",n),a.setAttribute("width",1),a.setAttribute("height",1),a.setAttribute("stroke-width",0),t.modules[n][u]?a.setAttribute("fill",l):a.setAttribute("fill",e.background),i.appendChild(a)}if(e.image){var h=document.createElementNS("http://www.w3.org/2000/svg","image");h.setAttributeNS("http://www.w3.org/1999/xlink","href",e.image),h.setAttribute("x",((r-e.imageSize*o)/2).toFixed(2)),h.setAttribute("y",((r-e.imageSize*o)/2).toFixed(2)),h.setAttribute("width",e.imageSize*o),h.setAttribute("height",e.imageSize*o),i.appendChild(h)}return i}}),t.exports=a},function(t,e){"use strict";function r(t){var e,r,o;return 128>t?[t]:2048>t?(e=192+(t>>6),r=128+(63&t),[e,r]):(e=224+(t>>12),r=128+(t>>6&63),o=128+(63&t),[e,r,o])}function o(t){for(var e=[],o=0;o=7&&this.setupTypeNumber(!0),this.mapData(this.dataCache,t)},setupPositionProbePattern:function(t,e){for(var r=-1;7>=r;r++)if(!(-1>=t+r||this.moduleCount<=t+r))for(var o=-1;7>=o;o++)-1>=e+o||this.moduleCount<=e+o||(r>=0&&6>=r&&(0==o||6==o)||o>=0&&6>=o&&(0==r||6==r)||r>=2&&4>=r&&o>=2&&4>=o?this.modules[t+r][e+o]=!0:this.modules[t+r][e+o]=!1)},createQrcode:function(){for(var t=0,e=0,r=null,o=0;8>o;o++){this.makeImpl(o);var i=l.getLostPoint(this);(0==o||t>i)&&(t=i,e=o,r=this.modules)}this.modules=r,this.setupTypeInfo(!1,e),this.typeNumber>=7&&this.setupTypeNumber(!1)},setupTimingPattern:function(){for(var t=8;t=n;n++)for(var s=-2;2>=s;s++)-2==n||2==n||-2==s||2==s||0==n&&0==s?this.modules[o+n][i+s]=!0:this.modules[o+n][i+s]=!1}},setupTypeNumber:function(t){for(var e=l.getBCHTypeNumber(this.typeNumber),r=0;18>r;r++){var o=!t&&1==(e>>r&1);this.modules[Math.floor(r/3)][r%3+this.moduleCount-8-3]=o,this.modules[r%3+this.moduleCount-8-3][Math.floor(r/3)]=o}},setupTypeInfo:function(t,e){for(var r=u[this.errorCorrectLevel]<<3|e,o=l.getBCHTypeInfo(r),i=0;15>i;i++){var n=!t&&1==(o>>i&1);6>i?this.modules[i][8]=n:8>i?this.modules[i+1][8]=n:this.modules[this.moduleCount-15+i][8]=n;var n=!t&&1==(o>>i&1);8>i?this.modules[8][this.moduleCount-i-1]=n:9>i?this.modules[8][15-i-1+1]=n:this.modules[8][15-i-1]=n}this.modules[this.moduleCount-8][8]=!t},createData:function(){var t=new s,e=this.typeNumber>9?16:8;t.put(4,4),t.put(this.utf8bytes.length,e);for(var r=0,o=this.utf8bytes.length;o>r;r++)t.put(this.utf8bytes[r],8);for(t.length+4<=8*this.totalDataCount&&t.put(0,4);t.length%8!=0;)t.putBit(!1);for(;;){if(t.length>=8*this.totalDataCount)break;if(t.put(i.PAD0,8),t.length>=8*this.totalDataCount)break;t.put(i.PAD1,8)}return this.createBytes(t)},createBytes:function(t){for(var e=0,r=0,o=0,i=this.rsBlock.length/3,s=new Array,u=0;i>u;u++)for(var a=this.rsBlock[3*u+0],h=this.rsBlock[3*u+1],g=this.rsBlock[3*u+2],c=0;a>c;c++)s.push([g,h]);for(var f=new Array(s.length),d=new Array(s.length),m=0;m=0?A.get(T):0}}for(var P=new Array(this.totalDataCount),y=0,u=0;r>u;u++)for(var m=0;mu;u++)for(var m=0;m0;s-=2)for(6==s&&s--;;){for(var u=0;2>u;u++)if(null==this.modules[o][s-u]){var a=!1;n>>i&1));var h=l.getMask(e,o,s-u);h&&(a=!a),this.modules[o][s-u]=a,i--,-1==i&&(n++,i=7)}if(o+=r,0>o||this.moduleCount<=o){o-=r,r=-r;break}}}},i.PAD0=236,i.PAD1=17;for(var u=[1,0,3,2],a={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},l={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]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(t){for(var e=t<<10;l.getBCHDigit(e)-l.getBCHDigit(l.G15)>=0;)e^=l.G15<=0;)e^=l.G18<>>=1;return e},getPatternPosition:function(t){return l.PATTERN_POSITION_TABLE[t-1]},getMask:function(t,e,r){switch(t){case a.PATTERN000:return(e+r)%2==0;case a.PATTERN001:return e%2==0;case a.PATTERN010:return r%3==0;case a.PATTERN011:return(e+r)%3==0;case a.PATTERN100:return(Math.floor(e/2)+Math.floor(r/3))%2==0;case a.PATTERN101:return e*r%2+e*r%3==0;case a.PATTERN110:return(e*r%2+e*r%3)%2==0;case a.PATTERN111:return(e*r%3+(e+r)%2)%2==0;default:throw new Error("bad maskPattern:"+t)}},getErrorCorrectPolynomial:function(t){for(var e=new n([1],0),r=0;t>r;r++)e=e.multiply(new n([1,h.gexp(r)],0));return e},getLostPoint:function(t){for(var e=t.getModuleCount(),r=0,o=0,i=0;e>i;i++)for(var n=0,s=t.modules[i][0],u=0;e>u;u++){var a=t.modules[i][u];if(e-6>u&&a&&!t.modules[i][u+1]&&t.modules[i][u+2]&&t.modules[i][u+3]&&t.modules[i][u+4]&&!t.modules[i][u+5]&&t.modules[i][u+6]&&(e-10>u?t.modules[i][u+7]&&t.modules[i][u+8]&&t.modules[i][u+9]&&t.modules[i][u+10]&&(r+=40):u>3&&t.modules[i][u-1]&&t.modules[i][u-2]&&t.modules[i][u-3]&&t.modules[i][u-4]&&(r+=40)),e-1>i&&e-1>u){var l=0;a&&l++,t.modules[i+1][u]&&l++,t.modules[i][u+1]&&l++,t.modules[i+1][u+1]&&l++,(0==l||4==l)&&(r+=3)}s^a?n++:(s=a,n>=5&&(r+=3+n-5),n=1),a&&o++}for(var u=0;e>u;u++)for(var n=0,s=t.modules[0][u],i=0;e>i;i++){var a=t.modules[i][u];e-6>i&&a&&!t.modules[i+1][u]&&t.modules[i+2][u]&&t.modules[i+3][u]&&t.modules[i+4][u]&&!t.modules[i+5][u]&&t.modules[i+6][u]&&(e-10>i?t.modules[i+7][u]&&t.modules[i+8][u]&&t.modules[i+9][u]&&t.modules[i+10][u]&&(r+=40):i>3&&t.modules[i-1][u]&&t.modules[i-2][u]&&t.modules[i-3][u]&&t.modules[i-4][u]&&(r+=40)),s^a?n++:(s=a,n>=5&&(r+=3+n-5),n=1)}var h=Math.abs(100*o/e/e-50)/5;return r+=10*h}},h={glog:function(t){if(1>t)throw new Error("glog("+t+")");return h.LOG_TABLE[t]},gexp:function(t){for(;0>t;)t+=255;for(;t>=256;)t-=255;return h.EXP_TABLE[t]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},g=0;8>g;g++)h.EXP_TABLE[g]=1<g;g++)h.EXP_TABLE[g]=h.EXP_TABLE[g-4]^h.EXP_TABLE[g-5]^h.EXP_TABLE[g-6]^h.EXP_TABLE[g-8];for(var g=0;255>g;g++)h.LOG_TABLE[h.EXP_TABLE[g]]=g;n.prototype={get:function(t){return this.num[t]},getLength:function(){return this.num.length},multiply:function(t){for(var e=new Array(this.getLength()+t.getLength()-1),r=0;re-r)return this;for(var o=new Array(e),i=0;e>i;i++)o[i]=this.get(i);for(;o.length>=r;){for(var s=h.glog(o[0])-h.glog(t.get(0)),i=0;it;t++){var e=c[4*(t-1)+this.errorCorrectLevel];if(void 0==e)throw new Error("bad rs block @ typeNumber:"+t+"/errorCorrectLevel:"+this.errorCorrectLevel);for(var r=e.length/3,o=0,i=0;r>i;i++){var n=e[3*i+0],s=e[3*i+2];o+=s*n}var u=t>9?2:1;if(this.utf8bytes.length+u>>7-t%8&1},put:function(t,e){for(var r=0;e>r;r++)this.putBit(t>>>e-r-1&1)},putBit:function(t){var e=Math.floor(this.length/8);this.buffer.length<=e&&this.buffer.push(0),t&&(this.buffer[e]|=128>>>this.length%8),this.length++}},t.exports=i},function(t,e){t.exports=function(t){for(var e,r=Array.prototype.slice.call(arguments,1),o=0;e=r[o];o++)if(e)for(var i in e)t[i]=e[i];return t}}]); -------------------------------------------------------------------------------- /examples/index.md: -------------------------------------------------------------------------------- 1 | # 演示文档 2 | 3 | --- 4 | 5 | ## 采用默认方式画二维码" 6 | 7 | 8 | ````html 9 |
10 | ```` 11 | 12 | ````javascript 13 | var qrcode = require('../src/index.js'); 14 | var qrnode = new qrcode({ 15 | text: 'http://www.alipay.com/' 16 | }); 17 | document.getElementById('qrcodeDefault').appendChild(qrnode); 18 | ```` 19 | 20 | ## 调用table画二维码 21 | 22 | ````html 23 |
24 | ```` 25 | 26 | ````javascript 27 | var qrcode = require('../src/index.js'); 28 | var qrnode = new qrcode({ 29 | render: 'table', 30 | correctLevel: 0, 31 | pdground: '#00aaee', 32 | text: 'http://www.alipay.com/', 33 | size: 100, 34 | image : 'https://t.alipayobjects.com/images/rmsweb/T1ZsxhXdxbXXXXXXXX.png' 35 | }); 36 | document.getElementById('qrcodeTable').appendChild(qrnode); 37 | ```` 38 | 39 | ## 调用canvas画二维码 40 | 41 | ````html 42 |
43 | ```` 44 | 45 | ````javascript 46 | var qrcode = require('../src/index.js'); 47 | var qrnode = new qrcode({ 48 | render: 'canvas', 49 | correctLevel: 0, 50 | text: 'http://www.alipay.com/', 51 | size: 300, 52 | background: '#eeeeee', 53 | foreground: '#667766', 54 | pdground: '#00aaee', 55 | image : 'https://t.alipayobjects.com/images/rmsweb/T1ZsxhXdxbXXXXXXXX.png', 56 | imageSize : 100 57 | }); 58 | document.getElementById('qrcodeCanvas').appendChild(qrnode); 59 | ```` 60 | 61 | ## 调用svg画二维码 62 | 63 | ````html 64 |
65 | ```` 66 | 67 | ````javascript 68 | var qrcode = require('../src/index.js'); 69 | var qrnode = new qrcode({ 70 | correctLevel: 0, 71 | render: 'svg', 72 | text: 'http://www.alipay.com/', 73 | size: 200, 74 | pdground: '#00aaee', 75 | image : 'https://t.alipayobjects.com/images/rmsweb/T1ZsxhXdxbXXXXXXXX.png', 76 | imageSize:30 77 | }); 78 | document.getElementById('qrcodeSVG').appendChild(qrnode); 79 | ```` 80 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/index.js'); -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var extend = require('extend'); 4 | var qrcodeAlgObjCache = []; 5 | var QRCodeAlg = require('./qrcodealg'); 6 | 7 | /** 8 | * 计算矩阵点的前景色 9 | * @param {Obj} config 10 | * @param {Number} config.row 点x坐标 11 | * @param {Number} config.col 点y坐标 12 | * @param {Number} config.count 矩阵大小 13 | * @param {Number} config.options 组件的options 14 | * @return {String} 15 | */ 16 | var getForeGround = function getForeGround(config) { 17 | var options = config.options; 18 | if (options.pdground && (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5 || config.row > config.count - 6 && config.row < config.count - 2 && config.col > 1 && config.col < 5 || config.row > 1 && config.row < 5 && config.col > config.count - 6 && config.col < config.count - 2)) { 19 | return options.pdground; 20 | } 21 | return options.foreground; 22 | }; 23 | /** 24 | * 点是否在Position Detection 25 | * @param {row} 矩阵行 26 | * @param {col} 矩阵列 27 | * @param {count} 矩阵大小 28 | * @return {Boolean} 29 | */ 30 | var inPositionDetection = function inPositionDetection(row, col, count) { 31 | if (row < 7 && col < 7 || row > count - 8 && col < 7 || row < 7 && col > count - 8) { 32 | return true; 33 | } 34 | return false; 35 | }; 36 | 37 | /** 38 | * 二维码构造函数,主要用于绘制 39 | * @param {参数列表} opt 传递参数 40 | * @return {} 41 | */ 42 | var qrcode = function qrcode(opt) { 43 | if (typeof opt === 'string') { 44 | // 只编码ASCII字符串 45 | opt = { 46 | text: opt 47 | }; 48 | } 49 | //设置默认参数 50 | this.options = extend({}, { 51 | text: '', 52 | render: '', 53 | size: 256, 54 | correctLevel: 3, 55 | background: '#ffffff', 56 | foreground: '#000000', 57 | image: '', 58 | imageSize: 30 59 | }, opt); 60 | 61 | //使用QRCodeAlg创建二维码结构 62 | var qrCodeAlg = null; 63 | for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) { 64 | if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) { 65 | qrCodeAlg = qrcodeAlgObjCache[i].obj; 66 | break; 67 | } 68 | } 69 | 70 | if (i == l) { 71 | qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel); 72 | qrcodeAlgObjCache.push({ text: this.options.text, correctLevel: this.options.correctLevel, obj: qrCodeAlg }); 73 | } 74 | 75 | if (this.options.render) { 76 | switch (this.options.render) { 77 | case 'canvas': 78 | return this.createCanvas(qrCodeAlg); 79 | case 'table': 80 | return this.createTable(qrCodeAlg); 81 | case 'svg': 82 | return this.createSVG(qrCodeAlg); 83 | default: 84 | return this.createDefault(qrCodeAlg); 85 | } 86 | } 87 | return this.createDefault(qrCodeAlg); 88 | }; 89 | 90 | extend(qrcode.prototype, { 91 | // default create canvas -> svg -> table 92 | createDefault: function createDefault(qrCodeAlg) { 93 | var canvas = document.createElement('canvas'); 94 | if (canvas.getContext) { 95 | return this.createCanvas(qrCodeAlg); 96 | } 97 | var SVG_NS = 'http://www.w3.org/2000/svg'; 98 | if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) { 99 | return this.createSVG(qrCodeAlg); 100 | } 101 | return this.createTable(qrCodeAlg); 102 | }, 103 | // canvas create 104 | createCanvas: function createCanvas(qrCodeAlg) { 105 | var options = this.options; 106 | var canvas = document.createElement('canvas'); 107 | var ctx = canvas.getContext('2d'); 108 | var count = qrCodeAlg.getModuleCount(); 109 | // preload img 110 | var loadImage = function loadImage(url, callback) { 111 | var img = new Image(); 112 | img.src = url; 113 | img.onload = function () { 114 | callback(this); 115 | img.onload = null; 116 | }; 117 | }; 118 | 119 | //计算每个点的长宽 120 | var tileW = (options.size / count).toPrecision(4); 121 | var tileH = (options.size / count).toPrecision(4); 122 | 123 | canvas.width = options.size; 124 | canvas.height = options.size; 125 | 126 | //绘制 127 | for (var row = 0; row < count; row++) { 128 | for (var col = 0; col < count; col++) { 129 | var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW); 130 | var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW); 131 | var foreground = getForeGround({ 132 | row: row, 133 | col: col, 134 | count: count, 135 | options: options 136 | }); 137 | ctx.fillStyle = qrCodeAlg.modules[row][col] ? foreground : options.background; 138 | ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h); 139 | } 140 | } 141 | if (options.image) { 142 | loadImage(options.image, function (img) { 143 | var x = ((options.size - options.imageSize) / 2).toFixed(2); 144 | var y = ((options.size - options.imageSize) / 2).toFixed(2); 145 | ctx.drawImage(img, x, y, options.imageSize, options.imageSize); 146 | }); 147 | } 148 | return canvas; 149 | }, 150 | // table create 151 | createTable: function createTable(qrCodeAlg) { 152 | var options = this.options; 153 | var count = qrCodeAlg.getModuleCount(); 154 | 155 | // 计算每个节点的长宽;取整,防止点之间出现分离 156 | var tileW = Math.floor(options.size / count); 157 | var tileH = Math.floor(options.size / count); 158 | if (tileW <= 0) { 159 | tileW = count < 80 ? 2 : 1; 160 | } 161 | if (tileH <= 0) { 162 | tileH = count < 80 ? 2 : 1; 163 | } 164 | 165 | //创建table节点 166 | //重算码大小 167 | var s = []; 168 | s.push(''); 169 | 170 | // 绘制二维码 171 | for (var row = 0; row < count; row++) { 172 | s.push(''); 173 | for (var col = 0; col < count; col++) { 174 | var foreground = getForeGround({ 175 | row: row, 176 | col: col, 177 | count: count, 178 | options: options 179 | }); 180 | if (qrCodeAlg.modules[row][col]) { 181 | s.push(''); 182 | } else { 183 | s.push(''); 184 | } 185 | } 186 | s.push(''); 187 | } 188 | s.push('
'); 189 | 190 | if (options.image) { 191 | // 计算表格的总大小 192 | var width = tileW * count; 193 | var height = tileH * count; 194 | var x = ((width - options.imageSize) / 2).toFixed(2); 195 | var y = ((height - options.imageSize) / 2).toFixed(2); 196 | s.unshift('
'); 197 | s.push(''); 198 | s.push('
'); 199 | } 200 | 201 | var span = document.createElement('span'); 202 | span.innerHTML = s.join(''); 203 | 204 | return span.firstChild; 205 | }, 206 | // create svg 207 | createSVG: function createSVG(qrCodeAlg) { 208 | var options = this.options; 209 | var count = qrCodeAlg.getModuleCount(); 210 | var scale = count / options.size; 211 | 212 | // create svg 213 | var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 214 | svg.setAttribute('width', options.size); 215 | svg.setAttribute('height', options.size); 216 | svg.setAttribute('viewBox', '0 0 ' + count + ' ' + count); 217 | 218 | for (var row = 0; row < count; row++) { 219 | for (var col = 0; col < count; col++) { 220 | var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); 221 | var foreground = getForeGround({ 222 | row: row, 223 | col: col, 224 | count: count, 225 | options: options 226 | }); 227 | rect.setAttribute('x', col); 228 | rect.setAttribute('y', row); 229 | rect.setAttribute('width', 1); 230 | rect.setAttribute('height', 1); 231 | rect.setAttribute('stroke-width', 0); 232 | if (qrCodeAlg.modules[row][col]) { 233 | rect.setAttribute('fill', foreground); 234 | } else { 235 | rect.setAttribute('fill', options.background); 236 | } 237 | svg.appendChild(rect); 238 | } 239 | } 240 | 241 | // create image 242 | if (options.image) { 243 | var img = document.createElementNS('http://www.w3.org/2000/svg', 'image'); 244 | img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', options.image); 245 | img.setAttribute('x', ((count - options.imageSize * scale) / 2).toFixed(2)); 246 | img.setAttribute('y', ((count - options.imageSize * scale) / 2).toFixed(2)); 247 | img.setAttribute('width', options.imageSize * scale); 248 | img.setAttribute('height', options.imageSize * scale); 249 | svg.appendChild(img); 250 | } 251 | 252 | return svg; 253 | } 254 | }); 255 | module.exports = qrcode; -------------------------------------------------------------------------------- /lib/qrcodealg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取单个字符的utf8编码 3 | * unicode BMP平面约65535个字符 4 | * @param {num} code 5 | * return {array} 6 | */ 7 | "use strict"; 8 | 9 | function unicodeFormat8(code) { 10 | // 1 byte 11 | if (code < 128) { 12 | return [code]; 13 | // 2 bytes 14 | } else if (code < 2048) { 15 | c0 = 192 + (code >> 6); 16 | c1 = 128 + (code & 63); 17 | return [c0, c1]; 18 | // 3 bytes 19 | } else { 20 | c0 = 224 + (code >> 12); 21 | c1 = 128 + (code >> 6 & 63); 22 | c2 = 128 + (code & 63); 23 | return [c0, c1, c2]; 24 | } 25 | } 26 | 27 | /** 28 | * 获取字符串的utf8编码字节串 29 | * @param {string} string 30 | * @return {array} 31 | */ 32 | function getUTF8Bytes(string) { 33 | var utf8codes = []; 34 | for (var i = 0; i < string.length; i++) { 35 | var code = string.charCodeAt(i); 36 | var utf8 = unicodeFormat8(code); 37 | for (var j = 0; j < utf8.length; j++) { 38 | utf8codes.push(utf8[j]); 39 | } 40 | } 41 | return utf8codes; 42 | } 43 | 44 | /** 45 | * 二维码算法实现 46 | * @param {string} data 要编码的信息字符串 47 | * @param {num} errorCorrectLevel 纠错等级 48 | */ 49 | function QRCodeAlg(data, errorCorrectLevel) { 50 | this.typeNumber = -1; //版本 51 | this.errorCorrectLevel = errorCorrectLevel; 52 | this.modules = null; //二维矩阵,存放最终结果 53 | this.moduleCount = 0; //矩阵大小 54 | this.dataCache = null; //数据缓存 55 | this.rsBlocks = null; //版本数据信息 56 | this.totalDataCount = -1; //可使用的数据量 57 | this.data = data; 58 | this.utf8bytes = getUTF8Bytes(data); 59 | this.make(); 60 | } 61 | 62 | QRCodeAlg.prototype = { 63 | constructor: QRCodeAlg, 64 | /** 65 | * 获取二维码矩阵大小 66 | * @return {num} 矩阵大小 67 | */ 68 | getModuleCount: function getModuleCount() { 69 | return this.moduleCount; 70 | }, 71 | /** 72 | * 编码 73 | */ 74 | make: function make() { 75 | this.getRightType(); 76 | this.dataCache = this.createData(); 77 | this.createQrcode(); 78 | }, 79 | /** 80 | * 设置二位矩阵功能图形 81 | * @param {bool} test 表示是否在寻找最好掩膜阶段 82 | * @param {num} maskPattern 掩膜的版本 83 | */ 84 | makeImpl: function makeImpl(maskPattern) { 85 | 86 | this.moduleCount = this.typeNumber * 4 + 17; 87 | this.modules = new Array(this.moduleCount); 88 | 89 | for (var row = 0; row < this.moduleCount; row++) { 90 | 91 | this.modules[row] = new Array(this.moduleCount); 92 | } 93 | this.setupPositionProbePattern(0, 0); 94 | this.setupPositionProbePattern(this.moduleCount - 7, 0); 95 | this.setupPositionProbePattern(0, this.moduleCount - 7); 96 | this.setupPositionAdjustPattern(); 97 | this.setupTimingPattern(); 98 | this.setupTypeInfo(true, maskPattern); 99 | 100 | if (this.typeNumber >= 7) { 101 | this.setupTypeNumber(true); 102 | } 103 | this.mapData(this.dataCache, maskPattern); 104 | }, 105 | /** 106 | * 设置二维码的位置探测图形 107 | * @param {num} row 探测图形的中心横坐标 108 | * @param {num} col 探测图形的中心纵坐标 109 | */ 110 | setupPositionProbePattern: function setupPositionProbePattern(row, col) { 111 | 112 | for (var r = -1; r <= 7; r++) { 113 | 114 | if (row + r <= -1 || this.moduleCount <= row + r) continue; 115 | 116 | for (var c = -1; c <= 7; c++) { 117 | 118 | if (col + c <= -1 || this.moduleCount <= col + c) continue; 119 | 120 | 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) { 121 | this.modules[row + r][col + c] = true; 122 | } else { 123 | this.modules[row + r][col + c] = false; 124 | } 125 | } 126 | } 127 | }, 128 | /** 129 | * 创建二维码 130 | * @return {[type]} [description] 131 | */ 132 | createQrcode: function createQrcode() { 133 | 134 | var minLostPoint = 0; 135 | var pattern = 0; 136 | var bestModules = null; 137 | 138 | for (var i = 0; i < 8; i++) { 139 | 140 | this.makeImpl(i); 141 | 142 | var lostPoint = QRUtil.getLostPoint(this); 143 | if (i == 0 || minLostPoint > lostPoint) { 144 | minLostPoint = lostPoint; 145 | pattern = i; 146 | bestModules = this.modules; 147 | } 148 | } 149 | this.modules = bestModules; 150 | this.setupTypeInfo(false, pattern); 151 | 152 | if (this.typeNumber >= 7) { 153 | this.setupTypeNumber(false); 154 | } 155 | }, 156 | /** 157 | * 设置定位图形 158 | * @return {[type]} [description] 159 | */ 160 | setupTimingPattern: function setupTimingPattern() { 161 | 162 | for (var r = 8; r < this.moduleCount - 8; r++) { 163 | if (this.modules[r][6] != null) { 164 | continue; 165 | } 166 | this.modules[r][6] = r % 2 == 0; 167 | 168 | if (this.modules[6][r] != null) { 169 | continue; 170 | } 171 | this.modules[6][r] = r % 2 == 0; 172 | } 173 | }, 174 | /** 175 | * 设置矫正图形 176 | * @return {[type]} [description] 177 | */ 178 | setupPositionAdjustPattern: function setupPositionAdjustPattern() { 179 | 180 | var pos = QRUtil.getPatternPosition(this.typeNumber); 181 | 182 | for (var i = 0; i < pos.length; i++) { 183 | 184 | for (var j = 0; j < pos.length; j++) { 185 | 186 | var row = pos[i]; 187 | var col = pos[j]; 188 | 189 | if (this.modules[row][col] != null) { 190 | continue; 191 | } 192 | 193 | for (var r = -2; r <= 2; r++) { 194 | 195 | for (var c = -2; c <= 2; c++) { 196 | 197 | if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) { 198 | this.modules[row + r][col + c] = true; 199 | } else { 200 | this.modules[row + r][col + c] = false; 201 | } 202 | } 203 | } 204 | } 205 | } 206 | }, 207 | /** 208 | * 设置版本信息(7以上版本才有) 209 | * @param {bool} test 是否处于判断最佳掩膜阶段 210 | * @return {[type]} [description] 211 | */ 212 | setupTypeNumber: function setupTypeNumber(test) { 213 | 214 | var bits = QRUtil.getBCHTypeNumber(this.typeNumber); 215 | 216 | for (var i = 0; i < 18; i++) { 217 | var mod = !test && (bits >> i & 1) == 1; 218 | this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod; 219 | this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod; 220 | } 221 | }, 222 | /** 223 | * 设置格式信息(纠错等级和掩膜版本) 224 | * @param {bool} test 225 | * @param {num} maskPattern 掩膜版本 226 | * @return {} 227 | */ 228 | setupTypeInfo: function setupTypeInfo(test, maskPattern) { 229 | 230 | var data = QRErrorCorrectLevel[this.errorCorrectLevel] << 3 | maskPattern; 231 | var bits = QRUtil.getBCHTypeInfo(data); 232 | 233 | // vertical 234 | for (var i = 0; i < 15; i++) { 235 | 236 | var mod = !test && (bits >> i & 1) == 1; 237 | 238 | if (i < 6) { 239 | this.modules[i][8] = mod; 240 | } else if (i < 8) { 241 | this.modules[i + 1][8] = mod; 242 | } else { 243 | this.modules[this.moduleCount - 15 + i][8] = mod; 244 | } 245 | 246 | // horizontal 247 | var mod = !test && (bits >> i & 1) == 1; 248 | 249 | if (i < 8) { 250 | this.modules[8][this.moduleCount - i - 1] = mod; 251 | } else if (i < 9) { 252 | this.modules[8][15 - i - 1 + 1] = mod; 253 | } else { 254 | this.modules[8][15 - i - 1] = mod; 255 | } 256 | } 257 | 258 | // fixed module 259 | this.modules[this.moduleCount - 8][8] = !test; 260 | }, 261 | /** 262 | * 数据编码 263 | * @return {[type]} [description] 264 | */ 265 | createData: function createData() { 266 | var buffer = new QRBitBuffer(); 267 | var lengthBits = this.typeNumber > 9 ? 16 : 8; 268 | buffer.put(4, 4); //添加模式 269 | buffer.put(this.utf8bytes.length, lengthBits); 270 | for (var i = 0, l = this.utf8bytes.length; i < l; i++) { 271 | buffer.put(this.utf8bytes[i], 8); 272 | } 273 | if (buffer.length + 4 <= this.totalDataCount * 8) { 274 | buffer.put(0, 4); 275 | } 276 | 277 | // padding 278 | while (buffer.length % 8 != 0) { 279 | buffer.putBit(false); 280 | } 281 | 282 | // padding 283 | while (true) { 284 | 285 | if (buffer.length >= this.totalDataCount * 8) { 286 | break; 287 | } 288 | buffer.put(QRCodeAlg.PAD0, 8); 289 | 290 | if (buffer.length >= this.totalDataCount * 8) { 291 | break; 292 | } 293 | buffer.put(QRCodeAlg.PAD1, 8); 294 | } 295 | return this.createBytes(buffer); 296 | }, 297 | /** 298 | * 纠错码编码 299 | * @param {buffer} buffer 数据编码 300 | * @return {[type]} 301 | */ 302 | createBytes: function createBytes(buffer) { 303 | 304 | var offset = 0; 305 | 306 | var maxDcCount = 0; 307 | var maxEcCount = 0; 308 | 309 | var length = this.rsBlock.length / 3; 310 | 311 | var rsBlocks = new Array(); 312 | 313 | for (var i = 0; i < length; i++) { 314 | 315 | var count = this.rsBlock[i * 3 + 0]; 316 | var totalCount = this.rsBlock[i * 3 + 1]; 317 | var dataCount = this.rsBlock[i * 3 + 2]; 318 | 319 | for (var j = 0; j < count; j++) { 320 | rsBlocks.push([dataCount, totalCount]); 321 | } 322 | } 323 | 324 | var dcdata = new Array(rsBlocks.length); 325 | var ecdata = new Array(rsBlocks.length); 326 | 327 | for (var r = 0; r < rsBlocks.length; r++) { 328 | 329 | var dcCount = rsBlocks[r][0]; 330 | var ecCount = rsBlocks[r][1] - dcCount; 331 | 332 | maxDcCount = Math.max(maxDcCount, dcCount); 333 | maxEcCount = Math.max(maxEcCount, ecCount); 334 | 335 | dcdata[r] = new Array(dcCount); 336 | 337 | for (var i = 0; i < dcdata[r].length; i++) { 338 | dcdata[r][i] = 0xff & buffer.buffer[i + offset]; 339 | } 340 | offset += dcCount; 341 | 342 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); 343 | var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); 344 | 345 | var modPoly = rawPoly.mod(rsPoly); 346 | ecdata[r] = new Array(rsPoly.getLength() - 1); 347 | for (var i = 0; i < ecdata[r].length; i++) { 348 | var modIndex = i + modPoly.getLength() - ecdata[r].length; 349 | ecdata[r][i] = modIndex >= 0 ? modPoly.get(modIndex) : 0; 350 | } 351 | } 352 | 353 | var data = new Array(this.totalDataCount); 354 | var index = 0; 355 | 356 | for (var i = 0; i < maxDcCount; i++) { 357 | for (var r = 0; r < rsBlocks.length; r++) { 358 | if (i < dcdata[r].length) { 359 | data[index++] = dcdata[r][i]; 360 | } 361 | } 362 | } 363 | 364 | for (var i = 0; i < maxEcCount; i++) { 365 | for (var r = 0; r < rsBlocks.length; r++) { 366 | if (i < ecdata[r].length) { 367 | data[index++] = ecdata[r][i]; 368 | } 369 | } 370 | } 371 | 372 | return data; 373 | }, 374 | /** 375 | * 布置模块,构建最终信息 376 | * @param {} data 377 | * @param {} maskPattern 378 | * @return {} 379 | */ 380 | mapData: function mapData(data, maskPattern) { 381 | 382 | var inc = -1; 383 | var row = this.moduleCount - 1; 384 | var bitIndex = 7; 385 | var byteIndex = 0; 386 | 387 | for (var col = this.moduleCount - 1; col > 0; col -= 2) { 388 | 389 | if (col == 6) col--; 390 | 391 | while (true) { 392 | 393 | for (var c = 0; c < 2; c++) { 394 | 395 | if (this.modules[row][col - c] == null) { 396 | 397 | var dark = false; 398 | 399 | if (byteIndex < data.length) { 400 | dark = (data[byteIndex] >>> bitIndex & 1) == 1; 401 | } 402 | 403 | var mask = QRUtil.getMask(maskPattern, row, col - c); 404 | 405 | if (mask) { 406 | dark = !dark; 407 | } 408 | 409 | this.modules[row][col - c] = dark; 410 | bitIndex--; 411 | 412 | if (bitIndex == -1) { 413 | byteIndex++; 414 | bitIndex = 7; 415 | } 416 | } 417 | } 418 | 419 | row += inc; 420 | 421 | if (row < 0 || this.moduleCount <= row) { 422 | row -= inc; 423 | inc = -inc; 424 | break; 425 | } 426 | } 427 | } 428 | } 429 | 430 | }; 431 | /** 432 | * 填充字段 433 | */ 434 | QRCodeAlg.PAD0 = 0xEC; 435 | QRCodeAlg.PAD1 = 0x11; 436 | 437 | //--------------------------------------------------------------------- 438 | // 纠错等级对应的编码 439 | //--------------------------------------------------------------------- 440 | 441 | var QRErrorCorrectLevel = [1, 0, 3, 2]; 442 | 443 | //--------------------------------------------------------------------- 444 | // 掩膜版本 445 | //--------------------------------------------------------------------- 446 | 447 | var QRMaskPattern = { 448 | PATTERN000: 0, 449 | PATTERN001: 1, 450 | PATTERN010: 2, 451 | PATTERN011: 3, 452 | PATTERN100: 4, 453 | PATTERN101: 5, 454 | PATTERN110: 6, 455 | PATTERN111: 7 456 | }; 457 | 458 | //--------------------------------------------------------------------- 459 | // 工具类 460 | //--------------------------------------------------------------------- 461 | 462 | var QRUtil = { 463 | 464 | /* 465 | 每个版本矫正图形的位置 466 | */ 467 | 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]], 468 | 469 | G15: 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0, 470 | G18: 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0, 471 | G15_MASK: 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1, 472 | 473 | /* 474 | BCH编码格式信息 475 | */ 476 | getBCHTypeInfo: function getBCHTypeInfo(data) { 477 | var d = data << 10; 478 | while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) { 479 | d ^= QRUtil.G15 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15); 480 | } 481 | return (data << 10 | d) ^ QRUtil.G15_MASK; 482 | }, 483 | /* 484 | BCH编码版本信息 485 | */ 486 | getBCHTypeNumber: function getBCHTypeNumber(data) { 487 | var d = data << 12; 488 | while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) { 489 | d ^= QRUtil.G18 << QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18); 490 | } 491 | return data << 12 | d; 492 | }, 493 | /* 494 | 获取BCH位信息 495 | */ 496 | getBCHDigit: function getBCHDigit(data) { 497 | 498 | var digit = 0; 499 | 500 | while (data != 0) { 501 | digit++; 502 | data >>>= 1; 503 | } 504 | 505 | return digit; 506 | }, 507 | /* 508 | 获取版本对应的矫正图形位置 509 | */ 510 | getPatternPosition: function getPatternPosition(typeNumber) { 511 | return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; 512 | }, 513 | /* 514 | 掩膜算法 515 | */ 516 | getMask: function getMask(maskPattern, i, j) { 517 | 518 | switch (maskPattern) { 519 | 520 | case QRMaskPattern.PATTERN000: 521 | return (i + j) % 2 == 0; 522 | case QRMaskPattern.PATTERN001: 523 | return i % 2 == 0; 524 | case QRMaskPattern.PATTERN010: 525 | return j % 3 == 0; 526 | case QRMaskPattern.PATTERN011: 527 | return (i + j) % 3 == 0; 528 | case QRMaskPattern.PATTERN100: 529 | return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0; 530 | case QRMaskPattern.PATTERN101: 531 | return i * j % 2 + i * j % 3 == 0; 532 | case QRMaskPattern.PATTERN110: 533 | return (i * j % 2 + i * j % 3) % 2 == 0; 534 | case QRMaskPattern.PATTERN111: 535 | return (i * j % 3 + (i + j) % 2) % 2 == 0; 536 | 537 | default: 538 | throw new Error("bad maskPattern:" + maskPattern); 539 | } 540 | }, 541 | /* 542 | 获取RS的纠错多项式 543 | */ 544 | getErrorCorrectPolynomial: function getErrorCorrectPolynomial(errorCorrectLength) { 545 | 546 | var a = new QRPolynomial([1], 0); 547 | 548 | for (var i = 0; i < errorCorrectLength; i++) { 549 | a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0)); 550 | } 551 | 552 | return a; 553 | }, 554 | /* 555 | 获取评价 556 | */ 557 | getLostPoint: function getLostPoint(qrCode) { 558 | 559 | var moduleCount = qrCode.getModuleCount(), 560 | lostPoint = 0, 561 | darkCount = 0; 562 | 563 | for (var row = 0; row < moduleCount; row++) { 564 | 565 | var sameCount = 0; 566 | var head = qrCode.modules[row][0]; 567 | 568 | for (var col = 0; col < moduleCount; col++) { 569 | 570 | var current = qrCode.modules[row][col]; 571 | 572 | //level 3 评价 573 | if (col < moduleCount - 6) { 574 | if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) { 575 | if (col < moduleCount - 10) { 576 | if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) { 577 | lostPoint += 40; 578 | } 579 | } else if (col > 3) { 580 | if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) { 581 | lostPoint += 40; 582 | } 583 | } 584 | } 585 | } 586 | 587 | //level 2 评价 588 | if (row < moduleCount - 1 && col < moduleCount - 1) { 589 | var count = 0; 590 | if (current) count++; 591 | if (qrCode.modules[row + 1][col]) count++; 592 | if (qrCode.modules[row][col + 1]) count++; 593 | if (qrCode.modules[row + 1][col + 1]) count++; 594 | if (count == 0 || count == 4) { 595 | lostPoint += 3; 596 | } 597 | } 598 | 599 | //level 1 评价 600 | if (head ^ current) { 601 | sameCount++; 602 | } else { 603 | head = current; 604 | if (sameCount >= 5) { 605 | lostPoint += 3 + sameCount - 5; 606 | } 607 | sameCount = 1; 608 | } 609 | 610 | //level 4 评价 611 | if (current) { 612 | darkCount++; 613 | } 614 | } 615 | } 616 | 617 | for (var col = 0; col < moduleCount; col++) { 618 | 619 | var sameCount = 0; 620 | var head = qrCode.modules[0][col]; 621 | 622 | for (var row = 0; row < moduleCount; row++) { 623 | 624 | var current = qrCode.modules[row][col]; 625 | 626 | //level 3 评价 627 | if (row < moduleCount - 6) { 628 | if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) { 629 | if (row < moduleCount - 10) { 630 | if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) { 631 | lostPoint += 40; 632 | } 633 | } else if (row > 3) { 634 | if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) { 635 | lostPoint += 40; 636 | } 637 | } 638 | } 639 | } 640 | 641 | //level 1 评价 642 | if (head ^ current) { 643 | sameCount++; 644 | } else { 645 | head = current; 646 | if (sameCount >= 5) { 647 | lostPoint += 3 + sameCount - 5; 648 | } 649 | sameCount = 1; 650 | } 651 | } 652 | } 653 | 654 | // LEVEL4 655 | 656 | var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; 657 | lostPoint += ratio * 10; 658 | 659 | return lostPoint; 660 | } 661 | 662 | }; 663 | 664 | //--------------------------------------------------------------------- 665 | // QRMath使用的数学工具 666 | //--------------------------------------------------------------------- 667 | 668 | var QRMath = { 669 | /* 670 | 将n转化为a^m 671 | */ 672 | glog: function glog(n) { 673 | 674 | if (n < 1) { 675 | throw new Error("glog(" + n + ")"); 676 | } 677 | 678 | return QRMath.LOG_TABLE[n]; 679 | }, 680 | /* 681 | 将a^m转化为n 682 | */ 683 | gexp: function gexp(n) { 684 | 685 | while (n < 0) { 686 | n += 255; 687 | } 688 | 689 | while (n >= 256) { 690 | n -= 255; 691 | } 692 | 693 | return QRMath.EXP_TABLE[n]; 694 | }, 695 | 696 | EXP_TABLE: new Array(256), 697 | 698 | LOG_TABLE: new Array(256) 699 | 700 | }; 701 | 702 | for (var i = 0; i < 8; i++) { 703 | QRMath.EXP_TABLE[i] = 1 << i; 704 | } 705 | for (var i = 8; i < 256; i++) { 706 | QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8]; 707 | } 708 | for (var i = 0; i < 255; i++) { 709 | QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; 710 | } 711 | 712 | //--------------------------------------------------------------------- 713 | // QRPolynomial 多项式 714 | //--------------------------------------------------------------------- 715 | /** 716 | * 多项式类 717 | * @param {Array} num 系数 718 | * @param {num} shift a^shift 719 | */ 720 | function QRPolynomial(num, shift) { 721 | 722 | if (num.length == undefined) { 723 | throw new Error(num.length + "/" + shift); 724 | } 725 | 726 | var offset = 0; 727 | 728 | while (offset < num.length && num[offset] == 0) { 729 | offset++; 730 | } 731 | 732 | this.num = new Array(num.length - offset + shift); 733 | for (var i = 0; i < num.length - offset; i++) { 734 | this.num[i] = num[i + offset]; 735 | } 736 | } 737 | 738 | QRPolynomial.prototype = { 739 | 740 | get: function get(index) { 741 | return this.num[index]; 742 | }, 743 | 744 | getLength: function getLength() { 745 | return this.num.length; 746 | }, 747 | /** 748 | * 多项式乘法 749 | * @param {QRPolynomial} e 被乘多项式 750 | * @return {[type]} [description] 751 | */ 752 | multiply: function multiply(e) { 753 | 754 | var num = new Array(this.getLength() + e.getLength() - 1); 755 | 756 | for (var i = 0; i < this.getLength(); i++) { 757 | for (var j = 0; j < e.getLength(); j++) { 758 | num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))); 759 | } 760 | } 761 | 762 | return new QRPolynomial(num, 0); 763 | }, 764 | /** 765 | * 多项式模运算 766 | * @param {QRPolynomial} e 模多项式 767 | * @return {} 768 | */ 769 | mod: function mod(e) { 770 | var tl = this.getLength(), 771 | el = e.getLength(); 772 | if (tl - el < 0) { 773 | return this; 774 | } 775 | var num = new Array(tl); 776 | for (var i = 0; i < tl; i++) { 777 | num[i] = this.get(i); 778 | } 779 | while (num.length >= el) { 780 | var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0)); 781 | 782 | for (var i = 0; i < e.getLength(); i++) { 783 | num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio); 784 | } 785 | while (num[0] == 0) { 786 | num.shift(); 787 | } 788 | } 789 | return new QRPolynomial(num, 0); 790 | } 791 | }; 792 | 793 | //--------------------------------------------------------------------- 794 | // RS_BLOCK_TABLE 795 | //--------------------------------------------------------------------- 796 | /* 797 | 二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数] 798 | */ 799 | var RS_BLOCK_TABLE = [ 800 | 801 | // L 802 | // M 803 | // Q 804 | // H 805 | 806 | // 1 807 | [1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], 808 | 809 | // 2 810 | [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], 811 | 812 | // 3 813 | [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], 814 | 815 | // 4 816 | [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], 817 | 818 | // 5 819 | [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], 820 | 821 | // 6 822 | [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], 823 | 824 | // 7 825 | [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], 826 | 827 | // 8 828 | [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], 829 | 830 | // 9 831 | [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], 832 | 833 | // 10 834 | [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], 835 | 836 | // 11 837 | [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], 838 | 839 | // 12 840 | [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], 841 | 842 | // 13 843 | [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], 844 | 845 | // 14 846 | [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], 847 | 848 | // 15 849 | [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], 850 | 851 | // 16 852 | [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], 853 | 854 | // 17 855 | [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], 856 | 857 | // 18 858 | [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], 859 | 860 | // 19 861 | [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], 862 | 863 | // 20 864 | [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], 865 | 866 | // 21 867 | [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], 868 | 869 | // 22 870 | [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], 871 | 872 | // 23 873 | [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], 874 | 875 | // 24 876 | [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], 877 | 878 | // 25 879 | [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], 880 | 881 | // 26 882 | [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], 883 | 884 | // 27 885 | [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], 886 | 887 | // 28 888 | [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], 889 | 890 | // 29 891 | [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], 892 | 893 | // 30 894 | [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], 895 | 896 | // 31 897 | [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], 898 | 899 | // 32 900 | [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], 901 | 902 | // 33 903 | [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], 904 | 905 | // 34 906 | [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], 907 | 908 | // 35 909 | [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], 910 | 911 | // 36 912 | [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], 913 | 914 | // 37 915 | [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], 916 | 917 | // 38 918 | [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], 919 | 920 | // 39 921 | [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], 922 | 923 | // 40 924 | [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]]; 925 | 926 | /** 927 | * 根据数据获取对应版本 928 | * @return {[type]} [description] 929 | */ 930 | QRCodeAlg.prototype.getRightType = function () { 931 | for (var typeNumber = 1; typeNumber < 41; typeNumber++) { 932 | var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel]; 933 | if (rsBlock == undefined) { 934 | throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel); 935 | } 936 | var length = rsBlock.length / 3; 937 | var totalDataCount = 0; 938 | for (var i = 0; i < length; i++) { 939 | var count = rsBlock[i * 3 + 0]; 940 | var dataCount = rsBlock[i * 3 + 2]; 941 | totalDataCount += dataCount * count; 942 | } 943 | 944 | var lengthBytes = typeNumber > 9 ? 2 : 1; 945 | if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) { 946 | this.typeNumber = typeNumber; 947 | this.rsBlock = rsBlock; 948 | this.totalDataCount = totalDataCount; 949 | break; 950 | } 951 | } 952 | }; 953 | 954 | //--------------------------------------------------------------------- 955 | // QRBitBuffer 956 | //--------------------------------------------------------------------- 957 | 958 | function QRBitBuffer() { 959 | this.buffer = new Array(); 960 | this.length = 0; 961 | } 962 | 963 | QRBitBuffer.prototype = { 964 | 965 | get: function get(index) { 966 | var bufIndex = Math.floor(index / 8); 967 | return this.buffer[bufIndex] >>> 7 - index % 8 & 1; 968 | }, 969 | 970 | put: function put(num, length) { 971 | for (var i = 0; i < length; i++) { 972 | this.putBit(num >>> length - i - 1 & 1); 973 | } 974 | }, 975 | 976 | putBit: function putBit(bit) { 977 | 978 | var bufIndex = Math.floor(this.length / 8); 979 | if (this.buffer.length <= bufIndex) { 980 | this.buffer.push(0); 981 | } 982 | 983 | if (bit) { 984 | this.buffer[bufIndex] |= 0x80 >>> this.length % 8; 985 | } 986 | 987 | this.length++; 988 | } 989 | }; 990 | module.exports = QRCodeAlg; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arale-qrcode", 3 | "version": "3.0.5", 4 | "description": "二维码绘制模块", 5 | "homepage": "http://aralejs.org/qrcode/", 6 | "author": "倚松 ", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/aralejs/qrcode/" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/aralejs/qrcode/issues" 13 | }, 14 | "keywords": ["utility", "qrcode", "二维码"], 15 | "licenses": "MIT", 16 | "spm": { 17 | "main": "index.js", 18 | "dependencies": { 19 | "extend": "~1.0.0", 20 | "jquery": "1.7.2" 21 | }, 22 | "devDependencies": { 23 | "expect.js": "0.3.1" 24 | }, 25 | "build": { 26 | "babel" : {}, 27 | "library" : "AraleQRCode", 28 | "libraryTarget" : "this" 29 | } 30 | }, 31 | "scripts": { 32 | "babel": "babel src --out-dir lib", 33 | "prepublish": "npm run babel", 34 | "build" : "spm-webpack" 35 | }, 36 | "devDependencies": { 37 | "babel" : "~5.8.21", 38 | "spm-webpack": "~0.7.3", 39 | "nico": "*", 40 | "mocha-browser": "*", 41 | "jscoverage": "~0.3.7" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var extend = require('extend'); 2 | var qrcodeAlgObjCache = []; 3 | var QRCodeAlg = require('./qrcodealg'); 4 | 5 | /** 6 | * 计算矩阵点的前景色 7 | * @param {Obj} config 8 | * @param {Number} config.row 点x坐标 9 | * @param {Number} config.col 点y坐标 10 | * @param {Number} config.count 矩阵大小 11 | * @param {Number} config.options 组件的options 12 | * @return {String} 13 | */ 14 | var getForeGround = function(config){ 15 | var options = config.options; 16 | if( options.pdground && ( 17 | (config.row > 1 && config.row < 5 && config.col >1 && config.col<5) 18 | || (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col >1 && config.col<5) 19 | || (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2)) 20 | )){ 21 | return options.pdground; 22 | } 23 | return options.foreground; 24 | } 25 | /** 26 | * 点是否在Position Detection 27 | * @param {row} 矩阵行 28 | * @param {col} 矩阵列 29 | * @param {count} 矩阵大小 30 | * @return {Boolean} 31 | */ 32 | var inPositionDetection = function(row, col, count){ 33 | if( 34 | (row<7 && col<7) 35 | || (row > (count - 8) && col < 7) 36 | || (row < 7 && col >(count - 8) ) 37 | ){ 38 | return true; 39 | } 40 | return false; 41 | } 42 | /** 43 | * 获取当前屏幕的设备像素比 devicePixelRatio/backingStore 44 | * @param {context} 当前 canvas 上下文,可以为 window 45 | */ 46 | var getPixelRatio = function(context) { 47 | var backingStore = context.backingStorePixelRatio 48 | || context.webkitBackingStorePixelRatio 49 | || context.mozBackingStorePixelRatio 50 | || context.msBackingStorePixelRatio 51 | || context.oBackingStorePixelRatio 52 | || context.backingStorePixelRatio 53 | || 1; 54 | 55 | return (window.devicePixelRatio || 1) / backingStore; 56 | }; 57 | 58 | /** 59 | * 二维码构造函数,主要用于绘制 60 | * @param {参数列表} opt 传递参数 61 | * @return {} 62 | */ 63 | var qrcode = function(opt) { 64 | if (typeof opt === 'string') { // 只编码ASCII字符串 65 | opt = { 66 | text: opt 67 | }; 68 | } 69 | //设置默认参数 70 | this.options = extend({}, { 71 | text:'', 72 | render: '', 73 | size: 256, 74 | correctLevel: 3, 75 | background: '#ffffff', 76 | foreground: '#000000', 77 | image : '', 78 | imageSize: 30 79 | }, opt); 80 | 81 | //使用QRCodeAlg创建二维码结构 82 | var qrCodeAlg = null; 83 | for(var i = 0, l = qrcodeAlgObjCache.length; i < l; i++){ 84 | if(qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel){ 85 | qrCodeAlg = qrcodeAlgObjCache[i].obj; 86 | break; 87 | } 88 | } 89 | 90 | if(i == l){ 91 | qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel); 92 | qrcodeAlgObjCache.push({text:this.options.text, correctLevel: this.options.correctLevel, obj:qrCodeAlg}); 93 | } 94 | 95 | if(this.options.render){ 96 | switch (this.options.render){ 97 | case 'canvas': 98 | return this.createCanvas(qrCodeAlg); 99 | case 'table': 100 | return this.createTable(qrCodeAlg); 101 | case 'svg': 102 | return this.createSVG(qrCodeAlg); 103 | default: 104 | return this.createDefault(qrCodeAlg); 105 | } 106 | } 107 | return this.createDefault(qrCodeAlg); 108 | }; 109 | 110 | extend(qrcode.prototype,{ 111 | // default create canvas -> svg -> table 112 | createDefault (qrCodeAlg) { 113 | var canvas = document.createElement('canvas'); 114 | if(canvas.getContext){ 115 | return this.createCanvas(qrCodeAlg); 116 | } 117 | var SVG_NS = 'http://www.w3.org/2000/svg'; 118 | if( !!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect ){ 119 | return this.createSVG(qrCodeAlg); 120 | } 121 | return this.createTable(qrCodeAlg); 122 | }, 123 | // canvas create 124 | createCanvas (qrCodeAlg) { 125 | var options = this.options; 126 | var canvas = document.createElement('canvas'); 127 | var ctx = canvas.getContext('2d'); 128 | var count = qrCodeAlg.getModuleCount(); 129 | var ratio = getPixelRatio(ctx); 130 | var size = options.size; 131 | var ratioSize = size * ratio; 132 | var ratioImgSize = options.imageSize * ratio; 133 | // preload img 134 | var loadImage = function(url,callback){ 135 | var img = new Image(); 136 | img.src = url; 137 | img.onload = function () { 138 | callback(this); 139 | img.onload = null 140 | }; 141 | } 142 | 143 | //计算每个点的长宽 144 | var tileW = (ratioSize / count).toPrecision(4); 145 | var tileH = (ratioSize / count).toPrecision(4); 146 | 147 | canvas.width = ratioSize; 148 | canvas.height = ratioSize; 149 | 150 | //绘制 151 | for (var row = 0; row < count; row++) { 152 | for (var col = 0; col < count; col++) { 153 | var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW)); 154 | var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW)); 155 | var foreground = getForeGround({ 156 | row : row, 157 | col : col, 158 | count : count, 159 | options : options 160 | }); 161 | ctx.fillStyle = qrCodeAlg.modules[row][col] ? foreground : options.background; 162 | ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h); 163 | } 164 | } 165 | if(options.image){ 166 | loadImage(options.image, function(img){ 167 | var x = ((ratioSize - ratioImgSize)/2).toFixed(2); 168 | var y = ((ratioSize - ratioImgSize)/2).toFixed(2); 169 | ctx.drawImage(img, x, y, ratioImgSize, ratioImgSize); 170 | }); 171 | } 172 | canvas.style.width = size + 'px'; 173 | canvas.style.height = size + 'px'; 174 | return canvas; 175 | }, 176 | // table create 177 | createTable (qrCodeAlg) { 178 | var options = this.options; 179 | var count = qrCodeAlg.getModuleCount(); 180 | 181 | // 计算每个节点的长宽;取整,防止点之间出现分离 182 | var tileW = Math.floor(options.size / count); 183 | var tileH = Math.floor(options.size / count); 184 | if(tileW <= 0){ 185 | tileW = count < 80 ? 2 : 1; 186 | } 187 | if(tileH <= 0){ 188 | tileH = count < 80 ? 2 : 1; 189 | } 190 | 191 | //创建table节点 192 | //重算码大小 193 | var s = []; 194 | s.push(``); 195 | 196 | // 绘制二维码 197 | for (var row = 0; row < count; row++) { 198 | s.push(``); 199 | for (var col = 0; col < count; col++) { 200 | var foreground = getForeGround({ 201 | row : row, 202 | col : col, 203 | count : count, 204 | options : options 205 | }); 206 | if(qrCodeAlg.modules[row][col]){ 207 | s.push(``); 208 | }else{ 209 | s.push(``); 210 | } 211 | } 212 | s.push(''); 213 | } 214 | s.push('
'); 215 | 216 | if(options.image){ 217 | // 计算表格的总大小 218 | var width = tileW * count; 219 | var height = tileH * count; 220 | var x = ((width - options.imageSize)/2).toFixed(2); 221 | var y = ((height - options.imageSize)/2).toFixed(2); 222 | s.unshift(`
`); 225 | s.push(``); 229 | s.push('
'); 230 | } 231 | 232 | var span = document.createElement('span'); 233 | span.innerHTML=s.join(''); 234 | 235 | return span.firstChild; 236 | }, 237 | // create svg 238 | createSVG (qrCodeAlg){ 239 | let options = this.options; 240 | let count = qrCodeAlg.getModuleCount(); 241 | let scale = count / options.size; 242 | 243 | // create svg 244 | let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 245 | svg.setAttribute('width', options.size); 246 | svg.setAttribute('height', options.size); 247 | svg.setAttribute('viewBox', `0 0 ${count} ${count}`); 248 | 249 | for (let row = 0; row < count; row++) { 250 | for (let col = 0; col < count; col++) { 251 | let rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); 252 | let foreground = getForeGround({ 253 | row : row, 254 | col : col, 255 | count : count, 256 | options : options 257 | }); 258 | rect.setAttribute('x', col); 259 | rect.setAttribute('y', row); 260 | rect.setAttribute('width', 1); 261 | rect.setAttribute('height', 1); 262 | rect.setAttribute('stroke-width', 0); 263 | if(qrCodeAlg.modules[row][ col]){ 264 | rect.setAttribute('fill', foreground); 265 | }else{ 266 | rect.setAttribute('fill', options.background); 267 | } 268 | svg.appendChild(rect); 269 | } 270 | } 271 | 272 | // create image 273 | if(options.image){ 274 | let img = document.createElementNS('http://www.w3.org/2000/svg', 'image'); 275 | img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', options.image); 276 | img.setAttribute('x', ((count - options.imageSize * scale)/2).toFixed(2)); 277 | img.setAttribute('y', ((count - options.imageSize * scale)/2).toFixed(2)); 278 | img.setAttribute('width', options.imageSize * scale); 279 | img.setAttribute('height', options.imageSize * scale); 280 | svg.appendChild(img); 281 | } 282 | 283 | return svg; 284 | } 285 | }); 286 | module.exports = qrcode; 287 | -------------------------------------------------------------------------------- /src/qrcodealg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取单个字符的utf8编码 3 | * unicode BMP平面约65535个字符 4 | * @param {num} code 5 | * return {array} 6 | */ 7 | function unicodeFormat8(code){ 8 | // 1 byte 9 | var c0, c1, c2; 10 | if(code < 128){ 11 | return [code]; 12 | // 2 bytes 13 | }else if(code < 2048){ 14 | c0 = 192 + (code >> 6); 15 | c1 = 128 + (code & 63); 16 | return [c0, c1]; 17 | // 3 bytes 18 | }else{ 19 | c0 = 224 + (code >> 12); 20 | c1 = 128 + (code >> 6 & 63); 21 | c2 = 128 + (code & 63); 22 | return [c0, c1, c2]; 23 | } 24 | } 25 | 26 | /** 27 | * 获取字符串的utf8编码字节串 28 | * @param {string} string 29 | * @return {array} 30 | */ 31 | function getUTF8Bytes(string){ 32 | var utf8codes = []; 33 | for(var i=0; i= 7) { 101 | this.setupTypeNumber(true); 102 | } 103 | this.mapData(this.dataCache, maskPattern); 104 | }, 105 | /** 106 | * 设置二维码的位置探测图形 107 | * @param {num} row 探测图形的中心横坐标 108 | * @param {num} col 探测图形的中心纵坐标 109 | */ 110 | setupPositionProbePattern: function(row, col) { 111 | 112 | for (var r = -1; r <= 7; r++) { 113 | 114 | if (row + r <= -1 || this.moduleCount <= row + r) continue; 115 | 116 | for (var c = -1; c <= 7; c++) { 117 | 118 | if (col + c <= -1 || this.moduleCount <= col + c) continue; 119 | 120 | 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)) { 121 | this.modules[row + r][col + c] = true; 122 | } else { 123 | this.modules[row + r][col + c] = false; 124 | } 125 | } 126 | } 127 | }, 128 | /** 129 | * 创建二维码 130 | * @return {[type]} [description] 131 | */ 132 | createQrcode: function() { 133 | 134 | var minLostPoint = 0; 135 | var pattern = 0; 136 | var bestModules = null; 137 | 138 | for (var i = 0; i < 8; i++) { 139 | 140 | this.makeImpl(i); 141 | 142 | var lostPoint = QRUtil.getLostPoint(this); 143 | if (i == 0 || minLostPoint > lostPoint) { 144 | minLostPoint = lostPoint; 145 | pattern = i; 146 | bestModules = this.modules; 147 | } 148 | } 149 | this.modules = bestModules; 150 | this.setupTypeInfo(false, pattern); 151 | 152 | if (this.typeNumber >= 7) { 153 | this.setupTypeNumber(false); 154 | } 155 | 156 | }, 157 | /** 158 | * 设置定位图形 159 | * @return {[type]} [description] 160 | */ 161 | setupTimingPattern: function() { 162 | 163 | for (var r = 8; r < this.moduleCount - 8; r++) { 164 | if (this.modules[r][6] != null) { 165 | continue; 166 | } 167 | this.modules[r][6] = (r % 2 == 0); 168 | 169 | if (this.modules[6][r] != null) { 170 | continue; 171 | } 172 | this.modules[6][r] = (r % 2 == 0); 173 | } 174 | }, 175 | /** 176 | * 设置矫正图形 177 | * @return {[type]} [description] 178 | */ 179 | setupPositionAdjustPattern: function() { 180 | 181 | var pos = QRUtil.getPatternPosition(this.typeNumber); 182 | 183 | for (var i = 0; i < pos.length; i++) { 184 | 185 | for (var j = 0; j < pos.length; j++) { 186 | 187 | var row = pos[i]; 188 | var col = pos[j]; 189 | 190 | if (this.modules[row][col] != null) { 191 | continue; 192 | } 193 | 194 | for (var r = -2; r <= 2; r++) { 195 | 196 | for (var c = -2; c <= 2; c++) { 197 | 198 | if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) { 199 | this.modules[row + r][col + c] = true; 200 | } else { 201 | this.modules[row + r][col + c] = false; 202 | } 203 | } 204 | } 205 | } 206 | } 207 | }, 208 | /** 209 | * 设置版本信息(7以上版本才有) 210 | * @param {bool} test 是否处于判断最佳掩膜阶段 211 | * @return {[type]} [description] 212 | */ 213 | setupTypeNumber: function(test) { 214 | 215 | var bits = QRUtil.getBCHTypeNumber(this.typeNumber); 216 | 217 | for (var i = 0; i < 18; i++) { 218 | var mod = (!test && ((bits >> i) & 1) == 1); 219 | this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod; 220 | this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod; 221 | } 222 | }, 223 | /** 224 | * 设置格式信息(纠错等级和掩膜版本) 225 | * @param {bool} test 226 | * @param {num} maskPattern 掩膜版本 227 | * @return {} 228 | */ 229 | setupTypeInfo: function(test, maskPattern) { 230 | 231 | var data = (QRErrorCorrectLevel[this.errorCorrectLevel] << 3) | maskPattern; 232 | var bits = QRUtil.getBCHTypeInfo(data); 233 | 234 | // vertical 235 | for (var i = 0; i < 15; i++) { 236 | 237 | var mod = (!test && ((bits >> i) & 1) == 1); 238 | 239 | if (i < 6) { 240 | this.modules[i][8] = mod; 241 | } else if (i < 8) { 242 | this.modules[i + 1][8] = mod; 243 | } else { 244 | this.modules[this.moduleCount - 15 + i][8] = mod; 245 | } 246 | 247 | // horizontal 248 | var mod = (!test && ((bits >> i) & 1) == 1); 249 | 250 | if (i < 8) { 251 | this.modules[8][this.moduleCount - i - 1] = mod; 252 | } else if (i < 9) { 253 | this.modules[8][15 - i - 1 + 1] = mod; 254 | } else { 255 | this.modules[8][15 - i - 1] = mod; 256 | } 257 | } 258 | 259 | // fixed module 260 | this.modules[this.moduleCount - 8][8] = (!test); 261 | 262 | }, 263 | /** 264 | * 数据编码 265 | * @return {[type]} [description] 266 | */ 267 | createData: function() { 268 | var buffer = new QRBitBuffer(); 269 | var lengthBits = this.typeNumber > 9 ? 16 : 8; 270 | buffer.put(4, 4); //添加模式 271 | buffer.put(this.utf8bytes.length, lengthBits); 272 | for (var i = 0, l = this.utf8bytes.length; i < l; i++) { 273 | buffer.put(this.utf8bytes[i], 8); 274 | } 275 | if (buffer.length + 4 <= this.totalDataCount * 8) { 276 | buffer.put(0, 4); 277 | } 278 | 279 | // padding 280 | while (buffer.length % 8 != 0) { 281 | buffer.putBit(false); 282 | } 283 | 284 | // padding 285 | while (true) { 286 | 287 | if (buffer.length >= this.totalDataCount * 8) { 288 | break; 289 | } 290 | buffer.put(QRCodeAlg.PAD0, 8); 291 | 292 | if (buffer.length >= this.totalDataCount * 8) { 293 | break; 294 | } 295 | buffer.put(QRCodeAlg.PAD1, 8); 296 | } 297 | return this.createBytes(buffer); 298 | }, 299 | /** 300 | * 纠错码编码 301 | * @param {buffer} buffer 数据编码 302 | * @return {[type]} 303 | */ 304 | createBytes: function(buffer) { 305 | 306 | var offset = 0; 307 | 308 | var maxDcCount = 0; 309 | var maxEcCount = 0; 310 | 311 | var length = this.rsBlock.length / 3; 312 | 313 | var rsBlocks = new Array(); 314 | 315 | for (var i = 0; i < length; i++) { 316 | 317 | var count = this.rsBlock[i * 3 + 0]; 318 | var totalCount = this.rsBlock[i * 3 + 1]; 319 | var dataCount = this.rsBlock[i * 3 + 2]; 320 | 321 | for (var j = 0; j < count; j++) { 322 | rsBlocks.push([dataCount, totalCount]); 323 | } 324 | } 325 | 326 | var dcdata = new Array(rsBlocks.length); 327 | var ecdata = new Array(rsBlocks.length); 328 | 329 | for (var r = 0; r < rsBlocks.length; r++) { 330 | 331 | var dcCount = rsBlocks[r][0]; 332 | var ecCount = rsBlocks[r][1] - dcCount; 333 | 334 | maxDcCount = Math.max(maxDcCount, dcCount); 335 | maxEcCount = Math.max(maxEcCount, ecCount); 336 | 337 | dcdata[r] = new Array(dcCount); 338 | 339 | for (var i = 0; i < dcdata[r].length; i++) { 340 | dcdata[r][i] = 0xff & buffer.buffer[i + offset]; 341 | } 342 | offset += dcCount; 343 | 344 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); 345 | var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1); 346 | 347 | var modPoly = rawPoly.mod(rsPoly); 348 | ecdata[r] = new Array(rsPoly.getLength() - 1); 349 | for (var i = 0; i < ecdata[r].length; i++) { 350 | var modIndex = i + modPoly.getLength() - ecdata[r].length; 351 | ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0; 352 | } 353 | } 354 | 355 | var data = new Array(this.totalDataCount); 356 | var index = 0; 357 | 358 | for (var i = 0; i < maxDcCount; i++) { 359 | for (var r = 0; r < rsBlocks.length; r++) { 360 | if (i < dcdata[r].length) { 361 | data[index++] = dcdata[r][i]; 362 | } 363 | } 364 | } 365 | 366 | for (var i = 0; i < maxEcCount; i++) { 367 | for (var r = 0; r < rsBlocks.length; r++) { 368 | if (i < ecdata[r].length) { 369 | data[index++] = ecdata[r][i]; 370 | } 371 | } 372 | } 373 | 374 | return data; 375 | 376 | }, 377 | /** 378 | * 布置模块,构建最终信息 379 | * @param {} data 380 | * @param {} maskPattern 381 | * @return {} 382 | */ 383 | mapData: function(data, maskPattern) { 384 | 385 | var inc = -1; 386 | var row = this.moduleCount - 1; 387 | var bitIndex = 7; 388 | var byteIndex = 0; 389 | 390 | for (var col = this.moduleCount - 1; col > 0; col -= 2) { 391 | 392 | if (col == 6) col--; 393 | 394 | while (true) { 395 | 396 | for (var c = 0; c < 2; c++) { 397 | 398 | if (this.modules[row][col - c] == null) { 399 | 400 | var dark = false; 401 | 402 | if (byteIndex < data.length) { 403 | dark = (((data[byteIndex] >>> bitIndex) & 1) == 1); 404 | } 405 | 406 | var mask = QRUtil.getMask(maskPattern, row, col - c); 407 | 408 | if (mask) { 409 | dark = !dark; 410 | } 411 | 412 | this.modules[row][col - c] = dark; 413 | bitIndex--; 414 | 415 | if (bitIndex == -1) { 416 | byteIndex++; 417 | bitIndex = 7; 418 | } 419 | } 420 | } 421 | 422 | row += inc; 423 | 424 | if (row < 0 || this.moduleCount <= row) { 425 | row -= inc; 426 | inc = -inc; 427 | break; 428 | } 429 | } 430 | } 431 | } 432 | 433 | }; 434 | /** 435 | * 填充字段 436 | */ 437 | QRCodeAlg.PAD0 = 0xEC; 438 | QRCodeAlg.PAD1 = 0x11; 439 | 440 | 441 | //--------------------------------------------------------------------- 442 | // 纠错等级对应的编码 443 | //--------------------------------------------------------------------- 444 | 445 | var QRErrorCorrectLevel = [1, 0, 3, 2]; 446 | 447 | //--------------------------------------------------------------------- 448 | // 掩膜版本 449 | //--------------------------------------------------------------------- 450 | 451 | var QRMaskPattern = { 452 | PATTERN000: 0, 453 | PATTERN001: 1, 454 | PATTERN010: 2, 455 | PATTERN011: 3, 456 | PATTERN100: 4, 457 | PATTERN101: 5, 458 | PATTERN110: 6, 459 | PATTERN111: 7 460 | }; 461 | 462 | //--------------------------------------------------------------------- 463 | // 工具类 464 | //--------------------------------------------------------------------- 465 | 466 | var QRUtil = { 467 | 468 | /* 469 | 每个版本矫正图形的位置 470 | */ 471 | PATTERN_POSITION_TABLE: [ 472 | [], 473 | [6, 18], 474 | [6, 22], 475 | [6, 26], 476 | [6, 30], 477 | [6, 34], 478 | [6, 22, 38], 479 | [6, 24, 42], 480 | [6, 26, 46], 481 | [6, 28, 50], 482 | [6, 30, 54], 483 | [6, 32, 58], 484 | [6, 34, 62], 485 | [6, 26, 46, 66], 486 | [6, 26, 48, 70], 487 | [6, 26, 50, 74], 488 | [6, 30, 54, 78], 489 | [6, 30, 56, 82], 490 | [6, 30, 58, 86], 491 | [6, 34, 62, 90], 492 | [6, 28, 50, 72, 94], 493 | [6, 26, 50, 74, 98], 494 | [6, 30, 54, 78, 102], 495 | [6, 28, 54, 80, 106], 496 | [6, 32, 58, 84, 110], 497 | [6, 30, 58, 86, 114], 498 | [6, 34, 62, 90, 118], 499 | [6, 26, 50, 74, 98, 122], 500 | [6, 30, 54, 78, 102, 126], 501 | [6, 26, 52, 78, 104, 130], 502 | [6, 30, 56, 82, 108, 134], 503 | [6, 34, 60, 86, 112, 138], 504 | [6, 30, 58, 86, 114, 142], 505 | [6, 34, 62, 90, 118, 146], 506 | [6, 30, 54, 78, 102, 126, 150], 507 | [6, 24, 50, 76, 102, 128, 154], 508 | [6, 28, 54, 80, 106, 132, 158], 509 | [6, 32, 58, 84, 110, 136, 162], 510 | [6, 26, 54, 82, 110, 138, 166], 511 | [6, 30, 58, 86, 114, 142, 170] 512 | ], 513 | 514 | G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0), 515 | G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0), 516 | G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1), 517 | 518 | /* 519 | BCH编码格式信息 520 | */ 521 | getBCHTypeInfo: function(data) { 522 | var d = data << 10; 523 | while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) { 524 | d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15))); 525 | } 526 | return ((data << 10) | d) ^ QRUtil.G15_MASK; 527 | }, 528 | /* 529 | BCH编码版本信息 530 | */ 531 | getBCHTypeNumber: function(data) { 532 | var d = data << 12; 533 | while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) { 534 | d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18))); 535 | } 536 | return (data << 12) | d; 537 | }, 538 | /* 539 | 获取BCH位信息 540 | */ 541 | getBCHDigit: function(data) { 542 | 543 | var digit = 0; 544 | 545 | while (data != 0) { 546 | digit++; 547 | data >>>= 1; 548 | } 549 | 550 | return digit; 551 | }, 552 | /* 553 | 获取版本对应的矫正图形位置 554 | */ 555 | getPatternPosition: function(typeNumber) { 556 | return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]; 557 | }, 558 | /* 559 | 掩膜算法 560 | */ 561 | getMask: function(maskPattern, i, j) { 562 | 563 | switch (maskPattern) { 564 | 565 | case QRMaskPattern.PATTERN000: 566 | return (i + j) % 2 == 0; 567 | case QRMaskPattern.PATTERN001: 568 | return i % 2 == 0; 569 | case QRMaskPattern.PATTERN010: 570 | return j % 3 == 0; 571 | case QRMaskPattern.PATTERN011: 572 | return (i + j) % 3 == 0; 573 | case QRMaskPattern.PATTERN100: 574 | return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0; 575 | case QRMaskPattern.PATTERN101: 576 | return (i * j) % 2 + (i * j) % 3 == 0; 577 | case QRMaskPattern.PATTERN110: 578 | return ((i * j) % 2 + (i * j) % 3) % 2 == 0; 579 | case QRMaskPattern.PATTERN111: 580 | return ((i * j) % 3 + (i + j) % 2) % 2 == 0; 581 | 582 | default: 583 | throw new Error("bad maskPattern:" + maskPattern); 584 | } 585 | }, 586 | /* 587 | 获取RS的纠错多项式 588 | */ 589 | getErrorCorrectPolynomial: function(errorCorrectLength) { 590 | 591 | var a = new QRPolynomial([1], 0); 592 | 593 | for (var i = 0; i < errorCorrectLength; i++) { 594 | a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0)); 595 | } 596 | 597 | return a; 598 | }, 599 | /* 600 | 获取评价 601 | */ 602 | getLostPoint: function(qrCode) { 603 | 604 | var moduleCount = qrCode.getModuleCount(), 605 | lostPoint = 0, 606 | darkCount = 0; 607 | 608 | for (var row = 0; row < moduleCount; row++) { 609 | 610 | var sameCount = 0; 611 | var head = qrCode.modules[row][0]; 612 | 613 | for (var col = 0; col < moduleCount; col++) { 614 | 615 | var current = qrCode.modules[row][col]; 616 | 617 | //level 3 评价 618 | if( col < moduleCount-6){ 619 | if (current && !qrCode.modules[row][ col + 1] && qrCode.modules[row][ col + 2] && qrCode.modules[row][ col + 3] && qrCode.modules[row][ col + 4] && !qrCode.modules[row][ col + 5] && qrCode.modules[row][ col + 6]) { 620 | if(col < moduleCount-10){ 621 | if(qrCode.modules[row][ col + 7] &&qrCode.modules[row][ col + 8] &&qrCode.modules[row][ col + 9] &&qrCode.modules[row][ col + 10]){ 622 | lostPoint += 40; 623 | } 624 | } else if(col > 3) { 625 | if(qrCode.modules[row][ col - 1] &&qrCode.modules[row][ col - 2] &&qrCode.modules[row][ col - 3] &&qrCode.modules[row][ col - 4]){ 626 | lostPoint += 40; 627 | } 628 | } 629 | 630 | } 631 | } 632 | 633 | //level 2 评价 634 | if( (row < moduleCount-1)&&(col < moduleCount-1) ){ 635 | var count = 0; 636 | if (current) count++; 637 | if (qrCode.modules[row + 1][ col]) count++; 638 | if (qrCode.modules[row][ col + 1]) count++; 639 | if (qrCode.modules[row + 1][ col + 1]) count++; 640 | if (count == 0 || count == 4) { 641 | lostPoint += 3; 642 | } 643 | } 644 | 645 | //level 1 评价 646 | if(head ^ current){ 647 | sameCount ++; 648 | } else { 649 | head = current; 650 | if (sameCount >= 5) { 651 | lostPoint += (3 + sameCount - 5); 652 | } 653 | sameCount = 1; 654 | } 655 | 656 | //level 4 评价 657 | if (current) { 658 | darkCount++; 659 | } 660 | 661 | } 662 | } 663 | 664 | for (var col = 0; col < moduleCount; col++) { 665 | 666 | var sameCount = 0; 667 | var head = qrCode.modules[0][col]; 668 | 669 | for (var row = 0; row < moduleCount; row++) { 670 | 671 | var current = qrCode.modules[row][col]; 672 | 673 | //level 3 评价 674 | if( row < moduleCount-6){ 675 | if (current && !qrCode.modules[row + 1][ col] && qrCode.modules[row + 2][ col] && qrCode.modules[row + 3][ col] && qrCode.modules[row + 4][ col] && !qrCode.modules[row + 5][ col] && qrCode.modules[row + 6][ col]) { 676 | if(row < moduleCount-10){ 677 | if(qrCode.modules[row + 7][ col] && qrCode.modules[row + 8][ col]&& qrCode.modules[row + 9][ col]&& qrCode.modules[row + 10][ col]){ 678 | lostPoint += 40; 679 | } 680 | } else if(row > 3) { 681 | if(qrCode.modules[row - 1][ col] && qrCode.modules[row - 2][ col]&& qrCode.modules[row - 3][ col]&& qrCode.modules[row - 4][ col]){ 682 | lostPoint += 40; 683 | } 684 | } 685 | } 686 | } 687 | 688 | //level 1 评价 689 | if(head ^ current){ 690 | sameCount ++; 691 | } else { 692 | head = current; 693 | if (sameCount >= 5) { 694 | lostPoint += (3 + sameCount - 5); 695 | } 696 | sameCount = 1; 697 | } 698 | 699 | } 700 | } 701 | 702 | // LEVEL4 703 | 704 | var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; 705 | lostPoint += ratio * 10; 706 | 707 | return lostPoint; 708 | } 709 | 710 | }; 711 | 712 | 713 | //--------------------------------------------------------------------- 714 | // QRMath使用的数学工具 715 | //--------------------------------------------------------------------- 716 | 717 | var QRMath = { 718 | /* 719 | 将n转化为a^m 720 | */ 721 | glog: function(n) { 722 | 723 | if (n < 1) { 724 | throw new Error("glog(" + n + ")"); 725 | } 726 | 727 | return QRMath.LOG_TABLE[n]; 728 | }, 729 | /* 730 | 将a^m转化为n 731 | */ 732 | gexp: function(n) { 733 | 734 | while (n < 0) { 735 | n += 255; 736 | } 737 | 738 | while (n >= 256) { 739 | n -= 255; 740 | } 741 | 742 | return QRMath.EXP_TABLE[n]; 743 | }, 744 | 745 | EXP_TABLE: new Array(256), 746 | 747 | LOG_TABLE: new Array(256) 748 | 749 | }; 750 | 751 | for (var i = 0; i < 8; i++) { 752 | QRMath.EXP_TABLE[i] = 1 << i; 753 | } 754 | for (var i = 8; i < 256; i++) { 755 | QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8]; 756 | } 757 | for (var i = 0; i < 255; i++) { 758 | QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; 759 | } 760 | 761 | //--------------------------------------------------------------------- 762 | // QRPolynomial 多项式 763 | //--------------------------------------------------------------------- 764 | /** 765 | * 多项式类 766 | * @param {Array} num 系数 767 | * @param {num} shift a^shift 768 | */ 769 | function QRPolynomial(num, shift) { 770 | 771 | if (num.length == undefined) { 772 | throw new Error(num.length + "/" + shift); 773 | } 774 | 775 | var offset = 0; 776 | 777 | while (offset < num.length && num[offset] == 0) { 778 | offset++; 779 | } 780 | 781 | this.num = new Array(num.length - offset + shift); 782 | for (var i = 0; i < num.length - offset; i++) { 783 | this.num[i] = num[i + offset]; 784 | } 785 | } 786 | 787 | QRPolynomial.prototype = { 788 | 789 | get: function(index) { 790 | return this.num[index]; 791 | }, 792 | 793 | getLength: function() { 794 | return this.num.length; 795 | }, 796 | /** 797 | * 多项式乘法 798 | * @param {QRPolynomial} e 被乘多项式 799 | * @return {[type]} [description] 800 | */ 801 | multiply: function(e) { 802 | 803 | var num = new Array(this.getLength() + e.getLength() - 1); 804 | 805 | for (var i = 0; i < this.getLength(); i++) { 806 | for (var j = 0; j < e.getLength(); j++) { 807 | num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j))); 808 | } 809 | } 810 | 811 | return new QRPolynomial(num, 0); 812 | }, 813 | /** 814 | * 多项式模运算 815 | * @param {QRPolynomial} e 模多项式 816 | * @return {} 817 | */ 818 | mod: function(e) { 819 | var tl = this.getLength(), 820 | el = e.getLength(); 821 | if (tl - el < 0) { 822 | return this; 823 | } 824 | var num = new Array(tl); 825 | for (var i = 0; i < tl; i++) { 826 | num[i] = this.get(i); 827 | } 828 | while (num.length >= el) { 829 | var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0)); 830 | 831 | for (var i = 0; i < e.getLength(); i++) { 832 | num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio); 833 | } 834 | while (num[0] == 0) { 835 | num.shift(); 836 | } 837 | } 838 | return new QRPolynomial(num, 0); 839 | } 840 | }; 841 | 842 | //--------------------------------------------------------------------- 843 | // RS_BLOCK_TABLE 844 | //--------------------------------------------------------------------- 845 | /* 846 | 二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数] 847 | */ 848 | var RS_BLOCK_TABLE = [ 849 | 850 | // L 851 | // M 852 | // Q 853 | // H 854 | 855 | // 1 856 | [1, 26, 19], 857 | [1, 26, 16], 858 | [1, 26, 13], 859 | [1, 26, 9], 860 | 861 | // 2 862 | [1, 44, 34], 863 | [1, 44, 28], 864 | [1, 44, 22], 865 | [1, 44, 16], 866 | 867 | // 3 868 | [1, 70, 55], 869 | [1, 70, 44], 870 | [2, 35, 17], 871 | [2, 35, 13], 872 | 873 | // 4 874 | [1, 100, 80], 875 | [2, 50, 32], 876 | [2, 50, 24], 877 | [4, 25, 9], 878 | 879 | // 5 880 | [1, 134, 108], 881 | [2, 67, 43], 882 | [2, 33, 15, 2, 34, 16], 883 | [2, 33, 11, 2, 34, 12], 884 | 885 | // 6 886 | [2, 86, 68], 887 | [4, 43, 27], 888 | [4, 43, 19], 889 | [4, 43, 15], 890 | 891 | // 7 892 | [2, 98, 78], 893 | [4, 49, 31], 894 | [2, 32, 14, 4, 33, 15], 895 | [4, 39, 13, 1, 40, 14], 896 | 897 | // 8 898 | [2, 121, 97], 899 | [2, 60, 38, 2, 61, 39], 900 | [4, 40, 18, 2, 41, 19], 901 | [4, 40, 14, 2, 41, 15], 902 | 903 | // 9 904 | [2, 146, 116], 905 | [3, 58, 36, 2, 59, 37], 906 | [4, 36, 16, 4, 37, 17], 907 | [4, 36, 12, 4, 37, 13], 908 | 909 | // 10 910 | [2, 86, 68, 2, 87, 69], 911 | [4, 69, 43, 1, 70, 44], 912 | [6, 43, 19, 2, 44, 20], 913 | [6, 43, 15, 2, 44, 16], 914 | 915 | // 11 916 | [4, 101, 81], 917 | [1, 80, 50, 4, 81, 51], 918 | [4, 50, 22, 4, 51, 23], 919 | [3, 36, 12, 8, 37, 13], 920 | 921 | // 12 922 | [2, 116, 92, 2, 117, 93], 923 | [6, 58, 36, 2, 59, 37], 924 | [4, 46, 20, 6, 47, 21], 925 | [7, 42, 14, 4, 43, 15], 926 | 927 | // 13 928 | [4, 133, 107], 929 | [8, 59, 37, 1, 60, 38], 930 | [8, 44, 20, 4, 45, 21], 931 | [12, 33, 11, 4, 34, 12], 932 | 933 | // 14 934 | [3, 145, 115, 1, 146, 116], 935 | [4, 64, 40, 5, 65, 41], 936 | [11, 36, 16, 5, 37, 17], 937 | [11, 36, 12, 5, 37, 13], 938 | 939 | // 15 940 | [5, 109, 87, 1, 110, 88], 941 | [5, 65, 41, 5, 66, 42], 942 | [5, 54, 24, 7, 55, 25], 943 | [11, 36, 12], 944 | 945 | // 16 946 | [5, 122, 98, 1, 123, 99], 947 | [7, 73, 45, 3, 74, 46], 948 | [15, 43, 19, 2, 44, 20], 949 | [3, 45, 15, 13, 46, 16], 950 | 951 | // 17 952 | [1, 135, 107, 5, 136, 108], 953 | [10, 74, 46, 1, 75, 47], 954 | [1, 50, 22, 15, 51, 23], 955 | [2, 42, 14, 17, 43, 15], 956 | 957 | // 18 958 | [5, 150, 120, 1, 151, 121], 959 | [9, 69, 43, 4, 70, 44], 960 | [17, 50, 22, 1, 51, 23], 961 | [2, 42, 14, 19, 43, 15], 962 | 963 | // 19 964 | [3, 141, 113, 4, 142, 114], 965 | [3, 70, 44, 11, 71, 45], 966 | [17, 47, 21, 4, 48, 22], 967 | [9, 39, 13, 16, 40, 14], 968 | 969 | // 20 970 | [3, 135, 107, 5, 136, 108], 971 | [3, 67, 41, 13, 68, 42], 972 | [15, 54, 24, 5, 55, 25], 973 | [15, 43, 15, 10, 44, 16], 974 | 975 | // 21 976 | [4, 144, 116, 4, 145, 117], 977 | [17, 68, 42], 978 | [17, 50, 22, 6, 51, 23], 979 | [19, 46, 16, 6, 47, 17], 980 | 981 | // 22 982 | [2, 139, 111, 7, 140, 112], 983 | [17, 74, 46], 984 | [7, 54, 24, 16, 55, 25], 985 | [34, 37, 13], 986 | 987 | // 23 988 | [4, 151, 121, 5, 152, 122], 989 | [4, 75, 47, 14, 76, 48], 990 | [11, 54, 24, 14, 55, 25], 991 | [16, 45, 15, 14, 46, 16], 992 | 993 | // 24 994 | [6, 147, 117, 4, 148, 118], 995 | [6, 73, 45, 14, 74, 46], 996 | [11, 54, 24, 16, 55, 25], 997 | [30, 46, 16, 2, 47, 17], 998 | 999 | // 25 1000 | [8, 132, 106, 4, 133, 107], 1001 | [8, 75, 47, 13, 76, 48], 1002 | [7, 54, 24, 22, 55, 25], 1003 | [22, 45, 15, 13, 46, 16], 1004 | 1005 | // 26 1006 | [10, 142, 114, 2, 143, 115], 1007 | [19, 74, 46, 4, 75, 47], 1008 | [28, 50, 22, 6, 51, 23], 1009 | [33, 46, 16, 4, 47, 17], 1010 | 1011 | // 27 1012 | [8, 152, 122, 4, 153, 123], 1013 | [22, 73, 45, 3, 74, 46], 1014 | [8, 53, 23, 26, 54, 24], 1015 | [12, 45, 15, 28, 46, 16], 1016 | 1017 | // 28 1018 | [3, 147, 117, 10, 148, 118], 1019 | [3, 73, 45, 23, 74, 46], 1020 | [4, 54, 24, 31, 55, 25], 1021 | [11, 45, 15, 31, 46, 16], 1022 | 1023 | // 29 1024 | [7, 146, 116, 7, 147, 117], 1025 | [21, 73, 45, 7, 74, 46], 1026 | [1, 53, 23, 37, 54, 24], 1027 | [19, 45, 15, 26, 46, 16], 1028 | 1029 | // 30 1030 | [5, 145, 115, 10, 146, 116], 1031 | [19, 75, 47, 10, 76, 48], 1032 | [15, 54, 24, 25, 55, 25], 1033 | [23, 45, 15, 25, 46, 16], 1034 | 1035 | // 31 1036 | [13, 145, 115, 3, 146, 116], 1037 | [2, 74, 46, 29, 75, 47], 1038 | [42, 54, 24, 1, 55, 25], 1039 | [23, 45, 15, 28, 46, 16], 1040 | 1041 | // 32 1042 | [17, 145, 115], 1043 | [10, 74, 46, 23, 75, 47], 1044 | [10, 54, 24, 35, 55, 25], 1045 | [19, 45, 15, 35, 46, 16], 1046 | 1047 | // 33 1048 | [17, 145, 115, 1, 146, 116], 1049 | [14, 74, 46, 21, 75, 47], 1050 | [29, 54, 24, 19, 55, 25], 1051 | [11, 45, 15, 46, 46, 16], 1052 | 1053 | // 34 1054 | [13, 145, 115, 6, 146, 116], 1055 | [14, 74, 46, 23, 75, 47], 1056 | [44, 54, 24, 7, 55, 25], 1057 | [59, 46, 16, 1, 47, 17], 1058 | 1059 | // 35 1060 | [12, 151, 121, 7, 152, 122], 1061 | [12, 75, 47, 26, 76, 48], 1062 | [39, 54, 24, 14, 55, 25], 1063 | [22, 45, 15, 41, 46, 16], 1064 | 1065 | // 36 1066 | [6, 151, 121, 14, 152, 122], 1067 | [6, 75, 47, 34, 76, 48], 1068 | [46, 54, 24, 10, 55, 25], 1069 | [2, 45, 15, 64, 46, 16], 1070 | 1071 | // 37 1072 | [17, 152, 122, 4, 153, 123], 1073 | [29, 74, 46, 14, 75, 47], 1074 | [49, 54, 24, 10, 55, 25], 1075 | [24, 45, 15, 46, 46, 16], 1076 | 1077 | // 38 1078 | [4, 152, 122, 18, 153, 123], 1079 | [13, 74, 46, 32, 75, 47], 1080 | [48, 54, 24, 14, 55, 25], 1081 | [42, 45, 15, 32, 46, 16], 1082 | 1083 | // 39 1084 | [20, 147, 117, 4, 148, 118], 1085 | [40, 75, 47, 7, 76, 48], 1086 | [43, 54, 24, 22, 55, 25], 1087 | [10, 45, 15, 67, 46, 16], 1088 | 1089 | // 40 1090 | [19, 148, 118, 6, 149, 119], 1091 | [18, 75, 47, 31, 76, 48], 1092 | [34, 54, 24, 34, 55, 25], 1093 | [20, 45, 15, 61, 46, 16] 1094 | ]; 1095 | 1096 | /** 1097 | * 根据数据获取对应版本 1098 | * @return {[type]} [description] 1099 | */ 1100 | QRCodeAlg.prototype.getRightType = function() { 1101 | for (var typeNumber = 1; typeNumber < 41; typeNumber++) { 1102 | var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel]; 1103 | if (rsBlock == undefined) { 1104 | throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel); 1105 | } 1106 | var length = rsBlock.length / 3; 1107 | var totalDataCount = 0; 1108 | for (var i = 0; i < length; i++) { 1109 | var count = rsBlock[i * 3 + 0]; 1110 | var dataCount = rsBlock[i * 3 + 2]; 1111 | totalDataCount += dataCount * count; 1112 | } 1113 | 1114 | var lengthBytes = typeNumber > 9 ? 2 : 1; 1115 | if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) { 1116 | this.typeNumber = typeNumber; 1117 | this.rsBlock = rsBlock; 1118 | this.totalDataCount = totalDataCount; 1119 | break; 1120 | } 1121 | } 1122 | }; 1123 | 1124 | //--------------------------------------------------------------------- 1125 | // QRBitBuffer 1126 | //--------------------------------------------------------------------- 1127 | 1128 | function QRBitBuffer() { 1129 | this.buffer = new Array(); 1130 | this.length = 0; 1131 | } 1132 | 1133 | QRBitBuffer.prototype = { 1134 | 1135 | get: function(index) { 1136 | var bufIndex = Math.floor(index / 8); 1137 | return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1); 1138 | }, 1139 | 1140 | put: function(num, length) { 1141 | for (var i = 0; i < length; i++) { 1142 | this.putBit(((num >>> (length - i - 1)) & 1)); 1143 | } 1144 | }, 1145 | 1146 | putBit: function(bit) { 1147 | 1148 | var bufIndex = Math.floor(this.length / 8); 1149 | if (this.buffer.length <= bufIndex) { 1150 | this.buffer.push(0); 1151 | } 1152 | 1153 | if (bit) { 1154 | this.buffer[bufIndex] |= (0x80 >>> (this.length % 8)); 1155 | } 1156 | 1157 | this.length++; 1158 | } 1159 | }; 1160 | module.exports = QRCodeAlg; 1161 | -------------------------------------------------------------------------------- /tests/qrcode-spec.js: -------------------------------------------------------------------------------- 1 | 2 | var qrcode = require('../src/index.js'); 3 | var $ = require('jquery'); 4 | var expect = require('expect.js'); 5 | 6 | describe('qrcode', function() { 7 | it('参数为空', function() { 8 | var qrnode = new qrcode(); 9 | 10 | }); 11 | it('参数是字符串', function() { 12 | var qrnode = new qrcode('www.alipay.com'); 13 | }); 14 | it('参数为JSON格式且为空', function() { 15 | var qrnode = new qrcode({}); 16 | }); 17 | it('设置地址参数', function() { 18 | var qrnode = new qrcode({ 19 | text:'www.alipay.com' 20 | }); 21 | }); 22 | it('设置二维码高度', function() { 23 | var qrnode = new qrcode({ 24 | height:300 25 | }); 26 | }); 27 | it('设置二维码宽度', function() { 28 | var qrnode = new qrcode({ 29 | width:300 30 | }); 31 | }); 32 | it('设置二维码前景色', function() { 33 | var qrnode = new qrcode({ 34 | background:'#100' 35 | }); 36 | }); 37 | it('设置二维码背景色', function() { 38 | var qrnode = new qrcode({ 39 | foreground:'#011' 40 | }); 41 | }); 42 | it('以table来显示', function() { 43 | var qrnode = new qrcode({ 44 | render:'table' 45 | }); 46 | }); 47 | it('以svg来显示', function() { 48 | var qrnode = new qrcode({ 49 | render:'svg' 50 | }); 51 | }); 52 | it('以canvas来显示', function() { 53 | var qrnode = new qrcode({ 54 | render:'canvas' 55 | }); 56 | }); 57 | it('以如果render传入错误参数,以default形式显示', function() { 58 | var qrnode = new qrcode({ 59 | render:'adsjlj' 60 | }); 61 | }); 62 | it('最长字符串', function() { 63 | var s = ''; 64 | for(var i = 0; i < 2953; i++){ 65 | s += i%10; 66 | } 67 | var qrnode = new qrcode({ 68 | width:500, 69 | height:500, 70 | text:s, 71 | correctLevel:0 72 | }); 73 | }); 74 | it('超过最长字符串容错处理,截取前2953个字符', function() { 75 | var s = ''; 76 | for(var i = 0; i < 3000; i++){ 77 | s += i%10; 78 | } 79 | var qrnode = new qrcode({ 80 | width:500, 81 | height:500, 82 | text:s, 83 | correctLevel:0 84 | }); 85 | }); 86 | it('中文容错', function() { 87 | var qrnode = new qrcode('你好'); 88 | }); 89 | it('设置logo', function() { 90 | var qrnode = new qrcode({ 91 | image : 'https://t.alipayobjects.com/images/rmsweb/T1ZsxhXdxbXXXXXXXX.png' 92 | }); 93 | }); 94 | it('设置pdground', function() { 95 | var qrnode = new qrcode({ 96 | pdground : '#ff0000' 97 | }); 98 | }); 99 | }); 100 | --------------------------------------------------------------------------------