reamkit 1.29.0 → 1.31.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 +60 -24
- package/dist/esm/core/converter/project.js +3 -1
- package/dist/esm/core/crypto/offcrypto.js +1 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +65 -0
- package/dist/esm/core/drawingml/shape-render.js +13 -1
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/ttf-build.d.ts +113 -0
- package/dist/esm/core/font/ttf-build.js +1224 -0
- package/dist/esm/core/font/ttf-subset.d.ts +19 -0
- package/dist/esm/core/font/ttf-subset.js +14 -1
- package/dist/esm/core/fonts/index.d.ts +1 -1
- package/dist/esm/core/fonts/provider.d.ts +17 -0
- package/dist/esm/core/fonts/provider.js +27 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +8 -0
- package/dist/esm/core/fonts/remote-fonts.js +108 -18
- package/dist/esm/core/ir/flow.d.ts +88 -1
- package/dist/esm/core/numbering/index.d.ts +1 -1
- package/dist/esm/core/numbering/state.d.ts +11 -1
- package/dist/esm/core/numbering/state.js +10 -1
- package/dist/esm/core/style-cascade/resolver.js +35 -4
- package/dist/esm/core/style-cascade/types.d.ts +19 -1
- package/dist/esm/core/style-cascade/types.js +3 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/layout/page-doc.d.ts +10 -3
- package/dist/esm/layout/styled-layout.d.ts +48 -7
- package/dist/esm/layout/styled-layout.js +881 -119
- package/dist/esm/layout/turned-section.d.ts +38 -0
- package/dist/esm/layout/turned-section.js +193 -0
- package/dist/esm/pdf/styled-page-emitter.js +78 -2
- 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/cff-outline.d.ts +27 -0
- package/dist/esm/pdf-reader/cff-outline.js +169 -21
- package/dist/esm/pdf-reader/cmap.js +5 -2
- package/dist/esm/pdf-reader/content.d.ts +70 -4
- package/dist/esm/pdf-reader/content.js +172 -12
- package/dist/esm/pdf-reader/display.d.ts +36 -0
- package/dist/esm/pdf-reader/display.js +82 -1
- package/dist/esm/pdf-reader/document.js +5 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +25 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +78 -9
- package/dist/esm/pdf-reader/encodings.d.ts +8 -0
- package/dist/esm/pdf-reader/encodings.js +25 -3
- package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
- package/dist/esm/pdf-reader/face-outlines.js +362 -0
- package/dist/esm/pdf-reader/figures.d.ts +52 -0
- package/dist/esm/pdf-reader/figures.js +433 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +224 -7
- package/dist/esm/pdf-reader/flow-build.js +545 -39
- package/dist/esm/pdf-reader/font.d.ts +27 -1
- package/dist/esm/pdf-reader/font.js +632 -63
- package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
- package/dist/esm/pdf-reader/glyf-outline.js +148 -3
- package/dist/esm/pdf-reader/glyph-names.js +154 -1
- 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 +119 -2
- package/dist/esm/pdf-reader/layout.js +2219 -184
- package/dist/esm/pdf-reader/lexer.d.ts +10 -0
- package/dist/esm/pdf-reader/lexer.js +17 -0
- package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
- package/dist/esm/pdf-reader/page-numbers.js +167 -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 +207 -28
- package/dist/esm/pdf-reader/text-rules.js +1 -1
- package/dist/esm/pdf-reader/text.d.ts +6 -3
- package/dist/esm/pdf-reader/text.js +106 -22
- package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
- package/dist/esm/pdf-reader/type1-outline.js +63 -8
- package/dist/esm/pdf-reader/vector.d.ts +8 -2
- package/dist/esm/pdf-reader/vector.js +105 -6
- package/dist/esm/word/doc/doc-reader.js +6 -2
- package/dist/esm/word/doc/doc-text.d.ts +6 -0
- package/dist/esm/word/doc/doc-text.js +19 -1
- package/dist/esm/word/document-parser.d.ts +2 -2
- package/dist/esm/word/document-parser.js +11 -1
- package/dist/esm/word/docx-reader.js +5 -3
- package/dist/esm/word/docx-writer.js +341 -54
- package/dist/esm/word/drawing-parser.d.ts +5 -3
- package/dist/esm/word/drawing-parser.js +56 -10
- package/dist/esm/word/font-embed.d.ts +30 -0
- package/dist/esm/word/font-embed.js +173 -0
- package/dist/esm/word/font-table.d.ts +10 -0
- package/dist/esm/word/font-table.js +13 -1
- package/dist/esm/word/index.js +1 -1
- package/dist/esm/word/numbering-parser.d.ts +3 -1
- package/dist/esm/word/numbering-parser.js +2 -1
- package/dist/esm/word/paragraph-properties.d.ts +7 -6
- package/dist/esm/word/paragraph-properties.js +16 -2
- package/dist/esm/word/run-properties.js +26 -0
- package/package.json +12 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
1
|
+
import { PDF_NULL, PdfHexString, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
2
|
import { cieToSrgb } from "./cie-color.js";
|
|
3
3
|
import { iccTransform } from "./icc.js";
|
|
4
4
|
import { readFunction } from "./function.js";
|
|
@@ -26,12 +26,102 @@ function buildShadingMap(file, resources) {
|
|
|
26
26
|
if (!(pat instanceof Map)) continue;
|
|
27
27
|
const shading = dictOf(file.resolve(pat.get("Shading") ?? PDF_NULL));
|
|
28
28
|
if (!shading) continue;
|
|
29
|
-
const
|
|
29
|
+
const m = numArray(file, pat.get("Matrix"));
|
|
30
|
+
const gradient = parseShading(file, shading, m && m.length >= 6 ? [
|
|
31
|
+
m[0],
|
|
32
|
+
m[1],
|
|
33
|
+
m[2],
|
|
34
|
+
m[3],
|
|
35
|
+
m[4],
|
|
36
|
+
m[5]
|
|
37
|
+
] : void 0);
|
|
30
38
|
if (gradient) out.set(nm, gradient);
|
|
31
39
|
}
|
|
32
40
|
return out;
|
|
33
41
|
}
|
|
34
42
|
/**
|
|
43
|
+
* A page's gradient as a shape filled with it shows it: its stops re-mapped
|
|
44
|
+
* from the whole axis to the part of it the shape's box covers.
|
|
45
|
+
*
|
|
46
|
+
* A DrawingML gradient spans the shape it fills, and a PDF shading spans its
|
|
47
|
+
* own axis, which may reach far past the shape: issue10572.pdf's pattern
|
|
48
|
+
* runs twenty-four stripes down 1800 points, of which its 450-point square
|
|
49
|
+
* shows six — spread over the square, all twenty-four came back as hairlines.
|
|
50
|
+
* Beyond either end of the axis the end colour carries on, as `/Extend` asks.
|
|
51
|
+
*
|
|
52
|
+
* @param g The gradient, with its axis where the page states one.
|
|
53
|
+
* @param box The shape's box, in the same space as the axis.
|
|
54
|
+
* @returns The gradient as the shape shows it, with no axis.
|
|
55
|
+
*/
|
|
56
|
+
function gradientOverBox(g, box) {
|
|
57
|
+
const { axis, ...plain } = g;
|
|
58
|
+
if (!axis || g.kind !== "linear" || g.stops.length === 0) return plain;
|
|
59
|
+
const dx = axis.x1 - axis.x0;
|
|
60
|
+
const dy = axis.y1 - axis.y0;
|
|
61
|
+
const len2 = dx * dx + dy * dy;
|
|
62
|
+
if (!(len2 > 0)) return plain;
|
|
63
|
+
const along = (x, y) => ((x - axis.x0) * dx + (y - axis.y0) * dy) / len2;
|
|
64
|
+
const ts = [
|
|
65
|
+
along(box.minX, box.minY),
|
|
66
|
+
along(box.minX, box.maxY),
|
|
67
|
+
along(box.maxX, box.minY),
|
|
68
|
+
along(box.maxX, box.maxY)
|
|
69
|
+
];
|
|
70
|
+
const t0 = Math.min(...ts);
|
|
71
|
+
const t1 = Math.max(...ts);
|
|
72
|
+
if (!(t1 - t0 > 1e-9)) return plain;
|
|
73
|
+
if (Math.abs(t0) < SPAN_NOISE && Math.abs(t1 - 1) < SPAN_NOISE) return plain;
|
|
74
|
+
const inner = g.stops.filter((st) => st.offset > t0 && st.offset < t1);
|
|
75
|
+
const stops = [
|
|
76
|
+
{
|
|
77
|
+
offset: 0,
|
|
78
|
+
colorHex: colorAlong(g.stops, t0, "after")
|
|
79
|
+
},
|
|
80
|
+
...inner.map((st) => ({
|
|
81
|
+
offset: (st.offset - t0) / (t1 - t0),
|
|
82
|
+
colorHex: st.colorHex
|
|
83
|
+
})),
|
|
84
|
+
{
|
|
85
|
+
offset: 1,
|
|
86
|
+
colorHex: colorAlong(g.stops, t1, "before")
|
|
87
|
+
}
|
|
88
|
+
];
|
|
89
|
+
return {
|
|
90
|
+
...plain,
|
|
91
|
+
stops
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** How near the box's span may come to the whole axis and be taken for it. */
|
|
95
|
+
var SPAN_NOISE = .001;
|
|
96
|
+
/**
|
|
97
|
+
* A gradient's colour at `t` along it, its end colours carried on past either
|
|
98
|
+
* end. Where two stops share `t` the gradient changes colour AT it, and `side`
|
|
99
|
+
* says which of the two is wanted: the one the span after `t` starts with, or
|
|
100
|
+
* the one the span before it ends with. issue10572.pdf's square starts on such
|
|
101
|
+
* an edge, and taken from the wrong side its first stripe faded in from blue.
|
|
102
|
+
*/
|
|
103
|
+
function colorAlong(stops, t, side) {
|
|
104
|
+
const first = stops[0];
|
|
105
|
+
const last = stops[stops.length - 1];
|
|
106
|
+
if (t <= first.offset) return first.colorHex;
|
|
107
|
+
if (t >= last.offset) return last.colorHex;
|
|
108
|
+
const at = stops.filter((st) => Math.abs(st.offset - t) < 1e-9);
|
|
109
|
+
if (at.length > 0) return (side === "after" ? at[at.length - 1] : at[0]).colorHex;
|
|
110
|
+
for (let i = 1; i < stops.length; i++) {
|
|
111
|
+
const b = stops[i];
|
|
112
|
+
if (t > b.offset) continue;
|
|
113
|
+
const a = stops[i - 1];
|
|
114
|
+
const span = b.offset - a.offset;
|
|
115
|
+
const f = span > 0 ? (t - a.offset) / span : 1;
|
|
116
|
+
const mix = (shift) => {
|
|
117
|
+
const ca = parseInt(a.colorHex, 16) >> shift & 255;
|
|
118
|
+
return ca + ((parseInt(b.colorHex, 16) >> shift & 255) - ca) * f;
|
|
119
|
+
};
|
|
120
|
+
return hex255(mix(16), mix(8), mix(0));
|
|
121
|
+
}
|
|
122
|
+
return last.colorHex;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
35
125
|
* §8.7.4.5.2 — an axial or radial shading as a gradient, for a bare `sh`.
|
|
36
126
|
*
|
|
37
127
|
* A `sh` paints the CLIP rather than a path, so what it needs is not a fill for
|
|
@@ -42,8 +132,8 @@ function buildShadingMap(file, resources) {
|
|
|
42
132
|
* @param sh The shading dictionary.
|
|
43
133
|
* @returns The gradient, or `undefined` for a type this does not read.
|
|
44
134
|
*/
|
|
45
|
-
function gradientShading(file, sh) {
|
|
46
|
-
return parseShading(file, sh);
|
|
135
|
+
function gradientShading(file, sh, ctm) {
|
|
136
|
+
return parseShading(file, sh, ctm);
|
|
47
137
|
}
|
|
48
138
|
/**
|
|
49
139
|
* §8.7.4.5 — which kind of shading this is, as the file states it.
|
|
@@ -55,7 +145,7 @@ function gradientShading(file, sh) {
|
|
|
55
145
|
function shadingTypeOf(file, sh) {
|
|
56
146
|
return numOf(file.get(sh, "ShadingType"));
|
|
57
147
|
}
|
|
58
|
-
function parseShading(file, sh) {
|
|
148
|
+
function parseShading(file, sh, matrix) {
|
|
59
149
|
const type = numOf(file.get(sh, "ShadingType"));
|
|
60
150
|
if (type !== 2 && type !== 3) return void 0;
|
|
61
151
|
const stops = parseFunction(file, sh.get("Function"), shadingSpace(file, sh));
|
|
@@ -65,11 +155,33 @@ function parseShading(file, sh) {
|
|
|
65
155
|
stops
|
|
66
156
|
};
|
|
67
157
|
const c = numArray(file, sh.get("Coords"));
|
|
68
|
-
return {
|
|
158
|
+
if (!c || c.length < 4) return {
|
|
69
159
|
kind: "linear",
|
|
70
|
-
angle:
|
|
160
|
+
angle: 0,
|
|
71
161
|
stops
|
|
72
162
|
};
|
|
163
|
+
const [a, b, cc, d, e, f] = matrix ?? [
|
|
164
|
+
1,
|
|
165
|
+
0,
|
|
166
|
+
0,
|
|
167
|
+
1,
|
|
168
|
+
0,
|
|
169
|
+
0
|
|
170
|
+
];
|
|
171
|
+
const at = (x, y) => [a * x + cc * y + e, b * x + d * y + f];
|
|
172
|
+
const [x0, y0] = at(c[0], c[1]);
|
|
173
|
+
const [x1, y1] = at(c[2], c[3]);
|
|
174
|
+
return {
|
|
175
|
+
kind: "linear",
|
|
176
|
+
angle: (Math.atan2(-(y1 - y0), x1 - x0) * 180 / Math.PI % 360 + 360) % 360,
|
|
177
|
+
stops,
|
|
178
|
+
axis: {
|
|
179
|
+
x0,
|
|
180
|
+
y0,
|
|
181
|
+
x1,
|
|
182
|
+
y1
|
|
183
|
+
}
|
|
184
|
+
};
|
|
73
185
|
}
|
|
74
186
|
function parseFunction(file, value, space) {
|
|
75
187
|
const resolved = value !== void 0 ? file.resolve(value) : void 0;
|
|
@@ -246,6 +358,17 @@ function numArray(file, v) {
|
|
|
246
358
|
function colorOf(c, space) {
|
|
247
359
|
return spaceColor(c, space) ?? spaceColor(c, void 0) ?? grayHex(c[0] ?? 0);
|
|
248
360
|
}
|
|
361
|
+
/** §8.6.6.3 — an Indexed space's table: a string, a hex string or a stream. */
|
|
362
|
+
function lookupBytes(file, v) {
|
|
363
|
+
const r = file.resolve(v ?? PDF_NULL);
|
|
364
|
+
if (r instanceof PdfHexString) return r.bytes;
|
|
365
|
+
if (typeof r === "string") {
|
|
366
|
+
const out = new Uint8Array(r.length);
|
|
367
|
+
for (let i = 0; i < r.length; i++) out[i] = r.charCodeAt(i) & 255;
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
if (r instanceof PdfStream) return file.streamData(r);
|
|
371
|
+
}
|
|
249
372
|
/**
|
|
250
373
|
* §8.6.8 — the colour a run of `sc` / `scn` components comes to.
|
|
251
374
|
*
|
|
@@ -301,6 +424,10 @@ function grayHex(v) {
|
|
|
301
424
|
function cmykHex(c, m, y, k) {
|
|
302
425
|
return rgbHex((1 - c) * (1 - k), (1 - m) * (1 - k), (1 - y) * (1 - k));
|
|
303
426
|
}
|
|
427
|
+
function hex255(r, g, b) {
|
|
428
|
+
const h = (x) => Math.max(0, Math.min(255, Math.round(x))).toString(16).padStart(2, "0");
|
|
429
|
+
return (h(r) + h(g) + h(b)).toUpperCase();
|
|
430
|
+
}
|
|
304
431
|
function pushStop(stops, offset, colorHex) {
|
|
305
432
|
const o = Math.max(0, Math.min(1, offset));
|
|
306
433
|
const last = stops[stops.length - 1];
|
|
@@ -350,7 +477,15 @@ function buildAlphaMap(file, resources) {
|
|
|
350
477
|
const blend = mode !== void 0 && mode !== "Normal" && mode !== "Compatible" && !darkens ? mode : void 0;
|
|
351
478
|
const statesMask = state.has("SMask");
|
|
352
479
|
const masked = statesMask ? file.resolve(state.get("SMask") ?? PDF_NULL) instanceof Map : void 0;
|
|
353
|
-
|
|
480
|
+
const lw = file.resolve(state.get("LW") ?? PDF_NULL);
|
|
481
|
+
const lineWidth = typeof lw === "number" && lw >= 0 ? lw : void 0;
|
|
482
|
+
const d = file.resolve(state.get("D") ?? PDF_NULL);
|
|
483
|
+
const dash = Array.isArray(d) ? dashLengths(file.resolve(d[0] ?? PDF_NULL)) : void 0;
|
|
484
|
+
const lc = file.resolve(state.get("LC") ?? PDF_NULL);
|
|
485
|
+
const lineCap = lc === 0 || lc === 1 || lc === 2 ? lc : void 0;
|
|
486
|
+
const caStroke = file.resolve(state.get("CA") ?? PDF_NULL);
|
|
487
|
+
const strokeAlpha = typeof caStroke === "number" && caStroke >= 0 && caStroke <= 1 ? caStroke : void 0;
|
|
488
|
+
if (alpha === void 0 && mode === void 0 && !statesMask && !(lineWidth !== void 0 || dash !== void 0 || lineCap !== void 0 || strokeAlpha !== void 0)) continue;
|
|
354
489
|
out.set(name, {
|
|
355
490
|
...alpha !== void 0 ? { alpha } : {},
|
|
356
491
|
...mode !== void 0 ? {
|
|
@@ -358,12 +493,30 @@ function buildAlphaMap(file, resources) {
|
|
|
358
493
|
darkens
|
|
359
494
|
} : {},
|
|
360
495
|
...blend !== void 0 ? { blend } : {},
|
|
361
|
-
...masked !== void 0 ? { masked } : {}
|
|
496
|
+
...masked !== void 0 ? { masked } : {},
|
|
497
|
+
...lineWidth !== void 0 ? { lineWidth } : {},
|
|
498
|
+
...dash !== void 0 ? { dash } : {},
|
|
499
|
+
...lineCap !== void 0 ? { lineCap } : {},
|
|
500
|
+
...strokeAlpha !== void 0 ? { strokeAlpha } : {}
|
|
362
501
|
});
|
|
363
502
|
}
|
|
364
503
|
return out;
|
|
365
504
|
}
|
|
366
505
|
/**
|
|
506
|
+
* §8.4.3.6 — a dash array as a stroke can use it: lengths that are numbers and
|
|
507
|
+
* not negative, and none at all where every one is zero, which draws nothing
|
|
508
|
+
* and is solid by the spec's own reading.
|
|
509
|
+
*
|
|
510
|
+
* @param value The array, as the file states it.
|
|
511
|
+
* @returns The lengths, or an empty array for a solid line.
|
|
512
|
+
*/
|
|
513
|
+
function dashLengths(value) {
|
|
514
|
+
if (!Array.isArray(value)) return [];
|
|
515
|
+
const lengths = value.filter((n) => typeof n === "number" && n >= 0);
|
|
516
|
+
if (lengths.length !== value.length || lengths.every((n) => n === 0)) return [];
|
|
517
|
+
return lengths;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
367
520
|
* The spaces a `/ColorSpace` resource dictionary names, by name.
|
|
368
521
|
*
|
|
369
522
|
* @param file The owning file.
|
|
@@ -405,7 +558,24 @@ function colorSpaceAt(file, cs, depth) {
|
|
|
405
558
|
icc
|
|
406
559
|
} : base;
|
|
407
560
|
}
|
|
408
|
-
if (head.value === "Indexed")
|
|
561
|
+
if (head.value === "Indexed" || head.value === "I") {
|
|
562
|
+
const base = depth < MAX_ALTERNATE ? colorSpaceAt(file, file.resolve(cs[1] ?? PDF_NULL), depth + 1) : void 0;
|
|
563
|
+
const hival = file.resolve(cs[2] ?? PDF_NULL);
|
|
564
|
+
const table = lookupBytes(file, cs[3]);
|
|
565
|
+
if (!base || base.kind === "tint" || typeof hival !== "number" || !table) return void 0;
|
|
566
|
+
const transform = ([index = 0]) => {
|
|
567
|
+
const at = Math.min(Math.max(0, Math.round(index)), Math.round(hival));
|
|
568
|
+
return Array.from({ length: base.components }, (_, k) => (table[at * base.components + k] ?? 0) / 255);
|
|
569
|
+
};
|
|
570
|
+
return {
|
|
571
|
+
kind: "tint",
|
|
572
|
+
components: 1,
|
|
573
|
+
tint: {
|
|
574
|
+
transform,
|
|
575
|
+
alternate: base
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
}
|
|
409
579
|
if (head.value === "CalRGB") {
|
|
410
580
|
const params = file.resolve(cs[1] ?? PDF_NULL);
|
|
411
581
|
const cie = params instanceof Map ? cieParams(file, params, true) : void 0;
|
|
@@ -516,4 +686,4 @@ function byFamily(family, n) {
|
|
|
516
686
|
}
|
|
517
687
|
}
|
|
518
688
|
//#endregion
|
|
519
|
-
export { buildAlphaMap, buildColorSpaceMap, buildShadingMap, cmykHex, gradientShading, grayHex, rgbHex, sampledShading, shadingTypeOf, spaceColor };
|
|
689
|
+
export { buildAlphaMap, buildColorSpaceMap, buildShadingMap, cmykHex, dashLengths, gradientOverBox, gradientShading, grayHex, rgbHex, sampledShading, shadingTypeOf, spaceColor };
|
|
@@ -23,17 +23,17 @@ function readStructTree(file) {
|
|
|
23
23
|
return pg instanceof Map ? pageMap.get(pg) : void 0;
|
|
24
24
|
};
|
|
25
25
|
const seen = /* @__PURE__ */ new Set();
|
|
26
|
+
const typeOf = roleResolver(file, stRoot);
|
|
26
27
|
const read = (value, parentPage) => {
|
|
27
28
|
const elem = file.resolve(value);
|
|
28
29
|
if (!(elem instanceof Map) || seen.has(elem) || seen.size > MAX_NODES) return void 0;
|
|
29
30
|
seen.add(elem);
|
|
30
31
|
const ownPage = pageIndexOf(elem.get("Pg") ?? PDF_NULL) ?? parentPage;
|
|
31
|
-
const
|
|
32
|
-
const children = [];
|
|
32
|
+
const content = [];
|
|
33
33
|
for (const kid of kidList(file, elem.get("K"))) {
|
|
34
34
|
const rk = file.resolve(kid);
|
|
35
35
|
if (typeof rk === "number") {
|
|
36
|
-
if (ownPage >= 0)
|
|
36
|
+
if (ownPage >= 0) content.push({
|
|
37
37
|
page: ownPage,
|
|
38
38
|
mcid: rk
|
|
39
39
|
});
|
|
@@ -42,22 +42,24 @@ function readStructTree(file) {
|
|
|
42
42
|
if (kind === "MCR") {
|
|
43
43
|
const m = rk.get("MCID");
|
|
44
44
|
const page = pageIndexOf(rk.get("Pg") ?? PDF_NULL) ?? ownPage;
|
|
45
|
-
if (typeof m === "number" && page >= 0)
|
|
45
|
+
if (typeof m === "number" && page >= 0) content.push({
|
|
46
46
|
page,
|
|
47
47
|
mcid: m
|
|
48
48
|
});
|
|
49
49
|
} else if (kind === "OBJR") {} else {
|
|
50
50
|
const child = read(rk, ownPage);
|
|
51
|
-
if (child)
|
|
51
|
+
if (!child) continue;
|
|
52
|
+
if (inline(file, rk, child.type)) content.push(...allMcids(child));
|
|
53
|
+
else content.push(child);
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
}
|
|
57
|
+
const type = typeOf(nameOf(elem.get("S")));
|
|
55
58
|
const alt = elem.get("Alt");
|
|
56
59
|
const { colSpan, rowSpan } = readSpans(file, elem.get("A") ?? PDF_NULL);
|
|
57
60
|
return {
|
|
58
|
-
type
|
|
59
|
-
|
|
60
|
-
children,
|
|
61
|
+
type,
|
|
62
|
+
...settle(type, content),
|
|
61
63
|
...typeof alt === "string" ? { alt } : {},
|
|
62
64
|
...colSpan > 1 ? { colSpan } : {},
|
|
63
65
|
...rowSpan > 1 ? { rowSpan } : {}
|
|
@@ -71,6 +73,108 @@ function readStructTree(file) {
|
|
|
71
73
|
children: roots
|
|
72
74
|
};
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* An element's own marked content and its child elements, as the node keeps
|
|
78
|
+
* them.
|
|
79
|
+
*
|
|
80
|
+
* An element holds text of its own AND block elements only where the tree
|
|
81
|
+
* mixes them — a heading carrying its number as a child, a paragraph with a
|
|
82
|
+
* formula set between two of its sentences. Kept as two lists, the order
|
|
83
|
+
* between them was gone and the element's own text was never read at all:
|
|
84
|
+
* bug1937438_mml_from_latex.pdf's heading came back as "1" without "A small
|
|
85
|
+
* example", and its sentence around a formula without its words. Each
|
|
86
|
+
* stretch of the element's own content becomes a paragraph of its own
|
|
87
|
+
* standing where it stood — except in a figure, whose marked content is its
|
|
88
|
+
* picture.
|
|
89
|
+
*/
|
|
90
|
+
function settle(type, content) {
|
|
91
|
+
const nodes = content.filter((c) => "type" in c);
|
|
92
|
+
const own = content.filter((c) => !("type" in c));
|
|
93
|
+
if (nodes.length === 0 || own.length === 0 || type === "Figure") return {
|
|
94
|
+
mcids: own,
|
|
95
|
+
children: nodes
|
|
96
|
+
};
|
|
97
|
+
const children = [];
|
|
98
|
+
let stretch = [];
|
|
99
|
+
const close = () => {
|
|
100
|
+
if (stretch.length > 0) children.push({
|
|
101
|
+
type: "P",
|
|
102
|
+
mcids: stretch,
|
|
103
|
+
children: []
|
|
104
|
+
});
|
|
105
|
+
stretch = [];
|
|
106
|
+
};
|
|
107
|
+
for (const c of content) if ("type" in c) {
|
|
108
|
+
close();
|
|
109
|
+
children.push(c);
|
|
110
|
+
} else stretch.push(c);
|
|
111
|
+
close();
|
|
112
|
+
return {
|
|
113
|
+
mcids: [],
|
|
114
|
+
children
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** Every marked content reference under a node, in reading order. */
|
|
118
|
+
function allMcids(node) {
|
|
119
|
+
return [...node.mcids, ...node.children.flatMap(allMcids)];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* §14.8.4.4 — the inline-level structure types (PDF 2.0 adds `Em`, `Strong`,
|
|
123
|
+
* `Sub`), a formula (§14.8.4.5.5, which is set in a line as often as out of
|
|
124
|
+
* one), and a list label, which is the start of its item's line.
|
|
125
|
+
*/
|
|
126
|
+
var INLINE = new Set([
|
|
127
|
+
"Span",
|
|
128
|
+
"Quote",
|
|
129
|
+
"Reference",
|
|
130
|
+
"BibEntry",
|
|
131
|
+
"Code",
|
|
132
|
+
"Link",
|
|
133
|
+
"Annot",
|
|
134
|
+
"Ruby",
|
|
135
|
+
"RB",
|
|
136
|
+
"RT",
|
|
137
|
+
"RP",
|
|
138
|
+
"Warichu",
|
|
139
|
+
"WT",
|
|
140
|
+
"WP",
|
|
141
|
+
"Em",
|
|
142
|
+
"Strong",
|
|
143
|
+
"Sub",
|
|
144
|
+
"Formula",
|
|
145
|
+
"Lbl"
|
|
146
|
+
]);
|
|
147
|
+
/** The MathML namespace (ISO 32000-2 §14.8.6): an element in it is mathematics. */
|
|
148
|
+
var MATHML = "http://www.w3.org/1998/Math/MathML";
|
|
149
|
+
/** Whether an element is inline — by its standard type, or as MathML. */
|
|
150
|
+
function inline(file, elem, type) {
|
|
151
|
+
if (INLINE.has(type)) return true;
|
|
152
|
+
const ns = file.resolve(elem.get("NS") ?? PDF_NULL);
|
|
153
|
+
const uri = ns instanceof Map ? file.resolve(ns.get("NS") ?? PDF_NULL) : void 0;
|
|
154
|
+
return typeof uri === "string" && uri === MATHML;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* §14.8.4.2 `/RoleMap` — a document's own structure types, mapped to the
|
|
158
|
+
* standard ones they stand for, followed until a type maps no further. A
|
|
159
|
+
* LaTeX document names its elements `section`, `text-unit` and
|
|
160
|
+
* `section-number`, and maps them to `H1`, `Part` and `Span`: read by their
|
|
161
|
+
* own names, none of them meant anything.
|
|
162
|
+
*/
|
|
163
|
+
function roleResolver(file, stRoot) {
|
|
164
|
+
const map = file.resolve(stRoot.get("RoleMap") ?? PDF_NULL);
|
|
165
|
+
if (!(map instanceof Map)) return (type) => type;
|
|
166
|
+
return (type) => {
|
|
167
|
+
let at = type;
|
|
168
|
+
for (let hop = 0; hop < MAX_ROLE_HOPS; hop++) {
|
|
169
|
+
const next = file.resolve(map.get(at) ?? PDF_NULL);
|
|
170
|
+
if (!(next instanceof PdfName) || next.value === at) break;
|
|
171
|
+
at = next.value;
|
|
172
|
+
}
|
|
173
|
+
return at;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** How far a chain of role mappings is followed before it is taken for a cycle. */
|
|
177
|
+
var MAX_ROLE_HOPS = 8;
|
|
74
178
|
function kidList(file, kVal) {
|
|
75
179
|
if (kVal === void 0) return [];
|
|
76
180
|
const k = file.resolve(kVal);
|