Use font writers

Writers keep the selected face and change its output container. They do not reduce the character or glyph set. Use Create a font subset first when the output should contain fewer glyphs.

The examples assume Composer's autoloader is loaded, a source font exists at fonts/Inter-Regular.ttf, and output exists. Run the first example with the PHP Brotli extension installed; it creates three new font files and prints nothing. Use Convert a font for a complete first script.

Write SFNT, WOFF, and WOFF2

<?php

use Alto\Font\Compression\BrotliExtensionCompressor;
use Alto\Font\Font;
use Alto\Font\Writer\SfntWriter;
use Alto\Font\Writer\Woff2Writer;
use Alto\Font\Writer\WoffWriter;

$font = Font::fromFile(__DIR__.'/fonts/Inter-Regular.ttf');

new SfntWriter()->write($font, __DIR__.'/output/inter.ttf');
new WoffWriter()->write($font, __DIR__.'/output/inter.woff');
new Woff2Writer(new BrotliExtensionCompressor())->write(
    $font,
    __DIR__.'/output/inter.woff2',
);

The output directory must exist. Writers create a new file exclusively and never replace an existing path. A partial file is removed if writing fails.

Return bytes instead of writing a file

Every writer provides dump():

<?php

use Alto\Font\Compression\BrotliExtensionCompressor;
use Alto\Font\Writer\SfntWriter;
use Alto\Font\Writer\Woff2Writer;
use Alto\Font\Writer\WoffWriter;

$sfntBytes = new SfntWriter()->dump($font);
$woffBytes = new WoffWriter()->dump($font);
$woff2Bytes = new Woff2Writer(
    new BrotliExtensionCompressor(),
)->dump($font);

dump() materializes the complete output as one string. Use it for storage APIs that accept bytes. Use write() for an exclusive filesystem destination.

Font::toSfnt() is the direct convenience API for in-memory SFNT output.

Extract a collection face

<?php

$font = Font::fromFile(__DIR__.'/fonts/Collection.ttc', faceIndex: 1);

new SfntWriter()->write($font, __DIR__.'/output/selected-face.ttf');

A selected TTC or OTC face is always written as a standalone font.

Writer contracts

SfntWriter, WoffWriter, and Woff2Writer belong to Alto\Font\Writer. They expose the same two methods:

Signature Result and side effects
dump(Font $font): string Return all output bytes in memory. No output file is created. Compression adapters may use temporary resources.
write(Font $font, string|Stringable $file): void Write a new local file. Its parent directory must exist; an existing destination is never replaced.

SfntWriter and WoffWriter need no constructor arguments. Woff2Writer requires a BrotliCompressorInterface; see WOFF2 compression for the available adapters.

Understand reconstruction

Source and target Result
Unchanged standalone SFNT to SFNT Preserved byte-for-byte
Collection face to SFNT Standalone face with rebuilt directory and checksums
WOFF or WOFF2 to SFNT Reconstructed standalone SFNT
Any supported face to WOFF Each table uses Zlib level 6 only when compression makes it smaller
Any supported face to WOFF2 Tables transformed when supported, then Brotli-compressed

WOFF and WOFF2 container metadata and private-data blocks are not preserved when a webfont is decoded and written again. WOFF and WOFF2 output remove DSIG. WOFF2 output also updates required head.flags and may reconstruct glyf, loca, and hmtx canonically. Rebuilt output is structurally equivalent, not necessarily byte-for-byte identical.

Handle failures

Exception Meaning
FontWriteException The destination is empty, exists already, or cannot be written
CompressionException Zlib or Brotli compression, support, or temporary streams failed
UnsupportedFontException The requested view or font feature cannot be written
InvalidFontException Required source tables are malformed or inconsistent

These exceptions implement FontExceptionInterface. A selected variable view created with withVariations() cannot be written as a static font.

See WOFF2 compression for Brotli configuration and memory behavior.

Create the destination directory before writing and choose a new filename for a rerun: writers do not create directories or overwrite files. For compression or temporary-storage failures, check the WOFF2 runtime.

A selected variable view is read-only. Use withoutVariations() to write the original variable font with all its axes, or supply a static source font when you need a fixed-weight file. See Variable fonts.