Fontisan is a Ruby gem providing font analysis tools and utilities.
It is designed as a pure Ruby implementation with full object-oriented architecture, supporting extraction of information from OpenType and TrueType fonts (OTF, TTF, OTC, TTC, dfont).
The gem provides both a Ruby library API and a command-line interface, with structured output formats (YAML, JSON, text) via lutaml-model.
Fontisan is designed to replace the following tools:
-
otfinfofrom LCDF Typetools. Fontisan supports all features provided byotfinfo, including extraction of font metadata, OpenType tables, glyph names, Unicode mappings, variable font axes, optical size information, supported scripts, OpenType features, and raw table dumps. -
extract_ttcfrom ExtractTTC. Fontisan fully supersedes extract_ttc with Docker-like commands (ls,info,unpack) that work on both collections and individual fonts. Fontisan provides allextract_ttcfunctionality plus comprehensive font analysis, subsetting, validation, format conversion, and collection creation. See extract_ttc Migration Guide for detailed command mappings and usage examples.
Add this line to your application’s Gemfile:
gem "fontisan"And then execute:
bundle installOr install it yourself as:
gem install fontisan- Color fonts and collections
-
-
Color font support: COLR/CPAL layered glyphs, sbix bitmap glyphs, and SVG table (see Color Fonts Guide)
-
Font collection validation with per-font reporting (see Collection Validation Guide)
-
- Font analysis and information
-
-
Bidirectional font hint conversion (see Font Hinting Guide)
-
Extract comprehensive font metadata (name, version, designer, license, etc.)
-
Brief mode for fast font indexing (5x faster, metadata-only loading)
-
List OpenType tables with checksums and offsets
-
Extract glyph names from post table
-
Display Unicode codepoint to glyph index mappings
-
Analyze variable font axes and named instances
-
Display optical size information
-
List supported scripts from GSUB/GPOS tables
-
List OpenType features (ligatures, kerning, etc.) by script
-
Dump raw binary table data for analysis
-
- Font operations
-
-
Generate static font instances from variable fonts
-
Font subsetting with multiple profiles (PDF, web, minimal)
-
Font validation framework with multiple profiles (indexability, usability, production, web, spec_compliance) (see Validation Guide)
-
Collection management (pack/unpack TTC/OTC/dfont files with table deduplication)
-
- Font format support
-
-
TTF, OTF, TTC, OTC font formats (production ready)
-
WOFF/WOFF2 format support with reading, writing, and conversion (see WOFF/WOFF2 Guide)
-
Apple legacy font support: 'true' signature TrueType fonts and dfont format (see Apple Legacy Fonts Guide)
-
SVG font generation (complete)
-
- Font engineering
-
-
Universal outline model for format-agnostic glyph representation
-
CFF CharString encoding/decoding (complete)
-
CFF INDEX structure building (complete)
-
CFF DICT structure building (complete)
-
TrueType curve converter for bi-directional quadratic/cubic conversion (complete)
-
Compound glyph decomposition with transformation support (complete)
-
CFF subroutine optimization for space-efficient OTF generation (preview mode)
-
Bidirectional hint conversion (TrueType ↔ PostScript) with validation (complete)
-
CFF2 variable font support for PostScript hint conversion (complete)
-
- Export and interfaces
-
-
TTX/YAML/JSON export (complete)
-
Command-line interface with 18 commands
-
Ruby library API for programmatic access
-
Structured output in YAML, JSON, and text formats
-
Extract comprehensive metadata from font files. This includes font names, version information, designer credits, vendor details, licensing information, and font metrics.
$ fontisan info FONT_FILE [--format FORMAT] [--brief]Where,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
FORMAT-
Output format:
text(default),json, oryaml --brief-
Show only basic font information
For font indexing systems that need to scan thousands of fonts quickly, use the
--brief flag to get essential metadata only. This mode uses metadata loading
and is 5x faster than full mode.
Brief mode provides significant performance improvements for font indexing:
-
5x faster than full mode by using the
metadataload mode -
Loads only 6 tables instead of 15-20 (name, head, hhea, maxp, OS/2, post)
-
Lower memory usage through reduced table loading
-
Optimized for batch processing of many fonts
Brief mode populates only the following 13 essential attributes:
- Font identification
-
-
font_format- Font format (truetype, cff) -
is_variable- Whether font is variable
-
- Essential names
-
-
family_name- Font family name -
subfamily_name- Font subfamily/style -
full_name- Full font name -
postscript_name- PostScript name
-
- Version info
-
-
version- Version string
-
- Metrics
-
-
font_revision- Font revision number -
units_per_em- Units per em
-
- Vendor
-
-
vendor_id- Vendor/foundry ID
-
Syntax:
$ fontisan info FONT_FILE --brief [--format FORMAT]# Individual font
$ fontisan info font.ttf --brief
Font type: TrueType (Not Variable)
Family: Noto Sans
...
# Collection
$ fontisan info fonts.ttc --brief
Collection: fonts.ttc
Fonts: 35
Font 0 (offset: 152):
Font type: OpenType (CFF) (Not Variable)
Family: Noto Serif CJK JP ExtraLight
...$ fontisan info spec/fixtures/fonts/MonaSans/variable/MonaSans[wdth,wght].ttf --brief
Font type: TrueType (Variable)
Family: Mona Sans ExtraLight
Subfamily: Regular
Full name: Mona Sans ExtraLight
PostScript name: MonaSans-ExtraLight
Version: Version 2.001
Vendor ID: GTHB
Font revision: 2.00101
Units per em: 1000$ fontisan info font.ttf --brief --format json{
"font_format": "truetype",
"is_variable": false,
"family_name": "Open Sans",
"subfamily_name": "Regular",
"full_name": "Open Sans Regular",
"postscript_name": "OpenSans-Regular",
"version": "Version 3.000",
"font_revision": 3.0,
"vendor_id": "2001",
"units_per_em": 2048
}require 'fontisan'
info = Fontisan.info("font.ttf", brief: true)
# Access populated fields
puts info.family_name # "Open Sans"
puts info.postscript_name # "OpenSans-Regular"
puts info.is_variable # false
# Non-essential fields are nil
puts info.copyright # nil (not populated)
puts info.designer # nil (not populated)
# Serialize to YAML/JSON
puts info.to_yaml
puts info.to_jsonrequire 'fontisan'
# Specify font index for TTC/OTC files
info = Fontisan.info("/path/to/fonts.ttc", brief: true, font_index: 0)
puts info.family_nameIn full mode, these additional attributes are populated (remain nil in brief
mode):
-
postscript_cid_name,preferred_family,preferred_subfamily,mac_font_menu_name -
unique_id,description,designer,designer_url -
manufacturer,vendor_url,trademark,copyright -
license_description,license_url,sample_text,permissions
Syntax:
$ fontisan info FONT_FILE [--format FORMAT]$ fontisan info spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
Font type: TrueType
Family: Libertinus Serif
Subfamily: Regular
Full name: Libertinus Serif Regular
PostScript name: LibertinusSerif-Regular
Version: Version 7.051;RELEASE
Unique ID: 5.000;QUE ;LibertinusSerif-Regular
Designer: Philipp H. Poll, Khaled Hosny
Manufacturer: Caleb Maclennan
Vendor URL: https://github.com/alerque/libertinus
Vendor ID: QUE
License Description: This Font Software is licensed under the SIL Open Font
License, Version 1.1. This license is available with a
FAQ at: https://openfontlicense.org
License URL: https://openfontlicense.org
Font revision: 7.05099
Permissions: Installable
Units per em: 1000$ fontisan info spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf --format yamlfont_format: truetype
is_variable: false
family_name: Libertinus Serif
subfamily_name: Regular
full_name: Libertinus Serif Regular
postscript_name: LibertinusSerif-Regular
version: Version 7.051;RELEASE
unique_id: 5.000;QUE ;LibertinusSerif-Regular
designer: Philipp H. Poll, Khaled Hosny
manufacturer: Caleb Maclennan
vendor_url: https://github.com/alerque/libertinus
vendor_id: QUE
license_description: 'This Font Software is licensed under the SIL Open Font License,
Version 1.1. This license is available with a FAQ at: https://openfontlicense.org'
license_url: https://openfontlicense.org
font_revision: 7.050994873046875
permissions: Installable
units_per_em: 1000To understanding font structure and verifying table integrity, Fontisan provides detailed table listings.
Display the font’s table directory, showing all OpenType tables with their sizes, offsets, and checksums.
Syntax:
$ fontisan tables FONT_FILE [--format FORMAT]Where,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
FORMAT-
Output format:
text(default),json, oryaml
$ fontisan tables spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttfSFNT Version: TrueType (0x00010000)
Number of tables: 16
Tables:
GDEF 834 bytes (offset: 542156, checksum: 0x429C5C0C)
GPOS 17870 bytes (offset: 542992, checksum: 0x29CE4200)
OS/2 96 bytes (offset: 392, checksum: 0x4830F1C3)
cmap 3620 bytes (offset: 11412, checksum: 0x03AD3899)
cvt 248 bytes (offset: 18868, checksum: 0x3098127E)
fpgm 3596 bytes (offset: 15032, checksum: 0x622F0781)
gasp 8 bytes (offset: 542148, checksum: 0x00000010)
glyf 484900 bytes (offset: 30044, checksum: 0x0FF34594)
head 54 bytes (offset: 268, checksum: 0x18F5BDD0)
hhea 36 bytes (offset: 324, checksum: 0x191E2264)
hmtx 10924 bytes (offset: 488, checksum: 0x1F9D892B)
loca 10928 bytes (offset: 19116, checksum: 0x230B1A58)
maxp 32 bytes (offset: 360, checksum: 0x0EF919E7)
name 894 bytes (offset: 514944, checksum: 0x4E9173E6)
post 26308 bytes (offset: 515840, checksum: 0xE3D70231)
prep 239 bytes (offset: 18628, checksum: 0x8B4AB356)$ fontisan tables spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf --format yaml---
sfnt_version: TrueType (0x00010000)
num_tables: 16
tables:
- tag: GDEF
length: 834
offset: 542156
checksum: 1117543436
- tag: GPOS
length: 17870
offset: 542992
checksum: 701383168
...Show all glyph names defined in the font’s post table. Each glyph is listed with its index and name, useful for understanding the font’s character coverage.
Syntax:
$ fontisan glyphs FONT_FILE [--format FORMAT]Where,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
FORMAT-
Output format:
text(default),json, oryaml
$ fontisan glyphs spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttfGlyph count: 2731
Source: post-2.0
Glyph names:
0 .notdef
1 space
2 exclam
3 quotedbl
4 numbersign
5 dollar
6 percent
7 ampersand
8 quotesingle
9 parenleft
10 parenright
11 asterisk
12 plus
13 comma
14 hyphen
15 period
16 slash
17 zero
18 one
19 two
20 three
...Display Unicode codepoint to glyph index mappings from the cmap table. Shows which glyphs are assigned to which Unicode characters.
Syntax:
$ fontisan unicode FONT_FILE [--format FORMAT]Where:
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
FORMAT-
Output format:
text(default),json, oryaml
$ fontisan unicode spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttfUnicode mappings: 2382
U+0020 glyph 1 space
U+0021 glyph 2 exclam
U+0022 glyph 3 quotedbl
U+0023 glyph 4 numbersign
U+0024 glyph 5 dollar
U+0025 glyph 6 percent
U+0026 glyph 7 ampersand
U+0027 glyph 8 quotesingle
U+0028 glyph 9 parenleft
U+0029 glyph 10 parenright
U+002A glyph 11 asterisk
U+002B glyph 12 plus
U+002C glyph 13 comma
U+002D glyph 14 hyphen
U+002E glyph 15 period
U+002F glyph 16 slash
U+0030 glyph 17 zero
U+0031 glyph 18 one
...Display variation axes and named instances for variable fonts. Shows the design space and predefined styles available in the font.
Syntax:
$ fontisan variable FONT_FILE [--format FORMAT]Where,
FONT_FILE-
Path to the variable font file
FORMAT-
Output format:
text(default),json, oryaml
$ fontisan variable spec/fixtures/fonts/MonaSans/variable/MonaSans[wdth,wght].ttfAxis 0: wdth
Axis 0 name: Width
Axis 0 range: 75 125
Axis 0 default: 100
Axis 1: wght
Axis 1 name: Weight
Axis 1 range: 200 900
Axis 1 default: 400
Instance 0 name: Mona Sans Narrow Thin
Instance 0 position: 75 200
Instance 1 name: Mona Sans Narrow ExtraLight
Instance 1 position: 75 250
Instance 2 name: Mona Sans Narrow Light
Instance 2 position: 75 300
...Generate static font instances from variable fonts at specific variation coordinates and output in any supported format (TTF, OTF, WOFF).
Syntax:
$ fontisan instance VARIABLE_FONT [OPTIONS]Where,
VARIABLE_FONT-
Path to the variable font file
OPTIONS-
Instance generation options
Options:
--wght VALUE-
Weight axis value
--wdth VALUE-
Width axis value
--slnt VALUE-
Slant axis value
--ital VALUE-
Italic axis value
--opsz VALUE-
Optical size axis value
--to FORMAT-
Output format:
ttf(default),otf,woff, orwoff2 --output FILE-
Output file path
--optimize-
Enable CFF optimization for OTF output
--named-instance INDEX-
Use named instance by index
--list-instances-
List available named instances
--validate-
Validate font before generation
--dry-run-
Preview instance without generating
--progress-
Show progress during generation
$ fontisan instance variable.ttf --wght 700 --output bold.ttf
Generating instance... done
Writing output... done
Static font instance written to: bold.ttf$ fontisan instance variable.ttf --wght 300 --to otf --output light.otf
Generating instance... done
Writing output... done
Static font instance written to: light.otf$ fontisan instance variable.ttf --wght 600 --to woff --output semibold.woff
Generating instance... done
Writing output... done
Static font instance written to: semibold.woff$ fontisan instance variable.ttf --wght 600 --wdth 75 --output condensed.ttf
Generating instance... done
Writing output... done
Static font instance written to: condensed.ttf$ fontisan instance variable.ttf --list-instances
Available named instances:
[0] Instance 4
Coordinates:
wdth: 75.0
wght: 200.0
[1] Instance 5
Coordinates:
wdth: 75.0
wght: 250.0
[2] Instance 6
Coordinates:
wdth: 75.0
wght: 300.0$ fontisan instance variable.ttf --named-instance 0 --output thin.ttf$ fontisan instance variable.ttf --wght 700 --dry-run
Dry-run mode: Preview of instance generation
Coordinates:
wght: 700.0
Output file: variable-instance.ttf
Format: same as input
Use without --dry-run to actually generate the instance.Syntax:
$ fontisan optical-size FONT_FILE [--format FORMAT]Where,
FONT_FILE-
Path to the font file with optical sizing
FORMAT-
Output format:
text(default),json, oryaml
$ fontisan optical-size spec/fixtures/fonts/libertinus/ttf/LibertinusSerifDisplay-Regular.ttfSize range: [18, 72) pt (source: OS/2_usLowerOpticalPointSize)Show all scripts (writing systems) supported by the font, extracted from GSUB and GPOS tables. Useful for understanding language coverage.
Syntax:
$ fontisan scripts FONT_FILE [--format FORMAT]Where,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
FORMAT-
Output format:
text(default),json, oryaml
$ fontisan scripts spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttfScript count: 5
DFLT Default
cyrl Cyrillic
grek Greek
hebr Hebrew
latn LatinShow OpenType layout features (typography features like ligatures, kerning, small capitals) available for specific scripts or all scripts.
Syntax:
$ fontisan features FONT_FILE [--script SCRIPT] [--format FORMAT]Where,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
SCRIPT-
Optional 4-character script tag (e.g.,
latn,cyrl,arab). If not specified, shows features for all scripts FORMAT-
Output format:
text(default),json, oryaml
$ fontisan features spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf --script latnScript: latn
Feature count: 4
cpsp Capital Spacing
kern Kerning
mark Mark Positioning
mkmk Mark to Mark Positioning$ fontisan features spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttfScript: DFLT
Feature count: 4
cpsp Capital Spacing
kern Kerning
mark Mark Positioning
mkmk Mark to Mark Positioning
Script: cyrl
Feature count: 4
cpsp Capital Spacing
kern Kerning
mark Mark Positioning
mkmk Mark to Mark Positioning
Script: grek
Feature count: 4
cpsp Capital Spacing
kern Kerning
mark Mark Positioning
mkmk Mark to Mark Positioning
Script: hebr
Feature count: 2
mark Mark Positioning
mkmk Mark to Mark Positioning
Script: latn
Feature count: 4
cpsp Capital Spacing
kern Kerning
mark Mark Positioning
mkmk Mark to Mark PositioningExtract raw binary data from a specific OpenType table. Useful for detailed analysis or debugging font issues.
TODO: should support output to file directly with --output FILE.
Syntax:
$ fontisan dump-table FONT_FILE TABLE_TAGWhere,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
TABLE_TAG-
Four-character table tag (e.g.,
name,head,GSUB,GPOS)
$ fontisan dump-table spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf name > name_table.bin
$ fontisan dump-table spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf GPOS > gpos_table.bin
$ fontisan dump-table spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf head > head_table.binThe output is binary data written directly to stdout, which can be redirected to a file for further analysis.
Export font structure to TTX (FontTools XML), YAML, or JSON formats for analysis, interchange, or version control. Supports selective table export and configurable binary data encoding.
Syntax:
$ fontisan export FONT_FILE [--output FILE] [--format FORMAT] [--tables TABLES] [--binary-format FORMAT]Where,
FONT_FILE-
Path to the font file (OTF, TTF, TTC, OTC, dfont)
--output FILE-
Output file path (default: stdout)
--format FORMAT-
Export format:
yaml(default),json, orttx --tables TABLES-
Specific tables to export (space-separated list)
--binary-format FORMAT-
Binary encoding:
hex(default) orbase64
$ fontisan export spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf --output font.yaml
# Output: font.yaml with complete font structure in YAML$ fontisan export spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf \
--format ttx --tables head hhea maxp name --output font.ttxExports only the specified tables in FontTools TTX XML format for compatibility with fonttools.
$ fontisan export font.ttf --format json --binary-format base64 --output font.jsonUses base64 encoding for binary data instead of hexadecimal, useful for JSON-based workflows.
Fontisan provides validation functionality to ensure font quality, structural integrity, and compliance with various standards.
The validation framework allows developers to create custom validators using a declarative DSL. Validators can perform checks on font tables, fields, structures, usability, instructions, and glyphs.
Fontisan includes validation profiles for different use cases:
indexability-
Fast font discovery and indexing (~5x faster, 8 checks, metadata-only)
usability-
Font installation compatibility (26 checks, macOS Font Book focused)
production-
Comprehensive production quality (36 checks, OpenType spec compliance - default)
web-
Web font embedding readiness (18 checks for web deployment)
spec_compliance-
Full OpenType specification compliance (detailed analysis mode)
default-
Alias for the production profile
$ fontisan validate font.ttf$ fontisan validate font.ttf -t web$ fontisan validate --list
Available validation profiles:
indexability - Fast validation for font discovery and indexing
usability - Basic usability for installation
production - Comprehensive quality checks
web - Web embedding and optimization
spec_compliance - Full OpenType spec compliance
default - Default validation profile (alias for production)$ fontisan validate font.ttf -t production -r -o report.txt$ fontisan validate font.ttf -S -R
0 errors, 2 warnings, 0 info
$ echo $?
4 # Exit code 4 indicates warnings found$ fontisan validate font.ttf -T
CHECK_ID | STATUS | SEVERITY | TABLE
------------------------------------------------------------
required_tables | PASS | error | N/A
name_version | PASS | error | name
family_name | PASS | error | name
...- -t, --test-list PROFILE
-
Select validation profile (indexability, usability, production, web, spec_compliance, default)
- -l, --list
-
List available validation profiles
- -o, --output FILE
-
Write report to file instead of stdout
- -r, --full-report
-
Generate full detailed report
- -R, --return-value-results
-
Use return value to indicate results (0=none, 1=error, 2=fatal, 3=major, 4=minor, 5=info)
- -S, --summary-report
-
Generate brief summary report
- -T, --table-report
-
Generate tabular format report
- -v, --verbose
-
Enable verbose output
- -w, --suppress-warnings
-
Suppress warning output
- -e, --exclude CHECKS
-
Exclude specific checks (comma-separated list)
The validate command automatically detects and validates font collections (TTC, OTC, dfont).
When validating a collection, fontisan validates all fonts in the collection and displays
per-font results with an overall summary.
All validation profiles work with font collections. The selected profile determines:
-
Which tables are loaded - metadata vs full mode
-
Which checks are performed - number and type of validations
-
Performance characteristics - indexability is ~5x faster than production
For large collections (CJK fonts with 30+ fonts), use the indexability profile for fast
validation when scanning for font discovery, or production for comprehensive quality
checks.
# Quick validation for indexing (metadata mode, 8 checks, ~5x faster)
$ fontisan validate /path/to/font.ttc -t indexability
# Comprehensive validation (full mode, 37 checks)
$ fontisan validate /path/to/font.ttc -t production
# Web font readiness validation
$ fontisan validate /path/to/font.ttc -t webThe collection validation output includes:
-
Collection header - Path, type, and number of fonts
-
Summary - Total errors, warnings, and info across all fonts
-
Per-font sections - Individual validation results for each font with:
-
Font index and name
-
Font path in
collection.ttc:indexformat -
Individual font status (VALID/INVALID/VALID_WITH_WARNINGS)
-
Font-specific errors and warnings
-
Exit codes* - For collections, uses the "worst" status across all fonts:
-
0 = All fonts valid
-
2 = Any font has fatal errors
-
3 = Any font has errors (and no fatal)
-
4 = Any font has warnings (and no errors)
-
5 = Any font has info issues (and no errors or warnings)
-
$ fontisan validate /path/to/font.ttc
Collection: /path/to/font.ttc
Type: TTC
Fonts: 4
Summary:
Total Errors: 14
Total Warnings: 8
Total Info: 0
=== Font 0: Lucida Grande ===
Font: /path/to/font.ttc:0
Status: INVALID
...
=== Font 1: Lucida Grande Bold ===
Font: /path/to/font.ttc:1
Status: INVALID
...The validation framework in Ruby consists of:
- DSL base class
-
Fontisan::Validators::Validatorprovides a declarative syntax for defining validation checks - Table validation helpers
-
56 helper methods across 8 core OpenType tables (name, head, maxp, hhea, glyf, cmap, post, OS/2) that perform specific validation checks
- ValidationReport
-
Structured reports with individual check results, severity levels, and comprehensive issue tracking
require 'fontisan'
# Validate with default profile (production)
report = Fontisan.validate('font.ttf')
puts report.valid? # => true or false
# Validate with specific profile
report = Fontisan.validate('font.ttf', profile: :web)
puts "Errors: #{report.summary.errors}"
puts "Warnings: #{report.summary.warnings}"
# Check validation status
if report.valid?
puts "Font is valid for web use!"
else
puts "Font has #{report.summary.errors} errors"
endreport = Fontisan.validate('font.ttf', profile: :production)
# Get issues by severity
fatal_issues = report.fatal_errors
error_issues = report.errors_only
warning_issues = report.warnings_only
info_issues = report.info_only
# Get issues by category
table_issues = report.issues_by_category('table_validation')
# Get check results
failed_ids = report.failed_check_ids
pass_rate = report.pass_rate
# Export to different formats
yaml_output = report.to_yaml
json_output = report.to_json
summary = report.to_summary # "2 errors, 3 warnings, 0 info"require 'fontisan'
# Load font
font = Fontisan::FontLoader.load('font.ttf')
# Use specific validator
validator = Fontisan::Validators::OpenTypeValidator.new
report = validator.validate(font)
# Check individual results
name_check = report.result_of(:name_validation)
puts name_check.passed?
puts name_check.severityCustom validators inherit from Fontisan::Validators::Validator and define
validation logic using the DSL.
The DSL provides 6 check methods for different validation types:
-
check_table- Validate table-level properties -
check_field- Validate specific field values -
check_structure- Validate font structure and relationships -
check_usability- Validate usability and best practices -
check_instructions- Validate TrueType instructions/hinting -
check_glyphs- Validate individual glyphs
Each check receives a unique ID, severity level (:info, :warning, :error, :fatal), and a validation block.
require 'fontisan/validators/validator'
# Define a custom validator
class MyFontValidator < Fontisan::Validators::Validator
private
def define_checks
# Check name table
check_table :name_validation, 'name', severity: :error do |table|
table.valid_version? &&
table.valid_encoding_heuristics? &&
table.family_name_present? &&
table.postscript_name_valid?
end
# Check head table
check_table :head_validation, 'head', severity: :error do |table|
table.valid_magic? &&
table.valid_version? &&
table.valid_units_per_em? &&
table.valid_bounding_box? &&
table.valid_index_to_loc_format? &&
table.valid_glyph_data_format?
end
# Check structure
check_structure :required_tables, severity: :error do |font|
%w[name head maxp hhea].all? { |tag| !font.table(tag).nil? }
end
end
end
# Use the validator
font = Fontisan::FontLoader.load('font.ttf')
validator = MyFontValidator.new
report = validator.validate(font)
# Check validation results
puts report.valid? # => true/false
puts report.status # => "valid", "valid_with_warnings", "invalid"
puts report.summary.errors # => number of errors
puts report.summary.warnings # => number of warnings
# Query specific checks
result = report.result_of(:name_validation)
puts result.passed? # => true/false
puts result.severity # => "error"
puts result.messages # => array of messages
# Get all failed checks
report.failed_checks.each do |check|
puts "#{check.check_id}: #{check.messages.join(', ')}"
end
# Serialize report
puts report.to_yaml
puts report.to_jsonclass ComprehensiveValidator < Fontisan::Validators::Validator
private
def define_checks
# Name table validation helpers
check_table :name_validation, 'name', severity: :error do |table|
table.valid_version? &&
table.valid_encoding_heuristics? &&
table.family_name_present? &&
table.postscript_name_valid?
end
# Head table validation helpers
check_table :head_validation, 'head', severity: :error do |table|
table.valid_magic? &&
table.valid_version? &&
table.valid_units_per_em? &&
table.valid_bounding_box? &&
table.valid_index_to_loc_format? &&
table.valid_glyph_data_format?
end
# Maxp table validation helpers
check_table :maxp_validation, 'maxp', severity: :error do |table|
table.valid_version? &&
table.valid_num_glyphs? &&
table.valid_max_zones? &&
table.has_truetype_metrics? &&
table.reasonable_metrics?
end
end
endvalidator = MyFontValidator.new
report = validator.validate(font)
# Overall validation status
if report.valid?
puts "Font is valid!"
else
puts "Font has issues:"
# Show errors
report.errors.each do |error|
puts " [ERROR] #{error.category}: #{error.message}"
puts " Location: #{error.location}" if error.location
end
# Show warnings
report.warnings.each do |warning|
puts " [WARN] #{warning.category}: #{warning.message}"
end
end
# Check specific validation results
if report.result_of(:name_validation)&.passed?
puts "Name table version is valid"
else
puts "Name table version check failed"
end
# Get summary statistics
puts "\nValidation Summary:"
puts " Total checks: #{report.check_results.count}"
puts " Passed: #{report.passed_checks.count}"
puts " Failed: #{report.failed_checks.count}"
puts " Errors: #{report.summary.errors}"
puts " Warnings: #{report.summary.warnings}"
puts " Info: #{report.summary.info}"The validation framework provides 56 helper methods across 8 core OpenType tables. Each helper returns a boolean indicating whether the validation passed.
Name table:
-
valid_version?- Check if version is 0 or 1 -
valid_encoding_heuristics?- Check platform/encoding combinations -
has_valid_platform_combos?- Check for required platform combinations -
family_name_present?- Check if family name exists and is non-empty -
postscript_name_present?- Check if PostScript name exists and is non-empty -
postscript_name_valid?- Check if PostScript name matches required pattern
Head table:
-
valid_magic?- Check magic number (0x5F0F3CF5) -
valid_version?- Check version is 1.0 -
valid_units_per_em?- Check units per em is valid (16-16384) -
valid_bounding_box?- Check bounding box coordinates -
valid_index_to_loc_format?- Check format is 0 or 1 -
valid_glyph_data_format?- Check format is 0
Maxp Table:
-
valid_version?- Check version is 0.5 or 1.0 -
valid_num_glyphs?- Check num glyphs >= 1 -
valid_max_zones?- Check maxZones is 1 or 2 -
has_truetype_metrics?- Check TrueType metrics are present -
reasonable_metrics?- Check metrics are within reasonable bounds
Hhea Table:
-
valid_version?- Check version is 1.0 -
valid_metric_data_format?- Check format is 0 -
valid_number_of_h_metrics?- Check count >= 1 -
valid_ascent_descent?- Check signs are correct -
valid_line_gap?- Check line gap >= 0 -
valid_advance_width_max?- Check max width > 0 -
valid_caret_slope?- Check caret slope values -
valid_x_max_extent?- Check extent > 0
Glyf Table:
-
has_empty_glyphs?- Check for empty glyphs -
has_clipped_glyphs?- Check for clipped glyphs -
has_instructions?- Check for TrueType instructions -
contours_valid?- Check contour counts -
glyphs_accessible?- Check glyph accessibility
Cmap Table:
-
valid_version?- Check version is 0 -
has_valid_subtables?- Check subtable validity -
has_unicode_mapping?- Check for Unicode support -
has_valid_bmp_coverage?- Check BMP coverage -
has_valid_format4?- Check format 4 subtable -
glyph_indices_valid?- Check glyph index validity -
supports_platform?- Check platform support
Post Table:
-
valid_version?- Check version (1.0, 2.0, 2.5, 3.0, 4.0) -
valid_italic_angle?- Check italic angle range -
valid_underline_position?- Check underline metrics -
valid_underline_thickness?- Check underline thickness -
valid_is_fixed_pitch?- Check fixed pitch flag -
has_glyph_names?- Check for glyph names
OS/2 Table:
-
valid_version?- Check version (0-5) -
valid_weight_class?- Check weight class (100-900) -
valid_width_class?- Check width class (1-9) -
valid_vendor_id?- Check vendor ID format -
valid_typo_metrics?- Check typographic metrics -
valid_win_metrics?- Check Windows metrics -
valid_unicode_ranges?- Check Unicode range bits -
has_valid_panose?- Check PANOSE classification -
valid_selection_flags?- Check style selection flags -
valid_first_char_index?- Check first character index -
valid_last_char_index?- Check last character index -
valid_typo_ascender?- Check typographic ascender -
valid_typo_descender?- Check typographic descender
Fontisan provides comprehensive tools for managing font containers and collections. A font container is defined as a packaging format that can hold one or more font assets.
In general, Fontisan supports the following levels of font packaging:
- Level 1
-
Outline formats. This level defines the actual glyph outline data format for fonts.
- TrueType
-
Quadratic Bézier curves (glyf/loca tables)
- CFF
-
Cubic Bézier curves (CFF table, PostScript outlines)
- CFF2
-
Variable CFF with cubic curves (CFF2 table)
- Level 2
-
Curve and metadata container. This level defines the overall font file format that encapsulates outline data along with various metadata tables.
- SFNT
-
"Spine/Scalable font" defines the overall structure and directory system for a font file, which houses different "tables" containing specific data like glyph outlines, character maps, and kerning information.
- TrueType (TTF)
-
A specific implementation of the SFNT container that uses TrueType outlines.
- OpenType (OTF)
-
A more versatile SFNT container that can house either TrueType or CFF outlines, along with additional typographic features.
- Web Open Font Format (WOFF and WOFF2)
-
Compressed SFNT-based formats optimized for web delivery.
- Level 3
-
Individual font file, representing a single font asset packaged in a specific container format.
- TTF
.ttf -
A single font file in TrueType format
- OTF
.otf -
A single font file in OpenType format
- WOFF
.woff -
A single font file in Web Open Font Format 1.0 that is SFNT-based and uses zlib compression
- WOFF2
.woff2 -
A single font file in Web Open Font Format 2.0 that is SFNT-based and uses Brotli compression
- TTF
- Level 4
-
Font collections, which are container formats that can hold multiple font assets within a single file.
- TTC (TrueType Collection)
-
Supported since OpenType 1.4. Contains fonts with TrueType outlines (glyf table). Multiple fonts can share identical tables for efficient storage. File extension:
.ttc - OTC (OpenType Collection)
-
Supported since OpenType 1.8. Contains fonts with CFF-format outlines (CFF table). Provides the same storage benefits and glyph-count advantages as TTC but for CFF fonts. File extension:
.otc- dfont (Apple suitcase)
-
Apple proprietary format storing complete Mac font suitcase resources in the data fork. Supports any SFNT fonts (TTF, OTF, or mixed). Mac OS X specific. File extension:
.dfont
Fontist returns the appropriate collection type based on the font data:
-
Examines font data within collection to determine type (TTC vs OTC)
-
TTC contains fonts with TrueType outlines (glyf table)
-
OTC contains fonts with CFF outlines (CFF table)
-
If ANY font in the collection has CFF outlines, use OpenTypeCollection
-
Only use TrueTypeCollection if ALL fonts have TrueType outlines
| Container | TrueType | CFF | Mixed | WOFF/WOFF2 | Platform |
|---|---|---|---|---|---|
TTC |
✅ ONLY |
❌ |
❌ |
❌ |
Cross-platform |
OTC |
✅ |
✅ |
✅ |
❌ |
Cross-platform |
dfont |
✅ |
✅ |
✅ |
❌ |
Apple only |
|
Note
|
WOFF/WOFF2 cannot be packaged into TTC, OTC or dfont collections. Web fonts are designed for single-font delivery only. |
Fontist supports working with TrueType Collections (TTC), OpenType Collections (OTC) and the legacy Apple dfont format. You can list fonts in a collection, extract individual fonts, unpack entire collections, and validate collection integrity.
|
Note
|
Both TTC and OTC files use the same ttcf tag in their binary format, but
differ in the type of font data they contain.
|
In particular, the TTC and OTC formats allows:
- Table sharing
-
Identical tables are stored once and referenced by multiple fonts
- Gap mode
-
Overcomes the 65,535 glyph limit per font by distributing glyphs across multiple fonts in a single file
- Efficient storage
-
Significant size reduction, especially for CJK fonts (e.g., Noto CJK OTC is ~10 MB smaller than separate OTF files)
Fontist returns the appropriate collection type based on the font data:
-
Examines font data within collection to determine type (TTC vs OTC)
-
TTC contains fonts with TrueType outlines (glyf table)
-
OTC contains fonts with CFF outlines (CFF table)
-
If ANY font in the collection has CFF outlines, use OpenTypeCollection
-
Only use TrueTypeCollection if ALL fonts have TrueType outlines
$ fontisan ls FONT.{ttc,otc}|
Note
|
In extract_ttc, this was done with extract_ttc --list FONT.ttc.
|
# List all fonts in a TTC with detailed info
$ fontisan ls spec/fixtures/fonts/NotoSerifCJK/NotoSerifCJK.ttc
Font 0: Noto Serif CJK JP
Family: Noto Serif CJK JP
Subfamily: Regular
PostScript: NotoSerifCJKJP-Regular
Font 1: Noto Serif CJK KR
Family: Noto Serif CJK KR
Subfamily: Regular
PostScript: NotoSerifCJKKR-Regular
Font 2: Noto Serif CJK SC
Family: Noto Serif CJK SC
Subfamily: Regular
PostScript: NotoSerifCJKSC-Regular
Font 3: Noto Serif CJK TC
Family: Noto Serif CJK TC
Subfamily: Regular
PostScript: NotoSerifCJKTC-Regular$ fontisan ls family.dfont
Font 0: Arial
Family: Arial
Subfamily: Regular
Font 1: Arial
Family: Arial
Subfamily: Bold
Font 2: Arial
Family: Arial
Subfamily: ItalicShow detailed information about a TrueType Collection (TTC) or OpenType Collection (OTC), including the number of fonts and metadata for each font.
$ fontisan info FONT.{ttc,otc}|
Note
|
In extract_ttc, this was done with extract_ttc --info FONT.ttc.
|
# Detailed collection analysis
$ fontisan info spec/fixtures/fonts/NotoSerifCJK/NotoSerifCJK.ttc --format yaml
---
collection_type: ttc
font_count: 4
fonts:
- index: 0
family_name: Noto Serif CJK JP
subfamily_name: Regular
postscript_name: NotoSerifCJKJP-Regular
font_format: opentype
- index: 1
family_name: Noto Serif CJK KR
subfamily_name: Regular
postscript_name: NotoSerifCJKKR-Regular
font_format: opentype
- index: 2
family_name: Noto Serif CJK SC
subfamily_name: Regular
postscript_name: NotoSerifCJKSC-Regular
font_format: opentype
- index: 3
family_name: Noto Serif CJK TC
subfamily_name: Regular
postscript_name: NotoSerifCJKTC-Regular
font_format: opentypeExtract all fonts from a TrueType Collection (TTC) or OpenType Collection (OTC) to a specified output directory.
$ fontisan unpack FONT.{ttc,otc} OUTPUT_DIR|
Note
|
In extract_ttc, this was done with extract_ttc --unpack FONT.ttc OUTPUT_DIR.
|
# Extract all fonts from collection
$ fontisan unpack family.ttc --output-dir extracted/
Collection unpacked successfully:
Input: family.ttc
Output directory: extracted/
Fonts extracted: 3/3
- font1.ttf (89.2 KB)
- font2.ttf (89.2 KB)
- font3.ttf (67.4 KB)
# Extract specific font with format conversion
$ fontisan unpack family.ttc --output-dir extracted/ --font-index 0 --format woff2Extract a specific font from a TrueType Collection (TTC) or OpenType Collection (OTC) by its index.
$ fontisan unpack FONT.{ttc,otc} --font-index INDEX OUTPUT.{ttf,otf}|
Note
|
In extract_ttc, this was done with extract_ttc --font-index INDEX FONT.ttc OUTPUT.ttf.
|
# Extract and validate simultaneously
$ fontisan unpack spec/fixtures/fonts/NotoSerifCJK/NotoSerifCJK.ttc extracted_fonts/ --validate
Extracting font 0: Noto Serif CJK JP → extracted_fonts/NotoSerifCJKJP-Regular.ttf
Extracting font 1: Noto Serif CJK KR → extracted_fonts/NotoSerifCJKKR-Regular.ttf
Extracting font 2: Noto Serif CJK SC → extracted_fonts/NotoSerifCJKSC-Regular.ttf
Extracting font 3: Noto Serif CJK TC → extracted_fonts/NotoSerifCJKTC-Regular.ttf
Validation: All fonts extracted successfullyCreate a new TrueType Collection (TTC) or OpenType Collection (OTC) from multiple font files. Fontisan optimizes the collection by deduplicating shared tables to reduce file size.
# Pack fonts into TTC with table sharing optimization
$ fontisan pack font1.ttf font2.ttf font3.ttf --output family.ttc --analyze
Collection Analysis:
Total fonts: 3
Shared tables: 12
Potential space savings: 45.2 KB
Table sharing: 68.5%
Collection created successfully:
Output: family.ttc
Format: TTC
Fonts: 3
Size: 245.8 KB
Space saved: 45.2 KB
Sharing: 68.5%$ fontisan pack Regular.otf Bold.otf Italic.otf --output family.otc --format otc$ fontisan pack Regular.otf Bold.otf Italic.otf --output family.otc --format otc
Collection created successfully:
Output: family.otc
Format: OTC
Fonts: 3|
Note
|
dfont supports mixed TrueType and OpenType fonts in the same suitcase. dfont does not perform table deduplication like TTC/OTC. |
Fontisan supports converting font collections between TrueType Collection (TTC), OpenType Collection (OTC), and Apple dfont formats.
All collection formats (TTC, OTC, dfont) support mixed TrueType and OpenType fonts.
By default, original font formats are preserved during conversion. Use the
--target-format option to standardize all fonts to a specific outline format:
-
--target-format preserve(default) - Keep original mixed TTF+OTF formats -
--target-format ttf- Convert all fonts to TrueType outlines -
--target-format otf- Convert all fonts to OpenType/CFF outlines
|
Note
|
dfont supports mixed TrueType and OpenType fonts in the same suitcase. |
$ fontisan convert family.ttc --to otc --output family.otc
Conversion complete!
Input: family.ttc (245.8 KB)
Output: family.otc (312.4 KB)
Format: TTC → OTC
Fonts: 3Original font formats are preserved (mixed TTF+OTF supported).
$ fontisan convert family.ttc --to otc --output family.otc --target-format otf
Conversion complete!
Input: family.ttc (245.8 KB)
Output: family.otc (312.4 KB)
Format: TTC → OTC
Fonts: 3All TrueType fonts are converted to OpenType/CFF format.
$ fontisan convert family.dfont --to otc --output family.otc
Conversion complete!
Input: family.dfont (387.6 KB)
Output: family.otc (312.4 KB)
Format: DFONT → OTC
Fonts: 3Fonts are extracted from dfont and repacked as OTC. Original font formats are preserved (mixed TTF+OTF supported).
$ fontisan convert family.dfont --to otc --output family.otc --target-format otf
Conversion complete!
Input: family.dfont (387.6 KB)
Output: family.otc (312.4 KB)
Format: DFONT → OTC
Fonts: 3Fonts are extracted from dfont and repacked as OTC. All TrueType fonts are converted to OpenType/CFF format.
Fontisan provides a comprehensive conversion options system based on the TypeTool 3 manual’s recommended options for different font format conversions. The system supports:
-
Format-specific default options
-
Named presets for common workflows
-
Fine-grained control over conversion behavior
-
Type-safe option validation
The conversion options system gives you control over how fonts are converted between formats, with options for opening (reading) fonts and generating (writing) fonts.
For complete documentation, see Conversion Guide and Type 1 Font Support.
Use --show-options to display the recommended options for a conversion:
$ fontisan convert font.ttf --to otf --show-options
Recommended options for TTF → OTF conversion:
======================================================================
Opening options:
--convert-curves: true
--scale-to-1000: true
--autohint: true
--decompose-composites: false
--store-custom-tables: true
Generating options:
--hinting-mode: auto
--decompose-on-output: true
Available presets:
type1_to_modern
modern_to_type1
web_optimized
archive_to_modern
To use preset:
fontisan convert ttf --to otf --preset <name> --output output.extPresets provide pre-configured options for common conversion scenarios:
# Convert Type 1 to modern OpenType
fontisan convert font.pfb --to otf --preset type1_to_modern --output font.otf
# Convert to web-optimized WOFF2
fontisan convert font.otf --to woff2 --preset web_optimized --output font.woff2
# Convert font archive to modern format
fontisan convert family.ttc --to otc --preset archive_to_modern --output family.otcIndividual options can be specified for fine-grained control:
Opening options (control how the source font is read):
-
--decompose- Decompose composite glyphs -
--convert-curves- Convert curve types during conversion -
--scale-to-1000- Scale units-per-em to 1000 -
--autohint- Auto-hint the font -
--generate-unicode- Generate Unicode mappings (Type 1)
Generating options (control how the output font is written):
-
--hinting-mode- Hint mode: preserve, auto, none, or full -
--optimize-tables- Enable table optimization -
--decompose-on-output- Decompose composites in output
# Convert with autohinting and optimization
fontisan convert font.ttf --to otf --output font.otf \
--autohint --hinting-mode auto --optimize-tablesFor detailed information on all available options and conversion scenarios, see the Conversion Guide.
require 'fontisan'
# Get recommended options for TTF → OTF conversion
options = Fontisan::ConversionOptions.recommended(from: :ttf, to: :otf)
# Access opening and generating options
options.opening # => { convert_curves: true, scale_to_1000: true, ... }
options.generating # => { hinting_mode: "auto", decompose_on_output: true }
# Use with converter
converter = Fontisan::Converters::OutlineConverter.new
tables = converter.convert(font, options: options)require 'fontisan'
# Load a preset
options = Fontisan::ConversionOptions.from_preset(:type1_to_modern)
# Convert with preset
converter = Fontisan::Converters::Type1Converter.new
tables = converter.convert(font, options: options)require 'fontisan'
# Build custom conversion options
options = Fontisan::ConversionOptions.new(
from: :ttf,
to: :otf,
opening: { autohint: true, convert_curves: true },
generating: { hinting_mode: "auto" }
)
# Use with converter
converter = Fontisan::Converters::OutlineConverter.new
tables = converter.convert(font, options: options)Fontisan ensures high-fidelity font conversion through comprehensive round-trip validation.
When converting between TrueType (TTF) and OpenType/CFF (OTF) formats, the validation system verifies that glyph geometry is preserved accurately.
Key validation features:
-
Command-Level Precision: Validates individual drawing commands (move, line, curve)
-
Coordinate Tolerance: Accepts ±2 pixels tolerance for rounding during conversion
-
Format-Aware Comparison: Handles differences between TrueType quadratic and CFF cubic curves
-
Closepath Handling: Smart detection of geometrically closed vs open contours
Round-trip validation works by:
Original TTF → Convert to CFF → Extract CFF → Compare Geometry
(Input) (Encode) (Decode) (Validate)Validation process:
-
Extract glyph outlines from original TTF
-
Convert to CFF format with CharString encoding
-
Parse CFF CharStrings back to universal outlines
-
Compare geometry with coordinate tolerance (±2 pixels)
Format differences handled:
-
Closepath: CFF has implicit closepath, TTF has explicit
-
Curve types: TrueType quadratic (
:quad_to) vs CFF cubic (:curve_to) -
Coordinate rounding: Different number encoding causes minor differences
Validation criteria: Geometry Match: . Same bounding box (±2 pixel tolerance) . Same number of path commands (excluding closepath) . Same endpoint coordinates for curves (±2 pixels) . Quadratic→cubic conversion accepted
The Fontisan Universal Outline Model (UOM) is based on a self-stable algorithm for converting soft glyph contours to outline format used in all tools of Fontisan. This ability allows easy modeling of import glyphs from one font format TrueType (TTF, OTF binaries), converting glyph elements into any font format, TrueType for example.
Locker is an object-oriented model for storing imported outlines and glyphs. Storage is based on monotonic spirals computed based on 2D points and curves. Invisible converting from TrueType, CFF Opentype and ColorGlyph formats.
Translation is an object-oriented model for converting from and to PostScript custom CFF charset. New encoding/decoding includes PostScript Type 2/3/composite Loron.
Support for layered import CFF color glyphs rasterizing on demand, with composite font support, a multi-layer color font represented by many CFF fonts stacked on top of each other. ColorGlyph support contains color glyphs, advanced color fonts glyphs and raster images (PNG or JPG) combined with TrueType outlines.
Fontisan can:
-
Import TrueType contours into Universal Outline Model (UOM)
-
Operate UOM outlines including transformations, serialization (save)
-
Select and convert all UOM contours to TTF/OTF
-
Cleaning
-
Improve
-
Render
-
Building works for TrueType
-
Convert colors (cvt to TTF/OTF or TTF to cvt)
-
Saving and sharing font structures
-
Working with advanced color fonts
Fontisan can:
-
Use Universal Outline Model (UOM) for TrueType contours and CFF color glyphs
-
Repository for investor-defined fonts
-
Custom Unicode assignments, rewriting Unicode configurations
-
Saving and import outlines, including TrueType and OTF/CFF
-
Rendering for advanced font types
-
Universal layer stacking for advanced color glyph combinations
(Fontisan, converted TTF, OTF files)
Fontisan can:
-
Import embedded TTF/OTF color layers
-
Assembler from individual TTF/OTF slices
-
Advanced managing layer maps in TTF color (CFF) fonts
-
Advanced color layer blending style management
-
Managing Gray/Overprint/Color-Full image comps and layer conversion
-
Strategy management for smart vector combos from raster
-
Importing and generation PNG block ruler layers
Fontisan has a comprehensive test suite covering all font operations, formats, and features.
# Run full test suite
bundle exec rspec
# Run with documentation format
bundle exec rspec --format documentation
# Run specific file
bundle exec rspec spec/fontisan/tables/maxp_spec.rbAll required font fixtures are automatically downloaded before tests run. The test suite uses a centralized fixture configuration system that ensures all necessary fonts are available.
# Manual fixture management (if needed)
bundle exec rake fixtures:download # Download all test fonts
bundle exec rake fixtures:clean # Remove downloaded fontsCopyright Ribose.
Fontisan is licensed under the Ribose 3-Clause BSD License. See the LICENSE file for details.