Add write time kerning group down conversion to UFOWriter.

This allows the UFO 3 up converted kerning group names to be reset to
their original names at the time of writing in UFO <= 2.
This commit is contained in:
Tal Leming 2014-02-18 11:49:26 -05:00
parent 0f6b6d741a
commit 8dc8742959
2 changed files with 175 additions and 8 deletions

View File

@ -591,6 +591,7 @@ class UFOWriter(object):
self._path = path self._path = path
self._formatVersion = formatVersion self._formatVersion = formatVersion
self._fileCreator = fileCreator self._fileCreator = fileCreator
self._downConversionKerningData = None
# if the file already exists, get the format version. # if the file already exists, get the format version.
# this will be needed for up and down conversion. # this will be needed for up and down conversion.
previousFormatVersion = None previousFormatVersion = None
@ -815,14 +816,67 @@ class UFOWriter(object):
# groups.plist # groups.plist
def setKerningGroupConversionRenameMaps(self, maps):
"""
Set maps defining the renaming that should be done
when writing groups and kerning in UFO 1 and UFO 2.
This will effectively undo the conversion done when
UFOReader reads this data. The dictionary should have
this form:
{
"side1" : {"group name to use when writing" : "group name in data"},
"side2" : {"group name to use when writing" : "group name in data"}
}
This is the same form returned by UFOReader's
getKerningGroupConversionRenameMaps method.
"""
if self._formatVersion >= 3:
return # XXX raise an error here
# flip the dictionaries
remap = {}
for side in ("side1", "side2"):
for writeName, dataName in maps[side].items():
remap[dataName] = writeName
self._downConversionKerningData = dict(groupRenameMap=remap)
def writeGroups(self, groups): def writeGroups(self, groups):
""" """
Write groups.plist. This method requires a Write groups.plist. This method requires a
dict of glyph groups as an argument. dict of glyph groups as an argument.
""" """
# validate the data structure
valid, message = groupsValidator(groups) valid, message = groupsValidator(groups)
if not valid: if not valid:
raise UFOLibError(message) raise UFOLibError(message)
# down convert
if self._formatVersion < 3 and self._downConversionKerningData is not None:
remap = self._downConversionKerningData["groupRenameMap"]
remappedGroups = {}
# there are some edge cases here that are ignored:
# 1. if a group is being renamed to a name that
# already exists, the existing group is always
# overwritten. (this is why there are two loops
# below.) there doesn't seem to be a logical
# solution to groups mismatching and overwriting
# with the specifiecd group seems like a better
# solution than throwing an error.
# 2. if side 1 and side 2 groups are being renamed
# to the same group name there is no check to
# ensure that the contents are identical. that
# is left up to the caller.
for name, contents in groups.items():
if name in remap:
continue
remappedGroups[name] = contents
for name, contents in groups.items():
if name not in remap:
continue
name = remap[name]
remappedGroups[name] = contents
groups = remappedGroups
# pack and write
self._makeDirectory() self._makeDirectory()
path = os.path.join(self._path, GROUPS_FILENAME) path = os.path.join(self._path, GROUPS_FILENAME)
groupsNew = {} groupsNew = {}
@ -881,6 +935,7 @@ class UFOWriter(object):
regards to conflicting pairs. The assumption is that the regards to conflicting pairs. The assumption is that the
kerning data being passed is standards compliant. kerning data being passed is standards compliant.
""" """
# validate the data structure
invalidFormatMessage = "The kerning is not properly formatted." invalidFormatMessage = "The kerning is not properly formatted."
if not isDictEnough(kerning): if not isDictEnough(kerning):
raise UFOLibError(invalidFormatMessage) raise UFOLibError(invalidFormatMessage)
@ -895,6 +950,16 @@ class UFOWriter(object):
raise UFOLibError(invalidFormatMessage) raise UFOLibError(invalidFormatMessage)
if not isinstance(value, (int, float)): if not isinstance(value, (int, float)):
raise UFOLibError(invalidFormatMessage) raise UFOLibError(invalidFormatMessage)
# down convert
if self._formatVersion < 3 and self._downConversionKerningData is not None:
remap = self._downConversionKerningData["groupRenameMap"]
remappedKerning = {}
for (side1, side2), value in kerning.items():
side1 = remap.get(side1, side1)
side2 = remap.get(side2, side2)
remappedKerning[side1, side2] = value
kerning = remappedKerning
# pack and write
self._makeDirectory() self._makeDirectory()
path = os.path.join(self._path, KERNING_FILENAME) path = os.path.join(self._path, KERNING_FILENAME)
kerningDict = {} kerningDict = {}

View File

@ -6,7 +6,7 @@ import unittest
import tempfile import tempfile
import codecs import codecs
from plistlib import writePlist, readPlist from plistlib import writePlist, readPlist
from ufoLib import convertUFOFormatVersion1ToFormatVersion2, UFOReader from ufoLib import convertUFOFormatVersion1ToFormatVersion2, UFOReader, UFOWriter
from testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion from testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion
@ -113,13 +113,6 @@ class ConversionFunctionsTestCase(unittest.TestCase):
convertUFOFormatVersion1ToFormatVersion2(path1, path2) convertUFOFormatVersion1ToFormatVersion2(path1, path2)
self.compareFileStructures(path2, path3, expectedFontInfo1To2Conversion, False) self.compareFileStructures(path2, path3, expectedFontInfo1To2Conversion, False)
# def test2To1(self):
# path1 = self.getFontPath("TestFont1 (UFO2).ufo")
# path2 = self.getFontPath("TestFont1 (UFO2) converted.ufo")
# path3 = self.getFontPath("TestFont1 (UFO1).ufo")
# convertUFOFormatVersion2ToFormatVersion1(path1, path2)
# self.compareFileStructures(path2, path3, expectedFontInfo2To1Conversion, False)
# --------------------- # ---------------------
# kerning up conversion # kerning up conversion
@ -236,6 +229,115 @@ class KerningUpConversionTestCase(unittest.TestCase):
reader.readInfo(info) reader.readInfo(info)
class KerningDownConversionTestCase(unittest.TestCase):
expectedKerning = {
("public.kern1.BGroup", "public.kern2.CGroup"): 7,
("public.kern1.BGroup", "public.kern2.DGroup"): 8,
("public.kern1.BGroup", "A"): 5,
("public.kern1.BGroup", "B"): 6,
("public.kern1.CGroup", "public.kern2.CGroup"): 11,
("public.kern1.CGroup", "public.kern2.DGroup"): 12,
("public.kern1.CGroup", "A"): 9,
("public.kern1.CGroup", "B"): 10,
("A", "public.kern2.CGroup"): 3,
("A", "public.kern2.DGroup"): 4,
("A", "A"): 1,
("A", "B"): 2
}
groups = {
"BGroup": ["B"],
"CGroup": ["C"],
"DGroup": ["D"],
"public.kern1.BGroup": ["B"],
"public.kern1.CGroup": ["C", "Ccedilla"],
"public.kern2.CGroup": ["C", "Ccedilla"],
"public.kern2.DGroup": ["D"],
"Not A Kerning Group" : ["A"]
}
expectedWrittenGroups = {
"BGroup": ["B"],
"CGroup": ["C", "Ccedilla"],
"DGroup": ["D"],
"Not A Kerning Group" : ["A"]
}
kerning = {
("public.kern1.BGroup", "public.kern2.CGroup"): 7,
("public.kern1.BGroup", "public.kern2.DGroup"): 8,
("public.kern1.BGroup", "A"): 5,
("public.kern1.BGroup", "B"): 6,
("public.kern1.CGroup", "public.kern2.CGroup"): 11,
("public.kern1.CGroup", "public.kern2.DGroup"): 12,
("public.kern1.CGroup", "A"): 9,
("public.kern1.CGroup", "B"): 10,
("A", "public.kern2.CGroup"): 3,
("A", "public.kern2.DGroup"): 4,
("A", "A"): 1,
("A", "B"): 2
}
expectedWrittenKerning = {
"BGroup" : {
"CGroup" : 7,
"DGroup" : 8,
"A" : 5,
"B" : 6
},
"CGroup" : {
"CGroup" : 11,
"DGroup" : 12,
"A" : 9,
"B" : 10
},
"A" : {
"CGroup" : 3,
"DGroup" : 4,
"A" : 1,
"B" : 2
}
}
downConversionMapping = {
"side1" : {
"BGroup" : "public.kern1.BGroup",
"CGroup" : "public.kern1.CGroup"
},
"side2" : {
"CGroup" : "public.kern2.CGroup",
"DGroup" : "public.kern2.DGroup"
}
}
def setUp(self):
self.tempDir = tempfile.mktemp()
os.mkdir(self.tempDir)
self.dstDir = os.path.join(self.tempDir, "test.ufo")
def tearDown(self):
shutil.rmtree(self.tempDir)
def tearDownUFO(self):
shutil.rmtree(self.dstDir)
def testWrite(self):
writer = UFOWriter(self.dstDir, formatVersion=2)
writer.setKerningGroupConversionRenameMaps(self.downConversionMapping)
writer.writeKerning(self.kerning)
writer.writeGroups(self.groups)
# test groups
path = os.path.join(self.dstDir, "groups.plist")
writtenGroups = readPlist(path)
self.assertEqual(writtenGroups, self.expectedWrittenGroups)
# test kerning
path = os.path.join(self.dstDir, "kerning.plist")
writtenKerning = readPlist(path)
self.assertEqual(writtenKerning, self.expectedWrittenKerning)
self.tearDownUFO()
if __name__ == "__main__": if __name__ == "__main__":
from robofab.test.testSupport import runTests from robofab.test.testSupport import runTests
runTests() runTests()