[designspaceLib] Update documentation and add version 5

This commit is contained in:
Jany Belluz 2022-04-14 15:05:20 +01:00
parent a7974986c3
commit 5a842cc249
11 changed files with 1908 additions and 1170 deletions

View File

@ -1,6 +1,6 @@
# fontTools Documentation
The fontTools project documentation updates continuously on Read the Docs as the project source changes.
The fontTools project documentation updates continuously on Read the Docs as the project source changes.
The documentation is hosted at https://fonttools.readthedocs.io/.
@ -19,7 +19,7 @@ You must have a Python 3 interpreter and the `pip` Python package manager instal
Pull the fontTools project source files, create a Python virtual environment, and then install fontTools and the documentation build dependencies by executing the following commands in the root of the fontTools source repository:
```
$ pip install -e . [all]
$ pip install -e .[all]
$ pip install -r Doc/docs-requirements.txt
```
@ -112,7 +112,7 @@ Build a local set of HTML documentation files with the instructions above and re
### Submit a Pull Request
Submit a Github pull request with your proposed improvements to the documentation.
Submit a Github pull request with your proposed improvements to the documentation.
Thanks for your contribution!

View File

@ -1,18 +1,217 @@
##############
designspaceLib
##############
#######################################################
designspaceLib: Read, write, and edit designspace files
#######################################################
MutatorMath started out with its own reader and writer for designspaces.
Since then the use of designspace has broadened and it would be useful
to have a reader and writer that are independent of a specific system.
Implements support for reading and manipulating ``designspace`` files.
Allows the users to define axes, rules, sources, variable fonts and instances,
and their STAT information.
.. toctree::
:maxdepth: 1
readme
python
xml
scripting
.. automodule:: fontTools.designspaceLib
:inherited-members:
:members:
:undoc-members:
Notes
=====
Paths and filenames
-------------------
A designspace file needs to store many references to UFO files.
- designspace files can be part of versioning systems and appear on
different computers. This means it is not possible to store absolute
paths.
- So, all paths are relative to the designspace document path.
- Using relative paths allows designspace files and UFO files to be
**near** each other, and that they can be **found** without enforcing
one particular structure.
- The **filename** attribute in the ``SourceDescriptor`` and
``InstanceDescriptor`` classes stores the preferred relative path.
- The **path** attribute in these objects stores the absolute path. It
is calculated from the document path and the relative path in the
filename attribute when the object is created.
- Only the **filename** attribute is written to file.
- Both **filename** and **path** must use forward slashes (``/``) as
path separators, even on Windows.
Right before we save we need to identify and respond to the following
situations:
In each descriptor, we have to do the right thing for the filename
attribute. Before writing to file, the ``documentObject.updatePaths()``
method prepares the paths as follows:
**Case 1**
::
descriptor.filename == None
descriptor.path == None
**Action**
- write as is, descriptors will not have a filename attr. Useless, but
no reason to interfere.
**Case 2**
::
descriptor.filename == "../something"
descriptor.path == None
**Action**
- write as is. The filename attr should not be touched.
**Case 3**
::
descriptor.filename == None
descriptor.path == "~/absolute/path/there"
**Action**
- calculate the relative path for filename. We're not overwriting some
other value for filename, it should be fine.
**Case 4**
::
descriptor.filename == '../somewhere'
descriptor.path == "~/absolute/path/there"
**Action**
- There is a conflict between the given filename, and the path. The
difference could have happened for any number of reasons. Assuming
the values were not in conflict when the object was created, either
could have changed. We can't guess.
- Assume the path attribute is more up to date. Calculate a new value
for filename based on the path and the document path.
Recommendation for editors
--------------------------
- If you want to explicitly set the **filename** attribute, leave the
path attribute empty.
- If you want to explicitly set the **path** attribute, leave the
filename attribute empty. It will be recalculated.
- Use ``documentObject.updateFilenameFromPath()`` to explicitly set the
**filename** attributes for all instance and source descriptors.
Common Lib Key Registry
=======================
public.skipExportGlyphs
-----------------------
This lib key works the same as the UFO lib key with the same name. The
difference is that applications using a Designspace as the corner stone of the
font compilation process should use the lib key in that Designspace instead of
any of the UFOs. If the lib key is empty or not present in the Designspace, all
glyphs should be exported, regardless of what the same lib key in any of the
UFOs says.
Implementation and differences
==============================
The designspace format has gone through considerable development.
- the format was originally written for MutatorMath.
- the format is now also used in fontTools.varlib.
- not all values are be required by all implementations.
Varlib vs. MutatorMath
----------------------
There are some differences between the way MutatorMath and fontTools.varlib handle designspaces.
- Varlib does not support anisotropic interpolations.
- MutatorMath will extrapolate over the boundaries of
the axes. Varlib can not (at the moment).
- Varlib requires much less data to define an instance than
MutatorMath.
- The goals of Varlib and MutatorMath are different, so not all
attributes are always needed.
Rules and generating static UFO instances
-----------------------------------------
When making instances as UFOs from a designspace with rules, it can
be useful to evaluate the rules so that the characterset of the UFO
reflects, as much as possible, the state of a variable font when seen
at the same location. This can be done by some swapping and renaming of
glyphs.
While useful for proofing or development work, it should be noted that
swapping and renaming leaves the UFOs with glyphnames that are no longer
descriptive. For instance, after a swap ``dollar.bar`` could contain a shape
without a bar. Also, when the swapped glyphs are part of other GSUB variations
it can become complex very quickly. So proceed with caution.
- Assuming ``rulesProcessingLast = True``:
- We need to swap the glyphs so that the original shape is still available.
For instance, if a rule swaps ``a`` for ``a.alt``, a glyph
that references ``a`` in a component would then show the new ``a.alt``.
- But that can lead to unexpected results, the two glyphs may have different
widths or height. So, glyphs that are not specifically referenced in a rule
**should not change appearance**. That means that the implementation that swaps
``a`` and ``a.alt`` also swap all components that reference these
glyphs in order to preserve their appearance.
- The swap function also needs to take care of swapping the names in
kerning data and any GPOS code.
Version history
===============
Version 5.0
-----------
The format was extended to describe the entire design space of a reasonably
regular font family in one file, with global data about the family to reduce
repetition in sub-sections. "Reasonably regular" means that the sources and
instances across the previously multiple Designspace files are positioned on a
grid and derive their metadata (like style name) in a way that's compatible with
the STAT model, based on their axis positions. Axis mappings must be the same
across the entire space.
1. Each axis can have labels attached to stops within the axis range, analogous to the
`OpenType STAT <https://docs.microsoft.com/en-us/typography/opentype/spec/stat>`_
table. Free-standing labels for locations are also allowed. The data is intended
to be compiled into a ``STAT`` table.
2. The axes can be discrete, to say that they do not interpolate, like a distinctly
constructed upright and italic variant of a family.
3. The data can be used to derive style and PostScript names for instances.
4. A new ``variable-fonts`` element can subdivide the Designspace into multiple subsets that
mix and match the globally available axes. It is possible for these sub-spaces to have
a different default location from the global default location. It is required if the
Designspace contains a discrete axis and you want to produce a variable font.
What is currently not supported is e.g.
1. A setup where different sources sit at the same logical location in the design space,
think "MyFont Regular" and "MyFont SmallCaps Regular". (this situation could be
encoded by adding a "SmallCaps" discrete axis, if that makes sense).
2. Anisotropic locations for axis labels.
Older versions
--------------
- In some implementations that preceed Variable Fonts, the `copyInfo`
flag in a source indicated the source was to be treated as the default.
This is no longer compatible with the assumption that the default font
is located on the default value of each axis.
- Older implementations did not require axis records to be present in
the designspace file. The axis extremes for instance were generated
from the locations used in the sources. This is no longer possible.

View File

@ -0,0 +1,199 @@
################################
1 DesignSpaceDocument Python API
################################
An object to read, write and edit interpolation systems for typefaces.
Define sources, axes, rules, variable fonts and instances.
Get an overview of the available classes in the Class Diagram below:
.. figure:: v5_class_diagram.png
:width: 650px
:alt: UML class diagram of designspaceLib
UML class diagram of designspaceLib. Click to enlarge.
.. contents:: Table of contents
:local:
.. _designspacedocument-object:
===================
DesignSpaceDocument
===================
.. autoclass:: fontTools.designspaceLib::DesignSpaceDocument
:members:
:undoc-members:
:member-order: bysource
.. _axis-descriptor-object:
AxisDescriptor
==============
.. autoclass:: fontTools.designspaceLib::AxisDescriptor
:members:
:undoc-members:
:inherited-members: SimpleDescriptor
:member-order: bysource
DiscreteAxisDescriptor
======================
.. autoclass:: fontTools.designspaceLib::DiscreteAxisDescriptor
:members:
:undoc-members:
:inherited-members: SimpleDescriptor
:member-order: bysource
AxisLabelDescriptor
-------------------
.. autoclass:: fontTools.designspaceLib::AxisLabelDescriptor
:members:
:undoc-members:
:member-order: bysource
LocationLabelDescriptor
=======================
.. autoclass:: fontTools.designspaceLib::LocationLabelDescriptor
:members:
:undoc-members:
:member-order: bysource
RuleDescriptor
==============
.. autoclass:: fontTools.designspaceLib::RuleDescriptor
:members:
:undoc-members:
:member-order: bysource
Evaluating rules
----------------
.. autofunction:: fontTools.designspaceLib::evaluateRule
.. autofunction:: fontTools.designspaceLib::evaluateConditions
.. autofunction:: fontTools.designspaceLib::processRules
.. _source-descriptor-object:
SourceDescriptor
================
.. autoclass:: fontTools.designspaceLib::SourceDescriptor
:members:
:undoc-members:
:member-order: bysource
VariableFontDescriptor
======================
.. autoclass:: fontTools.designspaceLib::VariableFontDescriptor
:members:
:undoc-members:
:member-order: bysource
RangeAxisSubsetDescriptor
-------------------------
.. autoclass:: fontTools.designspaceLib::RangeAxisSubsetDescriptor
:members:
:undoc-members:
:member-order: bysource
ValueAxisSubsetDescriptor
-------------------------
.. autoclass:: fontTools.designspaceLib::ValueAxisSubsetDescriptor
:members:
:undoc-members:
:member-order: bysource
.. _instance-descriptor-object:
InstanceDescriptor
==================
.. autoclass:: fontTools.designspaceLib::InstanceDescriptor
:members:
:undoc-members:
:member-order: bysource
.. _subclassing-descriptors:
=======================
Subclassing descriptors
=======================
The DesignSpaceDocument can take subclassed Reader and Writer objects.
This allows you to work with your own descriptors. You could subclass
the descriptors. But as long as they have the basic attributes the
descriptor does not need to be a subclass.
.. code:: python
class MyDocReader(BaseDocReader):
axisDescriptorClass = MyAxisDescriptor
discreteAxisDescriptorClass = MyDiscreteAxisDescriptor
axisLabelDescriptorClass = MyAxisLabelDescriptor
locationLabelDescriptorClass = MyLocationLabelDescriptor
ruleDescriptorClass = MyRuleDescriptor
sourceDescriptorClass = MySourceDescriptor
variableFontsDescriptorClass = MyVariableFontDescriptor
valueAxisSubsetDescriptorClass = MyValueAxisSubsetDescriptor
rangeAxisSubsetDescriptorClass = MyRangeAxisSubsetDescriptor
instanceDescriptorClass = MyInstanceDescriptor
class MyDocWriter(BaseDocWriter):
axisDescriptorClass = MyAxisDescriptor
discreteAxisDescriptorClass = MyDiscreteAxisDescriptor
axisLabelDescriptorClass = MyAxisLabelDescriptor
locationLabelDescriptorClass = MyLocationLabelDescriptor
ruleDescriptorClass = MyRuleDescriptor
sourceDescriptorClass = MySourceDescriptor
variableFontsDescriptorClass = MyVariableFontDescriptor
valueAxisSubsetDescriptorClass = MyValueAxisSubsetDescriptor
rangeAxisSubsetDescriptorClass = MyRangeAxisSubsetDescriptor
instanceDescriptorClass = MyInstanceDescriptor
myDoc = DesignSpaceDocument(MyDocReader, MyDocWriter)
==============
Helper modules
==============
fontTools.designspaceLib.split
==============================
See :ref:`Scripting > Working with DesignSpace version 5 <working_with_v5>`
for more information.
.. automodule:: fontTools.designspaceLib.split
fontTools.designspaceLib.stat
=============================
.. automodule:: fontTools.designspaceLib.stat
fontTools.designspaceLib.statNames
==================================
.. automodule:: fontTools.designspaceLib.statNames

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
#######################
Scripting a designspace
#######################
#########################
3 Scripting a designspace
#########################
It can be useful to build a designspace with a script rather than
construct one with an interface like
@ -60,7 +60,7 @@ Make a descriptor object and add it to the document.
Variation Axis
Tags <https://www.microsoft.com/typography/otspec/fvar.htm#VAT>`__
- The default master is expected at the intersection of all
default values of all axes.
default values of all axes.
Option: add label names
-----------------------
@ -88,6 +88,7 @@ OpenType <https://www.microsoft.com/typography/otspec/avar.htm>`__.
.. code:: python
# (user space, design space), (user space, design space)...
a1.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
Make a source object
@ -124,7 +125,7 @@ So go ahead and add another master:
s1.name = "master.bold"
s1.location = dict(weight=1000)
doc.addSource(s1)
Option: exclude glyphs
----------------------
@ -241,7 +242,7 @@ This is how you check the default font.
Generating?
***********
You can generate the UFO's with MutatorMath:
You can generate the UFOs with MutatorMath:
.. code:: python
@ -251,3 +252,39 @@ You can generate the UFO's with MutatorMath:
- Assuming the outline data in the masters is compatible.
Or you can use the file in making a **variable font** with varlib.
.. _working_with_v5:
**********************************
Working with DesignSpace version 5
**********************************
The new version 5 allows "discrete" axes, which do not interpolate across their
values. This is useful to store in one place family-wide data such as the STAT
information, however it prevents the usual things done on designspaces that
interpolate everywhere:
- checking that all sources are compatible for interpolation
- building variable fonts
In order to allow the above in tools that want to handle designspace v5,
the :mod:`fontTools.designspaceLib.split` module provides two methods to
split a designspace into interpolable sub-spaces,
:func:`splitInterpolable() <fontTools.designspaceLib.split.splitInterpolable>`
and then
:func:`splitVariableFonts() <fontTools.designspaceLib.split.splitVariableFonts>`.
.. figure:: v5_split_downconvert.png
:width: 680px
:alt: Example process diagram to check and build DesignSpace 5
Example process process to check and build Designspace 5.
Also, for older tools that don't know about the other version 5 additions such
as the STAT data fields, the function
:func:`convert5to4() <fontTools.designspaceLib.split.convert5to4>` allows to
strip new information from a designspace version 5 to downgrade it to a
collection of version 4 documents, one per variable font.

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

View File

@ -0,0 +1,292 @@
@startuml v5_class_diagram
title
Designspace v5 Class Diagram
<size:12>Note: the ""Descriptor"" suffix is omitted from most classes
end title
' left to right direction
skinparam class {
BackgroundColor<<New>> PaleGreen
}
class DesignSpaceDocument {
+ formatVersion: str = None
+ <color:green><b><<New>> elidedFallbackName: str = None
+ rulesProcessingLast: bool = False
+ lib: Dict = {}
}
note left of DesignSpaceDocument::elidedFallbackName
STAT Style Attributes Header field ""elidedFallbackNameID""
end note
abstract class AbstractAxis {
+ tag: str
+ name: str
+ labelNames: Dict[str, str]
+ hidden: bool
+ map: List[Tuple[float, float]]
+ <color:green><b><<New>> axisOrdering: int
}
DesignSpaceDocument *-- "*" AbstractAxis: axes >
note right of AbstractAxis::axisOrdering
STAT Axis Record field
end note
class Axis {
+ minimum: float
+ maximum: float
+ default: float
}
AbstractAxis <|--- Axis
note bottom of Axis
This is the usual
Axis, with a range
of values.
end note
class DiscreteAxis <<New>> {
+ values: List[float]
+ default: float
}
AbstractAxis <|--- DiscreteAxis
note bottom of DiscreteAxis
A discrete axis is not
interpolable, e.g.
Uprights vs Italics, and
so has "discrete" stops
instead of a continuous
range of values.
end note
Axis .[hidden] DiscreteAxis
class AxisLabel <<New>> {
+ userMinimum: Optional[float]
+ userValue: float
+ userMaximum: Optional[float]
+ name: str
+ elidable: bool
+ olderSibling: bool
+ linkedUserValue: Optional[float]
+ labelNames: Dict[str, str]
+ getFormat(): 1 | 2 | 3
}
note right of AxisLabel
Label for a
stop on an Axis
(STAT format
1,2,3)
end note
AbstractAxis *-- "*" AxisLabel: <<New>> \n axisLabels >
class LocationLabel <<New>> {
+ name: str
+ location: Dict[str, float]
+ elidable: bool
+ olderSibling: bool
+ labelNames: Dict[str, str]
}
note right of LocationLabel
Label for a
freestanding
location
(STAT format 4)
end note
DesignSpaceDocument *--- "*" LocationLabel: <<New>> \n locationLabels >
class Rule {
+ name: str
+ conditionSets: List[ConditionSet]
+ subs: Dict[str, str]
}
DesignSpaceDocument *- "*" Rule: rules >
class Source {
+ name: Optional[str]
+ filename: str
+ path: str
+ layerName: Optional[str]
+ <color:brown><s><<Deprecated>> location: Location
+ <color:green><b><<New>> designLocation: AnisotropicLocation
....
+ font: Optional[Font]
....
+ familyName: Optional[str]
+ styleName: Optional[str]
+ <color:green><b><<New>> localisedFamilyName: Dict
....
+ <color:brown><s><<Deprecated>> copyLib: bool
+ <color:brown><s><<Deprecated>> copyInfo: bool
+ <color:brown><s><<Deprecated>> copyGroups: bool
+ <color:brown><s><<Deprecated>> copyFeatures: bool
....
+ muteKerning: bool
+ muteInfo: bool
+ mutedGlyphNames: List[str]
----
+ <color:green><b><<New>> getFullDesignLocation(doc)
}
DesignSpaceDocument *-- "*" Source: sources >
note right of Source::localisedFamilyName
New field to allow generation
of localised instance names using
STAT information.
end note
note right of Source::copyGroups
These fields are already not meaningful
anymore in version 4 (the default source
will be used as "neutral" for groups, info
and features. ''copyLib'' can be emulated
by putting content in the designspace's lib.
end note
note as NLocSource
The location of
sources can still only
be defined in design
coordinates, and now
also by relying on
axis defaults.
To build the final,
"full" location, a
helper method is
provided, that uses
axis defaults and
axis mappings to
fill in the blanks.
end note
NLocSource . Source::designLocation
NLocSource . Source::getFullDesignLocation
class VariableFont <<New>> {
+ filename: str
+ lib: Dict
}
DesignSpaceDocument *--- "*" VariableFont: <<New>> \n variableFonts >
note right of VariableFont
A variable font is a
subset of the designspace
where everything interpolates
(and so can be compiled into
an OpenType variable font).
end note
abstract class AbstractAxisSubset <<New>> {
+ name: str
}
VariableFont *-- "*" AbstractAxisSubset: <<New>> \n axisSubsets >
note right of AbstractAxisSubset
An axis subset selects a range
or a spot on each the available
axes from the whole designspace.
By default, only the default value
of each axis is used to define the
variable font.
Continuous axes can be specified
to include their full range instead;
or a subset of the range.
Discrete axes can be specified
to include a different spot than the
default.
end note
class RangeAxisSubset <<New>> {
+ userMinimum: float
+ userDefault: float
+ userMaximum: float
}
AbstractAxisSubset <|-- RangeAxisSubset
class ValueAxisSubset <<New>> {
+ userValue: float
}
AbstractAxisSubset <|-- ValueAxisSubset
class Instance {
+ filename: str
+ path: str
+ <color:brown><s><<Deprecated>> location: Location
+ <color:green><b><<New>> locationLabel: str
+ <color:green><b><<New>> designLocation: AnisotropicLocation
+ <color:green><b><<New>> userLocation: SimpleLocation
....
+ font: Optional[Font]
....
+ <color:orange><b><<Changed>> name: Optional[str]
+ <color:orange><b><<Changed>> familyName: Optional[str]
+ <color:orange><b><<Changed>> styleName: Optional[str]
+ <color:orange><b><<Changed>> postScriptFontName: Optional[str]
+ <color:orange><b><<Changed>> styleMapFamilyName: Optional[str]
+ <color:orange><b><<Changed>> styleMapStyleName: Optional[str]
+ localisedFamilyName: Dict
+ localisedStyleName: Dict
+ localisedStyleMapFamilyName: Dict
+ localisedStyleMapStyleName: Dict
....
+ <color:brown><s><<Deprecated>> glyphs: Dict
+ <color:brown><s><<Deprecated>> kerning: bool
+ <color:brown><s><<Deprecated>> info: bool
....
+ lib: Dict
----
+ <color:green><b><<New>> clearLocation()
+ <color:green><b><<New>> getLocationLabelDescriptor(doc)
+ <color:green><b><<New>> getFullDesignLocation(doc)
+ <color:green><b><<New>> getFullUserLocation(doc)
}
DesignSpaceDocument *-- "*" Instance: instances >
note right of Instance::locationLabel
The location can now alternatively
be a string = name of a LocationLabel
(STAT format 4). The instance then
adopts the location of that label.
See the Decovar example file.
end note
note right of Instance::styleMapStyleName
All the name field are now optional.
Their default values will be computed
using the provided STAT table data
and the STAT rules from the spec.
For styleMap{Family,Style}Name, they
would be computed using the STAT
""linkedUserValue"" fields.
end note
note right of Instance::glyphs
This attribute has been replaced by rules
end note
note right of Instance::kerning
All instances get kerning and info
nowadays.
end note
note as NLocInstance
The location of instances
can now be defined using
either:
- a STAT LocationLabel
- design coordinates
- user coordinates.
- relying on axis defaults
To build the final, "full"
location, a few helper
methods are provided,
that aggregate data from
these sources and apply
the axis mappings.
end note
NLocInstance . Instance::designLocation
NLocInstance . Instance::getFullDesignLocation
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -0,0 +1,147 @@
@startuml v5_split_downconvert
title
How to split and down-convert a DesignSpace version 5
<size:14>so that existing tools can work with it
end title
start
#lightgrey:DesignSpace 5.0
- with STAT data
- optional instance names
- with discrete axes
- with multiple VFs]
note left
- STAT data means that compilers will need
to write that data to the ouput TTFs
- optional instance names means that
compilers will need to generate any missing
names using the STAT data
- discrete axes mean that not all sources
are compatible for interpolation
- multiple VFs means that compilers will
need to output several TTFs for one DS
end note
split
split again
:""splitInterpolating()"";
note left
- Create one DS document per interpolating
sub-space, for example: with a discrete
axis for Upright vs Italics, create 2
DesignSpaces, one for the Uprights, and
one for the Italics.
- Expand all missing instance names using
the STAT data.
- Drop all the STAT data because as we start
taking out discrete axes, the sub-documents
lose STAT data anyway (only the full
document at the start of the process should
be used to generate the STAT table)
end note
split
#lightgrey:DesignSpace 5.0
- (no STAT data)
- (explicit instance names)
- (at discrete location #1)
- with multiple VFs]
note left
All sources in this sub-space are
(supposed to be) compatible for
interpolation
end note
split again
#lightgrey:DesignSpace 5.0
- (no STAT data)
- (explicit instance names)
- (at discrete location #2)
- with multiple VFs]
detach
split again
#lightgrey:etc
...]
detach
end split
:check compatibility;
:build compatible master TTFs with cu2qu;
:""splitVariableFonts()"";
note left
Create one DS document per variable font,
for example: the above document may
describe a weight and width space, out of
which we'll build 3 variable fonts:
full weight + width, weight only, width only.
end note
split
#lightgrey:DesignSpace 5.0
- (no STAT data)
- (explicit instance names)
- (at discrete location #1)
- (describing just VF #1)]
note left
This document looks very much
like version 4.1, you can just
change the version number at
the top and feed it to a tool
that doesn't know about v5;
see ""convert5to4()"".
end note
split again
#lightgrey:DesignSpace 5.0
- (no STAT data)
- (explicit instance names)
- (at discrete location #1)
- (describing just VF #2)]
detach
split again
#lightgrey:etc
...]
detach
end split
:""varLib.build()"";
#lightgrey:Variable font TTF
No STAT data]
end split
:""buildVFStatTable()"";
note left
Apply STAT data using the full document
from the start of the process + filtering
entries based on this VF's location
end note
#lightgrey:Variable font TTF
With STAT data]
end
@enduml

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

File diff suppressed because it is too large Load Diff