1####################### 2Scripting 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 a1.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)] 92 93Make a source object 94==================== 95 96A **source** is an object that points to a UFO file. It provides the 97outline geometry, kerning and font.info that we want to work with. 98 99.. code:: python 100 101 s0 = SourceDescriptor() 102 s0.path = "my/path/to/thin.ufo" 103 s0.name = "master.thin" 104 s0.location = dict(weight=0) 105 doc.addSource(s0) 106 107- You'll need to have at least 2 sources in your document, so go ahead 108 and add another one. 109- The **location** attribute is a dictionary with the designspace 110 location for this master. 111- The axis names in the location have to match one of the ``axis.name`` 112 values you defined before. 113- The **path** attribute is the absolute path to an existing UFO. 114- The **name** attribute is a unique name for this source used to keep 115 track it. 116- The **layerName** attribute is the name of the UFO3 layer. Default None for ``foreground``. 117 118So go ahead and add another master: 119 120.. code:: python 121 122 s1 = SourceDescriptor() 123 s1.path = "my/path/to/bold.ufo" 124 s1.name = "master.bold" 125 s1.location = dict(weight=1000) 126 doc.addSource(s1) 127 128 129Option: exclude glyphs 130---------------------- 131 132By default all glyphs in a source will be processed. If you want to 133exclude certain glyphs, add their names to the ``mutedGlyphNames`` list. 134 135.. code:: python 136 137 s1.mutedGlyphNames = ["A.test", "A.old"] 138 139Make an instance object 140======================= 141 142An **instance** is description of a UFO that you want to generate with 143the designspace. For an instance you can define more things. If you want 144to generate UFO instances with MutatorMath then you can define different 145names and set flags for if you want to generate kerning and font info 146and so on. You can also set a path where to generate the instance. 147 148.. code:: python 149 150 i0 = InstanceDescriptor() 151 i0.familyName = "MyVariableFontPrototype" 152 i0.styleName = "Medium" 153 i0.path = os.path.join(root, "instances","MyVariableFontPrototype-Medium.ufo") 154 i0.location = dict(weight=500) 155 i0.kerning = True 156 i0.info = True 157 doc.addInstance(i0) 158 159- The ``path`` attribute needs to be the absolute (real or intended) 160 path for the instance. When the document is saved this path will 161 written as relative to the path of the document. 162- instance paths should be on the same level as the document, or in a 163 level below. 164- Instances for MutatorMath will generate to UFO. 165- Instances for variable fonts become **named instances**. 166 167Option: add more names 168---------------------- 169 170If you want you can add a PostScript font name, a stylemap familyName 171and a stylemap styleName. 172 173.. code:: python 174 175 i0.postScriptFontName = "MyVariableFontPrototype-Medium" 176 i0.styleMapFamilyName = "MyVarProtoMedium" 177 i0.styleMapStyleName = "regular" 178 179Option: add glyph specific masters 180---------------------------------- 181 182This bit is not supported by OpenType variable fonts, but it is needed 183for some designspaces intended for generating instances with 184MutatorMath. The code becomes a bit verbose, so you're invited to wrap 185this into something clever. 186 187.. code:: python 188 189 # we're making a dict with all sorts of 190 #(optional) settings for a glyph. 191 #In this example: the dollar. 192 glyphData = dict(name="dollar", unicodeValue=0x24) 193 194 # you can specify a different location for a glyph 195 glyphData['instanceLocation'] = dict(weight=500) 196 197 # You can specify different masters 198 # for this specific glyph. 199 # You can also give those masters new 200 # locations. It's a miniature designspace. 201 # Remember the "name" attribute we assigned to the sources? 202 glyphData['masters'] = [ 203 dict(font="master.thin", 204 glyphName="dollar.nostroke", 205 location=dict(weight=0)), 206 dict(font="master.bold", 207 glyphName="dollar.nostroke", 208 location=dict(weight=1000)), 209 ] 210 211 # With all of that set up, store it in the instance. 212 i4.glyphs['dollar'] = glyphData 213 214****** 215Saving 216****** 217 218.. code:: python 219 220 path = "myprototype.designspace" 221 doc.write(path) 222 223************************ 224Reading old designspaces 225************************ 226 227Old designspace files might not contain ``axes`` definitions. This is 228how you reconstruct the axes from the extremes of the source locations 229 230.. code:: python 231 232 doc.checkAxes() 233 234This is how you check the default font. 235 236.. code:: python 237 238 doc.checkDefault() 239 240*********** 241Generating? 242*********** 243 244You can generate the UFO's with MutatorMath: 245 246.. code:: python 247 248 from mutatorMath.ufo import build 249 build("whatevs/myprototype.designspace") 250 251- Assuming the outline data in the masters is compatible. 252 253Or you can use the file in making a **variable font** with varlib. 254