react-x11 1.2.0 → 2.0.0
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/README.md +330 -81
- package/package.json +132 -14
- package/src/ClickToComponent.js +9 -2
- package/src/DevToolsIntegration.js +440 -32
- package/src/Reconciler.js +463 -165
- package/src/a11y.js +1299 -0
- package/src/acceleratorhooks.js +86 -0
- package/src/accelerators.js +128 -0
- package/src/activate.js +168 -0
- package/src/anchor.js +360 -0
- package/src/appcontext.js +161 -0
- package/src/appearance.js +748 -0
- package/src/appearancehooks.js +96 -0
- package/src/apphooks.js +73 -0
- package/src/application.js +748 -0
- package/src/atspi.js +1953 -0
- package/src/bus.js +545 -0
- package/src/bushooks.js +136 -0
- package/src/clientmessage.js +140 -0
- package/src/clipboard.js +121 -0
- package/src/components/Button.js +148 -33
- package/src/components/Checkbox.js +66 -32
- package/src/components/Dialog.js +96 -29
- package/src/components/FileDialog.js +491 -0
- package/src/components/Icon.js +329 -0
- package/src/components/Menu.js +797 -133
- package/src/components/PasswordInput.js +434 -0
- package/src/components/ProgressBar.js +91 -12
- package/src/components/Radio.js +78 -35
- package/src/components/Select.js +255 -65
- package/src/components/Slider.js +102 -41
- package/src/components/SplitPane.js +206 -0
- package/src/components/Switch.js +100 -22
- package/src/components/Table.js +529 -0
- package/src/components/Tabs.js +211 -0
- package/src/components/Tooltip.js +528 -54
- package/src/components/anchor.js +154 -107
- package/src/components/change.js +34 -0
- package/src/components/dnd.js +103 -0
- package/src/components/index.js +16 -3
- package/src/components/keys.js +17 -20
- package/src/components/scribble.js +247 -0
- package/src/components/theme.js +350 -42
- package/src/components/typeahead.js +12 -2
- package/src/compose.js +868 -0
- package/src/compositing.js +224 -0
- package/src/dbusmenu.js +384 -0
- package/src/debug.d.ts +63 -0
- package/src/debug.js +642 -0
- package/src/decorations.js +486 -0
- package/src/desktopsettings.js +209 -0
- package/src/desktopsettingshooks.js +62 -0
- package/src/dnd.js +1725 -0
- package/src/editmenu.js +272 -0
- package/src/errors.js +98 -0
- package/src/events.js +1406 -162
- package/src/extensions.js +45 -0
- package/src/filedialog.js +375 -0
- package/src/filedialoghooks.js +132 -0
- package/src/fonthooks.js +64 -0
- package/src/fonts.js +301 -0
- package/src/foreignnodes.js +519 -0
- package/src/frame/child.js +33 -0
- package/src/frame/childmain.js +274 -0
- package/src/frame/env.js +140 -0
- package/src/frame/index.js +444 -0
- package/src/frame/lifecycle.js +67 -0
- package/src/frame/protocol.js +179 -0
- package/src/frames.js +73 -0
- package/src/glbackend.js +114 -0
- package/src/glnodes.js +125 -67
- package/src/globalmenu.js +683 -0
- package/src/host.d.ts +88 -0
- package/src/host.js +42 -0
- package/src/idle.js +490 -0
- package/src/idlehooks.js +100 -0
- package/src/imagesource.js +349 -0
- package/src/index.d.ts +475 -0
- package/src/index.js +83 -9
- package/src/inputtime.js +206 -0
- package/src/jsx-dev-runtime.d.ts +21 -0
- package/src/jsx-dev-runtime.js +2 -0
- package/src/jsx-runtime.d.ts +39 -0
- package/src/jsx-runtime.js +6 -0
- package/src/keyboard.js +256 -0
- package/src/keyboardstate.js +278 -0
- package/src/keyboardstatehooks.js +58 -0
- package/src/keysyms.d.ts +149 -0
- package/src/keysyms.js +270 -0
- package/src/locale.js +170 -0
- package/src/localehooks.js +47 -0
- package/src/menuitem.js +223 -0
- package/src/node.d.ts +597 -0
- package/src/node.js +44 -0
- package/src/nodes.js +9546 -690
- package/src/ntk.d.ts +44 -0
- package/src/ntk.js +25 -0
- package/src/paintcache.js +366 -0
- package/src/palette.js +380 -0
- package/src/pastestate.js +66 -0
- package/src/portal.js +461 -0
- package/src/priority.js +26 -0
- package/src/refresh/index.d.ts +40 -0
- package/src/refresh/index.js +122 -0
- package/src/refresh/loader.d.ts +37 -0
- package/src/refresh/loader.js +401 -0
- package/src/refresh/register.d.ts +5 -0
- package/src/refresh/register.js +13 -0
- package/src/registry.js +232 -0
- package/src/scale.js +626 -0
- package/src/scalehooks.js +27 -0
- package/src/screencolor.js +640 -0
- package/src/screencolorhooks.js +101 -0
- package/src/screens.js +754 -0
- package/src/screenshooks.js +137 -0
- package/src/startup.js +302 -0
- package/src/style.d.ts +126 -0
- package/src/style.js +33 -0
- package/src/styles.js +1482 -6
- package/src/svgnodes.js +306 -0
- package/src/testing/a11y.js +484 -0
- package/src/testing/components.js +414 -0
- package/src/testing/events.js +407 -0
- package/src/testing/harness.js +455 -0
- package/src/testing/index.d.ts +558 -0
- package/src/testing/index.js +85 -0
- package/src/testing/mock-app.js +463 -0
- package/src/testing/pixels.js +152 -0
- package/src/testing/queries.js +224 -0
- package/src/textrange.js +83 -0
- package/src/textselection.js +439 -0
- package/src/trace-registry.js +63 -0
- package/src/transfer.js +93 -0
- package/src/types/appearance.d.ts +84 -0
- package/src/types/application.d.ts +173 -0
- package/src/types/components.d.ts +820 -0
- package/src/types/dbus.d.ts +177 -0
- package/src/types/elements.d.ts +897 -0
- package/src/types/events.d.ts +549 -0
- package/src/types/filedialog.d.ts +260 -0
- package/src/types/fonts.d.ts +124 -0
- package/src/types/frame.d.ts +146 -0
- package/src/types/globalmenu.d.ts +42 -0
- package/src/types/nodes.d.ts +199 -0
- package/src/types/screencolor.d.ts +84 -0
- package/src/types/style.d.ts +434 -0
- package/src/types/system.d.ts +287 -0
- package/src/windowid.js +151 -0
- package/src/windowstate.js +393 -0
- package/src/xsettings.js +336 -0
- package/src/yoga.d.ts +55 -0
- package/src/yoga.js +135 -0
- package/src/components/Canvas3D.js +0 -28
- package/src/geometry3d.js +0 -223
- package/src/pointer3d.js +0 -158
- package/src/raycast3d.js +0 -146
- package/src/richnodes.js +0 -436
- package/src/scene3d.js +0 -683
package/src/fonts.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// Opening a font file, from the application's side of the seam.
|
|
2
|
+
//
|
|
3
|
+
// An app that wants to *look at* a font — a picker, a specimen, a
|
|
4
|
+
// preferences page that shows a family name — or to *ship* one had to leave
|
|
5
|
+
// react-x11 to do either (issue #346). The reading half was ntk's own class,
|
|
6
|
+
// `Font.loadSync(path)`, reached through an import an application author has
|
|
7
|
+
// no reason to know exists; the drawing half was a second, unrelated call,
|
|
8
|
+
// `app.fonts.load(path, { family: 'a-name-i-made-up' })`, and the made-up
|
|
9
|
+
// name then had to be kept consistent between the two by hand. Two calls,
|
|
10
|
+
// two mental models, and a string with no author.
|
|
11
|
+
//
|
|
12
|
+
// The two verbs here are the two things that were being confused:
|
|
13
|
+
//
|
|
14
|
+
// openFont(app, source) read the file. Nothing about the app changes.
|
|
15
|
+
// loadFont(app, source) read it *and* make it drawable, under a family
|
|
16
|
+
// name taken off the file and handed back.
|
|
17
|
+
//
|
|
18
|
+
// **Why opening does not register.** A registered face is not just an entry
|
|
19
|
+
// in a lookup table: ntk consults every registered font, in registration
|
|
20
|
+
// order, for *any codepoint the current face is missing* (`fallbackFor`), so
|
|
21
|
+
// an app that registered each file it merely previewed would quietly change
|
|
22
|
+
// which face draws the bullets and the curly quotes in its own UI. Reading a
|
|
23
|
+
// file and installing a font are different acts, so they are different
|
|
24
|
+
// calls, and the one whose name says "open" is the one that changes nothing.
|
|
25
|
+
//
|
|
26
|
+
// **Why both go through `app.fonts`.** The manager is the connection's one
|
|
27
|
+
// font cache, and a `Font` carries a glyph cache and a server-side glyphset
|
|
28
|
+
// with it: a second copy of a face is a second atlas uploaded over the wire,
|
|
29
|
+
// and a node built against one copy cannot be painted by the other. So
|
|
30
|
+
// neither verb parses a file the manager has already read — `loadFont` after
|
|
31
|
+
// `openFont` on the same path re-uses the open face rather than opening a
|
|
32
|
+
// second one, and a face fontconfig already matched is not re-read either.
|
|
33
|
+
//
|
|
34
|
+
// The same rule is why `react-x11/ntk` exists, for the code that does need
|
|
35
|
+
// `Font` itself: import ntk through react-x11, never as a dependency of its
|
|
36
|
+
// own (docs/styling.md#a-font-file-of-your-own).
|
|
37
|
+
|
|
38
|
+
import { basename, extname } from 'node:path';
|
|
39
|
+
|
|
40
|
+
import { Font } from 'ntk';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Per-app bookkeeping. Keyed on the app rather than stored on it, like
|
|
44
|
+
* `inputtime.js`: one process can drive several roots on several
|
|
45
|
+
* connections, and a family registered on one is not registered on another.
|
|
46
|
+
*
|
|
47
|
+
* `opened` maps a source key to the handle for it, which is what makes both
|
|
48
|
+
* verbs idempotent — a component that calls `useFont` on every render
|
|
49
|
+
* re-reads nothing and registers nothing twice. `claims` is the family
|
|
50
|
+
* namespace: `family|weight|italic` -> the key of the file that holds it.
|
|
51
|
+
*/
|
|
52
|
+
const state = new WeakMap();
|
|
53
|
+
|
|
54
|
+
function stateFor(app) {
|
|
55
|
+
let s = state.get(app);
|
|
56
|
+
if (!s) {
|
|
57
|
+
s = { opened: new Map(), dataKeys: new WeakMap(), claims: new Map(), n: 0 };
|
|
58
|
+
state.set(app, s);
|
|
59
|
+
}
|
|
60
|
+
return s;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function managerOf(app, fn) {
|
|
64
|
+
const fonts = app?.fonts;
|
|
65
|
+
if (!fonts) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`react-x11: ${fn}(app, …) needs the ntk connection — the app from ` +
|
|
68
|
+
'`createRoot({ app })` or `useApp()`, which is what carries `app.fonts`',
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return fonts;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The cache key for a source.
|
|
76
|
+
*
|
|
77
|
+
* A path's key is the one ntk computes for itself
|
|
78
|
+
* (`` `${path}#${postscriptName}` ``, its font.js) rather than one of ours,
|
|
79
|
+
* which is what makes `loadFont`'s `app.fonts.load()` land on the face
|
|
80
|
+
* `openFont` already opened instead of parsing the file a second time.
|
|
81
|
+
*
|
|
82
|
+
* Bytes have no name to key on, so they are keyed by the identity of the
|
|
83
|
+
* buffer they arrived in — the same array is the same font, a copy of it is
|
|
84
|
+
* not, which is the only answer available without hashing megabytes.
|
|
85
|
+
*/
|
|
86
|
+
function keyFor(st, source, postscriptName) {
|
|
87
|
+
if (typeof source === 'string') return `${source}#${postscriptName || ''}`;
|
|
88
|
+
let key = st.dataKeys.get(source);
|
|
89
|
+
if (!key) {
|
|
90
|
+
key = `data:${st.n++}#${postscriptName || ''}`;
|
|
91
|
+
st.dataKeys.set(source, key);
|
|
92
|
+
}
|
|
93
|
+
return key;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* ntk's cache, reached through the method that owns it.
|
|
98
|
+
*
|
|
99
|
+
* `_open` is underscored because ntk has no public "open without
|
|
100
|
+
* registering" — this module *is* that route from an application, which is
|
|
101
|
+
* the whole of issue #346. If a future ntk renames it, parsing directly is
|
|
102
|
+
* the honest fallback: a duplicated face costs memory and an extra glyph
|
|
103
|
+
* atlas, where throwing would cost the feature entirely.
|
|
104
|
+
*/
|
|
105
|
+
function openThrough(fonts, candidate) {
|
|
106
|
+
if (typeof fonts._open === 'function') return fonts._open(candidate);
|
|
107
|
+
return candidate.path !== undefined
|
|
108
|
+
? Font.loadSync(candidate.path, candidate.postscriptName)
|
|
109
|
+
: Font.fromData(candidate.data, candidate);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** `/usr/share/fonts/Inter-Variable.ttf` -> `Inter-Variable`. */
|
|
113
|
+
function stemOf(source) {
|
|
114
|
+
if (typeof source !== 'string') return '';
|
|
115
|
+
return basename(source, extname(source));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Where in a family this face sits — `weight|italic`, the two things ntk
|
|
120
|
+
* resolves a family's faces by.
|
|
121
|
+
*
|
|
122
|
+
* This mirrors ntk's own `detectStyle`, which is not exported. Mirroring is
|
|
123
|
+
* safe here in a way it would not be on the paint path: the answer only
|
|
124
|
+
* decides what a file is *named*, so drifting from ntk costs a scoped alias
|
|
125
|
+
* where none was needed, never a wrong glyph.
|
|
126
|
+
*/
|
|
127
|
+
function faceOf(font, opts) {
|
|
128
|
+
const os2 = font.fk?.['OS/2'];
|
|
129
|
+
const weight = opts.weight ?? (os2 ? os2.usWeightClass : 400);
|
|
130
|
+
const italic =
|
|
131
|
+
opts.style !== undefined
|
|
132
|
+
? /italic|oblique/i.test(opts.style)
|
|
133
|
+
: !!(
|
|
134
|
+
os2?.fsSelection?.italic ||
|
|
135
|
+
font.fk?.italicAngle ||
|
|
136
|
+
/italic|oblique/i.test(font.fk?.subfamilyName || '')
|
|
137
|
+
);
|
|
138
|
+
return `${weight}|${italic}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The family name a loaded file is registered under, when the caller did not
|
|
143
|
+
* name one.
|
|
144
|
+
*
|
|
145
|
+
* **The font's own name**, which is the decision worth stating: an app that
|
|
146
|
+
* ships `Inter.ttf` wants `fontFamily: 'Inter'` to mean *its* Inter, in
|
|
147
|
+
* every style it has already written, and registered faces beat fontconfig
|
|
148
|
+
* in `match()` — this is `@font-face` behaving the way the web taught
|
|
149
|
+
* everyone it behaves.
|
|
150
|
+
*
|
|
151
|
+
* **Scoped only when the face would be unreachable**, which is the other
|
|
152
|
+
* half. A family is a set of faces, so `Inter-Regular.ttf` and
|
|
153
|
+
* `Inter-Bold.ttf` both want to be "Inter" and `fontWeight` picks between
|
|
154
|
+
* them — that is a family being assembled, not a collision. Two *different*
|
|
155
|
+
* files claiming the same family at the same weight and slant are a
|
|
156
|
+
* collision, because ntk resolves that tie to whichever was registered first
|
|
157
|
+
* and the second file would never draw: it gets `Ubuntu 2`. Nobody has to
|
|
158
|
+
* know that happened — the family to draw with is the one `loadFont` hands
|
|
159
|
+
* back, and a caller that always uses it is always right.
|
|
160
|
+
*
|
|
161
|
+
* A name the `family` option asked for is used verbatim: two faces under one
|
|
162
|
+
* alias is a thing a caller may well mean, and second-guessing a name
|
|
163
|
+
* somebody chose is worse than the tie. It still claims its slot, so a later
|
|
164
|
+
* auto-derived name never lands on top of it.
|
|
165
|
+
*/
|
|
166
|
+
function claim(st, key, base, face, verbatim) {
|
|
167
|
+
const slot = (name) => `${name.toLowerCase()}|${face}`;
|
|
168
|
+
const held = (name) => st.claims.get(slot(name));
|
|
169
|
+
if (verbatim || !held(base) || held(base) === key) {
|
|
170
|
+
st.claims.set(slot(base), key);
|
|
171
|
+
return base;
|
|
172
|
+
}
|
|
173
|
+
for (let n = 2; ; n++) {
|
|
174
|
+
const alias = `${base} ${n}`;
|
|
175
|
+
if (!held(alias) || held(alias) === key) {
|
|
176
|
+
st.claims.set(slot(alias), key);
|
|
177
|
+
return alias;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Read a font file: metrics, coverage, variation axes.
|
|
184
|
+
*
|
|
185
|
+
* ```js
|
|
186
|
+
* const font = openFont(app, '/usr/share/fonts/truetype/inter/Inter.ttf');
|
|
187
|
+
* font.familyName; // 'Inter'
|
|
188
|
+
* font.metrics(30); // { ascent, descent, lineGap, capHeight, … }
|
|
189
|
+
* font.variationAxes; // { wght: { name, min, default, max } }
|
|
190
|
+
* font.hasGlyph(0x20b8); // is the tenge sign in this face?
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* `source` is a path to a `.ttf`/`.otf`/`.ttc`/`.woff`/`.woff2`, or the
|
|
194
|
+
* file's bytes. `postscriptName` picks one face out of a `.ttc` collection
|
|
195
|
+
* (the first otherwise).
|
|
196
|
+
*
|
|
197
|
+
* **Nothing about the app changes.** The face is not registered, so it is
|
|
198
|
+
* not a candidate for any `fontFamily` — and, the part that is easy to miss,
|
|
199
|
+
* it does not join the fallback chain either, which is what stops a font
|
|
200
|
+
* browser from changing the glyphs its own UI falls back to. {@link loadFont}
|
|
201
|
+
* is the call for when the point is to draw with it.
|
|
202
|
+
*
|
|
203
|
+
* The file is read once per app: the face is cached in the connection's font
|
|
204
|
+
* manager, so opening it again — every render, every repaint — hands back
|
|
205
|
+
* the same `Font`, and so does {@link loadFont} afterwards.
|
|
206
|
+
*
|
|
207
|
+
* Throws what ntk throws for a file that is missing or unparseable. When the
|
|
208
|
+
* path came from a user (a file dialog), that is a `try`/`catch` around this
|
|
209
|
+
* call rather than an error boundary.
|
|
210
|
+
*
|
|
211
|
+
* @param {object} app the ntk connection (`useApp()`)
|
|
212
|
+
* @param {string|Uint8Array|Buffer} source path, or the font file's bytes
|
|
213
|
+
* @param {{ postscriptName?: string }} [opts]
|
|
214
|
+
* @returns {object} ntk's `Font`
|
|
215
|
+
*/
|
|
216
|
+
export function openFont(app, source, opts = {}) {
|
|
217
|
+
const fonts = managerOf(app, 'openFont');
|
|
218
|
+
const st = stateFor(app);
|
|
219
|
+
const key = keyFor(st, source, opts.postscriptName);
|
|
220
|
+
const known = st.opened.get(key);
|
|
221
|
+
if (known) return known.font;
|
|
222
|
+
const font = openThrough(fonts, {
|
|
223
|
+
key,
|
|
224
|
+
postscriptName: opts.postscriptName,
|
|
225
|
+
...(typeof source === 'string' ? { path: source } : { data: source }),
|
|
226
|
+
});
|
|
227
|
+
st.opened.set(key, { font, family: null });
|
|
228
|
+
return font;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Read a font file **and** register it, so `fontFamily` can name it.
|
|
233
|
+
*
|
|
234
|
+
* ```jsx
|
|
235
|
+
* const { font, family } = loadFont(app, '/path/to/Inter.ttf');
|
|
236
|
+
* <text style={{ fontFamily: family, fontSize: 20 }}>Handgloves</text>;
|
|
237
|
+
* ```
|
|
238
|
+
*
|
|
239
|
+
* `family` is read off the file rather than invented by the caller: the
|
|
240
|
+
* font's own name, so an app that ships `Inter.ttf` can go on writing
|
|
241
|
+
* `fontFamily: 'Inter'` in the styles it already has. Several faces of one
|
|
242
|
+
* family — regular, bold, italic — all keep that name and `fontWeight` picks
|
|
243
|
+
* between them. Only a second file that would be *unreachable* under it,
|
|
244
|
+
* same family at the same weight and slant, is scoped: it comes back as
|
|
245
|
+
* `Inter 2`. Draw with the name that comes back and the question never
|
|
246
|
+
* arises.
|
|
247
|
+
*
|
|
248
|
+
* A registered face wins over fontconfig for its name, which is the point:
|
|
249
|
+
* an app that ships a font means the one it ships. Pass `family` to name it
|
|
250
|
+
* something else — `loadFont(app, picked, { family: 'preview' })` keeps a
|
|
251
|
+
* file the user chose out of the way of the app's own type. `weight` and
|
|
252
|
+
* `style` override what the file says about itself; `postscriptName` picks a
|
|
253
|
+
* face out of a `.ttc`.
|
|
254
|
+
*
|
|
255
|
+
* Registering also puts the face in the fallback chain, ahead of the system
|
|
256
|
+
* fonts: text in some other family that has no glyph for a character borrows
|
|
257
|
+
* it from here before fontconfig is asked. That is what an app shipping a
|
|
258
|
+
* symbol or icon face wants, and {@link openFont} is the call for when it is
|
|
259
|
+
* not.
|
|
260
|
+
*
|
|
261
|
+
* Idempotent per app and file: calling it again returns the same handle and
|
|
262
|
+
* registers nothing twice.
|
|
263
|
+
*
|
|
264
|
+
* @param {object} app the ntk connection (`useApp()`)
|
|
265
|
+
* @param {string|Uint8Array|Buffer} source path, or the font file's bytes
|
|
266
|
+
* @param {{ family?: string, weight?: number|string, style?: string,
|
|
267
|
+
* postscriptName?: string }} [opts]
|
|
268
|
+
* @returns {{ font: object, family: string }} ntk's `Font`, and the family
|
|
269
|
+
* name to draw it with
|
|
270
|
+
*/
|
|
271
|
+
export function loadFont(app, source, opts = {}) {
|
|
272
|
+
const fonts = managerOf(app, 'loadFont');
|
|
273
|
+
const st = stateFor(app);
|
|
274
|
+
const key = keyFor(st, source, opts.postscriptName);
|
|
275
|
+
const known = st.opened.get(key);
|
|
276
|
+
if (known?.family && (!opts.family || opts.family === known.family)) {
|
|
277
|
+
return known;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Opened first, because the name to register under is read off the face —
|
|
281
|
+
// and this is the read that does not happen twice: for a path, `load()`
|
|
282
|
+
// below computes the same cache key and gets this very font back.
|
|
283
|
+
const font = openFont(app, source, opts);
|
|
284
|
+
const family = claim(
|
|
285
|
+
st,
|
|
286
|
+
key,
|
|
287
|
+
opts.family || font.familyName || stemOf(source) || 'font',
|
|
288
|
+
faceOf(font, opts),
|
|
289
|
+
!!opts.family,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const registered = fonts.load(source, { ...opts, family });
|
|
293
|
+
// Bytes are the one source `load` cannot look up: the key it makes for
|
|
294
|
+
// them counts entries rather than naming the data, so it parses the buffer
|
|
295
|
+
// again and the face above is not the one that got registered. Keep the
|
|
296
|
+
// registered face — it is the one that will be drawn with, and so the one
|
|
297
|
+
// whose glyph cache is worth sharing — and let the other go.
|
|
298
|
+
const handle = { font: registered ?? font, family };
|
|
299
|
+
st.opened.set(key, handle);
|
|
300
|
+
return handle;
|
|
301
|
+
}
|