@mark1russell7/typeset 0.1.2 → 0.1.3
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/build/chunks/font.d.ts +25 -0
- package/build/chunks/font.d.ts.map +1 -1
- package/build/chunks/font.js +269 -40
- package/build/chunks/font.js.map +1 -1
- package/build/chunks/fonts.d.ts +25 -2
- package/build/chunks/fonts.d.ts.map +1 -1
- package/build/chunks/fonts.js +140 -3
- package/build/chunks/fonts.js.map +1 -1
- package/build/fonts-inspect.js +1 -1
- package/build/fonts.d.ts +2 -2
- package/build/fonts.js +2 -2
- package/package.json +2 -2
- package/src/fonts/bound.ts +167 -0
- package/src/fonts/font.ts +20 -4
- package/src/fonts/index.ts +1 -0
- package/src/fonts/indic.ts +54 -13
- package/src/fonts/kerning.ts +68 -7
- package/src/fonts/substitution.ts +101 -1
- package/src/fonts/tables.ts +22 -0
- package/src/fonts.ts +1 -0
package/build/chunks/font.d.ts
CHANGED
|
@@ -22,6 +22,15 @@ interface GlyphInfo {
|
|
|
22
22
|
/** The code point of the character of the glyph, after the normalization. */
|
|
23
23
|
readonly codePoint: number;
|
|
24
24
|
}
|
|
25
|
+
/** One substitution that a lookup can do, with no context. */
|
|
26
|
+
interface SubstitutionRule {
|
|
27
|
+
/** The glyph that the rule replaces. The first glyph of a ligature is this glyph. */
|
|
28
|
+
readonly from: number;
|
|
29
|
+
/** The glyphs that replace it. A multiple substitution can give no glyph. */
|
|
30
|
+
readonly to: readonly number[];
|
|
31
|
+
/** The other glyphs of a ligature, in order. Another rule has no such glyphs. */
|
|
32
|
+
readonly components?: readonly number[];
|
|
33
|
+
}
|
|
25
34
|
/** A lookup of GSUB for one script and one set of features. */
|
|
26
35
|
interface SubstitutionLookup {
|
|
27
36
|
/** The index of the lookup in the lookup list of GSUB. */
|
|
@@ -49,6 +58,12 @@ interface SubstitutionLookup {
|
|
|
49
58
|
* `hb_ot_layout_lookup_would_substitute` with zero context tells it. A context rule with backtrack or lookahead does not apply.
|
|
50
59
|
*/
|
|
51
60
|
wouldApply(glyphs: readonly number[]): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* This function gives the rules of the lookup, and the rules of each lookup that it nests in a context rule. Each lookup
|
|
63
|
+
* has one list, and the list of this lookup is the first. The lists do not tell a context, so each nested rule counts.
|
|
64
|
+
* An alternate substitution gives one rule for each alternate.
|
|
65
|
+
*/
|
|
66
|
+
rules(): readonly (readonly SubstitutionRule[])[];
|
|
52
67
|
}
|
|
53
68
|
/** One axis of `fvar`, in the user units of the axis. */
|
|
54
69
|
interface Axis {
|
|
@@ -80,6 +95,16 @@ interface PairLookup {
|
|
|
80
95
|
readonly skip?: (glyph: number) => boolean;
|
|
81
96
|
/** This function gives the value for a pair, or `undefined` when no subtable applies. */
|
|
82
97
|
pair(first: number, second: number): PairValue | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* This function gives the largest positive value of each glyph of a pair, over the pairs with two glyphs of `glyphs`.
|
|
100
|
+
* A value is at least 0. It uses the values of the instance, so it includes each variation delta.
|
|
101
|
+
*/
|
|
102
|
+
largest(glyphs: ReadonlySet<number>): LargestKerning;
|
|
103
|
+
}
|
|
104
|
+
/** The largest positive kerning of the first glyph and of the second glyph of a pair, in font units. */
|
|
105
|
+
interface LargestKerning {
|
|
106
|
+
readonly first: number;
|
|
107
|
+
readonly second: number;
|
|
83
108
|
}
|
|
84
109
|
/** The options of `readFont`. */
|
|
85
110
|
interface ReadOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"font.d.ts","names":[],"sources":["../../src/fonts/substitution.ts","../../src/fonts/variations.ts","../../src/fonts/kerning.ts","../../src/fonts/font.ts"],"mappings":";;;;;cAQa;;;;;UA6CI;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;WAES;;;UAIM;;WAEN;;WAEA;;WAEA;;WAEA;;;;;;EAMT,MAAM,kBAAkB,kBAAkB;;;;;;EAM1C,WAAW,kBAAkB,OAAO,aAAa,cAAc;;;;;EAK/D,WAAW;;;
|
|
1
|
+
{"version":3,"file":"font.d.ts","names":[],"sources":["../../src/fonts/substitution.ts","../../src/fonts/variations.ts","../../src/fonts/kerning.ts","../../src/fonts/font.ts"],"mappings":";;;;;cAQa;;;;;UA6CI;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;WAES;;;UAIM;;WAEN;;WAEA;;WAEA;;;UAIM;;WAEN;;WAEA;;WAEA;;WAEA;;;;;;EAMT,MAAM,kBAAkB,kBAAkB;;;;;;EAM1C,WAAW,kBAAkB,OAAO,aAAa,cAAc;;;;;EAK/D,WAAW;;;;;;EAMX,4BAA4B;;;UC3Gb;WACN;WACA;WACA;WACA;;;KAIC,WAAW,SAAS;;UCLf;WACN;WACA;;WAEA;;WAEA;;WAEA;;;UAIM;WACN;;;;;WAKA,QAAQ;;EAEjB,KAAK,eAAe,iBAAiB;;;;;EAKrC,QAAQ,QAAQ,sBAAsB;;;UAIvB;WACN;WACA;;;UCxBM;;WAEN,aAAa;;;;;WAKb,WAAW;;;;;WAKX;;;;;WAKA;;;;;WAKA;;;KAIC;;UAGK;;;;;WAKN;WACA;;WAEA,eAAe;;WAEf,UAAU;;WAEV;;WAEA,eAAe;WACf;;EAET,MAAM;;EAEN,QAAQ;;EAER,WAAW;;EAEX,QAAQ,0BAA0B;;EAElC,cAAc,gBAAgB,uCAAuC;;EAErE,GAAG,UAAU,WAAW;;;iBAIV,SAAS,OAAO,YAAY,UAAS,cAAmB;;UA8JvD;;WAEN;;;;;WAKA;;WAEA;;;;;;WAMA;;;;;;WAMA;;;;;WAKA;;;;;WAKA;;;;;WAKA;;;;;;iBAYK,aAAa;;;;;;;iBAgEb,cAAc,MAAM,MAAM,cAAc,UAAS;EAAwB;EAAkB;;;;;;iBAkG3F,aAAa,MAAM,MAAM,cAAc,cAAc,UAAS"}
|
package/build/chunks/font.js
CHANGED
|
@@ -123,6 +123,27 @@ function coverageIndex(table, glyph) {
|
|
|
123
123
|
}
|
|
124
124
|
throw new Error(`the Coverage format ${format} is unknown`);
|
|
125
125
|
}
|
|
126
|
+
/** This function gives each glyph of a Coverage table with its coverage index, in the order of the table. */
|
|
127
|
+
function coverageGlyphs(table) {
|
|
128
|
+
const format = table.u16(0);
|
|
129
|
+
const count = table.u16(2);
|
|
130
|
+
const glyphs = [];
|
|
131
|
+
if (format === 1) {
|
|
132
|
+
for (let k = 0; k < count; k++) glyphs.push([table.u16(4 + k * 2), k]);
|
|
133
|
+
return glyphs;
|
|
134
|
+
}
|
|
135
|
+
if (format === 2) {
|
|
136
|
+
for (let k = 0; k < count; k++) {
|
|
137
|
+
const at = 4 + k * 6;
|
|
138
|
+
const start = table.u16(at);
|
|
139
|
+
const end = table.u16(at + 2);
|
|
140
|
+
const index = table.u16(at + 4);
|
|
141
|
+
for (let glyph = start; glyph <= end; glyph++) glyphs.push([glyph, index + glyph - start]);
|
|
142
|
+
}
|
|
143
|
+
return glyphs;
|
|
144
|
+
}
|
|
145
|
+
throw new Error(`the Coverage format ${format} is unknown`);
|
|
146
|
+
}
|
|
126
147
|
/** The GDEF glyph classes: 1 base, 2 ligature, 3 mark, 4 component. */
|
|
127
148
|
function readGlyphClasses(gdef) {
|
|
128
149
|
if (gdef === void 0) return () => 0;
|
|
@@ -168,7 +189,7 @@ function pairValue(first, second, skipSecond) {
|
|
|
168
189
|
skipSecond
|
|
169
190
|
};
|
|
170
191
|
}
|
|
171
|
-
/** This function makes the
|
|
192
|
+
/** This function makes the functions of one PairPos subtable. */
|
|
172
193
|
function pairSubtable(table, store) {
|
|
173
194
|
const format = table.u16(0);
|
|
174
195
|
const coverage = table.part(table.u16(2));
|
|
@@ -177,28 +198,53 @@ function pairSubtable(table, store) {
|
|
|
177
198
|
const size1 = valueSize(format1);
|
|
178
199
|
const size2 = valueSize(format2);
|
|
179
200
|
const skipSecond = format2 !== 0;
|
|
180
|
-
if (format === 1)
|
|
181
|
-
const index = coverageIndex(coverage, first);
|
|
182
|
-
if (index < 0) return void 0;
|
|
183
|
-
const set = table.part(table.u16(10 + index * 2));
|
|
201
|
+
if (format === 1) {
|
|
184
202
|
const recordSize = 2 + size1 + size2;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
const pair = (first, second) => {
|
|
204
|
+
const index = coverageIndex(coverage, first);
|
|
205
|
+
if (index < 0) return void 0;
|
|
206
|
+
const set = table.part(table.u16(10 + index * 2));
|
|
207
|
+
let low = 0;
|
|
208
|
+
let high = set.u16(0) - 1;
|
|
209
|
+
while (low <= high) {
|
|
210
|
+
const middle = low + high >> 1;
|
|
211
|
+
const at = 2 + middle * recordSize;
|
|
212
|
+
const glyph = set.u16(at);
|
|
213
|
+
if (second < glyph) high = middle - 1;
|
|
214
|
+
else if (second > glyph) low = middle + 1;
|
|
215
|
+
else return pairValue(xAdvance(set, at + 2, format1, set, store), xAdvance(set, at + 2 + size1, format2, set, store), skipSecond);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const largest = (glyphs) => {
|
|
219
|
+
let first = 0;
|
|
220
|
+
let second = 0;
|
|
221
|
+
for (const glyph of glyphs) {
|
|
222
|
+
const index = coverageIndex(coverage, glyph);
|
|
223
|
+
if (index < 0) continue;
|
|
224
|
+
const set = table.part(table.u16(10 + index * 2));
|
|
225
|
+
for (let record = 0; record < set.u16(0); record++) {
|
|
226
|
+
const at = 2 + record * recordSize;
|
|
227
|
+
if (!glyphs.has(set.u16(at))) continue;
|
|
228
|
+
first = Math.max(first, xAdvance(set, at + 2, format1, set, store).value);
|
|
229
|
+
second = Math.max(second, xAdvance(set, at + 2 + size1, format2, set, store).value);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
first,
|
|
234
|
+
second
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
pair,
|
|
239
|
+
largest
|
|
240
|
+
};
|
|
241
|
+
}
|
|
196
242
|
if (format === 2) {
|
|
197
243
|
const classes1 = table.part(table.u16(8));
|
|
198
244
|
const classes2 = table.part(table.u16(10));
|
|
199
245
|
const count1 = table.u16(12);
|
|
200
246
|
const count2 = table.u16(14);
|
|
201
|
-
|
|
247
|
+
const pair = (first, second) => {
|
|
202
248
|
if (coverageIndex(coverage, first) < 0) return void 0;
|
|
203
249
|
const class1 = classOf(classes1, first);
|
|
204
250
|
const class2 = classOf(classes2, second);
|
|
@@ -206,6 +252,30 @@ function pairSubtable(table, store) {
|
|
|
206
252
|
const at = 16 + (class1 * count2 + class2) * (size1 + size2);
|
|
207
253
|
return pairValue(xAdvance(table, at, format1, table, store), xAdvance(table, at + size1, format2, table, store), skipSecond);
|
|
208
254
|
};
|
|
255
|
+
const largest = (glyphs) => {
|
|
256
|
+
const firsts = /* @__PURE__ */ new Set();
|
|
257
|
+
const seconds = /* @__PURE__ */ new Set();
|
|
258
|
+
for (const glyph of glyphs) {
|
|
259
|
+
if (coverageIndex(coverage, glyph) >= 0) firsts.add(classOf(classes1, glyph));
|
|
260
|
+
seconds.add(classOf(classes2, glyph));
|
|
261
|
+
}
|
|
262
|
+
let first = 0;
|
|
263
|
+
let second = 0;
|
|
264
|
+
for (const class1 of firsts) for (const class2 of seconds) {
|
|
265
|
+
if (class1 >= count1 || class2 >= count2) continue;
|
|
266
|
+
const at = 16 + (class1 * count2 + class2) * (size1 + size2);
|
|
267
|
+
first = Math.max(first, xAdvance(table, at, format1, table, store).value);
|
|
268
|
+
second = Math.max(second, xAdvance(table, at + size1, format2, table, store).value);
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
first,
|
|
272
|
+
second
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
return {
|
|
276
|
+
pair,
|
|
277
|
+
largest
|
|
278
|
+
};
|
|
209
279
|
}
|
|
210
280
|
throw new Error(`the PairPos format ${format} is unknown`);
|
|
211
281
|
}
|
|
@@ -278,9 +348,16 @@ function readGposKerning(gpos, script, store, filter) {
|
|
|
278
348
|
...skip === void 0 ? {} : { skip },
|
|
279
349
|
pair(first, second) {
|
|
280
350
|
for (const subtable of subtables) {
|
|
281
|
-
const value = subtable(first, second);
|
|
351
|
+
const value = subtable.pair(first, second);
|
|
282
352
|
if (value !== void 0) return value;
|
|
283
353
|
}
|
|
354
|
+
},
|
|
355
|
+
largest(glyphs) {
|
|
356
|
+
const values = subtables.map((subtable) => subtable.largest(glyphs));
|
|
357
|
+
return {
|
|
358
|
+
first: Math.max(...values.map((value) => value.first)),
|
|
359
|
+
second: Math.max(...values.map((value) => value.second))
|
|
360
|
+
};
|
|
284
361
|
}
|
|
285
362
|
}];
|
|
286
363
|
});
|
|
@@ -313,6 +390,14 @@ function readKernTable(kern) {
|
|
|
313
390
|
second: 0,
|
|
314
391
|
skipSecond: false
|
|
315
392
|
};
|
|
393
|
+
},
|
|
394
|
+
largest(glyphs) {
|
|
395
|
+
let first = 0;
|
|
396
|
+
for (const [key, value] of pairs) if (glyphs.has(Math.floor(key / 65536)) && glyphs.has(key % 65536)) first = Math.max(first, value);
|
|
397
|
+
return {
|
|
398
|
+
first,
|
|
399
|
+
second: 0
|
|
400
|
+
};
|
|
316
401
|
}
|
|
317
402
|
}];
|
|
318
403
|
}
|
|
@@ -540,6 +625,81 @@ function records(table, at, count) {
|
|
|
540
625
|
lookupIndex: table.u16(at + k * 4 + 2)
|
|
541
626
|
}));
|
|
542
627
|
}
|
|
628
|
+
/** This function gives each rule table of the rule sets at `at` of a context subtable of format 1 or 2. An offset 0 is an empty set. */
|
|
629
|
+
function ruleTables(t, at) {
|
|
630
|
+
const tables = [];
|
|
631
|
+
for (let s = 0; s < t.u16(at); s++) {
|
|
632
|
+
const offset = t.u16(at + 2 + s * 2);
|
|
633
|
+
if (offset === 0) continue;
|
|
634
|
+
const set = t.part(offset);
|
|
635
|
+
for (let r = 0; r < set.u16(0); r++) tables.push(set.part(set.u16(2 + r * 2)));
|
|
636
|
+
}
|
|
637
|
+
return tables;
|
|
638
|
+
}
|
|
639
|
+
/** This function gives the records of each rule of a context subtable (lookup type 5). */
|
|
640
|
+
function contextRecords(t) {
|
|
641
|
+
const format = t.u16(0);
|
|
642
|
+
if (format === 3) return records(t, 6 + t.u16(2) * 2, t.u16(4));
|
|
643
|
+
return ruleTables(t, format === 2 ? 6 : 4).flatMap((rule) => records(rule, 4 + (rule.u16(0) - 1) * 2, rule.u16(2)));
|
|
644
|
+
}
|
|
645
|
+
/** This function gives the records of each rule of a chained context subtable (lookup type 6). */
|
|
646
|
+
function chainRecords(t) {
|
|
647
|
+
const format = t.u16(0);
|
|
648
|
+
if (format === 3) {
|
|
649
|
+
const inputAt = 4 + t.u16(2) * 2;
|
|
650
|
+
const aheadAt = inputAt + 2 + t.u16(inputAt) * 2;
|
|
651
|
+
const recordsAt = aheadAt + 2 + t.u16(aheadAt) * 2;
|
|
652
|
+
return records(t, recordsAt + 2, t.u16(recordsAt));
|
|
653
|
+
}
|
|
654
|
+
return ruleTables(t, format === 2 ? 10 : 4).flatMap((rule) => {
|
|
655
|
+
const inputAt = 2 + rule.u16(0) * 2;
|
|
656
|
+
const aheadAt = inputAt + 2 + Math.max(0, rule.u16(inputAt) - 1) * 2;
|
|
657
|
+
const recordsAt = aheadAt + 2 + rule.u16(aheadAt) * 2;
|
|
658
|
+
return records(rule, recordsAt + 2, rule.u16(recordsAt));
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
/** This function adds the rules of one subtable of a lookup type to `rules`, and each nested lookup to `nested`. */
|
|
662
|
+
function readRules(type, t, rules, nested) {
|
|
663
|
+
if (type === 5 || type === 6) {
|
|
664
|
+
for (const record of type === 5 ? contextRecords(t) : chainRecords(t)) nested.push(record.lookupIndex);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (type < 1 || type > 8 || type === 7) return;
|
|
668
|
+
const format = t.u16(0);
|
|
669
|
+
for (const [from, index] of coverageGlyphs(t.part(t.u16(2)))) if (type === 1) rules.push({
|
|
670
|
+
from,
|
|
671
|
+
to: [format === 1 ? from + t.i16(4) & 65535 : t.u16(6 + index * 2)]
|
|
672
|
+
});
|
|
673
|
+
else if (type === 2) {
|
|
674
|
+
const sequence = t.part(t.u16(6 + index * 2));
|
|
675
|
+
rules.push({
|
|
676
|
+
from,
|
|
677
|
+
to: Array.from({ length: sequence.u16(0) }, (_, k) => sequence.u16(2 + k * 2))
|
|
678
|
+
});
|
|
679
|
+
} else if (type === 3) {
|
|
680
|
+
const set = t.part(t.u16(6 + index * 2));
|
|
681
|
+
for (let k = 0; k < set.u16(0); k++) rules.push({
|
|
682
|
+
from,
|
|
683
|
+
to: [set.u16(2 + k * 2)]
|
|
684
|
+
});
|
|
685
|
+
} else if (type === 4) {
|
|
686
|
+
const set = t.part(t.u16(6 + index * 2));
|
|
687
|
+
for (let l = 0; l < set.u16(0); l++) {
|
|
688
|
+
const ligature = set.part(set.u16(2 + l * 2));
|
|
689
|
+
rules.push({
|
|
690
|
+
from,
|
|
691
|
+
to: [ligature.u16(0)],
|
|
692
|
+
components: Array.from({ length: ligature.u16(2) - 1 }, (_, k) => ligature.u16(4 + k * 2))
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
} else {
|
|
696
|
+
const aheadAt = 6 + t.u16(4) * 2;
|
|
697
|
+
rules.push({
|
|
698
|
+
from,
|
|
699
|
+
to: [t.u16(aheadAt + 2 + t.u16(aheadAt) * 2 + 2 + index * 2)]
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
543
703
|
/** The lookups of one GSUB table. It reads each lookup at its first use. */
|
|
544
704
|
var Gsub = class {
|
|
545
705
|
lookups;
|
|
@@ -800,6 +960,23 @@ var Gsub = class {
|
|
|
800
960
|
return false;
|
|
801
961
|
});
|
|
802
962
|
}
|
|
963
|
+
/** This function gives the rules of a lookup and of each lookup that it nests, as `rules` of `SubstitutionLookup` tells. */
|
|
964
|
+
rulesOf(index) {
|
|
965
|
+
const lists = [];
|
|
966
|
+
const seen = /* @__PURE__ */ new Set();
|
|
967
|
+
const visit = (at) => {
|
|
968
|
+
const lookup = this.lookup(at);
|
|
969
|
+
if (seen.has(at) || lookup === void 0) return;
|
|
970
|
+
seen.add(at);
|
|
971
|
+
const rules = [];
|
|
972
|
+
const nested = [];
|
|
973
|
+
for (const subtable of lookup.subtables) readRules(lookup.type, subtable, rules, nested);
|
|
974
|
+
lists.push(rules);
|
|
975
|
+
nested.forEach(visit);
|
|
976
|
+
};
|
|
977
|
+
visit(index);
|
|
978
|
+
return lists;
|
|
979
|
+
}
|
|
803
980
|
/** This function applies one subtable at `i`. It gives the next position, or -1 when the subtable does not match. */
|
|
804
981
|
subtable(lookup, t, glyphs, i, depth) {
|
|
805
982
|
const glyph = glyphs[i];
|
|
@@ -977,7 +1154,8 @@ function readSubstitutions(gsub, script, features, filter) {
|
|
|
977
1154
|
features: tags,
|
|
978
1155
|
apply: (glyphs, masks, mask) => table.applyAll(index, glyphs, masks, mask),
|
|
979
1156
|
applyInfos: (glyphs, infos, mask, perSyllable) => table.applyRun(index, glyphs, infos, mask, perSyllable),
|
|
980
|
-
wouldApply: (glyphs) => table.wouldApply(index, glyphs)
|
|
1157
|
+
wouldApply: (glyphs) => table.wouldApply(index, glyphs),
|
|
1158
|
+
rules: () => table.rulesOf(index)
|
|
981
1159
|
});
|
|
982
1160
|
}
|
|
983
1161
|
return result;
|
|
@@ -1717,17 +1895,7 @@ function vowelConstraints(codePoints) {
|
|
|
1717
1895
|
*/
|
|
1718
1896
|
function normalize(codePoints, hasGlyph) {
|
|
1719
1897
|
const allSimple = !codePoints.some((codePoint, i) => i > 0 && isMark(codePoint));
|
|
1720
|
-
const decomposed =
|
|
1721
|
-
for (const codePoint of codePoints) {
|
|
1722
|
-
const parts = codePoint === 2353 ? [codePoint] : [...String.fromCodePoint(codePoint).normalize("NFD")].map((part) => part.codePointAt(0));
|
|
1723
|
-
if (parts.length > 1 || parts[0] !== codePoint) {
|
|
1724
|
-
if (parts.every(hasGlyph)) {
|
|
1725
|
-
decomposed.push(...parts);
|
|
1726
|
-
continue;
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
decomposed.push(codePoint);
|
|
1730
|
-
}
|
|
1898
|
+
const decomposed = codePoints.flatMap((codePoint) => decomposition(codePoint, hasGlyph));
|
|
1731
1899
|
if (allSimple) return decomposed;
|
|
1732
1900
|
for (let i = 1; i < decomposed.length; i++) for (let j = i; j > 0; j--) {
|
|
1733
1901
|
const before = combiningClass(decomposed[j - 1]);
|
|
@@ -1751,6 +1919,38 @@ function normalize(codePoints, hasGlyph) {
|
|
|
1751
1919
|
}
|
|
1752
1920
|
return out;
|
|
1753
1921
|
}
|
|
1922
|
+
/** This function gives the code points of a character after the decomposition of `normalize`. */
|
|
1923
|
+
function decomposition(codePoint, hasGlyph) {
|
|
1924
|
+
const parts = codePoint === 2353 ? [codePoint] : [...String.fromCodePoint(codePoint).normalize("NFD")].map((part) => part.codePointAt(0));
|
|
1925
|
+
return (parts.length > 1 || parts[0] !== codePoint) && parts.every(hasGlyph) ? parts : [codePoint];
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* This function gives the code points that the normalization can give for the characters `characters`. A composite is
|
|
1929
|
+
* not always in the set: a nukta that follows U+0928 gives U+0929. The function repeats until no new composite appears.
|
|
1930
|
+
*/
|
|
1931
|
+
function indicForms(characters, hasGlyph) {
|
|
1932
|
+
const parts = new Map([...characters].map((codePoint) => [codePoint, decomposition(codePoint, hasGlyph)]));
|
|
1933
|
+
const codePoints = new Set([...parts.values()].flat());
|
|
1934
|
+
for (const codePoint of characters) codePoints.add(codePoint);
|
|
1935
|
+
codePoints.add(9676);
|
|
1936
|
+
const composites = /* @__PURE__ */ new Map();
|
|
1937
|
+
for (let size = 0; size !== codePoints.size;) {
|
|
1938
|
+
size = codePoints.size;
|
|
1939
|
+
const marks = [...codePoints].filter(isMark);
|
|
1940
|
+
for (const starter of codePoints) for (const mark of marks) {
|
|
1941
|
+
const composed = compose(starter, mark);
|
|
1942
|
+
if (composed === void 0 || !hasGlyph(composed)) continue;
|
|
1943
|
+
const known = composites.get(starter) ?? [];
|
|
1944
|
+
if (!known.includes(composed)) composites.set(starter, [...known, composed]);
|
|
1945
|
+
codePoints.add(composed);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return {
|
|
1949
|
+
parts,
|
|
1950
|
+
composites,
|
|
1951
|
+
codePoints
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1754
1954
|
/** This function gives the canonical composite of two code points, or `undefined`. A mark does not compose with a mark. */
|
|
1755
1955
|
function compose(first, second) {
|
|
1756
1956
|
if (isMark(first)) return void 0;
|
|
@@ -1869,6 +2069,24 @@ const finalFeatures = [
|
|
|
1869
2069
|
"psts",
|
|
1870
2070
|
"haln"
|
|
1871
2071
|
];
|
|
2072
|
+
/** This function gives the features of the page that the last stage applies. */
|
|
2073
|
+
function pageFeatures(features) {
|
|
2074
|
+
return features.filter((tag) => ![
|
|
2075
|
+
"ccmp",
|
|
2076
|
+
"locl",
|
|
2077
|
+
"liga"
|
|
2078
|
+
].includes(tag) && !finalFeatures.includes(tag));
|
|
2079
|
+
}
|
|
2080
|
+
/** This function gives all the features that `indicShaper` applies for the features `features` of the page. */
|
|
2081
|
+
function indicFeatures(features) {
|
|
2082
|
+
return [
|
|
2083
|
+
"locl",
|
|
2084
|
+
"ccmp",
|
|
2085
|
+
...basicFeatures.map(([tag]) => tag),
|
|
2086
|
+
...finalFeatures,
|
|
2087
|
+
...pageFeatures(features)
|
|
2088
|
+
];
|
|
2089
|
+
}
|
|
1872
2090
|
/** The mask bit of the global features. Each glyph has it. */
|
|
1873
2091
|
const GLOBAL = 1;
|
|
1874
2092
|
/** The mask bits of the features that apply only to some glyphs of a syllable. */
|
|
@@ -1940,12 +2158,7 @@ function indicShaper(font, script, features) {
|
|
|
1940
2158
|
lookups: lookupsOf([tag]),
|
|
1941
2159
|
mask: global ? GLOBAL : featureBits.get(tag)
|
|
1942
2160
|
}));
|
|
1943
|
-
const
|
|
1944
|
-
"ccmp",
|
|
1945
|
-
"locl",
|
|
1946
|
-
"liga"
|
|
1947
|
-
].includes(tag) && !finalFeatures.includes(tag));
|
|
1948
|
-
const last = lookupsOf([...finalFeatures, ...extra]).map((lookup) => ({
|
|
2161
|
+
const last = lookupsOf([...finalFeatures, ...pageFeatures(features)]).map((lookup) => ({
|
|
1949
2162
|
lookup,
|
|
1950
2163
|
mask: lookup.features.reduce((mask, tag) => mask | (tag === "init" ? initMask : GLOBAL), 0),
|
|
1951
2164
|
perSyllable: lookup.features.some((tag) => finalFeatures.includes(tag))
|
|
@@ -3401,16 +3614,32 @@ function shapingWords(text) {
|
|
|
3401
3614
|
if (word !== "") words.push(word);
|
|
3402
3615
|
return words;
|
|
3403
3616
|
}
|
|
3404
|
-
/**
|
|
3405
|
-
function
|
|
3617
|
+
/** This function gives the choices of a set of options: the script, the features, the shaper and the kerning lookups. */
|
|
3618
|
+
function shapingOf(font, options) {
|
|
3406
3619
|
const script = options.script ?? "DFLT";
|
|
3407
3620
|
const features = options.features ?? defaultFeatures;
|
|
3408
3621
|
const kerning = options.kerning === false ? [] : font.kerning(script);
|
|
3409
3622
|
if (options.indic !== false && indicScripts.includes(script)) return {
|
|
3623
|
+
script,
|
|
3624
|
+
features,
|
|
3625
|
+
shaper: "indic",
|
|
3626
|
+
kerning
|
|
3627
|
+
};
|
|
3628
|
+
return {
|
|
3629
|
+
script,
|
|
3630
|
+
features,
|
|
3631
|
+
shaper: options.joining !== false && joiningScripts.includes(script) ? "joining" : "plain",
|
|
3632
|
+
kerning
|
|
3633
|
+
};
|
|
3634
|
+
}
|
|
3635
|
+
/** The GSUB pass of one set of options: a function that gives the glyphs of one word, and the kerning lookups. */
|
|
3636
|
+
function shaperOf(font, options) {
|
|
3637
|
+
const { script, features, shaper, kerning } = shapingOf(font, options);
|
|
3638
|
+
if (shaper === "indic") return {
|
|
3410
3639
|
substitute: indicShaper(font, script, features),
|
|
3411
3640
|
kerning
|
|
3412
3641
|
};
|
|
3413
|
-
const joins =
|
|
3642
|
+
const joins = shaper === "joining";
|
|
3414
3643
|
const lookups = joins ? [] : font.substitutions(script, features);
|
|
3415
3644
|
const stages = joins ? joiningStages(features, features.includes("rclt") && font.substitutions(script, ["rclt"]).length > 0).map((stage) => ({
|
|
3416
3645
|
stage,
|
|
@@ -3545,6 +3774,6 @@ function measureWidth(font, text, size, options = {}) {
|
|
|
3545
3774
|
for (const advance of advances) units += advance;
|
|
3546
3775
|
return units * size / font.unitsPerEm;
|
|
3547
3776
|
}
|
|
3548
|
-
export {
|
|
3777
|
+
export { shapingWords as a, indicFeatures as c, readTables as d, shapingOf as i, indicForms as l, measureWidth as n, canvasWords as o, readFont as r, joiningStages as s, glyphAdvances as t, defaultFeatures as u };
|
|
3549
3778
|
|
|
3550
3779
|
//# sourceMappingURL=font.js.map
|