@kolkrabbi/kol-foundry 0.4.2 → 0.5.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/package.json +4 -4
- package/src/FontPreviewSection.jsx +1 -1
- package/src/GlyphItem.jsx +51 -0
- package/src/GlyphMetricsSection.jsx +1 -1
- package/src/TypefaceStyleSection.jsx +1 -1
- package/src/VariableFontSection.jsx +1 -1
- package/src/engine/FontInfo.js +333 -0
- package/src/engine/FontLoader.js +76 -0
- package/src/engine/FontViewerComponent.jsx +449 -0
- package/src/engine/FontViewerSection.jsx +10 -0
- package/src/engine/GlyphAnimator.js +119 -0
- package/src/engine/MetricsOverlay.js +169 -0
- package/src/engine/Types.js +22 -0
- package/src/engine/UIControls.js +92 -0
- package/src/engine/VariationAxes.js +71 -0
- package/src/index.js +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-foundry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "KOL foundry component set — the type-specimen apparatus: typeface hero, variable-font axis playground, parsed-metric glyph inspector, character-set browser, font-preview, and the typeface-catalog grid. Every component renders, inspects, or manipulates a live font. Typeface data is consumer-injected. Sits above @kolkrabbi/kol-{theme,icons,component}.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
".": "./src/index.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@kolkrabbi/kol-
|
|
15
|
-
"@kolkrabbi/kol-
|
|
16
|
-
"@kolkrabbi/kol-theme": "0.
|
|
14
|
+
"@kolkrabbi/kol-icons": "0.7.0",
|
|
15
|
+
"@kolkrabbi/kol-component": "0.12.0",
|
|
16
|
+
"@kolkrabbi/kol-theme": "0.11.0"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
19
|
"react": "^18.3.0 || ^19.0.0",
|
|
@@ -157,7 +157,7 @@ const FontPreviewSection = ({
|
|
|
157
157
|
onStyleChange={setSelectedStyleVariant}
|
|
158
158
|
showDropdown={showDropdown}
|
|
159
159
|
badgeText={badgeText}
|
|
160
|
-
icon="
|
|
160
|
+
icon="foundation"
|
|
161
161
|
size="sm"
|
|
162
162
|
selectedWeight={selectedWeight}
|
|
163
163
|
onWeightChange={setSelectedWeight}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GlyphItem - Single glyph character display
|
|
5
|
+
*
|
|
6
|
+
* Foundry atom for displaying individual glyph characters
|
|
7
|
+
* Features bordered box with hover theme flip effect
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props
|
|
10
|
+
* @param {string} props.glyph - Single character to display
|
|
11
|
+
* @param {string} props.fontStyle - Font style: 'normal' or 'italic'
|
|
12
|
+
* @param {string} props.fontFamily - Font family name
|
|
13
|
+
* @param {boolean} props.isSelected - Whether this glyph is selected
|
|
14
|
+
* @param {Function} props.onClick - Callback fired on click
|
|
15
|
+
* @param {Function} props.onMouseEnter - Callback fired on mouse enter
|
|
16
|
+
* @param {string} props.className - Additional classes
|
|
17
|
+
*/
|
|
18
|
+
const GlyphItem = ({
|
|
19
|
+
glyph,
|
|
20
|
+
fontStyle = 'normal',
|
|
21
|
+
fontFamily = 'TGMalromur',
|
|
22
|
+
isSelected = false,
|
|
23
|
+
onClick,
|
|
24
|
+
onMouseEnter,
|
|
25
|
+
className = ''
|
|
26
|
+
}) => {
|
|
27
|
+
const handleClick = () => {
|
|
28
|
+
onClick?.(glyph)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const handleMouseEnter = () => {
|
|
32
|
+
onMouseEnter?.(glyph)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
onClick={handleClick}
|
|
38
|
+
onMouseEnter={handleMouseEnter}
|
|
39
|
+
className={`${isSelected ? 'bg-auto text-auto-inverse' : 'hoverFlipTheme'} aspect-square border rounded-lg flex items-center justify-center text-3xl leading-none transition-colors duration-300 w-16 h-16 cursor-pointer ${className}`.trim()}
|
|
40
|
+
style={{
|
|
41
|
+
fontFamily: fontFamily,
|
|
42
|
+
fontStyle: fontStyle,
|
|
43
|
+
borderColor: 'var(--kol-border-default)'
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{glyph}
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default GlyphItem
|
|
@@ -85,7 +85,7 @@ const GlyphMetricsSection = ({
|
|
|
85
85
|
showDropdown={showDropdown || (showAxisDropdown && valueOptions.length > 0)}
|
|
86
86
|
styleOptions={showDropdown ? italicOptions : valueOptions}
|
|
87
87
|
badgeText={badgeText}
|
|
88
|
-
icon="
|
|
88
|
+
icon="foundation"
|
|
89
89
|
size="sm"
|
|
90
90
|
showWeightDropdown={showAxisDropdown && valueOptions.length > 0}
|
|
91
91
|
weightOptions={valueOptions}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// core/FontInfo.js
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
import { AXIS_NAMES } from './Types.js';
|
|
6
|
+
|
|
7
|
+
const PREFERRED_LANGUAGE_CODES = ['en', 'en-US', 'en-GB', 'und', '0'];
|
|
8
|
+
|
|
9
|
+
export class FontInfoRenderer {
|
|
10
|
+
/**
|
|
11
|
+
* Renders font information into a specified container
|
|
12
|
+
* @param {HTMLElement} container - Container element to render into
|
|
13
|
+
* @param {Object} info - Font information object
|
|
14
|
+
*/
|
|
15
|
+
static renderFontInfo(container, info) {
|
|
16
|
+
if (!container || !info) return;
|
|
17
|
+
|
|
18
|
+
container.innerHTML = `
|
|
19
|
+
<p><strong>Names</strong><br>
|
|
20
|
+
Font family → ${info.fontFamily}<br>
|
|
21
|
+
Font style → ${info.fontStyle}<br>
|
|
22
|
+
|
|
23
|
+
<p><strong>Font file</strong><br>
|
|
24
|
+
Filename → ${info.filename}<br>
|
|
25
|
+
File format → ${info.format}<br>
|
|
26
|
+
Variable font → ${info.axes.length > 0 ? 'Yes' : 'No'}<br>
|
|
27
|
+
Monospaced → ${info.isFixedPitch}<br>
|
|
28
|
+
Units per Em → ${info.unitsPerEm}<br>
|
|
29
|
+
Glyph count → ${info.glyphCount}</p>
|
|
30
|
+
|
|
31
|
+
${info.axes.length ? `
|
|
32
|
+
<p><strong>Variable font axes</strong><br>
|
|
33
|
+
${info.axes.map(axis =>
|
|
34
|
+
`${axis.name} (${axis.tag}) → ${axis.min} to ${axis.max}, default → ${axis.default}`
|
|
35
|
+
).join('<br>')}</p>` : ''}
|
|
36
|
+
|
|
37
|
+
<p><strong>Version and dates</strong><br>
|
|
38
|
+
Version → ${info.version}<br>
|
|
39
|
+
Created → ${info.created}<br>
|
|
40
|
+
Modified → ${info.modified}</p>
|
|
41
|
+
|
|
42
|
+
<p><strong>Foundry</strong><br>
|
|
43
|
+
Manufacturer → ${info.manufacturer}<br>
|
|
44
|
+
${info.manufacturerURL !== 'Unknown' ? `Manufacturer URL → ${info.manufacturerURL}<br>` : ''}
|
|
45
|
+
Designer → ${info.designer}<br>
|
|
46
|
+
${info.designerURL !== 'Unknown' ? `Designer URL → ${info.designerURL}<br>` : ''}
|
|
47
|
+
Vendor ID → ${info.vendorID}</p>
|
|
48
|
+
|
|
49
|
+
<p><strong>Copyright</strong><br>
|
|
50
|
+
${info.copyright}</p>
|
|
51
|
+
|
|
52
|
+
<p><strong>Licence</strong><br>
|
|
53
|
+
${info.license}<br>
|
|
54
|
+
${info.licenseURL !== 'Unknown' ? `Licence URL → ${info.licenseURL}</p>` : '</p>'}
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Renders glyph information into a specified container
|
|
60
|
+
* @param {HTMLElement} container - Container element to render into
|
|
61
|
+
* @param {Object} font - OpenType.js font object
|
|
62
|
+
* @param {string} glyph - Current glyph character
|
|
63
|
+
*/
|
|
64
|
+
static renderGlyphInfo(container, font, glyph) {
|
|
65
|
+
if (!container) return;
|
|
66
|
+
|
|
67
|
+
if (!font || !glyph) {
|
|
68
|
+
container.innerHTML = '<p>No glyph selected</p>';
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const glyphIndex = font.charToGlyphIndex(glyph);
|
|
73
|
+
const glyphObj = font.glyphs.get(glyphIndex);
|
|
74
|
+
|
|
75
|
+
container.innerHTML = `
|
|
76
|
+
<div class="glyph-info-container">
|
|
77
|
+
<div class="info-column">
|
|
78
|
+
<p><strong>Glyph information</strong><br>
|
|
79
|
+
Character → ${glyph}<br>
|
|
80
|
+
Name → <span class="monospaced">${glyphObj.name}</span><br>
|
|
81
|
+
Unicode → <span class="monospaced">U+${glyphObj.unicode?.toString(16).toUpperCase().padStart(4, '0') || 'N/A'}</span><br>
|
|
82
|
+
Index → <span class="monospaced">${glyphIndex}</span><br>
|
|
83
|
+
Advance Width → <span class="monospaced">${glyphObj.advanceWidth}</span></p>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
${glyphObj.xMin !== undefined ? `
|
|
87
|
+
<div class="info-column">
|
|
88
|
+
<p><strong>Bounds</strong><br>
|
|
89
|
+
xMin → <span class="monospaced">${glyphObj.xMin}</span><br>
|
|
90
|
+
xMax → <span class="monospaced">${glyphObj.xMax}</span><br>
|
|
91
|
+
yMin → <span class="monospaced">${glyphObj.yMin}</span><br>
|
|
92
|
+
yMax → <span class="monospaced">${glyphObj.yMax}</span></p>
|
|
93
|
+
</div>
|
|
94
|
+
` : ''}
|
|
95
|
+
</div>
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Extracts comprehensive information about a font
|
|
102
|
+
* @param {Object} font - OpenType.js font object
|
|
103
|
+
* @param {string} filename - Original font filename
|
|
104
|
+
* @returns {Object} Detailed font information
|
|
105
|
+
*/
|
|
106
|
+
export function getFontInformation(font, filename) {
|
|
107
|
+
const names = font.names;
|
|
108
|
+
const os2 = font.tables.os2;
|
|
109
|
+
const head = font.tables.head;
|
|
110
|
+
|
|
111
|
+
const fontFamily = pickBestName([
|
|
112
|
+
names?.preferredFamily,
|
|
113
|
+
names?.fontFamily,
|
|
114
|
+
names?.windows?.preferredFamily,
|
|
115
|
+
names?.windows?.fontFamily
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
const fullName = pickBestName([
|
|
119
|
+
names?.fullName,
|
|
120
|
+
names?.windows?.fullName
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const version = pickBestName([
|
|
124
|
+
names?.version,
|
|
125
|
+
names?.windows?.version
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
const copyright = pickBestName([
|
|
129
|
+
names?.copyright,
|
|
130
|
+
names?.windows?.copyright
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
const manufacturer = pickBestName([
|
|
134
|
+
names?.manufacturer,
|
|
135
|
+
names?.windows?.manufacturer
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const manufacturerURL = pickBestName([
|
|
139
|
+
names?.manufacturerURL,
|
|
140
|
+
names?.windows?.manufacturerURL
|
|
141
|
+
]);
|
|
142
|
+
|
|
143
|
+
const designer = pickBestName([
|
|
144
|
+
names?.designer,
|
|
145
|
+
names?.windows?.designer
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
const designerURL = pickBestName([
|
|
149
|
+
names?.designerURL,
|
|
150
|
+
names?.windows?.designerURL
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
const license = pickBestName([
|
|
154
|
+
names?.license,
|
|
155
|
+
names?.windows?.license
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const licenseURL = pickBestName([
|
|
159
|
+
names?.licenseURL,
|
|
160
|
+
names?.windows?.licenseURL
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
filename,
|
|
165
|
+
fontFamily,
|
|
166
|
+
fullName,
|
|
167
|
+
version,
|
|
168
|
+
copyright,
|
|
169
|
+
manufacturer,
|
|
170
|
+
manufacturerURL,
|
|
171
|
+
designer,
|
|
172
|
+
designerURL,
|
|
173
|
+
license,
|
|
174
|
+
licenseURL,
|
|
175
|
+
vendorID: os2?.achVendID || 'Unknown',
|
|
176
|
+
format: font.outlinesFormat,
|
|
177
|
+
unitsPerEm: head?.unitsPerEm || 'Unknown',
|
|
178
|
+
created: head?.created ? new Date(head.created * 1000).toLocaleDateString() : 'Unknown',
|
|
179
|
+
modified: head?.modified ? new Date(head.modified * 1000).toLocaleDateString() : 'Unknown',
|
|
180
|
+
glyphCount: font.glyphs.length,
|
|
181
|
+
isFixedPitch: font.tables.post?.isFixedPitch ? 'Yes' : 'No',
|
|
182
|
+
fontStyle: extractFontStyle(font),
|
|
183
|
+
features: extractOpenTypeFeatures(font),
|
|
184
|
+
axes: extractVariableAxes(font)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Helper functions
|
|
189
|
+
/**
|
|
190
|
+
* Extracts a name from the OpenType names table using proper name ID structure
|
|
191
|
+
* @param {Object} names - The font.names object
|
|
192
|
+
* @param {number} nameId - OpenType name ID (1=family, 4=full name, 5=version, etc.)
|
|
193
|
+
* @returns {string} The extracted name or 'Unknown'
|
|
194
|
+
*/
|
|
195
|
+
function extractNameByID(names, nameId) {
|
|
196
|
+
if (!names) return 'Unknown';
|
|
197
|
+
|
|
198
|
+
// Try different platforms in order of preference
|
|
199
|
+
const platforms = ['windows', 'macintosh', 'unicode'];
|
|
200
|
+
|
|
201
|
+
for (const platform of platforms) {
|
|
202
|
+
if (names[platform] && names[platform][nameId]) {
|
|
203
|
+
const nameEntry = names[platform][nameId];
|
|
204
|
+
|
|
205
|
+
// Try different language codes
|
|
206
|
+
const languageCodes = ['en', 'en-US', 'en-GB', 'und', '0', '1033']; // 1033 is Windows English
|
|
207
|
+
|
|
208
|
+
for (const code of languageCodes) {
|
|
209
|
+
if (nameEntry[code]) {
|
|
210
|
+
return nameEntry[code];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// If no language code matches, get the first available value
|
|
215
|
+
const availableKeys = Object.keys(nameEntry);
|
|
216
|
+
if (availableKeys.length > 0) {
|
|
217
|
+
return nameEntry[availableKeys[0]];
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return 'Unknown';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Safely extracts a name value from font names table
|
|
227
|
+
* Tries multiple language codes and falls back to the first available value
|
|
228
|
+
* @param {Object} nameEntry - Name entry from font.names
|
|
229
|
+
* @returns {string} The extracted name or 'Unknown'
|
|
230
|
+
*/
|
|
231
|
+
function extractFontName(nameEntry) {
|
|
232
|
+
if (!nameEntry) {
|
|
233
|
+
return 'Unknown';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (const code of PREFERRED_LANGUAGE_CODES) {
|
|
237
|
+
if (nameEntry[code]) {
|
|
238
|
+
return nameEntry[code];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// If no standard language codes work, get the first available value
|
|
243
|
+
const availableKeys = Object.keys(nameEntry);
|
|
244
|
+
|
|
245
|
+
if (availableKeys.length > 0) {
|
|
246
|
+
const firstKey = availableKeys[0];
|
|
247
|
+
return nameEntry[firstKey];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return 'Unknown';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function pickBestName(entries = []) {
|
|
254
|
+
for (const entry of entries) {
|
|
255
|
+
const value = extractFontName(entry);
|
|
256
|
+
if (value && value !== 'Unknown') {
|
|
257
|
+
return value;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return 'Unknown';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Extracts font style (upright/italic/oblique) from OpenType tables
|
|
265
|
+
* @param {Object} font - OpenType.js font object
|
|
266
|
+
* @returns {string} Font style: 'Upright', 'Italic', or 'Oblique'
|
|
267
|
+
*/
|
|
268
|
+
function extractFontStyle(font) {
|
|
269
|
+
// Check OS/2 table fsSelection field first (most reliable)
|
|
270
|
+
if (font.tables.os2 && font.tables.os2.fsSelection !== undefined) {
|
|
271
|
+
const fsSelection = font.tables.os2.fsSelection;
|
|
272
|
+
|
|
273
|
+
// Bit 9 (512): Oblique/Slanted
|
|
274
|
+
if (fsSelection & 512) {
|
|
275
|
+
return 'Oblique';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Bit 0 (1): Italic
|
|
279
|
+
if (fsSelection & 1) {
|
|
280
|
+
return 'Italic';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Fallback to head table macStyle field
|
|
285
|
+
if (font.tables.head && font.tables.head.macStyle !== undefined) {
|
|
286
|
+
const macStyle = font.tables.head.macStyle;
|
|
287
|
+
|
|
288
|
+
// Bit 1 (2): Italic
|
|
289
|
+
if (macStyle & 2) {
|
|
290
|
+
return 'Italic';
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// If no italic/oblique flags are set, it's upright/roman
|
|
295
|
+
return 'Upright';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function extractOpenTypeFeatures(font) {
|
|
299
|
+
const features = [];
|
|
300
|
+
if (font.tables.gsub) {
|
|
301
|
+
font.tables.gsub.features.forEach(feature => {
|
|
302
|
+
features.push({
|
|
303
|
+
tag: feature.tag,
|
|
304
|
+
scripts: feature.feature.lookupListIndexes
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return features;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function extractVariableAxes(font) {
|
|
312
|
+
if (!font.tables.fvar) {
|
|
313
|
+
console.log('No fvar table found in font');
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
console.log('Raw fvar axes:', font.tables.fvar.axes);
|
|
318
|
+
|
|
319
|
+
const axes = font.tables.fvar.axes.map(axis => {
|
|
320
|
+
const mappedAxis = {
|
|
321
|
+
tag: axis.tag,
|
|
322
|
+
name: AXIS_NAMES[axis.tag] || axis.tag,
|
|
323
|
+
min: axis.minValue,
|
|
324
|
+
max: axis.maxValue,
|
|
325
|
+
default: axis.defaultValue
|
|
326
|
+
};
|
|
327
|
+
console.log('Mapped axis:', mappedAxis);
|
|
328
|
+
return mappedAxis;
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
console.log('Final extracted axes:', axes);
|
|
332
|
+
return axes;
|
|
333
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// core/FontLoader.js
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
import { getFontInformation } from './FontInfo.js';
|
|
6
|
+
// Namespace import for ESM/CJS interop: opentype.js ships a CJS-wrapped .mjs
|
|
7
|
+
// whose default export rollup (Vite 5) can't statically see. `parse` is a named
|
|
8
|
+
// export, so the namespace form resolves cleanly across bundlers.
|
|
9
|
+
import * as opentype from 'opentype.js';
|
|
10
|
+
|
|
11
|
+
export class FontLoader {
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new FontLoader instance
|
|
14
|
+
* @param {Object} options - Configuration options
|
|
15
|
+
* @param {Function} options.onFontLoaded - Callback when font is loaded
|
|
16
|
+
* @param {Function} options.onError - Callback when error occurs
|
|
17
|
+
*/
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
this.currentFont = null;
|
|
20
|
+
this.callbacks = options;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Loads a font from an ArrayBuffer
|
|
25
|
+
* @param {ArrayBuffer} buffer - The font file buffer
|
|
26
|
+
* @param {string} filename - Original filename
|
|
27
|
+
* @returns {Promise<Object>} Font information and loaded font
|
|
28
|
+
*/
|
|
29
|
+
async loadFont(buffer, filename) {
|
|
30
|
+
try {
|
|
31
|
+
// Parse the font using OpenType.js
|
|
32
|
+
const font = opentype.parse(buffer);
|
|
33
|
+
console.log('OpenType parsed font:', {
|
|
34
|
+
tables: Object.keys(font.tables),
|
|
35
|
+
hasFvar: !!font.tables.fvar,
|
|
36
|
+
axes: font.tables.fvar?.axes
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Create a unique name for this font instance
|
|
40
|
+
const uniqueFontName = `Font_${Date.now()}`;
|
|
41
|
+
|
|
42
|
+
// Create and load the font face
|
|
43
|
+
const fontFace = new FontFace(uniqueFontName, buffer);
|
|
44
|
+
await fontFace.load();
|
|
45
|
+
document.fonts.add(fontFace);
|
|
46
|
+
|
|
47
|
+
// Store current font and get info
|
|
48
|
+
this.currentFont = font;
|
|
49
|
+
const fontInfo = getFontInformation(font, filename);
|
|
50
|
+
|
|
51
|
+
console.log('Font info generated:', fontInfo);
|
|
52
|
+
|
|
53
|
+
this.callbacks.onFontLoaded?.({ font, fontInfo, fontFamily: uniqueFontName });
|
|
54
|
+
return { font, fontInfo, fontFamily: uniqueFontName };
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Error loading font:', error);
|
|
58
|
+
this.callbacks.onError?.(error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Cleans up the current font
|
|
65
|
+
*/
|
|
66
|
+
cleanup() {
|
|
67
|
+
if (this.currentFont) {
|
|
68
|
+
document.fonts.forEach(font => {
|
|
69
|
+
if (font.family.startsWith('Font_')) {
|
|
70
|
+
document.fonts.delete(font);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
this.currentFont = null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|