wasm-html-to-image 1.0.14 → 1.0.15
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/cli-options.d.ts +15 -0
- package/dist/cli-options.js +76 -3
- package/dist/cli.js +12 -5
- package/dist/html-to-image-wasm.js +3 -3
- package/dist/html-to-image.wasm +0 -0
- package/dist/web-workers.js +1 -1
- package/package.json +1 -1
package/dist/cli-options.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface CliFlags {
|
|
|
23
23
|
* Absent means infinite (libwebp loop_count 0 = the historical default).
|
|
24
24
|
*/
|
|
25
25
|
animationLoop?: number;
|
|
26
|
+
/** `--font <path-or-glob>` (repeatable); local font files to register. */
|
|
27
|
+
font?: string[];
|
|
26
28
|
}
|
|
27
29
|
/** Default frame rate for `--animation` when `--animation-fps` is omitted. */
|
|
28
30
|
export declare const DEFAULT_ANIMATION_FPS = 15;
|
|
@@ -47,6 +49,19 @@ export interface AnimationResolution {
|
|
|
47
49
|
}
|
|
48
50
|
/** Map `--animation` / `--animation-fps` / `--animation-loop` to the `render()` animation option. */
|
|
49
51
|
export declare function resolveAnimation(flags: CliFlags): AnimationResolution;
|
|
52
|
+
/** Output format implied by the output file's extension, if recognised. */
|
|
53
|
+
export declare function inferFormat(output: string | undefined): OutputFormat | undefined;
|
|
54
|
+
/** Inline markup passed on the command line (as opposed to a path or URL). */
|
|
55
|
+
export declare function isInlineHtml(input: string): boolean;
|
|
56
|
+
/** Read all of standard input as UTF-8 text. */
|
|
57
|
+
export declare function readStdin(): Promise<string>;
|
|
58
|
+
/** Expand `*` / `?` in the last path segment; other patterns are literal paths. */
|
|
59
|
+
export declare function expandGlob(pattern: string): Promise<string[]>;
|
|
60
|
+
/** Load `--font` files; each is named after its file (without extension). */
|
|
61
|
+
export declare function loadFonts(patterns: string[]): Promise<{
|
|
62
|
+
name: string;
|
|
63
|
+
data: Uint8Array;
|
|
64
|
+
}[]>;
|
|
50
65
|
/**
|
|
51
66
|
* URL inputs are fetched, not read from disk. `data:` URLs stay values so
|
|
52
67
|
* image inputs keep flowing through the converter pipeline.
|
package/dist/cli-options.js
CHANGED
|
@@ -38,6 +38,73 @@ export function resolveAnimation(flags) {
|
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
+
const EXTENSION_FORMATS = {
|
|
42
|
+
png: "png",
|
|
43
|
+
jpg: "jpeg",
|
|
44
|
+
jpeg: "jpeg",
|
|
45
|
+
webp: "webp",
|
|
46
|
+
apng: "apng",
|
|
47
|
+
gif: "gif",
|
|
48
|
+
svg: "svg",
|
|
49
|
+
pdf: "pdf",
|
|
50
|
+
tif: "tiff",
|
|
51
|
+
tiff: "tiff",
|
|
52
|
+
avif: "avif",
|
|
53
|
+
jxl: "jxl",
|
|
54
|
+
};
|
|
55
|
+
/** Output format implied by the output file's extension, if recognised. */
|
|
56
|
+
export function inferFormat(output) {
|
|
57
|
+
if (!output)
|
|
58
|
+
return undefined;
|
|
59
|
+
const ext = path.extname(output).slice(1).toLowerCase();
|
|
60
|
+
return EXTENSION_FORMATS[ext];
|
|
61
|
+
}
|
|
62
|
+
/** Inline markup passed on the command line (as opposed to a path or URL). */
|
|
63
|
+
export function isInlineHtml(input) {
|
|
64
|
+
return input.trimStart().startsWith("<");
|
|
65
|
+
}
|
|
66
|
+
/** Read all of standard input as UTF-8 text. */
|
|
67
|
+
export async function readStdin() {
|
|
68
|
+
const chunks = [];
|
|
69
|
+
for await (const chunk of process.stdin)
|
|
70
|
+
chunks.push(Buffer.from(chunk));
|
|
71
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
72
|
+
}
|
|
73
|
+
/** Expand `*` / `?` in the last path segment; other patterns are literal paths. */
|
|
74
|
+
export async function expandGlob(pattern) {
|
|
75
|
+
if (!/[*?]/.test(pattern))
|
|
76
|
+
return [pattern];
|
|
77
|
+
const dir = path.dirname(pattern);
|
|
78
|
+
const re = new RegExp("^" +
|
|
79
|
+
path
|
|
80
|
+
.basename(pattern)
|
|
81
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
82
|
+
.replace(/\*/g, ".*")
|
|
83
|
+
.replace(/\?/g, ".") +
|
|
84
|
+
"$");
|
|
85
|
+
const names = await fs.readdir(dir);
|
|
86
|
+
return names
|
|
87
|
+
.filter((n) => re.test(n))
|
|
88
|
+
.sort()
|
|
89
|
+
.map((n) => path.join(dir, n));
|
|
90
|
+
}
|
|
91
|
+
/** Load `--font` files; each is named after its file (without extension). */
|
|
92
|
+
export async function loadFonts(patterns) {
|
|
93
|
+
const fonts = [];
|
|
94
|
+
for (const pattern of patterns) {
|
|
95
|
+
const files = await expandGlob(pattern);
|
|
96
|
+
if (files.length === 0) {
|
|
97
|
+
console.warn(`wasm-html-to-image: no font files match "${pattern}"`);
|
|
98
|
+
}
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
fonts.push({
|
|
101
|
+
name: path.basename(file, path.extname(file)),
|
|
102
|
+
data: new Uint8Array(await fs.readFile(file)),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return fonts;
|
|
107
|
+
}
|
|
41
108
|
/**
|
|
42
109
|
* URL inputs are fetched, not read from disk. `data:` URLs stay values so
|
|
43
110
|
* image inputs keep flowing through the converter pipeline.
|
|
@@ -72,9 +139,14 @@ export async function readInput(input) {
|
|
|
72
139
|
* (`resolveHtmlSource`), while a file input falls back to its own directory.
|
|
73
140
|
*/
|
|
74
141
|
export async function buildRenderOptions(input, flags) {
|
|
75
|
-
const source =
|
|
76
|
-
? {
|
|
77
|
-
:
|
|
142
|
+
const source = isInlineHtml(input)
|
|
143
|
+
? { value: input, baseUrl: process.cwd() }
|
|
144
|
+
: input === "-"
|
|
145
|
+
? { value: await readStdin(), baseUrl: process.cwd() }
|
|
146
|
+
: isUrlInput(input)
|
|
147
|
+
? { url: input }
|
|
148
|
+
: await readInput(input);
|
|
149
|
+
const fonts = flags.font?.length ? await loadFonts(flags.font) : [];
|
|
78
150
|
const { animation, warning } = resolveAnimation(flags);
|
|
79
151
|
if (warning)
|
|
80
152
|
console.warn(`wasm-html-to-image: ${warning}`);
|
|
@@ -89,5 +161,6 @@ export async function buildRenderOptions(input, flags) {
|
|
|
89
161
|
...(flags.scale !== undefined ? { scale: flags.scale } : {}),
|
|
90
162
|
...(flags.dpi !== undefined ? { dpi: flags.dpi } : {}),
|
|
91
163
|
...(animation ? { animation } : {}),
|
|
164
|
+
...(fonts.length ? { fonts } : {}),
|
|
92
165
|
};
|
|
93
166
|
}
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { program } from "commander";
|
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { render } from "./single.js";
|
|
6
|
-
import { buildRenderOptions, isUrlInput } from "./cli-options.js";
|
|
6
|
+
import { buildRenderOptions, inferFormat, isInlineHtml, isUrlInput, } from "./cli-options.js";
|
|
7
7
|
const OUTPUT_FORMATS = [
|
|
8
8
|
"svg",
|
|
9
9
|
"png",
|
|
@@ -26,11 +26,12 @@ program
|
|
|
26
26
|
.name("wasm-html-to-image")
|
|
27
27
|
.description("HTML to image converter on the single unified WASM module (svg/pdf direct, png/jpeg/webp/avif/jxl/raw/thumbhash via encode or html_to_image)")
|
|
28
28
|
.version("0.1.0")
|
|
29
|
-
.argument("<input>", "input HTML/image file path or
|
|
29
|
+
.argument("<input>", "input HTML/image file path, URL, inline HTML string (starts with <), or - for stdin")
|
|
30
30
|
.option("-o, --output <path>", "output file path")
|
|
31
31
|
.option("-w, --width <number>", "viewport width", (v) => parseInt(v, 10), 800)
|
|
32
32
|
.option("-h, --height <number>", "viewport height (omit for auto)", (v) => parseInt(v, 10))
|
|
33
|
-
.option("-f, --format <format>", `output format (${OUTPUT_FORMATS.join(", ")})
|
|
33
|
+
.option("-f, --format <format>", `output format (${OUTPUT_FORMATS.join(", ")}); default: from the --output extension, else png`)
|
|
34
|
+
.option("--font <path>", "local font file or glob such as './fonts/*.woff2' (repeatable)", (v, prev = []) => [...prev, v])
|
|
34
35
|
.option("-q, --quality <number>", "encode quality 0-100 (encode stage only)", (v) => parseFloat(v), 85)
|
|
35
36
|
.option("--base-url <url>", "base URL for relative resources (URL input: defaults to the post-redirect document URL; file input: defaults to the file's directory)")
|
|
36
37
|
.option("--color-space <srgb|cmyk>", "output color space of tiff and pdf (default srgb; cmyk rewrites colors for print, with no ICC profile)", (v) => v.toLowerCase())
|
|
@@ -41,7 +42,7 @@ program
|
|
|
41
42
|
.option("--animation-loop <n>", "how many times the animated WebP / APNG / GIF repeats (0 = infinite, the default; 1 = play once; requires --animation)", (v) => parseInt(v, 10))
|
|
42
43
|
.action(async (input, options) => {
|
|
43
44
|
try {
|
|
44
|
-
const format = String(options.format).toLowerCase();
|
|
45
|
+
const format = String(options.format ?? inferFormat(options.output) ?? "png").toLowerCase();
|
|
45
46
|
if (!isOutputFormat(format)) {
|
|
46
47
|
console.error(`Error: unsupported format "${options.format}". Choose from: ${OUTPUT_FORMATS.join(", ")}`);
|
|
47
48
|
process.exit(1);
|
|
@@ -53,10 +54,15 @@ program
|
|
|
53
54
|
process.exit(1);
|
|
54
55
|
}
|
|
55
56
|
const isUrl = isUrlInput(input);
|
|
57
|
+
const isVirtual = isInlineHtml(input) || input === "-";
|
|
58
|
+
if (isVirtual && !options.output) {
|
|
59
|
+
console.error("Error: --output is required for inline HTML or stdin input");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
56
62
|
let outputPath = options.output;
|
|
57
63
|
if (!outputPath) {
|
|
58
64
|
const ext = format;
|
|
59
|
-
outputPath = isUrl
|
|
65
|
+
outputPath = isUrl || isVirtual
|
|
60
66
|
? `output.${ext}`
|
|
61
67
|
: path.format({
|
|
62
68
|
dir: path.dirname(input),
|
|
@@ -77,6 +83,7 @@ program
|
|
|
77
83
|
animation: options.animation,
|
|
78
84
|
animationFps: options.animationFps,
|
|
79
85
|
animationLoop: options.animationLoop,
|
|
86
|
+
font: options.font,
|
|
80
87
|
};
|
|
81
88
|
const result = await render(await buildRenderOptions(input, flags));
|
|
82
89
|
await fs.writeFile(outputPath, typeof result.data === "string"
|