@zephyr3d/scene 0.9.3 → 0.9.5
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/dist/animation/animationmask.js +224 -0
- package/dist/animation/animationmask.js.map +1 -0
- package/dist/animation/animationset.js +268 -67
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/animationtrack.js.map +1 -1
- package/dist/animation/eulerrotationtrack.js +3 -0
- package/dist/animation/eulerrotationtrack.js.map +1 -1
- package/dist/animation/fixed_geometry_cache_track.js +7 -0
- package/dist/animation/fixed_geometry_cache_track.js.map +1 -1
- package/dist/animation/ik_modifier.js.map +1 -1
- package/dist/animation/joint_dynamics/collision.js +6 -4
- package/dist/animation/joint_dynamics/collision.js.map +1 -1
- package/dist/animation/joint_dynamics/controller.js +390 -43
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +62 -3
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/joint_dynamics/solver.js +4 -3
- package/dist/animation/joint_dynamics/solver.js.map +1 -1
- package/dist/animation/joint_dynamics/types.js.map +1 -1
- package/dist/animation/joint_dynamics_modifier.js +2 -0
- package/dist/animation/joint_dynamics_modifier.js.map +1 -1
- package/dist/animation/morphtarget.js +3 -72
- package/dist/animation/morphtarget.js.map +1 -1
- package/dist/animation/morphtrack.js +3 -0
- package/dist/animation/morphtrack.js.map +1 -1
- package/dist/animation/pca_geometry_cache_track.js +3 -0
- package/dist/animation/pca_geometry_cache_track.js.map +1 -1
- package/dist/animation/proptrack.js +3 -0
- package/dist/animation/proptrack.js.map +1 -1
- package/dist/animation/rotationtrack.js +3 -0
- package/dist/animation/rotationtrack.js.map +1 -1
- package/dist/animation/scaletrack.js +3 -0
- package/dist/animation/scaletrack.js.map +1 -1
- package/dist/animation/skeleton.js +254 -79
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/skeleton_modifier.js.map +1 -1
- package/dist/animation/spring/spring_particle.js +4 -4
- package/dist/animation/spring/spring_particle.js.map +1 -1
- package/dist/animation/spring_modifier.js.map +1 -1
- package/dist/animation/translationtrack.js +3 -0
- package/dist/animation/translationtrack.js.map +1 -1
- package/dist/app/engine.js +129 -17
- package/dist/app/engine.js.map +1 -1
- package/dist/app/scriptregistry.js +251 -80
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +133 -102
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/gltf/gltf_loader.js +18 -14
- package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -1
- package/dist/asset/loaders/loader.js +1 -6
- package/dist/asset/loaders/loader.js.map +1 -1
- package/dist/asset/loaders/zabc/zabc_loader.js.map +1 -1
- package/dist/asset/model.js +1052 -183
- package/dist/asset/model.js.map +1 -1
- package/dist/avatar/wardrobe.js +474 -0
- package/dist/avatar/wardrobe.js.map +1 -0
- package/dist/index.d.ts +1810 -233
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/material/material.js +1 -1
- package/dist/material/material.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +6 -3
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +72 -15
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/msdf_text.js +180 -0
- package/dist/material/msdf_text.js.map +1 -0
- package/dist/material/msdf_text_sprite.js +64 -0
- package/dist/material/msdf_text_sprite.js.map +1 -0
- package/dist/material/pbrblueprint.js +21 -6
- package/dist/material/pbrblueprint.js.map +1 -1
- package/dist/material/shader/helper.js +277 -277
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/sprite_sdf.js +106 -0
- package/dist/material/sprite_sdf.js.map +1 -0
- package/dist/posteffect/ssr.js +1 -0
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/render/abuffer_oit.js +1 -0
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/cull_visitor.js +28 -0
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/drawable.js.map +1 -1
- package/dist/render/drawable_mixin.js +1 -0
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/envlight.js +54 -1
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +1 -0
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +1 -0
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/globalbindgroup_allocator.js +1 -0
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/render_queue.js +1 -0
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/sky.js +9 -0
- package/dist/render/sky.js.map +1 -1
- package/dist/scene/basesprite.js +1 -5
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +18 -8
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +16 -2
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/mesh.js +164 -102
- package/dist/scene/mesh.js.map +1 -1
- package/dist/scene/meshdrawable.js +96 -0
- package/dist/scene/meshdrawable.js.map +1 -0
- package/dist/scene/msdftext.js +341 -0
- package/dist/scene/msdftext.js.map +1 -0
- package/dist/scene/msdftextsprite.js +349 -0
- package/dist/scene/msdftextsprite.js.map +1 -0
- package/dist/scene/particlesys.js +1 -0
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/scene.js +2 -0
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +437 -230
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/sdftextsprite.js +322 -0
- package/dist/scene/sdftextsprite.js.map +1 -0
- package/dist/scene/terrain-cm/grass.js +1 -0
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grassmaterial.js +1 -0
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +1 -0
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/textsprite.js +120 -0
- package/dist/scene/textsprite.js.map +1 -0
- package/dist/scene/water.js +1 -0
- package/dist/scene/water.js.map +1 -1
- package/dist/shadow/shader.js +1 -0
- package/dist/shadow/shader.js.map +1 -1
- package/dist/text/font/font_asset.js +884 -0
- package/dist/text/font/font_asset.js.map +1 -0
- package/dist/text/font/sfnt_reader.js +43 -0
- package/dist/text/font/sfnt_reader.js.map +1 -0
- package/dist/text/msdf/generator.js +417 -0
- package/dist/text/msdf/generator.js.map +1 -0
- package/dist/text/msdf/shape.js +146 -0
- package/dist/text/msdf/shape.js.map +1 -0
- package/dist/text/runtime/msdf_glyph_atlas.js +146 -0
- package/dist/text/runtime/msdf_glyph_atlas.js.map +1 -0
- package/dist/text/runtime/msdf_text_atlas_manager.js +50 -0
- package/dist/text/runtime/msdf_text_atlas_manager.js.map +1 -0
- package/dist/text/runtime/text_layout.js +96 -0
- package/dist/text/runtime/text_layout.js.map +1 -0
- package/dist/utility/blueprint/material/inputs.js +188 -1
- package/dist/utility/blueprint/material/inputs.js.map +1 -1
- package/dist/utility/blueprint/material/ir.js +16 -1
- package/dist/utility/blueprint/material/ir.js.map +1 -1
- package/dist/utility/blueprint/material/pbr.js +32 -0
- package/dist/utility/blueprint/material/pbr.js.map +1 -1
- package/dist/utility/bounding_volume.js.map +1 -1
- package/dist/utility/pmrem.js +34 -8
- package/dist/utility/pmrem.js.map +1 -1
- package/dist/utility/serialization/manager.js +340 -283
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +702 -7
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +2 -0
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +2 -0
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +2 -0
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +1 -0
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +66 -4
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +189 -8
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +2 -0
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +1 -0
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +2 -0
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +2 -0
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +516 -0
- package/dist/utility/serialization/scene/text.js.map +1 -0
- package/dist/utility/serialization/scene/water.js +2 -0
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/textures/sheenlut.js +138 -0
- package/dist/utility/textures/sheenlut.js.map +1 -0
- package/dist/values.js +48 -48
- package/dist/values.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
import { SFNTReader } from './sfnt_reader.js';
|
|
2
|
+
|
|
3
|
+
const ARG_1_AND_2_ARE_WORDS = 1 << 0;
|
|
4
|
+
const ARGS_ARE_XY_VALUES = 1 << 1;
|
|
5
|
+
const WE_HAVE_A_SCALE = 1 << 3;
|
|
6
|
+
const MORE_COMPONENTS = 1 << 5;
|
|
7
|
+
const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
|
|
8
|
+
const WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
|
9
|
+
const DEFAULT_MSDF_ATLAS_PAGE_SIZE = 1024;
|
|
10
|
+
const DEFAULT_MSDF_GLYPH_SIZE = 32;
|
|
11
|
+
/**
|
|
12
|
+
* Minimal runtime font asset for TrueType outlines.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/ class FontAsset {
|
|
16
|
+
_reader;
|
|
17
|
+
_tables;
|
|
18
|
+
_glyphOffsets;
|
|
19
|
+
_advanceWidths;
|
|
20
|
+
_leftSideBearings;
|
|
21
|
+
_glyphCache;
|
|
22
|
+
_cmapFormat4;
|
|
23
|
+
_cmapFormat12;
|
|
24
|
+
_kernPairs;
|
|
25
|
+
_gposPairs;
|
|
26
|
+
_metrics;
|
|
27
|
+
_numberOfHMetrics;
|
|
28
|
+
_msdfAtlasSettings;
|
|
29
|
+
constructor(buffer, options){
|
|
30
|
+
this._reader = new SFNTReader(buffer);
|
|
31
|
+
this._tables = parseTableDirectory(this._reader);
|
|
32
|
+
const head = this.requireTable('head');
|
|
33
|
+
const maxp = this.requireTable('maxp');
|
|
34
|
+
const hhea = this.requireTable('hhea');
|
|
35
|
+
const hmtx = this.requireTable('hmtx');
|
|
36
|
+
const loca = this.requireTable('loca');
|
|
37
|
+
this.requireTable('glyf');
|
|
38
|
+
this.requireTable('cmap');
|
|
39
|
+
this._numberOfHMetrics = this._reader.uint16(hhea.offset + 34);
|
|
40
|
+
this._metrics = {
|
|
41
|
+
unitsPerEm: this._reader.uint16(head.offset + 18),
|
|
42
|
+
ascent: this._reader.int16(hhea.offset + 4),
|
|
43
|
+
descent: this._reader.int16(hhea.offset + 6),
|
|
44
|
+
lineGap: this._reader.int16(hhea.offset + 8),
|
|
45
|
+
glyphCount: this._reader.uint16(maxp.offset + 4)
|
|
46
|
+
};
|
|
47
|
+
this._glyphOffsets = parseGlyphOffsets(this._reader, loca.offset, this._metrics.glyphCount, this._reader.int16(head.offset + 50));
|
|
48
|
+
const metrics = parseHorizontalMetrics(this._reader, hmtx.offset, this._metrics.glyphCount, this._numberOfHMetrics);
|
|
49
|
+
this._advanceWidths = metrics.advanceWidths;
|
|
50
|
+
this._leftSideBearings = metrics.leftSideBearings;
|
|
51
|
+
const cmap = parseBestCMap(this._reader, this.requireTable('cmap').offset);
|
|
52
|
+
this._cmapFormat4 = cmap.format4;
|
|
53
|
+
this._cmapFormat12 = cmap.format12;
|
|
54
|
+
this._kernPairs = parseKernPairs(this._reader, this.getTable('kern'));
|
|
55
|
+
this._gposPairs = parseGPOSPairs(this._reader, this.getTable('GPOS'));
|
|
56
|
+
this._glyphCache = new Map();
|
|
57
|
+
this._msdfAtlasSettings = Object.freeze({
|
|
58
|
+
pageSize: normalizeMSDFAtlasPageSize(options?.pageSize),
|
|
59
|
+
glyphSize: normalizeMSDFGlyphSize(options?.glyphSize)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
static fromBuffer(buffer, options) {
|
|
63
|
+
return new FontAsset(buffer, options);
|
|
64
|
+
}
|
|
65
|
+
get metrics() {
|
|
66
|
+
return this._metrics;
|
|
67
|
+
}
|
|
68
|
+
get msdfAtlasSettings() {
|
|
69
|
+
return this._msdfAtlasSettings;
|
|
70
|
+
}
|
|
71
|
+
get msdfAtlasPageSize() {
|
|
72
|
+
return this._msdfAtlasSettings.pageSize;
|
|
73
|
+
}
|
|
74
|
+
get msdfGlyphSize() {
|
|
75
|
+
return this._msdfAtlasSettings.glyphSize;
|
|
76
|
+
}
|
|
77
|
+
getGlyphIndex(codePoint) {
|
|
78
|
+
if (this._cmapFormat12) {
|
|
79
|
+
const glyphIndex = lookupCMapFormat12(this._reader, this._cmapFormat12.offset, codePoint);
|
|
80
|
+
if (glyphIndex !== 0) {
|
|
81
|
+
return glyphIndex;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (this._cmapFormat4 && codePoint <= 0xffff) {
|
|
85
|
+
return lookupCMapFormat4(this._reader, this._cmapFormat4.offset, codePoint);
|
|
86
|
+
}
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
getKerning(leftGlyphIndex, rightGlyphIndex) {
|
|
90
|
+
const gpos = this.getPairAdjustment(leftGlyphIndex, rightGlyphIndex);
|
|
91
|
+
if (gpos) {
|
|
92
|
+
return gpos.firstXAdvance + gpos.secondXAdvance;
|
|
93
|
+
}
|
|
94
|
+
return this._kernPairs.get(leftGlyphIndex << 16 | rightGlyphIndex) ?? 0;
|
|
95
|
+
}
|
|
96
|
+
getPairAdjustment(leftGlyphIndex, rightGlyphIndex) {
|
|
97
|
+
return lookupGPOSPair(this._gposPairs, leftGlyphIndex, rightGlyphIndex);
|
|
98
|
+
}
|
|
99
|
+
getGlyph(glyphIndex) {
|
|
100
|
+
if (glyphIndex < 0 || glyphIndex >= this._metrics.glyphCount) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (this._glyphCache.has(glyphIndex)) {
|
|
104
|
+
return this._glyphCache.get(glyphIndex);
|
|
105
|
+
}
|
|
106
|
+
const glyph = this.loadGlyph(glyphIndex, 0);
|
|
107
|
+
this._glyphCache.set(glyphIndex, glyph);
|
|
108
|
+
return glyph;
|
|
109
|
+
}
|
|
110
|
+
getTable(tag) {
|
|
111
|
+
return this._tables.get(tag) ?? null;
|
|
112
|
+
}
|
|
113
|
+
requireTable(tag) {
|
|
114
|
+
const table = this.getTable(tag);
|
|
115
|
+
if (!table) {
|
|
116
|
+
throw new Error(`Font table not found: ${tag}`);
|
|
117
|
+
}
|
|
118
|
+
return table;
|
|
119
|
+
}
|
|
120
|
+
loadGlyph(glyphIndex, depth) {
|
|
121
|
+
if (depth > 16) {
|
|
122
|
+
throw new Error(`Composite glyph nesting too deep: ${glyphIndex}`);
|
|
123
|
+
}
|
|
124
|
+
const glyf = this.requireTable('glyf');
|
|
125
|
+
const offset = this._glyphOffsets[glyphIndex];
|
|
126
|
+
const nextOffset = this._glyphOffsets[glyphIndex + 1];
|
|
127
|
+
if (offset === nextOffset) {
|
|
128
|
+
return buildGlyphData(glyphIndex, this._advanceWidths, this._leftSideBearings, [], 0, 0, 0, 0);
|
|
129
|
+
}
|
|
130
|
+
const glyphOffset = glyf.offset + offset;
|
|
131
|
+
const header = parseGlyphHeader(this._reader, glyphOffset);
|
|
132
|
+
if (header.numberOfContours >= 0) {
|
|
133
|
+
const contours = parseSimpleGlyph(this._reader, glyphOffset, header.numberOfContours);
|
|
134
|
+
return buildGlyphData(glyphIndex, this._advanceWidths, this._leftSideBearings, contours, header.xMin, header.yMin, header.xMax, header.yMax);
|
|
135
|
+
}
|
|
136
|
+
const contours = parseCompositeGlyph(this._reader, glyphOffset, (childIndex)=>this.loadGlyph(childIndex, depth + 1));
|
|
137
|
+
const bounds = computeBounds(contours);
|
|
138
|
+
return buildGlyphData(glyphIndex, this._advanceWidths, this._leftSideBearings, contours, bounds.xMin, bounds.yMin, bounds.xMax, bounds.yMax);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function parseTableDirectory(reader) {
|
|
142
|
+
const numTables = reader.uint16(4);
|
|
143
|
+
const tables = new Map();
|
|
144
|
+
let offset = 12;
|
|
145
|
+
for(let i = 0; i < numTables; i++){
|
|
146
|
+
const tag = reader.tag(offset);
|
|
147
|
+
tables.set(tag, {
|
|
148
|
+
offset: reader.uint32(offset + 8),
|
|
149
|
+
length: reader.uint32(offset + 12)
|
|
150
|
+
});
|
|
151
|
+
offset += 16;
|
|
152
|
+
}
|
|
153
|
+
return tables;
|
|
154
|
+
}
|
|
155
|
+
function parseGlyphOffsets(reader, offset, glyphCount, indexToLocFormat) {
|
|
156
|
+
const result = new Uint32Array(glyphCount + 1);
|
|
157
|
+
if (indexToLocFormat === 0) {
|
|
158
|
+
for(let i = 0; i <= glyphCount; i++){
|
|
159
|
+
result[i] = reader.uint16(offset + i * 2) * 2;
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
for(let i = 0; i <= glyphCount; i++){
|
|
163
|
+
result[i] = reader.uint32(offset + i * 4);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
function parseHorizontalMetrics(reader, offset, glyphCount, numberOfHMetrics) {
|
|
169
|
+
const advanceWidths = new Uint16Array(glyphCount);
|
|
170
|
+
const leftSideBearings = new Int16Array(glyphCount);
|
|
171
|
+
let cursor = offset;
|
|
172
|
+
let lastAdvanceWidth = 0;
|
|
173
|
+
for(let i = 0; i < glyphCount; i++){
|
|
174
|
+
if (i < numberOfHMetrics) {
|
|
175
|
+
lastAdvanceWidth = reader.uint16(cursor);
|
|
176
|
+
advanceWidths[i] = lastAdvanceWidth;
|
|
177
|
+
leftSideBearings[i] = reader.int16(cursor + 2);
|
|
178
|
+
cursor += 4;
|
|
179
|
+
} else {
|
|
180
|
+
advanceWidths[i] = lastAdvanceWidth;
|
|
181
|
+
leftSideBearings[i] = reader.int16(cursor);
|
|
182
|
+
cursor += 2;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
advanceWidths,
|
|
187
|
+
leftSideBearings
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function parseBestCMap(reader, offset) {
|
|
191
|
+
const numTables = reader.uint16(offset + 2);
|
|
192
|
+
let format4 = null;
|
|
193
|
+
let format12 = null;
|
|
194
|
+
for(let i = 0; i < numTables; i++){
|
|
195
|
+
const recordOffset = offset + 4 + i * 8;
|
|
196
|
+
const platformId = reader.uint16(recordOffset);
|
|
197
|
+
const encodingId = reader.uint16(recordOffset + 2);
|
|
198
|
+
const subtableOffset = offset + reader.uint32(recordOffset + 4);
|
|
199
|
+
const format = reader.uint16(subtableOffset);
|
|
200
|
+
if (format === 12) {
|
|
201
|
+
if (!format12 || isPreferredCMap(platformId, encodingId, format12.platformId, format12.encodingId)) {
|
|
202
|
+
format12 = {
|
|
203
|
+
offset: subtableOffset,
|
|
204
|
+
platformId,
|
|
205
|
+
encodingId
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
} else if (format === 4) {
|
|
209
|
+
if (!format4 || isPreferredCMap(platformId, encodingId, format4.platformId, format4.encodingId)) {
|
|
210
|
+
format4 = {
|
|
211
|
+
offset: subtableOffset,
|
|
212
|
+
platformId,
|
|
213
|
+
encodingId
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
format4,
|
|
220
|
+
format12
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function isPreferredCMap(platformId, encodingId, currentPlatformId, currentEncodingId) {
|
|
224
|
+
const nextScore = getCMapScore(platformId, encodingId);
|
|
225
|
+
const currentScore = getCMapScore(currentPlatformId, currentEncodingId);
|
|
226
|
+
return nextScore > currentScore;
|
|
227
|
+
}
|
|
228
|
+
function getCMapScore(platformId, encodingId) {
|
|
229
|
+
if (platformId === 3 && encodingId === 10) {
|
|
230
|
+
return 5;
|
|
231
|
+
}
|
|
232
|
+
if (platformId === 0 && encodingId <= 6) {
|
|
233
|
+
return 4;
|
|
234
|
+
}
|
|
235
|
+
if (platformId === 3 && encodingId === 1) {
|
|
236
|
+
return 3;
|
|
237
|
+
}
|
|
238
|
+
if (platformId === 0) {
|
|
239
|
+
return 2;
|
|
240
|
+
}
|
|
241
|
+
return 1;
|
|
242
|
+
}
|
|
243
|
+
function lookupCMapFormat12(reader, offset, codePoint) {
|
|
244
|
+
const nGroups = reader.uint32(offset + 12);
|
|
245
|
+
let low = 0;
|
|
246
|
+
let high = nGroups - 1;
|
|
247
|
+
while(low <= high){
|
|
248
|
+
const mid = low + high >> 1;
|
|
249
|
+
const groupOffset = offset + 16 + mid * 12;
|
|
250
|
+
const startCharCode = reader.uint32(groupOffset);
|
|
251
|
+
const endCharCode = reader.uint32(groupOffset + 4);
|
|
252
|
+
if (codePoint < startCharCode) {
|
|
253
|
+
high = mid - 1;
|
|
254
|
+
} else if (codePoint > endCharCode) {
|
|
255
|
+
low = mid + 1;
|
|
256
|
+
} else {
|
|
257
|
+
return reader.uint32(groupOffset + 8) + (codePoint - startCharCode);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
function lookupCMapFormat4(reader, offset, codePoint) {
|
|
263
|
+
const segCount = reader.uint16(offset + 6) >> 1;
|
|
264
|
+
const endCodeOffset = offset + 14;
|
|
265
|
+
const startCodeOffset = endCodeOffset + segCount * 2 + 2;
|
|
266
|
+
const idDeltaOffset = startCodeOffset + segCount * 2;
|
|
267
|
+
const idRangeOffsetOffset = idDeltaOffset + segCount * 2;
|
|
268
|
+
for(let i = 0; i < segCount; i++){
|
|
269
|
+
const endCode = reader.uint16(endCodeOffset + i * 2);
|
|
270
|
+
if (codePoint > endCode) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const startCode = reader.uint16(startCodeOffset + i * 2);
|
|
274
|
+
if (codePoint < startCode) {
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
const idDelta = reader.int16(idDeltaOffset + i * 2);
|
|
278
|
+
const idRangeOffset = reader.uint16(idRangeOffsetOffset + i * 2);
|
|
279
|
+
if (idRangeOffset === 0) {
|
|
280
|
+
return codePoint + idDelta & 0xffff;
|
|
281
|
+
}
|
|
282
|
+
const glyphIndexAddress = idRangeOffsetOffset + i * 2 + idRangeOffset + (codePoint - startCode) * 2;
|
|
283
|
+
const glyphIndex = reader.uint16(glyphIndexAddress);
|
|
284
|
+
return glyphIndex === 0 ? 0 : glyphIndex + idDelta & 0xffff;
|
|
285
|
+
}
|
|
286
|
+
return 0;
|
|
287
|
+
}
|
|
288
|
+
function parseKernPairs(reader, table) {
|
|
289
|
+
const result = new Map();
|
|
290
|
+
if (!table || table.length < 4) {
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
const version = reader.uint16(table.offset);
|
|
294
|
+
if (version !== 0) {
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
const nTables = reader.uint16(table.offset + 2);
|
|
298
|
+
let cursor = table.offset + 4;
|
|
299
|
+
for(let i = 0; i < nTables; i++){
|
|
300
|
+
const length = reader.uint16(cursor + 2);
|
|
301
|
+
const coverage = reader.uint16(cursor + 4);
|
|
302
|
+
const format = coverage >> 8;
|
|
303
|
+
if (format === 0) {
|
|
304
|
+
const nPairs = reader.uint16(cursor + 6);
|
|
305
|
+
let pairOffset = cursor + 14;
|
|
306
|
+
for(let j = 0; j < nPairs; j++){
|
|
307
|
+
const left = reader.uint16(pairOffset);
|
|
308
|
+
const right = reader.uint16(pairOffset + 2);
|
|
309
|
+
const value = reader.int16(pairOffset + 4);
|
|
310
|
+
result.set(left << 16 | right, value);
|
|
311
|
+
pairOffset += 6;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
cursor += length;
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
function parseGPOSPairs(reader, table) {
|
|
319
|
+
if (!table || table.length < 10) {
|
|
320
|
+
return [];
|
|
321
|
+
}
|
|
322
|
+
const majorVersion = reader.uint16(table.offset);
|
|
323
|
+
if (majorVersion !== 1) {
|
|
324
|
+
return [];
|
|
325
|
+
}
|
|
326
|
+
const featureListOffset = reader.uint16(table.offset + 6);
|
|
327
|
+
const lookupListOffset = reader.uint16(table.offset + 8);
|
|
328
|
+
if (!featureListOffset || !lookupListOffset) {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
const featureLookupIndices = collectGPOSFeatureLookupIndices(reader, table.offset + featureListOffset);
|
|
332
|
+
if (featureLookupIndices.length === 0) {
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
return parseGPOSLookupSubtables(reader, table.offset + lookupListOffset, table.offset, featureLookupIndices);
|
|
336
|
+
}
|
|
337
|
+
function collectGPOSFeatureLookupIndices(reader, featureListOffset) {
|
|
338
|
+
const featureCount = reader.uint16(featureListOffset);
|
|
339
|
+
const featureTags = new Set([
|
|
340
|
+
'kern'
|
|
341
|
+
]);
|
|
342
|
+
const lookupIndices = new Set();
|
|
343
|
+
for(let i = 0; i < featureCount; i++){
|
|
344
|
+
const recordOffset = featureListOffset + 2 + i * 6;
|
|
345
|
+
const tag = reader.tag(recordOffset);
|
|
346
|
+
if (!featureTags.has(tag)) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const featureOffset = featureListOffset + reader.uint16(recordOffset + 4);
|
|
350
|
+
const lookupIndexCount = reader.uint16(featureOffset + 2);
|
|
351
|
+
for(let j = 0; j < lookupIndexCount; j++){
|
|
352
|
+
lookupIndices.add(reader.uint16(featureOffset + 4 + j * 2));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return [
|
|
356
|
+
...lookupIndices
|
|
357
|
+
].sort((a, b)=>a - b);
|
|
358
|
+
}
|
|
359
|
+
function parseGPOSLookupSubtables(reader, lookupListOffset, gposOffset, lookupIndices) {
|
|
360
|
+
const lookupCount = reader.uint16(lookupListOffset);
|
|
361
|
+
const result = [];
|
|
362
|
+
for (const lookupIndex of lookupIndices){
|
|
363
|
+
if (lookupIndex >= lookupCount) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const lookupOffset = lookupListOffset + reader.uint16(lookupListOffset + 2 + lookupIndex * 2);
|
|
367
|
+
const lookupType = reader.uint16(lookupOffset);
|
|
368
|
+
const subtableCount = reader.uint16(lookupOffset + 4);
|
|
369
|
+
for(let i = 0; i < subtableCount; i++){
|
|
370
|
+
const subtableOffset = lookupOffset + reader.uint16(lookupOffset + 6 + i * 2);
|
|
371
|
+
if (lookupType === 2) {
|
|
372
|
+
const parsed = parsePairPosSubtable(reader, subtableOffset);
|
|
373
|
+
if (parsed) {
|
|
374
|
+
result.push(parsed);
|
|
375
|
+
}
|
|
376
|
+
} else if (lookupType === 9) {
|
|
377
|
+
const extensionLookupType = reader.uint16(subtableOffset + 2);
|
|
378
|
+
const extensionOffset = reader.uint32(subtableOffset + 4);
|
|
379
|
+
if (extensionLookupType === 2) {
|
|
380
|
+
const parsed = parsePairPosSubtable(reader, subtableOffset + extensionOffset);
|
|
381
|
+
if (parsed) {
|
|
382
|
+
result.push(parsed);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
function parsePairPosSubtable(reader, offset) {
|
|
391
|
+
const format = reader.uint16(offset);
|
|
392
|
+
if (format === 1) {
|
|
393
|
+
return parsePairPosFormat1(reader, offset);
|
|
394
|
+
}
|
|
395
|
+
if (format === 2) {
|
|
396
|
+
return parsePairPosFormat2(reader, offset);
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
function parsePairPosFormat1(reader, offset) {
|
|
401
|
+
const coverage = parseCoverageTable(reader, offset + reader.uint16(offset + 2));
|
|
402
|
+
const valueFormat1 = reader.uint16(offset + 4);
|
|
403
|
+
const valueFormat2 = reader.uint16(offset + 6);
|
|
404
|
+
const pairSetCount = reader.uint16(offset + 8);
|
|
405
|
+
const pairs = new Map();
|
|
406
|
+
for(let i = 0; i < pairSetCount; i++){
|
|
407
|
+
const firstGlyph = getCoverageGlyph(coverage, i);
|
|
408
|
+
if (firstGlyph < 0) {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const pairSetOffset = offset + reader.uint16(offset + 10 + i * 2);
|
|
412
|
+
const pairValueCount = reader.uint16(pairSetOffset);
|
|
413
|
+
let cursor = pairSetOffset + 2;
|
|
414
|
+
const secondMap = new Map();
|
|
415
|
+
for(let j = 0; j < pairValueCount; j++){
|
|
416
|
+
const secondGlyph = reader.uint16(cursor);
|
|
417
|
+
cursor += 2;
|
|
418
|
+
const value1 = readValueRecordX(reader, cursor, valueFormat1);
|
|
419
|
+
cursor += getValueRecordSize(valueFormat1);
|
|
420
|
+
const value2 = readValueRecordX(reader, cursor, valueFormat2);
|
|
421
|
+
cursor += getValueRecordSize(valueFormat2);
|
|
422
|
+
const adjustment = {
|
|
423
|
+
firstXPlacement: value1.xPlacement,
|
|
424
|
+
firstXAdvance: value1.xAdvance,
|
|
425
|
+
secondXPlacement: value2.xPlacement,
|
|
426
|
+
secondXAdvance: value2.xAdvance
|
|
427
|
+
};
|
|
428
|
+
if (adjustment.firstXPlacement !== 0 || adjustment.firstXAdvance !== 0 || adjustment.secondXPlacement !== 0 || adjustment.secondXAdvance !== 0) {
|
|
429
|
+
secondMap.set(secondGlyph, adjustment);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
pairs.set(firstGlyph, secondMap);
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
kind: 'format1',
|
|
436
|
+
coverage,
|
|
437
|
+
pairs
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function parsePairPosFormat2(reader, offset) {
|
|
441
|
+
const coverage = parseCoverageTable(reader, offset + reader.uint16(offset + 2));
|
|
442
|
+
const valueFormat1 = reader.uint16(offset + 4);
|
|
443
|
+
const valueFormat2 = reader.uint16(offset + 6);
|
|
444
|
+
const classDef1 = parseClassDefTable(reader, offset + reader.uint16(offset + 8));
|
|
445
|
+
const classDef2 = parseClassDefTable(reader, offset + reader.uint16(offset + 10));
|
|
446
|
+
const class1Count = reader.uint16(offset + 12);
|
|
447
|
+
const class2Count = reader.uint16(offset + 14);
|
|
448
|
+
const values = new Array(class1Count * class2Count);
|
|
449
|
+
let cursor = offset + 16;
|
|
450
|
+
for(let c1 = 0; c1 < class1Count; c1++){
|
|
451
|
+
for(let c2 = 0; c2 < class2Count; c2++){
|
|
452
|
+
const value1 = readValueRecordX(reader, cursor, valueFormat1);
|
|
453
|
+
cursor += getValueRecordSize(valueFormat1);
|
|
454
|
+
const value2 = readValueRecordX(reader, cursor, valueFormat2);
|
|
455
|
+
cursor += getValueRecordSize(valueFormat2);
|
|
456
|
+
values[c1 * class2Count + c2] = {
|
|
457
|
+
firstXPlacement: value1.xPlacement,
|
|
458
|
+
firstXAdvance: value1.xAdvance,
|
|
459
|
+
secondXPlacement: value2.xPlacement,
|
|
460
|
+
secondXAdvance: value2.xAdvance
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
kind: 'format2',
|
|
466
|
+
coverage,
|
|
467
|
+
classDef1,
|
|
468
|
+
classDef2,
|
|
469
|
+
class1Count,
|
|
470
|
+
class2Count,
|
|
471
|
+
values
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function parseCoverageTable(reader, offset) {
|
|
475
|
+
const format = reader.uint16(offset);
|
|
476
|
+
if (format === 1) {
|
|
477
|
+
const glyphCount = reader.uint16(offset + 2);
|
|
478
|
+
const glyphs = new Uint16Array(glyphCount);
|
|
479
|
+
for(let i = 0; i < glyphCount; i++){
|
|
480
|
+
glyphs[i] = reader.uint16(offset + 4 + i * 2);
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
format: 1,
|
|
484
|
+
glyphs
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const rangeCount = reader.uint16(offset + 2);
|
|
488
|
+
const ranges = [];
|
|
489
|
+
for(let i = 0; i < rangeCount; i++){
|
|
490
|
+
const rangeOffset = offset + 4 + i * 6;
|
|
491
|
+
ranges.push({
|
|
492
|
+
start: reader.uint16(rangeOffset),
|
|
493
|
+
end: reader.uint16(rangeOffset + 2),
|
|
494
|
+
startIndex: reader.uint16(rangeOffset + 4)
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
format: 2,
|
|
499
|
+
ranges
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function getCoverageGlyph(coverage, index) {
|
|
503
|
+
if (coverage.format === 1) {
|
|
504
|
+
return coverage.glyphs[index] ?? -1;
|
|
505
|
+
}
|
|
506
|
+
for (const range of coverage.ranges){
|
|
507
|
+
const rangeLength = range.end - range.start + 1;
|
|
508
|
+
if (index >= range.startIndex && index < range.startIndex + rangeLength) {
|
|
509
|
+
return range.start + (index - range.startIndex);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return -1;
|
|
513
|
+
}
|
|
514
|
+
function coverageContains(coverage, glyphIndex) {
|
|
515
|
+
if (coverage.format === 1) {
|
|
516
|
+
return binarySearchU16(coverage.glyphs, glyphIndex) >= 0;
|
|
517
|
+
}
|
|
518
|
+
for (const range of coverage.ranges){
|
|
519
|
+
if (glyphIndex >= range.start && glyphIndex <= range.end) {
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
function parseClassDefTable(reader, offset) {
|
|
526
|
+
const format = reader.uint16(offset);
|
|
527
|
+
if (format === 1) {
|
|
528
|
+
const startGlyphId = reader.uint16(offset + 2);
|
|
529
|
+
const glyphCount = reader.uint16(offset + 4);
|
|
530
|
+
const classes = new Uint16Array(glyphCount);
|
|
531
|
+
for(let i = 0; i < glyphCount; i++){
|
|
532
|
+
classes[i] = reader.uint16(offset + 6 + i * 2);
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
format: 1,
|
|
536
|
+
startGlyphId,
|
|
537
|
+
classes
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
const classRangeCount = reader.uint16(offset + 2);
|
|
541
|
+
const ranges = [];
|
|
542
|
+
for(let i = 0; i < classRangeCount; i++){
|
|
543
|
+
const rangeOffset = offset + 4 + i * 6;
|
|
544
|
+
ranges.push({
|
|
545
|
+
start: reader.uint16(rangeOffset),
|
|
546
|
+
end: reader.uint16(rangeOffset + 2),
|
|
547
|
+
classId: reader.uint16(rangeOffset + 4)
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
format: 2,
|
|
552
|
+
ranges
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function getGlyphClass(classDef, glyphIndex) {
|
|
556
|
+
if (classDef.format === 1) {
|
|
557
|
+
const index = glyphIndex - classDef.startGlyphId;
|
|
558
|
+
return index >= 0 && index < classDef.classes.length ? classDef.classes[index] : 0;
|
|
559
|
+
}
|
|
560
|
+
for (const range of classDef.ranges){
|
|
561
|
+
if (glyphIndex >= range.start && glyphIndex <= range.end) {
|
|
562
|
+
return range.classId;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return 0;
|
|
566
|
+
}
|
|
567
|
+
function readValueRecordX(reader, offset, valueFormat) {
|
|
568
|
+
let cursor = offset;
|
|
569
|
+
let xPlacement = 0;
|
|
570
|
+
let xAdvance = 0;
|
|
571
|
+
if (valueFormat & 0x0001) {
|
|
572
|
+
xPlacement = reader.int16(cursor);
|
|
573
|
+
cursor += 2;
|
|
574
|
+
}
|
|
575
|
+
if (valueFormat & 0x0002) {
|
|
576
|
+
cursor += 2;
|
|
577
|
+
}
|
|
578
|
+
if (valueFormat & 0x0004) {
|
|
579
|
+
xAdvance = reader.int16(cursor);
|
|
580
|
+
cursor += 2;
|
|
581
|
+
}
|
|
582
|
+
if (valueFormat & 0x0008) {
|
|
583
|
+
cursor += 2;
|
|
584
|
+
}
|
|
585
|
+
if (valueFormat & 0x0010) {
|
|
586
|
+
cursor += 2;
|
|
587
|
+
}
|
|
588
|
+
if (valueFormat & 0x0020) {
|
|
589
|
+
cursor += 2;
|
|
590
|
+
}
|
|
591
|
+
if (valueFormat & 0x0040) {
|
|
592
|
+
cursor += 2;
|
|
593
|
+
}
|
|
594
|
+
if (valueFormat & 0x0080) {
|
|
595
|
+
cursor += 2;
|
|
596
|
+
}
|
|
597
|
+
return {
|
|
598
|
+
xPlacement,
|
|
599
|
+
xAdvance
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function getValueRecordSize(valueFormat) {
|
|
603
|
+
let size = 0;
|
|
604
|
+
for(let bit = 0; bit < 8; bit++){
|
|
605
|
+
if (valueFormat & 1 << bit) {
|
|
606
|
+
size += 2;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return size;
|
|
610
|
+
}
|
|
611
|
+
function lookupGPOSPair(subtables, leftGlyphIndex, rightGlyphIndex) {
|
|
612
|
+
let merged = null;
|
|
613
|
+
for (const subtable of subtables){
|
|
614
|
+
if (!coverageContains(subtable.coverage, leftGlyphIndex)) {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (subtable.kind === 'format1') {
|
|
618
|
+
const secondMap = subtable.pairs.get(leftGlyphIndex);
|
|
619
|
+
if (secondMap) {
|
|
620
|
+
const adjustment = secondMap.get(rightGlyphIndex);
|
|
621
|
+
if (adjustment) {
|
|
622
|
+
merged = mergePairAdjustment(merged, adjustment);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
} else {
|
|
626
|
+
const class1 = getGlyphClass(subtable.classDef1, leftGlyphIndex);
|
|
627
|
+
const class2 = getGlyphClass(subtable.classDef2, rightGlyphIndex);
|
|
628
|
+
if (class1 < subtable.class1Count && class2 < subtable.class2Count) {
|
|
629
|
+
const adjustment = subtable.values[class1 * subtable.class2Count + class2];
|
|
630
|
+
if (adjustment) {
|
|
631
|
+
merged = mergePairAdjustment(merged, adjustment);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return merged;
|
|
637
|
+
}
|
|
638
|
+
function binarySearchU16(values, target) {
|
|
639
|
+
let low = 0;
|
|
640
|
+
let high = values.length - 1;
|
|
641
|
+
while(low <= high){
|
|
642
|
+
const mid = low + high >> 1;
|
|
643
|
+
const value = values[mid];
|
|
644
|
+
if (value < target) {
|
|
645
|
+
low = mid + 1;
|
|
646
|
+
} else if (value > target) {
|
|
647
|
+
high = mid - 1;
|
|
648
|
+
} else {
|
|
649
|
+
return mid;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return -1;
|
|
653
|
+
}
|
|
654
|
+
function mergePairAdjustment(base, next) {
|
|
655
|
+
if (!base) {
|
|
656
|
+
return {
|
|
657
|
+
...next
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
firstXPlacement: base.firstXPlacement + next.firstXPlacement,
|
|
662
|
+
firstXAdvance: base.firstXAdvance + next.firstXAdvance,
|
|
663
|
+
secondXPlacement: base.secondXPlacement + next.secondXPlacement,
|
|
664
|
+
secondXAdvance: base.secondXAdvance + next.secondXAdvance
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function parseGlyphHeader(reader, offset) {
|
|
668
|
+
return {
|
|
669
|
+
numberOfContours: reader.int16(offset),
|
|
670
|
+
xMin: reader.int16(offset + 2),
|
|
671
|
+
yMin: reader.int16(offset + 4),
|
|
672
|
+
xMax: reader.int16(offset + 6),
|
|
673
|
+
yMax: reader.int16(offset + 8)
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
function parseSimpleGlyph(reader, offset, numberOfContours) {
|
|
677
|
+
const endPts = [];
|
|
678
|
+
let cursor = offset + 10;
|
|
679
|
+
for(let i = 0; i < numberOfContours; i++){
|
|
680
|
+
endPts.push(reader.uint16(cursor));
|
|
681
|
+
cursor += 2;
|
|
682
|
+
}
|
|
683
|
+
const pointCount = endPts[numberOfContours - 1] + 1;
|
|
684
|
+
const instructionLength = reader.uint16(cursor);
|
|
685
|
+
cursor += 2 + instructionLength;
|
|
686
|
+
const flags = new Uint8Array(pointCount);
|
|
687
|
+
for(let i = 0; i < pointCount;){
|
|
688
|
+
const flag = reader.uint8(cursor++);
|
|
689
|
+
flags[i++] = flag;
|
|
690
|
+
if (flag & 0x08) {
|
|
691
|
+
const repeatCount = reader.uint8(cursor++);
|
|
692
|
+
for(let j = 0; j < repeatCount; j++){
|
|
693
|
+
flags[i++] = flag;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const xs = new Int16Array(pointCount);
|
|
698
|
+
const ys = new Int16Array(pointCount);
|
|
699
|
+
let currentX = 0;
|
|
700
|
+
for(let i = 0; i < pointCount; i++){
|
|
701
|
+
const flag = flags[i];
|
|
702
|
+
if (flag & 0x02) {
|
|
703
|
+
const dx = reader.uint8(cursor++);
|
|
704
|
+
currentX += flag & 0x10 ? dx : -dx;
|
|
705
|
+
} else if (!(flag & 0x10)) {
|
|
706
|
+
currentX += reader.int16(cursor);
|
|
707
|
+
cursor += 2;
|
|
708
|
+
}
|
|
709
|
+
xs[i] = currentX;
|
|
710
|
+
}
|
|
711
|
+
let currentY = 0;
|
|
712
|
+
for(let i = 0; i < pointCount; i++){
|
|
713
|
+
const flag = flags[i];
|
|
714
|
+
if (flag & 0x04) {
|
|
715
|
+
const dy = reader.uint8(cursor++);
|
|
716
|
+
currentY += flag & 0x20 ? dy : -dy;
|
|
717
|
+
} else if (!(flag & 0x20)) {
|
|
718
|
+
currentY += reader.int16(cursor);
|
|
719
|
+
cursor += 2;
|
|
720
|
+
}
|
|
721
|
+
ys[i] = currentY;
|
|
722
|
+
}
|
|
723
|
+
const points = [];
|
|
724
|
+
for(let i = 0; i < pointCount; i++){
|
|
725
|
+
points.push({
|
|
726
|
+
x: xs[i],
|
|
727
|
+
y: ys[i],
|
|
728
|
+
onCurve: !!(flags[i] & 0x01)
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
const contours = [];
|
|
732
|
+
let start = 0;
|
|
733
|
+
for (const end of endPts){
|
|
734
|
+
contours.push(expandImplicitPoints(points.slice(start, end + 1)));
|
|
735
|
+
start = end + 1;
|
|
736
|
+
}
|
|
737
|
+
return contours;
|
|
738
|
+
}
|
|
739
|
+
function parseCompositeGlyph(reader, offset, loadGlyph) {
|
|
740
|
+
let cursor = offset + 10;
|
|
741
|
+
const contours = [];
|
|
742
|
+
let flags = 0;
|
|
743
|
+
do {
|
|
744
|
+
flags = reader.uint16(cursor);
|
|
745
|
+
const glyphIndex = reader.uint16(cursor + 2);
|
|
746
|
+
cursor += 4;
|
|
747
|
+
const component = parseCompositeComponent(reader, cursor, flags, glyphIndex);
|
|
748
|
+
cursor = component.cursor;
|
|
749
|
+
const child = loadGlyph(component.data.glyphIndex);
|
|
750
|
+
if (child) {
|
|
751
|
+
for (const contour of child.contours){
|
|
752
|
+
contours.push(contour.map((point)=>({
|
|
753
|
+
x: point.x * component.data.xx + point.y * component.data.xy + component.data.dx,
|
|
754
|
+
y: point.x * component.data.yx + point.y * component.data.yy + component.data.dy,
|
|
755
|
+
onCurve: point.onCurve
|
|
756
|
+
})));
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}while (flags & MORE_COMPONENTS)
|
|
760
|
+
return contours;
|
|
761
|
+
}
|
|
762
|
+
function parseCompositeComponent(reader, offset, flags, glyphIndex) {
|
|
763
|
+
let cursor = offset;
|
|
764
|
+
let arg1 = 0;
|
|
765
|
+
let arg2 = 0;
|
|
766
|
+
if (flags & ARG_1_AND_2_ARE_WORDS) {
|
|
767
|
+
arg1 = reader.int16(cursor);
|
|
768
|
+
arg2 = reader.int16(cursor + 2);
|
|
769
|
+
cursor += 4;
|
|
770
|
+
} else {
|
|
771
|
+
arg1 = reader.int8(cursor);
|
|
772
|
+
arg2 = reader.int8(cursor + 1);
|
|
773
|
+
cursor += 2;
|
|
774
|
+
}
|
|
775
|
+
let dx = 0;
|
|
776
|
+
let dy = 0;
|
|
777
|
+
if (flags & ARGS_ARE_XY_VALUES) {
|
|
778
|
+
dx = arg1;
|
|
779
|
+
dy = arg2;
|
|
780
|
+
}
|
|
781
|
+
let xx = 1;
|
|
782
|
+
let xy = 0;
|
|
783
|
+
let yx = 0;
|
|
784
|
+
let yy = 1;
|
|
785
|
+
if (flags & WE_HAVE_A_SCALE) {
|
|
786
|
+
const scale = readF2Dot14(reader, cursor);
|
|
787
|
+
cursor += 2;
|
|
788
|
+
xx = scale;
|
|
789
|
+
yy = scale;
|
|
790
|
+
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
|
791
|
+
xx = readF2Dot14(reader, cursor);
|
|
792
|
+
yy = readF2Dot14(reader, cursor + 2);
|
|
793
|
+
cursor += 4;
|
|
794
|
+
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
|
795
|
+
xx = readF2Dot14(reader, cursor);
|
|
796
|
+
xy = readF2Dot14(reader, cursor + 2);
|
|
797
|
+
yx = readF2Dot14(reader, cursor + 4);
|
|
798
|
+
yy = readF2Dot14(reader, cursor + 6);
|
|
799
|
+
cursor += 8;
|
|
800
|
+
}
|
|
801
|
+
return {
|
|
802
|
+
data: {
|
|
803
|
+
glyphIndex,
|
|
804
|
+
dx,
|
|
805
|
+
dy,
|
|
806
|
+
xx,
|
|
807
|
+
xy,
|
|
808
|
+
yx,
|
|
809
|
+
yy
|
|
810
|
+
},
|
|
811
|
+
cursor
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function readF2Dot14(reader, offset) {
|
|
815
|
+
return reader.int16(offset) / 16384;
|
|
816
|
+
}
|
|
817
|
+
function expandImplicitPoints(points) {
|
|
818
|
+
if (points.length === 0) {
|
|
819
|
+
return points;
|
|
820
|
+
}
|
|
821
|
+
const result = [];
|
|
822
|
+
for(let i = 0; i < points.length; i++){
|
|
823
|
+
const current = points[i];
|
|
824
|
+
const next = points[(i + 1) % points.length];
|
|
825
|
+
result.push(current);
|
|
826
|
+
if (!current.onCurve && !next.onCurve) {
|
|
827
|
+
result.push({
|
|
828
|
+
x: (current.x + next.x) * 0.5,
|
|
829
|
+
y: (current.y + next.y) * 0.5,
|
|
830
|
+
onCurve: true
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return result;
|
|
835
|
+
}
|
|
836
|
+
function computeBounds(contours) {
|
|
837
|
+
if (contours.length === 0) {
|
|
838
|
+
return {
|
|
839
|
+
xMin: 0,
|
|
840
|
+
yMin: 0,
|
|
841
|
+
xMax: 0,
|
|
842
|
+
yMax: 0
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
let xMin = Number.POSITIVE_INFINITY;
|
|
846
|
+
let yMin = Number.POSITIVE_INFINITY;
|
|
847
|
+
let xMax = Number.NEGATIVE_INFINITY;
|
|
848
|
+
let yMax = Number.NEGATIVE_INFINITY;
|
|
849
|
+
for (const contour of contours){
|
|
850
|
+
for (const point of contour){
|
|
851
|
+
xMin = Math.min(xMin, point.x);
|
|
852
|
+
yMin = Math.min(yMin, point.y);
|
|
853
|
+
xMax = Math.max(xMax, point.x);
|
|
854
|
+
yMax = Math.max(yMax, point.y);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
xMin,
|
|
859
|
+
yMin,
|
|
860
|
+
xMax,
|
|
861
|
+
yMax
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function buildGlyphData(glyphIndex, advanceWidths, leftSideBearings, contours, xMin, yMin, xMax, yMax) {
|
|
865
|
+
return {
|
|
866
|
+
glyphIndex,
|
|
867
|
+
advanceWidth: advanceWidths[glyphIndex] ?? 0,
|
|
868
|
+
leftSideBearing: leftSideBearings[glyphIndex] ?? 0,
|
|
869
|
+
xMin,
|
|
870
|
+
yMin,
|
|
871
|
+
xMax,
|
|
872
|
+
yMax,
|
|
873
|
+
contours
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function normalizeMSDFAtlasPageSize(value) {
|
|
877
|
+
return Math.max(32, Math.round(Number.isFinite(value) ? value : DEFAULT_MSDF_ATLAS_PAGE_SIZE));
|
|
878
|
+
}
|
|
879
|
+
function normalizeMSDFGlyphSize(value) {
|
|
880
|
+
return Math.max(8, Math.round(Number.isFinite(value) ? value : DEFAULT_MSDF_GLYPH_SIZE));
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export { FontAsset };
|
|
884
|
+
//# sourceMappingURL=font_asset.js.map
|