text-shaper 0.0.1
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/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/aat/state-machine.d.ts +34 -0
- package/dist/buffer/glyph-buffer.d.ts +55 -0
- package/dist/buffer/unicode-buffer.d.ts +51 -0
- package/dist/font/binary/reader.d.ts +72 -0
- package/dist/font/face.d.ts +65 -0
- package/dist/font/font.d.ts +189 -0
- package/dist/font/tables/avar.d.ts +36 -0
- package/dist/font/tables/base.d.ts +79 -0
- package/dist/font/tables/cbdt.d.ts +120 -0
- package/dist/font/tables/cff-charstring.d.ts +16 -0
- package/dist/font/tables/cff.d.ts +93 -0
- package/dist/font/tables/cff2.d.ts +85 -0
- package/dist/font/tables/cmap.d.ts +81 -0
- package/dist/font/tables/colr.d.ts +278 -0
- package/dist/font/tables/cpal.d.ts +46 -0
- package/dist/font/tables/feat.d.ts +359 -0
- package/dist/font/tables/fvar.d.ts +77 -0
- package/dist/font/tables/gdef.d.ts +45 -0
- package/dist/font/tables/glyf.d.ts +118 -0
- package/dist/font/tables/gpos-contextual.d.ts +88 -0
- package/dist/font/tables/gpos-mark.d.ts +95 -0
- package/dist/font/tables/gpos.d.ts +137 -0
- package/dist/font/tables/gsub-contextual.d.ts +88 -0
- package/dist/font/tables/gsub.d.ts +107 -0
- package/dist/font/tables/gvar.d.ts +56 -0
- package/dist/font/tables/head.d.ts +47 -0
- package/dist/font/tables/hhea.d.ts +24 -0
- package/dist/font/tables/hmtx.d.ts +23 -0
- package/dist/font/tables/hvar.d.ts +77 -0
- package/dist/font/tables/jstf.d.ts +97 -0
- package/dist/font/tables/kern.d.ts +44 -0
- package/dist/font/tables/kerx.d.ts +108 -0
- package/dist/font/tables/loca.d.ts +31 -0
- package/dist/font/tables/math.d.ts +168 -0
- package/dist/font/tables/maxp.d.ts +27 -0
- package/dist/font/tables/morx.d.ts +142 -0
- package/dist/font/tables/mvar.d.ts +99 -0
- package/dist/font/tables/name.d.ts +75 -0
- package/dist/font/tables/os2.d.ts +102 -0
- package/dist/font/tables/post.d.ts +25 -0
- package/dist/font/tables/sbix.d.ts +65 -0
- package/dist/font/tables/sfnt.d.ts +20 -0
- package/dist/font/tables/stat.d.ts +115 -0
- package/dist/font/tables/svg.d.ts +40 -0
- package/dist/font/tables/trak.d.ts +43 -0
- package/dist/font/tables/vhea.d.ts +41 -0
- package/dist/font/tables/vmtx.d.ts +35 -0
- package/dist/font/tables/vorg.d.ts +33 -0
- package/dist/font/tables/vvar.d.ts +36 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +92 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/layout/justify.d.ts +88 -0
- package/dist/layout/structures/class-def.d.ts +15 -0
- package/dist/layout/structures/coverage.d.ts +17 -0
- package/dist/layout/structures/device.d.ts +58 -0
- package/dist/layout/structures/feature-variations.d.ts +82 -0
- package/dist/layout/structures/layout-common.d.ts +77 -0
- package/dist/render/path.d.ts +136 -0
- package/dist/shaper/complex/arabic.d.ts +60 -0
- package/dist/shaper/complex/ethiopic.d.ts +70 -0
- package/dist/shaper/complex/georgian.d.ts +69 -0
- package/dist/shaper/complex/hangul.d.ts +64 -0
- package/dist/shaper/complex/hebrew.d.ts +28 -0
- package/dist/shaper/complex/indic.d.ts +117 -0
- package/dist/shaper/complex/khmer.d.ts +48 -0
- package/dist/shaper/complex/mongolian.d.ts +89 -0
- package/dist/shaper/complex/myanmar.d.ts +47 -0
- package/dist/shaper/complex/syriac.d.ts +80 -0
- package/dist/shaper/complex/thai-lao.d.ts +42 -0
- package/dist/shaper/complex/tibetan.d.ts +57 -0
- package/dist/shaper/complex/use.d.ts +80 -0
- package/dist/shaper/fallback.d.ts +16 -0
- package/dist/shaper/features.d.ts +189 -0
- package/dist/shaper/shape-plan.d.ts +29 -0
- package/dist/shaper/shaper.d.ts +18 -0
- package/dist/types.d.ts +153 -0
- package/dist/typeshaper.js +7 -0
- package/dist/unicode/bidi/brackets.d.ts +7 -0
- package/dist/unicode/bidi/brackets.data.d.ts +5 -0
- package/dist/unicode/bidi/char-types.d.ts +19 -0
- package/dist/unicode/bidi/char-types.data.d.ts +25 -0
- package/dist/unicode/bidi/embedding-levels.d.ts +18 -0
- package/dist/unicode/bidi/index.d.ts +9 -0
- package/dist/unicode/bidi/mirroring.d.ts +10 -0
- package/dist/unicode/bidi/mirroring.data.d.ts +2 -0
- package/dist/unicode/bidi/parse-character-map.d.ts +8 -0
- package/dist/unicode/bidi/reordering.d.ts +18 -0
- package/dist/unicode/bidi.d.ts +93 -0
- package/dist/unicode/line-break.d.ts +113 -0
- package/dist/unicode/normalize.d.ts +36 -0
- package/dist/unicode/script.d.ts +215 -0
- package/dist/unicode/segmentation.d.ts +104 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# text-shaper
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://github.com/sponsors/vivy-company)
|
|
5
|
+
|
|
6
|
+
Pure TypeScript text shaping engine. A port of [rustybuzz](https://github.com/RazrFalcon/rustybuzz)/[HarfBuzz](https://github.com/harfbuzz/harfbuzz) for the browser and Node.js.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **OpenType Layout**: Full GSUB (substitution) and GPOS (positioning) support
|
|
11
|
+
- **Complex Scripts**: Arabic, Indic, USE (Universal Shaping Engine) shapers
|
|
12
|
+
- **Variable Fonts**: fvar, gvar, avar, HVAR, VVAR, MVAR tables
|
|
13
|
+
- **AAT Support**: morx, kerx, trak tables for Apple fonts
|
|
14
|
+
- **Color Fonts**: SVG, sbix, CBDT/CBLC tables
|
|
15
|
+
- **BiDi**: UAX #9 bidirectional text algorithm
|
|
16
|
+
- **Zero Dependencies**: Pure TypeScript, works in browser and Node.js
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install text-shaper
|
|
22
|
+
# or
|
|
23
|
+
bun add text-shaper
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Font, shape, UnicodeBuffer } from "text-shaper";
|
|
30
|
+
|
|
31
|
+
// Load a font
|
|
32
|
+
const fontData = await Bun.file("path/to/font.ttf").arrayBuffer();
|
|
33
|
+
const font = new Font(fontData);
|
|
34
|
+
|
|
35
|
+
// Create a buffer with text
|
|
36
|
+
const buffer = new UnicodeBuffer();
|
|
37
|
+
buffer.addString("Hello, World!");
|
|
38
|
+
|
|
39
|
+
// Shape the text
|
|
40
|
+
const glyphBuffer = shape(font, buffer);
|
|
41
|
+
|
|
42
|
+
// Access shaped glyphs
|
|
43
|
+
for (let i = 0; i < glyphBuffer.length; i++) {
|
|
44
|
+
const info = glyphBuffer.info[i];
|
|
45
|
+
const pos = glyphBuffer.pos[i];
|
|
46
|
+
console.log(`Glyph ${info.glyphId}: advance=${pos.xAdvance}, offset=(${pos.xOffset}, ${pos.yOffset})`);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### With Features
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { Font, shape, UnicodeBuffer, feature, features } from "text-shaper";
|
|
54
|
+
|
|
55
|
+
const glyphBuffer = shape(font, buffer, {
|
|
56
|
+
features: features(
|
|
57
|
+
feature("smcp", 1), // Small caps
|
|
58
|
+
feature("liga", 1), // Ligatures
|
|
59
|
+
feature("kern", 1), // Kerning
|
|
60
|
+
),
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Variable Fonts
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Font, shape, UnicodeBuffer, tag } from "text-shaper";
|
|
68
|
+
|
|
69
|
+
const glyphBuffer = shape(font, buffer, {
|
|
70
|
+
variations: [
|
|
71
|
+
{ tag: tag("wght"), value: 700 }, // Bold
|
|
72
|
+
{ tag: tag("wdth"), value: 75 }, // Condensed
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Rendering to SVG
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { Font, shape, UnicodeBuffer, shapedTextToSVG } from "text-shaper";
|
|
81
|
+
|
|
82
|
+
const buffer = new UnicodeBuffer();
|
|
83
|
+
buffer.addString("Hello");
|
|
84
|
+
|
|
85
|
+
const glyphBuffer = shape(font, buffer);
|
|
86
|
+
const svg = shapedTextToSVG(font, glyphBuffer, { fontSize: 48 });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## API
|
|
90
|
+
|
|
91
|
+
### Core Classes
|
|
92
|
+
|
|
93
|
+
- `Font` - Load and parse OpenType/TrueType fonts
|
|
94
|
+
- `Face` - Font face with variation coordinates applied
|
|
95
|
+
- `UnicodeBuffer` - Input buffer for text to shape
|
|
96
|
+
- `GlyphBuffer` - Output buffer containing shaped glyphs
|
|
97
|
+
|
|
98
|
+
### Shaping
|
|
99
|
+
|
|
100
|
+
- `shape(font, buffer, options?)` - Shape text in a buffer
|
|
101
|
+
- `createShapePlan(font, options)` - Create a reusable shape plan
|
|
102
|
+
|
|
103
|
+
### Rendering
|
|
104
|
+
|
|
105
|
+
- `getGlyphPath(font, glyphId)` - Get glyph outline as path commands
|
|
106
|
+
- `shapedTextToSVG(font, buffer, options)` - Render shaped text to SVG
|
|
107
|
+
- `renderShapedText(ctx, font, buffer, options)` - Render to Canvas 2D context
|
|
108
|
+
|
|
109
|
+
### Feature Helpers
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Ligatures
|
|
113
|
+
standardLigatures() // liga
|
|
114
|
+
discretionaryLigatures() // dlig
|
|
115
|
+
contextualAlternates() // calt
|
|
116
|
+
|
|
117
|
+
// Caps
|
|
118
|
+
smallCaps() // smcp
|
|
119
|
+
capsToSmallCaps() // c2sc
|
|
120
|
+
allSmallCaps() // smcp + c2sc
|
|
121
|
+
|
|
122
|
+
// Figures
|
|
123
|
+
oldstyleFigures() // onum
|
|
124
|
+
liningFigures() // lnum
|
|
125
|
+
tabularFigures() // tnum
|
|
126
|
+
proportionalFigures() // pnum
|
|
127
|
+
|
|
128
|
+
// Stylistic
|
|
129
|
+
stylisticSet(n) // ss01-ss20
|
|
130
|
+
characterVariant(n) // cv01-cv99
|
|
131
|
+
swash() // swsh
|
|
132
|
+
|
|
133
|
+
// And many more...
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Supported Tables
|
|
137
|
+
|
|
138
|
+
### Required
|
|
139
|
+
head, hhea, hmtx, maxp, cmap, loca, glyf, name, OS/2, post
|
|
140
|
+
|
|
141
|
+
### OpenType Layout
|
|
142
|
+
GDEF, GSUB, GPOS, BASE
|
|
143
|
+
|
|
144
|
+
### CFF
|
|
145
|
+
CFF, CFF2
|
|
146
|
+
|
|
147
|
+
### Variable Fonts
|
|
148
|
+
fvar, gvar, avar, HVAR, VVAR, MVAR, STAT
|
|
149
|
+
|
|
150
|
+
### AAT (Apple)
|
|
151
|
+
morx, kerx, kern, trak
|
|
152
|
+
|
|
153
|
+
### Color
|
|
154
|
+
COLR, CPAL, SVG, sbix, CBDT, CBLC
|
|
155
|
+
|
|
156
|
+
### Vertical
|
|
157
|
+
vhea, vmtx, VORG
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ClassTable, MorxContextualSubtable, MorxInsertionSubtable, MorxLigatureSubtable, MorxRearrangementSubtable } from "../font/tables/morx.ts";
|
|
2
|
+
import type { GlyphId, GlyphInfo } from "../types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* State machine driver for AAT processing
|
|
5
|
+
*/
|
|
6
|
+
export interface StateMachineContext {
|
|
7
|
+
/** Current position in buffer */
|
|
8
|
+
pos: number;
|
|
9
|
+
/** Mark position (for some operations) */
|
|
10
|
+
mark: number;
|
|
11
|
+
/** Glyph stack for ligatures */
|
|
12
|
+
stack: number[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get class for a glyph
|
|
16
|
+
*/
|
|
17
|
+
export declare function getGlyphClass(classTable: ClassTable, glyphId: GlyphId): number;
|
|
18
|
+
/**
|
|
19
|
+
* Process rearrangement subtable
|
|
20
|
+
* Reorders glyphs based on state machine
|
|
21
|
+
*/
|
|
22
|
+
export declare function processRearrangement(subtable: MorxRearrangementSubtable, infos: GlyphInfo[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Process contextual substitution subtable
|
|
25
|
+
*/
|
|
26
|
+
export declare function processContextual(subtable: MorxContextualSubtable, infos: GlyphInfo[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* Process ligature subtable
|
|
29
|
+
*/
|
|
30
|
+
export declare function processLigature(subtable: MorxLigatureSubtable, infos: GlyphInfo[]): GlyphInfo[];
|
|
31
|
+
/**
|
|
32
|
+
* Process insertion subtable
|
|
33
|
+
*/
|
|
34
|
+
export declare function processInsertion(subtable: MorxInsertionSubtable, infos: GlyphInfo[]): GlyphInfo[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Direction, type GlyphId, type GlyphInfo, type GlyphPosition } from "../types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Output buffer containing shaped glyphs.
|
|
4
|
+
* Result of the shaping process.
|
|
5
|
+
*/
|
|
6
|
+
export declare class GlyphBuffer {
|
|
7
|
+
/** Direction used during shaping */
|
|
8
|
+
direction: Direction;
|
|
9
|
+
/** Script used during shaping */
|
|
10
|
+
script: string;
|
|
11
|
+
/** Language used during shaping */
|
|
12
|
+
language: string | null;
|
|
13
|
+
/** Glyph information array */
|
|
14
|
+
infos: GlyphInfo[];
|
|
15
|
+
/** Glyph position array */
|
|
16
|
+
positions: GlyphPosition[];
|
|
17
|
+
/** Create buffer with pre-allocated capacity */
|
|
18
|
+
static withCapacity(capacity: number): GlyphBuffer;
|
|
19
|
+
/** Number of glyphs */
|
|
20
|
+
get length(): number;
|
|
21
|
+
/** Initialize from glyph infos (positions zeroed) */
|
|
22
|
+
initFromInfos(infos: GlyphInfo[]): void;
|
|
23
|
+
/** Set advance width for a glyph */
|
|
24
|
+
setAdvance(index: number, xAdvance: number, yAdvance?: number): void;
|
|
25
|
+
/** Add offset to a glyph position */
|
|
26
|
+
addOffset(index: number, xOffset: number, yOffset: number): void;
|
|
27
|
+
/** Replace glyph at index */
|
|
28
|
+
replaceGlyph(index: number, glyphId: GlyphId): void;
|
|
29
|
+
/** Insert glyph at index */
|
|
30
|
+
insertGlyph(index: number, info: GlyphInfo, position: GlyphPosition): void;
|
|
31
|
+
/** Remove glyphs in range [start, end) */
|
|
32
|
+
removeRange(start: number, end: number): void;
|
|
33
|
+
/** Merge clusters from start to end (inclusive) */
|
|
34
|
+
mergeClusters(start: number, end: number): void;
|
|
35
|
+
/** Reverse glyph order (for RTL) */
|
|
36
|
+
reverse(): void;
|
|
37
|
+
/** Reverse range [start, end) */
|
|
38
|
+
reverseRange(start: number, end: number): void;
|
|
39
|
+
/** Get total advance width */
|
|
40
|
+
getTotalAdvance(): {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
};
|
|
44
|
+
/** Serialize to HarfBuzz-compatible format */
|
|
45
|
+
serialize(): string;
|
|
46
|
+
/** Get glyph IDs as array */
|
|
47
|
+
glyphIds(): GlyphId[];
|
|
48
|
+
/** Get clusters as array */
|
|
49
|
+
clusters(): number[];
|
|
50
|
+
/** Iterator for glyph info/position pairs */
|
|
51
|
+
[Symbol.iterator](): Iterator<{
|
|
52
|
+
info: GlyphInfo;
|
|
53
|
+
position: GlyphPosition;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { BufferFlags, ClusterLevel, Direction, type GlyphInfo } from "../types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Input buffer for text to be shaped.
|
|
4
|
+
* Holds Unicode codepoints with associated properties.
|
|
5
|
+
*/
|
|
6
|
+
export declare class UnicodeBuffer {
|
|
7
|
+
private _direction;
|
|
8
|
+
private _script;
|
|
9
|
+
private _language;
|
|
10
|
+
private _clusterLevel;
|
|
11
|
+
private _flags;
|
|
12
|
+
/** Codepoints to shape */
|
|
13
|
+
readonly codepoints: number[];
|
|
14
|
+
/** Cluster indices (maps each codepoint to its cluster) */
|
|
15
|
+
readonly clusters: number[];
|
|
16
|
+
/** Pre-context (text before the buffer for contextual shaping) */
|
|
17
|
+
preContext: number[];
|
|
18
|
+
/** Post-context (text after the buffer for contextual shaping) */
|
|
19
|
+
postContext: number[];
|
|
20
|
+
/** Add a string to the buffer */
|
|
21
|
+
addStr(text: string, startCluster?: number): this;
|
|
22
|
+
/** Add codepoints directly */
|
|
23
|
+
addCodepoints(codepoints: number[], startCluster?: number): this;
|
|
24
|
+
/** Add a single codepoint */
|
|
25
|
+
addCodepoint(codepoint: number, cluster?: number): this;
|
|
26
|
+
/** Set text direction */
|
|
27
|
+
setDirection(direction: Direction): this;
|
|
28
|
+
/** Set script (ISO 15924 tag, e.g., 'Latn', 'Arab') */
|
|
29
|
+
setScript(script: string): this;
|
|
30
|
+
/** Set language (BCP 47 tag, e.g., 'en', 'ar') */
|
|
31
|
+
setLanguage(language: string | null): this;
|
|
32
|
+
/** Set cluster level */
|
|
33
|
+
setClusterLevel(level: ClusterLevel): this;
|
|
34
|
+
/** Set buffer flags */
|
|
35
|
+
setFlags(flags: BufferFlags): this;
|
|
36
|
+
/** Set pre-context string */
|
|
37
|
+
setPreContext(text: string): this;
|
|
38
|
+
/** Set post-context string */
|
|
39
|
+
setPostContext(text: string): this;
|
|
40
|
+
/** Clear the buffer */
|
|
41
|
+
clear(): this;
|
|
42
|
+
/** Number of codepoints */
|
|
43
|
+
get length(): number;
|
|
44
|
+
get direction(): Direction;
|
|
45
|
+
get script(): string;
|
|
46
|
+
get language(): string | null;
|
|
47
|
+
get clusterLevel(): ClusterLevel;
|
|
48
|
+
get flags(): BufferFlags;
|
|
49
|
+
/** Convert to initial glyph infos (codepoint = glyphId initially) */
|
|
50
|
+
toGlyphInfos(): GlyphInfo[];
|
|
51
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { F2Dot14, Fixed, int16, int32, Offset16, Offset32, Tag, uint8, uint16, uint32 } from "../../types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Zero-copy binary reader for OpenType font data.
|
|
4
|
+
* All multi-byte values are big-endian per OpenType spec.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Reader {
|
|
7
|
+
private readonly data;
|
|
8
|
+
private readonly start;
|
|
9
|
+
private readonly end;
|
|
10
|
+
private pos;
|
|
11
|
+
constructor(buffer: ArrayBuffer | DataView, offset?: number, length?: number);
|
|
12
|
+
/** Current read position relative to start */
|
|
13
|
+
get offset(): number;
|
|
14
|
+
/** Bytes remaining to read */
|
|
15
|
+
get remaining(): number;
|
|
16
|
+
/** Total length of this reader's view */
|
|
17
|
+
get length(): number;
|
|
18
|
+
/** Seek to absolute offset (relative to this reader's start) */
|
|
19
|
+
seek(offset: number): void;
|
|
20
|
+
/** Skip bytes */
|
|
21
|
+
skip(bytes: number): void;
|
|
22
|
+
/** Create a sub-reader (zero-copy slice) */
|
|
23
|
+
slice(offset: number, length: number): Reader;
|
|
24
|
+
/** Create a sub-reader from current position */
|
|
25
|
+
sliceFrom(offset: number): Reader;
|
|
26
|
+
/** Peek at a value without advancing position */
|
|
27
|
+
peek<T>(fn: () => T): T;
|
|
28
|
+
uint8(): uint8;
|
|
29
|
+
int8(): number;
|
|
30
|
+
uint16(): uint16;
|
|
31
|
+
int16(): int16;
|
|
32
|
+
uint32(): uint32;
|
|
33
|
+
int32(): int32;
|
|
34
|
+
/** 16.16 fixed-point number */
|
|
35
|
+
fixed(): Fixed;
|
|
36
|
+
/** 2.14 fixed-point number */
|
|
37
|
+
f2dot14(): F2Dot14;
|
|
38
|
+
/** Signed 16-bit integer in font design units */
|
|
39
|
+
fword(): int16;
|
|
40
|
+
/** Unsigned 16-bit integer in font design units */
|
|
41
|
+
ufword(): uint16;
|
|
42
|
+
/** 64-bit signed integer (seconds since 1904-01-01) */
|
|
43
|
+
longDateTime(): bigint;
|
|
44
|
+
/** 4-byte ASCII tag as packed uint32 */
|
|
45
|
+
tag(): Tag;
|
|
46
|
+
/** 4-byte ASCII tag as string */
|
|
47
|
+
tagString(): string;
|
|
48
|
+
/** 16-bit offset */
|
|
49
|
+
offset16(): Offset16;
|
|
50
|
+
/** 32-bit offset */
|
|
51
|
+
offset32(): Offset32;
|
|
52
|
+
/** 24-bit unsigned integer */
|
|
53
|
+
uint24(): number;
|
|
54
|
+
uint8Array(count: number): Uint8Array;
|
|
55
|
+
uint16Array(count: number): Uint16Array;
|
|
56
|
+
int16Array(count: number): Int16Array;
|
|
57
|
+
uint32Array(count: number): Uint32Array;
|
|
58
|
+
/** Read array using custom reader function */
|
|
59
|
+
array<T>(count: number, readFn: (reader: Reader) => T): T[];
|
|
60
|
+
/** Read ASCII string of given length */
|
|
61
|
+
ascii(length: number): string;
|
|
62
|
+
/** Read UTF-16BE string (used in 'name' table) */
|
|
63
|
+
utf16be(length: number): string;
|
|
64
|
+
/** Check if there are enough bytes remaining */
|
|
65
|
+
hasRemaining(bytes: number): boolean;
|
|
66
|
+
/** Throw if not enough bytes remaining */
|
|
67
|
+
ensureRemaining(bytes: number): void;
|
|
68
|
+
/** Get raw bytes as Uint8Array (zero-copy view) */
|
|
69
|
+
bytes(length: number): Uint8Array;
|
|
70
|
+
/** Read value at specific offset without moving position */
|
|
71
|
+
readAt<T>(offset: number, fn: (reader: Reader) => T): T;
|
|
72
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { GlyphId, Tag, Variation } from "../types.ts";
|
|
2
|
+
import type { Font } from "./font.ts";
|
|
3
|
+
import { type VariationAxis } from "./tables/fvar.ts";
|
|
4
|
+
/**
|
|
5
|
+
* A Face represents a specific instance of a variable font.
|
|
6
|
+
* For non-variable fonts, it simply wraps the Font.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Face {
|
|
9
|
+
readonly font: Font;
|
|
10
|
+
/** Normalized axis coordinates [-1, 1] */
|
|
11
|
+
private _coords;
|
|
12
|
+
/** User-space axis values */
|
|
13
|
+
private _variations;
|
|
14
|
+
constructor(font: Font, variations?: Record<string, number> | Variation[]);
|
|
15
|
+
/**
|
|
16
|
+
* Set variation axis values
|
|
17
|
+
* @param variations Object with axis tags as keys (e.g., { wght: 700, wdth: 100 })
|
|
18
|
+
* or array of Variation objects
|
|
19
|
+
*/
|
|
20
|
+
setVariations(variations: Record<string, number> | Variation[]): void;
|
|
21
|
+
/**
|
|
22
|
+
* Get normalized coordinates for variation processing
|
|
23
|
+
*/
|
|
24
|
+
get normalizedCoords(): number[];
|
|
25
|
+
/**
|
|
26
|
+
* Check if this is a variable font instance
|
|
27
|
+
*/
|
|
28
|
+
get isVariable(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Get variation axes
|
|
31
|
+
*/
|
|
32
|
+
get axes(): VariationAxis[];
|
|
33
|
+
/**
|
|
34
|
+
* Get current value for an axis
|
|
35
|
+
*/
|
|
36
|
+
getAxisValue(axisTag: Tag | string): number | null;
|
|
37
|
+
/**
|
|
38
|
+
* Get advance width for a glyph, including variation deltas
|
|
39
|
+
*/
|
|
40
|
+
advanceWidth(glyphId: GlyphId): number;
|
|
41
|
+
/**
|
|
42
|
+
* Get left side bearing for a glyph, including variation deltas
|
|
43
|
+
*/
|
|
44
|
+
leftSideBearing(glyphId: GlyphId): number;
|
|
45
|
+
get numGlyphs(): number;
|
|
46
|
+
get unitsPerEm(): number;
|
|
47
|
+
get ascender(): number;
|
|
48
|
+
get descender(): number;
|
|
49
|
+
get lineGap(): number;
|
|
50
|
+
glyphId(codepoint: number): GlyphId;
|
|
51
|
+
glyphIdForChar(char: string): GlyphId;
|
|
52
|
+
hasTable(t: Tag): boolean;
|
|
53
|
+
get gdef(): import("./tables/gdef.ts").GdefTable | null;
|
|
54
|
+
get gsub(): import("./tables/gsub.ts").GsubTable | null;
|
|
55
|
+
get gpos(): import("./tables/gpos.ts").GposTable | null;
|
|
56
|
+
get kern(): import("./tables/kern.ts").KernTable | null;
|
|
57
|
+
get morx(): import("./tables/morx.ts").MorxTable | null;
|
|
58
|
+
get cmap(): import("./tables/cmap.ts").CmapTable;
|
|
59
|
+
get hmtx(): import("./tables/hmtx.ts").HmtxTable;
|
|
60
|
+
get hhea(): import("./tables/hhea.ts").HheaTable;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create a face from a font with optional variations
|
|
64
|
+
*/
|
|
65
|
+
export declare function createFace(font: Font, variations?: Record<string, number> | Variation[]): Face;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { GlyphId, TableRecord, Tag } from "../types.ts";
|
|
2
|
+
import { Reader } from "./binary/reader.ts";
|
|
3
|
+
import { type AvarTable } from "./tables/avar.ts";
|
|
4
|
+
import { type BaseTable } from "./tables/base.ts";
|
|
5
|
+
import { type CbdtTable, type CblcTable } from "./tables/cbdt.ts";
|
|
6
|
+
import { type CffTable } from "./tables/cff.ts";
|
|
7
|
+
import { type Cff2Table } from "./tables/cff2.ts";
|
|
8
|
+
import { type CmapTable } from "./tables/cmap.ts";
|
|
9
|
+
import { type ColrTable } from "./tables/colr.ts";
|
|
10
|
+
import { type CpalTable } from "./tables/cpal.ts";
|
|
11
|
+
import { type FvarTable } from "./tables/fvar.ts";
|
|
12
|
+
import { type GdefTable } from "./tables/gdef.ts";
|
|
13
|
+
import { type Contour, type GlyfTable, type Glyph } from "./tables/glyf.ts";
|
|
14
|
+
import { type GposTable } from "./tables/gpos.ts";
|
|
15
|
+
import { type GsubTable } from "./tables/gsub.ts";
|
|
16
|
+
import { type GvarTable } from "./tables/gvar.ts";
|
|
17
|
+
import { type HeadTable } from "./tables/head.ts";
|
|
18
|
+
import { type HheaTable } from "./tables/hhea.ts";
|
|
19
|
+
import { type HmtxTable } from "./tables/hmtx.ts";
|
|
20
|
+
import { type HvarTable } from "./tables/hvar.ts";
|
|
21
|
+
import { type JstfTable } from "./tables/jstf.ts";
|
|
22
|
+
import { type KernTable } from "./tables/kern.ts";
|
|
23
|
+
import { type KerxTable } from "./tables/kerx.ts";
|
|
24
|
+
import { type LocaTable } from "./tables/loca.ts";
|
|
25
|
+
import { type MathTable } from "./tables/math.ts";
|
|
26
|
+
import { type MaxpTable } from "./tables/maxp.ts";
|
|
27
|
+
import { type MorxTable } from "./tables/morx.ts";
|
|
28
|
+
import { type MvarTable } from "./tables/mvar.ts";
|
|
29
|
+
import { type NameTable } from "./tables/name.ts";
|
|
30
|
+
import { type Os2Table } from "./tables/os2.ts";
|
|
31
|
+
import { type PostTable } from "./tables/post.ts";
|
|
32
|
+
import { type SbixTable } from "./tables/sbix.ts";
|
|
33
|
+
import { type FeatTable } from "./tables/feat.ts";
|
|
34
|
+
import { type StatTable } from "./tables/stat.ts";
|
|
35
|
+
import { type SvgTable } from "./tables/svg.ts";
|
|
36
|
+
import { type TrakTable } from "./tables/trak.ts";
|
|
37
|
+
import { type VheaTable } from "./tables/vhea.ts";
|
|
38
|
+
import { type VmtxTable } from "./tables/vmtx.ts";
|
|
39
|
+
import { type VorgTable } from "./tables/vorg.ts";
|
|
40
|
+
import { type VvarTable } from "./tables/vvar.ts";
|
|
41
|
+
/** Font loading options */
|
|
42
|
+
export interface FontLoadOptions {
|
|
43
|
+
/** Tables to parse eagerly (default: lazy) */
|
|
44
|
+
eagerTables?: Tag[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Represents a loaded font file.
|
|
48
|
+
* Tables are parsed lazily on first access.
|
|
49
|
+
*/
|
|
50
|
+
export declare class Font {
|
|
51
|
+
private readonly reader;
|
|
52
|
+
private readonly directory;
|
|
53
|
+
private _head;
|
|
54
|
+
private _maxp;
|
|
55
|
+
private _hhea;
|
|
56
|
+
private _hmtx;
|
|
57
|
+
private _cmap;
|
|
58
|
+
private _gdef;
|
|
59
|
+
private _gsub;
|
|
60
|
+
private _gpos;
|
|
61
|
+
private _kern;
|
|
62
|
+
private _fvar;
|
|
63
|
+
private _hvar;
|
|
64
|
+
private _vhea;
|
|
65
|
+
private _vmtx;
|
|
66
|
+
private _morx;
|
|
67
|
+
private _gvar;
|
|
68
|
+
private _avar;
|
|
69
|
+
private _kerx;
|
|
70
|
+
private _trak;
|
|
71
|
+
private _cff;
|
|
72
|
+
private _cff2;
|
|
73
|
+
private _colr;
|
|
74
|
+
private _cpal;
|
|
75
|
+
private _vvar;
|
|
76
|
+
private _mvar;
|
|
77
|
+
private _os2;
|
|
78
|
+
private _name;
|
|
79
|
+
private _post;
|
|
80
|
+
private _base;
|
|
81
|
+
private _jstf;
|
|
82
|
+
private _math;
|
|
83
|
+
private _loca;
|
|
84
|
+
private _glyf;
|
|
85
|
+
private _svg;
|
|
86
|
+
private _vorg;
|
|
87
|
+
private _sbix;
|
|
88
|
+
private _stat;
|
|
89
|
+
private _cbdt;
|
|
90
|
+
private _cblc;
|
|
91
|
+
private _feat;
|
|
92
|
+
private constructor();
|
|
93
|
+
/** Load font from ArrayBuffer */
|
|
94
|
+
static load(buffer: ArrayBuffer, options?: FontLoadOptions): Font;
|
|
95
|
+
/** Load font from URL (works in browser and Bun) */
|
|
96
|
+
static fromURL(url: string, options?: FontLoadOptions): Promise<Font>;
|
|
97
|
+
/** Load font from file path (Bun only) */
|
|
98
|
+
static fromFile(path: string, options?: FontLoadOptions): Promise<Font>;
|
|
99
|
+
/** Check if font has a specific table */
|
|
100
|
+
hasTable(tag: Tag): boolean;
|
|
101
|
+
/** Get table record */
|
|
102
|
+
getTableRecord(tag: Tag): TableRecord | undefined;
|
|
103
|
+
/** Get reader for a table */
|
|
104
|
+
getTableReader(tag: Tag): Reader | null;
|
|
105
|
+
get head(): HeadTable;
|
|
106
|
+
get maxp(): MaxpTable;
|
|
107
|
+
get hhea(): HheaTable;
|
|
108
|
+
get hmtx(): HmtxTable;
|
|
109
|
+
get cmap(): CmapTable;
|
|
110
|
+
get gdef(): GdefTable | null;
|
|
111
|
+
get gsub(): GsubTable | null;
|
|
112
|
+
get gpos(): GposTable | null;
|
|
113
|
+
get kern(): KernTable | null;
|
|
114
|
+
get fvar(): FvarTable | null;
|
|
115
|
+
get hvar(): HvarTable | null;
|
|
116
|
+
get vhea(): VheaTable | null;
|
|
117
|
+
get vmtx(): VmtxTable | null;
|
|
118
|
+
get morx(): MorxTable | null;
|
|
119
|
+
get gvar(): GvarTable | null;
|
|
120
|
+
get avar(): AvarTable | null;
|
|
121
|
+
get kerx(): KerxTable | null;
|
|
122
|
+
get trak(): TrakTable | null;
|
|
123
|
+
get cff(): CffTable | null;
|
|
124
|
+
get cff2(): Cff2Table | null;
|
|
125
|
+
get colr(): ColrTable | null;
|
|
126
|
+
get cpal(): CpalTable | null;
|
|
127
|
+
get vvar(): VvarTable | null;
|
|
128
|
+
get mvar(): MvarTable | null;
|
|
129
|
+
get os2(): Os2Table | null;
|
|
130
|
+
get name(): NameTable | null;
|
|
131
|
+
get post(): PostTable | null;
|
|
132
|
+
get base(): BaseTable | null;
|
|
133
|
+
get jstf(): JstfTable | null;
|
|
134
|
+
get math(): MathTable | null;
|
|
135
|
+
get loca(): LocaTable | null;
|
|
136
|
+
get glyf(): GlyfTable | null;
|
|
137
|
+
get svg(): SvgTable | null;
|
|
138
|
+
get vorg(): VorgTable | null;
|
|
139
|
+
get sbix(): SbixTable | null;
|
|
140
|
+
get stat(): StatTable | null;
|
|
141
|
+
get cblc(): CblcTable | null;
|
|
142
|
+
get cbdt(): CbdtTable | null;
|
|
143
|
+
get feat(): FeatTable | null;
|
|
144
|
+
/** Number of glyphs in the font */
|
|
145
|
+
get numGlyphs(): number;
|
|
146
|
+
/** Units per em */
|
|
147
|
+
get unitsPerEm(): number;
|
|
148
|
+
/** Ascender (from hhea) */
|
|
149
|
+
get ascender(): number;
|
|
150
|
+
/** Descender (from hhea) */
|
|
151
|
+
get descender(): number;
|
|
152
|
+
/** Line gap (from hhea) */
|
|
153
|
+
get lineGap(): number;
|
|
154
|
+
/** Is this a TrueType font (vs CFF)? */
|
|
155
|
+
get isTrueType(): boolean;
|
|
156
|
+
/** Is this a CFF font? */
|
|
157
|
+
get isCFF(): boolean;
|
|
158
|
+
/** Is this a variable font? */
|
|
159
|
+
get isVariable(): boolean;
|
|
160
|
+
/** Has OpenType layout tables? */
|
|
161
|
+
get hasOpenTypeLayout(): boolean;
|
|
162
|
+
/** Has AAT layout tables? */
|
|
163
|
+
get hasAATLayout(): boolean;
|
|
164
|
+
/** Is this a color font? */
|
|
165
|
+
get isColorFont(): boolean;
|
|
166
|
+
/** Get glyph ID for a Unicode codepoint */
|
|
167
|
+
glyphId(codepoint: number): GlyphId;
|
|
168
|
+
/** Get glyph ID for a character */
|
|
169
|
+
glyphIdForChar(char: string): GlyphId;
|
|
170
|
+
/** Get advance width for a glyph */
|
|
171
|
+
advanceWidth(glyphId: GlyphId): number;
|
|
172
|
+
/** Get left side bearing for a glyph */
|
|
173
|
+
leftSideBearing(glyphId: GlyphId): number;
|
|
174
|
+
/** List all table tags in the font */
|
|
175
|
+
listTables(): string[];
|
|
176
|
+
/** Get raw glyph data (simple or composite) */
|
|
177
|
+
getGlyph(glyphId: GlyphId): Glyph | null;
|
|
178
|
+
/** Get flattened contours for a glyph (resolves composites) */
|
|
179
|
+
getGlyphContours(glyphId: GlyphId): Contour[] | null;
|
|
180
|
+
/** Get bounding box for a glyph */
|
|
181
|
+
getGlyphBounds(glyphId: GlyphId): {
|
|
182
|
+
xMin: number;
|
|
183
|
+
yMin: number;
|
|
184
|
+
xMax: number;
|
|
185
|
+
yMax: number;
|
|
186
|
+
} | null;
|
|
187
|
+
/** Get contours for a glyph with variation applied */
|
|
188
|
+
getGlyphContoursWithVariation(glyphId: GlyphId, axisCoords: number[]): Contour[] | null;
|
|
189
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { uint16 } from "../../types.ts";
|
|
2
|
+
import type { Reader } from "../binary/reader.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Axis Variations table (avar)
|
|
5
|
+
* Maps user-facing axis values to normalized coordinates
|
|
6
|
+
*/
|
|
7
|
+
export interface AvarTable {
|
|
8
|
+
majorVersion: uint16;
|
|
9
|
+
minorVersion: uint16;
|
|
10
|
+
axisSegmentMaps: AxisSegmentMap[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Segment map for an axis
|
|
14
|
+
*/
|
|
15
|
+
export interface AxisSegmentMap {
|
|
16
|
+
axisValueMaps: AxisValueMap[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Single value mapping
|
|
20
|
+
*/
|
|
21
|
+
export interface AxisValueMap {
|
|
22
|
+
fromCoordinate: number;
|
|
23
|
+
toCoordinate: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse avar table
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseAvar(reader: Reader, axisCount: number): AvarTable;
|
|
29
|
+
/**
|
|
30
|
+
* Apply avar mapping to a normalized coordinate
|
|
31
|
+
*/
|
|
32
|
+
export declare function applyAvarMapping(segmentMap: AxisSegmentMap, coord: number): number;
|
|
33
|
+
/**
|
|
34
|
+
* Apply avar mappings to all axis coordinates
|
|
35
|
+
*/
|
|
36
|
+
export declare function applyAvar(avar: AvarTable, coords: number[]): number[];
|