Repository: kivy/kivy

Issue:
`PIL` text provider: Critical issues if `font_size` is `< 1`
**Software Versions**
* Python: `3.8.9`
* OS: Windows
* Kivy: `2.3.0rc2`
* Kivy installation method: `python -m pip install --pre "kivy[base]"` / `pip install Kivy==2.3.0rc2`


**Describe the bug**
After upgrading from Pillow 9.x.x to Pillow 10.x.x, some errors are occurring, some with no clear cause. Some critical errors already existed in previous versions of kivy/Pillow.

🔴  Note: This same code works correctly (without errors) with the `"sdl2"` text provider


# Issue 1:
### Works in `9.x.x`, breaks in `10.x.x`:
```python
import os
os.environ["KIVY_TEXT"] = "pil"

from kivy.app import App
from kivy.uix.label import Label


class LabelTest(App):
    def build(self):
        return Label(text='Kivy rulz', font_size=0)

LabelTest().run()
```

### Error:
```
   File "lib\site-packages\kivy\uix\label.py", line 432, in texture_update
     self._label.refresh()
   File "lib\site-packages\kivy\core\text\__init__.py", line 836, in refresh
     sz = self.render()
   File "lib\site-packages\kivy\core\text\__init__.py", line 772, in render
     options['space_width'] = self.get_extents(' ')[0]
   File "lib\site-packages\kivy\core\text\text_pil.py", line 39, in get_extents
     font = self._select_font()
   File "lib\site-packages\kivy\core\text\text_pil.py", line 33, in _select_font
     font = ImageFont.truetype(fontname, fontsize)
   File "lib\site-packages\PIL\ImageFont.py", line 797, in truetype
     return freetype(font)
   File "lib\site-packages\PIL\ImageFont.py", line 794, in freetype
     return FreeTypeFont(font, size, index, encoding, layout_engine)
   File "lib\site-packages\PIL\ImageFont.py", line 226, in __init__
     self.font = core.getfont(
 OSError: invalid ppem value
```

# Issue 2:

### Breaks in `9.x.x`, and in `10.x.x`, and in previous versions of kivy:

```python
import os
os.environ["KIVY_TEXT"] = "pil"

from kivy.app import App
from kivy.uix.label import Label


class LabelTest(App):
    def build(self):
        return Label(text='Kivy rulz', font_size=-10)

LabelTest().run()
```

### Error:
```
   File "lib\site-packages\kivy\uix\label.py", line 432, in texture_update
     self._label.refresh()
   File "lib\site-packages\kivy\core\text\__init__.py", line 836, in refresh
     sz = self.render()
   File "lib\site-packages\kivy\core\text\__init__.py", line 812, in render
     w, h, clipped = layout_text(text, lines, (0, 0), (uw, uh), options,
   File "kivy\\core\\text\\text_layout.pyx", line 408, in kivy.core.text.text_layout.layout_text
   File "kivy\\core\\text\\text_layout.pyx", line 235, in kivy.core.text.text_layout.layout_text_unrestricted
   File "lib\site-packages\kivy\core\text\text_pil.py", line 37, in get_extents
     left, top, right, bottom = font.getbbox(text)
   File "lib\site-packages\PIL\ImageFont.py", line 408, in getbbox
     size, offset = self.font.getsize(
 OSError: invalid argument
```

# Issue 3:
### Works in `9.x.x`, breaks in `10.x.x`:
```
There is still no minimum reproducible code
```

### Error:
```
File "lib\site-packages\kivy\core\text\__init__.py", line 828, in _texture_fill
    self.render(real=True)
  File "lib\site-packages\kivy\core\text\markup.py", line 143, in render
    ret = self._render_real()
  File "lib\site-packages\kivy\core\text\__init__.py", line 752, in _render_real
    self.render_lines(lines, options, self._render_text, y, size)
  File "lib\site-packages\kivy\core\text\markup.py", line 500, in render_lines
    render_text(word.text, x, y + script_pos)
  File "lib\site-packages\kivy\core\text\text_pil.py", line 66, in _render_text
    self._pil_draw.text((int(x), int(y)),
  File "lib\site-packages\PIL\ImageDraw.py", line 565, in text
    draw_text(ink)
  File "lib\site-packages\PIL\ImageDraw.py", line 508, in draw_text
    mask, offset = font.getmask2(
  File "lib\site-packages\PIL\ImageFont.py", line 580, in getmask2
    offset = self.font.render(
OSError: unsupported bitmap pixel mode
```

**Expected behavior**
Behave like the `sdl2` text provider

### Proposed solutions
- issues 1 and 2 -  Limit within the `PIL` text provider the font size to `> 0`
- issue 3 -  If the root cause of the issue is not identified, treat the error on the line [66](https://github.com/kivy/kivy/blob/master/kivy/core/text/text_pil.py#L66):
```python
self._pil_draw.text((int(x), int(y)),
                    text, font=self._select_font(), fill=color)
```



Discussion:
### The third issue also appears to be related to font size (works if `font_size=1`):

```python
from PIL import Image, ImageDraw, ImageFont

font_path = 'path/to/font.ttf'
font_size = 0

image = Image.new('RGB', (100, 100), color='white')
draw = ImageDraw.Draw(image)

font = ImageFont.truetype(font_path, font_size)

text = "Hello, World!"
position = (0, 0)

draw.text(position, text, font=font, fill='black')

image.show()
```

Error:
```
Traceback (most recent call last):
  File "main.py", line 1143, in <module>
    draw.text(position, text, font=font, fill='black')
  File "lib\site-packages\PIL\ImageDraw.py", line 565, in text
    draw_text(ink)
  File "lib\site-packages\PIL\ImageDraw.py", line 508, in draw_text
    mask, offset = font.getmask2(
  File "lib\site-packages\PIL\ImageFont.py", line 580, in getmask2
    offset = self.font.render(
OSError: unsupported bitmap pixel mode
```





Resolving patch:
diff --git a/kivy/core/text/text_pil.py b/kivy/core/text/text_pil.py
--- a/kivy/core/text/text_pil.py
+++ b/kivy/core/text/text_pil.py
@@ -19,6 +19,9 @@ class LabelPIL(LabelBase):
     _cache = {}
 
     def _select_font(self):
+        if self.options['font_size'] < 1:
+            return None
+
         fontsize = int(self.options['font_size'])
         fontname = self.options['font_name_r']
         try:
@@ -34,6 +37,9 @@ def _select_font(self):
 
     def get_extents(self, text):
         font = self._select_font()
+        if not font:
+            return 0, 0
+
         left, top, right, bottom = font.getbbox(text)
         ascent, descent = font.getmetrics()
 
@@ -54,17 +60,19 @@ def _render_begin(self):
         self._pil_draw = ImageDraw.Draw(self._pil_im)
 
     def _render_text(self, text, x, y):
+        font = self._select_font()
+        if not font:
+            return
+
         color = tuple([int(c * 255) for c in self.options['color']])
 
         # Adjust x and y position to avoid text cutoff
         if self.options['limit_render_to_text_bbox']:
-            font = self._select_font()
             bbox = font.getbbox(text)
             x -= bbox[0]
             y -= bbox[1]
 
-        self._pil_draw.text((int(x), int(y)),
-                            text, font=self._select_font(), fill=color)
+        self._pil_draw.text((int(x), int(y)), text, font=font, fill=color)
 
     def _render_end(self):
         data = ImageData(self._size[0], self._size[1],


Test patch: