pi-filler 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -49
- package/package.json +9 -2
- package/src/docx.ts +4 -8
- package/src/index.ts +314 -25
- package/src/ooxml.ts +15 -0
- package/src/xlsx.ts +1415 -0
package/README.md
CHANGED
|
@@ -1,82 +1,235 @@
|
|
|
1
1
|
# pi-filler
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://gitlab.com/Joao-O-Santos/pi-filler/-/commits/main)
|
|
7
|
+
[](https://www.npmjs.com/package/pi-filler)
|
|
9
|
+
[](https://www.npmjs.com/package/pi-filler)
|
|
11
|
+
[](https://gitlab.com/Joao-O-Santos/pi-filler/-/blob/main/LICENSE)
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
workflows and final office-format requirements. It delegates common
|
|
9
|
-
conversion and PDF extraction to mature command-line tools while keeping
|
|
10
|
-
Word-specific mutations narrow and inspectable.
|
|
13
|
+
**Deterministic DOCX, PDF, and XLSX tooling for Pi.**
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
`pi-filler` covers document tasks that do not fit an ordinary Markdown
|
|
16
|
+
workflow. It delegates conversion and PDF extraction to mature
|
|
17
|
+
command-line tools while keeping DOCX and XLSX mutations narrow,
|
|
18
|
+
transactional, and inspectable.
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
The project is experimental and pre-1.0. The README documents the
|
|
21
|
+
current main branch; the npm badge shows the currently published
|
|
22
|
+
version, which may not yet contain every feature described here.
|
|
16
23
|
|
|
17
|
-
##
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Install the published package:
|
|
27
|
+
|
|
28
|
+
``` sh
|
|
29
|
+
pi install npm:pi-filler
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or install the current main branch for development and unreleased
|
|
33
|
+
features:
|
|
34
|
+
|
|
35
|
+
``` sh
|
|
36
|
+
pi install git:https://gitlab.com/Joao-O-Santos/pi-filler.git
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
External programs are required only for the operations listed under
|
|
40
|
+
[External tools](#external-tools). XLSX operations run locally through
|
|
41
|
+
Node.js and do not require an office suite.
|
|
42
|
+
|
|
43
|
+
## Tool and operation matrix
|
|
18
44
|
|
|
19
45
|
The extension exposes one `filler` tool:
|
|
20
46
|
|
|
21
|
-
```text
|
|
22
|
-
format: docx | pdf
|
|
47
|
+
``` text
|
|
48
|
+
format: docx | pdf | xlsx
|
|
23
49
|
action: read | search | write | patch
|
|
24
|
-
view: text | formatting | image
|
|
50
|
+
view: text | formatting | image | structure
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`format` names the document or target format, not every input: DOCX
|
|
54
|
+
writes take Markdown at `path`, and XLSX writes take a workbook template
|
|
55
|
+
at `path` plus CSV at `source_csv`. Choose a supported combination and
|
|
56
|
+
supply only its relevant fields:
|
|
57
|
+
|
|
58
|
+
| Format | Action | View | Required additional fields |
|
|
59
|
+
|---------------|---------------|---------------|---------------------------|
|
|
60
|
+
| DOCX | `read` | `text` or `formatting` | none |
|
|
61
|
+
| DOCX | `read` | `image` | `output`; optional `first_page`, `last_page` |
|
|
62
|
+
| DOCX | `search` | `text` or omitted | `query`; optional `ignore_case` |
|
|
63
|
+
| DOCX | `write` | omitted | Markdown `path`, `output`; optional `reference_docx` |
|
|
64
|
+
| DOCX | `patch` | omitted | `output`, `patches`; optional `dry_run` |
|
|
65
|
+
| PDF | `read` | `text` | optional `first_page`, `last_page` |
|
|
66
|
+
| PDF | `read` | `image` | `output`; optional `first_page`, `last_page` |
|
|
67
|
+
| PDF | `search` | `text` or omitted | `query`; optional `first_page`, `last_page`, `literal`, `ignore_case` |
|
|
68
|
+
| XLSX | `read` | `structure` | none |
|
|
69
|
+
| XLSX | `read` | `image` | `output`; optional `first_page`, `last_page`; all workbook print pages |
|
|
70
|
+
| XLSX | `write` | omitted | template `path`, `source_csv`, `sheet`, A1 `start_cell`, `output`; optional `column_map` |
|
|
71
|
+
| XLSX | `patch` | omitted | `sheet`, A1 `range`, `xlsx_patches`, `output` |
|
|
72
|
+
|
|
73
|
+
PDF write and patch, and XLSX search, are unsupported. Parameters
|
|
74
|
+
irrelevant to the chosen operation fail explicitly instead of being
|
|
75
|
+
ignored.
|
|
76
|
+
|
|
77
|
+
Paths resolve relative to Pi's current working directory; absolute paths
|
|
78
|
+
and Pi-style paths prefixed with `@` are also accepted. A write or patch
|
|
79
|
+
output must differ from its input. For image reads, `output` is an
|
|
80
|
+
existing directory or PNG prefix; the operation creates page images
|
|
81
|
+
rather than one final PNG file.
|
|
82
|
+
|
|
83
|
+
`first_page` and `last_page` select 1-based inclusive pages. Both must
|
|
84
|
+
be positive, and `first_page` cannot exceed `last_page`. Use
|
|
85
|
+
`start_cell` like `A2` and `range` like `A2:D10`; a range may also be
|
|
86
|
+
one cell.
|
|
87
|
+
|
|
88
|
+
PDF search interprets `query` as a regular expression by default; set
|
|
89
|
+
`literal: true` for literal text. It is case-sensitive unless
|
|
90
|
+
`ignore_case: true` is set. XLSX writes skip the first CSV record by
|
|
91
|
+
default and preserve fields as text by default. `dry_run: true` still
|
|
92
|
+
requires `output`, but it does not create or replace it.
|
|
93
|
+
|
|
94
|
+
Example calls:
|
|
95
|
+
|
|
96
|
+
``` ts
|
|
97
|
+
filler({
|
|
98
|
+
format: "pdf",
|
|
99
|
+
action: "search",
|
|
100
|
+
path: "paper.pdf",
|
|
101
|
+
query: "registered report",
|
|
102
|
+
ignore_case: true
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
filler({
|
|
106
|
+
format: "xlsx",
|
|
107
|
+
action: "write",
|
|
108
|
+
path: "template.xlsx",
|
|
109
|
+
source_csv: "results.csv",
|
|
110
|
+
sheet: "Results",
|
|
111
|
+
start_cell: "A2",
|
|
112
|
+
output: "completed.xlsx",
|
|
113
|
+
dry_run: true
|
|
114
|
+
})
|
|
25
115
|
```
|
|
26
116
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
117
|
+
Results may be truncated. For a truncated PDF text read, use a page
|
|
118
|
+
range; for PDF or DOCX search, narrow the query. PDF search can also use
|
|
119
|
+
a page range. Dry runs still read and validate all local inputs.
|
|
30
120
|
|
|
31
|
-
## PDF
|
|
121
|
+
## PDF operations
|
|
32
122
|
|
|
33
|
-
-
|
|
34
|
-
- Search
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
-
|
|
123
|
+
- Text reads use layout-preserving extraction and optional page ranges.
|
|
124
|
+
- Search returns page-aware matches and supports 1-based inclusive page
|
|
125
|
+
ranges, case-insensitive matching, and literal rather than
|
|
126
|
+
regular-expression queries.
|
|
127
|
+
- Image reads render selected pages to numbered PNG files.
|
|
128
|
+
- A failed render preserves previous files for the requested output
|
|
129
|
+
prefix.
|
|
39
130
|
|
|
40
|
-
## DOCX
|
|
131
|
+
## DOCX operations
|
|
41
132
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
-
|
|
133
|
+
- Text reads and search convert DOCX through Pandoc.
|
|
134
|
+
- Image reads convert DOCX through LibreOffice and then render the
|
|
135
|
+
resulting PDF pages.
|
|
136
|
+
- Writes convert a Markdown input into a validated DOCX, optionally
|
|
137
|
+
using a reference DOCX for styles and layout.
|
|
138
|
+
- Formatting reads inspect page setup, margins, sections, line and page
|
|
48
139
|
numbering, comments, inserted/deleted tracked-change markers, and
|
|
49
140
|
common core metadata.
|
|
50
|
-
-
|
|
51
|
-
core metadata.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
141
|
+
- Patches change first-section page setup, margins, line numbering, page
|
|
142
|
+
numbering, and common core metadata.
|
|
143
|
+
|
|
144
|
+
DOCX page dimensions and margins use twips (twentieths of a point); 1
|
|
145
|
+
inch is 1,440 twips. Page setup, margins, line numbering, and page
|
|
146
|
+
numbering patches apply to the first section only. Core-metadata patches
|
|
147
|
+
apply to the document.
|
|
148
|
+
|
|
149
|
+
`clearCoreMetadata` clears common `docProps/core.xml` fields. It does
|
|
150
|
+
**not** remove comment authors, revision authors, or every possible
|
|
151
|
+
identity-bearing property and must not be described as anonymization.
|
|
152
|
+
|
|
153
|
+
Untouched ZIP package parts remain content-identical. Edited XML parts
|
|
154
|
+
are parsed and serialized again, so their lexical formatting, prefixes,
|
|
155
|
+
or element ordering may change.
|
|
156
|
+
|
|
157
|
+
## XLSX operations and privacy
|
|
158
|
+
|
|
159
|
+
Spreadsheet cell values, CSV fields, formulas, comments, and other
|
|
160
|
+
workbook text are processed locally and are not returned to the model.
|
|
161
|
+
Structural results contain sheet names, ranges, dimensions, counts,
|
|
162
|
+
merged-range addresses, hidden row and column ranges, changed package
|
|
163
|
+
parts, and write status---not workbook or CSV contents. Layout-range
|
|
164
|
+
arrays are capped and report whether they were truncated; this metadata
|
|
165
|
+
can reveal limited layout and occupancy information.
|
|
166
|
+
|
|
167
|
+
- `read` with `view: "structure"` reports workbook and worksheet shape.
|
|
168
|
+
- `read` with `view: "image"` renders all workbook print pages through
|
|
169
|
+
LibreOffice to numbered PNG files; it does not select a sheet.
|
|
170
|
+
- `write` fills an existing workbook template from CSV.
|
|
171
|
+
- `patch` changes formatting or layout while preserving cell contents.
|
|
172
|
+
|
|
173
|
+
CSV writes skip the first record by default (`has_header: true`) and
|
|
174
|
+
preserve fields as text by default (`value_mode: "text"`). Automatic
|
|
175
|
+
mode recognizes only empty values, numbers, and booleans; it does not
|
|
176
|
+
infer dates or formulas. `column_map` optionally maps either all 1-based
|
|
177
|
+
CSV ordinals or all exact CSV headers to absolute XLSX column letters,
|
|
178
|
+
such as `{ "1": "A", "2": "C" }`. Header mappings require unique CSV
|
|
179
|
+
headers; destinations must be unique. The entire write fails if its
|
|
180
|
+
mapped destination cells intersect an existing formula or merged range.
|
|
181
|
+
|
|
182
|
+
XLSX patches support bold, italic, font size, six-digit RGB fills,
|
|
183
|
+
horizontal and vertical alignment, wrapping, Excel number-format codes,
|
|
184
|
+
uniform borders, column width, and row height. Output is validated and
|
|
185
|
+
atomically replaced only after successful processing.
|
|
186
|
+
|
|
187
|
+
XLSX support does not provide cell-content reads or search, formula
|
|
188
|
+
calculation or generation, spreadsheet scripting, or manipulation of
|
|
189
|
+
charts, pivots, macros, comments, named ranges, tables, data validation,
|
|
190
|
+
conditional formatting, or external data.
|
|
191
|
+
|
|
192
|
+
## Output and mutation behavior
|
|
193
|
+
|
|
194
|
+
DOCX and XLSX writes and patches use temporary sibling files, validate
|
|
195
|
+
the result, and rename it into place only after success. Source files
|
|
196
|
+
are not modified. PDF image rendering similarly protects prior output
|
|
197
|
+
until the new render succeeds.
|
|
198
|
+
|
|
199
|
+
`dry_run` validates the requested operation and inputs without creating
|
|
200
|
+
or replacing output; its required `output` path is still validated.
|
|
61
201
|
|
|
62
202
|
## External tools
|
|
63
203
|
|
|
64
204
|
Required for the corresponding operations:
|
|
65
205
|
|
|
66
|
-
- `pandoc` for DOCX text conversion and generation
|
|
67
|
-
- `libreoffice` for DOCX image rendering
|
|
68
|
-
- `pdftotext` for PDF text extraction
|
|
69
|
-
- `pdftocairo` for PDF and
|
|
206
|
+
- `pandoc` for DOCX text conversion, search, and generation;
|
|
207
|
+
- `libreoffice` for DOCX and XLSX image rendering;
|
|
208
|
+
- `pdftotext` for PDF text extraction;
|
|
209
|
+
- `pdftocairo` for PDF, DOCX, and XLSX page rendering; and
|
|
70
210
|
- `pdfgrep` for page-aware PDF search.
|
|
71
211
|
|
|
72
|
-
|
|
212
|
+
A missing executable produces an explicit error naming the unavailable
|
|
213
|
+
program.
|
|
214
|
+
|
|
215
|
+
## Development and release
|
|
73
216
|
|
|
74
|
-
```sh
|
|
217
|
+
``` sh
|
|
75
218
|
npm install
|
|
76
219
|
make verify
|
|
77
220
|
make site
|
|
78
221
|
```
|
|
79
222
|
|
|
223
|
+
`make verify` runs typechecking, lint and formatting checks,
|
|
224
|
+
deterministic tests, and `npm pack --dry-run`. `make site` regenerates
|
|
225
|
+
the tracked static site after documentation changes.
|
|
226
|
+
|
|
80
227
|
Dependencies and CI actions follow current upstream releases rather than
|
|
81
|
-
being pinned. GitLab is canonical and the only release authority.
|
|
82
|
-
|
|
228
|
+
being pinned. GitLab is canonical and the only release authority. GitHub
|
|
229
|
+
may mirror the repository, verify it, and publish GitHub Pages. A
|
|
230
|
+
`vX.Y.Z` GitLab tag must match `package.json`; after verification it
|
|
231
|
+
publishes to npm with provenance.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
[MIT](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-filler",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Deterministic DOCX and
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Deterministic DOCX, PDF, and XLSX tooling for Pi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi-extension",
|
|
8
8
|
"docx",
|
|
9
9
|
"pdf",
|
|
10
|
+
"xlsx",
|
|
10
11
|
"pandoc",
|
|
11
12
|
"document-processing"
|
|
12
13
|
],
|
|
@@ -46,6 +47,11 @@
|
|
|
46
47
|
"./src/index.ts"
|
|
47
48
|
]
|
|
48
49
|
},
|
|
50
|
+
"allowScripts": {
|
|
51
|
+
"@google/genai": false,
|
|
52
|
+
"esbuild": true,
|
|
53
|
+
"protobufjs": false
|
|
54
|
+
},
|
|
49
55
|
"peerDependencies": {
|
|
50
56
|
"@earendil-works/pi-ai": "*",
|
|
51
57
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -63,6 +69,7 @@
|
|
|
63
69
|
"typescript": "latest"
|
|
64
70
|
},
|
|
65
71
|
"dependencies": {
|
|
72
|
+
"csv-parse": "latest",
|
|
66
73
|
"fast-xml-parser": "latest",
|
|
67
74
|
"fflate": "latest"
|
|
68
75
|
}
|
package/src/docx.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, posix, resolve } from "node:path";
|
|
4
4
|
import { XMLBuilder, XMLParser } from "fast-xml-parser";
|
|
5
|
-
import {
|
|
5
|
+
import { type OfficePackage, packOfficePackage, unpackOfficePackage } from "./ooxml.js";
|
|
6
6
|
|
|
7
7
|
export interface DocxPatchSet {
|
|
8
8
|
page?: { width?: number; height?: number; orientation?: "portrait" | "landscape" };
|
|
@@ -50,7 +50,7 @@ export interface DocxPatchResult {
|
|
|
50
50
|
written: boolean;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
type Package =
|
|
53
|
+
type Package = OfficePackage;
|
|
54
54
|
type XmlObject = Record<string, unknown>;
|
|
55
55
|
|
|
56
56
|
const WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
@@ -86,11 +86,7 @@ function serializeXml(value: XmlObject): Uint8Array {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function unpack(bytes: Uint8Array): Package {
|
|
89
|
-
|
|
90
|
-
return unzipSync(bytes);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
throw new Error(`Invalid DOCX ZIP package: ${error instanceof Error ? error.message : error}`);
|
|
93
|
-
}
|
|
89
|
+
return unpackOfficePackage(bytes, "DOCX");
|
|
94
90
|
}
|
|
95
91
|
|
|
96
92
|
function findAll(value: unknown, suffix: string, found: XmlObject[] = []): XmlObject[] {
|
|
@@ -507,7 +503,7 @@ export async function patchDocx(
|
|
|
507
503
|
const temporary = join(dirname(output), `.${output.split(/[\\/]/).pop()}.${randomUUID()}.tmp`);
|
|
508
504
|
await mkdir(dirname(output), { recursive: true });
|
|
509
505
|
try {
|
|
510
|
-
await writeFile(temporary,
|
|
506
|
+
await writeFile(temporary, packOfficePackage(pkg));
|
|
511
507
|
await inspectDocx(temporary);
|
|
512
508
|
await rename(temporary, output);
|
|
513
509
|
result.written = true;
|