diff --git a/package-lock.json b/package-lock.json index 65237d72..831c6da6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,6 @@ "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-static": "^2.0.0", "@sveltejs/kit": "^1.5.0", - "merge-images": "^2.0.0", "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.8.1", "svelte": "^3.55.1", @@ -11413,12 +11412,6 @@ "optional": true, "peer": true }, - "node_modules/merge-images": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-images/-/merge-images-2.0.0.tgz", - "integrity": "sha512-bpI4j54n/Zl6ZTgxaR3xWou/lqI53RAAt8peXijW37BKqoON83LQ7XCZqtFiwzBfEXIws1isYyR06584yffAyA==", - "dev": true - }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", diff --git a/package.json b/package.json index d11ad4d7..a5aac252 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-static": "^2.0.0", "@sveltejs/kit": "^1.5.0", - "merge-images": "^2.0.0", "prettier": "^2.8.0", "prettier-plugin-svelte": "^2.8.1", "svelte": "^3.55.1", diff --git a/src/app.html b/src/app.html index 62862e56..eff8d754 100644 --- a/src/app.html +++ b/src/app.html @@ -5,6 +5,13 @@ + + + + + + + %sveltekit.head% diff --git a/src/lib/components/app/HealthLink.svelte b/src/lib/components/app/HealthLink.svelte index b5f39fee..2775fc77 100644 --- a/src/lib/components/app/HealthLink.svelte +++ b/src/lib/components/app/HealthLink.svelte @@ -23,7 +23,6 @@ ModalFooter, Row } from 'sveltestrap'; - import mergeImages from 'merge-images'; import { goto } from '$app/navigation'; import type { Writable } from 'svelte/store'; import type { SHLAdminParams, SHLClient } from '$lib/utils/managementClient'; @@ -45,18 +44,13 @@ let copyNotice = ''; let href: Promise; - let qrCode: Promise; + let qrCodeImage: Promise; let showPassword = false; $: type = showPassword ? 'text' : 'password'; $: icon = showPassword ? 'eye-fill' : 'eye-slash-fill'; $: { href = getUrl(shl); - } - - $: { - qrCode = href - .then((r) => QRCode.toDataURL(r, { errorCorrectionLevel: 'M' })) - // .then(qrCode => mergeImages([qrCode, {src: '/img/qrcode-logo.png', x:0, y:4}])); + qrCodeImage = createQrCodeImage(href); } let canShare = navigator?.canShare?.({ url: 'https://example.com', title: 'Title' }); @@ -74,6 +68,48 @@ } }); + // Combine header, qr code, and footer images into one image + async function createQrCodeImage(href: Promise) { + // create the qr code image + const qrCodeURI = await href.then(href => QRCode.toDataURL(href, { errorCorrectionLevel: 'M' })); + const qrCode = await new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = (err) => reject(new Error('Failed to load image from data URI.')); + img.src = qrCodeURI; + }) as HTMLImageElement; + // get the header and footer images + const header = document.getElementById('qrcode-header') as HTMLImageElement; + const footer = document.getElementById('qrcode-footer') as HTMLImageElement; + // scale the images down to match the smallest size + const targetWidth: number = Math.min(qrCode.width, header.width, footer.width); + const headerHeight: number = (header.height / header.width) * targetWidth; + const qrCodeImageHeight: number = (qrCode.height / qrCode.width) * targetWidth; + const footerHeight: number = (footer.height / footer.width) * targetWidth; + + // get the canvas and combine the images + const canvas = document.getElementById('qrcode') as HTMLCanvasElement; + if (!canvas) { + throw Error('Could not get qrcode canvas element'); + } + const ctx = canvas.getContext('2d'); + if (!ctx) { + throw Error('Could not get canvas context'); + } + const marginX = 5; + const marginY = 10; + canvas.width = targetWidth + marginX * 2; + canvas.height = headerHeight + qrCodeImageHeight + footerHeight + marginY * 2; + ctx.fillStyle = 'white'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(header, marginX, marginY, targetWidth, headerHeight); + ctx.drawImage(qrCode, marginX, headerHeight + marginY, targetWidth, qrCodeImageHeight); + ctx.drawImage(footer, marginX, headerHeight + qrCodeImageHeight + marginY, targetWidth, footerHeight); + + const fullImageDataUrl = canvas.toDataURL('image/png'); + return fullImageDataUrl; + } + async function getUrl(shl: SHLAdminParams) { let shlMin = { id: shl.id, @@ -125,73 +161,76 @@ {/if} + + + + +