From ede5de6fb0740bf94f8e47583bbd153980fc590e Mon Sep 17 00:00:00 2001 From: Jany Belluz Date: Wed, 14 Feb 2018 11:22:55 +0000 Subject: [PATCH] [designspaceLib] Switch doc files for designspaceLib from CRLF to LF --- Doc/source/designspaceLib/readme.rst | 2030 +++++++++++------------ Doc/source/designspaceLib/scripting.rst | 498 +++--- 2 files changed, 1264 insertions(+), 1264 deletions(-) diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst index e86cc6d4b..139122273 100644 --- a/Doc/source/designspaceLib/readme.rst +++ b/Doc/source/designspaceLib/readme.rst @@ -1,1015 +1,1015 @@ -################################# -DesignSpaceDocument Specification -################################# - -An object to read, write and edit interpolation systems for typefaces. - -- the format was originally written for MutatorMath. -- the format is now also used in fontTools.varlib. -- Define sources, axes and instances. -- Not all values might be required by all applications. - -A couple of differences between things that use designspaces: - -- Varlib does not support anisotropic interpolations. -- MutatorMath and Superpolator will extrapolate over the boundaries of - the axes. Varlib can not. -- 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. -- Need to expand the description of FDK use of designspace files. - -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. - -.. code:: python - - from designSpaceDocument import DesignSpaceDocument - doc = DesignSpaceDocument() - doc.read("some/path/to/my.designspace") - doc.axes - doc.sources - doc.instances - -********** -Validation -********** - -Some validation is done when reading. - -Axes -==== - -- If the ``axes`` element is available in the document then all - locations will check their dimensions against the defined axes. If a - location uses an axis that is not defined it will be ignored. -- If there are no ``axes`` in the document, locations will accept all - axis names, so that we can.. -- Use ``doc.checkAxes()`` to reconstruct axes definitions based on the - ``source.location`` values. If you save the document the axes will be - there. - -Default font -============ - -- The source with the ``copyInfo`` flag indicates this is the default - font. -- In mutatorMath the default font is selected automatically. A warning - is printed if the mutatorMath default selection differs from the one - set by ``copyInfo``. But the ``copyInfo`` source will be used. -- If no source has a ``copyInfo`` flag, mutatorMath will be used to - select one. This source gets its ``copyInfo`` flag set. If you save - the document this flag will be set. -- Use ``doc.checkDefault()`` to set the default font. - -************ -Localisation -************ - -Some of the descriptors support localised names. The names are stored in -dictionaries using the language code as key. That means that there are -now two places to store names: the old attribute and the new localised -dictionary, ``obj.stylename`` and ``obj.localisedStyleName['en']``. - -***** -Rules -***** - -**The ``rule`` element is experimental.** Some ideas behind how rules -could work in designspaces come from Superpolator. Such rules can maybe -be used to describe some of the conditional GSUB functionality of -OpenType 1.8. The definition of a rule is not that complicated. A rule -has a name, and it has a number of conditions. The rule also contains a -list of glyphname pairs: the glyphs that need to be substituted. - -Variable font instances -======================= - -- In an variable font the substitution happens at run time: there are - no changes in the font, only in the sequence of glyphnames that is - rendered. -- The infrastructure to get this rule data in a variable font needs to - be built. - -UFO instances -============= - -- When making instances as UFOs however, we need to swap the glyphs so - that the original shape is still available. For instance, if a rule - swaps ``a`` for ``a.alt``, but a glyph that references ``a`` in a - component would then show the new ``a.alt``. -- But that can lead to unexpected results. So, if there are no rules - for ``adieresis`` (assuming it references ``a``) then that glyph - **should not change appearance**. That means that when the rule swaps - ``a`` and ``a.alt`` it also swaps all components that reference these - glyphs so they keep their appearance. -- The swap function also needs to take care of swapping the names in - kerning data. - -********** -Python API -********** - -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. -- ``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. Also indicates this source is expected - to be the default font. MutatorMath + Varlib -- ``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. -- ``mutedGlyphNames``: list of glyphnames that should be suppressed in - the generation of this instance. -- ``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. Varlib. -- ``minimum``: number. The minimum value for this axis. MutatorMath + - Varlib. -- ``maximum``: number. The maximum value for this axis. 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. - MutatorMath + Varlib. -- ``map``: list of input / output values that can describe a warp of - user space to designspace coordinates. If no map values are present, - it is assumed it is [(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. Will be used to - reference this rule data. -- ``conditions``: list of dicts with condition data. -- Each condition specifies the axis name it is active on and the values - between which the condition is true. - -.. code:: python - - r1 = RuleDescriptor() - r1.name = "unique.rule.name" - r1.conditions.append(dict(name="weight", minimum=-10, maximum=10)) - r1.conditions.append(dict(name="width", minimum=-10, maximum=10)) - -.. _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. - -.. 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. -- ``maximum``: required, number. The maximum value for this axis. -- ``default``: required, number. The default value for this axis. -- ``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-1: - -Example -------- - -.. code:: xml - - قطر - Wéíght - -.. 12-map-element: - -1.2 map element -=============== - -- Defines a single node in a series of input value / output value - pairs. -- Together these values transform the designspace. -- Child of ``axis`` element. - -.. example-2: - -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-3: - -Example -------- - -.. code:: xml - - - - - - -.. 3-source-element: - -3. source element -================= - -- Defines a single font that contributes to the designspace. -- Child element of ``sources`` - -.. 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. - -.. 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:: python - - - - ... - The contents use the PLIST format. - - - - - Child element of ``designspace`` and ``instance`` - - Contains arbitrary data about the whole document or about a specific - instance. - -.. 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 + Varlib -- NOTE: **This presence of this element indicates this source is to be - the default font.** - -.. 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-4: - -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. - -.. 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 intances Localised names for instances can be -included with these simple elements with an xml:lang attribute: `XML -language -definition `__ - -- stylename -- familyname -- stylemapstylename -- stylemapfamilyname - -.. example-5: - -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-6: - -Example -------- - -.. code:: xml - - - - - - - - - - - - - - A note about this glyph - - - - - - - - - - - - - - - com.coolDesignspaceApp.specimenText - Hamburgerwhatever - - - - -.. 50-rules-element: - -5.0 rules element -================= - -- Container for ``rule`` elements - -.. 51-rule-element: - -5.1 rule element -================ - -- Defines a named rule with a set of conditions. -- The conditional substitutions specifed in the OpenType specification - can be much more elaborate than what it recorded in this element. -- So while authoring tools are welcome to use the ``sub`` element, - they're intended as preview / example / test substitutions for the - rule. - -.. attributes-11: - -Attributes ----------- - -- ``name``: required, string. A unique name that can be used to - identify this rule if it needs to be referenced elsewhere. - -.. 511-condition-element: - -5.1.1 condition element -======================= - -- Child element of ``rule`` -- Between the ``minimum`` and ``maximum`` this rule is ``true``. -- If ``minimum`` is not available, assume it is ``axis.minimum``. -- If ``maximum`` is not available, assume it is ``axis.maximum``. -- One or the other or both need to be present. - -.. attributes-12: - -Attributes ----------- - -- ``name``: string, required. Must match one of the defined ``axis`` - name attributes. -- ``minimum``: number, required*. The low value. -- ``maximum``: number, required*. The high value. - -.. 512-sub-element: - -5.1.2 sub element -================= - -- Child element of ``rule``. -- Defines which glyphs to replace when the rule is true. -- This element is optional. It may be useful for editors to know which - glyphs can be used to preview the axis. - -.. attributes-13: - -Attributes ----------- - -- ``name``: string, required. The name of the glyph this rule looks - for. -- ``byname``: string, required. The name of the glyph it is replaced - with. - -.. example-7: - -Example -------- - -.. 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-this-document: - -7 This document -=============== - -- The package is rather new and changes are to be expected. +################################# +DesignSpaceDocument Specification +################################# + +An object to read, write and edit interpolation systems for typefaces. + +- the format was originally written for MutatorMath. +- the format is now also used in fontTools.varlib. +- Define sources, axes and instances. +- Not all values might be required by all applications. + +A couple of differences between things that use designspaces: + +- Varlib does not support anisotropic interpolations. +- MutatorMath and Superpolator will extrapolate over the boundaries of + the axes. Varlib can not. +- 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. +- Need to expand the description of FDK use of designspace files. + +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. + +.. code:: python + + from designSpaceDocument import DesignSpaceDocument + doc = DesignSpaceDocument() + doc.read("some/path/to/my.designspace") + doc.axes + doc.sources + doc.instances + +********** +Validation +********** + +Some validation is done when reading. + +Axes +==== + +- If the ``axes`` element is available in the document then all + locations will check their dimensions against the defined axes. If a + location uses an axis that is not defined it will be ignored. +- If there are no ``axes`` in the document, locations will accept all + axis names, so that we can.. +- Use ``doc.checkAxes()`` to reconstruct axes definitions based on the + ``source.location`` values. If you save the document the axes will be + there. + +Default font +============ + +- The source with the ``copyInfo`` flag indicates this is the default + font. +- In mutatorMath the default font is selected automatically. A warning + is printed if the mutatorMath default selection differs from the one + set by ``copyInfo``. But the ``copyInfo`` source will be used. +- If no source has a ``copyInfo`` flag, mutatorMath will be used to + select one. This source gets its ``copyInfo`` flag set. If you save + the document this flag will be set. +- Use ``doc.checkDefault()`` to set the default font. + +************ +Localisation +************ + +Some of the descriptors support localised names. The names are stored in +dictionaries using the language code as key. That means that there are +now two places to store names: the old attribute and the new localised +dictionary, ``obj.stylename`` and ``obj.localisedStyleName['en']``. + +***** +Rules +***** + +**The ``rule`` element is experimental.** Some ideas behind how rules +could work in designspaces come from Superpolator. Such rules can maybe +be used to describe some of the conditional GSUB functionality of +OpenType 1.8. The definition of a rule is not that complicated. A rule +has a name, and it has a number of conditions. The rule also contains a +list of glyphname pairs: the glyphs that need to be substituted. + +Variable font instances +======================= + +- In an variable font the substitution happens at run time: there are + no changes in the font, only in the sequence of glyphnames that is + rendered. +- The infrastructure to get this rule data in a variable font needs to + be built. + +UFO instances +============= + +- When making instances as UFOs however, we need to swap the glyphs so + that the original shape is still available. For instance, if a rule + swaps ``a`` for ``a.alt``, but a glyph that references ``a`` in a + component would then show the new ``a.alt``. +- But that can lead to unexpected results. So, if there are no rules + for ``adieresis`` (assuming it references ``a``) then that glyph + **should not change appearance**. That means that when the rule swaps + ``a`` and ``a.alt`` it also swaps all components that reference these + glyphs so they keep their appearance. +- The swap function also needs to take care of swapping the names in + kerning data. + +********** +Python API +********** + +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. +- ``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. Also indicates this source is expected + to be the default font. MutatorMath + Varlib +- ``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. +- ``mutedGlyphNames``: list of glyphnames that should be suppressed in + the generation of this instance. +- ``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. Varlib. +- ``minimum``: number. The minimum value for this axis. MutatorMath + + Varlib. +- ``maximum``: number. The maximum value for this axis. 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. + MutatorMath + Varlib. +- ``map``: list of input / output values that can describe a warp of + user space to designspace coordinates. If no map values are present, + it is assumed it is [(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. Will be used to + reference this rule data. +- ``conditions``: list of dicts with condition data. +- Each condition specifies the axis name it is active on and the values + between which the condition is true. + +.. code:: python + + r1 = RuleDescriptor() + r1.name = "unique.rule.name" + r1.conditions.append(dict(name="weight", minimum=-10, maximum=10)) + r1.conditions.append(dict(name="width", minimum=-10, maximum=10)) + +.. _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. + +.. 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. +- ``maximum``: required, number. The maximum value for this axis. +- ``default``: required, number. The default value for this axis. +- ``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-1: + +Example +------- + +.. code:: xml + + قطر + Wéíght + +.. 12-map-element: + +1.2 map element +=============== + +- Defines a single node in a series of input value / output value + pairs. +- Together these values transform the designspace. +- Child of ``axis`` element. + +.. example-2: + +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-3: + +Example +------- + +.. code:: xml + + + + + + +.. 3-source-element: + +3. source element +================= + +- Defines a single font that contributes to the designspace. +- Child element of ``sources`` + +.. 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. + +.. 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:: python + + + + ... + The contents use the PLIST format. + + + + - Child element of ``designspace`` and ``instance`` + - Contains arbitrary data about the whole document or about a specific + instance. + +.. 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 + Varlib +- NOTE: **This presence of this element indicates this source is to be + the default font.** + +.. 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-4: + +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. + +.. 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 intances Localised names for instances can be +included with these simple elements with an xml:lang attribute: `XML +language +definition `__ + +- stylename +- familyname +- stylemapstylename +- stylemapfamilyname + +.. example-5: + +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-6: + +Example +------- + +.. code:: xml + + + + + + + + + + + + + + A note about this glyph + + + + + + + + + + + + + + + com.coolDesignspaceApp.specimenText + Hamburgerwhatever + + + + +.. 50-rules-element: + +5.0 rules element +================= + +- Container for ``rule`` elements + +.. 51-rule-element: + +5.1 rule element +================ + +- Defines a named rule with a set of conditions. +- The conditional substitutions specifed in the OpenType specification + can be much more elaborate than what it recorded in this element. +- So while authoring tools are welcome to use the ``sub`` element, + they're intended as preview / example / test substitutions for the + rule. + +.. attributes-11: + +Attributes +---------- + +- ``name``: required, string. A unique name that can be used to + identify this rule if it needs to be referenced elsewhere. + +.. 511-condition-element: + +5.1.1 condition element +======================= + +- Child element of ``rule`` +- Between the ``minimum`` and ``maximum`` this rule is ``true``. +- If ``minimum`` is not available, assume it is ``axis.minimum``. +- If ``maximum`` is not available, assume it is ``axis.maximum``. +- One or the other or both need to be present. + +.. attributes-12: + +Attributes +---------- + +- ``name``: string, required. Must match one of the defined ``axis`` + name attributes. +- ``minimum``: number, required*. The low value. +- ``maximum``: number, required*. The high value. + +.. 512-sub-element: + +5.1.2 sub element +================= + +- Child element of ``rule``. +- Defines which glyphs to replace when the rule is true. +- This element is optional. It may be useful for editors to know which + glyphs can be used to preview the axis. + +.. attributes-13: + +Attributes +---------- + +- ``name``: string, required. The name of the glyph this rule looks + for. +- ``byname``: string, required. The name of the glyph it is replaced + with. + +.. example-7: + +Example +------- + +.. 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-this-document: + +7 This document +=============== + +- The package is rather new and changes are to be expected. diff --git a/Doc/source/designspaceLib/scripting.rst b/Doc/source/designspaceLib/scripting.rst index 214d98d00..8192d2933 100644 --- a/Doc/source/designspaceLib/scripting.rst +++ b/Doc/source/designspaceLib/scripting.rst @@ -1,249 +1,249 @@ -####################### -Scripting a designspace -####################### - -It can be useful to build a designspace with a script rather than -construct one with an interface like -`Superpolator `__ or -`DesignSpaceEditor `__. - -`fontTools.designspaceLib` offers a some tools for building designspaces in -Python. This document shows an example. - -******************************** -Filling-in a DesignSpaceDocument -******************************** - -So, suppose you installed the `fontTools` package through your favorite -``git`` client. - -The ``DesignSpaceDocument`` object represents the document, whether it -already exists or not. Make a new one: - -.. code:: python - - from fontTools.designspaceLib import (DesignSpaceDocument, AxisDescriptor, - SourceDescriptor, InstanceDescriptor) - doc = DesignSpaceDocument() - -We want to create definitions for axes, sources and instances. That -means there are a lot of attributes to set. The **DesignSpaceDocument -object** uses objects to describe the axes, sources and instances. These -are relatively simple objects, think of these as collections of -attributes. - -- Attributes of the :ref:`source-descriptor-object` -- Attributes of the :ref:`instance-descriptor-object` -- Attributes of the :ref:`axis-descriptor-object` -- Read about :ref:`subclassing-descriptors` - -Make an axis object -=================== - -Make a descriptor object and add it to the document. - -.. code:: python - - a1 = AxisDescriptor() - a1.maximum = 1000 - a1.minimum = 0 - a1.default = 0 - a1.name = "weight" - a1.tag = "wght" - doc.addAxis(a1) - -- You can add as many axes as you need. OpenType has a maximum of - around 64K. DesignSpaceEditor has a maximum of 5. -- The ``name`` attribute is the name you'll be using as the axis name - in the locations. -- The ``tag`` attribute is the one of the registered `OpenType - Variation Axis - Tags `__ - -Option: add label names ------------------------ - -The **labelnames** attribute is intended to store localisable, human -readable names for this axis if this is not an axis that is registered -by OpenType. Think "The label next to the slider". The attribute is a -dictionary. The key is the `xml language -tag `__, the -value is a utf-8 string with the name. Whether or not this attribute is -used depends on the font building tool, the operating system and the -authoring software. This, at least, is the place to record it. - -.. code:: python - - a1.labelNames['fa-IR'] = u"قطر" - a1.labelNames['en'] = u"Wéíght" - -Option: add a map ------------------ - -The **map** attribute is a list of (input, output) mapping values -intended for `axis variations table of -OpenType `__. - -.. code:: python - - a1.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] - -Make a source object -==================== - -A **source** is an object that points to a UFO file. It provides the -outline geometry, kerning and font.info that we want to work with. - -.. code:: python - - s0 = SourceDescriptor() - s0.path = "my/path/to/thin.ufo" - s0.name = "master.thin" - s0.location = dict(weight=0) - doc.addSource(s0) - -- You'll need to have at least 2 sources in your document, so go ahead - and add another one. -- The **location** attribute is a dictionary with the designspace - location for this master. -- The axis names in the location have to match one of the ``axis.name`` - values you defined before. -- The **path** attribute is the absolute path to an existing UFO. -- The **name** attribute is a unique name for this source used to keep - track it. - -So go ahead and add another master: - -.. code:: python - - s1 = SourceDescriptor() - s1.path = "my/path/to/bold.ufo" - s1.name = "master.bold" - s1.location = dict(weight=1000) - doc.addSource(s1) - -Option: exclude glyphs ----------------------- - -By default all glyphs in a source will be processed. If you want to -exclude certain glyphs, add their names to the ``mutedGlyphNames`` list. - -.. code:: python - - s1.mutedGlyphNames = ["A.test", "A.old"] - -Make an instance object -======================= - -An **instance** is description of a UFO that you want to generate with -the designspace. For an instance you can define more things. If you want -to generate UFO instances with MutatorMath then you can define different -names and set flags for if you want to generate kerning and font info -and so on. You can also set a path where to generate the instance. - -.. code:: python - - i0 = InstanceDescriptor() - i0.familyName = "MyVariableFontPrototype" - i0.styleName = "Medium" - i0.path = os.path.join(root, "instances","MyVariableFontPrototype-Medium.ufo") - i0.location = dict(weight=500) - i0.kerning = True - i0.info = True - doc.addInstance(i0) - -- The ``path`` attribute needs to be the absolute (real or intended) - path for the instance. When the document is saved this path will - written as relative to the path of the document. -- instance paths should be on the same level as the document, or in a - level below. -- Instances for MutatorMath will generate to UFO. -- Instances for variable fonts become **named instances**. - -Option: add more names ----------------------- - -If you want you can add a PostScript font name, a stylemap familyName -and a stylemap styleName. - -.. code:: python - - i0.postScriptFontName = "MyVariableFontPrototype-Medium" - i0.styleMapFamilyName = "MyVarProtoMedium" - i0.styleMapStyleName = "regular" - -Option: add glyph specific masters ----------------------------------- - -This bit is not supported by OpenType variable fonts, but it is needed -for some designspaces intended for generating instances with -MutatorMath. The code becomes a bit verbose, so you're invited to wrap -this into something clever. - -.. code:: python - - # we're making a dict with all sorts of - #(optional) settings for a glyph. - #In this example: the dollar. - glyphData = dict(name="dollar", unicodeValue=0x24) - - # you can specify a different location for a glyph - glyphData['instanceLocation'] = dict(weight=500) - - # You can specify different masters - # for this specific glyph. - # You can also give those masters new - # locations. It's a miniature designspace. - # Remember the "name" attribute we assigned to the sources? - glyphData['masters'] = [ - dict(font="master.thin", - glyphName="dollar.nostroke", - location=dict(weight=0)), - dict(font="master.bold", - glyphName="dollar.nostroke", - location=dict(weight=1000)), - ] - - # With all of that set up, store it in the instance. - i4.glyphs['dollar'] = glyphData - -****** -Saving -****** - -.. code:: python - - path = "myprototype.designspace" - doc.write(path) - -************************ -Reading old designspaces -************************ - -Old designspace files might not contain ``axes`` definitions. This is -how you reconstruct the axes from the extremes of the source locations - -.. code:: python - - doc.checkAxes() - -This is how you check the default font. - -.. code:: python - - doc.checkDefault() - -*********** -Generating? -*********** - -You can generate the UFO's with MutatorMath: - -.. code:: python - - from mutatorMath.ufo import build - build("whatevs/myprototype.designspace") - -- Assuming the outline data in the masters is compatible. - -Or you can use the file in making a **variable font** with varlib. +####################### +Scripting a designspace +####################### + +It can be useful to build a designspace with a script rather than +construct one with an interface like +`Superpolator `__ or +`DesignSpaceEditor `__. + +`fontTools.designspaceLib` offers a some tools for building designspaces in +Python. This document shows an example. + +******************************** +Filling-in a DesignSpaceDocument +******************************** + +So, suppose you installed the `fontTools` package through your favorite +``git`` client. + +The ``DesignSpaceDocument`` object represents the document, whether it +already exists or not. Make a new one: + +.. code:: python + + from fontTools.designspaceLib import (DesignSpaceDocument, AxisDescriptor, + SourceDescriptor, InstanceDescriptor) + doc = DesignSpaceDocument() + +We want to create definitions for axes, sources and instances. That +means there are a lot of attributes to set. The **DesignSpaceDocument +object** uses objects to describe the axes, sources and instances. These +are relatively simple objects, think of these as collections of +attributes. + +- Attributes of the :ref:`source-descriptor-object` +- Attributes of the :ref:`instance-descriptor-object` +- Attributes of the :ref:`axis-descriptor-object` +- Read about :ref:`subclassing-descriptors` + +Make an axis object +=================== + +Make a descriptor object and add it to the document. + +.. code:: python + + a1 = AxisDescriptor() + a1.maximum = 1000 + a1.minimum = 0 + a1.default = 0 + a1.name = "weight" + a1.tag = "wght" + doc.addAxis(a1) + +- You can add as many axes as you need. OpenType has a maximum of + around 64K. DesignSpaceEditor has a maximum of 5. +- The ``name`` attribute is the name you'll be using as the axis name + in the locations. +- The ``tag`` attribute is the one of the registered `OpenType + Variation Axis + Tags `__ + +Option: add label names +----------------------- + +The **labelnames** attribute is intended to store localisable, human +readable names for this axis if this is not an axis that is registered +by OpenType. Think "The label next to the slider". The attribute is a +dictionary. The key is the `xml language +tag `__, the +value is a utf-8 string with the name. Whether or not this attribute is +used depends on the font building tool, the operating system and the +authoring software. This, at least, is the place to record it. + +.. code:: python + + a1.labelNames['fa-IR'] = u"قطر" + a1.labelNames['en'] = u"Wéíght" + +Option: add a map +----------------- + +The **map** attribute is a list of (input, output) mapping values +intended for `axis variations table of +OpenType `__. + +.. code:: python + + a1.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] + +Make a source object +==================== + +A **source** is an object that points to a UFO file. It provides the +outline geometry, kerning and font.info that we want to work with. + +.. code:: python + + s0 = SourceDescriptor() + s0.path = "my/path/to/thin.ufo" + s0.name = "master.thin" + s0.location = dict(weight=0) + doc.addSource(s0) + +- You'll need to have at least 2 sources in your document, so go ahead + and add another one. +- The **location** attribute is a dictionary with the designspace + location for this master. +- The axis names in the location have to match one of the ``axis.name`` + values you defined before. +- The **path** attribute is the absolute path to an existing UFO. +- The **name** attribute is a unique name for this source used to keep + track it. + +So go ahead and add another master: + +.. code:: python + + s1 = SourceDescriptor() + s1.path = "my/path/to/bold.ufo" + s1.name = "master.bold" + s1.location = dict(weight=1000) + doc.addSource(s1) + +Option: exclude glyphs +---------------------- + +By default all glyphs in a source will be processed. If you want to +exclude certain glyphs, add their names to the ``mutedGlyphNames`` list. + +.. code:: python + + s1.mutedGlyphNames = ["A.test", "A.old"] + +Make an instance object +======================= + +An **instance** is description of a UFO that you want to generate with +the designspace. For an instance you can define more things. If you want +to generate UFO instances with MutatorMath then you can define different +names and set flags for if you want to generate kerning and font info +and so on. You can also set a path where to generate the instance. + +.. code:: python + + i0 = InstanceDescriptor() + i0.familyName = "MyVariableFontPrototype" + i0.styleName = "Medium" + i0.path = os.path.join(root, "instances","MyVariableFontPrototype-Medium.ufo") + i0.location = dict(weight=500) + i0.kerning = True + i0.info = True + doc.addInstance(i0) + +- The ``path`` attribute needs to be the absolute (real or intended) + path for the instance. When the document is saved this path will + written as relative to the path of the document. +- instance paths should be on the same level as the document, or in a + level below. +- Instances for MutatorMath will generate to UFO. +- Instances for variable fonts become **named instances**. + +Option: add more names +---------------------- + +If you want you can add a PostScript font name, a stylemap familyName +and a stylemap styleName. + +.. code:: python + + i0.postScriptFontName = "MyVariableFontPrototype-Medium" + i0.styleMapFamilyName = "MyVarProtoMedium" + i0.styleMapStyleName = "regular" + +Option: add glyph specific masters +---------------------------------- + +This bit is not supported by OpenType variable fonts, but it is needed +for some designspaces intended for generating instances with +MutatorMath. The code becomes a bit verbose, so you're invited to wrap +this into something clever. + +.. code:: python + + # we're making a dict with all sorts of + #(optional) settings for a glyph. + #In this example: the dollar. + glyphData = dict(name="dollar", unicodeValue=0x24) + + # you can specify a different location for a glyph + glyphData['instanceLocation'] = dict(weight=500) + + # You can specify different masters + # for this specific glyph. + # You can also give those masters new + # locations. It's a miniature designspace. + # Remember the "name" attribute we assigned to the sources? + glyphData['masters'] = [ + dict(font="master.thin", + glyphName="dollar.nostroke", + location=dict(weight=0)), + dict(font="master.bold", + glyphName="dollar.nostroke", + location=dict(weight=1000)), + ] + + # With all of that set up, store it in the instance. + i4.glyphs['dollar'] = glyphData + +****** +Saving +****** + +.. code:: python + + path = "myprototype.designspace" + doc.write(path) + +************************ +Reading old designspaces +************************ + +Old designspace files might not contain ``axes`` definitions. This is +how you reconstruct the axes from the extremes of the source locations + +.. code:: python + + doc.checkAxes() + +This is how you check the default font. + +.. code:: python + + doc.checkDefault() + +*********** +Generating? +*********** + +You can generate the UFO's with MutatorMath: + +.. code:: python + + from mutatorMath.ufo import build + build("whatevs/myprototype.designspace") + +- Assuming the outline data in the masters is compatible. + +Or you can use the file in making a **variable font** with varlib.