document-cli 1.0.1 → 1.2.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 +50 -8
- package/dist/cli.js +223 -24
- package/dist/index.cjs +520 -20
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +522 -22
- package/dist/odb-structure-CX_0zL8_.js +382 -0
- package/dist/{tui-CucYgqmL.js → tui-DD4bQp39.js} +644 -74
- package/package.json +3 -3
- package/dist/io-Cl3MaB0L.js +0 -81
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { OdbNoEmbeddedDataSourceError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, createLocalDocumentConverter, odbToCsv, odbToXlsx, odmToPdf, readOdbTables, readPdf } from "documents.js";
|
|
2
|
+
import { OdbNoEmbeddedDataSourceError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, createLocalDocumentConverter, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, odbToCsv, odbToXlsx, odmToPdf, readOdbForms, readOdbReports, readOdbTables, readPdf, writePdf } from "documents.js";
|
|
3
3
|
import { basename, dirname, extname, join } from "node:path";
|
|
4
4
|
import { Command, InvalidArgumentError } from "commander";
|
|
5
|
-
import { decodePackage } from "odf.js";
|
|
5
|
+
import { decodePackage, encodePackage as encodePackage$1 } from "odf.js";
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
//#region src/format.ts
|
|
8
8
|
const EXTENSION_TO_FORMAT = {
|
|
@@ -14,6 +14,8 @@ const EXTENSION_TO_FORMAT = {
|
|
|
14
14
|
ods: "ods",
|
|
15
15
|
odg: "odg",
|
|
16
16
|
odf: "odf",
|
|
17
|
+
markdown: "markdown",
|
|
18
|
+
md: "markdown",
|
|
17
19
|
pdf: "pdf"
|
|
18
20
|
};
|
|
19
21
|
const FORMAT_TO_EXTENSION = {
|
|
@@ -25,6 +27,7 @@ const FORMAT_TO_EXTENSION = {
|
|
|
25
27
|
ods: "ods",
|
|
26
28
|
odg: "odg",
|
|
27
29
|
odf: "odf",
|
|
30
|
+
markdown: "md",
|
|
28
31
|
pdf: "pdf"
|
|
29
32
|
};
|
|
30
33
|
function isDocumentFormat(value) {
|
|
@@ -108,6 +111,22 @@ function createDiagnosticReporter(options) {
|
|
|
108
111
|
}
|
|
109
112
|
};
|
|
110
113
|
}
|
|
114
|
+
function createFontSubstitutionReporter(options) {
|
|
115
|
+
const { json, quiet, command } = options;
|
|
116
|
+
return (substitution) => {
|
|
117
|
+
if (quiet) return;
|
|
118
|
+
if (json) {
|
|
119
|
+
process.stderr.write(`${JSON.stringify({
|
|
120
|
+
type: "font-substitution",
|
|
121
|
+
command,
|
|
122
|
+
...substitution
|
|
123
|
+
})}\n`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const styleClause = `${substitution.requestedBold ? " bold" : ""}${substitution.requestedItalic ? " italic" : ""}`;
|
|
127
|
+
process.stderr.write(`[${command}] font substitution: "${substitution.requestedFamily}"${styleClause} -> "${substitution.resolvedFamily}" (${substitution.reason})\n`);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
111
130
|
function substitutionToDiagnostic(substitution, pageIndex) {
|
|
112
131
|
return {
|
|
113
132
|
severity: "warning",
|
|
@@ -116,6 +135,13 @@ function substitutionToDiagnostic(substitution, pageIndex) {
|
|
|
116
135
|
pageIndex
|
|
117
136
|
};
|
|
118
137
|
}
|
|
138
|
+
function fontSubstitutionToDiagnostic(substitution) {
|
|
139
|
+
return {
|
|
140
|
+
severity: "info",
|
|
141
|
+
code: "font/substituted",
|
|
142
|
+
message: `"${`${substitution.requestedFamily}${substitution.requestedBold ? " bold" : ""}${substitution.requestedItalic ? " italic" : ""}`}" is not available; ${substitution.reason === "vendored-substitute" ? `substituted the metric-compatible "${substitution.resolvedFamily}"` : `substituted another face of "${substitution.resolvedFamily}"`}`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
119
145
|
function pdfDiagnosticToDiagnostic(diagnostic) {
|
|
120
146
|
return {
|
|
121
147
|
severity: diagnostic.severity,
|
|
@@ -140,6 +166,172 @@ function mapErrorToExit(error, abortReason) {
|
|
|
140
166
|
return 1;
|
|
141
167
|
}
|
|
142
168
|
//#endregion
|
|
169
|
+
//#region src/runtime/font-face.ts
|
|
170
|
+
var FontFaceError = class extends Error {
|
|
171
|
+
constructor(message) {
|
|
172
|
+
super(message);
|
|
173
|
+
this.name = "FontFaceError";
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const SFNT_VERSION_TRUETYPE = 65536;
|
|
177
|
+
const SFNT_VERSION_CFF = 1330926671;
|
|
178
|
+
const SFNT_VERSION_APPLE_TRUE = 1953658213;
|
|
179
|
+
const SFNT_VERSION_APPLE_TYP1 = 1954115633;
|
|
180
|
+
const SFNT_VERSION_COLLECTION = 1953784678;
|
|
181
|
+
const SFNT_VERSIONS = /* @__PURE__ */ new Set([
|
|
182
|
+
SFNT_VERSION_TRUETYPE,
|
|
183
|
+
SFNT_VERSION_CFF,
|
|
184
|
+
SFNT_VERSION_APPLE_TRUE,
|
|
185
|
+
SFNT_VERSION_APPLE_TYP1
|
|
186
|
+
]);
|
|
187
|
+
const TABLE_DIRECTORY_HEADER_SIZE = 12;
|
|
188
|
+
const TABLE_RECORD_SIZE = 16;
|
|
189
|
+
const TABLE_TAG_SIZE = 4;
|
|
190
|
+
const NAME_HEADER_SIZE = 6;
|
|
191
|
+
const NAME_RECORD_SIZE = 12;
|
|
192
|
+
const NAME_ID_FAMILY = 1;
|
|
193
|
+
const NAME_ID_TYPOGRAPHIC_FAMILY = 16;
|
|
194
|
+
const PLATFORM_UNICODE = 0;
|
|
195
|
+
const PLATFORM_MACINTOSH = 1;
|
|
196
|
+
const PLATFORM_WINDOWS = 3;
|
|
197
|
+
const MACINTOSH_ENCODING_ROMAN = 0;
|
|
198
|
+
const OS2_FS_SELECTION_OFFSET = 62;
|
|
199
|
+
const OS2_MINIMUM_SIZE = 64;
|
|
200
|
+
const OS2_FS_SELECTION_ITALIC = 1;
|
|
201
|
+
const OS2_FS_SELECTION_BOLD = 32;
|
|
202
|
+
const HEAD_MAC_STYLE_OFFSET = 44;
|
|
203
|
+
const HEAD_MINIMUM_SIZE = 46;
|
|
204
|
+
const HEAD_MAC_STYLE_BOLD = 1;
|
|
205
|
+
const HEAD_MAC_STYLE_ITALIC = 2;
|
|
206
|
+
function viewOf(bytes) {
|
|
207
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
208
|
+
}
|
|
209
|
+
function decodeTag(view, offset) {
|
|
210
|
+
let tag = "";
|
|
211
|
+
for (let i = 0; i < TABLE_TAG_SIZE; i++) tag += String.fromCharCode(view.getUint8(offset + i));
|
|
212
|
+
return tag;
|
|
213
|
+
}
|
|
214
|
+
function decodeUtf16Be(view, offset, length) {
|
|
215
|
+
let text = "";
|
|
216
|
+
for (let i = 0; i + 1 < length; i += 2) text += String.fromCharCode(view.getUint16(offset + i));
|
|
217
|
+
return text;
|
|
218
|
+
}
|
|
219
|
+
function decodeMacintoshAscii(view, offset, length) {
|
|
220
|
+
let text = "";
|
|
221
|
+
for (let i = 0; i < length; i++) {
|
|
222
|
+
const byte = view.getUint8(offset + i);
|
|
223
|
+
if (byte > 126) return;
|
|
224
|
+
text += String.fromCharCode(byte);
|
|
225
|
+
}
|
|
226
|
+
return text;
|
|
227
|
+
}
|
|
228
|
+
function readTableDirectory(view, source) {
|
|
229
|
+
if (view.byteLength < TABLE_DIRECTORY_HEADER_SIZE) throw new FontFaceError(`${source} is too short to be a font file (${String(view.byteLength)} bytes)`);
|
|
230
|
+
const sfntVersion = view.getUint32(0);
|
|
231
|
+
if (sfntVersion === SFNT_VERSION_COLLECTION) throw new FontFaceError(`${source} is a TrueType Collection (.ttc), which packs several faces into one file; extract the single face you want and pass that instead`);
|
|
232
|
+
if (!SFNT_VERSIONS.has(sfntVersion)) throw new FontFaceError(`${source} is not a TrueType/OpenType font file (no recognised sfnt version); a .woff/.woff2 file must be converted to .ttf/.otf first`);
|
|
233
|
+
const numTables = view.getUint16(4);
|
|
234
|
+
if (view.byteLength < TABLE_DIRECTORY_HEADER_SIZE + numTables * TABLE_RECORD_SIZE) throw new FontFaceError(`${source} declares ${String(numTables)} tables but is too short to hold that many table records`);
|
|
235
|
+
const tables = /* @__PURE__ */ new Map();
|
|
236
|
+
for (let i = 0; i < numTables; i++) {
|
|
237
|
+
const recordOffset = TABLE_DIRECTORY_HEADER_SIZE + i * TABLE_RECORD_SIZE;
|
|
238
|
+
const tag = decodeTag(view, recordOffset);
|
|
239
|
+
const offset = view.getUint32(recordOffset + 8);
|
|
240
|
+
const length = view.getUint32(recordOffset + 12);
|
|
241
|
+
if (offset + length > view.byteLength || tables.has(tag)) continue;
|
|
242
|
+
tables.set(tag, {
|
|
243
|
+
offset,
|
|
244
|
+
length
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return tables;
|
|
248
|
+
}
|
|
249
|
+
function tableView(bytes, table) {
|
|
250
|
+
return new DataView(bytes.buffer, bytes.byteOffset + table.offset, table.length);
|
|
251
|
+
}
|
|
252
|
+
function readNameRecords(name) {
|
|
253
|
+
if (name.byteLength < NAME_HEADER_SIZE) return [];
|
|
254
|
+
const count = name.getUint16(2);
|
|
255
|
+
const storageOffset = name.getUint16(4);
|
|
256
|
+
if (name.byteLength < NAME_HEADER_SIZE + count * NAME_RECORD_SIZE) return [];
|
|
257
|
+
const records = [];
|
|
258
|
+
for (let i = 0; i < count; i++) {
|
|
259
|
+
const recordOffset = NAME_HEADER_SIZE + i * NAME_RECORD_SIZE;
|
|
260
|
+
records.push({
|
|
261
|
+
platformId: name.getUint16(recordOffset),
|
|
262
|
+
encodingId: name.getUint16(recordOffset + 2),
|
|
263
|
+
nameId: name.getUint16(recordOffset + 6),
|
|
264
|
+
length: name.getUint16(recordOffset + 8),
|
|
265
|
+
stringOffset: storageOffset + name.getUint16(recordOffset + 10)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return records;
|
|
269
|
+
}
|
|
270
|
+
function platformPreference(record) {
|
|
271
|
+
if (record.platformId === PLATFORM_WINDOWS) return 0;
|
|
272
|
+
if (record.platformId === PLATFORM_UNICODE) return 1;
|
|
273
|
+
if (record.platformId === PLATFORM_MACINTOSH && record.encodingId === MACINTOSH_ENCODING_ROMAN) return 2;
|
|
274
|
+
return Number.POSITIVE_INFINITY;
|
|
275
|
+
}
|
|
276
|
+
function readNameString(name, records, nameId) {
|
|
277
|
+
const candidates = records.filter((record) => record.nameId === nameId && Number.isFinite(platformPreference(record))).sort((left, right) => platformPreference(left) - platformPreference(right));
|
|
278
|
+
for (const record of candidates) {
|
|
279
|
+
if (record.stringOffset + record.length > name.byteLength) continue;
|
|
280
|
+
const text = record.platformId === PLATFORM_MACINTOSH ? decodeMacintoshAscii(name, record.stringOffset, record.length) : decodeUtf16Be(name, record.stringOffset, record.length);
|
|
281
|
+
if (text !== void 0 && text.length > 0) return text;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function readStyleBits(bytes, tables, source) {
|
|
285
|
+
const os2 = tables.get("OS/2");
|
|
286
|
+
if (os2 !== void 0 && os2.length >= OS2_MINIMUM_SIZE) {
|
|
287
|
+
const fsSelection = tableView(bytes, os2).getUint16(OS2_FS_SELECTION_OFFSET);
|
|
288
|
+
return {
|
|
289
|
+
bold: (fsSelection & OS2_FS_SELECTION_BOLD) !== 0,
|
|
290
|
+
italic: (fsSelection & OS2_FS_SELECTION_ITALIC) !== 0
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const head = tables.get("head");
|
|
294
|
+
if (head !== void 0 && head.length >= HEAD_MINIMUM_SIZE) {
|
|
295
|
+
const macStyle = tableView(bytes, head).getUint16(HEAD_MAC_STYLE_OFFSET);
|
|
296
|
+
return {
|
|
297
|
+
bold: (macStyle & HEAD_MAC_STYLE_BOLD) !== 0,
|
|
298
|
+
italic: (macStyle & HEAD_MAC_STYLE_ITALIC) !== 0
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
throw new FontFaceError(`${source} declares neither a readable 'OS/2' nor a readable 'head' table, so its weight and slope cannot be determined`);
|
|
302
|
+
}
|
|
303
|
+
function describeFontFace(bytes, source) {
|
|
304
|
+
const tables = readTableDirectory(viewOf(bytes), source);
|
|
305
|
+
const nameTable = tables.get("name");
|
|
306
|
+
if (nameTable === void 0) throw new FontFaceError(`${source} declares no 'name' table, so the font family it provides cannot be determined`);
|
|
307
|
+
const name = tableView(bytes, nameTable);
|
|
308
|
+
const records = readNameRecords(name);
|
|
309
|
+
const family = readNameString(name, records, NAME_ID_TYPOGRAPHIC_FAMILY) ?? readNameString(name, records, NAME_ID_FAMILY);
|
|
310
|
+
if (family === void 0) throw new FontFaceError(`${source} declares no family name in its 'name' table, so the font family it provides cannot be determined`);
|
|
311
|
+
const { bold, italic } = readStyleBits(bytes, tables, source);
|
|
312
|
+
return {
|
|
313
|
+
family,
|
|
314
|
+
bold,
|
|
315
|
+
italic
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/runtime/fonts.ts
|
|
320
|
+
async function loadProvidedFonts(paths, options) {
|
|
321
|
+
const fonts = [];
|
|
322
|
+
for (const path of paths) {
|
|
323
|
+
const bytes = new Uint8Array(await readFile(path, { signal: options?.signal }));
|
|
324
|
+
const face = describeFontFace(bytes, path);
|
|
325
|
+
fonts.push({
|
|
326
|
+
family: face.family,
|
|
327
|
+
bold: face.bold,
|
|
328
|
+
italic: face.italic,
|
|
329
|
+
bytes
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
return fonts;
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
143
335
|
//#region src/runtime/io.ts
|
|
144
336
|
function isUint8Array(value) {
|
|
145
337
|
return value instanceof Uint8Array;
|
|
@@ -180,6 +372,18 @@ function resolveDefaultOutputPath(inputPath, targetFormat) {
|
|
|
180
372
|
}
|
|
181
373
|
//#endregion
|
|
182
374
|
//#region src/commands/shared.ts
|
|
375
|
+
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, odf, markdown, pdf";
|
|
376
|
+
function resolveTargetFormat(output, out, to) {
|
|
377
|
+
if (to !== void 0) {
|
|
378
|
+
if (!isDocumentFormat(to)) return { errorMessage: `unknown --to format '${to}'; expected one of ${KNOWN_DOCUMENT_FORMATS}` };
|
|
379
|
+
return { format: to };
|
|
380
|
+
}
|
|
381
|
+
const destination = output ?? out;
|
|
382
|
+
if (destination === void 0) return { errorMessage: "cannot infer a target format -- pass an output path with a recognised extension, --out with one, or --to <format>" };
|
|
383
|
+
const inferred = inferFormatFromExtension(destination);
|
|
384
|
+
if (inferred === void 0) return { errorMessage: `cannot infer a target format from '${destination}'; pass --to <format> instead` };
|
|
385
|
+
return { format: inferred };
|
|
386
|
+
}
|
|
183
387
|
function formatError(error, verbose) {
|
|
184
388
|
if (!(error instanceof Error)) return `error: ${String(error)}`;
|
|
185
389
|
const stackClause = verbose && error.stack !== void 0 ? `\n${error.stack}` : "";
|
|
@@ -196,13 +400,22 @@ function buildConversionAction(source, target) {
|
|
|
196
400
|
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeoutMs });
|
|
197
401
|
try {
|
|
198
402
|
const inputBytes = await readInput(input, { signal });
|
|
403
|
+
const fonts = await loadProvidedFonts(options.fontFiles ?? [], { signal });
|
|
199
404
|
const result = await createLocalDocumentConverter().convert({
|
|
200
405
|
source: {
|
|
201
406
|
format: source,
|
|
202
407
|
bytes: new Uint8Array(inputBytes)
|
|
203
408
|
},
|
|
204
409
|
targetFormat: target
|
|
205
|
-
}, {
|
|
410
|
+
}, {
|
|
411
|
+
signal,
|
|
412
|
+
fonts,
|
|
413
|
+
onFontSubstitution: options.reportFontSubstitutions === true ? createFontSubstitutionReporter({
|
|
414
|
+
json: options.json,
|
|
415
|
+
quiet: options.quiet,
|
|
416
|
+
command
|
|
417
|
+
}) : void 0
|
|
418
|
+
});
|
|
206
419
|
await writeOutput(resolvedOutput, result.document.bytes);
|
|
207
420
|
const reporter = createDiagnosticReporter({
|
|
208
421
|
json: options.json,
|
|
@@ -211,7 +424,7 @@ function buildConversionAction(source, target) {
|
|
|
211
424
|
});
|
|
212
425
|
for (const diagnostic of result.diagnostics) reporter.report(diagnostic);
|
|
213
426
|
if (options.dumpPackage !== void 0) if (result.package === void 0) process.stderr.write(`[${command}] this conversion does not produce an intermediate DocumentPackage\n`);
|
|
214
|
-
else await writeFile(options.dumpPackage, JSON.stringify(result.package, void 0, 2));
|
|
427
|
+
else await writeFile(options.dumpPackage, JSON.stringify(documentPackageWithSchema(result.package), void 0, 2));
|
|
215
428
|
reporter.summarize({
|
|
216
429
|
output: resolvedOutput,
|
|
217
430
|
bytes: result.document.bytes.byteLength,
|
|
@@ -244,6 +457,14 @@ function addVerboseOption(command) {
|
|
|
244
457
|
function addDumpPackageOption(command) {
|
|
245
458
|
return command.option("--dump-package <file>", "write the intermediate DocumentPackage (content + layout) this conversion built to a JSON file");
|
|
246
459
|
}
|
|
460
|
+
function collectFontFile(value, previous) {
|
|
461
|
+
return [...previous, value];
|
|
462
|
+
}
|
|
463
|
+
function addFontOptions(command) {
|
|
464
|
+
command.option("--font-file <path>", "embed this font file (.ttf/.otf) when the document asks for the family it declares; repeatable. The family, weight, and slope are read from the font's own 'name'/'OS/2' tables, so no accompanying family flag is needed", collectFontFile, []);
|
|
465
|
+
command.option("--report-font-substitutions", "print each font face that resolved to something other than what the document asked for to stderr, as it happens", false);
|
|
466
|
+
return command;
|
|
467
|
+
}
|
|
247
468
|
function addConversionFlags(command) {
|
|
248
469
|
addOutOption(command);
|
|
249
470
|
addTimeoutOption(command);
|
|
@@ -261,21 +482,11 @@ function toConversionCommandOptions(options) {
|
|
|
261
482
|
json: options.json,
|
|
262
483
|
quiet: options.quiet,
|
|
263
484
|
verbose: options.verbose,
|
|
264
|
-
dumpPackage: options.dumpPackage
|
|
485
|
+
dumpPackage: options.dumpPackage,
|
|
486
|
+
fontFiles: options.fontFile,
|
|
487
|
+
reportFontSubstitutions: options.reportFontSubstitutions
|
|
265
488
|
};
|
|
266
489
|
}
|
|
267
|
-
const KNOWN_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, odf, pdf";
|
|
268
|
-
function resolveGenericTarget(output, options) {
|
|
269
|
-
if (options.to !== void 0) {
|
|
270
|
-
if (!isDocumentFormat(options.to)) return { errorMessage: `unknown --to format '${options.to}'; expected one of ${KNOWN_FORMATS}` };
|
|
271
|
-
return { format: options.to };
|
|
272
|
-
}
|
|
273
|
-
const destination = output ?? options.out;
|
|
274
|
-
if (destination === void 0) return { errorMessage: "cannot infer a target format -- pass an output path with a recognised extension, --out with one, or --to <format>" };
|
|
275
|
-
const inferred = inferFormatFromExtension(destination);
|
|
276
|
-
if (inferred === void 0) return { errorMessage: `cannot infer a target format from '${destination}'; pass --to <format> instead` };
|
|
277
|
-
return { format: inferred };
|
|
278
|
-
}
|
|
279
490
|
async function runGenericConvert(input, output, options) {
|
|
280
491
|
const extension = extname(input).toLowerCase();
|
|
281
492
|
if (extension === ".odm") {
|
|
@@ -288,10 +499,10 @@ async function runGenericConvert(input, output, options) {
|
|
|
288
499
|
}
|
|
289
500
|
const source = inferFormatFromExtension(input);
|
|
290
501
|
if (source === void 0) {
|
|
291
|
-
process.stderr.write(`convert: cannot infer a source format from '${input}'; rename the file with a recognised extension (${
|
|
502
|
+
process.stderr.write(`convert: cannot infer a source format from '${input}'; rename the file with a recognised extension (${KNOWN_DOCUMENT_FORMATS}) or use one of the explicit '<source>-to-<target>' commands\n`);
|
|
292
503
|
return 2;
|
|
293
504
|
}
|
|
294
|
-
const target =
|
|
505
|
+
const target = resolveTargetFormat(output, options.out, options.to);
|
|
295
506
|
if ("errorMessage" in target) {
|
|
296
507
|
process.stderr.write(`convert: ${target.errorMessage}\n`);
|
|
297
508
|
return 2;
|
|
@@ -305,6 +516,7 @@ function registerConversionCommands(program) {
|
|
|
305
516
|
const command = program.command(`${commandName} <input> [output]`).description(`convert a ${source} document to ${target}`);
|
|
306
517
|
addConversionFlags(command);
|
|
307
518
|
addDumpPackageOption(command);
|
|
519
|
+
if (target === "pdf") addFontOptions(command);
|
|
308
520
|
command.action(async (input, output, options) => {
|
|
309
521
|
process.exitCode = await buildConversionAction(source, target)(input, output, toConversionCommandOptions(options));
|
|
310
522
|
});
|
|
@@ -312,14 +524,15 @@ function registerConversionCommands(program) {
|
|
|
312
524
|
const generic = program.command("convert <input> [output]").description("convert between any two supported document formats, inferring source/target from file extensions where possible");
|
|
313
525
|
addConversionFlags(generic);
|
|
314
526
|
addDumpPackageOption(generic);
|
|
315
|
-
generic
|
|
527
|
+
addFontOptions(generic);
|
|
528
|
+
generic.option("--to <format>", `target format when it cannot be inferred from the output path (${KNOWN_DOCUMENT_FORMATS})`);
|
|
316
529
|
generic.action(async (input, output, options) => {
|
|
317
530
|
process.exitCode = await runGenericConvert(input, output, options);
|
|
318
531
|
});
|
|
319
532
|
}
|
|
320
533
|
//#endregion
|
|
321
534
|
//#region src/commands/formats.ts
|
|
322
|
-
const COMMANDS_NOT_LISTED = "odm-to-pdf, odb-to-csv, odb-to-xlsx, odb-tables, pdf-inspect";
|
|
535
|
+
const COMMANDS_NOT_LISTED = "odm-to-pdf, odb-to-csv, odb-to-xlsx, odb-tables, odb-forms, odb-reports, pdf-inspect, from-package";
|
|
323
536
|
function registerFormatsCommand(program) {
|
|
324
537
|
program.command("formats").description("list every source -> target conversion this CLI supports via a <source>-to-<target> command").option("--json", "emit the conversion list as a JSON array instead of a human-readable table", false).action((options) => {
|
|
325
538
|
const { conversions } = createLocalDocumentConverter();
|
|
@@ -332,6 +545,218 @@ function registerFormatsCommand(program) {
|
|
|
332
545
|
});
|
|
333
546
|
}
|
|
334
547
|
//#endregion
|
|
548
|
+
//#region src/commands/from-package.ts
|
|
549
|
+
function buildBytesForTarget(pkg, target) {
|
|
550
|
+
if (target === "pdf") {
|
|
551
|
+
if (pkg.layout === void 0) throw new Error("this DocumentPackage has no layout -- only a package dumped from a <format>-to-pdf or pdf-to-<format> conversion carries one; a bridge conversion's own dump (e.g. odt-to-docx) never does, so 'pdf' is not a reachable target from it");
|
|
552
|
+
return writePdf(pkg.layout);
|
|
553
|
+
}
|
|
554
|
+
switch (target) {
|
|
555
|
+
case "docx": return encodePackage(buildDocxPackage(pkg.content));
|
|
556
|
+
case "pptx": return encodePackage(buildPptxPackage(pkg.content));
|
|
557
|
+
case "odt": return encodePackage$1(buildOdtPackage(pkg.content));
|
|
558
|
+
case "odp": return encodePackage$1(buildOdpPackage(pkg.content));
|
|
559
|
+
case "ods": return encodePackage$1(buildOdsPackage(pkg.content));
|
|
560
|
+
case "odg": return encodePackage$1(buildOdgPackage(pkg.content));
|
|
561
|
+
case "markdown": return encodeMarkdownText(buildMarkdownText(pkg.content));
|
|
562
|
+
case "xlsx": throw new Error("'xlsx' cannot be built from a DocumentPackage directly -- documents.js does not re-export a ContentDocument-to-xlsx builder; convert to 'ods' here, then run 'ods-to-xlsx' on the result instead");
|
|
563
|
+
case "odf": throw new Error("'odf' (a standalone formula document) cannot be built from a DocumentPackage -- there is no ContentDocument-to-odf builder");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async function runFromPackage(input, output, options) {
|
|
567
|
+
const command = "from-package";
|
|
568
|
+
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
569
|
+
process.stderr.write(`[${command}] conflicting output destinations: positional '${output}' and --out '${options.out}'\n`);
|
|
570
|
+
return 2;
|
|
571
|
+
}
|
|
572
|
+
const target = resolveTargetFormat(output, options.out, options.to);
|
|
573
|
+
if ("errorMessage" in target) {
|
|
574
|
+
process.stderr.write(`[${command}] ${target.errorMessage}\n`);
|
|
575
|
+
return 2;
|
|
576
|
+
}
|
|
577
|
+
const resolvedOutput = output ?? options.out ?? (input === "-" ? "-" : resolveDefaultOutputPath(input, target.format));
|
|
578
|
+
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
579
|
+
try {
|
|
580
|
+
const inputBytes = await readInput(input, { signal });
|
|
581
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(inputBytes);
|
|
582
|
+
let parsed;
|
|
583
|
+
try {
|
|
584
|
+
parsed = JSON.parse(text);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
process.stderr.write(`[${command}] '${input}' is not valid JSON: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
587
|
+
return 1;
|
|
588
|
+
}
|
|
589
|
+
const result = documentFromJson(parsed);
|
|
590
|
+
if (result.kind !== "DocumentPackage") {
|
|
591
|
+
process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a DocumentPackage -- only a file written by --dump-package can be read back by this command\n`);
|
|
592
|
+
return 2;
|
|
593
|
+
}
|
|
594
|
+
const bytes = buildBytesForTarget(result.value, target.format);
|
|
595
|
+
await writeOutput(resolvedOutput, bytes);
|
|
596
|
+
createDiagnosticReporter({
|
|
597
|
+
json: options.json,
|
|
598
|
+
quiet: options.quiet,
|
|
599
|
+
command
|
|
600
|
+
}).summarize({
|
|
601
|
+
output: resolvedOutput,
|
|
602
|
+
bytes: bytes.byteLength,
|
|
603
|
+
diagnosticCount: 0
|
|
604
|
+
});
|
|
605
|
+
return 0;
|
|
606
|
+
} catch (error) {
|
|
607
|
+
if (error instanceof UnrecognizedDocumentSchemaError) {
|
|
608
|
+
process.stderr.write(`[${command}] '${input}' has no recognised $schema -- only a file written by --dump-package can be read back by this command\n`);
|
|
609
|
+
return 1;
|
|
610
|
+
}
|
|
611
|
+
process.stderr.write(`${formatError(error, options.verbose)}\n`);
|
|
612
|
+
return mapErrorToExit(error, getAbortReason());
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function registerFromPackageCommand(program) {
|
|
616
|
+
const command = program.command("from-package <input> [output]").description("read a DocumentPackage previously written by --dump-package and export it to a real target format");
|
|
617
|
+
addOutOption(command);
|
|
618
|
+
addTimeoutOption(command);
|
|
619
|
+
addJsonOption(command);
|
|
620
|
+
addQuietOption(command);
|
|
621
|
+
addVerboseOption(command);
|
|
622
|
+
command.option("--to <format>", `target format when it cannot be inferred from the output path (${KNOWN_DOCUMENT_FORMATS})`);
|
|
623
|
+
command.action(async (input, output, options) => {
|
|
624
|
+
process.exitCode = await runFromPackage(input, output, options);
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
//#endregion
|
|
628
|
+
//#region src/odb-structure.ts
|
|
629
|
+
const INDENT = " ";
|
|
630
|
+
function indent(depth) {
|
|
631
|
+
return INDENT.repeat(depth);
|
|
632
|
+
}
|
|
633
|
+
function quoted(value) {
|
|
634
|
+
return `"${value}"`;
|
|
635
|
+
}
|
|
636
|
+
function describeDataSource(command, commandType) {
|
|
637
|
+
if (command === void 0) return commandType;
|
|
638
|
+
return commandType === void 0 ? quoted(command) : `${commandType} ${quoted(command)}`;
|
|
639
|
+
}
|
|
640
|
+
function countOdbFormControls(controls) {
|
|
641
|
+
return controls.reduce((total, control) => total + 1 + countOdbFormControls(control.controls), 0);
|
|
642
|
+
}
|
|
643
|
+
function countOdbFormDefinitionControls(definition) {
|
|
644
|
+
return countOdbFormControls(definition.controls) + definition.subForms.reduce((total, subForm) => total + countOdbFormDefinitionControls(subForm), 0);
|
|
645
|
+
}
|
|
646
|
+
function countOdbFormBoundControls(controls) {
|
|
647
|
+
return controls.reduce((total, control) => total + (control.dataField === void 0 ? 0 : 1) + countOdbFormBoundControls(control.controls), 0);
|
|
648
|
+
}
|
|
649
|
+
function countOdbFormDefinitionBoundControls(definition) {
|
|
650
|
+
return countOdbFormBoundControls(definition.controls) + definition.subForms.reduce((total, subForm) => total + countOdbFormDefinitionBoundControls(subForm), 0);
|
|
651
|
+
}
|
|
652
|
+
function describeOdbForm(form) {
|
|
653
|
+
const controlCount = form.forms.reduce((total, definition) => total + countOdbFormDefinitionControls(definition), 0);
|
|
654
|
+
const boundCount = form.forms.reduce((total, definition) => total + countOdbFormDefinitionBoundControls(definition), 0);
|
|
655
|
+
return `${form.name} [${form.href}] -- ${form.forms.length} form${form.forms.length === 1 ? "" : "s"}, ${controlCount} control${controlCount === 1 ? "" : "s"} (${boundCount} bound)`;
|
|
656
|
+
}
|
|
657
|
+
function formControlLines(control, depth) {
|
|
658
|
+
const parts = [control.tag];
|
|
659
|
+
if (control.name !== void 0) parts.push(control.name);
|
|
660
|
+
if (control.dataField !== void 0) parts.push(`-> ${control.dataField}`);
|
|
661
|
+
if (control.label !== void 0) parts.push(`label ${quoted(control.label)}`);
|
|
662
|
+
if (control.controlImplementation !== void 0) parts.push(`(${control.controlImplementation})`);
|
|
663
|
+
const nested = control.controls.flatMap((child) => formControlLines(child, depth + 1));
|
|
664
|
+
return [`${indent(depth)}${parts.join(" ")}`, ...nested];
|
|
665
|
+
}
|
|
666
|
+
function formDefinitionLines(definition, depth, kindLabel) {
|
|
667
|
+
const headerParts = [kindLabel];
|
|
668
|
+
if (definition.name !== void 0) headerParts.push(definition.name);
|
|
669
|
+
const dataSource = describeDataSource(definition.command, definition.commandType);
|
|
670
|
+
if (dataSource !== void 0) headerParts.push(`on ${dataSource}`);
|
|
671
|
+
const lines = [`${indent(depth)}${headerParts.join(" ")}`];
|
|
672
|
+
if (definition.datasource !== void 0) lines.push(`${indent(depth + 1)}datasource: ${definition.datasource}`);
|
|
673
|
+
if (definition.filter !== void 0) lines.push(`${indent(depth + 1)}filter: ${definition.filter}`);
|
|
674
|
+
if (definition.order !== void 0) lines.push(`${indent(depth + 1)}order: ${definition.order}`);
|
|
675
|
+
if (definition.controls.length === 0) lines.push(`${indent(depth + 1)}(no controls)`);
|
|
676
|
+
for (const control of definition.controls) lines.push(...formControlLines(control, depth + 1));
|
|
677
|
+
for (const subForm of definition.subForms) lines.push(...formDefinitionLines(subForm, depth + 1, "subform"));
|
|
678
|
+
return lines;
|
|
679
|
+
}
|
|
680
|
+
function formatOdbFormLines(form) {
|
|
681
|
+
if (form.forms.length === 0) return ["(this form document declares no form:form definitions)"];
|
|
682
|
+
return form.forms.flatMap((definition) => formDefinitionLines(definition, 0, "form"));
|
|
683
|
+
}
|
|
684
|
+
function odbFormSummary(form) {
|
|
685
|
+
return {
|
|
686
|
+
name: form.name,
|
|
687
|
+
href: form.href,
|
|
688
|
+
forms: form.forms
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function countReportBandElements(band) {
|
|
692
|
+
return band?.elements.length ?? 0;
|
|
693
|
+
}
|
|
694
|
+
function countReportGroupElements(group) {
|
|
695
|
+
return countReportBandElements(group.header) + countReportBandElements(group.footer) + group.groups.reduce((total, nested) => total + countReportGroupElements(nested), 0);
|
|
696
|
+
}
|
|
697
|
+
function countReportGroups(groups) {
|
|
698
|
+
return groups.reduce((total, group) => total + 1 + countReportGroups(group.groups), 0);
|
|
699
|
+
}
|
|
700
|
+
function describeOdbReport(report) {
|
|
701
|
+
const dataSource = describeDataSource(report.command, report.commandType);
|
|
702
|
+
const groupCount = countReportGroups(report.groups);
|
|
703
|
+
const elementCount = countReportBandElements(report.reportHeader) + countReportBandElements(report.pageHeader) + countReportBandElements(report.detail) + countReportBandElements(report.pageFooter) + countReportBandElements(report.reportFooter) + report.groups.reduce((total, group) => total + countReportGroupElements(group), 0);
|
|
704
|
+
const source = dataSource === void 0 ? "no data source" : `on ${dataSource}`;
|
|
705
|
+
return `${report.name} [${report.href}] -- ${source}, ${groupCount} group${groupCount === 1 ? "" : "s"}, ${elementCount} element${elementCount === 1 ? "" : "s"}`;
|
|
706
|
+
}
|
|
707
|
+
function reportElementLine(element, depth) {
|
|
708
|
+
const parts = [element.tag];
|
|
709
|
+
if (element.name !== void 0) parts.push(quoted(element.name));
|
|
710
|
+
if (element.formula !== void 0) parts.push(`= ${element.formula}`);
|
|
711
|
+
if (element.dataField !== void 0) parts.push(`-> ${element.dataField}`);
|
|
712
|
+
const head = `${indent(depth)}${parts.join(" ")}`;
|
|
713
|
+
return element.text === void 0 ? head : `${head}: ${quoted(element.text)}`;
|
|
714
|
+
}
|
|
715
|
+
function reportBandLines(band, depth) {
|
|
716
|
+
if (band === void 0) return [];
|
|
717
|
+
const header = band.name === void 0 ? band.kind : `${band.kind} ${quoted(band.name)}`;
|
|
718
|
+
const lines = [`${indent(depth)}${header}`];
|
|
719
|
+
if (band.elements.length === 0) lines.push(`${indent(depth + 1)}(no elements)`);
|
|
720
|
+
for (const element of band.elements) lines.push(reportElementLine(element, depth + 1));
|
|
721
|
+
return lines;
|
|
722
|
+
}
|
|
723
|
+
function reportFunctionLines(functions, depth) {
|
|
724
|
+
if (functions.length === 0) return [];
|
|
725
|
+
return [`${indent(depth)}functions`, ...functions.map((fn) => `${indent(depth + 1)}${fn.name} = ${fn.formula}`)];
|
|
726
|
+
}
|
|
727
|
+
function reportGroupLines(group, depth) {
|
|
728
|
+
const headerParts = ["group"];
|
|
729
|
+
if (group.groupExpression !== void 0) headerParts.push(group.groupExpression);
|
|
730
|
+
const attributes = [];
|
|
731
|
+
if (group.sortExpression !== void 0) attributes.push(`sort ${group.sortExpression} ${group.sortAscending === false ? "descending" : "ascending"}`);
|
|
732
|
+
if (group.startNewColumn === true) attributes.push("new column");
|
|
733
|
+
if (group.resetPageNumber === true) attributes.push("reset page number");
|
|
734
|
+
if (group.keepTogether !== void 0) attributes.push(`keep together ${group.keepTogether}`);
|
|
735
|
+
if (attributes.length > 0) headerParts.push(`(${attributes.join(", ")})`);
|
|
736
|
+
return [
|
|
737
|
+
`${indent(depth)}${headerParts.join(" ")}`,
|
|
738
|
+
...reportBandLines(group.header, depth + 1),
|
|
739
|
+
...group.groups.flatMap((nested) => reportGroupLines(nested, depth + 1)),
|
|
740
|
+
...reportFunctionLines(group.functions, depth + 1),
|
|
741
|
+
...reportBandLines(group.footer, depth + 1)
|
|
742
|
+
];
|
|
743
|
+
}
|
|
744
|
+
function formatOdbReportLines(report) {
|
|
745
|
+
const lines = [];
|
|
746
|
+
const dataSource = describeDataSource(report.command, report.commandType);
|
|
747
|
+
if (dataSource !== void 0) lines.push(`data source: ${dataSource}`);
|
|
748
|
+
if (report.caption !== void 0) lines.push(`caption: ${report.caption}`);
|
|
749
|
+
if (report.mimeType !== void 0) lines.push(`mime type: ${report.mimeType}`);
|
|
750
|
+
lines.push(...reportBandLines(report.reportHeader, 0));
|
|
751
|
+
lines.push(...reportBandLines(report.pageHeader, 0));
|
|
752
|
+
lines.push(...report.groups.flatMap((group) => reportGroupLines(group, 0)));
|
|
753
|
+
lines.push(...reportBandLines(report.detail, 0));
|
|
754
|
+
lines.push(...reportBandLines(report.pageFooter, 0));
|
|
755
|
+
lines.push(...reportBandLines(report.reportFooter, 0));
|
|
756
|
+
lines.push(...reportFunctionLines(report.functions, 0));
|
|
757
|
+
return lines;
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
335
760
|
//#region src/commands/odb.ts
|
|
336
761
|
function reportOdbError(command, error, verbose, abortReason) {
|
|
337
762
|
if (error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError) {
|
|
@@ -433,6 +858,52 @@ async function runOdbTables(input, options) {
|
|
|
433
858
|
return reportOdbError(command, error, false, getAbortReason());
|
|
434
859
|
}
|
|
435
860
|
}
|
|
861
|
+
async function runOdbForms(input, options) {
|
|
862
|
+
const command = "odb-forms";
|
|
863
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
864
|
+
try {
|
|
865
|
+
const inputBytes = await readInput(input, { signal });
|
|
866
|
+
const forms = readOdbForms(decodePackage(new Uint8Array(inputBytes)));
|
|
867
|
+
if (options.json) {
|
|
868
|
+
process.stdout.write(`${JSON.stringify(forms.map((form) => odbFormSummary(form)))}\n`);
|
|
869
|
+
return 0;
|
|
870
|
+
}
|
|
871
|
+
if (forms.length === 0) {
|
|
872
|
+
process.stdout.write("This database declares no forms.\n");
|
|
873
|
+
return 0;
|
|
874
|
+
}
|
|
875
|
+
for (const form of forms) {
|
|
876
|
+
process.stdout.write(`${describeOdbForm(form)}\n`);
|
|
877
|
+
for (const line of formatOdbFormLines(form)) process.stdout.write(` ${line}\n`);
|
|
878
|
+
}
|
|
879
|
+
return 0;
|
|
880
|
+
} catch (error) {
|
|
881
|
+
return reportOdbError(command, error, false, getAbortReason());
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
async function runOdbReports(input, options) {
|
|
885
|
+
const command = "odb-reports";
|
|
886
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
887
|
+
try {
|
|
888
|
+
const inputBytes = await readInput(input, { signal });
|
|
889
|
+
const reports = readOdbReports(decodePackage(new Uint8Array(inputBytes)));
|
|
890
|
+
if (options.json) {
|
|
891
|
+
process.stdout.write(`${JSON.stringify(reports)}\n`);
|
|
892
|
+
return 0;
|
|
893
|
+
}
|
|
894
|
+
if (reports.length === 0) {
|
|
895
|
+
process.stdout.write("This database declares no reports.\n");
|
|
896
|
+
return 0;
|
|
897
|
+
}
|
|
898
|
+
for (const report of reports) {
|
|
899
|
+
process.stdout.write(`${describeOdbReport(report)}\n`);
|
|
900
|
+
for (const line of formatOdbReportLines(report)) process.stdout.write(` ${line}\n`);
|
|
901
|
+
}
|
|
902
|
+
return 0;
|
|
903
|
+
} catch (error) {
|
|
904
|
+
return reportOdbError(command, error, false, getAbortReason());
|
|
905
|
+
}
|
|
906
|
+
}
|
|
436
907
|
function registerOdbToXlsxCommand(program) {
|
|
437
908
|
const command = program.command("odb-to-xlsx <input> [output]").description("extract every table an embedded .odb database declares into one xlsx workbook, one sheet per table");
|
|
438
909
|
addOutOption(command);
|
|
@@ -461,10 +932,22 @@ function registerOdbTablesCommand(program) {
|
|
|
461
932
|
process.exitCode = await runOdbTables(input, options);
|
|
462
933
|
});
|
|
463
934
|
}
|
|
935
|
+
function registerOdbFormsCommand(program) {
|
|
936
|
+
program.command("odb-forms <input>").description("list every form an .odb declares, with each form's own data source and its field-bound controls").option("--json", "emit the form structure as a JSON array instead of a human-readable report", false).action(async (input, options) => {
|
|
937
|
+
process.exitCode = await runOdbForms(input, options);
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function registerOdbReportsCommand(program) {
|
|
941
|
+
program.command("odb-reports <input>").description("list every report an .odb declares, with each report's own data-source command, band/group structure, and rpt: formula expressions").option("--json", "emit the report structure as a JSON array instead of a human-readable report", false).action(async (input, options) => {
|
|
942
|
+
process.exitCode = await runOdbReports(input, options);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
464
945
|
function registerOdbCommands(program) {
|
|
465
946
|
registerOdbToXlsxCommand(program);
|
|
466
947
|
registerOdbToCsvCommand(program);
|
|
467
948
|
registerOdbTablesCommand(program);
|
|
949
|
+
registerOdbFormsCommand(program);
|
|
950
|
+
registerOdbReportsCommand(program);
|
|
468
951
|
}
|
|
469
952
|
//#endregion
|
|
470
953
|
//#region src/commands/odm.ts
|
|
@@ -500,11 +983,26 @@ async function runOdmToPdf(input, output, options) {
|
|
|
500
983
|
command
|
|
501
984
|
});
|
|
502
985
|
let diagnosticCount = 0;
|
|
986
|
+
const reportFontSubstitution = options.reportFontSubstitutions === true ? createFontSubstitutionReporter({
|
|
987
|
+
json: options.json,
|
|
988
|
+
quiet: options.quiet,
|
|
989
|
+
command
|
|
990
|
+
}) : void 0;
|
|
503
991
|
try {
|
|
504
992
|
const inputBytes = await readInput(input, { signal });
|
|
993
|
+
const fonts = await loadProvidedFonts(options.fontFile ?? [], { signal });
|
|
505
994
|
const bytes = odmToPdf(new Uint8Array(inputBytes), {
|
|
506
995
|
signal,
|
|
507
996
|
resolveSubDocument,
|
|
997
|
+
fonts,
|
|
998
|
+
onFontSubstitution: (substitution) => {
|
|
999
|
+
if (reportFontSubstitution !== void 0) {
|
|
1000
|
+
reportFontSubstitution(substitution);
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
diagnosticCount += 1;
|
|
1004
|
+
reporter.report(fontSubstitutionToDiagnostic(substitution));
|
|
1005
|
+
},
|
|
508
1006
|
onSubstitution: (substitution, context) => {
|
|
509
1007
|
diagnosticCount += 1;
|
|
510
1008
|
reporter.report(substitutionToDiagnostic(substitution, context.pageIndex));
|
|
@@ -533,6 +1031,7 @@ function registerOdmCommand(program) {
|
|
|
533
1031
|
addJsonOption(command);
|
|
534
1032
|
addQuietOption(command);
|
|
535
1033
|
addVerboseOption(command);
|
|
1034
|
+
addFontOptions(command);
|
|
536
1035
|
command.option("--chapters-dir <dir>", "directory to search for each unresolved chapter href, matched by the href's own basename");
|
|
537
1036
|
command.option("--chapter <href>=<file>", "resolve one chapter href to a local file explicitly; repeatable", collectChapterOverride, /* @__PURE__ */ new Map());
|
|
538
1037
|
command.action(async (input, output, options) => {
|
|
@@ -629,7 +1128,7 @@ function registerPdfInspectCommand(program) {
|
|
|
629
1128
|
}
|
|
630
1129
|
//#endregion
|
|
631
1130
|
//#region package.json
|
|
632
|
-
var version = "1.0
|
|
1131
|
+
var version = "1.2.0";
|
|
633
1132
|
//#endregion
|
|
634
1133
|
//#region src/program.ts
|
|
635
1134
|
function createProgram() {
|
|
@@ -642,6 +1141,7 @@ function createProgram() {
|
|
|
642
1141
|
});
|
|
643
1142
|
registerConversionCommands(program);
|
|
644
1143
|
registerFormatsCommand(program);
|
|
1144
|
+
registerFromPackageCommand(program);
|
|
645
1145
|
registerOdmCommand(program);
|
|
646
1146
|
registerOdbCommands(program);
|
|
647
1147
|
registerPdfInspectCommand(program);
|