doculi 3.1.8 → 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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joseph Mearman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# document-cli
|
|
2
2
|
|
|
3
|
-
[](https://github.com/ExaDev/document-cli) [](https://www.npmjs.com/package/document-cli) [](https://github.com/ExaDev/documents.js/tree/main/packages/document-cli) [](https://www.npmjs.com/package/document-cli) [](https://www.npmjs.com/package/document-cli) [](https://github.com/ExaDev/documents.js/actions)
|
|
4
4
|
|
|
5
|
-
> A command-line interface and an interactive terminal (Ink) app for [`documents.js`](https://github.com/ExaDev/documents.js): every docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/csv/svg/markdown conversion, bridge, and editor documents.js exposes, plus the outline projection [`document-outline.js`](
|
|
5
|
+
> A command-line interface and an interactive terminal (Ink) app for [`documents.js`](https://github.com/ExaDev/documents.js): every docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/csv/svg/markdown/rtf/wpd/doc/xls/ppt/epub conversion, bridge, and editor documents.js exposes, plus the outline projection [`document-outline.js`](../document-outline.js/README.md) builds over any readable document, wired up as a scriptable subcommand or a full-screen terminal editor. Installs as either `document-cli` or `doculi`.
|
|
6
6
|
|
|
7
7
|
`document-cli` adds no conversion or editing logic of its own — it is a dispatch layer over `documents.js`'s existing conversion functions, `DocumentConverter` port, live-view editors, and `.odb`/PDF readers. What it adds is two ways to drive them without writing TypeScript: a scriptable, Unix-shaped CLI (stdin/stdout, exit codes, `--json` diagnostics) for pipelines, and a full-screen Ink terminal app for browsing and editing a document interactively.
|
|
8
8
|
|
|
@@ -38,16 +38,16 @@ graph TD
|
|
|
38
38
|
odf --> cli
|
|
39
39
|
pdfcodec --> cli
|
|
40
40
|
|
|
41
|
-
click schema "https://github.com/ExaDev/document-schema.js" "document-schema.js"
|
|
42
|
-
click ooxml "https://github.com/ExaDev/ooxml.js" "ooxml.js"
|
|
43
|
-
click odf "https://github.com/ExaDev/odf.js" "odf.js"
|
|
44
|
-
click pdfcodec "https://github.com/ExaDev/pdf-codec" "pdf-codec"
|
|
45
|
-
click mdcodec "https://github.com/ExaDev/markdown-codec" "markdown-codec"
|
|
46
|
-
click bytecodec "https://github.com/ExaDev/byte-codec" "byte-codec"
|
|
41
|
+
click schema "https://github.com/ExaDev/documents.js/tree/main/packages/document-schema.js" "document-schema.js"
|
|
42
|
+
click ooxml "https://github.com/ExaDev/documents.js/tree/main/packages/ooxml.js" "ooxml.js"
|
|
43
|
+
click odf "https://github.com/ExaDev/documents.js/tree/main/packages/odf.js" "odf.js"
|
|
44
|
+
click pdfcodec "https://github.com/ExaDev/documents.js/tree/main/packages/pdf-codec" "pdf-codec"
|
|
45
|
+
click mdcodec "https://github.com/ExaDev/documents.js/tree/main/packages/markdown-codec" "markdown-codec"
|
|
46
|
+
click bytecodec "https://github.com/ExaDev/documents.js/tree/main/packages/byte-codec" "byte-codec"
|
|
47
47
|
click documents "https://github.com/ExaDev/documents.js" "documents.js"
|
|
48
|
-
click outline "https://github.com/ExaDev/document-outline.js" "document-outline.js"
|
|
49
|
-
click mcp "https://github.com/ExaDev/document-mcp" "document-mcp"
|
|
50
|
-
click cli "https://github.com/ExaDev/document-cli" "document-cli"
|
|
48
|
+
click outline "https://github.com/ExaDev/documents.js/tree/main/packages/document-outline.js" "document-outline.js"
|
|
49
|
+
click mcp "https://github.com/ExaDev/documents.js/tree/main/packages/document-mcp" "document-mcp"
|
|
50
|
+
click cli "https://github.com/ExaDev/documents.js/tree/main/packages/document-cli" "document-cli"
|
|
51
51
|
|
|
52
52
|
style cli fill:#f9a825,stroke:#333,stroke-width:3px
|
|
53
53
|
```
|
|
@@ -56,9 +56,9 @@ graph TD
|
|
|
56
56
|
|
|
57
57
|
`documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions (including a bounded SQL engine over an `.odb`'s own tables and full report rendering), its PDF inspector, and its document metadata/source-font introspection become a single command-line invocation, and its seven live-view editors (docx/pptx/odt/odp/ods/odg/markdown) become a keyboard-driven terminal app that never needs a code editor open at all.
|
|
58
58
|
|
|
59
|
-
[`document-mcp`](
|
|
59
|
+
[`document-mcp`](../document-mcp/README.md) is the sibling frontend over the identical `documents.js` library — an MCP server rather than a terminal CLI/TUI — so the two are independent consumers of one shared implementation, each exposing whatever subset of it suits a human at a terminal versus an MCP-speaking agent.
|
|
60
60
|
|
|
61
|
-
The CLI and the TUI are deliberately not two separate implementations of the same logic. The TUI's own document-opening, saving, and PDF-export code (`src/tui/format/`) calls the identical `documents.js` functions the CLI commands call — `openDocx`/`createDocx`/`docxToPdf` and their five siblings per format, plus `readOdbTables`/`readPdf` for the two read-only sources — so there is exactly one place either surface can drift from what documents.js itself does: nowhere. This CLI adds no conversion logic of its own, so a conversion's fidelity — which pairs round-trip losslessly, which are a best-effort reconstruction, and why — is exactly what [`documents.js`'s own Fidelity section](
|
|
61
|
+
The CLI and the TUI are deliberately not two separate implementations of the same logic. The TUI's own document-opening, saving, and PDF-export code (`src/tui/format/`) calls the identical `documents.js` functions the CLI commands call — `openDocx`/`createDocx`/`docxToPdf` and their five siblings per format, plus `readOdbTables`/`readPdf` for the two read-only sources — so there is exactly one place either surface can drift from what documents.js itself does: nowhere. This CLI adds no conversion logic of its own, so a conversion's fidelity — which pairs round-trip losslessly, which are a best-effort reconstruction, and why — is exactly what [`documents.js`'s own Fidelity section](../documents.js/README.md#fidelity) documents, table included; it is not restated here.
|
|
62
62
|
|
|
63
63
|
## Getting started
|
|
64
64
|
|
|
@@ -70,7 +70,7 @@ npm i -g document-cli
|
|
|
70
70
|
npm i -g doculi
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
Both names install the exact same package and the exact same binary — `package.json`'s `bin` field declares both `document-cli` and `doculi` pointing at the one built entry point unconditionally, so there is no "real" name and an alias; pick whichever you find easier to type.
|
|
73
|
+
Both names install the exact same package and the exact same binary — `package.json`'s `bin` field declares both `document-cli` and `doculi` pointing at the one built entry point unconditionally, so there is no "real" name and an alias; pick whichever you find easier to type. Unlike a sibling's second _npm package name_ (`documents.js`'s own `js.documents` — see that package's README, and note the older per-repo pipeline's GitHub Packages republish this pattern used to mirror is no longer running, per [ExaDev/documents.js#732](https://github.com/ExaDev/documents.js/issues/732)), this is one package with two `bin` entries: a second name for the same build, not a second build, and unaffected by that gap.
|
|
74
74
|
|
|
75
75
|
## Usage
|
|
76
76
|
|
|
@@ -99,7 +99,7 @@ document-cli convert report.docx - --to pdf > report.pdf
|
|
|
99
99
|
|
|
100
100
|
**`formats`** — lists every `source -> target` pair the commands above support (`--json` for a machine-readable array), plus a pointer to the commands not on that list because they don't fit the source/target shape (`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`, `outline`).
|
|
101
101
|
|
|
102
|
-
**`from-package <input> [output]`** — reads back a `
|
|
102
|
+
**`from-package <input> [output]`** — reads back a `DocumentTree` JSON file a previous conversion wrote via `--dump-package` (below) and exports it to a real target format, closing the round trip `--dump-package` otherwise has no return path for. Target resolution matches `convert`: an output path's own extension, or `--to <format>` when it doesn't have one. The package this command reads is the tree form since documents.js 3.0.0 — content grouped one group per container (section, slide, sheet, draw page) under `children`, with the minted `styles` table at the root — flattened once at this boundary, exactly as documents.js's own builders do. `pdf` rebuilds the layout from the package's own recorded positions — each content node's `frames` plus the package's `pages` geometry (`layoutDocumentFromPackage` → `writePdf`; a package no longer carries a separate `layout` half at all); every other format builds a fresh package from the flattened content through the identical `buildXPackage` function the matching `pdf-to-X`/bridge conversion already uses — `xlsx` included, via `ooxml.js`'s own `buildXlsxPackage`. `odf` is the one target rejected outright, since a standalone formula document has no write path from `ContentDocument` at all; a `csv` or `svg` target is written through the identical `buildCsvText`/`buildSvgText` functions the codec registry's own write wrappers call, so the `--delimiter`/`--sheet`/`--page` selection flags below reach it exactly as they do a live conversion. Only a file genuinely written by a current `--dump-package` round-trips here: the `$schema` URI a dump carries pins the document-schema.js release that wrote it, and any pre-4.0.0 dump — the flat `{ formatVersion, content, pages }` envelope documents.js 1.x/2.x wrote, whatever its `formatVersion` — is rejected with an error naming the pinned release, the flat-to-tree change, and the remedy, rather than a raw validation wall. A layout-document dump (a document-schema.js 3.x layout artefact, e.g. an old `pdf-inspect --full` output) gets its own pointer: that schema moved to pdf-codec.
|
|
103
103
|
|
|
104
104
|
```sh
|
|
105
105
|
document-cli docx-to-pdf report.docx report.pdf --dump-package report.package.json
|
|
@@ -116,7 +116,7 @@ document-cli odb-tables customers.odb
|
|
|
116
116
|
document-cli odb-to-csv customers.odb --table CUSTOMERS customers.csv
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
**`odb-forms <input>`**, **`odb-reports <input>`** — read a `.odb`'s form and report
|
|
119
|
+
**`odb-forms <input>`**, **`odb-reports <input>`** — read a `.odb`'s form and report _structure_ rather than its table data. A form or report is a static ODF sub-document inside the package, so neither command consults the embedded database at all: they work on an `.odb` whose connection points at an external server just as well as on an embedded one. `odb-forms` prints each form's own data source (table or saved query) and its field-bound controls, sub-forms nested under their parent with their own separate command; `odb-reports` prints each report's data-source command, its band structure (report/page headers and footers, groups, detail), every `rpt:` formula expression (`field:[AMOUNT]`, `rpt:SUM([AMOUNT])`), and any user-defined report functions. `--json` emits the same structure machine-readably — for a form that is the structure only, with the form's own parsed sub-document dropped:
|
|
120
120
|
|
|
121
121
|
```sh
|
|
122
122
|
document-cli odb-forms sales.odb
|
|
@@ -143,7 +143,7 @@ document-cli pdf-inspect report.pdf
|
|
|
143
143
|
document-cli pdf-inspect report.pdf --json
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
**`fonts <input>`** — lists every source-embedded font face a docx/pptx/odt/odp/ods/odg document carries (family, weight/style, byte length) — the same embedded faces every `<format>-to-pdf` conversion already extracts and renders through automatically (see [Real fonts](#real-fonts) below); this command just reports what's there without converting anything. Rejects a format with no source-embedded-font concept at all (xlsx, csv, svg, pdf, markdown, odf), naming it:
|
|
146
|
+
**`fonts <input>`** — lists every source-embedded font face a docx/pptx/odt/odp/ods/odg document carries (family, weight/style, byte length) — the same embedded faces every `<format>-to-pdf` conversion already extracts and renders through automatically (see [Real fonts](#real-fonts) below); this command just reports what's there without converting anything. Rejects a format with no source-embedded-font concept at all (xlsx, csv, svg, pdf, markdown, odf, rtf, wpd, doc, xls, ppt) or with no font-embedding support in this ecosystem's own reader yet (epub — EPUB 3 can embed fonts via CSS `@font-face`, but epub-codec's own reader doesn't extract them), naming it:
|
|
147
147
|
|
|
148
148
|
```sh
|
|
149
149
|
document-cli fonts report.docx
|
|
@@ -155,20 +155,20 @@ document-cli fonts report.docx
|
|
|
155
155
|
document-cli docx-extras report.docx
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
**`metadata <input>`** — prints a document's own title/author/subject/keywords/creator/producer/created/modified metadata, for any of the
|
|
158
|
+
**`metadata <input>`** — prints a document's own title/author/subject/keywords/creator/producer/created/modified metadata, for any of the eighteen supported formats (docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, rtf, wpd, doc, xls, ppt, epub, pdf); csv, svg, doc, xls, and ppt carry no metadata container of their own (doc-codec/xls-codec/ppt-codec don't read one yet — see each package's own README), so they always report none, and epub reports title/author/keywords but never subject (epub-codec's OPF reader has no Dublin Core mapping for it). `--json` emits the raw metadata object:
|
|
159
159
|
|
|
160
160
|
```sh
|
|
161
161
|
document-cli metadata report.pdf
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
**`set-metadata <input> [output]`** — patches a document's own title/author/subject/keywords, leaving every other field untouched (`--set-title`, `--set-author`, `--set-subject`, `--set-keywords` — the last a comma-separated list); source and target format must match, so run `convert`/`from-package` first if a different target format is also needed. A `pdf` source/target patches the parsed PDF directly (`writePdf`) with no layout engine involved
|
|
164
|
+
**`set-metadata <input> [output]`** — patches a document's own title/author/subject/keywords, leaving every other field untouched (`--set-title`, `--set-author`, `--set-subject`, `--set-keywords` — the last a comma-separated list); source and target format must match, so run `convert`/`from-package` first if a different target format is also needed. A `pdf` source/target patches the parsed PDF directly (`writePdf`) with no layout engine involved, and a `docx` source/target patches `docProps/core.xml` directly on the decoded package (`patchDocxMetadata`) with no `ContentDocument` rebuild involved — both genuinely lossless for everything else in the document, the docx path included: comments, footnotes, headers/footers, and numbering definitions (everything `docx-extras` covers) survive untouched, since nothing but the metadata part itself is ever written. Every other format (pptx, xlsx, odt, odp, ods, odg, markdown, rtf) rebuilds a fresh package from that format's own `ContentDocument`; a `csv` or `svg` source/target is rejected outright, since plain text has no metadata container and a rebuild would silently drop the override:
|
|
165
165
|
|
|
166
166
|
```sh
|
|
167
167
|
document-cli set-metadata report.docx report.docx --set-title "Q3 Report" --set-author "Finance"
|
|
168
168
|
document-cli set-metadata report.docx report.odt --set-keywords "draft,internal"
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
**`outline <input>`** — prints a document's outline: the table-of-contents projection over the tree-form `
|
|
171
|
+
**`outline <input>`** — prints a document's outline: the table-of-contents projection over the tree-form `DocumentTree` read straight off the source document's own bytes — headings nested by heading level, list items nested under their heading or slide, one group per slide (labelled `Slide N`), sheet (labelled with the sheet's own name), or draw page (labelled `Page N`) — rendered as indented text, two spaces per nesting depth. Leaves render their own text (a paragraph's runs, a table's cell text, an image's alt text, a formula's LaTeX) or their kind in brackets when they carry none (`[page-break]`, `[vector]`, `[embeddedObject]`). Works on any of the seventeen readable formats: the command reads the source's own native tree directly (documents.js's `readNativeDocumentTree`) — no bridging conversion runs and no output bytes are discarded — and projects that tree through [`document-outline.js`](../document-outline.js/README.md)'s own `buildOutline`, this command being that package's first real consumer. `--json` emits the outline tree itself — groups as `{ text, level, children }`, leaves as the package leaves they are — rather than a CLI-private shape; a `pdf` source's own `readPdf` parse diagnostics still reach stderr exactly as they would on the matching `pdf-to-docx` command. `--from <format>` names the source format when the input path carries no recognised extension to infer it from — the only way to outline a document read from stdin (`-`), which otherwise has no extension to read at all. Heading nesting depends on the source document carrying a heading-level signal on disk (`w:outlineLvl` for docx, `text:outline-level` for odt) — present in anything authored by Word or LibreOffice, always present for a markdown source (whose own reader parses `#`/`##` headings directly), and also stamped by this ecosystem's own docx/odt writers (`buildDocxPackage`'s `w:outlineLvl`, `buildOdtPackage`'s promotion to a real `text:h`), so outlining a docx/odt this CLI's own conversions produce nests headings correctly too:
|
|
172
172
|
|
|
173
173
|
```sh
|
|
174
174
|
document-cli outline report.docx
|
|
@@ -181,17 +181,17 @@ document-cli outline slides.pptx --json
|
|
|
181
181
|
|
|
182
182
|
The explicit conversions, `convert`, `odm-to-pdf`, `odb-to-xlsx`, `odb-to-csv`, `set-metadata`, and `odb-render-report` — every command that reads one file and writes one — share:
|
|
183
183
|
|
|
184
|
-
| Flag
|
|
185
|
-
|
|
186
|
-
| `-o, --out <file>` | Output path; defaults to the input path with the target format's own extension. Conflicts with a positional output argument that names a different path.
|
|
187
|
-
| `--timeout <ms>`
|
|
188
|
-
| `--json`
|
|
189
|
-
| `-q, --quiet`
|
|
190
|
-
| `--verbose`
|
|
184
|
+
| Flag | Meaning |
|
|
185
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
186
|
+
| `-o, --out <file>` | Output path; defaults to the input path with the target format's own extension. Conflicts with a positional output argument that names a different path. |
|
|
187
|
+
| `--timeout <ms>` | Abort the run after this many milliseconds. |
|
|
188
|
+
| `--json` | Emit diagnostics and the result summary as newline-delimited JSON on stderr, instead of human-readable lines. |
|
|
189
|
+
| `-q, --quiet` | Suppress diagnostic and summary output (the JSON result-summary line still prints in `--json` mode, so a script consuming NDJSON always gets a terminating record). |
|
|
190
|
+
| `--verbose` | Include a full stack trace in the error line when the run fails. |
|
|
191
191
|
|
|
192
192
|
Three further flags select what a csv or svg edge of a conversion works on, threaded straight into documents.js's own `ConversionOptions`: `--delimiter <char>` (the field delimiter a csv source reads with, or a csv target writes with — default `,`), `--sheet <name>` (the sheet a csv target writes, required when the source document has more than one), and `--page <index>` (the 0-based page an svg target draws, required when the source document has more than one). On the explicit commands they are registered only where the pair can reach the edge in question — `--delimiter` on any pair with a csv edge, `--sheet` on a csv target, `--page` on an svg target — and unconditionally on `convert` and `from-package`, whose target is only known once the output path or `--to` resolves at run time (the same registration reasoning the font flags below document). Leaving `--sheet` or `--page` unanswered on an ambiguous document fails with exit `3`, naming the sheets or page count to pick from:
|
|
193
193
|
|
|
194
|
-
`--dump-package <file>` is one flag further, registered only on the explicit conversions and `convert` — it writes the
|
|
194
|
+
`--dump-package <file>` is one flag further, registered only on the explicit conversions and `convert` — it writes the SOURCE document's own native `DocumentTree` (documents.js's `readNativeDocumentTree`, read straight off the input bytes a second time, independent of `--to`/the output path) to a JSON file, tagged with its own version-pinned `$schema` — the URI _is_ the package's version — so `from-package` (above) can read it back in. This is deliberately not the same tree `--to`'s own conversion produces internally: a target sharing no `ContentDocument` variant with the source composes through a lossy cross-variant bridge or a pdf pivot to get there (`xlsx-to-markdown`, say), and reporting that intermediate hop's shape would mean an xlsx source's own dump carries a wordprocessing tree with no sheet/cell/formula/A1 data at all rather than the workbook it actually is ([ExaDev/documents.js#823](https://github.com/ExaDev/documents.js/issues/823)) — `--dump-package` is about what the source carries, not about how `--to` got there. Every source format populates one: a `pdf` source's dump carries `pages` and per-node `frames` (a PDF has no representation other than positioned layout — the identical reconstruction `pdf-to-docx` runs), and every other source's dump is content-only, with no `pages` and no `frames` at all, `odf` included (its dump carries a `formula`-kind content with no invented page geometry — a standalone formula document has no page concept of its own until something renders it). `odm-to-pdf`/`odb-*`/`set-metadata` don't expose the flag at all, since none of them goes through `DocumentConverter.convert` in the first place. `odb-tables`, `odb-forms`, `odb-reports`, `fonts`, `docx-extras`, `metadata`, `formats`, and `pdf-inspect` each take only their own `--json` (plus `pdf-inspect`'s own `--full`); `odb-query` takes `--sql <text>`/`--query <savedName>` (mutually exclusive) alongside its own `--json`, with none of the shared flags above since it only reads and writes nothing; `from-package` and `set-metadata` each take `--to <format>` alongside the shared flags in this table; `outline` takes the shared flags except `--out` (it prints to stdout and writes no file), plus its own `--from <format>` for a source format extension inference can't resolve — it reads the source's own native tree directly (the same `readNativeDocumentTree` primitive `--dump-package` uses), so its diagnostics and `--timeout` behave like any conversion's; `odb-render-report` takes `--report <name>` and `--to <format>` alongside the shared flags and the font flags below; `tui` takes no flags at all, only an optional positional file.
|
|
195
195
|
|
|
196
196
|
### Real fonts
|
|
197
197
|
|
|
@@ -204,7 +204,7 @@ document-cli docx-to-pdf report.docx report.pdf \
|
|
|
204
204
|
--report-font-substitutions
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
The flag is repeatable, takes a `.ttf`/`.otf` path, and needs **no accompanying family flag**: the family, weight, and slope are read from the font file's own `name` and `OS/2` tables. That is a deliberate choice over the alternative of a parallel `--font-family`/`--font-bold`/`--font-italic` set — three repeatable flags whose values must stay index-aligned with a fourth is a silent-misalignment hazard (pass two font files and one `--font-family` and the second face is mis-declared, with nothing reporting it), and every real font already states all three facts itself. The consequence to know about: a font file can only be supplied
|
|
207
|
+
The flag is repeatable, takes a `.ttf`/`.otf` path, and needs **no accompanying family flag**: the family, weight, and slope are read from the font file's own `name` and `OS/2` tables. That is a deliberate choice over the alternative of a parallel `--font-family`/`--font-bold`/`--font-italic` set — three repeatable flags whose values must stay index-aligned with a fourth is a silent-misalignment hazard (pass two font files and one `--font-family` and the second face is mis-declared, with nothing reporting it), and every real font already states all three facts itself. The consequence to know about: a font file can only be supplied _as the family it says it is_. There is no way to say "draw Calibri using this file instead" — for that, the family in the document has to match the family in the font. A file that is not a readable font (a `.woff`, a `.ttc` collection, a mistyped path pointing at something else) fails the run outright, naming the file, rather than being quietly skipped.
|
|
208
208
|
|
|
209
209
|
`--report-font-substitutions` prints each face that resolved to something other than what the document asked for, as it happens, with its structured fields intact (`--json` makes it one more NDJSON record: `{"type":"font-substitution","requestedFamily":"Calibri",…}`). Without it, the same fallbacks are still reported — the `font/substituted` diagnostic lines every conversion already emits — just as rendered messages after the fact rather than structured events as they occur.
|
|
210
210
|
|
|
@@ -214,24 +214,24 @@ Diagnostics and the summary line always go to stderr; stdout is reserved for the
|
|
|
214
214
|
|
|
215
215
|
### Exit codes
|
|
216
216
|
|
|
217
|
-
| Code
|
|
218
|
-
|
|
219
|
-
| `0`
|
|
220
|
-
| `1`
|
|
221
|
-
| `2`
|
|
222
|
-
| `3`
|
|
223
|
-
| `124` | The run's own `--timeout` elapsed before it finished.
|
|
224
|
-
| `130` | Interrupted by `SIGINT` (Ctrl+C).
|
|
217
|
+
| Code | Meaning |
|
|
218
|
+
| ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
219
|
+
| `0` | Success. |
|
|
220
|
+
| `1` | The input was unusable — a malformed or encrypted PDF, or any other conversion failure not covered by the codes below. |
|
|
221
|
+
| `2` | A usage error — bad flags, conflicting output destinations, an unrecognised format, or (for a bare/`--help`/`--version` invocation) commander's own exit path. |
|
|
222
|
+
| `3` | documents.js needs more information to finish, not a different file — an unresolved `.odm` chapter, a `.odb` table that wasn't specified (or wasn't found, or has no embedded engine at all, or uses an unsupported HSQLDB script serialisation), a `.odb` report that wasn't specified when the database declares more than one, a csv target whose source carries more than one sheet (or a `--sheet` that names one it doesn't), or an svg target whose source carries more than one page (or a `--page` that indexes past the last). |
|
|
223
|
+
| `124` | The run's own `--timeout` elapsed before it finished. |
|
|
224
|
+
| `130` | Interrupted by `SIGINT` (Ctrl+C). |
|
|
225
225
|
|
|
226
226
|
## The TUI
|
|
227
227
|
|
|
228
228
|
Launch it either bare (`document-cli`, with no arguments) or explicitly with `document-cli tui [file]` — both open the same app; the explicit form additionally opens `file` immediately, skipping the launcher screen. The TUI needs an interactive terminal: a bare invocation with redirected stdout prints help text instead, and an explicit `tui` invocation with redirected stdout fails outright, since there's no terminal for Ink to draw into.
|
|
229
229
|
|
|
230
|
-
It supports the same
|
|
230
|
+
It supports the same formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg, markdown, and since their codecs gained writers the three legacy binary formats doc, xls, and ppt — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt/markdown/doc, slides and shapes for pptx/odp/ppt, sheets and cells for ods/xls, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate. On a docx document, `x` from the paragraph/table list opens a read-only view of the document's own comments, footnotes, headers/footers, and numbering definitions — a docx-only concept with no odt equivalent, and the TUI counterpart to the `docx-extras` command, rendered through the identical `src/docx-extras-format.ts` line formatter so the two can't drift apart. `m`, global to every screen with a document open, shows that document's own title/author/subject/keywords/etc metadata — every format the `metadata` command covers, including `.odb`/`.pdf`/`.xlsx`. For the seven live-view-editor formats, title/author/subject/keywords are editable in place through documents.js's own `editor.metadata` setter (ExaDev/documents.js#933): select a field and Enter opens it for editing, committing a partial merge that leaves every other field untouched. Every other format (and creator/producer/created/modified on the editable formats too, since those are derived/producer-stamped rather than user-authored) stays read-only here — use the `set-metadata` command for a format with no live-view editor.
|
|
231
231
|
|
|
232
232
|
Markdown (`.md`/`.markdown`) shares the same paragraph/run/table body-list screens docx and odt already use, through documents.js's own `MarkdownEditor` (`openMarkdown`/`createMarkdownEditor`) — a genuine live view over a mutable `ContentDocument`, the same live-view contract every other editor here follows, even though there is no `XmlElement` tree underneath it the way there is for docx/odt (`MarkdownEditor.toMarkdownText()` re-serialises the whole document fresh on every call, rather than exposing a `toBytes()`). Appending a paragraph, appending a run, and toggling bold/italic all go through the identical reducer actions docx/odt use; a markdown run has no underline, colour, font family, or font size at all (CommonMark/GFM has no construct for any of the four), so those keys — along with image insertion, which `MarkdownParagraph` has no counterpart for — are simply absent from a markdown paragraph's own key hints rather than opening a prompt that could only end in a warning. A markdown table can be created and its cells edited through the same 'T' wizard and table-view screens docx/odt use, but GFM tables have no cell-merge concept, so a merge requested alongside table creation still creates the table (unmerged) and reports why the merge itself didn't happen. `:view-source` (markdown documents only) shows the literal text the document was opened with side by side with what a save would write right now — these can genuinely differ even with no edits made this session, from a heading-style, bullet-marker, or line-ending choice the writer normalises. Every save re-serialises the whole document fresh through `buildMarkdownText`, a deliberate, permanent consequence of structured editing rather than something to work around. Diagnostics from the read side (a clamped heading level, a dropped front-matter key, a fenced code block's own info string with nowhere to go, …) now surface into the same diagnostics panel a PDF export's own substitutions already populate, the moment a `.md` file is opened, not only on export. documents.js's own `createMarkdownEditor()` exists now, but this TUI does not yet wire a "new markdown document" flow into `:new`/the new-document picker, so a markdown document can still only be opened from an existing file.
|
|
233
233
|
|
|
234
|
-
Three further kinds of format open read-only: a `.odb` browses its tables and rows with no write path at all (documents.js's own `.odb` support has no write direction to offer), a `.pdf` browses its pages and positioned items rather than being edited in place, and a `.xlsx`, `.csv`, or `.
|
|
234
|
+
Three further kinds of format open read-only: a `.odb` browses its tables and rows with no write path at all (documents.js's own `.odb` support has no write direction to offer), a `.pdf` browses its pages and positioned items rather than being edited in place, and a `.xlsx`, `.csv`, `.svg`, `.rtf`, `.wpd`, or `.epub` opens as a converted PDF preview — documents.js has no spreadsheet, svg, rtf, wpd, or epub editor to hold a live view into (wpd-codec ships no writer at all, so a wpd document could never gain one) — so opening one runs `xlsxToPdf`, `csvToPdf`, `svgToPdf`, `rtfToPdf`, `convertDocument("wpd", "pdf", ...)`, or `epubToPdf` once at open time and browses the result through the identical page-list/page-items/item-detail screens a real `.pdf` uses, with the original bytes kept alongside so a later export re-runs the same conversion with the caller's own fonts and diagnostics rather than reusing the fixed preview conversion. A `.doc`, `.xls`, or `.ppt` is NOT in that read-only group any more: each opens through its own live-view editor (`DocEditor`/`XlsEditor`/`PptEditor`) exactly the way docx/odt do — doc through the shared paragraph/run/table screens, xls through the sheet-list/grid pair (cell values of every kind, with the value editor the ods grid already uses), ppt through the slide list plus a detail screen scoped to what a .ppt can state (shape text, speaker notes, adding text boxes). A later export runs `docToPdf`/`xlsToPdf`/`pptToPdf` from the editor's current bytes, so it reflects edits made this session. Two honest limits carried over from the codecs themselves: an xls cell's formula is readable but never written back (xls-codec's writer has no formula path), and an xls merge is set as colSpan/rowSpan on the anchor cell rather than through a rectangle prompt. A `.odb` additionally browses its _structure_ alongside its data: `f` from the table list opens the form browser and `r` the report browser, each listing what the database declares and opening one to show it in full — a form's own data source and field-bound controls (sub-forms nested under their parent), a report's data-source command, band and group structure, and every `rpt:` formula. Both are rendered through the same `src/odb-structure.ts` the `odb-forms`/`odb-reports` commands print, so the two views cannot drift apart, and search filters by line (`/SUM` narrows a long report to its aggregate expressions). `Enter` on a report's own detail screen renders it — its data-source command resolved, its `rpt:` formulas evaluated, its bands laid out — to a real `docx`/`odt`/`pdf` file, through the same two-field destination-path-then-font-list form the PDF-export screen below uses; the TUI counterpart to the `odb-render-report` command. A standalone `.odf` formula document has no TUI editor either — nothing to edit interactively, only a PDF conversion.
|
|
235
235
|
|
|
236
236
|
The export-to-PDF screen (`e` from any editor screen) is a two-field form: a destination path, then an optional comma-separated list of local `.ttf`/`.otf` paths, which are the same `--font-file` faces the CLI takes and are derived the same way — each font's family, weight, and slope come from the file itself. `Enter` on the path field moves to the fonts field and `Enter` there exports, so leaving fonts empty is still "type a path, press Enter twice". Comma-separated rather than space-separated because a font path routinely contains spaces and almost never a comma. A face falling back to a substitute is reported into the same diagnostics panel a character substitution already is, and a bad font path fails the export with the file named, before anything is written to the destination.
|
|
237
237
|
|
|
@@ -239,24 +239,24 @@ The export-to-PDF screen (`e` from any editor screen) is a two-field form: a des
|
|
|
239
239
|
|
|
240
240
|
The global bindings below apply everywhere; individual screens (a docx run's own bold/italic toggles, an ods cell's own value-kind picker) add their own on top:
|
|
241
241
|
|
|
242
|
-
| Keys
|
|
243
|
-
|
|
244
|
-
| `↑` / `k`
|
|
245
|
-
| `↓` / `j`
|
|
246
|
-
| `Enter` / `→` / `l`
|
|
247
|
-
| `Esc` / `←` / `h`
|
|
248
|
-
| `PageUp` / `PageDown` | Scroll a page at a time
|
|
249
|
-
| `Home` / `End`
|
|
250
|
-
| `a`
|
|
251
|
-
| `m`
|
|
252
|
-
| `Ctrl+S`
|
|
253
|
-
| `Ctrl+W`
|
|
254
|
-
| `Ctrl+Z`
|
|
255
|
-
| `q` / `Ctrl+C`
|
|
256
|
-
| `:`
|
|
257
|
-
| `/`
|
|
258
|
-
| `?`
|
|
259
|
-
| `Ctrl+D`
|
|
242
|
+
| Keys | Action |
|
|
243
|
+
| --------------------- | ------------------------------------------------------------------------------ |
|
|
244
|
+
| `↑` / `k` | Move the selection up |
|
|
245
|
+
| `↓` / `j` | Move the selection down |
|
|
246
|
+
| `Enter` / `→` / `l` | Open or edit the selected item |
|
|
247
|
+
| `Esc` / `←` / `h` | Go back to the previous screen |
|
|
248
|
+
| `PageUp` / `PageDown` | Scroll a page at a time |
|
|
249
|
+
| `Home` / `End` | Jump to the first or last item |
|
|
250
|
+
| `a` | Append a new item to the current list |
|
|
251
|
+
| `m` | Show the open document's metadata (editable for docx/pptx/odt/odp/ods/odg/pdf) |
|
|
252
|
+
| `Ctrl+S` | Save the open document |
|
|
253
|
+
| `Ctrl+W` | Close the open document |
|
|
254
|
+
| `Ctrl+Z` | Undo the last change |
|
|
255
|
+
| `q` / `Ctrl+C` | Quit |
|
|
256
|
+
| `:` | Open the command palette |
|
|
257
|
+
| `/` | Search within the current screen |
|
|
258
|
+
| `?` | Show this help |
|
|
259
|
+
| `Ctrl+D` | Show the diagnostics panel |
|
|
260
260
|
|
|
261
261
|
## Architecture
|
|
262
262
|
|
|
@@ -286,7 +286,11 @@ pnpm test:workers # turbo run _test:workers -> vitest run --config vitest.worke
|
|
|
286
286
|
- **`tsdown.config.ts` disables `fixedExtension` on both build passes.** `platform: 'node'` defaults tsdown's `fixedExtension` to `true`, which would emit `dist/cli.mjs`/`dist/index.mjs` regardless of `package.json`'s own `"type": "module"` — mismatching the `.js`/`.cjs` paths `bin` and `exports` actually name. Both entries set `fixedExtension: false` explicitly so the build output matches what's published.
|
|
287
287
|
- **`readOdbTables` needs `decodeOdbPackage` from `documents.js`, not `decodePackage`.** `documents.js`'s own `decodePackage` re-exports `ooxml.js`'s OOXML-only reader and cannot decode an ODF `.odb` container at all; `decodeDocumentPackage(format, bytes)` dispatches to `odf.js` internally for every real `DocumentFormat` member (odt/odp/ods/odg/odf), but `.odb` is deliberately not one of those (it has no PDF conversion and no write direction — see documents.js's own README), so it has no format string to pass either. `decodeOdbPackage` is the `.odb`-specific sibling that decodes the identical raw ODF container directly — `commands/odb.ts` and the TUI's `format/open-document.ts` both use it for exactly this reason. `odf.js` and `pdf-codec` are devDependencies only: every runtime reach into them (the spreadsheet grid's `cellReference`/`columnIndexToLetters` for A1-style cell/column labelling, font-file inspection via `describeFontFace`) now goes through `documents.js`'s own re-exports, and `odf.js` survives in `devDependencies` solely because `src/test-support/embedded-font-fixture.ts` builds real ODF package fixtures from its low-level XML primitives.
|
|
288
288
|
- **A TUI screen must call an editor's own accessors fresh on every render, never cache them.** documents.js's live-view editors (`DocxRun`, `OdtParagraph`, `OdsCell`, ...) mutate the real XML tree in place — `editor.paragraphs()`/`slide.shapes()`/`sheet.cell(r, c)` called once and stored in `useState`/`useMemo` goes stale the instant any screen mutates the underlying document, with nothing in the type system or in React to catch it (see `src/tui/state/types.ts`'s own doc comment on `Screen`).
|
|
289
|
-
- **The conversions this CLI runs carry the same fidelity limits documents.js itself documents** — most notably that PDF-pivot conversions are not round-trip-lossless (line wrapping drifts under standard-14 font substitution, justified text renders left-aligned, tables and vector shapes don't reconstruct from a PDF), and that `pdf-to-ods` recovers only what a spreadsheet printed, never a real number/date/formula. None of that is specific to this CLI or the TUI; see documents.js's own README, particularly its [Fidelity](
|
|
289
|
+
- **The conversions this CLI runs carry the same fidelity limits documents.js itself documents** — most notably that PDF-pivot conversions are not round-trip-lossless (line wrapping drifts under standard-14 font substitution, justified text renders left-aligned, tables and vector shapes don't reconstruct from a PDF), and that `pdf-to-ods` recovers only what a spreadsheet printed, never a real number/date/formula. None of that is specific to this CLI or the TUI; see documents.js's own README, particularly its [Fidelity](../documents.js/README.md#fidelity) section, for the full, format-by-format account.
|
|
290
|
+
|
|
291
|
+
## Contributing
|
|
292
|
+
|
|
293
|
+
Release, CI, and commit-message conventions are all workspace-wide, not package-local — see the [monorepo root README](../../README.md#releases) for the release mechanism and [CONTRIBUTING.md](../../CONTRIBUTING.md) for the shared git hooks and history conventions. Work inside `packages/document-cli/`.
|
|
290
294
|
|
|
291
295
|
## License
|
|
292
296
|
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as odbFormSummary, c as
|
|
2
|
+
import { a as odbFormSummary, c as presentMetadataEntries, d as resolveDefaultOutputPath, f as writeOutput, g as isDocumentFormat, h as inferFormatFromExtension, i as formatOdbReportLines, l as formatDocxExtrasLines, n as describeOdbReport, o as formatMetadataLines, p as loadProvidedFonts, r as formatOdbFormLines, t as describeOdbForm, u as readInput } from "./odb-structure-CItnH50p.js";
|
|
3
3
|
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
4
4
|
import { writeFile } from "node:fs/promises";
|
|
5
5
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
6
|
-
import { CsvSheetNotFoundError, CsvSheetNotSpecifiedError, HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, SvgMultiPageNotSpecifiedError, SvgPageNotFoundError, UnrecognizedDocumentSchemaError, UnsupportedFontSourceFormatError, buildCsvText, buildDocumentBytes, buildSvgText, createLocalDocumentConverter, decodeOdbPackage, decodePackage, documentFromJson,
|
|
6
|
+
import { CsvSheetNotFoundError, CsvSheetNotSpecifiedError, HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, SvgMultiPageNotSpecifiedError, SvgPageNotFoundError, UnrecognizedDocumentSchemaError, UnsupportedFontSourceFormatError, buildCsvText, buildDocumentBytes, buildSvgText, createLocalDocumentConverter, decodeOdbPackage, decodePackage, 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";
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { flattenTree } from "document-schema.js";
|
|
8
9
|
import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.js";
|
|
9
10
|
//#region src/runtime/abort.ts
|
|
10
11
|
function combineSignals(a, b) {
|
|
@@ -13,9 +14,13 @@ function combineSignals(a, b) {
|
|
|
13
14
|
controller.abort(signal.reason);
|
|
14
15
|
};
|
|
15
16
|
if (a.aborted) forward(a);
|
|
16
|
-
else a.addEventListener("abort", () =>
|
|
17
|
+
else a.addEventListener("abort", () => {
|
|
18
|
+
forward(a);
|
|
19
|
+
}, { once: true });
|
|
17
20
|
if (b.aborted) forward(b);
|
|
18
|
-
else b.addEventListener("abort", () =>
|
|
21
|
+
else b.addEventListener("abort", () => {
|
|
22
|
+
forward(b);
|
|
23
|
+
}, { once: true });
|
|
19
24
|
return controller.signal;
|
|
20
25
|
}
|
|
21
26
|
function createRuntimeSignal(options) {
|
|
@@ -134,7 +139,7 @@ function createFilesystemMarkdownImageResolver(baseDir) {
|
|
|
134
139
|
}
|
|
135
140
|
//#endregion
|
|
136
141
|
//#region src/commands/shared.ts
|
|
137
|
-
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, pdf";
|
|
142
|
+
const KNOWN_DOCUMENT_FORMATS = "docx, pptx, xlsx, odt, odp, ods, odg, svg, odf, csv, markdown, rtf, wpd, doc, xls, ppt, epub, pdf";
|
|
138
143
|
function resolveTargetFormat(output, out, to) {
|
|
139
144
|
if (to !== void 0) {
|
|
140
145
|
if (!isDocumentFormat(to)) return { errorMessage: `unknown --to format '${to}'; expected one of ${KNOWN_DOCUMENT_FORMATS}` };
|
|
@@ -163,6 +168,7 @@ function buildConversionAction(source, target) {
|
|
|
163
168
|
try {
|
|
164
169
|
const inputBytes = await readInput(input, { signal });
|
|
165
170
|
const fonts = await loadProvidedFonts(options.fontFiles ?? [], { signal });
|
|
171
|
+
const images = createFilesystemMarkdownImageResolver(input === "-" ? "." : dirname(resolve(input)));
|
|
166
172
|
const result = await createLocalDocumentConverter().convert({
|
|
167
173
|
source: {
|
|
168
174
|
format: source,
|
|
@@ -177,7 +183,7 @@ function buildConversionAction(source, target) {
|
|
|
177
183
|
quiet: options.quiet,
|
|
178
184
|
command
|
|
179
185
|
}) : void 0,
|
|
180
|
-
images
|
|
186
|
+
images,
|
|
181
187
|
delimiter: options.delimiter,
|
|
182
188
|
sheet: options.sheet,
|
|
183
189
|
page: options.page
|
|
@@ -189,8 +195,13 @@ function buildConversionAction(source, target) {
|
|
|
189
195
|
command
|
|
190
196
|
});
|
|
191
197
|
for (const diagnostic of result.diagnostics) reporter.report(diagnostic);
|
|
192
|
-
if (options.dumpPackage !== void 0)
|
|
193
|
-
|
|
198
|
+
if (options.dumpPackage !== void 0) {
|
|
199
|
+
const nativeTree = readNativeDocumentTree(source, new Uint8Array(inputBytes), {
|
|
200
|
+
signal,
|
|
201
|
+
images
|
|
202
|
+
});
|
|
203
|
+
await writeFile(options.dumpPackage, JSON.stringify(documentTreeWithSchema(nativeTree), void 0, 2));
|
|
204
|
+
}
|
|
194
205
|
reporter.summarize({
|
|
195
206
|
output: resolvedOutput,
|
|
196
207
|
bytes: result.document.bytes.byteLength,
|
|
@@ -221,7 +232,7 @@ function addVerboseOption(command) {
|
|
|
221
232
|
return command.option("--verbose", "include a full stack trace when the run fails", false);
|
|
222
233
|
}
|
|
223
234
|
function addDumpPackageOption(command) {
|
|
224
|
-
return command.option("--dump-package <file>", "write the intermediate
|
|
235
|
+
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");
|
|
225
236
|
}
|
|
226
237
|
function collectFontFile(value, previous) {
|
|
227
238
|
return [...previous, value];
|
|
@@ -404,6 +415,9 @@ function isSchemaVersionMismatchError(error) {
|
|
|
404
415
|
function isLayoutSchemaDemotedError(error) {
|
|
405
416
|
return error instanceof Error && error.name === "LayoutSchemaDemotedError";
|
|
406
417
|
}
|
|
418
|
+
function isDocumentPackageRenamedError(error) {
|
|
419
|
+
return error instanceof Error && error.name === "DocumentPackageRenamedError";
|
|
420
|
+
}
|
|
407
421
|
async function runFromPackage(input, output, options) {
|
|
408
422
|
const command = "from-package";
|
|
409
423
|
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
@@ -428,11 +442,11 @@ async function runFromPackage(input, output, options) {
|
|
|
428
442
|
return 1;
|
|
429
443
|
}
|
|
430
444
|
const result = documentFromJson(parsed);
|
|
431
|
-
if (result.kind !== "
|
|
432
|
-
process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a
|
|
445
|
+
if (result.kind !== "DocumentTree") {
|
|
446
|
+
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`);
|
|
433
447
|
return 2;
|
|
434
448
|
}
|
|
435
|
-
const content =
|
|
449
|
+
const content = flattenTree(result.value);
|
|
436
450
|
const bytes = target.format === "csv" ? encodeCsvText(buildCsvText(content, {
|
|
437
451
|
delimiter: options.delimiter,
|
|
438
452
|
sheet: options.sheet
|
|
@@ -454,19 +468,23 @@ async function runFromPackage(input, output, options) {
|
|
|
454
468
|
return 1;
|
|
455
469
|
}
|
|
456
470
|
if (isSchemaVersionMismatchError(error)) {
|
|
457
|
-
process.stderr.write(`[${command}] '${input}' is a
|
|
471
|
+
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`);
|
|
458
472
|
return 1;
|
|
459
473
|
}
|
|
460
474
|
if (isLayoutSchemaDemotedError(error)) {
|
|
461
475
|
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`);
|
|
462
476
|
return 1;
|
|
463
477
|
}
|
|
478
|
+
if (isDocumentPackageRenamedError(error)) {
|
|
479
|
+
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`);
|
|
480
|
+
return 1;
|
|
481
|
+
}
|
|
464
482
|
process.stderr.write(`${formatError(error, options.verbose)}\n`);
|
|
465
483
|
return mapErrorToExit(error, getAbortReason());
|
|
466
484
|
}
|
|
467
485
|
}
|
|
468
486
|
function registerFromPackageCommand(program) {
|
|
469
|
-
const command = program.command("from-package <input> [output]").description("read a
|
|
487
|
+
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");
|
|
470
488
|
addOutOption(command);
|
|
471
489
|
addTimeoutOption(command);
|
|
472
490
|
addJsonOption(command);
|
|
@@ -958,20 +976,6 @@ function registerOdmCommand(program) {
|
|
|
958
976
|
}
|
|
959
977
|
//#endregion
|
|
960
978
|
//#region src/commands/outline.ts
|
|
961
|
-
const OUTLINE_CONVERSION_TARGET = {
|
|
962
|
-
docx: "odt",
|
|
963
|
-
odt: "docx",
|
|
964
|
-
markdown: "docx",
|
|
965
|
-
pptx: "odp",
|
|
966
|
-
odp: "pptx",
|
|
967
|
-
xlsx: "ods",
|
|
968
|
-
ods: "xlsx",
|
|
969
|
-
csv: "xlsx",
|
|
970
|
-
odg: "odp",
|
|
971
|
-
svg: "odg",
|
|
972
|
-
pdf: "docx",
|
|
973
|
-
odf: "pdf"
|
|
974
|
-
};
|
|
975
979
|
const INDENT = " ";
|
|
976
980
|
function singleLineText(text) {
|
|
977
981
|
return text.replaceAll(/\s+/g, " ").trim();
|
|
@@ -1007,7 +1011,6 @@ async function runOutline(input, options) {
|
|
|
1007
1011
|
process.stderr.write(`[${command}] ${source.errorMessage}\n`);
|
|
1008
1012
|
return 2;
|
|
1009
1013
|
}
|
|
1010
|
-
const target = OUTLINE_CONVERSION_TARGET[source.format];
|
|
1011
1014
|
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
1012
1015
|
const reporter = createDiagnosticReporter({
|
|
1013
1016
|
json: options.json,
|
|
@@ -1016,19 +1019,14 @@ async function runOutline(input, options) {
|
|
|
1016
1019
|
});
|
|
1017
1020
|
try {
|
|
1018
1021
|
const inputBytes = await readInput(input, { signal });
|
|
1019
|
-
const
|
|
1020
|
-
source: {
|
|
1021
|
-
format: source.format,
|
|
1022
|
-
bytes: new Uint8Array(inputBytes)
|
|
1023
|
-
},
|
|
1024
|
-
targetFormat: target
|
|
1025
|
-
}, {
|
|
1022
|
+
const tree = readNativeDocumentTree(source.format, new Uint8Array(inputBytes), {
|
|
1026
1023
|
signal,
|
|
1027
|
-
images: createFilesystemMarkdownImageResolver(input === "-" ? "." : dirname(resolve(input)))
|
|
1024
|
+
images: createFilesystemMarkdownImageResolver(input === "-" ? "." : dirname(resolve(input))),
|
|
1025
|
+
sink: (diagnostic) => {
|
|
1026
|
+
reporter.report(diagnostic);
|
|
1027
|
+
}
|
|
1028
1028
|
});
|
|
1029
|
-
|
|
1030
|
-
if (result.package === void 0) throw new Error(`the ${source.format}-to-${target} conversion produced no intermediate DocumentPackage`);
|
|
1031
|
-
const outline = buildOutline(result.package);
|
|
1029
|
+
const outline = buildOutline(tree);
|
|
1032
1030
|
if (options.json) {
|
|
1033
1031
|
process.stdout.write(`${JSON.stringify(outline, void 0, 2)}\n`);
|
|
1034
1032
|
return 0;
|
|
@@ -1176,11 +1174,11 @@ async function runSetMetadata(input, output, options) {
|
|
|
1176
1174
|
function registerSetMetadataCommand(program) {
|
|
1177
1175
|
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", [
|
|
1178
1176
|
"",
|
|
1179
|
-
"
|
|
1180
|
-
"
|
|
1181
|
-
"
|
|
1182
|
-
"
|
|
1183
|
-
"
|
|
1177
|
+
"Three write paths: a pdf source/target patches the metadata directly on the parsed PDF (writePdf), and a docx source/target",
|
|
1178
|
+
"patches docProps/core.xml directly on the decoded package -- both with no layout engine or ContentDocument rebuild involved",
|
|
1179
|
+
"at all, so everything else on the page (pdf) or in the package (docx -- comments, footnotes, headers/footers, numbering",
|
|
1180
|
+
"definitions included) survives byte-faithful. Every other supported format (pptx, xlsx, odt, odp, ods, odg, markdown, rtf)",
|
|
1181
|
+
"rebuilds a fresh package from that format's own ContentDocument instead.",
|
|
1184
1182
|
"",
|
|
1185
1183
|
"set-metadata does not convert format -- source and target must match. Run convert/from-package first, then",
|
|
1186
1184
|
"set-metadata on the result, if you need a different target format."
|
|
@@ -1201,12 +1199,12 @@ function registerSetMetadataCommand(program) {
|
|
|
1201
1199
|
}
|
|
1202
1200
|
//#endregion
|
|
1203
1201
|
//#region package.json
|
|
1204
|
-
var version = "
|
|
1202
|
+
var version = "5.7.10";
|
|
1205
1203
|
//#endregion
|
|
1206
1204
|
//#region src/program.ts
|
|
1207
1205
|
function createProgram() {
|
|
1208
1206
|
const program = new Command("document-cli");
|
|
1209
|
-
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");
|
|
1207
|
+
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");
|
|
1210
1208
|
program.version(version);
|
|
1211
1209
|
program.exitOverride((error) => {
|
|
1212
1210
|
process.exitCode = error.exitCode === 0 ? 0 : 2;
|
|
@@ -1229,7 +1227,7 @@ function createProgram() {
|
|
|
1229
1227
|
//#region src/cli.ts
|
|
1230
1228
|
async function launchTui(startPath, signal) {
|
|
1231
1229
|
try {
|
|
1232
|
-
const { runTui } = await import("./tui-
|
|
1230
|
+
const { runTui } = await import("./tui-CGo4rAhe.js");
|
|
1233
1231
|
await runTui({
|
|
1234
1232
|
startPath,
|
|
1235
1233
|
signal
|
|
@@ -1248,7 +1246,7 @@ async function main() {
|
|
|
1248
1246
|
const args = process.argv.slice(2);
|
|
1249
1247
|
const [dispatchToken] = args;
|
|
1250
1248
|
if (dispatchToken === void 0 || dispatchToken === "tui") {
|
|
1251
|
-
if (process.stdout.isTTY
|
|
1249
|
+
if (!process.stdout.isTTY) {
|
|
1252
1250
|
if (dispatchToken === "tui") {
|
|
1253
1251
|
process.stderr.write("the TUI requires an interactive terminal (TTY); stdout is currently redirected\n");
|
|
1254
1252
|
process.exitCode = 2;
|