Beispiel #1
0
def testOuroborosKerning(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    path1, path2, path3 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, "kerningTest.designspace")

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
        path1, name="master_1", location=dict(width=0), copyLib=True, copyGroups=True, copyInfo=True, copyFeatures=True
    )
    doc.addSource(
        path2,
        name="master_2",
        location=dict(width=1000),
        copyLib=False,
        copyGroups=False,
        copyInfo=False,
        copyFeatures=False,
    )
    doc.startInstance(fileName=path3, familyName="TestInstance", styleName="Regular", location=dict(width=100))
    doc.writeKerning(location=dict(width=500))
    doc.endInstance()
    doc.save()

    # execute the designspace. Kerning errors should be tripped by the
    doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)

    if cleanUp:
        # remove the mess
        shutil.rmtree(path1)
        shutil.rmtree(path2)
        shutil.rmtree(path3)

    return True
Beispiel #2
0
def testOuroborosKerning(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    path1, path2, path3 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, 'kerningTest.designspace')
    logPath = os.path.join(rootPath,"kerningTest.log")
    try:
        testLogFile = open(logPath, 'w')
        testLogFile.close()
    except:
        print "Can't make a logfile."

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
            path1,
            name="master_1", 
            location=dict(width=0), 
            copyLib=True,
            copyGroups=True,
            copyInfo=True, 
            copyFeatures=True,
            )
    doc.addSource(
            path2,
            name="master_2", 
            location=dict(width=1000), 
            copyLib=False,
            copyGroups=False,
            copyInfo=False, 
            copyFeatures=False,
            )
    doc.startInstance(fileName=path3,
            familyName="TestInstance",
            styleName="Regular",
            location=dict(width=100)
            )
    doc.writeKerning(location=dict(width=500))
    doc.endInstance()
    doc.save()

    # execute the designspace. Kerning errors should be tripped by the 
    doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, logPath=logPath, progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)

    # did we log the error?
    report = u"""invalidInstance.ufo:\nThese kerning pairs failed validation and have been removed:\nglyphOne, public.kern2.@MMK_R_two (-400) conflicts with public.kern1.@MMK_L_one, glyphThree (-250)\npublic.kern1.@MMK_L_one, glyphThree (-250) conflicts with glyphOne, public.kern2.@MMK_R_two (-400)"""
    log = open(logPath, 'r')
    logText = log.read()
    log.close()
    #print logText
    #assert report in logText

    if cleanUp:
        # remove the mess
        shutil.rmtree(path1)
        shutil.rmtree(path2)
        shutil.rmtree(path3)

    return True
Beispiel #3
0
def testOuroborosKerning(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    path1, path2, path3 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, 'kerningTest.designspace')
    logPath = os.path.join(rootPath,"kerningTest.log")
    try:
        testLogFile = open(logPath, 'w')
        testLogFile.close()
    except:
        print("Can't make a logfile.")

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
            path1,
            name="master_1", 
            location=dict(width=0), 
            copyLib=True,
            copyGroups=True,
            copyInfo=True, 
            copyFeatures=True,
            )
    doc.addSource(
            path2,
            name="master_2", 
            location=dict(width=1000), 
            copyLib=False,
            copyGroups=False,
            copyInfo=False, 
            copyFeatures=False,
            )
    doc.startInstance(fileName=path3,
            familyName="TestInstance",
            styleName="Regular",
            location=dict(width=100)
            )
    doc.writeKerning(location=dict(width=500))
    doc.endInstance()
    doc.save()

    # execute the designspace. Kerning errors should be tripped by the 
    doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, logPath=logPath, progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)

    # did we log the error?
    report = u"""invalidInstance.ufo:\nThese kerning pairs failed validation and have been removed:\nglyphOne, public.kern2.@MMK_R_two (-400) conflicts with public.kern1.@MMK_L_one, glyphThree (-250)\npublic.kern1.@MMK_L_one, glyphThree (-250) conflicts with glyphOne, public.kern2.@MMK_R_two (-400)"""
    log = open(logPath, 'r')
    logText = log.read()
    log.close()
    #print logText
    #assert report in logText

    if cleanUp:
        # remove the mess
        shutil.rmtree(path1)
        shutil.rmtree(path2)
        shutil.rmtree(path3)

    return True
Beispiel #4
0
def testOuroborosKerning(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    path1, path2, path3 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, 'kerningTest.designspace')

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
        path1,
        name="master_1",
        location=dict(width=0),
        copyLib=True,
        copyGroups=True,
        copyInfo=True,
        copyFeatures=True,
    )
    doc.addSource(
        path2,
        name="master_2",
        location=dict(width=1000),
        copyLib=False,
        copyGroups=False,
        copyInfo=False,
        copyFeatures=False,
    )
    doc.startInstance(fileName=path3,
                      familyName="TestInstance",
                      styleName="Regular",
                      location=dict(width=100))
    doc.writeKerning(location=dict(width=500))
    doc.endInstance()
    doc.save()

    # execute the designspace. Kerning errors should be tripped by the
    doc = DesignSpaceDocumentReader(documentPath,
                                    2,
                                    roundGeometry=True,
                                    verbose=True,
                                    progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)

    if cleanUp:
        # remove the mess
        shutil.rmtree(path1)
        shutil.rmtree(path2)
        shutil.rmtree(path3)

    return True

    def test1():
        """
def generate_designspace(family, path):

    def normalize_path(path):
        return os.path.join(family['working_directory'], path)

    doc = DesignSpaceDocumentWriter(normalize_path(path))

    for i, master in enumerate(family['masters']):

        doc.addSource(

            path = normalize_path(master['path']),
            name = 'master-' + master['name'],
            location = {'weight': master['interpolation_value']},

            copyLib    = True if i == 0 else False,
            copyGroups = True if i == 0 else False,
            copyInfo   = True if i == 0 else False,

            # muteInfo = False,
            # muteKerning = False,
            # mutedGlyphNames = None,

        )

    for style in family['styles']:

        doc.startInstance(
            name = 'instance-' + style['name'],
            location = {'weight': style['interpolation_value']},
            familyName = family['output_name'],
            styleName = style['name'],
            fileName = normalize_path(style['path']),
            postScriptFontName = style['output_full_name_postscript'],
            # styleMapFamilyName = None,
            # styleMapStyleName = None,
        )

        doc.writeInfo()

        if family['has_kerning']:
            doc.writeKerning()

        doc.endInstance()

    doc.save()
def makeInstances(stepsWeight, stepsWidth):
    doc = DesignSpaceDocumentWriter(designSpacePath, verbose=True)

    doc.addSource(os.path.join(mutatorSansFolder,
                               "MutatorSansLightCondensed.ufo"),
                  name="LightCondensed",
                  location=dict(weight=0, width=0),
                  copyInfo=True,
                  familyName="MutatorSans",
                  styleName="LightCondensed")

    doc.addSource(os.path.join(mutatorSansFolder, "MutatorSansLightWide.ufo"),
                  name="LightWide",
                  location=dict(weight=0, width=1))

    doc.addSource(os.path.join(mutatorSansFolder,
                               "MutatorSansBoldCondensed.ufo"),
                  name="BoldCondensed",
                  location=dict(weight=1, width=0))

    doc.addSource(os.path.join(mutatorSansFolder, "MutatorSansBoldWide.ufo"),
                  name="BoldWide",
                  location=dict(weight=1, width=1))

    for i in range(stepsWeight):
        wt = i * 1.0 / (stepsWeight - 1)
        for j in range(stepsWidth):
            wd = j * 1.0 / (stepsWidth - 1)
            doc.startInstance(fileName=os.path.join(
                ufosFolder, "MutatorSansInstance_%s-%s.ufo" %
                (int(wt * 1000), int(wd * 1000))),
                              familyName="MutatorSansExample",
                              styleName="%s-%s" %
                              (int(wt * 1000), int(wd * 1000)),
                              location=dict(weight=wt, width=wd))
            doc.endInstance()

    doc.save()

    doc = DesignSpaceDocumentReader(designSpacePath,
                                    3,
                                    roundGeometry=True,
                                    verbose=False)
    doc.process(makeGlyphs=True, makeKerning=False, makeInfo=False)
Beispiel #7
0
def testGeometry(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    path1, path2, path3, path4, path5 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, "geometryTest.designspace")
    logPath = os.path.join(rootPath, "geometryTest.log")
    try:
        testLogFile = open(logPath, "w")
        testLogFile.close()
    except:
        print "Can't make a logfile."

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
        path1, name="master_1", location=dict(width=0), copyLib=True, copyGroups=True, copyInfo=True, copyFeatures=True
    )
    doc.addSource(
        path2,
        name="master_2",
        location=dict(width=1000),
        copyLib=False,
        copyGroups=False,
        copyInfo=False,
        copyFeatures=False,
    )
    doc.startInstance(fileName=path3, familyName="TestInstance", styleName="Regular", location=dict(width=500))
    doc.endInstance()
    doc.startInstance(
        fileName=path4, familyName="TestInstance", styleName="Anisotropic1", location=dict(width=(0, 1000))
    )
    doc.endInstance()
    doc.startInstance(
        fileName=path5, familyName="TestInstance", styleName="Anisotropic2", location=dict(width=(1000, 0))
    )
    doc.endInstance()
    doc.save()

    # execute the designspace.
    doc = DesignSpaceDocumentReader(
        documentPath, 2, roundGeometry=True, verbose=True, logPath=logPath, progressFunc=testingProgressFunc
    )
    doc.process(makeGlyphs=True, makeKerning=False, makeInfo=True)

    r1 = Font(path3)
    assert r1["glyphOne"].bounds == (0, 0, 300, 300)

    r2 = Font(path4)
    assert r2["glyphOne"].bounds == (0, 0, 100, 500)

    r3 = Font(path5)
    assert r3["glyphOne"].bounds == (0, 0, 500, 100)

    if cleanUp:
        # remove the mess
        shutil.rmtree(path1)
        shutil.rmtree(path2)
        shutil.rmtree(path3)

    return True
Beispiel #8
0
def testMutingOptions(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    # path1 and path2 are masters. path3 is the instance
    path1, path2, path3 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, 'mutingTest.designspace')
    logPath = os.path.join(rootPath,"mutingTest.log")

    try:
        testLogFile = open(logPath, 'w')
        testLogFile.close()
    except:
        print "Can't make a logfile."

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
            path1,
            name="master_1", 
            location=dict(width=0), 
            copyLib=True,
            copyGroups=True,
            copyInfo=True, 
            copyFeatures=True,
            muteKerning=True
            )
    doc.addSource(
            path2,
            name="master_2", 
            location=dict(width=1000), 
            copyLib=False,
            copyGroups=False,
            copyInfo=False, 
            copyFeatures=False,
            muteInfo=True,
            mutedGlyphNames=['glyphThree']  # mute glyphThree in master 1
            )
    doc.startInstance(fileName=path3,
            familyName="TestInstance",
            styleName="Regular",
            location=dict(width=500)
            )
    doc.writeGlyph('glyphFour', mute=True)  # mute glyphFour in the instance
    doc.writeKerning()
    doc.writeInfo()
    doc.endInstance()
    doc.save()

    # execute the designspace. 
    doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, logPath=logPath, progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)

    # look at the results
    m1 = Font(path1)
    m2 = Font(path2)
    r = Font(path3)
    # 
    # the glyphThree master was muted in the second master
    # so the instance glyphThree should be the same as the first master:
    assert r['glyphThree'].bounds == m1['glyphThree'].bounds
    # we muted glyphFour in the instance.
    # so it should not be part of the instance UFO:
    assert "glyphFour" not in r
    # font.info is muted for master2, so the instance has to have the values from master 1
    assert r.info.unitsPerEm == m1.info.unitsPerEm
    # kerning is muted for master1, so the instance has to have the kerning from master 2
    assert r.kerning[('glyphOne', 'glyphOne')] == m2.kerning[('glyphOne', 'glyphOne')]

    if cleanUp:
        # remove the mess
        try:
            shutil.rmtree(path1)
            shutil.rmtree(path2)
            shutil.rmtree(path3)
        except:
            pass

    return True
Beispiel #9
0
def testMutingOptions(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    # path1 and path2 are masters. path3 is the instance
    path1, path2, path3 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, 'mutingTest.designspace')

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(path1,
                  name="master_1",
                  location=dict(width=0),
                  copyLib=True,
                  copyGroups=True,
                  copyInfo=True,
                  copyFeatures=True,
                  muteKerning=True)
    doc.addSource(
        path2,
        name="master_2",
        location=dict(width=1000),
        copyLib=False,
        copyGroups=False,
        copyInfo=False,
        copyFeatures=False,
        muteInfo=True,
        mutedGlyphNames=['glyphThree']  # mute glyphThree in master 1
    )
    doc.startInstance(fileName=path3,
                      familyName="TestInstance",
                      styleName="Regular",
                      location=dict(width=500))
    doc.writeGlyph('glyphFour', mute=True)  # mute glyphFour in the instance
    doc.writeKerning()
    doc.writeInfo()
    doc.endInstance()
    doc.save()

    # execute the designspace.
    doc = DesignSpaceDocumentReader(documentPath,
                                    2,
                                    roundGeometry=True,
                                    verbose=True,
                                    progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)

    # look at the results
    m1 = Font(path1)
    m2 = Font(path2)
    r = Font(path3)
    #
    # the glyphThree master was muted in the second master
    # so the instance glyphThree should be the same as the first master:
    assert r['glyphThree'].bounds == m1['glyphThree'].bounds
    # we muted glyphFour in the instance.
    # so it should not be part of the instance UFO:
    assert "glyphFour" not in r
    # font.info is muted for master2, so the instance has to have the values from master 1
    assert r.info.unitsPerEm == m1.info.unitsPerEm
    # kerning is muted for master1, so the instance has to have the kerning from master 2
    assert r.kerning[('glyphOne', 'glyphOne')] == m2.kerning[('glyphOne',
                                                              'glyphOne')]

    if cleanUp:
        # remove the mess
        try:
            shutil.rmtree(path1)
            shutil.rmtree(path2)
            shutil.rmtree(path3)
        except:
            pass

    return True
Beispiel #10
0
def testGeometry(rootPath, cleanUp=True):
    # that works, let's do it via MutatorMath
    path1, path2, path3, path4, path5 = makeTestFonts(rootPath)
    documentPath = os.path.join(rootPath, 'geometryTest.designspace')

    doc = DesignSpaceDocumentWriter(documentPath, verbose=True)
    doc.addSource(
            path1,
            name="master_1", 
            location=dict(width=0), 
            copyLib=True,
            copyGroups=True,
            copyInfo=True, 
            copyFeatures=True,
            )
    doc.addSource(
            path2,
            name="master_2", 
            location=dict(width=1000), 
            copyLib=False,
            copyGroups=False,
            copyInfo=False, 
            copyFeatures=False,
            )
    doc.startInstance(fileName=path3,
            familyName="TestInstance",
            styleName="Regular",
            location=dict(width=500)
            )
    doc.endInstance()
    doc.startInstance(fileName=path4,
            familyName="TestInstance",
            styleName="Anisotropic1",
            location=dict(width=(0, 1000))
            )
    doc.endInstance()
    doc.startInstance(fileName=path5,
            familyName="TestInstance",
            styleName="Anisotropic2",
            location=dict(width=(1000, 0))
            )
    doc.endInstance()
    doc.save()

    # execute the designspace.
    doc = DesignSpaceDocumentReader(documentPath, 2, roundGeometry=True, verbose=True, progressFunc=testingProgressFunc)
    doc.process(makeGlyphs=True, makeKerning=False, makeInfo=True)

    r1 = Font(path3)
    assert r1['glyphOne'].bounds == (0, 0, 300, 300)

    r2 = Font(path4)
    assert r2['glyphOne'].bounds == (0, 0, 100, 500)

    r3 = Font(path5)
    assert r3['glyphOne'].bounds == (0, 0, 500, 100)

    if cleanUp:
        # remove the mess
        shutil.rmtree(path1)
        shutil.rmtree(path2)
        shutil.rmtree(path3)

    return True
Beispiel #11
0
class DesignSpaceMaker(object):

    axes      = []
    masters   = []
    instances = []
    neutral   = None

    def __init__(self, project):
        self.project = project

        # remove existing .designspace file
        if os.path.exists(self.project.designspace_path):
            os.remove(self.project.designspace_path)

        # create fresh .designspace file
        self.doc_writer = DesignSpaceDocumentWriter(self.project.designspace_path, verbose=True)

    def add_masters(self, verbose=False):

        if verbose: print('\tadding masters...')

        for master in self.masters:
            ufo_path = os.path.join(self.project.ufos_folder, '%s.ufo' % master)

            # check if master exists
            if os.path.exists(ufo_path):

                # make Location
                L = {}
                for i in range(len(self.axes)):
                    axis  = self.axes[i]
                    param = self.masters[master][i]
                    L[axis] = param

                # add neutral
                if master == self.neutral:
                    if verbose: print('\t\tadding %s [neutral]...' % master)
                    self.doc_writer.addSource(
                        ufo_path, master, location=L,
                        copyInfo=True, copyGroups=True,
                        copyLib=False, copyFeatures=False)

                # add deltas
                else:
                    if verbose: print('\t\tadding %s...' % master)
                    self.doc_writer.addSource(ufo_path, master, location=L)

    def add_instances(self, verbose=False):

        if verbose: print('\tadding instances...')

        for instance in self.instances:
            ufo_filename = '%s.ufo' % instance.replace(' ', '-')
            ufo_path = os.path.join(self.project.instances_folder, ufo_filename)

            # make Location
            L = {}
            for i in range(len(self.axes)):
                axis  = self.axes[i]
                param = self.instances[instance][i]
                L[axis] = param

            # add instance
            if verbose: print('\t\tadding %s...' % instance)
            self.doc_writer.startInstance(
                    fileName=ufo_path,
                    familyName='Untitled',
                    styleName=instance,
                    location=L)
            self.doc_writer.endInstance()

    def save(self, verbose=False):
        if verbose: print('creating .designspace file...')
        # add masters
        self.add_masters(verbose)
        # add instances
        self.add_instances(verbose)
        if verbose: print('\tsaving file %s...' % self.project.designspace_path, end=' ')
        # save designspace file
        self.doc_writer.save()
        if verbose: print(os.path.exists(self.project.designspace_path))
        if verbose: print('...done.\n')
# instances
instances = [
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wght100-wdth125.ufo", location=dict(wght=100, wdth=125, opsz=144), styleName="opsz144-wght100-wdth125", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wght100-wdth075.ufo", location=dict(wght=100, wdth=87, opsz=144), styleName="opsz144-wght100-wdth075", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wght100-wdth50.ufo", location=dict(wght=100, wdth=75, opsz=144), styleName="opsz144-wght100-wdth50", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),

    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wght900-wdth125.ufo", location=dict(wght=900, wdth=125, opsz=144), styleName="opsz144-wght900-wdth125", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wght900-wdth075.ufo", location=dict(wght=900, wdth=76, opsz=144), styleName="opsz144-wght900-wdth075", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wght900-wdth50.ufo", location=dict(wght=900, wdth=75, opsz=144), styleName="opsz144-wght900-wdth50", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),

    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wdth125.ufo", location=dict(wght=400, wdth=125, opsz=144), styleName="opsz144-wdth125", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wdth075.ufo", location=dict(wght=400, wdth=80, opsz=144), styleName="opsz144-wdth075", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
    #	dict(fileName="instances/Amstelvar-Italic-opsz144-wdth50.ufo", location=dict(wght=400, wdth=75, opsz=144), styleName="opsz144-wdth50", familyName=familyName, postScriptFontName=None, styleMapFamilyName=None, styleMapStyleName=None),
]
for instance in instances:
    doc.startInstance(**instance)
    doc.writeInfo()
    doc.writeKerning()
    doc.endInstance()

doc.save()
# read and process the designspace
doc = DesignSpaceDocumentReader(tmpDesignSpace,
                                ufoVersion=2,
                                roundGeometry=False,
                                verbose=False)
print("Reading DesignSpace...")
doc.process(makeGlyphs=True, makeKerning=True, makeInfo=True)
os.remove(tmpDesignSpace)  # clean up

# update the instances with the source fonts
Beispiel #13
0
class DesignSpaceMaker(object):

    axes      = []
    masters   = []
    instances = []
    neutral   = None

    def __init__(self, project):
        self.project = project

        # remove existing .designspace file
        if os.path.exists(self.project.designspace_path):
            os.remove(self.project.designspace_path)

        # create fresh .designspace file
        self.doc_writer = DesignSpaceDocumentWriter(self.project.designspace_path, verbose=True)

    def add_masters(self, verbose=False):

        if verbose: print '\tadding masters...'

        for master in self.masters:
            ufo_path = os.path.join(self.project.ufos_folder, '%s.ufo' % master)

            # check if master exists
            if os.path.exists(ufo_path):

                # make Location
                L = {}
                for i in range(len(self.axes)):
                    axis  = self.axes[i]
                    param = self.masters[master][i]
                    L[axis] = param

                # add neutral
                if master == self.neutral:
                    if verbose: print '\t\tadding %s [neutral]...' % master
                    self.doc_writer.addSource(
                        ufo_path, master, location=L,
                        copyInfo=True, copyGroups=True,
                        copyLib=False, copyFeatures=False)

                # add deltas
                else:
                    if verbose: print '\t\tadding %s...' % master
                    self.doc_writer.addSource(ufo_path, master, location=L)

    def add_instances(self, verbose=False):

        if verbose: print '\tadding instances...'

        for instance in self.instances:
            ufo_filename = '%s.ufo' % instance.replace(' ', '-')
            ufo_path = os.path.join(self.project.instances_folder, ufo_filename)

            # make Location
            L = {}
            for i in range(len(self.axes)):
                axis  = self.axes[i]
                param = self.instances[instance][i]
                L[axis] = param

            # add instance
            if verbose: print '\t\tadding %s...' % instance
            self.doc_writer.startInstance(
                    fileName=ufo_path,
                    familyName='Untitled',
                    styleName=instance,
                    location=L)
            self.doc_writer.endInstance()

    def save(self, verbose=False):
        if verbose: print 'creating .designspace file...'
        # add masters
        self.add_masters(verbose)
        # add instances
        self.add_instances(verbose)
        if verbose: print '\tsaving file %s...' % self.project.designspace_path,
        # save designspace file
        self.doc_writer.save()
        if verbose: print os.path.exists(self.project.designspace_path)
        if verbose: print '...done.\n'