@meonode/canvas 2.0.2 → 2.0.4
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/dist/cjs/{src/canvas → canvas}/canvas.helper.d.ts +1 -20
- package/dist/cjs/canvas/canvas.helper.d.ts.map +1 -0
- package/dist/cjs/canvas/canvas.helper.js +0 -230
- package/dist/cjs/canvas/canvas.helper.js.map +1 -1
- package/dist/{esm/src → cjs}/canvas/canvas.type.d.ts +1 -12
- package/dist/cjs/canvas/canvas.type.d.ts.map +1 -0
- package/dist/cjs/{src/canvas → canvas}/chart.canvas.d.ts +1 -1
- package/dist/cjs/canvas/chart.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/chart.canvas.js +70 -144
- package/dist/cjs/canvas/chart.canvas.js.map +1 -1
- package/dist/cjs/canvas/grid.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/grid.canvas.js.map +1 -1
- package/dist/{esm/src → cjs}/canvas/image.canvas.d.ts +1 -1
- package/dist/cjs/canvas/image.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/image.canvas.js +2 -2
- package/dist/cjs/canvas/image.canvas.js.map +1 -1
- package/dist/{esm/src → cjs}/canvas/layout.canvas.d.ts +2 -2
- package/dist/cjs/canvas/layout.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/layout.canvas.js +6 -6
- package/dist/cjs/canvas/layout.canvas.js.map +1 -1
- package/dist/{esm/src → cjs}/canvas/root.canvas.d.ts +3 -2
- package/dist/cjs/canvas/root.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/root.canvas.js +23 -117
- package/dist/cjs/canvas/root.canvas.js.map +1 -1
- package/dist/cjs/{src/canvas → canvas}/text.canvas.d.ts +1 -1
- package/dist/cjs/canvas/text.canvas.d.ts.map +1 -0
- package/dist/cjs/canvas/text.canvas.js +2 -2
- package/dist/cjs/canvas/text.canvas.js.map +1 -1
- package/dist/cjs/constant/common.const.d.ts.map +1 -0
- package/dist/cjs/constant/common.const.js.map +1 -1
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/util/disk.cache.d.ts.map +1 -0
- package/dist/cjs/util/disk.cache.js.map +1 -1
- package/dist/cjs/worker/comlink.pool.d.ts +30 -0
- package/dist/cjs/worker/comlink.pool.d.ts.map +1 -0
- package/dist/cjs/worker/comlink.pool.js +164 -0
- package/dist/cjs/worker/comlink.pool.js.map +1 -0
- package/dist/cjs/worker/comlink.setup.d.ts +4 -0
- package/dist/cjs/worker/comlink.setup.d.ts.map +1 -0
- package/dist/cjs/worker/comlink.setup.js +53 -0
- package/dist/cjs/worker/comlink.setup.js.map +1 -0
- package/dist/cjs/worker/render.worker.d.ts.map +1 -0
- package/dist/cjs/worker/render.worker.js +58 -61
- package/dist/cjs/worker/render.worker.js.map +1 -1
- package/dist/cjs/worker/worker.types.d.ts +13 -0
- package/dist/cjs/worker/worker.types.d.ts.map +1 -0
- package/dist/esm/{src/canvas → canvas}/canvas.helper.d.ts +1 -20
- package/dist/esm/canvas/canvas.helper.d.ts.map +1 -0
- package/dist/esm/canvas/canvas.helper.js +1 -230
- package/dist/{cjs/src → esm}/canvas/canvas.type.d.ts +1 -12
- package/dist/esm/canvas/canvas.type.d.ts.map +1 -0
- package/dist/esm/{src/canvas → canvas}/chart.canvas.d.ts +1 -1
- package/dist/esm/canvas/chart.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/chart.canvas.js +71 -145
- package/dist/esm/canvas/grid.canvas.d.ts.map +1 -0
- package/dist/{cjs/src → esm}/canvas/image.canvas.d.ts +1 -1
- package/dist/esm/canvas/image.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/image.canvas.js +2 -2
- package/dist/{cjs/src → esm}/canvas/layout.canvas.d.ts +2 -2
- package/dist/esm/canvas/layout.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/layout.canvas.js +6 -6
- package/dist/{cjs/src → esm}/canvas/root.canvas.d.ts +3 -2
- package/dist/esm/canvas/root.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/root.canvas.js +23 -116
- package/dist/esm/{src/canvas → canvas}/text.canvas.d.ts +1 -1
- package/dist/esm/canvas/text.canvas.d.ts.map +1 -0
- package/dist/esm/canvas/text.canvas.js +2 -2
- package/dist/esm/constant/common.const.d.ts.map +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/util/disk.cache.d.ts.map +1 -0
- package/dist/esm/worker/comlink.pool.d.ts +30 -0
- package/dist/esm/worker/comlink.pool.d.ts.map +1 -0
- package/dist/esm/worker/comlink.pool.js +139 -0
- package/dist/esm/worker/comlink.setup.d.ts +4 -0
- package/dist/esm/worker/comlink.setup.d.ts.map +1 -0
- package/dist/esm/worker/comlink.setup.js +30 -0
- package/dist/esm/worker/render.worker.d.ts.map +1 -0
- package/dist/esm/worker/render.worker.js +38 -60
- package/dist/esm/worker/worker.types.d.ts +13 -0
- package/dist/esm/worker/worker.types.d.ts.map +1 -0
- package/package.json +2 -1
- package/dist/cjs/src/canvas/canvas.helper.d.ts.map +0 -1
- package/dist/cjs/src/canvas/canvas.type.d.ts.map +0 -1
- package/dist/cjs/src/canvas/chart.canvas.d.ts.map +0 -1
- package/dist/cjs/src/canvas/grid.canvas.d.ts.map +0 -1
- package/dist/cjs/src/canvas/image.canvas.d.ts.map +0 -1
- package/dist/cjs/src/canvas/layout.canvas.d.ts.map +0 -1
- package/dist/cjs/src/canvas/root.canvas.d.ts.map +0 -1
- package/dist/cjs/src/canvas/text.canvas.d.ts.map +0 -1
- package/dist/cjs/src/constant/common.const.d.ts.map +0 -1
- package/dist/cjs/src/index.d.ts.map +0 -1
- package/dist/cjs/src/util/disk.cache.d.ts.map +0 -1
- package/dist/cjs/src/worker/render.worker.d.ts.map +0 -1
- package/dist/cjs/src/worker/worker.types.d.ts +0 -76
- package/dist/cjs/src/worker/worker.types.d.ts.map +0 -1
- package/dist/esm/src/canvas/canvas.helper.d.ts.map +0 -1
- package/dist/esm/src/canvas/canvas.type.d.ts.map +0 -1
- package/dist/esm/src/canvas/chart.canvas.d.ts.map +0 -1
- package/dist/esm/src/canvas/grid.canvas.d.ts.map +0 -1
- package/dist/esm/src/canvas/image.canvas.d.ts.map +0 -1
- package/dist/esm/src/canvas/layout.canvas.d.ts.map +0 -1
- package/dist/esm/src/canvas/root.canvas.d.ts.map +0 -1
- package/dist/esm/src/canvas/text.canvas.d.ts.map +0 -1
- package/dist/esm/src/constant/common.const.d.ts.map +0 -1
- package/dist/esm/src/index.d.ts.map +0 -1
- package/dist/esm/src/util/disk.cache.d.ts.map +0 -1
- package/dist/esm/src/worker/render.worker.d.ts.map +0 -1
- package/dist/esm/src/worker/worker.types.d.ts +0 -76
- package/dist/esm/src/worker/worker.types.d.ts.map +0 -1
- /package/dist/cjs/{src/canvas → canvas}/grid.canvas.d.ts +0 -0
- /package/dist/cjs/{src/constant → constant}/common.const.d.ts +0 -0
- /package/dist/cjs/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/cjs/{src/util → util}/disk.cache.d.ts +0 -0
- /package/dist/cjs/{src/worker → worker}/render.worker.d.ts +0 -0
- /package/dist/esm/{src/canvas → canvas}/grid.canvas.d.ts +0 -0
- /package/dist/esm/{src/constant → constant}/common.const.d.ts +0 -0
- /package/dist/esm/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/esm/{src/util → util}/disk.cache.d.ts +0 -0
- /package/dist/esm/{src/worker → worker}/render.worker.d.ts +0 -0
|
@@ -8,14 +8,10 @@ var text_canvas = require('./text.canvas.js');
|
|
|
8
8
|
var chart_canvas = require('./chart.canvas.js');
|
|
9
9
|
var grid_canvas = require('./grid.canvas.js');
|
|
10
10
|
var common_const = require('../constant/common.const.js');
|
|
11
|
-
var canvas_helper = require('./canvas.helper.js');
|
|
12
11
|
var path = require('node:path');
|
|
13
12
|
var fs = require('node:fs');
|
|
14
13
|
var node_os = require('node:os');
|
|
15
|
-
var node_worker_threads = require('node:worker_threads');
|
|
16
|
-
var node_url = require('node:url');
|
|
17
14
|
|
|
18
|
-
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
19
15
|
function _interopNamespaceDefault(e) {
|
|
20
16
|
var n = Object.create(null);
|
|
21
17
|
if (e) {
|
|
@@ -43,14 +39,8 @@ const registeredFonts = new Map();
|
|
|
43
39
|
* This is a safety net — users should still call .release() explicitly.
|
|
44
40
|
*/
|
|
45
41
|
const canvasRegistry = new FinalizationRegistry(heldValue => {
|
|
46
|
-
// Best-effort cleanup — worker may already be terminated
|
|
47
42
|
try {
|
|
48
|
-
|
|
49
|
-
// For now, just try to send the message and let errors be caught
|
|
50
|
-
if (_workerPool) {
|
|
51
|
-
;
|
|
52
|
-
_workerPool.workers?.[heldValue.workerIdx]?.postMessage({ type: 'release', canvasId: heldValue.canvasId });
|
|
53
|
-
}
|
|
43
|
+
_workerPool?.releaseCanvas(heldValue.workerIdx, heldValue.canvasId);
|
|
54
44
|
}
|
|
55
45
|
catch {
|
|
56
46
|
// Worker already gone — nothing to clean up
|
|
@@ -90,7 +80,7 @@ function terminate() {
|
|
|
90
80
|
class WorkerCanvas {
|
|
91
81
|
width;
|
|
92
82
|
height;
|
|
93
|
-
_buffer;
|
|
83
|
+
_buffer;
|
|
94
84
|
_pool;
|
|
95
85
|
_workerIdx;
|
|
96
86
|
_canvasId;
|
|
@@ -101,12 +91,8 @@ class WorkerCanvas {
|
|
|
101
91
|
this._pool = opts.pool;
|
|
102
92
|
this._workerIdx = opts.workerIdx;
|
|
103
93
|
this._canvasId = opts.canvasId;
|
|
104
|
-
// Register for finalizer-based cleanup if user forgets to call .release()
|
|
105
94
|
canvasRegistry.register(this, { workerIdx: opts.workerIdx, canvasId: opts.canvasId }, this);
|
|
106
95
|
}
|
|
107
|
-
_call(method, ...args) {
|
|
108
|
-
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, method, args);
|
|
109
|
-
}
|
|
110
96
|
// --- Sync methods: return from pre-encoded PNG buffer ---
|
|
111
97
|
toBufferSync(_format, _options) {
|
|
112
98
|
return this._buffer;
|
|
@@ -114,128 +100,47 @@ class WorkerCanvas {
|
|
|
114
100
|
toURLSync(_format, _options) {
|
|
115
101
|
return `data:image/png;base64,${this._buffer.toString('base64')}`;
|
|
116
102
|
}
|
|
117
|
-
// --- Async methods: delegate to worker ---
|
|
103
|
+
// --- Async methods: delegate to worker via Comlink ---
|
|
118
104
|
toBuffer(format, options) {
|
|
119
|
-
return this.
|
|
105
|
+
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toBuffer', [format, options]);
|
|
120
106
|
}
|
|
121
107
|
toURL(format, options) {
|
|
122
|
-
return this.
|
|
108
|
+
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toURL', [format, options]);
|
|
123
109
|
}
|
|
124
110
|
toFile(filename, options) {
|
|
125
|
-
return this.
|
|
111
|
+
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toFile', [filename, options]);
|
|
126
112
|
}
|
|
127
|
-
/** Returns a Buffer (Sharp instance cannot be transferred across threads) */
|
|
128
113
|
toSharp(options) {
|
|
129
|
-
return this.
|
|
114
|
+
return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toSharp', [options]);
|
|
130
115
|
}
|
|
131
116
|
toSharpSync(_options) {
|
|
132
117
|
throw new Error('[canvas] toSharpSync() is not available in worker mode — use toSharp() instead');
|
|
133
118
|
}
|
|
134
119
|
// --- Async convenience getters ---
|
|
135
120
|
get png() {
|
|
136
|
-
return this.
|
|
121
|
+
return this.toBuffer('png');
|
|
137
122
|
}
|
|
138
123
|
get webp() {
|
|
139
|
-
return this.
|
|
124
|
+
return this.toBuffer('webp');
|
|
140
125
|
}
|
|
141
126
|
get jpg() {
|
|
142
|
-
return this.
|
|
127
|
+
return this.toBuffer('jpg');
|
|
143
128
|
}
|
|
144
129
|
get svg() {
|
|
145
|
-
return this.
|
|
130
|
+
return this.toBuffer('svg');
|
|
146
131
|
}
|
|
147
132
|
get pdf() {
|
|
148
|
-
return this.
|
|
133
|
+
return this.toBuffer('pdf');
|
|
149
134
|
}
|
|
150
135
|
get raw() {
|
|
151
|
-
return this.
|
|
136
|
+
return this.toBuffer('raw');
|
|
152
137
|
}
|
|
153
138
|
/** Release the Canvas from worker memory. Call when done with this object. */
|
|
154
139
|
release() {
|
|
155
140
|
this._pool.releaseCanvas(this._workerIdx, this._canvasId);
|
|
156
|
-
// Unregister from finalizer since we're explicitly cleaning up
|
|
157
141
|
canvasRegistry.unregister(this);
|
|
158
142
|
}
|
|
159
143
|
}
|
|
160
|
-
/** Worker thread pool — routes render and canvas-call messages */
|
|
161
|
-
class WorkerPool {
|
|
162
|
-
workers = [];
|
|
163
|
-
idle = [];
|
|
164
|
-
queue = [];
|
|
165
|
-
pending = new Map();
|
|
166
|
-
nextId = 0;
|
|
167
|
-
constructor(size) {
|
|
168
|
-
this.init(size);
|
|
169
|
-
}
|
|
170
|
-
init(size) {
|
|
171
|
-
const workerFile = path__namespace.join(path__namespace.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('canvas/root.canvas.js', document.baseURI).href)))), '../worker/render.worker.js');
|
|
172
|
-
for (let i = 0; i < size; i++) {
|
|
173
|
-
const workerIdx = i;
|
|
174
|
-
const worker = new node_worker_threads.Worker(workerFile);
|
|
175
|
-
worker.on('message', (msg) => {
|
|
176
|
-
const task = this.pending.get(msg.taskId);
|
|
177
|
-
if (!task)
|
|
178
|
-
return;
|
|
179
|
-
this.pending.delete(msg.taskId);
|
|
180
|
-
if ('error' in msg) {
|
|
181
|
-
task.reject(new Error(msg.error));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if ('canvasId' in msg) {
|
|
185
|
-
// Render complete — put worker back to idle
|
|
186
|
-
this.idle.push(worker);
|
|
187
|
-
this.drain();
|
|
188
|
-
const result = { buffer: msg.buffer, canvasId: msg.canvasId, workerIdx, width: msg.width, height: msg.height };
|
|
189
|
-
task.resolve(result);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
// Canvas method call complete
|
|
193
|
-
task.resolve(msg.result);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
this.workers.push(worker);
|
|
197
|
-
this.idle.push(worker);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
drain() {
|
|
201
|
-
while (this.queue.length > 0 && this.idle.length > 0) {
|
|
202
|
-
const task = this.queue.shift();
|
|
203
|
-
const worker = this.idle.pop();
|
|
204
|
-
const request = { type: 'render', taskId: task.id, props: task.props };
|
|
205
|
-
worker.postMessage(request);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
render(props) {
|
|
209
|
-
const sanitizedProps = canvas_helper.WorkerPreProcessor.process(props);
|
|
210
|
-
return new Promise((resolve, reject) => {
|
|
211
|
-
const id = this.nextId++;
|
|
212
|
-
this.pending.set(id, { resolve: resolve, reject });
|
|
213
|
-
if (this.idle.length > 0) {
|
|
214
|
-
const worker = this.idle.pop();
|
|
215
|
-
const request = { type: 'render', taskId: id, props: sanitizedProps };
|
|
216
|
-
worker.postMessage(request);
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
this.queue.push({ id, props: sanitizedProps });
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
callOnCanvas(workerIdx, canvasId, method, args) {
|
|
224
|
-
return new Promise((resolve, reject) => {
|
|
225
|
-
const id = this.nextId++;
|
|
226
|
-
this.pending.set(id, { resolve: resolve, reject });
|
|
227
|
-
const request = { type: 'call', taskId: id, canvasId, method, args };
|
|
228
|
-
this.workers[workerIdx].postMessage(request);
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
releaseCanvas(workerIdx, canvasId) {
|
|
232
|
-
const request = { type: 'release', canvasId };
|
|
233
|
-
this.workers[workerIdx]?.postMessage(request);
|
|
234
|
-
}
|
|
235
|
-
terminate() {
|
|
236
|
-
this.workers.forEach(w => w.terminate());
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
144
|
/**
|
|
240
145
|
* Converts a CanvasElement tree into actual BoxNode instances.
|
|
241
146
|
* Used both for non-worker rendering (inline tree building) and inside
|
|
@@ -339,12 +244,12 @@ class RootNode extends layout_canvas.ColumnNode {
|
|
|
339
244
|
}
|
|
340
245
|
return imageNodes;
|
|
341
246
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
247
|
+
async render(ctx, offsetX = 0, offsetY = 0) {
|
|
248
|
+
// If ctx is provided, delegate to parent render (used when called as a child node)
|
|
249
|
+
if (ctx) {
|
|
250
|
+
await super.render(ctx, offsetX, offsetY);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
348
253
|
const diskCacheKeys = this.props.useDiskCache ? new Set() : undefined;
|
|
349
254
|
try {
|
|
350
255
|
// Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources.
|
|
@@ -378,7 +283,7 @@ class RootNode extends layout_canvas.ColumnNode {
|
|
|
378
283
|
this.ctx = this.canvas.getContext('2d');
|
|
379
284
|
this.ctx.scale(this.scale, this.scale);
|
|
380
285
|
// Step 6: Render content
|
|
381
|
-
super.render(this.ctx, 0, 0);
|
|
286
|
+
await super.render(this.ctx, 0, 0);
|
|
382
287
|
if (!this.canvas) {
|
|
383
288
|
throw new Error('Canvas not initialized');
|
|
384
289
|
}
|
|
@@ -396,9 +301,10 @@ async function Root(props) {
|
|
|
396
301
|
const workerMode = props.workerMode ?? _defaultWorkerMode;
|
|
397
302
|
const workerPoolSize = props.workers ?? _defaultWorkerPoolSize;
|
|
398
303
|
if (workerMode) {
|
|
399
|
-
// Lazy initialize worker pool
|
|
304
|
+
// Lazy initialize worker pool — dynamic import to avoid loading Comlink in non-worker contexts
|
|
400
305
|
if (!_workerPool) {
|
|
401
|
-
|
|
306
|
+
const { ComlinkPool } = await Promise.resolve().then(function () { return require('../worker/comlink.pool.js'); });
|
|
307
|
+
_workerPool = new ComlinkPool(workerPoolSize);
|
|
402
308
|
}
|
|
403
309
|
const result = await _workerPool.render(props);
|
|
404
310
|
return new WorkerCanvas({ ...result, pool: _workerPool });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.canvas.js","sources":["../../../../../src/canvas/root.canvas.ts"],"sourcesContent":["import { Canvas, FontLibrary, type CanvasRenderingContext2D } from 'skia-canvas'\nimport type { ExportFormat, ExportOptions, SaveOptions, RenderOptions } from 'skia-canvas'\nimport { ColumnNode, BoxNode, RowNode } from '@/canvas/layout.canvas.js'\nimport type { BaseProps, RootProps, CanvasElement, RootPropsWithWorker, RootPropsWithoutWorker } from '@/canvas/canvas.type.js'\nimport type { CanvasCallMethod, CallArgs, CallResult, WorkerCallRequest, WorkerResponse, WorkerRequest } from '@/worker/worker.types.js'\nimport { ImageNode, type RenderImageCache } from '@/canvas/image.canvas.js'\nimport { deleteDiskCache } from '@/util/disk.cache.js'\nimport { TextNode } from '@/canvas/text.canvas.js'\nimport { ChartNode } from '@/canvas/chart.canvas.js'\nimport { GridNode, GridItemNode } from '@/canvas/grid.canvas.js'\nimport { Style } from '@/constant/common.const.js'\nimport { WorkerPreProcessor } from '@/canvas/canvas.helper.js'\nimport * as path from 'node:path'\nimport * as fs from 'node:fs'\nimport { cpus } from 'node:os'\nimport { Worker } from 'node:worker_threads'\nimport { fileURLToPath } from 'node:url'\n\n/** Registry to track fonts that have already been loaded */\nconst registeredFonts = new Map<string, Set<string>>()\n\n// Exported for testing purposes only\nexport const _clearRegisteredFonts = () => {\n registeredFonts.clear()\n}\n\n/**\n * FinalizationRegistry to clean up WorkerCanvas instances that were not explicitly released.\n * This is a safety net — users should still call .release() explicitly.\n */\nconst canvasRegistry = new FinalizationRegistry<{ workerIdx: number; canvasId: number }>(heldValue => {\n // Best-effort cleanup — worker may already be terminated\n try {\n // Access workers via a public method or make it accessible\n // For now, just try to send the message and let errors be caught\n if (_workerPool) {\n ;(_workerPool as any).workers?.[heldValue.workerIdx]?.postMessage({ type: 'release', canvasId: heldValue.canvasId })\n }\n } catch {\n // Worker already gone — nothing to clean up\n }\n})\n\n/** Engine configuration — legacy support for configure() */\nlet _defaultWorkerMode = true\nlet _defaultWorkerPoolSize = Math.max(1, cpus().length - 1)\nlet _workerPool: WorkerPool | null = null\n\nexport interface CanvasEngineConfig {\n /** Run rendering in worker threads to avoid blocking the event loop (default: true) */\n workerMode?: boolean\n /** Number of worker threads in the pool (default: os.cpus().length - 1) */\n workers?: number\n}\n\n/**\n * Configure the canvas rendering engine.\n * Call this once at application startup before rendering.\n * @deprecated Pass workerMode and workers directly to Root() props instead.\n */\nexport function configure(options: CanvasEngineConfig) {\n if (options.workerMode !== undefined) _defaultWorkerMode = options.workerMode\n if (options.workers !== undefined) _defaultWorkerPoolSize = options.workers\n}\n\n/**\n * Terminate all worker pools and free worker thread resources.\n * Call this when shutting down a long-running server to clean up immediately.\n * After calling, you must call configure() again before rendering.\n */\nexport function terminate() {\n if (_workerPool) {\n _workerPool.terminate()\n _workerPool = null\n }\n}\n\ninterface PendingTask {\n resolve: (value: unknown) => void\n reject: (err: Error) => void\n}\n\ninterface PoolRenderResult {\n buffer: Buffer\n canvasId: number\n workerIdx: number\n width: number\n height: number\n}\n\n/**\n * Proxies all skia-canvas Canvas APIs to a Canvas instance living inside a worker thread.\n * Sync methods (toBufferSync, toURLSync) return from a pre-encoded PNG buffer.\n * Async methods (toBuffer, toURL, toFile, getters) delegate to the worker.\n */\nclass WorkerCanvas {\n readonly width: number\n readonly height: number\n private readonly _buffer: Buffer // pre-encoded PNG for sync use\n private readonly _pool: WorkerPool\n private readonly _workerIdx: number\n private readonly _canvasId: number\n\n constructor(opts: PoolRenderResult & { pool: WorkerPool }) {\n this._buffer = opts.buffer\n this.width = opts.width\n this.height = opts.height\n this._pool = opts.pool\n this._workerIdx = opts.workerIdx\n this._canvasId = opts.canvasId\n // Register for finalizer-based cleanup if user forgets to call .release()\n canvasRegistry.register(this, { workerIdx: opts.workerIdx, canvasId: opts.canvasId }, this)\n }\n\n private _call<M extends CanvasCallMethod>(method: M, ...args: CallArgs<M>): Promise<CallResult<M>> {\n return this._pool.callOnCanvas(this._workerIdx, this._canvasId, method, args)\n }\n\n // --- Sync methods: return from pre-encoded PNG buffer ---\n\n toBufferSync(_format?: ExportFormat, _options?: ExportOptions): Buffer {\n return this._buffer\n }\n\n toURLSync(_format?: ExportFormat, _options?: ExportOptions): string {\n return `data:image/png;base64,${this._buffer.toString('base64')}`\n }\n\n // --- Async methods: delegate to worker ---\n\n toBuffer(format: ExportFormat, options?: ExportOptions): Promise<Buffer> {\n return this._call('toBuffer', format, options)\n }\n\n toURL(format: ExportFormat, options?: ExportOptions): Promise<string> {\n return this._call('toURL', format, options)\n }\n\n toFile(filename: string, options?: SaveOptions): Promise<void> {\n return this._call('toFile', filename, options)\n }\n\n /** Returns a Buffer (Sharp instance cannot be transferred across threads) */\n toSharp(options?: RenderOptions): Promise<Buffer> {\n return this._call('toSharp', options)\n }\n\n toSharpSync(_options?: RenderOptions): never {\n throw new Error('[canvas] toSharpSync() is not available in worker mode — use toSharp() instead')\n }\n\n // --- Async convenience getters ---\n\n get png(): Promise<Buffer> {\n return this._call('toBuffer', 'png')\n }\n get webp(): Promise<Buffer> {\n return this._call('toBuffer', 'webp')\n }\n get jpg(): Promise<Buffer> {\n return this._call('toBuffer', 'jpg')\n }\n get svg(): Promise<Buffer> {\n return this._call('toBuffer', 'svg')\n }\n get pdf(): Promise<Buffer> {\n return this._call('toBuffer', 'pdf')\n }\n get raw(): Promise<Buffer> {\n return this._call('toBuffer', 'raw')\n }\n\n /** Release the Canvas from worker memory. Call when done with this object. */\n release(): void {\n this._pool.releaseCanvas(this._workerIdx, this._canvasId)\n // Unregister from finalizer since we're explicitly cleaning up\n canvasRegistry.unregister(this)\n }\n}\n\n/** Worker thread pool — routes render and canvas-call messages */\nclass WorkerPool {\n private workers: Worker[] = []\n private idle: Worker[] = []\n private queue: Array<{ id: number; props: RootProps }> = []\n private pending = new Map<number, PendingTask>()\n private nextId = 0\n\n constructor(size: number) {\n this.init(size)\n }\n\n private init(size: number) {\n const workerFile = path.join(path.dirname(fileURLToPath(import.meta.url)), '../worker/render.worker.js')\n\n for (let i = 0; i < size; i++) {\n const workerIdx = i\n const worker = new Worker(workerFile)\n worker.on('message', (msg: WorkerResponse) => {\n const task = this.pending.get(msg.taskId)\n if (!task) return\n this.pending.delete(msg.taskId)\n\n if ('error' in msg) {\n task.reject(new Error(msg.error))\n return\n }\n\n if ('canvasId' in msg) {\n // Render complete — put worker back to idle\n this.idle.push(worker)\n this.drain()\n const result: PoolRenderResult = { buffer: msg.buffer, canvasId: msg.canvasId, workerIdx, width: msg.width, height: msg.height }\n task.resolve(result)\n } else {\n // Canvas method call complete\n task.resolve(msg.result)\n }\n })\n this.workers.push(worker)\n this.idle.push(worker)\n }\n }\n\n private drain() {\n while (this.queue.length > 0 && this.idle.length > 0) {\n const task = this.queue.shift()!\n const worker = this.idle.pop()!\n const request: WorkerRequest = { type: 'render', taskId: task.id, props: task.props }\n worker.postMessage(request)\n }\n }\n\n render(props: RootProps): Promise<PoolRenderResult> {\n const sanitizedProps = WorkerPreProcessor.process(props)\n return new Promise<PoolRenderResult>((resolve, reject) => {\n const id = this.nextId++\n this.pending.set(id, { resolve: resolve as (v: unknown) => void, reject })\n if (this.idle.length > 0) {\n const worker = this.idle.pop()!\n const request: WorkerRequest = { type: 'render', taskId: id, props: sanitizedProps }\n worker.postMessage(request)\n } else {\n this.queue.push({ id, props: sanitizedProps })\n }\n })\n }\n\n callOnCanvas<M extends CanvasCallMethod>(workerIdx: number, canvasId: number, method: M, args: CallArgs<M>): Promise<CallResult<M>> {\n return new Promise<CallResult<M>>((resolve, reject) => {\n const id = this.nextId++\n this.pending.set(id, { resolve: resolve as (v: unknown) => void, reject })\n const request = { type: 'call' as const, taskId: id, canvasId, method, args } as WorkerCallRequest\n this.workers[workerIdx].postMessage(request)\n })\n }\n\n releaseCanvas(workerIdx: number, canvasId: number): void {\n const request: WorkerRequest = { type: 'release', canvasId }\n this.workers[workerIdx]?.postMessage(request)\n }\n\n terminate() {\n this.workers.forEach(w => w.terminate())\n }\n}\n\n/**\n * Converts a CanvasElement tree into actual BoxNode instances.\n * Used both for non-worker rendering (inline tree building) and inside\n * the render worker (reconstructing the tree from serialized descriptors).\n */\nexport function buildTree(descriptor: CanvasElement): BoxNode {\n switch (descriptor.__type) {\n case 'Box':\n return new BoxNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) })\n case 'Column':\n return new ColumnNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) })\n case 'Row':\n return new RowNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) })\n case 'Grid':\n return new GridNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) as any })\n case 'GridItem':\n return new GridItemNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) as any })\n case 'Image':\n return new ImageNode(descriptor.props as any)\n case 'Text':\n return new TextNode(descriptor.text, descriptor.props)\n case 'Chart':\n return new ChartNode(descriptor.props as any)\n }\n}\n\n/**\n * Root node that manages the canvas rendering context and coordinates overall layout and drawing.\n * Inherits from ColumnNode to provide vertical layout capabilities.\n */\nexport class RootNode extends ColumnNode {\n declare props: RootProps & BaseProps\n /** The canvas instance used for rendering */\n private canvas: Canvas | undefined\n /** The 2D rendering context for the canvas */\n private ctx: CanvasRenderingContext2D | null = null\n /** Target width for the canvas in pixels */\n private readonly targetWidth: number\n /** Target height for the canvas in pixels */\n private readonly targetHeight: number | undefined\n /** Scale factor for rendering (e.g. 2 for 2x resolution) */\n private readonly scale: number\n\n /**\n * Creates a new root node for canvas rendering\n * @param props Configuration properties for the root node\n * @throws Error if width property is not provided\n */\n constructor(props: RootProps & BaseProps) {\n // Call the parent constructor with root name and props\n super({ name: 'Root', ...props })\n\n this.props = props\n\n // Validate the required width property\n if (!props.width) {\n throw new Error('Width and height are required for Root')\n }\n\n // Register provided fonts with caching\n if (props.fonts?.length) {\n for (const font of props.fonts) {\n const family = font.family\n const paths = font.paths.map(p => path.resolve(p))\n\n if (!registeredFonts.has(family)) {\n registeredFonts.set(family, new Set())\n }\n\n const cachedPaths = registeredFonts.get(family)!\n const newPaths = paths.filter(p => !cachedPaths.has(p) && fs.existsSync(p))\n\n if (newPaths.length > 0) {\n FontLibrary.use({ [family]: newPaths })\n newPaths.forEach(p => cachedPaths.add(p))\n }\n }\n }\n\n // Set up scale and width\n this.scale = props.scale || 1\n this.targetWidth = props.width\n this.targetHeight = props.height\n this.node.setWidth(this.targetWidth)\n\n // Convert any CanvasElement children to actual BoxNode instances\n if (this.props.children) {\n const childArray = Array.isArray(this.props.children) ? this.props.children : [this.props.children]\n this.props.children = childArray.map(child => {\n if (child && typeof child === 'object' && '__type' in child) {\n return buildTree(child as CanvasElement)\n }\n return child\n }) as any\n }\n\n // Initialize children nodes\n this.processInitialChildren()\n }\n\n /**\n * Traverses the node tree to find all ImageNode instances using breadth-first search\n * @returns Array of all ImageNode instances found in the tree\n */\n private findAllImageNodes(): ImageNode[] {\n const imageNodes: ImageNode[] = []\n const queue: BoxNode[] = [this]\n while (queue.length > 0) {\n const node = queue.shift()!\n if (node instanceof ImageNode) {\n imageNodes.push(node)\n }\n queue.push(...node.children)\n }\n return imageNodes\n }\n\n /**\n * Renders the entire node tree to a canvas, handling image loading, layout calculation,\n * and final drawing\n * @returns Promise resolving to the rendered Canvas instance\n */\n async render(): Promise<Canvas> {\n const diskCacheKeys = this.props.useDiskCache ? new Set<string>() : undefined\n\n try {\n // Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources.\n // A per-render cache deduplicates identical src+color combinations within this render pass.\n const imageNodes = this.findAllImageNodes()\n if (imageNodes.length > 0) {\n const imageCache: RenderImageCache = new Map()\n const CONCURRENCY = 5\n const queue = [...imageNodes]\n const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {\n while (queue.length > 0) {\n const node = queue.shift()!\n await node.load(imageCache, diskCacheKeys)\n }\n })\n await Promise.allSettled(workers)\n }\n\n // Step 2: Calculate initial layout\n this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR)\n\n // Step 3: Allow nodes to finalize their layout\n const needRecalculate = this.finalizeLayout()\n if (needRecalculate) {\n this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR)\n }\n\n // Step 4: Create a canvas with calculated dimensions\n const calculatedContentHeight = this.node.getComputedHeight()\n const finalCanvasWidth = Math.ceil(this.targetWidth * this.scale)\n const finalCanvasHeight = this.targetHeight ? Math.ceil(this.targetHeight * this.scale) : Math.max(1, Math.ceil(calculatedContentHeight * this.scale))\n\n // Step 5: Set up canvas context\n this.canvas = new Canvas(finalCanvasWidth, finalCanvasHeight)\n this.ctx = this.canvas.getContext('2d')\n this.ctx.scale(this.scale, this.scale)\n\n // Step 6: Render content\n super.render(this.ctx, 0, 0)\n\n if (!this.canvas) {\n throw new Error('Canvas not initialized')\n }\n\n return this.canvas\n } finally {\n if (diskCacheKeys?.size) {\n await Promise.allSettled([...diskCacheKeys].map(key => deleteDiskCache(key)))\n }\n }\n }\n}\n\n/**\n * Creates and renders a new root node with the given properties.\n * Rendering runs in worker threads by default for non-blocking operation.\n * @example\n * // Worker mode (default) - .release() available\n * const canvas = await Root({ width: 400, children: [...] })\n * canvas.release() // ✓ OK\n * @example\n * // Worker mode explicit - .release() available\n * const canvas = await Root({ width: 400, workerMode: true, workers: 2 })\n * canvas.release() // ✓ OK\n * @example\n * // Non-worker mode - .release() NOT available, workers not allowed\n * const canvas = await Root({ width: 400, workerMode: false })\n * canvas.release() // ✗ TypeScript error\n * @param props Configuration properties for the root node\n * @returns Canvas with .release() in worker mode, plain Canvas otherwise\n */\nexport function Root(props: RootPropsWithWorker): Promise<Canvas & { release(): void }>\nexport function Root(props: RootPropsWithoutWorker): Promise<Canvas>\nexport async function Root(props: RootProps): Promise<Canvas | (Canvas & { release(): void })> {\n // Determine worker mode: props override legacy configure()\n const workerMode = props.workerMode ?? _defaultWorkerMode\n const workerPoolSize = props.workers ?? _defaultWorkerPoolSize\n\n if (workerMode) {\n // Lazy initialize worker pool\n if (!_workerPool) {\n _workerPool = new WorkerPool(workerPoolSize)\n }\n const result = await _workerPool.render(props)\n return new WorkerCanvas({ ...result, pool: _workerPool }) as unknown as Canvas & { release(): void }\n }\n\n // Non-worker mode — render directly and return Canvas\n return new RootNode(props).render()\n}\n"],"names":["cpus","path","fileURLToPath","Worker","WorkerPreProcessor","BoxNode","ColumnNode","RowNode","GridNode","GridItemNode","ImageNode","TextNode","ChartNode","fs","FontLibrary","Style","Canvas","deleteDiskCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"root.canvas.js","sources":["../../../../src/canvas/root.canvas.ts"],"sourcesContent":["import { Canvas, FontLibrary, type CanvasRenderingContext2D } from 'skia-canvas'\nimport type { ExportFormat, ExportOptions, SaveOptions, RenderOptions } from 'skia-canvas'\nimport { ColumnNode, BoxNode, RowNode } from '@/canvas/layout.canvas.js'\nimport type { BaseProps, RootProps, CanvasElement, RootPropsWithWorker, RootPropsWithoutWorker } from '@/canvas/canvas.type.js'\nimport type { ComlinkPool as ComlinkPoolType, PoolRenderResult } from '@/worker/comlink.pool.js'\nimport { ImageNode, type RenderImageCache } from '@/canvas/image.canvas.js'\nimport { deleteDiskCache } from '@/util/disk.cache.js'\nimport { TextNode } from '@/canvas/text.canvas.js'\nimport { ChartNode } from '@/canvas/chart.canvas.js'\nimport { GridNode, GridItemNode } from '@/canvas/grid.canvas.js'\nimport { Style } from '@/constant/common.const.js'\nimport * as path from 'node:path'\nimport * as fs from 'node:fs'\nimport { cpus } from 'node:os'\n\n/** Registry to track fonts that have already been loaded */\nconst registeredFonts = new Map<string, Set<string>>()\n\n// Exported for testing purposes only\nexport const _clearRegisteredFonts = () => {\n registeredFonts.clear()\n}\n\n/**\n * FinalizationRegistry to clean up WorkerCanvas instances that were not explicitly released.\n * This is a safety net — users should still call .release() explicitly.\n */\nconst canvasRegistry = new FinalizationRegistry<{ workerIdx: number; canvasId: number }>(heldValue => {\n try {\n _workerPool?.releaseCanvas(heldValue.workerIdx, heldValue.canvasId)\n } catch {\n // Worker already gone — nothing to clean up\n }\n})\n\n/** Engine configuration — legacy support for configure() */\nlet _defaultWorkerMode = true\nlet _defaultWorkerPoolSize = Math.max(1, cpus().length - 1)\nlet _workerPool: ComlinkPoolType | null = null\n\nexport interface CanvasEngineConfig {\n /** Run rendering in worker threads to avoid blocking the event loop (default: true) */\n workerMode?: boolean\n /** Number of worker threads in the pool (default: os.cpus().length - 1) */\n workers?: number\n}\n\n/**\n * Configure the canvas rendering engine.\n * Call this once at application startup before rendering.\n * @deprecated Pass workerMode and workers directly to Root() props instead.\n */\nexport function configure(options: CanvasEngineConfig) {\n if (options.workerMode !== undefined) _defaultWorkerMode = options.workerMode\n if (options.workers !== undefined) _defaultWorkerPoolSize = options.workers\n}\n\n/**\n * Terminate all worker pools and free worker thread resources.\n * Call this when shutting down a long-running server to clean up immediately.\n * After calling, you must call configure() again before rendering.\n */\nexport function terminate() {\n if (_workerPool) {\n _workerPool.terminate()\n _workerPool = null\n }\n}\n\n/**\n * Proxies all skia-canvas Canvas APIs to a Canvas instance living inside a worker thread.\n * Sync methods (toBufferSync, toURLSync) return from a pre-encoded PNG buffer.\n * Async methods (toBuffer, toURL, toFile, getters) delegate to the worker.\n */\nclass WorkerCanvas {\n readonly width: number\n readonly height: number\n private readonly _buffer: Buffer\n private readonly _pool: ComlinkPoolType\n private readonly _workerIdx: number\n private readonly _canvasId: number\n\n constructor(opts: PoolRenderResult & { pool: ComlinkPoolType }) {\n this._buffer = opts.buffer\n this.width = opts.width\n this.height = opts.height\n this._pool = opts.pool\n this._workerIdx = opts.workerIdx\n this._canvasId = opts.canvasId\n canvasRegistry.register(this, { workerIdx: opts.workerIdx, canvasId: opts.canvasId }, this)\n }\n\n // --- Sync methods: return from pre-encoded PNG buffer ---\n\n toBufferSync(_format?: ExportFormat, _options?: ExportOptions): Buffer {\n return this._buffer\n }\n\n toURLSync(_format?: ExportFormat, _options?: ExportOptions): string {\n return `data:image/png;base64,${this._buffer.toString('base64')}`\n }\n\n // --- Async methods: delegate to worker via Comlink ---\n\n toBuffer(format: ExportFormat, options?: ExportOptions): Promise<Buffer> {\n return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toBuffer', [format, options]) as Promise<Buffer>\n }\n\n toURL(format: ExportFormat, options?: ExportOptions): Promise<string> {\n return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toURL', [format, options]) as Promise<string>\n }\n\n toFile(filename: string, options?: SaveOptions): Promise<void> {\n return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toFile', [filename, options]) as Promise<void>\n }\n\n toSharp(options?: RenderOptions): Promise<Buffer> {\n return this._pool.callOnCanvas(this._workerIdx, this._canvasId, 'toSharp', [options]) as Promise<Buffer>\n }\n\n toSharpSync(_options?: RenderOptions): never {\n throw new Error('[canvas] toSharpSync() is not available in worker mode — use toSharp() instead')\n }\n\n // --- Async convenience getters ---\n\n get png(): Promise<Buffer> {\n return this.toBuffer('png')\n }\n get webp(): Promise<Buffer> {\n return this.toBuffer('webp')\n }\n get jpg(): Promise<Buffer> {\n return this.toBuffer('jpg')\n }\n get svg(): Promise<Buffer> {\n return this.toBuffer('svg')\n }\n get pdf(): Promise<Buffer> {\n return this.toBuffer('pdf')\n }\n get raw(): Promise<Buffer> {\n return this.toBuffer('raw')\n }\n\n /** Release the Canvas from worker memory. Call when done with this object. */\n release(): void {\n this._pool.releaseCanvas(this._workerIdx, this._canvasId)\n canvasRegistry.unregister(this)\n }\n}\n\n/**\n * Converts a CanvasElement tree into actual BoxNode instances.\n * Used both for non-worker rendering (inline tree building) and inside\n * the render worker (reconstructing the tree from serialized descriptors).\n */\nexport function buildTree(descriptor: CanvasElement): BoxNode {\n switch (descriptor.__type) {\n case 'Box':\n return new BoxNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) })\n case 'Column':\n return new ColumnNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) })\n case 'Row':\n return new RowNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) })\n case 'Grid':\n return new GridNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) as any })\n case 'GridItem':\n return new GridItemNode({ ...descriptor.props, children: descriptor.children?.map(buildTree) as any })\n case 'Image':\n return new ImageNode(descriptor.props as any)\n case 'Text':\n return new TextNode(descriptor.text, descriptor.props)\n case 'Chart':\n return new ChartNode(descriptor.props as any)\n }\n}\n\n/**\n * Root node that manages the canvas rendering context and coordinates overall layout and drawing.\n * Inherits from ColumnNode to provide vertical layout capabilities.\n */\nexport class RootNode extends ColumnNode {\n declare props: RootProps & BaseProps\n /** The canvas instance used for rendering */\n private canvas: Canvas | undefined\n /** The 2D rendering context for the canvas */\n private ctx: CanvasRenderingContext2D | null = null\n /** Target width for the canvas in pixels */\n private readonly targetWidth: number\n /** Target height for the canvas in pixels */\n private readonly targetHeight: number | undefined\n /** Scale factor for rendering (e.g. 2 for 2x resolution) */\n private readonly scale: number\n\n /**\n * Creates a new root node for canvas rendering\n * @param props Configuration properties for the root node\n * @throws Error if width property is not provided\n */\n constructor(props: RootProps & BaseProps) {\n // Call the parent constructor with root name and props\n super({ name: 'Root', ...props })\n\n this.props = props\n\n // Validate the required width property\n if (!props.width) {\n throw new Error('Width and height are required for Root')\n }\n\n // Register provided fonts with caching\n if (props.fonts?.length) {\n for (const font of props.fonts) {\n const family = font.family\n const paths = font.paths.map(p => path.resolve(p))\n\n if (!registeredFonts.has(family)) {\n registeredFonts.set(family, new Set())\n }\n\n const cachedPaths = registeredFonts.get(family)!\n const newPaths = paths.filter(p => !cachedPaths.has(p) && fs.existsSync(p))\n\n if (newPaths.length > 0) {\n FontLibrary.use({ [family]: newPaths })\n newPaths.forEach(p => cachedPaths.add(p))\n }\n }\n }\n\n // Set up scale and width\n this.scale = props.scale || 1\n this.targetWidth = props.width\n this.targetHeight = props.height\n this.node.setWidth(this.targetWidth)\n\n // Convert any CanvasElement children to actual BoxNode instances\n if (this.props.children) {\n const childArray = Array.isArray(this.props.children) ? this.props.children : [this.props.children]\n this.props.children = childArray.map(child => {\n if (child && typeof child === 'object' && '__type' in child) {\n return buildTree(child as CanvasElement)\n }\n return child\n }) as any\n }\n\n // Initialize children nodes\n this.processInitialChildren()\n }\n\n /**\n * Traverses the node tree to find all ImageNode instances using breadth-first search\n * @returns Array of all ImageNode instances found in the tree\n */\n private findAllImageNodes(): ImageNode[] {\n const imageNodes: ImageNode[] = []\n const queue: BoxNode[] = [this]\n while (queue.length > 0) {\n const node = queue.shift()!\n if (node instanceof ImageNode) {\n imageNodes.push(node)\n }\n queue.push(...node.children)\n }\n return imageNodes\n }\n\n /**\n * Renders the entire node tree to a canvas, handling image loading, layout calculation,\n * and final drawing\n * @returns Promise resolving to the rendered Canvas instance\n */\n override async render(ctx: CanvasRenderingContext2D, offsetX?: number, offsetY?: number): Promise<void>\n async render(ctx?: CanvasRenderingContext2D, offsetX?: number, offsetY?: number): Promise<Canvas>\n async render(ctx?: CanvasRenderingContext2D, offsetX = 0, offsetY = 0): Promise<Canvas | void> {\n // If ctx is provided, delegate to parent render (used when called as a child node)\n if (ctx) {\n await super.render(ctx, offsetX, offsetY)\n return\n }\n\n const diskCacheKeys = this.props.useDiskCache ? new Set<string>() : undefined\n\n try {\n // Step 1: Load all images with a concurrency limit to avoid overwhelming remote sources.\n // A per-render cache deduplicates identical src+color combinations within this render pass.\n const imageNodes = this.findAllImageNodes()\n if (imageNodes.length > 0) {\n const imageCache: RenderImageCache = new Map()\n const CONCURRENCY = 5\n const queue = [...imageNodes]\n const workers = Array.from({ length: Math.min(CONCURRENCY, queue.length) }, async () => {\n while (queue.length > 0) {\n const node = queue.shift()!\n await node.load(imageCache, diskCacheKeys)\n }\n })\n await Promise.allSettled(workers)\n }\n\n // Step 2: Calculate initial layout\n this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR)\n\n // Step 3: Allow nodes to finalize their layout\n const needRecalculate = this.finalizeLayout()\n if (needRecalculate) {\n this.node.calculateLayout(this.targetWidth, undefined, Style.Direction.LTR)\n }\n\n // Step 4: Create a canvas with calculated dimensions\n const calculatedContentHeight = this.node.getComputedHeight()\n const finalCanvasWidth = Math.ceil(this.targetWidth * this.scale)\n const finalCanvasHeight = this.targetHeight ? Math.ceil(this.targetHeight * this.scale) : Math.max(1, Math.ceil(calculatedContentHeight * this.scale))\n\n // Step 5: Set up canvas context\n this.canvas = new Canvas(finalCanvasWidth, finalCanvasHeight)\n this.ctx = this.canvas.getContext('2d')\n this.ctx.scale(this.scale, this.scale)\n\n // Step 6: Render content\n await super.render(this.ctx, 0, 0)\n\n if (!this.canvas) {\n throw new Error('Canvas not initialized')\n }\n\n return this.canvas\n } finally {\n if (diskCacheKeys?.size) {\n await Promise.allSettled([...diskCacheKeys].map(key => deleteDiskCache(key)))\n }\n }\n }\n}\n\n/**\n * Creates and renders a new root node with the given properties.\n * Rendering runs in worker threads by default for non-blocking operation.\n * @example\n * // Worker mode (default) - .release() available\n * const canvas = await Root({ width: 400, children: [...] })\n * canvas.release() // ✓ OK\n * @example\n * // Worker mode explicit - .release() available\n * const canvas = await Root({ width: 400, workerMode: true, workers: 2 })\n * canvas.release() // ✓ OK\n * @example\n * // Non-worker mode - .release() NOT available, workers not allowed\n * const canvas = await Root({ width: 400, workerMode: false })\n * canvas.release() // ✗ TypeScript error\n * @param props Configuration properties for the root node\n * @returns Canvas with .release() in worker mode, plain Canvas otherwise\n */\nexport function Root(props: RootPropsWithWorker): Promise<Canvas & { release(): void }>\nexport function Root(props: RootPropsWithoutWorker): Promise<Canvas>\nexport async function Root(props: RootProps): Promise<Canvas | (Canvas & { release(): void })> {\n // Determine worker mode: props override legacy configure()\n const workerMode = props.workerMode ?? _defaultWorkerMode\n const workerPoolSize = props.workers ?? _defaultWorkerPoolSize\n\n if (workerMode) {\n // Lazy initialize worker pool — dynamic import to avoid loading Comlink in non-worker contexts\n if (!_workerPool) {\n const { ComlinkPool } = await import('@/worker/comlink.pool.js')\n _workerPool = new ComlinkPool(workerPoolSize)\n }\n const result = await _workerPool.render(props)\n return new WorkerCanvas({ ...result, pool: _workerPool }) as unknown as Canvas & { release(): void }\n }\n\n // Non-worker mode — render directly and return Canvas\n return new RootNode(props).render()\n}\n"],"names":["cpus","BoxNode","ColumnNode","RowNode","GridNode","GridItemNode","ImageNode","TextNode","ChartNode","path","fs","FontLibrary","Style","Canvas","deleteDiskCache"],"mappings":"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"}
|
|
@@ -156,7 +156,7 @@ export declare class TextNode extends BoxNode {
|
|
|
156
156
|
* @param width Content box total width including padding
|
|
157
157
|
* @param height Content box total height including padding
|
|
158
158
|
*/
|
|
159
|
-
protected _renderContent(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): void
|
|
159
|
+
protected _renderContent(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number): Promise<void>;
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
162
|
* Creates a new TextNode instance with rich text support
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.canvas.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACpF,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAGnD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;IAuB7D;;;;;;;;OAQG;WACW,gBAAgB,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAClC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,wBAAwB,CAAC,WAAW,CAAC,CAAA;QACjD,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;KACnD;cAwBW,aAAa,IAAI,IAAI;IAoDxC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;IA+NnB;;;;;;;;;OASG;IACH,OAAO,CAAC,YAAY;IA6KpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;;;;;;;;OAgBG;cACsB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAkX3H;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,KAAG,aAI9D,CAAA"}
|
|
@@ -818,8 +818,8 @@ class TextNode extends layout_canvas.BoxNode {
|
|
|
818
818
|
* @param width Content box total width including padding
|
|
819
819
|
* @param height Content box total height including padding
|
|
820
820
|
*/
|
|
821
|
-
_renderContent(ctx, x, y, width, height) {
|
|
822
|
-
super._renderContent(ctx, x, y, width, height);
|
|
821
|
+
async _renderContent(ctx, x, y, width, height) {
|
|
822
|
+
await super._renderContent(ctx, x, y, width, height);
|
|
823
823
|
ctx.save();
|
|
824
824
|
ctx.textBaseline = 'alphabetic';
|
|
825
825
|
ctx.letterSpacing = this.formatSpacing(this.props.letterSpacing);
|