h17-sspdf 0.1.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/cli.js ADDED
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { renderDocument } = require("./core/render-document");
6
+
7
+ const EXAMPLES_THEMES_DIR = path.join(__dirname, "examples", "themes");
8
+
9
+ function parseArgs(argv) {
10
+ const out = { source: null, theme: null, output: null, help: false };
11
+
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const arg = argv[i];
14
+ if (arg === "--source" || arg === "-s") {
15
+ out.source = argv[++i];
16
+ } else if (arg === "--theme" || arg === "-t") {
17
+ out.theme = argv[++i];
18
+ } else if (arg === "--output" || arg === "-o") {
19
+ out.output = argv[++i];
20
+ } else if (arg === "--help" || arg === "-h") {
21
+ out.help = true;
22
+ } else {
23
+ throw new Error(`Unknown argument: ${arg}`);
24
+ }
25
+ }
26
+
27
+ return out;
28
+ }
29
+
30
+ function resolveTheme(themePath) {
31
+ if (!themePath) {
32
+ throw new Error("Missing --theme. Provide a path to a theme .js file or a built-in name.");
33
+ }
34
+
35
+ // Try as a built-in name first (e.g., "newsprint" → examples/themes/theme-newsprint.js)
36
+ const builtIn = path.join(EXAMPLES_THEMES_DIR, `theme-${themePath}.js`);
37
+ if (fs.existsSync(builtIn)) {
38
+ return require(builtIn);
39
+ }
40
+
41
+ // Try as a direct path
42
+ const resolved = path.resolve(process.cwd(), themePath);
43
+ if (fs.existsSync(resolved)) {
44
+ return require(resolved);
45
+ }
46
+
47
+ // List available built-ins
48
+ const available = listBuiltInThemes();
49
+ throw new Error(
50
+ `Theme not found: "${themePath}"\n` +
51
+ ` Built-in themes: ${available.join(", ")}\n` +
52
+ ` Or provide a path to a .js theme file.`
53
+ );
54
+ }
55
+
56
+ function listBuiltInThemes() {
57
+ try {
58
+ return fs.readdirSync(EXAMPLES_THEMES_DIR)
59
+ .filter((f) => f.startsWith("theme-") && f.endsWith(".js"))
60
+ .map((f) => f.replace("theme-", "").replace(".js", ""));
61
+ } catch (e) {
62
+ return [];
63
+ }
64
+ }
65
+
66
+ function readSource(sourcePath) {
67
+ let raw = "";
68
+
69
+ if (sourcePath) {
70
+ raw = fs.readFileSync(path.resolve(process.cwd(), sourcePath), "utf8");
71
+ } else if (!process.stdin.isTTY) {
72
+ raw = fs.readFileSync(0, "utf8");
73
+ } else {
74
+ throw new Error("Missing --source. Provide a path to a .json file or pipe JSON via stdin.");
75
+ }
76
+
77
+ if (!raw || !raw.trim()) {
78
+ throw new Error("Empty source input.");
79
+ }
80
+
81
+ return JSON.parse(raw);
82
+ }
83
+
84
+ function printHelp() {
85
+ const themes = listBuiltInThemes();
86
+ console.log(`
87
+ sspdf CLI — Render a source JSON + theme into a PDF.
88
+
89
+ Usage:
90
+ node cli.js --source <file.json> --theme <theme> --output <file.pdf>
91
+ cat source.json | node cli.js --theme <theme> --output out.pdf
92
+
93
+ Options:
94
+ -s, --source <path> Path to source JSON file (or pipe via stdin)
95
+ -t, --theme <name|path> Built-in theme name or path to a .js theme file
96
+ -o, --output <path> Output PDF path (default: output/cli-output.pdf)
97
+ -h, --help Show this help
98
+
99
+ Built-in themes: ${themes.join(", ")}
100
+
101
+ Examples:
102
+ node cli.js -s examples/sources/source-article.json -t default -o output/article.pdf
103
+ node cli.js -s my-source.json -t ./my-theme.js -o my-doc.pdf
104
+ cat data.json | node cli.js -t newsprint -o output/newspaper.pdf
105
+ `.trim());
106
+ }
107
+
108
+ function main() {
109
+ const args = parseArgs(process.argv.slice(2));
110
+ if (args.help) {
111
+ printHelp();
112
+ return;
113
+ }
114
+
115
+ const source = readSource(args.source);
116
+ const theme = resolveTheme(args.theme);
117
+ const outputPath = path.resolve(
118
+ process.cwd(),
119
+ args.output || path.join("output", "cli-output.pdf")
120
+ );
121
+
122
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
123
+
124
+ const result = renderDocument({ source, theme, outputPath });
125
+
126
+ console.log(`[OK] ${result.operationsCount} operations rendered`);
127
+ console.log(`[OK] ${outputPath}`);
128
+ }
129
+
130
+ try {
131
+ main();
132
+ } catch (err) {
133
+ console.error(`[ERROR] ${err.message}`);
134
+ process.exit(1);
135
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Register theme-provided custom fonts into the core.
3
+ *
4
+ * Theme format:
5
+ * customFonts: [
6
+ * {
7
+ * family: "Inter",
8
+ * faces: [
9
+ * { style: "normal", fileName: "Inter-Regular.ttf", data: "<base64>" }
10
+ * ]
11
+ * }
12
+ * ]
13
+ *
14
+ * @param {import("./pdf-core").PDFCore} core
15
+ * @param {object} theme
16
+ */
17
+ function registerThemeFonts(core, theme) {
18
+ const customFonts = Array.isArray(theme.customFonts) ? theme.customFonts : [];
19
+
20
+ customFonts.forEach((familyDef) => {
21
+ if (!familyDef || !familyDef.family) {
22
+ throw new Error("Invalid customFonts family definition");
23
+ }
24
+
25
+ const faces = Array.isArray(familyDef.faces) ? familyDef.faces : [];
26
+ faces.forEach((face) => {
27
+ const data = face && (face.data || face.base64);
28
+ const fileName = face && face.fileName;
29
+ const style = (face && face.style) || "normal";
30
+
31
+ if (!data || !fileName) {
32
+ throw new Error(`Invalid font face for family "${familyDef.family}"`);
33
+ }
34
+
35
+ core.registerFont({
36
+ family: familyDef.family,
37
+ style,
38
+ fileName,
39
+ data,
40
+ });
41
+ });
42
+ });
43
+ }
44
+
45
+ module.exports = {
46
+ registerThemeFonts,
47
+ };