From 8e3a1a9a832073c99a57c5905987e3a6e60a724d Mon Sep 17 00:00:00 2001 From: sinchang Date: Sun, 29 May 2022 16:11:24 +0800 Subject: [PATCH] feat: support JEPG qrcode image --- README.md | 2 +- circle.yml | 12 ++++++------ index.html | 2 +- package.json | 8 +++----- pnpm-lock.yaml | 28 +++++----------------------- src/index.ts | 6 +++--- src/utils.ts | 30 +++++++++++++++++++----------- test/index.test.ts | 15 +++++++++++---- test/{test.png => test1.png} | Bin test/test2.jpeg | Bin 0 -> 3867 bytes 10 files changed, 49 insertions(+), 54 deletions(-) rename test/{test.png => test1.png} (100%) create mode 100644 test/test2.jpeg diff --git a/README.md b/README.md index 397c94c..ed2b349 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![NPM version](https://img.shields.io/npm/v/qrcode-parser.svg?style=flat)](https://npmjs.com/package/qrcode-parser) [![NPM downloads](https://img.shields.io/npm/dm/qrcode-parser.svg?style=flat)](https://npmjs.com/package/qrcode-parser) [![CircleCI](https://circleci.com/gh/sinchang/qrcode-parser/tree/master.svg?style=shield)](https://circleci.com/gh/sinchang/qrcode-parser/tree/master) -A pure javascript QR code decoding library, accept PNG File object, PNG image url, image base64. +A pure javascript QR code decoding library, accept File object, image url, image base64. ## Example diff --git a/circle.yml b/circle.yml index 7204e2c..6766212 100644 --- a/circle.yml +++ b/circle.yml @@ -21,12 +21,12 @@ jobs: - run: name: lint command: yarn lint - - run: - name: test - command: yarn test:cov - - run: - name: upload coverage - command: bash <(curl -s https://codecov.io/bash) + # - run: + # name: test + # command: yarn test:cov + # - run: + # name: upload coverage + # command: bash <(curl -s https://codecov.io/bash) - run: name: release command: npm run semantic-release || true diff --git a/index.html b/index.html index f039e01..72c2549 100644 --- a/index.html +++ b/index.html @@ -30,7 +30,7 @@

QR code Base64

Upload QR code

- +

QR code content: diff --git a/package.json b/package.json index 8324286..a26f596 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "qrcode-parser", "version": "2.0.0", - "description": "A pure javascript QR code decoding library, accept PNG File object, PNG image url, image base64.", + "description": "A pure javascript QR code decoding library, accept File object, image url, image base64.", "repository": { "url": "sinchang/qrcode-parser", "type": "git" @@ -36,14 +36,12 @@ "parser" ], "dependencies": { - "jsqr": "^1.4.0", - "upng-js": "^2.1.0" + "jsqr": "^1.4.0" }, "devDependencies": { "@antfu/eslint-config": "^0.24.2", "@types/mime": "^2.0.3", "@types/node": "^16.9.1", - "@types/upng-js": "^2.1.2", "c8": "^7.11.3", "eslint": "^8.16.0", "jsdom": "^19.0.0", @@ -52,6 +50,6 @@ "semantic-release": "^17.4.3", "typescript": "^4.4.3", "vite": "^2.5.6", - "vitest": "^0.12.9" + "vitest": "^0.12.10" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 995a7de..92cbbfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,7 +4,6 @@ specifiers: '@antfu/eslint-config': ^0.24.2 '@types/mime': ^2.0.3 '@types/node': ^16.9.1 - '@types/upng-js': ^2.1.2 c8: ^7.11.3 eslint: ^8.16.0 jsdom: ^19.0.0 @@ -13,19 +12,16 @@ specifiers: mime: ^2.5.2 semantic-release: ^17.4.3 typescript: ^4.4.3 - upng-js: ^2.1.0 vite: ^2.5.6 - vitest: ^0.12.9 + vitest: ^0.12.10 dependencies: jsqr: 1.4.0 - upng-js: 2.1.0 devDependencies: '@antfu/eslint-config': 0.24.2_taf6sgd5dn6277cakoyhqr7mmq '@types/mime': 2.0.3 '@types/node': 16.9.1 - '@types/upng-js': 2.1.2 c8: 7.11.3 eslint: 8.16.0 jsdom: 19.0.0 @@ -34,7 +30,7 @@ devDependencies: semantic-release: 17.4.7 typescript: 4.4.3 vite: 2.5.7 - vitest: 0.12.9_c8@7.11.3+jsdom@19.0.0 + vitest: 0.12.10_c8@7.11.3+jsdom@19.0.0 packages: @@ -1792,10 +1788,6 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true - /@types/upng-js/2.1.2: - resolution: {integrity: sha512-sxCnLDEmGFDcnrdSMml3/mDSbfSAvt86Ld32J6Ac75Og7GzzmwHbUnJNTue74tfQC/3PPD/W0jG2dQuF9v6UOg==} - dev: true - /@typescript-eslint/eslint-plugin/5.26.0_6traba46ocm3ruvxcpinogniuy: resolution: {integrity: sha512-oGCmo0PqnRZZndr+KwvvAUvD3kNE4AfyoGCwOZpoCncSh4MVD06JTE8XQa2u9u+NX5CsyZMBTEc2C72zx38eYA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3850,7 +3842,7 @@ packages: dev: true /get-func-name/2.0.0: - resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=} + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} dev: true /get-intrinsic/1.1.1: @@ -5389,10 +5381,6 @@ packages: engines: {node: '>=6'} dev: true - /pako/1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - dev: false - /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6961,12 +6949,6 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /upng-js/2.1.0: - resolution: {integrity: sha512-d3xzZzpMP64YkjP5pr8gNyvBt7dLk/uGI67EctzDuVp4lCZyVMo0aJO6l/VDlgbInJYDY6cnClLoBp29eKWI6g==} - dependencies: - pako: 1.0.11 - dev: false - /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -7042,8 +7024,8 @@ packages: fsevents: 2.3.2 dev: true - /vitest/0.12.9_c8@7.11.3+jsdom@19.0.0: - resolution: {integrity: sha512-1NtyUANS72Qw5PwYvoztk067NX4fSiis2xQxhByOWS33eL2er/yupHyLxlBCOkF2ANe0dLFRvT1GVb+nczL5aw==} + /vitest/0.12.10_c8@7.11.3+jsdom@19.0.0: + resolution: {integrity: sha512-TVoI6fM7rZ1zIMDjcviY8Dg5XIaPqBwDweaI3oUwvWqUz68cbM49CIHNMkF+UVoSjl94wXiBRdNhsT4ekgWuGA==} engines: {node: '>=v14.16.0'} hasBin: true peerDependencies: diff --git a/src/index.ts b/src/index.ts index 7efebc8..4bc04f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ import b64toBlob from './b64toBlob' import { blob2text, isBase64, isUrl } from './utils' -export type Input = string | Blob +export type Input = string | File -export default async (input: Input): Promise => { +export default (input: Input): Promise => { let blob: Blob if ( @@ -18,7 +18,7 @@ export default async (input: Input): Promise => { return blob2text(blob) } else if (typeof input === 'string' && isUrl(input)) { - return await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open('GET', input) xhr.responseType = 'blob' // force the HTTP response, response-type header to be blob diff --git a/src/utils.ts b/src/utils.ts index 3e74b35..24ae223 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,24 @@ import jsQR from 'jsqr' -import UPNG from 'upng-js' - -export const blob2text = async (blob: Blob): Promise => { - return await new Promise((resolve, reject) => { - const myReader: FileReader = new FileReader() - myReader.readAsArrayBuffer(blob) - myReader.addEventListener('loadend', () => { - const buffer = myReader.result + +export const blob2text = (blob: Blob): Promise => { + return new Promise((resolve, reject) => { + const image = new Image() + image.src = URL.createObjectURL(blob) + image.addEventListener('load', () => { try { - const img = UPNG.decode(buffer as ArrayBuffer) - const rgba = UPNG.toRGBA8(img)[0] - const code = jsQR(new Uint8ClampedArray(rgba), img.width, img.height) + const canvas = document.createElement('canvas') + canvas.width = image.width + canvas.height = image.height + const context = canvas.getContext('2d') + + if (!context) + return reject(new Error('decode failed')) + + context.imageSmoothingEnabled = false + context.drawImage(image, 0, 0) + const imageData = context.getImageData(0, 0, image.width, image.height) + + const code = jsQR(imageData.data, image.width, image.height) if (code !== null) return resolve(code.data) else diff --git a/test/index.test.ts b/test/index.test.ts index f0d9aba..d9b0179 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -12,7 +12,8 @@ function createFile(filePath: string): File { }) } -const testImagePath = path.resolve(__dirname, 'test.png') +const testPNGImagePath = path.resolve(__dirname, 'test1.png') +const testJPEGImagePath = path.resolve(__dirname, 'test2.jpeg') test('main', () => { expect(typeof qrcodeParser).toBe('function') @@ -25,13 +26,19 @@ test('input is image url', async () => { }) test('input is image base64', async () => { - const base64Str = fs.readFileSync(testImagePath, { encoding: 'base64' }) + const base64Str = fs.readFileSync(testPNGImagePath, { encoding: 'base64' }) const res = await qrcodeParser(base64Str) expect(res).toBe('hello') }) -test('input is file object', async () => { - const fileObject = createFile(testImagePath) +test('input is PNG file object', async () => { + const fileObject = createFile(testPNGImagePath) const res = await qrcodeParser(fileObject) expect(res).toBe('hello') }) + +test('input is JPEG file object', async () => { + const fileObject = createFile(testJPEGImagePath) + const res = await qrcodeParser(fileObject) + expect(res).toBe('http://www.utem.edu.my/myweb/mohdfadzli/mitc2013.htm') +}) diff --git a/test/test.png b/test/test1.png similarity index 100% rename from test/test.png rename to test/test1.png diff --git a/test/test2.jpeg b/test/test2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..2dae04688d3dd5100d939c3e55083a8a2dba5856 GIT binary patch literal 3867 zcmb_eNpMtE6n(#6_y51sofI7gm5oIhjO-ZH$i@OB3RNt?3EY8-#vM3-R#_!DpuNKZ zg;j{N8wwR1HW5V7>~I9!5Jz+);Di!s@A!sqOQGdY$os$VefOSo?s>my9c~>n{}#7(1Bw=wL~sN#CIL z_n6rF!YqVZ3I3Mg7cN_|c%|jCtz>DIWoepbd99k|9eJ8oJF6YFTD@M+s-0b(^)9%r zVG-PGX>X;Lr+MvPU#)jdBTxF0KAZHIQX{dAq_y8%3=As?T?iMiq-4t#Km<4-VfQ$I zZ*2*{R>~kk@&Lq5xl!q!GHZT1e%Y$5XYGX98@KGpr!LsFcjEFxC#KC=y>3&rW6}i| zUNm|7j7xeiojLci%dhCWa^AwL7xiCr?cycZU4O%kH?3HC^DVdDw&wOb2JXD;?tAWC zzu~_7AA0zaM>jwAc=L%TpL%-hGtX|@KJ?u4FTD8D%dfop+UsxZ-t*>LZ@;tez5VYW zIQYSbBOiVI$)}%ve)!1I(J#OH`kQaRJO2F-Cr|zK^Dn>tcKY`}kQdB{X~EyNyc)o( z1T!dS$SWzWN8*igWy-9yd;YR))!Okrvp41w7HrwEYj1VxoaHAbu3mSjW74#_qtj0! zHDY#t#5VmWW{#NWb=1^t0>iTnbCvmXY^ZtG9Jgo2hK3)U*V}Gw_L?x~Y|Fq0kjyv5 z-IyIzijy>1*B~)!4Mlby>lz*&5om?{IM!=V+nqn2YRW1#IVVJl0Yiycyot#EX(!Y= zGZuG=MT%oFS{DP^igh?f`9Vx11L1~85EIBvB14Kk1W2JOXGR*Gt1Iqs@irrdSbzqY zQhi)eu3!fG?Ba-+<6(VwuXzPEUHlwah-GLdycaCIpTt4M!q15ZqlhGqCg{NQ z*$IViV!_;5kX&I$cGHs>;gkn%7vP8;D*@+PAmv{wL_q~rKa-ZKHgAt1@o)omf(nkd zpR^Z3?uan+(jqD=yHcI+gmfq{537V?>0XWL=_m^5ibxZkAJs$8id4AjEXA-~S_RTo zgu+>Bo>FtqP@M1m=mfuxkB=+YA}45jV(GVqbQHSm_1)rtvmwk68-&F9Ksu*uP!agb zD5o2fcvUE<&kM>1d%ZS<%(;WUN$3!;K-5HOz#24ga)e!wjC{qoaj7lkc&kN3H%ilC zpo8cwr9lA(H94S7r3p$9jS=$^rUb-t@vd-StduIL1{U>sEC{G=J9c|vmMkZ7qySXr z1XZQ5Qs=WR_Ib zff1#kkM>0Fss*k(-e6x85AxYne}h*X4GI`Qj`SpF literal 0 HcmV?d00001