@mark1russell7/typeset 0.1.0 → 0.1.2

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.
Files changed (61) hide show
  1. package/README.md +38 -0
  2. package/build/breaking.d.ts +2 -1
  3. package/build/chunks/breaking.d.ts +2 -77
  4. package/build/chunks/breaking.d.ts.map +1 -1
  5. package/build/chunks/engines.d.ts +32 -0
  6. package/build/chunks/engines.d.ts.map +1 -0
  7. package/build/chunks/engines.js +35 -0
  8. package/build/chunks/engines.js.map +1 -0
  9. package/build/chunks/font.d.ts +203 -0
  10. package/build/chunks/font.d.ts.map +1 -0
  11. package/build/chunks/font.js +3550 -0
  12. package/build/chunks/font.js.map +1 -0
  13. package/build/chunks/fonts.d.ts +8 -0
  14. package/build/chunks/fonts.d.ts.map +1 -0
  15. package/build/chunks/fonts.js +13 -0
  16. package/build/chunks/fonts.js.map +1 -0
  17. package/build/chunks/measurer.d.ts +67 -0
  18. package/build/chunks/measurer.d.ts.map +1 -0
  19. package/build/chunks/measurer.js +277 -0
  20. package/build/chunks/measurer.js.map +1 -0
  21. package/build/chunks/paragraph.d.ts +78 -0
  22. package/build/chunks/paragraph.d.ts.map +1 -0
  23. package/build/chunks/sfnt.d.ts +22 -0
  24. package/build/chunks/sfnt.d.ts.map +1 -0
  25. package/build/chunks/text.d.ts +48 -0
  26. package/build/chunks/text.d.ts.map +1 -0
  27. package/build/chunks/text.js +237 -0
  28. package/build/chunks/text.js.map +1 -0
  29. package/build/fonts-inspect.d.ts +10 -0
  30. package/build/fonts-inspect.d.ts.map +1 -0
  31. package/build/fonts-inspect.js +1 -0
  32. package/build/fonts.d.ts +5 -0
  33. package/build/fonts.js +4 -0
  34. package/build/index.d.ts +4 -1
  35. package/build/index.js +5 -1
  36. package/build/measurer.d.ts +2 -0
  37. package/build/measurer.js +1 -0
  38. package/build/text.d.ts +2 -0
  39. package/build/text.js +1 -0
  40. package/package.json +22 -3
  41. package/src/fonts/bidi.ts +125 -0
  42. package/src/fonts/binary.ts +41 -0
  43. package/src/fonts/engines.ts +38 -0
  44. package/src/fonts/font.ts +442 -0
  45. package/src/fonts/gvar.ts +220 -0
  46. package/src/fonts/index.ts +10 -0
  47. package/src/fonts/indic.ts +585 -0
  48. package/src/fonts/joining.ts +141 -0
  49. package/src/fonts/kerning.ts +217 -0
  50. package/src/fonts/sfnt.ts +102 -0
  51. package/src/fonts/substitution.ts +586 -0
  52. package/src/fonts/tables.ts +140 -0
  53. package/src/fonts/variations.ts +175 -0
  54. package/src/fonts/words.ts +119 -0
  55. package/src/fonts-inspect.ts +8 -0
  56. package/src/fonts.ts +8 -0
  57. package/src/index.ts +3 -0
  58. package/src/measurer/advance.ts +307 -0
  59. package/src/measurer.ts +5 -0
  60. package/src/text/rope.ts +291 -0
  61. package/src/text.ts +1 -0
@@ -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,78 @@
1
+ /**
2
+ * The paragraph model of the breakers: a list of boxes, glue and penalties, as Knuth and Plass define it. A builder makes
3
+ * the items of a text. The breakers read only the items, so a new rule of break opportunities is a new builder.
4
+ */
5
+ /** A width function: the width of a run of text, in any unit. The breakers add the widths and compare them. */
6
+ type WidthOf = (text: string) => number;
7
+ /** A box: text that the breaker does not break. */
8
+ interface Box {
9
+ readonly type: "box";
10
+ readonly text: string;
11
+ readonly width: number;
12
+ }
13
+ /** Glue: a space between two boxes. A line can end at glue that comes after a box. The line then does not show the glue. */
14
+ interface Glue {
15
+ readonly type: "glue";
16
+ readonly text: string;
17
+ readonly width: number;
18
+ }
19
+ /**
20
+ * A penalty: a break opportunity with a cost. A line that ends at the penalty shows its text, for example a hyphen. A cost of
21
+ * `Infinity` forbids the break, and a cost of `-Infinity` forces it.
22
+ */
23
+ interface Penalty {
24
+ readonly type: "penalty";
25
+ readonly text: string;
26
+ readonly width: number;
27
+ readonly cost: number;
28
+ /** A flagged penalty is a hyphen. Total fit adds a cost to two flagged breaks in sequence. */
29
+ readonly flagged: boolean;
30
+ }
31
+ /** An item of a paragraph. */
32
+ type Item = Box | Glue | Penalty;
33
+ /** A paragraph: the items in the order of the text. */
34
+ interface Paragraph {
35
+ readonly items: readonly Item[];
36
+ }
37
+ /** The options of `paragraphOf`. */
38
+ interface ParagraphOptions {
39
+ /** The width function of the boxes, the spaces and the hyphens. */
40
+ readonly widthOf: WidthOf;
41
+ /**
42
+ * The hyphenation hook. It gives the offsets in a word where a break with a hyphen is possible. The default gives no
43
+ * offset. The width of each part of a word comes from `widthOf`, so the kerning across an offset is not in the sum.
44
+ */
45
+ readonly hyphenate?: (word: string) => readonly number[];
46
+ /** The text that a line shows at a hyphen break. The default is "-". */
47
+ readonly hyphen?: string;
48
+ /** The cost of a hyphen break. The default is 50, as in TeX. */
49
+ readonly hyphenCost?: number;
50
+ }
51
+ /**
52
+ * This function makes the paragraph of a text with the break opportunities of CSS `white-space: normal`. Each run of white
53
+ * space becomes one glue of the width of a space. Each word between two runs becomes a box, or boxes and hyphen penalties.
54
+ */
55
+ declare function paragraphOf(text: string, options: ParagraphOptions): Paragraph;
56
+ /** A line of a broken paragraph. */
57
+ interface Line {
58
+ readonly text: string;
59
+ /** The width of the line: its boxes, the glue between them and the text of a penalty at its end. */
60
+ readonly width: number;
61
+ /** The index of the first item of the line. */
62
+ readonly start: number;
63
+ /** The index after the last item of the line. A break at glue or at a penalty has the index of that item. */
64
+ readonly end: number;
65
+ }
66
+ /** This function tells if a line can end at the item `index`: glue after a box, or a penalty with a cost below `Infinity`. */
67
+ declare function isBreak(items: readonly Item[], index: number): boolean;
68
+ /** This function gives the index of the first item of a line after a break at `index`. It skips the glue and the penalties. */
69
+ declare function lineStart(items: readonly Item[], index: number): number;
70
+ /** This function gives the width of the line from the item `start` to a break at `end`, with the text of a penalty at `end`. */
71
+ declare function lineWidth(items: readonly Item[], start: number, end: number): number;
72
+ /**
73
+ * This function gives the lines of a paragraph from the indices of its breaks. Each break is the index of glue or of a penalty.
74
+ * The last line ends at the end of the items.
75
+ */
76
+ declare function linesOf(paragraph: Paragraph, breaks: readonly number[]): Line[];
77
+ export { Paragraph as a, WidthOf as c, lineWidth as d, linesOf as f, Line as i, isBreak as l, Glue as n, ParagraphOptions as o, paragraphOf as p, Item as r, Penalty as s, Box as t, lineStart as u };
78
+ //# sourceMappingURL=paragraph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paragraph.d.ts","names":[],"sources":["../../src/breaking/paragraph.ts"],"mappings":";;;;;KAMY,WAAW;;UAGN;WACN;WACA;WACA;;;UAIM;WACN;WACA;WACA;;;;;;UAOM;WACN;WACA;WACA;WACA;;WAEA;;;KAIC,OAAO,MAAM,OAAO;;UAGf;WACN,gBAAgB;;;UAIV;;WAEN,SAAS;;;;;WAKT,aAAa;;WAEb;;WAEA;;;;;;iBASK,YAAY,cAAc,SAAS,mBAAmB;;UAyBrD;WACN;;WAEA;;WAEA;;WAEA;;;iBAIK,QAAQ,gBAAgB,QAAQ;;iBAOhC,UAAU,gBAAgB,QAAQ;;iBAOlC,UAAU,gBAAgB,QAAQ,eAAe;;;;;iBAcjD,QAAQ,WAAW,WAAW,4BAA4B"}
@@ -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"}
@@ -0,0 +1,48 @@
1
+ import { c as WidthOf } from "./paragraph.js";
2
+ /** The options of a rope. */
3
+ interface TextOptions {
4
+ /**
5
+ * The separators of the separator law: each character is one UTF-16 code unit. The default is a space and a tab. An
6
+ * empty string makes each seam measure the full text of the two sides.
7
+ */
8
+ readonly separators?: string;
9
+ /** The largest count of UTF-16 code units in a leaf. The default is 256. */
10
+ readonly chunk?: number;
11
+ }
12
+ /**
13
+ * A text with its width: a persistent balanced rope. Each edit gives a new `Text` and does not change this one. The offsets
14
+ * are in UTF-16 code units, as the offsets of a JavaScript string.
15
+ */
16
+ declare class Text {
17
+ #private;
18
+ private constructor();
19
+ /** This function makes the rope of `text` with the width function `width`. */
20
+ static from(text: string, width: WidthOf, options?: TextOptions): Text;
21
+ /** The count of UTF-16 code units of the text. */
22
+ get length(): number;
23
+ /** The width of the text. It is equal to the width of the flat string when the width function obeys the separator law. */
24
+ get width(): number;
25
+ /** The height of the tree: 0 for a text of one leaf. The tests use it to examine the balance. */
26
+ get height(): number;
27
+ /** This function gives the flat string. */
28
+ toString(): string;
29
+ /** This function gives the strings of the leaves in the order of the text. */
30
+ chunks(): string[];
31
+ /** This function gives the text from `start` to `end`, as a new rope. */
32
+ slice(start: number, end?: number): Text;
33
+ /** This function gives the width of the text from `start` to `end`. */
34
+ widthOf(start: number, end?: number): number;
35
+ /** This function gives this text and then `other`. The two ropes must have the same width function and options. */
36
+ concat(other: Text): Text;
37
+ /** This function puts `text` at the offset `at`. */
38
+ insert(at: number, text: string): Text;
39
+ /** This function removes the text from `start` to `end`. */
40
+ remove(start: number, end: number): Text;
41
+ /** This function puts `text` in place of the text from `start` to `end`. */
42
+ replace(start: number, end: number, text: string): Text;
43
+ }
44
+ declare namespace text_d_exports {
45
+ export { Text, TextOptions };
46
+ }
47
+ export { Text as n, TextOptions as r, text_d_exports as t };
48
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","names":[],"sources":["../../src/text/rope.ts","../../src/text.ts"],"mappings":";;UAYiB;;;;;WAKN;;WAEA;;;;;;cAkLE;;UAIH;;SAMD,KAAK,cAAc,OAAO,SAAS,UAAS,cAAmB;;MAalE;;MAKA;;MAKA;;EAKJ;;EAKA;;EAKA,MAAM,eAAe,eAA4B;;EAQjD,QAAQ,eAAe;;EAKvB,OAAO,OAAO,OAAO;;EASrB,OAAO,YAAY,eAAe;;EAKlC,OAAO,eAAe,cAAc;;EAKpC,QAAQ,eAAe,aAAa,eAAe"}