diff --git a/Doc/README.md b/Doc/README.md index 26641b722..6927bd62f 100644 --- a/Doc/README.md +++ b/Doc/README.md @@ -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! diff --git a/Doc/source/designspaceLib/index.rst b/Doc/source/designspaceLib/index.rst index 2c33df7bc..5d17dc16b 100644 --- a/Doc/source/designspaceLib/index.rst +++ b/Doc/source/designspaceLib/index.rst @@ -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 `_ + 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. + diff --git a/Doc/source/designspaceLib/python.rst b/Doc/source/designspaceLib/python.rst new file mode 100644 index 000000000..6a43bdcc1 --- /dev/null +++ b/Doc/source/designspaceLib/python.rst @@ -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 ` +for more information. + +.. automodule:: fontTools.designspaceLib.split + + +fontTools.designspaceLib.stat +============================= + +.. automodule:: fontTools.designspaceLib.stat + + +fontTools.designspaceLib.statNames +================================== + +.. automodule:: fontTools.designspaceLib.statNames diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst deleted file mode 100644 index b9ba85a63..000000000 --- a/Doc/source/designspaceLib/readme.rst +++ /dev/null @@ -1,1150 +0,0 @@ -################################# -DesignSpaceDocument Specification -################################# - -An object to read, write and edit interpolation systems for typefaces. Define sources, axes, rules and instances. - -- `The Python API of the objects <#python-api>`_ -- `The document XML structure <#document-xml-structure>`_ - - -********** -Python API -********** - - - -.. _designspacedocument-object: - -DesignSpaceDocument object -========================== - -The DesignSpaceDocument object can read and write ``.designspace`` data. -It imports the axes, sources and instances to very basic **descriptor** -objects that store the data in attributes. Data is added to the document -by creating such descriptor objects, filling them with data and then -adding them to the document. This makes it easy to integrate this object -in different contexts. - -The **DesignSpaceDocument** object can be subclassed to work with -different objects, as long as they have the same attributes. Reader and -Writer objects can be subclassed as well. - -**Note:** Python attribute names are usually camelCased, the -corresponding `XML <#document-xml-structure>`_ attributes are usually -all lowercase. - -.. example-1: - -.. code:: python - - from fontTools.designspaceLib import DesignSpaceDocument - doc = DesignSpaceDocument() - doc.read("some/path/to/my.designspace") - doc.axes - doc.sources - doc.instances - -Attributes ----------- - -- ``axes``: list of axisDescriptors -- ``sources``: list of sourceDescriptors -- ``instances``: list of instanceDescriptors -- ``rules``: list if ruleDescriptors -- ``readerClass``: class of the reader object -- ``writerClass``: class of the writer object -- ``lib``: dict for user defined, custom data that needs to be stored - in the designspace. Use reverse-DNS notation to identify your own data. - Respect the data stored by others. -- ``rulesProcessingLast``: This flag indicates whether the substitution rules should be applied before or after other glyph substitution features. False: before, True: after. - -Methods -------- - -- ``read(path)``: read a designspace file from ``path`` -- ``write(path)``: write this designspace to ``path`` -- ``addSource(aSourceDescriptor)``: add this sourceDescriptor to - ``doc.sources``. -- ``addInstance(anInstanceDescriptor)``: add this instanceDescriptor - to ``doc.instances``. -- ``addAxis(anAxisDescriptor)``: add this instanceDescriptor to ``doc.axes``. -- ``newDefaultLocation()``: returns a dict with the default location - in designspace coordinates. -- ``updateFilenameFromPath(masters=True, instances=True, force=False)``: - set a descriptor filename attr from the path and this document. -- ``newAxisDescriptor()``: return a new axisDescriptor object. -- ``newSourceDescriptor()``: return a new sourceDescriptor object. -- ``newInstanceDescriptor()``: return a new instanceDescriptor object. -- ``getAxisOrder()``: return a list of axisnames -- ``findDefault()``: return the sourceDescriptor that is on the default - location. Returns None if there isn't one. -- ``normalizeLocation(aLocation)``: return a dict with normalized axis values. -- ``normalize()``: normalize the geometry of this designspace: scale all the - locations of all masters and instances to the ``-1 - 0 - 1`` value. -- ``loadSourceFonts()``: Ensure SourceDescriptor.font attributes are loaded, - and return list of fonts. -- ``tostring(encoding=None)``: Returns the designspace as a string. Default - encoding `utf-8`. - -Class Methods -------------- -- ``fromfile(path)`` -- ``fromstring(string)`` - - - - -.. _source-descriptor-object: - -SourceDescriptor object -======================= - -Attributes ----------- - -- ``filename``: string. A relative path to the source file, **as it is - in the document**. MutatorMath + Varlib. -- ``path``: string. Absolute path to the source file, calculated from - the document path and the string in the filename attr. MutatorMath + - Varlib. -- ``layerName``: string. The name of the layer in the source to look for - outline data. Default ``None`` which means ``foreground``. -- ``font``: Any Python object. Optional. Points to a representation of - this source font that is loaded in memory, as a Python object - (e.g. a ``defcon.Font`` or a ``fontTools.ttFont.TTFont``). The default - document reader will not fill-in this attribute, and the default - writer will not use this attribute. It is up to the user of - ``designspaceLib`` to either load the resource identified by ``filename`` - and store it in this field, or write the contents of this field to the - disk and make ``filename`` point to that. -- ``name``: string. Optional. Unique identifier name for this source, - if there is one or more ``instance.glyph`` elements in the document. - MutatorMath. -- ``location``: dict. Axis values for this source. MutatorMath + Varlib -- ``copyLib``: bool. Indicates if the contents of the font.lib need to - be copied to the instances. MutatorMath. -- ``copyInfo`` bool. Indicates if the non-interpolating font.info needs - to be copied to the instances. MutatorMath -- ``copyGroups`` bool. Indicates if the groups need to be copied to the - instances. MutatorMath. -- ``copyFeatures`` bool. Indicates if the feature text needs to be - copied to the instances. MutatorMath. -- ``muteKerning``: bool. Indicates if the kerning data from this source - needs to be muted (i.e. not be part of the calculations). - MutatorMath. -- ``muteInfo``: bool. Indicated if the interpolating font.info data for - this source needs to be muted. MutatorMath. -- ``mutedGlyphNames``: list. Glyphnames that need to be muted in the - instances. MutatorMath. -- ``familyName``: string. Family name of this source. Though this data - can be extracted from the font, it can be efficient to have it right - here. Varlib. -- ``styleName``: string. Style name of this source. Though this data - can be extracted from the font, it can be efficient to have it right - here. Varlib. - -.. code:: python - - doc = DesignSpaceDocument() - s1 = SourceDescriptor() - s1.path = masterPath1 - s1.name = "master.ufo1" - s1.font = defcon.Font("master.ufo1") - s1.copyLib = True - s1.copyInfo = True - s1.copyFeatures = True - s1.location = dict(weight=0) - s1.familyName = "MasterFamilyName" - s1.styleName = "MasterStyleNameOne" - s1.mutedGlyphNames.append("A") - s1.mutedGlyphNames.append("Z") - doc.addSource(s1) - -.. _instance-descriptor-object: - -InstanceDescriptor object -========================= - -.. attributes-1: - - -Attributes ----------- - -- ``filename``: string. Relative path to the instance file, **as it is - in the document**. The file may or may not exist. MutatorMath. -- ``path``: string. Absolute path to the source file, calculated from - the document path and the string in the filename attr. The file may - or may not exist. MutatorMath. -- ``name``: string. Unique identifier name of the instance, used to - identify it if it needs to be referenced from elsewhere in the - document. -- ``location``: dict. Axis values for this source. MutatorMath + - Varlib. -- ``familyName``: string. Family name of this instance. MutatorMath + - Varlib. -- ``localisedFamilyName``: dict. A dictionary of localised family name - strings, keyed by language code. -- ``styleName``: string. Style name of this source. MutatorMath + - Varlib. -- ``localisedStyleName``: dict. A dictionary of localised stylename - strings, keyed by language code. -- ``postScriptFontName``: string. Postscript fontname for this - instance. MutatorMath. -- ``styleMapFamilyName``: string. StyleMap familyname for this - instance. MutatorMath. -- ``localisedStyleMapFamilyName``: A dictionary of localised style map - familyname strings, keyed by language code. -- ``localisedStyleMapStyleName``: A dictionary of localised style map - stylename strings, keyed by language code. -- ``styleMapStyleName``: string. StyleMap stylename for this instance. - MutatorMath. -- ``glyphs``: dict for special master definitions for glyphs. If glyphs - need special masters (to record the results of executed rules for - example). MutatorMath. -- ``kerning``: bool. Indicates if this instance needs its kerning - calculated. MutatorMath. -- ``info``: bool. Indicated if this instance needs the interpolating - font.info calculated. -- ``lib``: dict. Custom data associated with this instance. - -Methods -------- - -These methods give easier access to the localised names. - -- ``setStyleName(styleName, languageCode="en")`` -- ``getStyleName(languageCode="en")`` -- ``setFamilyName(familyName, languageCode="en")`` -- ``getFamilyName(self, languageCode="en")`` -- ``setStyleMapStyleName(styleMapStyleName, languageCode="en")`` -- ``getStyleMapStyleName(languageCode="en")`` -- ``setStyleMapFamilyName(styleMapFamilyName, languageCode="en")`` -- ``getStyleMapFamilyName(languageCode="en")`` - -Example -------- - -.. code:: python - - i2 = InstanceDescriptor() - i2.path = instancePath2 - i2.familyName = "InstanceFamilyName" - i2.styleName = "InstanceStyleName" - i2.name = "instance.ufo2" - # anisotropic location - i2.location = dict(weight=500, width=(400,300)) - i2.postScriptFontName = "InstancePostscriptName" - i2.styleMapFamilyName = "InstanceStyleMapFamilyName" - i2.styleMapStyleName = "InstanceStyleMapStyleName" - glyphMasters = [dict(font="master.ufo1", glyphName="BB", location=dict(width=20,weight=20)), dict(font="master.ufo2", glyphName="CC", location=dict(width=900,weight=900))] - glyphData = dict(name="arrow", unicodeValue=1234) - glyphData['masters'] = glyphMasters - glyphData['note'] = "A note about this glyph" - glyphData['instanceLocation'] = dict(width=100, weight=120) - i2.glyphs['arrow'] = glyphData - i2.glyphs['arrow2'] = dict(mute=False) - i2.lib['com.coolDesignspaceApp.specimenText'] = 'Hamburgerwhatever' - doc.addInstance(i2) - -.. _axis-descriptor-object: - -AxisDescriptor object -===================== - -- ``tag``: string. Four letter tag for this axis. Some might be - registered at the `OpenType - specification `__. - Privately-defined axis tags must begin with an uppercase letter and - use only uppercase letters or digits. -- ``name``: string. Name of the axis as it is used in the location - dicts. MutatorMath + Varlib. -- ``labelNames``: dict. When defining a non-registered axis, it will be - necessary to define user-facing readable names for the axis. Keyed by - xml:lang code. Values are required to be ``unicode`` strings, even if - they only contain ASCII characters. -- ``minimum``: number. The minimum value for this axis in user space. - MutatorMath + Varlib. -- ``maximum``: number. The maximum value for this axis in user space. - MutatorMath + Varlib. -- ``default``: number. The default value for this axis, i.e. when a new - location is created, this is the value this axis will get in user - space. MutatorMath + Varlib. -- ``map``: list of input / output values that can describe a warp - of user space to design space coordinates. If no map values are present, it is assumed user space is the same as design space, as - in [(minimum, minimum), (maximum, maximum)]. Varlib. - -.. code:: python - - a1 = AxisDescriptor() - a1.minimum = 1 - a1.maximum = 1000 - a1.default = 400 - a1.name = "weight" - a1.tag = "wght" - a1.labelNames[u'fa-IR'] = u"قطر" - a1.labelNames[u'en'] = u"Wéíght" - a1.map = [(1.0, 10.0), (400.0, 66.0), (1000.0, 990.0)] - -RuleDescriptor object -===================== - -- ``name``: string. Unique name for this rule. Can be used to - reference this rule data. -- ``conditionSets``: a list of conditionsets -- Each conditionset is a list of conditions. -- Each condition is a dict with ``name``, ``minimum`` and ``maximum`` keys. -- ``subs``: list of substitutions -- Each substitution is stored as tuples of glyphnames, e.g. ("a", "a.alt"). -- Note: By default, rules are applied first, before other text shaping/OpenType layout, as they are part of the `Required Variation Alternates OpenType feature `_. See `5.0 rules element`_ § Attributes. - -Evaluating rules ----------------- - -- ``evaluateRule(rule, location)``: Return True if any of the rule's conditionsets - matches the given location. -- ``evaluateConditions(conditions, location)``: Return True if all the conditions - matches the given location. -- ``processRules(rules, location, glyphNames)``: Apply all the rules to the list - of glyphNames. Return a new list of glyphNames with substitutions applied. - -.. code:: python - - r1 = RuleDescriptor() - r1.name = "unique.rule.name" - r1.conditionSets.append([dict(name="weight", minimum=-10, maximum=10), dict(...)]) - r1.conditionSets.append([dict(...), dict(...)]) - r1.subs.append(("a", "a.alt")) - - -.. _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): - ruleDescriptorClass = MyRuleDescriptor - axisDescriptorClass = MyAxisDescriptor - sourceDescriptorClass = MySourceDescriptor - instanceDescriptorClass = MyInstanceDescriptor - - class MyDocWriter(BaseDocWriter): - ruleDescriptorClass = MyRuleDescriptor - axisDescriptorClass = MyAxisDescriptor - sourceDescriptorClass = MySourceDescriptor - instanceDescriptorClass = MyInstanceDescriptor - - myDoc = DesignSpaceDocument(KeyedDocReader, KeyedDocWriter) - -********************** -Document xml structure -********************** - -- The ``axes`` element contains one or more ``axis`` elements. -- The ``sources`` element contains one or more ``source`` elements. -- The ``instances`` element contains one or more ``instance`` elements. -- The ``rules`` element contains one or more ``rule`` elements. -- The ``lib`` element contains arbitrary data. - -.. code:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - -.. 1-axis-element: - -1. axis element -=============== - -- Define a single axis -- Child element of ``axes`` - -.. attributes-2: - -Attributes ----------- - -- ``name``: required, string. Name of the axis that is used in the - location elements. -- ``tag``: required, string, 4 letters. Some axis tags are registered - in the OpenType Specification. -- ``minimum``: required, number. The minimum value for this axis, in user space coordinates. -- ``maximum``: required, number. The maximum value for this axis, in user space coordinates. -- ``default``: required, number. The default value for this axis, in user space coordinates. -- ``hidden``: optional, 0 or 1. Records whether this axis needs to be - hidden in interfaces. - -.. code:: xml - - - -.. 11-labelname-element: - -1.1 labelname element -===================== - -- Defines a human readable name for UI use. -- Optional for non-registered axis names. -- Can be localised with ``xml:lang`` -- Child element of ``axis`` - -.. attributes-3: - -Attributes ----------- - -- ``xml:lang``: required, string. `XML language - definition `__ - -Value ------ - -- The natural language name of this axis. - -.. example-2: - -Example -------- - -.. code:: xml - - قطر - Wéíght - -.. 12-map-element: - -1.2 map element -=============== - -- Defines a single node in a series of input value (user space coordinate) - to output value (designspace coordinate) pairs. -- Together these values transform the designspace. -- Child of ``axis`` element. - -.. example-3: - -Example -------- - -.. code:: xml - - - - - -Example of all axis elements together: --------------------------------------- - -.. code:: xml - - - - قطر - Wéíght - - - - - - - - -.. 2-location-element: - -2. location element -=================== - -- Defines a coordinate in the design space. -- Dictionary of axisname: axisvalue -- Used in ``source``, ``instance`` and ``glyph`` elements. - -.. 21-dimension-element: - -2.1 dimension element -===================== - -- Child element of ``location`` - -.. attributes-4: - -Attributes ----------- - -- ``name``: required, string. Name of the axis. -- ``xvalue``: required, number. The value on this axis. -- ``yvalue``: optional, number. Separate value for anisotropic - interpolations. - -.. example-4: - -Example -------- - -.. code:: xml - - - - - - -.. 3-source-element: - -3. source element -================= - -- Defines a single font or layer that contributes to the designspace. -- Child element of ``sources`` -- Location in designspace coordinates. - -.. attributes-5: - -Attributes ----------- - -- ``familyname``: optional, string. The family name of the source font. - While this could be extracted from the font data itself, it can be - more efficient to add it here. -- ``stylename``: optional, string. The style name of the source font. -- ``name``: required, string. A unique name that can be used to - identify this font if it needs to be referenced elsewhere. -- ``filename``: required, string. A path to the source file, relative - to the root path of this document. The path can be at the same level - as the document or lower. -- ``layer``: optional, string. The name of the layer in the source file. - If no layer attribute is given assume the foreground layer should be used. - -.. 31-lib-element: - -3.1 lib element -=============== - -There are two meanings for the ``lib`` element: - -1. Source lib - - Example: ```` - - Child element of ``source`` - - Defines if the instances can inherit the data in the lib of this - source. - - MutatorMath only - -2. Document and instance lib - - Example: - - .. code:: xml - - - - ... - The contents use the PLIST format. - - - - - Child element of ``designspace`` and ``instance`` - - Contains arbitrary data about the whole document or about a specific - instance. - - Items in the dict need to use **reverse domain name notation** __ - -.. 32-info-element: - -3.2 info element -================ - -- ```` -- Child element of ``source`` -- Defines if the instances can inherit the non-interpolating font info - from this source. -- MutatorMath - -.. 33-features-element: - -3.3 features element -==================== - -- ```` -- Defines if the instances can inherit opentype feature text from this - source. -- Child element of ``source`` -- MutatorMath only - -.. 34-glyph-element: - -3.4 glyph element -================= - -- Can appear in ``source`` as well as in ``instance`` elements. -- In a ``source`` element this states if a glyph is to be excluded from - the calculation. -- MutatorMath only - -.. attributes-6: - -Attributes ----------- - -- ``mute``: optional attribute, number 1 or 0. Indicate if this glyph - should be ignored as a master. -- ```` -- MutatorMath only - -.. 35-kerning-element: - -3.5 kerning element -=================== - -- ```` -- Can appear in ``source`` as well as in ``instance`` elements. - -.. attributes-7: - -Attributes ----------- - -- ``mute``: required attribute, number 1 or 0. Indicate if the kerning - data from this source is to be excluded from the calculation. -- If the kerning element is not present, assume ``mute=0``, yes, - include the kerning of this source in the calculation. -- MutatorMath only - -.. example-5: - -Example -------- - -.. code:: xml - - - - - - - - - - - - - -.. 4-instance-element: - -4. instance element -=================== - -- Defines a single font that can be calculated with the designspace. -- Child element of ``instances`` -- For use in Varlib the instance element really only needs the names - and the location. The ``glyphs`` element is not required. -- MutatorMath uses the ``glyphs`` element to describe how certain - glyphs need different masters, mainly to describe the effects of - conditional rules in Superpolator. -- Location in designspace coordinates. - -.. attributes-8: - -Attributes ----------- - -- ``familyname``: required, string. The family name of the instance - font. Corresponds with ``font.info.familyName`` -- ``stylename``: required, string. The style name of the instance font. - Corresponds with ``font.info.styleName`` -- ``name``: required, string. A unique name that can be used to - identify this font if it needs to be referenced elsewhere. -- ``filename``: string. Required for MutatorMath. A path to the - instance file, relative to the root path of this document. The path - can be at the same level as the document or lower. -- ``postscriptfontname``: string. Optional for MutatorMath. Corresponds - with ``font.info.postscriptFontName`` -- ``stylemapfamilyname``: string. Optional for MutatorMath. Corresponds - with ``styleMapFamilyName`` -- ``stylemapstylename``: string. Optional for MutatorMath. Corresponds - with ``styleMapStyleName`` - -Example for varlib ------------------- - -.. code:: xml - - - - - - - - - - - com.coolDesignspaceApp.specimenText - Hamburgerwhatever - - - - -.. 41-glyphs-element: - -4.1 glyphs element -================== - -- Container for ``glyph`` elements. -- Optional -- MutatorMath only. - -.. 42-glyph-element: - -4.2 glyph element -================= - -- Child element of ``glyphs`` -- May contain a ``location`` element. - -.. attributes-9: - -Attributes ----------- - -- ``name``: string. The name of the glyph. -- ``unicode``: string. Unicode values for this glyph, in hexadecimal. - Multiple values should be separated with a space. -- ``mute``: optional attribute, number 1 or 0. Indicate if this glyph - should be supressed in the output. - -.. 421-note-element: - -4.2.1 note element -================== - -- String. The value corresponds to glyph.note in UFO. - -.. 422-masters-element: - -4.2.2 masters element -===================== - -- Container for ``master`` elements -- These ``master`` elements define an alternative set of glyph masters - for this glyph. - -.. 4221-master-element: - -4.2.2.1 master element -====================== - -- Defines a single alternative master for this glyph. - -4.3 Localised names for instances -================================= - -Localised names for instances can be included with these simple elements -with an ``xml:lang`` attribute: -`XML language definition `__ - -- stylename -- familyname -- stylemapstylename -- stylemapfamilyname - -.. example-6: - -Example -------- - -.. code:: xml - - Demigras - 半ば - Montserrat - モンセラート - Standard - Montserrat Halbfett - モンセラート SemiBold - -.. attributes-10: - -Attributes ----------- - -- ``glyphname``: the name of the alternate master glyph. -- ``source``: the identifier name of the source this master glyph needs - to be loaded from - -.. example-7: - -Example -------- - -.. code:: xml - - - - - - - - - - - - - - A note about this glyph - - - - - - - - - - - - - - - com.coolDesignspaceApp.specimenText - Hamburgerwhatever - - - - -.. 50-rules-element: - -5.0 rules element -================= - -- Container for ``rule`` elements -- The rules are evaluated in this order. - -Rules describe designspace areas in which one glyph should be replaced by another. -A rule has a name and a number of conditionsets. The rule also contains a list of -glyphname pairs: the glyphs that need to be substituted. For a rule to be triggered -**only one** of the conditionsets needs to be true, ``OR``. Within a conditionset -**all** conditions need to be true, ``AND``. - -.. attributes-11: - -Attributes ----------- - -- ``processing``: flag, optional. Valid values are [``first``, ``last``]. This flag indicates whether the substitution rules should be applied before or after other glyph substitution features. -- If no ``processing`` attribute is given, interpret as ``first``, and put the substitution rule in the `rvrn` feature. -- If ``processing`` is ``last``, put it in `rclt`. - -.. 51-rule-element: - -5.1 rule element -================ - -- Defines a named rule. -- Each ``rule`` element contains one or more ``conditionset`` elements. -- **Only one** ``conditionset`` needs to be true to trigger the rule. -- **All** conditions in a ``conditionset`` must be true to make the ``conditionset`` true. -- For backwards compatibility a ``rule`` can contain ``condition`` elements outside of a conditionset. These are then understood to be part of a single, implied, ``conditionset``. Note: these conditions should be written wrapped in a conditionset. -- A rule element needs to contain one or more ``sub`` elements in order to be compiled to a variable font. -- Rules without sub elements should be ignored when compiling a font. -- For authoring tools it might be necessary to save designspace files without ``sub`` elements just because the work is incomplete. - -.. attributes-11: - -Attributes ----------- - -- ``name``: optional, string. A unique name that can be used to - identify this rule if it needs to be referenced elsewhere. The name - is not important for compiling variable fonts. - -5.1.1 conditionset element -========================== - -- Child element of ``rule`` -- Contains one or more ``condition`` elements. - -.. 512-condition-element: - -5.1.2 condition element -======================= - -- Child element of ``conditionset`` -- Between the ``minimum`` and ``maximum`` this condition is ``True``. -- ``minimum`` and ``maximum`` are in designspace coordinates. -- If ``minimum`` is not available, assume it is ``axis.minimum``, mapped to designspace coordinates. -- If ``maximum`` is not available, assume it is ``axis.maximum``, mapped to designspace coordinates. -- The condition must contain at least a minimum or maximum or both. - -.. attributes-12: - -Attributes ----------- - -- ``name``: string, required. Must match one of the defined ``axis`` - name attributes. -- ``minimum``: number, required*. The low value, in designspace coordinates. -- ``maximum``: number, required*. The high value, in designspace coordinates. - -.. 513-sub-element: - -5.1.3 sub element -================= - -- Child element of ``rule``. -- Defines which glyph to replace when the rule evaluates to **True**. -- The ``sub`` element contains a pair of glyphnames. The ``name`` attribute is the glyph that should be visible when the rule evaluates to **False**. The ``with`` attribute is the glyph that should be visible when the rule evaluates to **True**. - -Axis values in Conditions are in designspace coordinates. - -.. attributes-13: - -Attributes ----------- - -- ``name``: string, required. The name of the glyph this rule looks - for. -- ``with``: string, required. The name of the glyph it is replaced - with. - -.. example-8: - -Example -------- - -Example with an implied ``conditionset``. Here the conditions are not -contained in a conditionset. - -.. code:: xml - - - - - - - - - -Example with ``conditionsets``. All conditions in a conditionset must be true. - -.. code:: xml - - - - - - - - - - - - - - - -.. 6-notes: - -6 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. - -.. 7-common-lib-key-registry: - -7 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. - -.. 8-implementation-and-differences: - - -8 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. - -8.1 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. - -8.2 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. - -8.3 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. - - -.. 9-this-document - -9 This document -=============== - -- Changes are to be expected. - - diff --git a/Doc/source/designspaceLib/scripting.rst b/Doc/source/designspaceLib/scripting.rst index 5a17816ad..63235eec0 100644 --- a/Doc/source/designspaceLib/scripting.rst +++ b/Doc/source/designspaceLib/scripting.rst @@ -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 `__ - 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 `__. .. 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() ` +and then +:func:`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() ` allows to +strip new information from a designspace version 5 to downgrade it to a +collection of version 4 documents, one per variable font. diff --git a/Doc/source/designspaceLib/v5_class_diagram.png b/Doc/source/designspaceLib/v5_class_diagram.png new file mode 100644 index 000000000..7c75bcb92 Binary files /dev/null and b/Doc/source/designspaceLib/v5_class_diagram.png differ diff --git a/Doc/source/designspaceLib/v5_class_diagram.puml b/Doc/source/designspaceLib/v5_class_diagram.puml new file mode 100644 index 000000000..31f9e9c36 --- /dev/null +++ b/Doc/source/designspaceLib/v5_class_diagram.puml @@ -0,0 +1,292 @@ +@startuml v5_class_diagram + +title + Designspace v5 Class Diagram + + Note: the ""Descriptor"" suffix is omitted from most classes +end title + +' left to right direction + +skinparam class { +BackgroundColor<> PaleGreen +} + +class DesignSpaceDocument { ++ formatVersion: str = None ++ <> 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]] ++ <> 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 <> { ++ 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 <> { ++ 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: <> \n axisLabels > + +class LocationLabel <> { ++ 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: <> \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] ++ <> location: Location ++ <> designLocation: AnisotropicLocation +.... ++ font: Optional[Font] +.... ++ familyName: Optional[str] ++ styleName: Optional[str] ++ <> localisedFamilyName: Dict +.... ++ <> copyLib: bool ++ <> copyInfo: bool ++ <> copyGroups: bool ++ <> copyFeatures: bool +.... ++ muteKerning: bool ++ muteInfo: bool ++ mutedGlyphNames: List[str] +---- ++ <> 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 <> { ++ filename: str ++ lib: Dict +} +DesignSpaceDocument *--- "*" VariableFont: <> \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 <> { ++ name: str +} +VariableFont *-- "*" AbstractAxisSubset: <> \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 <> { ++ userMinimum: float ++ userDefault: float ++ userMaximum: float +} +AbstractAxisSubset <|-- RangeAxisSubset + +class ValueAxisSubset <> { ++ userValue: float +} +AbstractAxisSubset <|-- ValueAxisSubset + +class Instance { ++ filename: str ++ path: str ++ <> location: Location ++ <> locationLabel: str ++ <> designLocation: AnisotropicLocation ++ <> userLocation: SimpleLocation +.... ++ font: Optional[Font] +.... ++ <> name: Optional[str] ++ <> familyName: Optional[str] ++ <> styleName: Optional[str] ++ <> postScriptFontName: Optional[str] ++ <> styleMapFamilyName: Optional[str] ++ <> styleMapStyleName: Optional[str] ++ localisedFamilyName: Dict ++ localisedStyleName: Dict ++ localisedStyleMapFamilyName: Dict ++ localisedStyleMapStyleName: Dict +.... ++ <> glyphs: Dict ++ <> kerning: bool ++ <> info: bool +.... ++ lib: Dict +---- ++ <> clearLocation() ++ <> getLocationLabelDescriptor(doc) ++ <> getFullDesignLocation(doc) ++ <> 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 + diff --git a/Doc/source/designspaceLib/v5_split_downconvert.png b/Doc/source/designspaceLib/v5_split_downconvert.png new file mode 100644 index 000000000..8867378bc Binary files /dev/null and b/Doc/source/designspaceLib/v5_split_downconvert.png differ diff --git a/Doc/source/designspaceLib/v5_split_downconvert.puml b/Doc/source/designspaceLib/v5_split_downconvert.puml new file mode 100644 index 000000000..6f51f80c9 --- /dev/null +++ b/Doc/source/designspaceLib/v5_split_downconvert.puml @@ -0,0 +1,147 @@ +@startuml v5_split_downconvert + +title +How to split and down-convert a DesignSpace version 5 +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 diff --git a/Doc/source/designspaceLib/v5_variable_fonts_vs_instances.png b/Doc/source/designspaceLib/v5_variable_fonts_vs_instances.png new file mode 100644 index 000000000..7a5915a31 Binary files /dev/null and b/Doc/source/designspaceLib/v5_variable_fonts_vs_instances.png differ diff --git a/Doc/source/designspaceLib/xml.rst b/Doc/source/designspaceLib/xml.rst new file mode 100644 index 000000000..58cf6a77e --- /dev/null +++ b/Doc/source/designspaceLib/xml.rst @@ -0,0 +1,1014 @@ +.. _document-xml-structure: + +********************** +Document XML structure +********************** + +.. sectnum:: + :start: 2 +.. Note: impossible with Sphinx to avoid numbering the document title +.. See this issue: https://github.com/sphinx-doc/sphinx/issues/4628 + +.. contents:: Table of contents (levels match the document structure) + :local: + +======== +Overview +======== + + +.. code:: xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +================== +```` element +================== + +The ```` element contains one or more ```` elements. + +.. rubric:: Attributes + +- ``elidedfallbackname``: optional, string. + STAT Style Attributes Header field ``elidedFallbackNameID``. + See: `OTSpec STAT Style Attributes Header + `_ + + .. versionadded:: 5.0 + + +```` element +================== + +- Define a single axis +- Child element of ``axes`` +- The axis can be either continuous (as in version 4.0) or discrete (new in version 5.0). + Discrete axes have a list of values instead of a range minimum and maximum. + + +.. rubric:: Attributes + +- ``name``: required, string. Name of the axis that is used in the + location elements. +- ``tag``: required, string, 4 letters. Some axis tags are registered + in the OpenType Specification. +- ``default``: required, number. The default value for this axis, in user space coordinates. +- ``hidden``: optional, 0 or 1. Records whether this axis needs to be + hidden in interfaces. + +For a continuous axis: + - ``minimum``: required, number. The minimum value for this axis, in user space coordinates. + - ``maximum``: required, number. The maximum value for this axis, in user space coordinates. + +For a discrete axis: + - ``values``: required, space-separated numbers. The exhaustive list of possible values along this axis. + + .. versionadded:: 5.0 + + +.. rubric:: Example + +.. code:: xml + + + + + + + +.. _labelname: + +```` element (axis) +------------------------------ + +- Defines a human readable name for UI use. +- Optional for non-registered axis names. +- Can be localised with ``xml:lang`` +- Child element of ```` or ``