Support guidelines.

git-svn-id: http://svn.robofab.com/branches/ufo3k@286 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c
This commit is contained in:
Tal Leming 2011-09-18 11:18:31 +00:00
parent 0f5645ddbe
commit 0709eaef85
3 changed files with 448 additions and 2 deletions

View File

@ -434,6 +434,47 @@ fontInfoVersion3 = {
],
"firstKerningGroupPrefix" : "@kern1",
"secondKerningGroupPrefix" : "@kern2",
"guidelines" : [
# ints
dict(x=100, y=200, angle=45),
# floats
dict(x=100.5, y=200.5, angle=45.5),
# edges
dict(x=0, y=0, angle=0),
dict(x=0, y=0, angle=360),
dict(x=0, y=0, angle=360.0),
# no y
dict(x=100),
# no x
dict(y=200),
# name
dict(x=100, y=200, angle=45, name="foo"),
dict(x=100, y=200, angle=45, name=""),
# identifier
dict(x=100, y=200, angle=45, identifier="guide1"),
dict(x=100, y=200, angle=45, identifier="guide2"),
dict(x=100, y=200, angle=45, identifier=u"\x20"),
dict(x=100, y=200, angle=45, identifier=u"\x7E"),
# colors
dict(x=100, y=200, angle=45, color="0,0,0,0"),
dict(x=100, y=200, angle=45, color="1,0,0,0"),
dict(x=100, y=200, angle=45, color="1,1,1,1"),
dict(x=100, y=200, angle=45, color="0,1,0,0"),
dict(x=100, y=200, angle=45, color="0,0,1,0"),
dict(x=100, y=200, angle=45, color="0,0,0,1"),
dict(x=100, y=200, angle=45, color="1, 0, 0, 0"),
dict(x=100, y=200, angle=45, color="0, 1, 0, 0"),
dict(x=100, y=200, angle=45, color="0, 0, 1, 0"),
dict(x=100, y=200, angle=45, color="0, 0, 0, 1"),
dict(x=100, y=200, angle=45, color=".5,0,0,0"),
dict(x=100, y=200, angle=45, color="0,.5,0,0"),
dict(x=100, y=200, angle=45, color="0,0,.5,0"),
dict(x=100, y=200, angle=45, color="0,0,0,.5"),
dict(x=100, y=200, angle=45, color=".5,1,1,1"),
dict(x=100, y=200, angle=45, color="1,.5,1,1"),
dict(x=100, y=200, angle=45, color="1,1,.5,1"),
dict(x=100, y=200, angle=45, color="1,1,1,.5"),
],
}
expectedFontInfo1To2Conversion = {

View File

@ -2426,6 +2426,167 @@ class ReadFontInfoVersion3TestCase(unittest.TestCase):
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
def testGuidelinesRead(self):
# x
## not an int or float
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x="1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
# y
## not an int or float
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(y="1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
# angle
## < 0
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, y=0, angle=-1)]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## > 360
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, y=0, angle=361)]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
# name
## not a string
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, name=1)]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
# color
## not a string
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color=1)]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## not enough commas
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1 0, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1 0 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1 0 0 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## not enough parts
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color=", 0, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, , 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, 0, , 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, 0, 0, ")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color=", , , ")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## not a number in all positions
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="r, 1, 1, 1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, g, 1, 1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, 1, b, 1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, 1, 1, a")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## too many parts
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="1, 0, 0, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## < 0 in each position
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="-1, 0, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="0, -1, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="0, 0, -1, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="0, 0, 0, -1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
## > 1 in each position
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="2, 0, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="0, 2, 0, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="0, 0, 2, 0")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, color="0, 0, 0, 2")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
# identifier
## duplicate
info = dict(fontInfoVersion3)
info["guidelines"] = [dict(x=0, identifier="guide1"), dict(y=0, identifier="guide1")]
self._writeInfoToPlist(info)
reader = UFOReader(self.dstDir)
self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
class WriteFontInfoVersion1TestCase(unittest.TestCase):
@ -4420,17 +4581,160 @@ class WriteFontInfoVersion3TestCase(unittest.TestCase):
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
# overlap
info = dict(fontInfoVersion3)
infoObject = self.makeInfoObject()
infoObject.firstKerningGroupPrefix = "@kern"
infoObject.secondKerningGroupPrefix = "@kern2"
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
info = dict(fontInfoVersion3)
infoObject = self.makeInfoObject()
infoObject.firstKerningGroupPrefix = "@kern1"
infoObject.secondKerningGroupPrefix = "@kern"
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
def testGuidelinesWrite(self):
# x
## not an int or float
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x="1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
# y
## not an int or float
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(y="1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
# angle
## < 0
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, y=0, angle=-1)]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## > 360
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, y=0, angle=361)]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
# name
## not a string
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, name=1)]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
# color
## not a string
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color=1)]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## not enough commas
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1 0, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1 0 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1 0 0 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## not enough parts
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color=", 0, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, , 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, 0, , 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, 0, 0, ")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color=", , , ")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## not a number in all positions
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="r, 1, 1, 1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, g, 1, 1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, 1, b, 1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, 1, 1, a")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## too many parts
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="1, 0, 0, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## < 0 in each position
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="-1, 0, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="0, -1, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="0, 0, -1, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="0, 0, 0, -1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## > 1 in each position
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="2, 0, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="0, 2, 0, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="0, 0, 2, 0")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, color="0, 0, 0, 2")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
# identifier
## duplicate
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, identifier="guide1"), dict(y=0, identifier="guide1")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## below min
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, identifier=u"\0x1F")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
## above max
infoObject = self.makeInfoObject()
infoObject.guidelines = [dict(x=0, identifier=u"\0x7F")]
writer = UFOWriter(self.dstDir, formatVersion=3)
self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
class UFO3ReadDataTestCase(unittest.TestCase):

View File

@ -1405,6 +1405,106 @@ def _fontInfoKerningPrefixValidator(value):
return False
return True
def _fontInfoGuidelinesValidator(value):
"""
Version 3+.
"""
if not isinstance(value, list):
return True
identifiers = set()
for guide in value:
if not _fontInfoGuidelineValidator(guide):
return False
identifier = guide.get("identifier")
if identifier is not None:
if identifier in identifiers:
return False
identifiers.add(identifier)
return True
def _fontInfoGuidelineValidator(value):
"""
Version 3+.
"""
dictPrototype = dict(
x=((int, float), False), y=((int, float), False), angle=((int, float), False),
name=(basestring, False), color=(basestring, False), identifier=(basestring, False)
)
if not _fontInfoDictValidator(value, dictPrototype):
return False
x = value.get("x")
y = value.get("y")
angle = value.get("angle")
# x or y must be present
if x is None and y is None:
return False
# if x or y are None, angle must not be present
if x is None or y is None:
if angle is not None:
return False
# angle must be between 0 and 360
if angle is not None:
if angle < 0:
return False
if angle > 360:
return False
# identifier must be 1 or more characters
identifier = value.get("identifier")
if identifier is not None and not _fontInfoIdentifierValidator(identifier):
return False
# color must follow the proper format
color = value.get("color")
if color is not None and not _fontInfoColorValidator(color):
return False
return True
def _fontInfoIdentifierValidator(value):
"""
Version 3+.
"""
validCharactersMin = 0x20
validCharactersMax = 0x7E
if not isinstance(value, basestring):
return False
if not value:
return False
for c in value:
c = ord(c)
if c < validCharactersMin or c > validCharactersMax:
return False
return True
def _fontInfoColorValidator(value):
"""
Version 3+.
"""
if not isinstance(value, basestring):
return False
parts = value.split(",")
if len(parts) != 4:
return False
for part in parts:
part = part.strip()
converted = False
try:
part = int(part)
converted = True
except ValueError:
pass
if not converted:
try:
part = float(part)
converted = True
except ValueError:
pass
if not converted:
return False
if part < 0:
return False
if part > 1:
return False
return True
# Value Options
_fontInfoOpenTypeHeadFlagsOptions = range(0, 14)
@ -1582,6 +1682,7 @@ _fontInfoAttributesVersion3ValueData.update({
"woffMetadataExtensions" : dict(type=list, valueValidator=_fontInfoWOFFMetadataExtensionsValidator),
"firstKerningGroupPrefix" : dict(type=basestring, valueValidator=_fontInfoKerningPrefixValidator),
"secondKerningGroupPrefix" : dict(type=basestring, valueValidator=_fontInfoKerningPrefixValidator),
"guidelines" : dict(type=list, valueValidator=_fontInfoGuidelinesValidator)
})
# insert the type validator for all attrs that