@openpresentation/opf 0.17.0 → 0.18.1
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 +4 -4
- package/dist/browser.js +38 -0
- package/dist/{catalog-refs-C4K4y-vh.d.ts → catalog-refs-BSfGELpt.d.ts} +1 -1
- package/dist/{catalog-schemas-g7RFQBdc.d.ts → catalog-schemas-CinPh5WA.d.ts} +1 -1
- package/dist/catalog.d.ts +2 -2
- package/dist/{chart-data-DTxdRRT1.d.ts → chart-data-BnNqgnC-.d.ts} +1 -1
- package/dist/{chunk-O2CX5NEH.js → chunk-64YSJZDH.js} +6 -5
- package/dist/{chunk-RA64UQKI.js → chunk-6WW2EDUW.js} +95 -71
- package/dist/{chunk-IS5H5JKH.js → chunk-CJPDTLGD.js} +1 -1
- package/dist/{chunk-BIC3SWPK.js → chunk-DBYZI75U.js} +30 -5
- package/dist/{chunk-NLNYNBTS.js → chunk-JT3QXA22.js} +25 -24
- package/dist/{chunk-YT7FMQER.js → chunk-JXVS7BJK.js} +1 -1
- package/dist/{chunk-IPW5BS3X.js → chunk-KXBZ6ZSB.js} +190 -172
- package/dist/{chunk-CW7GEAV3.js → chunk-MBU42S5Y.js} +291 -133
- package/dist/{chunk-TCD2FB2K.js → chunk-NS3VYB57.js} +5 -5
- package/dist/{chunk-WWYWFVOI.js → chunk-QLFWWHRF.js} +151 -25
- package/dist/{chunk-PPROJP2E.js → chunk-RAKEVM52.js} +1 -1
- package/dist/{chunk-ESRYBYLY.js → chunk-VCGRMNY2.js} +1 -1
- package/dist/{chunk-M4UR3NPN.js → chunk-YWXB4XAT.js} +2 -2
- package/dist/{chunk-O3CVNJTL.js → chunk-YZXTXX2D.js} +1 -1
- package/dist/{chunk-O263APNI.js → chunk-ZCFSDSUX.js} +3 -3
- package/dist/{chunk-N4EVOFEA.js → chunk-ZOHFLP3O.js} +4 -4
- package/dist/{composition-BbZ_9C-c.d.ts → composition-C-OoqvAr.d.ts} +80 -40
- package/dist/composition.d.ts +3 -3
- package/dist/composition.js +1 -1
- package/dist/{conversion-SBpTOwdg.d.ts → conversion-C5foedh6.d.ts} +96 -38
- package/dist/conversion-KGDVJM75.js +21 -0
- package/dist/convert.d.ts +4 -4
- package/dist/convert.js +7 -7
- package/dist/data.d.ts +7 -7
- package/dist/data.js +2 -2
- package/dist/{deck-report-Bx3naFlF.d.ts → deck-report-wszX7uo8.d.ts} +5 -5
- package/dist/deck.d.ts +8 -8
- package/dist/deck.js +10 -10
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +5 -5
- package/dist/docs.js +28 -28
- package/dist/examples.d.ts +1 -1
- package/dist/examples.js +181 -214
- package/dist/{finding-CDQ_wH8j.d.ts → finding-CT_f_vbD.d.ts} +1 -1
- package/dist/format.js +2 -2
- package/dist/index.d.ts +153 -20
- package/dist/index.js +62 -15
- package/dist/markdown.d.ts +12 -8
- package/dist/markdown.js +7 -7
- package/dist/node-engine.d.ts +37 -17
- package/dist/node-engine.js +39 -16
- package/dist/{pagination-BhX4M48Y.d.ts → pagination-BaUjDrTz.d.ts} +7 -4
- package/dist/pagination.d.ts +4 -4
- package/dist/pagination.js +4 -4
- package/dist/patch.d.ts +1 -1
- package/dist/patch.js +4 -4
- package/dist/{presentation-fb8CZmRq.d.ts → presentation-HondgJ3l.d.ts} +269 -382
- package/dist/repo-readme.js +1 -1
- package/dist/schemas.d.ts +4 -2
- package/dist/schemas.js +1 -1
- package/dist/spec/schemas/opf.schema.json +65 -70
- package/dist/types.d.ts +3 -3
- package/dist/{validation-types-iZbXfES6.d.ts → validation-types-PbGDrNUr.d.ts} +12 -7
- package/dist/validator.d.ts +8 -8
- package/dist/validator.js +3 -3
- package/dist/yaml.d.ts +6 -6
- package/dist/yaml.js +7 -7
- package/package.json +12 -10
- package/dist/node.d.ts +0 -60
- package/dist/node.js +0 -54
|
@@ -1,41 +1,101 @@
|
|
|
1
|
+
import { OPFApiError, parseSlideSelection, OPFExportError, asApiError, OPFImportError } from './chunk-QLFWWHRF.js';
|
|
1
2
|
import { defaultCatalog } from './chunk-WCG3THTD.js';
|
|
2
|
-
import { deckFormatOf, readDeckReport, stringify } from './chunk-
|
|
3
|
-
import { paginate } from './chunk-
|
|
4
|
-
import { validate, OPFValidationError } from './chunk-
|
|
5
|
-
import {
|
|
6
|
-
import { createRequire } from 'module';
|
|
3
|
+
import { deckFormatOf, readDeckReport, stringify } from './chunk-ZOHFLP3O.js';
|
|
4
|
+
import { paginate } from './chunk-64YSJZDH.js';
|
|
5
|
+
import { validate, OPFValidationError } from './chunk-JT3QXA22.js';
|
|
6
|
+
import { stat, readdir, lstat, mkdir, writeFile, rename, unlink, readFile } from 'fs/promises';
|
|
7
7
|
import path6 from 'path';
|
|
8
|
+
import { readFileSync, realpathSync, statSync } from 'fs';
|
|
8
9
|
import { pathToFileURL, fileURLToPath } from 'url';
|
|
9
|
-
import {
|
|
10
|
+
import { createRequire } from 'module';
|
|
10
11
|
import { randomUUID } from 'crypto';
|
|
11
12
|
import { crc32 } from 'zlib';
|
|
12
13
|
|
|
13
|
-
// src/node/
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
// src/node/catalogs.ts
|
|
15
|
+
var DEFAULT_CATALOGS = [defaultCatalog];
|
|
16
|
+
var MEDIA = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".svg": "image/svg+xml" };
|
|
17
|
+
var MAX_BYTES = 32 * 1024 * 1024;
|
|
18
|
+
var startsWith = (bytes, signature, offset = 0) => signature.every((value, index) => bytes[offset + index] === value);
|
|
19
|
+
function looksLike(type, bytes) {
|
|
20
|
+
switch (type) {
|
|
21
|
+
case "image/png":
|
|
22
|
+
return startsWith(bytes, [137, 80, 78, 71]);
|
|
23
|
+
case "image/jpeg":
|
|
24
|
+
return startsWith(bytes, [255, 216, 255]);
|
|
25
|
+
case "image/gif":
|
|
26
|
+
return startsWith(bytes, [71, 73, 70, 56]);
|
|
27
|
+
case "image/webp":
|
|
28
|
+
return startsWith(bytes, [82, 73, 70, 70]) && startsWith(bytes, [87, 69, 66, 80], 8);
|
|
29
|
+
default:
|
|
30
|
+
return /<svg[\s>]/i.test(new TextDecoder().decode(bytes.subarray(0, 4096)));
|
|
24
31
|
}
|
|
32
|
+
}
|
|
33
|
+
var AssetError = class extends Error {
|
|
34
|
+
constructor(message, details) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.details = details;
|
|
37
|
+
}
|
|
38
|
+
details;
|
|
39
|
+
code = "asset-unresolved";
|
|
25
40
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
function createImageResolver(root, reporter, passDirectory = "pass --asset-dir") {
|
|
42
|
+
const base = root === void 0 ? void 0 : realpathSync(root);
|
|
43
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
44
|
+
const shown = (source) => source.length > 80 ? `${source.slice(0, 77)}...` : source;
|
|
45
|
+
function load(source) {
|
|
46
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(source) && !/^file:/i.test(source) && !/^[a-z]:[\\/]/i.test(source)) return { skipped: true };
|
|
47
|
+
if (base === void 0) return { problem: "blocked", reason: "no asset directory was given, so no local file is read." };
|
|
48
|
+
let file;
|
|
49
|
+
try {
|
|
50
|
+
file = /^file:/i.test(source) ? fileURLToPath(source) : path6.resolve(base, source);
|
|
51
|
+
} catch {
|
|
52
|
+
return { problem: "blocked", reason: "the path is not valid." };
|
|
53
|
+
}
|
|
54
|
+
let real;
|
|
55
|
+
try {
|
|
56
|
+
real = realpathSync(file);
|
|
57
|
+
} catch {
|
|
58
|
+
return { problem: "missing", reason: `no such file under ${base}.` };
|
|
59
|
+
}
|
|
60
|
+
const relative = path6.relative(base, real);
|
|
61
|
+
if (relative.startsWith("..") || path6.isAbsolute(relative)) return { problem: "blocked", reason: `it is outside the asset directory ${base}.` };
|
|
62
|
+
const mediaType = MEDIA[path6.extname(real).toLowerCase()];
|
|
63
|
+
if (!mediaType) return { problem: "blocked", reason: "only .png, .jpg, .jpeg, .gif, .webp and .svg files are read." };
|
|
64
|
+
const info = statSync(real);
|
|
65
|
+
if (!info.isFile() || info.size > MAX_BYTES) return { problem: "blocked", reason: `it is not a regular file under ${MAX_BYTES / 1024 / 1024} MB.` };
|
|
66
|
+
const bytes = new Uint8Array(readFileSync(real));
|
|
67
|
+
if (!looksLike(mediaType, bytes)) return { problem: "blocked", reason: `the content is not a ${mediaType} image.` };
|
|
68
|
+
return { image: { bytes, mediaType } };
|
|
69
|
+
}
|
|
70
|
+
function read(source) {
|
|
71
|
+
let outcome = cache2.get(source);
|
|
72
|
+
if (!outcome) {
|
|
73
|
+
outcome = load(source);
|
|
74
|
+
cache2.set(source, outcome);
|
|
75
|
+
}
|
|
76
|
+
return outcome;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
/** opf-render: a data URI string, or null for the placeholder (a missing file is reported by the renderer as unresolved-asset). */
|
|
80
|
+
forSvg: (source, context) => {
|
|
81
|
+
if (!source) return null;
|
|
82
|
+
const outcome = read(source);
|
|
83
|
+
if ("image" in outcome) return `data:${outcome.image.mediaType};base64,${Buffer.from(outcome.image.bytes).toString("base64")}`;
|
|
84
|
+
if ("problem" in outcome && outcome.problem === "blocked") reporter.add("cli", { code: "asset-blocked", path: context.path, message: `Image "${shown(source)}" was not read: ${outcome.reason}` });
|
|
85
|
+
return null;
|
|
86
|
+
},
|
|
87
|
+
/** opf-pptx: bytes and media type; null for URLs and references it handles itself. An unreadable local file stops the export. */
|
|
88
|
+
forPptx: (source, context) => {
|
|
89
|
+
const outcome = read(source);
|
|
90
|
+
if ("image" in outcome) return { data: outcome.image.bytes, mediaType: outcome.image.mediaType };
|
|
91
|
+
if ("skipped" in outcome) return null;
|
|
92
|
+
throw new AssetError(`Image "${shown(source)}" was not read: ${outcome.reason} Fix the path, embed the image as a data URI, or ${passDirectory}.`, { path: context.path });
|
|
93
|
+
}
|
|
94
|
+
};
|
|
35
95
|
}
|
|
36
96
|
var RENDER_PACKAGE = "@openpresentation/opf-render";
|
|
37
97
|
var PPTX_PACKAGE = "@openpresentation/opf-pptx";
|
|
38
|
-
var PEER_RANGES = { [RENDER_PACKAGE]: "^0.
|
|
98
|
+
var PEER_RANGES = { [RENDER_PACKAGE]: "^0.18.0", [PPTX_PACKAGE]: "^0.18.0" };
|
|
39
99
|
var RENDER_EXTRAS = {
|
|
40
100
|
/** The faces of the office pack, which the CLI and `convert` always load (every format). */
|
|
41
101
|
fonts: ["@expo-google-fonts/roboto@0.4.3", "@expo-google-fonts/roboto-mono@0.4.2", "@expo-google-fonts/caladea@0.4.2", "@expo-google-fonts/arimo@0.4.3", "@expo-google-fonts/tinos@0.4.2", "@expo-google-fonts/cousine@0.4.3", "@expo-google-fonts/gelasio@0.4.1", "@expo-google-fonts/noto-sans@0.4.2"],
|
|
@@ -50,9 +110,9 @@ var renderHint = () => ` ${RENDER_PACKAGE} also needs its optional peers, by ou
|
|
|
50
110
|
png: npm install ${RENDER_EXTRAS.png.join(" ")}
|
|
51
111
|
pdf: npm install ${RENDER_EXTRAS.pdfRaster.join(" ")} (raster mode), ${RENDER_EXTRAS.pdfPictures.join(" ")} (when the deck has pictures)
|
|
52
112
|
svg: fonts only`;
|
|
53
|
-
var hint = (name) => `${name} is not installed. It is an optional peer of @openpresentation/opf, loaded only by @openpresentation/opf
|
|
113
|
+
var hint = (name) => `${name} is not installed. It is an optional peer of @openpresentation/opf, loaded only by the file API of @openpresentation/opf in Node (convert, open) and the opf commands that need it. Install it next to the CLI or in your project:
|
|
54
114
|
npm install -g ${name}@${PEER_RANGES[name]} (global CLI)
|
|
55
|
-
npm install ${name}@${PEER_RANGES[name]} (project that
|
|
115
|
+
npm install ${name}@${PEER_RANGES[name]} (project that calls convert or open of @openpresentation/opf, or depends on the CLI)
|
|
56
116
|
npx -p @openpresentation/cli -p @openpresentation/opf-render -p @openpresentation/opf-pptx opf <command> ... (one run)` + (name === RENDER_PACKAGE ? `
|
|
57
117
|
${renderHint()}` : "");
|
|
58
118
|
function bases() {
|
|
@@ -69,6 +129,40 @@ function locate(specifier) {
|
|
|
69
129
|
}
|
|
70
130
|
return void 0;
|
|
71
131
|
}
|
|
132
|
+
function locatePackage(name, from) {
|
|
133
|
+
const starts = [...bases(), ...from === void 0 ? [] : [pathToFileURL(from).href]];
|
|
134
|
+
for (const base of starts) {
|
|
135
|
+
const manifest = manifestFrom(name, base);
|
|
136
|
+
if (!manifest) continue;
|
|
137
|
+
try {
|
|
138
|
+
return { manifest, version: JSON.parse(readFileSync(manifest, "utf8")).version ?? "unknown" };
|
|
139
|
+
} catch {
|
|
140
|
+
return { manifest, version: "unknown" };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return void 0;
|
|
144
|
+
}
|
|
145
|
+
function manifestFrom(name, base) {
|
|
146
|
+
const require2 = createRequire(base);
|
|
147
|
+
try {
|
|
148
|
+
return require2.resolve(`${name}/package.json`);
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
let entry;
|
|
152
|
+
try {
|
|
153
|
+
entry = require2.resolve(name);
|
|
154
|
+
} catch {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
for (let folder = path6.dirname(entry); folder !== path6.dirname(folder); folder = path6.dirname(folder)) {
|
|
158
|
+
const candidate = path6.join(folder, "package.json");
|
|
159
|
+
try {
|
|
160
|
+
if (JSON.parse(readFileSync(candidate, "utf8")).name === name) return candidate;
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
72
166
|
var cache = /* @__PURE__ */ new Map();
|
|
73
167
|
async function loadPeer(name, subpath = "") {
|
|
74
168
|
const key = `${name}${subpath}`;
|
|
@@ -106,7 +200,7 @@ function missingPeerFrom(error) {
|
|
|
106
200
|
}
|
|
107
201
|
async function loadRenderer() {
|
|
108
202
|
const render = await loadPeer(RENDER_PACKAGE);
|
|
109
|
-
requireFeature(render, ["
|
|
203
|
+
requireFeature(render, ["toSvg", "toPng", "toPdf"]);
|
|
110
204
|
const fonts = await loadPeer(RENDER_PACKAGE, "/fonts-node");
|
|
111
205
|
requireFeature(fonts, ["loadFonts"]);
|
|
112
206
|
return { render, fonts: fonts.module };
|
|
@@ -116,8 +210,10 @@ async function loadPptx() {
|
|
|
116
210
|
requireFeature(pptx, ["toPptx", "fromPptx", "inventoryTypefaces"]);
|
|
117
211
|
return pptx;
|
|
118
212
|
}
|
|
213
|
+
|
|
214
|
+
// src/node/fonts.ts
|
|
119
215
|
var FONT_FILE = /\.(ttf|otf)$/i;
|
|
120
|
-
async function listFontDirectories(directories, label = "--
|
|
216
|
+
async function listFontDirectories(directories, label = "--fonts") {
|
|
121
217
|
const files = [];
|
|
122
218
|
for (const directory of directories) {
|
|
123
219
|
const resolved = path6.resolve(directory);
|
|
@@ -198,8 +294,8 @@ function scriptDiagnostic(diagnostic) {
|
|
|
198
294
|
const name = typeof diagnostic.package === "string" ? diagnostic.package : "the Noto script package";
|
|
199
295
|
return { ...diagnostic, message: `${diagnostic.message} Install ${name} next to the CLI (npm install -g ${name}) to draw this script with a bundled open font.` };
|
|
200
296
|
}
|
|
201
|
-
function embeddedFor(handle,
|
|
202
|
-
return
|
|
297
|
+
function embeddedFor(handle, text = "fonts") {
|
|
298
|
+
return text === "fonts" ? handle.embeddedFonts.map((face) => ({ ...face, embed: "used" })) : [];
|
|
203
299
|
}
|
|
204
300
|
function substitutionRows(handle) {
|
|
205
301
|
const rows = /* @__PURE__ */ new Map();
|
|
@@ -209,22 +305,6 @@ function substitutionRows(handle) {
|
|
|
209
305
|
}
|
|
210
306
|
return [...rows.values()].sort((a, b) => a.requested + a.resolved < b.requested + b.resolved ? -1 : 1);
|
|
211
307
|
}
|
|
212
|
-
function parseSlideSelection(spec, total, label = "--slides") {
|
|
213
|
-
if (total < 1) throw new OPFApiError("The presentation has no slides.", "no-slides");
|
|
214
|
-
const chosen = /* @__PURE__ */ new Set();
|
|
215
|
-
for (const part of spec.split(",")) {
|
|
216
|
-
const text = part.trim();
|
|
217
|
-
const match = /^(\d*)(-?)(\d*)$/.exec(text);
|
|
218
|
-
if (!text || !match || !match[1] && !match[3]) throw new OPFApiError(`${label} needs numbers like 1,3-5 (got "${spec}").`, "invalid-option");
|
|
219
|
-
const [, from, dash, to] = match;
|
|
220
|
-
const first = from ? Number(from) : 1;
|
|
221
|
-
const last = dash ? to ? Number(to) : total : first;
|
|
222
|
-
if (first < 1 || last < first) throw new OPFApiError(`${label} range "${text}" is not valid (slides count from 1).`, "invalid-option");
|
|
223
|
-
if (last > total) throw new OPFApiError(`${label} ${text} is outside the presentation, which has ${total} slide${total === 1 ? "" : "s"}.`, "invalid-option");
|
|
224
|
-
for (let n = first; n <= last; n++) chosen.add(n);
|
|
225
|
-
}
|
|
226
|
-
return [...chosen].sort((a, b) => a - b);
|
|
227
|
-
}
|
|
228
308
|
function stemOf(input) {
|
|
229
309
|
if (input === "-") return "deck";
|
|
230
310
|
return path6.basename(input).replace(/\.opf\.json$/i, "").replace(/\.(json|pptx|potx)$/i, "") || "deck";
|
|
@@ -292,7 +372,7 @@ var HELP = {
|
|
|
292
372
|
"unresolved-asset": "Embed the image as a data URI, or keep the file inside the deck folder (see --asset-dir). URLs are never fetched.",
|
|
293
373
|
"asset-blocked": "Move the file under the deck folder or pass --asset-dir <directory> that contains it.",
|
|
294
374
|
"script-font-not-installed": "Install the Noto script package named in the message next to the CLI, then run the command again.",
|
|
295
|
-
"font-glyph-fallback": "The chosen font lacks these characters; a bundled font draws them. Supply a font with --
|
|
375
|
+
"font-glyph-fallback": "The chosen font lacks these characters; a bundled font draws them. Supply a font with --fonts to change that.",
|
|
296
376
|
"unresolved-reference": "Use an id from opf catalog <kind>, define the record in catalogs.custom, or run opf embed to embed what the deck references.",
|
|
297
377
|
"unresolved-content": "Check the content at this path; it could not be drawn.",
|
|
298
378
|
"chart-data-adapted": "The chart data was reshaped to export a native chart; see the message.",
|
|
@@ -300,9 +380,9 @@ var HELP = {
|
|
|
300
380
|
"content-placeholder": "This content has no PowerPoint form; a placeholder frame stands in for it.",
|
|
301
381
|
"unsupported-image-treatment": "Remove or change the image treatment at this path.",
|
|
302
382
|
"date-needs-value": "Pass --date YYYY-MM-DD. The CLI never reads a clock, so output stays reproducible.",
|
|
303
|
-
"font-unavailable": "Pass --
|
|
304
|
-
"missing-glyph": "Install the script font package named by script-font-not-installed, or supply a covering font with --
|
|
305
|
-
"duplicate-font-face": "A --
|
|
383
|
+
"font-unavailable": "Pass --fonts with the font files, or choose a font the bundled pack covers (opf catalog fontSchemes).",
|
|
384
|
+
"missing-glyph": "Install the script font package named by script-font-not-installed, or supply a covering font with --fonts.",
|
|
385
|
+
"duplicate-font-face": "A --fonts face repeats a bundled family, weight and style; remove it or rename the family."
|
|
306
386
|
};
|
|
307
387
|
var Reporter = class {
|
|
308
388
|
findings = [];
|
|
@@ -415,92 +495,10 @@ function createZip(entries) {
|
|
|
415
495
|
end.setUint32(16, offset, true);
|
|
416
496
|
return Buffer.concat([...chunks, ...central, new Uint8Array(end.buffer)]);
|
|
417
497
|
}
|
|
418
|
-
var MEDIA = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".svg": "image/svg+xml" };
|
|
419
|
-
var MAX_BYTES = 32 * 1024 * 1024;
|
|
420
|
-
var startsWith = (bytes, signature, offset = 0) => signature.every((value, index) => bytes[offset + index] === value);
|
|
421
|
-
function looksLike(type, bytes) {
|
|
422
|
-
switch (type) {
|
|
423
|
-
case "image/png":
|
|
424
|
-
return startsWith(bytes, [137, 80, 78, 71]);
|
|
425
|
-
case "image/jpeg":
|
|
426
|
-
return startsWith(bytes, [255, 216, 255]);
|
|
427
|
-
case "image/gif":
|
|
428
|
-
return startsWith(bytes, [71, 73, 70, 56]);
|
|
429
|
-
case "image/webp":
|
|
430
|
-
return startsWith(bytes, [82, 73, 70, 70]) && startsWith(bytes, [87, 69, 66, 80], 8);
|
|
431
|
-
default:
|
|
432
|
-
return /<svg[\s>]/i.test(new TextDecoder().decode(bytes.subarray(0, 4096)));
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
var AssetError = class extends Error {
|
|
436
|
-
constructor(message, details) {
|
|
437
|
-
super(message);
|
|
438
|
-
this.details = details;
|
|
439
|
-
}
|
|
440
|
-
details;
|
|
441
|
-
code = "asset-unresolved";
|
|
442
|
-
};
|
|
443
|
-
function createImageResolver(root, reporter, passDirectory = "pass --asset-dir") {
|
|
444
|
-
const base = root === void 0 ? void 0 : realpathSync(root);
|
|
445
|
-
const cache2 = /* @__PURE__ */ new Map();
|
|
446
|
-
const shown = (source) => source.length > 80 ? `${source.slice(0, 77)}...` : source;
|
|
447
|
-
function load(source) {
|
|
448
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(source) && !/^file:/i.test(source) && !/^[a-z]:[\\/]/i.test(source)) return { skipped: true };
|
|
449
|
-
if (base === void 0) return { problem: "blocked", reason: "no asset directory was given, so no local file is read." };
|
|
450
|
-
let file;
|
|
451
|
-
try {
|
|
452
|
-
file = /^file:/i.test(source) ? fileURLToPath(source) : path6.resolve(base, source);
|
|
453
|
-
} catch {
|
|
454
|
-
return { problem: "blocked", reason: "the path is not valid." };
|
|
455
|
-
}
|
|
456
|
-
let real;
|
|
457
|
-
try {
|
|
458
|
-
real = realpathSync(file);
|
|
459
|
-
} catch {
|
|
460
|
-
return { problem: "missing", reason: `no such file under ${base}.` };
|
|
461
|
-
}
|
|
462
|
-
const relative = path6.relative(base, real);
|
|
463
|
-
if (relative.startsWith("..") || path6.isAbsolute(relative)) return { problem: "blocked", reason: `it is outside the asset directory ${base}.` };
|
|
464
|
-
const mediaType = MEDIA[path6.extname(real).toLowerCase()];
|
|
465
|
-
if (!mediaType) return { problem: "blocked", reason: "only .png, .jpg, .jpeg, .gif, .webp and .svg files are read." };
|
|
466
|
-
const info = statSync(real);
|
|
467
|
-
if (!info.isFile() || info.size > MAX_BYTES) return { problem: "blocked", reason: `it is not a regular file under ${MAX_BYTES / 1024 / 1024} MB.` };
|
|
468
|
-
const bytes = new Uint8Array(readFileSync(real));
|
|
469
|
-
if (!looksLike(mediaType, bytes)) return { problem: "blocked", reason: `the content is not a ${mediaType} image.` };
|
|
470
|
-
return { image: { bytes, mediaType } };
|
|
471
|
-
}
|
|
472
|
-
function read(source) {
|
|
473
|
-
let outcome = cache2.get(source);
|
|
474
|
-
if (!outcome) {
|
|
475
|
-
outcome = load(source);
|
|
476
|
-
cache2.set(source, outcome);
|
|
477
|
-
}
|
|
478
|
-
return outcome;
|
|
479
|
-
}
|
|
480
|
-
return {
|
|
481
|
-
/** opf-render: a data URI string, or null for the placeholder (a missing file is reported by the renderer as unresolved-asset). */
|
|
482
|
-
forSvg: (source, context) => {
|
|
483
|
-
if (!source) return null;
|
|
484
|
-
const outcome = read(source);
|
|
485
|
-
if ("image" in outcome) return `data:${outcome.image.mediaType};base64,${Buffer.from(outcome.image.bytes).toString("base64")}`;
|
|
486
|
-
if ("problem" in outcome && outcome.problem === "blocked") reporter.add("cli", { code: "asset-blocked", path: context.path, message: `Image "${shown(source)}" was not read: ${outcome.reason}` });
|
|
487
|
-
return null;
|
|
488
|
-
},
|
|
489
|
-
/** opf-pptx: bytes and media type; null for URLs and references it handles itself. An unreadable local file stops the export. */
|
|
490
|
-
forPptx: (source, context) => {
|
|
491
|
-
const outcome = read(source);
|
|
492
|
-
if ("image" in outcome) return { data: outcome.image.bytes, mediaType: outcome.image.mediaType };
|
|
493
|
-
if ("skipped" in outcome) return null;
|
|
494
|
-
throw new AssetError(`Image "${shown(source)}" was not read: ${outcome.reason} Fix the path, embed the image as a data URI, or ${passDirectory}.`, { path: context.path });
|
|
495
|
-
}
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// src/node/catalogs.ts
|
|
500
|
-
var DEFAULT_CATALOGS = [defaultCatalog];
|
|
501
498
|
|
|
502
499
|
// src/node/export.ts
|
|
503
500
|
var EXPORT_FORMATS = ["svg", "png", "pdf", "pptx"];
|
|
501
|
+
var EXPORT_TEXT = ["fonts", "system", "paths"];
|
|
504
502
|
var NO_FONTS = { fontFiles: [], useBundledFonts: false, loadSystemFonts: false };
|
|
505
503
|
var invalid = (message) => new OPFApiError(message, "invalid-option");
|
|
506
504
|
var oneOf = (name, value, allowed) => {
|
|
@@ -521,27 +519,42 @@ function checkScale(value, label = "scale") {
|
|
|
521
519
|
if (typeof value === "boolean" || !Number.isFinite(scale) || scale < 0.1 || scale > 8) throw invalid(`${label} must be a number from 0.1 to 8 (1 draws the 1280 x 720 reference slide at 1280 x 720 pixels).`);
|
|
522
520
|
return scale;
|
|
523
521
|
}
|
|
522
|
+
var RENAMED = {
|
|
523
|
+
svgFonts: 'text ("fonts", "system" or "paths")',
|
|
524
|
+
pdfMode: "raster (true for a picture PDF)",
|
|
525
|
+
chartex: 'charts ("auto", "native" or "picture")',
|
|
526
|
+
imageFormat: "images",
|
|
527
|
+
fontDirs: "fonts (a folder or a list of folders)",
|
|
528
|
+
filename: "name"
|
|
529
|
+
};
|
|
530
|
+
function checkRenamedOptions(options) {
|
|
531
|
+
for (const [old, replacement] of Object.entries(RENAMED)) if (old in options) throw invalid(`${old} was renamed ${replacement} in 0.18.`);
|
|
532
|
+
}
|
|
533
|
+
var isHandle = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
524
534
|
function resolveExportOptions(options) {
|
|
525
535
|
if (!options || typeof options !== "object") throw invalid("An export needs options with a format: svg, png, pdf or pptx.");
|
|
536
|
+
checkRenamedOptions(options);
|
|
526
537
|
const format = oneOf("format", options.format, EXPORT_FORMATS);
|
|
527
538
|
if (!format) throw invalid("An export needs options.format: svg, png, pdf or pptx.");
|
|
528
|
-
|
|
529
|
-
const
|
|
539
|
+
if (options.raster !== void 0 && typeof options.raster !== "boolean") throw invalid("raster must be true or false.");
|
|
540
|
+
const charts = oneOf("charts", options.charts, ["auto", "native", "picture"]);
|
|
530
541
|
const provenance = oneOf("provenance", options.provenance, ["full", "references-only", "none"]);
|
|
531
|
-
const
|
|
532
|
-
const
|
|
533
|
-
if (
|
|
534
|
-
if ((
|
|
542
|
+
const images = oneOf("images", options.images, ["compatible", "preserve"]);
|
|
543
|
+
const text = oneOf("text", options.text, EXPORT_TEXT);
|
|
544
|
+
if (options.raster && format !== "pdf") throw invalid("raster applies to format pdf.");
|
|
545
|
+
if ((charts !== void 0 || provenance !== void 0 || images !== void 0) && format !== "pptx") throw invalid("charts, provenance and images apply to format pptx.");
|
|
535
546
|
if (options.scale !== void 0 && format !== "png" && format !== "pdf") throw invalid("scale applies to format png (and raster pdf).");
|
|
536
547
|
if (options.includeHidden && format === "pptx") throw invalid("includeHidden applies to per-slide image and PDF output; the PPTX keeps a hidden slide as a hidden slide.");
|
|
537
548
|
if (options.slides !== void 0 && format === "pptx") throw invalid("slides is not available for pptx: the whole presentation is exported.");
|
|
538
|
-
if (
|
|
549
|
+
if (text !== void 0 && format !== "svg") throw invalid("text applies to format svg.");
|
|
539
550
|
if (options.zip && format !== "svg" && format !== "png") throw invalid("zip applies to format svg and png.");
|
|
540
|
-
if (options.slides !== void 0 && typeof options.slides !== "string" && !(Array.isArray(options.slides) && options.slides.every((n) => Number.isInteger(n) && n >= 1))) throw invalid('slides must be a
|
|
551
|
+
if (options.slides !== void 0 && typeof options.slides !== "string" && !(typeof options.slides === "number" && Number.isInteger(options.slides) && options.slides >= 1) && !(Array.isArray(options.slides) && options.slides.every((n) => Number.isInteger(n) && n >= 1))) throw invalid('slides must be a slide number, an array of slide numbers counted from 1, or a selection such as "1,3-5".');
|
|
541
552
|
const scale = checkScale(options.scale);
|
|
542
553
|
checkDate(options.date);
|
|
543
|
-
|
|
544
|
-
|
|
554
|
+
const { fonts, ...rest } = options;
|
|
555
|
+
if (fonts !== void 0 && typeof fonts !== "string" && !isHandle(fonts) && !(Array.isArray(fonts) && fonts.every((folder) => typeof folder === "string"))) throw invalid("fonts must be a fonts handle (loadFonts() of @openpresentation/opf-render/fonts-node), a folder of font files or a list of folders.");
|
|
556
|
+
const folders = typeof fonts === "string" ? [fonts] : Array.isArray(fonts) ? [...fonts] : [];
|
|
557
|
+
return { ...rest, format, text: text ?? "fonts", scale, ...isHandle(fonts) ? { fonts } : {}, fontFolders: folders };
|
|
545
558
|
}
|
|
546
559
|
function assetRoot(directory, flags = false) {
|
|
547
560
|
if (directory === void 0) return void 0;
|
|
@@ -568,9 +581,9 @@ async function runExport(presentation, options, ctx) {
|
|
|
568
581
|
const date = checkDate(options.date);
|
|
569
582
|
const renderer = ctx.renderer ?? await loadRenderer();
|
|
570
583
|
const pptx = format === "pptx" ? ctx.pptx ?? await loadPptx() : void 0;
|
|
571
|
-
const userFonts = options.fonts ? [] : ctx.userFonts ?? await listFontDirectories(
|
|
584
|
+
const userFonts = options.fonts ? [] : ctx.userFonts ?? await listFontDirectories(options.fontFolders, ctx.flags ? "--fonts" : "fonts");
|
|
572
585
|
const resolver = createImageResolver(assetRoot(options.assetDir, ctx.flags), reporter, ctx.flags ? "pass --asset-dir" : "pass assetDir");
|
|
573
|
-
const embedScripts = format === "svg" && options.
|
|
586
|
+
const embedScripts = format === "svg" && options.text === "fonts";
|
|
574
587
|
const lease = options.fonts ? void 0 : await leaseSharedFonts(renderer, presentation, userFonts, reporter, embedScripts);
|
|
575
588
|
try {
|
|
576
589
|
const handle = options.fonts ?? lease?.handle ?? (await prepareFonts(renderer, presentation, userFonts, reporter, embedScripts)).handle;
|
|
@@ -605,12 +618,12 @@ async function drawWith(presentation, handle, options, ctx, environment) {
|
|
|
605
618
|
const isHidden = (number) => deck.slides?.[number - 1]?.hidden === true;
|
|
606
619
|
const everySlide = Array.from({ length: slideCount }, (_, index) => index + 1);
|
|
607
620
|
const slidesLabel = ctx.flags ? "--slides" : "slides";
|
|
608
|
-
const named =
|
|
621
|
+
const named = options.slides === void 0 ? void 0 : parseSlideSelection(options.slides, slideCount, slidesLabel);
|
|
609
622
|
const selected = format === "pptx" ? [] : named ?? (options.includeHidden ? everySlide : everySlide.filter((number) => !isHidden(number)));
|
|
610
623
|
const skippedHidden = format === "pptx" || named || options.includeHidden ? [] : everySlide.filter(isHidden);
|
|
611
624
|
if (format !== "pptx" && !selected.length)
|
|
612
625
|
throw new OPFApiError(skippedHidden.length ? ctx.flags ? "Every slide is hidden. Use --include-hidden to write them, or --slides to name slides." : "Every slide is hidden. Use includeHidden to write them, or slides to name slides." : "The presentation has no slides.", skippedHidden.length ? "all-slides-hidden" : "no-slides");
|
|
613
|
-
const stem = deckStem(deck
|
|
626
|
+
const stem = deckStem(options.name === void 0 ? deck : { filename: options.name }, "-");
|
|
614
627
|
const run = (extra) => ({ fonts: fontSummary(), ...pagination ? { pagination } : {}, skippedHidden, ...extra });
|
|
615
628
|
try {
|
|
616
629
|
if (format === "pptx") {
|
|
@@ -620,19 +633,21 @@ async function drawWith(presentation, handle, options, ctx, environment) {
|
|
|
620
633
|
imageResolver: resolver.forPptx,
|
|
621
634
|
// The PNG fallback of an SVG picture is drawn by the same renderer the preview uses, with the same fonts handle,
|
|
622
635
|
// so SVG pictures export whichever way opf-pptx would find opf-render. A picture without text needs no faces.
|
|
623
|
-
svgRasterizer: async (svg, size) => new Uint8Array(await renderer.render.module.
|
|
636
|
+
svgRasterizer: async (svg, size) => new Uint8Array(await renderer.render.module.toPng(svg, { scale: size.scale, background: "rgba(0, 0, 0, 0)", fonts: size.text ? handle : NO_FONTS })),
|
|
624
637
|
onDiagnostic: (diagnostic) => reporter.add("pptx", diagnostic),
|
|
625
638
|
catalogs,
|
|
626
|
-
|
|
639
|
+
// opf-pptx still names its option `chartex` and its picture choice `fallback`.
|
|
640
|
+
...options.charts ? { chartex: options.charts === "picture" ? "fallback" : options.charts } : {},
|
|
627
641
|
...options.provenance ? { provenance: options.provenance === "none" ? false : options.provenance } : {},
|
|
628
|
-
...options.
|
|
642
|
+
...options.images ? { imageFormat: options.images } : {},
|
|
629
643
|
...date ? { date } : {}
|
|
630
644
|
});
|
|
631
645
|
return run({ files: [{ name: `${stem}.pptx`, type: MEDIA2.pptx, bytes }], skippedHidden: [] });
|
|
632
646
|
}
|
|
633
|
-
const embedded = format === "svg" ? embeddedFor(handle, options.
|
|
647
|
+
const embedded = format === "svg" ? embeddedFor(handle, options.text) : [];
|
|
634
648
|
const svgOptions = {
|
|
635
|
-
fonts: { textMeasurement: handle.textMeasurement, embeddedFonts: embedded },
|
|
649
|
+
fonts: options.text === "paths" && format === "svg" ? handle : { textMeasurement: handle.textMeasurement, embeddedFonts: embedded },
|
|
650
|
+
...format === "svg" && options.text !== "fonts" ? { text: options.text } : {},
|
|
636
651
|
imageResolver: resolver.forSvg,
|
|
637
652
|
onDiagnostic: (diagnostic) => reporter.add("render", diagnostic),
|
|
638
653
|
catalogs,
|
|
@@ -640,17 +655,17 @@ async function drawWith(presentation, handle, options, ctx, environment) {
|
|
|
640
655
|
};
|
|
641
656
|
const svgs = [];
|
|
642
657
|
for (const number of selected) {
|
|
643
|
-
const svg = renderer.render.module.
|
|
658
|
+
const svg = renderer.render.module.toSvg(deck, number, svgOptions);
|
|
644
659
|
svgs.push({ slide: number, id: deck.slides[number - 1]?.id, svg });
|
|
645
660
|
}
|
|
646
661
|
const width = Math.max(3, String(slideCount).length);
|
|
647
662
|
const name = (slide, extension) => `${stem}-${String(slide).padStart(width, "0")}.${extension}`;
|
|
648
663
|
if (format === "pdf") {
|
|
649
|
-
const mode = options.
|
|
664
|
+
const mode = options.raster ? "raster" : "vector";
|
|
650
665
|
const title = typeof deck.name === "string" ? deck.name : void 0;
|
|
651
|
-
const bytes = await renderer.render.module.
|
|
666
|
+
const bytes = await renderer.render.module.toPdf(
|
|
652
667
|
svgs.map((item) => item.svg),
|
|
653
|
-
{ fonts: handle, scale, ...options.
|
|
668
|
+
{ fonts: handle, scale, ...options.raster ? { raster: true } : {}, ...mode === "vector" && title ? { metadata: { title } } : {}, onDiagnostic: (diagnostic) => reporter.add("pdf", diagnostic) }
|
|
654
669
|
);
|
|
655
670
|
return run({ files: [{ name: `${stem}.pdf`, type: MEDIA2.pdf, bytes, pages: svgs.length, slides: selected }], pdf: { mode } });
|
|
656
671
|
}
|
|
@@ -660,7 +675,7 @@ async function drawWith(presentation, handle, options, ctx, environment) {
|
|
|
660
675
|
const bytes = new TextEncoder().encode(item.svg);
|
|
661
676
|
items.push({ name: name(item.slide, "svg"), type: MEDIA2.svg, bytes, slide: item.slide, ...item.id === void 0 ? {} : { id: item.id }, ...svgSize(item.svg) });
|
|
662
677
|
} else {
|
|
663
|
-
const bytes = await renderer.render.module.
|
|
678
|
+
const bytes = await renderer.render.module.toPng(item.svg, { fonts: handle, scale });
|
|
664
679
|
items.push({ name: name(item.slide, "png"), type: MEDIA2.png, bytes, slide: item.slide, ...item.id === void 0 ? {} : { id: item.id }, ...pngSize(bytes) });
|
|
665
680
|
}
|
|
666
681
|
}
|
|
@@ -713,7 +728,7 @@ async function prepareExport(options, flags = false) {
|
|
|
713
728
|
const resolved = resolveExportOptions(options);
|
|
714
729
|
const renderer = await loadRenderer();
|
|
715
730
|
const pptx = resolved.format === "pptx" ? await loadPptx() : void 0;
|
|
716
|
-
const userFonts = resolved.fonts ? [] : await listFontDirectories(
|
|
731
|
+
const userFonts = resolved.fonts ? [] : await listFontDirectories(resolved.fontFolders, flags ? "--fonts" : "fonts");
|
|
717
732
|
return { options: resolved, renderer, ...pptx ? { pptx } : {}, userFonts, flags };
|
|
718
733
|
}
|
|
719
734
|
async function runPreparedExport(prepared, presentation, checked) {
|
|
@@ -747,6 +762,8 @@ async function importPresentation(bytes, options, loaded) {
|
|
|
747
762
|
if (!check.valid) throw new OPFApiError(`The imported presentation is not valid OPF: ${firstError(check.findings)}`, "invalid-presentation", { findings: reporter.findings });
|
|
748
763
|
return { result: { presentation: run.presentation, findings: reporter.findings, ...run.signals ? { signals: run.signals } : {}, pptx: { package: PPTX_PACKAGE, version: pptx.version } }, check };
|
|
749
764
|
}
|
|
765
|
+
|
|
766
|
+
// src/node/conversion.ts
|
|
750
767
|
var EXPORT_EXTENSIONS = { ".pdf": "pdf", ".pptx": "pptx", ".png": "png", ".svg": "svg" };
|
|
751
768
|
var FORMATS = ["pdf", "pptx", "png", "svg", "json", "yaml", "markdown"];
|
|
752
769
|
var DECK_MEDIA = { json: "application/json", yaml: "application/yaml", markdown: "text/markdown" };
|
|
@@ -787,11 +804,12 @@ function targetOf(output, options, flags) {
|
|
|
787
804
|
return format === "json" || format === "yaml" || format === "markdown" ? { kind: "deck", format } : { kind: "export", format, zip: options.zip === true };
|
|
788
805
|
}
|
|
789
806
|
if (typeof output !== "string" || !output) throw invalid2(`${flags ? "opf convert" : "convert"} needs an output file path ending ${OUTPUTS}.`);
|
|
807
|
+
if (options.zip) throw invalid2(`${flags ? "--zip" : "zip"} is for output without a path; a .zip output path makes the archive.`);
|
|
808
|
+
if (options.name !== void 0) throw invalid2(`${flags ? "--name" : "name"} is for output without a path; the output path names the file.`);
|
|
790
809
|
if (path6.extname(output).toLowerCase() === ".zip") {
|
|
791
810
|
if (format !== void 0 && format !== "png" && format !== "svg") throw invalid2(`A .zip output holds png or svg slides; ${formatLabel} ${format} is neither.`);
|
|
792
811
|
return { kind: "export", format: format ?? "png", zip: true };
|
|
793
812
|
}
|
|
794
|
-
if (options.zip) throw invalid2(`${flags ? "--zip" : "zip"} writes one archive: give an output ending .zip.`);
|
|
795
813
|
const exported = exportFormatOf(output);
|
|
796
814
|
const deck = exported ? void 0 : deckFileFormat(output);
|
|
797
815
|
const named = exported ?? deck;
|
|
@@ -799,10 +817,10 @@ function targetOf(output, options, flags) {
|
|
|
799
817
|
if (format !== void 0 && format !== named) throw invalid2(`${formatLabel} ${format} does not match the output ${output}, which is ${named}.`);
|
|
800
818
|
return exported ? { kind: "export", format: exported, zip: false } : { kind: "deck", format: deck };
|
|
801
819
|
}
|
|
802
|
-
var EXPORT_ONLY = ["slides", "includeHidden", "paginate", "scale", "
|
|
820
|
+
var EXPORT_ONLY = ["slides", "includeHidden", "paginate", "scale", "raster", "text", "charts", "provenance", "images", "date", "fonts", "assetDir"];
|
|
803
821
|
var given = (value) => value !== void 0 && value !== false && !(Array.isArray(value) && value.length === 0);
|
|
804
822
|
function checkApplicable(source, target, options, flags) {
|
|
805
|
-
const name = (key) => flags ? `--${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
|
|
823
|
+
const name = (key) => flags ? `--${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}` : key;
|
|
806
824
|
const names = (...keys) => keys.map(name).join(", ").replace(/, ([^,]*)$/, " and $1");
|
|
807
825
|
if (given(options.signals) && source.kind !== "pptx") throw invalid2(`${name("signals")} applies to a .pptx input.`);
|
|
808
826
|
if (target.kind === "deck") {
|
|
@@ -811,12 +829,12 @@ function checkApplicable(source, target, options, flags) {
|
|
|
811
829
|
return;
|
|
812
830
|
}
|
|
813
831
|
const format = target.format;
|
|
814
|
-
if (options.
|
|
815
|
-
if ((options.
|
|
832
|
+
if (given(options.raster) && format !== "pdf") throw invalid2(`${name("raster")} applies to pdf output.`);
|
|
833
|
+
if ((options.charts !== void 0 || options.provenance !== void 0 || options.images !== void 0) && format !== "pptx") throw invalid2(`${names("charts", "provenance", "images")} apply to pptx output.`);
|
|
816
834
|
if (options.scale !== void 0 && format !== "png" && format !== "pdf") throw invalid2(`${name("scale")} applies to png output (and raster pdf).`);
|
|
817
835
|
if (given(options.includeHidden) && format === "pptx") throw invalid2(`${name("includeHidden")} applies to per-slide image and PDF output; the PPTX keeps a hidden slide as a hidden slide.`);
|
|
818
836
|
if (options.slides !== void 0 && format === "pptx") throw invalid2(`${name("slides")} is not available for pptx output: the whole presentation is exported.`);
|
|
819
|
-
if (options.
|
|
837
|
+
if (options.text !== void 0 && format !== "svg") throw invalid2(`${name("text")} applies to svg output.`);
|
|
820
838
|
checkScale(options.scale, name("scale"));
|
|
821
839
|
checkDate(options.date, name("date"));
|
|
822
840
|
}
|
|
@@ -852,6 +870,7 @@ function stemOfDeck(presentation) {
|
|
|
852
870
|
}
|
|
853
871
|
async function planConversion(input, output, options = {}, flags = false) {
|
|
854
872
|
if (!options || typeof options !== "object") throw invalid2("convert takes options as an object.");
|
|
873
|
+
checkRenamedOptions(options);
|
|
855
874
|
const source = sourceOf(input, flags);
|
|
856
875
|
const target = targetOf(output, options, flags);
|
|
857
876
|
checkApplicable(source, target, options, flags);
|
|
@@ -860,14 +879,12 @@ async function planConversion(input, output, options = {}, flags = false) {
|
|
|
860
879
|
const inputFile = typeof input === "string" ? input : void 0;
|
|
861
880
|
let prepared;
|
|
862
881
|
if (target.kind === "export") {
|
|
863
|
-
const { format: _format, zip: _zip, signals: _signals, overwrite: _overwrite, ...rest } = options;
|
|
882
|
+
const { format: _format, zip: _zip, name: _name, signals: _signals, overwrite: _overwrite, ...rest } = options;
|
|
864
883
|
const assetDir = options.assetDir ?? (inputFile === void 0 ? void 0 : path6.dirname(path6.resolve(inputFile)));
|
|
865
884
|
const exportOptions = {
|
|
866
885
|
...rest,
|
|
867
886
|
format: target.format,
|
|
868
887
|
...assetDir === void 0 ? {} : { assetDir },
|
|
869
|
-
// Without an output path the files are named as the commands name them: by the deck, else by the input file.
|
|
870
|
-
...output === void 0 && inputFile !== void 0 ? { filename: fileStem(inputFile) } : {},
|
|
871
888
|
...output === void 0 && target.zip ? { zip: true } : {}
|
|
872
889
|
};
|
|
873
890
|
prepared = await inStep(OPFExportError, "export-failed", () => prepareExport(exportOptions, flags));
|
|
@@ -897,9 +914,10 @@ async function planConversion(input, output, options = {}, flags = false) {
|
|
|
897
914
|
if (target.kind === "deck") {
|
|
898
915
|
const bytes = new TextEncoder().encode(deckText(presentation, target.format));
|
|
899
916
|
const extension = `opf.${target.format === "markdown" ? "md" : target.format}`;
|
|
900
|
-
const name = output === void 0 ? `${inputFile === void 0 ? stemOfDeck(presentation) : fileStem(inputFile)}.${extension}` : path6.basename(output);
|
|
917
|
+
const name = output === void 0 ? `${options.name ?? (inputFile === void 0 ? stemOfDeck(presentation) : fileStem(inputFile))}.${extension}` : path6.basename(output);
|
|
901
918
|
return { ...base, files: [{ name, ...output === void 0 ? {} : { path: output }, type: DECK_MEDIA[target.format], bytes }], findings };
|
|
902
919
|
}
|
|
920
|
+
if (output === void 0 && prepared) prepared.options = { ...prepared.options, name: options.name ?? deckStem(presentation, inputFile === void 0 ? "-" : fileStem(inputFile)) };
|
|
903
921
|
const exported = await inStep(OPFExportError, "export-failed", () => runPreparedExport(prepared, presentation, findings));
|
|
904
922
|
const drawn = exported.files;
|
|
905
923
|
const facts = (file) => ({
|
|
@@ -970,4 +988,4 @@ async function saveDeck(presentation, file, options = {}) {
|
|
|
970
988
|
return { path: file, format };
|
|
971
989
|
}
|
|
972
990
|
|
|
973
|
-
export {
|
|
991
|
+
export { DEFAULT_CATALOGS, EXPORT_FORMATS, EXPORT_TEXT, PEER_RANGES, PPTX_PACKAGE, RENDER_EXTRAS, RENDER_PACKAGE, Reporter, VERSIONS, checkDate, checkDestinations, checkOptions, checkRenamedOptions, checkScale, convertFiles, createZip, deckFileFormat, deckStem, embeddedFor, exportFormatOf, finishReport, importPresentation, leaseSharedFonts, listFontDirectories, loadPptx, loadRenderer, locatePackage, missingPeerFrom, openDeck, planConversion, pointerOf, prepareExport, prepareFonts, reaches, readInput, reportThrown, resolveExportOptions, runExport, runImport, runPreparedExport, saveDeck, stemOf, writeFiles, writePlanned };
|