pdf-codec 4.2.0 → 4.4.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.
@@ -0,0 +1,225 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_sfnt = require("./sfnt.cjs");
3
+ const require_ot_layout_common = require("./ot-layout-common.cjs");
4
+ //#region src/gsub-table.ts
5
+ const LIGATURE_FEATURE_TAGS = ["liga", "rlig"];
6
+ const GSUB_HEADER_SIZE = 10;
7
+ const SCRIPT_RECORD_SIZE = 6;
8
+ const FEATURE_RECORD_SIZE = 6;
9
+ const LANG_SYS_HEADER_SIZE = 6;
10
+ const FEATURE_HEADER_SIZE = 4;
11
+ const LOOKUP_HEADER_SIZE = 6;
12
+ const LOOKUP_TYPE_SINGLE_SUBST = 1;
13
+ const LOOKUP_TYPE_LIGATURE_SUBST = 4;
14
+ const LOOKUP_TYPE_EXTENSION_SUBST = 7;
15
+ const EXTENSION_SUBST_HEADER_SIZE = 8;
16
+ const PREFERRED_SCRIPT_TAGS = ["latn", "DFLT"];
17
+ function readTag(bytes, offset) {
18
+ return String.fromCharCode(require_sfnt.u16(bytes, offset) >> 8, require_sfnt.u16(bytes, offset) & 255, require_sfnt.u16(bytes, offset + 2) >> 8, require_sfnt.u16(bytes, offset + 2) & 255);
19
+ }
20
+ const SINGLE_SUBST_FORMAT_1_SIZE = 6;
21
+ function parseSingleSubstFormat1(bytes, subtableOffset) {
22
+ if (!require_sfnt.hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_1_SIZE)) return;
23
+ const coverage = require_ot_layout_common.parseCoverage(bytes, subtableOffset + require_sfnt.u16(bytes, subtableOffset + 2));
24
+ if (coverage === void 0) return;
25
+ const delta = require_sfnt.i16(bytes, subtableOffset + 4);
26
+ return {
27
+ kind: "single",
28
+ lookup: (glyphId) => {
29
+ if (coverage.coverageIndex(glyphId) === void 0) return;
30
+ return glyphId + delta;
31
+ }
32
+ };
33
+ }
34
+ const SINGLE_SUBST_FORMAT_2_HEADER_SIZE = 6;
35
+ function parseSingleSubstFormat2(bytes, subtableOffset) {
36
+ if (!require_sfnt.hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_2_HEADER_SIZE)) return;
37
+ const coverage = require_ot_layout_common.parseCoverage(bytes, subtableOffset + require_sfnt.u16(bytes, subtableOffset + 2));
38
+ if (coverage === void 0) return;
39
+ const glyphCount = require_sfnt.u16(bytes, subtableOffset + 4);
40
+ const substitutesOffset = subtableOffset + SINGLE_SUBST_FORMAT_2_HEADER_SIZE;
41
+ if (!require_sfnt.hasBytes(bytes, substitutesOffset, glyphCount * 2)) return;
42
+ return {
43
+ kind: "single",
44
+ lookup: (glyphId) => {
45
+ const coverageIndex = coverage.coverageIndex(glyphId);
46
+ if (coverageIndex === void 0 || coverageIndex >= glyphCount) return;
47
+ return require_sfnt.u16(bytes, substitutesOffset + coverageIndex * 2);
48
+ }
49
+ };
50
+ }
51
+ const LIGATURE_SUBST_FORMAT_1_HEADER_SIZE = 6;
52
+ const LIGATURE_SET_HEADER_SIZE = 2;
53
+ const LIGATURE_HEADER_PREFIX_SIZE = 4;
54
+ function parseLigatureSubstFormat1(bytes, subtableOffset) {
55
+ if (!require_sfnt.hasBytes(bytes, subtableOffset, LIGATURE_SUBST_FORMAT_1_HEADER_SIZE)) return;
56
+ const coverage = require_ot_layout_common.parseCoverage(bytes, subtableOffset + require_sfnt.u16(bytes, subtableOffset + 2));
57
+ if (coverage === void 0) return;
58
+ const ligSetCount = require_sfnt.u16(bytes, subtableOffset + 4);
59
+ const ligSetOffsetsOffset = subtableOffset + LIGATURE_SUBST_FORMAT_1_HEADER_SIZE;
60
+ if (!require_sfnt.hasBytes(bytes, ligSetOffsetsOffset, ligSetCount * 2)) return;
61
+ return {
62
+ kind: "ligature",
63
+ match: (glyphIds, index) => {
64
+ const coverageIndex = coverage.coverageIndex(glyphIds[index]);
65
+ if (coverageIndex === void 0 || coverageIndex >= ligSetCount) return;
66
+ const ligSetOffset = subtableOffset + require_sfnt.u16(bytes, ligSetOffsetsOffset + coverageIndex * 2);
67
+ if (!require_sfnt.hasBytes(bytes, ligSetOffset, LIGATURE_SET_HEADER_SIZE)) return;
68
+ const ligatureCount = require_sfnt.u16(bytes, ligSetOffset);
69
+ const ligatureOffsetsOffset = ligSetOffset + LIGATURE_SET_HEADER_SIZE;
70
+ if (!require_sfnt.hasBytes(bytes, ligatureOffsetsOffset, ligatureCount * 2)) return;
71
+ for (let i = 0; i < ligatureCount; i++) {
72
+ const ligatureOffset = ligSetOffset + require_sfnt.u16(bytes, ligatureOffsetsOffset + i * 2);
73
+ if (!require_sfnt.hasBytes(bytes, ligatureOffset, LIGATURE_HEADER_PREFIX_SIZE)) continue;
74
+ const ligatureGlyph = require_sfnt.u16(bytes, ligatureOffset);
75
+ const componentCount = require_sfnt.u16(bytes, ligatureOffset + 2);
76
+ if (componentCount === 0) continue;
77
+ const componentsOffset = ligatureOffset + LIGATURE_HEADER_PREFIX_SIZE;
78
+ if (!require_sfnt.hasBytes(bytes, componentsOffset, (componentCount - 1) * 2)) continue;
79
+ if (index + componentCount > glyphIds.length) continue;
80
+ let matches = true;
81
+ for (let c = 1; c < componentCount; c++) if (glyphIds[index + c] !== require_sfnt.u16(bytes, componentsOffset + (c - 1) * 2)) {
82
+ matches = false;
83
+ break;
84
+ }
85
+ if (matches) return {
86
+ ligatureGlyph,
87
+ componentCount
88
+ };
89
+ }
90
+ }
91
+ };
92
+ }
93
+ function parseSubtable(bytes, lookupType, subtableOffset) {
94
+ if (lookupType === LOOKUP_TYPE_EXTENSION_SUBST) {
95
+ if (!require_sfnt.hasBytes(bytes, subtableOffset, EXTENSION_SUBST_HEADER_SIZE) || require_sfnt.u16(bytes, subtableOffset) !== 1) return;
96
+ const extensionLookupType = require_sfnt.u16(bytes, subtableOffset + 2);
97
+ if (extensionLookupType === LOOKUP_TYPE_EXTENSION_SUBST) return;
98
+ return parseSubtable(bytes, extensionLookupType, subtableOffset + require_sfnt.u32(bytes, subtableOffset + 4));
99
+ }
100
+ if (lookupType === LOOKUP_TYPE_SINGLE_SUBST) {
101
+ if (!require_sfnt.hasBytes(bytes, subtableOffset, 2)) return;
102
+ const substFormat = require_sfnt.u16(bytes, subtableOffset);
103
+ if (substFormat === 1) return parseSingleSubstFormat1(bytes, subtableOffset);
104
+ if (substFormat === 2) return parseSingleSubstFormat2(bytes, subtableOffset);
105
+ return;
106
+ }
107
+ if (lookupType === LOOKUP_TYPE_LIGATURE_SUBST) {
108
+ if (!require_sfnt.hasBytes(bytes, subtableOffset, 2)) return;
109
+ if (require_sfnt.u16(bytes, subtableOffset) !== 1) return;
110
+ return parseLigatureSubstFormat1(bytes, subtableOffset);
111
+ }
112
+ }
113
+ function findScriptOffset(bytes, scriptListOffset) {
114
+ if (!require_sfnt.hasBytes(bytes, scriptListOffset, 2)) return;
115
+ const scriptCount = require_sfnt.u16(bytes, scriptListOffset);
116
+ const recordsOffset = scriptListOffset + 2;
117
+ if (!require_sfnt.hasBytes(bytes, recordsOffset, scriptCount * SCRIPT_RECORD_SIZE) || scriptCount === 0) return;
118
+ for (const wanted of PREFERRED_SCRIPT_TAGS) for (let i = 0; i < scriptCount; i++) {
119
+ const recordOffset = recordsOffset + i * SCRIPT_RECORD_SIZE;
120
+ if (readTag(bytes, recordOffset) === wanted) return scriptListOffset + require_sfnt.u16(bytes, recordOffset + 4);
121
+ }
122
+ return scriptListOffset + require_sfnt.u16(bytes, recordsOffset + 4);
123
+ }
124
+ function parseDefaultLangSysFeatureIndices(bytes, scriptOffset) {
125
+ if (!require_sfnt.hasBytes(bytes, scriptOffset, 2)) return [];
126
+ const defaultLangSysOffset = require_sfnt.u16(bytes, scriptOffset);
127
+ if (defaultLangSysOffset === 0) return [];
128
+ const langSysOffset = scriptOffset + defaultLangSysOffset;
129
+ if (!require_sfnt.hasBytes(bytes, langSysOffset, LANG_SYS_HEADER_SIZE)) return [];
130
+ const featureIndexCount = require_sfnt.u16(bytes, langSysOffset + 4);
131
+ const indicesOffset = langSysOffset + LANG_SYS_HEADER_SIZE;
132
+ if (!require_sfnt.hasBytes(bytes, indicesOffset, featureIndexCount * 2)) return [];
133
+ const indices = [];
134
+ for (let i = 0; i < featureIndexCount; i++) indices.push(require_sfnt.u16(bytes, indicesOffset + i * 2));
135
+ return indices;
136
+ }
137
+ function collectLigatureLookupIndices(bytes, featureListOffset, featureIndices) {
138
+ if (!require_sfnt.hasBytes(bytes, featureListOffset, 2)) return [];
139
+ const featureCount = require_sfnt.u16(bytes, featureListOffset);
140
+ const recordsOffset = featureListOffset + 2;
141
+ if (!require_sfnt.hasBytes(bytes, recordsOffset, featureCount * FEATURE_RECORD_SIZE)) return [];
142
+ const lookupIndices = [];
143
+ const seen = /* @__PURE__ */ new Set();
144
+ for (const featureIndex of featureIndices) {
145
+ if (featureIndex >= featureCount) continue;
146
+ const recordOffset = recordsOffset + featureIndex * FEATURE_RECORD_SIZE;
147
+ const tag = readTag(bytes, recordOffset);
148
+ if (!LIGATURE_FEATURE_TAGS.includes(tag)) continue;
149
+ const featureOffset = featureListOffset + require_sfnt.u16(bytes, recordOffset + 4);
150
+ if (!require_sfnt.hasBytes(bytes, featureOffset, FEATURE_HEADER_SIZE)) continue;
151
+ const lookupIndexCount = require_sfnt.u16(bytes, featureOffset + 2);
152
+ const indicesOffset = featureOffset + FEATURE_HEADER_SIZE;
153
+ if (!require_sfnt.hasBytes(bytes, indicesOffset, lookupIndexCount * 2)) continue;
154
+ for (let i = 0; i < lookupIndexCount; i++) {
155
+ const lookupIndex = require_sfnt.u16(bytes, indicesOffset + i * 2);
156
+ if (!seen.has(lookupIndex)) {
157
+ seen.add(lookupIndex);
158
+ lookupIndices.push(lookupIndex);
159
+ }
160
+ }
161
+ }
162
+ return lookupIndices;
163
+ }
164
+ function buildGsubShaper(font) {
165
+ const bytes = require_sfnt.sfntTableBytes(font, "GSUB");
166
+ if (bytes === void 0 || !require_sfnt.hasBytes(bytes, 0, GSUB_HEADER_SIZE) || require_sfnt.u16(bytes, 0) !== 1) return;
167
+ const scriptOffset = findScriptOffset(bytes, require_sfnt.u16(bytes, 4));
168
+ if (scriptOffset === void 0) return;
169
+ const lookupIndices = collectLigatureLookupIndices(bytes, require_sfnt.u16(bytes, 6), parseDefaultLangSysFeatureIndices(bytes, scriptOffset));
170
+ if (lookupIndices.length === 0) return;
171
+ const lookupListOffset = require_sfnt.u16(bytes, 8);
172
+ if (!require_sfnt.hasBytes(bytes, lookupListOffset, 2)) return;
173
+ const lookupCount = require_sfnt.u16(bytes, lookupListOffset);
174
+ const lookupOffsetsOffset = lookupListOffset + 2;
175
+ if (!require_sfnt.hasBytes(bytes, lookupOffsetsOffset, lookupCount * 2)) return;
176
+ const subtables = [];
177
+ for (const lookupIndex of lookupIndices) {
178
+ if (lookupIndex >= lookupCount) continue;
179
+ const lookupOffset = lookupListOffset + require_sfnt.u16(bytes, lookupOffsetsOffset + lookupIndex * 2);
180
+ if (!require_sfnt.hasBytes(bytes, lookupOffset, LOOKUP_HEADER_SIZE)) continue;
181
+ if (require_sfnt.u16(bytes, lookupOffset + 2) !== 0) continue;
182
+ const lookupType = require_sfnt.u16(bytes, lookupOffset);
183
+ const subTableCount = require_sfnt.u16(bytes, lookupOffset + 4);
184
+ const subtableOffsetsOffset = lookupOffset + LOOKUP_HEADER_SIZE;
185
+ if (!require_sfnt.hasBytes(bytes, subtableOffsetsOffset, subTableCount * 2)) continue;
186
+ for (let i = 0; i < subTableCount; i++) {
187
+ const subtable = parseSubtable(bytes, lookupType, lookupOffset + require_sfnt.u16(bytes, subtableOffsetsOffset + i * 2));
188
+ if (subtable !== void 0) subtables.push(subtable);
189
+ }
190
+ }
191
+ if (subtables.length === 0) return;
192
+ return (glyphIds) => {
193
+ const shaped = [];
194
+ const spans = [];
195
+ let index = 0;
196
+ outer: while (index < glyphIds.length) {
197
+ for (const subtable of subtables) if (subtable.kind === "single") {
198
+ const substitute = subtable.lookup(glyphIds[index]);
199
+ if (substitute !== void 0) {
200
+ shaped.push(substitute);
201
+ spans.push(1);
202
+ index += 1;
203
+ continue outer;
204
+ }
205
+ } else {
206
+ const match = subtable.match(glyphIds, index);
207
+ if (match !== void 0) {
208
+ shaped.push(match.ligatureGlyph);
209
+ spans.push(match.componentCount);
210
+ index += match.componentCount;
211
+ continue outer;
212
+ }
213
+ }
214
+ shaped.push(glyphIds[index]);
215
+ spans.push(1);
216
+ index += 1;
217
+ }
218
+ return {
219
+ glyphIds: shaped,
220
+ spans
221
+ };
222
+ };
223
+ }
224
+ //#endregion
225
+ exports.buildGsubShaper = buildGsubShaper;
@@ -0,0 +1,2 @@
1
+ import { n as GsubShaping, r as buildGsubShaper, t as GsubShaper } from "./gsub-table-URczLd85.cjs";
2
+ export { GsubShaper, GsubShaping, buildGsubShaper };
@@ -0,0 +1,2 @@
1
+ import { n as GsubShaping, r as buildGsubShaper, t as GsubShaper } from "./gsub-table-BRTQ8EUW.js";
2
+ export { GsubShaper, GsubShaping, buildGsubShaper };
@@ -0,0 +1,224 @@
1
+ import { hasBytes, i16, sfntTableBytes, u16, u32 } from "./sfnt.js";
2
+ import { parseCoverage } from "./ot-layout-common.js";
3
+ //#region src/gsub-table.ts
4
+ const LIGATURE_FEATURE_TAGS = ["liga", "rlig"];
5
+ const GSUB_HEADER_SIZE = 10;
6
+ const SCRIPT_RECORD_SIZE = 6;
7
+ const FEATURE_RECORD_SIZE = 6;
8
+ const LANG_SYS_HEADER_SIZE = 6;
9
+ const FEATURE_HEADER_SIZE = 4;
10
+ const LOOKUP_HEADER_SIZE = 6;
11
+ const LOOKUP_TYPE_SINGLE_SUBST = 1;
12
+ const LOOKUP_TYPE_LIGATURE_SUBST = 4;
13
+ const LOOKUP_TYPE_EXTENSION_SUBST = 7;
14
+ const EXTENSION_SUBST_HEADER_SIZE = 8;
15
+ const PREFERRED_SCRIPT_TAGS = ["latn", "DFLT"];
16
+ function readTag(bytes, offset) {
17
+ return String.fromCharCode(u16(bytes, offset) >> 8, u16(bytes, offset) & 255, u16(bytes, offset + 2) >> 8, u16(bytes, offset + 2) & 255);
18
+ }
19
+ const SINGLE_SUBST_FORMAT_1_SIZE = 6;
20
+ function parseSingleSubstFormat1(bytes, subtableOffset) {
21
+ if (!hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_1_SIZE)) return;
22
+ const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
23
+ if (coverage === void 0) return;
24
+ const delta = i16(bytes, subtableOffset + 4);
25
+ return {
26
+ kind: "single",
27
+ lookup: (glyphId) => {
28
+ if (coverage.coverageIndex(glyphId) === void 0) return;
29
+ return glyphId + delta;
30
+ }
31
+ };
32
+ }
33
+ const SINGLE_SUBST_FORMAT_2_HEADER_SIZE = 6;
34
+ function parseSingleSubstFormat2(bytes, subtableOffset) {
35
+ if (!hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_2_HEADER_SIZE)) return;
36
+ const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
37
+ if (coverage === void 0) return;
38
+ const glyphCount = u16(bytes, subtableOffset + 4);
39
+ const substitutesOffset = subtableOffset + SINGLE_SUBST_FORMAT_2_HEADER_SIZE;
40
+ if (!hasBytes(bytes, substitutesOffset, glyphCount * 2)) return;
41
+ return {
42
+ kind: "single",
43
+ lookup: (glyphId) => {
44
+ const coverageIndex = coverage.coverageIndex(glyphId);
45
+ if (coverageIndex === void 0 || coverageIndex >= glyphCount) return;
46
+ return u16(bytes, substitutesOffset + coverageIndex * 2);
47
+ }
48
+ };
49
+ }
50
+ const LIGATURE_SUBST_FORMAT_1_HEADER_SIZE = 6;
51
+ const LIGATURE_SET_HEADER_SIZE = 2;
52
+ const LIGATURE_HEADER_PREFIX_SIZE = 4;
53
+ function parseLigatureSubstFormat1(bytes, subtableOffset) {
54
+ if (!hasBytes(bytes, subtableOffset, LIGATURE_SUBST_FORMAT_1_HEADER_SIZE)) return;
55
+ const coverage = parseCoverage(bytes, subtableOffset + u16(bytes, subtableOffset + 2));
56
+ if (coverage === void 0) return;
57
+ const ligSetCount = u16(bytes, subtableOffset + 4);
58
+ const ligSetOffsetsOffset = subtableOffset + LIGATURE_SUBST_FORMAT_1_HEADER_SIZE;
59
+ if (!hasBytes(bytes, ligSetOffsetsOffset, ligSetCount * 2)) return;
60
+ return {
61
+ kind: "ligature",
62
+ match: (glyphIds, index) => {
63
+ const coverageIndex = coverage.coverageIndex(glyphIds[index]);
64
+ if (coverageIndex === void 0 || coverageIndex >= ligSetCount) return;
65
+ const ligSetOffset = subtableOffset + u16(bytes, ligSetOffsetsOffset + coverageIndex * 2);
66
+ if (!hasBytes(bytes, ligSetOffset, LIGATURE_SET_HEADER_SIZE)) return;
67
+ const ligatureCount = u16(bytes, ligSetOffset);
68
+ const ligatureOffsetsOffset = ligSetOffset + LIGATURE_SET_HEADER_SIZE;
69
+ if (!hasBytes(bytes, ligatureOffsetsOffset, ligatureCount * 2)) return;
70
+ for (let i = 0; i < ligatureCount; i++) {
71
+ const ligatureOffset = ligSetOffset + u16(bytes, ligatureOffsetsOffset + i * 2);
72
+ if (!hasBytes(bytes, ligatureOffset, LIGATURE_HEADER_PREFIX_SIZE)) continue;
73
+ const ligatureGlyph = u16(bytes, ligatureOffset);
74
+ const componentCount = u16(bytes, ligatureOffset + 2);
75
+ if (componentCount === 0) continue;
76
+ const componentsOffset = ligatureOffset + LIGATURE_HEADER_PREFIX_SIZE;
77
+ if (!hasBytes(bytes, componentsOffset, (componentCount - 1) * 2)) continue;
78
+ if (index + componentCount > glyphIds.length) continue;
79
+ let matches = true;
80
+ for (let c = 1; c < componentCount; c++) if (glyphIds[index + c] !== u16(bytes, componentsOffset + (c - 1) * 2)) {
81
+ matches = false;
82
+ break;
83
+ }
84
+ if (matches) return {
85
+ ligatureGlyph,
86
+ componentCount
87
+ };
88
+ }
89
+ }
90
+ };
91
+ }
92
+ function parseSubtable(bytes, lookupType, subtableOffset) {
93
+ if (lookupType === LOOKUP_TYPE_EXTENSION_SUBST) {
94
+ if (!hasBytes(bytes, subtableOffset, EXTENSION_SUBST_HEADER_SIZE) || u16(bytes, subtableOffset) !== 1) return;
95
+ const extensionLookupType = u16(bytes, subtableOffset + 2);
96
+ if (extensionLookupType === LOOKUP_TYPE_EXTENSION_SUBST) return;
97
+ return parseSubtable(bytes, extensionLookupType, subtableOffset + u32(bytes, subtableOffset + 4));
98
+ }
99
+ if (lookupType === LOOKUP_TYPE_SINGLE_SUBST) {
100
+ if (!hasBytes(bytes, subtableOffset, 2)) return;
101
+ const substFormat = u16(bytes, subtableOffset);
102
+ if (substFormat === 1) return parseSingleSubstFormat1(bytes, subtableOffset);
103
+ if (substFormat === 2) return parseSingleSubstFormat2(bytes, subtableOffset);
104
+ return;
105
+ }
106
+ if (lookupType === LOOKUP_TYPE_LIGATURE_SUBST) {
107
+ if (!hasBytes(bytes, subtableOffset, 2)) return;
108
+ if (u16(bytes, subtableOffset) !== 1) return;
109
+ return parseLigatureSubstFormat1(bytes, subtableOffset);
110
+ }
111
+ }
112
+ function findScriptOffset(bytes, scriptListOffset) {
113
+ if (!hasBytes(bytes, scriptListOffset, 2)) return;
114
+ const scriptCount = u16(bytes, scriptListOffset);
115
+ const recordsOffset = scriptListOffset + 2;
116
+ if (!hasBytes(bytes, recordsOffset, scriptCount * SCRIPT_RECORD_SIZE) || scriptCount === 0) return;
117
+ for (const wanted of PREFERRED_SCRIPT_TAGS) for (let i = 0; i < scriptCount; i++) {
118
+ const recordOffset = recordsOffset + i * SCRIPT_RECORD_SIZE;
119
+ if (readTag(bytes, recordOffset) === wanted) return scriptListOffset + u16(bytes, recordOffset + 4);
120
+ }
121
+ return scriptListOffset + u16(bytes, recordsOffset + 4);
122
+ }
123
+ function parseDefaultLangSysFeatureIndices(bytes, scriptOffset) {
124
+ if (!hasBytes(bytes, scriptOffset, 2)) return [];
125
+ const defaultLangSysOffset = u16(bytes, scriptOffset);
126
+ if (defaultLangSysOffset === 0) return [];
127
+ const langSysOffset = scriptOffset + defaultLangSysOffset;
128
+ if (!hasBytes(bytes, langSysOffset, LANG_SYS_HEADER_SIZE)) return [];
129
+ const featureIndexCount = u16(bytes, langSysOffset + 4);
130
+ const indicesOffset = langSysOffset + LANG_SYS_HEADER_SIZE;
131
+ if (!hasBytes(bytes, indicesOffset, featureIndexCount * 2)) return [];
132
+ const indices = [];
133
+ for (let i = 0; i < featureIndexCount; i++) indices.push(u16(bytes, indicesOffset + i * 2));
134
+ return indices;
135
+ }
136
+ function collectLigatureLookupIndices(bytes, featureListOffset, featureIndices) {
137
+ if (!hasBytes(bytes, featureListOffset, 2)) return [];
138
+ const featureCount = u16(bytes, featureListOffset);
139
+ const recordsOffset = featureListOffset + 2;
140
+ if (!hasBytes(bytes, recordsOffset, featureCount * FEATURE_RECORD_SIZE)) return [];
141
+ const lookupIndices = [];
142
+ const seen = /* @__PURE__ */ new Set();
143
+ for (const featureIndex of featureIndices) {
144
+ if (featureIndex >= featureCount) continue;
145
+ const recordOffset = recordsOffset + featureIndex * FEATURE_RECORD_SIZE;
146
+ const tag = readTag(bytes, recordOffset);
147
+ if (!LIGATURE_FEATURE_TAGS.includes(tag)) continue;
148
+ const featureOffset = featureListOffset + u16(bytes, recordOffset + 4);
149
+ if (!hasBytes(bytes, featureOffset, FEATURE_HEADER_SIZE)) continue;
150
+ const lookupIndexCount = u16(bytes, featureOffset + 2);
151
+ const indicesOffset = featureOffset + FEATURE_HEADER_SIZE;
152
+ if (!hasBytes(bytes, indicesOffset, lookupIndexCount * 2)) continue;
153
+ for (let i = 0; i < lookupIndexCount; i++) {
154
+ const lookupIndex = u16(bytes, indicesOffset + i * 2);
155
+ if (!seen.has(lookupIndex)) {
156
+ seen.add(lookupIndex);
157
+ lookupIndices.push(lookupIndex);
158
+ }
159
+ }
160
+ }
161
+ return lookupIndices;
162
+ }
163
+ function buildGsubShaper(font) {
164
+ const bytes = sfntTableBytes(font, "GSUB");
165
+ if (bytes === void 0 || !hasBytes(bytes, 0, GSUB_HEADER_SIZE) || u16(bytes, 0) !== 1) return;
166
+ const scriptOffset = findScriptOffset(bytes, u16(bytes, 4));
167
+ if (scriptOffset === void 0) return;
168
+ const lookupIndices = collectLigatureLookupIndices(bytes, u16(bytes, 6), parseDefaultLangSysFeatureIndices(bytes, scriptOffset));
169
+ if (lookupIndices.length === 0) return;
170
+ const lookupListOffset = u16(bytes, 8);
171
+ if (!hasBytes(bytes, lookupListOffset, 2)) return;
172
+ const lookupCount = u16(bytes, lookupListOffset);
173
+ const lookupOffsetsOffset = lookupListOffset + 2;
174
+ if (!hasBytes(bytes, lookupOffsetsOffset, lookupCount * 2)) return;
175
+ const subtables = [];
176
+ for (const lookupIndex of lookupIndices) {
177
+ if (lookupIndex >= lookupCount) continue;
178
+ const lookupOffset = lookupListOffset + u16(bytes, lookupOffsetsOffset + lookupIndex * 2);
179
+ if (!hasBytes(bytes, lookupOffset, LOOKUP_HEADER_SIZE)) continue;
180
+ if (u16(bytes, lookupOffset + 2) !== 0) continue;
181
+ const lookupType = u16(bytes, lookupOffset);
182
+ const subTableCount = u16(bytes, lookupOffset + 4);
183
+ const subtableOffsetsOffset = lookupOffset + LOOKUP_HEADER_SIZE;
184
+ if (!hasBytes(bytes, subtableOffsetsOffset, subTableCount * 2)) continue;
185
+ for (let i = 0; i < subTableCount; i++) {
186
+ const subtable = parseSubtable(bytes, lookupType, lookupOffset + u16(bytes, subtableOffsetsOffset + i * 2));
187
+ if (subtable !== void 0) subtables.push(subtable);
188
+ }
189
+ }
190
+ if (subtables.length === 0) return;
191
+ return (glyphIds) => {
192
+ const shaped = [];
193
+ const spans = [];
194
+ let index = 0;
195
+ outer: while (index < glyphIds.length) {
196
+ for (const subtable of subtables) if (subtable.kind === "single") {
197
+ const substitute = subtable.lookup(glyphIds[index]);
198
+ if (substitute !== void 0) {
199
+ shaped.push(substitute);
200
+ spans.push(1);
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
+ }
212
+ }
213
+ shaped.push(glyphIds[index]);
214
+ spans.push(1);
215
+ index += 1;
216
+ }
217
+ return {
218
+ glyphIds: shaped,
219
+ spans
220
+ };
221
+ };
222
+ }
223
+ //#endregion
224
+ export { buildGsubShaper };
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@ import { NOOP_DIAGNOSTIC_SINK, PdfDiagnostic, PdfDiagnosticSeverity, PdfDiagnost
3
3
  import { LAYOUT_FORMAT_VERSION, LayoutAnnotation, LayoutAnnotationQuad, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachment, LayoutAttachmentSchema, LayoutDestination, LayoutDestinationSchema, LayoutDestinationTarget, LayoutDestinationTargetSchema, LayoutDocument, LayoutDocumentSchema, LayoutEllipse, LayoutEllipseSchema, LayoutFormField, LayoutFormFieldSchema, LayoutFormWidget, LayoutFormWidgetSchema, LayoutImage, LayoutImageAsset, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLink, LayoutInternalLinkSchema, LayoutItem, LayoutItemSchema, LayoutLayer, LayoutLayerSchema, LayoutLine, LayoutLineSchema, LayoutLink, LayoutLinkSchema, LayoutOutlineItem, LayoutOutlineItemSchema, LayoutPage, LayoutPageSchema, LayoutPath, LayoutPathSchema, LayoutPathSegment, LayoutPathSegmentSchema, LayoutRect, LayoutRectSchema, LayoutStructureElement, LayoutStructureElementSchema, LayoutSubpath, LayoutSubpathSchema, LayoutText, LayoutTextSchema } from "./layout.cjs";
4
4
  import { t as GlyphInkBounds } from "./glyph-bounds-BV_Z40KS.cjs";
5
5
  import { PdfBytesSchema, pdfCodec } from "./codec.cjs";
6
- import { n as EmbeddedFaceMetrics, r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-BU6Jgcof.cjs";
6
+ import { n as EmbeddedFaceMetrics, r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-oc65VGlr.cjs";
7
7
  import { WinAnsiSubstitution } from "./winansi.cjs";
8
8
  import { PdfEncryptionError, PdfEncryptionOptions, PdfEncryptionPermissions, PdfEncryptionScheme } from "./encrypt-write.cjs";
9
9
  import { FontFace, FontFaceParseError, readFontFace } from "./font-face.cjs";
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { NOOP_DIAGNOSTIC_SINK, PdfDiagnostic, PdfDiagnosticSeverity, PdfDiagnost
3
3
  import { LAYOUT_FORMAT_VERSION, LayoutAnnotation, LayoutAnnotationQuad, LayoutAnnotationQuadSchema, LayoutAnnotationSchema, LayoutAttachment, LayoutAttachmentSchema, LayoutDestination, LayoutDestinationSchema, LayoutDestinationTarget, LayoutDestinationTargetSchema, LayoutDocument, LayoutDocumentSchema, LayoutEllipse, LayoutEllipseSchema, LayoutFormField, LayoutFormFieldSchema, LayoutFormWidget, LayoutFormWidgetSchema, LayoutImage, LayoutImageAsset, LayoutImageAssetSchema, LayoutImageSchema, LayoutInternalLink, LayoutInternalLinkSchema, LayoutItem, LayoutItemSchema, LayoutLayer, LayoutLayerSchema, LayoutLine, LayoutLineSchema, LayoutLink, LayoutLinkSchema, LayoutOutlineItem, LayoutOutlineItemSchema, LayoutPage, LayoutPageSchema, LayoutPath, LayoutPathSchema, LayoutPathSegment, LayoutPathSegmentSchema, LayoutRect, LayoutRectSchema, LayoutStructureElement, LayoutStructureElementSchema, LayoutSubpath, LayoutSubpathSchema, LayoutText, LayoutTextSchema } from "./layout.js";
4
4
  import { t as GlyphInkBounds } from "./glyph-bounds-BV_Z40KS.js";
5
5
  import { PdfBytesSchema, pdfCodec } from "./codec.js";
6
- import { n as EmbeddedFaceMetrics, r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-fREdbxnr.js";
6
+ import { n as EmbeddedFaceMetrics, r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-B4p9a3O3.js";
7
7
  import { WinAnsiSubstitution } from "./winansi.js";
8
8
  import { PdfEncryptionError, PdfEncryptionOptions, PdfEncryptionPermissions, PdfEncryptionScheme } from "./encrypt-write.js";
9
9
  import { FontFace, FontFaceParseError, readFontFace } from "./font-face.js";
@@ -40,7 +40,7 @@ function buildFontFileStream(font, compress) {
40
40
  }
41
41
  function toUnicodeEntries(usedGlyphs) {
42
42
  const entries = /* @__PURE__ */ new Map();
43
- for (const [glyphId, codePoint] of usedGlyphs) if (codePoint !== void 0) entries.set(glyphId, codePoint);
43
+ for (const [glyphId, codePoint] of usedGlyphs) if (codePoint !== void 0) entries.set(glyphId, [codePoint]);
44
44
  return entries;
45
45
  }
46
46
  function buildMathFontObjects(font, usedGlyphs, refs, compress) {
@@ -39,7 +39,7 @@ function buildFontFileStream(font, compress) {
39
39
  }
40
40
  function toUnicodeEntries(usedGlyphs) {
41
41
  const entries = /* @__PURE__ */ new Map();
42
- for (const [glyphId, codePoint] of usedGlyphs) if (codePoint !== void 0) entries.set(glyphId, codePoint);
42
+ for (const [glyphId, codePoint] of usedGlyphs) if (codePoint !== void 0) entries.set(glyphId, [codePoint]);
43
43
  return entries;
44
44
  }
45
45
  function buildMathFontObjects(font, usedGlyphs, refs, compress) {
@@ -71,7 +71,7 @@ function buildPostStub(font) {
71
71
  if (sourcePost !== void 0 && require_sfnt.hasBytes(sourcePost, POST_METRICS_OFFSET, POST_METRICS_SIZE)) post.set(sourcePost.subarray(POST_METRICS_OFFSET, 16), POST_METRICS_OFFSET);
72
72
  return post;
73
73
  }
74
- function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
74
+ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints, extraGlyphIds) {
75
75
  const used = /* @__PURE__ */ new Set([0]);
76
76
  const unmapped = /* @__PURE__ */ new Set();
77
77
  const pending = [0];
@@ -87,6 +87,13 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
87
87
  pending.push(glyphId);
88
88
  }
89
89
  }
90
+ for (const glyphId of extraGlyphIds) {
91
+ if (glyphId >= numGlyphs) return;
92
+ if (!used.has(glyphId)) {
93
+ used.add(glyphId);
94
+ pending.push(glyphId);
95
+ }
96
+ }
90
97
  while (pending.length > 0) {
91
98
  const glyphId = pending.pop();
92
99
  if (glyf.glyphBytes(glyphId) === void 0) return;
@@ -107,7 +114,7 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
107
114
  unmapped: [...unmapped].sort((a, b) => a - b)
108
115
  };
109
116
  }
110
- function subsetSfnt(font, codePoints) {
117
+ function subsetSfnt(font, codePoints, extraGlyphIds = []) {
111
118
  const head = require_font_tables.parseHead(font);
112
119
  const maxp = require_font_tables.parseMaxp(font);
113
120
  const sourceHead = require_sfnt.sfntTableBytes(font, "head");
@@ -124,7 +131,7 @@ function subsetSfnt(font, codePoints) {
124
131
  });
125
132
  const cmap = require_cmap_table.buildCmapLookup(font);
126
133
  if (glyf === void 0 || cmap === void 0) return;
127
- const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints);
134
+ const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints, extraGlyphIds);
128
135
  if (collected === void 0) return;
129
136
  const glyphIds = [...collected.used].sort((a, b) => a - b);
130
137
  const numGlyphs = glyphIds[glyphIds.length - 1] + 1;
@@ -6,6 +6,6 @@ interface SfntSubsetResult {
6
6
  readonly glyphIds: readonly number[];
7
7
  readonly unmappedCodePoints: readonly number[];
8
8
  }
9
- declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>): SfntSubsetResult | undefined;
9
+ declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>, extraGlyphIds?: Iterable<number>): SfntSubsetResult | undefined;
10
10
  //#endregion
11
11
  export { SfntSubsetResult, subsetSfnt };
@@ -6,6 +6,6 @@ interface SfntSubsetResult {
6
6
  readonly glyphIds: readonly number[];
7
7
  readonly unmappedCodePoints: readonly number[];
8
8
  }
9
- declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>): SfntSubsetResult | undefined;
9
+ declare function subsetSfnt(font: SfntFont, codePoints: Iterable<number>, extraGlyphIds?: Iterable<number>): SfntSubsetResult | undefined;
10
10
  //#endregion
11
11
  export { SfntSubsetResult, subsetSfnt };
@@ -70,7 +70,7 @@ function buildPostStub(font) {
70
70
  if (sourcePost !== void 0 && hasBytes(sourcePost, POST_METRICS_OFFSET, POST_METRICS_SIZE)) post.set(sourcePost.subarray(POST_METRICS_OFFSET, 16), POST_METRICS_OFFSET);
71
71
  return post;
72
72
  }
73
- function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
73
+ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints, extraGlyphIds) {
74
74
  const used = /* @__PURE__ */ new Set([0]);
75
75
  const unmapped = /* @__PURE__ */ new Set();
76
76
  const pending = [0];
@@ -86,6 +86,13 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
86
86
  pending.push(glyphId);
87
87
  }
88
88
  }
89
+ for (const glyphId of extraGlyphIds) {
90
+ if (glyphId >= numGlyphs) return;
91
+ if (!used.has(glyphId)) {
92
+ used.add(glyphId);
93
+ pending.push(glyphId);
94
+ }
95
+ }
89
96
  while (pending.length > 0) {
90
97
  const glyphId = pending.pop();
91
98
  if (glyf.glyphBytes(glyphId) === void 0) return;
@@ -106,7 +113,7 @@ function collectGlyphIds(glyf, cmap, numGlyphs, codePoints) {
106
113
  unmapped: [...unmapped].sort((a, b) => a - b)
107
114
  };
108
115
  }
109
- function subsetSfnt(font, codePoints) {
116
+ function subsetSfnt(font, codePoints, extraGlyphIds = []) {
110
117
  const head = parseHead(font);
111
118
  const maxp = parseMaxp(font);
112
119
  const sourceHead = sfntTableBytes(font, "head");
@@ -123,7 +130,7 @@ function subsetSfnt(font, codePoints) {
123
130
  });
124
131
  const cmap = buildCmapLookup(font);
125
132
  if (glyf === void 0 || cmap === void 0) return;
126
- const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints);
133
+ const collected = collectGlyphIds(glyf, cmap, maxp.numGlyphs, codePoints, extraGlyphIds);
127
134
  if (collected === void 0) return;
128
135
  const glyphIds = [...collected.used].sort((a, b) => a - b);
129
136
  const numGlyphs = glyphIds[glyphIds.length - 1] + 1;
@@ -8,8 +8,13 @@ function codePointToUtf16BEHex(codePoint) {
8
8
  for (let i = 0; i < text.length; i++) hex += text.charCodeAt(i).toString(16).padStart(4, "0");
9
9
  return hex;
10
10
  }
11
- function buildToUnicodeCMap(codeToCodePoint) {
12
- const entries = [...codeToCodePoint].sort((a, b) => a[0] - b[0]);
11
+ function sequenceToUtf16BEHex(codePoints) {
12
+ let hex = "";
13
+ for (const codePoint of codePoints) hex += codePointToUtf16BEHex(codePoint);
14
+ return hex;
15
+ }
16
+ function buildToUnicodeCMap(codeToSequence) {
17
+ const entries = [...codeToSequence].sort((a, b) => a[0] - b[0]);
13
18
  const lines = [
14
19
  "/CIDInit /ProcSet findresource begin",
15
20
  "12 dict begin",
@@ -24,7 +29,7 @@ function buildToUnicodeCMap(codeToCodePoint) {
24
29
  for (let start = 0; start < entries.length; start += MAX_BFCHAR_ENTRIES_PER_BLOCK) {
25
30
  const block = entries.slice(start, start + MAX_BFCHAR_ENTRIES_PER_BLOCK);
26
31
  lines.push(`${block.length} beginbfchar`);
27
- for (const [code, codePoint] of block) lines.push(`<${code.toString(16).padStart(4, "0")}> <${codePointToUtf16BEHex(codePoint)}>`);
32
+ for (const [code, sequence] of block) lines.push(`<${code.toString(16).padStart(4, "0")}> <${sequenceToUtf16BEHex(sequence)}>`);
28
33
  lines.push("endbfchar");
29
34
  }
30
35
  lines.push("endcmap", "CMapName currentdict /CMap defineresource pop", "end", "end");
@@ -1,5 +1,5 @@
1
1
  import { r as PdfObject } from "./objects-B7HdMeWS.cjs";
2
2
  //#region src/tounicode.d.ts
3
- declare function buildToUnicodeCMap(codeToCodePoint: ReadonlyMap<number, number>): PdfObject;
3
+ declare function buildToUnicodeCMap(codeToSequence: ReadonlyMap<number, readonly number[]>): PdfObject;
4
4
  //#endregion
5
5
  export { buildToUnicodeCMap };
@@ -1,5 +1,5 @@
1
1
  import { r as PdfObject } from "./objects-B7HdMeWS.js";
2
2
  //#region src/tounicode.d.ts
3
- declare function buildToUnicodeCMap(codeToCodePoint: ReadonlyMap<number, number>): PdfObject;
3
+ declare function buildToUnicodeCMap(codeToSequence: ReadonlyMap<number, readonly number[]>): PdfObject;
4
4
  //#endregion
5
5
  export { buildToUnicodeCMap };
package/dist/tounicode.js CHANGED
@@ -7,8 +7,13 @@ function codePointToUtf16BEHex(codePoint) {
7
7
  for (let i = 0; i < text.length; i++) hex += text.charCodeAt(i).toString(16).padStart(4, "0");
8
8
  return hex;
9
9
  }
10
- function buildToUnicodeCMap(codeToCodePoint) {
11
- const entries = [...codeToCodePoint].sort((a, b) => a[0] - b[0]);
10
+ function sequenceToUtf16BEHex(codePoints) {
11
+ let hex = "";
12
+ for (const codePoint of codePoints) hex += codePointToUtf16BEHex(codePoint);
13
+ return hex;
14
+ }
15
+ function buildToUnicodeCMap(codeToSequence) {
16
+ const entries = [...codeToSequence].sort((a, b) => a[0] - b[0]);
12
17
  const lines = [
13
18
  "/CIDInit /ProcSet findresource begin",
14
19
  "12 dict begin",
@@ -23,7 +28,7 @@ function buildToUnicodeCMap(codeToCodePoint) {
23
28
  for (let start = 0; start < entries.length; start += MAX_BFCHAR_ENTRIES_PER_BLOCK) {
24
29
  const block = entries.slice(start, start + MAX_BFCHAR_ENTRIES_PER_BLOCK);
25
30
  lines.push(`${block.length} beginbfchar`);
26
- for (const [code, codePoint] of block) lines.push(`<${code.toString(16).padStart(4, "0")}> <${codePointToUtf16BEHex(codePoint)}>`);
31
+ for (const [code, sequence] of block) lines.push(`<${code.toString(16).padStart(4, "0")}> <${sequenceToUtf16BEHex(sequence)}>`);
27
32
  lines.push("endbfchar");
28
33
  }
29
34
  lines.push("endcmap", "CMapName currentdict /CMap defineresource pop", "end", "end");