• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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