import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import fs from 'node:fs/promises'; import path from 'node:path'; import os from 'node:os'; import opentype from 'opentype.js'; import * as fontkit from 'fontkit'; import { registerFontConverter } from '../../src/converters/font.js'; import { resolve, listTargetFormats } from '../../src/converters/registry.js'; import { detectInputMime } from '../../src/mime.js'; async function parseWithOpentype(fixtureName) { const buf = await fs.readFile(path.join(import.meta.dirname, '..', 'fixtures', fixtureName)); const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); return opentype.parse(ab); } describe('font fixtures', () => { it('sample.otf parses as a CFF-flavored OpenType font', async () => { const font = await parseWithOpentype('sample.otf'); expect(font.outlinesFormat).toBe('cff'); expect(font.glyphs.length).toBe(1568); }); it('sample.ttf parses as a TrueType font with the same glyph count as sample.otf', async () => { const font = await parseWithOpentype('sample.ttf'); expect(font.outlinesFormat).toBe('truetype'); expect(font.glyphs.length).toBe(1568); }); it('sample.dfont is a valid dfont container with one embedded sfnt font', async () => { const buffer = await fs.readFile(path.join(import.meta.dirname, '..', 'fixtures', 'sample.dfont')); const dfont = fontkit.create(buffer); const sfntType = dfont.header.map.typeList.types.find((t) => t.name === 'sfnt'); expect(sfntType.refList).toHaveLength(1); }); }); let tmpDir; beforeAll(async () => { registerFontConverter(); tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'converter-font-')); }); afterAll(async () => { await fs.rm(tmpDir, { recursive: true, force: true }); }); async function glyphCountOf(filePath) { const buf = await fs.readFile(filePath); const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); return opentype.parse(ab).glyphs.length; } describe('font converter registration', () => { it('registers ttf, otf, and woff as sources and targets of each other', () => { expect(listTargetFormats('ttf')).toEqual(expect.arrayContaining(['otf', 'woff'])); expect(listTargetFormats('otf')).toEqual(expect.arrayContaining(['ttf', 'woff'])); expect(listTargetFormats('woff')).toEqual(expect.arrayContaining(['ttf', 'otf'])); }); it('does not register a format as its own target', () => { expect(listTargetFormats('ttf')).not.toContain('ttf'); }); it('has no registered converter for cff or cid in either direction', () => { expect(listTargetFormats('cff')).toEqual([]); expect(listTargetFormats('cid')).toEqual([]); expect(resolve('cff', 'ttf')).toBeNull(); expect(resolve('ttf', 'cff')).toBeNull(); expect(resolve('cid', 'ttf')).toBeNull(); }); }); describe('font format conversion', () => { it('converts otf to ttf, preserving glyph count', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.otf'); const outputPath = path.join(tmpDir, 'from-otf.ttf'); await resolve('otf', 'ttf').convert(inputPath, outputPath); const detected = await detectInputMime(outputPath); expect(detected.mime).toBe('font/ttf'); expect(await glyphCountOf(outputPath)).toBe(1568); }); it('converts ttf to woff', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.ttf'); const outputPath = path.join(tmpDir, 'from-ttf.woff'); await resolve('ttf', 'woff').convert(inputPath, outputPath); const detected = await detectInputMime(outputPath); expect(detected.mime).toBe('font/woff'); }); it('round-trips ttf -> woff -> ttf, preserving glyph count', async () => { const woffPath = path.join(tmpDir, 'roundtrip.woff'); const ttfPath = path.join(tmpDir, 'roundtrip.ttf'); await resolve('ttf', 'woff').convert(path.join(import.meta.dirname, '..', 'fixtures', 'sample.ttf'), woffPath); await resolve('woff', 'ttf').convert(woffPath, ttfPath); expect(await glyphCountOf(ttfPath)).toBe(1568); }); it('converts ttf to otf', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.ttf'); const outputPath = path.join(tmpDir, 'from-ttf.otf'); await resolve('ttf', 'otf').convert(inputPath, outputPath); const detected = await detectInputMime(outputPath); expect(detected.mime).toBe('font/otf'); }); });