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,351 @@
|
|
|
1
|
+
import { macGlyphName } from "./encodings.js";
|
|
2
|
+
//#region src/pdf-reader/glyf-outline.ts
|
|
3
|
+
/**
|
|
4
|
+
* Prepare an embedded TrueType program for outline reading (§9.6.6).
|
|
5
|
+
*
|
|
6
|
+
* @param program The raw `/FontFile2` bytes.
|
|
7
|
+
* @returns A reader, or `undefined` where the program carries no `glyf`/`loca`
|
|
8
|
+
* pair — an OpenType/CFF program under `/FontFile2` is one such, and
|
|
9
|
+
* its outlines are charstrings this does not read.
|
|
10
|
+
*/
|
|
11
|
+
function outlineSource(program) {
|
|
12
|
+
let tables;
|
|
13
|
+
try {
|
|
14
|
+
tables = sfntTables(program);
|
|
15
|
+
} catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const head = tables.get("head");
|
|
19
|
+
const loca = tables.get("loca");
|
|
20
|
+
const glyf = tables.get("glyf");
|
|
21
|
+
if (!head || !loca || !glyf) return void 0;
|
|
22
|
+
const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
|
|
23
|
+
const unitsPerEm = safeUint16(view, head.offset + 18) || 1e3;
|
|
24
|
+
const longLoca = safeInt16(view, head.offset + 50) === 1;
|
|
25
|
+
const cache = /* @__PURE__ */ new Map();
|
|
26
|
+
const glyphRange = (gid) => {
|
|
27
|
+
const stride = longLoca ? 4 : 2;
|
|
28
|
+
const at = loca.offset + gid * stride;
|
|
29
|
+
if (at + stride * 2 > loca.offset + loca.length) return void 0;
|
|
30
|
+
const read = (o) => longLoca ? safeUint32(view, o) : safeUint16(view, o) * 2;
|
|
31
|
+
const start = read(at);
|
|
32
|
+
const end = read(at + stride);
|
|
33
|
+
if (end <= start) return void 0;
|
|
34
|
+
if (glyf.offset + end > program.length) return void 0;
|
|
35
|
+
return {
|
|
36
|
+
start: glyf.offset + start,
|
|
37
|
+
end: glyf.offset + end
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
const read = (gid, depth) => {
|
|
41
|
+
const range = glyphRange(gid);
|
|
42
|
+
if (!range) return void 0;
|
|
43
|
+
const contours = safeInt16(view, range.start);
|
|
44
|
+
if (contours >= 0) return simpleGlyph(view, range.start, contours, range.end);
|
|
45
|
+
if (depth >= MAX_COMPONENT_DEPTH) return void 0;
|
|
46
|
+
return compositeGlyph(view, range.start + 10, range.end, (sub) => read(sub, depth + 1));
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
count: Math.max(0, Math.floor(loca.length / (longLoca ? 4 : 2)) - 1),
|
|
50
|
+
cmap: (tables.get("cmap")?.length ?? 0) > 0,
|
|
51
|
+
path: (gid) => {
|
|
52
|
+
const had = cache.get(gid);
|
|
53
|
+
if (had !== void 0 || cache.has(gid)) return had;
|
|
54
|
+
let out;
|
|
55
|
+
try {
|
|
56
|
+
const raw = read(gid, 0);
|
|
57
|
+
out = raw ? raw.map((seg) => scaleSeg(seg, 1 / unitsPerEm)) : void 0;
|
|
58
|
+
} catch {
|
|
59
|
+
out = void 0;
|
|
60
|
+
}
|
|
61
|
+
cache.set(gid, out);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** A composite glyph may name another composite; this is where that stops. */
|
|
67
|
+
var MAX_COMPONENT_DEPTH = 5;
|
|
68
|
+
/**
|
|
69
|
+
* §post format 2.0 — the NAME each glyph of a TrueType program goes by.
|
|
70
|
+
*
|
|
71
|
+
* A legacy eight-bit font is reached through this table and nothing else. Its
|
|
72
|
+
* program carries no `cmap`, so a code cannot be turned into a character and
|
|
73
|
+
* then into a glyph; what it can be turned into is a glyph NAME, through the
|
|
74
|
+
* encoding the font dictionary states (Annex D.2) — and the name is looked up
|
|
75
|
+
* here. TrueType_without_cmap.pdf is an Armenian face, Masis, whose `i` draws
|
|
76
|
+
* ի: read as text its line came back "'>in", and drawn by name it is the line
|
|
77
|
+
* the page shows.
|
|
78
|
+
*
|
|
79
|
+
* @param program The raw `/FontFile2` bytes.
|
|
80
|
+
* @returns Glyph name → index, or `undefined` where the table is missing or
|
|
81
|
+
* states no names of its own (formats 1.0 and 3.0).
|
|
82
|
+
*/
|
|
83
|
+
function postGlyphNames(program) {
|
|
84
|
+
let tables;
|
|
85
|
+
try {
|
|
86
|
+
tables = sfntTables(program);
|
|
87
|
+
} catch {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const post = tables.get("post");
|
|
91
|
+
if (!post || post.length < 34) return void 0;
|
|
92
|
+
const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
|
|
93
|
+
if (safeUint32(view, post.offset) !== POST_NAMED) return void 0;
|
|
94
|
+
const count = safeUint16(view, post.offset + 32);
|
|
95
|
+
const end = post.offset + post.length;
|
|
96
|
+
if (post.offset + 34 + count * 2 > end) return void 0;
|
|
97
|
+
const own = [];
|
|
98
|
+
for (let at = post.offset + 34 + count * 2; at < end;) {
|
|
99
|
+
const length = program[at] ?? 0;
|
|
100
|
+
if (at + 1 + length > end) break;
|
|
101
|
+
own.push(String.fromCharCode(...program.subarray(at + 1, at + 1 + length)));
|
|
102
|
+
at += 1 + length;
|
|
103
|
+
}
|
|
104
|
+
const out = /* @__PURE__ */ new Map();
|
|
105
|
+
for (let gid = 0; gid < count; gid++) {
|
|
106
|
+
const index = safeUint16(view, post.offset + 34 + gid * 2);
|
|
107
|
+
const name = index < MAC_ORDER_SIZE ? macGlyphName(index) : own[index - MAC_ORDER_SIZE];
|
|
108
|
+
if (name !== void 0 && name.length > 0 && !out.has(name)) out.set(name, gid);
|
|
109
|
+
}
|
|
110
|
+
return out.size > 0 ? out : void 0;
|
|
111
|
+
}
|
|
112
|
+
/** `post` version 2.0 — the only one that states names of its own. */
|
|
113
|
+
var POST_NAMED = 131072;
|
|
114
|
+
/** How many names the Macintosh standard order holds (§post). */
|
|
115
|
+
var MAC_ORDER_SIZE = 258;
|
|
116
|
+
/** The sfnt table directory: tag → where that table is. */
|
|
117
|
+
function sfntTables(program) {
|
|
118
|
+
const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
|
|
119
|
+
const out = /* @__PURE__ */ new Map();
|
|
120
|
+
const count = safeUint16(view, 4);
|
|
121
|
+
for (let i = 0; i < count; i++) {
|
|
122
|
+
const at = 12 + i * 16;
|
|
123
|
+
if (at + 16 > program.length) break;
|
|
124
|
+
const tag = String.fromCharCode(program[at], program[at + 1], program[at + 2], program[at + 3]);
|
|
125
|
+
out.set(tag, {
|
|
126
|
+
offset: safeUint32(view, at + 8),
|
|
127
|
+
length: safeUint32(view, at + 12)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function simpleGlyph(view, start, contours, end) {
|
|
133
|
+
let at = start + 10;
|
|
134
|
+
const ends = [];
|
|
135
|
+
for (let i = 0; i < contours; i++, at += 2) ends.push(safeUint16(view, at));
|
|
136
|
+
const points = contours > 0 ? (ends[contours - 1] ?? -1) + 1 : 0;
|
|
137
|
+
if (points <= 0 || points > MAX_POINTS) return void 0;
|
|
138
|
+
at += 2 + safeUint16(view, at);
|
|
139
|
+
const flags = new Uint8Array(points);
|
|
140
|
+
for (let i = 0; i < points && at < end;) {
|
|
141
|
+
const flag = safeUint8(view, at++);
|
|
142
|
+
flags[i++] = flag;
|
|
143
|
+
if ((flag & 8) !== 0) {
|
|
144
|
+
let repeats = safeUint8(view, at++);
|
|
145
|
+
while (repeats-- > 0 && i < points) flags[i++] = flag;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const xs = new Int16Array(points);
|
|
149
|
+
let x = 0;
|
|
150
|
+
for (let i = 0; i < points; i++) {
|
|
151
|
+
const flag = flags[i];
|
|
152
|
+
if ((flag & 2) !== 0) {
|
|
153
|
+
const d = safeUint8(view, at++);
|
|
154
|
+
x += (flag & 16) !== 0 ? d : -d;
|
|
155
|
+
} else if ((flag & 16) === 0) {
|
|
156
|
+
x += safeInt16(view, at);
|
|
157
|
+
at += 2;
|
|
158
|
+
}
|
|
159
|
+
xs[i] = x;
|
|
160
|
+
}
|
|
161
|
+
const ys = new Int16Array(points);
|
|
162
|
+
let y = 0;
|
|
163
|
+
for (let i = 0; i < points; i++) {
|
|
164
|
+
const flag = flags[i];
|
|
165
|
+
if ((flag & 4) !== 0) {
|
|
166
|
+
const d = safeUint8(view, at++);
|
|
167
|
+
y += (flag & 32) !== 0 ? d : -d;
|
|
168
|
+
} else if ((flag & 32) === 0) {
|
|
169
|
+
y += safeInt16(view, at);
|
|
170
|
+
at += 2;
|
|
171
|
+
}
|
|
172
|
+
ys[i] = y;
|
|
173
|
+
}
|
|
174
|
+
const out = [];
|
|
175
|
+
let from = 0;
|
|
176
|
+
for (const last of ends) {
|
|
177
|
+
if (last >= points) break;
|
|
178
|
+
emitContour(out, flags, xs, ys, from, last);
|
|
179
|
+
from = last + 1;
|
|
180
|
+
}
|
|
181
|
+
return out.length > 0 ? out : void 0;
|
|
182
|
+
}
|
|
183
|
+
/** How many points one glyph may hold — a guard, not a format limit. */
|
|
184
|
+
var MAX_POINTS = 1e4;
|
|
185
|
+
function emitContour(out, flags, xs, ys, from, last) {
|
|
186
|
+
const n = last - from + 1;
|
|
187
|
+
if (n <= 0) return;
|
|
188
|
+
const on = (i) => (flags[from + (i % n + n) % n] & 1) !== 0;
|
|
189
|
+
const px = (i) => xs[from + (i % n + n) % n];
|
|
190
|
+
const py = (i) => ys[from + (i % n + n) % n];
|
|
191
|
+
let startIdx = 0;
|
|
192
|
+
while (startIdx < n && !on(startIdx)) startIdx++;
|
|
193
|
+
let cx;
|
|
194
|
+
let cy;
|
|
195
|
+
if (startIdx === n) {
|
|
196
|
+
startIdx = 0;
|
|
197
|
+
cx = (px(0) + px(n - 1)) / 2;
|
|
198
|
+
cy = (py(0) + py(n - 1)) / 2;
|
|
199
|
+
} else {
|
|
200
|
+
cx = px(startIdx);
|
|
201
|
+
cy = py(startIdx);
|
|
202
|
+
}
|
|
203
|
+
out.push({
|
|
204
|
+
op: "move",
|
|
205
|
+
x: cx,
|
|
206
|
+
y: cy
|
|
207
|
+
});
|
|
208
|
+
let i = startIdx + 1;
|
|
209
|
+
const stop = startIdx + n;
|
|
210
|
+
while (i <= stop) {
|
|
211
|
+
if (on(i)) {
|
|
212
|
+
out.push({
|
|
213
|
+
op: "line",
|
|
214
|
+
x: px(i),
|
|
215
|
+
y: py(i)
|
|
216
|
+
});
|
|
217
|
+
cx = px(i);
|
|
218
|
+
cy = py(i);
|
|
219
|
+
i++;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const qx = px(i);
|
|
223
|
+
const qy = py(i);
|
|
224
|
+
const nextOn = on(i + 1);
|
|
225
|
+
const ex = nextOn ? px(i + 1) : (qx + px(i + 1)) / 2;
|
|
226
|
+
const ey = nextOn ? py(i + 1) : (qy + py(i + 1)) / 2;
|
|
227
|
+
out.push({
|
|
228
|
+
op: "cubic",
|
|
229
|
+
x1: cx + 2 / 3 * (qx - cx),
|
|
230
|
+
y1: cy + 2 / 3 * (qy - cy),
|
|
231
|
+
x2: ex + 2 / 3 * (qx - ex),
|
|
232
|
+
y2: ey + 2 / 3 * (qy - ey),
|
|
233
|
+
x: ex,
|
|
234
|
+
y: ey
|
|
235
|
+
});
|
|
236
|
+
cx = ex;
|
|
237
|
+
cy = ey;
|
|
238
|
+
i += nextOn ? 2 : 1;
|
|
239
|
+
}
|
|
240
|
+
out.push({ op: "close" });
|
|
241
|
+
}
|
|
242
|
+
function compositeGlyph(view, start, end, component) {
|
|
243
|
+
const out = [];
|
|
244
|
+
let at = start;
|
|
245
|
+
for (let guard = 0; guard < MAX_COMPONENTS && at + 4 <= end; guard++) {
|
|
246
|
+
const flags = safeUint16(view, at);
|
|
247
|
+
const gid = safeUint16(view, at + 2);
|
|
248
|
+
at += 4;
|
|
249
|
+
let dx = 0;
|
|
250
|
+
let dy = 0;
|
|
251
|
+
if ((flags & 1) !== 0) {
|
|
252
|
+
if ((flags & 2) !== 0) {
|
|
253
|
+
dx = safeInt16(view, at);
|
|
254
|
+
dy = safeInt16(view, at + 2);
|
|
255
|
+
}
|
|
256
|
+
at += 4;
|
|
257
|
+
} else {
|
|
258
|
+
if ((flags & 2) !== 0) {
|
|
259
|
+
dx = safeInt8(view, at);
|
|
260
|
+
dy = safeInt8(view, at + 1);
|
|
261
|
+
}
|
|
262
|
+
at += 2;
|
|
263
|
+
}
|
|
264
|
+
let a = 1;
|
|
265
|
+
let b = 0;
|
|
266
|
+
let c = 0;
|
|
267
|
+
let d = 1;
|
|
268
|
+
if ((flags & 8) !== 0) {
|
|
269
|
+
a = d = f2dot14(view, at);
|
|
270
|
+
at += 2;
|
|
271
|
+
} else if ((flags & 64) !== 0) {
|
|
272
|
+
a = f2dot14(view, at);
|
|
273
|
+
d = f2dot14(view, at + 2);
|
|
274
|
+
at += 4;
|
|
275
|
+
} else if ((flags & 128) !== 0) {
|
|
276
|
+
a = f2dot14(view, at);
|
|
277
|
+
b = f2dot14(view, at + 2);
|
|
278
|
+
c = f2dot14(view, at + 4);
|
|
279
|
+
d = f2dot14(view, at + 6);
|
|
280
|
+
at += 8;
|
|
281
|
+
}
|
|
282
|
+
const sub = component(gid);
|
|
283
|
+
if (sub) for (const seg of sub) out.push(transformSeg(seg, a, b, c, d, dx, dy));
|
|
284
|
+
if ((flags & 32) === 0) break;
|
|
285
|
+
}
|
|
286
|
+
return out.length > 0 ? out : void 0;
|
|
287
|
+
}
|
|
288
|
+
/** How many components one composite glyph may place. */
|
|
289
|
+
var MAX_COMPONENTS = 32;
|
|
290
|
+
function transformSeg(seg, a, b, c, d, dx, dy) {
|
|
291
|
+
const at = (x, y) => [a * x + c * y + dx, b * x + d * y + dy];
|
|
292
|
+
if (seg.op === "close") return seg;
|
|
293
|
+
if (seg.op === "cubic") {
|
|
294
|
+
const [x1, y1] = at(seg.x1, seg.y1);
|
|
295
|
+
const [x2, y2] = at(seg.x2, seg.y2);
|
|
296
|
+
const [x, y] = at(seg.x, seg.y);
|
|
297
|
+
return {
|
|
298
|
+
op: "cubic",
|
|
299
|
+
x1,
|
|
300
|
+
y1,
|
|
301
|
+
x2,
|
|
302
|
+
y2,
|
|
303
|
+
x,
|
|
304
|
+
y
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const [x, y] = at(seg.x, seg.y);
|
|
308
|
+
return {
|
|
309
|
+
op: seg.op,
|
|
310
|
+
x,
|
|
311
|
+
y
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function scaleSeg(seg, k) {
|
|
315
|
+
if (seg.op === "close") return seg;
|
|
316
|
+
if (seg.op === "cubic") return {
|
|
317
|
+
op: "cubic",
|
|
318
|
+
x1: seg.x1 * k,
|
|
319
|
+
y1: seg.y1 * k,
|
|
320
|
+
x2: seg.x2 * k,
|
|
321
|
+
y2: seg.y2 * k,
|
|
322
|
+
x: seg.x * k,
|
|
323
|
+
y: seg.y * k
|
|
324
|
+
};
|
|
325
|
+
return {
|
|
326
|
+
op: seg.op,
|
|
327
|
+
x: seg.x * k,
|
|
328
|
+
y: seg.y * k
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/** The 2.14 fixed-point a component transform is stated in. */
|
|
332
|
+
function f2dot14(view, at) {
|
|
333
|
+
return safeInt16(view, at) / 16384;
|
|
334
|
+
}
|
|
335
|
+
function safeUint8(view, at) {
|
|
336
|
+
return at >= 0 && at < view.byteLength ? view.getUint8(at) : 0;
|
|
337
|
+
}
|
|
338
|
+
function safeInt8(view, at) {
|
|
339
|
+
return at >= 0 && at < view.byteLength ? view.getInt8(at) : 0;
|
|
340
|
+
}
|
|
341
|
+
function safeUint16(view, at) {
|
|
342
|
+
return at >= 0 && at + 2 <= view.byteLength ? view.getUint16(at) : 0;
|
|
343
|
+
}
|
|
344
|
+
function safeInt16(view, at) {
|
|
345
|
+
return at >= 0 && at + 2 <= view.byteLength ? view.getInt16(at) : 0;
|
|
346
|
+
}
|
|
347
|
+
function safeUint32(view, at) {
|
|
348
|
+
return at >= 0 && at + 4 <= view.byteLength ? view.getUint32(at) : 0;
|
|
349
|
+
}
|
|
350
|
+
//#endregion
|
|
351
|
+
export { outlineSource, postGlyphNames };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dingbatChar } from "./dingbats.js";
|
|
1
2
|
//#region src/pdf-reader/glyph-names.ts
|
|
2
3
|
/** Glyph name → the text it stands for. */
|
|
3
4
|
var GLYPHS = {
|
|
@@ -22,6 +23,7 @@ var GLYPHS = {
|
|
|
22
23
|
Dcaron: "Ď",
|
|
23
24
|
Dcedilla: "Ḑ",
|
|
24
25
|
Ddotaccent: "Ḋ",
|
|
26
|
+
Delta: "∆",
|
|
25
27
|
Eacute: "É",
|
|
26
28
|
Ebreve: "Ĕ",
|
|
27
29
|
Ecaron: "Ě",
|
|
@@ -65,6 +67,7 @@ var GLYPHS = {
|
|
|
65
67
|
Lacute: "Ĺ",
|
|
66
68
|
Lcaron: "Ľ",
|
|
67
69
|
Lcedilla: "Ļ",
|
|
70
|
+
Lslash: "Ł",
|
|
68
71
|
Macute: "Ḿ",
|
|
69
72
|
Mdotaccent: "Ṁ",
|
|
70
73
|
Nacute: "Ń",
|
|
@@ -83,6 +86,7 @@ var GLYPHS = {
|
|
|
83
86
|
Ograve: "Ò",
|
|
84
87
|
Ohungarumlaut: "Ő",
|
|
85
88
|
Omacron: "Ō",
|
|
89
|
+
Omega: "Ω",
|
|
86
90
|
Oogonek: "Ǫ",
|
|
87
91
|
Oslash: "Ø",
|
|
88
92
|
Otilde: "Õ",
|
|
@@ -143,6 +147,7 @@ var GLYPHS = {
|
|
|
143
147
|
amacron: "ā",
|
|
144
148
|
ampersand: "&",
|
|
145
149
|
aogonek: "ą",
|
|
150
|
+
approxequal: "≈",
|
|
146
151
|
aring: "å",
|
|
147
152
|
asciicircum: "^",
|
|
148
153
|
asciitilde: "~",
|
|
@@ -223,6 +228,7 @@ var GLYPHS = {
|
|
|
223
228
|
gmacron: "ḡ",
|
|
224
229
|
grave: "`",
|
|
225
230
|
greater: ">",
|
|
231
|
+
greaterequal: "≥",
|
|
226
232
|
guillemotleft: "«",
|
|
227
233
|
guillemotright: "»",
|
|
228
234
|
guilsinglleft: "‹",
|
|
@@ -240,6 +246,8 @@ var GLYPHS = {
|
|
|
240
246
|
icircumflex: "î",
|
|
241
247
|
idieresis: "ï",
|
|
242
248
|
igrave: "ì",
|
|
249
|
+
infinity: "∞",
|
|
250
|
+
integral: "∫",
|
|
243
251
|
imacron: "ī",
|
|
244
252
|
iogonek: "į",
|
|
245
253
|
itilde: "ĩ",
|
|
@@ -252,7 +260,10 @@ var GLYPHS = {
|
|
|
252
260
|
lcaron: "ľ",
|
|
253
261
|
lcedilla: "ļ",
|
|
254
262
|
less: "<",
|
|
263
|
+
lessequal: "≤",
|
|
255
264
|
logicalnot: "¬",
|
|
265
|
+
lozenge: "◊",
|
|
266
|
+
lslash: "ł",
|
|
256
267
|
macron: "¯",
|
|
257
268
|
macute: "ḿ",
|
|
258
269
|
mdotaccent: "ṁ",
|
|
@@ -266,6 +277,7 @@ var GLYPHS = {
|
|
|
266
277
|
ndotaccent: "ṅ",
|
|
267
278
|
ngrave: "ǹ",
|
|
268
279
|
nine: "9",
|
|
280
|
+
notequal: "≠",
|
|
269
281
|
ntilde: "ñ",
|
|
270
282
|
numbersign: "#",
|
|
271
283
|
oacute: "ó",
|
|
@@ -292,12 +304,16 @@ var GLYPHS = {
|
|
|
292
304
|
paragraph: "¶",
|
|
293
305
|
parenleft: "(",
|
|
294
306
|
parenright: ")",
|
|
307
|
+
partialdiff: "∂",
|
|
295
308
|
pdotaccent: "ṗ",
|
|
296
309
|
percent: "%",
|
|
297
310
|
period: ".",
|
|
311
|
+
periodcentered: "·",
|
|
298
312
|
perthousand: "‰",
|
|
313
|
+
pi: "π",
|
|
299
314
|
plus: "+",
|
|
300
315
|
plusminus: "±",
|
|
316
|
+
product: "∏",
|
|
301
317
|
question: "?",
|
|
302
318
|
questiondown: "¿",
|
|
303
319
|
quotedbl: "\"",
|
|
@@ -308,6 +324,7 @@ var GLYPHS = {
|
|
|
308
324
|
quoteright: "’",
|
|
309
325
|
quotesinglbase: "‚",
|
|
310
326
|
quotesingle: "'",
|
|
327
|
+
radical: "√",
|
|
311
328
|
racute: "ŕ",
|
|
312
329
|
rcaron: "ř",
|
|
313
330
|
rcedilla: "ŗ",
|
|
@@ -328,6 +345,7 @@ var GLYPHS = {
|
|
|
328
345
|
softhyphen: "",
|
|
329
346
|
space: " ",
|
|
330
347
|
sterling: "£",
|
|
348
|
+
summation: "∑",
|
|
331
349
|
tcaron: "ť",
|
|
332
350
|
tcedilla: "ţ",
|
|
333
351
|
tdieresis: "ẗ",
|
|
@@ -403,6 +421,8 @@ function textForGlyphName(name) {
|
|
|
403
421
|
const cp = parseInt(u[1], 16);
|
|
404
422
|
return cp <= 1114111 ? String.fromCodePoint(cp) : void 0;
|
|
405
423
|
}
|
|
424
|
+
const dingbat = dingbatChar(base);
|
|
425
|
+
if (dingbat !== void 0) return dingbat;
|
|
406
426
|
}
|
|
407
427
|
//#endregion
|
|
408
428
|
export { textForGlyphName };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** How a profile turns its own components into sRGB, each 0..1. */
|
|
2
|
+
export type IccTransform = (comps: ReadonlyArray<number>) => [number, number, number];
|
|
3
|
+
/**
|
|
4
|
+
* Read an ICC profile into a transform to sRGB.
|
|
5
|
+
*
|
|
6
|
+
* @param profile The raw profile bytes, as `/ICCBased`'s stream holds them.
|
|
7
|
+
* @returns The transform, or `undefined` for a profile whose form this does not
|
|
8
|
+
* read — a CMYK one, a table-based one, or a damaged one.
|
|
9
|
+
*/
|
|
10
|
+
export declare function iccTransform(profile: Uint8Array): IccTransform | undefined;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
//#region src/pdf-reader/icc.ts
|
|
2
|
+
/**
|
|
3
|
+
* Read an ICC profile into a transform to sRGB.
|
|
4
|
+
*
|
|
5
|
+
* @param profile The raw profile bytes, as `/ICCBased`'s stream holds them.
|
|
6
|
+
* @returns The transform, or `undefined` for a profile whose form this does not
|
|
7
|
+
* read — a CMYK one, a table-based one, or a damaged one.
|
|
8
|
+
*/
|
|
9
|
+
function iccTransform(profile) {
|
|
10
|
+
if (profile.length < HEADER_SIZE + 4) return void 0;
|
|
11
|
+
const view = new DataView(profile.buffer, profile.byteOffset, profile.byteLength);
|
|
12
|
+
const space = tag(profile, 16);
|
|
13
|
+
if (space !== "RGB " && space !== "GRAY") return void 0;
|
|
14
|
+
const tags = tagTable(profile, view);
|
|
15
|
+
if (!tags) return void 0;
|
|
16
|
+
const white = xyzTag(profile, view, tags.get("wtpt")) ?? D50;
|
|
17
|
+
if (space === "GRAY") {
|
|
18
|
+
const curve = curveTag(profile, view, tags.get("kTRC"));
|
|
19
|
+
if (!curve) return void 0;
|
|
20
|
+
return (comps) => {
|
|
21
|
+
const y = curve(clamp01(comps[0] ?? 0));
|
|
22
|
+
const [x, yy, z] = adapt([
|
|
23
|
+
white[0] * y,
|
|
24
|
+
white[1] * y,
|
|
25
|
+
white[2] * y
|
|
26
|
+
], white);
|
|
27
|
+
return toSrgb(x, yy, z);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const r = xyzTag(profile, view, tags.get("rXYZ"));
|
|
31
|
+
const g = xyzTag(profile, view, tags.get("gXYZ"));
|
|
32
|
+
const b = xyzTag(profile, view, tags.get("bXYZ"));
|
|
33
|
+
if (!r || !g || !b) return void 0;
|
|
34
|
+
const rc = curveTag(profile, view, tags.get("rTRC"));
|
|
35
|
+
const gc = curveTag(profile, view, tags.get("gTRC"));
|
|
36
|
+
const bc = curveTag(profile, view, tags.get("bTRC"));
|
|
37
|
+
if (!rc || !gc || !bc) return void 0;
|
|
38
|
+
return (comps) => {
|
|
39
|
+
const lr = rc(clamp01(comps[0] ?? 0));
|
|
40
|
+
const lg = gc(clamp01(comps[1] ?? 0));
|
|
41
|
+
const lb = bc(clamp01(comps[2] ?? 0));
|
|
42
|
+
const [ax, ay, az] = adapt([
|
|
43
|
+
r[0] * lr + g[0] * lg + b[0] * lb,
|
|
44
|
+
r[1] * lr + g[1] * lg + b[1] * lb,
|
|
45
|
+
r[2] * lr + g[2] * lg + b[2] * lb
|
|
46
|
+
], D50);
|
|
47
|
+
return toSrgb(ax, ay, az);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** The profile header is fixed-length; the tag table follows it. */
|
|
51
|
+
var HEADER_SIZE = 128;
|
|
52
|
+
/** ICC.1:2010 §7.2.16 — the PCS every matrix/TRC profile is referred to. */
|
|
53
|
+
var D50 = [
|
|
54
|
+
.9642,
|
|
55
|
+
1,
|
|
56
|
+
.8249
|
|
57
|
+
];
|
|
58
|
+
/** The white every sRGB screen is referred to. */
|
|
59
|
+
var D65 = [
|
|
60
|
+
.9505,
|
|
61
|
+
1,
|
|
62
|
+
1.089
|
|
63
|
+
];
|
|
64
|
+
/** A four-byte signature, as the profile writes them. */
|
|
65
|
+
function tag(data, at) {
|
|
66
|
+
return String.fromCharCode(data[at] ?? 0, data[at + 1] ?? 0, data[at + 2] ?? 0, data[at + 3] ?? 0);
|
|
67
|
+
}
|
|
68
|
+
/** §7.3 — the tag table: a count, then twelve bytes of signature/offset/size each. */
|
|
69
|
+
function tagTable(data, view) {
|
|
70
|
+
const count = view.getUint32(HEADER_SIZE);
|
|
71
|
+
if (count === 0 || count > MAX_TAGS) return void 0;
|
|
72
|
+
const out = /* @__PURE__ */ new Map();
|
|
73
|
+
for (let i = 0; i < count; i++) {
|
|
74
|
+
const at = HEADER_SIZE + 4 + i * 12;
|
|
75
|
+
if (at + 12 > data.length) break;
|
|
76
|
+
const offset = view.getUint32(at + 4);
|
|
77
|
+
const size = view.getUint32(at + 8);
|
|
78
|
+
if (offset + size > data.length) continue;
|
|
79
|
+
out.set(tag(data, at), {
|
|
80
|
+
offset,
|
|
81
|
+
size
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return out.size > 0 ? out : void 0;
|
|
85
|
+
}
|
|
86
|
+
/** No profile carries more tags than this; a bigger count is a damaged file. */
|
|
87
|
+
var MAX_TAGS = 256;
|
|
88
|
+
/** §10.31 `XYZType` — a signature, four reserved bytes, then three s15Fixed16. */
|
|
89
|
+
function xyzTag(data, view, where) {
|
|
90
|
+
if (!where || where.size < 20 || tag(data, where.offset) !== "XYZ ") return void 0;
|
|
91
|
+
const at = where.offset + 8;
|
|
92
|
+
return [
|
|
93
|
+
s15(view, at),
|
|
94
|
+
s15(view, at + 4),
|
|
95
|
+
s15(view, at + 8)
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
/** The s15Fixed16 every ICC number is written as. */
|
|
99
|
+
function s15(view, at) {
|
|
100
|
+
return view.getInt32(at) / 65536;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* §10.6/§10.18 — a tone curve, as the function that linearises one component.
|
|
104
|
+
*
|
|
105
|
+
* `curv` is a table: no entries is the identity, one is a gamma written as
|
|
106
|
+
* u8Fixed8, and more is a sampled curve read by interpolation. `para` states
|
|
107
|
+
* one of five formulas by their parameters, of which the sRGB-style type 3 is
|
|
108
|
+
* the one every modern profile writes.
|
|
109
|
+
*/
|
|
110
|
+
function curveTag(data, view, where) {
|
|
111
|
+
if (!where || where.size < 12) return void 0;
|
|
112
|
+
const kind = tag(data, where.offset);
|
|
113
|
+
if (kind === "curv") {
|
|
114
|
+
const count = view.getUint32(where.offset + 8);
|
|
115
|
+
if (count === 0) return (v) => v;
|
|
116
|
+
if (count === 1) {
|
|
117
|
+
const gamma = view.getUint16(where.offset + 12) / 256;
|
|
118
|
+
return (v) => v ** gamma;
|
|
119
|
+
}
|
|
120
|
+
if (where.offset + 12 + count * 2 > data.length || count > MAX_CURVE_POINTS) return void 0;
|
|
121
|
+
const table = new Float64Array(count);
|
|
122
|
+
for (let i = 0; i < count; i++) table[i] = view.getUint16(where.offset + 12 + i * 2) / 65535;
|
|
123
|
+
return (v) => {
|
|
124
|
+
const at = clamp01(v) * (count - 1);
|
|
125
|
+
const i = Math.floor(at);
|
|
126
|
+
const lo = table[i] ?? 0;
|
|
127
|
+
return lo + ((table[Math.min(i + 1, count - 1)] ?? lo) - lo) * (at - i);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (kind === "para") {
|
|
131
|
+
const type = view.getUint16(where.offset + 8);
|
|
132
|
+
const p = (i) => s15(view, where.offset + 12 + i * 4);
|
|
133
|
+
const g = p(0);
|
|
134
|
+
if (type === 0) return (v) => v ** g;
|
|
135
|
+
if (type === 1) {
|
|
136
|
+
const [a, b] = [p(1), p(2)];
|
|
137
|
+
return (v) => v >= -b / a ? (a * v + b) ** g : 0;
|
|
138
|
+
}
|
|
139
|
+
if (type === 2) {
|
|
140
|
+
const [a, b, c] = [
|
|
141
|
+
p(1),
|
|
142
|
+
p(2),
|
|
143
|
+
p(3)
|
|
144
|
+
];
|
|
145
|
+
return (v) => v >= -b / a ? (a * v + b) ** g + c : c;
|
|
146
|
+
}
|
|
147
|
+
if (type === 3) {
|
|
148
|
+
const [a, b, c, d] = [
|
|
149
|
+
p(1),
|
|
150
|
+
p(2),
|
|
151
|
+
p(3),
|
|
152
|
+
p(4)
|
|
153
|
+
];
|
|
154
|
+
return (v) => v >= d ? (a * v + b) ** g : c * v;
|
|
155
|
+
}
|
|
156
|
+
if (type === 4) {
|
|
157
|
+
const [a, b, c, d, e, f] = [
|
|
158
|
+
p(1),
|
|
159
|
+
p(2),
|
|
160
|
+
p(3),
|
|
161
|
+
p(4),
|
|
162
|
+
p(5),
|
|
163
|
+
p(6)
|
|
164
|
+
];
|
|
165
|
+
return (v) => v >= d ? (a * v + b) ** g + e : c * v + f;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** No tone curve is sampled at more points than this. */
|
|
170
|
+
var MAX_CURVE_POINTS = 65536;
|
|
171
|
+
/** Bradford adaptation from the profile's white to the screen's. */
|
|
172
|
+
function adapt(xyz, from) {
|
|
173
|
+
const cone = (x, y, z) => [
|
|
174
|
+
.8951 * x + .2664 * y - .1614 * z,
|
|
175
|
+
-.7502 * x + 1.7135 * y + .0367 * z,
|
|
176
|
+
.0389 * x - .0685 * y + 1.0296 * z
|
|
177
|
+
];
|
|
178
|
+
const [sx, sy, sz] = cone(from[0], from[1], from[2]);
|
|
179
|
+
const [dx, dy, dz] = cone(D65[0], D65[1], D65[2]);
|
|
180
|
+
const [cx, cy, cz] = cone(xyz[0], xyz[1], xyz[2]);
|
|
181
|
+
const [ax, ay, az] = [
|
|
182
|
+
cx * (sx === 0 ? 1 : dx / sx),
|
|
183
|
+
cy * (sy === 0 ? 1 : dy / sy),
|
|
184
|
+
cz * (sz === 0 ? 1 : dz / sz)
|
|
185
|
+
];
|
|
186
|
+
return [
|
|
187
|
+
.9869929 * ax - .1470543 * ay + .1599627 * az,
|
|
188
|
+
.4323053 * ax + .5183603 * ay + .0492912 * az,
|
|
189
|
+
-.0085287 * ax + .0400428 * ay + .9684867 * az
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
/** IEC 61966-2-1 — XYZ (D65) to sRGB, gamma and all. */
|
|
193
|
+
function toSrgb(x, y, z) {
|
|
194
|
+
const r = 3.2404542 * x - 1.5371385 * y - .4985314 * z;
|
|
195
|
+
const g = -.969266 * x + 1.8760108 * y + .041556 * z;
|
|
196
|
+
const b = .0556434 * x - .2040259 * y + 1.0572252 * z;
|
|
197
|
+
return [
|
|
198
|
+
transfer(clamp01(r)),
|
|
199
|
+
transfer(clamp01(g)),
|
|
200
|
+
transfer(clamp01(b))
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
function transfer(v) {
|
|
204
|
+
return v <= .0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - .055;
|
|
205
|
+
}
|
|
206
|
+
function clamp01(v) {
|
|
207
|
+
return Math.min(1, Math.max(0, v));
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
export { iccTransform };
|
|
@@ -28,9 +28,10 @@ export type DecodedImage = {
|
|
|
28
28
|
* colour spaces; Flate, LZW (EP12), RunLength, ASCII85, ASCIIHex and CCITT
|
|
29
29
|
* Group 4 / Group 3 1-D fax (EP15) filters; PNG/TIFF predictors; bit depths
|
|
30
30
|
* 1/2/4/8/16; an `/SMask` folded in as the PNG alpha channel; and a stencil
|
|
31
|
-
* `/ImageMask` (§8.9.6.2) given back as RGBA in `fillHex
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* `/ImageMask` (§8.9.6.2) given back as RGBA in `fillHex`; `Lab` (§8.6.5.8) and
|
|
32
|
+
* a `Separation`/`DeviceN` through its own tint transform (§8.6.6.4).
|
|
33
|
+
* Unsupported inputs (CCITT Group 3 2-D) return a typed failure so the caller
|
|
34
|
+
* records a loss.
|
|
34
35
|
*
|
|
35
36
|
* @param file The owning file.
|
|
36
37
|
* @param stream The image XObject, or an inline image wrapped as one.
|