1. Introduction
This section is not normative.
Incremental Font Transfer (IFT) is a technology to improve the latency of remote fonts (or "web fonts") on the web. Without this technology, a browser needs to download every last byte of a font before it can render any characters using that font. IFT allows the browser to download only some of the bytes in the file, thereby decreasing the perceived latency between the time when a browser realizes it needs a font and when the necessary text can be rendered with that font.
The success of WebFonts is unevenly distributed. This specification allows WebFonts to be used where slow networks, very large fonts, or complex subsetting requirements currently preclude their use. For example, even using WOFF 2 [WOFF2] , fonts for CJK languages are too large to be practical.
1.1. Technical Motivation: Evaluation Report
See the Progressive Font Enrichment: Evaluation Report [PFE-report] for the investigation which led to this specification.
1.2. Overview
An IFT font is a regular OpenType font that is extended to include the incremental functionality via the addition of a two new tables . Using these new tables the font can be augmented (eg. to cover more codepoints) by loading and applying patches to it.
The IFT technology has four main pieces:
§ 4 Extensions to the Font Format : defines the new tables which contain a list of patches that are available to be applied to a font.
§ 5 Font Patch Formats : defines three different types of patches that can be used. These range from generic binary patches, to a patch format that is specific to the font format.
§ 6 Extending a Font Subset : provides the algorithm that is used by a client to select and apply patches.
§ 7 Encoder : encoders create the font and associated patches that form an incremental font.
At
a
high
level
an
IFT
works
as
follows:
font
is
used
like
this:
-
The client downloads an initial font file, which contains some subset of data from the
originalfull version of the font. -
Inside the font file there is embedded data which informs the client about a set of patches which are available and can be used to extend the font.
-
TheBased on the content the client is trying to render it selects, downloads, and applies these patches to extend theinitialfont to cover additional characters. This step is repeated every time new content is being rendered.
1.1.
1.3.
Technical
Motivation:
Evaluation
Report
Creating
an
Incremental
Font
See
It
is
expected
that
the
Progressive
Font
Enrichment:
Evaluation
Report
[PFE-report]
most
common
way
to
produce
an
incremental
font
will
be
to
convert
an
existing
font
to
use
the
incremental
encoding
defined
in
this
specification.
At
a
high
level
converting
an
existing
font
to
be
incremental
will
look
like
this:
Create the base subset , this will be the initial font file the client will load and usually will include the data from the original font that is expected to be always needed.
Choose a segmentation of the font. Individual segments will be added to the base subset by the client using patches. Choosing an appropriate segmentation is one of the most important parts of producing an efficient encoding.
Based on the chosen segmentation generate a set of patches where each patch will add the data for a particular segment.
Add a patch mapping to the
investigation which ledbase subset. This mapping lists all of the available patch files, the url they reside at, and information on what data the patch will add to the font.
Note:
this
specification.
is
a
highly
simplified
description
of
creating
an
incremental
font,
a
more
in-depth
discussion
of
generating
an
encoding
and
requirements
on
the
encoding
can
be
found
in
the
§ 7
Encoder
section.
1.2.
1.4.
Performance
Considerations
and
the
use
of
Incremental
Font
Transfer
Using incremental transfer may not always be beneficial, depending on the characteristics of the font, the network, and the content being rendered. This section provides non-normative guidance to help decide when incremental transfer should be utilized.
Incrementally loading a font has a fundamental performance trade off versus loading the whole font. Simplistically, under incremental transfer less bytes may be transferred at the potential cost of increasing the total number of network requests being made, and/or increased request processing latency. In general incremental font transfer will be beneficial where the reduction in latency from sending less bytes outweighs additional latency introduced by the incremental transfer method.
The first factor to consider is the language of the content being rendered. The evaluation report contains the results of simulating incremental font transfer across three categories of languages ( Progressive Font Enrichment: Evaluation Report § langtype ). See it’s conclusions Progressive Font Enrichment: Evaluation Report § conclusions for a discussion of the anticipated performance of incremental font transfer across the language categories.
Next,
how
much
of
the
font
is
expected
to
be
needed?
If
it’s
expected
that
most
of
the
font
will
be
needed
to
render
the
content
content,
then
incremental
font
transfer
is
unlikely
to
be
beneficial.
In
many
cases
however
only
part
of
a
font
is
expected
to
be
needed.
For
example:
-
If the font contains support for several languages but a user is expected to only render content in a subset of those languages.
-
If the content being rendered uses a small subset of the total characters in a font. This is often the case for Chinese, Japanese, Korean, Emoji, and Icon fonts.
-
Only a small amount of text is being rendered. For example a font that is only used for a headline.
An alternative to incremental transfer is to break a font into distinct subsets (typically by script) and use the unicode range feature of @font-face to load only the subsets needed. However, this can break rendering Progressive Font Enrichment: Evaluation Report § fail-subset if there are layout rules between characters in different subsets. Incremental font transfer does not suffer from this issue as it maintains the original font and all it’s layout rules.
1.2.1.
1.4.1.
Reducing
the
Number
of
Network
Requests
As discussed in the previous section the most basic implementation of incremental font transfer will tend to increase the total number of requests made vs traditional font loading. Since each augmentation will require at least one round trip time, performance can be negatively impacted if too many requests are made. It’s possible for a client to preemptively request patches for codepoints that are not currently needed, but expected to be needed in the future. Intelligent use of this feature by an implementation can help reduce the total number of requests being made. The evaluation report explored this by testing the performance of a basic character frequency based codepoint prediction scheme and found it improved overall performance.
2. Opt-In Mechanism
Web
pages
can
choose
to
opt-in
to
incremental
transfer
for
a
font
via
the
use
of
a
CSS
font
tech
keyword
(
CSS
Fonts
4
§ 11.1
Font
tech
)
inside
the
''@font-face''
block.
The
keyword
incremental
is
used
to
indicate
the
referenced
font
contains
IFT
data
and
should
only
be
loaded
by
a
user
agent
which
supports
incremental
font
transfer.
@font-face’s
that
include
the
incremental
tech
keyword
should
also
include
a
unicode-range
descriptor
.
This
informs
the
client
which
codepoints
are
available
in
the
font
prior
to
making
the
first
request,
which
can
be
used
to
avoid
requesting
the
font
unnecessarily.
Note:
Each
individual
@font-face
block
may
or
may
not
opt-in
to
IFT.
This
is
due
to
the
variety
of
ways
fonts
are
used
on
web
pages.
Authors
have
control
over
which
fonts
they
want
to
use
this
technology
with,
and
which
they
do
not.
Note:
the
IFT
tech
keyword
can
be
used
in
conjunction
with
other
font
tech
specifiers
to
perform
font
feature
selection.
For
example
a
@font-face
could
include
two
URIs
one
with
tech(incremental,
color-COLRv1)
and
the
other
with
tech(incremental,
color-COLRv0)
.
2.1. Offline Usage
Special
consideration
must
be
taken
when
saving
a
page
for
offline
usage
that
uses
an
incrementally
transferred
font
since
the
saved
page
won’t
be
able
to
increment
the
font
if
content
changes
(eg.
due
to
JavaScript
execution).
In
these
cases
the
page
saving
mechanism
should
produce
fully
expand
the
full
incremental
font
by
using
§ 7
Extending
invoking
Fully
Expand
a
Font
Subset
and
replace
references
to
apply
all
available
patches
listed
in
the
font.
incremental
font
with
the
fully
expanded
one.
3. Definitions
3.1. Font Subset
A font subset is a modified version of a font file [iso14496-22] that contains only the data needed to render a subset of:
-
the codepoints,
-
and design-variation space .
supported
by
the
original
font.
When
a
subsetted
font
is
used
to
render
text
using
any
combination
of
the
subset
codepoints,
layout
features
,
or
design-variation
space
it
must
should
render
identically
to
the
original
font.
This
includes
rendering
with
the
use
of
any
optional
typographic
features
that
a
renderer
may
choose
to
use
from
the
original
font,
such
as
hinting
instructions.
A
font
subset
definition
describes
the
minimum
data
(codepoints,
layout
features,
variation
axis
space)
that
a
font
subset
must
should
possess.
Note: For convenience the remainder of this document links to the [open-type] specification which is a copy of [iso14496-22] .
4.
3.2.
Data
Types
Encoded data structures in the remainder of this specification are described in terms of the data types defined in OpenType Specification § otff#data-types . As with the rest of OpenType, all fields use Big Endian byte ordering.
4.1.
3.3.
URI
Templates
URI templates [rfc6570] are used to convert numeric indices into URIs where patch files are located. Given a numeric index value, several variables are defined which are used to produce the expansion of the template:
Variable | Value |
---|---|
id | The numeric index value converted to a string in hexadecimal representation (using the digits 0-9, A-F). No padding with 0’s is used. |
d1 | The first digit (least significant digit) of the numeric index in hexadecimal. |
d2 | The second digit of the numeric index in hexadecimal, or 0 if the index does not have a second digit |
d3 | The third digit of the numeric index in hexadecimal, or 0 if the index does not have a third digit |
d4 | The fourth digit of the numeric index in hexadecimal, or 0 if the index does not have a fourth digit |
Some example inputs and the corresponding expansions:
Template | Input Index | Expansion |
---|---|---|
//foo.bar/{id} | 123 | //foo.bar/7B |
//foo.bar{/d1,d2,id} | 123 | //foo.bar/B/7/7B |
//foo.bar{/d1,d2,d3,id} | 123 | //foo.bar/B/7/0/7B |
5.
4.
Extensions
to
the
Font
Format
An
incremental
font
follows
the
existing
OpenType
format,
but
include
two
new
tables
:
identified
by
the
4-byte
tags
'IFT
'
and
'IFTX'.
These
new
tables
are
both
patch
maps
,
which
encode
a
collection
of
mappings
from
font
subset
definitions
to
URIs
which
host
patches
that
extend
the
incremental
font.
The
mappings
from
the
two
tables
is
unioned
together
to
produce
the
final
mapping.
All
incremental
fonts
must
contain
at
least
an
'IFT
'
table.
The
'IFTX'
table
is
optional.
Note:
allowing
the
mapping
to
be
split
between
two
distinct
tables
allows
an
incremental
font
to
more
easily
make
use
of
multiple
patch
types.
For
example
all
patches
of
one
type
can
be
specified
in
the
'IFT
'
table,
and
all
patches
of
a
second
type
in
the
'IFTX'
table.
Then
those
Those
patches
can
make
updates
only
to
one
of
the
mapping
tables
and
avoid
making
conflicting
updates.
5.1.
4.1.
Patch
Map
Table
A
patch
map
table
encodes
a
list
of
patch
map
entries
,
where
each
entry
has
a
key
and
value.
The
key
is
a
font
subset
definition
and
the
value
is
a
URI
and
the
§ 6
§ 5
Font
Patch
Formats
used
by
the
data
at
the
URI.
A
map
is
encoded
in
one
of
two
formats:
-
Format 1: a limited, but more compact encoding. Encodes a one-to-one mapping from glyph id to patch URIs. Does not support font subset definitions with design space or entries with overlapping subset definitions.
-
Format 2: can encode arbitrary mappings including ones with design space or overlapping subset definitions. However, typically less compact
thenthan format 1.
5.1.1.
4.1.1.
Patch
Map
Table:
Format
1
Format 1 Patch Map encoding:
Type | Name | Description |
---|---|---|
uint8 | format | Set to 1, identifies this as format 1. |
uint32 | reserved | Not used, set to 0. |
uint32 | id [4] | Unique ID used to identify patches that are compatible with this font. |
uint32 | entryCount | Number of entries encoded in this table. |
uint32 | glyphCount | Number of glyphs that mappings are provided for. Must match the numGlyphs field from maxp . |
Offset32 | glyphMapOffset | Offset to a Glyph Map sub table. Offset is from the start of this table. |
Offset32 | featureMapOffset | Offset to a Feature Map sub table. Offset is from the start of this table. |
uint8 | appliedEntriesBitMap [(entryCount + 7)/8] |
A
bit
map
which
tracks
which
entries
have
been
applied.
If
bit
i
is
set
that
indicates
the
patch
for
entry
i
has
been
applied
to
this
font.
Bit
0
is
the
least
significant
bit
of
appliedEntriesBitMap[0],
while
bit
7
is
the
most
significant
bit.
Bit
8
is
the
least
significant
bit
of
appliedEntriesBitMap[1]
and
so
on.
|
uint16 | uriTemplateLength | Length of the uriTemplate string. |
uint8 | uriTemplate [uriTemplateLength] | A [UTF-8] encoded string. Contains a template (TODO) which is used to produce URIs associated with each entry. |
uint8 | patchEncoding |
Specifies
the
format
of
the
patches
linked
to
by
uriTemplate.
Using
the
ID
number
from
the
|
Glyph Map encoding:
A glyph map table associates each glyph index in the font with an entry index.
Type | Name | Description |
---|---|---|
uint16 | firstMappedGlyph | All glyph indices less than firstMappedGlyph are implicitly mapped to entry index 0. |
uint8/uint16 | entryIndex [ glyphCount - firstMappedGlyph] |
The
entry
index
for
glyph
i
is
stored
in
entryIndex[
i
-
firstMappedGlyph].
Array
members
are
uint8
if
entryCount
is
less
than
256,
otherwise
they
are
uint16.
|
Feature Map encoding:
A feature map table associates combinations of feature tags and glyphs with an entry index.
Type | Name | Description |
---|---|---|
uint16 | featureCount | Number of featureRecords. |
FeatureRecord | featureRecords [featureCount] | Provides mappings for a specific feature tag . Must be sorted by featureTag with any feature tag occurring at most once. |
EntryMapRecord | entryMapRecords [variable] | Provides the key (entry index) for each feature mapping. The entryMapRecords array contains as many entries as the sum of the entryMapCount fields in the featureRecords array, with entryMapRecords[0] corresponding to the first entry of featureRecords[0], entryMapRecords[featureRecord[0].entryMapCount] corresponding to the first entry of featureRecords[1], entryMapRecords[featureRecords[0].entryMapCount + featureRecord[1].entryMapCount]] corresponding to the first entry of featureRecords[2], and so on. |
FeatureRecord encoding:
Type | Name | Description |
---|---|---|
Tag | featureTag | The feature tag this mapping is for. |
uint8/uint16 | firstEntryIndex | uint8 if entryCount is less than 256, otherwise uint16. The first entry index this record maps too. |
uint8/uint16 | entryMapCount | uint8 if entryCount is less than 256, otherwise uint16. The number of EntryMapRecord s associated with this feature. |
EntryMapRecord encoding:
Type | Name | Description |
---|---|---|
uint8/uint16 | firstEntryIndex | uint8 if entryCount is less than 256, otherwise uint16. |
uint8/uint16 | lastEntryIndex | uint8 if entryCount is less than 256, otherwise uint16. Must be greater than or equal to firstEntryIndex. |
An entry map record matches any entry indices that are greater than or equal to firstEntryIndex and less than or equal to lastEntryIndex.
5.1.1.1.
4.1.1.1.
Interpreting
Format
1
This algorithm is used to convert a format 1 patch map into a list of patch map entries .
Interpret Format 1 Patch Map
The inputs to this algorithm are:
-
patch map : a
§ 5.1.1§ 4.1.1 Patch Map Table: Format 1 encoded patch map. -
font subset : the font subset which contains patch map .
The algorithm outputs:
-
entry list : a list of patch map entries .
The algorithm:
-
Check that the patch map is valid according to the requirements in
§ 5.1.1§ 4.1.1 Patch Map Table: Format 1 . If it is not return an error. -
For each unique entry index in Glyph Map :
-
a. If the bit for entry index in appliedEntriesBitMap is set to 1, skip this entry index .
-
b. Collect the set of glyph indices that map to the entry index .
-
c. Convert the set of glyph indices to a set of unicode code points using the code point to glyph mapping in the cmap table of font subset . Ignore any glyph indices that are not mapped by cmap .
-
d. Convert entry index into a URI by applying uriTemplate following
§ 4.1§ 3.3 URI Templates . -
e. Add an entry to entry list whose subset definition contains only the unicode code point set and maps to the generated URI and the patch encoding specified by patchEncoding .
-
-
For each FeatureRecord and associated EntryMapRecord in featureRecords :
-
For each entry index between firstEntryIndex (inclusive) and lastEntryIndex (inclusive), find the set of unicode codepoints associated with that entry index using the same process as in step 2b through 2c.
-
Compute mapped entry index = FeatureRecord::firstEntryIndex + entry index - EntryMapRecord::firstEntryIndex .
-
Convert mapped entry index into a URI by applying uriTemplate following
§ 4.1§ 3.3 URI Templates . -
If the bit for mapped entry index in appliedEntriesBitMap is set to 1, skip this entry index .
-
Add an entry to entry list whose subset definition contains the unicode code point set and a feature tag set containing only featureTag . The entry maps to the generated URI and the patch encoding specified by patchEncoding .
-
-
5.1.2.
4.1.2.
Patch
Map
Table:
Format
2
Coming soon.
6.
5.
Font
Patch
Formats
In incremental font transfer font subsets are extended by applying patches. Due to the different capabilities required in different situations this specification defines three different patch formats that can be used.
6.1.
5.1.
Definitions
A font patch is a file which encodes changes to be made to an existing font file. Patches are used to extend an existing font subset and provide expanded coverage.
A
patch
format
is
a
specified
encoding
used
to
encode
changes
to
be
applied
to
a
font
subset
into
a
font
patch
.
Every
patch
format
has
an
associated
patch
application
algorithm
which
takes
a
font
subset
and
a
font
patch
encoded
in
the
patch
format
as
input
and
outputs
a
new
extended
font
subset
.
Each
patch
application
algorithm
can
be
categorized
as
either
independent
or
dependent.
Given
a
set
of
font
patches
,
P
,
that
are
all
valid
to
be
applied
to
a
specific
font
subset
,
F
:
-
dependent patches are only valid to be applied to font subset
F
. Applying any patches fromP
to font subsetF
will change the subset and thus invalidate any remaining dependent patches inP
. -
independent patches are only valid to be applied to font subset
F
and any font subset derived from F by the application of one or more independent patches fromP
.
6.2.
5.2.
Formats
Summary
The following patch formats are defined by this specification:
Name | Type | ID | Description |
---|---|---|---|
| dependent | 1 | A shared brotli encoded binary diff that uses the entire font subset as a base. |
| dependent | 2 | A collection of shared brotli encoded binary diffs that use tables from the font subset as bases. |
| independent | 3 | Contains a collection of opaque binary blobs, each associated with a glyph id and table. |
More detailed descriptions of each algorithm can be found in the following sections.
6.3.
5.3.
Shared
Brotli
Patch
In a shared brotli patch the target file is encoded with brotli compression using the source file as a shared LZ77 dictionary . A shared brotli encoded patch consists of a short header followed by brotli encoded data.
Shared brotli patch encoding:
Type | Name | Description |
---|---|---|
Tag | format | Identifies the encoding as shared brotli, set to 'ifbr' |
uint32 | id [4] | The id of the font subset which this patch can be applied too. |
uint8 | brotliStream [variable] | Brotli encoded byte stream. |
6.3.1.
5.3.1.
Applying
Shared
Brotli
Patches
This algorithm is used to apply a shared brotli patch to extend a font subset to cover additional codepoints, features, and/or design-variation space.
Apply shared brotli patch
The inputs to this algorithm are:
-
base font subset : a font subset which is to be extended.
-
patch : a shared brotli patch to be applied to base font subset .
The algorithm outputs:
-
extended font subset : a font subset that has been extended by the patch .
The algorithm:
-
Check that the format field in patch is equal to 'ifbr', if it is not
equalequal, then patch is not correctly formatted. Patch application has failed, return an error. -
Check that the id field in patch is equal to the at least one of the ids found in the 'IFT ' or 'IFTX' table of base font subset . If there is no match, or base font subset does not have either an 'IFT ' or 'IFTX'
tabletable, then patch application has failed, return an error. -
Decode the brotli encoded data in brotliStream following Brotli Compressed Data Format § section-10 and using the base font subset as a shared LZ77 dictionary . Return the decoded result as the extended font subset
6.4.
5.4.
Per
Table
Shared
Brotli
A per table shared brotli patch contains a collection of patches which are applied to the individual font tables in the input font file. Each table patch is encoded with brotli compression using the corresponding table from the input font file as a shared LZ77 dictionary . A per table shared brotli encoded patch consists of a short header followed by one or more brotli encoded patches. In addition to patching tables, patches may also replace (existing table data is not used) or remove tables in a font subset .
Per table shared brotli patch encoding:
Type | Name | Description |
---|---|---|
Tag | format | Identifies the encoding as shared brotli, set to 'ifbt' |
uint32 | id [4] | The id of the font subset which this patch can be applied too. |
uint16 | patchesCount | The number of entries in the patches array. |
Offset32 | patches [patchesCount+1] | Each entry is an offset from the start of this table to a TablePatch . Offsets must be sorted in ascending order. |
The difference between two consecutive offsets in the patches array gives the size of that TablePatch .
TablePatch encoding:
Type | Name | Description |
---|---|---|
Tag | tag | The tag that identifies the font table this patch applies too. |
uint8 | flags | Bit-field. If bit 0 (least significant bit) is set this patch replaces the existing table. If bit 1 is set this table is removed. |
uint8 | brotliStream [variable] | Brotli encoded byte stream. |
6.4.1.
5.4.1.
Applying
Per
Table
Shared
Brotli
Patches
This algorithm is used to apply a per table shared brotli patch to extend a font subset to cover additional codepoints, features, and/or design-variation space.
Apply per table shared brotli patch
The inputs to this algorithm are:
-
base font subset : a font subset which is to be extended.
-
patch : a per table shared brotli patch to be applied to base font subset .
The algorithm outputs:
-
extended font subset : a font subset that has been extended by the patch .
The algorithm:
-
Check that the format field in patch is equal to 'ifbt', if it is not equal then patch is not correctly formatted. Patch application has failed, return an error.
-
Check that the id field in patch is equal to the at least one of the ids found in the 'IFT ' or 'IFTX' table of base font subset . If there is no match, or base font subset does not have either an 'IFT ' or 'IFTX'
tabletable, then patch application has failed, return an error. -
For each entry in patches , with index i :
-
Find the TablePatch associated with index i . The object starts at the offset patches[i] (inclusive) and ends at the offset patches[i+1] (exclusive). Both offsets are relative to the start of the patch .
-
If an entry in patches was previously applied that has the same tag as this entry, then ignore this entry and continue the iteration to the next one. Entries are processed in same order as they are listed in the patches array.
-
If bit 0 (least significant bit) of flags is
set then,set, then decode brotliStream following Brotli Compressed Data Format § section-10 . No shared dictionary is used. Add a table to extended font subset identified by tag with it’s contents set to the decoded brotliStream . -
If bit 1 of flags is
set then,set, then do not copy or add a table to extended font subset identified by tag . -
Otherwise, decode brotliStream following Brotli Compressed Data Format § section-10 and using the table identified by tag in base font subset as a shared LZ77 dictionary . Add a table to extended font subset identified by tag with it’s contents set to the decoded brotliStream .
-
-
For each table in base font subset which has a tag that was not found in any of the entries processed in step 3, add a copy of that table to extended font subset .
6.5.
5.5.
Glyph
Keyed
A glyph keyed patch contains a collection of data chunks that are each associated with a glyph index and a font table . The encoded data replaces any existing data for that glyph index in the referenced table . Glyph keyed patches can encode data for glyf / loca , gvar , CFF , and CFF2 tables.
Glyph keyed patch encoding:
Type | Name | Description |
---|---|---|
Tag | format | Identifies the encoding as glyph keyed, set to 'ifgk' |
uint32 | reserved | Reserved for future use, set to 0. |
uint32 | id [4] | The id of the font subset which this patch can be applied too. |
uint32 | length | The uncompressed length of brotliStream . |
uint8 | brotliStream [variable] | Brotli encoded GlyphPatches table. |
GlyphPatches encoding:
Type | Name | Description |
---|---|---|
uint32 | glyphCount | The number of glyphs encoded in the patch. |
uint8 | tableCount | The number of tables the patch has data for. |
uint16 | glyphIds [glyphCount] | An array of glyph indices included in the patch. |
Tag | tables [tableCount] | An array of tables (by tag) included in the patch. |
Offset32 | glyphDataOffsets [glyphCount * tableCount + 1] | An array of offsets of to glyph data for each table. The first glyphCount offsets corresponding to tables[0] , the next glyphCount offsets (if present) corresponding to tables[1] , and so on. All offsets are from the start of the GlyphPatches table. Offsets must be sorted in ascending order. |
uint8 | glyphData [variable] | The actual glyph data picked out by the offsets. |
The difference between two consecutive offsets in the glyphDataOffsets array gives the size of that glyph data.
6.5.1.
5.5.1.
Applying
Glyph
Keyed
Patches
This algorithm is used to apply a glyph keyed patch to extend a font subset to cover additional codepoints, features, and/or design-variation space.
Apply glyph keyed patch
The inputs to this algorithm are:
-
base font subset : a font subset which is to be extended.
-
patch : a glyph keyed patch to be applied to base font subset .
The algorithm outputs:
-
extended font subset : a font subset that has been extended by the patch .
The algorithm:
-
Check that the format field in patch is equal to 'ifgk', if it is not
equalequal, then patch is not correctlyformatted. Patchformatted and patch application has failed, return an error. -
Check that the id field in patch is equal to the at least one of the ids found in the 'IFT ' or 'IFTX' table of base font subset . If there is no match, or base font subset does not have either an 'IFT ' or 'IFTX'
tabletable, then patch application has failed, return an error. -
Decode the brotli encoded data in brotliStream following Brotli Compressed Data Format § section-10 . The decoded data is a GlyphPatches table.
-
For each font table listed in tables , with index
i
:-
Using the corresponding table in base font subset , synthesize a new table where the data for each glyph is replaced with the data corresponding to that glyph index from glyphData if present, otherwise copied from the corresponding table in base font subset for that glyph index.
-
The patch glyph data for a glyph index is located by finding glyphIds[j] equal to the glyph index. The offset to the associated glyph data is glyphDataOffsets[i * glyphCount + j] . The length of the associated glyph data is glyphDataOffsets[i * glyphCount + j + 1] minus glyphDataOffsets[i * glyphCount + j] .
-
The specific process for synthesizing the new table, depends on the specified format of the table . Any non-glyph associated data should be copied from the table in base font subset . Tables of the type glyf / loca , gvar , CFF , or CFF2 are supported. Entries for tables of any other types must be ignored.
-
If base font subset does not have a matching table, return an error.
-
Insert the synthesized table into extended font subset .
-
-
For each table in base font subset which has a tag that was not found in any of the entries processed in step 4, add a copy of that table to extended font subset .
-
Modify the mappings in the IFT and IFTX table(s) in extended font subset if present to remove any mapping entries that map to this patch.
7.
6.
Extending
a
Font
Subset
This sections defines the algorithm that a client uses to extend an incremental font subset to cover additional codepoints, layout features and/or design space.
Extend a Font Subset
The inputs to this algorithm are:
-
font subset : an incremental font subset .
-
target subset definition : the font subset definition that the client wants to extend font subset to cover.
The algorithm outputs:
-
extended font subset : an extended version of font subset . May or may not be an incremental font .
The algorithm:
-
Set extended font subset to font subset .
-
Load the 'IFT ' and 'IFTX' (if present) mapping tables from extended font subset . Both tables are formatted as a
§ 5.1§ 4.1 Patch Map Table . Check that they are valid according to the requirements in§ 5.1§ 4.1 Patch Map Table . If either table is not valid, invoke Handle errors . If extended font subset does not have an 'IFT ' table, then it is not an incremental font and cannot be extended, return extended font subset . -
For each of tables 'IFT ' and 'IFTX' (if present): convert the table into a list of entries by invoking Interpret Format 1 Patch Map . Concatenate the output entry lists into a single list, entry list .
-
For each entry in entry list invoke Check entry intersection with entry and target subset definition as inputs, if it returns false remove entry from entry list .
-
If entry list is
emptyempty, then the extension operation is finished, return extended font subset . -
If entry list contains one or more patch map entries which have a patch format that is dependent
. Select, then select exactly one of the dependent entries in entry list and remove all others. The criteria for selecting the single dependent entry is left up to the implementation to decide. -
For each entry in entry list invoke Load patch file with the entries URI. Collect the returned patch files into patch list .
-
For each patch in patch list use the appropriate application algorithm (matching the patches format) from
§ 6§ 5 Font Patch Formats to apply the patch to extended font subset . The order of patch application is left up to the implementation. -
Go to step 2.
Check entry intersection
The inputs to this algorithm are:
-
mapping entry : a patch map entry .
-
subset definition : a font subset definition .
The algorithm outputs:
-
intersects : true if subset definition intersects mapping entry , otherwise false.
The algorithm:
-
For each set in subset definition (codepoints, feature tags, design space) check if the set intersects the corresponding set from mapping entry . A set intersects when:
subset definition set is empty subset definition set is not empty mapping entry set is empty true true mapping entry set is not empty false true if the two sets intersect -
If all sets checked in step 1
intersectintersect, then return true for intersects otherwise false.
Load patch file
The inputs to this algorithm are:
-
URI : a URI identifying the patch file to load.
The algorithm outputs:
-
patch file : the patch file identified by URI .
The algorithm:
-
TODO Coming soon.
Handle errors
Coming soon.
6.1. Fully Expanding a Font
This sections defines an algorithm that can be used to transform an incremental font into a fully expanded non-incremental font. This process loads all available data provided by the incremental font and produces a single static font file that contains no further patches to be applied.
Fully Expand a Font Subset
The inputs to this algorithm are:
font subset : an incremental font subset .
The algorithm outputs:
expanded font : an [open-type] font that is not incremental.
The algorithm:
Invoke Extend a Font Subset with font subset . The input target subset definition is a special one which is considered to intersect all entries in the Check entry intersection step. Return the resulting font subset as the expanded font .
7. Encoder
An encoder is a tool which produces an incremental font and set of associated patches . The incremental font and associated patches produced by a compliant encoder:
Must meet all of the requirements in § 4 Extensions to the Font Format and § 5 Font Patch Formats .
Must be consistent, that is: for any possible font subset definition the result of invoking Extend a Font Subset with that subset definition and the incremental font must always be the same regardless of the particular order of dependent patch selection chosen in step 6 of Extend a Font Subset .
Should preserve the functionality of the fully expanded font, that is: given the fully expanded font derived from the incremental font and any content, then the font subset produced by invoking Extend a Font Subset with the incremental font and the minimal subset definition covering that content should render identically to the fully expanded font for that content.
When an encoder is used to transform an existing font into an incremental font the associated fully expanded font should be equal to the existing font.
When an encoder is used to transform an existing font file into and incremental font and a client is implemented according to the other sections of this document, the intent of the IFT specification is that appearance and behavior of the font in the client will be the same as if the entire file were transferred to the client. A primary goal of the IFT specification is that the IFT format and protocol can serve as a neutral medium for font transfer, comparable to WOFF2. If an encoder produces an encoding from a source font which meets all of the above requirements (1. through 4.), then the encoding will preserve all of the functionality of the original font.
This may be important for cases where a foundry or other rights-owner of a font wants be confident that the encoding and transfer of that font using IFT will not change its behavior and therefore the intent of the font’s creators. Licenses or contracts might then include requirements about IFT conformance, and situations in which re-encoding a font in WOFF2 format is de facto permissible due to its content-neutrality might also permit IFT encoding of that font.
However, nothing about these requirements on encoding conformance is meant to rule out or deprecate the possibility and practical use of encodings that do not preserve all of the functionality of a source font. Any encoding meeting the minimum requirements (1. and 2. above) is valid and may have an appropriate use. Under some circumstances it might be desirable for an encoded font to omit support for some functionality/data from all of its patch files even if those were included in the original font file. In other cases a font might be directly encoded into the IFT format from font authoring source files.
7.1. Encoder Considerations
This section is not normative.
The details of the encoding process may differ by encoder and are beyond the scope of this document. However, this section provides guidance that encoder implementations may want to consider.
Utilize an existing font subsetter implementation
Choosing segmentations
One of the most important and complex decisions an encoder needs to make is how to segment the data in the encoded font. To maximize efficiency the encoder should try and group data (eg. codepoints) that are commonly used together into the same segments. This will reduce the amount of unneeded data load by clients when extending the font. The encoder must also decide the size of segments. Smaller segments will produce more patches, and thus incur more overhead by requiring more network requests, but will typically result in less unneeded data in each segment compared to larger segments. When segmenting codepoints, data on codepoint usage frequency can be helpful to guide segmentation.
During segmentation an encoder should also consider how codepoints interact with each other in the font. Keeping interacting codepoints in the same segment can avoid having to duplicate data between patches, which may be necessary to preserve the functionality of the original font when interacting codepoints reside in different segments.
Dependent patches can form patch graphs
Choosing patch formats
An encoder must choose the appropriate patch type to use. Dependent patches allow all types of data in the font to be patched, while independent patches are limited to updating outline and variation delta data. However, using dependent patches can cause the number of patches to increase exponentially as the number of segments increases. Independent patches on the other hand scale linearly with the number of segments.
Dependent patches are most useful when a font has sizable non-outline data and requires only a small number of segments. Independent patches are most useful when a fonts data is primarily in the outline tables and the font would benefit from using a large segment count.
Using per-table brotli patches and two mapping tables it becomes possible to mix both indepedent and dependent patching in the same incremental font. This can be accomplished by:
Keep all dependent patch entries in one mapping table and all independent entries in the other mapping table.
Use per-table shared brotli patches to update all tables except for the tables touched by the independent patches (outline, variation deltas, and the independent patch mapping table). These patches should use a small number of large segments to keep the patch count reasonable.
Lastly, use independent patches to update the remaining tables, here much smaller fine-grained segments can be utilized without required too many patches.
Managing the number of patches
Privacy Considerations
Coming soon.
Security Considerations
Coming soon.
Changes
Since the Working Draft of 30 May 2023 (see commit history ):
- Complete rewrite of the specification. Separate 'Patch Subset' and 'Range Request' methods have been removed in favour of a single unified approach.