diff --git a/README.md b/README.md
index 9b9d58be..6fb9dc8b 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ If you have issues / suggestions / notes / questions, please open an issue or co
### Extensions
If you would like to use additional stiles, you can connect extensions.
-#### [qr-border-plugin](https://www.lefe.dev/marketplace/qr-border-plugin)
+#### [qr-border-plugin](https://www.npmjs.com/package/qr-border-plugin)
diff --git a/src/core/QRCodeStyling.ts b/src/core/QRCodeStyling.ts
index abe94399..269fd8e4 100644
--- a/src/core/QRCodeStyling.ts
+++ b/src/core/QRCodeStyling.ts
@@ -6,8 +6,10 @@ import drawTypes from "../constants/drawTypes";
import defaultOptions, { RequiredOptions } from "./QROptions";
import sanitizeOptions from "../tools/sanitizeOptions";
-import { FileExtension, QRCode, Options, DownloadOptions, ExtensionFunction, Window, Canvas } from "../types";
+import { FileExtension, QRCode, Options, DownloadOptions, ExtensionFunction, Window } from "../types";
import qrcode from "qrcode-generator";
+import getMimeType from "../tools/getMimeType";
+import { Canvas as NodeCanvas, Image } from "canvas";
declare const window: Window;
@@ -15,7 +17,8 @@ export default class QRCodeStyling {
_options: RequiredOptions;
_window: Window;
_container?: HTMLElement;
- _canvas?: Canvas;
+ _domCanvas?: HTMLCanvasElement;
+ _nodeCanvas?: NodeCanvas;
_svg?: SVGElement;
_qr?: QRCode;
_extension?: ExtensionFunction;
@@ -57,12 +60,14 @@ export default class QRCodeStyling {
}
if (this._options.nodeCanvas?.createCanvas) {
- this._canvas = this._options.nodeCanvas.createCanvas(this._options.width, this._options.height);
+ this._nodeCanvas = this._options.nodeCanvas.createCanvas(this._options.width, this._options.height);
+ this._nodeCanvas.width = this._options.width;
+ this._nodeCanvas.height = this._options.height;
} else {
- this._canvas = document.createElement("canvas");
+ this._domCanvas = document.createElement("canvas");
+ this._domCanvas.width = this._options.width;
+ this._domCanvas.height = this._options.height;
}
- this._canvas.width = this._options.width;
- this._canvas.height = this._options.height;
this._setupSvg();
this._canvasDrawingPromise = this._svgDrawingPromise?.then(() => {
@@ -71,21 +76,21 @@ export default class QRCodeStyling {
const svg = this._svg;
const xml = new this._window.XMLSerializer().serializeToString(svg);
const svg64 = btoa(xml);
- const image64 = "data:image/svg+xml;base64," + svg64;
+ const image64 = `data:${getMimeType('svg')};base64,${svg64}`;
if (this._options.nodeCanvas?.loadImage) {
- return this._options.nodeCanvas.loadImage(image64).then((image: HTMLImageElement) => {
+ return this._options.nodeCanvas.loadImage(image64).then((image: Image) => {
// fix blurry svg
image.width = this._options.width;
image.height = this._options.height;
- this._canvas?.getContext("2d")?.drawImage(image, 0, 0);
+ this._nodeCanvas?.getContext("2d")?.drawImage(image, 0, 0);
});
} else {
const image = new this._window.Image();
return new Promise((resolve) => {
image.onload = (): void => {
- this._canvas?.getContext("2d")?.drawImage(image, 0, 0);
+ this._domCanvas?.getContext("2d")?.drawImage(image, 0, 0);
resolve();
};
@@ -95,7 +100,7 @@ export default class QRCodeStyling {
});
}
- async _getElement(extension: FileExtension = "png"): Promise {
+ async _getElement(extension: FileExtension = "png") {
if (!this._qr) throw "QR code is empty";
if (extension.toLowerCase() === "svg") {
@@ -105,11 +110,11 @@ export default class QRCodeStyling {
await this._svgDrawingPromise;
return this._svg;
} else {
- if (!this._canvas || !this._canvasDrawingPromise) {
+ if (!(this._domCanvas || this._nodeCanvas) || !this._canvasDrawingPromise) {
this._setupCanvas();
}
await this._canvasDrawingPromise;
- return this._canvas;
+ return this._domCanvas || this._nodeCanvas;
}
}
@@ -144,8 +149,8 @@ export default class QRCodeStyling {
}
if (this._options.type === drawTypes.canvas) {
- if (this._canvas) {
- container.appendChild(this._canvas);
+ if (this._domCanvas) {
+ container.appendChild(this._domCanvas);
}
} else {
if (this._svg) {
@@ -173,6 +178,7 @@ export default class QRCodeStyling {
async getRawData(extension: FileExtension = "png"): Promise {
if (!this._qr) throw "QR code is empty";
const element = await this._getElement(extension);
+ const mimeType = getMimeType(extension);
if (!element) {
return null;
@@ -180,20 +186,29 @@ export default class QRCodeStyling {
if (extension.toLowerCase() === "svg") {
const serializer = new this._window.XMLSerializer();
- const source = serializer.serializeToString(element);
+ const source = serializer.serializeToString(element as SVGElement);
const svgString = `\r\n${source}`;
if (typeof Blob !== "undefined" && !this._options.jsdom) {
- return new Blob([svgString], { type: "image/svg+xml" });
+ return new Blob([svgString], { type: mimeType });
} else {
return Buffer.from(svgString);
}
} else {
return new Promise((resolve) => {
- const canvas = element as Canvas;
- if (canvas.toBuffer) {
- resolve(canvas.toBuffer(`image/${extension}`));
- } else {
- canvas.toBlob(resolve, `image/${extension}`, 1);
+ const canvas = element;
+ if ('toBuffer' in canvas) {
+ // Different call is needed to prevent error TS2769: No overload matches this call.
+ if (mimeType === "image/png") {
+ resolve(canvas.toBuffer(mimeType));
+ } else if (mimeType === "image/jpeg") {
+ resolve(canvas.toBuffer(mimeType));
+ } else if (mimeType === "application/pdf") {
+ resolve(canvas.toBuffer(mimeType));
+ } else {
+ throw Error("Unsupported extension");
+ }
+ } else if ('toBlob' in canvas) {
+ (canvas).toBlob(resolve, mimeType, 1);
}
});
}
@@ -228,13 +243,13 @@ export default class QRCodeStyling {
if (extension.toLowerCase() === "svg") {
const serializer = new XMLSerializer();
- let source = serializer.serializeToString(element);
+ let source = serializer.serializeToString(element as SVGElement);
source = '\r\n' + source;
- const url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
+ const url = `data:${getMimeType(extension)};charset=utf-8,${encodeURIComponent(source)}`;
downloadURI(url, `${name}.svg`);
} else {
- const url = (element as HTMLCanvasElement).toDataURL(`image/${extension}`);
+ const url = (element as HTMLCanvasElement).toDataURL(getMimeType(extension));
downloadURI(url, `${name}.${extension}`);
}
}
diff --git a/src/core/QRSVG.ts b/src/core/QRSVG.ts
index cc23ba23..52297fc7 100644
--- a/src/core/QRSVG.ts
+++ b/src/core/QRSVG.ts
@@ -6,7 +6,8 @@ import QRCornerDot from "../figures/cornerDot/QRCornerDot";
import { RequiredOptions } from "./QROptions";
import gradientTypes from "../constants/gradientTypes";
import shapeTypes from "../constants/shapeTypes";
-import { QRCode, FilterFunction, Gradient, Window, Canvas } from "../types";
+import { QRCode, FilterFunction, Gradient, Window } from "../types";
+import { Canvas as NodeCanvas, Image } from "canvas";
const squareMask = [
[1, 1, 1, 1, 1, 1, 1],
@@ -29,7 +30,8 @@ const dotMask = [
];
export default class QRSVG {
- _canvas?: Canvas;
+ _domCanvas?: HTMLCanvasElement;
+ _nodeCanvas?: NodeCanvas;
_window: Window;
_element: SVGElement;
_defs: SVGElement;
@@ -39,7 +41,7 @@ export default class QRSVG {
_cornersDotClipPath?: SVGElement;
_options: RequiredOptions;
_qr?: QRCode;
- _image?: HTMLImageElement;
+ _image?: HTMLImageElement | Image;
_imageUri?: string;
_instanceId: number;
@@ -61,12 +63,14 @@ export default class QRSVG {
if (options.imageOptions.saveAsBlob) {
if (options.nodeCanvas?.createCanvas) {
- this._canvas = options.nodeCanvas.createCanvas(options.width, options.height);
+ this._nodeCanvas = options.nodeCanvas.createCanvas(options.width, options.height);
+ this._nodeCanvas.width = options.width;
+ this._nodeCanvas.height = options.height;
} else {
- this._canvas = document.createElement("canvas");
+ this._domCanvas = document.createElement("canvas");
+ this._domCanvas.width = options.width;
+ this._domCanvas.height = options.height;
}
- this._canvas.width = options.width;
- this._canvas.height = options.height;
}
this._imageUri = options.image;
this._instanceId = QRSVG.instanceCount++;
@@ -103,7 +107,6 @@ export default class QRSVG {
//We need it to get image size
await this.loadImage();
if (!this._image) return;
- this.imageToBlob();
const { imageOptions, qrOptions } = this._options;
const coverLevel = imageOptions.imageSize * errorCorrectionPercents[qrOptions.errorCorrectionLevel];
const maxHiddenDots = Math.floor(coverLevel * count * count);
@@ -456,17 +459,6 @@ export default class QRSVG {
});
}
- imageToBlob(): void {
- if (!this._image) return;
- if (this._options.imageOptions.saveAsBlob && this._canvas) {
- const ctx = this._canvas.getContext("2d");
- if (ctx) {
- ctx.drawImage(this._image, 0, 0, this._canvas.width, this._canvas.height);
- this._imageUri = this._canvas.toDataURL("image/png");
- }
- }
- }
-
loadImage(): Promise {
return new Promise((resolve, reject) => {
const options = this._options;
@@ -478,13 +470,17 @@ export default class QRSVG {
if (options.nodeCanvas?.loadImage) {
options.nodeCanvas
.loadImage(options.image)
- .then((image: HTMLImageElement) => {
+ .then((image: Image) => {
// fix blurry svg
if (/(\.svg$)|(^data:image\/svg)/.test(options.image ?? "")) {
image.width = this._options.width;
image.height = this._options.height;
}
this._image = image;
+ if (this._options.imageOptions.saveAsBlob && this._nodeCanvas) {
+ this._nodeCanvas.getContext('2d')?.drawImage(image, 0, 0, this._nodeCanvas.width, this._nodeCanvas.height);
+ this._imageUri = this._nodeCanvas.toDataURL('image/png');
+ }
resolve();
})
.catch(reject);
@@ -497,6 +493,10 @@ export default class QRSVG {
this._image = image;
image.onload = (): void => {
+ if (this._options.imageOptions.saveAsBlob && this._domCanvas) {
+ this._domCanvas.getContext('2d')?.drawImage(image, 0, 0, this._domCanvas.width, this._domCanvas.height);
+ this._imageUri = this._domCanvas.toDataURL('image/png');
+ }
resolve();
};
image.src = options.image;
diff --git a/src/index.html b/src/index.html
index 9595c036..c734b651 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,76 +1,169 @@
-
+
-
-
- QR Code Styling
-
+
+
+ QR Code Styling
+
+
+
+
+
+
-
+ // qrCode1.download({ name: 'qr-codes/Vanbilloen'+'-color', extension: 'svg' })
+ // qrCode2.download({ name: 'qr-codes/Vanbilloen'+'-linear', extension: 'svg' })
+ // qrCode3.download({ name: 'qr-codes/Vanbilloen'+'-rounded', extension: 'svg' })
+
+