@flighthq/font 0.1.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/dist/font.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { Font } from '@flighthq/types';
2
+ export declare function createFont(name: string): Font;
3
+ //# sourceMappingURL=font.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font.d.ts","sourceRoot":"","sources":["../src/font.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE7C"}
package/dist/font.js ADDED
@@ -0,0 +1,5 @@
1
+ import { createEntity } from '@flighthq/entity';
2
+ export function createFont(name) {
3
+ return createEntity({ name });
4
+ }
5
+ //# sourceMappingURL=font.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"font.js","sourceRoot":"","sources":["../src/font.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function inferFontFormat(url: string): string | null;
2
+ //# sourceMappingURL=fontFormat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontFormat.d.ts","sourceRoot":"","sources":["../src/fontFormat.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB1D"}
@@ -0,0 +1,20 @@
1
+ export function inferFontFormat(url) {
2
+ const ext = url.split('?')[0].split('.').pop()?.toLowerCase();
3
+ switch (ext) {
4
+ case 'woff':
5
+ return 'woff';
6
+ case 'woff2':
7
+ return 'woff2';
8
+ case 'ttf':
9
+ return 'truetype';
10
+ case 'otf':
11
+ return 'opentype';
12
+ case 'eot':
13
+ return 'embedded-opentype';
14
+ case 'svg':
15
+ return 'svg';
16
+ default:
17
+ return null;
18
+ }
19
+ }
20
+ //# sourceMappingURL=fontFormat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontFormat.js","sourceRoot":"","sources":["../src/fontFormat.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;IAC9D,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QACjB,KAAK,KAAK;YACR,OAAO,UAAU,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,UAAU,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,mBAAmB,CAAC;QAC7B,KAAK,KAAK;YACR,OAAO,KAAK,CAAC;QACf;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Font, FontUrl } from '@flighthq/types';
2
+ export declare function loadFontFromBytes(bytes: Uint8Array, family: string): Promise<Font>;
3
+ export declare function loadFontFromName(name: string): Promise<Font>;
4
+ export declare function loadFontFromUrl(url: string, family: string): Promise<Font>;
5
+ export declare function loadFontFromUrls(sources: FontUrl[], family: string): Promise<Font>;
6
+ //# sourceMappingURL=fontFrom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontFrom.d.ts","sourceRoot":"","sources":["../src/fontFrom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAKrD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQxF;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlE;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhF;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWxF"}
@@ -0,0 +1,31 @@
1
+ import { createFont } from './font';
2
+ import { inferFontFormat } from './fontFormat';
3
+ export async function loadFontFromBytes(bytes, family) {
4
+ const face = new FontFace(family, bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
5
+ await face.load();
6
+ document.fonts.add(face);
7
+ return createFont(family);
8
+ }
9
+ export async function loadFontFromName(name) {
10
+ await document.fonts.load(`1em '${name}'`);
11
+ return createFont(name);
12
+ }
13
+ export async function loadFontFromUrl(url, family) {
14
+ const face = new FontFace(family, `url(${url})`);
15
+ await face.load();
16
+ document.fonts.add(face);
17
+ return createFont(family);
18
+ }
19
+ export async function loadFontFromUrls(sources, family) {
20
+ const src = sources
21
+ .map(({ url, format }) => {
22
+ const fmt = format ?? inferFontFormat(url);
23
+ return fmt !== null ? `url(${url}) format('${fmt}')` : `url(${url})`;
24
+ })
25
+ .join(', ');
26
+ const face = new FontFace(family, src);
27
+ await face.load();
28
+ document.fonts.add(face);
29
+ return createFont(family);
30
+ }
31
+ //# sourceMappingURL=fontFrom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontFrom.js","sourceRoot":"","sources":["../src/fontFrom.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAiB,EAAE,MAAc;IACvE,MAAM,IAAI,GAAG,IAAI,QAAQ,CACvB,MAAM,EACL,KAAK,CAAC,MAAsB,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC3F,CAAC;IACF,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY;IACjD,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW,EAAE,MAAc;IAC/D,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC;IACjD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAkB,EAAE,MAAc;IACvE,MAAM,GAAG,GAAG,OAAO;SAChB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC;IACvE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FontResource } from '@flighthq/types';
2
+ export declare function createFontResource(family: string): FontResource;
3
+ //# sourceMappingURL=fontResource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontResource.d.ts","sourceRoot":"","sources":["../src/fontResource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAE/D"}
@@ -0,0 +1,4 @@
1
+ export function createFontResource(family) {
2
+ return { family, face: null };
3
+ }
4
+ //# sourceMappingURL=fontResource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontResource.js","sourceRoot":"","sources":["../src/fontResource.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { FontResource, FontUrl } from '@flighthq/types';
2
+ export declare function loadFontResourceFromBytes(out: FontResource, bytes: Uint8Array): Promise<FontResource>;
3
+ export declare function loadFontResourceFromName(out: FontResource): Promise<FontResource>;
4
+ export declare function loadFontResourceFromUrl(out: FontResource, url: string): Promise<FontResource>;
5
+ export declare function loadFontResourceFromUrls(out: FontResource, sources: FontUrl[]): Promise<FontResource>;
6
+ //# sourceMappingURL=fontResourceFrom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontResourceFrom.d.ts","sourceRoot":"","sources":["../src/fontResourceFrom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI7D,wBAAsB,yBAAyB,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAS3G;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAIvF;AAED,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAMnG;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAY3G"}
@@ -0,0 +1,35 @@
1
+ import { inferFontFormat } from './fontFormat';
2
+ export async function loadFontResourceFromBytes(out, bytes) {
3
+ const face = new FontFace(out.family, bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
4
+ await face.load();
5
+ document.fonts.add(face);
6
+ out.face = face;
7
+ return out;
8
+ }
9
+ export async function loadFontResourceFromName(out) {
10
+ const faces = await document.fonts.load(`1em '${out.family}'`);
11
+ if (faces.length > 0)
12
+ out.face = faces[0];
13
+ return out;
14
+ }
15
+ export async function loadFontResourceFromUrl(out, url) {
16
+ const face = new FontFace(out.family, `url(${url})`);
17
+ await face.load();
18
+ document.fonts.add(face);
19
+ out.face = face;
20
+ return out;
21
+ }
22
+ export async function loadFontResourceFromUrls(out, sources) {
23
+ const src = sources
24
+ .map(({ url, format }) => {
25
+ const fmt = format ?? inferFontFormat(url);
26
+ return fmt !== null ? `url(${url}) format('${fmt}')` : `url(${url})`;
27
+ })
28
+ .join(', ');
29
+ const face = new FontFace(out.family, src);
30
+ await face.load();
31
+ document.fonts.add(face);
32
+ out.face = face;
33
+ return out;
34
+ }
35
+ //# sourceMappingURL=fontResourceFrom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontResourceFrom.js","sourceRoot":"","sources":["../src/fontResourceFrom.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,GAAiB,EAAE,KAAiB;IAClF,MAAM,IAAI,GAAG,IAAI,QAAQ,CACvB,GAAG,CAAC,MAAM,EACT,KAAK,CAAC,MAAsB,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC3F,CAAC;IACF,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAiB;IAC9D,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,GAAiB,EAAE,GAAW;IAC1E,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC;IACrD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,GAAiB,EAAE,OAAkB;IAClF,MAAM,GAAG,GAAG,OAAO;SAChB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC;IACvE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAClB,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from './font';
2
+ export * from './fontFormat';
3
+ export * from './fontFrom';
4
+ export * from './fontResource';
5
+ export * from './fontResourceFrom';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './font';
2
+ export * from './fontFormat';
3
+ export * from './fontFrom';
4
+ export * from './fontResource';
5
+ export * from './fontResourceFrom';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@flighthq/font",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "src/**/*.test.ts",
16
+ "!dist/**/*.test.js",
17
+ "!dist/**/*.test.d.ts",
18
+ "!dist/**/*.test.js.map",
19
+ "!dist/**/*.test.d.ts.map"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -b",
23
+ "clean": "tsc -b --clean",
24
+ "test": "vitest run --config vitest.config.ts",
25
+ "test:watch": "vitest --watch --config vitest.config.ts",
26
+ "prepack": "npm run clean && npm run clean:dist && npm run build",
27
+ "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
28
+ },
29
+ "dependencies": {
30
+ "@flighthq/entity": "0.1.0",
31
+ "@flighthq/types": "0.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.3.0"
35
+ },
36
+ "description": "Font and font-resource types and constructors",
37
+ "sideEffects": false
38
+ }
@@ -0,0 +1,12 @@
1
+ import { createFont } from './font';
2
+
3
+ describe('createFont', () => {
4
+ it('returns a font with the given name', () => {
5
+ const font = createFont('Arial');
6
+ expect(font.name).toBe('Arial');
7
+ });
8
+
9
+ it('returns a new object each call', () => {
10
+ expect(createFont('Arial')).not.toBe(createFont('Arial'));
11
+ });
12
+ });
@@ -0,0 +1,43 @@
1
+ import { inferFontFormat } from './fontFormat';
2
+
3
+ describe('inferFontFormat', () => {
4
+ it('returns "woff" for .woff files', () => {
5
+ expect(inferFontFormat('font.woff')).toBe('woff');
6
+ });
7
+
8
+ it('returns "woff2" for .woff2 files', () => {
9
+ expect(inferFontFormat('font.woff2')).toBe('woff2');
10
+ });
11
+
12
+ it('returns "truetype" for .ttf files', () => {
13
+ expect(inferFontFormat('font.ttf')).toBe('truetype');
14
+ });
15
+
16
+ it('returns "opentype" for .otf files', () => {
17
+ expect(inferFontFormat('font.otf')).toBe('opentype');
18
+ });
19
+
20
+ it('returns "embedded-opentype" for .eot files', () => {
21
+ expect(inferFontFormat('font.eot')).toBe('embedded-opentype');
22
+ });
23
+
24
+ it('returns "svg" for .svg files', () => {
25
+ expect(inferFontFormat('font.svg')).toBe('svg');
26
+ });
27
+
28
+ it('returns null for unrecognized extensions', () => {
29
+ expect(inferFontFormat('font.bin')).toBeNull();
30
+ });
31
+
32
+ it('strips query parameters before matching', () => {
33
+ expect(inferFontFormat('font.woff2?v=123')).toBe('woff2');
34
+ });
35
+
36
+ it('is case-insensitive', () => {
37
+ expect(inferFontFormat('font.WOFF2')).toBe('woff2');
38
+ });
39
+
40
+ it('returns null for a URL with no extension', () => {
41
+ expect(inferFontFormat('font')).toBeNull();
42
+ });
43
+ });
@@ -0,0 +1,53 @@
1
+ import { loadFontFromBytes, loadFontFromName, loadFontFromUrl, loadFontFromUrls } from './fontFrom';
2
+
3
+ class MockFontFace {
4
+ load = vi.fn().mockResolvedValue(undefined);
5
+ }
6
+
7
+ // Re-applied per test (not once) and torn down, so under a shared (isolate:false) worker with
8
+ // unstubGlobals the FontFace stub survives every test and the document.fonts patch never leaks out.
9
+ let originalFonts: PropertyDescriptor | undefined;
10
+ beforeEach(() => {
11
+ vi.stubGlobal('FontFace', MockFontFace);
12
+ originalFonts = Object.getOwnPropertyDescriptor(document, 'fonts');
13
+ Object.defineProperty(document, 'fonts', {
14
+ value: { add: vi.fn(), load: vi.fn().mockResolvedValue([]) },
15
+ configurable: true,
16
+ });
17
+ });
18
+
19
+ afterEach(() => {
20
+ if (originalFonts) {
21
+ Object.defineProperty(document, 'fonts', originalFonts);
22
+ } else {
23
+ delete (document as unknown as { fonts?: unknown }).fonts;
24
+ }
25
+ });
26
+
27
+ describe('loadFontFromBytes', () => {
28
+ it('returns a font with the given family name', async () => {
29
+ const font = await loadFontFromBytes(new Uint8Array(0), 'TestFont');
30
+ expect(font.name).toBe('TestFont');
31
+ });
32
+ });
33
+
34
+ describe('loadFontFromName', () => {
35
+ it('returns a font with the given name', async () => {
36
+ const font = await loadFontFromName('MyFont');
37
+ expect(font.name).toBe('MyFont');
38
+ });
39
+ });
40
+
41
+ describe('loadFontFromUrl', () => {
42
+ it('returns a font with the given family name', async () => {
43
+ const font = await loadFontFromUrl('font.woff2', 'MyFont');
44
+ expect(font.name).toBe('MyFont');
45
+ });
46
+ });
47
+
48
+ describe('loadFontFromUrls', () => {
49
+ it('returns a font with the given family name from multiple sources', async () => {
50
+ const font = await loadFontFromUrls([{ url: 'font.woff2', format: 'woff2' }], 'MyFont');
51
+ expect(font.name).toBe('MyFont');
52
+ });
53
+ });
@@ -0,0 +1,17 @@
1
+ import { createFontResource } from './fontResource';
2
+
3
+ describe('createFontResource', () => {
4
+ it('creates a FontResource with the given family name', () => {
5
+ const resource = createFontResource('Arial');
6
+ expect(resource.family).toBe('Arial');
7
+ });
8
+
9
+ it('initializes face to null', () => {
10
+ const resource = createFontResource('serif');
11
+ expect(resource.face).toBeNull();
12
+ });
13
+
14
+ it('returns a new object each call', () => {
15
+ expect(createFontResource('Arial')).not.toBe(createFontResource('Arial'));
16
+ });
17
+ });
@@ -0,0 +1,91 @@
1
+ import { createFontResource } from './fontResource';
2
+ import {
3
+ loadFontResourceFromBytes,
4
+ loadFontResourceFromName,
5
+ loadFontResourceFromUrl,
6
+ loadFontResourceFromUrls,
7
+ } from './fontResourceFrom';
8
+
9
+ let mockFace: { load: ReturnType<typeof vi.fn> };
10
+
11
+ beforeEach(() => {
12
+ mockFace = { load: vi.fn().mockResolvedValue(undefined) };
13
+ vi.stubGlobal(
14
+ 'FontFace',
15
+ vi.fn(function () {
16
+ return mockFace;
17
+ }),
18
+ );
19
+ Object.defineProperty(document, 'fonts', {
20
+ value: { add: vi.fn(), load: vi.fn() },
21
+ configurable: true,
22
+ writable: true,
23
+ });
24
+ vi.spyOn(document.fonts, 'add').mockImplementation(() => document.fonts);
25
+ vi.spyOn(document.fonts, 'load').mockResolvedValue([mockFace as unknown as FontFace]);
26
+ });
27
+
28
+ afterEach(() => {
29
+ vi.unstubAllGlobals();
30
+ vi.restoreAllMocks();
31
+ });
32
+
33
+ describe('loadFontResourceFromBytes', () => {
34
+ it('loads the face and attaches it to the resource', async () => {
35
+ const resource = createFontResource('TestFont');
36
+ const result = await loadFontResourceFromBytes(resource, new Uint8Array(8));
37
+ expect(result).toBe(resource);
38
+ expect(resource.face).toBe(mockFace);
39
+ expect(mockFace.load).toHaveBeenCalledOnce();
40
+ expect(document.fonts.add).toHaveBeenCalledWith(mockFace);
41
+ });
42
+ });
43
+
44
+ describe('loadFontResourceFromName', () => {
45
+ it('resolves a face already registered with the document', async () => {
46
+ const resource = createFontResource('TestFont');
47
+ const result = await loadFontResourceFromName(resource);
48
+ expect(result).toBe(resource);
49
+ expect(resource.face).toBe(mockFace);
50
+ });
51
+
52
+ it('leaves face null when no registered faces are found', async () => {
53
+ vi.spyOn(document.fonts, 'load').mockResolvedValue([]);
54
+ const resource = createFontResource('UnknownFont');
55
+ await loadFontResourceFromName(resource);
56
+ expect(resource.face).toBeNull();
57
+ });
58
+ });
59
+
60
+ describe('loadFontResourceFromUrl', () => {
61
+ it('loads the face from a URL and attaches it to the resource', async () => {
62
+ const resource = createFontResource('TestFont');
63
+ const result = await loadFontResourceFromUrl(resource, 'https://example.com/font.woff2');
64
+ expect(result).toBe(resource);
65
+ expect(resource.face).toBe(mockFace);
66
+ expect(document.fonts.add).toHaveBeenCalledWith(mockFace);
67
+ });
68
+ });
69
+
70
+ describe('loadFontResourceFromUrls', () => {
71
+ it('builds a multi-source src string and loads the face', async () => {
72
+ const resource = createFontResource('TestFont');
73
+ const result = await loadFontResourceFromUrls(resource, [
74
+ { url: 'font.woff2', format: 'woff2' },
75
+ { url: 'font.ttf' },
76
+ ]);
77
+ expect(result).toBe(resource);
78
+ expect(resource.face).toBe(mockFace);
79
+ expect(document.fonts.add).toHaveBeenCalledWith(mockFace);
80
+ });
81
+
82
+ it('infers format from file extension when no format is provided', async () => {
83
+ const MockFontFace = vi.fn(function () {
84
+ return mockFace;
85
+ });
86
+ vi.stubGlobal('FontFace', MockFontFace);
87
+ const resource = createFontResource('TestFont');
88
+ await loadFontResourceFromUrls(resource, [{ url: 'font.otf' }]);
89
+ expect(MockFontFace).toHaveBeenCalledWith('TestFont', expect.stringContaining("format('opentype')"));
90
+ });
91
+ });