├── .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 | [](http://spmjs.io/package/arale-qrcode)
6 | [](https://travis-ci.org/aralejs/qrcode)
7 | [](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 |
--------------------------------------------------------------------------------