Repository: python-pillow/Pillow

Issue:
Support setting a default font
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?

Was printing CJK characters to an image but got an `AttributeError`. No doubt the default font wasn't supporting such characters.

### What did you expect to happen?

Understand I can use Unicode-friendly fonts everytime I do `text()`. However, can I somehow change the default font so that I won't need to change everywhere in my program `text()` with a font?

### What actually happened?

Checking the docs there's `PIL.ImageFont.load_default()` to 
> Load a “better than nothing” default font.
Well, it is very clear.
But can I provide some "better than nothing" default for my own use?

### What are your OS, Python and Pillow versions?

* OS: Ubuntu
* Python: 3.8.13
* Pillow: 9.2.0

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->

```python
from PIL import Image, ImageDraw

im = Image.new("RGB", (400, 300))
draw = ImageDraw.Draw(im)
draw.text((10, 10), "你好")

im.show()
```

It gives:
```
AttributeError: 'ImageFont' object has no attribute 'getmask2'
```



Discussion:
If you're looking for an immediate solution, you could patch Pillow. Using [one of our test fonts,](https://github.com/python-pillow/Pillow/blob/main/Tests/fonts/NotoSansJP-Regular.otf)
```python
from PIL import ImageFont
ImageFont.load_default = lambda: ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf")
```

In context,
```python
from PIL import Image, ImageDraw, ImageFont
ImageFont.load_default = lambda: ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf")

im = Image.new("RGB", (400, 300))
draw = ImageDraw.Draw(im)
draw.text((10, 10), "你好")

im.show()
```

Awesome! I am practicing an identical workaround.
However, patching `load_default` to set default isn't something we want to write in the documentation.
Looking forward to a consistent and reliable interface to handle this.

Resolving patch:
diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py
--- a/src/PIL/ImageDraw.py
+++ b/src/PIL/ImageDraw.py
@@ -46,6 +46,8 @@
 
 
 class ImageDraw:
+    font = None
+
     def __init__(self, im, mode=None):
         """
         Create a drawing instance.
@@ -86,12 +88,16 @@ def __init__(self, im, mode=None):
         else:
             self.fontmode = "L"  # aliasing is okay for other modes
         self.fill = 0
-        self.font = None
 
     def getfont(self):
         """
         Get the current default font.
 
+        To set the default font for all future ImageDraw instances::
+
+            from PIL import ImageDraw, ImageFont
+            ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf")
+
         :returns: An image font."""
         if not self.font:
             # FIXME: should add a font repository


Test patch:
diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py
--- a/Tests/test_imagedraw.py
+++ b/Tests/test_imagedraw.py
@@ -1314,6 +1314,23 @@ def test_stroke_multiline():
     assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_multiline.png", 3.3)
 
 
+def test_setting_default_font():
+    # Arrange
+    im = Image.new("RGB", (100, 250))
+    draw = ImageDraw.Draw(im)
+    font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120)
+
+    # Act
+    ImageDraw.ImageDraw.font = font
+
+    # Assert
+    try:
+        assert draw.getfont() == font
+    finally:
+        ImageDraw.ImageDraw.font = None
+        assert isinstance(draw.getfont(), ImageFont.ImageFont)
+
+
 def test_same_color_outline():
     # Prepare shape
     x0, y0 = 5, 5