ntk 8.17.5 → 8.17.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.
package/lib/fontconfig.js CHANGED
@@ -479,15 +479,18 @@ function slantRank(italic, [lo, hi]) {
479
479
  // Weight, for `weight` asked: between 400 and 500, up to 500 ascending, then
480
480
  // lighter descending, then past 500 ascending; below 400 lighter first, above
481
481
  // 500 heavier first. A variable face's range holds every weight in it.
482
- function weightRank(weight, [lo, hi]) {
482
+ // `regular` and `medium` are 400 and 500 on the scale the weights are on —
483
+ // fontconfig's here, CSS's in StaticFontSource, where an OS/2 weight class can
484
+ // be anything up to 65535 and so each direction is a million past the last.
485
+ export function weightRank(weight, [lo, hi], regular = REGULAR, medium = MEDIUM) {
483
486
  if (lo <= weight && hi >= weight) return 0;
484
487
  const x = hi < weight ? hi : lo;
485
- if (weight >= REGULAR && weight <= MEDIUM) {
486
- if (x > weight && x <= MEDIUM) return x - weight;
487
- return x < weight ? 1000 + weight - x : 2000 + x - weight;
488
+ if (weight >= regular && weight <= medium) {
489
+ if (x > weight && x <= medium) return x - weight;
490
+ return x < weight ? 1e6 + weight - x : 2e6 + x - weight;
488
491
  }
489
- if (weight < REGULAR) return x < weight ? weight - x : 1000 + x - weight;
490
- return x > weight ? x - weight : 1000 + weight - x;
492
+ if (weight < regular) return x < weight ? weight - x : 1e6 + x - weight;
493
+ return x > weight ? x - weight : 1e6 + weight - x;
491
494
  }
492
495
 
493
496
  /**
@@ -54,7 +54,8 @@ import {
54
54
  noFontsError,
55
55
  prewarmFaces,
56
56
  prewarmPatterns,
57
- supported
57
+ supported,
58
+ weightRank
58
59
  } from '../fontconfig.js';
59
60
  import Font from './font.js';
60
61
 
@@ -178,14 +179,16 @@ export class FontconfigFontSource {
178
179
  * source.alias('sans-serif', 'DejaVu Sans');
179
180
  * const app = await createClient({ fontSource: source });
180
181
  *
181
- * Matching mirrors fontconfig semantics: every added face is a fallback
182
+ * Matching mirrors the system path: every added face is a fallback
182
183
  * candidate; faces matching a requested family (in list order) rank first,
183
- * then closest weight, then style. Coverage checks use the real font
184
- * tables, so per-codepoint fallback works exactly like the system path.
184
+ * then within a family the style, then the weight CSS's font matching picks
185
+ * (CSS Fonts 4 §5.2) — as the fontconfig source does, so a family answers
186
+ * alike from either. Coverage checks use the real font tables, so
187
+ * per-codepoint fallback works exactly like the system path.
185
188
  */
186
189
  export class StaticFontSource {
187
190
  constructor() {
188
- this._faces = []; // { font, family, weight, italic, candidate }
191
+ this._faces = []; // { font, family, weight: [lo, hi], italic, candidate }
189
192
  this._aliases = new Map(); // 'sans-serif' -> 'dejavu sans'
190
193
  this._n = 0;
191
194
  /** files a font spec could not parse: [{ file, error }] */
@@ -196,7 +199,9 @@ export class StaticFontSource {
196
199
  * Register a font.
197
200
  * @param {Uint8Array|Buffer|Font} data font file bytes (or an open Font)
198
201
  * @param {object} [opts] { family (alias for matching; defaults to the
199
- * font's family name), weight, style, postscriptName (face of a .ttc) }
202
+ * font's family name), weight, style, postscriptName (face of a .ttc) }.
203
+ * A `weight` is the face's, as @font-face's descriptor is; without one a
204
+ * variable face holds every weight on its `wght` axis.
200
205
  * @returns {Font}
201
206
  */
202
207
  add(data, opts = {}) {
@@ -208,10 +213,12 @@ export class StaticFontSource {
208
213
  postscriptName: opts.postscriptName
209
214
  });
210
215
  const detected = detectStyle(font);
216
+ const axis = font.variationAxes.wght;
217
+ const weight = opts.weight !== undefined ? numericWeight(opts.weight) : detected.weight;
211
218
  this._faces.push({
212
219
  font,
213
220
  family: (opts.family || font.familyName || '').toLowerCase(),
214
- weight: opts.weight !== undefined ? numericWeight(opts.weight) : detected.weight,
221
+ weight: axis && opts.weight === undefined ? [axis.min, axis.max] : [weight, weight],
215
222
  italic:
216
223
  opts.style !== undefined ? String(opts.style).includes('italic') : detected.italic,
217
224
  candidate: null
@@ -234,8 +241,8 @@ export class StaticFontSource {
234
241
  *
235
242
  * This is not cosmetic. Every widget default in the toolkit is
236
243
  * `sans-serif`, and `code`/`pre` ask for `monospace`; with no alias for
237
- * them `matchSorted` ranks every face equally and breaks the tie on weight
238
- * distance and then insertion order, so `monospace` silently renders in a
244
+ * them `matchSorted` ranks every face equally and breaks the tie on style
245
+ * and weight and then insertion order, so `monospace` silently renders in a
239
246
  * proportional face. Inference happens automatically for a source built
240
247
  * from a font spec; a hand-built source calls this when it wants it.
241
248
  *
@@ -278,16 +285,19 @@ export class StaticFontSource {
278
285
  const weight = numericWeight(pattern.weight);
279
286
  const italic = !!(pattern.style && String(pattern.style).includes('italic'));
280
287
 
288
+ // family, then style, then weight in CSS's order; the sort is stable, so
289
+ // faces alike in all three stay in the order they were added
281
290
  const scored = this._faces.map((face) => {
282
291
  let rank = families.indexOf(face.family);
283
292
  if (rank === -1) rank = families.length; // non-matching faces trail
284
293
  return {
285
294
  face,
286
- score:
287
- rank * 1e6 + (face.italic !== italic ? 1e4 : 0) + Math.abs(face.weight - weight)
295
+ ranks: [rank, face.italic !== italic ? 1 : 0, weightRank(weight, face.weight, 400, 500)]
288
296
  };
289
297
  });
290
- scored.sort((a, b) => a.score - b.score);
298
+ scored.sort(
299
+ (a, b) => a.ranks[0] - b.ranks[0] || a.ranks[1] - b.ranks[1] || a.ranks[2] - b.ranks[2]
300
+ );
291
301
  return scored.map(({ face }) => {
292
302
  if (!face.candidate) {
293
303
  // the font's own family name, not the (lowercased, possibly aliased)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntk",
3
- "version": "8.17.5",
3
+ "version": "8.17.6",
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",