1######################### 23 Scripting a designspace 3######################### 4 5It can be useful to build a designspace with a script rather than 6construct one with an interface like 7`Superpolator <http://superpolator.com>`__ or 8`DesignSpaceEditor <https://github.com/LettError/designSpaceRoboFontExtension>`__. 9 10`fontTools.designspaceLib` offers a some tools for building designspaces in 11Python. This document shows an example. 12 13******************************** 14Filling-in a DesignSpaceDocument 15******************************** 16 17So, suppose you installed the `fontTools` package through your favorite 18``git`` client. 19 20The ``DesignSpaceDocument`` object represents the document, whether it 21already exists or not. Make a new one: 22 23.. code:: python 24 25 from fontTools.designspaceLib import (DesignSpaceDocument, AxisDescriptor, 26 SourceDescriptor, InstanceDescriptor) 27 doc = DesignSpaceDocument() 28 29We want to create definitions for axes, sources and instances. That 30means there are a lot of attributes to set. The **DesignSpaceDocument 31object** uses objects to describe the axes, sources and instances. These 32are relatively simple objects, think of these as collections of 33attributes. 34 35- Attributes of the :ref:`source-descriptor-object` 36- Attributes of the :ref:`instance-descriptor-object` 37- Attributes of the :ref:`axis-descriptor-object` 38- Read about :ref:`subclassing-descriptors` 39 40Make an axis object 41=================== 42 43Make a descriptor object and add it to the document. 44 45.. code:: python 46 47 a1 = AxisDescriptor() 48 a1.maximum = 1000 49 a1.minimum = 0 50 a1.default = 0 51 a1.name = "weight" 52 a1.tag = "wght" 53 doc.addAxis(a1) 54 55- You can add as many axes as you need. OpenType has a maximum of 56 around 64K. DesignSpaceEditor has a maximum of 5. 57- The ``name`` attribute is the name you'll be using as the axis name 58 in the locations. 59- The ``tag`` attribute is the one of the registered `OpenType 60 Variation Axis 61 Tags <https://www.microsoft.com/typography/otspec/fvar.htm#VAT>`__ 62- The default master is expected at the intersection of all 63 default values of all axes. 64 65Option: add label names 66----------------------- 67 68The **labelnames** attribute is intended to store localisable, human 69readable names for this axis if this is not an axis that is registered 70by OpenType. Think "The label next to the slider". The attribute is a 71dictionary. The key is the `xml language 72tag <https://www.w3.org/International/articles/language-tags/>`__, the 73value is a ``unicode`` string with the name. Whether or not this attribute is 74used depends on the font building tool, the operating system and the 75authoring software. This, at least, is the place to record it. 76 77.. code:: python 78 79 a1.labelNames['fa-IR'] = u"قطر" 80 a1.labelNames['en'] = u"Wéíght" 81 82Option: add a map 83----------------- 84 85The **map** attribute is a list of (input, output) mapping values 86intended for `axis variations table of 87OpenType <https://www.microsoft.com/typography/otspec/avar.htm>`__. 88 89.. code:: python 90 91 # (user space, design space), (user space, design space)... 92 a1.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] 93 94Make a source object 95==================== 96 97A **source** is an object that points to a UFO file. It provides the 98outline geometry, kerning and font.info that we want to work with. 99 100.. code:: python 101 102 s0 = SourceDescriptor() 103 s0.path = "my/path/to/thin.ufo" 104 s0.name = "master.thin" 105 s0.location = dict(weight=0) 106 doc.addSource(s0) 107 108- You'll need to have at least 2 sources in your document, so go ahead 109 and add another one. 110- The **location** attribute is a dictionary with the designspace 111 location for this master. 112- The axis names in the location have to match one of the ``axis.name`` 113 values you defined before. 114- The **path** attribute is the absolute path to an existing UFO. 115- The **name** attribute is a unique name for this source used to keep 116 track it. 117- The **layerName** attribute is the name of the UFO3 layer. Default None for ``foreground``. 118 119So go ahead and add another master: 120 121.. code:: python 122 123 s1 = SourceDescriptor() 124 s1.path = "my/path/to/bold.ufo" 125 s1.name = "master.bold" 126 s1.location = dict(weight=1000) 127 doc.addSource(s1) 128 129 130Option: exclude glyphs 131---------------------- 132 133By default all glyphs in a source will be processed. If you want to 134exclude certain glyphs, add their names to the ``mutedGlyphNames`` list. 135 136.. code:: python 137 138 s1.mutedGlyphNames = ["A.test", "A.old"] 139 140Make an instance object 141======================= 142 143An **instance** is description of a UFO that you want to generate with 144the designspace. For an instance you can define more things. If you want 145to generate UFO instances with MutatorMath then you can define different 146names and set flags for if you want to generate kerning and font info 147and so on. You can also set a path where to generate the instance. 148 149.. code:: python 150 151 i0 = InstanceDescriptor() 152 i0.familyName = "MyVariableFontPrototype" 153 i0.styleName = "Medium" 154 i0.path = os.path.join(root, "instances","MyVariableFontPrototype-Medium.ufo") 155 i0.location = dict(weight=500) 156 i0.kerning = True 157 i0.info = True 158 doc.addInstance(i0) 159 160- The ``path`` attribute needs to be the absolute (real or intended) 161 path for the instance. When the document is saved this path will 162 written as relative to the path of the document. 163- instance paths should be on the same level as the document, or in a 164 level below. 165- Instances for MutatorMath will generate to UFO. 166- Instances for variable fonts become **named instances**. 167 168Option: add more names 169---------------------- 170 171If you want you can add a PostScript font name, a stylemap familyName 172and a stylemap styleName. 173 174.. code:: python 175 176 i0.postScriptFontName = "MyVariableFontPrototype-Medium" 177 i0.styleMapFamilyName = "MyVarProtoMedium" 178 i0.styleMapStyleName = "regular" 179 180Option: add glyph specific masters 181---------------------------------- 182 183This bit is not supported by OpenType variable fonts, but it is needed 184for some designspaces intended for generating instances with 185MutatorMath. The code becomes a bit verbose, so you're invited to wrap 186this into something clever. 187 188.. code:: python 189 190 # we're making a dict with all sorts of 191 #(optional) settings for a glyph. 192 #In this example: the dollar. 193 glyphData = dict(name="dollar", unicodeValue=0x24) 194 195 # you can specify a different location for a glyph 196 glyphData['instanceLocation'] = dict(weight=500) 197 198 # You can specify different masters 199 # for this specific glyph. 200 # You can also give those masters new 201 # locations. It's a miniature designspace. 202 # Remember the "name" attribute we assigned to the sources? 203 glyphData['masters'] = [ 204 dict(font="master.thin", 205 glyphName="dollar.nostroke", 206 location=dict(weight=0)), 207 dict(font="master.bold", 208 glyphName="dollar.nostroke", 209 location=dict(weight=1000)), 210 ] 211 212 # With all of that set up, store it in the instance. 213 i4.glyphs['dollar'] = glyphData 214 215****** 216Saving 217****** 218 219.. code:: python 220 221 path = "myprototype.designspace" 222 doc.write(path) 223 224*********** 225Generating? 226*********** 227 228You can generate the UFOs with MutatorMath: 229 230.. code:: python 231 232 from mutatorMath.ufo import build 233 build("whatevs/myprototype.designspace") 234 235- Assuming the outline data in the masters is compatible. 236 237Or you can use the file in making a **variable font** with varLib. 238 239 240.. _working_with_v5: 241 242********************************** 243Working with DesignSpace version 5 244********************************** 245 246The new version 5 allows "discrete" axes, which do not interpolate across their 247values. This is useful to store in one place family-wide data such as the STAT 248information, however it prevents the usual things done on designspaces that 249interpolate everywhere: 250 251- checking that all sources are compatible for interpolation 252- building variable fonts 253 254In order to allow the above in tools that want to handle designspace v5, 255the :mod:`fontTools.designspaceLib.split` module provides two methods to 256split a designspace into interpolable sub-spaces, 257:func:`splitInterpolable() <fontTools.designspaceLib.split.splitInterpolable>` 258and then 259:func:`splitVariableFonts() <fontTools.designspaceLib.split.splitVariableFonts>`. 260 261 262.. figure:: v5_split_downconvert.png 263 :width: 680px 264 :alt: Example process diagram to check and build DesignSpace 5 265 266 Example process process to check and build Designspace 5. 267 268 269Also, for older tools that don't know about the other version 5 additions such 270as the STAT data fields, the function 271:func:`convert5to4() <fontTools.designspaceLib.split.convert5to4>` allows to 272strip new information from a designspace version 5 to downgrade it to a 273collection of version 4 documents, one per variable font. 274