reamkit 1.26.0 → 1.28.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 +14 -8
- package/dist/esm/core/converter/ream.d.ts +0 -9
- package/dist/esm/core/converter/ream.js +0 -1
- package/dist/esm/core/document-model/types.d.ts +5 -3
- package/dist/esm/core/font/index.d.ts +1 -0
- package/dist/esm/core/font/ligatures.d.ts +17 -0
- package/dist/esm/core/font/ligatures.js +49 -0
- package/dist/esm/core/font/ttf-parser.d.ts +2 -1
- package/dist/esm/core/font/ttf-parser.js +11 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +1 -1
- package/dist/esm/core/fonts/remote-fonts.js +47 -2
- package/dist/esm/core/fonts/scripts.js +10 -5
- package/dist/esm/excel/header-footer.js +53 -8
- package/dist/esm/layout/styled-layout.js +53 -3
- package/dist/esm/pdf/cid-font.js +35 -8
- package/dist/esm/pdf-reader/annot-draw.d.ts +65 -0
- package/dist/esm/pdf-reader/annot-draw.js +487 -0
- package/dist/esm/pdf-reader/annots.d.ts +0 -12
- package/dist/esm/pdf-reader/annots.js +30 -4
- package/dist/esm/pdf-reader/ccitt.d.ts +20 -3
- package/dist/esm/pdf-reader/ccitt.js +102 -6
- package/dist/esm/pdf-reader/cff-outline.d.ts +36 -0
- package/dist/esm/pdf-reader/cff-outline.js +1122 -0
- package/dist/esm/pdf-reader/cie-color.d.ts +33 -0
- package/dist/esm/pdf-reader/cie-color.js +112 -0
- package/dist/esm/pdf-reader/cmap.js +5 -2
- package/dist/esm/pdf-reader/content.d.ts +123 -3
- package/dist/esm/pdf-reader/content.js +232 -54
- package/dist/esm/pdf-reader/dingbats.d.ts +11 -0
- package/dist/esm/pdf-reader/dingbats.js +1033 -0
- package/dist/esm/pdf-reader/display.d.ts +1 -1
- package/dist/esm/pdf-reader/display.js +61 -6
- package/dist/esm/pdf-reader/document.d.ts +6 -0
- package/dist/esm/pdf-reader/document.js +55 -10
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +23 -3
- package/dist/esm/pdf-reader/embedded-fonts.js +36 -4
- package/dist/esm/pdf-reader/encodings.d.ts +25 -0
- package/dist/esm/pdf-reader/encodings.js +110 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +19 -5
- package/dist/esm/pdf-reader/flow-build.js +111 -11
- package/dist/esm/pdf-reader/font.js +403 -21
- package/dist/esm/pdf-reader/function.d.ts +16 -0
- package/dist/esm/pdf-reader/function.js +414 -0
- package/dist/esm/pdf-reader/glyf-outline.d.ts +43 -0
- package/dist/esm/pdf-reader/glyf-outline.js +351 -0
- package/dist/esm/pdf-reader/glyph-names.js +20 -0
- package/dist/esm/pdf-reader/icc.d.ts +10 -0
- package/dist/esm/pdf-reader/icc.js +210 -0
- package/dist/esm/pdf-reader/image-decode.d.ts +9 -4
- package/dist/esm/pdf-reader/image-decode.js +274 -72
- package/dist/esm/pdf-reader/images.d.ts +18 -0
- package/dist/esm/pdf-reader/images.js +154 -11
- package/dist/esm/pdf-reader/jbig2.d.ts +23 -0
- package/dist/esm/pdf-reader/jbig2.js +126 -32
- package/dist/esm/pdf-reader/layout.d.ts +32 -0
- package/dist/esm/pdf-reader/layout.js +1316 -64
- package/dist/esm/pdf-reader/lexer.d.ts +2 -0
- package/dist/esm/pdf-reader/lexer.js +4 -0
- package/dist/esm/pdf-reader/math-rows.d.ts +23 -0
- package/dist/esm/pdf-reader/math-rows.js +198 -0
- package/dist/esm/pdf-reader/optional-content.d.ts +36 -0
- package/dist/esm/pdf-reader/optional-content.js +93 -0
- package/dist/esm/pdf-reader/predefined-cmap.d.ts +21 -0
- package/dist/esm/pdf-reader/predefined-cmap.js +102 -0
- package/dist/esm/pdf-reader/reader.d.ts +6 -6
- package/dist/esm/pdf-reader/reader.js +102 -16
- package/dist/esm/pdf-reader/shading.d.ts +139 -8
- package/dist/esm/pdf-reader/shading.js +309 -39
- package/dist/esm/pdf-reader/standard-metrics.d.ts +8 -0
- package/dist/esm/pdf-reader/standard-metrics.js +18 -0
- package/dist/esm/pdf-reader/standard-widths.d.ts +20 -0
- package/dist/esm/pdf-reader/standard-widths.js +62 -0
- package/dist/esm/pdf-reader/stream-filters.d.ts +6 -0
- package/dist/esm/pdf-reader/stream-filters.js +67 -0
- package/dist/esm/pdf-reader/tagged.js +204 -32
- package/dist/esm/pdf-reader/text-rules.d.ts +16 -0
- package/dist/esm/pdf-reader/text-rules.js +185 -0
- package/dist/esm/pdf-reader/text.js +157 -4
- package/dist/esm/pdf-reader/type1-outline.d.ts +21 -0
- package/dist/esm/pdf-reader/type1-outline.js +576 -0
- package/dist/esm/pdf-reader/vector.d.ts +5 -0
- package/dist/esm/pdf-reader/vector.js +79 -27
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-writer.js +74 -11
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ export interface Display {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* The {@link Display} a page's
|
|
20
|
+
* The {@link Display} a page's shown box and `/Rotate` describe.
|
|
21
21
|
*
|
|
22
22
|
* `/Rotate` turns the page CLOCKWISE when shown, so the content turns with it —
|
|
23
23
|
* counter-clockwise by the same amount as seen from the content's own frame,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/pdf-reader/display.ts
|
|
2
2
|
/**
|
|
3
|
-
* The {@link Display} a page's
|
|
3
|
+
* The {@link Display} a page's shown box and `/Rotate` describe.
|
|
4
4
|
*
|
|
5
5
|
* `/Rotate` turns the page CLOCKWISE when shown, so the content turns with it —
|
|
6
6
|
* counter-clockwise by the same amount as seen from the content's own frame,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @returns Its size, its turn, and the map onto it.
|
|
11
11
|
*/
|
|
12
12
|
function displayOf(page) {
|
|
13
|
-
const [x0, y0, x1, y1] = page.
|
|
13
|
+
const [x0, y0, x1, y1] = page.cropBox;
|
|
14
14
|
const left = Math.min(x0, x1);
|
|
15
15
|
const bottom = Math.min(y0, y1);
|
|
16
16
|
const w = Math.abs(x1 - x0);
|
|
@@ -66,7 +66,60 @@ function placeRuns(runs, d) {
|
|
|
66
66
|
endY: end.y,
|
|
67
67
|
...Math.abs(angle) > .5 ? { angleDeg: angle } : {}
|
|
68
68
|
};
|
|
69
|
-
});
|
|
69
|
+
}).filter((r) => shows(d, Math.min(r.x, r.endX), Math.min(r.y, r.endY) - r.fontSizePt * .25, Math.max(r.x, r.endX), Math.max(r.y, r.endY) + r.fontSizePt * .85)).map((r) => cropped(r, d));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* §14.11.2 — a run the crop's edge cuts through, as the letters it shows.
|
|
73
|
+
*
|
|
74
|
+
* A run that reaches into the shown page was kept whole, which is right where
|
|
75
|
+
* the crop trims a hair and wrong where it trims a line: endchar.pdf is one
|
|
76
|
+
* line of a poster, "LE HOLD-UP PLANÉTAIRE", cropped to the fourteen points
|
|
77
|
+
* that hold its É. Every viewer shows that one letter. Reflowed, the whole
|
|
78
|
+
* line was re-set into a column fourteen points wide and came back as four
|
|
79
|
+
* pages of one letter each.
|
|
80
|
+
*
|
|
81
|
+
* A letter is kept where any part of it is inside, so a crop that shaves an
|
|
82
|
+
* edge takes nothing. Where in the run each letter falls is estimated from the
|
|
83
|
+
* run's own width, which is exact for a face set solid and near enough for one
|
|
84
|
+
* that is not — the answer is a letter either way.
|
|
85
|
+
*
|
|
86
|
+
* @param run The run, already placed on the shown page.
|
|
87
|
+
* @param d The page's shown geometry.
|
|
88
|
+
* @returns The run, or the part of it the page shows.
|
|
89
|
+
*/
|
|
90
|
+
function cropped(run, d) {
|
|
91
|
+
const from = Math.min(run.x, run.endX);
|
|
92
|
+
const to = Math.max(run.x, run.endX);
|
|
93
|
+
const chars = [...run.text];
|
|
94
|
+
if (from >= 0 && to <= d.width) return run;
|
|
95
|
+
if (run.angleDeg !== void 0 || chars.length < 2 || !(to > from)) return run;
|
|
96
|
+
const step = (to - from) / chars.length;
|
|
97
|
+
const first = Math.max(0, Math.floor((0 - from) / step));
|
|
98
|
+
const last = Math.min(chars.length - 1, Math.ceil((d.width - from) / step) - 1);
|
|
99
|
+
if (first > last) return run;
|
|
100
|
+
if (first === 0 && last === chars.length - 1) return run;
|
|
101
|
+
const backwards = run.endX < run.x;
|
|
102
|
+
const startX = from + first * step;
|
|
103
|
+
const endX = from + (last + 1) * step;
|
|
104
|
+
return {
|
|
105
|
+
...run,
|
|
106
|
+
text: chars.slice(first, last + 1).join(""),
|
|
107
|
+
x: backwards ? endX : startX,
|
|
108
|
+
endX: backwards ? startX : endX
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* §14.11.2 — whether a box falls inside the region the page SHOWS.
|
|
113
|
+
*
|
|
114
|
+
* The crop box is the region "to which the contents of the page shall be
|
|
115
|
+
* clipped", and a page cropped to less than its sheet is generally a page with
|
|
116
|
+
* something outside the crop: freeculture.pdf carries a printer's slug —
|
|
117
|
+
* "14773_07_347-348_r4jm.qxd 2/10/04 4:45 PM Page 347" — in the 42 points the
|
|
118
|
+
* crop cuts off the top, and lifted with the rest it stood at the head of the
|
|
119
|
+
* page and pushed the sheet taller to hold it.
|
|
120
|
+
*/
|
|
121
|
+
function shows(d, minX, minY, maxX, maxY) {
|
|
122
|
+
return maxX > 0 && minX < d.width && maxY > 0 && minY < d.height;
|
|
70
123
|
}
|
|
71
124
|
/**
|
|
72
125
|
* The same pictures, placed on the shown page. A quarter turn swaps a picture's
|
|
@@ -89,14 +142,16 @@ function placeImages(images, d) {
|
|
|
89
142
|
const ys = corners.map((c) => c.y);
|
|
90
143
|
const minX = Math.min(...xs);
|
|
91
144
|
const minY = Math.min(...ys);
|
|
145
|
+
const turned = (img.rotationDeg ?? 0) + d.turnDeg;
|
|
92
146
|
return {
|
|
93
147
|
...img,
|
|
94
148
|
x: minX,
|
|
95
149
|
y: minY,
|
|
96
150
|
widthPt: Math.max(...xs) - minX,
|
|
97
|
-
heightPt: Math.max(...ys) - minY
|
|
151
|
+
heightPt: Math.max(...ys) - minY,
|
|
152
|
+
...Math.abs(turned) > .5 ? { rotationDeg: turned } : {}
|
|
98
153
|
};
|
|
99
|
-
});
|
|
154
|
+
}).filter((img) => shows(d, img.x, img.y, img.x + img.widthPt, img.y + img.heightPt));
|
|
100
155
|
}
|
|
101
156
|
/**
|
|
102
157
|
* The same paths, placed on the shown page. A path is geometry, so every point
|
|
@@ -156,7 +211,7 @@ function placeVectors(vectors, d) {
|
|
|
156
211
|
maxX: Math.max(...xs),
|
|
157
212
|
maxY: Math.max(...ys)
|
|
158
213
|
};
|
|
159
|
-
});
|
|
214
|
+
}).filter((v) => shows(d, v.minX, v.minY, v.maxX, v.maxY));
|
|
160
215
|
}
|
|
161
216
|
//#endregion
|
|
162
217
|
export { displayOf, placeImages, placeRuns, placeVectors };
|
|
@@ -5,6 +5,12 @@ export type Rectangle = readonly [number, number, number, number];
|
|
|
5
5
|
export interface PdfPage {
|
|
6
6
|
readonly dict: PdfDict;
|
|
7
7
|
readonly mediaBox: Rectangle;
|
|
8
|
+
/**
|
|
9
|
+
* §14.11.2 `/CropBox` — the region of the page a viewer SHOWS, which is what
|
|
10
|
+
* a page's size means to anyone looking at it. Defaults to the media box and
|
|
11
|
+
* is clipped to it; inherited down the page tree like the media box is.
|
|
12
|
+
*/
|
|
13
|
+
readonly cropBox: Rectangle;
|
|
8
14
|
/**
|
|
9
15
|
* §14.11.1 `/Rotate` — how far the page turns CLOCKWISE when it is shown,
|
|
10
16
|
* normalised to 0, 90, 180 or 270 and inherited down the page tree.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { PDF_NULL, PdfName, PdfRef, PdfStream } from "../pdf/objects.js";
|
|
2
|
+
import { lzwDecodeMsb } from "../core/lzw.js";
|
|
2
3
|
import { buildDecryptor } from "./decrypt.js";
|
|
3
4
|
import { Lexer } from "./lexer.js";
|
|
4
5
|
import { parseIndirectObject, parseObject } from "./parser.js";
|
|
5
6
|
import { reversePredictor } from "./predictor.js";
|
|
7
|
+
import { ascii85Decode, asciiHexDecode, runLengthDecode } from "./stream-filters.js";
|
|
6
8
|
import { unzlibSync } from "fflate";
|
|
7
9
|
//#region src/pdf-reader/document.ts
|
|
8
10
|
var DEFAULT_MEDIA_BOX = [
|
|
@@ -162,6 +164,7 @@ var PdfFile = class PdfFile {
|
|
|
162
164
|
if (out.length >= MAX_PAGES || seen.has(node)) return;
|
|
163
165
|
seen.add(node);
|
|
164
166
|
const mediaBox = readRectangle(this.get(node, "MediaBox")) ?? inherited.mediaBox;
|
|
167
|
+
const cropBox = readRectangle(this.get(node, "CropBox")) ?? inherited.cropBox;
|
|
165
168
|
const resourcesVal = this.get(node, "Resources");
|
|
166
169
|
const resources = resourcesVal instanceof Map ? resourcesVal : inherited.resources;
|
|
167
170
|
const rotate = readRotate(this.get(node, "Rotate")) ?? inherited.rotate;
|
|
@@ -172,6 +175,7 @@ var PdfFile = class PdfFile {
|
|
|
172
175
|
const kidNode = this.resolve(kid);
|
|
173
176
|
if (kidNode instanceof Map) this.walkPageTree(kidNode, {
|
|
174
177
|
mediaBox,
|
|
178
|
+
cropBox,
|
|
175
179
|
resources,
|
|
176
180
|
rotate
|
|
177
181
|
}, out, seen);
|
|
@@ -179,9 +183,11 @@ var PdfFile = class PdfFile {
|
|
|
179
183
|
}
|
|
180
184
|
return;
|
|
181
185
|
}
|
|
186
|
+
const media = mediaBox ?? DEFAULT_MEDIA_BOX;
|
|
182
187
|
out.push({
|
|
183
188
|
dict: node,
|
|
184
|
-
mediaBox:
|
|
189
|
+
mediaBox: media,
|
|
190
|
+
cropBox: shownBox(media, cropBox),
|
|
185
191
|
rotate: rotate ?? 0,
|
|
186
192
|
resources
|
|
187
193
|
});
|
|
@@ -215,7 +221,19 @@ var PdfFile = class PdfFile {
|
|
|
215
221
|
data = unzlibSync(data);
|
|
216
222
|
flate = true;
|
|
217
223
|
} catch {}
|
|
218
|
-
else if (
|
|
224
|
+
else if (GENERAL_FILTERS[f.value]) {
|
|
225
|
+
data = GENERAL_FILTERS[f.value](data);
|
|
226
|
+
flate = true;
|
|
227
|
+
} else if (f.value === "LZWDecode" || f.value === "LZW") {
|
|
228
|
+
const parms = this.resolve(stream.dict.get("DecodeParms") ?? PDF_NULL);
|
|
229
|
+
const p = Array.isArray(parms) ? this.resolve(parms[0] ?? PDF_NULL) : parms;
|
|
230
|
+
const early = p instanceof Map ? this.resolve(p.get("EarlyChange") ?? PDF_NULL) : void 0;
|
|
231
|
+
data = lzwDecodeMsb(data, {
|
|
232
|
+
earlyChange: early === 0 ? 0 : 1,
|
|
233
|
+
limit: MAX_LZW_OUT
|
|
234
|
+
});
|
|
235
|
+
flate = true;
|
|
236
|
+
} else if (this.filters[f.value]) {
|
|
219
237
|
const decoded = runFilter(this.filters[f.value], data);
|
|
220
238
|
if (decoded) data = decoded;
|
|
221
239
|
else this.unknownFilters.add(f.value);
|
|
@@ -236,15 +254,18 @@ function runFilter(filter, data) {
|
|
|
236
254
|
return;
|
|
237
255
|
}
|
|
238
256
|
}
|
|
257
|
+
/** A decompressed LZW stream larger than this is a bomb, not a document. */
|
|
258
|
+
var MAX_LZW_OUT = 1 << 28;
|
|
259
|
+
/** §7.4.2–7.4.5 — the filters that are transport, whatever the stream holds. */
|
|
260
|
+
var GENERAL_FILTERS = {
|
|
261
|
+
ASCIIHexDecode: asciiHexDecode,
|
|
262
|
+
AHx: asciiHexDecode,
|
|
263
|
+
ASCII85Decode: ascii85Decode,
|
|
264
|
+
A85: ascii85Decode,
|
|
265
|
+
RunLengthDecode: runLengthDecode,
|
|
266
|
+
RL: runLengthDecode
|
|
267
|
+
};
|
|
239
268
|
var PASSTHROUGH_FILTERS = new Set([
|
|
240
|
-
"LZWDecode",
|
|
241
|
-
"LZW",
|
|
242
|
-
"RunLengthDecode",
|
|
243
|
-
"RL",
|
|
244
|
-
"ASCII85Decode",
|
|
245
|
-
"A85",
|
|
246
|
-
"ASCIIHexDecode",
|
|
247
|
-
"AHx",
|
|
248
269
|
"DCTDecode",
|
|
249
270
|
"DCT",
|
|
250
271
|
"JPXDecode",
|
|
@@ -526,5 +547,29 @@ function lastIndexOfAscii(buf, needle) {
|
|
|
526
547
|
}
|
|
527
548
|
return -1;
|
|
528
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* §14.11.2 — the box a viewer SHOWS: the crop box where there is one, clipped
|
|
552
|
+
* to the media box, and the media box where there is not.
|
|
553
|
+
*
|
|
554
|
+
* A crop box that states nothing usable — zero-sized, or outside the sheet
|
|
555
|
+
* altogether — is a crop box no viewer honours, and the media box stands.
|
|
556
|
+
* bug1844576.pdf is 612×792 of paper cropped to 181×53.75, and read as its
|
|
557
|
+
* media box the form on it arrived a fifth of the way down a letter page.
|
|
558
|
+
*/
|
|
559
|
+
function shownBox(media, crop) {
|
|
560
|
+
if (!crop) return media;
|
|
561
|
+
const [mx0, my0, mx1, my1] = media;
|
|
562
|
+
const [cx0, cy0, cx1, cy1] = crop;
|
|
563
|
+
const x0 = Math.max(Math.min(mx0, mx1), Math.min(cx0, cx1));
|
|
564
|
+
const y0 = Math.max(Math.min(my0, my1), Math.min(cy0, cy1));
|
|
565
|
+
const x1 = Math.min(Math.max(mx0, mx1), Math.max(cx0, cx1));
|
|
566
|
+
const y1 = Math.min(Math.max(my0, my1), Math.max(cy0, cy1));
|
|
567
|
+
return x1 - x0 > 1 && y1 - y0 > 1 ? [
|
|
568
|
+
x0,
|
|
569
|
+
y0,
|
|
570
|
+
x1,
|
|
571
|
+
y1
|
|
572
|
+
] : media;
|
|
573
|
+
}
|
|
529
574
|
//#endregion
|
|
530
575
|
export { PdfFile };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PdfDict } from '../pdf/objects.js';
|
|
2
2
|
import { PdfFile, PdfPage } from './document.js';
|
|
3
|
+
import { Loss } from '../core/ir/index.js';
|
|
3
4
|
import { FontRegistry } from '../core/font/index.js';
|
|
4
5
|
/**
|
|
5
6
|
* Every embedded TrueType program the pages use, by the name a run will ask for
|
|
@@ -15,11 +16,19 @@ import { FontRegistry } from '../core/font/index.js';
|
|
|
15
16
|
* (`/FontFile`) are different formats that the layout's parser does not take;
|
|
16
17
|
* those keep their substitute.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* And only one whose OUTLINES this pipeline can carry: a program with no
|
|
20
|
+
* `glyf`/`loca` cannot be subset, and a face offered here is one the writer
|
|
21
|
+
* will be asked to embed. bug1186827.pdf ships an OpenType/CFF program under
|
|
22
|
+
* `/FontFile2`, which parses like any other and then killed the whole
|
|
23
|
+
* conversion — "Subsetting requires a TrueType font with glyf+loca tables" —
|
|
24
|
+
* rather than losing one face.
|
|
25
|
+
*
|
|
26
|
+
* @param file The owning file.
|
|
27
|
+
* @param pages The pages whose fonts are wanted.
|
|
28
|
+
* @param losses Appended to for a face the page embeds and this cannot carry.
|
|
20
29
|
* @returns Name → a one-face registry holding that program.
|
|
21
30
|
*/
|
|
22
|
-
export declare function collectEmbeddedFonts(file: PdfFile, pages: ReadonlyArray<PdfPage>): Map<string, FontRegistry>;
|
|
31
|
+
export declare function collectEmbeddedFonts(file: PdfFile, pages: ReadonlyArray<PdfPage>, losses?: Array<Loss>): Map<string, FontRegistry>;
|
|
23
32
|
/**
|
|
24
33
|
* The name a run set in `fontDict` will ask for: its `/BaseFont` without the
|
|
25
34
|
* six-capital subset prefix (§9.6.4), lowercased.
|
|
@@ -29,3 +38,14 @@ export declare function collectEmbeddedFonts(file: PdfFile, pages: ReadonlyArray
|
|
|
29
38
|
* @returns The name, or `undefined` when the font states none.
|
|
30
39
|
*/
|
|
31
40
|
export declare function embeddedFontName(file: PdfFile, fontDict: PdfDict): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Whether this font carries a program this pipeline can set the page in.
|
|
43
|
+
*
|
|
44
|
+
* Only `/FontFile2` is liftable, and a run in such a face is filed under the
|
|
45
|
+
* face's own name — so that name must be left exactly as it is. Every other
|
|
46
|
+
* face is SUBSTITUTED, and its name is free to say what it is.
|
|
47
|
+
*
|
|
48
|
+
* @param file The document.
|
|
49
|
+
* @param fontDict The font dictionary.
|
|
50
|
+
*/
|
|
51
|
+
export declare function hasLiftableProgram(file: PdfFile, fontDict: PdfDict): boolean;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { parseTtf } from "../core/font/ttf-parser.js";
|
|
1
2
|
import { FontRegistry } from "../core/font/font-registry.js";
|
|
2
3
|
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
4
|
+
import { FEATURES } from "../core/ir/features.js";
|
|
3
5
|
//#region src/pdf-reader/embedded-fonts.ts
|
|
4
6
|
var MAX_FORM_DEPTH = 8;
|
|
5
7
|
/**
|
|
@@ -16,11 +18,19 @@ var MAX_FORM_DEPTH = 8;
|
|
|
16
18
|
* (`/FontFile`) are different formats that the layout's parser does not take;
|
|
17
19
|
* those keep their substitute.
|
|
18
20
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
+
* And only one whose OUTLINES this pipeline can carry: a program with no
|
|
22
|
+
* `glyf`/`loca` cannot be subset, and a face offered here is one the writer
|
|
23
|
+
* will be asked to embed. bug1186827.pdf ships an OpenType/CFF program under
|
|
24
|
+
* `/FontFile2`, which parses like any other and then killed the whole
|
|
25
|
+
* conversion — "Subsetting requires a TrueType font with glyf+loca tables" —
|
|
26
|
+
* rather than losing one face.
|
|
27
|
+
*
|
|
28
|
+
* @param file The owning file.
|
|
29
|
+
* @param pages The pages whose fonts are wanted.
|
|
30
|
+
* @param losses Appended to for a face the page embeds and this cannot carry.
|
|
21
31
|
* @returns Name → a one-face registry holding that program.
|
|
22
32
|
*/
|
|
23
|
-
function collectEmbeddedFonts(file, pages) {
|
|
33
|
+
function collectEmbeddedFonts(file, pages, losses) {
|
|
24
34
|
const out = /* @__PURE__ */ new Map();
|
|
25
35
|
const seen = /* @__PURE__ */ new Set();
|
|
26
36
|
const visiting = /* @__PURE__ */ new Set();
|
|
@@ -32,6 +42,15 @@ function collectEmbeddedFonts(file, pages) {
|
|
|
32
42
|
const program = fontProgram(file, fontDict);
|
|
33
43
|
if (!program) return;
|
|
34
44
|
try {
|
|
45
|
+
const parsed = parseTtf(program);
|
|
46
|
+
if (!parsed.tables.has("glyf") || !parsed.tables.has("loca")) {
|
|
47
|
+
losses?.push({
|
|
48
|
+
severity: "degraded",
|
|
49
|
+
feature: FEATURES.text,
|
|
50
|
+
detail: `embedded font ${name} has no TrueType outlines (a CFF program under /FontFile2); its text is re-set in a substitute face`
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
35
54
|
out.set(name, FontRegistry.fromBytes({ regular: program }));
|
|
36
55
|
} catch {}
|
|
37
56
|
};
|
|
@@ -73,6 +92,19 @@ function embeddedFontName(file, fontDict) {
|
|
|
73
92
|
const name = base.value.replace(/^[A-Z]{6}\+/u, "").trim();
|
|
74
93
|
return name.length > 0 ? name.toLowerCase() : void 0;
|
|
75
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Whether this font carries a program this pipeline can set the page in.
|
|
97
|
+
*
|
|
98
|
+
* Only `/FontFile2` is liftable, and a run in such a face is filed under the
|
|
99
|
+
* face's own name — so that name must be left exactly as it is. Every other
|
|
100
|
+
* face is SUBSTITUTED, and its name is free to say what it is.
|
|
101
|
+
*
|
|
102
|
+
* @param file The document.
|
|
103
|
+
* @param fontDict The font dictionary.
|
|
104
|
+
*/
|
|
105
|
+
function hasLiftableProgram(file, fontDict) {
|
|
106
|
+
return fontProgram(file, fontDict) !== void 0;
|
|
107
|
+
}
|
|
76
108
|
/** §9.9 `/FontFile2` — the TrueType program, off the font or its descendant. */
|
|
77
109
|
function fontProgram(file, fontDict) {
|
|
78
110
|
const owner = descendant(file, fontDict) ?? fontDict;
|
|
@@ -91,4 +123,4 @@ function descendant(file, fontDict) {
|
|
|
91
123
|
return first instanceof Map ? first : void 0;
|
|
92
124
|
}
|
|
93
125
|
//#endregion
|
|
94
|
-
export { collectEmbeddedFonts, embeddedFontName };
|
|
126
|
+
export { collectEmbeddedFonts, embeddedFontName, hasLiftableProgram };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** One of the base encodings this reader has a table for (Annex D.2). */
|
|
2
|
+
export type BaseEncodingName = 'StandardEncoding' | 'WinAnsiEncoding' | 'MacRomanEncoding';
|
|
3
|
+
/**
|
|
4
|
+
* The glyph names of a base encoding, or `undefined` for a name this has no
|
|
5
|
+
* table for — `/MacExpertEncoding`, whose repertory no reader here can set.
|
|
6
|
+
*
|
|
7
|
+
* @param name The `/Encoding` or `/BaseEncoding` name, without the slash.
|
|
8
|
+
*/
|
|
9
|
+
export declare function baseEncodingTable(name: string): ReadonlyMap<number, string> | undefined;
|
|
10
|
+
/** The encoding built into the standard Latin text faces (§9.6.2.2). */
|
|
11
|
+
export declare function standardEncodingTable(): ReadonlyMap<number, string>;
|
|
12
|
+
/**
|
|
13
|
+
* The name a `post` table's glyph-name index below 258 stands for.
|
|
14
|
+
*
|
|
15
|
+
* @param index The index as the table states it.
|
|
16
|
+
* @returns The name, or `undefined` for an index outside the standard order.
|
|
17
|
+
*/
|
|
18
|
+
export declare function macGlyphName(index: number): string | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a `/BaseFont` names one of the standard Latin faces, whose built-in
|
|
21
|
+
* encoding is StandardEncoding.
|
|
22
|
+
*
|
|
23
|
+
* @param baseFont The name as the font dictionary states it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isStandardLatinFace(baseFont: string): boolean;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
//#region src/pdf-reader/encodings.ts
|
|
2
|
+
/**
|
|
3
|
+
* Code → glyph name, from names read in code order. `.` marks a code the
|
|
4
|
+
* encoding leaves unused, and the rows are the spec's own — sixteen to a line,
|
|
5
|
+
* so a row can be checked against Annex D at a glance.
|
|
6
|
+
*/
|
|
7
|
+
function table(start, ...rows) {
|
|
8
|
+
const out = /* @__PURE__ */ new Map();
|
|
9
|
+
let code = start;
|
|
10
|
+
for (const row of rows) for (const name of row.split(/\s+/u).filter(Boolean)) {
|
|
11
|
+
if (name !== ".") out.set(code, name);
|
|
12
|
+
code++;
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
var ASCII = table(32, "space exclam quotedbl numbersign dollar percent ampersand quotesingle parenleft parenright asterisk plus comma hyphen period slash", "zero one two three four five six seven eight nine colon semicolon less equal greater question", "at A B C D E F G H I J K L M N O", "P Q R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore", "grave a b c d e f g h i j k l m n o", "p q r s t u v w x y z braceleft bar braceright asciitilde .");
|
|
17
|
+
/** Annex D.2 STD — the built-in encoding of the standard Latin text faces. */
|
|
18
|
+
var STANDARD = new Map([
|
|
19
|
+
...ASCII,
|
|
20
|
+
...table(39, "quoteright"),
|
|
21
|
+
...table(96, "quoteleft"),
|
|
22
|
+
...table(160, ". exclamdown cent sterling fraction yen florin section currency quotesingle quotedblleft guillemotleft guilsinglleft guilsinglright fi fl", ". endash dagger daggerdbl periodcentered . paragraph bullet quotesinglbase quotedblbase quotedblright guillemotright ellipsis perthousand . questiondown", ". grave acute circumflex tilde macron breve dotaccent dieresis . ring cedilla . hungarumlaut ogonek caron", "emdash . . . . . . . . . . . . . . .", ". AE . ordfeminine . . . . Lslash Oslash OE ordmasculine . . . .", ". ae . . . dotlessi . . lslash oslash oe germandbls . . . .")
|
|
23
|
+
]);
|
|
24
|
+
var TABLES = {
|
|
25
|
+
StandardEncoding: STANDARD,
|
|
26
|
+
WinAnsiEncoding: new Map([...ASCII, ...table(128, "Euro . quotesinglbase florin quotedblbase ellipsis dagger daggerdbl circumflex perthousand Scaron guilsinglleft OE . Zcaron .", ". quoteleft quoteright quotedblleft quotedblright bullet endash emdash tilde trademark scaron guilsinglright oe . zcaron Ydieresis")]),
|
|
27
|
+
MacRomanEncoding: new Map([...ASCII, ...table(128, "Adieresis Aring Ccedilla Eacute Ntilde Odieresis Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave", "ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve ocircumflex odieresis otilde uacute ugrave ucircumflex udieresis", "dagger degree cent sterling section bullet paragraph germandbls registered copyright trademark acute dieresis notequal AE Oslash", "infinity plusminus lessequal greaterequal yen mu partialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash", "questiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft guillemotright ellipsis space Agrave Atilde Otilde OE oe", "endash emdash quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis fraction currency guilsinglleft guilsinglright fi fl", "daggerdbl periodcentered quotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex", ". Ograve Uacute Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla hungarumlaut ogonek caron")])
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The glyph names of a base encoding, or `undefined` for a name this has no
|
|
31
|
+
* table for — `/MacExpertEncoding`, whose repertory no reader here can set.
|
|
32
|
+
*
|
|
33
|
+
* @param name The `/Encoding` or `/BaseEncoding` name, without the slash.
|
|
34
|
+
*/
|
|
35
|
+
function baseEncodingTable(name) {
|
|
36
|
+
return name in TABLES ? TABLES[name] : void 0;
|
|
37
|
+
}
|
|
38
|
+
/** The encoding built into the standard Latin text faces (§9.6.2.2). */
|
|
39
|
+
function standardEncodingTable() {
|
|
40
|
+
return STANDARD;
|
|
41
|
+
}
|
|
42
|
+
var STANDARD_LATIN_FACES = new Set([
|
|
43
|
+
"courier",
|
|
44
|
+
"courier-bold",
|
|
45
|
+
"courier-oblique",
|
|
46
|
+
"courier-boldoblique",
|
|
47
|
+
"helvetica",
|
|
48
|
+
"helvetica-bold",
|
|
49
|
+
"helvetica-oblique",
|
|
50
|
+
"helvetica-boldoblique",
|
|
51
|
+
"times-roman",
|
|
52
|
+
"times-bold",
|
|
53
|
+
"times-italic",
|
|
54
|
+
"times-bolditalic"
|
|
55
|
+
]);
|
|
56
|
+
var MAC_ORDER = `
|
|
57
|
+
.notdef .null nonmarkingreturn space exclam quotedbl numbersign dollar
|
|
58
|
+
percent ampersand quotesingle parenleft parenright asterisk plus comma
|
|
59
|
+
hyphen period slash zero one two three four
|
|
60
|
+
five six seven eight nine colon semicolon less
|
|
61
|
+
equal greater question at A B C D
|
|
62
|
+
E F G H I J K L
|
|
63
|
+
M N O P Q R S T
|
|
64
|
+
U V W X Y Z bracketleft backslash
|
|
65
|
+
bracketright asciicircum underscore grave a b c d
|
|
66
|
+
e f g h i j k l
|
|
67
|
+
m n o p q r s t
|
|
68
|
+
u v w x y z braceleft bar
|
|
69
|
+
braceright asciitilde Adieresis Aring Ccedilla Eacute Ntilde Odieresis
|
|
70
|
+
Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla
|
|
71
|
+
eacute egrave ecircumflex edieresis iacute igrave icircumflex idieresis
|
|
72
|
+
ntilde oacute ograve ocircumflex odieresis otilde uacute ugrave
|
|
73
|
+
ucircumflex udieresis dagger degree cent sterling section bullet
|
|
74
|
+
paragraph germandbls registered copyright trademark acute dieresis notequal
|
|
75
|
+
AE Oslash infinity plusminus lessequal greaterequal yen mu
|
|
76
|
+
partialdiff summation product pi integral ordfeminine ordmasculine Omega
|
|
77
|
+
ae oslash questiondown exclamdown logicalnot radical florin approxequal
|
|
78
|
+
Delta guillemotleft guillemotright ellipsis nonbreakingspace Agrave Atilde Otilde
|
|
79
|
+
OE oe endash emdash quotedblleft quotedblright quoteleft quoteright
|
|
80
|
+
divide lozenge ydieresis Ydieresis fraction currency guilsinglleft guilsinglright
|
|
81
|
+
fi fl daggerdbl periodcentered quotesinglbase quotedblbase perthousand Acircumflex
|
|
82
|
+
Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave
|
|
83
|
+
Oacute Ocircumflex apple Ograve Uacute Ucircumflex Ugrave dotlessi
|
|
84
|
+
circumflex tilde macron breve dotaccent ring cedilla hungarumlaut
|
|
85
|
+
ogonek caron Lslash lslash Scaron scaron Zcaron zcaron
|
|
86
|
+
brokenbar Eth eth Yacute yacute Thorn thorn minus
|
|
87
|
+
multiply onesuperior twosuperior threesuperior onehalf onequarter threequarters franc
|
|
88
|
+
Gbreve gbreve Idotaccent Scedilla scedilla Cacute cacute Ccaron
|
|
89
|
+
ccaron dcroat
|
|
90
|
+
`.split(/\s+/u).filter(Boolean);
|
|
91
|
+
/**
|
|
92
|
+
* The name a `post` table's glyph-name index below 258 stands for.
|
|
93
|
+
*
|
|
94
|
+
* @param index The index as the table states it.
|
|
95
|
+
* @returns The name, or `undefined` for an index outside the standard order.
|
|
96
|
+
*/
|
|
97
|
+
function macGlyphName(index) {
|
|
98
|
+
return MAC_ORDER[index];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether a `/BaseFont` names one of the standard Latin faces, whose built-in
|
|
102
|
+
* encoding is StandardEncoding.
|
|
103
|
+
*
|
|
104
|
+
* @param baseFont The name as the font dictionary states it.
|
|
105
|
+
*/
|
|
106
|
+
function isStandardLatinFace(baseFont) {
|
|
107
|
+
return STANDARD_LATIN_FACES.has(baseFont.trim().replace(/^[A-Za-z]{6}\+/u, "").toLowerCase());
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { BodyElement, SectionProperties, TextOutline } from '../core/document-model/index.js';
|
|
1
|
+
import { BodyElement, ParagraphProperties, Section, SectionProperties, TextOutline } from '../core/document-model/index.js';
|
|
2
2
|
import { FlowDoc } from '../core/ir/flow.js';
|
|
3
3
|
import { FontRegistry } from '../core/font/index.js';
|
|
4
4
|
import { Loss, ResourceStore } from '../core/ir/index.js';
|
|
5
5
|
import { PdfImage } from './images.js';
|
|
6
6
|
import { PdfPage } from './document.js';
|
|
7
7
|
import { PdfVector } from './vector.js';
|
|
8
|
+
import { TextMarkup } from './annot-draw.js';
|
|
9
|
+
import { TextRun } from './content.js';
|
|
8
10
|
/**
|
|
9
11
|
* A reconstruction's document plus the losses incurred reading it (e.g. an
|
|
10
12
|
* undecodable image colour space) — surfaced through the reader's `LossReport`.
|
|
@@ -49,6 +51,14 @@ export interface TextSpan {
|
|
|
49
51
|
readonly bold?: boolean;
|
|
50
52
|
/** §9.8.1 — the face was a slanted one. */
|
|
51
53
|
readonly italic?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* §17.3.2.42 — the glyphs stood OFF the baseline the line is set on, and
|
|
56
|
+
* smaller: a footnote mark, an exponent, an index. The page states it by
|
|
57
|
+
* placement, and a document by the property.
|
|
58
|
+
*/
|
|
59
|
+
readonly script?: 'superscript' | 'subscript';
|
|
60
|
+
/** §12.5.6.10 — a text-markup annotation marks these words. */
|
|
61
|
+
readonly markup?: TextMarkup;
|
|
52
62
|
}
|
|
53
63
|
/**
|
|
54
64
|
* Build a paragraph {@link BodyElement} from positioned {@link TextSpan}s,
|
|
@@ -56,13 +66,13 @@ export interface TextSpan {
|
|
|
56
66
|
* survives as its own run) and squashing whitespace. With no hrefs this
|
|
57
67
|
* collapses to a single run — the same shape {@link paragraphBlock} produces.
|
|
58
68
|
*/
|
|
59
|
-
export declare function paragraphFromRuns(spans: ReadonlyArray<TextSpan>, outlineLevel?: number): BodyElement;
|
|
69
|
+
export declare function paragraphFromRuns(spans: ReadonlyArray<TextSpan>, outlineLevel?: number, placement?: Pick<ParagraphProperties, 'alignment' | 'spacingBefore' | 'indentLeft' | 'indentFirstLine'>): BodyElement;
|
|
60
70
|
/**
|
|
61
71
|
* Store a {@link PdfImage}'s bytes (content-addressed dedup) and build the image
|
|
62
72
|
* {@link BodyElement} that references them, sized in points from the placement
|
|
63
73
|
* CTM. `alt` becomes the block's alt text when given.
|
|
64
74
|
*/
|
|
65
|
-
export declare function imageBlock(image: PdfImage, resources: ResourceStore, alt?: string, frame?: PageFrame, zOrder?: number): BodyElement;
|
|
75
|
+
export declare function imageBlock(image: PdfImage, resources: ResourceStore, alt?: string, frame?: PageFrame, zOrder?: number, behind?: boolean): BodyElement;
|
|
66
76
|
/**
|
|
67
77
|
* A line of text as an anchored box, standing where the page set it.
|
|
68
78
|
*
|
|
@@ -100,7 +110,7 @@ export declare function dedupeLosses(losses: ReadonlyArray<Loss>): Array<Loss>;
|
|
|
100
110
|
* a paragraph: 22060_A1_01_Plans.pdf is one A3 sheet of vectors, and stacking
|
|
101
111
|
* its forty-nine paths one under another spilled it onto a second page.
|
|
102
112
|
*/
|
|
103
|
-
export declare function shapeBlock(v: PdfVector, frame?: PageFrame, zOrder?: number): BodyElement;
|
|
113
|
+
export declare function shapeBlock(v: PdfVector, frame?: PageFrame, zOrder?: number, behind?: boolean): BodyElement;
|
|
104
114
|
/**
|
|
105
115
|
* Derive the {@link SectionProperties} geometry from the source pages so a
|
|
106
116
|
* reconstructed PDF re-renders at its real page size and orientation rather than
|
|
@@ -113,6 +123,10 @@ export declare function shapeBlock(v: PdfVector, frame?: PageFrame, zOrder?: num
|
|
|
113
123
|
* `A4`. Returns `undefined` when there is no usable first-page box.
|
|
114
124
|
*/
|
|
115
125
|
export declare function sectionFromPdfPages(pages: ReadonlyArray<PdfPage>): SectionProperties | undefined;
|
|
126
|
+
export declare function withMeasuredMargins(section: SectionProperties | undefined, shown: ReadonlyArray<{
|
|
127
|
+
width: number;
|
|
128
|
+
height: number;
|
|
129
|
+
}>, pageRuns: ReadonlyArray<ReadonlyArray<TextRun>>): SectionProperties | undefined;
|
|
116
130
|
/**
|
|
117
131
|
* Assemble the final {@link FlowDoc} for a reconstruction: the body elements
|
|
118
132
|
* with their styles resolved against the empty style sheet, the lifted-image
|
|
@@ -120,4 +134,4 @@ export declare function sectionFromPdfPages(pages: ReadonlyArray<PdfPage>): Sect
|
|
|
120
134
|
* both reconstruction paths (the tagged fast-path EP3 and the heuristic layout
|
|
121
135
|
* path EP4).
|
|
122
136
|
*/
|
|
123
|
-
export declare function buildFlowDoc(body: ReadonlyArray<BodyElement>, resources?: ResourceStore, section?: SectionProperties, embeddedFonts?: ReadonlyMap<string, FontRegistry
|
|
137
|
+
export declare function buildFlowDoc(body: ReadonlyArray<BodyElement>, resources?: ResourceStore, section?: SectionProperties, embeddedFonts?: ReadonlyMap<string, FontRegistry>, sections?: ReadonlyArray<Section>, headersFooters?: ReadonlyMap<string, ReadonlyArray<BodyElement>>): FlowDoc;
|