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._formatVersion = formatVersion
self._fileCreator = fileCreator
self._downConversionKerningData = None
# if the file already exists, get the format version.
# this will be needed for up and down conversion.
previousFormatVersion = None
@ -815,14 +816,67 @@ class UFOWriter(object):
# 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):
"""
Write groups.plist. This method requires a
dict of glyph groups as an argument.
"""
# validate the data structure
valid, message = groupsValidator(groups)
if not valid:
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()
path = os.path.join(self._path, GROUPS_FILENAME)
groupsNew = {}
@ -881,6 +935,7 @@ class UFOWriter(object):
regards to conflicting pairs. The assumption is that the
kerning data being passed is standards compliant.
"""
# validate the data structure
invalidFormatMessage = "The kerning is not properly formatted."
if not isDictEnough(kerning):
raise UFOLibError(invalidFormatMessage)
@ -895,6 +950,16 @@ class UFOWriter(object):
raise UFOLibError(invalidFormatMessage)
if not isinstance(value, (int, float)):
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()
path = os.path.join(self._path, KERNING_FILENAME)
kerningDict = {}

View File

@ -6,7 +6,7 @@ import unittest
import tempfile
import codecs
from plistlib import writePlist, readPlist
from ufoLib import convertUFOFormatVersion1ToFormatVersion2, UFOReader
from ufoLib import convertUFOFormatVersion1ToFormatVersion2, UFOReader, UFOWriter
from testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion
@ -113,13 +113,6 @@ class ConversionFunctionsTestCase(unittest.TestCase):
convertUFOFormatVersion1ToFormatVersion2(path1, path2)
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
@ -236,6 +229,115 @@ class KerningUpConversionTestCase(unittest.TestCase):
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__":
from robofab.test.testSupport import runTests
runTests()