Repository: python-pillow/Pillow

Issue:
Deprecate old FreeType?
We're supporting some very old versions of FreeType. Here's some version we check for, and the release date for that version:

| FreeType | Release date |
| - | - |
| 2.4 | 2010-07-12 |
| 2.5 | 2013-06-19 |
| 2.7 | 2016-09-08 |
| 2.9.1 | 2018-05-02 |
| 2.10 | 2019-03-15 |

The latest [FreeType 2.10.4](https://www.freetype.org/) fixes a severe vulnerability: "All users should update immediately".

We're only testing recent releases.

Shall we deprecate support for some old versions of FreeType?

It's likely the next major release Pillow 9.0.0 will be on 2022-01-02, due to the Python 3.6 EOL (2021-12-23), giving plenty of time for deprecation.

Checking some distros from https://repology.org/project/freetype/versions / https://packages.debian.org/source/buster/freetype:

| distro | FreeType |
| - | - |
| Alpine 3.10 | 2.10.0 |
| Alpine 3.11 | 2.10.1 |
| Alpine 3.12 | 2.10.4 |
| Amazon Linux 1 | 2.3.11 |
| Amazon Linux 2 | 2.4.11 / 2.8 |
| Arch | 2.10.4 |
| Centos 6 | 2.3.11 |
| Centos 7 | 2.8 |
| Centos 8 | 2.9.1 |
| Debian 10 Buster | 2.9.1 |
| Fedora 32 | 2.10.1 / 2.10.4 |
| Fedora 33 | 2.10.2 / 2.10.4 |
| Ubuntu 16.04 Xenial | 2.6.1 |
| Ubuntu 18.04 Bionic | 2.8.1 |
| Ubuntu 20.04 Focal | 2.10.1 |

Removing those distros which will be EOL before 2022-01-02 and sorting by FreeType version:

| distro | FreeType |
| - | - |
| Amazon Linux 2 | 2.4.11 / 2.8 |
| Centos 7 | 2.8 |
| Ubuntu 18.04 Bionic | 2.8.1 |
| Centos 8 | 2.9.1 |
| Debian 10 Buster | 2.9.1 |
| Ubuntu 20.04 Focal | 2.10.1 |
| Alpine 3.12 | 2.10.4 |
| Arch | 2.10.4 |

Suggesting 2.7 and older would be a good cutoff.

Shall we deprecate support for 2.7 and older (or something else)? Is it worth it?

What would the deprecation look like?



Discussion:
Apart from updating tests, the only change that I can see in the code is

```diff
diff --git a/src/_imagingft.c b/src/_imagingft.c
index 62db561e..17797c83 100644
--- a/src/_imagingft.c
+++ b/src/_imagingft.c
@@ -1034,12 +1034,7 @@ font_render(FontObject* self, PyObject* args)
             case FT_PIXEL_MODE_GRAY4:
                 if (!bitmap_converted_ready) {
 
-#if FREETYPE_MAJOR > 2 ||\
-    (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 6)
                     FT_Bitmap_Init(&bitmap_converted);
-#else
-                    FT_Bitmap_New(&bitmap_converted);
-#endif
                     bitmap_converted_ready = 1;
                 }
                 error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1);
```

On the day when we deprecate < 2.9.1, we can drop errors about not supporting variation fonts.

So the gain in code simplicity seems minimal to me. That said, if we wanted to deprecate older FreeType to encourage good security behaviour from users, or to better reflect the fact that we're not testing them properly anymore, then sure.

I would suggest throwing the `DeprecationWarning` in the constructor of `ImageFont.FreeTypeFont`.
It is worth noting that I would have missed the block above in #4955 without the tests (now removed in #5060), which would have unintentionally removed support for *some version* of FreeType before 2.7 (I did not look through the changelog, but 2.6.1 seems to be fine without it). So +1 to deprecate to reflect that it is no longer tested. Raising a warning in the constructor sounds like a good option.

Resolving patch:
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -32,6 +32,7 @@
     "sphinx.ext.autodoc",
     "sphinx.ext.intersphinx",
     "sphinx.ext.viewcode",
+    "sphinx_issues",
     "sphinx_removed_in",
 ]
 
diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py
--- a/src/PIL/ImageFont.py
+++ b/src/PIL/ImageFont.py
@@ -28,9 +28,10 @@
 import base64
 import os
 import sys
+import warnings
 from io import BytesIO
 
-from . import Image
+from . import Image, features
 from ._util import isDirectory, isPath
 
 LAYOUT_BASIC = 0
@@ -164,6 +165,21 @@ def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None)
         self.index = index
         self.encoding = encoding
 
+        try:
+            from packaging.version import parse as parse_version
+        except ImportError:
+            pass
+        else:
+            freetype_version = parse_version(features.version_module("freetype2"))
+            if freetype_version < parse_version("2.8"):
+                warnings.warn(
+                    "Support for FreeType 2.7 is deprecated and will be removed"
+                    " in Pillow 9 (2022-01-02). Please upgrade to FreeType 2.8 "
+                    "or newer, preferably FreeType 2.10.4 which fixes "
+                    "CVE-2020-15999.",
+                    DeprecationWarning,
+                )
+
         if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM):
             layout_engine = LAYOUT_BASIC
             if core.HAVE_RAQM:


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -103,7 +103,7 @@ jobs:
     - name: Docs
       if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.9
       run: |
-        python3 -m pip install sphinx-removed-in sphinx-rtd-theme
+        python3 -m pip install sphinx-issues sphinx-removed-in sphinx-rtd-theme
         make doccheck
 
     - name: After success
diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py
--- a/Tests/test_imagefont.py
+++ b/Tests/test_imagefont.py
@@ -998,3 +998,15 @@ def test_render_mono_size():
 
     draw.text((10, 10), "r" * 10, "black", ttf)
     assert_image_equal_tofile(im, "Tests/images/text_mono.gif")
+
+
+def test_freetype_deprecation(monkeypatch):
+    # Arrange: mock features.version_module to return fake FreeType version
+    def fake_version_module(module):
+        return "2.7"
+
+    monkeypatch.setattr(features, "version_module", fake_version_module)
+
+    # Act / Assert
+    with pytest.warns(DeprecationWarning):
+        ImageFont.truetype(FONT_PATH, FONT_SIZE)