SFNTReader: define __getstate__/__setstate__ to reopen external file
Instead of copying to BytesIO, we can return the file name in getstate and reopen the file in setstate. This keeps the TTFont truly lazy as it avoids the extra copy
This commit is contained in:
parent
942fbfe07a
commit
72f9e7794a
@ -123,31 +123,29 @@ class SFNTReader(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
self.file.close()
|
self.file.close()
|
||||||
|
|
||||||
def __getstate__(self):
|
# We define custom __getstate__ and __setstate__ to make SFNTReader pickle-able
|
||||||
"""Makes SFNTReader pickle/deepcopy-able even with TTFont loaded as lazy=True.
|
# and deepcopy-able. When a TTFont is loaded as lazy=True, SFNTReader holds a
|
||||||
|
# reference to an external file object which is not pickleable. So in __getstate__
|
||||||
|
# we store the file name and current position, and in __setstate__ we reopen the
|
||||||
|
# same named file after unpickling.
|
||||||
|
|
||||||
If SFNTReader holds a reference to an external file object which is not
|
def __getstate__(self):
|
||||||
pickleable, we copy the file data to an in-memory bytes stream, which is
|
|
||||||
pickelable and behaves just like a file.
|
|
||||||
"""
|
|
||||||
if isinstance(self.file, BytesIO):
|
if isinstance(self.file, BytesIO):
|
||||||
|
# BytesIO is already pickleable, return the state unmodified
|
||||||
return self.__dict__
|
return self.__dict__
|
||||||
|
|
||||||
|
# remove unpickleable file attribute, and only store its name and pos
|
||||||
state = self.__dict__.copy()
|
state = self.__dict__.copy()
|
||||||
state["file"] = _copy_file_to_bytes_stream(state["file"])
|
del state["file"]
|
||||||
|
state["_filename"] = self.file.name
|
||||||
|
state["_filepos"] = self.file.tell()
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
def __setstate__(self, state):
|
||||||
def _copy_file_to_bytes_stream(f) -> BytesIO:
|
if "file" not in state:
|
||||||
# Copy bytes from file object to BytesIO stream.
|
self.file = open(state.pop("_filename"), "rb")
|
||||||
# The returned stream also records the original file `name` and current position.
|
self.file.seek(state.pop("_filepos"))
|
||||||
pos = f.tell()
|
self.__dict__.update(state)
|
||||||
f.seek(0)
|
|
||||||
buf = BytesIO(f.read())
|
|
||||||
f.seek(pos)
|
|
||||||
buf.seek(pos)
|
|
||||||
if hasattr(f, "name"):
|
|
||||||
buf.name = f.name
|
|
||||||
return buf
|
|
||||||
|
|
||||||
|
|
||||||
# default compression level for WOFF 1.0 tables and metadata
|
# default compression level for WOFF 1.0 tables and metadata
|
||||||
|
Loading…
x
Reference in New Issue
Block a user