@cairn-tool/cairn 3.2.0 → 3.3.1
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 +73 -29
- package/dist/agent/audit/index.d.ts +6 -6
- package/dist/agent/audit/index.js +6 -17
- package/dist/agent/audit/index.js.map +1 -1
- package/dist/atomic-write.d.ts +22 -0
- package/dist/atomic-write.js +42 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/binary-kind.d.ts +13 -0
- package/dist/binary-kind.js +25 -0
- package/dist/binary-kind.js.map +1 -0
- package/dist/cli.js +543 -250
- package/dist/cli.js.map +1 -1
- package/dist/commands/jira.js +1 -21
- package/dist/commands/jira.js.map +1 -1
- package/dist/commands/pdf.d.ts +29 -0
- package/dist/commands/pdf.js +345 -0
- package/dist/commands/pdf.js.map +1 -0
- package/dist/contract/registry.js +64 -0
- package/dist/contract/registry.js.map +1 -1
- package/dist/contract/schemas/index.js +2 -0
- package/dist/contract/schemas/index.js.map +1 -1
- package/dist/contract/schemas/pdf.d.ts +2 -0
- package/dist/contract/schemas/pdf.js +292 -0
- package/dist/contract/schemas/pdf.js.map +1 -0
- package/dist/jira/adf/to-markdown.js +2 -32
- package/dist/jira/adf/to-markdown.js.map +1 -1
- package/dist/markdown-stringify.d.ts +35 -0
- package/dist/markdown-stringify.js +44 -0
- package/dist/markdown-stringify.js.map +1 -0
- package/dist/pdf/attachments.d.ts +50 -0
- package/dist/pdf/attachments.js +229 -0
- package/dist/pdf/attachments.js.map +1 -0
- package/dist/pdf/diagnostics.d.ts +110 -0
- package/dist/pdf/diagnostics.js +137 -0
- package/dist/pdf/diagnostics.js.map +1 -0
- package/dist/pdf/document.d.ts +159 -0
- package/dist/pdf/document.js +272 -0
- package/dist/pdf/document.js.map +1 -0
- package/dist/pdf/forms.d.ts +9 -0
- package/dist/pdf/forms.js +101 -0
- package/dist/pdf/forms.js.map +1 -0
- package/dist/pdf/inspect.d.ts +42 -0
- package/dist/pdf/inspect.js +232 -0
- package/dist/pdf/inspect.js.map +1 -0
- package/dist/pdf/layout.d.ts +78 -0
- package/dist/pdf/layout.js +524 -0
- package/dist/pdf/layout.js.map +1 -0
- package/dist/pdf/outline.d.ts +30 -0
- package/dist/pdf/outline.js +127 -0
- package/dist/pdf/outline.js.map +1 -0
- package/dist/pdf/read.d.ts +62 -0
- package/dist/pdf/read.js +193 -0
- package/dist/pdf/read.js.map +1 -0
- package/dist/pdf/struct.d.ts +35 -0
- package/dist/pdf/struct.js +226 -0
- package/dist/pdf/struct.js.map +1 -0
- package/dist/pdf/text.d.ts +71 -0
- package/dist/pdf/text.js +166 -0
- package/dist/pdf/text.js.map +1 -0
- package/dist/pdf/to-markdown.d.ts +22 -0
- package/dist/pdf/to-markdown.js +289 -0
- package/dist/pdf/to-markdown.js.map +1 -0
- package/dist/pdf/types.d.ts +205 -0
- package/dist/pdf/types.js +2 -0
- package/dist/pdf/types.js.map +1 -0
- package/dist/pdf/validate.d.ts +51 -0
- package/dist/pdf/validate.js +136 -0
- package/dist/pdf/validate.js.map +1 -0
- package/dist/serve/pdf-tools.d.ts +2 -0
- package/dist/serve/pdf-tools.js +196 -0
- package/dist/serve/pdf-tools.js.map +1 -0
- package/dist/serve/tools.d.ts +2 -17
- package/dist/serve/tools.js +4 -0
- package/dist/serve/tools.js.map +1 -1
- package/dist/serve/types.d.ts +22 -0
- package/dist/serve/types.js +2 -0
- package/dist/serve/types.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CODES, diagnostic } from "./diagnostics.js";
|
|
4
|
+
/** Raised when the wall-clock budget expires. Classified as `AP005`. */
|
|
5
|
+
export class DeadlineExceeded extends Error {
|
|
6
|
+
ms;
|
|
7
|
+
constructor(ms) {
|
|
8
|
+
super(`Exceeded the ${ms}ms budget`);
|
|
9
|
+
this.ms = ms;
|
|
10
|
+
this.name = "DeadlineExceeded";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Raised when the install is broken, rather than the document. */
|
|
14
|
+
export class PdfjsUnavailableError extends Error {
|
|
15
|
+
constructor(cause) {
|
|
16
|
+
super(`The PDF parser could not be loaded (${cause}). Reinstall @cairn-tool/cairn; ` +
|
|
17
|
+
"this is an installation problem rather than a problem with the document.");
|
|
18
|
+
this.name = "PdfjsUnavailableError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolves pdfjs's asset directories.
|
|
23
|
+
*
|
|
24
|
+
* `createRequire(import.meta.url)` is the idiom `src/sqlite.ts` already uses.
|
|
25
|
+
* Resolving `package.json` rather than the entry point lands at the package root
|
|
26
|
+
* with no `..` climbing, and works because pdfjs-dist publishes no `exports`
|
|
27
|
+
* map.
|
|
28
|
+
*
|
|
29
|
+
* These are **runtime resolutions into another package's directory, not files
|
|
30
|
+
* cairn packs**. Nothing here belongs in `package.json` `files`; this looks like
|
|
31
|
+
* the `.markdownlintrc` packaging trap and is not one.
|
|
32
|
+
*/
|
|
33
|
+
function pdfjsRoot() {
|
|
34
|
+
try {
|
|
35
|
+
return path.dirname(createRequire(import.meta.url).resolve("pdfjs-dist/package.json"));
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
throw new PdfjsUnavailableError(error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A trailing separator is load-bearing: pdf.js concatenates
|
|
43
|
+
* `cMapUrl + name + ".bcmap"`, so omitting it produces a wrong path, no cmap,
|
|
44
|
+
* and silently missing CJK text — a degradation indistinguishable from success.
|
|
45
|
+
*/
|
|
46
|
+
function assetDir(root, name) {
|
|
47
|
+
return `${path.join(root, name)}${path.sep}`;
|
|
48
|
+
}
|
|
49
|
+
let cached;
|
|
50
|
+
/**
|
|
51
|
+
* Imports the **legacy** build.
|
|
52
|
+
*
|
|
53
|
+
* Not a preference. The package-root entry prints
|
|
54
|
+
* "Please use the `legacy` build in Node.js environments" and then throws
|
|
55
|
+
* `hashOriginal.toHex is not a function`: it assumes a V8 with
|
|
56
|
+
* `Uint8Array.prototype.toHex`, which Node 22 and 23 do not have. The legacy
|
|
57
|
+
* build works across cairn's whole `engines` range.
|
|
58
|
+
*
|
|
59
|
+
* The `process.emitWarning` swap is the same guard `src/sqlite.ts` applies to
|
|
60
|
+
* `node:sqlite`: stderr carries the diagnostics stream, and an experimental
|
|
61
|
+
* warning emitted at import time would land in it.
|
|
62
|
+
*/
|
|
63
|
+
async function loadPdfjs() {
|
|
64
|
+
if (cached)
|
|
65
|
+
return cached;
|
|
66
|
+
const emit = process.emitWarning;
|
|
67
|
+
try {
|
|
68
|
+
process.emitWarning = () => { };
|
|
69
|
+
cached = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw new PdfjsUnavailableError(error.message);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
process.emitWarning = emit;
|
|
76
|
+
}
|
|
77
|
+
return cached;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A pathological document can emit a warning per object; past this the count is
|
|
81
|
+
* kept and the text is dropped.
|
|
82
|
+
*/
|
|
83
|
+
const CAPTURE_CAP = 2000;
|
|
84
|
+
/**
|
|
85
|
+
* Opens a document, runs `body`, and tears down — in that order, always.
|
|
86
|
+
*
|
|
87
|
+
* A scoped-resource function rather than an open/close pair so a caller cannot
|
|
88
|
+
* forget the teardown. On 6.3.289 pdf.js uses the main-thread fake worker in
|
|
89
|
+
* Node (no worker thread is spawned, verified), so a leaked handle does not hang
|
|
90
|
+
* the process — but `destroy()` still releases the parsed object graph, and a
|
|
91
|
+
* future version spawning a real worker would make it load-bearing.
|
|
92
|
+
*/
|
|
93
|
+
export async function withDocument(data, options, body) {
|
|
94
|
+
const pdfjs = await loadPdfjs();
|
|
95
|
+
const root = pdfjsRoot();
|
|
96
|
+
// pdf.js refuses a Node `Buffer` outright — "Please provide binary data as
|
|
97
|
+
// `Uint8Array`" — even though a Buffer is one. `read.ts` already hands over a
|
|
98
|
+
// plain array it owns, but normalizing here means no caller can get it wrong,
|
|
99
|
+
// and this is the site whose job is to absorb exactly that.
|
|
100
|
+
const bytes = data.constructor === Uint8Array ? data : Uint8Array.from(data);
|
|
101
|
+
const captured = [];
|
|
102
|
+
let dropped = 0;
|
|
103
|
+
const saved = {
|
|
104
|
+
log: console.log,
|
|
105
|
+
warn: console.warn,
|
|
106
|
+
error: console.error,
|
|
107
|
+
info: console.info,
|
|
108
|
+
debug: console.debug,
|
|
109
|
+
};
|
|
110
|
+
const capture = (...args) => {
|
|
111
|
+
if (captured.length < CAPTURE_CAP)
|
|
112
|
+
captured.push(args.map(String).join(" "));
|
|
113
|
+
else
|
|
114
|
+
dropped += 1;
|
|
115
|
+
};
|
|
116
|
+
// Captured rather than silenced. pdf.js routes warn() to console.warn, which
|
|
117
|
+
// is stderr — the stream that carries this toolset's diagnostics and must be
|
|
118
|
+
// empty on a clean run. Capturing also gives `validate` its only signal for a
|
|
119
|
+
// reconstructed xref, a substituted font, or an unsupported filter; setting
|
|
120
|
+
// verbosity to ERRORS would keep stderr clean and take those away.
|
|
121
|
+
console.log = capture;
|
|
122
|
+
console.warn = capture;
|
|
123
|
+
console.error = capture;
|
|
124
|
+
console.info = capture;
|
|
125
|
+
console.debug = capture;
|
|
126
|
+
const deadline = Date.now() + options.timeoutMs;
|
|
127
|
+
const within = async (work) => {
|
|
128
|
+
const remaining = deadline - Date.now();
|
|
129
|
+
if (remaining <= 0)
|
|
130
|
+
throw new DeadlineExceeded(options.timeoutMs);
|
|
131
|
+
let timer;
|
|
132
|
+
try {
|
|
133
|
+
return await Promise.race([
|
|
134
|
+
work(),
|
|
135
|
+
new Promise((_, reject) => {
|
|
136
|
+
// unref'd, or the timer itself keeps the event loop alive and the CLI
|
|
137
|
+
// waits out the whole budget after it has already written its output.
|
|
138
|
+
timer = setTimeout(() => reject(new DeadlineExceeded(options.timeoutMs)), remaining);
|
|
139
|
+
timer.unref();
|
|
140
|
+
}),
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
if (timer)
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
let task;
|
|
149
|
+
try {
|
|
150
|
+
let needsPassword = false;
|
|
151
|
+
task = pdfjs.getDocument({
|
|
152
|
+
data: bytes,
|
|
153
|
+
// No `url` and no `docBaseUrl`: absence is the hardening. Supplying either
|
|
154
|
+
// would make pdf.js the thing performing I/O, and the toolset could no
|
|
155
|
+
// longer claim it does not reach the network.
|
|
156
|
+
verbosity: pdfjs.VerbosityLevel.WARNINGS,
|
|
157
|
+
cMapUrl: assetDir(root, "cmaps"),
|
|
158
|
+
cMapPacked: true,
|
|
159
|
+
standardFontDataUrl: assetDir(root, "standard_fonts"),
|
|
160
|
+
iccUrl: assetDir(root, "iccs"),
|
|
161
|
+
wasmUrl: assetDir(root, "wasm"),
|
|
162
|
+
useWasm: false,
|
|
163
|
+
// The field that actually keeps the no-network claim true: it forces the
|
|
164
|
+
// main thread to supply cmap and font bytes from the paths above instead
|
|
165
|
+
// of letting the worker call fetch().
|
|
166
|
+
useWorkerFetch: false,
|
|
167
|
+
// Probing the host's installed fonts would make output machine-dependent.
|
|
168
|
+
useSystemFonts: false,
|
|
169
|
+
disableFontFace: true,
|
|
170
|
+
// Always false, in every command. Enumerating every damaged page is worth
|
|
171
|
+
// more than dying on the first, and `--strict` changes the exit rule
|
|
172
|
+
// rather than the parse.
|
|
173
|
+
stopAtErrors: false,
|
|
174
|
+
maxImageSize: 32 * 1024 * 1024,
|
|
175
|
+
});
|
|
176
|
+
// Never prompts: a CLI that blocks on a TTY read is unusable in a pipeline.
|
|
177
|
+
// There is no --password flag, so this only ever refuses.
|
|
178
|
+
task.onPassword = (reply) => {
|
|
179
|
+
needsPassword = true;
|
|
180
|
+
reply(new Error("password required"));
|
|
181
|
+
};
|
|
182
|
+
const doc = (await within(() => task.promise));
|
|
183
|
+
if (doc.numPages > options.maxPages)
|
|
184
|
+
throw new PageBudgetExceeded(doc.numPages, options.maxPages);
|
|
185
|
+
void needsPassword;
|
|
186
|
+
const permissions = await within(() => doc.getPermissions());
|
|
187
|
+
const handle = {
|
|
188
|
+
doc,
|
|
189
|
+
notices: captured,
|
|
190
|
+
// getPermissions() returns null on an unencrypted document and a Set on an
|
|
191
|
+
// encrypted one, verified against 6.3.289 — so a non-null result here with
|
|
192
|
+
// no password supplied means the document opened on an empty user
|
|
193
|
+
// password, and its restrictions are advisory only.
|
|
194
|
+
encrypted: permissions !== null,
|
|
195
|
+
within,
|
|
196
|
+
};
|
|
197
|
+
return await body(handle);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
// destroy() first, restore second. pdf.js fires callbacks during teardown,
|
|
201
|
+
// and restoring the console before the worker is gone lets a late warning
|
|
202
|
+
// escape onto a stream that is carrying a payload.
|
|
203
|
+
if (task) {
|
|
204
|
+
try {
|
|
205
|
+
await task.destroy();
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Teardown failure cannot change the answer already computed.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
Object.assign(console, saved);
|
|
212
|
+
if (dropped > 0)
|
|
213
|
+
captured.push(`(${dropped} further parser messages dropped)`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** Raised before any page is touched, when the document is larger than allowed. */
|
|
217
|
+
export class PageBudgetExceeded extends Error {
|
|
218
|
+
pages;
|
|
219
|
+
limit;
|
|
220
|
+
constructor(pages, limit) {
|
|
221
|
+
super(`Document has ${pages} pages, more than the ${limit} allowed`);
|
|
222
|
+
this.pages = pages;
|
|
223
|
+
this.limit = limit;
|
|
224
|
+
this.name = "PageBudgetExceeded";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Maps a thrown value onto a finding.
|
|
229
|
+
*
|
|
230
|
+
* Matched by class where pdf.js exports one, never by `error.name`: a name is a
|
|
231
|
+
* string that has drifted before, and the classes are exported precisely so a
|
|
232
|
+
* consumer can branch on them.
|
|
233
|
+
*
|
|
234
|
+
* The distinction between `AP002` and `AP100` is worth keeping: `AP002` means
|
|
235
|
+
* the bytes never had a `%PDF-` header, decided by `read.ts` before pdf.js ran,
|
|
236
|
+
* and `AP100` means it looked like a PDF and could not be parsed. That is the
|
|
237
|
+
* difference between "wrong file" and "damaged file".
|
|
238
|
+
*/
|
|
239
|
+
export async function classify(error, page) {
|
|
240
|
+
const pdfjs = await loadPdfjs().catch(() => null);
|
|
241
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
242
|
+
const build = (code, text, remediation) => diagnostic({
|
|
243
|
+
code,
|
|
244
|
+
severity: "error",
|
|
245
|
+
quality: "unsupported",
|
|
246
|
+
message: text,
|
|
247
|
+
...(page !== undefined ? { page } : {}),
|
|
248
|
+
...(remediation ? { remediation } : {}),
|
|
249
|
+
});
|
|
250
|
+
if (error instanceof DeadlineExceeded)
|
|
251
|
+
return build(CODES.timedOut, message, "Raise the budget with --timeout.");
|
|
252
|
+
if (error instanceof PageBudgetExceeded)
|
|
253
|
+
return build(CODES.tooManyPages, message, "Raise the bound with --max-pages.");
|
|
254
|
+
if (error instanceof PdfjsUnavailableError)
|
|
255
|
+
return build(CODES.failure, message);
|
|
256
|
+
if (pdfjs) {
|
|
257
|
+
if (error instanceof pdfjs.PasswordException) {
|
|
258
|
+
const incorrect = error.code === pdfjs.PasswordResponses?.INCORRECT_PASSWORD;
|
|
259
|
+
return build(incorrect ? CODES.passwordIncorrect : CODES.passwordRequired, incorrect
|
|
260
|
+
? "The document was encrypted with a password this tool was not given"
|
|
261
|
+
: "The document is encrypted and requires a password to open", "This toolset takes no password. Decrypt the document with another tool first.");
|
|
262
|
+
}
|
|
263
|
+
if (error instanceof pdfjs.InvalidPDFException)
|
|
264
|
+
return build(CODES.unparseable, `The document could not be parsed even after recovery: ${message}`);
|
|
265
|
+
if (pdfjs.ResponseException && error instanceof pdfjs.ResponseException)
|
|
266
|
+
return build(CODES.failure, `The parser attempted a network request, which should be impossible: ${message}`, "Report this: it means a hardening option stopped taking effect.");
|
|
267
|
+
}
|
|
268
|
+
if (page !== undefined)
|
|
269
|
+
return build(CODES.pageUnreadable, `Page ${page}: ${message}`);
|
|
270
|
+
return build(CODES.failure, message);
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/pdf/document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OpenDocument } from "./document.js";
|
|
2
|
+
import type { PdfDiagnostic, PdfForm } from "./types.js";
|
|
3
|
+
export interface FormResult {
|
|
4
|
+
form: PdfForm;
|
|
5
|
+
diagnostics: PdfDiagnostic[];
|
|
6
|
+
}
|
|
7
|
+
export declare function readForm(handle: OpenDocument): Promise<FormResult>;
|
|
8
|
+
/** The human and llm rendering: one line per field. */
|
|
9
|
+
export declare function formatForm(form: PdfForm): string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { CODES, DiagnosticSink } from "./diagnostics.js";
|
|
2
|
+
/** Field values arrive as strings, names, or arrays; anything else is dropped. */
|
|
3
|
+
function text(value) {
|
|
4
|
+
if (typeof value === "string")
|
|
5
|
+
return value;
|
|
6
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
7
|
+
return String(value);
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
const parts = value.filter((item) => typeof item === "string");
|
|
10
|
+
return parts.length ? parts.join(", ") : undefined;
|
|
11
|
+
}
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
function fieldFrom(name, widgets, pageCount) {
|
|
15
|
+
// The widgets of one field agree on everything but position, so the first is
|
|
16
|
+
// representative; the count is what the rest contribute.
|
|
17
|
+
const first = widgets[0] ?? {};
|
|
18
|
+
// A field attached to no page reports -1, not undefined — so the range check
|
|
19
|
+
// below is what turns the sentinel into `page: null` instead of page 0.
|
|
20
|
+
const zeroBased = typeof first.page === "number" ? first.page : null;
|
|
21
|
+
const page = zeroBased === null || zeroBased < 0 || zeroBased >= pageCount ? null : zeroBased + 1;
|
|
22
|
+
return {
|
|
23
|
+
name,
|
|
24
|
+
type: typeof first.type === "string" && first.type ? first.type : "unknown",
|
|
25
|
+
page,
|
|
26
|
+
...(text(first.value) === undefined ? {} : { value: text(first.value) }),
|
|
27
|
+
...(text(first.defaultValue) === undefined ? {} : { defaultValue: text(first.defaultValue) }),
|
|
28
|
+
readOnly: first.editable === false,
|
|
29
|
+
hidden: first.hidden === true,
|
|
30
|
+
password: first.password === true,
|
|
31
|
+
...(typeof first.charLimit === "number" && first.charLimit > 0
|
|
32
|
+
? { charLimit: first.charLimit }
|
|
33
|
+
: {}),
|
|
34
|
+
...(text(first.exportValues) === undefined ? {} : { exportValues: text(first.exportValues) }),
|
|
35
|
+
widgets: widgets.length,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export async function readForm(handle) {
|
|
39
|
+
const sink = new DiagnosticSink();
|
|
40
|
+
// Checked before the field lookup, not after: an XFA-only document has no
|
|
41
|
+
// AcroForm field objects at all, so an empty list here would otherwise be
|
|
42
|
+
// indistinguishable from a document carrying no form.
|
|
43
|
+
if (handle.doc.isPureXfa) {
|
|
44
|
+
sink.add({
|
|
45
|
+
code: CODES.formXfa,
|
|
46
|
+
quality: "approximate",
|
|
47
|
+
message: "This is an XFA form; its field values live in an XML packet that is not read",
|
|
48
|
+
remediation: "Open it in a viewer that supports XFA, or use pdf text for whatever static content the document carries.",
|
|
49
|
+
});
|
|
50
|
+
return { form: { type: "xfa", fieldCount: 0, fields: [] }, diagnostics: sink.all() };
|
|
51
|
+
}
|
|
52
|
+
// Null covers both "no /AcroForm" and "an /AcroForm declaring no fields":
|
|
53
|
+
// pdf.js reports `hasFields: false` for each and there is nothing in the API
|
|
54
|
+
// that separates them, so `none` is the only claim this can honestly make.
|
|
55
|
+
const objects = await handle.within(() => handle.doc.getFieldObjects());
|
|
56
|
+
if (!objects || objects.size === 0)
|
|
57
|
+
return { form: { type: "none", fieldCount: 0, fields: [] }, diagnostics: sink.all() };
|
|
58
|
+
const fields = [];
|
|
59
|
+
for (const [name, widgets] of objects) {
|
|
60
|
+
const list = (Array.isArray(widgets) ? widgets : []);
|
|
61
|
+
if (list.length === 0)
|
|
62
|
+
continue;
|
|
63
|
+
const field = fieldFrom(name, list, handle.doc.numPages);
|
|
64
|
+
if (field.page === null)
|
|
65
|
+
sink.add({
|
|
66
|
+
code: CODES.formFieldPageUnresolved,
|
|
67
|
+
quality: "exact",
|
|
68
|
+
construct: name,
|
|
69
|
+
message: `Field '${name}' does not resolve to a page in this document`,
|
|
70
|
+
});
|
|
71
|
+
fields.push(field);
|
|
72
|
+
}
|
|
73
|
+
// Byte comparison, never localeCompare: a differently configured CI runner
|
|
74
|
+
// would otherwise reorder the payload.
|
|
75
|
+
fields.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
76
|
+
return {
|
|
77
|
+
form: { type: "acroform", fieldCount: fields.length, fields },
|
|
78
|
+
diagnostics: sink.all(),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** The human and llm rendering: one line per field. */
|
|
82
|
+
export function formatForm(form) {
|
|
83
|
+
if (form.type === "xfa")
|
|
84
|
+
return "xfa form: field values are not readable\n";
|
|
85
|
+
if (form.fieldCount === 0)
|
|
86
|
+
return "no form fields\n";
|
|
87
|
+
const width = Math.max(...form.fields.map((field) => field.name.length));
|
|
88
|
+
const lines = form.fields.map((field) => {
|
|
89
|
+
const flags = [
|
|
90
|
+
field.readOnly ? "read-only" : "",
|
|
91
|
+
field.hidden ? "hidden" : "",
|
|
92
|
+
field.password ? "password" : "",
|
|
93
|
+
].filter(Boolean);
|
|
94
|
+
const page = field.page === null ? " -" : String(field.page).padStart(3);
|
|
95
|
+
const value = field.value === undefined ? "" : ` = ${JSON.stringify(field.value)}`;
|
|
96
|
+
const suffix = flags.length ? ` [${flags.join(", ")}]` : "";
|
|
97
|
+
return `${page} ${field.name.padEnd(width)} ${field.type}${value}${suffix}`;
|
|
98
|
+
});
|
|
99
|
+
return `${lines.join("\n")}\n`;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=forms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forms.js","sourceRoot":"","sources":["../../src/pdf/forms.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { OpenDocument, PdfStructNode } from "./document.js";
|
|
2
|
+
import type { PdfDiagnostic, PdfDocumentInfo, PdfPageInfo, TextLayer } from "./types.js";
|
|
3
|
+
export declare function classifyTextLayer(characters: number, density: number): TextLayer;
|
|
4
|
+
/**
|
|
5
|
+
* Converts a PDF `D:YYYYMMDDHHmmSS` date to ISO 8601 in UTC.
|
|
6
|
+
*
|
|
7
|
+
* Omitted rather than guessed when it does not parse, and never rendered in the
|
|
8
|
+
* host timezone: that would make the payload machine-dependent, which is the
|
|
9
|
+
* same reasoning the ADF converter applies to its own timestamps.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parsePdfDate(value: unknown): string | undefined;
|
|
12
|
+
/** True when a structure tree carries at least one marked-content leaf. */
|
|
13
|
+
export declare function hasStructContent(node: PdfStructNode | null | undefined): boolean;
|
|
14
|
+
export interface SummaryResult {
|
|
15
|
+
document: PdfDocumentInfo;
|
|
16
|
+
diagnostics: PdfDiagnostic[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The document facts that cost nothing: no page is opened.
|
|
20
|
+
*
|
|
21
|
+
* Every command carries this, so a caller can read `document.tagged` off a
|
|
22
|
+
* `to-markdown` payload rather than making a second call. `structured` and
|
|
23
|
+
* `textLayer` are absent here because only a command that walks pages can
|
|
24
|
+
* measure them, and a field nobody measured must not read as a measurement.
|
|
25
|
+
*/
|
|
26
|
+
export declare function documentSummary(handle: OpenDocument): Promise<SummaryResult>;
|
|
27
|
+
export interface InspectResult {
|
|
28
|
+
document: PdfDocumentInfo;
|
|
29
|
+
pages: PdfPageInfo[];
|
|
30
|
+
diagnostics: PdfDiagnostic[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The page inventory, on top of {@link documentSummary}.
|
|
34
|
+
*
|
|
35
|
+
* `probeStructure` drives whether each page's structure tree is opened. It is on
|
|
36
|
+
* for `inspect`, because `structured` is one of the two fields the command
|
|
37
|
+
* exists to report, and off elsewhere.
|
|
38
|
+
*/
|
|
39
|
+
export declare function inspectDocument(handle: OpenDocument, options?: {
|
|
40
|
+
pages?: number[];
|
|
41
|
+
probeStructure?: boolean;
|
|
42
|
+
}): Promise<InspectResult>;
|