Skip to content

Format Conversion ​

Convert variable fonts between formats while preserving or converting variation data.

Conversion Strategy ​

Compatible Formats (Same Outline) ​

  • Variable TTF ↔ Variable TTF/WOFF/WOFF2: All variation tables preserved
  • Variable OTF ↔ Variable OTF/WOFF/WOFF2: All variation tables preserved

Convertible Formats (Different Outline) ​

  • Variable TTF ↔ Variable OTF: Common tables preserved (fvar, avar, STAT, metrics)
  • Outline-specific tables require conversion (gvar ↔ CFF2 blend)

Unsupported ​

  • Variable fonts to SVG: Creates instance at default coordinates

CLI Usage ​

Preserve Variation ​

bash
# Variable TTF to WOFF2 (preserves all variation)
fontisan convert variable.ttf --to woff2 --output variable.woff2

# Variable OTF to WOFF2 (preserves all variation)
fontisan convert variable.otf --to woff2 --output variable.woff2

Convert Outline Format ​

bash
# Variable TTF to OTF (preserves common variation tables)
fontisan convert variable.ttf --to otf --output variable.otf

# Note: Shows warning about gvar → CFF2 conversion
# Preserves: fvar, avar, STAT, HVAR, VVAR, MVAR
# Does not preserve: gvar (requires conversion to CFF2)

Create Static Font ​

bash
# Remove all variation data
fontisan convert variable.ttf --to ttf --output static.ttf --no-preserve-variation

# Creates static font at default variation coordinates

Ruby API ​

Preserve Variation ​

ruby
font = Fontisan::FontLoader.load('variable.ttf')

# Convert with variation preservation
options = Fontisan::ConversionOptions.new(
  preserve_variation: true
)

Fontisan::FontWriter.write(font, 'variable.woff2', options: options)

Convert Outline Format ​

ruby
# Variable TTF to OTF
converter = Fontisan::Converters::OutlineConverter.new

options = Fontisan::ConversionOptions.new(
  from: :ttf,
  to: :otf,
  preserve_variation: true  # Preserves fvar, avar, STAT
)

result = converter.convert(font, options: options)

Remove Variation ​

ruby
# Create static font at default coordinates
writer = Fontisan::Variation::InstanceWriter.new(font)
static_font = writer.generate_instance({})  # Empty hash = default values

Fontisan::FontWriter.write(static_font, 'static.ttf')

Tables Preserved ​

Same Outline Format ​

All variation tables preserved:

TablePreserved
fvar✓
gvar✓ (TrueType)
CFF2✓ (OpenType)
avar✓
STAT✓
HVAR✓
VVAR✓
MVAR✓

Cross Outline Format ​

Common tables preserved:

TablePreserved
fvar✓
avar✓
STAT✓
HVAR✓
VVAR✓
MVAR✓
gvar✗ (requires CFF2 conversion)
CFF2✗ (requires gvar conversion)

Limitations ​

gvar ↔ CFF2 ​

Converting between TrueType (gvar) and OpenType (CFF2) variation data is not fully implemented:

  • TTF → OTF: gvar variation data is lost
  • OTF → TTF: CFF2 blend operators are lost

The fvar, avar, and STAT tables are preserved, but the actual glyph variation data is not.

Workaround ​

Generate static instances before conversion:

ruby
# Generate instances from variable TTF
writer = Fontisan::Variation::InstanceWriter.new(font)
instance = writer.generate_instance(wght: 700)

# Convert instance to OTF
converter = Fontisan::Converters::OutlineConverter.new
otf_font = converter.convert(instance, options: otf_options)

Web Font Conversion ​

Variable WOFF2 ​

bash
# Smallest file size with variation
fontisan convert variable.ttf --to woff2 --output variable.woff2

Benefits:

  • 30-50% smaller than variable TTF
  • All variation preserved
  • Best for web delivery

Instance Generation for Web ​

bash
# Generate instances as WOFF2
fontisan instance variable.ttf --wght 700 --to woff2 --output bold.woff2

Examples ​

Convert Variable Font Family ​

ruby
require 'fontisan'

# Load variable font
font = Fontisan::FontLoader.load('variable.ttf')

# Convert to WOFF2
options = Fontisan::ConversionOptions.from_preset(:web_optimized)
Fontisan::FontWriter.write(font, 'variable.woff2', options: options)

# Generate key instances
fvar = font.tables['fvar']
writer = Fontisan::Variation::InstanceWriter.new(font)

[400, 500, 600, 700].each do |wght|
  instance = writer.generate_instance(wght: wght)
  Fontisan::FontWriter.write(instance, "weight-#{wght}.woff2", options: options)
end

Fontisan is a [Ribose](https://open.ribose.com/) project