ufoLib/glifLib: use TupleEnum instead of namedtuple for {UFO,GLIF}FormatVersion
Also add dedicated Unsupported{UFO,GLIF}Format exceptions in fontTools.ufoLib.errors module
This commit is contained in:
parent
f120be17e4
commit
57f4904363
@ -4,7 +4,7 @@ from os import fsdecode
|
|||||||
import logging
|
import logging
|
||||||
import zipfile
|
import zipfile
|
||||||
import enum
|
import enum
|
||||||
import collections
|
from collections import OrderedDict
|
||||||
import fs
|
import fs
|
||||||
import fs.base
|
import fs.base
|
||||||
import fs.subfs
|
import fs.subfs
|
||||||
@ -14,13 +14,12 @@ import fs.osfs
|
|||||||
import fs.zipfs
|
import fs.zipfs
|
||||||
import fs.tempfs
|
import fs.tempfs
|
||||||
import fs.tools
|
import fs.tools
|
||||||
from fontTools.misc.py23 import tostr
|
|
||||||
from fontTools.misc import plistlib
|
from fontTools.misc import plistlib
|
||||||
from fontTools.ufoLib.validators import *
|
from fontTools.ufoLib.validators import *
|
||||||
from fontTools.ufoLib.filenames import userNameToFileName
|
from fontTools.ufoLib.filenames import userNameToFileName
|
||||||
from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
|
from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
|
||||||
from fontTools.ufoLib.errors import UFOLibError
|
from fontTools.ufoLib.errors import UFOLibError
|
||||||
from fontTools.ufoLib.utils import numberTypes
|
from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A library for importing .ufo files and their descendants.
|
A library for importing .ufo files and their descendants.
|
||||||
@ -94,32 +93,10 @@ LAYERINFO_FILENAME = "layerinfo.plist"
|
|||||||
DEFAULT_LAYER_NAME = "public.default"
|
DEFAULT_LAYER_NAME = "public.default"
|
||||||
|
|
||||||
|
|
||||||
class FormatVersion(collections.namedtuple("FormatVersion", ["major", "minor"])):
|
class UFOFormatVersion(tuple, _VersionTupleEnumMixin, enum.Enum):
|
||||||
"""Convert single-digit formatVersion into (major, minor=0) namedtuple.
|
FORMAT_1_0 = (1, 0)
|
||||||
|
FORMAT_2_0 = (2, 0)
|
||||||
This allows to ease transition to new format versions that define a minor
|
FORMAT_3_0 = (3, 0)
|
||||||
component, without breaking API for old formats where only major was defined.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
|
||||||
if len(args) == 1 and isinstance(args[0], collections.abc.Iterable):
|
|
||||||
args = tuple(args[0])
|
|
||||||
if "minor" not in kwargs and len(args) < 2:
|
|
||||||
kwargs["minor"] = 0
|
|
||||||
return super().__new__(cls, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
UFO_FORMAT_1_0 = FormatVersion(1)
|
|
||||||
UFO_FORMAT_2_0 = FormatVersion(2)
|
|
||||||
UFO_FORMAT_3_0 = FormatVersion(3)
|
|
||||||
|
|
||||||
supportedUFOFormatVersions = [
|
|
||||||
UFO_FORMAT_1_0,
|
|
||||||
UFO_FORMAT_2_0,
|
|
||||||
UFO_FORMAT_3_0,
|
|
||||||
]
|
|
||||||
|
|
||||||
LATEST_UFO_FORMAT = sorted(supportedUFOFormatVersions)[-1]
|
|
||||||
|
|
||||||
|
|
||||||
class UFOFileStructure(enum.Enum):
|
class UFOFileStructure(enum.Enum):
|
||||||
@ -315,7 +292,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
return self._formatVersionMajor
|
return self._formatVersion.major
|
||||||
|
|
||||||
formatVersion = property(
|
formatVersion = property(
|
||||||
_get_formatVersion,
|
_get_formatVersion,
|
||||||
@ -327,7 +304,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
"""The (major, minor) format version of the UFO.
|
"""The (major, minor) format version of the UFO.
|
||||||
This is determined by reading metainfo.plist during __init__.
|
This is determined by reading metainfo.plist during __init__.
|
||||||
"""
|
"""
|
||||||
return FormatVersion(self._formatVersionMajor, self._formatVersionMinor)
|
return self._formatVersion
|
||||||
|
|
||||||
def _get_fileStructure(self):
|
def _get_fileStructure(self):
|
||||||
return self._fileStructure
|
return self._fileStructure
|
||||||
@ -435,29 +412,33 @@ class UFOReader(_UFOBaseIO):
|
|||||||
data = self._getPlist(METAINFO_FILENAME)
|
data = self._getPlist(METAINFO_FILENAME)
|
||||||
if validate and not isinstance(data, dict):
|
if validate and not isinstance(data, dict):
|
||||||
raise UFOLibError("metainfo.plist is not properly formatted.")
|
raise UFOLibError("metainfo.plist is not properly formatted.")
|
||||||
formatVersionMajor = data["formatVersion"]
|
try:
|
||||||
|
formatVersionMajor = data["formatVersion"]
|
||||||
|
except KeyError:
|
||||||
|
raise UFOLibError(
|
||||||
|
f"Missing required formatVersion in '{METAINFO_FILENAME}' on {self.fs}"
|
||||||
|
)
|
||||||
formatVersionMinor = data.setdefault("formatVersionMinor", 0)
|
formatVersionMinor = data.setdefault("formatVersionMinor", 0)
|
||||||
if validate:
|
|
||||||
if not (
|
|
||||||
isinstance(formatVersionMajor, int) and
|
|
||||||
isinstance(formatVersionMinor, int)
|
|
||||||
):
|
|
||||||
raise UFOLibError(
|
|
||||||
"formatVersion must be specified as an integer in '%s' on %s"
|
|
||||||
% (METAINFO_FILENAME, self.fs)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (formatVersionMajor, formatVersionMinor) not in supportedUFOFormatVersions:
|
try:
|
||||||
|
formatVersion = UFOFormatVersion((formatVersionMajor, formatVersionMinor))
|
||||||
|
except ValueError as e:
|
||||||
unsupportedMsg = (
|
unsupportedMsg = (
|
||||||
"Unsupported UFO format (%d.%d) in '%s' on %s"
|
f"Unsupported UFO format ({formatVersionMajor}.{formatVersionMinor}) "
|
||||||
% (formatVersionMajor, formatVersionMinor, METAINFO_FILENAME, self.fs)
|
f"in '{METAINFO_FILENAME}' on {self.fs}"
|
||||||
)
|
)
|
||||||
if validate:
|
if validate:
|
||||||
raise UFOLibError(unsupportedMsg)
|
from fontTools.ufoLib.errors import UnsupportedUFOFormat
|
||||||
logger.warn(
|
|
||||||
"%s. Some data may be skipped or parsed incorrectly", unsupportedMsg
|
|
||||||
)
|
|
||||||
|
|
||||||
|
raise UnsupportedUFOFormat(unsupportedMsg) from e
|
||||||
|
|
||||||
|
formatVersion = UFOFormatVersion.default()
|
||||||
|
logger.warning(
|
||||||
|
"%s. Assuming the latest supported version (%s). "
|
||||||
|
"Some data may be skipped or parsed incorrectly",
|
||||||
|
unsupportedMsg, formatVersion
|
||||||
|
)
|
||||||
|
data["formatVersionTuple"] = formatVersion
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def readMetaInfo(self, validate=None):
|
def readMetaInfo(self, validate=None):
|
||||||
@ -468,8 +449,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
to the class's validate value, can be overridden.
|
to the class's validate value, can be overridden.
|
||||||
"""
|
"""
|
||||||
data = self._readMetaInfo(validate=validate)
|
data = self._readMetaInfo(validate=validate)
|
||||||
self._formatVersionMajor = data["formatVersion"]
|
self._formatVersion = data["formatVersionTuple"]
|
||||||
self._formatVersionMinor = data["formatVersionMinor"]
|
|
||||||
|
|
||||||
# groups.plist
|
# groups.plist
|
||||||
|
|
||||||
@ -485,7 +465,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
# handle up conversion
|
# handle up conversion
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
self._upConvertKerning(validate)
|
self._upConvertKerning(validate)
|
||||||
groups = self._upConvertedKerningData["groups"]
|
groups = self._upConvertedKerningData["groups"]
|
||||||
# normal
|
# normal
|
||||||
@ -516,7 +496,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
"""
|
"""
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
if self._formatVersionMajor >= 3:
|
if self._formatVersion >= UFOFormatVersion.FORMAT_3_0:
|
||||||
return dict(side1={}, side2={})
|
return dict(side1={}, side2={})
|
||||||
# use the public group reader to force the load and
|
# use the public group reader to force the load and
|
||||||
# conversion of the data if it hasn't happened yet.
|
# conversion of the data if it hasn't happened yet.
|
||||||
@ -546,7 +526,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
infoDict = self._readInfo(validate)
|
infoDict = self._readInfo(validate)
|
||||||
infoDataToSet = {}
|
infoDataToSet = {}
|
||||||
# version 1
|
# version 1
|
||||||
if self._formatVersionMajor == 1:
|
if self._formatVersion == UFOFormatVersion.FORMAT_1_0:
|
||||||
for attr in fontInfoAttributesVersion1:
|
for attr in fontInfoAttributesVersion1:
|
||||||
value = infoDict.get(attr)
|
value = infoDict.get(attr)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
@ -554,15 +534,15 @@ class UFOReader(_UFOBaseIO):
|
|||||||
infoDataToSet = _convertFontInfoDataVersion1ToVersion2(infoDataToSet)
|
infoDataToSet = _convertFontInfoDataVersion1ToVersion2(infoDataToSet)
|
||||||
infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
|
infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
|
||||||
# version 2
|
# version 2
|
||||||
elif self._formatVersionMajor == 2:
|
elif self._formatVersion == UFOFormatVersion.FORMAT_2_0:
|
||||||
for attr, dataValidationDict in list(fontInfoAttributesVersion2ValueData.items()):
|
for attr, dataValidationDict in list(fontInfoAttributesVersion2ValueData.items()):
|
||||||
value = infoDict.get(attr)
|
value = infoDict.get(attr)
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
infoDataToSet[attr] = value
|
infoDataToSet[attr] = value
|
||||||
infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
|
infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
|
||||||
# version 3
|
# version 3.x
|
||||||
elif self._formatVersionMajor == 3:
|
elif self._formatVersion.major == UFOFormatVersion.FORMAT_3_0.major:
|
||||||
for attr, dataValidationDict in list(fontInfoAttributesVersion3ValueData.items()):
|
for attr, dataValidationDict in list(fontInfoAttributesVersion3ValueData.items()):
|
||||||
value = infoDict.get(attr)
|
value = infoDict.get(attr)
|
||||||
if value is None:
|
if value is None:
|
||||||
@ -570,7 +550,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
infoDataToSet[attr] = value
|
infoDataToSet[attr] = value
|
||||||
# unsupported version
|
# unsupported version
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError(self._formatVersion)
|
||||||
# validate data
|
# validate data
|
||||||
if validate:
|
if validate:
|
||||||
infoDataToSet = validateInfoVersion3Data(infoDataToSet)
|
infoDataToSet = validateInfoVersion3Data(infoDataToSet)
|
||||||
@ -597,7 +577,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
# handle up conversion
|
# handle up conversion
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
self._upConvertKerning(validate)
|
self._upConvertKerning(validate)
|
||||||
kerningNested = self._upConvertedKerningData["kerning"]
|
kerningNested = self._upConvertedKerningData["kerning"]
|
||||||
# normal
|
# normal
|
||||||
@ -655,7 +635,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
|
|
||||||
``validate`` will validate the layer contents.
|
``validate`` will validate the layer contents.
|
||||||
"""
|
"""
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
return [(DEFAULT_LAYER_NAME, DEFAULT_GLYPHS_DIRNAME)]
|
return [(DEFAULT_LAYER_NAME, DEFAULT_GLYPHS_DIRNAME)]
|
||||||
contents = self._getPlist(LAYERCONTENTS_FILENAME)
|
contents = self._getPlist(LAYERCONTENTS_FILENAME)
|
||||||
if validate:
|
if validate:
|
||||||
@ -730,7 +710,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
)
|
)
|
||||||
return GlyphSet(
|
return GlyphSet(
|
||||||
glyphSubFS,
|
glyphSubFS,
|
||||||
ufoFormatVersion=self.formatVersionTuple,
|
ufoFormatVersion=self._formatVersion,
|
||||||
validateRead=validateRead,
|
validateRead=validateRead,
|
||||||
validateWrite=validateWrite,
|
validateWrite=validateWrite,
|
||||||
)
|
)
|
||||||
@ -787,7 +767,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
``validate`` will validate the data, by default it is set to the
|
``validate`` will validate the data, by default it is set to the
|
||||||
class's validate value, can be overridden.
|
class's validate value, can be overridden.
|
||||||
"""
|
"""
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
return []
|
return []
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
@ -837,9 +817,9 @@ class UFOReader(_UFOBaseIO):
|
|||||||
"""
|
"""
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
raise UFOLibError(
|
raise UFOLibError(
|
||||||
"Reading images is not allowed in UFO %d." % self._formatVersionMajor
|
f"Reading images is not allowed in UFO {self._formatVersion.major}."
|
||||||
)
|
)
|
||||||
fileName = fsdecode(fileName)
|
fileName = fsdecode(fileName)
|
||||||
try:
|
try:
|
||||||
@ -880,20 +860,30 @@ class UFOWriter(UFOReader):
|
|||||||
By default, the written data will be validated before writing. Set ``validate`` to
|
By default, the written data will be validated before writing. Set ``validate`` to
|
||||||
``False`` if you do not want to validate the data. Validation can also be overriden
|
``False`` if you do not want to validate the data. Validation can also be overriden
|
||||||
on a per method level if desired.
|
on a per method level if desired.
|
||||||
|
|
||||||
|
The ``formatVersion`` argument allows to specify the UFO format version as a tuple
|
||||||
|
of integers (major, minor), or as a single integer for the major digit only (minor
|
||||||
|
is implied as 0). By default the latest formatVersion will be used; currently it's
|
||||||
|
3.0, which is equivalent to formatVersion=(3, 0).
|
||||||
|
|
||||||
|
An UnsupportedUFOFormat exception is raised if the requested UFO formatVersion is
|
||||||
|
not supported.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
path,
|
path,
|
||||||
formatVersion=LATEST_UFO_FORMAT,
|
formatVersion=None,
|
||||||
fileCreator="com.github.fonttools.ufoLib",
|
fileCreator="com.github.fonttools.ufoLib",
|
||||||
structure=None,
|
structure=None,
|
||||||
validate=True,
|
validate=True,
|
||||||
):
|
):
|
||||||
if not isinstance(formatVersion, FormatVersion):
|
try:
|
||||||
formatVersion = FormatVersion(formatVersion)
|
formatVersion = UFOFormatVersion(formatVersion)
|
||||||
if formatVersion not in supportedUFOFormatVersions:
|
except ValueError as e:
|
||||||
raise UFOLibError("Unsupported UFO format (%d.%d)." % formatVersion)
|
from fontTools.ufoLib.errors import UnsupportedUFOFormat
|
||||||
|
|
||||||
|
raise UnsupportedUFOFormat(f"Unsupported UFO format: {formatVersion!r}") from e
|
||||||
|
|
||||||
if hasattr(path, "__fspath__"): # support os.PathLike objects
|
if hasattr(path, "__fspath__"): # support os.PathLike objects
|
||||||
path = path.__fspath__()
|
path = path.__fspath__()
|
||||||
@ -1001,7 +991,7 @@ class UFOWriter(UFOReader):
|
|||||||
|
|
||||||
# establish some basic stuff
|
# establish some basic stuff
|
||||||
self._path = fsdecode(path)
|
self._path = fsdecode(path)
|
||||||
self._formatVersionMajor, self._formatVersionMinor = formatVersion
|
self._formatVersion = formatVersion
|
||||||
self._fileCreator = fileCreator
|
self._fileCreator = fileCreator
|
||||||
self._downConversionKerningData = None
|
self._downConversionKerningData = None
|
||||||
self._validate = validate
|
self._validate = validate
|
||||||
@ -1010,21 +1000,21 @@ class UFOWriter(UFOReader):
|
|||||||
previousFormatVersion = None
|
previousFormatVersion = None
|
||||||
if self._havePreviousFile:
|
if self._havePreviousFile:
|
||||||
metaInfo = self._readMetaInfo(validate=validate)
|
metaInfo = self._readMetaInfo(validate=validate)
|
||||||
previousFormatVersion = FormatVersion(
|
previousFormatVersion = metaInfo["formatVersionTuple"]
|
||||||
metaInfo["formatVersion"], metaInfo["formatVersionMinor"]
|
|
||||||
)
|
|
||||||
# catch down conversion
|
# catch down conversion
|
||||||
if previousFormatVersion > formatVersion:
|
if previousFormatVersion > formatVersion:
|
||||||
raise UFOLibError(
|
from fontTools.ufoLib.errors import UnsupportedUFOFormat
|
||||||
"The UFO located at this path is a higher version (%d.%d) than the "
|
|
||||||
"version (%d.%d) that is trying to be written. This is not supported."
|
raise UnsupportedUFOFormat(
|
||||||
% (*previousFormatVersion, *formatVersion)
|
"The UFO located at this path is a higher version "
|
||||||
|
f"({previousFormatVersion}) than the version ({formatVersion}) "
|
||||||
|
"that is trying to be written. This is not supported."
|
||||||
)
|
)
|
||||||
# handle the layer contents
|
# handle the layer contents
|
||||||
self.layerContents = {}
|
self.layerContents = {}
|
||||||
if previousFormatVersion is not None and previousFormatVersion.major >= 3:
|
if previousFormatVersion is not None and previousFormatVersion.major >= 3:
|
||||||
# already exists
|
# already exists
|
||||||
self.layerContents = collections.OrderedDict(self._readLayerContents(validate))
|
self.layerContents = OrderedDict(self._readLayerContents(validate))
|
||||||
else:
|
else:
|
||||||
# previous < 3
|
# previous < 3
|
||||||
# imply the layer contents
|
# imply the layer contents
|
||||||
@ -1158,10 +1148,10 @@ class UFOWriter(UFOReader):
|
|||||||
def _writeMetaInfo(self):
|
def _writeMetaInfo(self):
|
||||||
metaInfo = dict(
|
metaInfo = dict(
|
||||||
creator=self._fileCreator,
|
creator=self._fileCreator,
|
||||||
formatVersion=self._formatVersionMajor,
|
formatVersion=self._formatVersion.major,
|
||||||
)
|
)
|
||||||
if self._formatVersionMinor != 0:
|
if self._formatVersion.minor != 0:
|
||||||
metaInfo["formatVersionMinor"] = self._formatVersionMinor
|
metaInfo["formatVersionMinor"] = self._formatVersion.minor
|
||||||
self._writePlist(METAINFO_FILENAME, metaInfo)
|
self._writePlist(METAINFO_FILENAME, metaInfo)
|
||||||
|
|
||||||
# groups.plist
|
# groups.plist
|
||||||
@ -1182,7 +1172,7 @@ class UFOWriter(UFOReader):
|
|||||||
This is the same form returned by UFOReader's
|
This is the same form returned by UFOReader's
|
||||||
getKerningGroupConversionRenameMaps method.
|
getKerningGroupConversionRenameMaps method.
|
||||||
"""
|
"""
|
||||||
if self._formatVersionMajor >= 3:
|
if self._formatVersion >= UFOFormatVersion.FORMAT_3_0:
|
||||||
return # XXX raise an error here
|
return # XXX raise an error here
|
||||||
# flip the dictionaries
|
# flip the dictionaries
|
||||||
remap = {}
|
remap = {}
|
||||||
@ -1207,7 +1197,10 @@ class UFOWriter(UFOReader):
|
|||||||
if not valid:
|
if not valid:
|
||||||
raise UFOLibError(message)
|
raise UFOLibError(message)
|
||||||
# down convert
|
# down convert
|
||||||
if self._formatVersionMajor < 3 and self._downConversionKerningData is not None:
|
if (
|
||||||
|
self._formatVersion < UFOFormatVersion.FORMAT_3_0
|
||||||
|
and self._downConversionKerningData is not None
|
||||||
|
):
|
||||||
remap = self._downConversionKerningData["groupRenameMap"]
|
remap = self._downConversionKerningData["groupRenameMap"]
|
||||||
remappedGroups = {}
|
remappedGroups = {}
|
||||||
# there are some edge cases here that are ignored:
|
# there are some edge cases here that are ignored:
|
||||||
@ -1268,14 +1261,14 @@ class UFOWriter(UFOReader):
|
|||||||
continue
|
continue
|
||||||
infoData[attr] = value
|
infoData[attr] = value
|
||||||
# down convert data if necessary and validate
|
# down convert data if necessary and validate
|
||||||
if self._formatVersionMajor == 3:
|
if self._formatVersion == UFOFormatVersion.FORMAT_3_0:
|
||||||
if validate:
|
if validate:
|
||||||
infoData = validateInfoVersion3Data(infoData)
|
infoData = validateInfoVersion3Data(infoData)
|
||||||
elif self._formatVersionMajor == 2:
|
elif self._formatVersion == UFOFormatVersion.FORMAT_2_0:
|
||||||
infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
|
infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
|
||||||
if validate:
|
if validate:
|
||||||
infoData = validateInfoVersion2Data(infoData)
|
infoData = validateInfoVersion2Data(infoData)
|
||||||
elif self._formatVersionMajor == 1:
|
elif self._formatVersion == UFOFormatVersion.FORMAT_1_0:
|
||||||
infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
|
infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
|
||||||
if validate:
|
if validate:
|
||||||
infoData = validateInfoVersion2Data(infoData)
|
infoData = validateInfoVersion2Data(infoData)
|
||||||
@ -1318,7 +1311,10 @@ class UFOWriter(UFOReader):
|
|||||||
if not isinstance(value, numberTypes):
|
if not isinstance(value, numberTypes):
|
||||||
raise UFOLibError(invalidFormatMessage)
|
raise UFOLibError(invalidFormatMessage)
|
||||||
# down convert
|
# down convert
|
||||||
if self._formatVersionMajor < 3 and self._downConversionKerningData is not None:
|
if (
|
||||||
|
self._formatVersion < UFOFormatVersion.FORMAT_3_0
|
||||||
|
and self._downConversionKerningData is not None
|
||||||
|
):
|
||||||
remap = self._downConversionKerningData["groupRenameMap"]
|
remap = self._downConversionKerningData["groupRenameMap"]
|
||||||
remappedKerning = {}
|
remappedKerning = {}
|
||||||
for (side1, side2), value in list(kerning.items()):
|
for (side1, side2), value in list(kerning.items()):
|
||||||
@ -1368,7 +1364,7 @@ class UFOWriter(UFOReader):
|
|||||||
"""
|
"""
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
if self._formatVersionMajor == 1:
|
if self._formatVersion == UFOFormatVersion.FORMAT_1_0:
|
||||||
raise UFOLibError("features.fea is not allowed in UFO Format Version 1.")
|
raise UFOLibError("features.fea is not allowed in UFO Format Version 1.")
|
||||||
if validate:
|
if validate:
|
||||||
if not isinstance(features, str):
|
if not isinstance(features, str):
|
||||||
@ -1387,7 +1383,7 @@ class UFOWriter(UFOReader):
|
|||||||
"""
|
"""
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
return
|
return
|
||||||
if layerOrder is not None:
|
if layerOrder is not None:
|
||||||
newOrder = []
|
newOrder = []
|
||||||
@ -1435,9 +1431,12 @@ class UFOWriter(UFOReader):
|
|||||||
if validateWrite is None:
|
if validateWrite is None:
|
||||||
validateWrite = self._validate
|
validateWrite = self._validate
|
||||||
# only default can be written in < 3
|
# only default can be written in < 3
|
||||||
if self._formatVersionMajor < 3 and (not defaultLayer or layerName is not None):
|
if (
|
||||||
|
self._formatVersion < UFOFormatVersion.FORMAT_3_0
|
||||||
|
and (not defaultLayer or layerName is not None)
|
||||||
|
):
|
||||||
raise UFOLibError(
|
raise UFOLibError(
|
||||||
"Only the default layer can be writen in UFO %d." % self._formatVersionMajor
|
f"Only the default layer can be writen in UFO {self._formatVersion.major}."
|
||||||
)
|
)
|
||||||
# locate a layer name when None has been given
|
# locate a layer name when None has been given
|
||||||
if layerName is None and defaultLayer:
|
if layerName is None and defaultLayer:
|
||||||
@ -1449,42 +1448,27 @@ class UFOWriter(UFOReader):
|
|||||||
elif layerName is None and not defaultLayer:
|
elif layerName is None and not defaultLayer:
|
||||||
raise UFOLibError("A layer name must be provided for non-default layers.")
|
raise UFOLibError("A layer name must be provided for non-default layers.")
|
||||||
# move along to format specific writing
|
# move along to format specific writing
|
||||||
if self._formatVersionMajor == 1:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
return self._getGlyphSetFormatVersion1(validateRead, validateWrite, glyphNameToFileNameFunc=glyphNameToFileNameFunc)
|
return self._getDefaultGlyphSet(validateRead, validateWrite, glyphNameToFileNameFunc=glyphNameToFileNameFunc)
|
||||||
elif self._formatVersionMajor == 2:
|
elif self._formatVersion.major == UFOFormatVersion.FORMAT_3_0.major:
|
||||||
return self._getGlyphSetFormatVersion2(validateRead, validateWrite, glyphNameToFileNameFunc=glyphNameToFileNameFunc)
|
|
||||||
elif self._formatVersionMajor == 3:
|
|
||||||
return self._getGlyphSetFormatVersion3(
|
return self._getGlyphSetFormatVersion3(
|
||||||
validateRead,
|
validateRead,
|
||||||
validateWrite,
|
validateWrite,
|
||||||
layerName=layerName,
|
layerName=layerName,
|
||||||
defaultLayer=defaultLayer,
|
defaultLayer=defaultLayer,
|
||||||
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
||||||
formatVersionMinor=self._formatVersionMinor,
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise AssertionError(self._formatVersionMajor)
|
raise NotImplementedError(self._formatVersion)
|
||||||
|
|
||||||
def _getGlyphSetFormatVersion1(self, validateRead, validateWrite, glyphNameToFileNameFunc=None):
|
def _getDefaultGlyphSet(self, validateRead, validateWrite, glyphNameToFileNameFunc=None):
|
||||||
from fontTools.ufoLib.glifLib import GlyphSet
|
from fontTools.ufoLib.glifLib import GlyphSet
|
||||||
|
|
||||||
glyphSubFS = self.fs.makedir(DEFAULT_GLYPHS_DIRNAME, recreate=True)
|
glyphSubFS = self.fs.makedir(DEFAULT_GLYPHS_DIRNAME, recreate=True)
|
||||||
return GlyphSet(
|
return GlyphSet(
|
||||||
glyphSubFS,
|
glyphSubFS,
|
||||||
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
||||||
ufoFormatVersion=UFO_FORMAT_1_0,
|
ufoFormatVersion=self._formatVersion,
|
||||||
validateRead=validateRead,
|
|
||||||
validateWrite=validateWrite,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _getGlyphSetFormatVersion2(self, validateRead, validateWrite, glyphNameToFileNameFunc=None):
|
|
||||||
from fontTools.ufoLib.glifLib import GlyphSet
|
|
||||||
|
|
||||||
glyphSubFS = self.fs.makedir(DEFAULT_GLYPHS_DIRNAME, recreate=True)
|
|
||||||
return GlyphSet(
|
|
||||||
glyphSubFS,
|
|
||||||
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
|
||||||
ufoFormatVersion=UFO_FORMAT_2_0,
|
|
||||||
validateRead=validateRead,
|
validateRead=validateRead,
|
||||||
validateWrite=validateWrite,
|
validateWrite=validateWrite,
|
||||||
)
|
)
|
||||||
@ -1496,7 +1480,6 @@ class UFOWriter(UFOReader):
|
|||||||
layerName=None,
|
layerName=None,
|
||||||
defaultLayer=True,
|
defaultLayer=True,
|
||||||
glyphNameToFileNameFunc=None,
|
glyphNameToFileNameFunc=None,
|
||||||
formatVersionMinor=0,
|
|
||||||
):
|
):
|
||||||
from fontTools.ufoLib.glifLib import GlyphSet
|
from fontTools.ufoLib.glifLib import GlyphSet
|
||||||
|
|
||||||
@ -1533,7 +1516,7 @@ class UFOWriter(UFOReader):
|
|||||||
return GlyphSet(
|
return GlyphSet(
|
||||||
glyphSubFS,
|
glyphSubFS,
|
||||||
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
glyphNameToFileNameFunc=glyphNameToFileNameFunc,
|
||||||
ufoFormatVersion=FormatVersion(3, formatVersionMinor),
|
ufoFormatVersion=self._formatVersion,
|
||||||
validateRead=validateRead,
|
validateRead=validateRead,
|
||||||
validateWrite=validateWrite,
|
validateWrite=validateWrite,
|
||||||
)
|
)
|
||||||
@ -1546,7 +1529,7 @@ class UFOWriter(UFOReader):
|
|||||||
layerName, it is up to the caller to inform that object that
|
layerName, it is up to the caller to inform that object that
|
||||||
the directory it represents has changed.
|
the directory it represents has changed.
|
||||||
"""
|
"""
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
# ignore renaming glyph sets for UFO1 UFO2
|
# ignore renaming glyph sets for UFO1 UFO2
|
||||||
# just write the data from the default layer
|
# just write the data from the default layer
|
||||||
return
|
return
|
||||||
@ -1585,7 +1568,7 @@ class UFOWriter(UFOReader):
|
|||||||
"""
|
"""
|
||||||
Remove the glyph set matching layerName.
|
Remove the glyph set matching layerName.
|
||||||
"""
|
"""
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
# ignore deleting glyph sets for UFO1 UFO2 as there are no layers
|
# ignore deleting glyph sets for UFO1 UFO2 as there are no layers
|
||||||
# just write the data from the default layer
|
# just write the data from the default layer
|
||||||
return
|
return
|
||||||
@ -1615,9 +1598,9 @@ class UFOWriter(UFOReader):
|
|||||||
"""
|
"""
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
raise UFOLibError(
|
raise UFOLibError(
|
||||||
"Images are not allowed in UFO %d." % self._formatVersionMajor
|
f"Images are not allowed in UFO {self._formatVersion.major}."
|
||||||
)
|
)
|
||||||
fileName = fsdecode(fileName)
|
fileName = fsdecode(fileName)
|
||||||
if validate:
|
if validate:
|
||||||
@ -1631,9 +1614,9 @@ class UFOWriter(UFOReader):
|
|||||||
Remove the file named fileName from the
|
Remove the file named fileName from the
|
||||||
images directory.
|
images directory.
|
||||||
"""
|
"""
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
raise UFOLibError(
|
raise UFOLibError(
|
||||||
"Images are not allowed in UFO %d." % self._formatVersionMajor
|
f"Images are not allowed in UFO {self._formatVersion.major}."
|
||||||
)
|
)
|
||||||
self.removePath(f"{IMAGES_DIRNAME}/{fsdecode(fileName)}")
|
self.removePath(f"{IMAGES_DIRNAME}/{fsdecode(fileName)}")
|
||||||
|
|
||||||
@ -1645,9 +1628,9 @@ class UFOWriter(UFOReader):
|
|||||||
"""
|
"""
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validate
|
validate = self._validate
|
||||||
if self._formatVersionMajor < 3:
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
||||||
raise UFOLibError(
|
raise UFOLibError(
|
||||||
"Images are not allowed in UFO %d." % self._formatVersionMajor
|
f"Images are not allowed in UFO {self._formatVersion.major}."
|
||||||
)
|
)
|
||||||
sourcePath = f"{IMAGES_DIRNAME}/{fsdecode(sourceFileName)}"
|
sourcePath = f"{IMAGES_DIRNAME}/{fsdecode(sourceFileName)}"
|
||||||
destPath = f"{IMAGES_DIRNAME}/{fsdecode(destFileName)}"
|
destPath = f"{IMAGES_DIRNAME}/{fsdecode(destFileName)}"
|
||||||
|
@ -4,5 +4,13 @@ class UFOLibError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedUFOFormat(UFOLibError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class GlifLibError(UFOLibError):
|
class GlifLibError(UFOLibError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedGLIFFormat(GlifLibError):
|
||||||
|
pass
|
||||||
|
@ -11,6 +11,7 @@ glyph data. See the class doc string for details.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import enum
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import fs
|
import fs
|
||||||
@ -33,14 +34,8 @@ from fontTools.ufoLib.validators import (
|
|||||||
glyphLibValidator,
|
glyphLibValidator,
|
||||||
)
|
)
|
||||||
from fontTools.misc import etree
|
from fontTools.misc import etree
|
||||||
from fontTools.ufoLib import (
|
from fontTools.ufoLib import _UFOBaseIO, UFOFormatVersion
|
||||||
_UFOBaseIO,
|
from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
|
||||||
FormatVersion,
|
|
||||||
LATEST_UFO_FORMAT,
|
|
||||||
UFO_FORMAT_3_0,
|
|
||||||
supportedUFOFormatVersions,
|
|
||||||
)
|
|
||||||
from fontTools.ufoLib.utils import numberTypes
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@ -60,15 +55,27 @@ logger = logging.getLogger(__name__)
|
|||||||
CONTENTS_FILENAME = "contents.plist"
|
CONTENTS_FILENAME = "contents.plist"
|
||||||
LAYERINFO_FILENAME = "layerinfo.plist"
|
LAYERINFO_FILENAME = "layerinfo.plist"
|
||||||
|
|
||||||
GLIF_FORMAT_1_0 = FormatVersion(1)
|
|
||||||
GLIF_FORMAT_2_0 = FormatVersion(2)
|
|
||||||
|
|
||||||
supportedGLIFFormatVersions = {
|
class GLIFFormatVersion(tuple, _VersionTupleEnumMixin, enum.Enum):
|
||||||
GLIF_FORMAT_1_0,
|
FORMAT_1_0 = (1, 0)
|
||||||
GLIF_FORMAT_2_0,
|
FORMAT_2_0 = (2, 0)
|
||||||
}
|
|
||||||
|
|
||||||
LATEST_GLIF_FORMAT = sorted(supportedGLIFFormatVersions)[-1]
|
@classmethod
|
||||||
|
def default(cls, ufoFormatVersion=None):
|
||||||
|
if ufoFormatVersion is not None:
|
||||||
|
return max(cls.supported_versions(ufoFormatVersion))
|
||||||
|
return super().default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def supported_versions(cls, ufoFormatVersion=None):
|
||||||
|
if ufoFormatVersion is None:
|
||||||
|
# if ufo format unspecified, return all the supported GLIF formats
|
||||||
|
return super().supported_versions()
|
||||||
|
# else only return the GLIF formats supported by the given UFO format
|
||||||
|
versions = {cls.FORMAT_1_0}
|
||||||
|
if ufoFormatVersion >= UFOFormatVersion.FORMAT_3_0:
|
||||||
|
versions.add(cls.FORMAT_2_0)
|
||||||
|
return frozenset(versions)
|
||||||
|
|
||||||
|
|
||||||
# ------------
|
# ------------
|
||||||
@ -125,7 +132,7 @@ class GlyphSet(_UFOBaseIO):
|
|||||||
self,
|
self,
|
||||||
path,
|
path,
|
||||||
glyphNameToFileNameFunc=None,
|
glyphNameToFileNameFunc=None,
|
||||||
ufoFormatVersion=LATEST_UFO_FORMAT,
|
ufoFormatVersion=None,
|
||||||
validateRead=True,
|
validateRead=True,
|
||||||
validateWrite=True,
|
validateWrite=True,
|
||||||
):
|
):
|
||||||
@ -142,10 +149,18 @@ class GlyphSet(_UFOBaseIO):
|
|||||||
``validateRead`` will validate read operations. Its default is ``True``.
|
``validateRead`` will validate read operations. Its default is ``True``.
|
||||||
``validateWrite`` will validate write operations. Its default is ``True``.
|
``validateWrite`` will validate write operations. Its default is ``True``.
|
||||||
"""
|
"""
|
||||||
if not isinstance(ufoFormatVersion, FormatVersion):
|
try:
|
||||||
ufoFormatVersion = FormatVersion(ufoFormatVersion)
|
ufoFormatVersion = UFOFormatVersion(ufoFormatVersion)
|
||||||
if ufoFormatVersion not in supportedUFOFormatVersions and validateRead:
|
except ValueError as e:
|
||||||
raise GlifLibError("Unsupported UFO format version: %d.%d" % ufoFormatVersion)
|
from fontTools.ufoLib.errors import UnsupportedUFOFormat
|
||||||
|
|
||||||
|
raise UnsupportedUFOFormat(
|
||||||
|
f"Unsupported UFO format: {ufoFormatVersion!r}"
|
||||||
|
) from e
|
||||||
|
|
||||||
|
if hasattr(path, "__fspath__"): # support os.PathLike objects
|
||||||
|
path = path.__fspath__()
|
||||||
|
|
||||||
if isinstance(path, str):
|
if isinstance(path, str):
|
||||||
try:
|
try:
|
||||||
filesystem = fs.osfs.OSFS(path)
|
filesystem = fs.osfs.OSFS(path)
|
||||||
@ -369,9 +384,7 @@ class GlyphSet(_UFOBaseIO):
|
|||||||
validate = self._validateRead
|
validate = self._validateRead
|
||||||
text = self.getGLIF(glyphName)
|
text = self.getGLIF(glyphName)
|
||||||
tree = _glifTreeFromString(text)
|
tree = _glifTreeFromString(text)
|
||||||
formatVersions = {GLIF_FORMAT_1_0}
|
formatVersions = GLIFFormatVersion.supported_versions(self.ufoFormatVersionTuple)
|
||||||
if self.ufoFormatVersionTuple >= UFO_FORMAT_3_0:
|
|
||||||
formatVersions.add(GLIF_FORMAT_2_0)
|
|
||||||
_readGlyphFromTree(tree, glyphObject, pointPen, formatVersions=formatVersions, validate=validate)
|
_readGlyphFromTree(tree, glyphObject, pointPen, formatVersions=formatVersions, validate=validate)
|
||||||
|
|
||||||
def writeGlyph(self, glyphName, glyphObject=None, drawPointsFunc=None, formatVersion=None, validate=None):
|
def writeGlyph(self, glyphName, glyphObject=None, drawPointsFunc=None, formatVersion=None, validate=None):
|
||||||
@ -401,23 +414,35 @@ class GlyphSet(_UFOBaseIO):
|
|||||||
The GLIF format version will be chosen based on the ufoFormatVersion
|
The GLIF format version will be chosen based on the ufoFormatVersion
|
||||||
passed during the creation of this object. If a particular format
|
passed during the creation of this object. If a particular format
|
||||||
version is desired, it can be passed with the formatVersion argument.
|
version is desired, it can be passed with the formatVersion argument.
|
||||||
|
The formatVersion argument accepts either a tuple of integers for
|
||||||
|
(major, minor), or a single integer for the major digit only (with
|
||||||
|
minor digit implied as 0).
|
||||||
|
|
||||||
|
An UnsupportedGLIFFormat exception is raised if the requested GLIF
|
||||||
|
formatVersion is not supported.
|
||||||
|
|
||||||
``validate`` will validate the data, by default it is set to the
|
``validate`` will validate the data, by default it is set to the
|
||||||
class's ``validateWrite`` value, can be overridden.
|
class's ``validateWrite`` value, can be overridden.
|
||||||
"""
|
"""
|
||||||
if formatVersion is None:
|
if formatVersion is None:
|
||||||
if self.ufoFormatVersionTuple >= UFO_FORMAT_3_0:
|
formatVersion = GLIFFormatVersion.default(self.ufoFormatVersionTuple)
|
||||||
formatVersion = GLIF_FORMAT_2_0
|
else:
|
||||||
else:
|
try:
|
||||||
formatVersion = GLIF_FORMAT_1_0
|
formatVersion = GLIFFormatVersion(formatVersion)
|
||||||
elif not isinstance(formatVersion, FormatVersion):
|
except ValueError as e:
|
||||||
formatVersion = FormatVersion(formatVersion)
|
from fontTools.ufoLib.errors import UnsupportedGLIFFormat
|
||||||
if formatVersion not in supportedGLIFFormatVersions:
|
|
||||||
raise GlifLibError("Unsupported GLIF format version: %d.%d" % formatVersion)
|
raise UnsupportedGLIFFormat(
|
||||||
if formatVersion.major == 2 and self.ufoFormatVersionTuple.major < 3:
|
f"Unsupported GLIF format version: {formatVersion!r}"
|
||||||
raise GlifLibError(
|
) from e
|
||||||
"Unsupported GLIF format version (%d.%d) for UFO format version %d.%d."
|
if formatVersion not in GLIFFormatVersion.supported_versions(
|
||||||
% (*formatVersion, *self.ufoFormatVersionTuple)
|
self.ufoFormatVersionTuple
|
||||||
|
):
|
||||||
|
from fontTools.ufoLib.errors import UnsupportedGLIFFormat
|
||||||
|
|
||||||
|
raise UnsupportedGLIFFormat(
|
||||||
|
f"Unsupported GLIF format version ({formatVersion!s}) "
|
||||||
|
f"for UFO format version {self.ufoFormatVersionTuple!s}."
|
||||||
)
|
)
|
||||||
if validate is None:
|
if validate is None:
|
||||||
validate = self._validateWrite
|
validate = self._validateWrite
|
||||||
@ -555,7 +580,7 @@ def readGlyphFromString(
|
|||||||
aString,
|
aString,
|
||||||
glyphObject=None,
|
glyphObject=None,
|
||||||
pointPen=None,
|
pointPen=None,
|
||||||
formatVersions=supportedGLIFFormatVersions,
|
formatVersions=None,
|
||||||
validate=True,
|
validate=True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -586,17 +611,37 @@ def readGlyphFromString(
|
|||||||
conforming to the PointPen protocol as the 'pointPen' argument.
|
conforming to the PointPen protocol as the 'pointPen' argument.
|
||||||
This argument may be None if you don't need the outline data.
|
This argument may be None if you don't need the outline data.
|
||||||
|
|
||||||
The formatVersions argument defined the GLIF format versions
|
The formatVersions optional argument defines the GLIF format versions
|
||||||
that are allowed to be read.
|
that are allowed to be read.
|
||||||
|
The type is Optional[Iterable[Tuple[int, int], int]]. It can contain
|
||||||
|
either integers (for the major versions to be allowed, with minor
|
||||||
|
digits defaulting to 0), or tuples of integers to specify both
|
||||||
|
(major, minor) versions.
|
||||||
|
|
||||||
``validate`` will validate the read data. It is set to ``True`` by default.
|
``validate`` will validate the read data. It is set to ``True`` by default.
|
||||||
"""
|
"""
|
||||||
tree = _glifTreeFromString(aString)
|
tree = _glifTreeFromString(aString)
|
||||||
formatVersions = {
|
|
||||||
FormatVersion(v) if not isinstance(v, FormatVersion) else v
|
if formatVersions is None:
|
||||||
for v in formatVersions
|
validFormatVersions = GLIFFormatVersion.supported_versions()
|
||||||
}
|
else:
|
||||||
_readGlyphFromTree(tree, glyphObject, pointPen, formatVersions=formatVersions, validate=validate)
|
validFormatVersions, invalidFormatVersions = set(), set()
|
||||||
|
for v in formatVersions:
|
||||||
|
try:
|
||||||
|
formatVersion = GLIFFormatVersion(v)
|
||||||
|
except ValueError:
|
||||||
|
invalidFormatVersions.add(v)
|
||||||
|
else:
|
||||||
|
validFormatVersions.add(formatVersion)
|
||||||
|
if not validFormatVersions:
|
||||||
|
raise ValueError(
|
||||||
|
"None of the requested GLIF formatVersions are supported: "
|
||||||
|
f"{formatVersions!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
_readGlyphFromTree(
|
||||||
|
tree, glyphObject, pointPen, formatVersions=validFormatVersions, validate=validate
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _writeGlyphToBytes(
|
def _writeGlyphToBytes(
|
||||||
@ -604,10 +649,16 @@ def _writeGlyphToBytes(
|
|||||||
glyphObject=None,
|
glyphObject=None,
|
||||||
drawPointsFunc=None,
|
drawPointsFunc=None,
|
||||||
writer=None,
|
writer=None,
|
||||||
formatVersion=LATEST_GLIF_FORMAT,
|
formatVersion=None,
|
||||||
validate=True,
|
validate=True,
|
||||||
):
|
):
|
||||||
"""Return .glif data for a glyph as a UTF-8 encoded bytes string."""
|
"""Return .glif data for a glyph as a UTF-8 encoded bytes string."""
|
||||||
|
try:
|
||||||
|
formatVersion = GLIFFormatVersion(formatVersion)
|
||||||
|
except ValueError:
|
||||||
|
from fontTools.ufoLib.errors import UnsupportedGLIFFormat
|
||||||
|
|
||||||
|
raise UnsupportedGLIFFormat("Unsupported GLIF format version: {formatVersion!r}")
|
||||||
# start
|
# start
|
||||||
if validate and not isinstance(glyphName, str):
|
if validate and not isinstance(glyphName, str):
|
||||||
raise GlifLibError("The glyph name is not properly formatted.")
|
raise GlifLibError("The glyph name is not properly formatted.")
|
||||||
@ -660,7 +711,7 @@ def writeGlyphToString(
|
|||||||
glyphName,
|
glyphName,
|
||||||
glyphObject=None,
|
glyphObject=None,
|
||||||
drawPointsFunc=None,
|
drawPointsFunc=None,
|
||||||
formatVersion=LATEST_GLIF_FORMAT,
|
formatVersion=None,
|
||||||
validate=True,
|
validate=True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@ -688,11 +739,14 @@ def writeGlyphToString(
|
|||||||
proper PointPen methods to transfer the outline to the .glif file.
|
proper PointPen methods to transfer the outline to the .glif file.
|
||||||
|
|
||||||
The GLIF format version can be specified with the formatVersion argument.
|
The GLIF format version can be specified with the formatVersion argument.
|
||||||
|
This accepts either a tuple of integers for (major, minor), or a single
|
||||||
|
integer for the major digit only (with minor digit implied as 0).
|
||||||
|
|
||||||
|
An UnsupportedGLIFFormat exception is raised if the requested UFO
|
||||||
|
formatVersion is not supported.
|
||||||
|
|
||||||
``validate`` will validate the written data. It is set to ``True`` by default.
|
``validate`` will validate the written data. It is set to ``True`` by default.
|
||||||
"""
|
"""
|
||||||
if not isinstance(formatVersion, FormatVersion):
|
|
||||||
formatVersion = FormatVersion(formatVersion)
|
|
||||||
data = _writeGlyphToBytes(
|
data = _writeGlyphToBytes(
|
||||||
glyphName,
|
glyphName,
|
||||||
glyphObject=glyphObject,
|
glyphObject=glyphObject,
|
||||||
@ -931,7 +985,7 @@ def _readGlyphFromTree(
|
|||||||
tree,
|
tree,
|
||||||
glyphObject=None,
|
glyphObject=None,
|
||||||
pointPen=None,
|
pointPen=None,
|
||||||
formatVersions=supportedGLIFFormatVersions,
|
formatVersions=GLIFFormatVersion.supported_versions(),
|
||||||
validate=True,
|
validate=True,
|
||||||
):
|
):
|
||||||
# check the format version
|
# check the format version
|
||||||
@ -940,27 +994,40 @@ def _readGlyphFromTree(
|
|||||||
raise GlifLibError("Unspecified format version in GLIF.")
|
raise GlifLibError("Unspecified format version in GLIF.")
|
||||||
formatVersionMinor = tree.get("formatMinor", 0)
|
formatVersionMinor = tree.get("formatMinor", 0)
|
||||||
try:
|
try:
|
||||||
formatVersion = FormatVersion(int(formatVersionMajor), int(formatVersionMinor))
|
formatVersion = GLIFFormatVersion((int(formatVersionMajor), int(formatVersionMinor)))
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
raise GlifLibError(
|
msg = "Unsupported GLIF format: %s.%s" % (formatVersionMajor, formatVersionMinor)
|
||||||
"Invalid GLIF format version: (%r, %r)" % (formatVersionMajor, formatVersionMinor)
|
|
||||||
)
|
|
||||||
if validate and formatVersion not in formatVersions:
|
|
||||||
raise GlifLibError("Forbidden GLIF format version: %s.%s" % formatVersion)
|
|
||||||
|
|
||||||
readGlyphFromTree = _READ_GLYPH_FROM_TREE_FUNCS.get(formatVersion)
|
|
||||||
if not readGlyphFromTree:
|
|
||||||
msg = "Unsupported GLIF format version: %s.%s" % formatVersion
|
|
||||||
if validate:
|
if validate:
|
||||||
raise GlifLibError(msg)
|
from fontTools.ufoLib.errors import UnsupportedGLIFFormat
|
||||||
|
|
||||||
|
raise UnsupportedGLIFFormat(msg) from e
|
||||||
# warn but continue using the latest supported format
|
# warn but continue using the latest supported format
|
||||||
logger.warn("%s. Some data may be skipped or parsed incorrectly.", msg)
|
formatVersion = GLIFFormatVersion.default()
|
||||||
readGlyphFromTree = _READ_GLYPH_FROM_TREE_FUNCS[LATEST_GLIF_FORMAT]
|
logger.warning(
|
||||||
|
"%s. Assuming the latest supported version (%s). "
|
||||||
|
"Some data may be skipped or parsed incorrectly.",
|
||||||
|
msg,
|
||||||
|
formatVersion,
|
||||||
|
)
|
||||||
|
|
||||||
readGlyphFromTree(tree=tree, glyphObject=glyphObject, pointPen=pointPen, validate=validate)
|
if validate and formatVersion not in formatVersions:
|
||||||
|
raise GlifLibError(f"Forbidden GLIF format version: {formatVersion!s}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
readGlyphFromTree = _READ_GLYPH_FROM_TREE_FUNCS[formatVersion]
|
||||||
|
except KeyError:
|
||||||
|
raise NotImplementedError(formatVersion)
|
||||||
|
|
||||||
|
readGlyphFromTree(
|
||||||
|
tree=tree,
|
||||||
|
glyphObject=glyphObject,
|
||||||
|
pointPen=pointPen,
|
||||||
|
validate=validate,
|
||||||
|
formatMinor=formatVersion.minor,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=None):
|
def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=None, **kwargs):
|
||||||
# get the name
|
# get the name
|
||||||
_readName(glyphObject, tree, validate)
|
_readName(glyphObject, tree, validate)
|
||||||
# populate the sub elements
|
# populate the sub elements
|
||||||
@ -1098,8 +1165,8 @@ def _readGlyphFromTreeFormat2(
|
|||||||
|
|
||||||
|
|
||||||
_READ_GLYPH_FROM_TREE_FUNCS = {
|
_READ_GLYPH_FROM_TREE_FUNCS = {
|
||||||
GLIF_FORMAT_1_0: _readGlyphFromTreeFormat1,
|
GLIFFormatVersion.FORMAT_1_0: _readGlyphFromTreeFormat1,
|
||||||
GLIF_FORMAT_2_0: _readGlyphFromTreeFormat2,
|
GLIFFormatVersion.FORMAT_2_0: _readGlyphFromTreeFormat2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1585,12 +1652,10 @@ class GLIFPointPen(AbstractPointPen):
|
|||||||
part of .glif files.
|
part of .glif files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, element, formatVersion=LATEST_GLIF_FORMAT, identifiers=None, validate=True):
|
def __init__(self, element, formatVersion=None, identifiers=None, validate=True):
|
||||||
if identifiers is None:
|
if identifiers is None:
|
||||||
identifiers = set()
|
identifiers = set()
|
||||||
if not isinstance(formatVersion, FormatVersion):
|
self.formatVersion = GLIFFormatVersion(formatVersion)
|
||||||
formatVersion = FormatVersion(formatVersion)
|
|
||||||
self.formatVersion = formatVersion
|
|
||||||
self.identifiers = identifiers
|
self.identifiers = identifiers
|
||||||
self.outline = element
|
self.outline = element
|
||||||
self.contour = None
|
self.contour = None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user