184 lines
6.8 KiB
Markdown
184 lines
6.8 KiB
Markdown
# Scripting a designspace
|
|
|
|
It can be useful to build a designspace with a script rather than construct one with an interface like [Superpolator](http://superpolator.com) or [DesignSpaceEditor](https://github.com/LettError/designSpaceRoboFontExtension). The [designSpaceDocument](https://github.com/LettError/designSpaceDocument) offers a some tools for building designspaces in Python. This document shows an example.
|
|
|
|
So, suppose you installed the [designSpaceDocument](https://github.com/LettError/designSpaceDocument) package through your favorite `git` client.
|
|
|
|
The `DesignSpaceDocument` object represents the document, whether it already exists or not. Make a new one:
|
|
|
|
```python
|
|
import os
|
|
from designSpaceDocument 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 descibe the axes, sources and instances. These are relatively simple objects, think of these as collections of attributes.
|
|
|
|
* [Attributes of the Source descriptor](https://github.com/LettError/designSpaceDocument#source-descriptor-object-attributes)
|
|
* [Attributes of the Instance descriptor](https://github.com/LettError/designSpaceDocument#instance-descriptor-object)
|
|
* [Attributes of the Axis descriptor](https://github.com/LettError/designSpaceDocument#axis-descriptor-object)
|
|
* Read about [subclassing descriptors](https://github.com/LettError/designSpaceDocument#subclassing-descriptors)
|
|
|
|
## Make an axis object
|
|
|
|
Make a descriptor object and add it to the document.
|
|
|
|
```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.
|
|
|
|
### 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](https://www.w3.org/International/articles/language-tags/), 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.
|
|
|
|
```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](https://www.microsoft.com/typography/otspec/avar.htm).
|
|
|
|
```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.
|
|
|
|
```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:
|
|
|
|
```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.
|
|
|
|
```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.
|
|
|
|
```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.
|
|
|
|
```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.
|
|
|
|
```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
|
|
|
|
```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
|
|
|
|
```python
|
|
doc.checkAxes()
|
|
```
|
|
|
|
This is how you check the default font.
|
|
|
|
```python
|
|
doc.checkDefault()
|
|
```
|
|
|
|
|
|
# Generating?
|
|
|
|
You can generate the UFO's with MutatorMath:
|
|
|
|
```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.
|
|
|