@cairn-tool/cairn 3.1.0 → 3.3.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 +74 -28
- 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/archive/run.d.ts +14 -7
- package/dist/archive/run.js +4 -1
- package/dist/archive/run.js.map +1 -1
- package/dist/archive/sets.d.ts +26 -1
- package/dist/archive/sets.js +108 -0
- package/dist/archive/sets.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 +48 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/archive.js +11 -5
- package/dist/commands/archive.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 +65 -1
- 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/dist/usage/providers/cursor.d.ts +2 -0
- package/dist/usage/providers/cursor.js +570 -0
- package/dist/usage/providers/cursor.js.map +1 -0
- package/dist/usage/providers/index.js +2 -0
- package/dist/usage/providers/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { CODES } from "./diagnostics.js";
|
|
2
|
+
import { round2 } from "./text.js";
|
|
3
|
+
const byPosition = (a, b) => a.page - b.page || a.y - b.y || a.x - b.x || a.index - b.index;
|
|
4
|
+
/**
|
|
5
|
+
* The value occurring most often.
|
|
6
|
+
*
|
|
7
|
+
* The tie-break direction is a real decision, not a detail. For a *size* the
|
|
8
|
+
* larger value wins, because a tie between body and caption should read as the
|
|
9
|
+
* body. For a *spacing* the smaller wins: line spacing within a paragraph is the
|
|
10
|
+
* tightest recurring gap, and heading-to-body gaps tie with it on any document
|
|
11
|
+
* short enough that body lines do not outnumber them — at which point picking
|
|
12
|
+
* the larger elects the heading gap as the body leading and no paragraph ever
|
|
13
|
+
* breaks.
|
|
14
|
+
*/
|
|
15
|
+
function mode(values, prefer = "largest") {
|
|
16
|
+
const counts = new Map();
|
|
17
|
+
for (const value of values)
|
|
18
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
19
|
+
let best = 0;
|
|
20
|
+
let bestCount = -1;
|
|
21
|
+
for (const [value, count] of counts) {
|
|
22
|
+
const wins = count > bestCount ||
|
|
23
|
+
(count === bestCount && (prefer === "largest" ? value > best : value < best));
|
|
24
|
+
if (wins) {
|
|
25
|
+
best = value;
|
|
26
|
+
bestCount = count;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return best;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Splits out runs no axis-aligned heuristic can place.
|
|
33
|
+
*
|
|
34
|
+
* Text at an arbitrary angle and vertical writing are excluded rather than
|
|
35
|
+
* folded in at the wrong position, and reported. Every stage after this may
|
|
36
|
+
* assume upright, left-to-right runs.
|
|
37
|
+
*/
|
|
38
|
+
export function normalize(runs, sink) {
|
|
39
|
+
const body = [];
|
|
40
|
+
let dropped = 0;
|
|
41
|
+
for (const run of runs) {
|
|
42
|
+
if (run.angle === null || run.angle !== 0 || run.vertical) {
|
|
43
|
+
dropped += 1;
|
|
44
|
+
sink.add({
|
|
45
|
+
code: CODES.rotatedTextDropped,
|
|
46
|
+
quality: "unsupported",
|
|
47
|
+
message: "Rotated or vertical text cannot be placed in reading order and was excluded",
|
|
48
|
+
page: run.page,
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
body.push(run);
|
|
53
|
+
}
|
|
54
|
+
return { body, dropped };
|
|
55
|
+
}
|
|
56
|
+
/* ------------------------------------------------------------------ *
|
|
57
|
+
* Stage 2 — running headers and footers
|
|
58
|
+
* ------------------------------------------------------------------ */
|
|
59
|
+
/** Digits masked, so `Page 3 of 40` and `Page 4 of 40` land in one bucket. */
|
|
60
|
+
function artifactKey(run) {
|
|
61
|
+
return `${Math.round(run.y / 2)}|${run.text.trim().replace(/\d+/g, "#")}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Removes text that repeats at the same height on most pages.
|
|
65
|
+
*
|
|
66
|
+
* Requires at least four pages: below that, repetition is not evidence. A bucket
|
|
67
|
+
* that repeats but is *not* near an edge is left alone — that is a section title
|
|
68
|
+
* or a watermark, and dropping it would delete content.
|
|
69
|
+
*/
|
|
70
|
+
export function removeArtifacts(runs, pageCount, pageHeight, sink) {
|
|
71
|
+
if (pageCount < 4)
|
|
72
|
+
return runs;
|
|
73
|
+
const pagesFor = new Map();
|
|
74
|
+
for (const run of runs) {
|
|
75
|
+
const key = artifactKey(run);
|
|
76
|
+
const seen = pagesFor.get(key);
|
|
77
|
+
if (seen)
|
|
78
|
+
seen.add(run.page);
|
|
79
|
+
else
|
|
80
|
+
pagesFor.set(key, new Set([run.page]));
|
|
81
|
+
}
|
|
82
|
+
const edge = pageHeight * 0.12;
|
|
83
|
+
const artifacts = new Set();
|
|
84
|
+
for (const [key, pages] of pagesFor) {
|
|
85
|
+
// 40%, not a majority. A bound book alternates its running heads between
|
|
86
|
+
// verso and recto — the title on one side, the chapter on the other — so
|
|
87
|
+
// each appears on about half the pages and a majority threshold catches
|
|
88
|
+
// neither. Content that genuinely repeats at a fixed height within the top
|
|
89
|
+
// or bottom eighth of two pages in five is a running head by any reading.
|
|
90
|
+
if (pages.size / pageCount < 0.4)
|
|
91
|
+
continue;
|
|
92
|
+
const y = Number(key.split("|")[0]) * 2;
|
|
93
|
+
if (y > edge && y < pageHeight - edge)
|
|
94
|
+
continue;
|
|
95
|
+
artifacts.add(key);
|
|
96
|
+
}
|
|
97
|
+
if (artifacts.size === 0)
|
|
98
|
+
return runs;
|
|
99
|
+
const kept = runs.filter((run) => !artifacts.has(artifactKey(run)));
|
|
100
|
+
sink.add({
|
|
101
|
+
code: CODES.artifactsRemoved,
|
|
102
|
+
quality: "exact",
|
|
103
|
+
message: `Removed ${runs.length - kept.length} running header or footer run(s) repeating across pages`,
|
|
104
|
+
});
|
|
105
|
+
return kept;
|
|
106
|
+
}
|
|
107
|
+
/* ------------------------------------------------------------------ *
|
|
108
|
+
* Stage 3 — columns
|
|
109
|
+
* ------------------------------------------------------------------ */
|
|
110
|
+
/**
|
|
111
|
+
* Splits a page's runs into columns, or reports that it could not.
|
|
112
|
+
*
|
|
113
|
+
* Reading a two-column page top to bottom interleaves the columns into text that
|
|
114
|
+
* reads like prose and is not — a degradation indistinguishable from success, so
|
|
115
|
+
* refusing to look is not the safe option. Looking, with a reported failure
|
|
116
|
+
* mode, is.
|
|
117
|
+
*
|
|
118
|
+
* A gutter is a vertical band at least 3% of the page wide containing no run and
|
|
119
|
+
* spanning at least 70% of the content's height. A candidate is rejected when
|
|
120
|
+
* the resulting sides differ by more than 6:1, which is a hanging indent or a
|
|
121
|
+
* figure rather than a gutter.
|
|
122
|
+
*/
|
|
123
|
+
export function splitColumns(runs, pageWidth, sink, page) {
|
|
124
|
+
if (runs.length < 8)
|
|
125
|
+
return [runs];
|
|
126
|
+
const minGutter = pageWidth * 0.03;
|
|
127
|
+
const left = Math.min(...runs.map((run) => run.x));
|
|
128
|
+
const right = Math.max(...runs.map((run) => run.x + run.width));
|
|
129
|
+
const top = Math.min(...runs.map((run) => run.y));
|
|
130
|
+
const bottom = Math.max(...runs.map((run) => run.y));
|
|
131
|
+
const span = bottom - top;
|
|
132
|
+
// Sample the content band; a column boundary is an x with no ink either side.
|
|
133
|
+
const step = Math.max(1, Math.floor((right - left) / 200));
|
|
134
|
+
const occupied = [];
|
|
135
|
+
for (let x = left; x <= right; x += step) {
|
|
136
|
+
const index = Math.floor((x - left) / step);
|
|
137
|
+
occupied[index] = runs.some((run) => run.x <= x && run.x + run.width >= x);
|
|
138
|
+
}
|
|
139
|
+
const gutters = [];
|
|
140
|
+
let runStart = -1;
|
|
141
|
+
for (let index = 0; index < occupied.length; index += 1) {
|
|
142
|
+
if (!occupied[index]) {
|
|
143
|
+
if (runStart === -1)
|
|
144
|
+
runStart = index;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (runStart !== -1) {
|
|
148
|
+
const width = (index - runStart) * step;
|
|
149
|
+
if (width >= minGutter && runStart > 0)
|
|
150
|
+
gutters.push(left + (runStart + (index - runStart) / 2) * step);
|
|
151
|
+
runStart = -1;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (gutters.length === 0 || gutters.length > 2) {
|
|
155
|
+
if (gutters.length > 2)
|
|
156
|
+
sink.add({
|
|
157
|
+
code: CODES.readingOrderUncertain,
|
|
158
|
+
quality: "approximate",
|
|
159
|
+
message: `Found ${gutters.length + 1} candidate columns; the page was read top to bottom instead`,
|
|
160
|
+
page,
|
|
161
|
+
});
|
|
162
|
+
return [runs];
|
|
163
|
+
}
|
|
164
|
+
const bounds = [left - 1, ...gutters, right + 1];
|
|
165
|
+
const columns = [];
|
|
166
|
+
for (let index = 0; index < bounds.length - 1; index += 1)
|
|
167
|
+
columns.push(runs.filter((run) => run.x > bounds[index] && run.x <= bounds[index + 1]));
|
|
168
|
+
const sizes = columns.map((column) => column.length).filter((count) => count > 0);
|
|
169
|
+
if (sizes.length < 2 || Math.max(...sizes) / Math.min(...sizes) > 6 || span <= 0)
|
|
170
|
+
return [runs];
|
|
171
|
+
sink.add({
|
|
172
|
+
code: CODES.columnsDetected,
|
|
173
|
+
quality: "exact",
|
|
174
|
+
message: `Read as ${columns.length} columns`,
|
|
175
|
+
page,
|
|
176
|
+
});
|
|
177
|
+
return columns.filter((column) => column.length > 0);
|
|
178
|
+
}
|
|
179
|
+
/* ------------------------------------------------------------------ *
|
|
180
|
+
* Stage 4 — lines
|
|
181
|
+
* ------------------------------------------------------------------ */
|
|
182
|
+
/** Groups runs into lines and records the wide gaps within each. */
|
|
183
|
+
export function buildLines(runs) {
|
|
184
|
+
if (runs.length === 0)
|
|
185
|
+
return [];
|
|
186
|
+
const ordered = [...runs].sort(byPosition);
|
|
187
|
+
const groups = [[ordered[0]]];
|
|
188
|
+
for (const run of ordered.slice(1)) {
|
|
189
|
+
const group = groups[groups.length - 1];
|
|
190
|
+
const previous = group[group.length - 1];
|
|
191
|
+
const tolerance = Math.max(previous.size, run.size) * 0.35;
|
|
192
|
+
if (run.page === previous.page && Math.abs(run.y - previous.y) <= tolerance)
|
|
193
|
+
group.push(run);
|
|
194
|
+
else
|
|
195
|
+
groups.push([run]);
|
|
196
|
+
}
|
|
197
|
+
return groups.map((group) => {
|
|
198
|
+
const sorted = [...group].sort((a, b) => a.x - b.x || a.index - b.index);
|
|
199
|
+
const gaps = [];
|
|
200
|
+
let text = sorted[0].text;
|
|
201
|
+
for (let index = 1; index < sorted.length; index += 1) {
|
|
202
|
+
const previous = sorted[index - 1];
|
|
203
|
+
const run = sorted[index];
|
|
204
|
+
const gap = run.x - (previous.x + previous.width);
|
|
205
|
+
if (gap > previous.size * 1.5)
|
|
206
|
+
gaps.push(round2(run.x));
|
|
207
|
+
const needsSpace = gap > previous.size * 0.25 && !/\s$/.test(text) && !/^\s/.test(run.text);
|
|
208
|
+
text += needsSpace ? ` ${run.text}` : run.text;
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
page: sorted[0].page,
|
|
212
|
+
y: sorted[0].y,
|
|
213
|
+
runs: sorted,
|
|
214
|
+
text: text.trim(),
|
|
215
|
+
x: round2(sorted[0].x),
|
|
216
|
+
size: mode(sorted.map((run) => run.size)),
|
|
217
|
+
bold: sorted.every((run) => run.bold),
|
|
218
|
+
gaps,
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/* ------------------------------------------------------------------ *
|
|
223
|
+
* Stages 5-9 — blocks
|
|
224
|
+
* ------------------------------------------------------------------ */
|
|
225
|
+
const BULLETS = /^[•‣▪◦·⁃∙*–—-]\s*$/;
|
|
226
|
+
const ORDINAL = /^\(?(\d{1,3}|[ivxlcdm]{1,7}|[A-Za-z])[.)]$/;
|
|
227
|
+
/**
|
|
228
|
+
* A line whose first run is a list marker.
|
|
229
|
+
*
|
|
230
|
+
* `rest` is the line's text with the marker consumed. Keeping it would render
|
|
231
|
+
* `- • The first finding`, because remark-stringify supplies the bullet and the
|
|
232
|
+
* source's own marker is then duplicated inside the item.
|
|
233
|
+
*/
|
|
234
|
+
function markerOf(line) {
|
|
235
|
+
const first = line.runs[0];
|
|
236
|
+
if (!first)
|
|
237
|
+
return null;
|
|
238
|
+
const token = first.text.trim();
|
|
239
|
+
const withoutFirstRun = () => line.runs
|
|
240
|
+
.slice(1)
|
|
241
|
+
.map((run) => run.text)
|
|
242
|
+
.join(" ")
|
|
243
|
+
.replace(/\s+/g, " ")
|
|
244
|
+
.trim();
|
|
245
|
+
if (line.runs.length >= 2) {
|
|
246
|
+
const next = line.runs[1];
|
|
247
|
+
if (BULLETS.test(token))
|
|
248
|
+
return { ordered: false, textX: next.x, rest: withoutFirstRun() };
|
|
249
|
+
if (ORDINAL.test(token))
|
|
250
|
+
return { ordered: true, textX: next.x, rest: withoutFirstRun() };
|
|
251
|
+
}
|
|
252
|
+
const inline = /^([•‣▪◦·*]|\(?\d{1,3}[.)])\s+/.exec(line.text);
|
|
253
|
+
if (inline)
|
|
254
|
+
return {
|
|
255
|
+
ordered: /\d/.test(inline[1]),
|
|
256
|
+
textX: line.x + first.size,
|
|
257
|
+
rest: line.text.slice(inline[0].length).trim(),
|
|
258
|
+
};
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
/** Tokens seen hyphenated mid-line, which must not be rejoined at a line end. */
|
|
262
|
+
function realHyphenations(lines) {
|
|
263
|
+
const seen = new Set();
|
|
264
|
+
for (const line of lines)
|
|
265
|
+
for (const match of line.text.matchAll(/(\w+-\w+)/g))
|
|
266
|
+
seen.add(match[1].toLowerCase());
|
|
267
|
+
return seen;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Turns one document's pages into blocks.
|
|
271
|
+
*
|
|
272
|
+
* Column splitting, line grouping, and paragraph breaking are per page; the
|
|
273
|
+
* modal body size and the heading size ranking are computed across the whole
|
|
274
|
+
* document, because a per-page ranking makes heading levels drift between pages
|
|
275
|
+
* of the same document.
|
|
276
|
+
*/
|
|
277
|
+
export function blocksFromLayout(pages, sink, options = {}) {
|
|
278
|
+
const allRuns = pages.flatMap((page) => page.runs);
|
|
279
|
+
if (allRuns.length === 0)
|
|
280
|
+
return [];
|
|
281
|
+
const { body } = normalize(allRuns, sink);
|
|
282
|
+
if (body.length === 0)
|
|
283
|
+
return [];
|
|
284
|
+
const pageHeight = mode(pages.map((page) => page.height));
|
|
285
|
+
const kept = removeArtifacts(body, pages.length, pageHeight, sink);
|
|
286
|
+
// Weighted by characters, not by run count: a document full of tiny footnote
|
|
287
|
+
// runs would otherwise elect the footnote size as the body size.
|
|
288
|
+
const weights = new Map();
|
|
289
|
+
for (const run of kept) {
|
|
290
|
+
const bucket = Math.round(run.size * 4) / 4;
|
|
291
|
+
weights.set(bucket, (weights.get(bucket) ?? 0) + run.text.length);
|
|
292
|
+
}
|
|
293
|
+
let bodySize = 0;
|
|
294
|
+
let bodyWeight = -1;
|
|
295
|
+
for (const [size, weight] of weights)
|
|
296
|
+
if (weight > bodyWeight || (weight === bodyWeight && size > bodySize)) {
|
|
297
|
+
bodySize = size;
|
|
298
|
+
bodyWeight = weight;
|
|
299
|
+
}
|
|
300
|
+
const lines = [];
|
|
301
|
+
for (const page of pages) {
|
|
302
|
+
const pageRuns = kept.filter((run) => run.page === page.page);
|
|
303
|
+
if (pageRuns.length === 0)
|
|
304
|
+
continue;
|
|
305
|
+
for (const column of splitColumns(pageRuns, page.width, sink, page.page))
|
|
306
|
+
lines.push(...buildLines(column));
|
|
307
|
+
}
|
|
308
|
+
if (lines.length === 0)
|
|
309
|
+
return [];
|
|
310
|
+
// Heading level by rank, never by ratio. A ratio produces H1/H3/H6 with no H2,
|
|
311
|
+
// which markdownlint flags — and a converted document has to lint clean where
|
|
312
|
+
// it lands.
|
|
313
|
+
const candidateSizes = [
|
|
314
|
+
...new Set(lines
|
|
315
|
+
.filter((line) => line.size >= bodySize * 1.15 || (line.bold && line.size >= bodySize))
|
|
316
|
+
.map((line) => line.size)),
|
|
317
|
+
].sort((a, b) => b - a);
|
|
318
|
+
const levelFor = new Map();
|
|
319
|
+
candidateSizes.forEach((size, index) => levelFor.set(size, Math.min(index + 1, 6)));
|
|
320
|
+
if (candidateSizes.length > 6)
|
|
321
|
+
sink.add({
|
|
322
|
+
code: CODES.headingLevelsCollapsed,
|
|
323
|
+
quality: "approximate",
|
|
324
|
+
message: `${candidateSizes.length} distinct heading sizes; the smallest were collapsed to level 6`,
|
|
325
|
+
});
|
|
326
|
+
const hyphenated = realHyphenations(lines);
|
|
327
|
+
// `bodySize` is bucketed to a quarter point and a line's size is a mode of
|
|
328
|
+
// 2-dp run sizes, so `line.size === bodySize` is almost never true on a real
|
|
329
|
+
// document — a 10.91pt body never equals its own 11.0 bucket. Comparing with
|
|
330
|
+
// the bucket width instead is what keeps the two statistics below from being
|
|
331
|
+
// computed over an empty set, which is silent and makes every paragraph split
|
|
332
|
+
// after one line.
|
|
333
|
+
const isBody = (line) => Math.abs(line.size - bodySize) <= 0.25;
|
|
334
|
+
// Measured only between consecutive *body-size* lines. A heading-to-body gap is
|
|
335
|
+
// not line spacing, and on any document with more headings than paragraphs it
|
|
336
|
+
// outnumbers the real leading and elects itself — after which no paragraph ever
|
|
337
|
+
// breaks, because every real gap is smaller than the "modal" one.
|
|
338
|
+
const spacings = [];
|
|
339
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
340
|
+
const previous = lines[index - 1];
|
|
341
|
+
const line = lines[index];
|
|
342
|
+
if (line.page !== previous.page)
|
|
343
|
+
continue;
|
|
344
|
+
if (!isBody(line) || !isBody(previous))
|
|
345
|
+
continue;
|
|
346
|
+
const delta = line.y - previous.y;
|
|
347
|
+
if (delta > 0)
|
|
348
|
+
spacings.push(Math.round(delta * 2) / 2);
|
|
349
|
+
}
|
|
350
|
+
const modalSpacing = mode(spacings, "smallest") || bodySize * 1.2;
|
|
351
|
+
// The body's left margin, taken over body-size lines. A first-line indent is
|
|
352
|
+
// measured *against this*, not against the previous line: in a typeset
|
|
353
|
+
// document the first line of a paragraph is the indented one, so comparing
|
|
354
|
+
// neighbours makes every continuation line — which sits back at the margin —
|
|
355
|
+
// look like a fresh paragraph, and every paragraph splits after one line.
|
|
356
|
+
const modalLeft = mode(lines.filter(isBody).map((line) => Math.round(line.x)), "smallest");
|
|
357
|
+
const blocks = [];
|
|
358
|
+
let rejoined = 0;
|
|
359
|
+
let paragraph = [];
|
|
360
|
+
let listItems = [];
|
|
361
|
+
let listOrdered = false;
|
|
362
|
+
const provenance = (quality, pageNumbers) => ({
|
|
363
|
+
path: "geometric",
|
|
364
|
+
quality,
|
|
365
|
+
pages: [...new Set(pageNumbers)].sort((a, b) => a - b),
|
|
366
|
+
});
|
|
367
|
+
const spansFrom = (group) => {
|
|
368
|
+
let text = "";
|
|
369
|
+
for (const line of group) {
|
|
370
|
+
if (!text) {
|
|
371
|
+
text = line.text;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const match = /(\w+)-$/.exec(text);
|
|
375
|
+
if (match && /^[a-z]/.test(line.text) && !hyphenated.has(`${match[1].toLowerCase()}-`)) {
|
|
376
|
+
text = `${text.slice(0, -1)}${line.text}`;
|
|
377
|
+
rejoined += 1;
|
|
378
|
+
}
|
|
379
|
+
else
|
|
380
|
+
text += ` ${line.text}`;
|
|
381
|
+
}
|
|
382
|
+
const bold = group.every((line) => line.bold);
|
|
383
|
+
return text ? [{ text, bold, italic: false, code: false }] : [];
|
|
384
|
+
};
|
|
385
|
+
const flushParagraph = () => {
|
|
386
|
+
if (paragraph.length === 0)
|
|
387
|
+
return;
|
|
388
|
+
const spans = spansFrom(paragraph);
|
|
389
|
+
if (spans.length)
|
|
390
|
+
blocks.push({
|
|
391
|
+
kind: "paragraph",
|
|
392
|
+
spans,
|
|
393
|
+
children: [],
|
|
394
|
+
provenance: provenance("approximate", paragraph.map((line) => line.page)),
|
|
395
|
+
});
|
|
396
|
+
paragraph = [];
|
|
397
|
+
};
|
|
398
|
+
const flushList = () => {
|
|
399
|
+
if (listItems.length === 0)
|
|
400
|
+
return;
|
|
401
|
+
blocks.push({
|
|
402
|
+
kind: "list",
|
|
403
|
+
ordered: listOrdered,
|
|
404
|
+
spans: [],
|
|
405
|
+
children: listItems.map((item) => ({
|
|
406
|
+
kind: "listItem",
|
|
407
|
+
spans: spansFrom(item.lines),
|
|
408
|
+
children: [],
|
|
409
|
+
provenance: provenance("approximate", item.lines.map((line) => line.page)),
|
|
410
|
+
})),
|
|
411
|
+
provenance: provenance("approximate", listItems.flatMap((item) => item.lines.map((l) => l.page))),
|
|
412
|
+
});
|
|
413
|
+
if (listOrdered)
|
|
414
|
+
sink.add({
|
|
415
|
+
code: CODES.listNumberingLost,
|
|
416
|
+
quality: "exact",
|
|
417
|
+
message: "An ordered list's original numbering is renumbered on output",
|
|
418
|
+
});
|
|
419
|
+
listItems = [];
|
|
420
|
+
};
|
|
421
|
+
const flushAll = () => {
|
|
422
|
+
flushParagraph();
|
|
423
|
+
flushList();
|
|
424
|
+
};
|
|
425
|
+
// Tabular runs are detected, flattened, and reported — never reconstructed.
|
|
426
|
+
// A geometric table gets merged cells, wrapped cell text, and rules drawn as
|
|
427
|
+
// vector paths wrong, and produces a confidently wrong table the consumer
|
|
428
|
+
// cannot tell from a right one. Real tables come from the structure tree.
|
|
429
|
+
const tabular = new Set();
|
|
430
|
+
for (let index = 0; index + 2 < lines.length; index += 1) {
|
|
431
|
+
const window = [lines[index], lines[index + 1], lines[index + 2]];
|
|
432
|
+
if (!window.every((line) => line.gaps.length >= 2))
|
|
433
|
+
continue;
|
|
434
|
+
const first = window[0].gaps;
|
|
435
|
+
const consistent = window.every((line) => line.gaps.every((gap) => first.some((reference) => Math.abs(gap - reference) <= 2)));
|
|
436
|
+
if (consistent)
|
|
437
|
+
for (let offset = 0; offset < 3; offset += 1)
|
|
438
|
+
tabular.add(index + offset);
|
|
439
|
+
}
|
|
440
|
+
if (tabular.size > 0)
|
|
441
|
+
sink.add({
|
|
442
|
+
code: CODES.tableFlattened,
|
|
443
|
+
quality: "approximate",
|
|
444
|
+
message: `Tabular content on ${tabular.size} line(s) was emitted as one paragraph per row`,
|
|
445
|
+
remediation: "A geometric table cannot be reconstructed faithfully; only a tagged PDF's tables are.",
|
|
446
|
+
});
|
|
447
|
+
lines.forEach((line, index) => {
|
|
448
|
+
const previous = index > 0 ? lines[index - 1] : null;
|
|
449
|
+
const level = levelFor.get(line.size);
|
|
450
|
+
const marker = markerOf(line);
|
|
451
|
+
const isHeading = level !== undefined &&
|
|
452
|
+
line.runs.length > 0 &&
|
|
453
|
+
!marker &&
|
|
454
|
+
line.text.length > 0 &&
|
|
455
|
+
line.size > bodySize;
|
|
456
|
+
if (isHeading) {
|
|
457
|
+
flushAll();
|
|
458
|
+
const pinned = options.headingLevels?.get(line.text);
|
|
459
|
+
blocks.push({
|
|
460
|
+
kind: "heading",
|
|
461
|
+
level: Math.min(pinned ?? level, 6),
|
|
462
|
+
// Not bold: the heading level already carries the emphasis, and wrapping
|
|
463
|
+
// the text in `**` as well renders `# **Title**`.
|
|
464
|
+
spans: [{ text: line.text, bold: false, italic: false, code: false }],
|
|
465
|
+
children: [],
|
|
466
|
+
provenance: provenance(pinned ? "exact" : "approximate", [line.page]),
|
|
467
|
+
});
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (marker) {
|
|
471
|
+
flushParagraph();
|
|
472
|
+
if (listItems.length === 0)
|
|
473
|
+
listOrdered = marker.ordered;
|
|
474
|
+
listItems.push({ lines: [{ ...line, text: marker.rest }], ordered: marker.ordered });
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (listItems.length > 0) {
|
|
478
|
+
const last = listItems[listItems.length - 1];
|
|
479
|
+
const itemX = last.lines[0].runs[1]?.x ?? last.lines[0].x;
|
|
480
|
+
if (previous && line.page === previous.page && Math.abs(line.x - itemX) <= 1.5) {
|
|
481
|
+
last.lines.push(line);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
flushList();
|
|
485
|
+
}
|
|
486
|
+
if (previous) {
|
|
487
|
+
const delta = line.page === previous.page ? line.y - previous.y : 0;
|
|
488
|
+
// Indented *past* the margin, which is what a paragraph's first line does.
|
|
489
|
+
// A line returning to the margin is a continuation and must not break.
|
|
490
|
+
const indented = paragraph.length > 0 && line.x > modalLeft + Math.max(2, bodySize * 0.4);
|
|
491
|
+
// A tolerance, not equality: a line carrying an inline code span or a
|
|
492
|
+
// symbol run has a slightly different modal size and is not a new block.
|
|
493
|
+
const fontChanged = Math.abs(line.size - previous.size) > 0.5;
|
|
494
|
+
const shortAndFinal = /[.!?]["')\]]?$/.test(previous.text) && previous.text.length < line.text.length * 0.85;
|
|
495
|
+
// All four are needed: leading alone misses indent-led paragraphs, indent
|
|
496
|
+
// alone splits hanging indents, and terminal punctuation alone splits at
|
|
497
|
+
// every abbreviation.
|
|
498
|
+
if (delta > modalSpacing * 1.35 || indented || fontChanged || (delta > 0 && shortAndFinal))
|
|
499
|
+
flushParagraph();
|
|
500
|
+
else if (line.page !== previous.page && paragraph.length > 0) {
|
|
501
|
+
const ends = /[.!?]["')\]]?$/.test(previous.text);
|
|
502
|
+
if (ends)
|
|
503
|
+
flushParagraph();
|
|
504
|
+
else
|
|
505
|
+
sink.add({
|
|
506
|
+
code: CODES.paragraphSpansPages,
|
|
507
|
+
quality: "exact",
|
|
508
|
+
message: `A paragraph continuing across the page ${previous.page} boundary was rejoined`,
|
|
509
|
+
page: line.page,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
paragraph.push(line);
|
|
514
|
+
});
|
|
515
|
+
flushAll();
|
|
516
|
+
if (rejoined > 0)
|
|
517
|
+
sink.add({
|
|
518
|
+
code: CODES.hyphenationRejoined,
|
|
519
|
+
quality: "exact",
|
|
520
|
+
message: `Rejoined ${rejoined} word(s) split by a line-end hyphen`,
|
|
521
|
+
});
|
|
522
|
+
return blocks;
|
|
523
|
+
}
|
|
524
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/pdf/layout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { OpenDocument } from "./document.js";
|
|
2
|
+
import type { PdfDiagnostic, PdfOutlineEntry } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The outline is attacker-shaped structure and the walk is recursive, so it is
|
|
5
|
+
* bounded for the same reason the ADF reader bounds nesting depth: a stack
|
|
6
|
+
* overflow with no diagnostic is the wrong failure for input off a network.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_OUTLINE_DEPTH = 64;
|
|
9
|
+
export interface OutlineResult {
|
|
10
|
+
outline: PdfOutlineEntry[];
|
|
11
|
+
diagnostics: PdfDiagnostic[];
|
|
12
|
+
/** Normalized title to level, for `layout.ts` to pin heading levels against. */
|
|
13
|
+
headingLevels: Map<string, number>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reads the declared outline and resolves each destination to a page.
|
|
17
|
+
*
|
|
18
|
+
* The outline a document declares, never one inferred from its text: a document
|
|
19
|
+
* with no `/Outlines` returns an empty tree and exits 0, which is an answer
|
|
20
|
+
* rather than a failure.
|
|
21
|
+
*
|
|
22
|
+
* URLs are recorded, never followed — no fetch, no HEAD, no DNS. `unsafeUrl` is
|
|
23
|
+
* deliberately not surfaced as `url`: pdf.js populates `url` only when the
|
|
24
|
+
* scheme passed its own validation, and putting a rejected `javascript:` or
|
|
25
|
+
* `file:` URI into a field named as though it were clickable would be a
|
|
26
|
+
* misrepresentation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readOutline(handle: OpenDocument): Promise<OutlineResult>;
|
|
29
|
+
/** Renders the tree for the human-facing streams, two spaces per level. */
|
|
30
|
+
export declare function formatOutline(entries: PdfOutlineEntry[]): string;
|