ntk 8.12.2 → 8.12.3

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/lib/text/font.js CHANGED
@@ -75,6 +75,12 @@ export default class Font {
75
75
  this.path = path;
76
76
  /** stable cache key for glyph pages and font caches */
77
77
  this.key = `${path}#${postscriptName || fkFont.postscriptName || ''}`;
78
+ // What the face's tables say, read once. fontkit's getters walk to the
79
+ // table on every read, and a text layout asks per run and per word: in
80
+ // a long document they were most of a relayout (see `metrics`).
81
+ this._upem = 0;
82
+ this._vertical = null;
83
+ this._space = -1;
78
84
  }
79
85
 
80
86
  static loadSync(path, postscriptName) {
@@ -214,28 +220,48 @@ export default class Font {
214
220
  }
215
221
 
216
222
  scale(size) {
217
- return size / this.fk.unitsPerEm;
223
+ return size / (this._upem ||= this.fk.unitsPerEm);
218
224
  }
219
225
 
220
226
  /**
221
227
  * Pixel-space metrics for a font size. `ascent`/`capHeight`/`xHeight` are
222
228
  * positive (above baseline), `descent` is positive (below baseline).
229
+ *
230
+ * A new object every call, which a caller may keep or change; the numbers
231
+ * behind it are the face's, read from its tables once.
223
232
  */
224
233
  metrics(size) {
234
+ const v = (this._vertical ??= {
235
+ ascent: this.fk.ascent,
236
+ descent: -this.fk.descent,
237
+ lineGap: this.fk.lineGap,
238
+ capHeight: this.fk.capHeight,
239
+ xHeight: this.fk.xHeight
240
+ });
225
241
  const s = this.scale(size);
226
- const ascent = this.fk.ascent * s;
227
- const descent = -this.fk.descent * s;
228
- const lineGap = this.fk.lineGap * s;
242
+ const ascent = v.ascent * s;
243
+ const descent = v.descent * s;
244
+ const lineGap = v.lineGap * s;
229
245
  return {
230
246
  ascent,
231
247
  descent,
232
248
  lineGap,
233
249
  lineHeight: ascent + descent + lineGap,
234
- capHeight: this.fk.capHeight * s,
235
- xHeight: this.fk.xHeight * s
250
+ capHeight: v.capHeight * s,
251
+ xHeight: v.xHeight * s
236
252
  };
237
253
  }
238
254
 
255
+ /**
256
+ * The advance of U+0020 at `size` — what a run of trailing spaces is
257
+ * worth to a line. Looked up once: it was a cmap lookup for every word a
258
+ * layout ended with a space, which in a paragraph is every word.
259
+ */
260
+ spaceAdvance(size) {
261
+ if (this._space < 0) this._space = this.fk.glyphForCodePoint(0x20).advanceWidth;
262
+ return this._space * this.scale(size);
263
+ }
264
+
239
265
  hasGlyph(codepoint) {
240
266
  return this.fk.hasGlyphForCodePoint(codepoint);
241
267
  }
@@ -324,10 +324,8 @@ export class TextLayout {
324
324
  if (last) {
325
325
  const m = TRAILING_WS.exec(last.text);
326
326
  if (m) {
327
- const spaceGlyph = last.span.font.fk.glyphForCodePoint(0x20);
328
327
  const spacing = Number.isFinite(last.span.letterSpacing) ? last.span.letterSpacing : 0;
329
- wsWidth =
330
- m[0].length * (spaceGlyph.advanceWidth * last.span.font.scale(last.span.size) + spacing);
328
+ wsWidth = m[0].length * (last.span.font.spaceAdvance(last.span.size) + spacing);
331
329
  }
332
330
  }
333
331
  return { fragments, width, wsWidth, required, start, end };
package/lib/text/shape.js CHANGED
@@ -25,9 +25,29 @@ function neverSwitches(cp) {
25
25
  */
26
26
  export function embeddingLevels(text, direction = 'auto') {
27
27
  const explicit = direction === 'ltr' || direction === 'rtl' ? direction : null;
28
+ // Text that nothing can make right-to-left is level 0 throughout, which
29
+ // is what the algorithm would work out a character at a time: every
30
+ // layout asks, and most text is Latin, CJK or digits with no bidi at all.
31
+ if (explicit !== 'rtl' && !MAY_REVERSE.test(text)) {
32
+ return {
33
+ levels: new Uint8Array(text.length),
34
+ paragraphs: text.length ? [{ start: 0, end: text.length - 1, level: 0 }] : []
35
+ };
36
+ }
28
37
  return bidi.getEmbeddingLevels(text, explicit);
29
38
  }
30
39
 
40
+ /**
41
+ * What can raise a level in a left-to-right paragraph: a strong
42
+ * right-to-left character (Hebrew, Arabic, Syriac, Thaana, N'Ko and their
43
+ * neighbours, their presentation forms, and the right-to-left blocks of the
44
+ * supplementary planes, found by their high surrogates), an Arabic number,
45
+ * RLM and ALM, and every explicit embedding, override and isolate. Anything
46
+ * else resolves to 0.
47
+ */
48
+ const MAY_REVERSE =
49
+ /[\u0590-\u08FF\u200F\u202A-\u202E\u2066-\u2069\uFB1D-\uFDFF\uFE70-\uFEFF]|[\uD802\uD803\uD83A\uD83B]/;
50
+
31
51
  /**
32
52
  * Compact embedding-levels encoding for the shaping memo key: a single
33
53
  * number when uniform over [start, end) — the common case — else
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntk",
3
- "version": "8.12.2",
3
+ "version": "8.12.3",
4
4
  "description": "Desktop UI toolkit for X11 with canvas-like 2d and OpenGL rendering",
5
5
  "author": "Andrey Sidorov <sidorares@yandex.ru>",
6
6
  "license": "MIT",