UNPKG

@akamfoad/qrcode

Version:

The library is generating QR codes as SVG, HTML5 Canvas, PNG and JPG files, or text.

609 lines (493 loc) 30.1 kB
[![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) # @akamfoad/qrcode The library is for generating QR codes like SVG, HTML5 Canvas, PNG and JPG files, or text. #### Acknowledgment This project initially has been forked from @cheprasov/js-qrcode. #### Features - The library has classes for generation SVG, Canvas, PNG / JPG or text with QR code. - it is easy to use and configure (error correction level, type number, padding and so on). - Supports inverting of data. - The library is covered by tests. - Easy to extend the classes or create own class for generation QR code. - SVG (see [class `QRCodeSVG`](#33-class-qrcodesvg)) - Returns generated QR code as SVG (xml) or DataURL. - Optimized SVG structure for xml and for DataURL. - Supports adding an image (logo) to QR code (allows to use url, dataUrl, Image, Canvas). See [example](#23-qr-code-with-image) - Allows to specify relative/abcolute position/size of image on QR code. - Canvas (see [class `QRCodeCanvas`](#32-class-qrcodecanvas)) - Draws QR code on provided canvas or returns new canvas element with QR code. - Allows to get PNG / JPG files with QR code like dataUrl. - Supports adding a image (logo) to QR code (allows to use url, dataUrl, Image, Canvas). - Has image loader for images for QR code via Promise. - It is possible to specify scale or canvas size. - Allows to specify relative/abcolute position/size of image on QR code. - Text (see [class `QRCodeText`](34-class-qrcodetext)) - It is possible to create QR code for consoles or text output. #### Plans to do - To add possibility to use patterns, themes and flexible customisation of QR code. - to add support of rgb & rgba format for canvas colors. ### TypeDocs <https://akamfoad.github.io/qrcode/> ### 1. How to install ```bash > npm install @akamfoad/qrcode ``` ```javascript import { QRCodeRaw, QRCodeSVG, QRCodeCanvas, QRCodeText, } from '@akamfoad/qrcode'; ``` ### 2. Quick examples #### 2.1. Create SVG QR Code ```javascript import { QRCodeSVG } from '@akamfoad/qrcode'; const qrSVG = new QRCodeSVG('some value for QR code'); const dataUrlWithSVGQRCode = qrSVG.toDataUrl(); const xmlWithQRCode = qrSVG.toString(); ``` #### 2.2. Create Image QR Code ```javascript import { QRCodeCanvas } from '@akamfoad/qrcode'; const qrCanvas = new QRCodeCanvas('some value for QR code'); const dataUrlWithQRCode = qrCanvas.toDataUrl(); const canvasWithQRCode = qrCanvas.getCanvas(); ``` #### 2.3. QR Code with Image ```javascript import { QRCodeSVG } from '@akamfoad/qrcode'; const divElement = document.getElementById('some-id'); const qrSVG = new QRCodeSVG('JohnDoe', { level: 'Q', image: { source: 'GitHub-Mark-120px-plus.png', width: '20%', height: '20%', x: 'center', y: 'center', }, }); divElement.innerHTML = qrSVG.toString(); ``` Result: Note, padding & image.border = 1 by default. ![test](./assets/qrcode_scheme.png) ### 3. Documentation #### 3.1. class `QRCodeRaw` The class base class for all QR code generators, returns raw data with information about QR dots and padding. ```javascript import { QRCodeRaw } from '@akamfoad/qrcode'; ``` Public methods: #### `constructor(value: string, config: object)` Create new instance of QRCodeRaw Params: - `value` (string) - new value for encoding to QR code - `config` (object, optional) - parameters of configuration - `level` (string, optional, default = `L`) - error correction level. Note, the level affects QR Code data size. Allowed values: - `L` - Allows recovery of up to 7% data loss - `M` - Allows recovery of up to 15% data loss - `Q` - Allows recovery of up to 25% data loss - `H` - Allows recovery of up to 30% data loss - `typeNumber` (number, optional, default = `0`) - data capacity type, see details in [appendix 4.1](#41-data-capacity-in-bytes). Type number (`1` ~ `40`), or `0` for auto detection. - `invert` (boolean, optional, default = `false`) - inverting data of QR code. - `padding` (number, optional, default = `1`) - count of white spaces on sides QR code. 1 unit has size like 1 information dot. - `errorsEnabled`: (boolean, optional, default = `false`) - if it is enabled and QR code generator can not create a QR Code then an error will thrown. If it is disabled then methods will return `null` of fail. #### `setValue(value: string): void` Set new value for encoding to QR code Params: - `value` (string) - new value for encoding to QR code #### `getDataSize(): number` Get size of QR code width / height (width and height are equal) Method will return `0` if QR code can not be generated by some reasons. #### `getData(): boolean[][]` Get raw data of QR code. Method will return `null` if QR code can not be generated by some reasons. Example: ```javascript import { QRCodeRaw } from '@akamfoad/qrcode'; const config = { level: 'H', // use high error correction level padding: 0, // do not use padding around qr code data }; const qrRaw = new QRCodeRaw('some value', config); const qrCodeRaw = qrRaw.getData(); if (qrCodeRaw) { console.log(qrCodeRaw); // [ // 0: [true, true, true, true, ... true], // 1: [true, false, false, false, ... true], // ... // 24: [true, true, true, ... true], // ] } ``` #### 3.2. class `QRCodeCanvas` The QR code generator based on HTML5 Canvas. It can create a canvas with QR code, or PNG/JPG data url. The class extends [`QRCodeRaw`](31-class-qrcoderaw), therefore please see there description about public method and configuration params. ```javascript import { QRCodeCanvas } from '@akamfoad/qrcode'; ``` Public methods: #### `constructor(value: string, config: object)` Create new instance of QRCodeCanvas. Please see config description of [`QRCodeRaw.constructor`](#constructorvalue-string-config-object). Config has additional parameters: - `config` (object, optional) - parameters of configuration - see config of [`QRCodeRaw.constructor`](#constructorvalue-string-config-object) - `fgColor` (string, optional, default = `#000`) - foreground color of the QR code, is it allowed to use the next formats: - `RGB` or `#RGB`, example: `#ABC`, will be converted to `#AABBCC` - `RGBA` or `#RGBA`, example: `#ABCD`, will be converted to `#AABBCCDD` - `RRGGBB` or `#RRGGBB`, example: `#AABBCC` - `RRGGBBAA` or `#RRGGBBAA`, example: `#AABBCCDD` - Other formats (like `red`, `rgb(...)`, `rgba(...)`) are not supported and will be converted to `#0000` - `bgColor` (string, optional, default = `#FFF`) - background color of the QR code, see description of `fgColor`. - `scale` (number, optional, default = `10`) - scale size of QR code. For example, when scale is 5 then QR generator will use 5 pixel for draw 1 data dot. - `size` (number, optional, default = `null`) - size (width & height) of canvas in pixels. If size is specified then scale param will be ignored. Note, that the original canvas with QR code will be stretched to the specified size. See [image scheme](#23-qr-code-with-image) - `image` (object, optional, default = `null`) - parameters on an image, that should be added to QR code, like logo. - `source` (string|Image|Canvas) - source of image for QR Code, allowed to use the next types: - `string` - url to resource or dataUrl of image. - `Image` - it is allowed to use Image. The image's src should be loaded before use it. - `Canvas` - allowed to use HTML5 canvas element. - `width` (number|string) - width of the image in QR code dots (not a pixel), allowed formats: - `<number>` - defines the width of image, example: `width: 30` - `<number>%` - defines the width in percent of QR code without padding, example: `width: '20%'` - `height` (number|string) - height of the image in QR code dots, see `width` - `x` (number|string, optional, default = `0`) - position of image on QR code by horizontal in QR code dots (not a pixel), allowed formats: - `<number>` - sets the left edge position from left to right, example: `x: 10` - `<number>%` - sets the left edge position in % of QR code without padding. Negative values are allowed. Example: `x: '50%'` - `left` - aligns the image to the left, example: `x: 'left'` - `right` - aligns the image to the right, example: `x: 'right'` - `center` - Centers the image in center of QR code, example: `x: 'center'` - `left <number>` - the same as `<number>` - `left <number>%` - the same as `<number>%` - `right <number>` - sets the right edge position from right to left, example: `x: 'right 5'` - `right <number>%` - sets the tight edge position in % of QR code without padding, example: `x: 'right 10%'` - `y` (number|string, optional, default = `0`) - position of image on QR code by vertical in QR code dots (not a pixel), allowed formats: - `<number>` - sets the top edge position from top to bottom, example: `y: 10` - `<number>%` - sets the top edge position in % of QR code without padding. Negative values are allowed. Example: `y: '50%'` - `top` - aligns the image to the top, example: `y: 'top'` - `bottom` - aligns the image to the bottom, example: `y: 'bottom'` - `center` - Centers the image in center of QR code, example: `y: 'center'` - `top <number>` - the same as `<number>` - `top <number>%` - the same as `<number>%` - `bottom <number>` - sets the bottom edge position from bottom to top, example: `y: 'bottom 5'` - `bottom <number>%` - sets the bottom edge position in % of QR code without padding, example: `y: 'bottom 10%'` - `border` (number|null, optional, default = 1) - white space length around the images in dots. Negative values are allowed. - use `0` - for white space only under the image - use `null` to remove any white spaces under image and leave QR data dots #### `draw(canvas: HTMLCanvasElement = null): null | HTMLCanvasElement| Promise` Draws QR code on a canvas element and return the canvas if the canvas is provided, or returns a new canvas element if canvas is not provided (see `getCanvas()`). If QR code can not be generated then `null` will be returned. If `config.image` is provided AND `config.image.source` is `string` (url or dataUrl) then a promise will be returned with a canvas as result. #### `getCanvas(): null | HTMLCanvasElement | Promise` Returns new canvas element with QR code. If QR code can not be generated then `null` will be returned. If `config.image` is provided AND `config.image.source` is `string` (url or dataUrl) then a promise will be returned with a canvas as result. #### `toDataUrl(type: string = 'image/png', encoderOptions: number = 0.92): null | string | Promise` Allowed alias: `toDataURL(...)` Returns dataUrl with QR code. If QR code can not be generated then `null` will be returned. If `config.image` is provided AND `config.image.source` is `string` (url or dataUrl) then a promise will be returned with a dataUrl as result. See params descriptions here: <https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL> Example ```javascript import { QRCodeCanvas } from '@akamfoad/qrcode'; const qrCanvas = new QRCodeCanvas('some value'); const dataUrl = qrCanvas.toDataUrl(); console.log(dataUrl); // data:image/png;base64,iVBORw0KGgoAAAA...g== ``` Example with promise ```javascript import { QRCodeCanvas } from '@akamfoad/qrcode'; // Example with promise const config = { level: 'H', // use high error correction level padding: 0, // do not use padding around qr code data, image: { source: 'https://some-url.com/foo.png', // or data:image/jpeg;base64,... width: '10%', height: '10%', x: 'center', y: 'center', }, }; const qrCanvas = new QRCodeCanvas('some value', config); const promise = qrCanvas.toDataUrl(); // promise is returned because image.source is a string promise.then((dataUrl) => { console.log(dataUrl); // data:image/png;base64,iVBORw0KGgoAAAAN... }); ``` #### 3.3. class `QRCodeSVG` The class creates QR code as SVG in string or data url formats. The class extends [`QRCodeRaw`](31-class-qrcoderaw), therefore please see there description about public method and configuration params. ```javascript import { QRCodeSVG } from '@akamfoad/qrcode'; ``` Public methods: #### `constructor(value: string, config: object)` Create new instance of QRCodeSVG. Please see config description of [`QRCodeRaw.constructor`](#constructorvalue-string-config-object). Config has additional parameters: - `config` (object, optional) - parameters of configuration - see config of [`QRCodeRaw.constructor`](#constructorvalue-string-config-object) - `fgColor` (string, optional, default = `#000`) - foreground color of the QR code in CSS format - `bgColor` (string, optional, default = `#FFF`) - background color of the QR code in CSS format - `image` (object, optional, default = `null`) - parameters on an image, that should be added to QR code, like logo. See [image scheme](#23-qr-code-with-image) - `source` (string|Image|Canvas) - source of image for QR Code, allowed to use the next types: - `string` - url to resource or dataUrl of image. - `Image` - it is allowed to use Image. It is not necessary to have loaded image. - `Canvas` - allowed to use HTML5 canvas element. - `width` (number|string) - width of the image in QR code dots (not a pixel), allowed formats: - `<number>` - defines the width of image, example: `width: 30` - `<number>%` - defines the width in percent of QR code without padding, example: `width: '20%'` - `height` (number|string) - height of the image in QR code dots, see `width` - `x` (number|string, optional, default = `0`) - position of image on QR code by horizontal in QR code dots (not a pixel), allowed formats: - `<number>` - sets the left edge position from left to right, example: `x: 10` - `<number>%` - sets the left edge position in % of QR code without padding. Negative values are allowed. Example: `x: '50%'` - `left` - aligns the image to the left, example: `x: 'left'` - `right` - aligns the image to the right, example: `x: 'right'` - `center` - Centers the image in center of QR code, example: `x: 'center'` - `left <number>` - the same as `<number>` - `left <number>%` - the same as `<number>%` - `right <number>` - sets the right edge position from right to left, example: `x: 'right 5'` - `right <number>%` - sets the tight edge position in % of QR code without padding, example: `x: 'right 10%'` - `y` (number|string, optional, default = `0`) - position of image on QR code by vertical in QR code dots (not a pixel), allowed formats: - `<number>` - sets the top edge position from top to bottom, example: `y: 10` - `<number>%` - sets the top edge position in % of QR code without padding. Negative values are allowed. Example: `y: '50%'` - `top` - aligns the image to the top, example: `y: 'top'` - `bottom` - aligns the image to the bottom, example: `y: 'bottom'` - `center` - Centers the image in center of QR code, example: `y: 'center'` - `top <number>` - the same as `<number>` - `top <number>%` - the same as `<number>%` - `bottom <number>` - sets the bottom edge position from bottom to top, example: `y: 'bottom 5'` - `bottom <number>%` - sets the bottom edge position in % of QR code without padding, example: `y: 'bottom 10%'` - `border` (number|null, optional, default = 1) - white space length around the images in dots. Negative values are allowed. - use `0` - for white space only under the image - use `null` to remove any white spaces under image and leave QR data dots #### `toString(): null | string` Returns SVG with QR code as string. If QR code can not be generated then `null` will be returned. #### `toDataUrl(): null | string` Allowed alias: `toDataURL(...)` Returns SVG with QR code as dataUrl (string). If QR code can not be generated then `null` will be returned. Example ```javascript import { QRCodeSVG } from '@akamfoad/qrcode'; const qrSVG = new QRCodeSVG('some value'); const dataUrl = qrSVG.toDataUrl(); console.log(dataUrl); // data:image/png;base64,iVBORw0KGgoAAAA...g== ``` Example with image ```javascript import { QRCodeSVG } from '@akamfoad/qrcode'; const config = { level: 'M', // use high error correction level padding: 0, // do not use padding around qr code data, image: { source: 'https://some-url.com/foo.png', // or data:image/jpeg;base64,... width: '10%', height: '10%', x: 'center', y: 'center', }, }; const qrSVG = new QRCodeSVG('some value', config); const svg = qrSVG.toString(); console.log(svg); // output: // <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" shape-rendering="crispEdges" viewBox="0 0 21 21"> // <rect x="0" y="0" height="21" width="21" fill="#FFF"/> // <rect x="0" y="0" height="1" width="7" fill="#000"/> // <rect x="9" y="0" height="1" width="2" fill="#000"/> // ... // <image xlink:href="https://some-url.com/foo.png" x="10" y="10" width="2" height="2"/> // </svg> ``` #### 3.4. class `QRCodeText` The class creates QR code as text. It is possible to show QR code in terminal. The class extends [`QRCodeRaw`](31-class-qrcoderaw), therefore please see there description about public method and configuration params. ```javascript import { QRCodeSVG } from '@akamfoad/qrcode'; ``` Public methods: #### `constructor(value: string, config: object)` Create new instance of QRCodeSVG. Please see config description of [`QRCodeRaw.constructor`](#constructorvalue-string-config-object). Config has additional parameters: - `config` (object, optional) - parameters of configuration - see config of [`QRCodeRaw.constructor`](#constructorvalue-string-config-object) - `blackSymbol` (string, optional, default = `▓▓`) - symbol(s) for black QR code dot. - `whiteSymbol` (string, optional, default = `▓▓`) - symbol(s) for white QR code dot. #### `toString(): null | string` Returns QR code as string. If QR code can not be generated then `null` will be returned. Example ```javascript import { QRCodeText } from '@akamfoad/qrcode'; const qrText = new QRCodeText('some value', { blackSymbol: '@@', whiteSymbol: '..', }); const qrCode = qrText.toString(); console.log(qrCode); // .............................................. // ..@@@@@@@@@@@@@@..@@@@@@@@@@..@@@@@@@@@@@@@@.. // ..@@..........@@..@@@@..@@@@..@@..........@@.. // ..@@..@@@@@@..@@....@@@@@@....@@..@@@@@@..@@.. // ..@@..@@@@@@..@@....@@..@@@@..@@..@@@@@@..@@.. // ..@@..@@@@@@..@@..@@......@@..@@..@@@@@@..@@.. // ..@@..........@@..@@..@@......@@..........@@.. // ..@@@@@@@@@@@@@@..@@..@@..@@..@@@@@@@@@@@@@@.. // ..................@@@@@@@@.................... // ..@@@@@@....@@@@..@@@@@@@@@@@@@@@@@@....@@@@.. // ..@@@@@@@@..@@..@@..........@@@@..@@@@@@..@@.. // ..@@......@@..@@@@..@@@@....@@@@..@@......@@.. // ......@@..@@@@..@@........@@@@..@@..@@..@@@@.. // ....@@..@@@@@@@@..@@............@@@@@@..@@@@.. // ..................@@@@@@@@..@@....@@@@@@@@@@.. // ..@@@@@@@@@@@@@@........@@@@..@@..@@..@@..@@.. // ..@@..........@@..@@@@@@@@..@@....@@@@....@@.. // ..@@..@@@@@@..@@....@@..@@@@@@....@@@@..@@.... // ..@@..@@@@@@..@@......@@....@@..@@@@@@........ // ..@@..@@@@@@..@@..@@..........@@..@@..@@@@@@.. // ..@@..........@@..@@......@@.................. // ..@@@@@@@@@@@@@@..@@@@......@@@@@@@@@@....@@.. // .............................................. ``` ### 4. Appendix #### 4.1. Data capacity in bytes <table> <thead> <tr> <th rowspan="2">TypeNumber</th> <th colspan="4">Numeric</th> <th colspan="4">Alphanumeric</th> <th colspan="4">Byte</th> <th colspan="4">Kanji</th> </tr> <tr> <th>L</th><th>M</th><th>Q</th><th>H</th><th>L</th><th>M</th><th>Q</th><th>H</th><th>L</th><th>M</th><th>Q</th><th>H</th><th>L</th><th>M</th><th>Q</th><th>H</th> </tr> </thead> <tbody> <tr> <td>1</td><td>41</td><td>34</td><td>27</td><td>17</td><td>25</td><td>20</td><td>16</td><td>10</td><td>17</td><td>14</td><td>11</td><td>7</td><td>10</td><td>8</td><td>7</td><td>4</td> </tr> <tr> <td>2</td><td>77</td><td>63</td><td>48</td><td>34</td><td>47</td><td>38</td><td>29</td><td>20</td><td>32</td><td>26</td><td>20</td><td>14</td><td>20</td><td>16</td><td>12</td><td>8</td> </tr> <tr> <td>3</td><td>127</td><td>101</td><td>77</td><td>58</td><td>77</td><td>61</td><td>47</td><td>35</td><td>53</td><td>42</td><td>32</td><td>24</td><td>32</td><td>26</td><td>20</td><td>15</td> </tr> <tr> <td>4</td><td>187</td><td>149</td><td>111</td><td>82</td><td>114</td><td>90</td><td>67</td><td>50</td><td>78</td><td>62</td><td>46</td><td>34</td><td>48</td><td>38</td><td>28</td><td>21</td> </tr> <tr> <td>5</td><td>255</td><td>202</td><td>144</td><td>106</td><td>154</td><td>122</td><td>87</td><td>64</td><td>106</td><td>84</td><td>60</td><td>44</td><td>65</td><td>52</td><td>37</td><td>27</td> </tr> <tr> <td>6</td><td>322</td><td>255</td><td>178</td><td>139</td><td>195</td><td>154</td><td>108</td><td>84</td><td>134</td><td>106</td><td>74</td><td>58</td><td>82</td><td>65</td><td>45</td><td>36</td> </tr> <tr> <td>7</td><td>370</td><td>293</td><td>207</td><td>154</td><td>224</td><td>178</td><td>125</td><td>93</td><td>154</td><td>122</td><td>86</td><td>64</td><td>95</td><td>75</td><td>53</td><td>39</td> </tr> <tr> <td>8</td><td>461</td><td>365</td><td>259</td><td>202</td><td>279</td><td>221</td><td>157</td><td>122</td><td>192</td><td>152</td><td>108</td><td>84</td><td>118</td><td>93</td><td>66</td><td>52</td> </tr> <tr> <td>9</td><td>552</td><td>432</td><td>312</td><td>235</td><td>335</td><td>262</td><td>189</td><td>143</td><td>230</td><td>180</td><td>130</td><td>98</td><td>141</td><td>111</td><td>80</td><td>60</td> </tr> <tr> <td>10</td><td>652</td><td>513</td><td>364</td><td>288</td><td>395</td><td>311</td><td>221</td><td>174</td><td>271</td><td>213</td><td>151</td><td>119</td><td>167</td><td>131</td><td>93</td><td>74</td> </tr> <tr> <td>11</td><td>772</td><td>604</td><td>427</td><td>331</td><td>468</td><td>366</td><td>259</td><td>200</td><td>321</td><td>251</td><td>177</td><td>137</td><td>198</td><td>155</td><td>109</td><td>85</td> </tr> <tr> <td>12</td><td>883</td><td>691</td><td>489</td><td>374</td><td>535</td><td>419</td><td>296</td><td>227</td><td>367</td><td>287</td><td>203</td><td>155</td><td>226</td><td>177</td><td>125</td><td>96</td> </tr> <tr> <td>13</td><td>1022</td><td>796</td><td>580</td><td>427</td><td>619</td><td>483</td><td>352</td><td>259</td><td>425</td><td>331</td><td>241</td><td>177</td><td>262</td><td>204</td><td>149</td><td>109</td> </tr> <tr> <td>14</td><td>1101</td><td>871</td><td>621</td><td>468</td><td>667</td><td>528</td><td>376</td><td>283</td><td>458</td><td>362</td><td>258</td><td>194</td><td>282</td><td>223</td><td>159</td><td>120</td> </tr> <tr> <td>15</td><td>1250</td><td>991</td><td>703</td><td>530</td><td>758</td><td>600</td><td>426</td><td>321</td><td>520</td><td>412</td><td>292</td><td>220</td><td>320</td><td>254</td><td>180</td><td>136</td> </tr> <tr> <td>16</td><td>1408</td><td>1082</td><td>775</td><td>602</td><td>854</td><td>656</td><td>470</td><td>365</td><td>586</td><td>450</td><td>322</td><td>250</td><td>361</td><td>277</td><td>198</td><td>154</td> </tr> <tr> <td>17</td><td>1548</td><td>1212</td><td>876</td><td>674</td><td>938</td><td>734</td><td>531</td><td>408</td><td>644</td><td>504</td><td>364</td><td>280</td><td>397</td><td>310</td><td>224</td><td>173</td> </tr> <tr> <td>18</td><td>1725</td><td>1346</td><td>948</td><td>746</td><td>1046</td><td>816</td><td>574</td><td>452</td><td>718</td><td>560</td><td>394</td><td>310</td><td>442</td><td>345</td><td>243</td><td>191</td> </tr> <tr> <td>19</td><td>1903</td><td>1500</td><td>1063</td><td>813</td><td>1153</td><td>909</td><td>644</td><td>493</td><td>792</td><td>624</td><td>442</td><td>338</td><td>488</td><td>384</td><td>272</td><td>208</td> </tr> <tr> <td>20</td><td>2061</td><td>1600</td><td>1159</td><td>919</td><td>1249</td><td>970</td><td>702</td><td>557</td><td>858</td><td>666</td><td>482</td><td>382</td><td>528</td><td>410</td><td>297</td><td>235</td> </tr> <tr> <td>21</td><td>2232</td><td>1708</td><td>1224</td><td>969</td><td>1352</td><td>1035</td><td>742</td><td>587</td><td>929</td><td>711</td><td>509</td><td>403</td><td>572</td><td>438</td><td>314</td><td>248</td> </tr> <tr> <td>22</td><td>2409</td><td>1872</td><td>1358</td><td>1056</td><td>1460</td><td>1134</td><td>823</td><td>640</td><td>1003</td><td>779</td><td>565</td><td>439</td><td>618</td><td>480</td><td>348</td><td>270</td> </tr> <tr> <td>23</td><td>2620</td><td>2059</td><td>1468</td><td>1108</td><td>1588</td><td>1248</td><td>890</td><td>672</td><td>1091</td><td>857</td><td>611</td><td>461</td><td>672</td><td>528</td><td>376</td><td>284</td> </tr> <tr> <td>24</td><td>2812</td><td>2188</td><td>1588</td><td>1228</td><td>1704</td><td>1326</td><td>963</td><td>744</td><td>1171</td><td>911</td><td>661</td><td>511</td><td>721</td><td>561</td><td>407</td><td>315</td> </tr> <tr> <td>25</td><td>3057</td><td>2395</td><td>1718</td><td>1286</td><td>1853</td><td>1451</td><td>1041</td><td>779</td><td>1273</td><td>997</td><td>715</td><td>535</td><td>784</td><td>614</td><td>440</td><td>330</td> </tr> <tr> <td>26</td><td>3283</td><td>2544</td><td>1804</td><td>1425</td><td>1990</td><td>1542</td><td>1094</td><td>864</td><td>1367</td><td>1059</td><td>751</td><td>593</td><td>842</td><td>652</td><td>462</td><td>365</td> </tr> <tr> <td>27</td><td>3517</td><td>2701</td><td>1933</td><td>1501</td><td>2132</td><td>1637</td><td>1172</td><td>910</td><td>1465</td><td>1125</td><td>805</td><td>625</td><td>902</td><td>692</td><td>496</td><td>385</td> </tr> <tr> <td>28</td><td>3669</td><td>2857</td><td>2085</td><td>1581</td><td>2223</td><td>1732</td><td>1263</td><td>958</td><td>1528</td><td>1190</td><td>868</td><td>658</td><td>940</td><td>732</td><td>534</td><td>405</td> </tr> <tr> <td>29</td><td>3909</td><td>3035</td><td>2181</td><td>1677</td><td>2369</td><td>1839</td><td>1322</td><td>1016</td><td>1628</td><td>1264</td><td>908</td><td>698</td><td>1002</td><td>778</td><td>559</td><td>430</td> </tr> <tr> <td>30</td><td>4158</td><td>3289</td><td>2358</td><td>1782</td><td>2520</td><td>1994</td><td>1429</td><td>1080</td><td>1732</td><td>1370</td><td>982</td><td>742</td><td>1066</td><td>843</td><td>604</td><td>457</td> </tr> <tr> <td>31</td><td>4417</td><td>3486</td><td>2473</td><td>1897</td><td>2677</td><td>2113</td><td>1499</td><td>1150</td><td>1840</td><td>1452</td><td>1030</td><td>790</td><td>1132</td><td>894</td><td>634</td><td>486</td> </tr> <tr> <td>32</td><td>4686</td><td>3693</td><td>2670</td><td>2022</td><td>2840</td><td>2238</td><td>1618</td><td>1226</td><td>1952</td><td>1538</td><td>1112</td><td>842</td><td>1201</td><td>947</td><td>684</td><td>518</td> </tr> <tr> <td>33</td><td>4965</td><td>3909</td><td>2805</td><td>2157</td><td>3009</td><td>2369</td><td>1700</td><td>1307</td><td>2068</td><td>1628</td><td>1168</td><td>898</td><td>1273</td><td>1002</td><td>719</td><td>553</td> </tr> <tr> <td>34</td><td>5253</td><td>4134</td><td>2949</td><td>2301</td><td>3183</td><td>2506</td><td>1787</td><td>1394</td><td>2188</td><td>1722</td><td>1228</td><td>958</td><td>1347</td><td>1060</td><td>756</td><td>590</td> </tr> <tr> <td>35</td><td>5529</td><td>4343</td><td>3081</td><td>2361</td><td>3351</td><td>2632</td><td>1867</td><td>1431</td><td>2303</td><td>1809</td><td>1283</td><td>983</td><td>1417</td><td>1113</td><td>790</td><td>605</td> </tr> <tr> <td>36</td><td>5836</td><td>4588</td><td>3244</td><td>2524</td><td>3537</td><td>2780</td><td>1966</td><td>1530</td><td>2431</td><td>1911</td><td>1351</td><td>1051</td><td>1496</td><td>1176</td><td>832</td><td>647</td> </tr> <tr> <td>37</td><td>6153</td><td>4775</td><td>3417</td><td>2625</td><td>3729</td><td>2894</td><td>2071</td><td>1591</td><td>2563</td><td>1989</td><td>1423</td><td>1093</td><td>1577</td><td>1224</td><td>876</td><td>673</td> </tr> <tr> <td>38</td><td>6479</td><td>5039</td><td>3599</td><td>2735</td><td>3927</td><td>3054</td><td>2181</td><td>1658</td><td>2699</td><td>2099</td><td>1499</td><td>1139</td><td>1661</td><td>1292</td><td>923</td><td>701</td> </tr> <tr> <td>39</td><td>6743</td><td>5313</td><td>3791</td><td>2927</td><td>4087</td><td>3220</td><td>2298</td><td>1774</td><td>2809</td><td>2213</td><td>1579</td><td>1219</td><td>1729</td><td>1362</td><td>972</td><td>750</td> </tr> <tr> <td>40</td><td>7089</td><td>5596</td><td>3993</td><td>3057</td><td>4296</td><td>3391</td><td>2420</td><td>1852</td><td>2953</td><td>2331</td><td>1663</td><td>1273</td><td>1817</td><td>1435</td><td>1024</td><td>784</td> </tr> </tbody> </table> ## Something does not work Feel free to fork project, fix bugs, write tests and finally request for pull