import { CreateFontCommand } from '../core/commands/assetCommands'; import type { FontStore, UploadedFont } from '../core/store/FontStore'; import { type ImportKind, type ImportOutcome, importFiles, importStored, pickAndImport } from './assetImport'; import type { EditorState } from './state'; /** What the file dialog offers and a drop accepts; the server rejects everything else anyway. */ export const FONT_ACCEPT = '.ttf,.otf,.woff,.woff2'; const FONT_EXTENSION = /\.(ttf|otf|woff|woff2)$/i; /** * Fonts are told apart by their name, not their MIME type: browsers report a dropped `.ttf` as * `font/ttf`, `application/x-font-ttf` or nothing at all, depending on the platform. */ export function isFontFileName(name: string): boolean { return FONT_EXTENSION.test(name); } export interface FontImportDeps { state: EditorState; fonts: FontStore; } const FONT_KIND: ImportKind = { type: 'font', noun: 'font', fallbackName: 'Font', store: (deps) => deps.fonts, // The bytes go up untouched: unlike a picture, a typeface cannot be re-encoded smaller here. prepare: (file) => isFontFileName(file.name) ? Promise.resolve({ blob: file, type: file.type || 'application/octet-stream' }) : Promise.reject(new Error('Not a font file – use TTF, OTF, WOFF or WOFF2.')), create: (name, font) => new CreateFontCommand(name, font.file), }; export function importFonts(files: readonly File[], deps: FontImportDeps): Promise { return importFiles(FONT_KIND, files, deps); } export function importStoredFont(font: UploadedFont, deps: FontImportDeps): ImportOutcome { return importStored(FONT_KIND, font, deps); } export function pickAndImportFonts(deps: FontImportDeps): void { pickAndImport(FONT_KIND, FONT_ACCEPT, deps); }