From 454391036723f89c39533953252bafbc358daace Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 21 Mar 2023 16:51:20 +0000 Subject: [PATCH] [ttFont] fail when input is not seekable and lazy=True the SFNTReader expects the input file to be seekable, and it already rewinds the file with file.seek(0) to get to the sfnt table directory. Thus, if TTFont is loaded with an unseekable file object and lazy=True, we raise a TTLibError requiring one to either pass a seekable input file, or to not set lazy=True (in which case the input is loaded in a seekable BytesIO) Fixes https://github.com/fonttools/fonttools/issues/3052 --- Lib/fontTools/ttLib/ttFont.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Lib/fontTools/ttLib/ttFont.py b/Lib/fontTools/ttLib/ttFont.py index 3618d3f91..cc1da3856 100644 --- a/Lib/fontTools/ttLib/ttFont.py +++ b/Lib/fontTools/ttLib/ttFont.py @@ -6,7 +6,7 @@ from fontTools.misc.loggingTools import deprecateArgument from fontTools.ttLib import TTLibError from fontTools.ttLib.ttGlyphSet import _TTGlyph, _TTGlyphSetCFF, _TTGlyphSetGlyf from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter -from io import BytesIO, StringIO +from io import BytesIO, StringIO, UnsupportedOperation import os import logging import traceback @@ -126,6 +126,7 @@ class TTFont(object): self.flavor = flavor self.flavorData = None return + seekable = True if not hasattr(file, "read"): closeStream = True # assume file is a string @@ -146,12 +147,20 @@ class TTFont(object): else: # assume "file" is a readable file object closeStream = False - if file.seekable(): - file.seek(0) + # SFNTReader wants the input file to be seekable. + # SpooledTemporaryFile has no seekable() on < 3.11, but still can seek: + # https://github.com/fonttools/fonttools/issues/3052 + if hasattr(file, "seekable"): + seekable = file.seekable() + elif hasattr(file, "seek"): + try: + file.seek(0) + except UnsupportedOperation: + seekable = False if not self.lazy: # read input file in memory and wrap a stream around it to allow overwriting - if file.seekable(): + if seekable: file.seek(0) tmp = BytesIO(file.read()) if hasattr(file, "name"): @@ -160,6 +169,8 @@ class TTFont(object): if closeStream: file.close() file = tmp + elif not seekable: + raise TTLibError("Input file must be seekable when lazy=True") self._tableCache = _tableCache self.reader = SFNTReader(file, checkChecksums, fontNumber=fontNumber) self.sfntVersion = self.reader.sfntVersion