LettError / designSpaceDocument

Python reader and writer object for designspace files.
35 stars 9 forks source link

DesignSpaceDocument moved to fontTools

It's called fonttools.designspaceLib.

https://github.com/fonttools/fonttools/tree/master/Doc/source/designspaceLib

MutatorMath started out with its own reader and writer for designspaces. Since then the use of designspace has broadened and it would be useful to have a reader and writer that are independent of a specific system.

DesignSpaceDocument

An object to read, write and edit interpolation systems for typefaces.

A couple of differences between things that use designspaces:

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.

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

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

UFO instances

SourceDescriptor object

Attributes

doc = DesignSpaceDocument()
s1 = SourceDescriptor()
s1.path = masterPath1
s1.name = "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)

InstanceDescriptor object

Attributes

Methods

These methods give easier access to the localised names.

Example

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)
doc.addInstance(i2)

AxisDescriptor object

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

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

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.

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

<?xml version='1.0' encoding='utf-8'?>
<designspace format="3">
    <axes>
        <!-- define axes here -->
        <axis../>
        </axes>
    <sources>
        <!-- define masters here -->
        <source../>
    </sources>
    <instances>
        <!-- define instances here -->
        <instance../>
    </instances>
</designspace>

1. axis element

Attributes

<axis name="weight" tag="wght" minimum="1" maximum="1000" default="400">

1.1 labelname element

Attributes

Value

Example

<labelname xml:lang="fa-IR">قطر</labelname>
<labelname xml:lang="en">Wéíght</labelname>

1.2 map element

Example

<map input="1.0" output="10.0" />
<map input="400.0" output="66.0" />
<map input="1000.0" output="990.0" />

Example of all axis elements together:

    <axes>
        <axis default="1" maximum="1000" minimum="0" name="weight" tag="wght">
            <labelname xml:lang="fa-IR">قطر</labelname>
            <labelname xml:lang="en">Wéíght</labelname>
        </axis>
        <axis default="100" maximum="200" minimum="50" name="width" tag="wdth">
            <map input="50.0" output="10.0" />
            <map input="100.0" output="66.0" />
            <map input="200.0" output="990.0" />
        </axis>
    </axes>

2. location element

2.1 dimension element

Attributes

Example

<location>
  <dimension name="width" xvalue="0.000000" />
  <dimension name="weight" xvalue="0.000000" yvalue="0.003" />
</location>

3. source element

Attributes

3.1 lib element

3.2 info element

3.3 features element

3.4 glyph element

Attributes

3.5 kerning element

Attributes

Example

<source familyname="MasterFamilyName" filename="masters/masterTest1.ufo" name="master.ufo1" stylename="MasterStyleNameOne">
    <lib copy="1" />
   <features copy="1" />
    <info copy="1" />
    <glyph mute="1" name="A" />
   <glyph mute="1" name="Z" />
   <location>
      <dimension name="width" xvalue="0.000000" />
      <dimension name="weight" xvalue="0.000000" />
   </location>
</source>

4. instance element

Attributes

Example for varlib

<instance familyname="InstanceFamilyName" filename="instances/instanceTest2.ufo" name="instance.ufo2" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">
<location>
    <dimension name="width" xvalue="400" yvalue="300" />
   <dimension name="weight" xvalue="66" />
</location>
<kerning />
<info />
</instance>

4.1 glyphs element

4.2 glyph element

Attributes

4.2.1 note element

4.2.2 masters element

4.2.2.1 master element

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

Example

<stylename xml:lang="fr">Demigras</stylename>
<stylename xml:lang="ja">半ば</stylename>
<familyname xml:lang="fr">Montserrat</familyname>
<familyname xml:lang="ja">モンセラート</familyname>
<stylemapstylename xml:lang="de">Standard</stylemapstylename>
<stylemapfamilyname xml:lang="de">Montserrat Halbfett</stylemapfamilyname>
<stylemapfamilyname xml:lang="ja">モンセラート SemiBold</stylemapfamilyname>

Attributes

Example

<instance familyname="InstanceFamilyName" filename="instances/instanceTest2.ufo" name="instance.ufo2" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName" stylename="InstanceStyleName">
<location>
    <dimension name="width" xvalue="400" yvalue="300" />
   <dimension name="weight" xvalue="66" />
</location>
<glyphs>
    <glyph name="arrow2" />
    <glyph name="arrow" unicode="0x4d2 0x4d3">
    <location>
        <dimension name="width" xvalue="100" />
            <dimension name="weight" xvalue="120" />
    </location>
    <note>A note about this glyph</note>
    <masters>
        <master glyphname="BB" source="master.ufo1">
        <location>
            <dimension name="width" xvalue="20" />
            <dimension name="weight" xvalue="20" />
        </location>
        </master>
    </masters>
    </glyph>
</glyphs>
<kerning />
<info />
</instance>

5.0 rules element

5.1 rule element

Attributes

5.1.1 condition element

Attributes

5.1.2 sub element

Attributes

Example

<rules>
    <rule name="named.rule.1">
        <condition minimum="250" maximum="750" name="weight" />
        <condition minimum="50" maximum="100" name="width" />
        <sub name="dollar" byname="dollar.alt"/>
    </rule>
</rules>

6 Notes

Paths and filenames

A designspace file needs to store many references to UFO files.

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

Case 2

descriptor.filename == "../something"
descriptor.path == None

Action

Case 3

descriptor.filename == None
descriptor.path == "~/absolute/path/there"

Action

Case 4

descriptor.filename == '../somewhere'
descriptor.path == "~/absolute/path/there"

Action

Recommendation for editors

7 This document