Last active
March 1, 2025 09:44
-
-
Save kyasu1/0def72d6f0826b0a9571b6e13f3c9065 to your computer and use it in GitHub Desktop.
A light weight qrcode plugin for pdfme
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import type * as CSS from 'csstype'; | |
| import { createSvgStr, convertForPdfLayoutProps, isEditable, addAlphaToHex, createErrorElm} | |
| from '@pdfme/schemas/utils'; | |
| import { Plugin, UIRenderProps } from '@pdfme/common'; | |
| import type { PDFRenderProps, Schema } from '@pdfme/common'; | |
| import * as Lucide from 'lucide'; | |
| import QRCode from 'qrcode'; | |
| const DEFAULT_OPACITY = 1; | |
| const HEX_COLOR_PATTERN = '^#(?:[A-Fa-f0-9]{6})$'; | |
| const DEFAULT_BARCODE_BG_COLOR = '#ffffff'; | |
| const DEFAULT_BARCODE_COLOR = '#000000'; | |
| interface QRCodeSchema extends Schema { | |
| backgroundColor: string; | |
| barColor: string; | |
| } | |
| /* | |
| uiRender | |
| */ | |
| const fullSize = { width: '100%', height: '100%' }; | |
| const createQRCodeImageElm = async (value: string) => { | |
| const qrCodeDataURL: string = await QRCode.toDataURL(value); | |
| const img: HTMLImageElement = document.createElement('img'); | |
| img.src = qrCodeDataURL; | |
| const imgStyle: CSS.Properties = { ...fullSize, borderRadius: 0 }; | |
| Object.assign(img.style, imgStyle); | |
| return img; | |
| }; | |
| // | |
| const uiRender = async (arg: UIRenderProps<QRCodeSchema>) => { | |
| const { value, rootElement, mode, onChange, stopEditing, tabIndex, placeholder, schema, theme } = | |
| arg; | |
| const container = document.createElement('div'); | |
| const containerStyle: CSS.Properties = { | |
| ...fullSize, | |
| display: 'flex', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| fontFamily: "'Open Sans', sans-serif", | |
| }; | |
| Object.assign(container.style, containerStyle); | |
| rootElement.appendChild(container); | |
| const editable = isEditable(mode, schema); | |
| if (editable) { | |
| const input = document.createElement('input'); | |
| const inputStyle: CSS.Properties = { | |
| width: '100%', | |
| position: 'absolute', | |
| textAlign: 'center', | |
| fontSize: '12pt', | |
| fontWeight: 'bold', | |
| color: theme.colorWhite, | |
| backgroundColor: editable || value ? addAlphaToHex('#000000', 80) : 'none', | |
| border: 'none', | |
| display: 'flex', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| overflow: 'auto', | |
| }; | |
| Object.assign(input.style, inputStyle); | |
| input.value = value; | |
| input.placeholder = placeholder || ''; | |
| input.tabIndex = tabIndex || 0; | |
| input.addEventListener('change', (e: Event) => { | |
| onChange && onChange({ key: 'content', value: (e.target as HTMLInputElement).value }); | |
| }); | |
| input.addEventListener('blur', () => { | |
| stopEditing && stopEditing(); | |
| }); | |
| container.appendChild(input); | |
| input.setSelectionRange(value.length, value.length); | |
| if (mode === 'designer') { | |
| input.focus(); | |
| } | |
| } | |
| if (!value) return; | |
| try { | |
| // if (!validateBarcodeInput(schema.type, value)) | |
| // throw new Error('[@pdfme/schemas/barcodes] Invalid barcode input'); | |
| const imgElm = await createQRCodeImageElm(value); | |
| container.appendChild(imgElm); | |
| } catch (err) { | |
| console.error(`[@pdfme/ui] ${err}`); | |
| container.appendChild(createErrorElm()); | |
| } | |
| }; | |
| /* | |
| pdfRender | |
| */ | |
| const getBarcodeCacheKey = (schema: QRCodeSchema & Schema, value: string) => { | |
| return `${schema.type}${schema.backgroundColor}${schema.barColor}${value}`; | |
| }; | |
| const pdfRender = async (arg: PDFRenderProps<QRCodeSchema>) => { | |
| const { value, schema, pdfDoc, page, _cache } = arg; | |
| if (!value) return; | |
| const inputBarcodeCacheKey = getBarcodeCacheKey(schema, value); | |
| let image = _cache.get(inputBarcodeCacheKey); | |
| if (!image) { | |
| const dataURL: string = await QRCode.toDataURL(value); | |
| const base64 = dataURL.split(',')[1]; | |
| if (base64) { | |
| image = await pdfDoc.embedPng(base64); | |
| _cache.set(inputBarcodeCacheKey, image); | |
| } else { | |
| console.error("Failed to generate QRCode"); | |
| } | |
| } | |
| const pageHeight = page.getHeight(); | |
| const { | |
| width, | |
| height, | |
| rotate, | |
| position: { x, y }, | |
| opacity, | |
| } = convertForPdfLayoutProps({ schema, pageHeight }); | |
| page.drawImage(image, { x, y, rotate, width, height, opacity }); | |
| }; | |
| const qrCodeSchema: Plugin<QRCodeSchema> = { | |
| pdf: pdfRender, | |
| ui: uiRender, | |
| propPanel: { | |
| schema: ({ i18n }: { i18n: (key: string) => string }) => ({ | |
| barColor: { | |
| title: i18n('schemas.barcodes.barColor'), | |
| type: 'string', | |
| widget: 'color', | |
| props: { | |
| disabledAlpha: true, | |
| }, | |
| rules: [ | |
| { | |
| pattern: HEX_COLOR_PATTERN, | |
| message: i18n('validation.hexColor'), | |
| }, | |
| ], | |
| }, | |
| backgroundColor: { | |
| title: i18n('schemas.bgColor'), | |
| type: 'string', | |
| widget: 'color', | |
| props: { | |
| disabledAlpha: true, | |
| }, | |
| rules: [ | |
| { | |
| pattern: HEX_COLOR_PATTERN, | |
| message: i18n('validation.hexColor'), | |
| }, | |
| ], | |
| }, | |
| }), | |
| defaultSchema: { | |
| name: '', | |
| type: 'node-qrCode', | |
| content: 'https://pdfme.com/', | |
| position: { x: 0, y: 0 }, | |
| width: 20, | |
| height: 20, | |
| backgroundColor: DEFAULT_BARCODE_BG_COLOR, | |
| barColor: DEFAULT_BARCODE_COLOR, | |
| rotate: 0, | |
| opacity: DEFAULT_OPACITY, | |
| }, | |
| }, | |
| icon: createSvgStr(Lucide.QrCode), | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment


Before using this plugin, please install
qrcodepackage from npm.Then put this file under your projfect folder somewhere like `./src/plugins/qrCode.ts', then import it from your generator / playgournd file as follows.
Add the following schema into your template.
{ "type": "node-qrCode", "content": "https://pdfme.com/", "position": { "x": 178, "y": 20 }, "backgroundColor": "#ffffff", "barColor": "#000000", "width": 16, "height": 16, "rotate": 0, "opacity": 1, "required": false, "readOnly": false, "name": "qrCode" },