From eb75089ba2fa5cc961b4a873c60a79fb1db7aafd Mon Sep 17 00:00:00 2001 From: pwarszawski Date: Sun, 8 Nov 2026 21:12:35 +0100 Subject: [PATCH] Python screens: ship Pillow with the Windows build, and the screen module for Pillow 10+ - The Windows build job installs Pillow into the Python 3.14 whose Lib, with site-packages, CMake copies next to the executable as python64\Lib, as the Linux job already does; the README says the same for a local build. - Both build jobs copy staticFiles/python/local (the Python 3 version of the module every cab screen subclasses) into python/local of the build. - The module puts back what Pillow 10 removed and the screen scripts use: ImageFont.getsize() and ImageDraw.textsize() on top of getbbox() / textbbox(), and Image.ANTIALIAS as LANCZOS. With an older Pillow nothing changes. Part of #300. --- .github/workflows/build_bin.yml | 25 ++++++++++++++++++- README.md | 2 +- .../python/local/abstractscreenrenderer.py | 20 ++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_bin.yml b/.github/workflows/build_bin.yml index 2f1781ed..d37817d5 100644 --- a/.github/workflows/build_bin.yml +++ b/.github/workflows/build_bin.yml @@ -45,6 +45,15 @@ jobs: python-version: "3.14" architecture: "x64" + # the cab screens are drawn with Pillow; CMake copies this Python's Lib, site-packages included, next to the + # executable as python64\Lib, so Pillow has to be installed here to come with the build (as on Linux) + - name: Install Pillow for the screens + shell: pwsh + run: | + $pythonExe = "${{ steps.python.outputs.python-path }}" + & $pythonExe -m pip install --no-cache-dir Pillow + & $pythonExe -c "from PIL import Image; print('Pillow:', Image.__version__)" + - name: Show environment shell: pwsh run: | @@ -114,6 +123,18 @@ jobs: -Recurse ` -Force + # the Python 3 version of the module every cab screen builds on; the game data still has the Python 2 one + New-Item ` + -ItemType Directory ` + -Force ` + -Path "$output/python/local" | Out-Null + + Copy-Item ` + -Path "staticFiles/python/local/*" ` + -Destination "$output/python/local" ` + -Recurse ` + -Force + - name: Upload Windows build uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: @@ -251,9 +272,11 @@ jobs: - name: Copy shaders and translations run: | - mkdir -p build/bin/shaders build/bin/lang + mkdir -p build/bin/shaders build/bin/lang build/bin/python/local cp -R shaders/. build/bin/shaders/ cp -R lang/. build/bin/lang/ + # the Python 3 version of the module every cab screen builds on; the game data still has the Python 2 one + cp -R staticFiles/python/local/. build/bin/python/local/ - name: Upload Linux build uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/README.md b/README.md index 47741a51..675026cc 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ MaSzyna compiles and runs natively on **Linux** and **Windows**. Other platforms ## Windows -Install **Python 3.14 x64** from [python.org](https://www.python.org/downloads/windows/) and include the optional **Python native development** feature (headers and `.lib` for embedding). Pass `-DPython3_ROOT_DIR="C:\Path\To\Python314"` on the `cmake` line when you want the layout under that directory used for embedding and for copying `Lib` / `DLLs` next to the executable. +Install **Python 3.14 x64** from [python.org](https://www.python.org/downloads/windows/) and include the optional **Python native development** feature (headers and `.lib` for embedding). Pass `-DPython3_ROOT_DIR="C:\Path\To\Python314"` on the `cmake` line when you want the layout under that directory used for embedding and for copying `Lib` / `DLLs` next to the executable. The cab screens are drawn with [Pillow](https://pypi.org/project/pillow/): install it into that Python (`python -m pip install Pillow`) before building, so it is copied along with `Lib`. **AppVeyor CI** builds with the worker image’s preinstalled Python at `C:/Python314-x64` (see [`appveyor.yml`](./appveyor.yml): `-DPython3_ROOT_DIR=...`). diff --git a/staticFiles/python/local/abstractscreenrenderer.py b/staticFiles/python/local/abstractscreenrenderer.py index ea8be2c5..3ebf2081 100644 --- a/staticFiles/python/local/abstractscreenrenderer.py +++ b/staticFiles/python/local/abstractscreenrenderer.py @@ -1,7 +1,25 @@ -from PIL import Image +from PIL import Image, ImageDraw, ImageFont from math import ceil, floor, cos, sin, radians import os +# Pillow 10 removed ImageFont.getsize(), ImageDraw.textsize() and Image.ANTIALIAS, which the screen scripts use; for +# Python 3.14 only a newer Pillow is available, so they are put back, on top of getbbox() / textbbox() and LANCZOS +def _font_getsize(self, text, *args, **kwargs): + box = self.getbbox(text, *args, **kwargs) + return (box[2], box[3]) + +def _draw_textsize(self, text, font=None, *args, **kwargs): + box = self.textbbox((0, 0), text, font, *args, **kwargs) + return (box[2], box[3]) + +for _fontclass in (ImageFont.FreeTypeFont, ImageFont.ImageFont, getattr(ImageFont, 'TransposedFont', None)): + if _fontclass is not None and not hasattr(_fontclass, 'getsize') and hasattr(_fontclass, 'getbbox'): + _fontclass.getsize = _font_getsize +if not hasattr(ImageDraw.ImageDraw, 'textsize'): + ImageDraw.ImageDraw.textsize = _draw_textsize +if not hasattr(Image, 'ANTIALIAS'): + Image.ANTIALIAS = Image.LANCZOS + class abstractscreenrenderer(): def __init__(self, lookup_path): self.width = 0