doculi 3.1.7 → 5.7.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +62 -58
- package/dist/cli.js +47 -49
- package/dist/index.cjs +66 -50
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +67 -51
- package/dist/{odb-structure-B1ygPCEa.js → odb-structure-CItnH50p.js} +24 -6
- package/dist/{tui-tT3An-cI.js → tui-CGo4rAhe.js} +1256 -333
- package/package.json +37 -38
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ let node_path = require("node:path");
|
|
|
4
4
|
let documents_js = require("documents.js");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
6
6
|
let commander = require("commander");
|
|
7
|
+
let document_schema_js = require("document-schema.js");
|
|
7
8
|
let document_outline_js = require("document-outline.js");
|
|
8
9
|
//#region src/format.ts
|
|
9
10
|
const EXTENSION_TO_FORMAT = {
|
|
@@ -30,6 +31,12 @@ const EXTENSION_TO_FORMAT = {
|
|
|
30
31
|
svg: "svg",
|
|
31
32
|
markdown: "markdown",
|
|
32
33
|
md: "markdown",
|
|
34
|
+
rtf: "rtf",
|
|
35
|
+
wpd: "wpd",
|
|
36
|
+
doc: "doc",
|
|
37
|
+
xls: "xls",
|
|
38
|
+
ppt: "ppt",
|
|
39
|
+
epub: "epub",
|
|
33
40
|
pdf: "pdf"
|
|
34
41
|
};
|
|
35
42
|
const FORMAT_TO_EXTENSION = {
|
|
@@ -44,6 +51,12 @@ const FORMAT_TO_EXTENSION = {
|
|
|
44
51
|
csv: "csv",
|
|
45
52
|
svg: "svg",
|
|
46
53
|
markdown: "md",
|
|
54
|
+
rtf: "rtf",
|
|
55
|
+
wpd: "wpd",
|
|
56
|
+
doc: "doc",
|
|
57
|
+
xls: "xls",
|
|
58
|
+
ppt: "ppt",
|
|
59
|
+
epub: "epub",
|
|
47
60
|
pdf: "pdf"
|
|
48
61
|
};
|
|
49
62
|
function isDocumentFormat(value) {
|
|
@@ -68,9 +81,13 @@ function combineSignals(a, b) {
|
|
|
68
81
|
controller.abort(signal.reason);
|
|
69
82
|
};
|
|
70
83
|
if (a.aborted) forward(a);
|
|
71
|
-
else a.addEventListener("abort", () =>
|
|
84
|
+
else a.addEventListener("abort", () => {
|
|
85
|
+
forward(a);
|
|
86
|
+
}, { once: true });
|
|
72
87
|
if (b.aborted) forward(b);
|
|
73
|
-
else b.addEventListener("abort", () =>
|
|
88
|
+
else b.addEventListener("abort", () => {
|
|
89
|
+
forward(b);
|
|
90
|
+
}, { once: true });
|
|
74
91
|
return controller.signal;
|
|
75
92
|
}
|
|
76
93
|
function createRuntimeSignal(options) {
|
|
@@ -252,7 +269,7 @@ function resolveDefaultOutputPath(inputPath, targetFormat) {
|
|
|
252
269
|
}
|
|
253
270
|
//#endregion
|
|
254
271
|
//#region src/commands/shared.ts
|
|
255
|
-
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, pdf";
|
|
272
|
+
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, rtf, wpd, doc, xls, ppt, epub, pdf";
|
|
256
273
|
function resolveTargetFormat(output, out, to) {
|
|
257
274
|
if (to !== void 0) {
|
|
258
275
|
if (!isDocumentFormat(to)) return { errorMessage: `unknown --to format '${to}'; expected one of ${KNOWN_DOCUMENT_FORMATS}` };
|
|
@@ -281,6 +298,7 @@ function buildConversionAction(source, target) {
|
|
|
281
298
|
try {
|
|
282
299
|
const inputBytes = await readInput(input, { signal });
|
|
283
300
|
const fonts = await loadProvidedFonts(options.fontFiles ?? [], { signal });
|
|
301
|
+
const images = createFilesystemMarkdownImageResolver(input === "-" ? "." : (0, node_path.dirname)((0, node_path.resolve)(input)));
|
|
284
302
|
const result = await (0, documents_js.createLocalDocumentConverter)().convert({
|
|
285
303
|
source: {
|
|
286
304
|
format: source,
|
|
@@ -295,7 +313,7 @@ function buildConversionAction(source, target) {
|
|
|
295
313
|
quiet: options.quiet,
|
|
296
314
|
command
|
|
297
315
|
}) : void 0,
|
|
298
|
-
images
|
|
316
|
+
images,
|
|
299
317
|
delimiter: options.delimiter,
|
|
300
318
|
sheet: options.sheet,
|
|
301
319
|
page: options.page
|
|
@@ -307,8 +325,13 @@ function buildConversionAction(source, target) {
|
|
|
307
325
|
command
|
|
308
326
|
});
|
|
309
327
|
for (const diagnostic of result.diagnostics) reporter.report(diagnostic);
|
|
310
|
-
if (options.dumpPackage !== void 0)
|
|
311
|
-
|
|
328
|
+
if (options.dumpPackage !== void 0) {
|
|
329
|
+
const nativeTree = (0, documents_js.readNativeDocumentTree)(source, new Uint8Array(inputBytes), {
|
|
330
|
+
signal,
|
|
331
|
+
images
|
|
332
|
+
});
|
|
333
|
+
await (0, node_fs_promises.writeFile)(options.dumpPackage, JSON.stringify((0, documents_js.documentTreeWithSchema)(nativeTree), void 0, 2));
|
|
334
|
+
}
|
|
312
335
|
reporter.summarize({
|
|
313
336
|
output: resolvedOutput,
|
|
314
337
|
bytes: result.document.bytes.byteLength,
|
|
@@ -339,7 +362,7 @@ function addVerboseOption(command) {
|
|
|
339
362
|
return command.option("--verbose", "include a full stack trace when the run fails", false);
|
|
340
363
|
}
|
|
341
364
|
function addDumpPackageOption(command) {
|
|
342
|
-
return command.option("--dump-package <file>", "write the intermediate
|
|
365
|
+
return command.option("--dump-package <file>", "write the intermediate DocumentTree (the tree form: container-grouped content carrying per-node rendered frames, plus page sizes) this conversion built to a JSON file");
|
|
343
366
|
}
|
|
344
367
|
function collectFontFile(value, previous) {
|
|
345
368
|
return [...previous, value];
|
|
@@ -454,9 +477,15 @@ function footnotesSection(footnotes) {
|
|
|
454
477
|
if (footnotes.length === 0) return [];
|
|
455
478
|
return ["footnotes", ...footnotes.map((footnote, index) => footnoteLine(footnote, index + 1))];
|
|
456
479
|
}
|
|
457
|
-
function
|
|
458
|
-
|
|
459
|
-
|
|
480
|
+
function partText(blocks) {
|
|
481
|
+
let text = "";
|
|
482
|
+
for (const block of blocks) if (block.kind === "paragraph") text += block.runs.map((run) => run.text).join("");
|
|
483
|
+
else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) text += partText(cell.blocks);
|
|
484
|
+
return text;
|
|
485
|
+
}
|
|
486
|
+
function headerOrFooterSection(label, parts) {
|
|
487
|
+
if (parts.length === 0) return [];
|
|
488
|
+
return [label, ...parts.map((part, index) => `${indent$1(1)}[${index + 1}] ${partText(part.blocks)}`)];
|
|
460
489
|
}
|
|
461
490
|
function numberingLevelLine(ilvl, level) {
|
|
462
491
|
const restartSuffix = level.restart === void 0 ? "" : `, restarts at level ${level.restart}`;
|
|
@@ -483,8 +512,8 @@ function formatDocxExtrasLines(extras) {
|
|
|
483
512
|
const nonEmptySections = [
|
|
484
513
|
commentsSection(extras.comments),
|
|
485
514
|
footnotesSection(extras.footnotes),
|
|
486
|
-
headerOrFooterSection("headers", extras.
|
|
487
|
-
headerOrFooterSection("footers", extras.
|
|
515
|
+
headerOrFooterSection("headers", extras.headerFooterParts.filter((part) => part.kind === "header")),
|
|
516
|
+
headerOrFooterSection("footers", extras.headerFooterParts.filter((part) => part.kind === "footer")),
|
|
488
517
|
numberingSection(extras.numbering)
|
|
489
518
|
].filter((section) => section.length > 0);
|
|
490
519
|
if (nonEmptySections.length === 0) return ["This document carries no comments, footnotes, headers, footers, or numbering definitions."];
|
|
@@ -580,6 +609,9 @@ function isSchemaVersionMismatchError(error) {
|
|
|
580
609
|
function isLayoutSchemaDemotedError(error) {
|
|
581
610
|
return error instanceof Error && error.name === "LayoutSchemaDemotedError";
|
|
582
611
|
}
|
|
612
|
+
function isDocumentPackageRenamedError(error) {
|
|
613
|
+
return error instanceof Error && error.name === "DocumentPackageRenamedError";
|
|
614
|
+
}
|
|
583
615
|
async function runFromPackage(input, output, options) {
|
|
584
616
|
const command = "from-package";
|
|
585
617
|
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
@@ -604,11 +636,11 @@ async function runFromPackage(input, output, options) {
|
|
|
604
636
|
return 1;
|
|
605
637
|
}
|
|
606
638
|
const result = (0, documents_js.documentFromJson)(parsed);
|
|
607
|
-
if (result.kind !== "
|
|
608
|
-
process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a
|
|
639
|
+
if (result.kind !== "DocumentTree") {
|
|
640
|
+
process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a DocumentTree -- only a file written by --dump-package can be read back by this command\n`);
|
|
609
641
|
return 2;
|
|
610
642
|
}
|
|
611
|
-
const content = (0,
|
|
643
|
+
const content = (0, document_schema_js.flattenTree)(result.value);
|
|
612
644
|
const bytes = target.format === "csv" ? (0, documents_js.encodeCsvText)((0, documents_js.buildCsvText)(content, {
|
|
613
645
|
delimiter: options.delimiter,
|
|
614
646
|
sheet: options.sheet
|
|
@@ -630,19 +662,23 @@ async function runFromPackage(input, output, options) {
|
|
|
630
662
|
return 1;
|
|
631
663
|
}
|
|
632
664
|
if (isSchemaVersionMismatchError(error)) {
|
|
633
|
-
process.stderr.write(`[${command}] '${input}' is a
|
|
665
|
+
process.stderr.write(`[${command}] '${input}' is a DocumentTree dump from document-schema.js@${error.dumpVersion}, but this CLI's documents.js reads only @${error.installedVersion}-major dumps -- 4.0.0 replaced the flat { formatVersion, content, pages } shape with the tree-form DocumentTree (ExaDev/document-schema.js#20); re-run the source conversion with --dump-package to write a current dump\n`);
|
|
634
666
|
return 1;
|
|
635
667
|
}
|
|
636
668
|
if (isLayoutSchemaDemotedError(error)) {
|
|
637
669
|
process.stderr.write(`[${command}] '${input}' is a LayoutDocument dump -- LayoutDocument moved to pdf-codec in document-schema.js 4.0.0 and is no longer a schema-stamped input; re-run the source conversion with --dump-package and read that package back instead\n`);
|
|
638
670
|
return 1;
|
|
639
671
|
}
|
|
672
|
+
if (isDocumentPackageRenamedError(error)) {
|
|
673
|
+
process.stderr.write(`[${command}] '${input}' is a document-package dump -- DocumentPackage was renamed to DocumentTree in document-schema.js 5.0.0 (ExaDev/documents.js#661); re-run the source conversion with --dump-package to write a current tree-form DocumentTree dump\n`);
|
|
674
|
+
return 1;
|
|
675
|
+
}
|
|
640
676
|
process.stderr.write(`${formatError(error, options.verbose)}\n`);
|
|
641
677
|
return mapErrorToExit(error, getAbortReason());
|
|
642
678
|
}
|
|
643
679
|
}
|
|
644
680
|
function registerFromPackageCommand(program) {
|
|
645
|
-
const command = program.command("from-package <input> [output]").description("read a
|
|
681
|
+
const command = program.command("from-package <input> [output]").description("read a DocumentTree previously written by --dump-package and export it to a real target format");
|
|
646
682
|
addOutOption(command);
|
|
647
683
|
addTimeoutOption(command);
|
|
648
684
|
addJsonOption(command);
|
|
@@ -1290,20 +1326,6 @@ function registerOdmCommand(program) {
|
|
|
1290
1326
|
}
|
|
1291
1327
|
//#endregion
|
|
1292
1328
|
//#region src/commands/outline.ts
|
|
1293
|
-
const OUTLINE_CONVERSION_TARGET = {
|
|
1294
|
-
docx: "odt",
|
|
1295
|
-
odt: "docx",
|
|
1296
|
-
markdown: "docx",
|
|
1297
|
-
pptx: "odp",
|
|
1298
|
-
odp: "pptx",
|
|
1299
|
-
xlsx: "ods",
|
|
1300
|
-
ods: "xlsx",
|
|
1301
|
-
csv: "xlsx",
|
|
1302
|
-
odg: "odp",
|
|
1303
|
-
svg: "odg",
|
|
1304
|
-
pdf: "docx",
|
|
1305
|
-
odf: "pdf"
|
|
1306
|
-
};
|
|
1307
1329
|
const INDENT = " ";
|
|
1308
1330
|
function singleLineText(text) {
|
|
1309
1331
|
return text.replaceAll(/\s+/g, " ").trim();
|
|
@@ -1339,7 +1361,6 @@ async function runOutline(input, options) {
|
|
|
1339
1361
|
process.stderr.write(`[${command}] ${source.errorMessage}\n`);
|
|
1340
1362
|
return 2;
|
|
1341
1363
|
}
|
|
1342
|
-
const target = OUTLINE_CONVERSION_TARGET[source.format];
|
|
1343
1364
|
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
1344
1365
|
const reporter = createDiagnosticReporter({
|
|
1345
1366
|
json: options.json,
|
|
@@ -1348,19 +1369,14 @@ async function runOutline(input, options) {
|
|
|
1348
1369
|
});
|
|
1349
1370
|
try {
|
|
1350
1371
|
const inputBytes = await readInput(input, { signal });
|
|
1351
|
-
const
|
|
1352
|
-
source: {
|
|
1353
|
-
format: source.format,
|
|
1354
|
-
bytes: new Uint8Array(inputBytes)
|
|
1355
|
-
},
|
|
1356
|
-
targetFormat: target
|
|
1357
|
-
}, {
|
|
1372
|
+
const tree = (0, documents_js.readNativeDocumentTree)(source.format, new Uint8Array(inputBytes), {
|
|
1358
1373
|
signal,
|
|
1359
|
-
images: createFilesystemMarkdownImageResolver(input === "-" ? "." : (0, node_path.dirname)((0, node_path.resolve)(input)))
|
|
1374
|
+
images: createFilesystemMarkdownImageResolver(input === "-" ? "." : (0, node_path.dirname)((0, node_path.resolve)(input))),
|
|
1375
|
+
sink: (diagnostic) => {
|
|
1376
|
+
reporter.report(diagnostic);
|
|
1377
|
+
}
|
|
1360
1378
|
});
|
|
1361
|
-
|
|
1362
|
-
if (result.package === void 0) throw new Error(`the ${source.format}-to-${target} conversion produced no intermediate DocumentPackage`);
|
|
1363
|
-
const outline = (0, document_outline_js.buildOutline)(result.package);
|
|
1379
|
+
const outline = (0, document_outline_js.buildOutline)(tree);
|
|
1364
1380
|
if (options.json) {
|
|
1365
1381
|
process.stdout.write(`${JSON.stringify(outline, void 0, 2)}\n`);
|
|
1366
1382
|
return 0;
|
|
@@ -1508,11 +1524,11 @@ async function runSetMetadata(input, output, options) {
|
|
|
1508
1524
|
function registerSetMetadataCommand(program) {
|
|
1509
1525
|
const command = program.command("set-metadata <input> [output]").description("patch a document's own title/author/subject/keywords, leaving every other field and every other flag as-is").addHelpText("after", [
|
|
1510
1526
|
"",
|
|
1511
|
-
"
|
|
1512
|
-
"
|
|
1513
|
-
"
|
|
1514
|
-
"
|
|
1515
|
-
"
|
|
1527
|
+
"Three write paths: a pdf source/target patches the metadata directly on the parsed PDF (writePdf), and a docx source/target",
|
|
1528
|
+
"patches docProps/core.xml directly on the decoded package -- both with no layout engine or ContentDocument rebuild involved",
|
|
1529
|
+
"at all, so everything else on the page (pdf) or in the package (docx -- comments, footnotes, headers/footers, numbering",
|
|
1530
|
+
"definitions included) survives byte-faithful. Every other supported format (pptx, xlsx, odt, odp, ods, odg, markdown, rtf)",
|
|
1531
|
+
"rebuilds a fresh package from that format's own ContentDocument instead.",
|
|
1516
1532
|
"",
|
|
1517
1533
|
"set-metadata does not convert format -- source and target must match. Run convert/from-package first, then",
|
|
1518
1534
|
"set-metadata on the result, if you need a different target format."
|
|
@@ -1533,12 +1549,12 @@ function registerSetMetadataCommand(program) {
|
|
|
1533
1549
|
}
|
|
1534
1550
|
//#endregion
|
|
1535
1551
|
//#region package.json
|
|
1536
|
-
var version = "
|
|
1552
|
+
var version = "5.7.10";
|
|
1537
1553
|
//#endregion
|
|
1538
1554
|
//#region src/program.ts
|
|
1539
1555
|
function createProgram() {
|
|
1540
1556
|
const program = new commander.Command("document-cli");
|
|
1541
|
-
program.description("every documents.js docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/csv/svg/markdown conversion, bridge, and inspector as a scriptable command");
|
|
1557
|
+
program.description("every documents.js docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/csv/svg/markdown/rtf conversion, bridge, and inspector as a scriptable command");
|
|
1542
1558
|
program.version(version);
|
|
1543
1559
|
program.exitOverride((error) => {
|
|
1544
1560
|
process.exitCode = error.exitCode === 0 ? 0 : 2;
|
package/dist/index.d.cts
CHANGED
|
@@ -32,6 +32,6 @@ declare const EXIT_USAGE_ERROR = 2;
|
|
|
32
32
|
declare const EXIT_NEEDS_INFO = 3;
|
|
33
33
|
declare const EXIT_TIMEOUT = 124;
|
|
34
34
|
declare const EXIT_INTERRUPTED = 130;
|
|
35
|
-
declare function mapErrorToExit(error: unknown, abortReason:
|
|
35
|
+
declare function mapErrorToExit(error: unknown, abortReason: "interrupt" | "timeout" | undefined): number;
|
|
36
36
|
//#endregion
|
|
37
37
|
export { type ConversionCommandOptions, EXIT_INPUT_ERROR, EXIT_INTERRUPTED, EXIT_NEEDS_INFO, EXIT_SUCCESS, EXIT_TIMEOUT, EXIT_USAGE_ERROR, buildConversionAction, createProgram, formatError, formatToExtension, inferFormatFromExtension, isDocumentFormat, mapErrorToExit };
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,6 @@ declare const EXIT_USAGE_ERROR = 2;
|
|
|
32
32
|
declare const EXIT_NEEDS_INFO = 3;
|
|
33
33
|
declare const EXIT_TIMEOUT = 124;
|
|
34
34
|
declare const EXIT_INTERRUPTED = 130;
|
|
35
|
-
declare function mapErrorToExit(error: unknown, abortReason:
|
|
35
|
+
declare function mapErrorToExit(error: unknown, abortReason: "interrupt" | "timeout" | undefined): number;
|
|
36
36
|
//#endregion
|
|
37
37
|
export { type ConversionCommandOptions, EXIT_INPUT_ERROR, EXIT_INTERRUPTED, EXIT_NEEDS_INFO, EXIT_SUCCESS, EXIT_TIMEOUT, EXIT_USAGE_ERROR, buildConversionAction, createProgram, formatError, formatToExtension, inferFormatFromExtension, isDocumentFormat, mapErrorToExit };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
3
|
-
import { CsvSheetNotFoundError, CsvSheetNotSpecifiedError, HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, SvgMultiPageNotSpecifiedError, SvgPageNotFoundError, UnrecognizedDocumentSchemaError, UnsupportedFontSourceFormatError, buildCsvText, buildDocumentBytes, buildSvgText, createLocalDocumentConverter, decodeOdbPackage, decodePackage, describeFontFace, documentFromJson,
|
|
3
|
+
import { CsvSheetNotFoundError, CsvSheetNotSpecifiedError, HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, SvgMultiPageNotSpecifiedError, SvgPageNotFoundError, UnrecognizedDocumentSchemaError, UnsupportedFontSourceFormatError, buildCsvText, buildDocumentBytes, buildSvgText, createLocalDocumentConverter, decodeOdbPackage, decodePackage, describeFontFace, documentFromJson, documentTreeWithSchema, encodeCsvText, encodeSvgText, evaluateSelect, extractSourceFontsForFormat, hsqldbCellDisplayText, odbReportToDocx, odbReportToOdt, odbReportToPdf, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readDocumentMetadata, readDocxExtras, readNativeDocumentTree, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, setDocumentMetadata } from "documents.js";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { Command, InvalidArgumentError } from "commander";
|
|
6
|
+
import { flattenTree } from "document-schema.js";
|
|
6
7
|
import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.js";
|
|
7
8
|
//#region src/format.ts
|
|
8
9
|
const EXTENSION_TO_FORMAT = {
|
|
@@ -29,6 +30,12 @@ const EXTENSION_TO_FORMAT = {
|
|
|
29
30
|
svg: "svg",
|
|
30
31
|
markdown: "markdown",
|
|
31
32
|
md: "markdown",
|
|
33
|
+
rtf: "rtf",
|
|
34
|
+
wpd: "wpd",
|
|
35
|
+
doc: "doc",
|
|
36
|
+
xls: "xls",
|
|
37
|
+
ppt: "ppt",
|
|
38
|
+
epub: "epub",
|
|
32
39
|
pdf: "pdf"
|
|
33
40
|
};
|
|
34
41
|
const FORMAT_TO_EXTENSION = {
|
|
@@ -43,6 +50,12 @@ const FORMAT_TO_EXTENSION = {
|
|
|
43
50
|
csv: "csv",
|
|
44
51
|
svg: "svg",
|
|
45
52
|
markdown: "md",
|
|
53
|
+
rtf: "rtf",
|
|
54
|
+
wpd: "wpd",
|
|
55
|
+
doc: "doc",
|
|
56
|
+
xls: "xls",
|
|
57
|
+
ppt: "ppt",
|
|
58
|
+
epub: "epub",
|
|
46
59
|
pdf: "pdf"
|
|
47
60
|
};
|
|
48
61
|
function isDocumentFormat(value) {
|
|
@@ -67,9 +80,13 @@ function combineSignals(a, b) {
|
|
|
67
80
|
controller.abort(signal.reason);
|
|
68
81
|
};
|
|
69
82
|
if (a.aborted) forward(a);
|
|
70
|
-
else a.addEventListener("abort", () =>
|
|
83
|
+
else a.addEventListener("abort", () => {
|
|
84
|
+
forward(a);
|
|
85
|
+
}, { once: true });
|
|
71
86
|
if (b.aborted) forward(b);
|
|
72
|
-
else b.addEventListener("abort", () =>
|
|
87
|
+
else b.addEventListener("abort", () => {
|
|
88
|
+
forward(b);
|
|
89
|
+
}, { once: true });
|
|
73
90
|
return controller.signal;
|
|
74
91
|
}
|
|
75
92
|
function createRuntimeSignal(options) {
|
|
@@ -251,7 +268,7 @@ function resolveDefaultOutputPath(inputPath, targetFormat) {
|
|
|
251
268
|
}
|
|
252
269
|
//#endregion
|
|
253
270
|
//#region src/commands/shared.ts
|
|
254
|
-
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, pdf";
|
|
271
|
+
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, rtf, wpd, doc, xls, ppt, epub, pdf";
|
|
255
272
|
function resolveTargetFormat(output, out, to) {
|
|
256
273
|
if (to !== void 0) {
|
|
257
274
|
if (!isDocumentFormat(to)) return { errorMessage: `unknown --to format '${to}'; expected one of ${KNOWN_DOCUMENT_FORMATS}` };
|
|
@@ -280,6 +297,7 @@ function buildConversionAction(source, target) {
|
|
|
280
297
|
try {
|
|
281
298
|
const inputBytes = await readInput(input, { signal });
|
|
282
299
|
const fonts = await loadProvidedFonts(options.fontFiles ?? [], { signal });
|
|
300
|
+
const images = createFilesystemMarkdownImageResolver(input === "-" ? "." : dirname(resolve(input)));
|
|
283
301
|
const result = await createLocalDocumentConverter().convert({
|
|
284
302
|
source: {
|
|
285
303
|
format: source,
|
|
@@ -294,7 +312,7 @@ function buildConversionAction(source, target) {
|
|
|
294
312
|
quiet: options.quiet,
|
|
295
313
|
command
|
|
296
314
|
}) : void 0,
|
|
297
|
-
images
|
|
315
|
+
images,
|
|
298
316
|
delimiter: options.delimiter,
|
|
299
317
|
sheet: options.sheet,
|
|
300
318
|
page: options.page
|
|
@@ -306,8 +324,13 @@ function buildConversionAction(source, target) {
|
|
|
306
324
|
command
|
|
307
325
|
});
|
|
308
326
|
for (const diagnostic of result.diagnostics) reporter.report(diagnostic);
|
|
309
|
-
if (options.dumpPackage !== void 0)
|
|
310
|
-
|
|
327
|
+
if (options.dumpPackage !== void 0) {
|
|
328
|
+
const nativeTree = readNativeDocumentTree(source, new Uint8Array(inputBytes), {
|
|
329
|
+
signal,
|
|
330
|
+
images
|
|
331
|
+
});
|
|
332
|
+
await writeFile(options.dumpPackage, JSON.stringify(documentTreeWithSchema(nativeTree), void 0, 2));
|
|
333
|
+
}
|
|
311
334
|
reporter.summarize({
|
|
312
335
|
output: resolvedOutput,
|
|
313
336
|
bytes: result.document.bytes.byteLength,
|
|
@@ -338,7 +361,7 @@ function addVerboseOption(command) {
|
|
|
338
361
|
return command.option("--verbose", "include a full stack trace when the run fails", false);
|
|
339
362
|
}
|
|
340
363
|
function addDumpPackageOption(command) {
|
|
341
|
-
return command.option("--dump-package <file>", "write the intermediate
|
|
364
|
+
return command.option("--dump-package <file>", "write the intermediate DocumentTree (the tree form: container-grouped content carrying per-node rendered frames, plus page sizes) this conversion built to a JSON file");
|
|
342
365
|
}
|
|
343
366
|
function collectFontFile(value, previous) {
|
|
344
367
|
return [...previous, value];
|
|
@@ -453,9 +476,15 @@ function footnotesSection(footnotes) {
|
|
|
453
476
|
if (footnotes.length === 0) return [];
|
|
454
477
|
return ["footnotes", ...footnotes.map((footnote, index) => footnoteLine(footnote, index + 1))];
|
|
455
478
|
}
|
|
456
|
-
function
|
|
457
|
-
|
|
458
|
-
|
|
479
|
+
function partText(blocks) {
|
|
480
|
+
let text = "";
|
|
481
|
+
for (const block of blocks) if (block.kind === "paragraph") text += block.runs.map((run) => run.text).join("");
|
|
482
|
+
else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) text += partText(cell.blocks);
|
|
483
|
+
return text;
|
|
484
|
+
}
|
|
485
|
+
function headerOrFooterSection(label, parts) {
|
|
486
|
+
if (parts.length === 0) return [];
|
|
487
|
+
return [label, ...parts.map((part, index) => `${indent$1(1)}[${index + 1}] ${partText(part.blocks)}`)];
|
|
459
488
|
}
|
|
460
489
|
function numberingLevelLine(ilvl, level) {
|
|
461
490
|
const restartSuffix = level.restart === void 0 ? "" : `, restarts at level ${level.restart}`;
|
|
@@ -482,8 +511,8 @@ function formatDocxExtrasLines(extras) {
|
|
|
482
511
|
const nonEmptySections = [
|
|
483
512
|
commentsSection(extras.comments),
|
|
484
513
|
footnotesSection(extras.footnotes),
|
|
485
|
-
headerOrFooterSection("headers", extras.
|
|
486
|
-
headerOrFooterSection("footers", extras.
|
|
514
|
+
headerOrFooterSection("headers", extras.headerFooterParts.filter((part) => part.kind === "header")),
|
|
515
|
+
headerOrFooterSection("footers", extras.headerFooterParts.filter((part) => part.kind === "footer")),
|
|
487
516
|
numberingSection(extras.numbering)
|
|
488
517
|
].filter((section) => section.length > 0);
|
|
489
518
|
if (nonEmptySections.length === 0) return ["This document carries no comments, footnotes, headers, footers, or numbering definitions."];
|
|
@@ -579,6 +608,9 @@ function isSchemaVersionMismatchError(error) {
|
|
|
579
608
|
function isLayoutSchemaDemotedError(error) {
|
|
580
609
|
return error instanceof Error && error.name === "LayoutSchemaDemotedError";
|
|
581
610
|
}
|
|
611
|
+
function isDocumentPackageRenamedError(error) {
|
|
612
|
+
return error instanceof Error && error.name === "DocumentPackageRenamedError";
|
|
613
|
+
}
|
|
582
614
|
async function runFromPackage(input, output, options) {
|
|
583
615
|
const command = "from-package";
|
|
584
616
|
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
@@ -603,11 +635,11 @@ async function runFromPackage(input, output, options) {
|
|
|
603
635
|
return 1;
|
|
604
636
|
}
|
|
605
637
|
const result = documentFromJson(parsed);
|
|
606
|
-
if (result.kind !== "
|
|
607
|
-
process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a
|
|
638
|
+
if (result.kind !== "DocumentTree") {
|
|
639
|
+
process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a DocumentTree -- only a file written by --dump-package can be read back by this command\n`);
|
|
608
640
|
return 2;
|
|
609
641
|
}
|
|
610
|
-
const content =
|
|
642
|
+
const content = flattenTree(result.value);
|
|
611
643
|
const bytes = target.format === "csv" ? encodeCsvText(buildCsvText(content, {
|
|
612
644
|
delimiter: options.delimiter,
|
|
613
645
|
sheet: options.sheet
|
|
@@ -629,19 +661,23 @@ async function runFromPackage(input, output, options) {
|
|
|
629
661
|
return 1;
|
|
630
662
|
}
|
|
631
663
|
if (isSchemaVersionMismatchError(error)) {
|
|
632
|
-
process.stderr.write(`[${command}] '${input}' is a
|
|
664
|
+
process.stderr.write(`[${command}] '${input}' is a DocumentTree dump from document-schema.js@${error.dumpVersion}, but this CLI's documents.js reads only @${error.installedVersion}-major dumps -- 4.0.0 replaced the flat { formatVersion, content, pages } shape with the tree-form DocumentTree (ExaDev/document-schema.js#20); re-run the source conversion with --dump-package to write a current dump\n`);
|
|
633
665
|
return 1;
|
|
634
666
|
}
|
|
635
667
|
if (isLayoutSchemaDemotedError(error)) {
|
|
636
668
|
process.stderr.write(`[${command}] '${input}' is a LayoutDocument dump -- LayoutDocument moved to pdf-codec in document-schema.js 4.0.0 and is no longer a schema-stamped input; re-run the source conversion with --dump-package and read that package back instead\n`);
|
|
637
669
|
return 1;
|
|
638
670
|
}
|
|
671
|
+
if (isDocumentPackageRenamedError(error)) {
|
|
672
|
+
process.stderr.write(`[${command}] '${input}' is a document-package dump -- DocumentPackage was renamed to DocumentTree in document-schema.js 5.0.0 (ExaDev/documents.js#661); re-run the source conversion with --dump-package to write a current tree-form DocumentTree dump\n`);
|
|
673
|
+
return 1;
|
|
674
|
+
}
|
|
639
675
|
process.stderr.write(`${formatError(error, options.verbose)}\n`);
|
|
640
676
|
return mapErrorToExit(error, getAbortReason());
|
|
641
677
|
}
|
|
642
678
|
}
|
|
643
679
|
function registerFromPackageCommand(program) {
|
|
644
|
-
const command = program.command("from-package <input> [output]").description("read a
|
|
680
|
+
const command = program.command("from-package <input> [output]").description("read a DocumentTree previously written by --dump-package and export it to a real target format");
|
|
645
681
|
addOutOption(command);
|
|
646
682
|
addTimeoutOption(command);
|
|
647
683
|
addJsonOption(command);
|
|
@@ -1289,20 +1325,6 @@ function registerOdmCommand(program) {
|
|
|
1289
1325
|
}
|
|
1290
1326
|
//#endregion
|
|
1291
1327
|
//#region src/commands/outline.ts
|
|
1292
|
-
const OUTLINE_CONVERSION_TARGET = {
|
|
1293
|
-
docx: "odt",
|
|
1294
|
-
odt: "docx",
|
|
1295
|
-
markdown: "docx",
|
|
1296
|
-
pptx: "odp",
|
|
1297
|
-
odp: "pptx",
|
|
1298
|
-
xlsx: "ods",
|
|
1299
|
-
ods: "xlsx",
|
|
1300
|
-
csv: "xlsx",
|
|
1301
|
-
odg: "odp",
|
|
1302
|
-
svg: "odg",
|
|
1303
|
-
pdf: "docx",
|
|
1304
|
-
odf: "pdf"
|
|
1305
|
-
};
|
|
1306
1328
|
const INDENT = " ";
|
|
1307
1329
|
function singleLineText(text) {
|
|
1308
1330
|
return text.replaceAll(/\s+/g, " ").trim();
|
|
@@ -1338,7 +1360,6 @@ async function runOutline(input, options) {
|
|
|
1338
1360
|
process.stderr.write(`[${command}] ${source.errorMessage}\n`);
|
|
1339
1361
|
return 2;
|
|
1340
1362
|
}
|
|
1341
|
-
const target = OUTLINE_CONVERSION_TARGET[source.format];
|
|
1342
1363
|
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
1343
1364
|
const reporter = createDiagnosticReporter({
|
|
1344
1365
|
json: options.json,
|
|
@@ -1347,19 +1368,14 @@ async function runOutline(input, options) {
|
|
|
1347
1368
|
});
|
|
1348
1369
|
try {
|
|
1349
1370
|
const inputBytes = await readInput(input, { signal });
|
|
1350
|
-
const
|
|
1351
|
-
source: {
|
|
1352
|
-
format: source.format,
|
|
1353
|
-
bytes: new Uint8Array(inputBytes)
|
|
1354
|
-
},
|
|
1355
|
-
targetFormat: target
|
|
1356
|
-
}, {
|
|
1371
|
+
const tree = readNativeDocumentTree(source.format, new Uint8Array(inputBytes), {
|
|
1357
1372
|
signal,
|
|
1358
|
-
images: createFilesystemMarkdownImageResolver(input === "-" ? "." : dirname(resolve(input)))
|
|
1373
|
+
images: createFilesystemMarkdownImageResolver(input === "-" ? "." : dirname(resolve(input))),
|
|
1374
|
+
sink: (diagnostic) => {
|
|
1375
|
+
reporter.report(diagnostic);
|
|
1376
|
+
}
|
|
1359
1377
|
});
|
|
1360
|
-
|
|
1361
|
-
if (result.package === void 0) throw new Error(`the ${source.format}-to-${target} conversion produced no intermediate DocumentPackage`);
|
|
1362
|
-
const outline = buildOutline(result.package);
|
|
1378
|
+
const outline = buildOutline(tree);
|
|
1363
1379
|
if (options.json) {
|
|
1364
1380
|
process.stdout.write(`${JSON.stringify(outline, void 0, 2)}\n`);
|
|
1365
1381
|
return 0;
|
|
@@ -1507,11 +1523,11 @@ async function runSetMetadata(input, output, options) {
|
|
|
1507
1523
|
function registerSetMetadataCommand(program) {
|
|
1508
1524
|
const command = program.command("set-metadata <input> [output]").description("patch a document's own title/author/subject/keywords, leaving every other field and every other flag as-is").addHelpText("after", [
|
|
1509
1525
|
"",
|
|
1510
|
-
"
|
|
1511
|
-
"
|
|
1512
|
-
"
|
|
1513
|
-
"
|
|
1514
|
-
"
|
|
1526
|
+
"Three write paths: a pdf source/target patches the metadata directly on the parsed PDF (writePdf), and a docx source/target",
|
|
1527
|
+
"patches docProps/core.xml directly on the decoded package -- both with no layout engine or ContentDocument rebuild involved",
|
|
1528
|
+
"at all, so everything else on the page (pdf) or in the package (docx -- comments, footnotes, headers/footers, numbering",
|
|
1529
|
+
"definitions included) survives byte-faithful. Every other supported format (pptx, xlsx, odt, odp, ods, odg, markdown, rtf)",
|
|
1530
|
+
"rebuilds a fresh package from that format's own ContentDocument instead.",
|
|
1515
1531
|
"",
|
|
1516
1532
|
"set-metadata does not convert format -- source and target must match. Run convert/from-package first, then",
|
|
1517
1533
|
"set-metadata on the result, if you need a different target format."
|
|
@@ -1532,12 +1548,12 @@ function registerSetMetadataCommand(program) {
|
|
|
1532
1548
|
}
|
|
1533
1549
|
//#endregion
|
|
1534
1550
|
//#region package.json
|
|
1535
|
-
var version = "
|
|
1551
|
+
var version = "5.7.10";
|
|
1536
1552
|
//#endregion
|
|
1537
1553
|
//#region src/program.ts
|
|
1538
1554
|
function createProgram() {
|
|
1539
1555
|
const program = new Command("document-cli");
|
|
1540
|
-
program.description("every documents.js docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/csv/svg/markdown conversion, bridge, and inspector as a scriptable command");
|
|
1556
|
+
program.description("every documents.js docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/csv/svg/markdown/rtf conversion, bridge, and inspector as a scriptable command");
|
|
1541
1557
|
program.version(version);
|
|
1542
1558
|
program.exitOverride((error) => {
|
|
1543
1559
|
process.exitCode = error.exitCode === 0 ? 0 : 2;
|
|
@@ -26,6 +26,12 @@ const EXTENSION_TO_FORMAT = {
|
|
|
26
26
|
svg: "svg",
|
|
27
27
|
markdown: "markdown",
|
|
28
28
|
md: "markdown",
|
|
29
|
+
rtf: "rtf",
|
|
30
|
+
wpd: "wpd",
|
|
31
|
+
doc: "doc",
|
|
32
|
+
xls: "xls",
|
|
33
|
+
ppt: "ppt",
|
|
34
|
+
epub: "epub",
|
|
29
35
|
pdf: "pdf"
|
|
30
36
|
};
|
|
31
37
|
const FORMAT_TO_EXTENSION = {
|
|
@@ -40,6 +46,12 @@ const FORMAT_TO_EXTENSION = {
|
|
|
40
46
|
csv: "csv",
|
|
41
47
|
svg: "svg",
|
|
42
48
|
markdown: "md",
|
|
49
|
+
rtf: "rtf",
|
|
50
|
+
wpd: "wpd",
|
|
51
|
+
doc: "doc",
|
|
52
|
+
xls: "xls",
|
|
53
|
+
ppt: "ppt",
|
|
54
|
+
epub: "epub",
|
|
43
55
|
pdf: "pdf"
|
|
44
56
|
};
|
|
45
57
|
function isDocumentFormat(value) {
|
|
@@ -133,9 +145,15 @@ function footnotesSection(footnotes) {
|
|
|
133
145
|
if (footnotes.length === 0) return [];
|
|
134
146
|
return ["footnotes", ...footnotes.map((footnote, index) => footnoteLine(footnote, index + 1))];
|
|
135
147
|
}
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
148
|
+
function partText(blocks) {
|
|
149
|
+
let text = "";
|
|
150
|
+
for (const block of blocks) if (block.kind === "paragraph") text += block.runs.map((run) => run.text).join("");
|
|
151
|
+
else if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) text += partText(cell.blocks);
|
|
152
|
+
return text;
|
|
153
|
+
}
|
|
154
|
+
function headerOrFooterSection(label, parts) {
|
|
155
|
+
if (parts.length === 0) return [];
|
|
156
|
+
return [label, ...parts.map((part, index) => `${indent$1(1)}[${index + 1}] ${partText(part.blocks)}`)];
|
|
139
157
|
}
|
|
140
158
|
function numberingLevelLine(ilvl, level) {
|
|
141
159
|
const restartSuffix = level.restart === void 0 ? "" : `, restarts at level ${level.restart}`;
|
|
@@ -162,8 +180,8 @@ function formatDocxExtrasLines(extras) {
|
|
|
162
180
|
const nonEmptySections = [
|
|
163
181
|
commentsSection(extras.comments),
|
|
164
182
|
footnotesSection(extras.footnotes),
|
|
165
|
-
headerOrFooterSection("headers", extras.
|
|
166
|
-
headerOrFooterSection("footers", extras.
|
|
183
|
+
headerOrFooterSection("headers", extras.headerFooterParts.filter((part) => part.kind === "header")),
|
|
184
|
+
headerOrFooterSection("footers", extras.headerFooterParts.filter((part) => part.kind === "footer")),
|
|
167
185
|
numberingSection(extras.numbering)
|
|
168
186
|
].filter((section) => section.length > 0);
|
|
169
187
|
if (nonEmptySections.length === 0) return ["This document carries no comments, footnotes, headers, footers, or numbering definitions."];
|
|
@@ -326,4 +344,4 @@ function formatOdbReportLines(report) {
|
|
|
326
344
|
return lines;
|
|
327
345
|
}
|
|
328
346
|
//#endregion
|
|
329
|
-
export { odbFormSummary as a,
|
|
347
|
+
export { odbFormSummary as a, presentMetadataEntries as c, resolveDefaultOutputPath as d, writeOutput as f, isDocumentFormat as g, inferFormatFromExtension as h, formatOdbReportLines as i, formatDocxExtrasLines as l, formatToExtension as m, describeOdbReport as n, formatMetadataLines as o, loadProvidedFonts as p, formatOdbFormLines as r, formatMetadataValue as s, describeOdbForm as t, readInput as u };
|