Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 9 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@ This code repository contains an implementation of an

## Status

This implementation is still in the early stages and as at the moment is work in progress.
We aim to keep it updated and consistent with the current IFT specification working draft
found [here](https://w3c.github.io/IFT/Overview.html).
This implementation is still a work in progress. We aim to keep it updated and consistent with the
current IFT specification working draft found [here](https://w3c.github.io/IFT/Overview.html).
The current implementation is capable of producing a spec-compliant and optimized encodings

The current implementation is capable of producing a spec-compliant encoding, but does not
yet fully support all aspects of the specification. Notably:

* Not all encoder config options are supported yet. These are marked as unimplemented in the schema.

Additionally, the produced encodings may not be fully optimized for minimal size yet.

See this repos issue tracker for a more complete list of missing functionality.
However, there is still remaining work to do on the encoder that will increase performance and
produce even more optimized encodings. See this repos issue tracker for a more complete list of missing
functionality.

## Building and Testing

Expand All @@ -43,11 +38,9 @@ There are two main phases to producing an IFT font:
patches to maximize performance.
* Compilation: takes the font and the segmentation and produces the IFT font and patch files.

For more information see the documents under [docs/experimental](docs/experimental). Of note:
* [compiler.md](docs/experimental/compiler.md)
* [closure_glyph_segmentation.md](docs/experimental/closure_glyph_segmentation.md)
* [closure_glyph_segmentation_merging.md](docs/experimental/closure_glyph_segmentation_merging.md)
* [closure_glyph_segmentation_complex_conditions.md](docs/experimental/closure_glyph_segmentation_complex_conditions.md)
For more information see the documents under [docs/](docs/). Of note:
* [segmenter.md](docs/segmenter.md)
* [compiler.md](docs/compiler.md)

### font2ift with Auto Config

Expand Down
File renamed without changes.
18 changes: 12 additions & 6 deletions docs/experimental/dependency_graph.md → docs/dependency_graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

Author: Garret Rieger
Date: Mar 23, 2026
Updated: Aug 5th, 2026

## Introduction

This document provides some information on the [DependencyGraph](../../ift/dep_graph/dependency_graph.h) representation
of glyph closure dependencies in a font.

A font dependency graph encodes the dependencies that exist within a font. For example if `f` and `i` can be substituted
for a `fi` glyph then the dependency graph would encode an edge from `f` and `i` to the `fi` glyph. For more details
see the harfbuzz documentation [harfbuzz/docs/depend-api.md](https://github.com/harfbuzz/harfbuzz/blob/main/docs/depend-api.md)

`DependencyGraph` is a wrapper around the harfbuzz dependency graph API. It unifies glyph to glyph dependency information
with additional dependency information from:
* UVS mappings specified in `cmap 14`.
* Unicode to Glyph mappings from `cmap`
* Segment and initial font definitions from a segmentation information object.

`DependencyGraph` provides methods for traversing the dependency graph, and optionally supports doing the traversal
Expand All @@ -29,7 +35,7 @@ The dependency graph is a directed graph where edges encode dependency informati
from `f -> fi` with context `{i}` and an edge from `i -> fi` with context `{f}`. For ligatures, features, and UVS
dependencies the graph will always contain edges for each member of the conjunction (see the ligature example). For
contextual GSUB substitutions this is not the case.

## Init Font

The dependency graph is configured with an initial font definition. For the graph traversal all glyphs, code points and
Expand All @@ -42,29 +48,29 @@ disjunctive due to the `i` context requirement already being satisfied.
* `Segment`: segment nodes are the entry point into the graph. There is one segment node per segment in the segmentation
information object. Each segment has an out going edge for each code point and layout feature in the segment's
definition.

* `Unicode code point`: each code point node can have two types of out going edges derived from the font's `cmap`
table. The first type is for regular `cmap` mappings of code point to glyph. The second type is a conjunctive edge
derived from the `cmap 14` sub table, which maps from code point pairs to a substitution glyph. Beyond `cmap` a code
point may also have outgoing disjunctive edges to other code points which model bidi substitutions made in harfbuzz's
glyph closure.

* `Glyph`: each glyph node can have out going edges to additional glyphs that are derived from various tables in the
font (eg. COLR, GSUB, MATH, glyf). These can be disjunctive or conjunctive. Conjunctive edges in GSUB may have a layout
feature as part of the edge's context if that layout feature is not part of the initial font. This layout feature is
the feature which activates the lookup associated with the edge.

* `Feature`: each feature node can have outgoing edges to any glyphs where the feature is part of the conjunctive
condition as discussed above.

## Traversal

Graph traversal takes a set of starting nodes and traverses the graph to discover which nodes can be reached. There are
two main traversal modes:

* Enforced Context: during traversal before a conjunctive edge can be traversed it's context requirements must have been
reached. This mimics harfbuzz's glyph closure, but may still overestimate it in some cases.


* Non-enforced Context: conjunctive edges are always traversed even if their requirements are not met. This mode is
useful for modeling all glyphs that could potentially be reached from a starting point.
Expand Down
59 changes: 59 additions & 0 deletions docs/dependency_graph_condition_extract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Dependency Graph Glyph Condition Extraction

Author: Garret Rieger
Date: June 3rd, 2026
Updated: Aug 5th, 2026

## Introduction

This document describes how the [dependency graph](dependency_graph.md) is used to find activation conditions for glyphs
in a font. For more details see [Segmenter concepts](segmenter.md#concepts) which are used in this document, so it's recommended to review that before reading this.

## Representing Activation Conditions

An activation condition is a boolean expression in terms of segment presence. To keep the conditions normalized all
boolean conditions are stored in monotonic [conjunctive normal form](https://en.wikipedia.org/wiki/Conjunctive_normal_form)

The [activation condition](../ift/encoder/activation_condition.h) class implements a couple of operations to help in this analysis:

* `ActivationCondition::Or(A, B)`: joins two activation conditions in a new condition `(A) OR (B)`. The new condition is
automatically normalized and simplified to give a canonical representation.

* `ActivationCondition::And(A, B)`: joins two activation conditions in a new condition `(A) AND (B)`. The new condition is
automatically normalized and simplified to give a canonical representation.

## The Algorithm

1. Initialize a map from graph node to activation condition: all nodes which are part of the initial font definition are
marked as always true. One node is added per segment which an activation condition of itself. All other nodes are left
unmapped.

2. Precompute the set of incoming edges for each node of the dependency graph.

3. Find the [strongly connected components](https://en.wikipedia.org/wiki/Strongly_connected_component) of the dependency graph.
The implementation uses [Tarjan's algorithm](https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)

4. Process the components in topological order (which is determined as a by product of Tarjan's algorithm).

* If a component has only one node: construct the condition for the node by joining the activation condition for
each parent node (via incoming edges). Parent node conditions are joined with disjunction. Conjunctive edges have
multiple parent nodes, these conditions are joined first using conjunction. Simplify and normalize the combined
condition into conjunctive normal form.

* Otherwise if a component has more than one node: Iterate through the nodes of the component. Propagate conditions
as described in the one node case. Repeat this iteration until no node conditions change.

## Closure Phasing

The intention of this condition extraction is to mirror font subsetting closure, which happens in phases that each
process a specific dependency type. As a result the extraction algorithm must also be phased. The above algorithm is run
once per subsetting closure phase and only works with graph edges that are inscope for that phase.

## Caveats

Since this approach relies on the dependency graph, which in some cases over approximates glyph conditions, the extracted
conditions may also over approximate the true conditions. This primarily affects glyphs reachable via contextual GSUB substitutions.

## Implementation

The implementation of dependency graph condition extraction can be found in [dependency_closure.cc](../ift/encoder/dependency_closure.cc).
3 changes: 3 additions & 0 deletions docs/experimental/closure_glyph_segmentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Updated: Dec 17, 2025

## Introduction

*Note: the approach in this document is obsolete and has been replaced by
[dependency graph condition analysis](../dependency_graph_condition_extract.md).*

A key part of encoding an [IFT](https://w3c.github.io/IFT/Overview.html) font is splitting the
outline (eg. glyf, gvar) data into a set of patches which are loaded by the client as needed. Each
patch contains the data associated with one or more glyph ids. Patch loads are triggered by a client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Date: Dec 17, 2025

## Introduction

*Note: the approach in this document is obsolete and has been replaced by
[dependency graph condition analysis](../dependency_graph_condition_extract.md).*

Before reading this document is recommended to first review the
[closure glyph segmentation](./closure_glyph_segmentation.md) document. This document borrows concepts and terms from it.

Expand Down
Loading
Loading