@gjsify/gtk-host 0.46.0 → 0.47.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/lib/esm/font-dir.js +1 -0
- package/lib/esm/fonts.js +1 -0
- package/lib/types/font-dir.d.ts +42 -0
- package/lib/types/font-dir.spec.d.ts +2 -0
- package/lib/types/fonts.d.ts +64 -0
- package/lib/types/fonts.spec.d.ts +2 -0
- package/package.json +9 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";const e=[`.ttf`,`.otf`,`.ttc`,`.otc`];function resolveFontDir(e={}){return nonEmpty(e.fontDir)??nonEmpty(e.env?.GJSIFY_FONT_DIR)}function isFontFace(t){let n=t.toLowerCase();return e.some(e=>n.endsWith(e))}function nonEmpty(e){if(e===void 0)return;let t=e.trim();return t===``?void 0:t}export{e as FONT_FACE_EXTENSIONS,isFontFace,resolveFontDir};
|
package/lib/esm/fonts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{isFontFace as e,resolveFontDir as t}from"./font-dir.js";import n from"gi://GLib?version=2.0";import r from"gi://Gio?version=2.0";import i from"gi://PangoCairo?version=1.0";function isUnsupportedByFontMap(e){return e instanceof n.Error&&e.matches(r.io_error_quark(),r.IOErrorEnum.NOT_SUPPORTED)}function initFonts(e={}){let a=t({...e,env:{GJSIFY_FONT_DIR:n.getenv(`GJSIFY_FONT_DIR`)??void 0}}),o=[],s=[],c=[];if(a===void 0)return{dir:a,registered:o,declined:s,failed:c};let l=[];collectFaces(r.File.new_for_path(a),l,c);let u=i.FontMap.get_default();for(let e of l.sort())try{u.add_font_file(e),o.push(e)}catch(t){if(isUnsupportedByFontMap(t)){s.push(e);continue}c.push({path:e,message:messageOf(t)})}for(let e of c)console.warn(`initFonts: ${e.path} could not be read as an application font (${e.message}). Text asking for a family this application ships will render in a substituted one.`);return{dir:a,registered:o,declined:s,failed:c}}function collectFaces(t,n,i){try{let a=t.enumerate_children(`standard::name,standard::type`,r.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null);for(let t=a.next_file(null);t!==null;t=a.next_file(null)){let o=a.get_child(t),s=o.get_path();s!==null&&(t.get_file_type()===r.FileType.DIRECTORY?collectFaces(o,n,i):e(t.get_name())&&n.push(s))}}catch(e){i.push({path:t.get_path()??``,message:messageOf(e)})}}function messageOf(e){return e instanceof n.Error||e instanceof Error?e.message:String(e)}export{initFonts,isUnsupportedByFontMap};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The desktop face extensions `gjsify ship` stages, lower-cased.
|
|
3
|
+
*
|
|
4
|
+
* The same four the writing half accepts (`discoverFonts`, ADR 0038), and deliberately not a
|
|
5
|
+
* shared constant: this package cannot depend on `@gjsify/cli` — wrong direction and wrong tier —
|
|
6
|
+
* so the pairing is a specification both ends implement rather than a value one imports.
|
|
7
|
+
*
|
|
8
|
+
* `.woff`/`.woff2`/`.eot` are absent from BOTH ends. The writer refuses them by name because
|
|
9
|
+
* whether FreeType opens one is `FT_CONFIG_OPTION_USE_BROTLI`, a build option of whichever
|
|
10
|
+
* FreeType the SHIPPED artifact loads rather than of the packaging host; a reader that accepted
|
|
11
|
+
* them would hand the font map a file it may decline on the target and nowhere else.
|
|
12
|
+
*/
|
|
13
|
+
export declare const FONT_FACE_EXTENSIONS: readonly string[];
|
|
14
|
+
export interface ResolveFontDirOptions {
|
|
15
|
+
/** Wins over everything — a dev tree (`data/fonts`), or a test fixture. */
|
|
16
|
+
fontDir?: string;
|
|
17
|
+
/** Environment to read `GJSIFY_FONT_DIR` from. */
|
|
18
|
+
env?: Record<string, string | undefined>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the directory holding the application's shipped faces, or `undefined` when none is named.
|
|
22
|
+
*
|
|
23
|
+
* Precedence: explicit option, then `GJSIFY_FONT_DIR` — exported by the `gjsify ship` launcher,
|
|
24
|
+
* which is the only party that knows whether the payload became `/usr`, a `--prefix` tree, `/app`,
|
|
25
|
+
* a bundle's `Contents/Resources` or a Windows program directory.
|
|
26
|
+
*
|
|
27
|
+
* There is NO system fallback, and that is the one place this parts company with
|
|
28
|
+
* `resolveLocaleDir`, whose `/usr/share/locale` default is both correct and free. The analogous
|
|
29
|
+
* `/usr/share/fonts` is neither: fontconfig has already scanned it, so walking it again would add
|
|
30
|
+
* hundreds of `add_font_file` calls to every startup to arrive at families the font map holds
|
|
31
|
+
* anyway. `undefined` therefore means "this application ships no faces of its own" — the ordinary
|
|
32
|
+
* case, and a quiet one, because the launcher exports the variable only when it staged a face.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveFontDir(options?: ResolveFontDirOptions): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Is `name` a face {@link FONT_FACE_EXTENSIONS} covers?
|
|
37
|
+
*
|
|
38
|
+
* Extension-only and case-insensitive: `BRAND.TTF` is an ordinary name on the case-preserving
|
|
39
|
+
* filesystems macOS and Windows have, and a lowercase-only test would drop it into exactly the
|
|
40
|
+
* substituted-family failure this mechanism exists to prevent.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isFontFace(name: string): boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type ResolveFontDirOptions } from './font-dir.js';
|
|
2
|
+
/** Everything {@link resolveFontDir} takes except the environment, which {@link initFonts} reads. */
|
|
3
|
+
export type InitFontsOptions = Omit<ResolveFontDirOptions, 'env'>;
|
|
4
|
+
/** A face the font map would not take, and why. */
|
|
5
|
+
export interface FontFaceFailure {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
}
|
|
9
|
+
/** What {@link initFonts} did, so a caller that cares can assert on it. */
|
|
10
|
+
export interface InitFontsResult {
|
|
11
|
+
/** The directory that was read, or `undefined` when nothing named one. */
|
|
12
|
+
readonly dir: string | undefined;
|
|
13
|
+
/** Faces now on the default font map. */
|
|
14
|
+
readonly registered: readonly string[];
|
|
15
|
+
/** Faces the font map declined as unsupported — see {@link isUnsupportedByFontMap}. */
|
|
16
|
+
readonly declined: readonly string[];
|
|
17
|
+
/** Faces that failed for any other reason. Each was warned about; none threw. */
|
|
18
|
+
readonly failed: readonly FontFaceFailure[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Did the font map decline runtime registration outright?
|
|
22
|
+
*
|
|
23
|
+
* `pango_font_map_add_font_file()` is a vfunc, and the CoreText map implements none — so on macOS
|
|
24
|
+
* the call falls through to the base implementation, which answers `G_IO_ERROR_NOT_SUPPORTED`.
|
|
25
|
+
* That is not a failure to report: macOS is already correct declaratively, because the bundle's
|
|
26
|
+
* `ATSApplicationFontsPath` had the OS activate the staged directory before the process started,
|
|
27
|
+
* and the ordering makes the runtime call the wrong tool there rather than merely a redundant one
|
|
28
|
+
* — `pango_core_text_font_map_changed()` only bumps a serial, there is no
|
|
29
|
+
* `kCTFontManagerRegisteredFontsChangedNotification` observer and no re-scan path in
|
|
30
|
+
* `pangocoretext-fontmap.c`, so a face registered after the map initialises could not be recovered
|
|
31
|
+
* by poking it anyway.
|
|
32
|
+
*
|
|
33
|
+
* Keyed on the ERROR rather than on `process.platform`, which is the difference between a
|
|
34
|
+
* capability test and a guess about who is asking. It costs no `gjsify.os` declaration (this
|
|
35
|
+
* package makes no OS decision, and ADR 0018's candidate set is derived from the code that reads
|
|
36
|
+
* the host), it stays right if a fontconfig-backed Pango is ever selected on darwin — the backend
|
|
37
|
+
* is chosen by what is compiled in, not per platform, and `PANGOCAIRO_BACKEND=fc` selects one — and
|
|
38
|
+
* it stays right for any other map that declines. The OS name was never the thing being asked.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isUnsupportedByFontMap(error: unknown): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Register every face in the application's shipped font directory with the default font map.
|
|
43
|
+
*
|
|
44
|
+
* Call it once at startup, and BEFORE any text is laid out — that ordering is load-bearing on the
|
|
45
|
+
* fontconfig backend rather than tidiness: it caches the FONTSET resolved for a description and
|
|
46
|
+
* `add_font_file` does not invalidate it, so a `Pango.Layout` that measured the family first keeps
|
|
47
|
+
* measuring the fallback for the life of the process even though the family is then in
|
|
48
|
+
* `list_families()`. win32 clears its cache instead and recovers, so registering early is free
|
|
49
|
+
* there and unrecoverable-if-missed on Linux (measured both ways; `fonts.spec.ts`).
|
|
50
|
+
*
|
|
51
|
+
* Nothing here is eager: this
|
|
52
|
+
* package is the element model renderers bind to and owns no application lifecycle, and a
|
|
53
|
+
* module-load side effect would decide an application's initialisation order invisibly — the same
|
|
54
|
+
* reason `gjsify ship` stages the faces and names the directory instead of injecting the call.
|
|
55
|
+
*
|
|
56
|
+
* Total, like `installDevtools`: a face that will not open costs one stderr line and appears in
|
|
57
|
+
* {@link InitFontsResult.failed}, never an exception. Taking an application down over a decorative
|
|
58
|
+
* face would be worse than rendering it in a fallback — but doing so SILENTLY is the defect this
|
|
59
|
+
* exists against, so it is loud and it is reported.
|
|
60
|
+
*
|
|
61
|
+
* Safe to call when the application ships no faces: `GJSIFY_FONT_DIR` is exported only when
|
|
62
|
+
* `gjsify ship` staged one, so an unset variable is the ordinary case and does nothing quietly.
|
|
63
|
+
*/
|
|
64
|
+
export declare function initFonts(options?: InitFontsOptions): InitFontsResult;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/gtk-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"description": "Framework-agnostic GTK4/Adwaita host: the element model UI-framework renderers bind to",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "lib/esm/index.js",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"types": "./lib/types/list/index.d.ts",
|
|
19
19
|
"default": "./lib/esm/list/index.js"
|
|
20
20
|
},
|
|
21
|
+
"./fonts": {
|
|
22
|
+
"types": "./lib/types/fonts.d.ts",
|
|
23
|
+
"default": "./lib/esm/fonts.js"
|
|
24
|
+
},
|
|
21
25
|
"./vue": {
|
|
22
26
|
"types": "./lib/types/adapters/vue.d.ts",
|
|
23
27
|
"default": "./lib/esm/adapters/vue.js"
|
|
@@ -93,7 +97,8 @@
|
|
|
93
97
|
"@girs/glib-2.0": "4.6.0",
|
|
94
98
|
"@girs/gobject-2.0": "4.6.0",
|
|
95
99
|
"@girs/gtk-4.0": "4.6.0",
|
|
96
|
-
"@girs/pango-1.0": "4.6.0"
|
|
100
|
+
"@girs/pango-1.0": "4.6.0",
|
|
101
|
+
"@girs/pangocairo-1.0": "4.6.0"
|
|
97
102
|
},
|
|
98
103
|
"peerDependencies": {
|
|
99
104
|
"@vue/runtime-core": "^3.5.0",
|
|
@@ -116,10 +121,10 @@
|
|
|
116
121
|
}
|
|
117
122
|
},
|
|
118
123
|
"devDependencies": {
|
|
119
|
-
"@gjsify/cli": "^0.
|
|
124
|
+
"@gjsify/cli": "^0.47.0",
|
|
120
125
|
"@vue/runtime-core": "^3.5.0",
|
|
121
126
|
"solid-js": "^1.9.0",
|
|
122
|
-
"@gjsify/unit": "^0.
|
|
127
|
+
"@gjsify/unit": "^0.47.0",
|
|
123
128
|
"@types/node": "^25.9.2",
|
|
124
129
|
"typescript": "^6.0.3",
|
|
125
130
|
"@gjsify/node-gi": "file:../../node-gi/node-gi",
|