Support for other image formats (#95)

* Upgrade packages (#86)

Co-authored-by: Marvin Sextro <marvin.sextro@gmail.com>

* Support JPEG and other image formats; remove pngjs dependency (#93)

* Support JPEG and other image formats; remove pngjs

By using native Canvas to render images it was possible to remove
dependency on external libraries to support image formats while
supporting more formats like JPEG, GIF and WEBP for the QR image

* Constrain images to 2 Mpx

Co-authored-by: Marvin Sextro <marvin.sextro@gmail.com>

* Upgrade packages

Co-authored-by: Marvin Sextro <marvin.sextro@gmail.com>
Co-authored-by: iBobo <andrea@bhweb.it>
This commit is contained in:
Hauke Tönjes 2021-09-03 14:34:04 +02:00 committed by GitHub
parent de82c494ad
commit cc7fddb2af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 925 additions and 852 deletions

View File

@ -186,7 +186,7 @@ function Form(): JSX.Element {
className={`${isCameraOpen ? undefined : "hidden"} rounded-md w-full`}/> className={`${isCameraOpen ? undefined : "hidden"} rounded-md w-full`}/>
<input type='file' <input type='file'
id='file' id='file'
accept="application/pdf,image/png" accept="application/pdf,image/png,image/jpeg,image/webp,image/gif"
ref={inputFile} ref={inputFile}
style={{display: 'none'}} style={{display: 'none'}}
/> />

3
next-env.d.ts vendored
View File

@ -1,3 +1,6 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/types/global" /> /// <reference types="next/types/global" />
/// <reference types="next/image-types/global" /> /// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -16,14 +16,12 @@
"cbor-js": "^0.1.0", "cbor-js": "^0.1.0",
"do-not-zip": "^1.0.0", "do-not-zip": "^1.0.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jpeg-js": "^0.4.3",
"jsqr": "^1.4.0", "jsqr": "^1.4.0",
"next": "latest", "next": "^11.1.0",
"next-i18next": "^8.5.1", "next-i18next": "^8.5.1",
"next-seo": "^4.26.0", "next-seo": "^4.26.0",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"pdfjs-dist": "^2.5.207", "pdfjs-dist": "^2.5.207",
"pngjs": "^6.0.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"uuid": "^8.3.2", "uuid": "^8.3.2",

View File

@ -1,5 +1,4 @@
import {PayloadBody} from "./payload"; import {PayloadBody} from "./payload";
import {PNG} from 'pngjs'
import * as PdfJS from 'pdfjs-dist' import * as PdfJS from 'pdfjs-dist'
import jsQR, {QRCode} from "jsqr"; import jsQR, {QRCode} from "jsqr";
import {decodeData} from "./decode"; import {decodeData} from "./decode";
@ -9,19 +8,21 @@ import {COLORS} from "./colors";
PdfJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PdfJS.version}/pdf.worker.js` PdfJS.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PdfJS.version}/pdf.worker.js`
export async function getPayloadBodyFromFile(file: File, color: COLORS): Promise<PayloadBody> { export async function getPayloadBodyFromFile(file: File, color: COLORS): Promise<PayloadBody> {
// Read file
const fileBuffer = await file.arrayBuffer();
let imageData: ImageData; let imageData: ImageData;
switch (file.type) { switch (file.type) {
case 'application/pdf': case 'application/pdf':
console.log('pdf') console.log('pdf')
// Read file
const fileBuffer = await file.arrayBuffer();
imageData = await getImageDataFromPdf(fileBuffer) imageData = await getImageDataFromPdf(fileBuffer)
break break
case 'image/png': case 'image/png':
console.log('png') case 'image/jpeg':
imageData = await getImageDataFromPng(fileBuffer) case 'image/webp':
case 'image/gif':
console.log(`image ${file.type}`)
imageData = await getImageDataFromImage(file)
break break
default: default:
throw Error('invalidFileType') throw Error('invalidFileType')
@ -81,18 +82,48 @@ export async function getPayloadBodyFromQR(qrCodeResult: Result, color: COLORS):
} }
} }
async function getImageDataFromPng(fileBuffer: ArrayBuffer): Promise<ImageData> { function getImageDataFromImage(file: File): Promise<ImageData> {
return new Promise(async (resolve, reject) => { return new Promise((resolve, reject) => {
let png = new PNG({filterType: 4}) const canvas = <HTMLCanvasElement>document.getElementById('canvas');
const canvasContext = canvas.getContext('2d');
png.parse(fileBuffer, (error, data) => { // create Image object
if (error) { const img = new Image();
reject();
img.onload = () => {
// constrain image to 2 Mpx
const maxPx = 2000000;
let width: number;
let height: number;
if (img.naturalWidth * img.naturalHeight > maxPx) {
const ratio = img.naturalHeight / img.naturalWidth;
width = Math.sqrt(maxPx / ratio);
height = Math.floor(width * ratio);
width = Math.floor(width);
} else {
width = img.naturalWidth;
height = img.naturalHeight;
} }
resolve(data); // Set correct canvas width / height
}) canvas.width = width;
}) canvas.height = height;
// draw image into canvas
canvasContext.clearRect(0, 0, width, height);
canvasContext.drawImage(img, 0, 0, width, height);
// Obtain image data
resolve(canvasContext.getImageData(0, 0, width, height));
};
img.onerror = (e) => {
reject(e);
};
// start loading image from file
img.src = URL.createObjectURL(file);
});
} }
async function getImageDataFromPdf(fileBuffer: ArrayBuffer): Promise<ImageData> { async function getImageDataFromPdf(fileBuffer: ArrayBuffer): Promise<ImageData> {

1707
yarn.lock

File diff suppressed because it is too large Load Diff