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/LICENSE +201 -0
- package/README.md +409 -0
- package/cli.js +135 -0
- package/core/font-registry.js +47 -0
- package/core/pdf-core.js +853 -0
- package/core/plugin-chart.js +109 -0
- package/core/plugin-registry.js +46 -0
- package/core/render-document.js +931 -0
- package/core/shapes.js +354 -0
- package/core/units.js +39 -0
- package/core/validate.js +151 -0
- package/fonts/crimson-text.js +13 -0
- package/fonts/fira-code.js +9 -0
- package/fonts/ibm-plex-sans.js +13 -0
- package/fonts/inter.js +9 -0
- package/fonts/jetbrains-mono.js +13 -0
- package/fonts/lato.js +13 -0
- package/fonts/libre-baskerville.js +11 -0
- package/fonts/lora.js +13 -0
- package/fonts/merriweather.js +13 -0
- package/fonts/montserrat.js +13 -0
- package/fonts/nunito.js +13 -0
- package/fonts/open-sans.js +13 -0
- package/fonts/oswald.js +9 -0
- package/fonts/playfair-display.js +13 -0
- package/fonts/pt-sans.js +13 -0
- package/fonts/raleway.js +13 -0
- package/fonts/roboto.js +13 -0
- package/fonts/source-code-pro.js +13 -0
- package/fonts/source-serif-4.js +13 -0
- package/fonts/work-sans.js +13 -0
- package/index.js +24 -0
- package/package.json +62 -0
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
|
+
};
|