@hirokisakabe/pom-cli 0.4.0 → 0.5.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/README.md +31 -2
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +2 -32
- package/dist/cli.js +47 -11
- package/dist/glimpse.d.ts +3 -0
- package/dist/glimpse.d.ts.map +1 -0
- package/dist/glimpse.js +15 -0
- package/dist/input.d.ts +8 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/input.js +37 -0
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +4 -43
- package/dist/render.d.ts +7 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +71 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hirokisakabe/pom-cli
|
|
2
2
|
|
|
3
|
-
CLI tool for [pom](https://github.com/hirokisakabe/pom) — preview and
|
|
3
|
+
CLI tool for [pom](https://github.com/hirokisakabe/pom) — preview, build, and render presentations from pom XML / Markdown.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -83,9 +83,38 @@ To print per-step timing on stderr:
|
|
|
83
83
|
pom build slides.pom.xml -o output.pptx --verbose
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
### Render
|
|
87
|
+
|
|
88
|
+
Renders each slide to a PNG (default) or SVG image — no LibreOffice or other external tools required.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pom render slides.pom.xml -o ./images
|
|
92
|
+
pom render slides.pom.md -o ./images
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The images are written to the output directory as `slide-01.png`, `slide-02.png`, ... The directory is created if it does not exist. The rendering pipeline is the same as the preview server, so the images match what you see in `pom preview`.
|
|
96
|
+
|
|
97
|
+
To output SVG instead of PNG:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pom render slides.pom.xml -o ./images --format svg
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
To render only specific slides (1-based, comma-separated) — useful when re-checking just the slides you edited:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pom render slides.pom.xml -o ./images --slides 2,5
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
To print per-step timing on stderr:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pom render slides.pom.xml -o ./images --verbose
|
|
113
|
+
```
|
|
114
|
+
|
|
86
115
|
## Fonts
|
|
87
116
|
|
|
88
|
-
This package bundles Carlito and Noto Sans CJK JP fonts for
|
|
117
|
+
This package bundles Carlito and Noto Sans CJK JP fonts for image rendering. These fonts are used when converting slides to SVG in the preview server and to PNG / SVG in `pom render`. System fonts are not scanned.
|
|
89
118
|
|
|
90
119
|
## License
|
|
91
120
|
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAWA,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAWA,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,OAAO,CAAC,IAAI,CAAC,CAuDf"}
|
package/dist/build.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { buildPptx, DiagnosticsError } from "@hirokisakabe/pom";
|
|
4
|
-
import {
|
|
4
|
+
import { loadInput } from "./input.js";
|
|
5
5
|
import { watchInputFile } from "./watch.js";
|
|
6
6
|
function makeLog(verbose) {
|
|
7
7
|
if (!verbose)
|
|
@@ -18,37 +18,7 @@ export async function runBuild(inputFile, outputFile, options = {}) {
|
|
|
18
18
|
throw new Error(`Input file not found: ${absInput}`);
|
|
19
19
|
}
|
|
20
20
|
log(`Reading file: ${absInput}`);
|
|
21
|
-
const
|
|
22
|
-
const ext = path.extname(absInput);
|
|
23
|
-
let xml;
|
|
24
|
-
let slideWidth = 1280;
|
|
25
|
-
let slideHeight = 720;
|
|
26
|
-
let masterPptxData;
|
|
27
|
-
if (ext === ".md") {
|
|
28
|
-
const t = Date.now();
|
|
29
|
-
const result = parseMd(content);
|
|
30
|
-
xml = result.xml;
|
|
31
|
-
slideWidth = result.meta.size.w;
|
|
32
|
-
slideHeight = result.meta.size.h;
|
|
33
|
-
log(`Parsing Markdown... done (${Date.now() - t}ms)`);
|
|
34
|
-
if (result.meta.masterPptx) {
|
|
35
|
-
const masterPath = path.resolve(path.dirname(absInput), result.meta.masterPptx);
|
|
36
|
-
try {
|
|
37
|
-
masterPptxData = new Uint8Array(fs.readFileSync(masterPath));
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
|
41
|
-
console.warn(`Warning: masterPptx not found: ${masterPath}`);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
throw e;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
xml = content;
|
|
51
|
-
}
|
|
21
|
+
const { xml, slideWidth, slideHeight, masterPptxData } = loadInput(absInput, log);
|
|
52
22
|
const t1 = Date.now();
|
|
53
23
|
const { pptx } = await buildPptx(xml, { w: slideWidth, h: slideHeight }, {
|
|
54
24
|
...(masterPptxData ? { masterPptx: masterPptxData } : {}),
|
package/dist/cli.js
CHANGED
|
@@ -4,12 +4,25 @@ import { Command } from "commander";
|
|
|
4
4
|
import { DiagnosticsError } from "@hirokisakabe/pom";
|
|
5
5
|
import { runBuild, runBuildWatch } from "./build.js";
|
|
6
6
|
import { runPreview } from "./preview.js";
|
|
7
|
+
import { runRender } from "./render.js";
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
8
9
|
const { version } = require("../package.json");
|
|
9
10
|
const program = new Command();
|
|
11
|
+
function printBuildError(err) {
|
|
12
|
+
if (err instanceof DiagnosticsError) {
|
|
13
|
+
const count = err.diagnostics.length;
|
|
14
|
+
console.error(`✗ Build failed (${count} ${count === 1 ? "error" : "errors"})\n`);
|
|
15
|
+
for (const d of err.diagnostics) {
|
|
16
|
+
console.error(` [${d.code}] ${d.message}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
10
23
|
program
|
|
11
24
|
.name("pom")
|
|
12
|
-
.description("CLI tool for pom — preview and
|
|
25
|
+
.description("CLI tool for pom — preview, build, and render presentations")
|
|
13
26
|
.version(version);
|
|
14
27
|
program
|
|
15
28
|
.command("preview")
|
|
@@ -54,18 +67,41 @@ program
|
|
|
54
67
|
}
|
|
55
68
|
else {
|
|
56
69
|
runBuild(input, options.o, { verbose: options.verbose }).catch((err) => {
|
|
57
|
-
|
|
58
|
-
const count = err.diagnostics.length;
|
|
59
|
-
console.error(`✗ Build failed (${count} ${count === 1 ? "error" : "errors"})\n`);
|
|
60
|
-
for (const d of err.diagnostics) {
|
|
61
|
-
console.error(` [${d.code}] ${d.message}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
66
|
-
}
|
|
70
|
+
printBuildError(err);
|
|
67
71
|
process.exit(1);
|
|
68
72
|
});
|
|
69
73
|
}
|
|
70
74
|
});
|
|
75
|
+
program
|
|
76
|
+
.command("render")
|
|
77
|
+
.description("Render each slide to a PNG or SVG image")
|
|
78
|
+
.argument("<input>", "Input file (.pom.xml or .pom.md)")
|
|
79
|
+
.requiredOption("-o <dir>", "Output directory for rendered images")
|
|
80
|
+
.option("--format <format>", "Output format: png or svg", "png")
|
|
81
|
+
.option("--slides <numbers>", "Comma-separated slide numbers to render (e.g. 2,5)")
|
|
82
|
+
.option("--verbose", "Show build step timing on stderr")
|
|
83
|
+
.action((input, options) => {
|
|
84
|
+
if (options.format !== "png" && options.format !== "svg") {
|
|
85
|
+
console.error(`Invalid format: ${options.format} (expected "png" or "svg")`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
const format = options.format;
|
|
89
|
+
let slides;
|
|
90
|
+
if (options.slides !== undefined) {
|
|
91
|
+
slides = options.slides.split(",").map((s) => Number(s.trim()));
|
|
92
|
+
if (slides.length === 0 ||
|
|
93
|
+
slides.some((n) => !Number.isInteger(n) || n <= 0)) {
|
|
94
|
+
console.error(`Invalid slides: ${options.slides} (expected comma-separated slide numbers, e.g. 2,5)`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
runRender(input, options.o, {
|
|
99
|
+
format,
|
|
100
|
+
slides,
|
|
101
|
+
verbose: options.verbose,
|
|
102
|
+
}).catch((err) => {
|
|
103
|
+
printBuildError(err);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
71
107
|
program.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glimpse.d.ts","sourceRoot":"","sources":["../src/glimpse.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGrD,CAAC;AAEF,wBAAgB,sBAAsB,IAAI,MAAM,CAQ/C"}
|
package/dist/glimpse.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
export const EXTRA_FONT_MAPPING = {
|
|
6
|
+
"游ゴシック Light": "Noto Sans CJK JP",
|
|
7
|
+
"Yu Gothic Light": "Noto Sans CJK JP",
|
|
8
|
+
};
|
|
9
|
+
export function resolveBundledFontsDir() {
|
|
10
|
+
const fontsDir = path.resolve(__dirname, "../fonts");
|
|
11
|
+
if (!fs.existsSync(fontsDir)) {
|
|
12
|
+
throw new Error(`Bundled fonts directory not found: ${fontsDir}. The package may be corrupted.`);
|
|
13
|
+
}
|
|
14
|
+
return fontsDir;
|
|
15
|
+
}
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,UAAU,CAAC;CAC7B;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACpC,WAAW,CAqCb"}
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { parseMd } from "@hirokisakabe/pom-md";
|
|
4
|
+
export function loadInput(absInput, log = () => { }) {
|
|
5
|
+
const content = fs.readFileSync(absInput, "utf-8");
|
|
6
|
+
const ext = path.extname(absInput);
|
|
7
|
+
let xml;
|
|
8
|
+
let slideWidth = 1280;
|
|
9
|
+
let slideHeight = 720;
|
|
10
|
+
let masterPptxData;
|
|
11
|
+
if (ext === ".md") {
|
|
12
|
+
const t = Date.now();
|
|
13
|
+
const result = parseMd(content);
|
|
14
|
+
xml = result.xml;
|
|
15
|
+
slideWidth = result.meta.size.w;
|
|
16
|
+
slideHeight = result.meta.size.h;
|
|
17
|
+
log(`Parsing Markdown... done (${Date.now() - t}ms)`);
|
|
18
|
+
if (result.meta.masterPptx) {
|
|
19
|
+
const masterPath = path.resolve(path.dirname(absInput), result.meta.masterPptx);
|
|
20
|
+
try {
|
|
21
|
+
masterPptxData = new Uint8Array(fs.readFileSync(masterPath));
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
|
25
|
+
console.warn(`Warning: masterPptx not found: ${masterPath}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
xml = content;
|
|
35
|
+
}
|
|
36
|
+
return { xml, slideWidth, slideHeight, masterPptxData };
|
|
37
|
+
}
|
package/dist/preview.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAyWA,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAqB,EAC3B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAClD,IAAI,CAmIN"}
|
package/dist/preview.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import http from "http";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
4
|
import path from "path";
|
|
6
5
|
import { buildPptx } from "@hirokisakabe/pom";
|
|
7
|
-
import { parseMd } from "@hirokisakabe/pom-md";
|
|
8
6
|
import { convertPptxToSvg } from "pptx-glimpse";
|
|
7
|
+
import { EXTRA_FONT_MAPPING, resolveBundledFontsDir } from "./glimpse.js";
|
|
8
|
+
import { loadInput } from "./input.js";
|
|
9
9
|
import { watchInputFile } from "./watch.js";
|
|
10
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
10
|
const DEFAULT_PORT = 3000;
|
|
12
11
|
function makeLog(verbose) {
|
|
13
12
|
if (!verbose)
|
|
@@ -36,43 +35,9 @@ function openBrowser(url) {
|
|
|
36
35
|
});
|
|
37
36
|
child.unref();
|
|
38
37
|
}
|
|
39
|
-
const EXTRA_FONT_MAPPING = {
|
|
40
|
-
"游ゴシック Light": "Noto Sans CJK JP",
|
|
41
|
-
"Yu Gothic Light": "Noto Sans CJK JP",
|
|
42
|
-
};
|
|
43
38
|
async function generateSvgs(inputFile, verbose = false) {
|
|
44
39
|
const log = makeLog(verbose);
|
|
45
|
-
const
|
|
46
|
-
const ext = path.extname(inputFile);
|
|
47
|
-
let xml;
|
|
48
|
-
let slideWidth = 1280;
|
|
49
|
-
let slideHeight = 720;
|
|
50
|
-
let masterPptxData;
|
|
51
|
-
if (ext === ".md") {
|
|
52
|
-
const t = Date.now();
|
|
53
|
-
const result = parseMd(content);
|
|
54
|
-
xml = result.xml;
|
|
55
|
-
slideWidth = result.meta.size.w;
|
|
56
|
-
slideHeight = result.meta.size.h;
|
|
57
|
-
log(`Parsing Markdown... done (${Date.now() - t}ms)`);
|
|
58
|
-
if (result.meta.masterPptx) {
|
|
59
|
-
const masterPath = path.resolve(path.dirname(inputFile), result.meta.masterPptx);
|
|
60
|
-
try {
|
|
61
|
-
masterPptxData = new Uint8Array(fs.readFileSync(masterPath));
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
if (e instanceof Error && "code" in e && e.code === "ENOENT") {
|
|
65
|
-
process.stderr.write(`Warning: masterPptx not found: ${masterPath}\n`);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
throw e;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
xml = content;
|
|
75
|
-
}
|
|
40
|
+
const { xml, slideWidth, slideHeight, masterPptxData } = loadInput(inputFile, log);
|
|
76
41
|
if (!xml.trim()) {
|
|
77
42
|
return { type: "empty" };
|
|
78
43
|
}
|
|
@@ -86,11 +51,7 @@ async function generateSvgs(inputFile, verbose = false) {
|
|
|
86
51
|
if (!(buffer instanceof Uint8Array)) {
|
|
87
52
|
throw new Error("Unexpected output type from pptx.write");
|
|
88
53
|
}
|
|
89
|
-
const
|
|
90
|
-
if (!fs.existsSync(fontsDir)) {
|
|
91
|
-
throw new Error(`Bundled fonts directory not found: ${fontsDir}. The package may be corrupted.`);
|
|
92
|
-
}
|
|
93
|
-
const fontDirs = [fontsDir];
|
|
54
|
+
const fontDirs = [resolveBundledFontsDir()];
|
|
94
55
|
const t2 = Date.now();
|
|
95
56
|
const slides = await convertPptxToSvg(buffer, {
|
|
96
57
|
width: slideWidth,
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,CAAC;AAOzC,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,OAAO,CAAC,IAAI,CAAC,CAoFf"}
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { buildPptx } from "@hirokisakabe/pom";
|
|
4
|
+
import { convertPptxToPng, convertPptxToSvg } from "pptx-glimpse";
|
|
5
|
+
import { EXTRA_FONT_MAPPING, resolveBundledFontsDir } from "./glimpse.js";
|
|
6
|
+
import { loadInput } from "./input.js";
|
|
7
|
+
function makeLog(verbose) {
|
|
8
|
+
if (!verbose)
|
|
9
|
+
return (_msg) => { };
|
|
10
|
+
return (msg) => process.stderr.write(`[pom] ${msg}\n`);
|
|
11
|
+
}
|
|
12
|
+
export async function runRender(inputFile, outputDir, options = {}) {
|
|
13
|
+
const verbose = options.verbose ?? false;
|
|
14
|
+
const format = options.format ?? "png";
|
|
15
|
+
const log = makeLog(verbose);
|
|
16
|
+
const totalStart = Date.now();
|
|
17
|
+
const absInput = path.resolve(inputFile);
|
|
18
|
+
const absOutputDir = path.resolve(outputDir);
|
|
19
|
+
if (!fs.existsSync(absInput)) {
|
|
20
|
+
throw new Error(`Input file not found: ${absInput}`);
|
|
21
|
+
}
|
|
22
|
+
log(`Reading file: ${absInput}`);
|
|
23
|
+
const { xml, slideWidth, slideHeight, masterPptxData } = loadInput(absInput, log);
|
|
24
|
+
const t1 = Date.now();
|
|
25
|
+
const { pptx } = await buildPptx(xml, { w: slideWidth, h: slideHeight }, {
|
|
26
|
+
textMeasurement: "fallback",
|
|
27
|
+
...(masterPptxData ? { masterPptx: masterPptxData } : {}),
|
|
28
|
+
strict: true,
|
|
29
|
+
});
|
|
30
|
+
log(`Building PPTX... done (${Date.now() - t1}ms)`);
|
|
31
|
+
const buffer = await pptx.write({ outputType: "uint8array" });
|
|
32
|
+
if (!(buffer instanceof Uint8Array)) {
|
|
33
|
+
throw new Error("Unexpected output type from pptx.write");
|
|
34
|
+
}
|
|
35
|
+
const convertOptions = {
|
|
36
|
+
width: slideWidth,
|
|
37
|
+
fontDirs: [resolveBundledFontsDir()],
|
|
38
|
+
fontMapping: EXTRA_FONT_MAPPING,
|
|
39
|
+
skipSystemFonts: true,
|
|
40
|
+
...(options.slides ? { slides: options.slides } : {}),
|
|
41
|
+
};
|
|
42
|
+
const t2 = Date.now();
|
|
43
|
+
let outputs;
|
|
44
|
+
if (format === "svg") {
|
|
45
|
+
const slides = await convertPptxToSvg(buffer, convertOptions);
|
|
46
|
+
outputs = slides.map((s) => ({ slideNumber: s.slideNumber, data: s.svg }));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const slides = await convertPptxToPng(buffer, convertOptions);
|
|
50
|
+
outputs = slides.map((s) => ({ slideNumber: s.slideNumber, data: s.png }));
|
|
51
|
+
}
|
|
52
|
+
log(`Rendering ${format.toUpperCase()}... done (${Date.now() - t2}ms)`);
|
|
53
|
+
if (options.slides) {
|
|
54
|
+
const rendered = new Set(outputs.map((o) => o.slideNumber));
|
|
55
|
+
const missing = options.slides.filter((n) => !rendered.has(n));
|
|
56
|
+
if (missing.length > 0) {
|
|
57
|
+
console.warn(`Warning: slide ${missing.join(", ")} not found in the presentation`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (outputs.length === 0) {
|
|
61
|
+
throw new Error("No slides were rendered");
|
|
62
|
+
}
|
|
63
|
+
fs.mkdirSync(absOutputDir, { recursive: true });
|
|
64
|
+
const padWidth = Math.max(2, ...outputs.map((o) => String(o.slideNumber).length));
|
|
65
|
+
for (const { slideNumber, data } of outputs) {
|
|
66
|
+
const file = path.join(absOutputDir, `slide-${String(slideNumber).padStart(padWidth, "0")}.${format}`);
|
|
67
|
+
fs.writeFileSync(file, data);
|
|
68
|
+
console.log(`Saved: ${file}`);
|
|
69
|
+
}
|
|
70
|
+
log(`Total: ${Date.now() - totalStart}ms`);
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hirokisakabe/pom-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI tool for pom — preview and
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "CLI tool for pom — preview, build, and render presentations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"pom": "./dist/cli.js"
|