reamkit 1.27.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 +6 -5
- 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.js +114 -1
- package/dist/esm/pdf-reader/annots.d.ts +0 -14
- package/dist/esm/pdf-reader/annots.js +13 -1
- package/dist/esm/pdf-reader/ccitt.d.ts +2 -3
- package/dist/esm/pdf-reader/ccitt.js +32 -4
- 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/cmap.js +5 -2
- package/dist/esm/pdf-reader/content.d.ts +76 -0
- package/dist/esm/pdf-reader/content.js +142 -59
- 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.js +41 -1
- package/dist/esm/pdf-reader/document.js +26 -9
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +11 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +14 -1
- 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 +9 -3
- package/dist/esm/pdf-reader/flow-build.js +13 -7
- package/dist/esm/pdf-reader/font.js +329 -8
- 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 +4 -3
- package/dist/esm/pdf-reader/image-decode.js +85 -64
- package/dist/esm/pdf-reader/images.d.ts +6 -0
- package/dist/esm/pdf-reader/images.js +50 -7
- package/dist/esm/pdf-reader/jbig2.js +104 -19
- package/dist/esm/pdf-reader/layout.js +1148 -45
- 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/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 -9
- package/dist/esm/pdf-reader/reader.js +34 -21
- package/dist/esm/pdf-reader/shading.d.ts +81 -2
- package/dist/esm/pdf-reader/shading.js +191 -25
- 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/text-rules.js +83 -10
- package/dist/esm/pdf-reader/text.js +82 -3
- 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.js +43 -4
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-writer.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
//#region src/pdf-reader/type1-outline.ts
|
|
2
|
+
/**
|
|
3
|
+
* Read an embedded Type 1 program.
|
|
4
|
+
*
|
|
5
|
+
* @param program The raw `/FontFile` bytes — the cleartext header, the
|
|
6
|
+
* `eexec`-encrypted body, and the trailing zeros.
|
|
7
|
+
* @returns A reader, or `undefined` where the program cannot be read.
|
|
8
|
+
*/
|
|
9
|
+
function type1Font(program) {
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
const read = parseType1(program);
|
|
13
|
+
if (!read) return void 0;
|
|
14
|
+
parsed = read;
|
|
15
|
+
} catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const cache = /* @__PURE__ */ new Map();
|
|
19
|
+
return {
|
|
20
|
+
path: (name) => {
|
|
21
|
+
const had = cache.get(name);
|
|
22
|
+
if (had !== void 0 || cache.has(name)) return had;
|
|
23
|
+
let out;
|
|
24
|
+
try {
|
|
25
|
+
const charstring = parsed.charstrings.get(name);
|
|
26
|
+
out = charstring ? runGlyph(parsed, charstring, 0) : void 0;
|
|
27
|
+
} catch {
|
|
28
|
+
out = void 0;
|
|
29
|
+
}
|
|
30
|
+
cache.set(name, out);
|
|
31
|
+
return out;
|
|
32
|
+
},
|
|
33
|
+
has: (name) => parsed.charstrings.has(name),
|
|
34
|
+
...parsed.encoding ? { encoding: parsed.encoding } : {}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function parseType1(program) {
|
|
38
|
+
const body = pfbSegments(program) ?? program;
|
|
39
|
+
const at = indexOf(body, "eexec");
|
|
40
|
+
if (at < 0) return void 0;
|
|
41
|
+
const clear = new TextDecoder("latin1").decode(body.subarray(0, at));
|
|
42
|
+
let from = at + 5;
|
|
43
|
+
while (from < body.length && isSpace(body[from])) from++;
|
|
44
|
+
const plain = eexec(looksHex(body, from) ? unhex(body, from) : body.subarray(from), EEXEC_R, 4);
|
|
45
|
+
const text = new TextDecoder("latin1").decode(plain);
|
|
46
|
+
const lenIV = readInt(text, "/lenIV") ?? 4;
|
|
47
|
+
const charstrings = readCharstrings(plain, text, lenIV);
|
|
48
|
+
if (charstrings.size === 0) return void 0;
|
|
49
|
+
const encoding = readEncoding(clear);
|
|
50
|
+
return {
|
|
51
|
+
charstrings,
|
|
52
|
+
subrs: readSubrs(plain, text, lenIV),
|
|
53
|
+
...encoding ? { encoding } : {},
|
|
54
|
+
scale: readMatrixScale(clear) ?? .001
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** The `eexec` key (§7), and the one every charstring is encrypted with (§6.2). */
|
|
58
|
+
var EEXEC_R = 55665;
|
|
59
|
+
var CHARSTRING_R = 4330;
|
|
60
|
+
/** §7 — the decryption both layers use, less the leading random bytes. */
|
|
61
|
+
function eexec(data, key, skip) {
|
|
62
|
+
let r = key;
|
|
63
|
+
const out = new Uint8Array(Math.max(0, data.length - skip));
|
|
64
|
+
for (let i = 0; i < data.length; i++) {
|
|
65
|
+
const c = data[i];
|
|
66
|
+
const p = c ^ r >> 8;
|
|
67
|
+
r = (c + r) * 52845 + 22719 & 65535;
|
|
68
|
+
if (i >= skip) out[i - skip] = p & 255;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A PFB wrapper: 0x80 0x01/0x02 then a four-byte little-endian length. The
|
|
74
|
+
* segments are the file with its lengths interleaved, so they are joined back.
|
|
75
|
+
*/
|
|
76
|
+
function pfbSegments(program) {
|
|
77
|
+
if (program[0] !== 128) return void 0;
|
|
78
|
+
const parts = [];
|
|
79
|
+
let at = 0;
|
|
80
|
+
while (at + 6 <= program.length && program[at] === 128 && program[at + 1] !== 3) {
|
|
81
|
+
const len = program[at + 2] | program[at + 3] << 8 | program[at + 4] << 16 | program[at + 5] << 24;
|
|
82
|
+
const from = at + 6;
|
|
83
|
+
if (len < 0 || from + len > program.length) break;
|
|
84
|
+
parts.push(program.subarray(from, from + len));
|
|
85
|
+
at = from + len;
|
|
86
|
+
}
|
|
87
|
+
if (parts.length === 0) return void 0;
|
|
88
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
89
|
+
let off = 0;
|
|
90
|
+
for (const p of parts) {
|
|
91
|
+
out.set(p, off);
|
|
92
|
+
off += p.length;
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
/** §10 — `/CharStrings n dict dup begin /name len RD <bytes> ND` for each glyph. */
|
|
97
|
+
function readCharstrings(plain, text, lenIV) {
|
|
98
|
+
const out = /* @__PURE__ */ new Map();
|
|
99
|
+
const start = text.indexOf("/CharStrings");
|
|
100
|
+
if (start < 0) return out;
|
|
101
|
+
const entry = /\/([^\s/{}()[\]<>]+)\s+(\d+)\s+(-\||RD|ND)[ ]/gu;
|
|
102
|
+
entry.lastIndex = start;
|
|
103
|
+
for (let m = entry.exec(text); m; m = entry.exec(text)) {
|
|
104
|
+
const length = Number(m[2]);
|
|
105
|
+
const from = m.index + m[0].length;
|
|
106
|
+
if (!Number.isFinite(length) || length <= 0 || from + length > plain.length) continue;
|
|
107
|
+
if (m[3] === "ND") continue;
|
|
108
|
+
if (!out.has(m[1])) out.set(m[1], eexec(plain.subarray(from, from + length), CHARSTRING_R, lenIV));
|
|
109
|
+
entry.lastIndex = from + length;
|
|
110
|
+
if (out.size >= MAX_GLYPHS) break;
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/** How many glyphs one program may hold — a guard, not a format limit. */
|
|
115
|
+
var MAX_GLYPHS = 5e3;
|
|
116
|
+
/** §8 — `/Subrs n array dup i len RD <bytes> NP`, called by index. */
|
|
117
|
+
function readSubrs(plain, text, lenIV) {
|
|
118
|
+
const out = [];
|
|
119
|
+
const start = text.indexOf("/Subrs");
|
|
120
|
+
if (start < 0) return out;
|
|
121
|
+
const entry = /dup\s+(\d+)\s+(\d+)\s+(-\||RD)[ ]/gu;
|
|
122
|
+
entry.lastIndex = start;
|
|
123
|
+
for (let m = entry.exec(text); m; m = entry.exec(text)) {
|
|
124
|
+
const index = Number(m[1]);
|
|
125
|
+
const length = Number(m[2]);
|
|
126
|
+
const from = m.index + m[0].length;
|
|
127
|
+
if (!Number.isFinite(length) || from + length > plain.length) break;
|
|
128
|
+
if (index >= 0 && index < MAX_GLYPHS) out[index] = eexec(plain.subarray(from, from + length), CHARSTRING_R, lenIV);
|
|
129
|
+
entry.lastIndex = from + length;
|
|
130
|
+
if (text.startsWith("/CharStrings", entry.lastIndex)) break;
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* §5 — the program's own `/Encoding`: either `StandardEncoding` by name, or an
|
|
136
|
+
* array built up with `dup <code> /<name> put`.
|
|
137
|
+
*/
|
|
138
|
+
function readEncoding(clear) {
|
|
139
|
+
const at = clear.indexOf("/Encoding");
|
|
140
|
+
if (at < 0) return void 0;
|
|
141
|
+
const out = /* @__PURE__ */ new Map();
|
|
142
|
+
const put = /dup\s+(\d+)\s*\/([^\s/{}()[\]<>]+)\s+put/gu;
|
|
143
|
+
put.lastIndex = at;
|
|
144
|
+
const end = clear.indexOf("readonly def", at);
|
|
145
|
+
for (let m = put.exec(clear); m; m = put.exec(clear)) {
|
|
146
|
+
if (end >= 0 && m.index > end) break;
|
|
147
|
+
const code = Number(m[1]);
|
|
148
|
+
if (Number.isFinite(code) && code >= 0 && code < 256) out.set(code, m[2]);
|
|
149
|
+
}
|
|
150
|
+
return out.size > 0 ? out : void 0;
|
|
151
|
+
}
|
|
152
|
+
/** §5 — `/FontMatrix [a b c d e f]`, whose `a` is the em this program states. */
|
|
153
|
+
function readMatrixScale(clear) {
|
|
154
|
+
const m = /\/FontMatrix\s*\[\s*([-\d.eE]+)/u.exec(clear);
|
|
155
|
+
const a = m ? Number(m[1]) : NaN;
|
|
156
|
+
return Number.isFinite(a) && a !== 0 ? a : void 0;
|
|
157
|
+
}
|
|
158
|
+
function readInt(text, key) {
|
|
159
|
+
const m = new RegExp(`${key}\\s+(-?\\d+)`, "u").exec(text);
|
|
160
|
+
const v = m ? Number(m[1]) : NaN;
|
|
161
|
+
return Number.isFinite(v) ? v : void 0;
|
|
162
|
+
}
|
|
163
|
+
function indexOf(data, needle) {
|
|
164
|
+
const bytes = [...needle].map((c) => c.charCodeAt(0));
|
|
165
|
+
outer: for (let i = 0; i + bytes.length <= data.length; i++) {
|
|
166
|
+
for (let k = 0; k < bytes.length; k++) if (data[i + k] !== bytes[k]) continue outer;
|
|
167
|
+
return i;
|
|
168
|
+
}
|
|
169
|
+
return -1;
|
|
170
|
+
}
|
|
171
|
+
function isSpace(b) {
|
|
172
|
+
return b === 32 || b === 10 || b === 13 || b === 9;
|
|
173
|
+
}
|
|
174
|
+
/** The ciphertext may be hexadecimal — four hex digits at the head give it away. */
|
|
175
|
+
function looksHex(data, from) {
|
|
176
|
+
const hex = (b) => b >= 48 && b <= 57 || b >= 65 && b <= 70 || b >= 97 && b <= 102;
|
|
177
|
+
return [
|
|
178
|
+
0,
|
|
179
|
+
1,
|
|
180
|
+
2,
|
|
181
|
+
3
|
|
182
|
+
].every((k) => hex(data[from + k] ?? 0));
|
|
183
|
+
}
|
|
184
|
+
function unhex(data, from) {
|
|
185
|
+
const out = [];
|
|
186
|
+
let half = -1;
|
|
187
|
+
for (let i = from; i < data.length; i++) {
|
|
188
|
+
const b = data[i];
|
|
189
|
+
let v = -1;
|
|
190
|
+
if (b >= 48 && b <= 57) v = b - 48;
|
|
191
|
+
else if (b >= 65 && b <= 70) v = b - 55;
|
|
192
|
+
else if (b >= 97 && b <= 102) v = b - 87;
|
|
193
|
+
else if (isSpace(b)) continue;
|
|
194
|
+
else break;
|
|
195
|
+
if (half < 0) half = v;
|
|
196
|
+
else {
|
|
197
|
+
out.push(half << 4 | v);
|
|
198
|
+
half = -1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return Uint8Array.from(out);
|
|
202
|
+
}
|
|
203
|
+
/** How deep a charstring may call, and how many operators it may run. */
|
|
204
|
+
var MAX_CALL_DEPTH = 10;
|
|
205
|
+
var MAX_STEPS = 65536;
|
|
206
|
+
function runGlyph(font, program, depth) {
|
|
207
|
+
const out = [];
|
|
208
|
+
const stack = [];
|
|
209
|
+
const ps = [];
|
|
210
|
+
const flex = [];
|
|
211
|
+
let inFlex = false;
|
|
212
|
+
let x = 0;
|
|
213
|
+
let y = 0;
|
|
214
|
+
let open = false;
|
|
215
|
+
let steps = 0;
|
|
216
|
+
const moveTo = () => {
|
|
217
|
+
if (inFlex) {
|
|
218
|
+
flex.push(x, y);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (open) out.push({ op: "close" });
|
|
222
|
+
out.push({
|
|
223
|
+
op: "move",
|
|
224
|
+
x,
|
|
225
|
+
y
|
|
226
|
+
});
|
|
227
|
+
open = true;
|
|
228
|
+
};
|
|
229
|
+
const curveTo = (x1, y1, x2, y2) => {
|
|
230
|
+
out.push({
|
|
231
|
+
op: "cubic",
|
|
232
|
+
x1,
|
|
233
|
+
y1,
|
|
234
|
+
x2,
|
|
235
|
+
y2,
|
|
236
|
+
x,
|
|
237
|
+
y
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
const run = (code, level) => {
|
|
241
|
+
for (let i = 0; i < code.length;) {
|
|
242
|
+
if (++steps > MAX_STEPS) return false;
|
|
243
|
+
const b = code[i++];
|
|
244
|
+
if (b >= 32) {
|
|
245
|
+
if (b <= 246) stack.push(b - 139);
|
|
246
|
+
else if (b <= 250) stack.push((b - 247) * 256 + (code[i++] ?? 0) + 108);
|
|
247
|
+
else if (b <= 254) stack.push(-(b - 251) * 256 - (code[i++] ?? 0) - 108);
|
|
248
|
+
else {
|
|
249
|
+
stack.push(code[i] << 24 | code[i + 1] << 16 | code[i + 2] << 8 | code[i + 3] | 0);
|
|
250
|
+
i += 4;
|
|
251
|
+
}
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
switch (b) {
|
|
255
|
+
case 13:
|
|
256
|
+
x = stack[0] ?? 0;
|
|
257
|
+
y = 0;
|
|
258
|
+
stack.length = 0;
|
|
259
|
+
break;
|
|
260
|
+
case 9:
|
|
261
|
+
if (open) out.push({ op: "close" });
|
|
262
|
+
open = false;
|
|
263
|
+
stack.length = 0;
|
|
264
|
+
break;
|
|
265
|
+
case 1:
|
|
266
|
+
case 3:
|
|
267
|
+
stack.length = 0;
|
|
268
|
+
break;
|
|
269
|
+
case 21:
|
|
270
|
+
x += stack[stack.length - 2] ?? 0;
|
|
271
|
+
y += stack[stack.length - 1] ?? 0;
|
|
272
|
+
moveTo();
|
|
273
|
+
stack.length = 0;
|
|
274
|
+
break;
|
|
275
|
+
case 22:
|
|
276
|
+
x += stack[stack.length - 1] ?? 0;
|
|
277
|
+
moveTo();
|
|
278
|
+
stack.length = 0;
|
|
279
|
+
break;
|
|
280
|
+
case 4:
|
|
281
|
+
y += stack[stack.length - 1] ?? 0;
|
|
282
|
+
moveTo();
|
|
283
|
+
stack.length = 0;
|
|
284
|
+
break;
|
|
285
|
+
case 5:
|
|
286
|
+
x += stack[0] ?? 0;
|
|
287
|
+
y += stack[1] ?? 0;
|
|
288
|
+
out.push({
|
|
289
|
+
op: "line",
|
|
290
|
+
x,
|
|
291
|
+
y
|
|
292
|
+
});
|
|
293
|
+
stack.length = 0;
|
|
294
|
+
break;
|
|
295
|
+
case 6:
|
|
296
|
+
x += stack[0] ?? 0;
|
|
297
|
+
out.push({
|
|
298
|
+
op: "line",
|
|
299
|
+
x,
|
|
300
|
+
y
|
|
301
|
+
});
|
|
302
|
+
stack.length = 0;
|
|
303
|
+
break;
|
|
304
|
+
case 7:
|
|
305
|
+
y += stack[0] ?? 0;
|
|
306
|
+
out.push({
|
|
307
|
+
op: "line",
|
|
308
|
+
x,
|
|
309
|
+
y
|
|
310
|
+
});
|
|
311
|
+
stack.length = 0;
|
|
312
|
+
break;
|
|
313
|
+
case 8: {
|
|
314
|
+
const x1 = x + (stack[0] ?? 0);
|
|
315
|
+
const y1 = y + (stack[1] ?? 0);
|
|
316
|
+
const x2 = x1 + (stack[2] ?? 0);
|
|
317
|
+
const y2 = y1 + (stack[3] ?? 0);
|
|
318
|
+
x = x2 + (stack[4] ?? 0);
|
|
319
|
+
y = y2 + (stack[5] ?? 0);
|
|
320
|
+
curveTo(x1, y1, x2, y2);
|
|
321
|
+
stack.length = 0;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case 30: {
|
|
325
|
+
const x1 = x;
|
|
326
|
+
const y1 = y + (stack[0] ?? 0);
|
|
327
|
+
const x2 = x1 + (stack[1] ?? 0);
|
|
328
|
+
const y2 = y1 + (stack[2] ?? 0);
|
|
329
|
+
x = x2 + (stack[3] ?? 0);
|
|
330
|
+
y = y2;
|
|
331
|
+
curveTo(x1, y1, x2, y2);
|
|
332
|
+
stack.length = 0;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 31: {
|
|
336
|
+
const x1 = x + (stack[0] ?? 0);
|
|
337
|
+
const y1 = y;
|
|
338
|
+
const x2 = x1 + (stack[1] ?? 0);
|
|
339
|
+
const y2 = y1 + (stack[2] ?? 0);
|
|
340
|
+
x = x2;
|
|
341
|
+
y = y2 + (stack[3] ?? 0);
|
|
342
|
+
curveTo(x1, y1, x2, y2);
|
|
343
|
+
stack.length = 0;
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
case 10: {
|
|
347
|
+
const index = stack.pop() ?? 0;
|
|
348
|
+
const sub = font.subrs[index];
|
|
349
|
+
if (sub && level < MAX_CALL_DEPTH && !run(sub, level + 1)) return false;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case 11: return true;
|
|
353
|
+
case 14:
|
|
354
|
+
if (open) out.push({ op: "close" });
|
|
355
|
+
open = false;
|
|
356
|
+
return false;
|
|
357
|
+
case 12: {
|
|
358
|
+
const op2 = code[i++];
|
|
359
|
+
if (op2 === 12) {
|
|
360
|
+
const den = stack.pop() ?? 1;
|
|
361
|
+
const num = stack.pop() ?? 0;
|
|
362
|
+
stack.push(den === 0 ? 0 : num / den);
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
if (op2 === 16) {
|
|
366
|
+
const which = stack.pop() ?? 0;
|
|
367
|
+
const count = stack.pop() ?? 0;
|
|
368
|
+
const args = stack.splice(Math.max(0, stack.length - count), count);
|
|
369
|
+
if (which === 1) {
|
|
370
|
+
inFlex = true;
|
|
371
|
+
flex.length = 0;
|
|
372
|
+
} else if (which === 0) {
|
|
373
|
+
inFlex = false;
|
|
374
|
+
if (flex.length >= 14) {
|
|
375
|
+
const p = flex.slice(2);
|
|
376
|
+
out.push({
|
|
377
|
+
op: "cubic",
|
|
378
|
+
x1: p[0],
|
|
379
|
+
y1: p[1],
|
|
380
|
+
x2: p[2],
|
|
381
|
+
y2: p[3],
|
|
382
|
+
x: p[4],
|
|
383
|
+
y: p[5]
|
|
384
|
+
});
|
|
385
|
+
out.push({
|
|
386
|
+
op: "cubic",
|
|
387
|
+
x1: p[6],
|
|
388
|
+
y1: p[7],
|
|
389
|
+
x2: p[8],
|
|
390
|
+
y2: p[9],
|
|
391
|
+
x: p[10],
|
|
392
|
+
y: p[11]
|
|
393
|
+
});
|
|
394
|
+
x = p[10];
|
|
395
|
+
y = p[11];
|
|
396
|
+
}
|
|
397
|
+
ps.push(y, x);
|
|
398
|
+
} else if (which === 3) ps.push(3);
|
|
399
|
+
else for (const a of args.reverse()) ps.push(a);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
if (op2 === 17) {
|
|
403
|
+
stack.push(ps.pop() ?? 0);
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
if (op2 === 6) {
|
|
407
|
+
const base = STANDARD_ENCODING[stack[3] ?? 0];
|
|
408
|
+
const charstring = base !== void 0 ? font.charstrings.get(base) : void 0;
|
|
409
|
+
if (charstring && depth < 2) {
|
|
410
|
+
const drawn = runGlyph(font, charstring, depth + 1);
|
|
411
|
+
if (drawn) out.push(...drawn.map((seg) => unscale(seg, font.scale)));
|
|
412
|
+
}
|
|
413
|
+
stack.length = 0;
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
if (op2 === 7) {
|
|
417
|
+
x = stack[0] ?? 0;
|
|
418
|
+
y = stack[1] ?? 0;
|
|
419
|
+
stack.length = 0;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
if (op2 === 33) {
|
|
423
|
+
x = stack[0] ?? x;
|
|
424
|
+
y = stack[1] ?? y;
|
|
425
|
+
stack.length = 0;
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
stack.length = 0;
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
default:
|
|
432
|
+
stack.length = 0;
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return true;
|
|
437
|
+
};
|
|
438
|
+
run(program, 0);
|
|
439
|
+
if (out.length > 0 && out[out.length - 1]?.op !== "close") out.push({ op: "close" });
|
|
440
|
+
if (out.length === 0) return void 0;
|
|
441
|
+
return out.map((seg) => scaleSeg(seg, font.scale));
|
|
442
|
+
}
|
|
443
|
+
function scaleSeg(seg, k) {
|
|
444
|
+
if (seg.op === "close") return seg;
|
|
445
|
+
if (seg.op === "cubic") return {
|
|
446
|
+
op: "cubic",
|
|
447
|
+
x1: seg.x1 * k,
|
|
448
|
+
y1: seg.y1 * k,
|
|
449
|
+
x2: seg.x2 * k,
|
|
450
|
+
y2: seg.y2 * k,
|
|
451
|
+
x: seg.x * k,
|
|
452
|
+
y: seg.y * k
|
|
453
|
+
};
|
|
454
|
+
return {
|
|
455
|
+
op: seg.op,
|
|
456
|
+
x: seg.x * k,
|
|
457
|
+
y: seg.y * k
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
/** The base of a `seac` comes back already scaled; this puts it back in glyph space. */
|
|
461
|
+
function unscale(seg, k) {
|
|
462
|
+
return scaleSeg(seg, k === 0 ? 1 : 1 / k);
|
|
463
|
+
}
|
|
464
|
+
/** §C.1 — the names `seac` selects its two parts by, at the codes it states. */
|
|
465
|
+
var STANDARD_ENCODING = {
|
|
466
|
+
32: "space",
|
|
467
|
+
33: "exclam",
|
|
468
|
+
34: "quotedbl",
|
|
469
|
+
35: "numbersign",
|
|
470
|
+
36: "dollar",
|
|
471
|
+
37: "percent",
|
|
472
|
+
38: "ampersand",
|
|
473
|
+
39: "quoteright",
|
|
474
|
+
40: "parenleft",
|
|
475
|
+
41: "parenright",
|
|
476
|
+
42: "asterisk",
|
|
477
|
+
43: "plus",
|
|
478
|
+
44: "comma",
|
|
479
|
+
45: "hyphen",
|
|
480
|
+
46: "period",
|
|
481
|
+
47: "slash",
|
|
482
|
+
48: "zero",
|
|
483
|
+
49: "one",
|
|
484
|
+
50: "two",
|
|
485
|
+
51: "three",
|
|
486
|
+
52: "four",
|
|
487
|
+
53: "five",
|
|
488
|
+
54: "six",
|
|
489
|
+
55: "seven",
|
|
490
|
+
56: "eight",
|
|
491
|
+
57: "nine",
|
|
492
|
+
58: "colon",
|
|
493
|
+
59: "semicolon",
|
|
494
|
+
60: "less",
|
|
495
|
+
61: "equal",
|
|
496
|
+
62: "greater",
|
|
497
|
+
63: "question",
|
|
498
|
+
64: "at",
|
|
499
|
+
65: "A",
|
|
500
|
+
66: "B",
|
|
501
|
+
67: "C",
|
|
502
|
+
68: "D",
|
|
503
|
+
69: "E",
|
|
504
|
+
70: "F",
|
|
505
|
+
71: "G",
|
|
506
|
+
72: "H",
|
|
507
|
+
73: "I",
|
|
508
|
+
74: "J",
|
|
509
|
+
75: "K",
|
|
510
|
+
76: "L",
|
|
511
|
+
77: "M",
|
|
512
|
+
78: "N",
|
|
513
|
+
79: "O",
|
|
514
|
+
80: "P",
|
|
515
|
+
81: "Q",
|
|
516
|
+
82: "R",
|
|
517
|
+
83: "S",
|
|
518
|
+
84: "T",
|
|
519
|
+
85: "U",
|
|
520
|
+
86: "V",
|
|
521
|
+
87: "W",
|
|
522
|
+
88: "X",
|
|
523
|
+
89: "Y",
|
|
524
|
+
90: "Z",
|
|
525
|
+
91: "bracketleft",
|
|
526
|
+
92: "backslash",
|
|
527
|
+
93: "bracketright",
|
|
528
|
+
94: "asciicircum",
|
|
529
|
+
95: "underscore",
|
|
530
|
+
96: "quoteleft",
|
|
531
|
+
97: "a",
|
|
532
|
+
98: "b",
|
|
533
|
+
99: "c",
|
|
534
|
+
100: "d",
|
|
535
|
+
101: "e",
|
|
536
|
+
102: "f",
|
|
537
|
+
103: "g",
|
|
538
|
+
104: "h",
|
|
539
|
+
105: "i",
|
|
540
|
+
106: "j",
|
|
541
|
+
107: "k",
|
|
542
|
+
108: "l",
|
|
543
|
+
109: "m",
|
|
544
|
+
110: "n",
|
|
545
|
+
111: "o",
|
|
546
|
+
112: "p",
|
|
547
|
+
113: "q",
|
|
548
|
+
114: "r",
|
|
549
|
+
115: "s",
|
|
550
|
+
116: "t",
|
|
551
|
+
117: "u",
|
|
552
|
+
118: "v",
|
|
553
|
+
119: "w",
|
|
554
|
+
120: "x",
|
|
555
|
+
121: "y",
|
|
556
|
+
122: "z",
|
|
557
|
+
123: "braceleft",
|
|
558
|
+
124: "bar",
|
|
559
|
+
125: "braceright",
|
|
560
|
+
126: "asciitilde",
|
|
561
|
+
193: "grave",
|
|
562
|
+
194: "acute",
|
|
563
|
+
195: "circumflex",
|
|
564
|
+
196: "tilde",
|
|
565
|
+
197: "macron",
|
|
566
|
+
198: "breve",
|
|
567
|
+
199: "dotaccent",
|
|
568
|
+
200: "dieresis",
|
|
569
|
+
202: "ring",
|
|
570
|
+
203: "cedilla",
|
|
571
|
+
205: "hungarumlaut",
|
|
572
|
+
206: "ogonek",
|
|
573
|
+
207: "caron"
|
|
574
|
+
};
|
|
575
|
+
//#endregion
|
|
576
|
+
export { type1Font };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
2
|
import { FEATURES } from "../core/ir/features.js";
|
|
3
|
+
import { buildAlphaMap, buildColorSpaceMap, buildShadingMap, gradientShading, shadingTypeOf } from "./shading.js";
|
|
3
4
|
import { IDENTITY, interpretContent, multiply } from "./content.js";
|
|
4
|
-
import { buildAlphaMap, buildColorSpaceMap, buildShadingMap } from "./shading.js";
|
|
5
5
|
import { collectPageAppearances } from "./annots.js";
|
|
6
6
|
import { hiddenProperties, hiddenXObject } from "./optional-content.js";
|
|
7
7
|
import { buildFonts } from "./text.js";
|
|
8
8
|
//#region src/pdf-reader/vector.ts
|
|
9
9
|
function paintedVectors(file, page) {
|
|
10
10
|
const out = [];
|
|
11
|
+
let bareShadings = 0;
|
|
11
12
|
const visiting = /* @__PURE__ */ new Set();
|
|
12
13
|
const stateCache = /* @__PURE__ */ new Map();
|
|
13
14
|
const mapsOf = (resources) => {
|
|
@@ -27,11 +28,39 @@ function paintedVectors(file, page) {
|
|
|
27
28
|
const xobjDict = xobjects instanceof Map ? xobjects : void 0;
|
|
28
29
|
const maps = mapsOf(resources);
|
|
29
30
|
const result = interpretContent(content, buildFonts(file, resources), baseCtm, maps.shadings, maps.alphas, maps.spaces, hiddenProperties(file, resources));
|
|
31
|
+
for (const paint of result.shadings) {
|
|
32
|
+
if (out.length >= MAX_VECTORS) break;
|
|
33
|
+
const dict = resources ? file.get(resources, "Shading") : PDF_NULL;
|
|
34
|
+
const found = dict instanceof Map ? file.resolve(dict.get(paint.name) ?? PDF_NULL) : PDF_NULL;
|
|
35
|
+
const sh = found instanceof PdfStream ? found.dict : found instanceof Map ? found : void 0;
|
|
36
|
+
if (sh && shadingTypeOf(file, sh) === 1) continue;
|
|
37
|
+
const gradient = sh ? gradientShading(file, sh) : void 0;
|
|
38
|
+
if (paint.masked) {
|
|
39
|
+
bareShadings++;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!sh || !gradient || !paint.clip) {
|
|
43
|
+
bareShadings++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
out.push({
|
|
47
|
+
order: paint.order,
|
|
48
|
+
segs: paint.clip.segs,
|
|
49
|
+
gradient,
|
|
50
|
+
...paint.alpha !== void 0 ? { alpha: paint.alpha } : {},
|
|
51
|
+
...paint.darkens ? { darkens: true } : {},
|
|
52
|
+
orderKey: [...prefix, paint.order]
|
|
53
|
+
});
|
|
54
|
+
}
|
|
30
55
|
const events = [
|
|
31
56
|
...result.vectors.map((vector) => ({
|
|
32
57
|
order: vector.order,
|
|
33
58
|
vector
|
|
34
59
|
})),
|
|
60
|
+
...result.outlines.map((vector) => ({
|
|
61
|
+
order: vector.order,
|
|
62
|
+
vector
|
|
63
|
+
})),
|
|
35
64
|
...result.images.map((xobject) => ({
|
|
36
65
|
order: xobject.order,
|
|
37
66
|
xobject
|
|
@@ -75,7 +104,10 @@ function paintedVectors(file, page) {
|
|
|
75
104
|
collectPageAppearances(file, page).forEach((appearance, index) => {
|
|
76
105
|
walk(appearance.resources ?? page.resources, file.streamData(appearance.stream), appearance.ctm, 1, [Number.MAX_SAFE_INTEGER, index]);
|
|
77
106
|
});
|
|
78
|
-
return
|
|
107
|
+
return {
|
|
108
|
+
placements: out,
|
|
109
|
+
bareShadings
|
|
110
|
+
};
|
|
79
111
|
}
|
|
80
112
|
/** §8.10.2 `/Matrix` — the form's own space, composed onto the placement CTM. */
|
|
81
113
|
function formMatrix(file, dict) {
|
|
@@ -123,8 +155,10 @@ function collectPageVectors(file, page, occupied = []) {
|
|
|
123
155
|
const pageArea = Math.max(1, Math.abs((px1 - px0) * (py1 - py0)));
|
|
124
156
|
const out = [];
|
|
125
157
|
const painted = [...occupied];
|
|
126
|
-
const
|
|
158
|
+
const lifted = paintedVectors(file, page);
|
|
159
|
+
const raws = lifted.placements;
|
|
127
160
|
const losses = [];
|
|
161
|
+
const bareShadings = lifted.bareShadings;
|
|
128
162
|
const asked = /* @__PURE__ */ new Set();
|
|
129
163
|
for (const raw of raws) {
|
|
130
164
|
if (raw.masked === true) asked.add("PDF soft mask (/SMask in the graphics state) is not applied; the shape is drawn at full opacity throughout");
|
|
@@ -150,7 +184,7 @@ function collectPageVectors(file, page, occupied = []) {
|
|
|
150
184
|
const short = Math.min(w, h);
|
|
151
185
|
const isBox = short >= MIN_SIDE && area >= MIN_AREA;
|
|
152
186
|
const isRule = long >= MIN_RULE_LEN && short > 0;
|
|
153
|
-
const filled = (v.gradient !== void 0 || solidFill) && (isBox || isRule);
|
|
187
|
+
const filled = (v.gradient !== void 0 || solidFill) && (isBox || isRule || v.glyph === true);
|
|
154
188
|
const stroked = v.strokeHex !== void 0 && (v.strokeHex !== "FFFFFF" || painted.some((box) => overlaps(box, b))) && Math.max(w, h) >= MIN_STROKE_LEN && area <= .85 * pageArea;
|
|
155
189
|
if (!filled && !stroked) continue;
|
|
156
190
|
painted.push(b);
|
|
@@ -173,6 +207,11 @@ function collectPageVectors(file, page, occupied = []) {
|
|
|
173
207
|
feature: FEATURES.shapes,
|
|
174
208
|
detail: `page carries more than ${String(MAX_VECTORS)} painted paths; the rest were not read`
|
|
175
209
|
});
|
|
210
|
+
if (bareShadings > 0) losses.push({
|
|
211
|
+
severity: "dropped",
|
|
212
|
+
feature: FEATURES.images,
|
|
213
|
+
detail: `${String(bareShadings)} bare-shading (sh) region${bareShadings === 1 ? "" : "s"} painted the clip rather than a path, and ${bareShadings === 1 ? "was" : "were"} not reconstructed`
|
|
214
|
+
});
|
|
176
215
|
return {
|
|
177
216
|
vectors: out,
|
|
178
217
|
losses
|
|
@@ -262,7 +262,10 @@ function parseSectPrNode(sectPr) {
|
|
|
262
262
|
const type = poAttr(child, "type") ?? "default";
|
|
263
263
|
const pitch = poIntAttr(child, "linePitch");
|
|
264
264
|
if ((type === "lines" || type === "linesAndChars") && pitch !== void 0 && pitch > 0) gridLinePitchPt = twipsToPt(pitch);
|
|
265
|
-
} else if (poIs(child, "w:type"))
|
|
265
|
+
} else if (poIs(child, "w:type")) {
|
|
266
|
+
const val = poAttr(child, "val");
|
|
267
|
+
sectionStart = val === "continuous" || val === "oddPage" || val === "evenPage" ? val : "nextPage";
|
|
268
|
+
}
|
|
266
269
|
return {
|
|
267
270
|
pageSize,
|
|
268
271
|
...margins ? { margins } : {},
|
|
@@ -976,6 +976,7 @@ function sectPrXml(s, hf) {
|
|
|
976
976
|
const parts = [];
|
|
977
977
|
for (const h of hf.headers) parts.push(`<w:headerReference w:type="${h.type}" r:id="${h.relId}"/>`);
|
|
978
978
|
for (const f of hf.footers) parts.push(`<w:footerReference w:type="${f.type}" r:id="${f.relId}"/>`);
|
|
979
|
+
if (s.sectionStart !== void 0 && s.sectionStart !== "nextPage") parts.push(`<w:type w:val="${s.sectionStart}"/>`);
|
|
979
980
|
if (s.pageSize) {
|
|
980
981
|
const orient = s.pageSize.orientation === "landscape" ? " w:orient=\"landscape\"" : "";
|
|
981
982
|
parts.push(`<w:pgSz w:w="${twips(s.pageSize.width)}" w:h="${twips(s.pageSize.height)}"${orient}/>`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Krassavin <info@reamkit.dev>",
|