harfbuzzjs 0.10.3 → 1.0.0-beta.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 +1 -4
- package/README.md +41 -48
- package/dist/harfbuzz-subset.wasm +0 -0
- package/dist/harfbuzz.d.ts +4 -0
- package/dist/harfbuzz.js +2 -0
- package/{hb.wasm → dist/harfbuzz.wasm} +0 -0
- package/dist/index.d.mts +610 -0
- package/dist/index.mjs +1237 -0
- package/package.json +50 -37
- package/Makefile +0 -70
- package/config-override-subset.h +0 -7
- package/config-override.h +0 -14
- package/em.runtime +0 -12
- package/examples/hb-subset.example.node.js +0 -66
- package/examples/hbjs.example.html +0 -16
- package/examples/hbjs.example.js +0 -37
- package/examples/hbjs.example.node.js +0 -8
- package/examples/nohbjs.html +0 -64
- package/hb-subset.symbols +0 -34
- package/hb-subset.wasm +0 -0
- package/hb.js +0 -2
- package/hb.symbols +0 -96
- package/hbjs.js +0 -1463
- package/index.js +0 -8
- package/logo.png +0 -0
- package/test/fonts/noto/NotoSans-Regular.otf +0 -0
- package/test/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/test/fonts/noto/NotoSansArabic-Variable.ttf +0 -0
- package/test/fonts/noto/NotoSansDevanagari-Regular.otf +0 -0
- package/test/fonts/noto/OFL.txt +0 -93
- package/test/index.js +0 -1040
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1237 @@
|
|
|
1
|
+
import createHarfBuzz from "./harfbuzz.js";
|
|
2
|
+
//#region src/helpers.ts
|
|
3
|
+
let Module;
|
|
4
|
+
let exports;
|
|
5
|
+
let freeFuncPtr;
|
|
6
|
+
const utf8Decoder = new TextDecoder("utf8");
|
|
7
|
+
const utf8Encoder = new TextEncoder();
|
|
8
|
+
const registry = new FinalizationRegistry((cleanup) => {
|
|
9
|
+
cleanup();
|
|
10
|
+
});
|
|
11
|
+
function track(obj, destroy) {
|
|
12
|
+
const ptr = obj.ptr;
|
|
13
|
+
registry.register(obj, () => destroy(ptr));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Initialize the HarfBuzz module.
|
|
17
|
+
* @param module The Emscripten module instance created with {@link
|
|
18
|
+
* createHarfBuzz}.
|
|
19
|
+
*/
|
|
20
|
+
function init(module) {
|
|
21
|
+
Module = module;
|
|
22
|
+
exports = Module.wasmExports;
|
|
23
|
+
freeFuncPtr = Module.addFunction((ptr) => {
|
|
24
|
+
exports.free(ptr);
|
|
25
|
+
}, "vi");
|
|
26
|
+
}
|
|
27
|
+
function hb_tag(s) {
|
|
28
|
+
return (s.charCodeAt(0) & 255) << 24 | (s.charCodeAt(1) & 255) << 16 | (s.charCodeAt(2) & 255) << 8 | (s.charCodeAt(3) & 255) << 0;
|
|
29
|
+
}
|
|
30
|
+
function hb_untag(tag) {
|
|
31
|
+
return [
|
|
32
|
+
String.fromCharCode(tag >> 24 & 255),
|
|
33
|
+
String.fromCharCode(tag >> 16 & 255),
|
|
34
|
+
String.fromCharCode(tag >> 8 & 255),
|
|
35
|
+
String.fromCharCode(tag >> 0 & 255)
|
|
36
|
+
].join("");
|
|
37
|
+
}
|
|
38
|
+
function utf8_ptr_to_string(ptr, length) {
|
|
39
|
+
let end;
|
|
40
|
+
if (length === void 0) end = Module.HEAPU8.indexOf(0, ptr);
|
|
41
|
+
else end = ptr + length;
|
|
42
|
+
return utf8Decoder.decode(Module.HEAPU8.subarray(ptr, end));
|
|
43
|
+
}
|
|
44
|
+
function utf16_ptr_to_string(ptr, length) {
|
|
45
|
+
const end = ptr / 2 + length;
|
|
46
|
+
return String.fromCharCode(...Module.HEAPU16.subarray(ptr / 2, end));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Use when you know the input range should be ASCII.
|
|
50
|
+
* Faster than encoding to UTF-8
|
|
51
|
+
*/
|
|
52
|
+
function string_to_ascii_ptr(text) {
|
|
53
|
+
const ptr = exports.malloc(text.length + 1);
|
|
54
|
+
for (let i = 0; i < text.length; ++i) {
|
|
55
|
+
const char = text.charCodeAt(i);
|
|
56
|
+
if (char > 127) throw new Error("Expected ASCII text");
|
|
57
|
+
Module.HEAPU8[ptr + i] = char;
|
|
58
|
+
}
|
|
59
|
+
Module.HEAPU8[ptr + text.length] = 0;
|
|
60
|
+
return {
|
|
61
|
+
ptr,
|
|
62
|
+
length: text.length,
|
|
63
|
+
free: function() {
|
|
64
|
+
exports.free(ptr);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function string_to_utf8_ptr(text) {
|
|
69
|
+
const ptr = exports.malloc(text.length);
|
|
70
|
+
utf8Encoder.encodeInto(text, Module.HEAPU8.subarray(ptr, ptr + text.length));
|
|
71
|
+
return {
|
|
72
|
+
ptr,
|
|
73
|
+
length: text.length,
|
|
74
|
+
free: function() {
|
|
75
|
+
exports.free(ptr);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function string_to_utf16_ptr(text) {
|
|
80
|
+
const ptr = exports.malloc(text.length * 2);
|
|
81
|
+
const words = Module.HEAPU16.subarray(ptr / 2, ptr / 2 + text.length);
|
|
82
|
+
for (let i = 0; i < words.length; ++i) words[i] = text.charCodeAt(i);
|
|
83
|
+
return {
|
|
84
|
+
ptr,
|
|
85
|
+
length: words.length,
|
|
86
|
+
free: function() {
|
|
87
|
+
exports.free(ptr);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function language_to_string(language) {
|
|
92
|
+
return utf8_ptr_to_string(exports.hb_language_to_string(language));
|
|
93
|
+
}
|
|
94
|
+
function language_from_string(str) {
|
|
95
|
+
const languageStr = string_to_ascii_ptr(str);
|
|
96
|
+
const languagePtr = exports.hb_language_from_string(languageStr.ptr, -1);
|
|
97
|
+
languageStr.free();
|
|
98
|
+
return languagePtr;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Return the typed array of HarfBuzz set contents.
|
|
102
|
+
* @param setPtr Pointer of set
|
|
103
|
+
* @returns Typed array instance
|
|
104
|
+
*/
|
|
105
|
+
function typed_array_from_set(setPtr) {
|
|
106
|
+
const setCount = exports.hb_set_get_population(setPtr);
|
|
107
|
+
const arrayPtr = exports.malloc(setCount << 2);
|
|
108
|
+
const arrayOffset = arrayPtr >> 2;
|
|
109
|
+
exports.hb_set_next_many(setPtr, -1, arrayPtr, setCount);
|
|
110
|
+
return Module.HEAPU32.subarray(arrayOffset, arrayOffset + setCount);
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/types.ts
|
|
114
|
+
const GlyphFlag = {
|
|
115
|
+
UNSAFE_TO_BREAK: 1,
|
|
116
|
+
UNSAFE_TO_CONCAT: 2,
|
|
117
|
+
SAFE_TO_INSERT_TATWEEL: 4,
|
|
118
|
+
DEFINED: 7
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/blob.ts
|
|
122
|
+
/**
|
|
123
|
+
* An object representing a {@link https://harfbuzz.github.io/harfbuzz-hb-blob.html | HarfBuzz blob}.
|
|
124
|
+
* A blob wraps a chunk of binary data, typically the contents of a font file.
|
|
125
|
+
*/
|
|
126
|
+
var Blob = class {
|
|
127
|
+
/**
|
|
128
|
+
* @param data Binary font data.
|
|
129
|
+
*/
|
|
130
|
+
constructor(data) {
|
|
131
|
+
const blobPtr = exports.malloc(data.byteLength);
|
|
132
|
+
Module.HEAPU8.set(new Uint8Array(data), blobPtr);
|
|
133
|
+
this.ptr = exports.hb_blob_create(blobPtr, data.byteLength, 2, blobPtr, freeFuncPtr);
|
|
134
|
+
track(this, exports.hb_blob_destroy);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/face.ts
|
|
139
|
+
const HB_OT_NAME_ID_INVALID = 65535;
|
|
140
|
+
const GlyphClass = {
|
|
141
|
+
UNCLASSIFIED: 0,
|
|
142
|
+
BASE_GLYPH: 1,
|
|
143
|
+
LIGATURE: 2,
|
|
144
|
+
MARK: 3,
|
|
145
|
+
COMPONENT: 4
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* An object representing a {@link https://harfbuzz.github.io/harfbuzz-hb-face.html | HarfBuzz face}.
|
|
149
|
+
* A face represents a single face in a binary font file and is the
|
|
150
|
+
* foundation for creating Font objects used in text shaping.
|
|
151
|
+
*/
|
|
152
|
+
var Face = class {
|
|
153
|
+
/**
|
|
154
|
+
* @param blob A Blob containing font data.
|
|
155
|
+
* @param index The index of the font in the blob. (0 for most files,
|
|
156
|
+
* or a 0-indexed font number if the `blob` came from a font collection file.)
|
|
157
|
+
*/
|
|
158
|
+
constructor(blob, index = 0) {
|
|
159
|
+
this.ptr = exports.hb_face_create(blob.ptr, index);
|
|
160
|
+
this.upem = exports.hb_face_get_upem(this.ptr);
|
|
161
|
+
track(this, exports.hb_face_destroy);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Return the binary contents of an OpenType table.
|
|
165
|
+
* @param table Table name
|
|
166
|
+
* @returns A Uint8Array of the table data, or undefined if the table is not found.
|
|
167
|
+
*/
|
|
168
|
+
referenceTable(table) {
|
|
169
|
+
const blob = exports.hb_face_reference_table(this.ptr, hb_tag(table));
|
|
170
|
+
const length = exports.hb_blob_get_length(blob);
|
|
171
|
+
if (!length) return;
|
|
172
|
+
const blobptr = exports.hb_blob_get_data(blob, 0);
|
|
173
|
+
return Module.HEAPU8.subarray(blobptr, blobptr + length);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Return variation axis infos.
|
|
177
|
+
* @returns A dictionary mapping axis tags to {min, default, max} values.
|
|
178
|
+
*/
|
|
179
|
+
getAxisInfos() {
|
|
180
|
+
const sp = Module.stackSave();
|
|
181
|
+
const axis = Module.stackAlloc(2048);
|
|
182
|
+
const c = Module.stackAlloc(4);
|
|
183
|
+
Module.HEAPU32[c / 4] = 64;
|
|
184
|
+
exports.hb_ot_var_get_axis_infos(this.ptr, 0, c, axis);
|
|
185
|
+
const result = {};
|
|
186
|
+
Array.from({ length: Module.HEAPU32[c / 4] }).forEach((_, i) => {
|
|
187
|
+
result[hb_untag(Module.HEAPU32[axis / 4 + i * 8 + 1])] = {
|
|
188
|
+
min: Module.HEAPF32[axis / 4 + i * 8 + 4],
|
|
189
|
+
default: Module.HEAPF32[axis / 4 + i * 8 + 5],
|
|
190
|
+
max: Module.HEAPF32[axis / 4 + i * 8 + 6]
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
Module.stackRestore(sp);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Return unicodes the face supports.
|
|
198
|
+
* @returns A Uint32Array of supported Unicode code points.
|
|
199
|
+
*/
|
|
200
|
+
collectUnicodes() {
|
|
201
|
+
const unicodeSetPtr = exports.hb_set_create();
|
|
202
|
+
exports.hb_face_collect_unicodes(this.ptr, unicodeSetPtr);
|
|
203
|
+
const result = typed_array_from_set(unicodeSetPtr);
|
|
204
|
+
exports.hb_set_destroy(unicodeSetPtr);
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Return all scripts enumerated in the specified face's
|
|
209
|
+
* GSUB table or GPOS table.
|
|
210
|
+
* @param table The table to query, either "GSUB" or "GPOS".
|
|
211
|
+
* @returns An array of 4-character script tag strings.
|
|
212
|
+
*/
|
|
213
|
+
getTableScriptTags(table) {
|
|
214
|
+
const sp = Module.stackSave();
|
|
215
|
+
const tableTag = hb_tag(table);
|
|
216
|
+
let startOffset = 0;
|
|
217
|
+
let scriptCount = 128;
|
|
218
|
+
const scriptCountPtr = Module.stackAlloc(4);
|
|
219
|
+
const scriptTagsPtr = Module.stackAlloc(512);
|
|
220
|
+
const tags = [];
|
|
221
|
+
while (scriptCount == 128) {
|
|
222
|
+
Module.HEAPU32[scriptCountPtr / 4] = scriptCount;
|
|
223
|
+
exports.hb_ot_layout_table_get_script_tags(this.ptr, tableTag, startOffset, scriptCountPtr, scriptTagsPtr);
|
|
224
|
+
scriptCount = Module.HEAPU32[scriptCountPtr / 4];
|
|
225
|
+
const scriptTags = Module.HEAPU32.subarray(scriptTagsPtr / 4, scriptTagsPtr / 4 + scriptCount);
|
|
226
|
+
tags.push(...Array.from(scriptTags).map(hb_untag));
|
|
227
|
+
startOffset += scriptCount;
|
|
228
|
+
}
|
|
229
|
+
Module.stackRestore(sp);
|
|
230
|
+
return tags;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Return all features enumerated in the specified face's
|
|
234
|
+
* GSUB table or GPOS table.
|
|
235
|
+
* @param table The table to query, either "GSUB" or "GPOS".
|
|
236
|
+
* @returns An array of 4-character feature tag strings.
|
|
237
|
+
*/
|
|
238
|
+
getTableFeatureTags(table) {
|
|
239
|
+
const sp = Module.stackSave();
|
|
240
|
+
const tableTag = hb_tag(table);
|
|
241
|
+
let startOffset = 0;
|
|
242
|
+
let featureCount = 128;
|
|
243
|
+
const featureCountPtr = Module.stackAlloc(4);
|
|
244
|
+
const featureTagsPtr = Module.stackAlloc(512);
|
|
245
|
+
const tags = [];
|
|
246
|
+
while (featureCount == 128) {
|
|
247
|
+
Module.HEAPU32[featureCountPtr / 4] = featureCount;
|
|
248
|
+
exports.hb_ot_layout_table_get_feature_tags(this.ptr, tableTag, startOffset, featureCountPtr, featureTagsPtr);
|
|
249
|
+
featureCount = Module.HEAPU32[featureCountPtr / 4];
|
|
250
|
+
const featureTags = Module.HEAPU32.subarray(featureTagsPtr / 4, featureTagsPtr / 4 + featureCount);
|
|
251
|
+
tags.push(...Array.from(featureTags).map(hb_untag));
|
|
252
|
+
startOffset += featureCount;
|
|
253
|
+
}
|
|
254
|
+
Module.stackRestore(sp);
|
|
255
|
+
return tags;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Return language tags in the given face's GSUB or GPOS table, underneath
|
|
259
|
+
* the specified script index.
|
|
260
|
+
* @param table The table to query, either "GSUB" or "GPOS".
|
|
261
|
+
* @param scriptIndex The index of the script to query.
|
|
262
|
+
* @returns An array of 4-character language tag strings.
|
|
263
|
+
*/
|
|
264
|
+
getScriptLanguageTags(table, scriptIndex) {
|
|
265
|
+
const sp = Module.stackSave();
|
|
266
|
+
const tableTag = hb_tag(table);
|
|
267
|
+
let startOffset = 0;
|
|
268
|
+
let languageCount = 128;
|
|
269
|
+
const languageCountPtr = Module.stackAlloc(4);
|
|
270
|
+
const languageTagsPtr = Module.stackAlloc(512);
|
|
271
|
+
const tags = [];
|
|
272
|
+
while (languageCount == 128) {
|
|
273
|
+
Module.HEAPU32[languageCountPtr / 4] = languageCount;
|
|
274
|
+
exports.hb_ot_layout_script_get_language_tags(this.ptr, tableTag, scriptIndex, startOffset, languageCountPtr, languageTagsPtr);
|
|
275
|
+
languageCount = Module.HEAPU32[languageCountPtr / 4];
|
|
276
|
+
const languageTags = Module.HEAPU32.subarray(languageTagsPtr / 4, languageTagsPtr / 4 + languageCount);
|
|
277
|
+
tags.push(...Array.from(languageTags).map(hb_untag));
|
|
278
|
+
startOffset += languageCount;
|
|
279
|
+
}
|
|
280
|
+
Module.stackRestore(sp);
|
|
281
|
+
return tags;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Return all features in the specified face's GSUB table or GPOS table,
|
|
285
|
+
* underneath the specified script and language.
|
|
286
|
+
* @param table The table to query, either "GSUB" or "GPOS".
|
|
287
|
+
* @param scriptIndex The index of the script to query.
|
|
288
|
+
* @param languageIndex The index of the language to query.
|
|
289
|
+
* @returns An array of 4-character feature tag strings.
|
|
290
|
+
*/
|
|
291
|
+
getLanguageFeatureTags(table, scriptIndex, languageIndex) {
|
|
292
|
+
const sp = Module.stackSave();
|
|
293
|
+
const tableTag = hb_tag(table);
|
|
294
|
+
let startOffset = 0;
|
|
295
|
+
let featureCount = 128;
|
|
296
|
+
const featureCountPtr = Module.stackAlloc(4);
|
|
297
|
+
const featureTagsPtr = Module.stackAlloc(512);
|
|
298
|
+
const tags = [];
|
|
299
|
+
while (featureCount == 128) {
|
|
300
|
+
Module.HEAPU32[featureCountPtr / 4] = featureCount;
|
|
301
|
+
exports.hb_ot_layout_language_get_feature_tags(this.ptr, tableTag, scriptIndex, languageIndex, startOffset, featureCountPtr, featureTagsPtr);
|
|
302
|
+
featureCount = Module.HEAPU32[featureCountPtr / 4];
|
|
303
|
+
const featureTags = Module.HEAPU32.subarray(featureTagsPtr / 4, featureTagsPtr / 4 + featureCount);
|
|
304
|
+
tags.push(...Array.from(featureTags).map(hb_untag));
|
|
305
|
+
startOffset += featureCount;
|
|
306
|
+
}
|
|
307
|
+
Module.stackRestore(sp);
|
|
308
|
+
return tags;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get the GDEF class of the requested glyph.
|
|
312
|
+
* @param glyph The glyph to get the class of.
|
|
313
|
+
* @returns The {@link GlyphClass} of the glyph.
|
|
314
|
+
*/
|
|
315
|
+
getGlyphClass(glyph) {
|
|
316
|
+
return exports.hb_ot_layout_get_glyph_class(this.ptr, glyph);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Return all names in the specified face's name table.
|
|
320
|
+
* @returns An array of {nameId, language} entries.
|
|
321
|
+
*/
|
|
322
|
+
listNames() {
|
|
323
|
+
const sp = Module.stackSave();
|
|
324
|
+
const numEntriesPtr = Module.stackAlloc(4);
|
|
325
|
+
const entriesPtr = exports.hb_ot_name_list_names(this.ptr, numEntriesPtr);
|
|
326
|
+
const numEntries = Module.HEAPU32[numEntriesPtr / 4];
|
|
327
|
+
const entries = [];
|
|
328
|
+
for (let i = 0; i < numEntries; i++) entries.push({
|
|
329
|
+
nameId: Module.HEAPU32[entriesPtr / 4 + i * 3],
|
|
330
|
+
language: language_to_string(Module.HEAPU32[entriesPtr / 4 + i * 3 + 2])
|
|
331
|
+
});
|
|
332
|
+
Module.stackRestore(sp);
|
|
333
|
+
return entries;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get the name of the specified face.
|
|
337
|
+
* @param nameId The ID of the name to get.
|
|
338
|
+
* @param language The language of the name to get.
|
|
339
|
+
* @returns The name string.
|
|
340
|
+
*/
|
|
341
|
+
getName(nameId, language) {
|
|
342
|
+
const sp = Module.stackSave();
|
|
343
|
+
const languagePtr = language_from_string(language);
|
|
344
|
+
const nameLen = exports.hb_ot_name_get_utf16(this.ptr, nameId, languagePtr, 0, 0) + 1;
|
|
345
|
+
const textSizePtr = Module.stackAlloc(4);
|
|
346
|
+
const textPtr = exports.malloc(nameLen * 2);
|
|
347
|
+
Module.HEAPU32[textSizePtr / 4] = nameLen;
|
|
348
|
+
exports.hb_ot_name_get_utf16(this.ptr, nameId, languagePtr, textSizePtr, textPtr);
|
|
349
|
+
const name = utf16_ptr_to_string(textPtr, nameLen - 1);
|
|
350
|
+
exports.free(textPtr);
|
|
351
|
+
Module.stackRestore(sp);
|
|
352
|
+
return name;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Get the name IDs of the specified feature.
|
|
356
|
+
* @param table The table to query, either "GSUB" or "GPOS".
|
|
357
|
+
* @param featureIndex The index of the feature to query.
|
|
358
|
+
* @returns An object with name IDs, or null if not found.
|
|
359
|
+
*/
|
|
360
|
+
getFeatureNameIds(table, featureIndex) {
|
|
361
|
+
const sp = Module.stackSave();
|
|
362
|
+
const tableTag = hb_tag(table);
|
|
363
|
+
const labelIdPtr = Module.stackAlloc(4);
|
|
364
|
+
const tooltipIdPtr = Module.stackAlloc(4);
|
|
365
|
+
const sampleIdPtr = Module.stackAlloc(4);
|
|
366
|
+
const numNamedParametersPtr = Module.stackAlloc(4);
|
|
367
|
+
const firstParameterIdPtr = Module.stackAlloc(4);
|
|
368
|
+
const found = exports.hb_ot_layout_feature_get_name_ids(this.ptr, tableTag, featureIndex, labelIdPtr, tooltipIdPtr, sampleIdPtr, numNamedParametersPtr, firstParameterIdPtr);
|
|
369
|
+
let names = null;
|
|
370
|
+
if (found) {
|
|
371
|
+
const uiLabelNameId = Module.HEAPU32[labelIdPtr / 4];
|
|
372
|
+
const uiTooltipTextNameId = Module.HEAPU32[tooltipIdPtr / 4];
|
|
373
|
+
const sampleTextNameId = Module.HEAPU32[sampleIdPtr / 4];
|
|
374
|
+
const numNamedParameters = Module.HEAPU32[numNamedParametersPtr / 4];
|
|
375
|
+
const firstParameterId = Module.HEAPU32[firstParameterIdPtr / 4];
|
|
376
|
+
const paramUiLabelNameIds = Array.from({ length: numNamedParameters }, (_, i) => firstParameterId + i);
|
|
377
|
+
names = {
|
|
378
|
+
uiLabelNameId: uiLabelNameId == HB_OT_NAME_ID_INVALID ? null : uiLabelNameId,
|
|
379
|
+
uiTooltipTextNameId: uiTooltipTextNameId == HB_OT_NAME_ID_INVALID ? null : uiTooltipTextNameId,
|
|
380
|
+
sampleTextNameId: sampleTextNameId == HB_OT_NAME_ID_INVALID ? null : sampleTextNameId,
|
|
381
|
+
paramUiLabelNameIds
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
Module.stackRestore(sp);
|
|
385
|
+
return names;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/font.ts
|
|
390
|
+
/**
|
|
391
|
+
* An object representing a {@link https://harfbuzz.github.io/harfbuzz-hb-font.html | HarfBuzz font}.
|
|
392
|
+
* A font represents a face at a specific size and with certain other
|
|
393
|
+
* parameters (pixels-per-em, variation settings) specified. Fonts are the
|
|
394
|
+
* primary input to the shaping process.
|
|
395
|
+
*/
|
|
396
|
+
var Font = class Font {
|
|
397
|
+
constructor(arg) {
|
|
398
|
+
this.drawPtrs = {
|
|
399
|
+
drawFuncsPtr: null,
|
|
400
|
+
moveToPtr: null,
|
|
401
|
+
lineToPtr: null,
|
|
402
|
+
cubicToPtr: null,
|
|
403
|
+
quadToPtr: null,
|
|
404
|
+
closePathPtr: null,
|
|
405
|
+
pathBuffer: ""
|
|
406
|
+
};
|
|
407
|
+
if (typeof arg === "number") this.ptr = exports.hb_font_reference(arg);
|
|
408
|
+
else this.ptr = exports.hb_font_create(arg.ptr);
|
|
409
|
+
const ptr = this.ptr;
|
|
410
|
+
const drawState = this.drawPtrs;
|
|
411
|
+
registry.register(this, () => {
|
|
412
|
+
exports.hb_font_destroy(ptr);
|
|
413
|
+
if (drawState.drawFuncsPtr) {
|
|
414
|
+
exports.hb_draw_funcs_destroy(drawState.drawFuncsPtr);
|
|
415
|
+
Module.removeFunction(drawState.moveToPtr);
|
|
416
|
+
Module.removeFunction(drawState.lineToPtr);
|
|
417
|
+
Module.removeFunction(drawState.cubicToPtr);
|
|
418
|
+
Module.removeFunction(drawState.quadToPtr);
|
|
419
|
+
Module.removeFunction(drawState.closePathPtr);
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Create a sub font that inherits this font's properties.
|
|
425
|
+
* @returns A new Font object representing the sub font.
|
|
426
|
+
*/
|
|
427
|
+
subFont() {
|
|
428
|
+
return new Font(exports.hb_font_create_sub_font(this.ptr));
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Return font horizontal extents.
|
|
432
|
+
* @returns Object with ascender, descender, and lineGap properties.
|
|
433
|
+
*/
|
|
434
|
+
hExtents() {
|
|
435
|
+
const sp = Module.stackSave();
|
|
436
|
+
const extentsPtr = Module.stackAlloc(48);
|
|
437
|
+
exports.hb_font_get_h_extents(this.ptr, extentsPtr);
|
|
438
|
+
const extents = {
|
|
439
|
+
ascender: Module.HEAP32[extentsPtr / 4],
|
|
440
|
+
descender: Module.HEAP32[extentsPtr / 4 + 1],
|
|
441
|
+
lineGap: Module.HEAP32[extentsPtr / 4 + 2]
|
|
442
|
+
};
|
|
443
|
+
Module.stackRestore(sp);
|
|
444
|
+
return extents;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Return font vertical extents.
|
|
448
|
+
* @returns Object with ascender, descender, and lineGap properties.
|
|
449
|
+
*/
|
|
450
|
+
vExtents() {
|
|
451
|
+
const sp = Module.stackSave();
|
|
452
|
+
const extentsPtr = Module.stackAlloc(48);
|
|
453
|
+
exports.hb_font_get_v_extents(this.ptr, extentsPtr);
|
|
454
|
+
const extents = {
|
|
455
|
+
ascender: Module.HEAP32[extentsPtr / 4],
|
|
456
|
+
descender: Module.HEAP32[extentsPtr / 4 + 1],
|
|
457
|
+
lineGap: Module.HEAP32[extentsPtr / 4 + 2]
|
|
458
|
+
};
|
|
459
|
+
Module.stackRestore(sp);
|
|
460
|
+
return extents;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Return glyph name.
|
|
464
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
465
|
+
* @returns The glyph name string.
|
|
466
|
+
*/
|
|
467
|
+
glyphName(glyphId) {
|
|
468
|
+
const sp = Module.stackSave();
|
|
469
|
+
const strSize = 256;
|
|
470
|
+
const strPtr = Module.stackAlloc(strSize);
|
|
471
|
+
exports.hb_font_glyph_to_string(this.ptr, glyphId, strPtr, strSize);
|
|
472
|
+
const name = utf8_ptr_to_string(strPtr);
|
|
473
|
+
Module.stackRestore(sp);
|
|
474
|
+
return name;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Return a glyph as an SVG path string.
|
|
478
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
479
|
+
* @returns SVG path data string.
|
|
480
|
+
*/
|
|
481
|
+
glyphToPath(glyphId) {
|
|
482
|
+
const ds = this.drawPtrs;
|
|
483
|
+
if (!ds.drawFuncsPtr) {
|
|
484
|
+
const moveTo = (dfuncs, draw_data, draw_state, to_x, to_y, user_data) => {
|
|
485
|
+
ds.pathBuffer += `M${to_x},${to_y}`;
|
|
486
|
+
};
|
|
487
|
+
const lineTo = (dfuncs, draw_data, draw_state, to_x, to_y, user_data) => {
|
|
488
|
+
ds.pathBuffer += `L${to_x},${to_y}`;
|
|
489
|
+
};
|
|
490
|
+
const cubicTo = (dfuncs, draw_data, draw_state, c1_x, c1_y, c2_x, c2_y, to_x, to_y, user_data) => {
|
|
491
|
+
ds.pathBuffer += `C${c1_x},${c1_y} ${c2_x},${c2_y} ${to_x},${to_y}`;
|
|
492
|
+
};
|
|
493
|
+
const quadTo = (dfuncs, draw_data, draw_state, c_x, c_y, to_x, to_y, user_data) => {
|
|
494
|
+
ds.pathBuffer += `Q${c_x},${c_y} ${to_x},${to_y}`;
|
|
495
|
+
};
|
|
496
|
+
const closePath = (dfuncs, draw_data, draw_state, user_data) => {
|
|
497
|
+
ds.pathBuffer += "Z";
|
|
498
|
+
};
|
|
499
|
+
ds.moveToPtr = Module.addFunction(moveTo, "viiiffi");
|
|
500
|
+
ds.lineToPtr = Module.addFunction(lineTo, "viiiffi");
|
|
501
|
+
ds.cubicToPtr = Module.addFunction(cubicTo, "viiiffffffi");
|
|
502
|
+
ds.quadToPtr = Module.addFunction(quadTo, "viiiffffi");
|
|
503
|
+
ds.closePathPtr = Module.addFunction(closePath, "viiii");
|
|
504
|
+
ds.drawFuncsPtr = exports.hb_draw_funcs_create();
|
|
505
|
+
exports.hb_draw_funcs_set_move_to_func(ds.drawFuncsPtr, ds.moveToPtr, 0, 0);
|
|
506
|
+
exports.hb_draw_funcs_set_line_to_func(ds.drawFuncsPtr, ds.lineToPtr, 0, 0);
|
|
507
|
+
exports.hb_draw_funcs_set_cubic_to_func(ds.drawFuncsPtr, ds.cubicToPtr, 0, 0);
|
|
508
|
+
exports.hb_draw_funcs_set_quadratic_to_func(ds.drawFuncsPtr, ds.quadToPtr, 0, 0);
|
|
509
|
+
exports.hb_draw_funcs_set_close_path_func(ds.drawFuncsPtr, ds.closePathPtr, 0, 0);
|
|
510
|
+
}
|
|
511
|
+
ds.pathBuffer = "";
|
|
512
|
+
exports.hb_font_draw_glyph(this.ptr, glyphId, ds.drawFuncsPtr, 0);
|
|
513
|
+
return ds.pathBuffer;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Return glyph horizontal advance.
|
|
517
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
518
|
+
* @returns The horizontal advance width.
|
|
519
|
+
*/
|
|
520
|
+
glyphHAdvance(glyphId) {
|
|
521
|
+
return exports.hb_font_get_glyph_h_advance(this.ptr, glyphId);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Return glyph vertical advance.
|
|
525
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
526
|
+
* @returns The vertical advance height.
|
|
527
|
+
*/
|
|
528
|
+
glyphVAdvance(glyphId) {
|
|
529
|
+
return exports.hb_font_get_glyph_v_advance(this.ptr, glyphId);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Return glyph horizontal origin.
|
|
533
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
534
|
+
* @returns [x, y] origin coordinates, or null if not available.
|
|
535
|
+
*/
|
|
536
|
+
glyphHOrigin(glyphId) {
|
|
537
|
+
const sp = Module.stackSave();
|
|
538
|
+
const xPtr = Module.stackAlloc(4);
|
|
539
|
+
const yPtr = Module.stackAlloc(4);
|
|
540
|
+
let origin = null;
|
|
541
|
+
if (exports.hb_font_get_glyph_h_origin(this.ptr, glyphId, xPtr, yPtr)) origin = [Module.HEAP32[xPtr / 4], Module.HEAP32[yPtr / 4]];
|
|
542
|
+
Module.stackRestore(sp);
|
|
543
|
+
return origin;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Return glyph vertical origin.
|
|
547
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
548
|
+
* @returns [x, y] origin coordinates, or null if not available.
|
|
549
|
+
*/
|
|
550
|
+
glyphVOrigin(glyphId) {
|
|
551
|
+
const sp = Module.stackSave();
|
|
552
|
+
const xPtr = Module.stackAlloc(4);
|
|
553
|
+
const yPtr = Module.stackAlloc(4);
|
|
554
|
+
let origin = null;
|
|
555
|
+
if (exports.hb_font_get_glyph_v_origin(this.ptr, glyphId, xPtr, yPtr)) origin = [Module.HEAP32[xPtr / 4], Module.HEAP32[yPtr / 4]];
|
|
556
|
+
Module.stackRestore(sp);
|
|
557
|
+
return origin;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Return glyph extents.
|
|
561
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
562
|
+
* @returns An object with xBearing, yBearing, width, and height, or null.
|
|
563
|
+
*/
|
|
564
|
+
glyphExtents(glyphId) {
|
|
565
|
+
const sp = Module.stackSave();
|
|
566
|
+
const extentsPtr = Module.stackAlloc(16);
|
|
567
|
+
let extents = null;
|
|
568
|
+
if (exports.hb_font_get_glyph_extents(this.ptr, glyphId, extentsPtr)) extents = {
|
|
569
|
+
xBearing: Module.HEAP32[extentsPtr / 4],
|
|
570
|
+
yBearing: Module.HEAP32[extentsPtr / 4 + 1],
|
|
571
|
+
width: Module.HEAP32[extentsPtr / 4 + 2],
|
|
572
|
+
height: Module.HEAP32[extentsPtr / 4 + 3]
|
|
573
|
+
};
|
|
574
|
+
Module.stackRestore(sp);
|
|
575
|
+
return extents;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Return glyph ID from name.
|
|
579
|
+
* @param name Name of the requested glyph in the font.
|
|
580
|
+
* @returns The glyph ID, or null if not found.
|
|
581
|
+
*/
|
|
582
|
+
glyphFromName(name) {
|
|
583
|
+
const sp = Module.stackSave();
|
|
584
|
+
const glyphIdPtr = Module.stackAlloc(4);
|
|
585
|
+
const namePtr = string_to_utf8_ptr(name);
|
|
586
|
+
let glyphId = null;
|
|
587
|
+
if (exports.hb_font_get_glyph_from_name(this.ptr, namePtr.ptr, namePtr.length, glyphIdPtr)) glyphId = Module.HEAPU32[glyphIdPtr / 4];
|
|
588
|
+
namePtr.free();
|
|
589
|
+
Module.stackRestore(sp);
|
|
590
|
+
return glyphId;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Return a glyph as a JSON path string
|
|
594
|
+
* based on format described on https://svgwg.org/specs/paths/#InterfaceSVGPathSegment
|
|
595
|
+
* @param glyphId ID of the requested glyph in the font.
|
|
596
|
+
* @returns An array of path segment objects with type and values.
|
|
597
|
+
*/
|
|
598
|
+
glyphToJson(glyphId) {
|
|
599
|
+
return this.glyphToPath(glyphId).replace(/([MLQCZ])/g, "|$1 ").split("|").filter((x) => x.length).map((x) => {
|
|
600
|
+
const [type, ...values] = x.split(/[ ,]/g).filter((s) => s.length);
|
|
601
|
+
return {
|
|
602
|
+
type,
|
|
603
|
+
values: values.map(Number)
|
|
604
|
+
};
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Set the font's scale factor, affecting the position values returned from
|
|
609
|
+
* shaping.
|
|
610
|
+
* @param xScale Units to scale in the X dimension.
|
|
611
|
+
* @param yScale Units to scale in the Y dimension.
|
|
612
|
+
*/
|
|
613
|
+
setScale(xScale, yScale) {
|
|
614
|
+
exports.hb_font_set_scale(this.ptr, xScale, yScale);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Set the font's variations.
|
|
618
|
+
* @param variations Dictionary of variations to set.
|
|
619
|
+
*/
|
|
620
|
+
setVariations(variations) {
|
|
621
|
+
const entries = Object.entries(variations);
|
|
622
|
+
const vars = exports.malloc(8 * entries.length);
|
|
623
|
+
entries.forEach(([tag, value], i) => {
|
|
624
|
+
Module.HEAPU32[vars / 4 + i * 2 + 0] = hb_tag(tag);
|
|
625
|
+
Module.HEAPF32[vars / 4 + i * 2 + 1] = value;
|
|
626
|
+
});
|
|
627
|
+
exports.hb_font_set_variations(this.ptr, vars, entries.length);
|
|
628
|
+
exports.free(vars);
|
|
629
|
+
}
|
|
630
|
+
/** Set the font's font functions. */
|
|
631
|
+
setFuncs(fontFuncs) {
|
|
632
|
+
exports.hb_font_set_funcs(this.ptr, fontFuncs.ptr);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/font-funcs.ts
|
|
637
|
+
/**
|
|
638
|
+
* An object representing {@link https://harfbuzz.github.io/harfbuzz-hb-font.html | HarfBuzz font functions}.
|
|
639
|
+
* Font functions define the methods used by a Font for lower-level queries
|
|
640
|
+
* like glyph advances and extents. HarfBuzz provides built-in default
|
|
641
|
+
* implementations which can be selectively overridden.
|
|
642
|
+
*/
|
|
643
|
+
var FontFuncs = class {
|
|
644
|
+
constructor() {
|
|
645
|
+
this.ptr = exports.hb_font_funcs_create();
|
|
646
|
+
track(this, exports.hb_font_funcs_destroy);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Set the font's glyph extents function.
|
|
650
|
+
* @param func The callback receives a Font and glyph ID. It should return
|
|
651
|
+
* an object with xBearing, yBearing, width, and height, or null on failure.
|
|
652
|
+
*/
|
|
653
|
+
setGlyphExtentsFunc(func) {
|
|
654
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, glyph, extentsPtr, user_data) => {
|
|
655
|
+
const extents = func(new Font(fontPtr), glyph);
|
|
656
|
+
if (extents) {
|
|
657
|
+
Module.HEAP32[extentsPtr / 4] = extents.xBearing;
|
|
658
|
+
Module.HEAP32[extentsPtr / 4 + 1] = extents.yBearing;
|
|
659
|
+
Module.HEAP32[extentsPtr / 4 + 2] = extents.width;
|
|
660
|
+
Module.HEAP32[extentsPtr / 4 + 3] = extents.height;
|
|
661
|
+
return 1;
|
|
662
|
+
}
|
|
663
|
+
return 0;
|
|
664
|
+
}, "ippipp");
|
|
665
|
+
exports.hb_font_funcs_set_glyph_extents_func(this.ptr, funcPtr, 0, 0);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Set the font's glyph from name function.
|
|
669
|
+
* @param func The callback receives a Font and glyph name. It should return
|
|
670
|
+
* the glyph ID, or null on failure.
|
|
671
|
+
*/
|
|
672
|
+
setGlyphFromNameFunc(func) {
|
|
673
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, namePtr, len, glyphPtr, user_data) => {
|
|
674
|
+
const glyph = func(new Font(fontPtr), utf8_ptr_to_string(namePtr, len));
|
|
675
|
+
if (glyph) {
|
|
676
|
+
Module.HEAPU32[glyphPtr / 4] = glyph;
|
|
677
|
+
return 1;
|
|
678
|
+
}
|
|
679
|
+
return 0;
|
|
680
|
+
}, "ipppipp");
|
|
681
|
+
exports.hb_font_funcs_set_glyph_from_name_func(this.ptr, funcPtr, 0, 0);
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Set the font's glyph horizontal advance function.
|
|
685
|
+
* @param func The callback receives a Font and glyph ID. It should return
|
|
686
|
+
* the horizontal advance of the glyph.
|
|
687
|
+
*/
|
|
688
|
+
setGlyphHAdvanceFunc(func) {
|
|
689
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, glyph, user_data) => {
|
|
690
|
+
return func(new Font(fontPtr), glyph);
|
|
691
|
+
}, "ippip");
|
|
692
|
+
exports.hb_font_funcs_set_glyph_h_advance_func(this.ptr, funcPtr, 0, 0);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Set the font's glyph vertical advance function.
|
|
696
|
+
* @param func The callback receives a Font and glyph ID. It should return
|
|
697
|
+
* the vertical advance of the glyph.
|
|
698
|
+
*/
|
|
699
|
+
setGlyphVAdvanceFunc(func) {
|
|
700
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, glyph, user_data) => {
|
|
701
|
+
return func(new Font(fontPtr), glyph);
|
|
702
|
+
}, "ippip");
|
|
703
|
+
exports.hb_font_funcs_set_glyph_v_advance_func(this.ptr, funcPtr, 0, 0);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Set the font's glyph horizontal origin function.
|
|
707
|
+
* @param func The callback receives a Font and glyph ID. It should return
|
|
708
|
+
* the [x, y] horizontal origin of the glyph, or null on failure.
|
|
709
|
+
*/
|
|
710
|
+
setGlyphHOriginFunc(func) {
|
|
711
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, glyph, xPtr, yPtr, user_data) => {
|
|
712
|
+
const origin = func(new Font(fontPtr), glyph);
|
|
713
|
+
if (origin) {
|
|
714
|
+
Module.HEAP32[xPtr / 4] = origin[0];
|
|
715
|
+
Module.HEAP32[yPtr / 4] = origin[1];
|
|
716
|
+
return 1;
|
|
717
|
+
}
|
|
718
|
+
return 0;
|
|
719
|
+
}, "ippippp");
|
|
720
|
+
exports.hb_font_funcs_set_glyph_h_origin_func(this.ptr, funcPtr, 0, 0);
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Set the font's glyph vertical origin function.
|
|
724
|
+
* @param func The callback receives a Font and glyph ID. It should return
|
|
725
|
+
* the [x, y] vertical origin of the glyph, or null on failure.
|
|
726
|
+
*/
|
|
727
|
+
setGlyphVOriginFunc(func) {
|
|
728
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, glyph, xPtr, yPtr, user_data) => {
|
|
729
|
+
const origin = func(new Font(fontPtr), glyph);
|
|
730
|
+
if (origin) {
|
|
731
|
+
Module.HEAP32[xPtr / 4] = origin[0];
|
|
732
|
+
Module.HEAP32[yPtr / 4] = origin[1];
|
|
733
|
+
return 1;
|
|
734
|
+
}
|
|
735
|
+
return 0;
|
|
736
|
+
}, "ippippp");
|
|
737
|
+
exports.hb_font_funcs_set_glyph_v_origin_func(this.ptr, funcPtr, 0, 0);
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Set the font's glyph horizontal kerning function.
|
|
741
|
+
* @param func The callback receives a Font, first glyph ID, and second glyph ID.
|
|
742
|
+
* It should return the horizontal kerning of the glyphs.
|
|
743
|
+
*/
|
|
744
|
+
setGlyphHKerningFunc(func) {
|
|
745
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, firstGlyph, secondGlyph, user_data) => {
|
|
746
|
+
return func(new Font(fontPtr), firstGlyph, secondGlyph);
|
|
747
|
+
}, "ippiip");
|
|
748
|
+
exports.hb_font_funcs_set_glyph_h_kerning_func(this.ptr, funcPtr, 0, 0);
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Set the font's glyph name function.
|
|
752
|
+
* @param func The callback receives a Font and glyph ID. It should return
|
|
753
|
+
* the name of the glyph, or null on failure.
|
|
754
|
+
*/
|
|
755
|
+
setGlyphNameFunc(func) {
|
|
756
|
+
const utf8Encoder = new TextEncoder();
|
|
757
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, glyph, namePtr, size, user_data) => {
|
|
758
|
+
const name = func(new Font(fontPtr), glyph);
|
|
759
|
+
if (name) {
|
|
760
|
+
utf8Encoder.encodeInto(name, Module.HEAPU8.subarray(namePtr, namePtr + size));
|
|
761
|
+
return 1;
|
|
762
|
+
}
|
|
763
|
+
return 0;
|
|
764
|
+
}, "ippipip");
|
|
765
|
+
exports.hb_font_funcs_set_glyph_name_func(this.ptr, funcPtr, 0, 0);
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Set the font's nominal glyph function.
|
|
769
|
+
* @param func The callback receives a Font and unicode code point. It should
|
|
770
|
+
* return the nominal glyph of the unicode, or null on failure.
|
|
771
|
+
*/
|
|
772
|
+
setNominalGlyphFunc(func) {
|
|
773
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, unicode, glyphPtr, user_data) => {
|
|
774
|
+
const glyph = func(new Font(fontPtr), unicode);
|
|
775
|
+
if (glyph) {
|
|
776
|
+
Module.HEAPU32[glyphPtr / 4] = glyph;
|
|
777
|
+
return 1;
|
|
778
|
+
}
|
|
779
|
+
return 0;
|
|
780
|
+
}, "ippipp");
|
|
781
|
+
exports.hb_font_funcs_set_nominal_glyph_func(this.ptr, funcPtr, 0, 0);
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Set the font's variation glyph function.
|
|
785
|
+
* @param func The callback receives a Font, unicode code point, and variation
|
|
786
|
+
* selector. It should return the variation glyph, or null on failure.
|
|
787
|
+
*/
|
|
788
|
+
setVariationGlyphFunc(func) {
|
|
789
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, unicode, variationSelector, glyphPtr, user_data) => {
|
|
790
|
+
const glyph = func(new Font(fontPtr), unicode, variationSelector);
|
|
791
|
+
if (glyph) {
|
|
792
|
+
Module.HEAPU32[glyphPtr / 4] = glyph;
|
|
793
|
+
return 1;
|
|
794
|
+
}
|
|
795
|
+
return 0;
|
|
796
|
+
}, "ippiipp");
|
|
797
|
+
exports.hb_font_funcs_set_variation_glyph_func(this.ptr, funcPtr, 0, 0);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Set the font's horizontal extents function.
|
|
801
|
+
* @param func The callback receives a Font. It should return an object with
|
|
802
|
+
* ascender, descender, and lineGap, or null on failure.
|
|
803
|
+
*/
|
|
804
|
+
setFontHExtentsFunc(func) {
|
|
805
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, extentsPtr, user_data) => {
|
|
806
|
+
const extents = func(new Font(fontPtr));
|
|
807
|
+
if (extents) {
|
|
808
|
+
Module.HEAP32[extentsPtr / 4] = extents.ascender;
|
|
809
|
+
Module.HEAP32[extentsPtr / 4 + 1] = extents.descender;
|
|
810
|
+
Module.HEAP32[extentsPtr / 4 + 2] = extents.lineGap;
|
|
811
|
+
return 1;
|
|
812
|
+
}
|
|
813
|
+
return 0;
|
|
814
|
+
}, "ipppp");
|
|
815
|
+
exports.hb_font_funcs_set_font_h_extents_func(this.ptr, funcPtr, 0, 0);
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Set the font's vertical extents function.
|
|
819
|
+
* @param func The callback receives a Font. It should return an object with
|
|
820
|
+
* ascender, descender, and lineGap, or null on failure.
|
|
821
|
+
*/
|
|
822
|
+
setFontVExtentsFunc(func) {
|
|
823
|
+
const funcPtr = Module.addFunction((fontPtr, font_data, extentsPtr, user_data) => {
|
|
824
|
+
const extents = func(new Font(fontPtr));
|
|
825
|
+
if (extents) {
|
|
826
|
+
Module.HEAP32[extentsPtr / 4] = extents.ascender;
|
|
827
|
+
Module.HEAP32[extentsPtr / 4 + 1] = extents.descender;
|
|
828
|
+
Module.HEAP32[extentsPtr / 4 + 2] = extents.lineGap;
|
|
829
|
+
return 1;
|
|
830
|
+
}
|
|
831
|
+
return 0;
|
|
832
|
+
}, "ipppp");
|
|
833
|
+
exports.hb_font_funcs_set_font_v_extents_func(this.ptr, funcPtr, 0, 0);
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/buffer.ts
|
|
838
|
+
const BufferContentType = {
|
|
839
|
+
INVALID: 0,
|
|
840
|
+
UNICODE: 1,
|
|
841
|
+
GLYPHS: 2
|
|
842
|
+
};
|
|
843
|
+
const BufferSerializeFlag = {
|
|
844
|
+
DEFAULT: 0,
|
|
845
|
+
NO_CLUSTERS: 1,
|
|
846
|
+
NO_POSITIONS: 2,
|
|
847
|
+
NO_GLYPH_NAMES: 4,
|
|
848
|
+
GLYPH_EXTENTS: 8,
|
|
849
|
+
GLYPH_FLAGS: 16,
|
|
850
|
+
NO_ADVANCES: 32,
|
|
851
|
+
DEFINED: 63
|
|
852
|
+
};
|
|
853
|
+
const BufferFlag = {
|
|
854
|
+
DEFAULT: 0,
|
|
855
|
+
BOT: 1,
|
|
856
|
+
EOT: 2,
|
|
857
|
+
PRESERVE_DEFAULT_IGNORABLES: 4,
|
|
858
|
+
REMOVE_DEFAULT_IGNORABLES: 8,
|
|
859
|
+
DO_NOT_INSERT_DOTTED_CIRCLE: 16,
|
|
860
|
+
VERIFY: 32,
|
|
861
|
+
PRODUCE_UNSAFE_TO_CONCAT: 64,
|
|
862
|
+
PRODUCE_SAFE_TO_INSERT_TATWEEL: 128,
|
|
863
|
+
DEFINED: 255
|
|
864
|
+
};
|
|
865
|
+
const Direction = {
|
|
866
|
+
INVALID: 0,
|
|
867
|
+
LTR: 4,
|
|
868
|
+
RTL: 5,
|
|
869
|
+
TTB: 6,
|
|
870
|
+
BTT: 7
|
|
871
|
+
};
|
|
872
|
+
const BufferSerializeFormat = {
|
|
873
|
+
INVALID: 0,
|
|
874
|
+
TEXT: hb_tag("TEXT"),
|
|
875
|
+
JSON: hb_tag("JSON")
|
|
876
|
+
};
|
|
877
|
+
/**
|
|
878
|
+
* An object representing a {@link https://harfbuzz.github.io/harfbuzz-hb-buffer.html | HarfBuzz buffer}.
|
|
879
|
+
* A buffer holds the input text and its properties before shaping, and the
|
|
880
|
+
* output glyphs and their information after shaping.
|
|
881
|
+
*/
|
|
882
|
+
var Buffer = class Buffer {
|
|
883
|
+
constructor(existingPtr) {
|
|
884
|
+
if (existingPtr !== void 0) this.ptr = exports.hb_buffer_reference(existingPtr);
|
|
885
|
+
else this.ptr = exports.hb_buffer_create();
|
|
886
|
+
track(this, exports.hb_buffer_destroy);
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Add text to the buffer.
|
|
890
|
+
* @param text Text to be added to the buffer.
|
|
891
|
+
* @param itemOffset The offset of the first character to add to the buffer.
|
|
892
|
+
* @param itemLength The number of characters to add to the buffer, or null for the end of text.
|
|
893
|
+
*/
|
|
894
|
+
addText(text, itemOffset = 0, itemLength = null) {
|
|
895
|
+
const str = string_to_utf16_ptr(text);
|
|
896
|
+
if (itemLength == null) itemLength = str.length;
|
|
897
|
+
exports.hb_buffer_add_utf16(this.ptr, str.ptr, str.length, itemOffset, itemLength);
|
|
898
|
+
str.free();
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Add code points to the buffer.
|
|
902
|
+
* @param codePoints Array of code points to be added to the buffer.
|
|
903
|
+
* @param itemOffset The offset of the first code point to add to the buffer.
|
|
904
|
+
* @param itemLength The number of code points to add to the buffer, or null for the end of the array.
|
|
905
|
+
*/
|
|
906
|
+
addCodePoints(codePoints, itemOffset = 0, itemLength = null) {
|
|
907
|
+
const codePointsPtr = exports.malloc(codePoints.length * 4);
|
|
908
|
+
Module.HEAPU32.subarray(codePointsPtr / 4, codePointsPtr / 4 + codePoints.length).set(codePoints);
|
|
909
|
+
if (itemLength == null) itemLength = codePoints.length;
|
|
910
|
+
exports.hb_buffer_add_codepoints(this.ptr, codePointsPtr, codePoints.length, itemOffset, itemLength);
|
|
911
|
+
exports.free(codePointsPtr);
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Set buffer script, language and direction.
|
|
915
|
+
*
|
|
916
|
+
* This needs to be done before shaping.
|
|
917
|
+
*/
|
|
918
|
+
guessSegmentProperties() {
|
|
919
|
+
exports.hb_buffer_guess_segment_properties(this.ptr);
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Set buffer direction explicitly.
|
|
923
|
+
* @param dir A {@link Direction} value.
|
|
924
|
+
*/
|
|
925
|
+
setDirection(dir) {
|
|
926
|
+
exports.hb_buffer_set_direction(this.ptr, dir);
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Set buffer flags explicitly.
|
|
930
|
+
* @param flags A combination of {@link BufferFlag} values (OR them together).
|
|
931
|
+
*/
|
|
932
|
+
setFlags(flags) {
|
|
933
|
+
exports.hb_buffer_set_flags(this.ptr, flags);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Set buffer language explicitly.
|
|
937
|
+
* @param language The buffer language
|
|
938
|
+
*/
|
|
939
|
+
setLanguage(language) {
|
|
940
|
+
const str = string_to_ascii_ptr(language);
|
|
941
|
+
exports.hb_buffer_set_language(this.ptr, exports.hb_language_from_string(str.ptr, -1));
|
|
942
|
+
str.free();
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Set buffer script explicitly.
|
|
946
|
+
* @param script The buffer script
|
|
947
|
+
*/
|
|
948
|
+
setScript(script) {
|
|
949
|
+
const str = string_to_ascii_ptr(script);
|
|
950
|
+
exports.hb_buffer_set_script(this.ptr, exports.hb_script_from_string(str.ptr, -1));
|
|
951
|
+
str.free();
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Set the HarfBuzz clustering level.
|
|
955
|
+
*
|
|
956
|
+
* Affects the cluster values returned from shaping.
|
|
957
|
+
* @param level Clustering level. See the HarfBuzz manual chapter on Clusters.
|
|
958
|
+
*/
|
|
959
|
+
setClusterLevel(level) {
|
|
960
|
+
exports.hb_buffer_set_cluster_level(this.ptr, level);
|
|
961
|
+
}
|
|
962
|
+
/** Reset the buffer to its initial status. */
|
|
963
|
+
reset() {
|
|
964
|
+
exports.hb_buffer_reset(this.ptr);
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Similar to reset(), but does not clear the Unicode functions and the
|
|
968
|
+
* replacement code point.
|
|
969
|
+
*/
|
|
970
|
+
clearContents() {
|
|
971
|
+
exports.hb_buffer_clear_contents(this.ptr);
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Set message func.
|
|
975
|
+
* @param func The function to set. It receives the buffer, font, and message
|
|
976
|
+
* string as arguments. Returning false will skip this shaping step and move
|
|
977
|
+
* to the next one.
|
|
978
|
+
*/
|
|
979
|
+
setMessageFunc(func) {
|
|
980
|
+
const traceFunc = (bufferPtr, fontPtr, messagePtr, user_data) => {
|
|
981
|
+
const message = utf8_ptr_to_string(messagePtr);
|
|
982
|
+
return func(new Buffer(bufferPtr), new Font(fontPtr), message) ? 1 : 0;
|
|
983
|
+
};
|
|
984
|
+
const traceFuncPtr = Module.addFunction(traceFunc, "iiiii");
|
|
985
|
+
exports.hb_buffer_set_message_func(this.ptr, traceFuncPtr, 0, 0);
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Get the the number of items in the buffer.
|
|
989
|
+
* @returns The buffer length.
|
|
990
|
+
*/
|
|
991
|
+
getLength() {
|
|
992
|
+
return exports.hb_buffer_get_length(this.ptr);
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Get the glyph information from the buffer.
|
|
996
|
+
* @returns An array of {@link GlyphInfo} objects.
|
|
997
|
+
*/
|
|
998
|
+
getGlyphInfos() {
|
|
999
|
+
const infosPtr32 = exports.hb_buffer_get_glyph_infos(this.ptr, 0) / 4;
|
|
1000
|
+
const infosArray = Module.HEAPU32.subarray(infosPtr32, infosPtr32 + this.getLength() * 5);
|
|
1001
|
+
const infos = [];
|
|
1002
|
+
for (let i = 0; i < infosArray.length; i += 5) infos.push({
|
|
1003
|
+
codepoint: infosArray[i],
|
|
1004
|
+
cluster: infosArray[i + 2]
|
|
1005
|
+
});
|
|
1006
|
+
return infos;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Get the glyph positions from the buffer.
|
|
1010
|
+
* @returns An array of {@link GlyphPosition} objects.
|
|
1011
|
+
*/
|
|
1012
|
+
getGlyphPositions() {
|
|
1013
|
+
const positionsPtr32 = exports.hb_buffer_get_glyph_positions(this.ptr, 0) / 4;
|
|
1014
|
+
if (positionsPtr32 == 0) return [];
|
|
1015
|
+
const positionsArray = Module.HEAP32.subarray(positionsPtr32, positionsPtr32 + this.getLength() * 5);
|
|
1016
|
+
const positions = [];
|
|
1017
|
+
for (let i = 0; i < positionsArray.length; i += 5) positions.push({
|
|
1018
|
+
xAdvance: positionsArray[i],
|
|
1019
|
+
yAdvance: positionsArray[i + 1],
|
|
1020
|
+
xOffset: positionsArray[i + 2],
|
|
1021
|
+
yOffset: positionsArray[i + 3]
|
|
1022
|
+
});
|
|
1023
|
+
return positions;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Get the glyph information and positions from the buffer.
|
|
1027
|
+
* @returns The glyph information and positions.
|
|
1028
|
+
*
|
|
1029
|
+
* The glyph information is returned as an array of objects with the
|
|
1030
|
+
* properties from getGlyphInfos and getGlyphPositions combined.
|
|
1031
|
+
*/
|
|
1032
|
+
getGlyphInfosAndPositions() {
|
|
1033
|
+
const infosPtr32 = exports.hb_buffer_get_glyph_infos(this.ptr, 0) / 4;
|
|
1034
|
+
const infosArray = Module.HEAPU32.subarray(infosPtr32, infosPtr32 + this.getLength() * 5);
|
|
1035
|
+
const positionsPtr32 = exports.hb_buffer_get_glyph_positions(this.ptr, 0) / 4;
|
|
1036
|
+
const positionsArray = positionsPtr32 ? Module.HEAP32.subarray(positionsPtr32, positionsPtr32 + this.getLength() * 5) : null;
|
|
1037
|
+
const out = [];
|
|
1038
|
+
for (let i = 0; i < infosArray.length; i += 5) {
|
|
1039
|
+
const info = {
|
|
1040
|
+
codepoint: infosArray[i],
|
|
1041
|
+
cluster: infosArray[i + 2]
|
|
1042
|
+
};
|
|
1043
|
+
for (const [name, idx] of [
|
|
1044
|
+
["mask", 1],
|
|
1045
|
+
["var1", 3],
|
|
1046
|
+
["var2", 4]
|
|
1047
|
+
]) Object.defineProperty(info, name, {
|
|
1048
|
+
value: infosArray[i + idx],
|
|
1049
|
+
enumerable: false
|
|
1050
|
+
});
|
|
1051
|
+
if (positionsArray) {
|
|
1052
|
+
info.xAdvance = positionsArray[i];
|
|
1053
|
+
info.yAdvance = positionsArray[i + 1];
|
|
1054
|
+
info.xOffset = positionsArray[i + 2];
|
|
1055
|
+
info.yOffset = positionsArray[i + 3];
|
|
1056
|
+
Object.defineProperty(info, "var", {
|
|
1057
|
+
value: positionsArray[i + 4],
|
|
1058
|
+
enumerable: false
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
out.push(info);
|
|
1062
|
+
}
|
|
1063
|
+
return out;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Update the glyph positions in the buffer.
|
|
1067
|
+
* WARNING: Do not use unless you know what you are doing.
|
|
1068
|
+
*/
|
|
1069
|
+
updateGlyphPositions(positions) {
|
|
1070
|
+
const positionsPtr32 = exports.hb_buffer_get_glyph_positions(this.ptr, 0) / 4;
|
|
1071
|
+
if (positionsPtr32 == 0) return;
|
|
1072
|
+
const len = Math.min(positions.length, this.getLength());
|
|
1073
|
+
const positionsArray = Module.HEAP32.subarray(positionsPtr32, positionsPtr32 + len * 5);
|
|
1074
|
+
for (let i = 0; i < len; i++) {
|
|
1075
|
+
positionsArray[i * 5] = positions[i].xAdvance;
|
|
1076
|
+
positionsArray[i * 5 + 1] = positions[i].yAdvance;
|
|
1077
|
+
positionsArray[i * 5 + 2] = positions[i].xOffset;
|
|
1078
|
+
positionsArray[i * 5 + 3] = positions[i].yOffset;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Serialize the buffer contents to a string.
|
|
1083
|
+
* @param font The font to use for serialization.
|
|
1084
|
+
* @param start The starting index of the glyphs to serialize.
|
|
1085
|
+
* @param end The ending index of the glyphs to serialize.
|
|
1086
|
+
* @param format The {@link BufferSerializeFormat} to serialize the buffer contents to.
|
|
1087
|
+
* @param flags A combination of {@link BufferSerializeFlag} values (OR them together).
|
|
1088
|
+
*
|
|
1089
|
+
* @returns The serialized buffer contents.
|
|
1090
|
+
*/
|
|
1091
|
+
serialize(font, start = 0, end, format = BufferSerializeFormat.TEXT, flags = 0) {
|
|
1092
|
+
const sp = Module.stackSave();
|
|
1093
|
+
const endPos = end ?? this.getLength();
|
|
1094
|
+
const bufLen = 32 * 1024;
|
|
1095
|
+
const bufPtr = exports.malloc(bufLen);
|
|
1096
|
+
const bufConsumedPtr = Module.stackAlloc(4);
|
|
1097
|
+
let result = "";
|
|
1098
|
+
while (start < endPos) {
|
|
1099
|
+
start += exports.hb_buffer_serialize(this.ptr, start, endPos, bufPtr, bufLen, bufConsumedPtr, font ? font.ptr : 0, format, flags);
|
|
1100
|
+
const bufConsumed = Module.HEAPU32[bufConsumedPtr / 4];
|
|
1101
|
+
if (bufConsumed == 0) break;
|
|
1102
|
+
result += utf8_ptr_to_string(bufPtr, bufConsumed);
|
|
1103
|
+
}
|
|
1104
|
+
exports.free(bufPtr);
|
|
1105
|
+
Module.stackRestore(sp);
|
|
1106
|
+
return result;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Return the buffer content type.
|
|
1110
|
+
*
|
|
1111
|
+
* @returns The buffer content type as a {@link BufferContentType} value.
|
|
1112
|
+
*/
|
|
1113
|
+
getContentType() {
|
|
1114
|
+
return exports.hb_buffer_get_content_type(this.ptr);
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Return the buffer contents as a JSON object.
|
|
1118
|
+
* @returns An array of {@link JsonGlyph} objects.
|
|
1119
|
+
*/
|
|
1120
|
+
json() {
|
|
1121
|
+
const buf = this.serialize(null, 0, null, BufferSerializeFormat.JSON, BufferSerializeFlag.NO_GLYPH_NAMES | BufferSerializeFlag.GLYPH_FLAGS);
|
|
1122
|
+
return JSON.parse(buf);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
//#endregion
|
|
1126
|
+
//#region src/shape.ts
|
|
1127
|
+
const TracePhase = {
|
|
1128
|
+
DONT_STOP: 0,
|
|
1129
|
+
GSUB: 1,
|
|
1130
|
+
GPOS: 2
|
|
1131
|
+
};
|
|
1132
|
+
/**
|
|
1133
|
+
* Shape a buffer with a given font.
|
|
1134
|
+
*
|
|
1135
|
+
* Converts the Unicode text in the buffer into positioned glyphs.
|
|
1136
|
+
* The buffer is modified in place.
|
|
1137
|
+
*
|
|
1138
|
+
* @param font The Font to shape with.
|
|
1139
|
+
* @param buffer The Buffer containing text to shape, suitably prepared
|
|
1140
|
+
* (text added, segment properties set).
|
|
1141
|
+
* @param features A string of comma-separated OpenType features to apply.
|
|
1142
|
+
*/
|
|
1143
|
+
function shape(font, buffer, features) {
|
|
1144
|
+
let featuresPtr = 0;
|
|
1145
|
+
let featuresLen = 0;
|
|
1146
|
+
if (features) {
|
|
1147
|
+
const featureList = features.split(",");
|
|
1148
|
+
featuresPtr = exports.malloc(16 * featureList.length);
|
|
1149
|
+
featureList.forEach((feature) => {
|
|
1150
|
+
const str = string_to_ascii_ptr(feature);
|
|
1151
|
+
if (exports.hb_feature_from_string(str.ptr, -1, featuresPtr + featuresLen * 16)) featuresLen++;
|
|
1152
|
+
str.free();
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
exports.hb_shape(font.ptr, buffer.ptr, featuresPtr, featuresLen);
|
|
1156
|
+
if (featuresPtr) exports.free(featuresPtr);
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Shape a buffer with a given font, returning a JSON trace of the shaping process.
|
|
1160
|
+
*
|
|
1161
|
+
* This function supports "partial shaping", where the shaping process is
|
|
1162
|
+
* terminated after a given lookup ID is reached.
|
|
1163
|
+
*
|
|
1164
|
+
* @param font The Font to shape with.
|
|
1165
|
+
* @param buffer The Buffer containing text to shape, suitably prepared.
|
|
1166
|
+
* @param features A string of comma-separated OpenType features to apply.
|
|
1167
|
+
* @param stop_at A lookup ID at which to terminate shaping.
|
|
1168
|
+
* @param stop_phase The {@link TracePhase} at which to stop shaping.
|
|
1169
|
+
* @returns An array of trace entries, each with a message, serialized glyphs, and phase info.
|
|
1170
|
+
*/
|
|
1171
|
+
function shapeWithTrace(font, buffer, features, stop_at, stop_phase) {
|
|
1172
|
+
const trace = [];
|
|
1173
|
+
let currentPhase = TracePhase.DONT_STOP;
|
|
1174
|
+
let stopping = false;
|
|
1175
|
+
buffer.setMessageFunc((buffer, font, message) => {
|
|
1176
|
+
if (message.startsWith("start table GSUB")) currentPhase = TracePhase.GSUB;
|
|
1177
|
+
else if (message.startsWith("start table GPOS")) currentPhase = TracePhase.GPOS;
|
|
1178
|
+
if (currentPhase != stop_phase) stopping = false;
|
|
1179
|
+
if (stop_phase != TracePhase.DONT_STOP && currentPhase == stop_phase && message.startsWith("end lookup " + stop_at)) stopping = true;
|
|
1180
|
+
if (stopping) return false;
|
|
1181
|
+
const traceBuf = buffer.serialize(font, 0, null, BufferSerializeFormat.JSON, BufferSerializeFlag.NO_GLYPH_NAMES);
|
|
1182
|
+
trace.push({
|
|
1183
|
+
m: message,
|
|
1184
|
+
t: JSON.parse(traceBuf),
|
|
1185
|
+
glyphs: buffer.getContentType() == BufferContentType.GLYPHS
|
|
1186
|
+
});
|
|
1187
|
+
return true;
|
|
1188
|
+
});
|
|
1189
|
+
shape(font, buffer, features);
|
|
1190
|
+
return trace;
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Return the HarfBuzz version.
|
|
1194
|
+
* @returns An object with major, minor, and micro version numbers.
|
|
1195
|
+
*/
|
|
1196
|
+
function version() {
|
|
1197
|
+
const sp = Module.stackSave();
|
|
1198
|
+
const versionPtr = Module.stackAlloc(12);
|
|
1199
|
+
exports.hb_version(versionPtr, versionPtr + 4, versionPtr + 8);
|
|
1200
|
+
const ver = {
|
|
1201
|
+
major: Module.HEAPU32[versionPtr / 4],
|
|
1202
|
+
minor: Module.HEAPU32[(versionPtr + 4) / 4],
|
|
1203
|
+
micro: Module.HEAPU32[(versionPtr + 8) / 4]
|
|
1204
|
+
};
|
|
1205
|
+
Module.stackRestore(sp);
|
|
1206
|
+
return ver;
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Return the HarfBuzz version as a string.
|
|
1210
|
+
* @returns A version string in the form "major.minor.micro".
|
|
1211
|
+
*/
|
|
1212
|
+
function versionString() {
|
|
1213
|
+
return utf8_ptr_to_string(exports.hb_version_string());
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Convert an OpenType script tag to HarfBuzz script.
|
|
1217
|
+
* @param tag The tag to convert.
|
|
1218
|
+
* @returns The script.
|
|
1219
|
+
*/
|
|
1220
|
+
function otTagToScript(tag) {
|
|
1221
|
+
const hbTag = hb_tag(tag);
|
|
1222
|
+
return hb_untag(exports.hb_ot_tag_to_script(hbTag));
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Convert an OpenType language tag to HarfBuzz language.
|
|
1226
|
+
* @param tag The tag to convert.
|
|
1227
|
+
* @returns The language.
|
|
1228
|
+
*/
|
|
1229
|
+
function otTagToLanguage(tag) {
|
|
1230
|
+
const hbTag = hb_tag(tag);
|
|
1231
|
+
return language_to_string(exports.hb_ot_tag_to_language(hbTag));
|
|
1232
|
+
}
|
|
1233
|
+
//#endregion
|
|
1234
|
+
//#region src/index.ts
|
|
1235
|
+
init(await createHarfBuzz());
|
|
1236
|
+
//#endregion
|
|
1237
|
+
export { Blob, Buffer, BufferContentType, BufferFlag, BufferSerializeFlag, BufferSerializeFormat, Direction, Face, Font, FontFuncs, GlyphClass, GlyphFlag, TracePhase, otTagToLanguage, otTagToScript, shape, shapeWithTrace, version, versionString };
|