← All Tools ZeroDataUpload Home

Font Converter

Convert web fonts between TTF, OTF, WOFF & WOFF2 with binary SFNT parsing and compression

Launch Font Converter →
Font Converter

Table of Contents

  1. Overview
  2. Key Features
  3. Binary Format Deep Dive
  4. How to Use
  5. Conversion Matrix
  6. Frequently Asked Questions
  7. Privacy & Security

Overview

The Font Converter is a browser-based tool that converts web font files between four major formats: TTF (TrueType), OTF (OpenType), WOFF (Web Open Font Format), and WOFF2 (Web Open Font Format 2). Unlike server-based converters that upload your proprietary typeface data to remote servers, this tool performs all binary parsing, compression, and repackaging entirely within your browser using JavaScript. Your font files never leave your device.

At its core, the converter works directly with the binary SFNT (Spline Font) container format that underpins all four font types. It reads raw bytes from an ArrayBuffer, parses 12-byte SFNT headers, iterates through 16-byte table directory records, and reconstructs the binary output byte-by-byte using DataView operations. For WOFF encoding, each table is independently compressed using zlib deflate via the pako 2.1.0 library (46KB). For WOFF2, all table data is concatenated and compressed as a single block using the browser's native Brotli implementation via CompressionStream('br').

The tool also provides a live font preview using dynamically injected @font-face rules with blob URLs, a glyph grid displaying up to 120 characters from the ASCII and Latin-1 Extended Unicode ranges, and detailed metadata extraction (font family, style, version, glyph count, units per em) powered by opentype.js 1.3.4. After conversion, a side-by-side file size comparison shows the original and converted sizes with a color-coded percentage change indicator -- green for size reduction, red for size increase.

Key Features

4 Font Formats

Full support for TTF (TrueType with quadratic B-spline outlines, magic number 0x00010000), OTF (OpenType with CFF cubic Bezier outlines, magic 0x4F54544F / 'OTTO'), WOFF (zlib per-table compression, magic 0x774F4646 / 'wOFF'), and WOFF2 (Brotli whole-font compression, magic 0x774F4632 / 'wOF2'). Each format is identified by its 4-byte magic number at offset 0.

Binary SFNT Parsing

Reads the 12-byte SFNT header (uint32 version, uint16 numTables, uint16 searchRange, uint16 entrySelector, uint16 rangeShift) followed by 16-byte table records (uint32 tag, uint32 checksum, uint32 offset, uint32 length) for each of the 63 known table tags including cmap, head, hhea, hmtx, maxp, name, OS/2, post, glyf, loca, CFF , GPOS, GSUB, and more. All offsets are validated and aligned to 4-byte boundaries using the pad4() function: (n + 3) & ~3.

zlib Compression (WOFF)

WOFF encoding compresses each SFNT table independently using pako.deflate() from the pako 2.1.0 library. Tables are sorted alphabetically by tag (a WOFF specification requirement). If the compressed output is larger than the original, the uncompressed data is used instead -- ensuring WOFF files are never unnecessarily bloated. Typical compression yields 20-40% reduction compared to raw TTF.

Brotli Compression (WOFF2)

WOFF2 encoding concatenates all table data into a single block and compresses it using the browser's native CompressionStream('br') Brotli implementation. The table directory uses UIntBase128 variable-length integer encoding (continuation bit on bit 7, 1-5 bytes per value, MSB first) for compact storage. Typical compression yields 40-60% reduction vs raw TTF. Requires Chrome 120+, Edge 120+, or other browsers with Brotli stream support.

Font Preview

Generates a live font preview by creating a blob URL from the font data, injecting a dynamic @font-face CSS rule with a unique font-family name (using an incrementing counter to prevent browser caching), and rendering two sample blocks: a pangram sentence at 2rem for quick visual assessment, and a full character set at 1rem for detailed inspection. MIME types are correctly assigned: font/ttf, font/otf, font/woff, font/woff2.

Glyph Grid

Displays up to 120 individual glyphs in a responsive auto-fill CSS grid. Characters are drawn from two Unicode ranges: printable ASCII U+0021 through U+007E (94 characters covering ! through ~) and Latin-1 Extended U+00C0 through U+00FF (64 characters covering accented letters like À, É, Ñ, Ü). Each glyph cell shows its Unicode codepoint on hover in U+XXXX format. Grid cells are 48px on desktop and 40px on mobile.

Metadata Extraction

Extracts and displays font metadata using opentype.js 1.3.4 (167KB): Font Family name, Subfamily/Style (Regular, Bold, Italic, etc.), Full Name, Version string, total glyph count (read from the maxp table at offset 4 as a uint16), and units per em (the font's internal coordinate grid size, typically 1000 for OTF or 2048 for TTF). Uses the lowMemory: true option for large fonts to avoid excessive memory consumption.

Size Comparison

After conversion, displays a clear before-and-after comparison showing the original file size, the converted file size, and the percentage change. Sizes are automatically formatted with appropriate units (B, KB, or MB with decimal places). Reductions are shown in green (e.g., "37% smaller") and increases in red (e.g., "12% larger"), giving you immediate feedback on the compression efficiency.

Binary Format Deep Dive

Understanding how font files are structured at the binary level is essential to understanding what the Font Converter actually does during conversion. All four supported formats -- TTF, OTF, WOFF, and WOFF2 -- are built on top of the SFNT (Spline Font) container, which organizes font data into a series of tagged tables. Here is a byte-by-byte breakdown of each format's binary structure.

SFNT Container (TTF & OTF)

Both TTF and OTF files use the raw SFNT container format. The file begins with a 12-byte header immediately followed by an array of 16-byte table records.

SFNT Header (12 bytes):

Table Record (16 bytes each, repeated numTables times):

After the table directory, the actual table data blocks are stored sequentially. Each block is padded to a 4-byte boundary using the pad4() function: (n + 3) & ~3. This bitwise operation rounds any value up to the nearest multiple of 4 by adding 3, then clearing the bottom 2 bits.

WOFF Header (44 bytes)

The WOFF format wraps an SFNT font with per-table compression. The file begins with a 44-byte header:

WOFF Table Directory Entry (20 bytes each):

During WOFF encoding (sfntToWoff()), the converter sorts all tables alphabetically by tag (as required by the WOFF specification), then compresses each table independently with pako.deflate(). If the compressed output is not smaller than the original, the uncompressed data is stored instead and compLength is set equal to origLength. This ensures the WOFF file is never larger than necessary.

WOFF2 Header (48 bytes)

WOFF2 uses a more compact binary format with variable-length integer encoding for the table directory:

WOFF2 Table Directory (variable length per entry):

Unlike WOFF's fixed 20-byte entries, WOFF2 table directory entries use a compact variable-length encoding. Each entry contains:

UIntBase128 Encoding Algorithm

WOFF2 uses UIntBase128 to encode integers compactly. This variable-length encoding represents values using 1 to 5 bytes, with a continuation bit on the most significant bit (bit 7) of each byte. The algorithm works as follows:

This encoding is more space-efficient than fixed 4-byte integers for the small values typically found in font table directories. A table length of 1000 bytes encodes in just 2 bytes ([0x87, 0x68]) instead of 4.

Key Binary Helper Functions

How to Use

  1. Open the Font Converter -- Navigate to the Font Converter page and drag-and-drop your font file onto the upload area, or click to browse. Supported input formats are .ttf, .otf, .woff, and .woff2.
  2. Automatic Format Detection -- The converter reads the first 4 bytes of the file to identify the magic number: 0x00010000 for TTF, 0x4F54544F for OTF, 0x774F4646 for WOFF, or 0x774F4632 for WOFF2. The detected format is displayed along with font metadata, a live preview, and the glyph grid.
  3. Review the Font Preview -- Examine the pangram text sample (rendered at 2rem) and the full character set (at 1rem) to verify your font loaded correctly. The preview uses a dynamically injected @font-face rule with a blob URL.
  4. Inspect the Glyph Grid -- Browse the 120-character grid showing printable ASCII (U+0021 through U+007E) and Latin-1 Extended (U+00C0 through U+00FF) glyphs. Hover over any glyph to see its Unicode codepoint in U+XXXX format.
  5. Select Your Output Format -- Choose from the dropdown menu. Only valid conversions are shown -- for example, TTF to OTF is not available because generating CFF outlines from TrueType data is computationally impractical in a browser. If your browser does not support CompressionStream('br'), the WOFF2 output option is hidden.
  6. Click Convert -- The converter parses the binary SFNT structure, extracts all table data, applies the appropriate compression or decompression (zlib for WOFF, Brotli for WOFF2), rebuilds the table directory, recalculates checksums, and assembles the output file. The size comparison appears: original size vs. converted size with percentage change.
  7. Download Your Font -- Click the download button to save your converted font file with the correct file extension (.ttf, .otf, .woff, or .woff2) and the proper MIME type. The file is generated as a blob and downloaded via a temporary anchor element.

Conversion Matrix

Not all conversions between font formats are possible in a browser environment. The table below shows which conversions the Font Converter supports and why certain paths are unavailable:

* WOFF2 output is conditionally available. If your browser does not provide CompressionStream('br') for Brotli compression, the WOFF2 option is automatically hidden from the output format dropdown. As of early 2026, Chrome 120+, Edge 120+, and Opera 106+ support this API. Safari and Firefox do not yet support Brotli via CompressionStream.

A note on table transforms: The WOFF2 specification defines optional transforms for the glyf, loca, and hmtx tables that can improve compression. The Font Converter detects these transforms during WOFF2 decoding but does not fully reverse complex glyf transforms -- if a WOFF2 file uses glyf table transformation, the converter will throw a 'glyf_transform_not_implemented' error. Most WOFF2 files generated by standard tools do use this transform, so decoding WOFF2 files may fail for some fonts. WOFF2 encoding from TTF/OTF does not apply transforms.

Frequently Asked Questions

What font formats are supported?
The Font Converter supports four formats, all based on the SFNT (Spline Font) binary container: TTF (TrueType Font, magic number 0x00010000), OTF (OpenType Font with CFF outlines, magic number 0x4F54544F), WOFF (Web Open Font Format with per-table zlib compression, magic number 0x774F4646), and WOFF2 (Web Open Font Format 2 with whole-font Brotli compression, magic number 0x774F4632). Each format is automatically detected by reading the first 4 bytes of the uploaded file.
What is the difference between TTF and OTF?
Both TTF and OTF are SFNT container formats, but they use fundamentally different outline representations. TTF (TrueType) stores glyph outlines as quadratic B-spline curves defined by on-curve and off-curve control points -- this format was developed by Apple and Microsoft. OTF (OpenType with CFF) stores outlines as cubic Bezier curves in the Compact Font Format (CFF) -- this approach comes from Adobe's PostScript tradition. Cubic Beziers can represent curves more precisely with fewer control points, which is why type designers often prefer OTF. However, TrueType's quadratic curves are natively supported by all screen rasterizers and are slightly faster to render. The glyf table holds TrueType outlines; the CFF  table holds OpenType CFF outlines.
Why can I not convert TTF to OTF?
Converting TTF to OTF requires generating CFF (Compact Font Format) outlines from TrueType quadratic B-spline data. This is not a simple repackaging operation -- it involves converting quadratic curves to cubic Beziers, encoding the results in CFF charstring format (a PostScript-derived bytecode), building CFF index structures (Name INDEX, Top DICT, String INDEX, Global Subr INDEX, CharStrings INDEX), and optionally performing subroutinization (shared outline fragments) for compression. This is a complex computational process that requires a full font engineering pipeline and is impractical to implement reliably in browser-based JavaScript. The reverse direction (OTF to TTF) is possible because opentype.js 1.3.4 includes CFF parsing and can approximate cubic curves with quadratic segments.
Is the conversion lossless?
For WOFF and WOFF2 conversions to and from TTF/OTF, the conversion is completely lossless. WOFF and WOFF2 are purely compression wrappers around the same SFNT table data -- no font information is altered, removed, or approximated. The raw table bytes are identical before and after compression/decompression. The one exception is OTF to TTF conversion, which involves converting cubic Bezier curves (CFF outlines) to quadratic B-splines (TrueType outlines). This curve approximation process may introduce very minor differences in glyph shapes at the sub-pixel level, though these are typically imperceptible in normal rendering.
What is UIntBase128 encoding?
UIntBase128 is a variable-length unsigned integer encoding used in the WOFF2 table directory to store values compactly. Each byte carries 7 bits of data (bits 0-6) and 1 continuation bit (bit 7). If bit 7 is set, more bytes follow; if clear, this is the final byte. Bytes are ordered most-significant first. For example, the value 127 fits in a single byte: 0x7F. The value 128 requires two bytes: [0x81, 0x00] (the first byte carries the high bit with continuation flag, the second carries the low 7 bits). The maximum encodable value uses 5 bytes and can represent integers up to 2^35 - 1, though the WOFF2 spec limits values to 32-bit unsigned integers. The decoder validates that no leading byte is 0x80 (which would be a padded zero) and checks for overflow at the 0xFE000000 boundary.
Why does WOFF2 not work in my browser?
WOFF2 encoding requires native Brotli compression support via the CompressionStream('br') API. This is a relatively new web standard that is not yet universally available. As of early 2026, it is supported in Chrome 120+, Edge 120+, and Opera 106+, but is not yet available in Safari or Firefox. The Font Converter detects this at runtime -- if your browser lacks Brotli stream support, the WOFF2 output option is automatically hidden from the format dropdown. WOFF2 decoding (reading WOFF2 files) uses DecompressionStream('br'), which has similar browser requirements. If you need WOFF2 output, use a Chromium-based browser.
How much smaller are web font formats?
Compression savings depend on the font's complexity (number of glyphs, outline detail, number of tables), but typical reductions are consistent across most fonts. TTF to WOFF (zlib per-table compression) typically yields a 20-40% file size reduction. TTF to WOFF2 (Brotli whole-font compression) typically yields a 40-60% reduction. WOFF to WOFF2 typically yields an additional 30% reduction on top of WOFF's compression. For example, a 200KB TTF file might compress to 140KB as WOFF (30% reduction) and 90KB as WOFF2 (55% reduction). OTF files with CFF outlines tend to compress slightly better than TTF because CFF data has more redundancy that zlib and Brotli can exploit.
What is the glyph grid showing?
The glyph grid displays up to 120 individual characters rendered in the uploaded font. The characters come from two Unicode ranges: 94 printable ASCII characters from U+0021 (!) through U+007E (~), covering all standard keyboard characters including letters, digits, and punctuation; plus 64 Latin-1 Extended characters from U+00C0 (À) through U+00FF (ÿ), covering accented characters common in European languages like À, É, Ñ, Ö, ß, and þ. Hovering over any glyph cell displays its Unicode codepoint in the standard U+XXXX hexadecimal notation. The grid uses CSS auto-fill with 48px cells on desktop and 40px cells on mobile for a responsive layout.
Are variable fonts supported?
Variable fonts (fonts containing a fvar table with design variation axes like weight, width, and slant) are processed by the Font Converter as binary data. When converting between WOFF/WOFF2 and TTF/OTF, the variable font tables (fvar, gvar, STAT, avar, MVAR, HVAR, VVAR, cvar) are preserved byte-for-byte during compression and decompression -- they are treated the same as any other SFNT table. However, the converter does not specifically optimize for variable fonts, and the metadata display does not show variation axes. The font preview renders the default instance of the variable font.
Is my font data safe?
Yes, completely. All binary parsing, compression, decompression, and format conversion happens entirely within your browser's JavaScript runtime. The two libraries used -- pako 2.1.0 for zlib compression and opentype.js 1.3.4 for font parsing -- both operate on in-memory ArrayBuffer data with no network calls. No font data, binary content, file names, or metadata are ever transmitted to any server. The blob URLs created for font preview are local to your browser session and are revoked after use. Your font files, whether they contain proprietary typeface designs, commercial licenses, or personal projects, remain entirely on your device throughout the entire conversion process.

Privacy & Security

Your Font Files Never Leave Your Device

Font files can contain proprietary typeface data, licensing information, and embedded metadata including designer names, foundry details, and copyright notices. The Font Converter processes everything locally using pako for zlib compression and opentype.js for font parsing. No font data, binary content, or metadata is ever transmitted to any server. The binary SFNT parsing reads your font's raw bytes directly from an in-memory ArrayBuffer -- there are no XMLHttpRequests, no fetch calls, no WebSocket connections, and no server-side processing of any kind. Blob URLs created for the font preview are scoped to your browser tab and automatically revoked. Your typography assets remain completely on your device.

Ready to convert your fonts? It's free, private, and runs entirely in your browser.

Launch Font Converter →

Related

Milan Salvi

Milan Salvi

Founder, Leena Software Solutions

Milan is the founder of ZeroDataUpload and Leena Software Solutions, building privacy-first browser tools that process everything client-side. View all articles ยท About the author.

Last Updated: March 26, 2026