reamkit 1.29.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/core/document-model/types.d.ts +2 -0
- package/dist/esm/core/drawingml/shape-render.js +13 -1
- package/dist/esm/core/fonts/index.d.ts +1 -1
- package/dist/esm/core/fonts/remote-fonts.d.ts +8 -0
- package/dist/esm/core/fonts/remote-fonts.js +100 -17
- package/dist/esm/core/ir/flow.d.ts +17 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/pdf-reader/annot-draw.js +93 -1
- package/dist/esm/pdf-reader/annots.d.ts +18 -0
- package/dist/esm/pdf-reader/annots.js +86 -9
- package/dist/esm/pdf-reader/cmap.js +5 -2
- package/dist/esm/pdf-reader/content.d.ts +17 -4
- package/dist/esm/pdf-reader/content.js +163 -11
- package/dist/esm/pdf-reader/display.js +16 -0
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +24 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +48 -9
- package/dist/esm/pdf-reader/flow-build.d.ts +96 -5
- package/dist/esm/pdf-reader/flow-build.js +210 -23
- package/dist/esm/pdf-reader/font.d.ts +27 -1
- package/dist/esm/pdf-reader/font.js +313 -29
- package/dist/esm/pdf-reader/glyf-outline.js +13 -2
- package/dist/esm/pdf-reader/glyph-shapes.d.ts +18 -0
- package/dist/esm/pdf-reader/glyph-shapes.js +57 -0
- package/dist/esm/pdf-reader/image-decode.js +70 -4
- package/dist/esm/pdf-reader/images.d.ts +5 -0
- package/dist/esm/pdf-reader/images.js +4 -2
- package/dist/esm/pdf-reader/jbig2.d.ts +40 -1
- package/dist/esm/pdf-reader/jbig2.js +78 -16
- package/dist/esm/pdf-reader/jpeg.d.ts +6 -3
- package/dist/esm/pdf-reader/jpeg.js +21 -1
- package/dist/esm/pdf-reader/layout.d.ts +26 -2
- package/dist/esm/pdf-reader/layout.js +638 -92
- package/dist/esm/pdf-reader/lexer.d.ts +10 -0
- package/dist/esm/pdf-reader/lexer.js +17 -0
- package/dist/esm/pdf-reader/pattern-tint.d.ts +11 -1
- package/dist/esm/pdf-reader/pattern-tint.js +21 -3
- package/dist/esm/pdf-reader/regions.d.ts +25 -0
- package/dist/esm/pdf-reader/regions.js +167 -0
- package/dist/esm/pdf-reader/shading.d.ts +58 -2
- package/dist/esm/pdf-reader/shading.js +181 -11
- package/dist/esm/pdf-reader/struct-tree.js +112 -8
- package/dist/esm/pdf-reader/tagged.js +27 -7
- package/dist/esm/pdf-reader/text-rules.js +1 -1
- package/dist/esm/pdf-reader/text.js +9 -14
- package/dist/esm/pdf-reader/vector.d.ts +8 -2
- package/dist/esm/pdf-reader/vector.js +34 -5
- package/dist/esm/word/docx-writer.js +137 -36
- package/dist/esm/word/drawing-parser.js +7 -2
- package/dist/esm/word/paragraph-properties.js +2 -0
- package/package.json +5 -3
|
@@ -2,6 +2,7 @@ import { pt } from "../core/ir/units.js";
|
|
|
2
2
|
import { ResourceStore } from "../core/ir/resources.js";
|
|
3
3
|
import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
|
|
4
4
|
import "../core/style-cascade/index.js";
|
|
5
|
+
import { gradientOverBox } from "./shading.js";
|
|
5
6
|
import { displayOf } from "./display.js";
|
|
6
7
|
//#region src/pdf-reader/flow-build.ts
|
|
7
8
|
/**
|
|
@@ -21,6 +22,27 @@ function paragraphBlock(text, outlineLevel) {
|
|
|
21
22
|
}
|
|
22
23
|
};
|
|
23
24
|
}
|
|
25
|
+
/** One piece of reconstructed text, carrying any hyperlink (E-PDF EP8). */
|
|
26
|
+
/**
|
|
27
|
+
* The space set between a span and what follows it, in the span's own size and
|
|
28
|
+
* face: a word space is as wide as the type it stands in.
|
|
29
|
+
*
|
|
30
|
+
* Written bare, the space took the document's default size, whatever the words
|
|
31
|
+
* around it were set at: issue10665_reduced.pdf sets "78" and "110" twenty
|
|
32
|
+
* points apart in 60-point type, and the eleven-point space between them closed
|
|
33
|
+
* them up to "78110"; in 7-point footnotes the same space is half as wide
|
|
34
|
+
* again as the page's, and pushes their lines over.
|
|
35
|
+
*
|
|
36
|
+
* @param before The span the space follows, where there is one.
|
|
37
|
+
* @returns The space.
|
|
38
|
+
*/
|
|
39
|
+
function spaceAfter(before) {
|
|
40
|
+
return {
|
|
41
|
+
text: " ",
|
|
42
|
+
...before?.sizePt !== void 0 ? { sizePt: before.sizePt } : {},
|
|
43
|
+
...before?.fontName !== void 0 ? { fontName: before.fontName } : {}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
24
46
|
/**
|
|
25
47
|
* Build a paragraph {@link BodyElement} from positioned {@link TextSpan}s,
|
|
26
48
|
* coalescing consecutive spans that share an `href` into one run (so a link
|
|
@@ -65,7 +87,10 @@ function paragraphFromRuns(spans, outlineLevel, placement) {
|
|
|
65
87
|
properties: {
|
|
66
88
|
...r.sizePt !== void 0 ? { fontSizePt: pt(r.sizePt) } : {},
|
|
67
89
|
...r.colorHex !== void 0 ? { colorHex: r.colorHex } : {},
|
|
68
|
-
...r.fontName !== void 0 ? { fontFamily: {
|
|
90
|
+
...r.fontName !== void 0 ? { fontFamily: {
|
|
91
|
+
ascii: r.fontName,
|
|
92
|
+
hAnsi: r.fontName
|
|
93
|
+
} } : {},
|
|
69
94
|
...r.outline !== void 0 ? { textOutline: r.outline } : {},
|
|
70
95
|
...r.bold ? { bold: true } : {},
|
|
71
96
|
...r.italic ? { italic: true } : {},
|
|
@@ -85,6 +110,23 @@ function sameMarkup(a, b) {
|
|
|
85
110
|
return a?.highlightHex === b?.highlightHex && a?.underline === b?.underline && a?.underlineHex === b?.underlineHex && a?.strike === b?.strike;
|
|
86
111
|
}
|
|
87
112
|
/**
|
|
113
|
+
* The height of a line that carries nothing to read: one twip, the least
|
|
114
|
+
* §17.3.1.33 can state. Zero is not a height a writer states at all — it is
|
|
115
|
+
* read as "no line spacing given", and the reader's single spacing comes back.
|
|
116
|
+
*/
|
|
117
|
+
var CARRIER_LINE_PT = pt(.05);
|
|
118
|
+
/**
|
|
119
|
+
* §17.3.1.33 — the paragraph a floating mark is anchored in, which takes no
|
|
120
|
+
* room: the mark stands where the page drew it and the line carrying it is on
|
|
121
|
+
* no page. Left at a reader's single spacing, every rule and fill an invoice
|
|
122
|
+
* draws was a blank line in the flow, and the text under it moved down a line
|
|
123
|
+
* for each.
|
|
124
|
+
*/
|
|
125
|
+
var FLOAT_CARRIER = {
|
|
126
|
+
spacingLine: CARRIER_LINE_PT,
|
|
127
|
+
spacingLineRule: "exact"
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
88
130
|
* Store a {@link PdfImage}'s bytes (content-addressed dedup) and build the image
|
|
89
131
|
* {@link BodyElement} that references them, sized in points from the placement
|
|
90
132
|
* CTM. `alt` becomes the block's alt text when given.
|
|
@@ -112,9 +154,10 @@ function imageBlock(image, resources, alt, frame, zOrder, behind = false) {
|
|
|
112
154
|
width: pt(image.widthPt),
|
|
113
155
|
height: pt(image.heightPt),
|
|
114
156
|
...image.rotationDeg !== void 0 ? { rotation60k: Math.round(-image.rotationDeg * 6e4) } : {},
|
|
157
|
+
...image.flipV === true ? { flipV: true } : {},
|
|
115
158
|
...image.crop ? { crop: image.crop } : {},
|
|
116
159
|
...image.alpha !== void 0 ? { alpha: image.alpha } : {},
|
|
117
|
-
paragraphProperties: {},
|
|
160
|
+
paragraphProperties: float ? FLOAT_CARRIER : {},
|
|
118
161
|
...alt ? { altText: alt } : {}
|
|
119
162
|
}
|
|
120
163
|
};
|
|
@@ -138,7 +181,10 @@ function imageBlock(image, resources, alt, frame, zOrder, behind = false) {
|
|
|
138
181
|
* @returns A shape carrying the text, anchored where the glyphs were.
|
|
139
182
|
*/
|
|
140
183
|
function positionedText(spans, box, frame, zOrder, rotation60k) {
|
|
141
|
-
const paragraph = paragraphFromRuns(spans
|
|
184
|
+
const paragraph = paragraphFromRuns(spans, void 0, {
|
|
185
|
+
spacingLine: pt(Math.max(1, box.height)),
|
|
186
|
+
spacingLineRule: "exact"
|
|
187
|
+
});
|
|
142
188
|
return {
|
|
143
189
|
kind: "shape",
|
|
144
190
|
shape: {
|
|
@@ -169,7 +215,7 @@ function positionedText(spans, box, frame, zOrder, rotation60k) {
|
|
|
169
215
|
insetRight: pt(0),
|
|
170
216
|
insetBottom: pt(0)
|
|
171
217
|
},
|
|
172
|
-
paragraphProperties:
|
|
218
|
+
paragraphProperties: FLOAT_CARRIER
|
|
173
219
|
}
|
|
174
220
|
};
|
|
175
221
|
}
|
|
@@ -226,17 +272,21 @@ function shapeBlock(v, frame, zOrder, behind = false) {
|
|
|
226
272
|
const alpha = v.alpha !== void 0 ? { alpha: v.alpha } : {};
|
|
227
273
|
const fill = v.gradient !== void 0 ? {
|
|
228
274
|
kind: "gradient",
|
|
229
|
-
gradient: v.gradient,
|
|
275
|
+
gradient: gradientOverBox(v.gradient, v),
|
|
230
276
|
...alpha
|
|
231
277
|
} : v.fillHex !== void 0 ? {
|
|
232
278
|
kind: "solid",
|
|
233
279
|
colorHex: v.fillHex,
|
|
234
280
|
...alpha
|
|
235
281
|
} : { kind: "none" };
|
|
282
|
+
const dash = v.strokeHex !== void 0 && v.dash !== void 0 ? penDash(v.dash, pen) : [];
|
|
236
283
|
const line = v.strokeHex !== void 0 ? {
|
|
237
284
|
width: pt(pen),
|
|
238
285
|
colorHex: v.strokeHex,
|
|
239
|
-
fill: "solid"
|
|
286
|
+
fill: "solid",
|
|
287
|
+
...dash.length > 0 ? { customDash: dash } : {},
|
|
288
|
+
cap: v.cap ?? "flat",
|
|
289
|
+
...v.strokeAlpha !== void 0 ? { alpha: v.strokeAlpha } : {}
|
|
240
290
|
} : void 0;
|
|
241
291
|
const float = frame !== void 0 ? {
|
|
242
292
|
wrap: "none",
|
|
@@ -267,7 +317,7 @@ function shapeBlock(v, frame, zOrder, behind = false) {
|
|
|
267
317
|
},
|
|
268
318
|
fill,
|
|
269
319
|
...line ? { line } : {},
|
|
270
|
-
paragraphProperties: {}
|
|
320
|
+
paragraphProperties: float ? FLOAT_CARRIER : {}
|
|
271
321
|
}
|
|
272
322
|
};
|
|
273
323
|
}
|
|
@@ -306,6 +356,131 @@ function sectionFromPdfPages(pages) {
|
|
|
306
356
|
};
|
|
307
357
|
}
|
|
308
358
|
/**
|
|
359
|
+
* §8.4.3.6 — a page's dash pattern as DrawingML states one (§20.1.8.21
|
|
360
|
+
* `a:custDash`): dash and gap in turn, as multiples of the pen's width.
|
|
361
|
+
*
|
|
362
|
+
* An odd count of lengths runs twice before it repeats, so the dashes and the
|
|
363
|
+
* gaps trade places the second time round; written out once more, the pairs
|
|
364
|
+
* come out even. A dash of no length is a DOT, drawn by the pen's cap alone —
|
|
365
|
+
* which no .docx line carries — so it is set a pen's width long and the gap
|
|
366
|
+
* after it gives that back, keeping the pattern's period.
|
|
367
|
+
*
|
|
368
|
+
* @param dash The lengths in page-space points.
|
|
369
|
+
* @param pen The pen's width in points.
|
|
370
|
+
* @returns The pattern in pen widths, or an empty array for a solid line.
|
|
371
|
+
*/
|
|
372
|
+
function penDash(dash, pen) {
|
|
373
|
+
if (dash.length === 0 || !(pen > 0)) return [];
|
|
374
|
+
const out = (dash.length % 2 === 1 ? [...dash, ...dash] : [...dash]).map((n) => n / pen);
|
|
375
|
+
for (let i = 0; i < out.length; i += 2) {
|
|
376
|
+
if (out[i] > 0) continue;
|
|
377
|
+
out[i] = 1;
|
|
378
|
+
out[i + 1] = Math.max(out[i + 1] - 1, MIN_DASH_GAP);
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
382
|
+
/** The least gap a dash pattern keeps, in pen widths, so a dotted line stays dotted. */
|
|
383
|
+
var MIN_DASH_GAP = .5;
|
|
384
|
+
/** What the measure gives back, so the widest line still fits when re-set. */
|
|
385
|
+
var SLACK = .01;
|
|
386
|
+
/** How much of the sheet a margin down the page may take. */
|
|
387
|
+
var DEEPEST_MARGIN = .5;
|
|
388
|
+
/**
|
|
389
|
+
* How far in, as a share of the sheet, a right margin the lines do not show
|
|
390
|
+
* may come: what a sheet of a line or two is re-set across is at least the
|
|
391
|
+
* rest of it.
|
|
392
|
+
*/
|
|
393
|
+
var GUESSED_MARGIN = 1 / 3;
|
|
394
|
+
/** The narrowest measure a sheet's own lines may leave, as a share of the sheet. */
|
|
395
|
+
var NARROWEST_MEASURE = .2;
|
|
396
|
+
/**
|
|
397
|
+
* The lines a sheet's runs stand on — baselines further apart than half the
|
|
398
|
+
* size of the type on them — and how many of them END at the sheet's right
|
|
399
|
+
* edge, within an em of the farthest: the lines a measure broke, or a margin
|
|
400
|
+
* justified, rather than the ones their author stopped.
|
|
401
|
+
*/
|
|
402
|
+
function linesOf(runs) {
|
|
403
|
+
const ink = runs.filter((r) => r.text.trim() !== "").map((r) => ({
|
|
404
|
+
y: r.y,
|
|
405
|
+
end: r.endX,
|
|
406
|
+
size: r.fontSizePt || 10
|
|
407
|
+
})).sort((a, b) => b.y - a.y);
|
|
408
|
+
const lines = [];
|
|
409
|
+
for (const at of ink) {
|
|
410
|
+
const last = lines[lines.length - 1];
|
|
411
|
+
if (last !== void 0 && last.y - at.y <= Math.max(last.size, at.size) / 2) {
|
|
412
|
+
last.end = Math.max(last.end, at.end);
|
|
413
|
+
last.size = Math.max(last.size, at.size);
|
|
414
|
+
} else lines.push({ ...at });
|
|
415
|
+
}
|
|
416
|
+
const edge = Math.max(...lines.map((l) => l.end));
|
|
417
|
+
return {
|
|
418
|
+
count: lines.length,
|
|
419
|
+
atEdge: lines.filter((l) => edge - l.end <= l.size).length
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* The white, in ems of the text, a running foot keeps above it — which is
|
|
424
|
+
* what the reader asked of it before it took the line for a foot at all.
|
|
425
|
+
*/
|
|
426
|
+
var FOOT_CLEAR_EM = 2;
|
|
427
|
+
/**
|
|
428
|
+
* §17.3.1.33 — where the baseline of an EXACT line stands in its box, from the
|
|
429
|
+
* top. LibreOffice puts it at four fifths of the height; Word at the height
|
|
430
|
+
* less the face's descent, which for the faces documents use is within a tenth
|
|
431
|
+
* of a line of the same place.
|
|
432
|
+
*/
|
|
433
|
+
var BASELINE_AT = .8;
|
|
434
|
+
/** A line's box where no pitch was measured: the ordinary single spacing, in ems. */
|
|
435
|
+
var NATURAL_LINE_EM = 1.2;
|
|
436
|
+
/**
|
|
437
|
+
* How far above its baseline the first line's BOX reaches, as a fraction of the
|
|
438
|
+
* size — which is what the top margin has to leave room for. The lines are set
|
|
439
|
+
* in exact boxes (see `groupIntoParagraphs`), so this is where the box's top
|
|
440
|
+
* stands, not where a face's ascender happens to.
|
|
441
|
+
*/
|
|
442
|
+
var ASCENDER = BASELINE_AT * NATURAL_LINE_EM;
|
|
443
|
+
/**
|
|
444
|
+
* And how far below its baseline the last line's box reaches. A box set half
|
|
445
|
+
* as deep again as its size reaches three tenths of it down; measured to a
|
|
446
|
+
* face's descender instead, bug1337429.pdf's last line did not fit the sheet
|
|
447
|
+
* it was drawn on and went to a second one.
|
|
448
|
+
*/
|
|
449
|
+
var DESCENDER = (1 - BASELINE_AT) * 1.5;
|
|
450
|
+
/**
|
|
451
|
+
* How small, against the document's own text, type may be set and still be
|
|
452
|
+
* read as text rather than as a mark the producer left on the sheet.
|
|
453
|
+
*/
|
|
454
|
+
var LEGIBLE_SHARE = .25;
|
|
455
|
+
/**
|
|
456
|
+
* The size a document's text is set in: the middle of every size its runs
|
|
457
|
+
* carry.
|
|
458
|
+
*
|
|
459
|
+
* @param pageRuns Each page's runs.
|
|
460
|
+
* @returns The size, in points; 0 where no run states one.
|
|
461
|
+
*/
|
|
462
|
+
function textSizeOf(pageRuns) {
|
|
463
|
+
const sizes = pageRuns.flat().map((r) => r.fontSizePt).filter((s) => s > 0).sort((a, b) => a - b);
|
|
464
|
+
return sizes[Math.floor(sizes.length / 2)] ?? 0;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Whether a run is set too small to be read, beside the text of its document —
|
|
468
|
+
* a mark the producer leaves on the sheet, not a line of the page.
|
|
469
|
+
*
|
|
470
|
+
* TCPDF signs the last page of everything it makes "Powered by TCPDF
|
|
471
|
+
* (www.tcpdf.org)" in type one point high, three points from the corner of the
|
|
472
|
+
* paper. Taken for text it was the leftmost and the lowest thing on the page:
|
|
473
|
+
* the margins came in at the edge of the sheet and every line of basicapi.pdf
|
|
474
|
+
* and alphatrans.pdf was set against it.
|
|
475
|
+
*
|
|
476
|
+
* @param run The run.
|
|
477
|
+
* @param textSize The size the document's text is set in (see {@link textSizeOf}).
|
|
478
|
+
* @returns `true` where the run is a mark rather than text.
|
|
479
|
+
*/
|
|
480
|
+
function tooSmallToRead(run, textSize) {
|
|
481
|
+
return run.fontSizePt > 0 && run.fontSizePt < textSize * LEGIBLE_SHARE;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
309
484
|
* The margins the SOURCE used, measured off where its words actually sit.
|
|
310
485
|
*
|
|
311
486
|
* A PDF states none — text is placed anywhere on the MediaBox — so the reader
|
|
@@ -322,18 +497,13 @@ function sectionFromPdfPages(pages) {
|
|
|
322
497
|
* @param section The section the page box gave, or `undefined`.
|
|
323
498
|
* @param shown Each page as it is shown, for its own width and height.
|
|
324
499
|
* @param pageRuns Each page's runs, already placed on the shown page.
|
|
500
|
+
* @param pageMarks Each page's pictures, which the measure has to hold.
|
|
501
|
+
* @param foot The running foot lifted off the pages, as the first page
|
|
502
|
+
* showed it — the band the text block stands above.
|
|
325
503
|
* @returns The section with measured margins, or `section` when nothing is
|
|
326
504
|
* measurable.
|
|
327
505
|
*/
|
|
328
|
-
|
|
329
|
-
var SLACK = .01;
|
|
330
|
-
/** How much of the sheet a margin down the page may take. */
|
|
331
|
-
var DEEPEST_MARGIN = .5;
|
|
332
|
-
/** How far a face's ascender stands above its baseline, as a fraction of the size. */
|
|
333
|
-
var ASCENDER = .8;
|
|
334
|
-
/** And its descender below — the two together are a little over one em. */
|
|
335
|
-
var DESCENDER = .22;
|
|
336
|
-
function withMeasuredMargins(section, shown, pageRuns, pageMarks = []) {
|
|
506
|
+
function withMeasuredMargins(section, shown, pageRuns, pageMarks = [], foot = []) {
|
|
337
507
|
if (!section?.pageSize) return section;
|
|
338
508
|
const width = section.pageSize.width;
|
|
339
509
|
const height = section.pageSize.height;
|
|
@@ -341,8 +511,10 @@ function withMeasuredMargins(section, shown, pageRuns, pageMarks = []) {
|
|
|
341
511
|
const rights = [];
|
|
342
512
|
const tops = [];
|
|
343
513
|
const bottoms = [];
|
|
344
|
-
|
|
514
|
+
const textSize = textSizeOf(pageRuns);
|
|
515
|
+
pageRuns.forEach((all, i) => {
|
|
345
516
|
const page = shown[i];
|
|
517
|
+
const runs = all.filter((r) => r.annotation !== true && !tooSmallToRead(r, textSize));
|
|
346
518
|
if (!page || runs.length === 0) return;
|
|
347
519
|
let minX = Infinity;
|
|
348
520
|
let maxX = -Infinity;
|
|
@@ -365,6 +537,7 @@ function withMeasuredMargins(section, shown, pageRuns, pageMarks = []) {
|
|
|
365
537
|
topSize = r.fontSizePt;
|
|
366
538
|
}
|
|
367
539
|
}
|
|
540
|
+
const textEnd = maxX;
|
|
368
541
|
for (const mark of pageMarks[i] ?? []) {
|
|
369
542
|
if (!Number.isFinite(mark.x) || !Number.isFinite(mark.y)) continue;
|
|
370
543
|
maxX = Math.max(maxX, mark.x + mark.widthPt);
|
|
@@ -372,7 +545,12 @@ function withMeasuredMargins(section, shown, pageRuns, pageMarks = []) {
|
|
|
372
545
|
}
|
|
373
546
|
if (!Number.isFinite(minX) || !Number.isFinite(minY)) return;
|
|
374
547
|
lefts.push(minX);
|
|
375
|
-
|
|
548
|
+
const lines = linesOf(runs);
|
|
549
|
+
rights.push({
|
|
550
|
+
value: page.width - maxX,
|
|
551
|
+
full: lines.count >= 3 || maxX > textEnd,
|
|
552
|
+
edged: lines.atEdge >= 2 && maxX <= textEnd
|
|
553
|
+
});
|
|
376
554
|
tops.push(page.height - maxY - topSize * ASCENDER);
|
|
377
555
|
bottoms.push(minY - bottomSize * DESCENDER);
|
|
378
556
|
});
|
|
@@ -382,13 +560,21 @@ function withMeasuredMargins(section, shown, pageRuns, pageMarks = []) {
|
|
|
382
560
|
return s[Math.floor(s.length / 2)] ?? 0;
|
|
383
561
|
};
|
|
384
562
|
const clamp = (v, span, most = 1 / 3) => pt(Math.max(0, Math.min(v, span * most)));
|
|
563
|
+
const inked = foot.filter((r) => r.text.trim() !== "");
|
|
564
|
+
const footTop = Math.max(...inked.map((r) => r.y + r.fontSizePt * ASCENDER));
|
|
565
|
+
const footBottom = Math.min(...inked.map((r) => r.y - r.fontSizePt * DESCENDER));
|
|
566
|
+
const floor = inked.length > 0 ? footTop + textSize * FOOT_CLEAR_EM : Infinity;
|
|
567
|
+
const left = clamp(Math.min(...lefts), width);
|
|
568
|
+
const voters = rights.filter((r) => r.full);
|
|
569
|
+
const farthest = voters.length > 0 && voters.every((r) => r.edged) ? Math.max(width * GUESSED_MARGIN, width * (1 - NARROWEST_MEASURE) - left) : width * GUESSED_MARGIN;
|
|
385
570
|
return {
|
|
386
571
|
...section,
|
|
387
572
|
margins: {
|
|
388
|
-
left
|
|
389
|
-
right:
|
|
573
|
+
left,
|
|
574
|
+
right: pt(Math.max(0, Math.min(median((voters.length > 0 ? voters : rights).map((r) => r.value)) - width * SLACK, farthest))),
|
|
390
575
|
top: clamp(Math.min(...tops), height, DEEPEST_MARGIN),
|
|
391
|
-
bottom: clamp(Math.min(...bottoms) - height * SLACK, height, DEEPEST_MARGIN)
|
|
576
|
+
bottom: clamp(Math.min(...bottoms, floor) - height * SLACK, height, DEEPEST_MARGIN),
|
|
577
|
+
...inked.length > 0 ? { footer: clamp(footBottom, height, DEEPEST_MARGIN) } : {}
|
|
392
578
|
}
|
|
393
579
|
};
|
|
394
580
|
}
|
|
@@ -399,7 +585,7 @@ function withMeasuredMargins(section, shown, pageRuns, pageMarks = []) {
|
|
|
399
585
|
* both reconstruction paths (the tagged fast-path EP3 and the heuristic layout
|
|
400
586
|
* path EP4).
|
|
401
587
|
*/
|
|
402
|
-
function buildFlowDoc(body, resources = new ResourceStore(), section, embeddedFonts, sections = [], headersFooters) {
|
|
588
|
+
function buildFlowDoc(body, resources = new ResourceStore(), section, embeddedFonts, sections = [], headersFooters, faceFamilies) {
|
|
403
589
|
return {
|
|
404
590
|
kind: "flow",
|
|
405
591
|
body: resolveBodyStyles([...body], EMPTY_STYLE_SHEET),
|
|
@@ -407,9 +593,10 @@ function buildFlowDoc(body, resources = new ResourceStore(), section, embeddedFo
|
|
|
407
593
|
...headersFooters && headersFooters.size > 0 ? { headersFooters } : {},
|
|
408
594
|
...section ? { section } : {},
|
|
409
595
|
...embeddedFonts && embeddedFonts.size > 0 ? { embeddedFonts } : {},
|
|
596
|
+
...faceFamilies && faceFamilies.size > 0 ? { faceFamilies } : {},
|
|
410
597
|
styles: EMPTY_STYLE_SHEET,
|
|
411
598
|
resources
|
|
412
599
|
};
|
|
413
600
|
}
|
|
414
601
|
//#endregion
|
|
415
|
-
export { buildFlowDoc, dedupeLosses, imageBlock, paragraphBlock, paragraphFromRuns, positionedText, sectionFromPdfPages, shapeBlock, withMeasuredMargins };
|
|
602
|
+
export { BASELINE_AT, CARRIER_LINE_PT, GUESSED_MARGIN, NATURAL_LINE_EM, buildFlowDoc, dedupeLosses, imageBlock, paragraphBlock, paragraphFromRuns, positionedText, sectionFromPdfPages, shapeBlock, spaceAfter, textSizeOf, tooSmallToRead, withMeasuredMargins };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PdfDict } from '../pdf/objects.js';
|
|
2
2
|
import { ContentFont } from './content.js';
|
|
3
|
-
import { PdfFile } from './document.js';
|
|
3
|
+
import { PdfFile, PdfPage } from './document.js';
|
|
4
|
+
import { FaceFamily } from '../core/ir/flow.js';
|
|
4
5
|
/**
|
|
5
6
|
* Build a {@link ContentFont} (the interpreter's decode + advance hooks) from a
|
|
6
7
|
* `/Font` dictionary (E-PDF EP2). Unicode comes from the `/ToUnicode` CMap;
|
|
@@ -14,3 +15,28 @@ import { PdfFile } from './document.js';
|
|
|
14
15
|
* @returns The decode/advance hooks plus the code width (1 or 2 bytes per code).
|
|
15
16
|
*/
|
|
16
17
|
export declare function buildContentFont(file: PdfFile, fontDict: PdfDict): ContentFont;
|
|
18
|
+
/**
|
|
19
|
+
* For every face a run may name, the family a word processor knows it by —
|
|
20
|
+
* keyed by the name the run carries (see {@link FaceFamily}).
|
|
21
|
+
*
|
|
22
|
+
* A PDF names the FACE and a .docx names a family: written as the face,
|
|
23
|
+
* `inter-semibold` was a font no reader has, and LibreOffice set the whole of
|
|
24
|
+
* an invoice drawn in Inter in its default serif. The family is the one the
|
|
25
|
+
* descriptor states (§9.8.1 `/FontFamily`) or, where it states none, the one
|
|
26
|
+
* the PostScript name is made of.
|
|
27
|
+
*
|
|
28
|
+
* @param file The document.
|
|
29
|
+
* @param pages The pages whose faces are wanted.
|
|
30
|
+
* @returns Run font name → its family.
|
|
31
|
+
*/
|
|
32
|
+
export declare function collectFaceFamilies(file: PdfFile, pages: ReadonlyArray<PdfPage>): Map<string, FaceFamily>;
|
|
33
|
+
/**
|
|
34
|
+
* The family a PostScript face name is made of: `Inter-SemiBold` → `Inter`,
|
|
35
|
+
* `ArialMT` → `Arial`, `TimesNewRomanPS-BoldMT` → `Times New Roman`.
|
|
36
|
+
*
|
|
37
|
+
* §9.6.2.1 names a face `Family-Style` (Word writes `Family,Style`), with the
|
|
38
|
+
* family's words run together. What follows the separator is a style only if
|
|
39
|
+
* it is made of style words — `MS-Mincho` is a family of its own — and the
|
|
40
|
+
* words come apart where the capitals say they do.
|
|
41
|
+
*/
|
|
42
|
+
export declare function familyOfFace(baseFont: string): string;
|