@cantoo/fontkit 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/dist/browser-module.mjs +4448 -1142
  2. package/dist/browser-module.mjs.map +1 -1
  3. package/dist/browser.cjs +4449 -1142
  4. package/dist/browser.cjs.map +1 -1
  5. package/dist/main.cjs +4449 -1142
  6. package/dist/main.cjs.map +1 -1
  7. package/dist/module.mjs +4448 -1142
  8. package/dist/module.mjs.map +1 -1
  9. package/package.json +4 -1
  10. package/src/CmapProcessor.js +60 -21
  11. package/src/DFont.js +114 -14
  12. package/src/TTFFont.js +260 -62
  13. package/src/TrueTypeCollection.js +59 -15
  14. package/src/WOFF2Font.js +128 -22
  15. package/src/WOFFFont.js +45 -8
  16. package/src/aat/AATFeatureMap.js +83 -40
  17. package/src/aat/AATLayoutEngine.js +34 -2
  18. package/src/aat/AATLookupTable.js +24 -6
  19. package/src/aat/AATMorxProcessor.js +202 -65
  20. package/src/aat/AATStateMachine.js +35 -3
  21. package/src/base.js +20 -1
  22. package/src/cff/CFFCharsets.js +3 -0
  23. package/src/cff/CFFDict.js +81 -11
  24. package/src/cff/CFFEncodings.js +2 -0
  25. package/src/cff/CFFFont.js +120 -24
  26. package/src/cff/CFFIndex.js +66 -9
  27. package/src/cff/CFFOperand.js +53 -28
  28. package/src/cff/CFFPointer.js +41 -2
  29. package/src/cff/CFFPrivateDict.js +34 -24
  30. package/src/cff/CFFStandardStrings.js +68 -67
  31. package/src/cff/CFFTop.js +161 -68
  32. package/src/decorators.js +34 -15
  33. package/src/encodings.js +133 -116
  34. package/src/fs.js +10 -0
  35. package/src/glyph/BBox.js +14 -0
  36. package/src/glyph/CFFGlyph.js +293 -152
  37. package/src/glyph/COLRGlyph.js +40 -5
  38. package/src/glyph/Glyph.js +72 -13
  39. package/src/glyph/GlyphVariationProcessor.js +158 -62
  40. package/src/glyph/Path.js +119 -32
  41. package/src/glyph/SBIXGlyph.js +57 -8
  42. package/src/glyph/StandardNames.js +1 -0
  43. package/src/glyph/TTFGlyph.js +203 -63
  44. package/src/glyph/TTFGlyphEncoder.js +58 -26
  45. package/src/glyph/WOFF2Glyph.js +25 -6
  46. package/src/layout/GlyphPosition.js +6 -0
  47. package/src/layout/GlyphRun.js +28 -10
  48. package/src/layout/KernProcessor.js +63 -4
  49. package/src/layout/LayoutEngine.js +99 -26
  50. package/src/layout/Script.js +52 -26
  51. package/src/layout/UnicodeLayoutEngine.js +34 -10
  52. package/src/opentype/GPOSProcessor.js +156 -45
  53. package/src/opentype/GSUBProcessor.js +92 -33
  54. package/src/opentype/GlyphInfo.js +53 -5
  55. package/src/opentype/GlyphIterator.js +51 -2
  56. package/src/opentype/OTLayoutEngine.js +66 -8
  57. package/src/opentype/OTProcessor.js +284 -71
  58. package/src/opentype/ShapingPlan.js +52 -5
  59. package/src/opentype/shapers/ArabicShaper.js +27 -8
  60. package/src/opentype/shapers/DefaultShaper.js +54 -10
  61. package/src/opentype/shapers/HangulShaper.js +120 -42
  62. package/src/opentype/shapers/HebrewShaper.js +50 -16
  63. package/src/opentype/shapers/IndicShaper.js +159 -44
  64. package/src/opentype/shapers/ThaiShaper.js +50 -19
  65. package/src/opentype/shapers/UniversalShaper.js +83 -17
  66. package/src/opentype/shapers/index.js +48 -35
  67. package/src/opentype/shapers/indic-data.js +2 -2
  68. package/src/packages/unicode-properties/data-trie.js +2 -1
  69. package/src/packages/unicode-properties/data.js +13 -1
  70. package/src/packages/unicode-properties/dist/index.cjs +23 -10
  71. package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
  72. package/src/packages/unicode-properties/dist/index.mjs +22 -10
  73. package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
  74. package/src/packages/unicode-properties/generate.js +3 -4
  75. package/src/packages/unicode-properties/index.js +95 -30
  76. package/src/packages/unicode-properties/package.json +1 -1
  77. package/src/packages/unicode-trie/builder.js +146 -29
  78. package/src/packages/unicode-trie/dist/builder.cjs +135 -25
  79. package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
  80. package/src/packages/unicode-trie/dist/builder.mjs +134 -25
  81. package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
  82. package/src/packages/unicode-trie/dist/index.cjs +22 -9
  83. package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
  84. package/src/packages/unicode-trie/dist/index.mjs +21 -9
  85. package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
  86. package/src/packages/unicode-trie/index.js +63 -12
  87. package/src/packages/unicode-trie/package.json +1 -1
  88. package/src/packages/unicode-trie/swap.js +15 -2
  89. package/src/subset/CFFSubset.js +112 -22
  90. package/src/subset/Subset.js +23 -3
  91. package/src/subset/TTFSubset.js +84 -41
  92. package/src/tables/BASE.js +28 -27
  93. package/src/tables/COLR.js +10 -9
  94. package/src/tables/CPAL.js +2 -1
  95. package/src/tables/DSIG.js +8 -7
  96. package/src/tables/EBDT.js +10 -4
  97. package/src/tables/EBLC.js +70 -10
  98. package/src/tables/GDEF.js +18 -17
  99. package/src/tables/GPOS.js +153 -90
  100. package/src/tables/GSUB.js +50 -39
  101. package/src/tables/HVAR.js +51 -5
  102. package/src/tables/JSTF.js +17 -16
  103. package/src/tables/LTSH.js +4 -3
  104. package/src/tables/OS2.js +50 -49
  105. package/src/tables/PCLT.js +16 -15
  106. package/src/tables/STAT.js +46 -6
  107. package/src/tables/VDMX.js +18 -17
  108. package/src/tables/VORG.js +8 -7
  109. package/src/tables/WOFF2Directory.js +57 -9
  110. package/src/tables/WOFFDirectory.js +32 -20
  111. package/src/tables/aat.js +160 -35
  112. package/src/tables/avar.js +1 -0
  113. package/src/tables/bsln.js +3 -2
  114. package/src/tables/cmap.js +83 -63
  115. package/src/tables/cvt.js +1 -0
  116. package/src/tables/directory.js +56 -28
  117. package/src/tables/feat.js +24 -2
  118. package/src/tables/fpgm.js +1 -0
  119. package/src/tables/fvar.js +32 -4
  120. package/src/tables/gasp.js +6 -5
  121. package/src/tables/glyf.js +2 -1
  122. package/src/tables/gvar.js +29 -2
  123. package/src/tables/hdmx.js +18 -7
  124. package/src/tables/head.js +17 -16
  125. package/src/tables/hhea.js +15 -14
  126. package/src/tables/hmtx.js +18 -2
  127. package/src/tables/index.js +1 -5
  128. package/src/tables/just.js +15 -4
  129. package/src/tables/kern.js +84 -47
  130. package/src/tables/loca.js +23 -10
  131. package/src/tables/maxp.js +16 -15
  132. package/src/tables/morx.js +27 -20
  133. package/src/tables/name.js +120 -39
  134. package/src/tables/opbd.js +2 -1
  135. package/src/tables/opentype.js +126 -91
  136. package/src/tables/post.js +18 -11
  137. package/src/tables/prep.js +1 -0
  138. package/src/tables/sbix.js +12 -1
  139. package/src/tables/variations.js +29 -9
  140. package/src/tables/vhea.js +15 -14
  141. package/src/tables/vmtx.js +20 -4
  142. package/src/utils.js +15 -0
@@ -1,46 +1,61 @@
1
1
  import TTFGlyph, { Point } from './TTFGlyph';
2
2
  import BBox from './BBox';
3
3
 
4
+ /** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
5
+ /** @typedef {import('../../types/fontkit').GlyphComponent} GlyphComponent */
6
+
4
7
  /**
5
8
  * Represents a TrueType glyph in the WOFF2 format, which compresses glyphs differently.
6
9
  */
7
10
  export default class WOFF2Glyph extends TTFGlyph {
8
11
  type = 'WOFF2';
9
12
 
13
+ /**
14
+ * @returns {DecodedGlyf | null}
15
+ */
10
16
  _decode() {
11
- let cached = this._font._transformedGlyphs[this.id];
17
+ let cached = this._font._transformedGlyphs?.[this.id];
12
18
  if (!cached) {
13
19
  return null;
14
20
  }
15
21
 
16
22
  if (!this._font._variationProcessor) {
17
23
  // Non-variable path: return the pre-decoded glyph without copying.
18
- return cached;
24
+ return /** @type {DecodedGlyf} */ (cached);
19
25
  }
20
26
 
21
27
  // Clone so per-instance deltas do not mutate the shared transformed cache.
22
28
  // Transformed glyf has no per-glyph bbox; zeros keep phantom-point math
23
29
  // aligned with hmtx (same approach as a missing bbox bitmap entry).
30
+ /** @type {DecodedGlyf} */
24
31
  let glyph = {
25
32
  numberOfContours: cached.numberOfContours,
26
33
  xMin: 0,
27
34
  yMin: 0,
28
35
  xMax: 0,
29
- yMax: 0,
36
+ yMax: 0
30
37
  };
31
38
 
32
39
  if (cached.points) {
33
- glyph.points = cached.points.map(p => p.copy());
40
+ glyph.points = cached.points.map((p) => {
41
+ if (typeof p.copy === 'function') {
42
+ return p.copy();
43
+ }
44
+ return new Point(p.onCurve, p.endContour, p.x, p.y);
45
+ });
34
46
  let points = glyph.points.concat(this._getPhantomPoints(glyph));
35
47
  this._font._variationProcessor.transformPoints(this.id, points);
36
48
  glyph.phantomPoints = points.slice(-4);
37
49
  }
38
50
 
39
51
  if (cached.components) {
40
- glyph.components = cached.components.map(c => {
41
- return Object.assign(Object.create(Object.getPrototypeOf(c)), c);
52
+ glyph.components = cached.components.map((c) => {
53
+ /** @type {GlyphComponent} */
54
+ let clone = Object.assign(Object.create(Object.getPrototypeOf(c)), c);
55
+ return clone;
42
56
  });
43
57
 
58
+ /** @type {Point[]} */
44
59
  let points = [];
45
60
  for (let component of glyph.components) {
46
61
  points.push(new Point(true, true, component.dx, component.dy));
@@ -59,6 +74,10 @@ export default class WOFF2Glyph extends TTFGlyph {
59
74
  return glyph;
60
75
  }
61
76
 
77
+ /**
78
+ * @param {boolean} [internal]
79
+ * @returns {BBox}
80
+ */
62
81
  _getCBox(internal) {
63
82
  // Avoid path recursion while building phantom points during variation decode.
64
83
  if (internal) {
@@ -2,6 +2,12 @@
2
2
  * Represents positioning information for a glyph in a GlyphRun.
3
3
  */
4
4
  export default class GlyphPosition {
5
+ /**
6
+ * @param {number} [xAdvance]
7
+ * @param {number} [yAdvance]
8
+ * @param {number} [xOffset]
9
+ * @param {number} [yOffset]
10
+ */
5
11
  constructor(xAdvance = 0, yAdvance = 0, xOffset = 0, yOffset = 0) {
6
12
  /**
7
13
  * The amount to move the virtual pen in the X direction after rendering this glyph.
@@ -6,43 +6,50 @@ import * as Script from '../layout/Script';
6
6
  * Returned by the font layout method.
7
7
  */
8
8
  export default class GlyphRun {
9
+ /**
10
+ * @param {import('../glyph/Glyph').default[]} glyphs
11
+ * @param {import('../../types/fontkit').FeatureInput | null | undefined} features
12
+ * @param {import('../../types/fontkit').ScriptTag | string[] | null | undefined} script
13
+ * @param {import('../../types/fontkit').LanguageTag | null | undefined} language
14
+ * @param {import('../../types/fontkit').TextDirection | null | undefined} direction
15
+ */
9
16
  constructor(glyphs, features, script, language, direction) {
10
17
  /**
11
18
  * An array of Glyph objects in the run
12
- * @type {Glyph[]}
19
+ * @type {import('../glyph/Glyph').default[]}
13
20
  */
14
21
  this.glyphs = glyphs;
15
22
 
16
23
  /**
17
24
  * An array of GlyphPosition objects for each glyph in the run
18
- * @type {GlyphPosition[]}
25
+ * @type {import('./GlyphPosition').default[] | null}
19
26
  */
20
27
  this.positions = null;
21
28
 
22
29
  /**
23
30
  * The script that was requested for shaping. This was either passed in or detected automatically.
24
- * @type {string}
31
+ * @type {import('../../types/fontkit').ScriptTag | string[] | null | undefined}
25
32
  */
26
33
  this.script = script;
27
34
 
28
35
  /**
29
36
  * The language requested for shaping, as passed in. If `null`, the default language for the
30
37
  * script was used.
31
- * @type {string}
38
+ * @type {import('../../types/fontkit').LanguageTag | null}
32
39
  */
33
40
  this.language = language || null;
34
41
 
35
42
  /**
36
43
  * The direction requested for shaping, as passed in (either ltr or rtl).
37
44
  * If `null`, the default direction of the script is used.
38
- * @type {string}
45
+ * @type {import('../../types/fontkit').TextDirection}
39
46
  */
40
47
  this.direction = direction || Script.direction(script);
41
48
 
42
49
  /**
43
50
  * The features requested during shaping. This is a combination of user
44
51
  * specified features and features chosen by the shaper.
45
- * @type {object}
52
+ * @type {import('../../types/fontkit').FeatureMap}
46
53
  */
47
54
  this.features = {};
48
55
 
@@ -51,7 +58,7 @@ export default class GlyphRun {
51
58
  for (let tag of features) {
52
59
  this.features[tag] = true;
53
60
  }
54
- } else if (typeof features === 'object') {
61
+ } else if (typeof features === 'object' && features !== null) {
55
62
  this.features = features;
56
63
  }
57
64
  }
@@ -62,6 +69,10 @@ export default class GlyphRun {
62
69
  */
63
70
  get advanceWidth() {
64
71
  let width = 0;
72
+ if (!this.positions) {
73
+ return width;
74
+ }
75
+
65
76
  for (let position of this.positions) {
66
77
  width += position.xAdvance;
67
78
  }
@@ -69,12 +80,16 @@ export default class GlyphRun {
69
80
  return width;
70
81
  }
71
82
 
72
- /**
83
+ /**
73
84
  * The total advance height of the run.
74
85
  * @type {number}
75
86
  */
76
87
  get advanceHeight() {
77
88
  let height = 0;
89
+ if (!this.positions) {
90
+ return height;
91
+ }
92
+
78
93
  for (let position of this.positions) {
79
94
  height += position.yAdvance;
80
95
  }
@@ -82,12 +97,15 @@ export default class GlyphRun {
82
97
  return height;
83
98
  }
84
99
 
85
- /**
100
+ /**
86
101
  * The bounding box containing all glyphs in the run.
87
102
  * @type {BBox}
88
103
  */
89
104
  get bbox() {
90
- let bbox = new BBox;
105
+ let bbox = new BBox();
106
+ if (!this.positions) {
107
+ return bbox;
108
+ }
91
109
 
92
110
  let x = 0;
93
111
  let y = 0;
@@ -1,10 +1,54 @@
1
- import {binarySearch} from '../utils';
1
+ import { binarySearch } from '../utils';
2
+
3
+ /** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
4
+ /** @typedef {import('../../types/fontkit').KernTable} KernTable */
5
+ /** @typedef {import('../../types/fontkit').KernPair} KernPair */
6
+ /** @typedef {import('../../types/fontkit').KernFormat0} KernFormat0 */
7
+ /** @typedef {import('../../types/fontkit').KernFormat2} KernFormat2 */
8
+ /** @typedef {import('../../types/fontkit').KernFormat3} KernFormat3 */
9
+ /** @typedef {import('../../types/fontkit').GlyphInfoLike} GlyphInfoLike */
10
+ /** @typedef {import('../../types/fontkit').LayoutGlyph} LayoutGlyph */
11
+ /** @typedef {import('../../types/fontkit').GlyphPositionLike} GlyphPositionLike */
12
+
13
+ /**
14
+ * @param {unknown} s
15
+ * @returns {s is KernFormat0}
16
+ */
17
+ function isKernFormat0(s) {
18
+ return typeof s === 'object' && s !== null && Array.isArray(/** @type {{ pairs?: unknown }} */ (s).pairs);
19
+ }
20
+
21
+ /**
22
+ * @param {unknown} s
23
+ * @returns {s is KernFormat2}
24
+ */
25
+ function isKernFormat2(s) {
26
+ return typeof s === 'object' && s !== null
27
+ && 'leftTable' in s && 'rightTable' in s && 'array' in s;
28
+ }
29
+
30
+ /**
31
+ * @param {unknown} s
32
+ * @returns {s is KernFormat3}
33
+ */
34
+ function isKernFormat3(s) {
35
+ return typeof s === 'object' && s !== null
36
+ && 'glyphCount' in s && 'kernValue' in s && 'kernIndex' in s;
37
+ }
2
38
 
3
39
  export default class KernProcessor {
40
+ /**
41
+ * @param {LayoutFont} font
42
+ */
4
43
  constructor(font) {
5
- this.kern = font.kern;
44
+ /** @type {KernTable} */
45
+ this.kern = /** @type {KernTable} */ (font.kern);
6
46
  }
7
47
 
48
+ /**
49
+ * @param {Array<LayoutGlyph | GlyphInfoLike>} glyphs
50
+ * @param {GlyphPositionLike[]} positions
51
+ */
8
52
  process(glyphs, positions) {
9
53
  for (let glyphIndex = 0; glyphIndex < glyphs.length - 1; glyphIndex++) {
10
54
  let left = glyphs[glyphIndex].id;
@@ -13,6 +57,11 @@ export default class KernProcessor {
13
57
  }
14
58
  }
15
59
 
60
+ /**
61
+ * @param {number} left
62
+ * @param {number} right
63
+ * @returns {number}
64
+ */
16
65
  getKerning(left, right) {
17
66
  let res = 0;
18
67
 
@@ -42,7 +91,10 @@ export default class KernProcessor {
42
91
  let s = table.subtable;
43
92
  switch (table.format) {
44
93
  case 0: {
45
- let pairIdx = binarySearch(s.pairs, function (pair) {
94
+ if (!isKernFormat0(s)) {
95
+ break;
96
+ }
97
+ let pairIdx = binarySearch(s.pairs, /** @param {KernPair} pair */ (pair) => {
46
98
  return (left - pair.left) || (right - pair.right);
47
99
  });
48
100
 
@@ -54,7 +106,11 @@ export default class KernProcessor {
54
106
  }
55
107
 
56
108
  case 2: {
57
- let leftOffset, rightOffset = 0;
109
+ if (!isKernFormat2(s)) {
110
+ break;
111
+ }
112
+ let leftOffset = 0;
113
+ let rightOffset = 0;
58
114
  if (left >= s.leftTable.firstGlyph && left < s.leftTable.firstGlyph + s.leftTable.nGlyphs) {
59
115
  leftOffset = s.leftTable.offsets[left - s.leftTable.firstGlyph];
60
116
  } else {
@@ -71,6 +127,9 @@ export default class KernProcessor {
71
127
  }
72
128
 
73
129
  case 3: {
130
+ if (!isKernFormat3(s)) {
131
+ break;
132
+ }
74
133
  if (left >= s.glyphCount || right >= s.glyphCount) {
75
134
  return 0;
76
135
  }
@@ -6,32 +6,60 @@ import * as Script from './Script';
6
6
  import AATLayoutEngine from '../aat/AATLayoutEngine';
7
7
  import OTLayoutEngine from '../opentype/OTLayoutEngine';
8
8
 
9
+ /** @typedef {import('../../types/fontkit').LayoutFont} LayoutFont */
10
+ /** @typedef {import('../../types/fontkit').LayoutGlyph} LayoutGlyph */
11
+ /** @typedef {import('../../types/fontkit').FeatureInput} FeatureInput */
12
+ /** @typedef {import('../../types/fontkit').FeatureMap} FeatureMap */
13
+ /** @typedef {import('../../types/fontkit').ScriptTag} ScriptTag */
14
+ /** @typedef {import('../../types/fontkit').LanguageTag} LanguageTag */
15
+ /** @typedef {import('../../types/fontkit').TextDirection} TextDirection */
16
+ /** @typedef {import('../../types/fontkit').AdvancedLayoutEngine} AdvancedLayoutEngine */
17
+ /** @typedef {import('../../types/fontkit').GlyphRunLike} GlyphRunLike */
18
+ /** @typedef {import('../../types/fontkit').GlyphPositionLike} GlyphPositionLike */
19
+ /** @typedef {import('./GlyphRun').default} GlyphRunInstance */
20
+
9
21
  export default class LayoutEngine {
22
+ /**
23
+ * @param {LayoutFont} font
24
+ */
10
25
  constructor(font) {
26
+ /** @type {LayoutFont} */
11
27
  this.font = font;
28
+ /** @type {UnicodeLayoutEngine | null} */
12
29
  this.unicodeLayoutEngine = null;
30
+ /** @type {KernProcessor | null} */
13
31
  this.kernProcessor = null;
32
+ /** @type {AdvancedLayoutEngine | undefined} */
33
+ this.engine = undefined;
14
34
 
15
35
  // Choose an advanced layout engine. We try the AAT morx table first since more
16
36
  // scripts are currently supported because the shaping logic is built into the font.
17
37
  if (this.font.morx) {
18
- this.engine = new AATLayoutEngine(this.font);
19
-
38
+ this.engine = /** @type {AdvancedLayoutEngine} */ (new AATLayoutEngine(this.font));
20
39
  } else if (this.font.GSUB || this.font.GPOS) {
21
- this.engine = new OTLayoutEngine(this.font);
40
+ this.engine = /** @type {AdvancedLayoutEngine} */ (new OTLayoutEngine(this.font));
22
41
  }
23
42
  }
24
43
 
44
+ /**
45
+ * @param {string | LayoutGlyph[]} string
46
+ * @param {FeatureInput | ScriptTag | string[] | null | undefined} [features]
47
+ * @param {ScriptTag | string[] | LanguageTag | null | undefined} [script]
48
+ * @param {LanguageTag | TextDirection | null | undefined} [language]
49
+ * @param {TextDirection | null | undefined} [direction]
50
+ * @returns {GlyphRunInstance}
51
+ */
25
52
  layout(string, features, script, language, direction) {
26
53
  // Make the features parameter optional
27
54
  if (typeof features === 'string') {
28
- direction = language;
29
- language = script;
55
+ direction = /** @type {TextDirection | null | undefined} */ (language);
56
+ language = /** @type {LanguageTag | null | undefined} */ (script);
30
57
  script = features;
31
58
  features = [];
32
59
  }
33
60
 
34
61
  // Map string to glyphs if needed
62
+ /** @type {LayoutGlyph[]} */
35
63
  let glyphs;
36
64
  if (typeof string === 'string') {
37
65
  // Attempt to detect the script from the string if not provided.
@@ -43,6 +71,7 @@ export default class LayoutEngine {
43
71
  } else {
44
72
  // Attempt to detect the script from the glyph code points if not provided.
45
73
  if (script == null) {
74
+ /** @type {number[]} */
46
75
  let codePoints = [];
47
76
  for (let glyph of string) {
48
77
  codePoints.push(...glyph.codePoints);
@@ -54,7 +83,13 @@ export default class LayoutEngine {
54
83
  glyphs = string;
55
84
  }
56
85
 
57
- let glyphRun = new GlyphRun(glyphs, features, script, language, direction);
86
+ let glyphRun = new GlyphRun(
87
+ /** @type {import('../glyph/Glyph').default[]} */ (glyphs),
88
+ /** @type {FeatureInput | null | undefined} */ (features),
89
+ script,
90
+ /** @type {LanguageTag | null | undefined} */ (language),
91
+ /** @type {TextDirection | null | undefined} */ (direction)
92
+ );
58
93
 
59
94
  // Return early if there are no glyphs
60
95
  if (glyphs.length === 0) {
@@ -64,14 +99,16 @@ export default class LayoutEngine {
64
99
 
65
100
  // Setup the advanced layout engine
66
101
  if (this.engine && this.engine.setup) {
67
- this.engine.setup(glyphRun);
102
+ this.engine.setup(/** @type {GlyphRunLike} */ (glyphRun));
68
103
  }
69
104
 
70
105
  // Substitute and position the glyphs
71
106
  this.substitute(glyphRun);
72
107
  this.position(glyphRun);
73
108
 
74
- this.hideDefaultIgnorables(glyphRun.glyphs, glyphRun.positions);
109
+ if (glyphRun.positions) {
110
+ this.hideDefaultIgnorables(/** @type {LayoutGlyph[]} */ (glyphRun.glyphs), glyphRun.positions);
111
+ }
75
112
 
76
113
  // Let the layout engine clean up any state it might have
77
114
  if (this.engine && this.engine.cleanup) {
@@ -81,21 +118,30 @@ export default class LayoutEngine {
81
118
  return glyphRun;
82
119
  }
83
120
 
121
+ /**
122
+ * @param {GlyphRunInstance} glyphRun
123
+ */
84
124
  substitute(glyphRun) {
85
125
  // Call the advanced layout engine to make substitutions
86
126
  if (this.engine && this.engine.substitute) {
87
- this.engine.substitute(glyphRun);
127
+ this.engine.substitute(/** @type {GlyphRunLike} */ (glyphRun));
88
128
  }
89
129
  }
90
130
 
131
+ /**
132
+ * @param {GlyphRunInstance} glyphRun
133
+ */
91
134
  position(glyphRun) {
92
135
  // Get initial glyph positions
93
- glyphRun.positions = glyphRun.glyphs.map(glyph => new GlyphPosition(glyph.advanceWidth));
136
+ glyphRun.positions = /** @type {LayoutGlyph[]} */ (glyphRun.glyphs).map(
137
+ /** @param {LayoutGlyph} glyph */ glyph => new GlyphPosition(glyph.advanceWidth)
138
+ );
139
+ /** @type {FeatureMap | Record<string, import('../../types/fontkit').OTFeature> | null | undefined | false} */
94
140
  let positioned = null;
95
141
 
96
142
  // Call the advanced layout engine. Returns the features applied.
97
143
  if (this.engine && this.engine.position) {
98
- positioned = this.engine.position(glyphRun);
144
+ positioned = this.engine.position(/** @type {GlyphRunLike} */ (glyphRun));
99
145
  }
100
146
 
101
147
  // if there is no GPOS table, use unicode properties to position marks.
@@ -104,7 +150,10 @@ export default class LayoutEngine {
104
150
  this.unicodeLayoutEngine = new UnicodeLayoutEngine(this.font);
105
151
  }
106
152
 
107
- this.unicodeLayoutEngine.positionGlyphs(glyphRun.glyphs, glyphRun.positions);
153
+ this.unicodeLayoutEngine.positionGlyphs(
154
+ /** @type {LayoutGlyph[]} */ (glyphRun.glyphs),
155
+ /** @type {GlyphPositionLike[]} */ (glyphRun.positions)
156
+ );
108
157
  }
109
158
 
110
159
  // if kerning is not supported by GPOS, do kerning with the TrueType/AAT kern table
@@ -113,13 +162,23 @@ export default class LayoutEngine {
113
162
  this.kernProcessor = new KernProcessor(this.font);
114
163
  }
115
164
 
116
- this.kernProcessor.process(glyphRun.glyphs, glyphRun.positions);
165
+ this.kernProcessor.process(
166
+ /** @type {LayoutGlyph[]} */ (glyphRun.glyphs),
167
+ /** @type {GlyphPositionLike[]} */ (glyphRun.positions)
168
+ );
117
169
  glyphRun.features.kern = true;
118
170
  }
119
171
  }
120
172
 
173
+ /**
174
+ * @param {LayoutGlyph[]} glyphs
175
+ * @param {GlyphPositionLike[]} positions
176
+ */
121
177
  hideDefaultIgnorables(glyphs, positions) {
122
178
  let space = this.font.glyphForCodePoint(0x20);
179
+ if (!space) {
180
+ return;
181
+ }
123
182
  for (let i = 0; i < glyphs.length; i++) {
124
183
  if (this.isDefaultIgnorable(glyphs[i].codePoints[0])) {
125
184
  glyphs[i] = space;
@@ -129,6 +188,10 @@ export default class LayoutEngine {
129
188
  }
130
189
  }
131
190
 
191
+ /**
192
+ * @param {number} ch
193
+ * @returns {boolean}
194
+ */
132
195
  isDefaultIgnorable(ch) {
133
196
  // From DerivedCoreProperties.txt in the Unicode database,
134
197
  // minus U+115F, U+1160, U+3164 and U+FFA0, which is what
@@ -137,27 +200,33 @@ export default class LayoutEngine {
137
200
  if (plane === 0) {
138
201
  // BMP
139
202
  switch (ch >> 8) {
140
- case 0x00: return ch === 0x00AD;
141
- case 0x03: return ch === 0x034F;
142
- case 0x06: return ch === 0x061C;
143
- case 0x17: return 0x17B4 <= ch && ch <= 0x17B5;
144
- case 0x18: return 0x180B <= ch && ch <= 0x180E;
145
- case 0x20: return (0x200B <= ch && ch <= 0x200F) || (0x202A <= ch && ch <= 0x202E) || (0x2060 <= ch && ch <= 0x206F);
146
- case 0xFE: return (0xFE00 <= ch && ch <= 0xFE0F) || ch === 0xFEFF;
147
- case 0xFF: return 0xFFF0 <= ch && ch <= 0xFFF8;
148
- default: return false;
203
+ case 0x00: return ch === 0x00AD;
204
+ case 0x03: return ch === 0x034F;
205
+ case 0x06: return ch === 0x061C;
206
+ case 0x17: return 0x17B4 <= ch && ch <= 0x17B5;
207
+ case 0x18: return 0x180B <= ch && ch <= 0x180E;
208
+ case 0x20: return (0x200B <= ch && ch <= 0x200F) || (0x202A <= ch && ch <= 0x202E) || (0x2060 <= ch && ch <= 0x206F);
209
+ case 0xFE: return (0xFE00 <= ch && ch <= 0xFE0F) || ch === 0xFEFF;
210
+ case 0xFF: return 0xFFF0 <= ch && ch <= 0xFFF8;
211
+ default: return false;
149
212
  }
150
213
  } else {
151
214
  // Other planes
152
215
  switch (plane) {
153
- case 0x01: return (0x1BCA0 <= ch && ch <= 0x1BCA3) || (0x1D173 <= ch && ch <= 0x1D17A);
154
- case 0x0E: return 0xE0000 <= ch && ch <= 0xE0FFF;
155
- default: return false;
216
+ case 0x01: return (0x1BCA0 <= ch && ch <= 0x1BCA3) || (0x1D173 <= ch && ch <= 0x1D17A);
217
+ case 0x0E: return 0xE0000 <= ch && ch <= 0xE0FFF;
218
+ default: return false;
156
219
  }
157
220
  }
158
221
  }
159
222
 
223
+ /**
224
+ * @param {ScriptTag | string[] | null | undefined} [script]
225
+ * @param {LanguageTag | null | undefined} [language]
226
+ * @returns {string[]}
227
+ */
160
228
  getAvailableFeatures(script, language) {
229
+ /** @type {string[]} */
161
230
  let features = [];
162
231
 
163
232
  if (this.engine) {
@@ -171,8 +240,12 @@ export default class LayoutEngine {
171
240
  return features;
172
241
  }
173
242
 
243
+ /**
244
+ * @param {number} gid
245
+ * @returns {string[]}
246
+ */
174
247
  stringsForGlyph(gid) {
175
- let result = new Set;
248
+ let result = new Set();
176
249
 
177
250
  let codePoints = this.font._cmapProcessor.codePointsForGlyph(gid);
178
251
  for (let codePoint of codePoints) {
@@ -1,8 +1,11 @@
1
- import {getScript} from '../packages/unicode-properties/index.js';
1
+ import { getScript } from '../packages/unicode-properties/index.js';
2
+
3
+ /** @typedef {import('../../types/fontkit').TextDirection} TextDirection */
2
4
 
3
5
  // This maps the Unicode Script property to an OpenType script tag
4
6
  // Data from http://www.microsoft.com/typography/otspec/scripttags.htm
5
7
  // and http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt.
8
+ /** @type {Record<string, string | string[]>} */
6
9
  const UNICODE_SCRIPTS = {
7
10
  Caucasian_Albanian: 'aghb',
8
11
  Arabic: 'arab',
@@ -133,6 +136,7 @@ const UNICODE_SCRIPTS = {
133
136
  Unknown: 'zzzz'
134
137
  };
135
138
 
139
+ /** @type {Record<string, string>} */
136
140
  const OPENTYPE_SCRIPTS = {};
137
141
  for (let script in UNICODE_SCRIPTS) {
138
142
  let tag = UNICODE_SCRIPTS[script];
@@ -145,14 +149,26 @@ for (let script in UNICODE_SCRIPTS) {
145
149
  }
146
150
  }
147
151
 
152
+ /**
153
+ * @param {string} script
154
+ * @returns {string | string[] | undefined}
155
+ */
148
156
  export function fromUnicode(script) {
149
157
  return UNICODE_SCRIPTS[script];
150
158
  }
151
159
 
160
+ /**
161
+ * @param {string} tag
162
+ * @returns {string | undefined}
163
+ */
152
164
  export function fromOpenType(tag) {
153
165
  return OPENTYPE_SCRIPTS[tag];
154
166
  }
155
167
 
168
+ /**
169
+ * @param {string} string
170
+ * @returns {string | string[]}
171
+ */
156
172
  export function forString(string) {
157
173
  let len = string.length;
158
174
  let idx = 0;
@@ -179,6 +195,10 @@ export function forString(string) {
179
195
  return UNICODE_SCRIPTS.Unknown;
180
196
  }
181
197
 
198
+ /**
199
+ * @param {number[]} codePoints
200
+ * @returns {string | string[]}
201
+ */
182
202
  export function forCodePoints(codePoints) {
183
203
  for (let i = 0; i < codePoints.length; i++) {
184
204
  let codePoint = codePoints[i];
@@ -192,38 +212,44 @@ export function forCodePoints(codePoints) {
192
212
  }
193
213
 
194
214
  // The scripts in this map are written from right to left
215
+ /** @type {Record<string, boolean>} */
195
216
  const RTL = {
196
- arab: true, // Arabic
197
- hebr: true, // Hebrew
198
- syrc: true, // Syriac
199
- thaa: true, // Thaana
200
- cprt: true, // Cypriot Syllabary
201
- khar: true, // Kharosthi
202
- phnx: true, // Phoenician
217
+ arab: true, // Arabic
218
+ hebr: true, // Hebrew
219
+ syrc: true, // Syriac
220
+ thaa: true, // Thaana
221
+ cprt: true, // Cypriot Syllabary
222
+ khar: true, // Kharosthi
223
+ phnx: true, // Phoenician
203
224
  'nko ': true, // N'Ko
204
- lydi: true, // Lydian
205
- avst: true, // Avestan
206
- armi: true, // Imperial Aramaic
207
- phli: true, // Inscriptional Pahlavi
208
- prti: true, // Inscriptional Parthian
209
- sarb: true, // Old South Arabian
210
- orkh: true, // Old Turkic, Orkhon Runic
211
- samr: true, // Samaritan
212
- mand: true, // Mandaic, Mandaean
213
- merc: true, // Meroitic Cursive
214
- mero: true, // Meroitic Hieroglyphs
225
+ lydi: true, // Lydian
226
+ avst: true, // Avestan
227
+ armi: true, // Imperial Aramaic
228
+ phli: true, // Inscriptional Pahlavi
229
+ prti: true, // Inscriptional Parthian
230
+ sarb: true, // Old South Arabian
231
+ orkh: true, // Old Turkic, Orkhon Runic
232
+ samr: true, // Samaritan
233
+ mand: true, // Mandaic, Mandaean
234
+ merc: true, // Meroitic Cursive
235
+ mero: true, // Meroitic Hieroglyphs
215
236
 
216
237
  // Unicode 7.0 (not listed on http://www.microsoft.com/typography/otspec/scripttags.htm)
217
- mani: true, // Manichaean
218
- mend: true, // Mende Kikakui
219
- nbat: true, // Nabataean
220
- narb: true, // Old North Arabian
221
- palm: true, // Palmyrene
222
- phlp: true // Psalter Pahlavi
238
+ mani: true, // Manichaean
239
+ mend: true, // Mende Kikakui
240
+ nbat: true, // Nabataean
241
+ narb: true, // Old North Arabian
242
+ palm: true, // Palmyrene
243
+ phlp: true // Psalter Pahlavi
223
244
  };
224
245
 
246
+ /**
247
+ * @param {string | string[] | null | undefined} script
248
+ * @returns {TextDirection}
249
+ */
225
250
  export function direction(script) {
226
- if (RTL[script]) {
251
+ let tag = Array.isArray(script) ? script[0] : script;
252
+ if (tag && RTL[tag]) {
227
253
  return 'rtl';
228
254
  }
229
255