@mark1russell7/typeset 0.1.1 → 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/README.md +21 -1
- package/build/chunks/engines.d.ts +32 -0
- package/build/chunks/engines.d.ts.map +1 -0
- package/build/chunks/engines.js +35 -0
- package/build/chunks/engines.js.map +1 -0
- package/build/chunks/font.d.ts +228 -0
- package/build/chunks/font.d.ts.map +1 -0
- package/build/chunks/font.js +3779 -0
- package/build/chunks/font.js.map +1 -0
- package/build/chunks/fonts.d.ts +31 -0
- package/build/chunks/fonts.d.ts.map +1 -0
- package/build/chunks/fonts.js +150 -0
- package/build/chunks/fonts.js.map +1 -0
- package/build/chunks/measurer.d.ts +67 -0
- package/build/chunks/measurer.d.ts.map +1 -0
- package/build/chunks/measurer.js +277 -0
- package/build/chunks/measurer.js.map +1 -0
- package/build/chunks/sfnt.d.ts +22 -0
- package/build/chunks/sfnt.d.ts.map +1 -0
- package/build/chunks/text.js.map +1 -1
- package/build/fonts-inspect.d.ts +10 -0
- package/build/fonts-inspect.d.ts.map +1 -0
- package/build/fonts-inspect.js +1 -0
- package/build/fonts.d.ts +5 -0
- package/build/fonts.js +4 -0
- package/build/index.d.ts +3 -1
- package/build/index.js +3 -1
- package/build/measurer.d.ts +2 -0
- package/build/measurer.js +1 -0
- package/package.json +18 -4
- package/src/fonts/bidi.ts +125 -0
- package/src/fonts/binary.ts +41 -0
- package/src/fonts/bound.ts +167 -0
- package/src/fonts/engines.ts +38 -0
- package/src/fonts/font.ts +458 -0
- package/src/fonts/gvar.ts +220 -0
- package/src/fonts/index.ts +11 -0
- package/src/fonts/indic.ts +626 -0
- package/src/fonts/joining.ts +141 -0
- package/src/fonts/kerning.ts +278 -0
- package/src/fonts/sfnt.ts +102 -0
- package/src/fonts/substitution.ts +686 -0
- package/src/fonts/tables.ts +162 -0
- package/src/fonts/variations.ts +175 -0
- package/src/fonts/words.ts +119 -0
- package/src/fonts-inspect.ts +8 -0
- package/src/fonts.ts +9 -0
- package/src/index.ts +2 -0
- package/src/measurer/advance.ts +307 -0
- package/src/measurer.ts +5 -0
- package/src/text/rope.ts +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime.js";
|
|
2
|
+
import { c as indicFeatures, i as shapingOf, l as indicForms, n as measureWidth, r as readFont, s as joiningStages } from "./font.js";
|
|
3
|
+
import { n as isEngineName, t as engines } from "./engines.js";
|
|
4
|
+
// src/fonts/bound.ts
|
|
5
|
+
/**
|
|
6
|
+
* The largest count of passes over the lookups of a shaper with stages. A pass covers one stage, and no shaper has more stages:
|
|
7
|
+
* the Indic shaper has 13 and the joining has 11. A rule that expands its own glyph has no fixed point, so it stops at this count.
|
|
8
|
+
*/
|
|
9
|
+
const ROUNDS = 16;
|
|
10
|
+
/** The relative error of one rounding to single precision. */
|
|
11
|
+
const FLOAT32 = 2 ** -24;
|
|
12
|
+
/** The relative error of one rounding to double precision. */
|
|
13
|
+
const FLOAT64 = 2 ** -53;
|
|
14
|
+
/** The largest size of a kerning value of a `ValueRecord`, and of its variation delta, in font units: the range of `int16`. */
|
|
15
|
+
const KERNING_RANGE = 2 ** 15;
|
|
16
|
+
function larger(a, b) {
|
|
17
|
+
return {
|
|
18
|
+
advance: Math.max(a.advance, b.advance),
|
|
19
|
+
glyphs: Math.max(a.glyphs, b.glyphs)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function sum(a, b) {
|
|
23
|
+
return {
|
|
24
|
+
advance: a.advance + b.advance,
|
|
25
|
+
glyphs: a.glyphs + b.glyphs
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** This function tells if a rule can apply to the glyphs `reached`. A ligature needs its first glyph and all its components. */
|
|
29
|
+
function applies(rule, reached) {
|
|
30
|
+
return reached.has(rule.from) && (rule.components ?? []).every((glyph) => reached.has(glyph));
|
|
31
|
+
}
|
|
32
|
+
/** This function adds the glyphs that the rules can give to `reached`. */
|
|
33
|
+
function reach(reached, rules) {
|
|
34
|
+
for (let size = -1; size !== reached.size;) {
|
|
35
|
+
size = reached.size;
|
|
36
|
+
for (const rule of rules) if (applies(rule, reached)) for (const glyph of rule.to) reached.add(glyph);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* This function applies one list of rules to the expansions. Each rule uses the values from before the list. A lookup applies
|
|
41
|
+
* once at a position, so its output does not go back into it. The function tells if a value changed.
|
|
42
|
+
*/
|
|
43
|
+
function relax(rules, expansions) {
|
|
44
|
+
const changes = [];
|
|
45
|
+
for (const { from, to } of rules) {
|
|
46
|
+
const before = expansions.get(from);
|
|
47
|
+
const after = to.reduce((total, glyph) => sum(total, expansions.get(glyph)), {
|
|
48
|
+
advance: 0,
|
|
49
|
+
glyphs: 0
|
|
50
|
+
});
|
|
51
|
+
if (after.advance > before.advance || after.glyphs > before.glyphs) changes.push([from, after]);
|
|
52
|
+
}
|
|
53
|
+
for (const [from, after] of changes) expansions.set(from, larger(expansions.get(from), after));
|
|
54
|
+
return changes.length > 0;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* This function gives the largest expansion of each glyph of `reached`. Each ligature charges its first glyph, so each output
|
|
58
|
+
* glyph comes from one input glyph. Each pass goes backward over the lookups: a glyph then gets the expansions of the lookups
|
|
59
|
+
* after the lookup that makes it. A shaper with stages goes over the list again for each stage, so the passes repeat.
|
|
60
|
+
*
|
|
61
|
+
* One context rule can apply its nested lookups in any order. So the lists of a lookup with nested lookups repeat until no
|
|
62
|
+
* value changes, with the same limit. A value that stops changing holds for each order.
|
|
63
|
+
*/
|
|
64
|
+
function expansionsOf(font, reached, lookups, rounds) {
|
|
65
|
+
const expansions = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const glyph of reached) expansions.set(glyph, {
|
|
67
|
+
advance: Math.max(0, font.advance(glyph)),
|
|
68
|
+
glyphs: 1
|
|
69
|
+
});
|
|
70
|
+
const active = lookups.map((lists) => lists.map((rules) => rules.filter((rule) => applies(rule, reached))));
|
|
71
|
+
for (let round = 0; round < rounds; round++) {
|
|
72
|
+
let changed = false;
|
|
73
|
+
for (let i = active.length - 1; i >= 0; i--) {
|
|
74
|
+
const lists = active[i];
|
|
75
|
+
for (let step = 0; step < (lists.length > 1 ? ROUNDS : 1); step++) {
|
|
76
|
+
let moved = false;
|
|
77
|
+
for (const rules of lists) moved = relax(rules, expansions) || moved;
|
|
78
|
+
if (!moved) break;
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!changed) break;
|
|
83
|
+
}
|
|
84
|
+
return expansions;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* This function gives a limit for the width of each text over the characters `characters`, with no measurement. For each such
|
|
88
|
+
* text, `measureWidth(font, text, size, options)` is at most `width(text.length, size)` of the result. Give the same `options`.
|
|
89
|
+
* A text with another character has no limit from this function.
|
|
90
|
+
*
|
|
91
|
+
* The limit is sound, not tight. It follows the widest character of the set. It includes each glyph of the GSUB closure, each
|
|
92
|
+
* dotted circle, each part or composite of the Indic normalization, and the largest positive kerning.
|
|
93
|
+
*/
|
|
94
|
+
function widthBound(font, characters, options = {}) {
|
|
95
|
+
const { script, features, shaper, kerning: pairLookups } = shapingOf(font, options);
|
|
96
|
+
const set = new Set(Array.from(characters, (character) => character.codePointAt(0)));
|
|
97
|
+
const indic = shaper === "indic" ? indicForms(set, (codePoint) => font.glyph(codePoint) !== 0) : void 0;
|
|
98
|
+
const reached = new Set([...indic?.codePoints ?? set].map((codePoint) => font.glyph(codePoint)));
|
|
99
|
+
const tags = shaper === "indic" ? indicFeatures(features) : shaper === "joining" ? joiningStages(features, true).flatMap((stage) => stage.features) : features;
|
|
100
|
+
const lookups = font.substitutions(script, tags).map((lookup) => lookup.rules());
|
|
101
|
+
reach(reached, lookups.flat(2));
|
|
102
|
+
const expansions = expansionsOf(font, reached, lookups, shaper === "plain" ? 1 : ROUNDS);
|
|
103
|
+
const form = (codePoint) => (indic?.composites.get(codePoint) ?? []).reduce((best, composite) => larger(best, form(composite)), expansions.get(font.glyph(codePoint)));
|
|
104
|
+
const inserted = indic === void 0 ? {
|
|
105
|
+
advance: 0,
|
|
106
|
+
glyphs: 0
|
|
107
|
+
} : expansions.get(font.glyph(9676));
|
|
108
|
+
let largest = {
|
|
109
|
+
advance: 0,
|
|
110
|
+
glyphs: 0
|
|
111
|
+
};
|
|
112
|
+
for (const codePoint of set) {
|
|
113
|
+
const parts = indic?.parts.get(codePoint) ?? [codePoint];
|
|
114
|
+
largest = larger(largest, parts.reduce((total, part) => sum(total, sum(form(part), inserted)), {
|
|
115
|
+
advance: 0,
|
|
116
|
+
glyphs: 0
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
let kerning = 0;
|
|
120
|
+
for (const lookup of pairLookups) {
|
|
121
|
+
const { first, second } = lookup.largest(reached);
|
|
122
|
+
kerning += first + second;
|
|
123
|
+
}
|
|
124
|
+
const { advance, glyphs } = largest;
|
|
125
|
+
const pixel = options.rounding === "pixel";
|
|
126
|
+
const float32 = options.sum === "float32" && !pixel;
|
|
127
|
+
const pixelMargin = pixel ? .5078125 + (kerning > 0 ? .5 : 0) : 0;
|
|
128
|
+
const positions = pairLookups.length * 2;
|
|
129
|
+
const positionMargin = float32 ? positions * (1.5 + 2 * KERNING_RANGE / font.unitsPerEm) / 65536 : 0;
|
|
130
|
+
const kerningMargin = float32 ? positions * 5 * FLOAT32 * KERNING_RANGE : 0;
|
|
131
|
+
return {
|
|
132
|
+
advance,
|
|
133
|
+
kerning,
|
|
134
|
+
glyphs,
|
|
135
|
+
width(length, size) {
|
|
136
|
+
return length * ((advance + glyphs * (kerning + kerningMargin)) * (size / font.unitsPerEm) + glyphs * (pixelMargin + positionMargin)) * (float32 ? (1 + FLOAT32) ** (length + 6) : 1) * (1 + (8192 + length) * FLOAT64);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// src/fonts.ts
|
|
141
|
+
var fonts_exports = /* @__PURE__ */ __exportAll({
|
|
142
|
+
engines: () => engines,
|
|
143
|
+
isEngineName: () => isEngineName,
|
|
144
|
+
measureWidth: () => measureWidth,
|
|
145
|
+
readFont: () => readFont,
|
|
146
|
+
widthBound: () => widthBound
|
|
147
|
+
});
|
|
148
|
+
export { widthBound as n, fonts_exports as t };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.js","names":[],"sources":["../../src/fonts/bound.ts","../../src/fonts.ts"],"sourcesContent":["import { shapingOf, type Font, type MeasureOptions } from \"./font.ts\";\nimport { indicFeatures, indicForms } from \"./indic.ts\";\nimport { joiningStages } from \"./joining.ts\";\nimport type { SubstitutionRule } from \"./substitution.ts\";\n\n/** The limit of `widthBound` for the width of a text over a declared set of characters. It needs no measurement. */\nexport interface WidthBound {\n /** The largest total advance of one character with all its expansions, in font units. */\n readonly advance: number;\n /** The largest positive kerning of one glyph, in font units. */\n readonly kerning: number;\n /** The largest count of glyphs of one character. */\n readonly glyphs: number;\n /**\n * This function gives the limit in px for a text of `length` UTF-16 units at the size `size`. It takes the length of a\n * string, so it does not read the text. The limit is for a text with only the characters of the set.\n */\n width(length: number, size: number): number;\n}\n\n/**\n * The largest count of passes over the lookups of a shaper with stages. A pass covers one stage, and no shaper has more stages:\n * the Indic shaper has 13 and the joining has 11. A rule that expands its own glyph has no fixed point, so it stops at this count.\n */\nconst ROUNDS = 16;\n/** The relative error of one rounding to single precision. */\nconst FLOAT32 = 2 ** -24;\n/** The relative error of one rounding to double precision. */\nconst FLOAT64 = 2 ** -53;\n/** The largest size of a kerning value of a `ValueRecord`, and of its variation delta, in font units: the range of `int16`. */\nconst KERNING_RANGE = 2 ** 15;\n\n/** The largest expansion of a glyph: the sum of its advances, and the count of its glyphs. */\ninterface Expansion {\n readonly advance: number;\n readonly glyphs: number;\n}\n\nfunction larger(a: Expansion, b: Expansion): Expansion {\n return { advance: Math.max(a.advance, b.advance), glyphs: Math.max(a.glyphs, b.glyphs) };\n}\n\nfunction sum(a: Expansion, b: Expansion): Expansion {\n return { advance: a.advance + b.advance, glyphs: a.glyphs + b.glyphs };\n}\n\n/** This function tells if a rule can apply to the glyphs `reached`. A ligature needs its first glyph and all its components. */\nfunction applies(rule: SubstitutionRule, reached: ReadonlySet<number>): boolean {\n return reached.has(rule.from) && (rule.components ?? []).every((glyph) => reached.has(glyph));\n}\n\n/** This function adds the glyphs that the rules can give to `reached`. */\nfunction reach(reached: Set<number>, rules: readonly SubstitutionRule[]): void {\n for (let size = -1; size !== reached.size; ) {\n size = reached.size;\n for (const rule of rules) if (applies(rule, reached)) for (const glyph of rule.to) reached.add(glyph);\n }\n}\n\n/**\n * This function applies one list of rules to the expansions. Each rule uses the values from before the list. A lookup applies\n * once at a position, so its output does not go back into it. The function tells if a value changed.\n */\nfunction relax(rules: readonly SubstitutionRule[], expansions: Map<number, Expansion>): boolean {\n const changes: (readonly [number, Expansion])[] = [];\n for (const { from, to } of rules) {\n const before = expansions.get(from)!;\n const after = to.reduce((total, glyph) => sum(total, expansions.get(glyph)!), { advance: 0, glyphs: 0 });\n if (after.advance > before.advance || after.glyphs > before.glyphs) changes.push([from, after]);\n }\n for (const [from, after] of changes) expansions.set(from, larger(expansions.get(from)!, after));\n return changes.length > 0;\n}\n\n/**\n * This function gives the largest expansion of each glyph of `reached`. Each ligature charges its first glyph, so each output\n * glyph comes from one input glyph. Each pass goes backward over the lookups: a glyph then gets the expansions of the lookups\n * after the lookup that makes it. A shaper with stages goes over the list again for each stage, so the passes repeat.\n *\n * One context rule can apply its nested lookups in any order. So the lists of a lookup with nested lookups repeat until no\n * value changes, with the same limit. A value that stops changing holds for each order.\n */\nfunction expansionsOf(font: Font, reached: ReadonlySet<number>, lookups: readonly (readonly (readonly SubstitutionRule[])[])[], rounds: number): Map<number, Expansion> {\n const expansions = new Map<number, Expansion>();\n for (const glyph of reached) expansions.set(glyph, { advance: Math.max(0, font.advance(glyph)), glyphs: 1 });\n const active = lookups.map((lists) => lists.map((rules) => rules.filter((rule) => applies(rule, reached))));\n for (let round = 0; round < rounds; round++) {\n let changed = false;\n for (let i = active.length - 1; i >= 0; i--) {\n const lists = active[i]!;\n for (let step = 0; step < (lists.length > 1 ? ROUNDS : 1); step++) {\n let moved = false;\n for (const rules of lists) moved = relax(rules, expansions) || moved;\n if (!moved) break;\n changed = true;\n }\n }\n if (!changed) break;\n }\n return expansions;\n}\n\n/**\n * This function gives a limit for the width of each text over the characters `characters`, with no measurement. For each such\n * text, `measureWidth(font, text, size, options)` is at most `width(text.length, size)` of the result. Give the same `options`.\n * A text with another character has no limit from this function.\n *\n * The limit is sound, not tight. It follows the widest character of the set. It includes each glyph of the GSUB closure, each\n * dotted circle, each part or composite of the Indic normalization, and the largest positive kerning.\n */\nexport function widthBound(font: Font, characters: string, options: MeasureOptions = {}): WidthBound {\n const { script, features, shaper, kerning: pairLookups } = shapingOf(font, options);\n const set = new Set(Array.from(characters, (character) => character.codePointAt(0)!));\n const indic = shaper === \"indic\" ? indicForms(set, (codePoint) => font.glyph(codePoint) !== 0) : undefined;\n\n // The roots: the glyph of each code point that the shaper can see. A character with no glyph gives the glyph 0\n const reached = new Set([...(indic?.codePoints ?? set)].map((codePoint) => font.glyph(codePoint)));\n // A union of the features gives the same lookups as the union of the stages. Each stage then counts, in the rounds\n const tags = shaper === \"indic\" ? indicFeatures(features) : shaper === \"joining\" ? joiningStages(features, true).flatMap((stage) => stage.features) : features;\n const lookups = font.substitutions(script, tags).map((lookup) => lookup.rules());\n reach(reached, lookups.flat(2));\n // The plain shaper is one stage: it applies each lookup once, in the order of the list\n const expansions = expansionsOf(font, reached, lookups, shaper === \"plain\" ? 1 : ROUNDS);\n\n const form = (codePoint: number): Expansion => (indic?.composites.get(codePoint) ?? []).reduce((best, composite) => larger(best, form(composite)), expansions.get(font.glyph(codePoint))!);\n // The Indic shaper can insert a dotted circle before each code point. Without a glyph, the code point U+25CC gives the glyph 0.\n // One code point gets at most one: a circle of the vowel constraints takes the next matra, so that syllable is not broken\n const inserted = indic === undefined ? { advance: 0, glyphs: 0 } : expansions.get(font.glyph(0x25cc))!;\n let largest: Expansion = { advance: 0, glyphs: 0 };\n for (const codePoint of set) {\n const parts = indic?.parts.get(codePoint) ?? [codePoint];\n largest = larger(largest, parts.reduce((total, part) => sum(total, sum(form(part), inserted)), { advance: 0, glyphs: 0 }));\n }\n\n // One glyph gets at most one first value and one second value from each lookup, and each subtable of a lookup is an alternative\n let kerning = 0;\n for (const lookup of pairLookups) {\n const { first, second } = lookup.largest(reached);\n kerning += first + second;\n }\n\n const { advance, glyphs } = largest;\n const pixel = options.rounding === \"pixel\";\n const float32 = options.sum === \"float32\" && !pixel;\n // Pixel: an advance gets at most 1/128 px from the grid of 26.6, and then 0.5 px. A kerning sum gets at most 0.5 px\n const pixelMargin = pixel ? 0.5 + 1 / 128 + (kerning > 0 ? 0.5 : 0) : 0;\n // Float32: each lookup gives a glyph two positions, in units of 1/65536 px. A negative value gets no help from the relative\n // factor below, so it has its own margin. The floor of a value and the round of a delta give 1 unit, the floor of the\n // multiplier gives 0.5 units, and the cut of the scale gives KERNING_RANGE / unitsPerEm units to each of the two parts.\n // The single precision gives FLOAT32 to the value and 4 FLOAT32 to the delta\n const positions = pairLookups.length * 2;\n const positionMargin = float32 ? (positions * (1.5 + (2 * KERNING_RANGE) / font.unitsPerEm)) / 65536 : 0;\n const kerningMargin = float32 ? positions * 5 * FLOAT32 * KERNING_RANGE : 0;\n return {\n advance,\n kerning,\n glyphs,\n width(length: number, size: number): number {\n const total = length * ((advance + glyphs * (kerning + kerningMargin)) * (size / font.unitsPerEm) + glyphs * (pixelMargin + positionMargin));\n // Float32: the fround of a glyph or 4 steps of a positive delta, the fround of a segment, and one step of the sum for\n // each segment. A text has at most one segment for each UTF-16 unit\n const single = float32 ? (1 + FLOAT32) ** (length + 6) : 1;\n // The double precision: the scale and a few products, and one rounding for each step of a sum of fractions\n return total * single * (1 + (2 ** 13 + length) * FLOAT64);\n },\n };\n}\n","/**\n * The subpath `./fonts`: the font model of typeset. It reads the tables of a TrueType, OpenType or WOFF2 file. It gives\n * the width of a text as the text engine of a browser gives it. The host gives the bytes of the font files.\n */\nexport { measureWidth, readFont, type Font, type MeasureOptions, type ReadOptions } from \"./fonts/font.ts\";\nexport { widthBound, type WidthBound } from \"./fonts/bound.ts\";\nexport { engines, isEngineName, type EngineName, type EngineRules } from \"./fonts/engines.ts\";\nexport type { Decompress } from \"./fonts/sfnt.ts\";\nexport type { Axis, Instance } from \"./fonts/variations.ts\";\n"],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { n as EngineRules, t as EngineName } from "./engines.js";
|
|
2
|
+
import { t as Decompress } from "./sfnt.js";
|
|
3
|
+
/** One font file of the host, with the family, the weight and the style that CSS matches. */
|
|
4
|
+
interface FontFace {
|
|
5
|
+
/** The family name, as the CSS font names it. The match ignores the case. */
|
|
6
|
+
readonly family: string;
|
|
7
|
+
/** The bytes of a TrueType, OpenType or WOFF2 file. */
|
|
8
|
+
readonly data: Uint8Array;
|
|
9
|
+
/**
|
|
10
|
+
* The weight of the face: one value, or the range of a variable font. The default is the range of the `wght` axis of the
|
|
11
|
+
* font, or 400 for a font with no `wght` axis.
|
|
12
|
+
*/
|
|
13
|
+
readonly weight?: number | readonly [number, number];
|
|
14
|
+
/** The style of the face. The default is `normal`. */
|
|
15
|
+
readonly style?: "normal" | "italic" | "oblique";
|
|
16
|
+
/** The Brotli decompressor of a WOFF2 file. */
|
|
17
|
+
readonly decompress?: Decompress;
|
|
18
|
+
}
|
|
19
|
+
/** The options of `fontAdvance`. */
|
|
20
|
+
interface FontAdvanceOptions {
|
|
21
|
+
/** The font files of the host. The order sets the choice between two faces with the same match. */
|
|
22
|
+
readonly faces: readonly FontFace[];
|
|
23
|
+
/** The text engine: a row name of `engines`, or the rules of a row. The default is `freetype`, as Chromium on Linux. */
|
|
24
|
+
readonly engine?: EngineName | EngineRules;
|
|
25
|
+
/**
|
|
26
|
+
* The shaping of `measureWidth`. The default is `canvas`, as `canvasAdvance` of layout-contract measures each segment
|
|
27
|
+
* with `measureText`. Use `dom` for the widths of a text in a page.
|
|
28
|
+
*/
|
|
29
|
+
readonly shaping?: "dom" | "canvas";
|
|
30
|
+
}
|
|
31
|
+
/** The parts of a CSS font shorthand that change a width. */
|
|
32
|
+
interface FontSpec {
|
|
33
|
+
readonly style: "normal" | "italic" | "oblique";
|
|
34
|
+
/** The angle of an oblique style in degrees. CSS gives 14 when the font names no angle. */
|
|
35
|
+
readonly angle: number;
|
|
36
|
+
/** The weight from 1 to 1000. */
|
|
37
|
+
readonly weight: number;
|
|
38
|
+
/** The width of the font as a percent of the normal width, from the keyword of `font-stretch`. */
|
|
39
|
+
readonly stretch: number;
|
|
40
|
+
/** The size in px. */
|
|
41
|
+
readonly size: number;
|
|
42
|
+
/** The family names in the order of the font, with no quotes. */
|
|
43
|
+
readonly families: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* This function reads a CSS font shorthand, for example `italic 700 16px/1.2 "Noto Sans", sans-serif`. The size must be in
|
|
47
|
+
* `px` or `pt`. The function refuses `small-caps`, the system font keywords and the relative sizes with a `RangeError`.
|
|
48
|
+
*/
|
|
49
|
+
declare function parseFont(font: string): FontSpec;
|
|
50
|
+
/**
|
|
51
|
+
* This function gives the OpenType script tag of a text, as HarfBuzz guesses it. The tag is the script of the first
|
|
52
|
+
* character that is not of the scripts Common, Inherited or Unknown. A text of only those characters, for example emoji, gets `DFLT`.
|
|
53
|
+
*/
|
|
54
|
+
declare function scriptTag(text: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* This function gives the advance function of the faces of the host: the width in px of a text in a CSS font. Each run
|
|
57
|
+
* of the text gets the first family of the font with a glyph for its first character. When no family has a glyph, the
|
|
58
|
+
* run gets the first family that has a face. The width of a run is `measureWidth` with the size of the font, the script
|
|
59
|
+
* of `scriptTag` and the rules of the engine. The instance of a variable face takes the weight and the stretch as `wdth`.
|
|
60
|
+
* It takes the style as `ital` or `slnt`, and the size as `opsz`, as `font-optical-sizing: auto` does.
|
|
61
|
+
*/
|
|
62
|
+
declare function fontAdvance(options: FontAdvanceOptions): (text: string, font: string) => number;
|
|
63
|
+
declare namespace measurer_d_exports {
|
|
64
|
+
export { FontAdvanceOptions, FontFace, FontSpec, fontAdvance, parseFont, scriptTag };
|
|
65
|
+
}
|
|
66
|
+
export { fontAdvance as a, FontSpec as i, FontAdvanceOptions as n, parseFont as o, FontFace as r, scriptTag as s, measurer_d_exports as t };
|
|
67
|
+
//# sourceMappingURL=measurer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measurer.d.ts","names":[],"sources":["../../src/measurer/advance.ts","../../src/measurer.ts"],"mappings":";;;UAWiB;;WAEN;;WAEA,MAAM;;;;;WAKN;;WAEA;;WAEA,aAAa;;;UAIP;;WAEN,gBAAgB;;WAEhB,SAAS,aAAa;;;;;WAKtB;;;UAIM;WACN;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;;;;;iBAuBK,UAAU,eAAe;;;;;iBAiFzB,UAAU;;;;;;;;iBAmDV,YAAY,SAAS,sBAAsB,cAAc"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime.js";
|
|
2
|
+
import { n as isEngineName, t as engines } from "./engines.js";
|
|
3
|
+
import { n as measureWidth, r as readFont } from "./font.js";
|
|
4
|
+
// src/measurer/advance.ts
|
|
5
|
+
/**
|
|
6
|
+
* The advance function of the font model: the width of a text in a CSS font, as the measurer of
|
|
7
|
+
* `@mark1russell7/layout-contract/text` asks for it. The host gives the font files as faces. The function finds the face
|
|
8
|
+
* of each run of the text with the matching rules of CSS. It finds the script of the run, and measures the run with the
|
|
9
|
+
* rules of one text engine.
|
|
10
|
+
*/
|
|
11
|
+
const stretches = {
|
|
12
|
+
"ultra-condensed": 50,
|
|
13
|
+
"extra-condensed": 62.5,
|
|
14
|
+
condensed: 75,
|
|
15
|
+
"semi-condensed": 87.5,
|
|
16
|
+
"semi-expanded": 112.5,
|
|
17
|
+
expanded: 125,
|
|
18
|
+
"extra-expanded": 150,
|
|
19
|
+
"ultra-expanded": 200
|
|
20
|
+
};
|
|
21
|
+
const sizePattern = /^((?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)(px|pt)(\/.*)?$/i;
|
|
22
|
+
const anglePattern = /^(-?(?:\d+(?:\.\d+)?|\.\d+))deg$/i;
|
|
23
|
+
const weightPattern = /^(?:\d+(?:\.\d+)?|\.\d+)$/;
|
|
24
|
+
/**
|
|
25
|
+
* This function reads a CSS font shorthand, for example `italic 700 16px/1.2 "Noto Sans", sans-serif`. The size must be in
|
|
26
|
+
* `px` or `pt`. The function refuses `small-caps`, the system font keywords and the relative sizes with a `RangeError`.
|
|
27
|
+
*/
|
|
28
|
+
function parseFont(font) {
|
|
29
|
+
const tokens = font.trim().split(/\s+/);
|
|
30
|
+
let style = "normal";
|
|
31
|
+
let angle = 14;
|
|
32
|
+
let weight = 400;
|
|
33
|
+
let stretch = 100;
|
|
34
|
+
let index = 0;
|
|
35
|
+
for (; index < tokens.length; index++) {
|
|
36
|
+
const token = tokens[index];
|
|
37
|
+
const lower = token.toLowerCase();
|
|
38
|
+
if (sizePattern.exec(token) !== null) break;
|
|
39
|
+
if (lower === "normal") continue;
|
|
40
|
+
if (lower === "italic") style = "italic";
|
|
41
|
+
else if (lower === "oblique") {
|
|
42
|
+
style = "oblique";
|
|
43
|
+
const next = anglePattern.exec(tokens[index + 1] ?? "");
|
|
44
|
+
if (next !== null) {
|
|
45
|
+
angle = Number(next[1]);
|
|
46
|
+
index++;
|
|
47
|
+
}
|
|
48
|
+
} else if (lower === "bold" || lower === "bolder") weight = 700;
|
|
49
|
+
else if (lower === "lighter") weight = 100;
|
|
50
|
+
else if (weightPattern.test(token) && Number(token) >= 1 && Number(token) <= 1e3) weight = Number(token);
|
|
51
|
+
else if (Object.hasOwn(stretches, lower)) stretch = stretches[lower];
|
|
52
|
+
else throw new RangeError(`the font "${font}" has the part "${token}", which the measurer does not know`);
|
|
53
|
+
}
|
|
54
|
+
const size = sizePattern.exec(tokens[index] ?? "");
|
|
55
|
+
if (size === null) throw new RangeError(`the font "${font}" has no size in px or pt`);
|
|
56
|
+
let rest = tokens.slice(index + 1).join(" ");
|
|
57
|
+
if (size[3] === void 0 && rest.startsWith("/")) rest = rest.slice(1).trim().replace(/^\S+\s*/, "");
|
|
58
|
+
const families = rest.split(",").map((family) => family.trim().replace(/^(["'])(.*)\1$/, "$2").trim()).filter((family) => family.length > 0);
|
|
59
|
+
if (families.length === 0) throw new RangeError(`the font "${font}" has no family`);
|
|
60
|
+
const value = Number(size[1]);
|
|
61
|
+
return {
|
|
62
|
+
style,
|
|
63
|
+
angle,
|
|
64
|
+
weight,
|
|
65
|
+
stretch,
|
|
66
|
+
size: size[2].toLowerCase() === "pt" ? value * 4 / 3 : value,
|
|
67
|
+
families
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** The Unicode scripts and their OpenType tags. A script that is not in the list gets `DFLT`. */
|
|
71
|
+
const scriptPatterns = [
|
|
72
|
+
[/\p{sc=Latin}/u, "latn"],
|
|
73
|
+
[/\p{sc=Greek}/u, "grek"],
|
|
74
|
+
[/\p{sc=Cyrillic}/u, "cyrl"],
|
|
75
|
+
[/\p{sc=Armenian}/u, "armn"],
|
|
76
|
+
[/\p{sc=Hebrew}/u, "hebr"],
|
|
77
|
+
[/\p{sc=Arabic}/u, "arab"],
|
|
78
|
+
[/\p{sc=Syriac}/u, "syrc"],
|
|
79
|
+
[/\p{sc=Thaana}/u, "thaa"],
|
|
80
|
+
[/\p{sc=Devanagari}/u, "deva"],
|
|
81
|
+
[/\p{sc=Bengali}/u, "beng"],
|
|
82
|
+
[/\p{sc=Gurmukhi}/u, "guru"],
|
|
83
|
+
[/\p{sc=Gujarati}/u, "gujr"],
|
|
84
|
+
[/\p{sc=Oriya}/u, "orya"],
|
|
85
|
+
[/\p{sc=Tamil}/u, "taml"],
|
|
86
|
+
[/\p{sc=Telugu}/u, "telu"],
|
|
87
|
+
[/\p{sc=Kannada}/u, "knda"],
|
|
88
|
+
[/\p{sc=Malayalam}/u, "mlym"],
|
|
89
|
+
[/\p{sc=Sinhala}/u, "sinh"],
|
|
90
|
+
[/\p{sc=Thai}/u, "thai"],
|
|
91
|
+
[/\p{sc=Lao}/u, "lao "],
|
|
92
|
+
[/\p{sc=Tibetan}/u, "tibt"],
|
|
93
|
+
[/\p{sc=Myanmar}/u, "mymr"],
|
|
94
|
+
[/\p{sc=Georgian}/u, "geor"],
|
|
95
|
+
[/\p{sc=Hangul}/u, "hang"],
|
|
96
|
+
[/\p{sc=Ethiopic}/u, "ethi"],
|
|
97
|
+
[/\p{sc=Khmer}/u, "khmr"],
|
|
98
|
+
[/\p{sc=Mongolian}/u, "mong"],
|
|
99
|
+
[/[\p{sc=Hiragana}\p{sc=Katakana}]/u, "kana"],
|
|
100
|
+
[/\p{sc=Bopomofo}/u, "bopo"],
|
|
101
|
+
[/\p{sc=Han}/u, "hani"]
|
|
102
|
+
];
|
|
103
|
+
const neutral = /[\p{sc=Common}\p{sc=Inherited}\p{sc=Unknown}]/u;
|
|
104
|
+
/**
|
|
105
|
+
* This function gives the OpenType script tag of a text, as HarfBuzz guesses it. The tag is the script of the first
|
|
106
|
+
* character that is not of the scripts Common, Inherited or Unknown. A text of only those characters, for example emoji, gets `DFLT`.
|
|
107
|
+
*/
|
|
108
|
+
function scriptTag(text) {
|
|
109
|
+
for (const character of text) {
|
|
110
|
+
if (neutral.test(character)) continue;
|
|
111
|
+
for (const [pattern, tag] of scriptPatterns) if (pattern.test(character)) return tag;
|
|
112
|
+
return "DFLT";
|
|
113
|
+
}
|
|
114
|
+
return "DFLT";
|
|
115
|
+
}
|
|
116
|
+
/** A character that stays in the run of the character before it: a mark, a joiner, a variation selector, a tag or a skin tone. */
|
|
117
|
+
const attached = /^[\p{M}︀-️\u{e0020}-\u{e007f}\u{e0100}-\u{e01ef}\u{1f3fb}-\u{1f3ff}]$/u;
|
|
118
|
+
const styleOrder = {
|
|
119
|
+
normal: [
|
|
120
|
+
"normal",
|
|
121
|
+
"oblique",
|
|
122
|
+
"italic"
|
|
123
|
+
],
|
|
124
|
+
italic: [
|
|
125
|
+
"italic",
|
|
126
|
+
"oblique",
|
|
127
|
+
"normal"
|
|
128
|
+
],
|
|
129
|
+
oblique: [
|
|
130
|
+
"oblique",
|
|
131
|
+
"italic",
|
|
132
|
+
"normal"
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* This function gives the rank of a face for the weight `weight`, with the rules of CSS Fonts 4. A lower rank is a
|
|
137
|
+
* better match. A face whose range holds the weight has the rank 0.
|
|
138
|
+
*/
|
|
139
|
+
function weightRank(range, weight) {
|
|
140
|
+
const [min, max] = range;
|
|
141
|
+
if (min <= weight && weight <= max) return 0;
|
|
142
|
+
const above = min > weight;
|
|
143
|
+
if (weight >= 400 && weight <= 500) {
|
|
144
|
+
if (above && min <= 500) return 1e3 + min;
|
|
145
|
+
if (!above) return 3e3 - max;
|
|
146
|
+
return 5e3 + min;
|
|
147
|
+
}
|
|
148
|
+
if (weight < 400) return above ? 3e3 + min : 1e3 - max;
|
|
149
|
+
return above ? 1e3 + min : 3e3 - max;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* This function gives the advance function of the faces of the host: the width in px of a text in a CSS font. Each run
|
|
153
|
+
* of the text gets the first family of the font with a glyph for its first character. When no family has a glyph, the
|
|
154
|
+
* run gets the first family that has a face. The width of a run is `measureWidth` with the size of the font, the script
|
|
155
|
+
* of `scriptTag` and the rules of the engine. The instance of a variable face takes the weight and the stretch as `wdth`.
|
|
156
|
+
* It takes the style as `ital` or `slnt`, and the size as `opsz`, as `font-optical-sizing: auto` does.
|
|
157
|
+
*/
|
|
158
|
+
function fontAdvance(options) {
|
|
159
|
+
const engine = options.engine ?? "freetype";
|
|
160
|
+
if (typeof engine === "string" && !isEngineName(engine)) throw new RangeError(`the engine "${engine}" is not a row of engines`);
|
|
161
|
+
const rules = typeof engine === "string" ? engines[engine] : engine;
|
|
162
|
+
const shaping = options.shaping ?? "canvas";
|
|
163
|
+
const faces = options.faces;
|
|
164
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
165
|
+
const specs = /* @__PURE__ */ new Map();
|
|
166
|
+
const instances = /* @__PURE__ */ new Map();
|
|
167
|
+
const families = /* @__PURE__ */ new Map();
|
|
168
|
+
const load = (index) => {
|
|
169
|
+
let entry = loaded.get(index);
|
|
170
|
+
if (entry === void 0) {
|
|
171
|
+
const face = faces[index];
|
|
172
|
+
const font = readFont(face.data, {
|
|
173
|
+
deltas: rules.deltas,
|
|
174
|
+
metrics: rules.metrics,
|
|
175
|
+
...face.decompress === void 0 ? {} : { decompress: face.decompress }
|
|
176
|
+
});
|
|
177
|
+
const axis = font.axes.find((value) => value.tag === "wght");
|
|
178
|
+
const declared = face.weight;
|
|
179
|
+
entry = {
|
|
180
|
+
face,
|
|
181
|
+
index,
|
|
182
|
+
font,
|
|
183
|
+
range: typeof declared === "number" ? [declared, declared] : declared ?? (axis === void 0 ? [400, 400] : [axis.min, axis.max])
|
|
184
|
+
};
|
|
185
|
+
loaded.set(index, entry);
|
|
186
|
+
}
|
|
187
|
+
return entry;
|
|
188
|
+
};
|
|
189
|
+
/** This function gives the font of the best face of a family for the spec, or `undefined` when the family has no face. */
|
|
190
|
+
const fontOf = (family, spec) => {
|
|
191
|
+
const name = family.toLowerCase();
|
|
192
|
+
const candidates = [];
|
|
193
|
+
for (let i = 0; i < faces.length; i++) if (faces[i].family.toLowerCase() === name) candidates.push(load(i));
|
|
194
|
+
if (candidates.length === 0) return void 0;
|
|
195
|
+
const style = styleOrder[spec.style].find((value) => candidates.some((entry) => (entry.face.style ?? "normal") === value));
|
|
196
|
+
let best;
|
|
197
|
+
let bestRank = Infinity;
|
|
198
|
+
for (const entry of candidates) {
|
|
199
|
+
if ((entry.face.style ?? "normal") !== style) continue;
|
|
200
|
+
const rank = weightRank(entry.range, spec.weight);
|
|
201
|
+
if (rank < bestRank) {
|
|
202
|
+
best = entry;
|
|
203
|
+
bestRank = rank;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const chosen = best;
|
|
207
|
+
const tags = new Set(chosen.font.axes.map((axis) => axis.tag));
|
|
208
|
+
const instance = {};
|
|
209
|
+
if (tags.has("wght")) instance["wght"] = Math.min(chosen.range[1], Math.max(chosen.range[0], spec.weight));
|
|
210
|
+
if (tags.has("wdth")) instance["wdth"] = spec.stretch;
|
|
211
|
+
if (tags.has("opsz")) instance["opsz"] = spec.size;
|
|
212
|
+
if (tags.has("ital")) instance["ital"] = spec.style === "italic" ? 1 : 0;
|
|
213
|
+
if (tags.has("slnt")) instance["slnt"] = spec.style === "oblique" ? -spec.angle : 0;
|
|
214
|
+
const key = `${chosen.index}|${JSON.stringify(instance)}`;
|
|
215
|
+
let font = instances.get(key);
|
|
216
|
+
if (font === void 0) {
|
|
217
|
+
font = Object.keys(instance).length === 0 ? chosen.font : chosen.font.at(instance);
|
|
218
|
+
instances.set(key, font);
|
|
219
|
+
}
|
|
220
|
+
return font;
|
|
221
|
+
};
|
|
222
|
+
const fontsOf = (font) => {
|
|
223
|
+
let spec = specs.get(font);
|
|
224
|
+
if (spec === void 0) {
|
|
225
|
+
spec = parseFont(font);
|
|
226
|
+
if (specs.size >= 256) specs.clear();
|
|
227
|
+
specs.set(font, spec);
|
|
228
|
+
}
|
|
229
|
+
let fonts = families.get(font);
|
|
230
|
+
if (fonts === void 0) {
|
|
231
|
+
fonts = spec.families.map((family) => fontOf(family, spec)).filter((value) => value !== void 0);
|
|
232
|
+
if (fonts.length === 0) throw new RangeError(`no face of the host has a family of the font "${font}"`);
|
|
233
|
+
if (families.size >= 256) families.clear();
|
|
234
|
+
families.set(font, fonts);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
spec,
|
|
238
|
+
fonts
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
return (text, font) => {
|
|
242
|
+
const { spec, fonts } = fontsOf(font);
|
|
243
|
+
const measure = (run, face) => measureWidth(face, run, spec.size, {
|
|
244
|
+
script: scriptTag(run),
|
|
245
|
+
shaping,
|
|
246
|
+
rounding: rules.rounding,
|
|
247
|
+
sum: rules.sum
|
|
248
|
+
});
|
|
249
|
+
if (fonts.length === 1) return measure(text, fonts[0]);
|
|
250
|
+
let width = 0;
|
|
251
|
+
let run = "";
|
|
252
|
+
let current;
|
|
253
|
+
let joined = false;
|
|
254
|
+
for (const character of text) {
|
|
255
|
+
const code = character.codePointAt(0);
|
|
256
|
+
const face = current !== void 0 && (joined || attached.test(character)) ? current : fonts.find((value) => value.glyph(code) !== 0) ?? fonts[0];
|
|
257
|
+
if (face !== current && run.length > 0) {
|
|
258
|
+
width += measure(run, current);
|
|
259
|
+
run = "";
|
|
260
|
+
}
|
|
261
|
+
current = face;
|
|
262
|
+
run += character;
|
|
263
|
+
joined = code === 8205;
|
|
264
|
+
}
|
|
265
|
+
if (run.length > 0) width += measure(run, current);
|
|
266
|
+
return width;
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
// src/measurer.ts
|
|
270
|
+
var measurer_exports = /* @__PURE__ */ __exportAll({
|
|
271
|
+
fontAdvance: () => fontAdvance,
|
|
272
|
+
parseFont: () => parseFont,
|
|
273
|
+
scriptTag: () => scriptTag
|
|
274
|
+
});
|
|
275
|
+
export { scriptTag as i, fontAdvance as n, parseFont as r, measurer_exports as t };
|
|
276
|
+
|
|
277
|
+
//# sourceMappingURL=measurer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"measurer.js","names":[],"sources":["../../src/measurer/advance.ts","../../src/measurer.ts"],"sourcesContent":["/**\n * The advance function of the font model: the width of a text in a CSS font, as the measurer of\n * `@mark1russell7/layout-contract/text` asks for it. The host gives the font files as faces. The function finds the face\n * of each run of the text with the matching rules of CSS. It finds the script of the run, and measures the run with the\n * rules of one text engine.\n */\nimport { engines, isEngineName, type EngineName, type EngineRules } from \"../fonts/engines.ts\";\nimport { measureWidth, readFont, type Font } from \"../fonts/font.ts\";\nimport type { Decompress } from \"../fonts/sfnt.ts\";\n\n/** One font file of the host, with the family, the weight and the style that CSS matches. */\nexport interface FontFace {\n /** The family name, as the CSS font names it. The match ignores the case. */\n readonly family: string;\n /** The bytes of a TrueType, OpenType or WOFF2 file. */\n readonly data: Uint8Array;\n /**\n * The weight of the face: one value, or the range of a variable font. The default is the range of the `wght` axis of the\n * font, or 400 for a font with no `wght` axis.\n */\n readonly weight?: number | readonly [number, number];\n /** The style of the face. The default is `normal`. */\n readonly style?: \"normal\" | \"italic\" | \"oblique\";\n /** The Brotli decompressor of a WOFF2 file. */\n readonly decompress?: Decompress;\n}\n\n/** The options of `fontAdvance`. */\nexport interface FontAdvanceOptions {\n /** The font files of the host. The order sets the choice between two faces with the same match. */\n readonly faces: readonly FontFace[];\n /** The text engine: a row name of `engines`, or the rules of a row. The default is `freetype`, as Chromium on Linux. */\n readonly engine?: EngineName | EngineRules;\n /**\n * The shaping of `measureWidth`. The default is `canvas`, as `canvasAdvance` of layout-contract measures each segment\n * with `measureText`. Use `dom` for the widths of a text in a page.\n */\n readonly shaping?: \"dom\" | \"canvas\";\n}\n\n/** The parts of a CSS font shorthand that change a width. */\nexport interface FontSpec {\n readonly style: \"normal\" | \"italic\" | \"oblique\";\n /** The angle of an oblique style in degrees. CSS gives 14 when the font names no angle. */\n readonly angle: number;\n /** The weight from 1 to 1000. */\n readonly weight: number;\n /** The width of the font as a percent of the normal width, from the keyword of `font-stretch`. */\n readonly stretch: number;\n /** The size in px. */\n readonly size: number;\n /** The family names in the order of the font, with no quotes. */\n readonly families: readonly string[];\n}\n\nconst stretches: Readonly<Record<string, number>> = {\n \"ultra-condensed\": 50,\n \"extra-condensed\": 62.5,\n condensed: 75,\n \"semi-condensed\": 87.5,\n \"semi-expanded\": 112.5,\n expanded: 125,\n \"extra-expanded\": 150,\n \"ultra-expanded\": 200,\n};\n\n// Each number pattern matches a text in one way only. Thus a long run of digits does not make the match slow.\nconst sizePattern = /^((?:\\d+(?:\\.\\d+)?|\\.\\d+)(?:e[+-]?\\d+)?)(px|pt)(\\/.*)?$/i;\nconst anglePattern = /^(-?(?:\\d+(?:\\.\\d+)?|\\.\\d+))deg$/i;\nconst weightPattern = /^(?:\\d+(?:\\.\\d+)?|\\.\\d+)$/;\n\n/**\n * This function reads a CSS font shorthand, for example `italic 700 16px/1.2 \"Noto Sans\", sans-serif`. The size must be in\n * `px` or `pt`. The function refuses `small-caps`, the system font keywords and the relative sizes with a `RangeError`.\n */\nexport function parseFont(font: string): FontSpec {\n const tokens = font.trim().split(/\\s+/);\n let style: FontSpec[\"style\"] = \"normal\";\n let angle = 14;\n let weight = 400;\n let stretch = 100;\n let index = 0;\n for (; index < tokens.length; index++) {\n const token = tokens[index]!;\n const lower = token.toLowerCase();\n const size = sizePattern.exec(token);\n if (size !== null) break;\n if (lower === \"normal\") continue;\n if (lower === \"italic\") style = \"italic\";\n else if (lower === \"oblique\") {\n style = \"oblique\";\n const next = anglePattern.exec(tokens[index + 1] ?? \"\");\n if (next !== null) {\n angle = Number(next[1]);\n index++;\n }\n } else if (lower === \"bold\" || lower === \"bolder\") weight = 700;\n else if (lower === \"lighter\") weight = 100;\n else if (weightPattern.test(token) && Number(token) >= 1 && Number(token) <= 1000) weight = Number(token);\n else if (Object.hasOwn(stretches, lower)) stretch = stretches[lower]!;\n else throw new RangeError(`the font \"${font}\" has the part \"${token}\", which the measurer does not know`);\n }\n const size = sizePattern.exec(tokens[index] ?? \"\");\n if (size === null) throw new RangeError(`the font \"${font}\" has no size in px or pt`);\n let rest = tokens.slice(index + 1).join(\" \");\n // A line height after a space: \"16px / 1.2\" or \"16px /1.2\"\n if (size[3] === undefined && rest.startsWith(\"/\")) rest = rest.slice(1).trim().replace(/^\\S+\\s*/, \"\");\n const families = rest\n .split(\",\")\n .map((family) => family.trim().replace(/^([\"'])(.*)\\1$/, \"$2\").trim())\n .filter((family) => family.length > 0);\n if (families.length === 0) throw new RangeError(`the font \"${font}\" has no family`);\n const value = Number(size[1]);\n return { style, angle, weight, stretch, size: size[2]!.toLowerCase() === \"pt\" ? (value * 4) / 3 : value, families };\n}\n\n/** The Unicode scripts and their OpenType tags. A script that is not in the list gets `DFLT`. */\nconst scriptPatterns: readonly (readonly [RegExp, string])[] = [\n [/\\p{sc=Latin}/u, \"latn\"],\n [/\\p{sc=Greek}/u, \"grek\"],\n [/\\p{sc=Cyrillic}/u, \"cyrl\"],\n [/\\p{sc=Armenian}/u, \"armn\"],\n [/\\p{sc=Hebrew}/u, \"hebr\"],\n [/\\p{sc=Arabic}/u, \"arab\"],\n [/\\p{sc=Syriac}/u, \"syrc\"],\n [/\\p{sc=Thaana}/u, \"thaa\"],\n [/\\p{sc=Devanagari}/u, \"deva\"],\n [/\\p{sc=Bengali}/u, \"beng\"],\n [/\\p{sc=Gurmukhi}/u, \"guru\"],\n [/\\p{sc=Gujarati}/u, \"gujr\"],\n [/\\p{sc=Oriya}/u, \"orya\"],\n [/\\p{sc=Tamil}/u, \"taml\"],\n [/\\p{sc=Telugu}/u, \"telu\"],\n [/\\p{sc=Kannada}/u, \"knda\"],\n [/\\p{sc=Malayalam}/u, \"mlym\"],\n [/\\p{sc=Sinhala}/u, \"sinh\"],\n [/\\p{sc=Thai}/u, \"thai\"],\n [/\\p{sc=Lao}/u, \"lao \"],\n [/\\p{sc=Tibetan}/u, \"tibt\"],\n [/\\p{sc=Myanmar}/u, \"mymr\"],\n [/\\p{sc=Georgian}/u, \"geor\"],\n [/\\p{sc=Hangul}/u, \"hang\"],\n [/\\p{sc=Ethiopic}/u, \"ethi\"],\n [/\\p{sc=Khmer}/u, \"khmr\"],\n [/\\p{sc=Mongolian}/u, \"mong\"],\n [/[\\p{sc=Hiragana}\\p{sc=Katakana}]/u, \"kana\"],\n [/\\p{sc=Bopomofo}/u, \"bopo\"],\n [/\\p{sc=Han}/u, \"hani\"],\n];\n\nconst neutral = /[\\p{sc=Common}\\p{sc=Inherited}\\p{sc=Unknown}]/u;\n\n/**\n * This function gives the OpenType script tag of a text, as HarfBuzz guesses it. The tag is the script of the first\n * character that is not of the scripts Common, Inherited or Unknown. A text of only those characters, for example emoji, gets `DFLT`.\n */\nexport function scriptTag(text: string): string {\n for (const character of text) {\n if (neutral.test(character)) continue;\n for (const [pattern, tag] of scriptPatterns) if (pattern.test(character)) return tag;\n return \"DFLT\";\n }\n return \"DFLT\";\n}\n\n/** A character that stays in the run of the character before it: a mark, a joiner, a variation selector, a tag or a skin tone. */\nconst attached = /^[\\p{M}︀-️\\u{e0020}-\\u{e007f}\\u{e0100}-\\u{e01ef}\\u{1f3fb}-\\u{1f3ff}]$/u;\n\nconst styleOrder: Readonly<Record<FontSpec[\"style\"], readonly FontSpec[\"style\"][]>> = {\n normal: [\"normal\", \"oblique\", \"italic\"],\n italic: [\"italic\", \"oblique\", \"normal\"],\n oblique: [\"oblique\", \"italic\", \"normal\"],\n};\n\n/** A face with its font, read at the first use. */\ninterface Loaded {\n readonly face: FontFace;\n readonly index: number;\n readonly font: Font;\n readonly range: readonly [number, number];\n}\n\n/**\n * This function gives the rank of a face for the weight `weight`, with the rules of CSS Fonts 4. A lower rank is a\n * better match. A face whose range holds the weight has the rank 0.\n */\nfunction weightRank(range: readonly [number, number], weight: number): number {\n const [min, max] = range;\n if (min <= weight && weight <= max) return 0;\n const above = min > weight;\n // The groups of CSS: 1 is the first choice after an exact match, 3 is the last\n if (weight >= 400 && weight <= 500) {\n if (above && min <= 500) return 1000 + min;\n if (!above) return 3000 - max;\n return 5000 + min;\n }\n if (weight < 400) return above ? 3000 + min : 1000 - max;\n return above ? 1000 + min : 3000 - max;\n}\n\n/**\n * This function gives the advance function of the faces of the host: the width in px of a text in a CSS font. Each run\n * of the text gets the first family of the font with a glyph for its first character. When no family has a glyph, the\n * run gets the first family that has a face. The width of a run is `measureWidth` with the size of the font, the script\n * of `scriptTag` and the rules of the engine. The instance of a variable face takes the weight and the stretch as `wdth`.\n * It takes the style as `ital` or `slnt`, and the size as `opsz`, as `font-optical-sizing: auto` does.\n */\nexport function fontAdvance(options: FontAdvanceOptions): (text: string, font: string) => number {\n const engine = options.engine ?? \"freetype\";\n if (typeof engine === \"string\" && !isEngineName(engine)) throw new RangeError(`the engine \"${engine}\" is not a row of engines`);\n const rules: EngineRules = typeof engine === \"string\" ? engines[engine] : engine;\n const shaping = options.shaping ?? \"canvas\";\n const faces = options.faces;\n const loaded = new Map<number, Loaded>();\n const specs = new Map<string, FontSpec>();\n const instances = new Map<string, Font>();\n const families = new Map<string, readonly Font[] | undefined>();\n\n const load = (index: number): Loaded => {\n let entry = loaded.get(index);\n if (entry === undefined) {\n const face = faces[index]!;\n const font = readFont(face.data, { deltas: rules.deltas, metrics: rules.metrics, ...(face.decompress === undefined ? {} : { decompress: face.decompress }) });\n const axis = font.axes.find((value) => value.tag === \"wght\");\n const declared = face.weight;\n const range: readonly [number, number] = typeof declared === \"number\" ? [declared, declared] : (declared ?? (axis === undefined ? [400, 400] : [axis.min, axis.max]));\n entry = { face, index, font, range };\n loaded.set(index, entry);\n }\n return entry;\n };\n\n /** This function gives the font of the best face of a family for the spec, or `undefined` when the family has no face. */\n const fontOf = (family: string, spec: FontSpec): Font | undefined => {\n const name = family.toLowerCase();\n const candidates: Loaded[] = [];\n for (let i = 0; i < faces.length; i++) if (faces[i]!.family.toLowerCase() === name) candidates.push(load(i));\n if (candidates.length === 0) return undefined;\n const style = styleOrder[spec.style].find((value) => candidates.some((entry) => (entry.face.style ?? \"normal\") === value))!;\n let best: Loaded | undefined;\n let bestRank = Infinity;\n for (const entry of candidates) {\n if ((entry.face.style ?? \"normal\") !== style) continue;\n const rank = weightRank(entry.range, spec.weight);\n if (rank < bestRank) {\n best = entry;\n bestRank = rank;\n }\n }\n const chosen = best!;\n const tags = new Set(chosen.font.axes.map((axis) => axis.tag));\n const instance: Record<string, number> = {};\n if (tags.has(\"wght\")) instance[\"wght\"] = Math.min(chosen.range[1], Math.max(chosen.range[0], spec.weight));\n if (tags.has(\"wdth\")) instance[\"wdth\"] = spec.stretch;\n if (tags.has(\"opsz\")) instance[\"opsz\"] = spec.size;\n if (tags.has(\"ital\")) instance[\"ital\"] = spec.style === \"italic\" ? 1 : 0;\n if (tags.has(\"slnt\")) instance[\"slnt\"] = spec.style === \"oblique\" ? -spec.angle : 0;\n const key = `${chosen.index}|${JSON.stringify(instance)}`;\n let font = instances.get(key);\n if (font === undefined) {\n font = Object.keys(instance).length === 0 ? chosen.font : chosen.font.at(instance);\n instances.set(key, font);\n }\n return font;\n };\n\n const fontsOf = (font: string): { readonly spec: FontSpec; readonly fonts: readonly Font[] } => {\n let spec = specs.get(font);\n if (spec === undefined) {\n spec = parseFont(font);\n if (specs.size >= 256) specs.clear();\n specs.set(font, spec);\n }\n let fonts = families.get(font);\n if (fonts === undefined) {\n fonts = spec.families.map((family) => fontOf(family, spec)).filter((value) => value !== undefined);\n if (fonts.length === 0) throw new RangeError(`no face of the host has a family of the font \"${font}\"`);\n if (families.size >= 256) families.clear();\n families.set(font, fonts);\n }\n return { spec, fonts };\n };\n\n return (text: string, font: string): number => {\n const { spec, fonts } = fontsOf(font);\n const measure = (run: string, face: Font): number => measureWidth(face, run, spec.size, { script: scriptTag(run), shaping, rounding: rules.rounding, sum: rules.sum });\n if (fonts.length === 1) return measure(text, fonts[0]!);\n let width = 0;\n let run = \"\";\n let current: Font | undefined;\n let joined = false;\n for (const character of text) {\n const code = character.codePointAt(0)!;\n const stays = current !== undefined && (joined || attached.test(character));\n const face = stays ? current! : (fonts.find((value) => value.glyph(code) !== 0) ?? fonts[0]!);\n if (face !== current && run.length > 0) {\n width += measure(run, current!);\n run = \"\";\n }\n current = face;\n run += character;\n joined = code === 0x200d;\n }\n if (run.length > 0) width += measure(run, current!);\n return width;\n };\n}\n","/**\n * The subpath `./measurer`: the advance function of a layout engine, from the font model of typeset. The measurer of\n * `@mark1russell7/layout-contract/text` takes it as its `AdvanceFn`.\n */\nexport { fontAdvance, parseFont, scriptTag, type FontAdvanceOptions, type FontFace, type FontSpec } from \"./measurer/advance.ts\";\n"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** A big-endian reader over a part of a font file. The offsets are relative to the start of the part. */
|
|
2
|
+
declare class Reader {
|
|
3
|
+
readonly bytes: Uint8Array;
|
|
4
|
+
private readonly view;
|
|
5
|
+
constructor(bytes: Uint8Array);
|
|
6
|
+
get length(): number;
|
|
7
|
+
u8(offset: number): number;
|
|
8
|
+
u16(offset: number): number;
|
|
9
|
+
i16(offset: number): number;
|
|
10
|
+
u32(offset: number): number;
|
|
11
|
+
tag(offset: number): string;
|
|
12
|
+
/** This method gives a reader over the bytes from `offset`, with the length `length` or to the end. */
|
|
13
|
+
part(offset: number, length?: number): Reader;
|
|
14
|
+
}
|
|
15
|
+
/** A function that decompresses Brotli data. Node gives one as `brotliDecompressSync` of `node:zlib`. */
|
|
16
|
+
type Decompress = (data: Uint8Array) => Uint8Array;
|
|
17
|
+
/** The tables of a font, by tag. A table that the decoder does not need can be absent. */
|
|
18
|
+
type TableMap = ReadonlyMap<string, Reader>;
|
|
19
|
+
/** This function gives the tables of a TrueType, OpenType or WOFF2 file. */
|
|
20
|
+
declare function readTables(bytes: Uint8Array, decompress?: Decompress): TableMap;
|
|
21
|
+
export { readTables as n, Decompress as t };
|
|
22
|
+
//# sourceMappingURL=sfnt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sfnt.d.ts","names":[],"sources":["../../src/fonts/binary.ts","../../src/fonts/sfnt.ts"],"mappings":";cACa;WACF,OAAO;mBACC;EAEjB,YAAY,OAAO;MAKf;EAIJ,GAAG;EAIH,IAAI;EAIJ,IAAI;EAIJ,IAAI;EAIJ,IAAI;;EAKJ,KAAK,gBAAgB,kBAAkB;;;KChC7B,cAAc,MAAM,eAAe;;KAGnC,WAAW,oBAAoB;;iBAW3B,WAAW,OAAO,YAAY,aAAa,aAAa"}
|