diff --git a/README.md b/README.md index ab6dc0b..d1848fc 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ A angular component to generate QR codes for rendering to the DOM. ## Introduction Support canvas and svg two formats + Support adding QR code logo Angular version >= 14 @@ -45,14 +46,14 @@ import { QrcodeModule } from 'qrcode-angular'; ## API | prop | type | default value | note | -| ------------ | --------------------------------- | --------------------------------- |---------------------------| +| ------------ | --------------------------------- |-----------------------------------|---------------------------| | `mode` | `'canvas'|'svg'` | `'canvas'` | output mode | | `value` | `string` | - | scanned link | | `color` | `{ dark: string, light: string }` | `{ dark: '#000', light: '#fff' }` | QR code Color | | `size` | `number` | `160` | QR code Size | | `icon` | `string` | - | QR code include logo url | | `iconSize` | `number` | `40` | QR code include logo size | -| `errorLevel` | `'L'|'M'|'Q'|'H'` | `H` | Error Code Level | +| `errorLevel` | `'L'|'M'|'Q'|'H'` | `M` | Error Code Level | ## Notice diff --git a/projects/qrcode-angular/README.md b/projects/qrcode-angular/README.md index f5df9fb..eb442a6 100644 --- a/projects/qrcode-angular/README.md +++ b/projects/qrcode-angular/README.md @@ -5,6 +5,7 @@ A angular component to generate QR codes for rendering to the DOM. ## Introduction Support canvas and svg two formats + Support adding QR code logo Angular version >= 14 @@ -45,14 +46,14 @@ import { QrcodeModule } from 'qrcode-angular'; ## API | prop | type | default value | note | -| ------------ | --------------------------------- | --------------------------------- |---------------------------| +| ------------ | --------------------------------- |-----------------------------------|---------------------------| | `mode` | `'canvas'|'svg'` | `'canvas'` | output mode | | `value` | `string` | - | scanned link | | `color` | `{ dark: string, light: string }` | `{ dark: '#000', light: '#fff' }` | QR code Color | | `size` | `number` | `160` | QR code Size | | `icon` | `string` | - | QR code include logo url | | `iconSize` | `number` | `40` | QR code include logo size | -| `errorLevel` | `'L'|'M'|'Q'|'H'` | `H` | Error Code Level | +| `errorLevel` | `'L'|'M'|'Q'|'H'` | `M` | Error Code Level | ## Notice diff --git a/projects/qrcode-angular/src/lib/qrcode.component.ts b/projects/qrcode-angular/src/lib/qrcode.component.ts index 9498d96..d370891 100644 --- a/projects/qrcode-angular/src/lib/qrcode.component.ts +++ b/projects/qrcode-angular/src/lib/qrcode.component.ts @@ -10,7 +10,7 @@ import { ViewChild } from '@angular/core'; -import { drawCanvas, plotQrCodeData, toSvgString } from './qrcode'; +import { drawCanvas, ERROR_LEVEL_MAP, plotQrCodeData, toSvgString } from './qrcode'; @Component({ selector: 'qrcode', template: ` @@ -30,7 +30,7 @@ import { drawCanvas, plotQrCodeData, toSvgString } from './qrcode'; ; @Input() mode: 'canvas' | 'svg' = 'canvas'; @Input() value: string = ''; @Input() color: { dark: string; light: string } = { dark: '#000', light: '#fff' }; @Input() size: number = 160; @Input() icon: string = ''; @Input() iconSize: number = 40; - @Input() errorLevel: 'L' | 'M' | 'Q' | 'H' = 'M'; + @Input() errorLevel: keyof typeof ERROR_LEVEL_MAP = 'M'; + + svgImg: boolean = true; path: string | null = null; viewBox: string | null = null; @@ -82,8 +87,8 @@ export class QrcodeComponent implements OnInit, OnChanges, AfterViewInit { ngOnInit(): void {} ngOnChanges(changes: SimpleChanges): void { - const { mode, value, icon, errorLevel } = changes; - if (mode || value || icon || errorLevel) { + const { mode, value, color, icon, errorLevel } = changes; + if (mode || value || color || icon || errorLevel) { switch (this.mode) { case 'canvas': if (this.canvas) this.drawCanvasQRCode(); @@ -107,6 +112,22 @@ export class QrcodeComponent implements OnInit, OnChanges, AfterViewInit { } drawSvgQrCode(): void { + if (!this.icon) { + this.drawSvgQrCodeFilling(); + } else { + const iconImg = new Image(); + iconImg.src = this.icon; + iconImg.crossOrigin = 'anonymous'; + iconImg.onload = () => this.drawSvgQrCodeFilling(); + iconImg.onerror = () => { + this.svgImg = false; + this.drawSvgQrCodeFilling(); + this.cdr.markForCheck(); + }; + } + } + + drawSvgQrCodeFilling(): void { const svg = toSvgString(plotQrCodeData(this.value, this.errorLevel)); this.path = svg.parts.join(' '); this.viewBox = `0 0 ${svg.size} ${svg.size}`; @@ -118,12 +139,8 @@ export class QrcodeComponent implements OnInit, OnChanges, AfterViewInit { } drawCanvasQRCode(): void { - if (!this.value) { - return; - } - drawCanvas( - this.canvas, + this.canvas.nativeElement, plotQrCodeData(this.value, this.errorLevel), this.size, 20, diff --git a/projects/qrcode-angular/src/lib/qrcode.ts b/projects/qrcode-angular/src/lib/qrcode.ts index 358aa5b..6f07175 100644 --- a/projects/qrcode-angular/src/lib/qrcode.ts +++ b/projects/qrcode-angular/src/lib/qrcode.ts @@ -1,22 +1,23 @@ -import { ElementRef } from '@angular/core'; - import qrcodegen from './qrcodegen'; -const ERROR_LEVEL_MAP: { [index: string]: qrcodegen.QrCode.Ecc } = { +export const ERROR_LEVEL_MAP: { [index in 'L' | 'M' | 'Q' | 'H']: qrcodegen.QrCode.Ecc } = { L: qrcodegen.QrCode.Ecc.LOW, M: qrcodegen.QrCode.Ecc.MEDIUM, Q: qrcodegen.QrCode.Ecc.QUARTILE, H: qrcodegen.QrCode.Ecc.HIGH -}; +} as const; const DEFAULT_SIZE = 160; const DEFAULT_SCALE = 10; const DEFAULT_BGCOLOR = '#FFFFFF'; const DEFAULT_FGCOLOR = '#000000'; const DEFAULT_ICONSIZE = 40; -const DEFAULT_LEVEL = 'L'; +const DEFAULT_LEVEL: keyof typeof ERROR_LEVEL_MAP = 'M'; -export const toSvgString = (value: qrcodegen.QrCode): { size: number; parts: string[] } => { +export const toSvgString = (value: qrcodegen.QrCode | null): { size: number; parts: string[] } => { + if (!value) { + return { size: 0, parts: [] }; + } let parts: string[] = []; for (let y = 0; y < value.size; y++) { for (let x = 0; x < value.size; x++) { @@ -27,13 +28,19 @@ export const toSvgString = (value: qrcodegen.QrCode): { size: number; parts: str return { size: value.size, parts }; }; -export const plotQrCodeData = (value: string, level = DEFAULT_LEVEL): qrcodegen.QrCode => { +export const plotQrCodeData = ( + value: string, + level: keyof typeof ERROR_LEVEL_MAP = DEFAULT_LEVEL +): qrcodegen.QrCode | null => { + if (!value) { + return null; + } return qrcodegen.QrCode.encodeText(value, ERROR_LEVEL_MAP[level]); }; export function drawCanvas( - canvas: ElementRef, - value: qrcodegen.QrCode, + canvas: HTMLCanvasElement, + value: qrcodegen.QrCode | null, size = DEFAULT_SIZE, scale = DEFAULT_SCALE, lightColor = DEFAULT_BGCOLOR, @@ -41,47 +48,53 @@ export function drawCanvas( iconSize = DEFAULT_ICONSIZE, icon?: string ): void { - const ctx = canvas.nativeElement.getContext('2d') as CanvasRenderingContext2D; - canvas.nativeElement.width = value.size * scale; - canvas.nativeElement.height = value.size * scale; - canvas.nativeElement.style.width = `${size}px`; - canvas.nativeElement.style.height = `${size}px`; + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + canvas.style.width = `${size}px`; + canvas.style.height = `${size}px`; + if (!value) { + ctx.fillStyle = lightColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return; + } + canvas.width = value.size * scale; + canvas.height = value.size * scale; if (!icon) { - for (let y = 0; y < value.size; y++) { - for (let x = 0; x < value.size; x++) { - ctx.fillStyle = value.getModule(x, y) ? darkColor : lightColor; - ctx.fillRect(x * scale, y * scale, scale, scale); - } - } + drawCanvasColor(ctx, value, scale, darkColor, lightColor); } else { const iconImg = new Image(); iconImg.src = icon; iconImg.crossOrigin = 'anonymous'; - iconImg.width = iconSize * (canvas.nativeElement.width / size); - iconImg.height = iconSize * (canvas.nativeElement.width / size); + iconImg.width = iconSize * (canvas.width / size); + iconImg.height = iconSize * (canvas.width / size); iconImg.onload = () => { - for (let y = 0; y < value.size; y++) { - for (let x = 0; x < value.size; x++) { - ctx.fillStyle = value.getModule(x, y) ? darkColor : lightColor; - ctx.fillRect(x * scale, y * scale, scale, scale); - } - } - const iconCoordinate = canvas.nativeElement.width / 2 - (iconSize * (canvas.nativeElement.width / size)) / 2; + drawCanvasColor(ctx, value, scale, darkColor, lightColor); + const iconCoordinate = canvas.width / 2 - (iconSize * (canvas.width / size)) / 2; ctx.fillStyle = lightColor; - ctx.fillRect( - iconCoordinate, - iconCoordinate, - iconSize * (canvas.nativeElement.width / size), - iconSize * (canvas.nativeElement.width / size) - ); + ctx.fillRect(iconCoordinate, iconCoordinate, iconSize * (canvas.width / size), iconSize * (canvas.width / size)); ctx.drawImage( iconImg, iconCoordinate, iconCoordinate, - iconSize * (canvas.nativeElement.width / size), - iconSize * (canvas.nativeElement.width / size) + iconSize * (canvas.width / size), + iconSize * (canvas.width / size) ); }; + iconImg.onerror = () => drawCanvasColor(ctx, value, scale, darkColor, lightColor); + } +} + +export function drawCanvasColor( + ctx: CanvasRenderingContext2D, + value: qrcodegen.QrCode, + scale: number, + darkColor: string, + lightColor: string +): void { + for (let y = 0; y < value.size; y++) { + for (let x = 0; x < value.size; x++) { + ctx.fillStyle = value.getModule(x, y) ? darkColor : lightColor; + ctx.fillRect(x * scale, y * scale, scale, scale); + } } } diff --git a/projects/qrcode-angular/src/lib/qrcodegen/index.ts b/projects/qrcode-angular/src/lib/qrcodegen.ts similarity index 97% rename from projects/qrcode-angular/src/lib/qrcodegen/index.ts rename to projects/qrcode-angular/src/lib/qrcodegen.ts index 0434727..611b0a7 100644 --- a/projects/qrcode-angular/src/lib/qrcodegen/index.ts +++ b/projects/qrcode-angular/src/lib/qrcodegen.ts @@ -4,6 +4,31 @@ * SPDX-License-Identifier: MIT */ +/** + * QR Code generator library (TypeScript) + * + * Copyright (c) Project Nayuki. + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + 'use strict'; namespace qrcodegen { diff --git a/projects/qrcode-angular/src/lib/qrcodegen/LICENSE b/projects/qrcode-angular/src/lib/qrcodegen/LICENSE deleted file mode 100644 index 4f9a9fa..0000000 --- a/projects/qrcode-angular/src/lib/qrcodegen/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -QR Code generator library (TypeScript) - -Copyright (c) Project Nayuki. -https://www.nayuki.io/page/qr-code-generator-library - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/projects/qrcode-angular/src/lib/qrcodegen/README.md b/projects/qrcode-angular/src/lib/qrcodegen/README.md deleted file mode 100644 index a0409d2..0000000 --- a/projects/qrcode-angular/src/lib/qrcodegen/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# QR Code generator library (TypeScript) - -Copyright (c) Project Nayuki. (MIT License) - -https://www.nayuki.io/page/qr-code-generator-library - -Obtained via https://github.com/nayuki/QR-Code-generator/blob/942f4319a6ba913dbc6775d8e665ccf18f401d83/typescript-javascript/qrcodegen.ts - -## Modifications: -- Export for use as a module -- Added `getModules` method to `QrCode` class, to bypass excessive calls to `getModule`.