@happy-dom/node-canvas-adapter 20.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ ![Happy DOM Logo](https://github.com/capricorn86/happy-dom/raw/master/docs/happy-dom-logo.jpg)
2
+
3
+ Pluggable canvas adapter for [Happy DOM](https://github.com/capricorn86/happy-dom) using [node-canvas](https://github.com/Automattic/node-canvas).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install canvas @happy-dom/node-canvas-adapter
9
+ ```
10
+
11
+ ## Documentation
12
+
13
+ You will find the documentation in the [Happy DOM Wiki](https://github.com/capricorn86/happy-dom/wiki) under [Node Canvas Adapter](https://github.com/capricorn86/happy-dom/wiki/Node-Canvas-Adapter).
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import { Window } from 'happy-dom';
19
+ import { CanvasAdapter } from '@happy-dom/node-canvas-adapter';
20
+
21
+ const window = new Window({
22
+ settings: {
23
+ canvasAdapter: new CanvasAdapter(),
24
+ // Optionally, enable image file loading (e.g. for <img> elements)
25
+ enableImageFileLoading: true
26
+ }
27
+ });
28
+
29
+ const canvas = window.document.createElement('canvas');
30
+ const context = canvas.getContext('2d');
31
+
32
+ canvas.width = 200;
33
+ canvas.height = 200;
34
+
35
+ // Now you can use canvas context
36
+ context.fillStyle = 'red';
37
+ context.fillRect(0, 0, 100, 100);
38
+
39
+ // Get data URL
40
+ const dataUrl = canvas.toDataURL();
41
+
42
+ // Output the data URL
43
+ console.log(dataUrl);
44
+ ```
45
+
46
+ ## Happy DOM
47
+
48
+ [Documentation](https://github.com/capricorn86/happy-dom/wiki/) | [Getting Started](https://github.com/capricorn86/happy-dom/wiki/Getting-started) | [Setup as Test Environment](https://github.com/capricorn86/happy-dom/wiki/Setup-as-Test-Environment) | [GitHub](https://github.com/capricorn86/happy-dom/)
@@ -0,0 +1,69 @@
1
+ import { type ICanvasAdapter, type ICanvasRenderingContext2D, type ICanvasAdapterCaller, type Blob } from 'happy-dom';
2
+ /**
3
+ * Canvas adapter that delegates rendering to the `canvas` npm package.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { Window } from 'happy-dom';
8
+ * import { CanvasAdapter } from '@happy-dom/node-canvas-adapter';
9
+ *
10
+ * const window = new Window({
11
+ * settings: { canvasAdapter: new CanvasAdapter() }
12
+ * });
13
+ *
14
+ * const canvas = window.document.createElement('canvas');
15
+ * const ctx = canvas.getContext('2d');
16
+ * ```
17
+ */
18
+ export default class CanvasAdapter implements ICanvasAdapter {
19
+ #private;
20
+ /**
21
+ * Creates a rendering context for the given canvas element.
22
+ *
23
+ * @param canvas.canvas
24
+ * @param canvas The canvas element.
25
+ * @param canvas.window Window.
26
+ * @param contextType The context identifier ('2d', 'webgl', etc.).
27
+ * @param contextAttributes Optional context creation attributes.
28
+ * @param canvas.canvasType
29
+ * @returns The rendering context, or null if the type is unsupported.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const ctx = adapter.getContext(canvas, '2d');
34
+ * ```
35
+ */
36
+ getContext({ canvas, window }: ICanvasAdapterCaller, contextType: string, contextAttributes?: Record<string, unknown>): ICanvasRenderingContext2D | null;
37
+ /**
38
+ * Serialize the canvas content as a data URL.
39
+ *
40
+ * @param caller Information about the caller, including the canvas element and its associated window and browser frame.
41
+ * @param caller.canvas Canvas.
42
+ * @param type MIME type of the output image.
43
+ * @param quality Encoder quality for lossy formats, in the range 0–1.
44
+ * @returns A data URL string.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const url = adapter.toDataURL(canvas, 'image/png');
49
+ * ```
50
+ */
51
+ toDataURL({ canvas }: ICanvasAdapterCaller, type?: string, quality?: unknown): string;
52
+ /**
53
+ * Creates a Blob from the canvas content and passes it to the callback.
54
+ *
55
+ * @param caller Information about the caller, including the canvas element and its associated window and browser frame.
56
+ * @param caller.canvas Canvas.
57
+ * @param caller.window Window.
58
+ * @param canvas The canvas element.
59
+ * @param callback Receives the resulting Blob, or null on failure.
60
+ * @param type MIME type of the output image.
61
+ * @param quality Encoder quality for lossy formats, in the range 0–1.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * adapter.toBlob(canvas, (blob) => console.log(blob?.size));
66
+ * ```
67
+ */
68
+ toBlob({ canvas, window }: ICanvasAdapterCaller, callback: (blob: Blob | null) => void, type?: string, quality?: unknown): void;
69
+ }
@@ -0,0 +1,216 @@
1
+ import { createCanvas, Image as CanvasImage } from 'canvas';
2
+ import { HTMLCanvasElement, HTMLImageElement, OffscreenCanvas, ImageData, PropertySymbol, HTMLVideoElement, ImageBitmap } from 'happy-dom';
3
+ const EXTENDED_SYMBOL = Symbol('extended');
4
+ /**
5
+ * Canvas adapter that delegates rendering to the `canvas` npm package.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { Window } from 'happy-dom';
10
+ * import { CanvasAdapter } from '@happy-dom/node-canvas-adapter';
11
+ *
12
+ * const window = new Window({
13
+ * settings: { canvasAdapter: new CanvasAdapter() }
14
+ * });
15
+ *
16
+ * const canvas = window.document.createElement('canvas');
17
+ * const ctx = canvas.getContext('2d');
18
+ * ```
19
+ */
20
+ export default class CanvasAdapter {
21
+ #canvases = new WeakMap();
22
+ /**
23
+ * Returns or creates a node-canvas instance bound to the given canvas element.
24
+ *
25
+ * @param canvas The canvas element.
26
+ * @returns The bound node-canvas instance.
27
+ */
28
+ #getNodeCanvas(canvas) {
29
+ const existing = this.#canvases.get(canvas);
30
+ if (existing !== undefined) {
31
+ return existing;
32
+ }
33
+ const nodeCanvas = createCanvas(canvas.width, canvas.height);
34
+ this.#canvases.set(canvas, nodeCanvas);
35
+ return nodeCanvas;
36
+ }
37
+ /**
38
+ * Creates a rendering context for the given canvas element.
39
+ *
40
+ * @param canvas.canvas
41
+ * @param canvas The canvas element.
42
+ * @param canvas.window Window.
43
+ * @param contextType The context identifier ('2d', 'webgl', etc.).
44
+ * @param contextAttributes Optional context creation attributes.
45
+ * @param canvas.canvasType
46
+ * @returns The rendering context, or null if the type is unsupported.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const ctx = adapter.getContext(canvas, '2d');
51
+ * ```
52
+ */
53
+ getContext({ canvas, window }, contextType, contextAttributes) {
54
+ if (contextType !== '2d') {
55
+ return null;
56
+ }
57
+ const context = this.#getNodeCanvas(canvas).getContext('2d', contextAttributes);
58
+ if (context === null) {
59
+ return null;
60
+ }
61
+ if (context[EXTENDED_SYMBOL]) {
62
+ return context;
63
+ }
64
+ context[EXTENDED_SYMBOL] = true;
65
+ // drawImage()
66
+ const originalDrawImage = context.drawImage;
67
+ context.drawImage = (...args) => {
68
+ const shape = args[0];
69
+ const drawImageBuffer = (buffer) => {
70
+ const canvasImage = new CanvasImage();
71
+ canvasImage.src = buffer;
72
+ canvasImage.width = shape.width;
73
+ canvasImage.height = shape.height;
74
+ args[0] = canvasImage;
75
+ originalDrawImage.apply(context, args);
76
+ };
77
+ if (shape instanceof HTMLImageElement) {
78
+ if (shape[PropertySymbol.buffer]) {
79
+ drawImageBuffer(shape[PropertySymbol.buffer]);
80
+ return;
81
+ }
82
+ if (!shape.complete) {
83
+ shape.addEventListener('load', () => {
84
+ if (shape[PropertySymbol.buffer]) {
85
+ drawImageBuffer(shape[PropertySymbol.buffer]);
86
+ }
87
+ });
88
+ }
89
+ return;
90
+ }
91
+ if (shape instanceof HTMLVideoElement) {
92
+ // Not supported yet
93
+ return;
94
+ }
95
+ if (shape instanceof ImageBitmap ||
96
+ shape instanceof HTMLCanvasElement ||
97
+ shape instanceof OffscreenCanvas) {
98
+ const nodeCanvas = this.#getNodeCanvas(shape[PropertySymbol.canvas] || shape);
99
+ if (nodeCanvas) {
100
+ args[0] = nodeCanvas;
101
+ originalDrawImage.apply(context, args);
102
+ }
103
+ return;
104
+ }
105
+ };
106
+ // createImageData()
107
+ const originalCreateImageData = context.createImageData;
108
+ context.createImageData = (...args) => {
109
+ const imageData = originalCreateImageData.apply(context, args);
110
+ return new ImageData(imageData.data, imageData.width, imageData.height);
111
+ };
112
+ // putImageData()
113
+ const originalPutImageData = context.putImageData;
114
+ context.putImageData = (...args) => {
115
+ const imageData = args[0];
116
+ if (!(imageData instanceof ImageData)) {
117
+ throw new window.TypeError(`Failed to execute 'putImageData' on 'CanvasRenderingContext2D': parameter 1 is not of type 'ImageData'`);
118
+ }
119
+ const canvasImageData = originalCreateImageData.call(context, imageData.width, imageData.height);
120
+ canvasImageData.data.set(imageData.data);
121
+ args[0] = canvasImageData;
122
+ originalPutImageData.apply(context, args);
123
+ };
124
+ // getImageData()
125
+ const originalGetImageData = context.getImageData;
126
+ context.getImageData = (...args) => {
127
+ const imageData = originalGetImageData.apply(context, args);
128
+ return new ImageData(imageData.data, imageData.width, imageData.height);
129
+ };
130
+ // createPattern()
131
+ const originalCreatePattern = context.createPattern;
132
+ context.createPattern = (shape, repetition) => {
133
+ if (shape instanceof HTMLImageElement) {
134
+ if (!shape[PropertySymbol.buffer]) {
135
+ return null;
136
+ }
137
+ const canvasImage = new CanvasImage();
138
+ canvasImage.src = shape[PropertySymbol.buffer];
139
+ canvasImage.width = shape.width;
140
+ canvasImage.height = shape.height;
141
+ return originalCreatePattern.call(context, canvasImage, repetition);
142
+ }
143
+ if (shape instanceof HTMLVideoElement) {
144
+ // Not supported yet
145
+ return null;
146
+ }
147
+ if (shape instanceof ImageBitmap ||
148
+ shape instanceof HTMLCanvasElement ||
149
+ shape instanceof OffscreenCanvas) {
150
+ const nodeCanvas = this.#getNodeCanvas(shape[PropertySymbol.canvas] || shape);
151
+ if (nodeCanvas) {
152
+ return originalCreatePattern.call(context, nodeCanvas, repetition);
153
+ }
154
+ }
155
+ return null;
156
+ };
157
+ return context;
158
+ }
159
+ /**
160
+ * Serialize the canvas content as a data URL.
161
+ *
162
+ * @param caller Information about the caller, including the canvas element and its associated window and browser frame.
163
+ * @param caller.canvas Canvas.
164
+ * @param type MIME type of the output image.
165
+ * @param quality Encoder quality for lossy formats, in the range 0–1.
166
+ * @returns A data URL string.
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const url = adapter.toDataURL(canvas, 'image/png');
171
+ * ```
172
+ */
173
+ toDataURL({ canvas }, type, quality) {
174
+ const nodeCanvas = this.#getNodeCanvas(canvas);
175
+ if (type === 'image/jpeg') {
176
+ return nodeCanvas.toDataURL('image/jpeg', quality);
177
+ }
178
+ return nodeCanvas.toDataURL('image/png');
179
+ }
180
+ /**
181
+ * Creates a Blob from the canvas content and passes it to the callback.
182
+ *
183
+ * @param caller Information about the caller, including the canvas element and its associated window and browser frame.
184
+ * @param caller.canvas Canvas.
185
+ * @param caller.window Window.
186
+ * @param canvas The canvas element.
187
+ * @param callback Receives the resulting Blob, or null on failure.
188
+ * @param type MIME type of the output image.
189
+ * @param quality Encoder quality for lossy formats, in the range 0–1.
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * adapter.toBlob(canvas, (blob) => console.log(blob?.size));
194
+ * ```
195
+ */
196
+ toBlob({ canvas, window }, callback, type, quality) {
197
+ const nodeCanvas = this.#getNodeCanvas(canvas);
198
+ if (type === 'image/jpeg') {
199
+ nodeCanvas.toBuffer((error, buffer) => {
200
+ if (error !== null) {
201
+ callback(null);
202
+ return;
203
+ }
204
+ callback(new window.Blob([new Uint8Array(buffer)], { type: 'image/jpeg' }));
205
+ }, 'image/jpeg', { quality: quality });
206
+ return;
207
+ }
208
+ nodeCanvas.toBuffer((error, buffer) => {
209
+ if (error !== null) {
210
+ callback(null);
211
+ return;
212
+ }
213
+ callback(new window.Blob([new Uint8Array(buffer)], { type: type ?? 'image/png' }));
214
+ });
215
+ }
216
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { default as CanvasAdapter } from './CanvasAdapter.js';
package/lib/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default as CanvasAdapter } from './CanvasAdapter.js';
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@happy-dom/node-canvas-adapter",
3
+ "version": "20.10.0",
4
+ "license": "MIT",
5
+ "homepage": "https://github.com/capricorn86/happy-dom/tree/master/packages/@happy-dom/node-canvas-adapter",
6
+ "repository": "https://github.com/capricorn86/happy-dom",
7
+ "author": "David Ortner",
8
+ "description": "Pluggable canvas adapter for happy-dom using node-canvas.",
9
+ "main": "lib/index.js",
10
+ "type": "module",
11
+ "keywords": [
12
+ "canvas",
13
+ "happy-dom",
14
+ "adapter",
15
+ "node-canvas",
16
+ "rendering"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "compile": "tsc",
23
+ "watch": "tsc -w --preserveWatchOutput",
24
+ "test": "vitest run",
25
+ "test:debug": "vitest run --inspect-brk --no-file-parallelism"
26
+ },
27
+ "peerDependencies": {
28
+ "canvas": "^3.2.3",
29
+ "happy-dom": ">=20.9.0"
30
+ },
31
+ "devDependencies": {
32
+ "@vitest/ui": "^4.0.16",
33
+ "canvas": "^3.2.1",
34
+ "happy-dom": "^20.10.0",
35
+ "typescript": "^5.8.3",
36
+ "vitest": "^4.0.16"
37
+ },
38
+ "engines": {
39
+ "node": ">=20.0.0"
40
+ }
41
+ }
@@ -0,0 +1,275 @@
1
+ import { createCanvas, Image as CanvasImage } from 'canvas';
2
+ import {
3
+ HTMLCanvasElement,
4
+ HTMLImageElement,
5
+ OffscreenCanvas,
6
+ ImageData,
7
+ PropertySymbol,
8
+ type ICanvasAdapter,
9
+ type ICanvasRenderingContext2D,
10
+ type ICanvasAdapterCaller,
11
+ type ICanvasShape,
12
+ type Blob,
13
+ HTMLVideoElement,
14
+ ImageBitmap
15
+ } from 'happy-dom';
16
+
17
+ const EXTENDED_SYMBOL = Symbol('extended');
18
+
19
+ /**
20
+ * Canvas adapter that delegates rendering to the `canvas` npm package.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { Window } from 'happy-dom';
25
+ * import { CanvasAdapter } from '@happy-dom/node-canvas-adapter';
26
+ *
27
+ * const window = new Window({
28
+ * settings: { canvasAdapter: new CanvasAdapter() }
29
+ * });
30
+ *
31
+ * const canvas = window.document.createElement('canvas');
32
+ * const ctx = canvas.getContext('2d');
33
+ * ```
34
+ */
35
+ export default class CanvasAdapter implements ICanvasAdapter {
36
+ readonly #canvases = new WeakMap<ICanvasShape, ReturnType<typeof createCanvas>>();
37
+
38
+ /**
39
+ * Returns or creates a node-canvas instance bound to the given canvas element.
40
+ *
41
+ * @param canvas The canvas element.
42
+ * @returns The bound node-canvas instance.
43
+ */
44
+ #getNodeCanvas(canvas: ICanvasShape): ReturnType<typeof createCanvas> {
45
+ const existing = this.#canvases.get(canvas);
46
+ if (existing !== undefined) {
47
+ return existing;
48
+ }
49
+ const nodeCanvas = createCanvas(canvas.width, canvas.height);
50
+ this.#canvases.set(canvas, nodeCanvas);
51
+ return nodeCanvas;
52
+ }
53
+
54
+ /**
55
+ * Creates a rendering context for the given canvas element.
56
+ *
57
+ * @param canvas.canvas
58
+ * @param canvas The canvas element.
59
+ * @param canvas.window Window.
60
+ * @param contextType The context identifier ('2d', 'webgl', etc.).
61
+ * @param contextAttributes Optional context creation attributes.
62
+ * @param canvas.canvasType
63
+ * @returns The rendering context, or null if the type is unsupported.
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * const ctx = adapter.getContext(canvas, '2d');
68
+ * ```
69
+ */
70
+ public getContext(
71
+ { canvas, window }: ICanvasAdapterCaller,
72
+ contextType: string,
73
+ contextAttributes?: Record<string, unknown>
74
+ ): ICanvasRenderingContext2D | null {
75
+ if (contextType !== '2d') {
76
+ return null;
77
+ }
78
+ const context = this.#getNodeCanvas(canvas).getContext('2d', contextAttributes);
79
+
80
+ if (context === null) {
81
+ return null;
82
+ }
83
+
84
+ if ((<any>context)[EXTENDED_SYMBOL]) {
85
+ return <ICanvasRenderingContext2D>(<unknown>context);
86
+ }
87
+
88
+ (<any>context)[EXTENDED_SYMBOL] = true;
89
+
90
+ // drawImage()
91
+ const originalDrawImage = context.drawImage;
92
+ context.drawImage = (...args: any[]) => {
93
+ const shape = <ICanvasShape>args[0];
94
+ const drawImageBuffer = (buffer: Buffer): void => {
95
+ const canvasImage = new CanvasImage();
96
+ canvasImage.src = buffer;
97
+ canvasImage.width = shape.width;
98
+ canvasImage.height = shape.height;
99
+ args[0] = canvasImage;
100
+ (<any>originalDrawImage).apply(context, args);
101
+ };
102
+ if (shape instanceof HTMLImageElement) {
103
+ if (shape[PropertySymbol.buffer]) {
104
+ drawImageBuffer(shape[PropertySymbol.buffer]);
105
+ return;
106
+ }
107
+ if (!shape.complete) {
108
+ shape.addEventListener('load', () => {
109
+ if (shape[PropertySymbol.buffer]) {
110
+ drawImageBuffer(shape[PropertySymbol.buffer]);
111
+ }
112
+ });
113
+ }
114
+ return;
115
+ }
116
+ if (shape instanceof HTMLVideoElement) {
117
+ // Not supported yet
118
+ return;
119
+ }
120
+ if (
121
+ shape instanceof ImageBitmap ||
122
+ shape instanceof HTMLCanvasElement ||
123
+ shape instanceof OffscreenCanvas
124
+ ) {
125
+ const nodeCanvas = this.#getNodeCanvas(
126
+ (<ImageBitmap>shape)[PropertySymbol.canvas] || shape
127
+ );
128
+ if (nodeCanvas) {
129
+ args[0] = nodeCanvas;
130
+ (<any>originalDrawImage).apply(context, args);
131
+ }
132
+ return;
133
+ }
134
+ };
135
+
136
+ // createImageData()
137
+ const originalCreateImageData = context.createImageData;
138
+ context.createImageData = (...args: any[]) => {
139
+ const imageData = originalCreateImageData.apply(context, <any>args);
140
+ return new ImageData(imageData.data, imageData.width, imageData.height);
141
+ };
142
+
143
+ // putImageData()
144
+ const originalPutImageData = context.putImageData;
145
+ context.putImageData = (...args: any[]) => {
146
+ const imageData = args[0];
147
+ if (!(imageData instanceof ImageData)) {
148
+ throw new window.TypeError(
149
+ `Failed to execute 'putImageData' on 'CanvasRenderingContext2D': parameter 1 is not of type 'ImageData'`
150
+ );
151
+ }
152
+ const canvasImageData = (<any>originalCreateImageData).call(
153
+ context,
154
+ imageData.width,
155
+ imageData.height
156
+ );
157
+ canvasImageData.data.set(imageData.data);
158
+ args[0] = canvasImageData;
159
+ (<any>originalPutImageData).apply(context, args);
160
+ };
161
+
162
+ // getImageData()
163
+ const originalGetImageData = context.getImageData;
164
+ context.getImageData = (...args: any[]) => {
165
+ const imageData = originalGetImageData.apply(context, <any>args);
166
+ return new ImageData(imageData.data, imageData.width, imageData.height);
167
+ };
168
+
169
+ // createPattern()
170
+ const originalCreatePattern = context.createPattern;
171
+ context.createPattern = (
172
+ shape: any,
173
+ repetition: '' | 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat'
174
+ ): any => {
175
+ if (shape instanceof HTMLImageElement) {
176
+ if (!shape[PropertySymbol.buffer]) {
177
+ return null;
178
+ }
179
+
180
+ const canvasImage = new CanvasImage();
181
+ canvasImage.src = shape[PropertySymbol.buffer];
182
+ canvasImage.width = shape.width;
183
+ canvasImage.height = shape.height;
184
+ return originalCreatePattern.call(context, canvasImage, repetition);
185
+ }
186
+ if (shape instanceof HTMLVideoElement) {
187
+ // Not supported yet
188
+ return null;
189
+ }
190
+ if (
191
+ shape instanceof ImageBitmap ||
192
+ shape instanceof HTMLCanvasElement ||
193
+ shape instanceof OffscreenCanvas
194
+ ) {
195
+ const nodeCanvas = this.#getNodeCanvas(
196
+ (<ImageBitmap>shape)[PropertySymbol.canvas] || shape
197
+ );
198
+ if (nodeCanvas) {
199
+ return originalCreatePattern.call(context, nodeCanvas, repetition);
200
+ }
201
+ }
202
+ return null;
203
+ };
204
+
205
+ return <ICanvasRenderingContext2D>(<unknown>context);
206
+ }
207
+
208
+ /**
209
+ * Serialize the canvas content as a data URL.
210
+ *
211
+ * @param caller Information about the caller, including the canvas element and its associated window and browser frame.
212
+ * @param caller.canvas Canvas.
213
+ * @param type MIME type of the output image.
214
+ * @param quality Encoder quality for lossy formats, in the range 0–1.
215
+ * @returns A data URL string.
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * const url = adapter.toDataURL(canvas, 'image/png');
220
+ * ```
221
+ */
222
+ public toDataURL({ canvas }: ICanvasAdapterCaller, type?: string, quality?: unknown): string {
223
+ const nodeCanvas = this.#getNodeCanvas(canvas);
224
+ if (type === 'image/jpeg') {
225
+ return nodeCanvas.toDataURL('image/jpeg', <number>quality);
226
+ }
227
+ return nodeCanvas.toDataURL('image/png');
228
+ }
229
+
230
+ /**
231
+ * Creates a Blob from the canvas content and passes it to the callback.
232
+ *
233
+ * @param caller Information about the caller, including the canvas element and its associated window and browser frame.
234
+ * @param caller.canvas Canvas.
235
+ * @param caller.window Window.
236
+ * @param canvas The canvas element.
237
+ * @param callback Receives the resulting Blob, or null on failure.
238
+ * @param type MIME type of the output image.
239
+ * @param quality Encoder quality for lossy formats, in the range 0–1.
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * adapter.toBlob(canvas, (blob) => console.log(blob?.size));
244
+ * ```
245
+ */
246
+ public toBlob(
247
+ { canvas, window }: ICanvasAdapterCaller,
248
+ callback: (blob: Blob | null) => void,
249
+ type?: string,
250
+ quality?: unknown
251
+ ): void {
252
+ const nodeCanvas = this.#getNodeCanvas(canvas);
253
+ if (type === 'image/jpeg') {
254
+ nodeCanvas.toBuffer(
255
+ (error, buffer) => {
256
+ if (error !== null) {
257
+ callback(null);
258
+ return;
259
+ }
260
+ callback(new window.Blob([new Uint8Array(buffer)], { type: 'image/jpeg' }));
261
+ },
262
+ 'image/jpeg',
263
+ { quality: <number>quality }
264
+ );
265
+ return;
266
+ }
267
+ nodeCanvas.toBuffer((error, buffer) => {
268
+ if (error !== null) {
269
+ callback(null);
270
+ return;
271
+ }
272
+ callback(new window.Blob([new Uint8Array(buffer)], { type: type ?? 'image/png' }));
273
+ });
274
+ }
275
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { default as CanvasAdapter } from './CanvasAdapter.js';