@flighthq/bitmapfont 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.
@@ -0,0 +1,8 @@
1
+ import type { BitmapFont, BitmapFontData, GlyphEntry, GlyphMetrics, TextureAtlas } from '@flighthq/types';
2
+ export declare function createBitmapFont(data: Readonly<BitmapFontData>): BitmapFont;
3
+ export declare function getBitmapFontGlyph(font: Readonly<BitmapFont>, codepoint: number): GlyphEntry | null;
4
+ export declare function getBitmapFontKerning(font: Readonly<BitmapFont>, left: number, right: number): number;
5
+ export declare function getBitmapFontMetrics(font: Readonly<BitmapFont>): Readonly<GlyphMetrics>;
6
+ export declare function getBitmapFontPage(font: Readonly<BitmapFont>, page?: number): TextureAtlas | null;
7
+ export declare function getBitmapFontPages(font: Readonly<BitmapFont>): readonly TextureAtlas[];
8
+ //# sourceMappingURL=bitmapFont.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitmapFont.d.ts","sourceRoot":"","sources":["../src/bitmapFont.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAS1G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,UAAU,CAiC3E;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAEnG;AAID,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpG;AAGD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAEvF;AAKD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,SAAI,GAAG,YAAY,GAAG,IAAI,CAE3F;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,YAAY,EAAE,CAEtF"}
@@ -0,0 +1,73 @@
1
+ // Builds an immutable static bitmap font from plain data: the glyph list becomes a
2
+ // `codepoint → GlyphEntry` map, the kerning pairs become a `(left << 16) | right → amount` map, and
3
+ // the page-indexed atlas list, line metrics, and encoding (default `raster`) are carried as-is. Each
4
+ // glyph's `page` (default 0) indexes `data.pages`; an out-of-range page is clamped to 0 so the glyph
5
+ // is still placed (on the primary page) rather than dropped — a bad page index is a source-data
6
+ // defect the font should survive, not a reason to lose a glyph. Nothing mutates the font after this
7
+ // call — it is the static counterpart to the growing `@flighthq/glyphatlas`.
8
+ export function createBitmapFont(data) {
9
+ const pageCount = data.pages.length;
10
+ const glyphs = new Map();
11
+ for (const glyph of data.glyphs) {
12
+ const page = glyph.page ?? 0;
13
+ glyphs.set(glyph.codepoint, {
14
+ advance: glyph.advance,
15
+ bearingX: glyph.bearingX,
16
+ bearingY: glyph.bearingY,
17
+ height: glyph.height,
18
+ page: page >= 0 && page < pageCount ? page : 0,
19
+ width: glyph.width,
20
+ x: glyph.x,
21
+ y: glyph.y,
22
+ });
23
+ }
24
+ const kerning = new Map();
25
+ if (data.kerning !== undefined) {
26
+ for (const pair of data.kerning) {
27
+ kerning.set(packBitmapFontKerningKey(pair.left, pair.right), pair.amount);
28
+ }
29
+ }
30
+ return {
31
+ encoding: data.encoding ?? 'raster',
32
+ glyphs,
33
+ kerning,
34
+ metrics: {
35
+ ascent: data.metrics.ascent,
36
+ descent: data.metrics.descent,
37
+ lineGap: data.metrics.lineGap,
38
+ },
39
+ pages: data.pages.slice(),
40
+ };
41
+ }
42
+ // The glyph entry (atlas rectangle + advance + bearing) for a codepoint, or `null` when the font
43
+ // carries no glyph for it. A pure map lookup — the static counterpart to glyphatlas's rasterize-on-miss.
44
+ export function getBitmapFontGlyph(font, codepoint) {
45
+ return font.glyphs.get(codepoint) ?? null;
46
+ }
47
+ // The horizontal kerning adjustment (pixels) between an adjacent `left`/`right` glyph pair, or 0 when
48
+ // the font carries no entry for the pair.
49
+ export function getBitmapFontKerning(font, left, right) {
50
+ return font.kerning.get(packBitmapFontKerningKey(left, right)) ?? 0;
51
+ }
52
+ // The font's shared line metrics (ascent/descent/lineGap), in pixels at the baked glyph size.
53
+ export function getBitmapFontMetrics(font) {
54
+ return font.metrics;
55
+ }
56
+ // The texture atlas for one page of a multi-page font, or `null` when `page` is out of range. Page 0
57
+ // is the primary page. The pixels a renderer uploads and samples for glyphs whose `page` matches.
58
+ // Immutable: the font shares these references, it does not clone the atlases.
59
+ export function getBitmapFontPage(font, page = 0) {
60
+ return font.pages[page] ?? null;
61
+ }
62
+ // The font's page-indexed atlas list — one `TextureAtlas` per page image. A single-page font has
63
+ // length 1; each glyph's `page` indexes this array. Immutable: shared, not cloned.
64
+ export function getBitmapFontPages(font) {
65
+ return font.pages;
66
+ }
67
+ // Packs an adjacent glyph pair into the single-number kerning-map key `(left << 16) | right`. Both
68
+ // codepoints are assumed to lie in the Basic Multilingual Plane (< 0x10000); supplementary-plane
69
+ // pairs are outside this table's addressable range.
70
+ function packBitmapFontKerningKey(left, right) {
71
+ return (left << 16) | right;
72
+ }
73
+ //# sourceMappingURL=bitmapFont.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitmapFont.js","sourceRoot":"","sources":["../src/bitmapFont.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { BitmapFont, GlyphSource } from '@flighthq/types';
2
+ export declare function createGlyphSourceFromBitmapFont(font: Readonly<BitmapFont>): GlyphSource;
3
+ //# sourceMappingURL=glyphSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphSource.d.ts","sourceRoot":"","sources":["../src/glyphSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAQ/D,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,WAAW,CAiBvF"}
@@ -0,0 +1,24 @@
1
+ import { getBitmapFontGlyph, getBitmapFontKerning, getBitmapFontMetrics } from './bitmapFont';
2
+ // Adapts a `BitmapFont` into the `GlyphSource` seam a text renderer consumes, binding the font's pure
3
+ // lookups into the method object. `getGlyphEntry` is the static map lookup (no side effects) — the
4
+ // static counterpart to `@flighthq/glyphatlas`'s `createGlyphSourceFromGlyphAtlas`, whose
5
+ // `getGlyphEntry` rasterizes on miss. A renderer holds either behind the one seam without knowing which.
6
+ export function createGlyphSourceFromBitmapFont(font) {
7
+ return {
8
+ getGlyphAtlasImage(page = 0) {
9
+ // Each page's atlas image, indexed by the glyph's `page`. An out-of-range page (or a page whose
10
+ // atlas carries no image yet) yields null — the renderer skips glyphs it cannot sample.
11
+ return font.pages[page]?.image ?? null;
12
+ },
13
+ getGlyphEntry(codepoint) {
14
+ return getBitmapFontGlyph(font, codepoint);
15
+ },
16
+ getGlyphKerning(left, right) {
17
+ return getBitmapFontKerning(font, left, right);
18
+ },
19
+ getGlyphMetrics() {
20
+ return getBitmapFontMetrics(font);
21
+ },
22
+ };
23
+ }
24
+ //# sourceMappingURL=glyphSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyphSource.js","sourceRoot":"","sources":["../src/glyphSource.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE9F,sGAAsG;AACtG,mGAAmG;AACnG,0FAA0F;AAC1F,yGAAyG;AACzG,MAAM,UAAU,+BAA+B,CAAC,IAA0B;IACxE,OAAO;QACL,kBAAkB,CAAC,IAAI,GAAG,CAAC;YACzB,gGAAgG;YAChG,wFAAwF;YACxF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;QACzC,CAAC;QACD,aAAa,CAAC,SAAS;YACrB,OAAO,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC7C,CAAC;QACD,eAAe,CAAC,IAAI,EAAE,KAAK;YACzB,OAAO,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;QACD,eAAe;YACb,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './bitmapFont';
2
+ export * from './glyphSource';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './bitmapFont';
2
+ export * from './glyphSource';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@flighthq/bitmapfont",
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/textureatlas": "0.1.0",
31
+ "@flighthq/types": "0.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.3.0"
35
+ },
36
+ "description": "Static bitmap font — an immutable pre-baked atlas + glyph/kerning/metrics tables implementing the GlyphSource seam, the static sibling of the dynamic glyphatlas",
37
+ "sideEffects": false
38
+ }
@@ -0,0 +1,127 @@
1
+ import { createTextureAtlas } from '@flighthq/textureatlas';
2
+ import type { BitmapFontData } from '@flighthq/types';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ import {
6
+ createBitmapFont,
7
+ getBitmapFontGlyph,
8
+ getBitmapFontKerning,
9
+ getBitmapFontMetrics,
10
+ getBitmapFontPage,
11
+ getBitmapFontPages,
12
+ } from './bitmapFont';
13
+
14
+ describe('createBitmapFont', () => {
15
+ it('builds glyph and kerning lookups from plain data', () => {
16
+ const font = createBitmapFont(sampleFontData());
17
+
18
+ expect(font.glyphs.size).toBe(3);
19
+ expect(font.kerning.size).toBe(1);
20
+ expect(getBitmapFontGlyph(font, 65)).toEqual({
21
+ advance: 9,
22
+ bearingX: 1,
23
+ bearingY: 8,
24
+ height: 8,
25
+ page: 0,
26
+ width: 7,
27
+ x: 0,
28
+ y: 0,
29
+ });
30
+ });
31
+
32
+ it('defaults encoding to raster and carries an explicit encoding', () => {
33
+ expect(createBitmapFont(sampleFontData()).encoding).toBe('raster');
34
+ expect(createBitmapFont({ ...sampleFontData(), encoding: 'msdf' }).encoding).toBe('msdf');
35
+ });
36
+
37
+ it('assigns each glyph its declared page and defaults an omitted page to 0', () => {
38
+ const font = createBitmapFont({
39
+ ...sampleFontData(),
40
+ glyphs: [
41
+ { advance: 9, bearingX: 1, bearingY: 8, codepoint: 65, height: 8, page: 1, width: 7, x: 0, y: 0 },
42
+ { advance: 10, bearingX: 1, bearingY: 8, codepoint: 66, height: 8, width: 7, x: 8, y: 0 },
43
+ ],
44
+ pages: [createTextureAtlas(), createTextureAtlas()],
45
+ });
46
+
47
+ expect(getBitmapFontGlyph(font, 65)!.page).toBe(1);
48
+ expect(getBitmapFontGlyph(font, 66)!.page).toBe(0);
49
+ });
50
+
51
+ it('clamps an out-of-range glyph page to 0 rather than dropping the glyph', () => {
52
+ const font = createBitmapFont({
53
+ ...sampleFontData(),
54
+ glyphs: [{ advance: 9, bearingX: 1, bearingY: 8, codepoint: 65, height: 8, page: 5, width: 7, x: 0, y: 0 }],
55
+ });
56
+
57
+ expect(getBitmapFontGlyph(font, 65)!.page).toBe(0);
58
+ });
59
+ });
60
+
61
+ describe('getBitmapFontGlyph', () => {
62
+ it('returns the entry for a present codepoint and null for an absent one', () => {
63
+ const font = createBitmapFont(sampleFontData());
64
+
65
+ expect(getBitmapFontGlyph(font, 66)?.advance).toBe(10);
66
+ expect(getBitmapFontGlyph(font, 0x1f600)).toBeNull();
67
+ });
68
+
69
+ it('returns a stable entry across repeated lookups (immutable)', () => {
70
+ const font = createBitmapFont(sampleFontData());
71
+
72
+ expect(getBitmapFontGlyph(font, 65)).toBe(getBitmapFontGlyph(font, 65));
73
+ });
74
+ });
75
+
76
+ describe('getBitmapFontKerning', () => {
77
+ it('returns the pair amount and 0 for an absent pair', () => {
78
+ const font = createBitmapFont(sampleFontData());
79
+
80
+ expect(getBitmapFontKerning(font, 65, 86)).toBe(-2);
81
+ expect(getBitmapFontKerning(font, 86, 65)).toBe(0);
82
+ });
83
+ });
84
+
85
+ describe('getBitmapFontMetrics', () => {
86
+ it('returns the font line metrics', () => {
87
+ const font = createBitmapFont(sampleFontData());
88
+
89
+ expect(getBitmapFontMetrics(font)).toEqual({ ascent: 8, descent: 2, lineGap: 1 });
90
+ });
91
+ });
92
+
93
+ describe('getBitmapFontPage', () => {
94
+ it('returns the page atlas by index and null when out of range', () => {
95
+ const page0 = createTextureAtlas();
96
+ const page1 = createTextureAtlas();
97
+ const font = createBitmapFont({ ...sampleFontData(), pages: [page0, page1] });
98
+
99
+ expect(getBitmapFontPage(font)).toBe(page0);
100
+ expect(getBitmapFontPage(font, 0)).toBe(page0);
101
+ expect(getBitmapFontPage(font, 1)).toBe(page1);
102
+ expect(getBitmapFontPage(font, 2)).toBeNull();
103
+ });
104
+ });
105
+
106
+ describe('getBitmapFontPages', () => {
107
+ it('returns the page-indexed atlas list', () => {
108
+ const page0 = createTextureAtlas();
109
+ const page1 = createTextureAtlas();
110
+ const font = createBitmapFont({ ...sampleFontData(), pages: [page0, page1] });
111
+
112
+ expect(getBitmapFontPages(font)).toEqual([page0, page1]);
113
+ });
114
+ });
115
+
116
+ function sampleFontData(): BitmapFontData {
117
+ return {
118
+ glyphs: [
119
+ { advance: 9, bearingX: 1, bearingY: 8, codepoint: 65, height: 8, width: 7, x: 0, y: 0 },
120
+ { advance: 10, bearingX: 1, bearingY: 8, codepoint: 66, height: 8, width: 7, x: 8, y: 0 },
121
+ { advance: 8, bearingX: 1, bearingY: 8, codepoint: 86, height: 8, width: 6, x: 16, y: 0 },
122
+ ],
123
+ kerning: [{ amount: -2, left: 65, right: 86 }],
124
+ metrics: { ascent: 8, descent: 2, lineGap: 1 },
125
+ pages: [createTextureAtlas()],
126
+ };
127
+ }
@@ -0,0 +1,56 @@
1
+ import { createTextureAtlas } from '@flighthq/textureatlas';
2
+ import type { BitmapFontData, ImageResource } from '@flighthq/types';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ import { createBitmapFont, getBitmapFontGlyph } from './bitmapFont';
6
+ import { createGlyphSourceFromBitmapFont } from './glyphSource';
7
+
8
+ describe('createGlyphSourceFromBitmapFont', () => {
9
+ it('exposes the font as a GlyphSource whose lookups match the font', () => {
10
+ const font = createBitmapFont(sampleFontData());
11
+ const source = createGlyphSourceFromBitmapFont(font);
12
+
13
+ expect(source.getGlyphEntry(65)).toBe(getBitmapFontGlyph(font, 65));
14
+ expect(source.getGlyphEntry(65)!.page).toBe(0);
15
+ expect(source.getGlyphEntry(0x1f600)).toBeNull();
16
+ expect(source.getGlyphKerning(65, 86)).toBe(-2);
17
+ expect(source.getGlyphKerning(86, 65)).toBe(0);
18
+ expect(source.getGlyphMetrics()).toEqual({ ascent: 8, descent: 2, lineGap: 1 });
19
+ });
20
+
21
+ it('pairs page 0 with the font atlas image and has no other page', () => {
22
+ const image = {} as ImageResource;
23
+ const font = createBitmapFont({ ...sampleFontData(), pages: [createTextureAtlas({ image })] });
24
+ const source = createGlyphSourceFromBitmapFont(font);
25
+
26
+ expect(source.getGlyphAtlasImage(0)).toBe(image);
27
+ expect(source.getGlyphAtlasImage()).toBe(image);
28
+ expect(source.getGlyphAtlasImage(1)).toBeNull();
29
+ });
30
+
31
+ it('resolves each page image of a multi-page font', () => {
32
+ const image0 = {} as ImageResource;
33
+ const image1 = {} as ImageResource;
34
+ const font = createBitmapFont({
35
+ ...sampleFontData(),
36
+ pages: [createTextureAtlas({ image: image0 }), createTextureAtlas({ image: image1 })],
37
+ });
38
+ const source = createGlyphSourceFromBitmapFont(font);
39
+
40
+ expect(source.getGlyphAtlasImage(0)).toBe(image0);
41
+ expect(source.getGlyphAtlasImage(1)).toBe(image1);
42
+ expect(source.getGlyphAtlasImage(2)).toBeNull();
43
+ });
44
+ });
45
+
46
+ function sampleFontData(): BitmapFontData {
47
+ return {
48
+ glyphs: [
49
+ { advance: 9, bearingX: 1, bearingY: 8, codepoint: 65, height: 8, width: 7, x: 0, y: 0 },
50
+ { advance: 8, bearingX: 1, bearingY: 8, codepoint: 86, height: 8, width: 6, x: 16, y: 0 },
51
+ ],
52
+ kerning: [{ amount: -2, left: 65, right: 86 }],
53
+ metrics: { ascent: 8, descent: 2, lineGap: 1 },
54
+ pages: [createTextureAtlas()],
55
+ };
56
+ }