pdf-parser.js 4.5.6 → 4.7.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 +5 -5
- package/dist/codec.d.cts +8 -0
- package/dist/codec.d.ts +8 -0
- package/dist/content-write.d.cts +1 -1
- package/dist/content-write.d.ts +1 -1
- package/dist/{embedded-font-B4p9a3O3.d.ts → embedded-font-BRhZfEN2.d.ts} +1 -1
- package/dist/{embedded-font-oc65VGlr.d.cts → embedded-font-DJUn9kMH.d.cts} +1 -1
- package/dist/embedded-font-write.d.cts +1 -1
- package/dist/embedded-font-write.d.ts +1 -1
- package/dist/embedded-font.cjs +1 -1
- package/dist/embedded-font.d.cts +1 -1
- package/dist/embedded-font.d.ts +1 -1
- package/dist/embedded-font.js +1 -1
- package/dist/filters.cjs +14 -3
- package/dist/filters.d.cts +4 -0
- package/dist/filters.d.ts +4 -0
- package/dist/filters.js +14 -3
- package/dist/font-registry.d.cts +1 -1
- package/dist/font-registry.d.ts +1 -1
- package/dist/gdef-table.cjs +48 -0
- package/dist/gdef-table.d.cts +15 -0
- package/dist/gdef-table.d.ts +15 -0
- package/dist/gdef-table.js +42 -0
- package/dist/gsub-table-Bm9kSMzk.d.cts +16 -0
- package/dist/gsub-table-CdfESH2-.d.ts +16 -0
- package/dist/gsub-table.cjs +404 -87
- package/dist/gsub-table.d.cts +2 -2
- package/dist/gsub-table.d.ts +2 -2
- package/dist/gsub-table.js +404 -89
- package/dist/images-read.cjs +12 -2
- package/dist/images-read.d.cts +5 -0
- package/dist/images-read.d.ts +5 -0
- package/dist/images-read.js +12 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/layout.cjs +6 -1
- package/dist/layout.d.cts +16 -0
- package/dist/layout.d.ts +16 -0
- package/dist/layout.js +6 -1
- package/dist/read.cjs +9 -4
- package/dist/read.js +9 -4
- package/dist/tounicode.cjs +1 -1
- package/dist/tounicode.js +1 -1
- package/dist/write.cjs +50 -5
- package/dist/write.d.cts +1 -1
- package/dist/write.d.ts +1 -1
- package/dist/write.js +50 -5
- package/package.json +3 -3
- package/dist/gsub-table-BRTQ8EUW.d.ts +0 -10
- package/dist/gsub-table-URczLd85.d.cts +0 -10
package/dist/gsub-table.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { hasBytes, i16, sfntTableBytes, u16, u32 } from "./sfnt.js";
|
|
2
|
-
import { parseCoverage } from "./ot-layout-common.js";
|
|
2
|
+
import { parseClassDef, parseCoverage } from "./ot-layout-common.js";
|
|
3
|
+
import { parseGdefTable } from "./gdef-table.js";
|
|
3
4
|
//#region src/gsub-table.ts
|
|
4
|
-
const
|
|
5
|
+
const DEFAULT_GSUB_FEATURE_TAGS = [
|
|
6
|
+
"liga",
|
|
7
|
+
"rlig",
|
|
8
|
+
"calt",
|
|
9
|
+
"clig"
|
|
10
|
+
];
|
|
11
|
+
const GSUB_OPT_IN_FEATURE_TAGS = ["smcp", "dlig"];
|
|
5
12
|
const GSUB_HEADER_SIZE = 10;
|
|
6
13
|
const SCRIPT_RECORD_SIZE = 6;
|
|
7
14
|
const FEATURE_RECORD_SIZE = 6;
|
|
@@ -10,105 +17,411 @@ const FEATURE_HEADER_SIZE = 4;
|
|
|
10
17
|
const LOOKUP_HEADER_SIZE = 6;
|
|
11
18
|
const LOOKUP_TYPE_SINGLE_SUBST = 1;
|
|
12
19
|
const LOOKUP_TYPE_LIGATURE_SUBST = 4;
|
|
20
|
+
const LOOKUP_TYPE_CONTEXT_SUBST = 5;
|
|
21
|
+
const LOOKUP_TYPE_CHAIN_CONTEXT_SUBST = 6;
|
|
13
22
|
const LOOKUP_TYPE_EXTENSION_SUBST = 7;
|
|
14
23
|
const EXTENSION_SUBST_HEADER_SIZE = 8;
|
|
24
|
+
const LOOKUP_FLAG_IGNORE_BASE_GLYPHS = 2;
|
|
25
|
+
const LOOKUP_FLAG_IGNORE_LIGATURES = 4;
|
|
26
|
+
const LOOKUP_FLAG_IGNORE_MARKS = 8;
|
|
27
|
+
const LOOKUP_FLAG_USE_MARK_FILTERING_SET = 16;
|
|
28
|
+
const LOOKUP_FLAG_MARK_ATTACHMENT_TYPE = 65280;
|
|
29
|
+
const MAX_LOOKUP_NESTING_DEPTH = 6;
|
|
15
30
|
const PREFERRED_SCRIPT_TAGS = ["latn", "DFLT"];
|
|
16
31
|
function readTag(bytes, offset) {
|
|
17
32
|
return String.fromCharCode(u16(bytes, offset) >> 8, u16(bytes, offset) & 255, u16(bytes, offset + 2) >> 8, u16(bytes, offset + 2) & 255);
|
|
18
33
|
}
|
|
34
|
+
function glyphSkipper(lookupFlag, markFilteringSet, gdef) {
|
|
35
|
+
if (gdef === void 0 || lookupFlag === 0) return () => false;
|
|
36
|
+
const ignoreBase = (lookupFlag & LOOKUP_FLAG_IGNORE_BASE_GLYPHS) !== 0;
|
|
37
|
+
const ignoreLigatures = (lookupFlag & LOOKUP_FLAG_IGNORE_LIGATURES) !== 0;
|
|
38
|
+
const ignoreMarks = (lookupFlag & LOOKUP_FLAG_IGNORE_MARKS) !== 0;
|
|
39
|
+
const markAttachmentType = (lookupFlag & LOOKUP_FLAG_MARK_ATTACHMENT_TYPE) >> 8;
|
|
40
|
+
const useMarkFilteringSet = (lookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET) !== 0;
|
|
41
|
+
return (glyphId) => {
|
|
42
|
+
const glyphClass = gdef.glyphClass(glyphId);
|
|
43
|
+
if (ignoreBase && glyphClass === 1) return true;
|
|
44
|
+
if (ignoreLigatures && glyphClass === 2) return true;
|
|
45
|
+
if (glyphClass !== 3) return false;
|
|
46
|
+
if (ignoreMarks) return true;
|
|
47
|
+
if (markAttachmentType !== 0 && gdef.markAttachClass(glyphId) !== markAttachmentType) return true;
|
|
48
|
+
return useMarkFilteringSet && !gdef.markFilteringSetCovers(markFilteringSet, glyphId);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function nextVisibleSlot(slots, from, skip) {
|
|
52
|
+
for (let i = Math.max(from, 0); i < slots.length; i++) if (!skip(slots[i].glyphId)) return i;
|
|
53
|
+
}
|
|
54
|
+
function prevVisibleSlot(slots, before, skip) {
|
|
55
|
+
for (let i = Math.min(before, slots.length) - 1; i >= 0; i--) if (!skip(slots[i].glyphId)) return i;
|
|
56
|
+
}
|
|
19
57
|
const SINGLE_SUBST_FORMAT_1_SIZE = 6;
|
|
20
|
-
function parseSingleSubstFormat1(bytes, subtableOffset) {
|
|
58
|
+
function parseSingleSubstFormat1(bytes, subtableOffset, skip) {
|
|
21
59
|
if (!hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_1_SIZE)) return;
|
|
22
60
|
const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
|
|
23
61
|
if (coverage === void 0) return;
|
|
24
62
|
const delta = i16(bytes, subtableOffset + 4);
|
|
25
|
-
return {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
63
|
+
return (slots, index) => {
|
|
64
|
+
const slot = slots[index];
|
|
65
|
+
if (skip(slot.glyphId)) return;
|
|
66
|
+
if (coverage.coverageIndex(slot.glyphId) === void 0) return;
|
|
67
|
+
return {
|
|
68
|
+
consumed: 1,
|
|
69
|
+
replacement: [{
|
|
70
|
+
glyphId: slot.glyphId + delta,
|
|
71
|
+
span: slot.span
|
|
72
|
+
}]
|
|
73
|
+
};
|
|
31
74
|
};
|
|
32
75
|
}
|
|
33
76
|
const SINGLE_SUBST_FORMAT_2_HEADER_SIZE = 6;
|
|
34
|
-
function parseSingleSubstFormat2(bytes, subtableOffset) {
|
|
77
|
+
function parseSingleSubstFormat2(bytes, subtableOffset, skip) {
|
|
35
78
|
if (!hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_2_HEADER_SIZE)) return;
|
|
36
79
|
const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
|
|
37
80
|
if (coverage === void 0) return;
|
|
38
81
|
const glyphCount = u16(bytes, subtableOffset + 4);
|
|
39
82
|
const substitutesOffset = subtableOffset + SINGLE_SUBST_FORMAT_2_HEADER_SIZE;
|
|
40
83
|
if (!hasBytes(bytes, substitutesOffset, glyphCount * 2)) return;
|
|
41
|
-
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
return (slots, index) => {
|
|
85
|
+
const slot = slots[index];
|
|
86
|
+
if (skip(slot.glyphId)) return;
|
|
87
|
+
const coverageIndex = coverage.coverageIndex(slot.glyphId);
|
|
88
|
+
if (coverageIndex === void 0 || coverageIndex >= glyphCount) return;
|
|
89
|
+
return {
|
|
90
|
+
consumed: 1,
|
|
91
|
+
replacement: [{
|
|
92
|
+
glyphId: u16(bytes, substitutesOffset + coverageIndex * 2),
|
|
93
|
+
span: slot.span
|
|
94
|
+
}]
|
|
95
|
+
};
|
|
48
96
|
};
|
|
49
97
|
}
|
|
50
98
|
const LIGATURE_SUBST_FORMAT_1_HEADER_SIZE = 6;
|
|
51
99
|
const LIGATURE_SET_HEADER_SIZE = 2;
|
|
52
100
|
const LIGATURE_HEADER_PREFIX_SIZE = 4;
|
|
53
|
-
function parseLigatureSubstFormat1(bytes, subtableOffset) {
|
|
101
|
+
function parseLigatureSubstFormat1(bytes, subtableOffset, skip) {
|
|
54
102
|
if (!hasBytes(bytes, subtableOffset, LIGATURE_SUBST_FORMAT_1_HEADER_SIZE)) return;
|
|
55
103
|
const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
|
|
56
104
|
if (coverage === void 0) return;
|
|
57
105
|
const ligSetCount = u16(bytes, subtableOffset + 4);
|
|
58
106
|
const ligSetOffsetsOffset = subtableOffset + LIGATURE_SUBST_FORMAT_1_HEADER_SIZE;
|
|
59
107
|
if (!hasBytes(bytes, ligSetOffsetsOffset, ligSetCount * 2)) return;
|
|
60
|
-
return {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
108
|
+
return (slots, index) => {
|
|
109
|
+
const first = slots[index];
|
|
110
|
+
if (skip(first.glyphId)) return;
|
|
111
|
+
const coverageIndex = coverage.coverageIndex(first.glyphId);
|
|
112
|
+
if (coverageIndex === void 0 || coverageIndex >= ligSetCount) return;
|
|
113
|
+
const ligSetOffset = subtableOffset + u16(bytes, ligSetOffsetsOffset + coverageIndex * 2);
|
|
114
|
+
if (!hasBytes(bytes, ligSetOffset, LIGATURE_SET_HEADER_SIZE)) return;
|
|
115
|
+
const ligatureCount = u16(bytes, ligSetOffset);
|
|
116
|
+
const ligatureOffsetsOffset = ligSetOffset + LIGATURE_SET_HEADER_SIZE;
|
|
117
|
+
if (!hasBytes(bytes, ligatureOffsetsOffset, ligatureCount * 2)) return;
|
|
118
|
+
for (let i = 0; i < ligatureCount; i++) {
|
|
119
|
+
const ligatureOffset = ligSetOffset + u16(bytes, ligatureOffsetsOffset + i * 2);
|
|
120
|
+
if (!hasBytes(bytes, ligatureOffset, LIGATURE_HEADER_PREFIX_SIZE)) continue;
|
|
121
|
+
const ligatureGlyph = u16(bytes, ligatureOffset);
|
|
122
|
+
const componentCount = u16(bytes, ligatureOffset + 2);
|
|
123
|
+
if (componentCount === 0) continue;
|
|
124
|
+
const componentsOffset = ligatureOffset + LIGATURE_HEADER_PREFIX_SIZE;
|
|
125
|
+
if (!hasBytes(bytes, componentsOffset, (componentCount - 1) * 2)) continue;
|
|
126
|
+
const matchedSlots = [index];
|
|
127
|
+
let cursor = index;
|
|
128
|
+
let matches = true;
|
|
129
|
+
for (let c = 1; c < componentCount; c++) {
|
|
130
|
+
const next = nextVisibleSlot(slots, cursor + 1, skip);
|
|
131
|
+
if (next === void 0 || slots[next].glyphId !== u16(bytes, componentsOffset + (c - 1) * 2)) {
|
|
81
132
|
matches = false;
|
|
82
133
|
break;
|
|
83
134
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
135
|
+
matchedSlots.push(next);
|
|
136
|
+
cursor = next;
|
|
137
|
+
}
|
|
138
|
+
if (!matches) continue;
|
|
139
|
+
const lastSlot = matchedSlots[matchedSlots.length - 1];
|
|
140
|
+
let windowSpan = 0;
|
|
141
|
+
const retained = [];
|
|
142
|
+
for (let s = index; s <= lastSlot; s++) {
|
|
143
|
+
windowSpan += slots[s].span;
|
|
144
|
+
if (skip(slots[s].glyphId)) retained.push({
|
|
145
|
+
glyphId: slots[s].glyphId,
|
|
146
|
+
span: 0
|
|
147
|
+
});
|
|
88
148
|
}
|
|
149
|
+
return {
|
|
150
|
+
consumed: lastSlot - index + 1,
|
|
151
|
+
replacement: [{
|
|
152
|
+
glyphId: ligatureGlyph,
|
|
153
|
+
span: windowSpan
|
|
154
|
+
}, ...retained]
|
|
155
|
+
};
|
|
89
156
|
}
|
|
90
157
|
};
|
|
91
158
|
}
|
|
92
|
-
function
|
|
159
|
+
function matchContextualRule(slots, index, skip, backtrack, inputTests, lookahead) {
|
|
160
|
+
const inputSlots = [index];
|
|
161
|
+
let cursor = index;
|
|
162
|
+
for (const test of inputTests) {
|
|
163
|
+
const next = nextVisibleSlot(slots, cursor + 1, skip);
|
|
164
|
+
if (next === void 0 || !test(slots[next].glyphId)) return;
|
|
165
|
+
inputSlots.push(next);
|
|
166
|
+
cursor = next;
|
|
167
|
+
}
|
|
168
|
+
let back = index;
|
|
169
|
+
for (let b = backtrack.length - 1; b >= 0; b--) {
|
|
170
|
+
const previous = prevVisibleSlot(slots, back, skip);
|
|
171
|
+
if (previous === void 0 || !backtrack[b](slots[previous].glyphId)) return;
|
|
172
|
+
back = previous;
|
|
173
|
+
}
|
|
174
|
+
let ahead = inputSlots[inputSlots.length - 1];
|
|
175
|
+
for (const test of lookahead) {
|
|
176
|
+
const next = nextVisibleSlot(slots, ahead + 1, skip);
|
|
177
|
+
if (next === void 0 || !test(slots[next].glyphId)) return;
|
|
178
|
+
ahead = next;
|
|
179
|
+
}
|
|
180
|
+
return inputSlots;
|
|
181
|
+
}
|
|
182
|
+
function applyContextualRule(slots, inputSlots, records, lookupOf, skip, depth) {
|
|
183
|
+
const first = inputSlots[0];
|
|
184
|
+
const last = inputSlots[inputSlots.length - 1];
|
|
185
|
+
const window = [];
|
|
186
|
+
for (let s = first; s <= last; s++) window.push({
|
|
187
|
+
glyphId: slots[s].glyphId,
|
|
188
|
+
span: slots[s].span
|
|
189
|
+
});
|
|
190
|
+
for (const record of records) {
|
|
191
|
+
const lookup = lookupOf(record.lookupIndex);
|
|
192
|
+
if (lookup === void 0 || record.sequenceIndex >= inputSlots.length || depth >= MAX_LOOKUP_NESTING_DEPTH) return;
|
|
193
|
+
const target = visibleInputSlot(window, record.sequenceIndex, skip);
|
|
194
|
+
if (target !== void 0) applyLookupAtSlot(window, target, lookup, depth + 1);
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
consumed: last - first + 1,
|
|
198
|
+
replacement: window
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function visibleInputSlot(window, position, skip) {
|
|
202
|
+
let seen = 0;
|
|
203
|
+
for (let i = 0; i < window.length; i++) {
|
|
204
|
+
if (skip(window[i].glyphId)) continue;
|
|
205
|
+
if (seen === position) return i;
|
|
206
|
+
seen += 1;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function applyLookupAtSlot(slots, index, lookup, depth) {
|
|
210
|
+
for (const subtable of lookup.subtables) {
|
|
211
|
+
const application = subtable(slots, index, depth);
|
|
212
|
+
if (application !== void 0) {
|
|
213
|
+
slots.splice(index, application.consumed, ...application.replacement);
|
|
214
|
+
return application.replacement.length;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
const SUBST_LOOKUP_RECORD_SIZE = 4;
|
|
220
|
+
function readSubstLookupRecords(bytes, offset, count) {
|
|
221
|
+
if (!hasBytes(bytes, offset, count * SUBST_LOOKUP_RECORD_SIZE)) return;
|
|
222
|
+
const records = [];
|
|
223
|
+
for (let i = 0; i < count; i++) {
|
|
224
|
+
const recordOffset = offset + i * SUBST_LOOKUP_RECORD_SIZE;
|
|
225
|
+
records.push({
|
|
226
|
+
sequenceIndex: u16(bytes, recordOffset),
|
|
227
|
+
lookupIndex: u16(bytes, recordOffset + 2)
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
return records;
|
|
231
|
+
}
|
|
232
|
+
function parseFormat3Rule(bytes, subtableOffset, chained) {
|
|
233
|
+
let offset = subtableOffset + 2;
|
|
234
|
+
const backtrack = [];
|
|
235
|
+
const input = [];
|
|
236
|
+
const lookahead = [];
|
|
237
|
+
const readCoverageArray = () => {
|
|
238
|
+
if (!hasBytes(bytes, offset, 2)) return;
|
|
239
|
+
const count = u16(bytes, offset);
|
|
240
|
+
offset += 2;
|
|
241
|
+
if (!hasBytes(bytes, offset, count * 2)) return;
|
|
242
|
+
const coverages = [];
|
|
243
|
+
for (let i = 0; i < count; i++) {
|
|
244
|
+
const coverageOffset = u16(bytes, offset + i * 2);
|
|
245
|
+
if (coverageOffset === 0) return;
|
|
246
|
+
const coverage = parseCoverage(bytes, subtableOffset + coverageOffset);
|
|
247
|
+
if (coverage === void 0) return;
|
|
248
|
+
coverages.push(coverage);
|
|
249
|
+
}
|
|
250
|
+
offset += count * 2;
|
|
251
|
+
return coverages;
|
|
252
|
+
};
|
|
253
|
+
if (chained) {
|
|
254
|
+
const backtracks = readCoverageArray();
|
|
255
|
+
if (backtracks === void 0) return;
|
|
256
|
+
backtrack.push(...backtracks);
|
|
257
|
+
}
|
|
258
|
+
{
|
|
259
|
+
const inputs = readCoverageArray();
|
|
260
|
+
if (inputs === void 0 || inputs.length === 0) return;
|
|
261
|
+
input.push(...inputs);
|
|
262
|
+
}
|
|
263
|
+
if (chained) {
|
|
264
|
+
const lookaheads = readCoverageArray();
|
|
265
|
+
if (lookaheads === void 0) return;
|
|
266
|
+
lookahead.push(...lookaheads);
|
|
267
|
+
}
|
|
268
|
+
if (!hasBytes(bytes, offset, 2)) return;
|
|
269
|
+
const substCount = u16(bytes, offset);
|
|
270
|
+
const records = readSubstLookupRecords(bytes, offset + 2, substCount);
|
|
271
|
+
if (records === void 0) return;
|
|
272
|
+
return {
|
|
273
|
+
backtrack,
|
|
274
|
+
input,
|
|
275
|
+
lookahead,
|
|
276
|
+
records
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function format3Subtable(rule, skip, lookupOf) {
|
|
280
|
+
const inputRest = [];
|
|
281
|
+
for (let i = 1; i < rule.input.length; i++) {
|
|
282
|
+
const coverage = rule.input[i];
|
|
283
|
+
inputRest.push((glyphId) => coverage.coverageIndex(glyphId) !== void 0);
|
|
284
|
+
}
|
|
285
|
+
const backtrack = rule.backtrack.map((coverage) => (glyphId) => coverage.coverageIndex(glyphId) !== void 0);
|
|
286
|
+
const lookahead = rule.lookahead.map((coverage) => (glyphId) => coverage.coverageIndex(glyphId) !== void 0);
|
|
287
|
+
return (slots, index, depth) => {
|
|
288
|
+
const slot = slots[index];
|
|
289
|
+
if (skip(slot.glyphId)) return;
|
|
290
|
+
if (rule.input[0].coverageIndex(slot.glyphId) === void 0) return;
|
|
291
|
+
const inputSlots = matchContextualRule(slots, index, skip, backtrack, inputRest, lookahead);
|
|
292
|
+
if (inputSlots === void 0) return;
|
|
293
|
+
return applyContextualRule(slots, inputSlots, rule.records, lookupOf, skip, depth);
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function readSequenceRule(bytes, ruleOffset, chained) {
|
|
297
|
+
let offset = ruleOffset;
|
|
298
|
+
const readU16 = () => {
|
|
299
|
+
if (!hasBytes(bytes, offset, 2)) return;
|
|
300
|
+
const value = u16(bytes, offset);
|
|
301
|
+
offset += 2;
|
|
302
|
+
return value;
|
|
303
|
+
};
|
|
304
|
+
const readArray = () => {
|
|
305
|
+
const count = readU16();
|
|
306
|
+
if (count === void 0 || !hasBytes(bytes, offset, count * 2)) return;
|
|
307
|
+
const values = [];
|
|
308
|
+
for (let i = 0; i < count; i++) values.push(u16(bytes, offset + i * 2));
|
|
309
|
+
offset += count * 2;
|
|
310
|
+
return values;
|
|
311
|
+
};
|
|
312
|
+
const backtrack = chained ? readArray() : [];
|
|
313
|
+
const glyphCount = readU16();
|
|
314
|
+
if (backtrack === void 0 || glyphCount === void 0 || glyphCount === 0 || !hasBytes(bytes, offset, (glyphCount - 1) * 2)) return;
|
|
315
|
+
const input = [];
|
|
316
|
+
for (let i = 0; i < glyphCount - 1; i++) input.push(u16(bytes, offset + i * 2));
|
|
317
|
+
offset += (glyphCount - 1) * 2;
|
|
318
|
+
const lookahead = chained ? readArray() : [];
|
|
319
|
+
const substCount = readU16();
|
|
320
|
+
if (lookahead === void 0 || substCount === void 0) return;
|
|
321
|
+
const records = readSubstLookupRecords(bytes, offset, substCount);
|
|
322
|
+
if (records === void 0) return;
|
|
323
|
+
return {
|
|
324
|
+
backtrack,
|
|
325
|
+
input,
|
|
326
|
+
lookahead,
|
|
327
|
+
records
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function sequenceSubtable(bytes, subtableOffset, coverage, setCount, setOffsetsOffset, chained, access, skip, lookupOf) {
|
|
331
|
+
const readSet = (entryGlyphId) => {
|
|
332
|
+
const setIndex = access.setIndexOf(entryGlyphId);
|
|
333
|
+
if (setIndex === void 0 || setIndex >= setCount) return [];
|
|
334
|
+
const setOffset = u16(bytes, setOffsetsOffset + setIndex * 2);
|
|
335
|
+
if (setOffset === 0) return [];
|
|
336
|
+
const ruleSetOffset = subtableOffset + setOffset;
|
|
337
|
+
if (!hasBytes(bytes, ruleSetOffset, 2)) return [];
|
|
338
|
+
const ruleCount = u16(bytes, ruleSetOffset);
|
|
339
|
+
if (!hasBytes(bytes, ruleSetOffset + 2, ruleCount * 2)) return [];
|
|
340
|
+
const rules = [];
|
|
341
|
+
for (let i = 0; i < ruleCount; i++) {
|
|
342
|
+
const rule = readSequenceRule(bytes, ruleSetOffset + u16(bytes, ruleSetOffset + 2 + i * 2), chained);
|
|
343
|
+
if (rule !== void 0) rules.push(rule);
|
|
344
|
+
}
|
|
345
|
+
return rules;
|
|
346
|
+
};
|
|
347
|
+
return (slots, index, depth) => {
|
|
348
|
+
const slot = slots[index];
|
|
349
|
+
if (skip(slot.glyphId)) return;
|
|
350
|
+
if (coverage.coverageIndex(slot.glyphId) === void 0) return;
|
|
351
|
+
for (const rule of readSet(slot.glyphId)) {
|
|
352
|
+
const inputSlots = matchContextualRule(slots, index, skip, rule.backtrack.map(access.backtrackTest), rule.input.map(access.inputTest), rule.lookahead.map(access.lookaheadTest));
|
|
353
|
+
if (inputSlots !== void 0) return applyContextualRule(slots, inputSlots, rule.records, lookupOf, skip, depth);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function parseSubtable(bytes, lookupType, subtableOffset, skip, lookupOf) {
|
|
93
358
|
if (lookupType === LOOKUP_TYPE_EXTENSION_SUBST) {
|
|
94
359
|
if (!hasBytes(bytes, subtableOffset, EXTENSION_SUBST_HEADER_SIZE) || u16(bytes, subtableOffset) !== 1) return;
|
|
95
360
|
const extensionLookupType = u16(bytes, subtableOffset + 2);
|
|
96
361
|
if (extensionLookupType === LOOKUP_TYPE_EXTENSION_SUBST) return;
|
|
97
|
-
return parseSubtable(bytes, extensionLookupType, subtableOffset + u32(bytes, subtableOffset + 4));
|
|
362
|
+
return parseSubtable(bytes, extensionLookupType, subtableOffset + u32(bytes, subtableOffset + 4), skip, lookupOf);
|
|
98
363
|
}
|
|
99
364
|
if (lookupType === LOOKUP_TYPE_SINGLE_SUBST) {
|
|
100
365
|
if (!hasBytes(bytes, subtableOffset, 2)) return;
|
|
101
366
|
const substFormat = u16(bytes, subtableOffset);
|
|
102
|
-
if (substFormat === 1) return parseSingleSubstFormat1(bytes, subtableOffset);
|
|
103
|
-
if (substFormat === 2) return parseSingleSubstFormat2(bytes, subtableOffset);
|
|
367
|
+
if (substFormat === 1) return parseSingleSubstFormat1(bytes, subtableOffset, skip);
|
|
368
|
+
if (substFormat === 2) return parseSingleSubstFormat2(bytes, subtableOffset, skip);
|
|
104
369
|
return;
|
|
105
370
|
}
|
|
106
371
|
if (lookupType === LOOKUP_TYPE_LIGATURE_SUBST) {
|
|
107
372
|
if (!hasBytes(bytes, subtableOffset, 2)) return;
|
|
108
373
|
if (u16(bytes, subtableOffset) !== 1) return;
|
|
109
|
-
return parseLigatureSubstFormat1(bytes, subtableOffset);
|
|
374
|
+
return parseLigatureSubstFormat1(bytes, subtableOffset, skip);
|
|
375
|
+
}
|
|
376
|
+
if (lookupType === LOOKUP_TYPE_CONTEXT_SUBST || lookupType === LOOKUP_TYPE_CHAIN_CONTEXT_SUBST) return parseContextualSubtable(bytes, lookupType === LOOKUP_TYPE_CHAIN_CONTEXT_SUBST, subtableOffset, skip, lookupOf);
|
|
377
|
+
}
|
|
378
|
+
function parseContextualSubtable(bytes, chained, subtableOffset, skip, lookupOf) {
|
|
379
|
+
if (!hasBytes(bytes, subtableOffset, 2)) return;
|
|
380
|
+
const substFormat = u16(bytes, subtableOffset);
|
|
381
|
+
if (substFormat === 3) {
|
|
382
|
+
const rule = parseFormat3Rule(bytes, subtableOffset, chained);
|
|
383
|
+
return rule === void 0 ? void 0 : format3Subtable(rule, skip, lookupOf);
|
|
384
|
+
}
|
|
385
|
+
if (substFormat === 1) {
|
|
386
|
+
const prefixSize = 6;
|
|
387
|
+
if (!hasBytes(bytes, subtableOffset, prefixSize)) return;
|
|
388
|
+
const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
|
|
389
|
+
if (coverage === void 0) return;
|
|
390
|
+
const setCount = u16(bytes, subtableOffset + 4);
|
|
391
|
+
const setOffsetsOffset = subtableOffset + prefixSize;
|
|
392
|
+
if (!hasBytes(bytes, setOffsetsOffset, setCount * 2)) return;
|
|
393
|
+
const glyphEquality = (expected) => (glyphId) => glyphId === expected;
|
|
394
|
+
return sequenceSubtable(bytes, subtableOffset, coverage, setCount, setOffsetsOffset, chained, {
|
|
395
|
+
setIndexOf: (entryGlyphId) => coverage.coverageIndex(entryGlyphId),
|
|
396
|
+
backtrackTest: glyphEquality,
|
|
397
|
+
inputTest: glyphEquality,
|
|
398
|
+
lookaheadTest: glyphEquality
|
|
399
|
+
}, skip, lookupOf);
|
|
400
|
+
}
|
|
401
|
+
if (substFormat === 2) {
|
|
402
|
+
const prefixSize = chained ? 12 : 8;
|
|
403
|
+
if (!hasBytes(bytes, subtableOffset, prefixSize)) return;
|
|
404
|
+
const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
|
|
405
|
+
if (coverage === void 0) return;
|
|
406
|
+
const inputClassDefOffset = chained ? u16(bytes, subtableOffset + 6) : u16(bytes, subtableOffset + 4);
|
|
407
|
+
const inputClassDef = inputClassDefOffset === 0 ? constantZeroClassDef : parseClassDef(bytes, subtableOffset + inputClassDefOffset);
|
|
408
|
+
const readAuxClassDef = (offset) => offset === 0 ? constantZeroClassDef : parseClassDef(bytes, subtableOffset + offset);
|
|
409
|
+
const backtrackClassDef = chained ? readAuxClassDef(u16(bytes, subtableOffset + 4)) : constantZeroClassDef;
|
|
410
|
+
const lookaheadClassDef = chained ? readAuxClassDef(u16(bytes, subtableOffset + 8)) : constantZeroClassDef;
|
|
411
|
+
if (inputClassDef === void 0 || backtrackClassDef === void 0 || lookaheadClassDef === void 0) return;
|
|
412
|
+
const setCount = u16(bytes, subtableOffset + prefixSize - 2);
|
|
413
|
+
const setOffsetsOffset = subtableOffset + prefixSize;
|
|
414
|
+
if (!hasBytes(bytes, setOffsetsOffset, setCount * 2)) return;
|
|
415
|
+
const classEquality = (classDef, expected) => (glyphId) => classDef(glyphId) === expected;
|
|
416
|
+
return sequenceSubtable(bytes, subtableOffset, coverage, setCount, setOffsetsOffset, chained, {
|
|
417
|
+
setIndexOf: (entryGlyphId) => inputClassDef(entryGlyphId),
|
|
418
|
+
backtrackTest: (expected) => classEquality(backtrackClassDef, expected),
|
|
419
|
+
inputTest: (expected) => classEquality(inputClassDef, expected),
|
|
420
|
+
lookaheadTest: (expected) => classEquality(lookaheadClassDef, expected)
|
|
421
|
+
}, skip, lookupOf);
|
|
110
422
|
}
|
|
111
423
|
}
|
|
424
|
+
const constantZeroClassDef = () => 0;
|
|
112
425
|
function findScriptOffset(bytes, scriptListOffset) {
|
|
113
426
|
if (!hasBytes(bytes, scriptListOffset, 2)) return;
|
|
114
427
|
const scriptCount = u16(bytes, scriptListOffset);
|
|
@@ -133,7 +446,7 @@ function parseDefaultLangSysFeatureIndices(bytes, scriptOffset) {
|
|
|
133
446
|
for (let i = 0; i < featureIndexCount; i++) indices.push(u16(bytes, indicesOffset + i * 2));
|
|
134
447
|
return indices;
|
|
135
448
|
}
|
|
136
|
-
function
|
|
449
|
+
function collectLookupIndices(bytes, featureListOffset, featureIndices, featureTags) {
|
|
137
450
|
if (!hasBytes(bytes, featureListOffset, 2)) return [];
|
|
138
451
|
const featureCount = u16(bytes, featureListOffset);
|
|
139
452
|
const recordsOffset = featureListOffset + 2;
|
|
@@ -143,8 +456,7 @@ function collectLigatureLookupIndices(bytes, featureListOffset, featureIndices)
|
|
|
143
456
|
for (const featureIndex of featureIndices) {
|
|
144
457
|
if (featureIndex >= featureCount) continue;
|
|
145
458
|
const recordOffset = recordsOffset + featureIndex * FEATURE_RECORD_SIZE;
|
|
146
|
-
|
|
147
|
-
if (!LIGATURE_FEATURE_TAGS.includes(tag)) continue;
|
|
459
|
+
if (!featureTags.includes(readTag(bytes, recordOffset))) continue;
|
|
148
460
|
const featureOffset = featureListOffset + u16(bytes, recordOffset + 4);
|
|
149
461
|
if (!hasBytes(bytes, featureOffset, FEATURE_HEADER_SIZE)) continue;
|
|
150
462
|
const lookupIndexCount = u16(bytes, featureOffset + 2);
|
|
@@ -160,65 +472,68 @@ function collectLigatureLookupIndices(bytes, featureListOffset, featureIndices)
|
|
|
160
472
|
}
|
|
161
473
|
return lookupIndices;
|
|
162
474
|
}
|
|
163
|
-
function buildGsubShaper(font) {
|
|
475
|
+
function buildGsubShaper(font, options = {}) {
|
|
164
476
|
const bytes = sfntTableBytes(font, "GSUB");
|
|
165
477
|
if (bytes === void 0 || !hasBytes(bytes, 0, GSUB_HEADER_SIZE) || u16(bytes, 0) !== 1) return;
|
|
166
478
|
const scriptOffset = findScriptOffset(bytes, u16(bytes, 4));
|
|
167
479
|
if (scriptOffset === void 0) return;
|
|
168
|
-
const
|
|
480
|
+
const featureTags = [...DEFAULT_GSUB_FEATURE_TAGS, ...options.optInFeatures ?? []];
|
|
481
|
+
const lookupIndices = collectLookupIndices(bytes, u16(bytes, 6), parseDefaultLangSysFeatureIndices(bytes, scriptOffset), featureTags);
|
|
169
482
|
if (lookupIndices.length === 0) return;
|
|
170
483
|
const lookupListOffset = u16(bytes, 8);
|
|
171
484
|
if (!hasBytes(bytes, lookupListOffset, 2)) return;
|
|
172
485
|
const lookupCount = u16(bytes, lookupListOffset);
|
|
173
486
|
const lookupOffsetsOffset = lookupListOffset + 2;
|
|
174
487
|
if (!hasBytes(bytes, lookupOffsetsOffset, lookupCount * 2)) return;
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (
|
|
488
|
+
const gdef = parseGdefTable(font);
|
|
489
|
+
const lookups = [];
|
|
490
|
+
const lookupOf = (lookupIndex) => lookups[lookupIndex];
|
|
491
|
+
for (let i = 0; i < lookupCount; i++) {
|
|
492
|
+
const lookupOffset = lookupListOffset + u16(bytes, lookupOffsetsOffset + i * 2);
|
|
493
|
+
if (!hasBytes(bytes, lookupOffset, LOOKUP_HEADER_SIZE)) {
|
|
494
|
+
lookups.push(void 0);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
const lookupFlag = u16(bytes, lookupOffset + 2);
|
|
181
498
|
const lookupType = u16(bytes, lookupOffset);
|
|
182
499
|
const subTableCount = u16(bytes, lookupOffset + 4);
|
|
183
500
|
const subtableOffsetsOffset = lookupOffset + LOOKUP_HEADER_SIZE;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
501
|
+
const markFilteringSetWidth = (lookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET) !== 0 ? 2 : 0;
|
|
502
|
+
if (!hasBytes(bytes, subtableOffsetsOffset, subTableCount * 2 + markFilteringSetWidth)) {
|
|
503
|
+
lookups.push(void 0);
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
const skip = glyphSkipper(lookupFlag, markFilteringSetWidth === 0 ? 0 : u16(bytes, subtableOffsetsOffset + subTableCount * 2), gdef);
|
|
507
|
+
const subtables = [];
|
|
508
|
+
for (let s = 0; s < subTableCount; s++) {
|
|
509
|
+
const subtable = parseSubtable(bytes, lookupType, lookupOffset + u16(bytes, subtableOffsetsOffset + s * 2), skip, lookupOf);
|
|
187
510
|
if (subtable !== void 0) subtables.push(subtable);
|
|
188
511
|
}
|
|
512
|
+
lookups.push({ subtables });
|
|
189
513
|
}
|
|
190
|
-
|
|
514
|
+
const appliedLookups = [];
|
|
515
|
+
for (const lookupIndex of lookupIndices) {
|
|
516
|
+
const lookup = lookupOf(lookupIndex);
|
|
517
|
+
if (lookup !== void 0) appliedLookups.push(lookup);
|
|
518
|
+
}
|
|
519
|
+
if (appliedLookups.every((lookup) => lookup.subtables.length === 0)) return;
|
|
191
520
|
return (glyphIds) => {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
index += 1;
|
|
202
|
-
continue outer;
|
|
203
|
-
}
|
|
204
|
-
} else {
|
|
205
|
-
const match = subtable.match(glyphIds, index);
|
|
206
|
-
if (match !== void 0) {
|
|
207
|
-
shaped.push(match.ligatureGlyph);
|
|
208
|
-
spans.push(match.componentCount);
|
|
209
|
-
index += match.componentCount;
|
|
210
|
-
continue outer;
|
|
211
|
-
}
|
|
521
|
+
const slots = glyphIds.map((glyphId) => ({
|
|
522
|
+
glyphId,
|
|
523
|
+
span: 1
|
|
524
|
+
}));
|
|
525
|
+
for (const lookup of appliedLookups) {
|
|
526
|
+
let slotIndex = 0;
|
|
527
|
+
while (slotIndex < slots.length) {
|
|
528
|
+
const advanced = applyLookupAtSlot(slots, slotIndex, lookup, 0);
|
|
529
|
+
slotIndex += advanced !== 0 ? advanced : 1;
|
|
212
530
|
}
|
|
213
|
-
shaped.push(glyphIds[index]);
|
|
214
|
-
spans.push(1);
|
|
215
|
-
index += 1;
|
|
216
531
|
}
|
|
217
532
|
return {
|
|
218
|
-
glyphIds:
|
|
219
|
-
spans
|
|
533
|
+
glyphIds: slots.map((slot) => slot.glyphId),
|
|
534
|
+
spans: slots.map((slot) => slot.span)
|
|
220
535
|
};
|
|
221
536
|
};
|
|
222
537
|
}
|
|
223
538
|
//#endregion
|
|
224
|
-
export { buildGsubShaper };
|
|
539
|
+
export { DEFAULT_GSUB_FEATURE_TAGS, GSUB_OPT_IN_FEATURE_TAGS, buildGsubShaper };
|
package/dist/images-read.cjs
CHANGED
|
@@ -249,7 +249,11 @@ function readJpeg2000Image(dict, raw, resolver, sink) {
|
|
|
249
249
|
format: "png",
|
|
250
250
|
bytes: (0, byte_codec.encodePng)(withAlpha),
|
|
251
251
|
widthPx: image.width,
|
|
252
|
-
heightPx: image.height
|
|
252
|
+
heightPx: image.height,
|
|
253
|
+
original: {
|
|
254
|
+
filter: "jpeg2000",
|
|
255
|
+
bytes: raw
|
|
256
|
+
}
|
|
253
257
|
};
|
|
254
258
|
}
|
|
255
259
|
function readImageXObject(dict, raw, resolver, sink) {
|
|
@@ -262,6 +266,11 @@ function readImageXObject(dict, raw, resolver, sink) {
|
|
|
262
266
|
return;
|
|
263
267
|
}
|
|
264
268
|
const decoded = require_filters.decodeStream(raw, dict, sink, (obj) => resolver.resolve(obj));
|
|
269
|
+
const jbig2Original = decoded.jbig2 === void 0 ? void 0 : {
|
|
270
|
+
filter: "jbig2",
|
|
271
|
+
bytes: decoded.jbig2.encoded,
|
|
272
|
+
...decoded.jbig2.globals !== void 0 ? { globalsBytes: decoded.jbig2.globals } : {}
|
|
273
|
+
};
|
|
265
274
|
if (decoded.remainingFilter === "DCTDecode") {
|
|
266
275
|
let info;
|
|
267
276
|
try {
|
|
@@ -323,7 +332,8 @@ function readImageXObject(dict, raw, resolver, sink) {
|
|
|
323
332
|
format: "png",
|
|
324
333
|
bytes: (0, byte_codec.encodePng)(withAlpha),
|
|
325
334
|
widthPx: width,
|
|
326
|
-
heightPx: height
|
|
335
|
+
heightPx: height,
|
|
336
|
+
...jbig2Original !== void 0 ? { original: jbig2Original } : {}
|
|
327
337
|
};
|
|
328
338
|
}
|
|
329
339
|
//#endregion
|
package/dist/images-read.d.cts
CHANGED
|
@@ -7,6 +7,11 @@ interface ExtractedPdfImage {
|
|
|
7
7
|
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
8
8
|
readonly widthPx: number;
|
|
9
9
|
readonly heightPx: number;
|
|
10
|
+
readonly original?: {
|
|
11
|
+
readonly filter: "jbig2" | "jpeg2000";
|
|
12
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
13
|
+
readonly globalsBytes?: Uint8Array<ArrayBuffer>;
|
|
14
|
+
};
|
|
10
15
|
}
|
|
11
16
|
declare function readImageXObject(dict: PdfDict, raw: Uint8Array<ArrayBuffer>, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): ExtractedPdfImage | undefined;
|
|
12
17
|
//#endregion
|
package/dist/images-read.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ interface ExtractedPdfImage {
|
|
|
7
7
|
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
8
8
|
readonly widthPx: number;
|
|
9
9
|
readonly heightPx: number;
|
|
10
|
+
readonly original?: {
|
|
11
|
+
readonly filter: "jbig2" | "jpeg2000";
|
|
12
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
13
|
+
readonly globalsBytes?: Uint8Array<ArrayBuffer>;
|
|
14
|
+
};
|
|
10
15
|
}
|
|
11
16
|
declare function readImageXObject(dict: PdfDict, raw: Uint8Array<ArrayBuffer>, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): ExtractedPdfImage | undefined;
|
|
12
17
|
//#endregion
|