@@ -10,19 +10,25 @@ import { codepointsIntersectRanges, parseUnicodeRange } from './unicode-range'
1010
1111export { codepointsIntersectRanges , extractCodepoints , parseUnicodeRange } from './unicode-range'
1212
13+ type FontFormat = 'ttf' | 'otf' | 'woff' | 'woff2'
14+
15+ function fontFormat ( src : string ) : FontFormat {
16+ if ( src . endsWith ( '.woff2' ) )
17+ return 'woff2'
18+ if ( src . endsWith ( '.woff' ) )
19+ return 'woff'
20+ if ( src . endsWith ( '.otf' ) )
21+ return 'otf'
22+ return 'ttf'
23+ }
24+
1325export interface LoadFontsOptions {
1426 /**
15- * Whether the renderer supports WOFF2.
16- * When false, WOFF2 fonts will use satoriSrc (converted TTF) if available,
17- * otherwise they will be skipped.
18- */
19- supportsWoff2 : boolean
20- /**
21- * Prefer static (satoriSrc) fonts over WOFF2 when available.
22- * Used by Takumi to avoid WOFF2 subset decompression bugs while still
23- * falling through to WOFF2 when no static alternative exists.
27+ * Font formats the renderer can parse.
28+ * Satori: ttf, otf, woff
29+ * Takumi: ttf, woff2
2430 */
25- preferStatic ?: boolean
31+ supportedFormats : Set < FontFormat >
2632 /** Component pascalName — filters fonts to only what this component needs */
2733 component ?: string
2834 /** When set, ensures this font family is included even if not in requirements */
@@ -160,23 +166,22 @@ export async function loadAllFonts(event: H3Event, options: LoadFontsOptions): P
160166 const results = await Promise . all (
161167 fonts . map ( async ( f ) => {
162168 let src = f . src
163- const isWoff2 = f . src . endsWith ( '.woff2' )
169+ const srcFormat = fontFormat ( f . src )
164170
165- if ( isWoff2 && ( options . preferStatic || ! options . supportsWoff2 ) ) {
166- if ( f . satoriSrc ) {
167- // Use static alternative (TTF/WOFF) downloaded by fontless
171+ // If the primary src format isn't supported, try satoriSrc as alternative
172+ if ( ! options . supportedFormats . has ( srcFormat ) ) {
173+ if ( f . satoriSrc && options . supportedFormats . has ( fontFormat ( f . satoriSrc ) ) ) {
168174 src = f . satoriSrc
169175 }
170- else if ( ! options . supportsWoff2 ) {
171- // Satori: can't use WOFF2 at all , skip this font
176+ else {
177+ // No usable format available , skip this font
172178 return null
173179 }
174- // Takumi (preferStatic + supportsWoff2): fall through to WOFF2 — better than nothing
175180 }
176181
177182 let data = await loadFont ( event , f , src )
178- // When satoriSrc fails to load (e.g. 404), try original WOFF2 for renderers that support it
179- if ( ! data && src !== f . src && options . supportsWoff2 ) {
183+ // When satoriSrc fails to load (e.g. 404), try original src if its format is supported
184+ if ( ! data && src !== f . src && options . supportedFormats . has ( srcFormat ) ) {
180185 data = await loadFont ( event , f , f . src )
181186 if ( data )
182187 src = f . src
@@ -203,7 +208,7 @@ export async function loadAllFonts(event: H3Event, options: LoadFontsOptions): P
203208 const isCommunity = options . component && ( map as Record < string , any > ) [ options . component ] ?. category === 'community'
204209
205210 // Warn when rendering with Satori and only variable fonts are available (deduplicated)
206- if ( ! options . supportsWoff2 && loaded . length === 0 && fonts . length > 0 && ! isCommunity ) {
211+ if ( ! options . supportedFormats . has ( 'woff2' ) && loaded . length === 0 && fonts . length > 0 && ! isCommunity ) {
207212 const variableFamilies = [ ...new Set ( fonts . map ( f => f . family ) ) ]
208213 const warnKey = `variable-fonts-${ variableFamilies . join ( ',' ) } `
209214 if ( ! _warnedFontKeys . has ( warnKey ) ) {
0 commit comments