Writing files

Write a file when a loaded face, or the result of a subset, has to leave PHP: a TTF for an application bundle, a WOFF or WOFF2 for a web server, or the bytes for a storage API. A writer changes the container and keeps every character and glyph; Subsetting is the step that removes them, before writing.

Write TTF, WOFF, and WOFF2

Place a font at fonts/Inter-Regular.ttf beside vendor and create an output directory. The WOFF2 line needs the PHP Brotli extension; remove it if the extension is not installed.

<?php

require __DIR__.'/vendor/autoload.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 script creates three files and prints nothing. Each writer creates its file exclusively: the parent directory must exist, the destination must be a new path, and a partial file is removed if writing fails. Choose a new filename for each run, or delete the previous output in your application. Woff2Writer always takes a compressor; Compression covers the two adapters and the quality profiles.

Return bytes instead of a file

Every writer also has dump(), which returns the complete output as one string, for a storage API or an HTTP response:

<?php

$woffBytes = new WoffWriter()->dump($font);
$sfntBytes = $font->toSfnt();

Font::toSfnt() is the shortcut for TTF bytes. Both materialize the whole output in memory; the process-backed Brotli adapter can stream WOFF2 to a file instead, see Adapters.

Extract one face from a collection

A face selected from a TTC or OTC is written as a standalone font:

<?php

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

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

What the output keeps

An unchanged standalone TTF is written byte for byte. Everything else gets a rebuilt table directory and checksums:

Source and target Result
Collection face to TTF Standalone face, rebuilt directory and checksums
WOFF or WOFF2 to TTF Reconstructed standalone font
Any face to WOFF Each table Zlib-compressed at level 6 when that makes it smaller
Any face to WOFF2 Tables transformed where the format allows, then one Brotli stream

Output is structurally equivalent to the source, not necessarily byte identical: WOFF and WOFF2 output drop DSIG, WOFF2 output updates head.flags and may rebuild glyf, loca, and hmtx in canonical form, and the metadata and private-data blocks of a WOFF or WOFF2 source are not carried over.

Write a variable font

A variable font is written with all its axes. A view selected with withVariations() is a reading position rather than a file: call withoutVariations() to write its source, or start from a static font when you need one weight. See Variable fonts.

When writing fails

  • FontWriteException: the destination is empty, exists already, or its directory is missing or not writable.
  • CompressionException: Zlib or Brotli failed, the Brotli adapter is unusable in this runtime, or temporary storage is not writable; see Adapters.
  • InvalidFontException: a required source table is malformed or inconsistent.

All of them implement FontExceptionInterface; Errors lists every exception.