@flighthq/bitmapfont-formats 0.5.1-next.920.da8f530 → 0.6.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 +2 -2
- package/dist/.tsbuildinfo.contract +1 -0
- package/dist/.tsbuildinfo.runtime +1 -0
- package/dist/bitmapFontBinary.d.ts +4 -0
- package/dist/bitmapFontBinary.d.ts.map +1 -0
- package/dist/bitmapFontBinary.js +129 -0
- package/dist/bitmapFontBinary.js.map +1 -0
- package/dist/bitmapFontDetect.d.ts +59 -0
- package/dist/bitmapFontDetect.d.ts.map +1 -0
- package/dist/bitmapFontDetect.js +133 -0
- package/dist/bitmapFontDetect.js.map +1 -0
- package/dist/bitmapFontFnt.d.ts +2 -1
- package/dist/bitmapFontFnt.d.ts.map +1 -1
- package/dist/bitmapFontFnt.js +1 -1
- package/dist/bitmapFontFnt.js.map +1 -1
- package/dist/bitmapFontFormatKind.d.ts +37 -0
- package/dist/bitmapFontFormatKind.d.ts.map +1 -0
- package/dist/bitmapFontFormatKind.js +123 -0
- package/dist/bitmapFontFormatKind.js.map +1 -0
- package/dist/bitmapFontJson.d.ts +2 -1
- package/dist/bitmapFontJson.d.ts.map +1 -1
- package/dist/bitmapFontJson.js +4 -3
- package/dist/bitmapFontJson.js.map +1 -1
- package/dist/bitmapFontRecord.d.ts +3 -1
- package/dist/bitmapFontRecord.d.ts.map +1 -1
- package/dist/bitmapFontRecord.js +1 -2
- package/dist/bitmapFontRecord.js.map +1 -1
- package/dist/bitmapFontXml.d.ts +2 -1
- package/dist/bitmapFontXml.d.ts.map +1 -1
- package/dist/bitmapFontXml.js +1 -1
- package/dist/bitmapFontXml.js.map +1 -1
- package/dist/bitmapfontFormatRequirements.d.ts +9 -0
- package/dist/bitmapfontFormatRequirements.d.ts.map +1 -0
- package/dist/bitmapfontFormatRequirements.js +9 -0
- package/dist/bitmapfontFormatRequirements.js.map +1 -0
- package/dist/contract.bitmapfont-formats-types.d.ts +2 -0
- package/dist/contract.bitmapfont-formats-types.d.ts.map +1 -0
- package/dist/contract.bitmapfont-formats-types.js +4 -0
- package/dist/contract.bitmapfont-formats-types.js.map +1 -0
- package/dist/contract.d.ts +10 -3
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +10 -3
- package/dist/contract.js.map +1 -1
- package/dist/explainBitmapFontParse.d.ts +4 -0
- package/dist/explainBitmapFontParse.d.ts.map +1 -0
- package/dist/explainBitmapFontParse.js +194 -0
- package/dist/explainBitmapFontParse.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +13 -7
- package/src/bitmapFontBinary.test.ts +196 -0
- package/src/bitmapFontDetect.test.ts +335 -0
- package/src/bitmapFontFnt.test.ts +5 -2
- package/src/bitmapFontFormatKind.test.ts +71 -0
- package/src/bitmapFontJson.test.ts +5 -5
- package/src/bitmapFontRecord.test.ts +3 -2
- package/src/bitmapFontXml.test.ts +2 -2
- package/src/explainBitmapFontParse.test.ts +161 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { BitmapFontParseOptions } from '@flighthq/bitmapfont/contract';
|
|
2
|
+
import type { BitmapFontParseExplanation } from './types.ts';
|
|
3
|
+
export declare function explainBitmapFontParse(text: string, options?: Readonly<BitmapFontParseOptions>): BitmapFontParseExplanation;
|
|
4
|
+
//# sourceMappingURL=explainBitmapFontParse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explainBitmapFontParse.d.ts","sourceRoot":"","sources":["../src/explainBitmapFontParse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAE5E,OAAO,KAAK,EACV,0BAA0B,EAG3B,MAAM,YAAY,CAAC;AAEpB,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC,GACzC,0BAA0B,CAgB5B"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
export function explainBitmapFontParse(text, options) {
|
|
2
|
+
const json = probeJson(text);
|
|
3
|
+
if (json !== null)
|
|
4
|
+
return buildExplanation('json', json, options);
|
|
5
|
+
const xml = probeXml(text);
|
|
6
|
+
if (xml !== null)
|
|
7
|
+
return buildExplanation('xml', xml, options);
|
|
8
|
+
const fnt = probeFnt(text);
|
|
9
|
+
if (fnt !== null)
|
|
10
|
+
return buildExplanation('fnt', fnt, options);
|
|
11
|
+
return {
|
|
12
|
+
charCount: 0,
|
|
13
|
+
detectedFormat: 'unknown',
|
|
14
|
+
kerningCount: 0,
|
|
15
|
+
pageCount: 0,
|
|
16
|
+
reason: 'invalid-data',
|
|
17
|
+
success: false,
|
|
18
|
+
unresolvedPages: [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function buildExplanation(detectedFormat, probe, options) {
|
|
22
|
+
let reason;
|
|
23
|
+
const unresolvedPages = [];
|
|
24
|
+
if (!probe.hasCommon) {
|
|
25
|
+
reason = 'missing-common';
|
|
26
|
+
}
|
|
27
|
+
else if (probe.charCount === 0) {
|
|
28
|
+
reason = 'missing-chars';
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
const resolvePage = options?.resolvePage;
|
|
32
|
+
for (const pageId of probe.referencedPages) {
|
|
33
|
+
if (resolvePage === undefined || resolvePage(pageId, '') === null) {
|
|
34
|
+
unresolvedPages.push(pageId);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
reason = unresolvedPages.length > 0 ? 'unresolved-page' : 'ok';
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
charCount: probe.charCount,
|
|
41
|
+
detectedFormat,
|
|
42
|
+
kerningCount: probe.kerningCount,
|
|
43
|
+
pageCount: probe.pageIds.length,
|
|
44
|
+
reason,
|
|
45
|
+
success: reason === 'ok',
|
|
46
|
+
unresolvedPages,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function isObject(value) {
|
|
50
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
function probeJson(text) {
|
|
53
|
+
let root;
|
|
54
|
+
try {
|
|
55
|
+
root = JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (!isObject(root))
|
|
61
|
+
return null;
|
|
62
|
+
const common = root.common;
|
|
63
|
+
const hasCommon = isObject(common) &&
|
|
64
|
+
typeof common.lineHeight === 'number' &&
|
|
65
|
+
Number.isFinite(common.lineHeight) &&
|
|
66
|
+
typeof common.base === 'number' &&
|
|
67
|
+
Number.isFinite(common.base);
|
|
68
|
+
const rawChars = root.chars;
|
|
69
|
+
const charIterable = Array.isArray(rawChars) ? rawChars : isObject(rawChars) ? Object.values(rawChars) : [];
|
|
70
|
+
let charCount = 0;
|
|
71
|
+
const referencedPages = new Set();
|
|
72
|
+
for (const raw of charIterable) {
|
|
73
|
+
if (isObject(raw) && typeof raw.id === 'number' && Number.isFinite(raw.id)) {
|
|
74
|
+
charCount++;
|
|
75
|
+
const page = typeof raw.page === 'number' && Number.isFinite(raw.page) ? raw.page : 0;
|
|
76
|
+
referencedPages.add(page);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const pageIds = [];
|
|
80
|
+
if (Array.isArray(root.pages)) {
|
|
81
|
+
for (let id = 0; id < root.pages.length; id++)
|
|
82
|
+
pageIds.push(id);
|
|
83
|
+
}
|
|
84
|
+
let kerningCount = 0;
|
|
85
|
+
if (Array.isArray(root.kernings)) {
|
|
86
|
+
for (const raw of root.kernings) {
|
|
87
|
+
if (isObject(raw) &&
|
|
88
|
+
typeof raw.first === 'number' &&
|
|
89
|
+
typeof raw.second === 'number' &&
|
|
90
|
+
typeof raw.amount === 'number') {
|
|
91
|
+
kerningCount++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { charCount, hasCommon, kerningCount, pageIds, referencedPages };
|
|
96
|
+
}
|
|
97
|
+
function probeFnt(text) {
|
|
98
|
+
let hasCommon = false;
|
|
99
|
+
let charCount = 0;
|
|
100
|
+
let kerningCount = 0;
|
|
101
|
+
const pageIds = [];
|
|
102
|
+
const referencedPages = new Set();
|
|
103
|
+
let hasAnyTag = false;
|
|
104
|
+
for (const rawLine of text.split(/\r\n?|\n/)) {
|
|
105
|
+
const line = rawLine.trim();
|
|
106
|
+
if (line === '')
|
|
107
|
+
continue;
|
|
108
|
+
const spaceAt = line.search(/\s/);
|
|
109
|
+
const tag = spaceAt < 0 ? line : line.slice(0, spaceAt);
|
|
110
|
+
if (tag === 'common') {
|
|
111
|
+
hasAnyTag = true;
|
|
112
|
+
const fields = parseFntFields(line.slice(tag.length));
|
|
113
|
+
hasCommon = fields.lineHeight !== undefined && fields.base !== undefined;
|
|
114
|
+
}
|
|
115
|
+
else if (tag === 'page') {
|
|
116
|
+
hasAnyTag = true;
|
|
117
|
+
const fields = parseFntFields(line.slice(tag.length));
|
|
118
|
+
const id = readFntNumber(fields.id);
|
|
119
|
+
if (id !== null)
|
|
120
|
+
pageIds.push(id);
|
|
121
|
+
}
|
|
122
|
+
else if (tag === 'char') {
|
|
123
|
+
hasAnyTag = true;
|
|
124
|
+
const fields = parseFntFields(line.slice(tag.length));
|
|
125
|
+
if (fields.id !== undefined) {
|
|
126
|
+
charCount++;
|
|
127
|
+
const page = readFntNumber(fields.page) ?? 0;
|
|
128
|
+
referencedPages.add(page);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (tag === 'kerning') {
|
|
132
|
+
hasAnyTag = true;
|
|
133
|
+
kerningCount++;
|
|
134
|
+
}
|
|
135
|
+
else if (tag === 'info' || tag === 'chars' || tag === 'kernings') {
|
|
136
|
+
hasAnyTag = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!hasAnyTag)
|
|
140
|
+
return null;
|
|
141
|
+
return { charCount, hasCommon, kerningCount, pageIds, referencedPages };
|
|
142
|
+
}
|
|
143
|
+
function probeXml(text) {
|
|
144
|
+
const trimmed = text.trimStart();
|
|
145
|
+
if (!trimmed.startsWith('<'))
|
|
146
|
+
return null;
|
|
147
|
+
if (trimmed.indexOf('<font') < 0 && trimmed.indexOf('<Font') < 0)
|
|
148
|
+
return null;
|
|
149
|
+
let hasCommon = false;
|
|
150
|
+
let charCount = 0;
|
|
151
|
+
let kerningCount = 0;
|
|
152
|
+
const pageIds = [];
|
|
153
|
+
const referencedPages = new Set();
|
|
154
|
+
const commonMatch = /<common\s[^>]*?lineHeight\s*=\s*"(\d+)"[^>]*?base\s*=\s*"(\d+)"/i.exec(text);
|
|
155
|
+
if (commonMatch === null) {
|
|
156
|
+
const altMatch = /<common\s[^>]*?base\s*=\s*"(\d+)"[^>]*?lineHeight\s*=\s*"(\d+)"/i.exec(text);
|
|
157
|
+
hasCommon = altMatch !== null;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
hasCommon = true;
|
|
161
|
+
}
|
|
162
|
+
const pageRe = /<page\s[^>]*?id\s*=\s*"(\d+)"/gi;
|
|
163
|
+
let pageMatch;
|
|
164
|
+
while ((pageMatch = pageRe.exec(text)) !== null) {
|
|
165
|
+
pageIds.push(Number(pageMatch[1]));
|
|
166
|
+
}
|
|
167
|
+
const charRe = /<char\s[^>]*?id\s*=\s*"(\d+)"[^>]*/gi;
|
|
168
|
+
let charMatch;
|
|
169
|
+
while ((charMatch = charRe.exec(text)) !== null) {
|
|
170
|
+
charCount++;
|
|
171
|
+
const pageAttr = /\bpage\s*=\s*"(\d+)"/.exec(charMatch[0]);
|
|
172
|
+
referencedPages.add(pageAttr !== null ? Number(pageAttr[1]) : 0);
|
|
173
|
+
}
|
|
174
|
+
const kerningRe = /<kerning\s/gi;
|
|
175
|
+
while (kerningRe.exec(text) !== null)
|
|
176
|
+
kerningCount++;
|
|
177
|
+
return { charCount, hasCommon, kerningCount, pageIds, referencedPages };
|
|
178
|
+
}
|
|
179
|
+
function parseFntFields(rest) {
|
|
180
|
+
const fields = {};
|
|
181
|
+
const re = /([A-Za-z_]\w*)\s*=\s*(?:"([^"]*)"|(\S+))/g;
|
|
182
|
+
let match;
|
|
183
|
+
while ((match = re.exec(rest)) !== null) {
|
|
184
|
+
fields[match[1]] = match[2] !== undefined ? match[2] : (match[3] ?? '');
|
|
185
|
+
}
|
|
186
|
+
return fields;
|
|
187
|
+
}
|
|
188
|
+
function readFntNumber(value) {
|
|
189
|
+
if (value === undefined || value.trim() === '')
|
|
190
|
+
return null;
|
|
191
|
+
const parsed = Number(value);
|
|
192
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=explainBitmapFontParse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explainBitmapFontParse.js","sourceRoot":"","sources":["../src/explainBitmapFontParse.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './bitmapfontFormatRequirements.ts';
|
|
2
|
+
export * from './bitmapFontDetect.ts';
|
|
3
|
+
export * from './bitmapFontBinary.ts';
|
|
4
|
+
export * from './bitmapFontFnt.ts';
|
|
5
|
+
export * from './bitmapFontFormatKind.ts';
|
|
6
|
+
export * from './bitmapFontJson.ts';
|
|
7
|
+
export * from './bitmapFontXml.ts';
|
|
8
|
+
export * from './explainBitmapFontParse.ts';
|
|
9
|
+
export * from './types.ts';
|
|
2
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAClD,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './bitmapfontFormatRequirements.js';
|
|
2
|
+
export * from './bitmapFontDetect.js';
|
|
3
|
+
export * from './bitmapFontBinary.js';
|
|
4
|
+
export * from './bitmapFontFnt.js';
|
|
5
|
+
export * from './bitmapFontFormatKind.js';
|
|
6
|
+
export * from './bitmapFontJson.js';
|
|
7
|
+
export * from './bitmapFontXml.js';
|
|
8
|
+
export * from './explainBitmapFontParse.js';
|
|
9
|
+
export * from './types.js';
|
|
2
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAClD,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BitmapFont, BitmapFontEncoding, BitmapFontParseOptions } from '@flighthq/bitmapfont/contract';
|
|
2
|
+
import type { ImportDiagnostic } from '@flighthq/importdiagnostics/contract';
|
|
3
|
+
/** Identifies a bitmap font descriptor format for detection and dispatch. */
|
|
4
|
+
export type BitmapFontFormatKind = string;
|
|
5
|
+
export declare const BitmapFontFormatKindBmFontBinary = "BmFontBinary";
|
|
6
|
+
export declare const BitmapFontFormatKindBmFontText = "BmFontText";
|
|
7
|
+
export declare const BitmapFontFormatKindBmFontXml = "BmFontXml";
|
|
8
|
+
export declare const BitmapFontFormatKindBmFontJson = "BmFontJson";
|
|
9
|
+
export interface BitmapFontFormatEntry {
|
|
10
|
+
detect: (bytes: Readonly<Uint8Array>) => boolean;
|
|
11
|
+
parse: (bytes: Readonly<Uint8Array>, options?: Readonly<BitmapFontParseOptions>, diagnostics?: ImportDiagnostic[]) => BitmapFont | null;
|
|
12
|
+
}
|
|
13
|
+
export interface BitmapFontFormatDescriptor {
|
|
14
|
+
readonly entry: BitmapFontFormatEntry;
|
|
15
|
+
readonly kind: BitmapFontFormatKind;
|
|
16
|
+
}
|
|
17
|
+
export interface BitmapFontImportOptions {
|
|
18
|
+
readonly formats?: readonly Readonly<BitmapFontFormatDescriptor>[];
|
|
19
|
+
}
|
|
20
|
+
export type BitmapFontParseExplanationFormat = 'fnt' | 'json' | 'unknown' | 'xml';
|
|
21
|
+
export type BitmapFontParseExplanationReason = 'invalid-data' | 'missing-chars' | 'missing-common' | 'ok' | 'unresolved-page';
|
|
22
|
+
export interface BitmapFontParseExplanation {
|
|
23
|
+
charCount: number;
|
|
24
|
+
detectedFormat: BitmapFontParseExplanationFormat;
|
|
25
|
+
kerningCount: number;
|
|
26
|
+
pageCount: number;
|
|
27
|
+
reason: BitmapFontParseExplanationReason;
|
|
28
|
+
success: boolean;
|
|
29
|
+
unresolvedPages: readonly number[];
|
|
30
|
+
}
|
|
31
|
+
export interface BitmapFontCharRecord {
|
|
32
|
+
height: number;
|
|
33
|
+
id: number;
|
|
34
|
+
page: number;
|
|
35
|
+
width: number;
|
|
36
|
+
x: number;
|
|
37
|
+
xadvance: number;
|
|
38
|
+
xoffset: number;
|
|
39
|
+
y: number;
|
|
40
|
+
yoffset: number;
|
|
41
|
+
}
|
|
42
|
+
export interface BitmapFontKerningRecord {
|
|
43
|
+
amount: number;
|
|
44
|
+
first: number;
|
|
45
|
+
second: number;
|
|
46
|
+
}
|
|
47
|
+
export interface BitmapFontPageRecord {
|
|
48
|
+
file: string;
|
|
49
|
+
id: number;
|
|
50
|
+
}
|
|
51
|
+
export interface BitmapFontRecord {
|
|
52
|
+
base: number;
|
|
53
|
+
chars: BitmapFontCharRecord[];
|
|
54
|
+
encoding: BitmapFontEncoding;
|
|
55
|
+
kernings: BitmapFontKerningRecord[];
|
|
56
|
+
lineHeight: number;
|
|
57
|
+
pages: BitmapFontPageRecord[];
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,6EAA6E;AAC7E,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE1C,eAAO,MAAM,gCAAgC,iBAAiB,CAAC;AAC/D,eAAO,MAAM,8BAA8B,eAAe,CAAC;AAC3D,eAAO,MAAM,6BAA6B,cAAc,CAAC;AACzD,eAAO,MAAM,8BAA8B,eAAe,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;IACjD,KAAK,EAAE,CACL,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,OAAO,CAAC,EAAE,QAAQ,CAAC,sBAAsB,CAAC,EAC1C,WAAW,CAAC,EAAE,gBAAgB,EAAE,KAC7B,UAAU,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;CACrC;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,QAAQ,CAAC,0BAA0B,CAAC,EAAE,CAAC;CACpE;AAED,MAAM,MAAM,gCAAgC,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;AAElF,MAAM,MAAM,gCAAgC,GACxC,cAAc,GACd,eAAe,GACf,gBAAgB,GAChB,IAAI,GACJ,iBAAiB,CAAC;AAEtB,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,gCAAgC,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gCAAgC,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,CAAC,EAAE,MAAM,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC/B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const BitmapFontFormatKindBmFontBinary = 'BmFontBinary';
|
|
2
|
+
export const BitmapFontFormatKindBmFontText = 'BmFontText';
|
|
3
|
+
export const BitmapFontFormatKindBmFontXml = 'BmFontXml';
|
|
4
|
+
export const BitmapFontFormatKindBmFontJson = 'BmFontJson';
|
|
5
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,gCAAgC,GAAG,cAAc,CAAC;AAC/D,MAAM,CAAC,MAAM,8BAA8B,GAAG,YAAY,CAAC;AAC3D,MAAM,CAAC,MAAM,6BAA6B,GAAG,WAAW,CAAC;AACzD,MAAM,CAAC,MAAM,8BAA8B,GAAG,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/bitmapfont-formats",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,20 +32,26 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsc -b",
|
|
34
34
|
"clean": "tsc -b --clean",
|
|
35
|
+
"generate": "tsx ../../scripts/generate-rust-package.ts --crate flight-bitmapfont-formats --package bitmapfont-formats",
|
|
36
|
+
"generate:check": "tsx ../../scripts/generate-rust-package.ts --crate flight-bitmapfont-formats --package bitmapfont-formats --check",
|
|
35
37
|
"test": "vitest run --config vitest.config.ts",
|
|
36
38
|
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
37
39
|
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
38
40
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|
|
41
|
-
"@flighthq/bitmapfont": "0.
|
|
42
|
-
"@flighthq/
|
|
43
|
-
"@flighthq/
|
|
44
|
-
"@flighthq/
|
|
43
|
+
"@flighthq/bitmapfont": "0.6.0",
|
|
44
|
+
"@flighthq/encoding": "0.6.0",
|
|
45
|
+
"@flighthq/glyphatlas": "0.6.0",
|
|
46
|
+
"@flighthq/importdiagnostics": "0.6.0",
|
|
47
|
+
"@flighthq/registry": "0.6.0",
|
|
48
|
+
"@flighthq/requirement": "0.6.0",
|
|
49
|
+
"@flighthq/textureatlas": "0.6.0",
|
|
50
|
+
"@flighthq/xml": "0.6.0"
|
|
45
51
|
},
|
|
46
52
|
"devDependencies": {
|
|
47
|
-
"@flighthq/
|
|
48
|
-
"typescript": "^
|
|
53
|
+
"@flighthq/image": "*",
|
|
54
|
+
"typescript": "^7.0.2"
|
|
49
55
|
},
|
|
50
56
|
"description": "AngelCode/BMFont codec neighbor of @flighthq/bitmapfont — parse the text/XML/JSON .fnt variants into a BitmapFont and re-emit the text form",
|
|
51
57
|
"sideEffects": false
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createTextureAtlas } from '@flighthq/textureatlas/contract';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { parseBitmapFontBinary } from './bitmapFontBinary.ts';
|
|
5
|
+
import type { BitmapFontCharRecord, BitmapFontKerningRecord, BitmapFontPageRecord } from './types.ts';
|
|
6
|
+
|
|
7
|
+
function buildBinaryFont(options: {
|
|
8
|
+
chars?: BitmapFontCharRecord[];
|
|
9
|
+
common?: { base: number; lineHeight: number };
|
|
10
|
+
kernings?: BitmapFontKerningRecord[];
|
|
11
|
+
pages?: BitmapFontPageRecord[];
|
|
12
|
+
}): Uint8Array {
|
|
13
|
+
const blocks: Uint8Array[] = [];
|
|
14
|
+
|
|
15
|
+
if (options.common !== undefined) {
|
|
16
|
+
const common = new Uint8Array(15);
|
|
17
|
+
const view = new DataView(common.buffer);
|
|
18
|
+
view.setUint16(0, options.common.lineHeight, true);
|
|
19
|
+
view.setUint16(2, options.common.base, true);
|
|
20
|
+
blocks.push(makeBlock(2, common));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (options.pages !== undefined && options.pages.length > 0) {
|
|
24
|
+
const parts: number[] = [];
|
|
25
|
+
for (const page of options.pages) {
|
|
26
|
+
for (let i = 0; i < page.file.length; i++) parts.push(page.file.charCodeAt(i));
|
|
27
|
+
parts.push(0);
|
|
28
|
+
}
|
|
29
|
+
blocks.push(makeBlock(3, new Uint8Array(parts)));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (options.chars !== undefined) {
|
|
33
|
+
const data = new Uint8Array(options.chars.length * 20);
|
|
34
|
+
const view = new DataView(data.buffer);
|
|
35
|
+
for (let i = 0; i < options.chars.length; i++) {
|
|
36
|
+
const c = options.chars[i];
|
|
37
|
+
const off = i * 20;
|
|
38
|
+
view.setUint32(off, c.id, true);
|
|
39
|
+
view.setUint16(off + 4, c.x, true);
|
|
40
|
+
view.setUint16(off + 6, c.y, true);
|
|
41
|
+
view.setUint16(off + 8, c.width, true);
|
|
42
|
+
view.setUint16(off + 10, c.height, true);
|
|
43
|
+
view.setInt16(off + 12, c.xoffset, true);
|
|
44
|
+
view.setInt16(off + 14, c.yoffset, true);
|
|
45
|
+
view.setInt16(off + 16, c.xadvance, true);
|
|
46
|
+
view.setUint8(off + 18, c.page);
|
|
47
|
+
}
|
|
48
|
+
blocks.push(makeBlock(4, data));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (options.kernings !== undefined && options.kernings.length > 0) {
|
|
52
|
+
const data = new Uint8Array(options.kernings.length * 10);
|
|
53
|
+
const view = new DataView(data.buffer);
|
|
54
|
+
for (let i = 0; i < options.kernings.length; i++) {
|
|
55
|
+
const k = options.kernings[i];
|
|
56
|
+
const off = i * 10;
|
|
57
|
+
view.setUint32(off, k.first, true);
|
|
58
|
+
view.setUint32(off + 4, k.second, true);
|
|
59
|
+
view.setInt16(off + 8, k.amount, true);
|
|
60
|
+
}
|
|
61
|
+
blocks.push(makeBlock(5, data));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let total = 4;
|
|
65
|
+
for (const block of blocks) total += block.length;
|
|
66
|
+
const result = new Uint8Array(total);
|
|
67
|
+
result[0] = 66;
|
|
68
|
+
result[1] = 77;
|
|
69
|
+
result[2] = 70;
|
|
70
|
+
result[3] = 3;
|
|
71
|
+
let offset = 4;
|
|
72
|
+
for (const block of blocks) {
|
|
73
|
+
result.set(block, offset);
|
|
74
|
+
offset += block.length;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makeBlock(type: number, data: Uint8Array): Uint8Array {
|
|
80
|
+
const block = new Uint8Array(5 + data.length);
|
|
81
|
+
block[0] = type;
|
|
82
|
+
const view = new DataView(block.buffer);
|
|
83
|
+
view.setUint32(1, data.length, true);
|
|
84
|
+
block.set(data, 5);
|
|
85
|
+
return block;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
describe('parseBitmapFontBinary', () => {
|
|
89
|
+
it('returns null for empty input', () => {
|
|
90
|
+
expect(parseBitmapFontBinary(new Uint8Array(0))).toBe(null);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns null for wrong magic bytes', () => {
|
|
94
|
+
expect(parseBitmapFontBinary(new Uint8Array([0, 0, 0, 3]))).toBe(null);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('returns null for wrong version', () => {
|
|
98
|
+
expect(parseBitmapFontBinary(new Uint8Array([66, 77, 70, 2]))).toBe(null);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('returns null when common block is missing', () => {
|
|
102
|
+
const bytes = buildBinaryFont({
|
|
103
|
+
chars: [{ height: 10, id: 65, page: 0, width: 10, x: 0, xadvance: 10, xoffset: 0, y: 0, yoffset: 0 }],
|
|
104
|
+
});
|
|
105
|
+
expect(parseBitmapFontBinary(bytes)).toBe(null);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('returns null when chars block is missing', () => {
|
|
109
|
+
const bytes = buildBinaryFont({ common: { base: 24, lineHeight: 32 } });
|
|
110
|
+
expect(parseBitmapFontBinary(bytes)).toBe(null);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('parses a minimal binary font', () => {
|
|
114
|
+
const bytes = buildBinaryFont({
|
|
115
|
+
chars: [{ height: 10, id: 65, page: 0, width: 8, x: 0, xadvance: 10, xoffset: 1, y: 0, yoffset: 2 }],
|
|
116
|
+
common: { base: 24, lineHeight: 32 },
|
|
117
|
+
pages: [{ file: 'font.png', id: 0 }],
|
|
118
|
+
});
|
|
119
|
+
const font = parseBitmapFontBinary(bytes, {
|
|
120
|
+
resolvePage: () => createTextureAtlas(),
|
|
121
|
+
});
|
|
122
|
+
expect(font).not.toBe(null);
|
|
123
|
+
expect(font!.glyphs.has(65)).toBe(true);
|
|
124
|
+
const glyph = font!.glyphs.get(65)!;
|
|
125
|
+
expect(glyph.width).toBe(8);
|
|
126
|
+
expect(glyph.advance).toBe(10);
|
|
127
|
+
expect(glyph.bearingX).toBe(1);
|
|
128
|
+
expect(glyph.bearingY).toBe(24 - 2);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('parses kerning pairs', () => {
|
|
132
|
+
const bytes = buildBinaryFont({
|
|
133
|
+
chars: [{ height: 10, id: 65, page: 0, width: 8, x: 0, xadvance: 10, xoffset: 0, y: 0, yoffset: 0 }],
|
|
134
|
+
common: { base: 24, lineHeight: 32 },
|
|
135
|
+
kernings: [{ amount: -2, first: 65, second: 86 }],
|
|
136
|
+
pages: [{ file: 'font.png', id: 0 }],
|
|
137
|
+
});
|
|
138
|
+
const font = parseBitmapFontBinary(bytes, {
|
|
139
|
+
resolvePage: () => createTextureAtlas(),
|
|
140
|
+
});
|
|
141
|
+
expect(font).not.toBe(null);
|
|
142
|
+
expect(font!.kerning.size).toBe(1);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('parses multiple pages', () => {
|
|
146
|
+
const bytes = buildBinaryFont({
|
|
147
|
+
chars: [
|
|
148
|
+
{ height: 10, id: 65, page: 0, width: 8, x: 0, xadvance: 10, xoffset: 0, y: 0, yoffset: 0 },
|
|
149
|
+
{ height: 10, id: 66, page: 1, width: 8, x: 0, xadvance: 10, xoffset: 0, y: 0, yoffset: 0 },
|
|
150
|
+
],
|
|
151
|
+
common: { base: 24, lineHeight: 32 },
|
|
152
|
+
pages: [
|
|
153
|
+
{ file: 'font0.png', id: 0 },
|
|
154
|
+
{ file: 'font1.png', id: 1 },
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
const font = parseBitmapFontBinary(bytes, {
|
|
158
|
+
resolvePage: () => createTextureAtlas(),
|
|
159
|
+
});
|
|
160
|
+
expect(font).not.toBe(null);
|
|
161
|
+
expect(font!.pages).toHaveLength(2);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('returns null when a referenced page cannot be resolved', () => {
|
|
165
|
+
const bytes = buildBinaryFont({
|
|
166
|
+
chars: [{ height: 10, id: 65, page: 0, width: 8, x: 0, xadvance: 10, xoffset: 0, y: 0, yoffset: 0 }],
|
|
167
|
+
common: { base: 24, lineHeight: 32 },
|
|
168
|
+
pages: [{ file: 'font.png', id: 0 }],
|
|
169
|
+
});
|
|
170
|
+
expect(parseBitmapFontBinary(bytes)).toBe(null);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('handles negative xoffset and yoffset', () => {
|
|
174
|
+
const bytes = buildBinaryFont({
|
|
175
|
+
chars: [{ height: 10, id: 65, page: 0, width: 8, x: 0, xadvance: 10, xoffset: -3, y: 0, yoffset: -1 }],
|
|
176
|
+
common: { base: 24, lineHeight: 32 },
|
|
177
|
+
pages: [{ file: 'font.png', id: 0 }],
|
|
178
|
+
});
|
|
179
|
+
const font = parseBitmapFontBinary(bytes, {
|
|
180
|
+
resolvePage: () => createTextureAtlas(),
|
|
181
|
+
});
|
|
182
|
+
expect(font).not.toBe(null);
|
|
183
|
+
const glyph = font!.glyphs.get(65)!;
|
|
184
|
+
expect(glyph.bearingX).toBe(-3);
|
|
185
|
+
expect(glyph.bearingY).toBe(24 - -1);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('tolerates truncated block gracefully', () => {
|
|
189
|
+
const bytes = buildBinaryFont({
|
|
190
|
+
chars: [{ height: 10, id: 65, page: 0, width: 8, x: 0, xadvance: 10, xoffset: 0, y: 0, yoffset: 0 }],
|
|
191
|
+
common: { base: 24, lineHeight: 32 },
|
|
192
|
+
});
|
|
193
|
+
const truncated = bytes.slice(0, bytes.length - 5);
|
|
194
|
+
expect(parseBitmapFontBinary(truncated)).toBe(null);
|
|
195
|
+
});
|
|
196
|
+
});
|