satoru-render 1.0.2 → 1.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/core.d.ts +13 -0
- package/dist/core.js +39 -3
- package/dist/node.js +12 -4
- package/dist/satoru-single.js +0 -0
- package/dist/satoru.js +2 -4727
- package/dist/satoru.wasm +0 -0
- package/dist/web-workers.js +442 -551
- package/dist/workers-parent.js +32 -3
- package/package.json +1 -1
package/dist/core.d.ts
CHANGED
|
@@ -57,6 +57,13 @@ export interface RenderOptions {
|
|
|
57
57
|
outputHeight?: number;
|
|
58
58
|
/** Resizing strategy to fit the canvas into the output size (default: "contain") */
|
|
59
59
|
fit?: "contain" | "cover" | "fill";
|
|
60
|
+
/** Alignment origin when fitted (default: {x: 0.5, y: 0.5}) */
|
|
61
|
+
fitPosition?: {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
};
|
|
65
|
+
/** Background color of the output canvas. (e.g., "#ffffff", "rgba(0,0,0,0.5)") */
|
|
66
|
+
backgroundColor?: string;
|
|
60
67
|
/** Output format */
|
|
61
68
|
format?: "svg" | "png" | "webp" | "pdf";
|
|
62
69
|
textToPaths?: boolean;
|
|
@@ -104,11 +111,17 @@ export declare abstract class SatoruBase {
|
|
|
104
111
|
outputWidth?: number;
|
|
105
112
|
outputHeight?: number;
|
|
106
113
|
fit?: "contain" | "cover" | "fill";
|
|
114
|
+
fitPosition?: {
|
|
115
|
+
x: number;
|
|
116
|
+
y: number;
|
|
117
|
+
};
|
|
118
|
+
backgroundColor?: string;
|
|
107
119
|
format?: "svg" | "png" | "webp" | "pdf";
|
|
108
120
|
textToPaths?: boolean;
|
|
109
121
|
}): Promise<string | Uint8Array>;
|
|
110
122
|
destroyInstance(inst: any): Promise<void>;
|
|
111
123
|
loadFallbackFont(data: Uint8Array): Promise<void>;
|
|
124
|
+
protected parseColor(color?: string): number;
|
|
112
125
|
protected abstract resolveDefaultResource(resource: RequiredResource, baseUrl?: string, userAgent?: string): Promise<Uint8Array | ResolvedFontResult | null>;
|
|
113
126
|
protected abstract fetchHtml(url: string, userAgent?: string): Promise<string>;
|
|
114
127
|
render(options: RenderOptions & {
|
package/dist/core.js
CHANGED
|
@@ -237,6 +237,9 @@ export class SatoruBase {
|
|
|
237
237
|
cropY: options.crop?.y ?? 0,
|
|
238
238
|
cropWidth: options.crop?.width ?? 0,
|
|
239
239
|
cropHeight: options.crop?.height ?? 0,
|
|
240
|
+
fitPositionX: options.fitPosition?.x ?? 0.5,
|
|
241
|
+
fitPositionY: options.fitPosition?.y ?? 0.5,
|
|
242
|
+
backgroundColor: this.parseColor(options.backgroundColor),
|
|
240
243
|
});
|
|
241
244
|
if (!result) {
|
|
242
245
|
if (options.format === "svg")
|
|
@@ -262,6 +265,36 @@ export class SatoruBase {
|
|
|
262
265
|
mod.destroy_instance(inst);
|
|
263
266
|
}
|
|
264
267
|
}
|
|
268
|
+
parseColor(color) {
|
|
269
|
+
if (!color)
|
|
270
|
+
return 0x00000000;
|
|
271
|
+
if (color.startsWith("#")) {
|
|
272
|
+
let hex = color.slice(1);
|
|
273
|
+
if (hex.length === 3) {
|
|
274
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
275
|
+
}
|
|
276
|
+
if (hex.length === 6) {
|
|
277
|
+
return (0xff000000 | parseInt(hex, 16)) >>> 0;
|
|
278
|
+
}
|
|
279
|
+
if (hex.length === 8) {
|
|
280
|
+
// RRGGBBAA -> AARRGGBB
|
|
281
|
+
const r = hex.slice(0, 2);
|
|
282
|
+
const g = hex.slice(2, 4);
|
|
283
|
+
const b = hex.slice(4, 6);
|
|
284
|
+
const a = hex.slice(6, 8);
|
|
285
|
+
return parseInt(a + r + g + b, 16) >>> 0;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const m = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
289
|
+
if (m) {
|
|
290
|
+
const r = parseInt(m[1]);
|
|
291
|
+
const g = parseInt(m[2]);
|
|
292
|
+
const b = parseInt(m[3]);
|
|
293
|
+
const a = m[4] ? Math.round(parseFloat(m[4]) * 255) : 255;
|
|
294
|
+
return ((a << 24) | (r << 16) | (g << 8) | b) >>> 0;
|
|
295
|
+
}
|
|
296
|
+
return 0x00000000;
|
|
297
|
+
}
|
|
265
298
|
async render(options) {
|
|
266
299
|
let { format = "svg", value, url, baseUrl } = options;
|
|
267
300
|
if (format === "pdf" && Array.isArray(value) && value.length > 1) {
|
|
@@ -374,7 +407,7 @@ export class SatoruBase {
|
|
|
374
407
|
catch (e) {
|
|
375
408
|
// fall through
|
|
376
409
|
}
|
|
377
|
-
let typeInt = 1;
|
|
410
|
+
let typeInt = 1; // Font
|
|
378
411
|
if (r.type === "image")
|
|
379
412
|
typeInt = 2;
|
|
380
413
|
if (r.type === "css")
|
|
@@ -382,7 +415,7 @@ export class SatoruBase {
|
|
|
382
415
|
mod.add_resource(instancePtr, r.url, typeInt, uint8);
|
|
383
416
|
})();
|
|
384
417
|
}
|
|
385
|
-
let typeInt = 1;
|
|
418
|
+
let typeInt = 1; // Font
|
|
386
419
|
if (r.type === "image")
|
|
387
420
|
typeInt = 2;
|
|
388
421
|
if (r.type === "css")
|
|
@@ -422,7 +455,7 @@ export class SatoruBase {
|
|
|
422
455
|
"fonts" in data) {
|
|
423
456
|
const fontResult = data;
|
|
424
457
|
// Load the CSS first so C++ can parse @font-face
|
|
425
|
-
mod.add_resource(instancePtr, r.url,
|
|
458
|
+
mod.add_resource(instancePtr, r.url, 3, // Css type
|
|
426
459
|
fontResult.css);
|
|
427
460
|
// Then load all prefetched font binaries directly
|
|
428
461
|
for (const font of fontResult.fonts) {
|
|
@@ -476,6 +509,9 @@ export class SatoruBase {
|
|
|
476
509
|
cropY: options.crop?.y ?? 0,
|
|
477
510
|
cropWidth: options.crop?.width ?? 0,
|
|
478
511
|
cropHeight: options.crop?.height ?? 0,
|
|
512
|
+
fitPositionX: options.fitPosition?.x ?? 0.5,
|
|
513
|
+
fitPositionY: options.fitPosition?.y ?? 0.5,
|
|
514
|
+
backgroundColor: this.parseColor(options.backgroundColor),
|
|
479
515
|
});
|
|
480
516
|
if (!result) {
|
|
481
517
|
if (format === "svg")
|
package/dist/node.js
CHANGED
|
@@ -12,7 +12,7 @@ export class Satoru extends SatoruBase {
|
|
|
12
12
|
if (resource.url.startsWith("provider:google-fonts")) {
|
|
13
13
|
return resolveGoogleFonts(resource, userAgent);
|
|
14
14
|
}
|
|
15
|
-
const isAbsolute = /^[a-z][a-z0-9+.-]
|
|
15
|
+
const isAbsolute = /^[a-z][a-z0-9+.-]*:\/\//i.test(resource.url) || resource.url.startsWith("data:");
|
|
16
16
|
let baseDir = baseUrl
|
|
17
17
|
? baseUrl.startsWith("file://")
|
|
18
18
|
? baseUrl.slice(7)
|
|
@@ -24,7 +24,7 @@ export class Satoru extends SatoruBase {
|
|
|
24
24
|
if (!isAbsolute &&
|
|
25
25
|
!/^[a-z][a-z0-9+.-]*:\/\//i.test(baseDir) &&
|
|
26
26
|
!baseDir.startsWith("data:")) {
|
|
27
|
-
const filePath = path.join(baseDir, resource.url);
|
|
27
|
+
const filePath = path.isAbsolute(resource.url) ? resource.url : path.join(baseDir, resource.url);
|
|
28
28
|
if (fs.existsSync(filePath)) {
|
|
29
29
|
return new Uint8Array(fs.readFileSync(filePath));
|
|
30
30
|
}
|
|
@@ -33,8 +33,16 @@ export class Satoru extends SatoruBase {
|
|
|
33
33
|
if (isAbsolute) {
|
|
34
34
|
finalUrl = resource.url;
|
|
35
35
|
}
|
|
36
|
-
else if (baseUrl
|
|
37
|
-
|
|
36
|
+
else if (baseUrl) {
|
|
37
|
+
try {
|
|
38
|
+
const base = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseUrl)
|
|
39
|
+
? baseUrl
|
|
40
|
+
: new URL(`file:///${baseUrl.replace(/\\/g, "/")}`).href;
|
|
41
|
+
finalUrl = new URL(resource.url, base).href;
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
// ignore
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
47
|
if (!finalUrl)
|
|
40
48
|
return null;
|
package/dist/satoru-single.js
CHANGED
|
Binary file
|