From 18d34b287f88f62fd185619daa3ed624d4801450 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 10 Nov 2021 14:34:20 +0200 Subject: [PATCH 1/3] Add support for pickling TrueType fonts --- Tests/test_pickle.py | 52 +++++++++++++++++++++++++++++++++++-- docs/releasenotes/9.0.0.rst | 15 +++++++++++ src/PIL/ImageFont.py | 7 +++++ 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index a10dcec8c59..f87801d7ed3 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -2,9 +2,12 @@ import pytest -from PIL import Image +from PIL import Image, ImageDraw, ImageFont -from .helper import skip_unless_feature +from .helper import assert_image_equal, skip_unless_feature + +FONT_SIZE = 20 +FONT_PATH = "Tests/fonts/DejaVuSans/DejaVuSans.ttf" def helper_pickle_file(tmp_path, pickle, protocol, test_file, mode): @@ -92,3 +95,48 @@ def test_pickle_tell(): # Assert assert unpickled_image.tell() == 0 + + +def helper_assert_pickled_font_images(font1, font2): + # Arrange + im1 = Image.new(mode="RGBA", size=(300, 100)) + im2 = Image.new(mode="RGBA", size=(300, 100)) + draw1 = ImageDraw.Draw(im1) + draw2 = ImageDraw.Draw(im2) + txt = "Hello World!" + + # Act + draw1.text((10, 10), txt, font=font1) + draw2.text((10, 10), txt, font=font2) + + # Assert + assert_image_equal(im1, im2) + + +@pytest.mark.parametrize("protocol", list(range(0, pickle.HIGHEST_PROTOCOL + 1))) +def test_pickle_font_string(protocol): + # Arrange + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + # Act: roundtrip + pickled_font = pickle.dumps(font, protocol) + unpickled_font = pickle.loads(pickled_font) + + # Assert + helper_assert_pickled_font_images(font, unpickled_font) + + +@pytest.mark.parametrize("protocol", list(range(0, pickle.HIGHEST_PROTOCOL + 1))) +def test_pickle_font_file(tmp_path, protocol): + # Arrange + font = ImageFont.truetype(FONT_PATH, FONT_SIZE) + filename = str(tmp_path / "temp.pkl") + + # Act: roundtrip + with open(filename, "wb") as f: + pickle.dump(font, f, protocol) + with open(filename, "rb") as f: + unpickled_font = pickle.load(f) + + # Assert + helper_assert_pickled_font_images(font, unpickled_font) diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index db3c16ac66d..748e54dd7d6 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -72,6 +72,21 @@ Support has been added for the "title" argument in argument will also now be supported, e.g. ``im.show(title="My Image")`` and ``ImageShow.show(im, title="My Image")``. +Added support for pickling TrueType fonts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +TrueType fonts may now be pickled and unpickled. For example: + +.. code-block:: python + + from PIL import Image, ImageDraw, ImageFont + + font = ImageFont.truetype("arial.ttf", size=30) + pickled_font = pickle.dumps(font1, protocol=pickle.HIGHEST_PROTOCOL) + + # Later... + unpickled_font = pickle.loads(pickled_font) + Security ======== diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index a212110a88c..805c8fff96b 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -196,6 +196,13 @@ def load_from_bytes(f): else: load_from_bytes(font) + def __getstate__(self): + return [self.path, self.size, self.index, self.encoding, self.layout_engine] + + def __setstate__(self, state): + path, size, index, encoding, layout_engine = state + self.__init__(path, size, index, encoding, layout_engine) + def _multiline_split(self, text): split_character = "\n" if isinstance(text, str) else b"\n" return text.split(split_character) From 81af72bb89ab0d24761a017ae6843902c6bac27c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 11 Nov 2021 11:27:13 +0200 Subject: [PATCH 2/3] Fix code example Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/releasenotes/9.0.0.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index 748e54dd7d6..5ed065ce1a0 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -79,10 +79,11 @@ TrueType fonts may now be pickled and unpickled. For example: .. code-block:: python - from PIL import Image, ImageDraw, ImageFont + import pickle + from PIL import ImageFont font = ImageFont.truetype("arial.ttf", size=30) - pickled_font = pickle.dumps(font1, protocol=pickle.HIGHEST_PROTOCOL) + pickled_font = pickle.dumps(font, protocol=pickle.HIGHEST_PROTOCOL) # Later... unpickled_font = pickle.loads(pickled_font) From 9f7f340bb768fb0691eee564cb91c66de1a6330e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 11 Nov 2021 14:46:48 +0200 Subject: [PATCH 3/3] Move to 'Other Changes' --- docs/releasenotes/9.0.0.rst | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index 5ed065ce1a0..067bda83215 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -72,6 +72,17 @@ Support has been added for the "title" argument in argument will also now be supported, e.g. ``im.show(title="My Image")`` and ``ImageShow.show(im, title="My Image")``. +Security +======== + +TODO +^^^^ + +TODO + +Other Changes +============= + Added support for pickling TrueType fonts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,19 +98,3 @@ TrueType fonts may now be pickled and unpickled. For example: # Later... unpickled_font = pickle.loads(pickled_font) - -Security -======== - -TODO -^^^^ - -TODO - -Other Changes -============= - -TODO -^^^^ - -TODO