Project

fontisan

0.0
The project is in a healthy, maintained state
Fontisan provides 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). The gem provides both a Ruby library API and a command-line interface, with structured output formats (YAML, JSON, text).
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 2.5
~> 1.4
 Project Readme

Fontisan: Font analysis tools and utilities

RubyGems Version License Build Status

Purpose

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, TTC).

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:

  • otfinfo from LCDF Typetools. Fontisan supports all features provided by otfinfo, 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.

Installation

Add this line to your application’s Gemfile:

gem "fontisan"

And then execute:

bundle install

Or install it yourself as:

gem install fontisan

Features

  • Extract comprehensive font metadata (name, version, designer, license, etc.)

  • 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

  • Support for OTF, TTF, and TTC font formats

  • Command-line interface with full otfinfo parity

  • Ruby library API for programmatic access

  • Structured output in YAML, JSON, and text formats

Usage

Command-line interface

Font information

Extract comprehensive metadata from font files. This includes font names, version information, designer credits, vendor details, licensing information, and font metrics.

Syntax:

$ fontisan info FONT_FILE [--format FORMAT]

Where,

FONT_FILE

Path to the font file (OTF, TTF, or TTC)

FORMAT

Output format: text (default), json, or yaml

Example 1. Font information for Libertinus Serif Regular
$ 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
Example 2. Output in structured YAML format
$ fontisan info spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf --format yaml
font_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: 1000

List OpenType tables

Display the font’s table directory, showing all OpenType tables with their sizes, offsets, and checksums. Useful for understanding font structure and verifying table integrity.

Syntax:

$ fontisan tables FONT_FILE [--format FORMAT]

Where,

FONT_FILE

Path to the font file (OTF, TTF, or TTC)

FORMAT

Output format: text (default), json, or yaml

Example 3. List of OpenType tables in Libertinus Serif Regular
$ fontisan tables spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
SFNT 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)
Example 4. Output in structured YAML format
$ 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
- tag: OS/2
  length: 96
  offset: 392
  checksum: 1211167171
- tag: cmap
  length: 3620
  offset: 11412
  checksum: 61683865
- tag: 'cvt '
  length: 248
  offset: 18868
  checksum: 815272574
- tag: fpgm
  length: 3596
  offset: 15032
  checksum: 1647249281

List glyph names

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, or TTC)

FORMAT

Output format: text (default), json, or yaml

Example 5. List of glyph names in Libertinus Serif Regular
$ fontisan glyphs spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
Glyph 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
     ...

Show Unicode mappings

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, or TTC)

FORMAT

Output format: text (default), json, or yaml

Example 6. Unicode to glyph mappings in Libertinus Serif Regular
$ fontisan unicode spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
Unicode 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
...

Variable font information

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, or yaml

Example 7. Variable font axes and instances in Mona Sans
$ fontisan variable spec/fixtures/fonts/MonaSans/variable/MonaSans[wdth,wght].ttf
Axis 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
...

Optical size information

Display optical size range from the OS/2 table for fonts designed for specific point sizes.

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, or yaml

Example 8. Optical size information in Libertinus Serif Display
$ fontisan optical-size spec/fixtures/fonts/libertinus/ttf/LibertinusSerifDisplay-Regular.ttf
Size range: [18, 72) pt  (source: OS/2_usLowerOpticalPointSize)

List supported scripts

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, or TTC)

FORMAT

Output format: text (default), json, or yaml

Example 9. Supported scripts in Libertinus Serif Regular
$ fontisan scripts spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
Script count: 5

DFLT  Default
cyrl  Cyrillic
grek  Greek
hebr  Hebrew
latn  Latin

List OpenType features

Show 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, or TTC)

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, or yaml

Example 10. OpenType features for Latin script
$ fontisan features spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf --script latn
Script: latn
Feature count: 4

cpsp  Capital Spacing
kern  Kerning
mark  Mark Positioning
mkmk  Mark to Mark Positioning
Example 11. OpenType features for all scripts
$ fontisan features spec/fixtures/fonts/libertinus/ttf/LibertinusSerif-Regular.ttf
Script: 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 Positioning

Dump raw table data

Extract raw binary data from a specific OpenType table. Useful for detailed analysis or debugging font issues.

Syntax:

$ fontisan dump-table FONT_FILE TABLE_TAG

Where,

FONT_FILE

Path to the font file (OTF, TTF, or TTC)

TABLE_TAG

Four-character table tag (e.g., name, head, GSUB, GPOS)

Example 12. Dump raw table data to files
$ 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.bin

The output is binary data written directly to stdout, which can be redirected to a file for further analysis.

General options

All commands support these options:

--format FORMAT

Output format: text (default), json, or yaml

--font-index INDEX

Font index for TTC files (default: 0)

--verbose

Enable verbose output

--quiet

Suppress non-error output

Version information

Display the Fontisan version:

fontisan version

Ruby API

General

Fontisan provides a comprehensive Ruby API for programmatic font analysis.

All functionality available via the CLI is accessible through the library.

Loading fonts

Load TrueType and OpenType fonts:

Example 13. Loading a TrueType font
require "fontisan"

# Load a TrueType font (.ttf)
font = Fontisan::TrueTypeFont.from_file("path/to/font.ttf")

# Load an OpenType font (.otf with CFF outlines)
font = Fontisan::OpenTypeFont.from_file("path/to/font.otf")
Example 14. Loading fonts from TrueType Collections
# Load a specific font from a TTC file
ttc = Fontisan::TrueTypeCollection.from_file("path/to/fonts.ttc")
font = ttc.font_at_index(0)  # Get first font

# Or use FontLoader to auto-detect format
font = Fontisan::FontLoader.load_file("path/to/any-font-file.ttf", font_index: 0)

Accessing font tables

Access and parse OpenType tables:

Example 15. Working with the name table
name_table = font.table("name")

# Get standard name entries
family = name_table.english_name(Fontisan::Tables::Name::FAMILY)
subfamily = name_table.english_name(Fontisan::Tables::Name::SUBFAMILY)
full_name = name_table.english_name(Fontisan::Tables::Name::FULL_NAME)
postscript_name = name_table.english_name(Fontisan::Tables::Name::POSTSCRIPT)

# Get all available names
name_table.name_records.each do |record|
  puts "#{record.name_id}: #{record.string}"
end
Example 16. Working with the head table
head_table = font.table("head")

# Get font revision
revision = head_table.font_revision  # => 7.050994873046875

# Get units per em
units_per_em = head_table.units_per_em  # => 1000

# Get created/modified timestamps
created = head_table.created
modified = head_table.modified
Example 17. Working with the OS/2 table
os2_table = font.table("OS/2")

# Get vendor ID
vendor_id = os2_table.ach_vend_id  # => "QUE "

# Check optical size
if os2_table.version >= 5
  lower_size = os2_table.us_lower_optical_point_size  # => 18
  upper_size = os2_table.us_upper_optical_point_size  # => 72
end

# Get weight class
weight = os2_table.us_weight_class  # => 400 (Regular)
Example 18. Working with the post table
post_table = font.table("post")

# Get all glyph names
glyph_names = post_table.glyph_names  # => [".notdef", "space", "exclam", ...]

# Check post table version
version = post_table.version  # => 2.0

Working with cmap (Unicode mappings)

Access Unicode to glyph mappings:

Example 19. Getting Unicode mappings
cmap_table = font.table("cmap")

# Get all Unicode to glyph index mappings
mappings = cmap_table.unicode_mappings
# => { 0x0020 => 1, 0x0021 => 2, 0x0022 => 3, ... }

# Look up specific  Unicode codepoint
glyph_index = mappings[0x0041]  # => glyph index for 'A'

# Get subtables
subtables = cmap_table.subtables
subtables.each do |subtable|
  puts "Platform #{subtable.platform_id}, Encoding #{subtable.encoding_id}"
end

Working with GSUB/GPOS (scripts and features)

Extract OpenType layout information:

Example 20. Getting supported scripts
# Get scripts from GSUB table
gsub = font.table("GSUB")
scripts = gsub.scripts  # => ["DFLT", "latn", "cyrl", "grek", "hebr"]

# Get scripts from GPOS table
gpos = font.table("GPOS")
scripts = gpos.scripts  # => ["DFLT", "latn", "cyrl", "grek", "hebr"]

# Combine scripts from both tables
all_scripts = (gsub.scripts + gpos.scripts).uniq.sort
Example 21. Getting OpenType features
gsub = font.table("GSUB")

# Get features for a specific script
latin_features = gsub.features(script_tag: "latn")
# => ["cpsp", "kern", "mark", "mkmk"]

# Get features for all scripts
scripts = gsub.scripts
features_by_script = scripts.each_with_object({}) do |script, hash|
  hash[script] = gsub.features(script_tag: script)
end
# => {"DFLT"=>["cpsp", "kern", ...], "latn"=>["cpsp", "kern", ...], ...}

# Combine GSUB and GPOS features
gpos = font.table("GPOS")
all_features = (gsub.features(script_tag: "latn") + gpos.features(script_tag: "latn")).uniq

Working with variable fonts

Access variation axes and instances:

Example 22. Analyzing variable fonts
fvar_table = font.table("fvar")

# Check if font is variable
is_variable = !fvar_table.nil?

# Get variation axes
axes = fvar_table.axes
axes.each do |axis|
  puts "Axis: #{axis.axis_tag}"
  puts "  Name: #{axis.axis_name_id}"
  puts "  Range: #{axis.min_value} to #{axis.max_value}"
  puts "  Default: #{axis.default_value}"
end

# Get named instances
instances = fvar_table.instances
instances.each do |instance|
  puts "Instance: #{instance.subfamily_name_id}"
  puts "  Coordinates: #{instance.coordinates.inspect}"
end

Font inspection utilities

Check table presence and extract basic info:

Example 23. Font inspection
# Ch eck if font has specific tables
has_gsub = font.has_table?("GSUB")  # => true
has_gpos = font.has_table?("GPOS")  # => true
has_fvar = font.has_table?("fvar")  # => false (not a variable font)

# Get list of all table tags
table_names = font.table_names
# => ["GDEF", "GPOS", "OS/2", "cmap", "cvt ", ...]

# Get font format
font_format = font.header.sfnt_version == 0x00010000 ? "TrueType" : "OpenType"

# Get number of glyphs
post = font.table("post")
glyph_count = post.glyph_names.length  # => 2731

Using commands programmatically

Use command classes for structured output:

Example 24. Getting structured font information
# Use InfoCommand to get structured info
cmd = Fontisan::Commands::InfoCommand.new("font.ttf", {})
info = cmd.run  # Returns Fontisan::Models::FontInfo

# Access structured data
puts info.family_name      # => "Libertinus Serif"
puts info.designer         # => "Philipp H. Poll, Khaled Hosny"
puts info.font_format      # => "truetype"

# Serialize to formats
json_output = info.to_json
yaml_output = info.to_yaml
Example 25. Getting scripts and features programmatically
# Get scripts
scripts_cmd = Fontisan::Commands::ScriptsCommand.new("font.ttf", {})
scripts_info = scripts_cmd.run  # Returns Fontisan::Models::ScriptsInfo

scripts_info.scripts.each do |script|
  puts "#{script.tag}: #{script.description}"
end

# Get features for a script
features_cmd = Fontisan::Commands::FeaturesCommand.new(
  "font.ttf",
  { script: "latn" }
)
features_info = features_cmd.run  # Returns Fontisan::Models::FeaturesInfo

features_info.features.each do |feature|
  puts "#{feature.tag}: #{feature.description}"
end

Development

After checking out the repo, run bundle install to install dependencies.

Running tests

bundle exec rake spec

Code style

Check code style with RuboCop:

bundle exec rake rubocop

Test fixtures

The test suite uses real font files for testing.

Rake tasks are provided to download fonts from GitHub releases for testing.

Download test fixtures

Download font fixtures automatically (only downloads if files don’t already exist):

bundle exec rake fixtures:download

This downloads four font collections:

  • Libertinus 7.051 - Serif, Sans, and Mono families with extensive OpenType features (included in repository)

  • Mona Sans v2.0 - Variable TTF with width and weight axes for testing variable fonts (downloaded via Rake, not in repository)

  • Noto Serif CJK 2.003 (Static) - Large CJK TTC for testing static font collections (downloaded via Rake, not in repository)

  • Noto Serif CJK 2.003 (Variable) - Variable OTF/OTC for testing variable OpenType collections (downloaded via Rake, not in repository)

The Rake task uses file dependencies, so running it multiple times won’t re-download existing files. This makes it safe and efficient to run repeatedly during development.

Note
Noto CJK and Mona Sans fonts are excluded from the repository (see .gitignore) due to their size. They are automatically downloaded when running rake fixtures:download.

Clean test fixtures

Remove all downloaded fixture files:

bundle exec rake fixtures:clean

Built-in fixtures

The repository includes pre-installed Libertinus fixtures in spec/fixtures/fonts/libertinus/ which are sufficient for basic testing.

Large font collections (Noto CJK, Mona Sans) are not committed to the repository due to their size. Use rake fixtures:download to obtain them for comprehensive testing of:

  • Variable fonts (MonaSans variable TTF)

  • Large static font collections (NotoSerifCJK TTC)

  • Variable OpenType collections (NotoSerifCJK-VF OTC)

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/fontist/fontisan.

License

The gem is available as open source under the terms of the BSD-2-Clause license.

Copyright Ribose.