Skip to content

PRO-2684/Morphio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

86 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Morphio

GitHub License GitHub Repo stars GitHub Issues or Pull Requests GitHub Issues or Pull Requests Crates.io Version Crates.io Total Downloads docs.rs

Morphs the font, so one word renders as another.

📥 Installation

Note

You can try Morphio without installing it in the browser.

Using binstall

cargo binstall morphio

Downloading from Releases

Navigate to the Releases page and download respective binary for your platform. Make sure to give it execute permissions.

Compiling from Source

cargo install morphio

📖 Usage

💻 CLI

Morphio: Morphs the font, so one word renders as another.

Options:
  -i, --input       input font file path
  -o, --output      output font file path
  -r, --recipe      load morph rules and word matching options from a TOML
                    recipe file, ignoring command-line options
  -m, --no-word-match
                    disable both start and end word matching
  --no-word-match-start
                    disable word matching at the start of the source word
  --no-word-match-end
                    disable word matching at the end of the source word
  --skip-missing-glyphs
                    skip rules that reference missing glyphs instead of failing
  -y, --yes         allow overwrite output file if it exists
  -h, --help        display usage information

Without a recipe file, pass rules as positional FROM TO pairs:

morphio -i input.ttf -o output.ttf banana orange from to

To relax word-boundary matching or skip unsupported rules:

morphio -i input.ttf -o output.ttf --no-word-match-end --skip-missing-glyphs banana orange

To drive the CLI from a recipe file:

morphio -i input.ttf -o output.ttf -r tests/recipes/simple.toml

🌐 Web Interface

The browser demo supports:

  • Uploading a font file
  • Adding multiple morph rules
  • Toggling advanced options:
    • Match word start
    • Match word end
    • Skip missing glyphs
  • Previewing original and morphed text side by side
  • Downloading the morphed font
  • Importing and exporting recipe TOML files

The web UI uses the same recipe format as the CLI and library.

🧾 Recipes

Recipes are TOML files that store:

  • Morph rules
  • Word-boundary matching options
  • Whether rules with missing glyphs should be skipped

Example:

[options]
word_match_start = true
word_match_end = true
skip_missing_glyphs = false

[[rules]]
from = "banana"
to = "orange"

[[rules]]
from = "from"
to = "to"

Notes:

  • word_match_start = false allows matches inside a longer word prefix, such as morphing xbanana to xorange.
  • word_match_end = false allows matches before a longer word suffix, such as morphing bananas to oranges.
  • skip_missing_glyphs = true ignores rules whose source or target characters are not present in the font instead of aborting the whole morph.

You can find example recipes under tests/recipes.

✅ TODO

  • Actual shaping tests via harfrust (when it updates on crates.io to use read-fonts 0.38.0)
  • Replace our rough implementation of TTC support after write-fonts adds support for that.
  • Recipe (configuration) support
    • Advanced settings
    • Morph rules
  • Skip rules with missing glyphs instead of failing
  • Configure word matching of start and end of words separately
  • Reduce TTC font sizes (table sharing?)
  • Allow morphing multiple words in one go
  • Allow morphing words with different lengths
  • ServiceWorker
  • Option for enabling/disabling word matching
    • Say we want to morph "banana" to "orange"
    • When word matching is enabled, xbanana will not be morphed, because we're matching whole words, not letters
    • When word matching is disabled, xbanana will be morphed to xorange
  • Optimization
    • Use CoverageTable::Format2, which allows for more efficient storage of contiguous ranges of glyphs
  • Better word matching
    • Currently we consider whole words only by letters ([a-zA-Z]+)
    • Next stage: consider digits ([0-9]+) and underscores (_)
    • Maybe an option for toggling which characters to consider as part of words?
  • Bug fixes
    • Might not work with fonts with multiple language records, e.g. IMPACT.TTF

The following is of low priority, and may not get implemented:

  • Reuse 1 -> N and N -> 1 mappings: Not likely for those to be shared, and adds complexity
  • Determine "best split point", instead of just split in the end: Need to determine how to measure "best", i.e. a scoring algorithm, which could consider:
    • maximize unchanged 1 -> 1 pairs we can skip
    • maximize reuse of existing 1 -> 1, 1 -> N, or N -> 1 mappings
    • maybe prefer a suffix split instead of a prefix split in some cases
  • Use contextual substitution instead of chained contextual substitution, when word_match_* options are all disabled, because we don't have to look forward or backward in this case
    • Tiny performance gain and size reduction
    • Cleaner and semantically more correct
    • Low complexity

🎉 Credits

About

Morphs the font, so one word renders as another.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors