From 4001ded1995a5686129e9e685a8909ef827ac68d Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 20 Dec 2018 14:18:59 +0000 Subject: [PATCH] override SFNTReader __deepcopy__ for 'file' isn't copyable; use it in varLib --- Lib/fontTools/ttLib/sfnt.py | 25 ++++++++++++++++++++++ Lib/fontTools/ttLib/ttFont.py | 13 ----------- Lib/fontTools/varLib/__init__.py | 3 ++- Lib/fontTools/varLib/interpolate_layout.py | 3 ++- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py index 6dc48baf5..e41030fbf 100644 --- a/Lib/fontTools/ttLib/sfnt.py +++ b/Lib/fontTools/ttLib/sfnt.py @@ -123,6 +123,31 @@ class SFNTReader(object): def close(self): self.file.close() + def __deepcopy__(self, memo): + """Overrides the default deepcopy of SFNTReader object, to make it work + in the case when TTFont is loaded with lazy=True, and thus reader holds a + reference to a file object which is not pickleable. + We work around it by manually copying the data into a in-memory stream. + """ + from copy import deepcopy + + cls = self.__class__ + obj = cls.__new__(cls) + for k, v in self.__dict__.items(): + if k == "file": + f = self.file + start = f.tell() + f.seek(0) + buf = BytesIO(f.read()) + f.seek(start) + buf.seek(start) + if hasattr(f, "name"): + buf.name = f.name + obj.file = buf + else: + obj.__dict__[k] = deepcopy(v, memo) + return obj + # default compression level for WOFF 1.0 tables and metadata ZLIB_COMPRESSION_LEVEL = 6 diff --git a/Lib/fontTools/ttLib/ttFont.py b/Lib/fontTools/ttLib/ttFont.py index 82a35fdfc..3a89592ff 100644 --- a/Lib/fontTools/ttLib/ttFont.py +++ b/Lib/fontTools/ttLib/ttFont.py @@ -693,19 +693,6 @@ class TTFont(object): """ return self["cmap"].getBestCmap(cmapPreferences=cmapPreferences) - def copy(self): - """Return a new TTFont instance containing the same data. - - >>> f1 = TTFont() - >>> f2 = f1.copy() - >>> f2 is not f1 - True - """ - buf = BytesIO() - self.save(buf) - buf.seek(0) - return TTFont(buf) - class _TTGlyphSet(object): diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index ead9bec51..ba5307136 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -40,6 +40,7 @@ from fontTools.designspaceLib import DesignSpaceDocument, AxisDescriptor from collections import OrderedDict, namedtuple import os.path import logging +from copy import deepcopy from pprint import pformat log = logging.getLogger("fontTools.varLib") @@ -764,7 +765,7 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): master_ttfs.append(None) # in-memory fonts have no path # Copy the base master to work from it - vf = master_fonts[ds.base_idx].copy() + vf = deepcopy(master_fonts[ds.base_idx]) # TODO append masters as named-instances as well; needs .designspace change. fvar = _add_fvar(vf, ds.axes, ds.instances) diff --git a/Lib/fontTools/varLib/interpolate_layout.py b/Lib/fontTools/varLib/interpolate_layout.py index c86f9a29c..f252149a5 100644 --- a/Lib/fontTools/varLib/interpolate_layout.py +++ b/Lib/fontTools/varLib/interpolate_layout.py @@ -8,6 +8,7 @@ from fontTools.varLib import models, VarLibError, load_designspace, load_masters from fontTools.varLib.merger import InstancerMerger import os.path import logging +from copy import deepcopy from pprint import pformat log = logging.getLogger("fontTools.varLib.interpolate_layout") @@ -37,7 +38,7 @@ def interpolate_layout(designspace, loc, master_finder=lambda s:s, mapped=False) log.info("Loading master fonts") master_fonts = load_masters(designspace, master_finder) - font = master_fonts[ds.base_idx].copy() + font = deepcopy(master_fonts[ds.base_idx]) log.info("Location: %s", pformat(loc)) if not mapped: