@openhealthnz-credentials/pdf-image-qr-scanner
Version:
Library to scan user-uploaded PDF's or images locally and reliably for QR codes using jsQR.
1 lines • 6.54 kB
Source Map (JSON)
{"version":3,"file":"index.modern.mjs","sources":["../src/index.ts"],"sourcesContent":["/**\n * pdf-image-qr-scanner\n * Library to scan user-uploaded PDF's or images locally and reliably for QR codes using jsQR.\n * @since 0.0.0\n */\n\nimport jsQR from 'jsqr';\nimport pdfLib from 'pdfjs-dist';\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nconst { getDocument, GlobalWorkerOptions, version: pdfjsVersion } = pdfLib;\nGlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsVersion}/pdf.worker.min.js`;\n\n/**\n * Scan PDF file and return QR code data.\n *\n * @param {File} FileHandle - File handle of the PDF/Image file to scan.\n * @returns {Promise<string?>} QR Code Data.\n * @since 0.0.0\n */\n\nexport async function scanFile(FileHandle: File): Promise<string | null> {\n\t// async read of selected PDF file\n\tconst uploadedFile = new Response(FileHandle);\n\n\tif (FileHandle.type === 'application/pdf') {\n\t\t// PDF.js is used to parse the PDF file from the typed array.\n\t\tconst pdfTypedArray = new Uint8Array(await uploadedFile.arrayBuffer());\n\t\tconst loadedPDF = await getDocument(pdfTypedArray).promise;\n\n\t\treturn await renderPDF(loadedPDF);\n\t} else {\n\t\t// It's an image, so we use the canvas to render the image.\n\t\tconst imageBlob = await uploadedFile.blob();\n\t\treturn await renderImage(imageBlob);\n\t}\n}\n\n/**\n * Render PDF to canvas, and scans the QR code using the Canvas data.\n * @param {PDFDocumentProxy} pdfData PDF data to be scanned.\n * @return {string?} QR code data.\n * @since 0.0.0\n */\n\nasync function renderPDF(pdfTypedArray: PDFDocumentProxy): Promise<string | null> {\n\tlet qrResult: string | null = null;\n\tfor (let pageToRender = 1; pageToRender <= pdfTypedArray.numPages; pageToRender++) {\n\t\tconst page = await pdfTypedArray.getPage(pageToRender);\n\n\t\tconst canvas = document.createElement('canvas');\n\t\t// Simulation of A4 at 300dpi resolution\n\t\tcanvas.height = 3508;\n\t\tcanvas.width = 2480;\n\n\t\tconst unscaledViewport = page.getViewport({ scale: 1 });\n\n\t\tconst scale = Math.min(canvas.height / unscaledViewport.height, canvas.width / unscaledViewport.width);\n\n\t\tconst viewport = page.getViewport({ scale });\n\n\t\tconst renderContext = {\n\t\t\tcanvasContext: canvas.getContext('2d'),\n\t\t\tviewport: viewport,\n\t\t};\n\n\t\tawait page.render(renderContext).promise;\n\n\t\t// PDF Page is now fully rendered to canvas, so we can now extract the QR code.\n\t\tqrResult = extractQRCode(canvas);\n\t\tif (qrResult !== null) {\n\t\t\treturn qrResult;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Wrapper for new Image() to load image from blob.\n * Needed to make it compatible with Async/Await.\n * @param {Blob} imageBlob\n * @returns {Promise<Image>}\n * @since 0.0.0\n */\n\nasync function generateImageObject(imageBlob: Blob): Promise<HTMLImageElement> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst image = new Image();\n\t\timage.onload = () => resolve(image);\n\t\timage.onerror = reject;\n\t\timage.src = URL.createObjectURL(imageBlob);\n\t});\n}\n\n/**\n * Takes image file, creates Image, then renders it on a canvas, and finally extracts the QRCode data\n * @param {Blob} imageBlob\n * @return {string?} QR code data.\n * @since 0.0.0\n */\nasync function renderImage(imageBlob: Blob): Promise<string | null> {\n\tconst image = await generateImageObject(imageBlob);\n\n\tconst canvas: HTMLCanvasElement = document.createElement('canvas');\n\n\t// Try scanning for multiple scales, helps jsQR to find the QR code.\n\tfor (const scale of [0.5, 1, 0.25]) {\n\t\tcanvas.height = image.height * scale;\n\t\tcanvas.width = image.width * scale;\n\n\t\tconst context = canvas.getContext('2d');\n\t\tcontext.drawImage(image, 0, 0, canvas.width, canvas.height);\n\n\t\tconst result = extractQRCode(canvas);\n\t\tif (result) {\n\t\t\treturn result;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Extracts QR code from canvas.\n * @param {HTMLCanvasElement} canvas Canvas element to extract QR code from.\n * @return {string?} QR code data.\n * @since 0.0.0\n */\n\nfunction extractQRCode(canvas: HTMLCanvasElement): string | null {\n\tconst ctx = canvas.getContext('2d');\n\tconst imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\tconst qrData = jsQR(imageData.data, canvas.width, canvas.height, {\n\t\tinversionAttempts: 'attemptBoth',\n\t});\n\treturn qrData?.data || null;\n}\n"],"names":["getDocument","GlobalWorkerOptions","version","pdfjsVersion","pdfLib","scanFile","FileHandle","uploadedFile","Response","type","pdfTypedArray","Uint8Array","arrayBuffer","loadedPDF","promise","async","qrResult","pageToRender","numPages","page","getPage","canvas","document","createElement","height","width","unscaledViewport","getViewport","scale","Math","min","viewport","renderContext","canvasContext","getContext","render","extractQRCode","renderPDF","imageBlob","blob","image","Promise","resolve","reject","Image","onload","onerror","src","URL","createObjectURL","generateImageObject","drawImage","result","renderImage","imageData","getImageData","qrData","jsQR","data","inversionAttempts","workerSrc"],"mappings":"8CASA,MAAMA,YAAEA,EAAFC,oBAAeA,EAAqBC,QAASC,GAAiBC,iBAW9CC,EAASC,GAE9B,MAAMC,EAAe,IAAIC,SAASF,GAElC,GAAwB,oBAApBA,EAAWG,KAA4B,CAE1C,MAAMC,EAAgB,IAAIC,iBAAiBJ,EAAaK,eAClDC,QAAkBb,EAAYU,GAAeI,QAEnD,aAeFC,eAAyBL,GACxB,IAAIM,EAA0B,KAC9B,IAAK,IAAIC,EAAe,EAAGA,GAAgBP,EAAcQ,SAAUD,IAAgB,CAClF,MAAME,QAAaT,EAAcU,QAAQH,GAEnCI,EAASC,SAASC,cAAc,UAEtCF,EAAOG,OAAS,KAChBH,EAAOI,MAAQ,KAEf,MAAMC,EAAmBP,EAAKQ,YAAY,CAAEC,MAAO,IAE7CA,EAAQC,KAAKC,IAAIT,EAAOG,OAASE,EAAiBF,OAAQH,EAAOI,MAAQC,EAAiBD,OAE1FM,EAAWZ,EAAKQ,YAAY,CAAEC,MAAAA,IAE9BI,EAAgB,CACrBC,cAAeZ,EAAOa,WAAW,MACjCH,SAAUA,GAOX,SAJMZ,EAAKgB,OAAOH,GAAelB,QAGjCE,EAAWoB,EAAcf,GACR,OAAbL,EACH,OAAOA,EAGT,YA5CcqB,CAAUxB,GACjB,CAEN,MAAMyB,QAAkB/B,EAAagC,OACrC,aAkEFxB,eAA2BuB,GAC1B,MAAME,QAhBPzB,eAAmCuB,GAClC,WAAWG,QAAQ,CAACC,EAASC,KAC5B,MAAMH,EAAQ,IAAII,MAClBJ,EAAMK,OAAS,IAAMH,EAAQF,GAC7BA,EAAMM,QAAUH,EAChBH,EAAMO,IAAMC,IAAIC,gBAAgBX,KAWbY,CAAoBZ,GAElCjB,EAA4BC,SAASC,cAAc,UAGzD,IAAK,MAAMK,IAAS,CAAC,GAAK,EAAG,KAAO,CACnCP,EAAOG,OAASgB,EAAMhB,OAASI,EAC/BP,EAAOI,MAAQe,EAAMf,MAAQG,EAEbP,EAAOa,WAAW,MAC1BiB,UAAUX,EAAO,EAAG,EAAGnB,EAAOI,MAAOJ,EAAOG,QAEpD,MAAM4B,EAAShB,EAAcf,GAC7B,GAAI+B,EACH,OAAOA,EAGT,YApFcC,CAAYf,IA8F3B,SAASF,EAAcf,GACtB,MACMiC,EADMjC,EAAOa,WAAW,MACRqB,aAAa,EAAG,EAAGlC,EAAOI,MAAOJ,EAAOG,QACxDgC,EAASC,EAAKH,EAAUI,KAAMrC,EAAOI,MAAOJ,EAAOG,OAAQ,CAChEmC,kBAAmB,gBAEpB,aAAOH,SAAAA,EAAQE,OAAQ,KA3HxBzD,EAAoB2D,2DAA6DzD"}