document-cli 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +305 -30
- package/dist/index.cjs +378 -21
- package/dist/index.js +385 -28
- package/dist/{odb-structure-CX_0zL8_.js → odb-structure-Du1w_hOU.js} +83 -1
- package/dist/{tui-C8ITn1zQ.js → tui-B-o3iI9a.js} +161 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as odbFormSummary, c as
|
|
2
|
+
import { a as odbFormSummary, c as formatDocxExtrasLines, d as writeOutput, f as loadProvidedFonts, h as isDocumentFormat, i as formatOdbReportLines, l as readInput, m as inferFormatFromExtension, n as describeOdbReport, o as formatMetadataLines, r as formatOdbFormLines, s as presentMetadataEntries, t as describeOdbForm, u as resolveDefaultOutputPath } from "./odb-structure-Du1w_hOU.js";
|
|
3
3
|
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
4
4
|
import { writeFile } from "node:fs/promises";
|
|
5
|
-
import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, convertWordprocessingToLayout, createFontMeasurer, createFontRegistry, createLocalDocumentConverter, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, evaluateSelect, hsqldbCellDisplayText, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, writePdf } from "documents.js";
|
|
5
|
+
import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, convertWordprocessingToLayout, createFontMeasurer, createFontRegistry, createLocalDocumentConverter, decodeMarkdownText, decodePackage, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, evaluateSelect, extractSourceFonts, hsqldbCellDisplayText, layoutDocumentWithSchema, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readDocxContent, readDocxExtras, readMarkdownContent, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readOdfFormulaContent, readOdgContent, readOdpContent, readOdsContent, readOdtContent, readPdf, readPptxContent, writePdf, xlsxToPdf } from "documents.js";
|
|
6
6
|
import { basename, dirname, extname, join } from "node:path";
|
|
7
|
-
import { decodePackage, encodePackage as encodePackage$1 } from "odf.js";
|
|
7
|
+
import { decodePackage as decodePackage$1, encodePackage as encodePackage$1 } from "odf.js";
|
|
8
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
9
|
//#region src/runtime/abort.ts
|
|
10
10
|
function combineSignals(a, b) {
|
|
@@ -280,8 +280,100 @@ function registerConversionCommands(program) {
|
|
|
280
280
|
});
|
|
281
281
|
}
|
|
282
282
|
//#endregion
|
|
283
|
+
//#region src/commands/docx-extras.ts
|
|
284
|
+
async function runDocxExtras(input, options) {
|
|
285
|
+
const command = "docx-extras";
|
|
286
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
287
|
+
try {
|
|
288
|
+
const inputBytes = await readInput(input, { signal });
|
|
289
|
+
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
290
|
+
const extras = readDocxExtras(pkg);
|
|
291
|
+
if (options.json) {
|
|
292
|
+
process.stdout.write(`${JSON.stringify(extras)}\n`);
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
for (const line of formatDocxExtrasLines(extras)) process.stdout.write(`${line}\n`);
|
|
296
|
+
return 0;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
process.stderr.write(`[${command}] ${formatError(error, false)}\n`);
|
|
299
|
+
return mapErrorToExit(error, getAbortReason());
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function registerDocxExtrasCommand(program) {
|
|
303
|
+
program.command("docx-extras <input>").description("print a docx's own comments, footnotes, headers, footers, and numbering definitions -- data readDocxContent's ContentDocument cannot carry").option("--json", "emit the raw DocxExtras object as JSON instead of a human-readable report", false).action(async (input, options) => {
|
|
304
|
+
process.exitCode = await runDocxExtras(input, options);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/commands/fonts.ts
|
|
309
|
+
const FONT_SOURCE_FORMATS = {
|
|
310
|
+
docx: true,
|
|
311
|
+
pptx: true,
|
|
312
|
+
odt: true,
|
|
313
|
+
odp: true,
|
|
314
|
+
ods: true,
|
|
315
|
+
odg: true
|
|
316
|
+
};
|
|
317
|
+
function isFontSourceFormat(format) {
|
|
318
|
+
return format in FONT_SOURCE_FORMATS;
|
|
319
|
+
}
|
|
320
|
+
function resolveFontSourcePackage(format, bytes) {
|
|
321
|
+
if (format === "docx" || format === "pptx") return {
|
|
322
|
+
kind: format,
|
|
323
|
+
package: decodePackage(bytes)
|
|
324
|
+
};
|
|
325
|
+
return {
|
|
326
|
+
kind: "odf",
|
|
327
|
+
package: decodePackage$1(bytes)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async function runFonts(input, options) {
|
|
331
|
+
const command = "fonts";
|
|
332
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
333
|
+
try {
|
|
334
|
+
const format = inferFormatFromExtension(input);
|
|
335
|
+
if (format === void 0) {
|
|
336
|
+
process.stderr.write(`[${command}] cannot infer a document format from '${input}'; expected one of docx, pptx, odt, odp, ods, odg\n`);
|
|
337
|
+
return 2;
|
|
338
|
+
}
|
|
339
|
+
if (!isFontSourceFormat(format)) {
|
|
340
|
+
process.stderr.write(`[${command}] '${format}' documents carry no source-embedded font faces this command can extract; expected one of docx, pptx, odt, odp, ods, odg\n`);
|
|
341
|
+
return 2;
|
|
342
|
+
}
|
|
343
|
+
const inputBytes = await readInput(input, { signal });
|
|
344
|
+
const source = resolveFontSourcePackage(format, new Uint8Array(inputBytes));
|
|
345
|
+
const summaries = extractSourceFonts(source).map((face) => ({
|
|
346
|
+
family: face.family,
|
|
347
|
+
bold: face.bold,
|
|
348
|
+
italic: face.italic,
|
|
349
|
+
byteLength: face.bytes.length
|
|
350
|
+
}));
|
|
351
|
+
if (options.json) {
|
|
352
|
+
process.stdout.write(`${JSON.stringify(summaries)}\n`);
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
if (summaries.length === 0) {
|
|
356
|
+
process.stdout.write("This document embeds no source fonts.\n");
|
|
357
|
+
return 0;
|
|
358
|
+
}
|
|
359
|
+
for (const face of summaries) {
|
|
360
|
+
const style = [face.bold ? "bold" : void 0, face.italic ? "italic" : void 0].filter((value) => value !== void 0).join(" ");
|
|
361
|
+
process.stdout.write(`${face.family}${style === "" ? "" : ` (${style})`} -- ${face.byteLength} bytes\n`);
|
|
362
|
+
}
|
|
363
|
+
return 0;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
process.stderr.write(`${formatError(error, false)}\n`);
|
|
366
|
+
return mapErrorToExit(error, getAbortReason());
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function registerFontsCommand(program) {
|
|
370
|
+
program.command("fonts <input>").description("list every source-embedded font face a docx/pptx/odt/odp/ods/odg document carries (family, weight/style, byte length)").option("--json", "emit the face list as a JSON array instead of a human-readable report", false).action(async (input, options) => {
|
|
371
|
+
process.exitCode = await runFonts(input, options);
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
//#endregion
|
|
283
375
|
//#region src/commands/formats.ts
|
|
284
|
-
const COMMANDS_NOT_LISTED = "odm-to-pdf, odb-to-csv, odb-to-xlsx, odb-tables, odb-forms, odb-reports, pdf-inspect, from-package";
|
|
376
|
+
const COMMANDS_NOT_LISTED = "odm-to-pdf, odb-to-csv, odb-to-xlsx, odb-tables, odb-forms, odb-reports, pdf-inspect, from-package, fonts, docx-extras, metadata, set-metadata";
|
|
285
377
|
function registerFormatsCommand(program) {
|
|
286
378
|
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) => {
|
|
287
379
|
const { conversions } = createLocalDocumentConverter();
|
|
@@ -374,6 +466,54 @@ function registerFromPackageCommand(program) {
|
|
|
374
466
|
});
|
|
375
467
|
}
|
|
376
468
|
//#endregion
|
|
469
|
+
//#region src/commands/metadata.ts
|
|
470
|
+
function readMetadataForFormat(format, bytes, signal) {
|
|
471
|
+
switch (format) {
|
|
472
|
+
case "docx": return readDocxContent(decodePackage(bytes)).metadata;
|
|
473
|
+
case "pptx": return readPptxContent(decodePackage(bytes)).metadata;
|
|
474
|
+
case "odt": return readOdtContent(decodePackage$1(bytes)).metadata;
|
|
475
|
+
case "odp": return readOdpContent(decodePackage$1(bytes)).metadata;
|
|
476
|
+
case "ods": return readOdsContent(decodePackage$1(bytes)).metadata;
|
|
477
|
+
case "odg": return readOdgContent(decodePackage$1(bytes)).metadata;
|
|
478
|
+
case "odf": return readOdfFormulaContent(decodePackage$1(bytes)).metadata;
|
|
479
|
+
case "markdown": return readMarkdownContent(decodeMarkdownText(bytes)).metadata;
|
|
480
|
+
case "pdf": return readPdf(bytes, { signal }).metadata;
|
|
481
|
+
case "xlsx": return readPdf(xlsxToPdf(bytes, { signal }), { signal }).metadata;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function runMetadata(input, options) {
|
|
485
|
+
const command = "metadata";
|
|
486
|
+
const source = inferFormatFromExtension(input);
|
|
487
|
+
if (source === void 0) {
|
|
488
|
+
process.stderr.write(`[${command}] cannot infer a source format from '${input}'; rename the file with a recognised extension (${KNOWN_DOCUMENT_FORMATS})\n`);
|
|
489
|
+
return 2;
|
|
490
|
+
}
|
|
491
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
492
|
+
try {
|
|
493
|
+
const inputBytes = await readInput(input, { signal });
|
|
494
|
+
const metadata = readMetadataForFormat(source, new Uint8Array(inputBytes), signal);
|
|
495
|
+
if (options.json) {
|
|
496
|
+
process.stdout.write(`${JSON.stringify(metadata)}\n`);
|
|
497
|
+
return 0;
|
|
498
|
+
}
|
|
499
|
+
const lines = formatMetadataLines(metadata);
|
|
500
|
+
if (lines.length === 0) {
|
|
501
|
+
process.stdout.write("This document carries no metadata.\n");
|
|
502
|
+
return 0;
|
|
503
|
+
}
|
|
504
|
+
for (const line of lines) process.stdout.write(`${line}\n`);
|
|
505
|
+
return 0;
|
|
506
|
+
} catch (error) {
|
|
507
|
+
process.stderr.write(`[${command}] ${formatError(error, false)}\n`);
|
|
508
|
+
return mapErrorToExit(error, getAbortReason());
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function registerMetadataCommand(program) {
|
|
512
|
+
program.command("metadata <input>").description(`print a document's own title/author/subject/keywords/creator/producer/created/modified metadata (${KNOWN_DOCUMENT_FORMATS})`).option("--json", "emit the metadata as a JSON object instead of a human-readable report", false).action(async (input, options) => {
|
|
513
|
+
process.exitCode = await runMetadata(input, options);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
//#endregion
|
|
377
517
|
//#region src/sql-result-format.ts
|
|
378
518
|
const COLUMN_GAP = " ";
|
|
379
519
|
function columnWidths(columns, cells) {
|
|
@@ -483,7 +623,7 @@ async function runOdbTables(input, options) {
|
|
|
483
623
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
484
624
|
try {
|
|
485
625
|
const inputBytes = await readInput(input, { signal });
|
|
486
|
-
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
626
|
+
const pkg = decodePackage$1(new Uint8Array(inputBytes));
|
|
487
627
|
const tables = readOdbTables(pkg);
|
|
488
628
|
if (options.json) {
|
|
489
629
|
const summary = tables.map((table) => ({
|
|
@@ -528,7 +668,7 @@ async function runOdbQuery(input, options) {
|
|
|
528
668
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
529
669
|
try {
|
|
530
670
|
const inputBytes = await readInput(input, { signal });
|
|
531
|
-
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
671
|
+
const pkg = decodePackage$1(new Uint8Array(inputBytes));
|
|
532
672
|
const resolved = resolveQuerySql(pkg, options);
|
|
533
673
|
if ("errorMessage" in resolved) {
|
|
534
674
|
process.stderr.write(`[${command}] ${resolved.errorMessage}\n`);
|
|
@@ -550,7 +690,7 @@ async function runOdbForms(input, options) {
|
|
|
550
690
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
551
691
|
try {
|
|
552
692
|
const inputBytes = await readInput(input, { signal });
|
|
553
|
-
const forms = readOdbForms(decodePackage(new Uint8Array(inputBytes)));
|
|
693
|
+
const forms = readOdbForms(decodePackage$1(new Uint8Array(inputBytes)));
|
|
554
694
|
if (options.json) {
|
|
555
695
|
process.stdout.write(`${JSON.stringify(forms.map((form) => odbFormSummary(form)))}\n`);
|
|
556
696
|
return 0;
|
|
@@ -573,7 +713,7 @@ async function runOdbReports(input, options) {
|
|
|
573
713
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
574
714
|
try {
|
|
575
715
|
const inputBytes = await readInput(input, { signal });
|
|
576
|
-
const reports = readOdbReports(decodePackage(new Uint8Array(inputBytes)));
|
|
716
|
+
const reports = readOdbReports(decodePackage$1(new Uint8Array(inputBytes)));
|
|
577
717
|
if (options.json) {
|
|
578
718
|
process.stdout.write(`${JSON.stringify(reports)}\n`);
|
|
579
719
|
return 0;
|
|
@@ -657,7 +797,7 @@ async function runOdbRenderReport(input, output, options) {
|
|
|
657
797
|
try {
|
|
658
798
|
const inputBytes = await readInput(input, { signal });
|
|
659
799
|
const fonts = await loadProvidedFonts(options.fontFile ?? [], { signal });
|
|
660
|
-
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
800
|
+
const pkg = decodePackage$1(new Uint8Array(inputBytes));
|
|
661
801
|
const bytes = renderOdbReportBytes(readOdbReportContent(pkg, { report: options.report }), targetFormat, {
|
|
662
802
|
fonts,
|
|
663
803
|
signal,
|
|
@@ -845,12 +985,6 @@ function countImagesByFormat(images) {
|
|
|
845
985
|
for (const asset of Object.values(images)) counts.set(asset.format, (counts.get(asset.format) ?? 0) + 1);
|
|
846
986
|
return counts;
|
|
847
987
|
}
|
|
848
|
-
function isPresent(entry) {
|
|
849
|
-
return entry[1] !== void 0;
|
|
850
|
-
}
|
|
851
|
-
function formatMetadataValue(value) {
|
|
852
|
-
return typeof value === "string" ? value : value.join(", ");
|
|
853
|
-
}
|
|
854
988
|
async function runPdfInspect(input, options) {
|
|
855
989
|
const command = "pdf-inspect";
|
|
856
990
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
@@ -868,7 +1002,7 @@ async function runPdfInspect(input, options) {
|
|
|
868
1002
|
}
|
|
869
1003
|
});
|
|
870
1004
|
if (options.full) {
|
|
871
|
-
process.stdout.write(`${JSON.stringify(layout, void 0, 2)}\n`);
|
|
1005
|
+
process.stdout.write(`${JSON.stringify(layoutDocumentWithSchema(layout), void 0, 2)}\n`);
|
|
872
1006
|
return 0;
|
|
873
1007
|
}
|
|
874
1008
|
const imagesByFormat = countImagesByFormat(layout.images);
|
|
@@ -892,19 +1026,9 @@ async function runPdfInspect(input, options) {
|
|
|
892
1026
|
const histogramText = Array.from(histogram.entries()).map(([kind, count]) => `${kind}=${count}`).join(", ");
|
|
893
1027
|
process.stdout.write(` page ${index + 1}: ${page.widthPt}pt x ${page.heightPt}pt${histogramText === "" ? "" : ` (${histogramText})`}\n`);
|
|
894
1028
|
});
|
|
895
|
-
|
|
896
|
-
["title", layout.metadata.title],
|
|
897
|
-
["author", layout.metadata.author],
|
|
898
|
-
["subject", layout.metadata.subject],
|
|
899
|
-
["keywords", layout.metadata.keywords],
|
|
900
|
-
["creator", layout.metadata.creator],
|
|
901
|
-
["producer", layout.metadata.producer],
|
|
902
|
-
["createdIso", layout.metadata.createdIso],
|
|
903
|
-
["modifiedIso", layout.metadata.modifiedIso]
|
|
904
|
-
].filter(isPresent);
|
|
905
|
-
if (presentMetadata.length > 0) {
|
|
1029
|
+
if (presentMetadataEntries(layout.metadata).length > 0) {
|
|
906
1030
|
process.stdout.write("metadata:\n");
|
|
907
|
-
for (const
|
|
1031
|
+
for (const line of formatMetadataLines(layout.metadata)) process.stdout.write(` ${line}\n`);
|
|
908
1032
|
}
|
|
909
1033
|
if (imagesByFormat.size > 0) {
|
|
910
1034
|
process.stdout.write("images:\n");
|
|
@@ -922,8 +1046,155 @@ function registerPdfInspectCommand(program) {
|
|
|
922
1046
|
});
|
|
923
1047
|
}
|
|
924
1048
|
//#endregion
|
|
1049
|
+
//#region src/commands/set-metadata.ts
|
|
1050
|
+
const REBUILD_FORMATS = {
|
|
1051
|
+
docx: true,
|
|
1052
|
+
pptx: true,
|
|
1053
|
+
odt: true,
|
|
1054
|
+
odp: true,
|
|
1055
|
+
ods: true,
|
|
1056
|
+
odg: true,
|
|
1057
|
+
markdown: true
|
|
1058
|
+
};
|
|
1059
|
+
function isRebuildFormat(format) {
|
|
1060
|
+
return format in REBUILD_FORMATS;
|
|
1061
|
+
}
|
|
1062
|
+
function readContentForFormat(format, bytes) {
|
|
1063
|
+
switch (format) {
|
|
1064
|
+
case "docx": return readDocxContent(decodePackage(bytes));
|
|
1065
|
+
case "pptx": return readPptxContent(decodePackage(bytes));
|
|
1066
|
+
case "odt": return readOdtContent(decodePackage$1(bytes));
|
|
1067
|
+
case "odp": return readOdpContent(decodePackage$1(bytes));
|
|
1068
|
+
case "ods": return readOdsContent(decodePackage$1(bytes));
|
|
1069
|
+
case "odg": return readOdgContent(decodePackage$1(bytes));
|
|
1070
|
+
case "markdown": return readMarkdownContent(decodeMarkdownText(bytes));
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
function buildBytesForRebuildFormat(format, content) {
|
|
1074
|
+
switch (format) {
|
|
1075
|
+
case "docx": return encodePackage(buildDocxPackage(content));
|
|
1076
|
+
case "pptx": return encodePackage(buildPptxPackage(content));
|
|
1077
|
+
case "odt": return encodePackage$1(buildOdtPackage(content));
|
|
1078
|
+
case "odp": return encodePackage$1(buildOdpPackage(content));
|
|
1079
|
+
case "ods": return encodePackage$1(buildOdsPackage(content));
|
|
1080
|
+
case "odg": return encodePackage$1(buildOdgPackage(content));
|
|
1081
|
+
case "markdown": return encodeMarkdownText(buildMarkdownText(content));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function mergeMetadata(current, overrides) {
|
|
1085
|
+
return {
|
|
1086
|
+
...current,
|
|
1087
|
+
...overrides.title !== void 0 ? { title: overrides.title } : {},
|
|
1088
|
+
...overrides.author !== void 0 ? { author: overrides.author } : {},
|
|
1089
|
+
...overrides.subject !== void 0 ? { subject: overrides.subject } : {},
|
|
1090
|
+
...overrides.keywords !== void 0 ? { keywords: overrides.keywords } : {}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
function parseKeywords(csv) {
|
|
1094
|
+
return csv.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1095
|
+
}
|
|
1096
|
+
function classifyWritePath(source, target) {
|
|
1097
|
+
if (source === "pdf" && target === "pdf") return { kind: "pdf" };
|
|
1098
|
+
if (target === "xlsx" || source === "xlsx") return { errorMessage: "'xlsx' is not a supported set-metadata source or target -- documents.js does not re-export a ContentDocument-to-xlsx builder or a readXlsxContent from its own public surface (see that package's own README, Architecture section); convert with 'xlsx-to-ods'/'ods-to-xlsx' first, then set metadata on the ods" };
|
|
1099
|
+
if (target === "odf" || source === "odf") return { errorMessage: "'odf' (a standalone formula document) is not a supported set-metadata source or target -- it has no write path back out at all" };
|
|
1100
|
+
if (!isRebuildFormat(source) || !isRebuildFormat(target)) return { errorMessage: `set-metadata only patches metadata in place; it does not convert format -- source ('${source}') and target ('${target}') must be the same format (or both 'pdf'). Run 'convert'/'from-package' first if you need a different target format.` };
|
|
1101
|
+
if (source !== target) return { errorMessage: `set-metadata only patches metadata in place; it does not convert format -- source ('${source}') and target ('${target}') must be the same format. Run 'convert'/'from-package' first if you need a different target format.` };
|
|
1102
|
+
return {
|
|
1103
|
+
kind: "rebuild",
|
|
1104
|
+
format: source
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
async function runSetMetadata(input, output, options) {
|
|
1108
|
+
const command = "set-metadata";
|
|
1109
|
+
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
1110
|
+
process.stderr.write(`[${command}] conflicting output destinations: positional '${output}' and --out '${options.out}'\n`);
|
|
1111
|
+
return 2;
|
|
1112
|
+
}
|
|
1113
|
+
const target = resolveTargetFormat(output, options.out, options.to);
|
|
1114
|
+
if ("errorMessage" in target) {
|
|
1115
|
+
process.stderr.write(`[${command}] ${target.errorMessage}\n`);
|
|
1116
|
+
return 2;
|
|
1117
|
+
}
|
|
1118
|
+
const source = inferFormatFromExtension(input);
|
|
1119
|
+
if (source === void 0) {
|
|
1120
|
+
process.stderr.write(`[${command}] cannot infer a source format from '${input}'; rename the file with a recognised extension (${KNOWN_DOCUMENT_FORMATS})\n`);
|
|
1121
|
+
return 2;
|
|
1122
|
+
}
|
|
1123
|
+
const writePath = classifyWritePath(source, target.format);
|
|
1124
|
+
if ("errorMessage" in writePath) {
|
|
1125
|
+
process.stderr.write(`[${command}] ${writePath.errorMessage}\n`);
|
|
1126
|
+
return 2;
|
|
1127
|
+
}
|
|
1128
|
+
const overrides = {
|
|
1129
|
+
title: options.setTitle,
|
|
1130
|
+
author: options.setAuthor,
|
|
1131
|
+
subject: options.setSubject,
|
|
1132
|
+
keywords: options.setKeywords === void 0 ? void 0 : parseKeywords(options.setKeywords)
|
|
1133
|
+
};
|
|
1134
|
+
const resolvedOutput = output ?? options.out ?? (input === "-" ? "-" : resolveDefaultOutputPath(input, target.format));
|
|
1135
|
+
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
1136
|
+
try {
|
|
1137
|
+
const inputBytes = await readInput(input, { signal });
|
|
1138
|
+
const bytes = writePath.kind === "pdf" ? (() => {
|
|
1139
|
+
const layout = readPdf(new Uint8Array(inputBytes), { signal });
|
|
1140
|
+
const patched = {
|
|
1141
|
+
...layout,
|
|
1142
|
+
metadata: mergeMetadata(layout.metadata, overrides)
|
|
1143
|
+
};
|
|
1144
|
+
return writePdf(patched, { signal });
|
|
1145
|
+
})() : (() => {
|
|
1146
|
+
const content = readContentForFormat(writePath.format, new Uint8Array(inputBytes));
|
|
1147
|
+
const nextContent = {
|
|
1148
|
+
...content,
|
|
1149
|
+
metadata: mergeMetadata(content.metadata, overrides)
|
|
1150
|
+
};
|
|
1151
|
+
return buildBytesForRebuildFormat(writePath.format, nextContent);
|
|
1152
|
+
})();
|
|
1153
|
+
await writeOutput(resolvedOutput, bytes);
|
|
1154
|
+
createDiagnosticReporter({
|
|
1155
|
+
json: options.json,
|
|
1156
|
+
quiet: options.quiet,
|
|
1157
|
+
command
|
|
1158
|
+
}).summarize({
|
|
1159
|
+
output: resolvedOutput,
|
|
1160
|
+
bytes: bytes.byteLength,
|
|
1161
|
+
diagnosticCount: 0
|
|
1162
|
+
});
|
|
1163
|
+
return 0;
|
|
1164
|
+
} catch (error) {
|
|
1165
|
+
process.stderr.write(`${formatError(error, options.verbose)}\n`);
|
|
1166
|
+
return mapErrorToExit(error, getAbortReason());
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
function registerSetMetadataCommand(program) {
|
|
1170
|
+
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", [
|
|
1171
|
+
"",
|
|
1172
|
+
"Two write paths: a pdf source/target patches the metadata directly on the parsed PDF (writePdf), with no layout engine",
|
|
1173
|
+
"involved at all -- genuinely lossless for everything else on the page. Every other supported format (docx, pptx, odt,",
|
|
1174
|
+
"odp, ods, odg, markdown) rebuilds a fresh package from that format's own ContentDocument -- for docx specifically,",
|
|
1175
|
+
"this is LOSSY: it drops anything docx-extras covers (comments, footnotes, headers/footers, numbering definitions),",
|
|
1176
|
+
"since buildDocxPackage builds a fresh package from the ContentDocument alone, with no way to carry that data through.",
|
|
1177
|
+
"",
|
|
1178
|
+
"set-metadata does not convert format -- source and target must match. Run convert/from-package first, then",
|
|
1179
|
+
"set-metadata on the result, if you need a different target format."
|
|
1180
|
+
].join("\n"));
|
|
1181
|
+
addOutOption(command);
|
|
1182
|
+
addTimeoutOption(command);
|
|
1183
|
+
addJsonOption(command);
|
|
1184
|
+
addQuietOption(command);
|
|
1185
|
+
addVerboseOption(command);
|
|
1186
|
+
command.option("--to <format>", `target format when it cannot be inferred from the output path (${KNOWN_DOCUMENT_FORMATS})`);
|
|
1187
|
+
command.option("--set-title <text>", "set the title field");
|
|
1188
|
+
command.option("--set-author <text>", "set the author field");
|
|
1189
|
+
command.option("--set-subject <text>", "set the subject field");
|
|
1190
|
+
command.option("--set-keywords <csv>", "set the keywords field, comma-separated (trimmed, empty entries dropped)");
|
|
1191
|
+
command.action(async (input, output, options) => {
|
|
1192
|
+
process.exitCode = await runSetMetadata(input, output, options);
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
//#endregion
|
|
925
1196
|
//#region package.json
|
|
926
|
-
var version = "1.
|
|
1197
|
+
var version = "1.5.0";
|
|
927
1198
|
//#endregion
|
|
928
1199
|
//#region src/program.ts
|
|
929
1200
|
function createProgram() {
|
|
@@ -940,13 +1211,17 @@ function createProgram() {
|
|
|
940
1211
|
registerOdmCommand(program);
|
|
941
1212
|
registerOdbCommands(program);
|
|
942
1213
|
registerPdfInspectCommand(program);
|
|
1214
|
+
registerFontsCommand(program);
|
|
1215
|
+
registerDocxExtrasCommand(program);
|
|
1216
|
+
registerMetadataCommand(program);
|
|
1217
|
+
registerSetMetadataCommand(program);
|
|
943
1218
|
return program;
|
|
944
1219
|
}
|
|
945
1220
|
//#endregion
|
|
946
1221
|
//#region src/cli.ts
|
|
947
1222
|
async function launchTui(startPath, signal) {
|
|
948
1223
|
try {
|
|
949
|
-
const { runTui } = await import("./tui-
|
|
1224
|
+
const { runTui } = await import("./tui-B-o3iI9a.js");
|
|
950
1225
|
await runTui({
|
|
951
1226
|
startPath,
|
|
952
1227
|
signal
|