@mark1russell7/typeset 0.1.1 → 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 (50) hide show
  1. package/README.md +21 -1
  2. package/build/chunks/engines.d.ts +32 -0
  3. package/build/chunks/engines.d.ts.map +1 -0
  4. package/build/chunks/engines.js +35 -0
  5. package/build/chunks/engines.js.map +1 -0
  6. package/build/chunks/font.d.ts +203 -0
  7. package/build/chunks/font.d.ts.map +1 -0
  8. package/build/chunks/font.js +3550 -0
  9. package/build/chunks/font.js.map +1 -0
  10. package/build/chunks/fonts.d.ts +8 -0
  11. package/build/chunks/fonts.d.ts.map +1 -0
  12. package/build/chunks/fonts.js +13 -0
  13. package/build/chunks/fonts.js.map +1 -0
  14. package/build/chunks/measurer.d.ts +67 -0
  15. package/build/chunks/measurer.d.ts.map +1 -0
  16. package/build/chunks/measurer.js +277 -0
  17. package/build/chunks/measurer.js.map +1 -0
  18. package/build/chunks/sfnt.d.ts +22 -0
  19. package/build/chunks/sfnt.d.ts.map +1 -0
  20. package/build/chunks/text.js.map +1 -1
  21. package/build/fonts-inspect.d.ts +10 -0
  22. package/build/fonts-inspect.d.ts.map +1 -0
  23. package/build/fonts-inspect.js +1 -0
  24. package/build/fonts.d.ts +5 -0
  25. package/build/fonts.js +4 -0
  26. package/build/index.d.ts +3 -1
  27. package/build/index.js +3 -1
  28. package/build/measurer.d.ts +2 -0
  29. package/build/measurer.js +1 -0
  30. package/package.json +17 -3
  31. package/src/fonts/bidi.ts +125 -0
  32. package/src/fonts/binary.ts +41 -0
  33. package/src/fonts/engines.ts +38 -0
  34. package/src/fonts/font.ts +442 -0
  35. package/src/fonts/gvar.ts +220 -0
  36. package/src/fonts/index.ts +10 -0
  37. package/src/fonts/indic.ts +585 -0
  38. package/src/fonts/joining.ts +141 -0
  39. package/src/fonts/kerning.ts +217 -0
  40. package/src/fonts/sfnt.ts +102 -0
  41. package/src/fonts/substitution.ts +586 -0
  42. package/src/fonts/tables.ts +140 -0
  43. package/src/fonts/variations.ts +175 -0
  44. package/src/fonts/words.ts +119 -0
  45. package/src/fonts-inspect.ts +8 -0
  46. package/src/fonts.ts +8 -0
  47. package/src/index.ts +2 -0
  48. package/src/measurer/advance.ts +307 -0
  49. package/src/measurer.ts +5 -0
  50. package/src/text/rope.ts +1 -1
@@ -0,0 +1,220 @@
1
+ import type { Reader } from "./binary.ts";
2
+ import { axisScalar } from "./variations.ts";
3
+
4
+ /** The composite flags of `glyf` that change the length of a component record. */
5
+ const ARG_1_AND_2_ARE_WORDS = 0x1;
6
+ const WE_HAVE_A_SCALE = 0x8;
7
+ const MORE_COMPONENTS = 0x20;
8
+ const WE_HAVE_AN_X_AND_Y_SCALE = 0x40;
9
+ const WE_HAVE_A_TWO_BY_TWO = 0x80;
10
+ /** The composite flag that gives the metrics of the component to the composite glyph. */
11
+ const USE_MY_METRICS = 0x200;
12
+
13
+ /** The points of the glyphs of a `glyf` table. */
14
+ export interface GlyphPoints {
15
+ /** The count of points of each glyph, as `gvar` counts them. The four phantom points are not in the count. */
16
+ readonly counts: Uint32Array;
17
+ /** The component with the flag USE_MY_METRICS of each composite glyph, or -1. */
18
+ readonly metrics: Int32Array;
19
+ }
20
+
21
+ /**
22
+ * This function reads the composite records that start at `at`. It gives their length in bytes and the count of
23
+ * components. It also gives the first component with the flag USE_MY_METRICS, or -1.
24
+ */
25
+ function components(glyph: Reader, at: number): { count: number; length: number; metrics: number } {
26
+ let count = 0;
27
+ let offset = at;
28
+ let metrics = -1;
29
+ for (;;) {
30
+ const flags = glyph.u16(offset);
31
+ if ((flags & USE_MY_METRICS) !== 0 && metrics < 0) metrics = glyph.u16(offset + 2);
32
+ offset += 4 + ((flags & ARG_1_AND_2_ARE_WORDS) !== 0 ? 4 : 2);
33
+ if ((flags & WE_HAVE_A_SCALE) !== 0) offset += 2;
34
+ else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) !== 0) offset += 4;
35
+ else if ((flags & WE_HAVE_A_TWO_BY_TWO) !== 0) offset += 8;
36
+ count++;
37
+ if ((flags & MORE_COMPONENTS) === 0) return { count, length: offset - at, metrics };
38
+ }
39
+ }
40
+
41
+ /**
42
+ * This function gives the points of each glyph. A glyph of contours has the points of its contours. A composite glyph
43
+ * has one point for each component.
44
+ */
45
+ export function readPointCounts(glyf: Reader, loca: Reader, indexToLocFormat: number, numGlyphs: number): GlyphPoints {
46
+ const counts = new Uint32Array(numGlyphs);
47
+ const metrics = new Int32Array(numGlyphs).fill(-1);
48
+ const offsetOf = (glyph: number) => (indexToLocFormat === 0 ? loca.u16(glyph * 2) * 2 : loca.u32(glyph * 4));
49
+ for (let glyph = 0; glyph < numGlyphs; glyph++) {
50
+ const start = offsetOf(glyph);
51
+ if (offsetOf(glyph + 1) <= start) continue;
52
+ const contours = glyf.i16(start);
53
+ if (contours > 0) counts[glyph] = glyf.u16(start + 10 + (contours - 1) * 2) + 1;
54
+ else if (contours < 0) {
55
+ const found = components(glyf, start + 10);
56
+ counts[glyph] = found.count;
57
+ metrics[glyph] = found.metrics;
58
+ }
59
+ }
60
+ return { counts, metrics };
61
+ }
62
+
63
+ /** This function reads a 255UInt16 of WOFF2. It gives the value and the next offset. */
64
+ function read255(stream: Reader, at: number): [number, number] {
65
+ const code = stream.u8(at);
66
+ if (code === 253) return [stream.u16(at + 1), at + 3];
67
+ if (code === 254) return [stream.u8(at + 1) + 506, at + 2];
68
+ if (code === 255) return [stream.u8(at + 1) + 253, at + 2];
69
+ return [code, at + 1];
70
+ }
71
+
72
+ /** This function gives the points of each glyph from the transformed `glyf` table of WOFF2. */
73
+ export function readTransformedPointCounts(glyf: Reader, numGlyphs: number): GlyphPoints {
74
+ const counts = new Uint32Array(numGlyphs);
75
+ const metrics = new Int32Array(numGlyphs).fill(-1);
76
+ const sizes = Array.from({ length: 7 }, (_, i) => glyf.u32(8 + i * 4));
77
+ const contourStream = glyf.part(36, sizes[0]);
78
+ const pointStream = glyf.part(36 + sizes[0]!, sizes[1]);
79
+ // The composite stream comes after the streams of the contours, the points, the flags and the glyphs
80
+ const compositeStream = glyf.part(36 + sizes[0]! + sizes[1]! + sizes[2]! + sizes[3]!, sizes[4]);
81
+ let pointAt = 0;
82
+ let compositeAt = 0;
83
+ const glyphs = Math.min(numGlyphs, glyf.u16(4));
84
+ for (let glyph = 0; glyph < glyphs; glyph++) {
85
+ const contours = contourStream.i16(glyph * 2);
86
+ if (contours > 0) {
87
+ let total = 0;
88
+ for (let c = 0; c < contours; c++) {
89
+ const [points, next] = read255(pointStream, pointAt);
90
+ total += points;
91
+ pointAt = next;
92
+ }
93
+ counts[glyph] = total;
94
+ } else if (contours < 0) {
95
+ const found = components(compositeStream, compositeAt);
96
+ counts[glyph] = found.count;
97
+ metrics[glyph] = found.metrics;
98
+ compositeAt += found.length;
99
+ }
100
+ }
101
+ return { counts, metrics };
102
+ }
103
+
104
+ /** This function reads packed point numbers. It gives the point numbers, or `undefined` for all the points, and the next offset. */
105
+ function readPoints(data: Reader, at: number): [number[] | undefined, number] {
106
+ let count = data.u8(at++);
107
+ if ((count & 0x80) !== 0) count = ((count & 0x7f) << 8) | data.u8(at++);
108
+ if (count === 0) return [undefined, at];
109
+ const points: number[] = [];
110
+ let last = 0;
111
+ while (points.length < count) {
112
+ const control = data.u8(at++);
113
+ const words = (control & 0x80) !== 0;
114
+ const run = (control & 0x7f) + 1;
115
+ for (let k = 0; k < run && points.length < count; k++) {
116
+ last += words ? data.u16(at) : data.u8(at);
117
+ at += words ? 2 : 1;
118
+ points.push(last);
119
+ }
120
+ }
121
+ return [points, at];
122
+ }
123
+
124
+ /** This function reads `count` packed deltas. It gives the deltas and the next offset. */
125
+ function readDeltas(data: Reader, at: number, count: number): [number[], number] {
126
+ const deltas: number[] = [];
127
+ while (deltas.length < count) {
128
+ const control = data.u8(at++);
129
+ const run = (control & 0x3f) + 1;
130
+ const kind = control & 0xc0;
131
+ for (let k = 0; k < run && deltas.length < count; k++) {
132
+ if (kind === 0x80) deltas.push(0);
133
+ else if (kind === 0x40) {
134
+ deltas.push(data.i16(at));
135
+ at += 2;
136
+ } else if (kind === 0xc0) {
137
+ deltas.push(data.u32(at) | 0);
138
+ at += 4;
139
+ } else deltas.push((data.u8(at++) << 24) >> 24);
140
+ }
141
+ }
142
+ return [deltas, at];
143
+ }
144
+
145
+ /**
146
+ * This function gives the advance delta of each glyph at the coordinates, from the phantom points of `gvar`. The delta
147
+ * is the x delta of the second phantom point minus the x delta of the first. With `round`, the difference is rounded to
148
+ * whole font units, and a half goes away from 0, as HarfBuzz rounds it.
149
+ */
150
+ export function gvarAdvanceDeltas(gvar: Reader, pointCounts: Uint32Array, coordinates: readonly number[], deltas: "round" | "exact"): Float64Array {
151
+ const axisCount = gvar.u16(4);
152
+ const sharedCount = gvar.u16(6);
153
+ const sharedAt = gvar.u32(8);
154
+ const glyphCount = Math.min(gvar.u16(12), pointCounts.length);
155
+ const longOffsets = (gvar.u16(14) & 1) !== 0;
156
+ const dataAt = gvar.u32(16);
157
+ const offsetOf = (glyph: number) => (longOffsets ? gvar.u32(20 + glyph * 4) : gvar.u16(20 + glyph * 2) * 2);
158
+ const peakOf = (at: number) => Array.from({ length: axisCount }, (_, a) => gvar.i16(at + a * 2) / 16384);
159
+ const shared = Array.from({ length: sharedCount }, (_, t) => peakOf(sharedAt + t * axisCount * 2));
160
+ const result = new Float64Array(pointCounts.length);
161
+ for (let glyph = 0; glyph < glyphCount; glyph++) {
162
+ const start = offsetOf(glyph);
163
+ const end = offsetOf(glyph + 1);
164
+ if (end <= start) continue;
165
+ const data = gvar.part(dataAt + start, end - start);
166
+ const tupleCount = data.u16(0) & 0x0fff;
167
+ const sharedPoints = (data.u16(0) & 0x8000) !== 0;
168
+ let serialized = data.u16(2);
169
+ let header = 4;
170
+ const allPoints = pointCounts[glyph]! + 4;
171
+ let sharedList: number[] | undefined;
172
+ if (sharedPoints) [sharedList, serialized] = readPoints(data, serialized);
173
+ const first = pointCounts[glyph]!;
174
+ let left = 0;
175
+ let right = 0;
176
+ for (let t = 0; t < tupleCount; t++) {
177
+ const size = data.u16(header);
178
+ const index = data.u16(header + 2);
179
+ header += 4;
180
+ let peak: number[];
181
+ if ((index & 0x8000) !== 0) {
182
+ peak = Array.from({ length: axisCount }, (_, a) => data.i16(header + a * 2) / 16384);
183
+ header += axisCount * 2;
184
+ } else peak = shared[index & 0x0fff] ?? [];
185
+ let startTuple: number[] | undefined;
186
+ let endTuple: number[] | undefined;
187
+ if ((index & 0x4000) !== 0) {
188
+ startTuple = Array.from({ length: axisCount }, (_, a) => data.i16(header + a * 2) / 16384);
189
+ endTuple = Array.from({ length: axisCount }, (_, a) => data.i16(header + axisCount * 2 + a * 2) / 16384);
190
+ header += axisCount * 4;
191
+ }
192
+ let scalar = 1;
193
+ for (let a = 0; a < axisCount && scalar !== 0; a++) {
194
+ const p = peak[a] ?? 0;
195
+ const s = startTuple?.[a] ?? Math.min(p, 0);
196
+ const e = endTuple?.[a] ?? Math.max(p, 0);
197
+ scalar *= axisScalar(s, p, e, coordinates[a] ?? 0);
198
+ }
199
+ const tupleEnd = serialized + size;
200
+ if (scalar !== 0) {
201
+ let at = serialized;
202
+ let points = sharedList;
203
+ if ((index & 0x2000) !== 0) [points, at] = readPoints(data, at);
204
+ const count = points === undefined ? allPoints : points.length;
205
+ const [xs] = readDeltas(data, at, count);
206
+ const deltaOf = (point: number): number => {
207
+ if (points === undefined) return xs[point] ?? 0;
208
+ const k = points.indexOf(point);
209
+ return k < 0 ? 0 : xs[k]!;
210
+ };
211
+ left += scalar * deltaOf(first);
212
+ right += scalar * deltaOf(first + 1);
213
+ }
214
+ serialized = tupleEnd;
215
+ }
216
+ const delta = right - left;
217
+ result[glyph] = deltas === "round" ? Math.sign(delta) * Math.round(Math.abs(delta)) : delta;
218
+ }
219
+ return result;
220
+ }
@@ -0,0 +1,10 @@
1
+ export { readFont, measureWidth, glyphAdvances, shapingWords, type AdvanceSource, type Font, type ReadOptions, type MeasureOptions } from "./font.ts";
2
+ export { canvasSegments, canvasWords, isCjkIdeographOrSymbol } from "./words.ts";
3
+ export { bidiLevels, visualOrder } from "./bidi.ts";
4
+ export { indicScripts, indicShaper, indicSyllables, type IndicSyllableType } from "./indic.ts";
5
+ export { joiningFeatures, joiningForms, joiningScripts, joiningStages, type JoiningForm, type JoiningStage } from "./joining.ts";
6
+ export { engines, isEngineName, type EngineName, type EngineRules } from "./engines.ts";
7
+ export { readTables, type Decompress, type TableMap } from "./sfnt.ts";
8
+ export type { PairLookup, PairValue } from "./kerning.ts";
9
+ export { defaultFeatures, LIGATED, MULTIPLIED, SUBSTITUTED, type GlyphFilter, type GlyphInfo, type SubstitutionLookup } from "./substitution.ts";
10
+ export type { Axis, Instance } from "./variations.ts";