diff --git a/common/fonts.py b/common/fonts.py new file mode 100644 index 000000000..99387a058 --- /dev/null +++ b/common/fonts.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# This file is part of pi-stomp. +# +# pi-stomp is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pi-stomp is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with pi-stomp. If not, see . + +from pathlib import Path + +FONTS_DIR = Path(__file__).parent.parent / "fonts" + + +def font_path(name: str) -> str: + """Return the absolute path to a bundled font by filename.""" + return str(FONTS_DIR / name) diff --git a/emulator/__init__.py b/emulator/__init__.py index 472fb2ea9..5f9f4a0a8 100644 --- a/emulator/__init__.py +++ b/emulator/__init__.py @@ -2,13 +2,14 @@ from pathlib import Path from PIL import ImageFont -_FONTS_DIR = Path(__file__).parent.parent / "fonts" +from common.fonts import FONTS_DIR + _orig_truetype = ImageFont.truetype def _resolve_truetype(font=None, size=10, **kwargs): if isinstance(font, str) and not Path(font).is_absolute() and not Path(font).exists(): - candidate = _FONTS_DIR / font + candidate = FONTS_DIR / font if candidate.exists(): font = str(candidate) return _orig_truetype(font, size, **kwargs) diff --git a/pistomp-testui.py b/pistomp-testui.py index 0dd7b0ace..76171a044 100755 --- a/pistomp-testui.py +++ b/pistomp-testui.py @@ -12,6 +12,8 @@ import random +from common.fonts import font_path + lcd = LcdIli9341(board.SPI(), digitalio.DigitalInOut(board.CE0), digitalio.DigitalInOut(board.D6), @@ -81,7 +83,7 @@ def do_param_dialog(event, data): def do_main_screen(): - title_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 26) + title_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 26) display_width = 320 display_height = 240 plugin_width = 79 diff --git a/pistomp/lcd128x64.py b/pistomp/lcd128x64.py index 6992d2581..1a9def2fa 100755 --- a/pistomp/lcd128x64.py +++ b/pistomp/lcd128x64.py @@ -30,6 +30,9 @@ from pistomp.footswitch import Footswitch +from common.fonts import font_path + + class Lcd(ABC): def __init__(self, cwd): @@ -91,12 +94,12 @@ def __init__(self, cwd): ImageDraw.Draw(self.images[6]), ImageDraw.Draw(self.images[7])] # Load fonts - self.splash_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 18) - self.title_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 11) - self.label_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 10) - self.small_bold_font = ImageFont.truetype("DejaVuSansMono-Bold.ttf", 8) - self.small_font = ImageFont.truetype("DejaVuSansMono.ttf", 8) - #self.small_font = ImageFont.truetype(os.path.join(cwd, "fonts", "EtBt6001-JO47.ttf"), 6) + self.splash_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 18) + self.title_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 11) + self.label_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 10) + self.small_bold_font = ImageFont.truetype(font_path("DejaVuSansMono-Bold.ttf"), 8) + self.small_font = ImageFont.truetype(font_path("DejaVuSansMono.ttf"), 8) + #self.small_font = ImageFont.truetype(font_path("EtBt6001-JO47.ttf"), 6) # Splash text_im = Image.new('L', (103, 63)) diff --git a/pistomp/lcd135x240.py b/pistomp/lcd135x240.py index 66e65705c..9af39ddac 100755 --- a/pistomp/lcd135x240.py +++ b/pistomp/lcd135x240.py @@ -24,6 +24,9 @@ import adafruit_rgb_display.st7789 as st7789 +from common.fonts import font_path + + class Lcd(ABC): def __init__(self, cwd): @@ -69,9 +72,9 @@ def __init__(self, cwd): # Font self.font_size = 30 - self.font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', self.font_size) + self.font = ImageFont.truetype(font_path("DejaVuSans.ttf"), self.font_size) self.splash_font_size = 40 - self.splash_font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', self.splash_font_size) + self.splash_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), self.splash_font_size) # Splash self.splash_show() diff --git a/pistomp/lcd320x240.py b/pistomp/lcd320x240.py index 77fa9612e..92f9509e7 100644 --- a/pistomp/lcd320x240.py +++ b/pistomp/lcd320x240.py @@ -34,6 +34,9 @@ #import traceback +from common.fonts import font_path + + class Lcd(abstract_lcd.Lcd): def __init__(self, cwd, handler=None, flip=False, display=None, spi_speed_mhz=24): @@ -84,10 +87,10 @@ def __init__(self, cwd, handler=None, flip=False, display=None, spi_speed_mhz=24 } # TODO get fonts from config.json - self.title_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 26) - self.splash_font = ImageFont.truetype('DejaVuSans.ttf', 48) - self.small_font = ImageFont.truetype("DejaVuSans.ttf", 20) - self.tiny_font = ImageFont.truetype("DejaVuSans.ttf", 16) + self.title_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 26) + self.splash_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 48) + self.small_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 20) + self.tiny_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 16) self.title_split_orig = 190 self.title_split = self.title_split_orig self.display_width = 320 diff --git a/pistomp/lcdgfx.py b/pistomp/lcdgfx.py index 80a763461..1261d7095 100755 --- a/pistomp/lcdgfx.py +++ b/pistomp/lcdgfx.py @@ -25,6 +25,8 @@ from pistomp.footswitch import Footswitch # TODO would like to avoid this module knowing such details +from common.fonts import font_path + class Lcd(abstract_lcd.Lcd): __single = None @@ -95,12 +97,12 @@ def __init__(self, cwd, lcd=None, backlight=None, touch=None): ImageDraw.Draw(self.images[6]), ImageDraw.Draw(self.images[7])] # Load fonts - self.splash_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 18) - self.title_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 11) - self.label_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 10) - self.small_bold_font = ImageFont.truetype("DejaVuSansMono-Bold.ttf", 8) - #self.small_font = ImageFont.truetype("DejaVuSansMono.ttf", 8) - self.small_font = ImageFont.truetype(os.path.join(cwd, "fonts", "EtBt6001-JO47.ttf"), 6) + self.splash_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 18) + self.title_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 11) + self.label_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 10) + self.small_bold_font = ImageFont.truetype(font_path("DejaVuSansMono-Bold.ttf"), 8) + #self.small_font = ImageFont.truetype(font_path("DejaVuSansMono.ttf"), 8) + self.small_font = ImageFont.truetype(font_path("EtBt6001-JO47.ttf"), 6) # Splash text_im = Image.new('L', (103, 63)) diff --git a/pistomp/lcdili9341.py b/pistomp/lcdili9341.py index a092e5c61..f2c6df057 100755 --- a/pistomp/lcdili9341.py +++ b/pistomp/lcdili9341.py @@ -29,6 +29,8 @@ # Most draw methods should be implemented in the parent class unless that needs to be overriden for this display # All __init__ parameters from the lcdbase.py should be specified in this __init__ +from common.fonts import font_path + class Lcd(lcdcolor.Lcdcolor): @@ -50,11 +52,11 @@ def __init__(self, cwd, mod=None, flip=False): self.init_spi_display() # Fonts - self.title_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 26) - self.splash_font = ImageFont.truetype('DejaVuSans.ttf', 48) - self.small_font = ImageFont.truetype("DejaVuSans.ttf", 20) - self.tiny_font = ImageFont.truetype("DejaVuSans.ttf", 16) - #self.tiny_font = ImageFont.truetype(os.path.join(cwd, "fonts", "EtBt6001-JO47.ttf"), 12) + self.title_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 26) + self.splash_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 48) + self.small_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 20) + self.tiny_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 16) + #self.tiny_font = ImageFont.truetype(font_path("EtBt6001-JO47.ttf"), 12) # Colors self.background = (0, 0, 0) diff --git a/pistomp/lcdsy7789.py b/pistomp/lcdsy7789.py index e50b41f71..5471d2833 100755 --- a/pistomp/lcdsy7789.py +++ b/pistomp/lcdsy7789.py @@ -26,6 +26,9 @@ import ST7789 +from common.fonts import font_path + + class Lcd(ABC): def __init__(self, cwd): @@ -61,9 +64,9 @@ def __init__(self, cwd): # Font self.font_size = 26 - self.font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', self.font_size) + self.font = ImageFont.truetype(font_path("DejaVuSans.ttf"), self.font_size) self.splash_font_size = 40 - self.splash_font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', self.splash_font_size) + self.splash_font = ImageFont.truetype(font_path("DejaVuSans.ttf"), self.splash_font_size) # Turn on the backlight backlight = digitalio.DigitalInOut(board.D22) diff --git a/pistomp/tuner/panel.py b/pistomp/tuner/panel.py index a21f118d5..09fb87909 100644 --- a/pistomp/tuner/panel.py +++ b/pistomp/tuner/panel.py @@ -5,6 +5,8 @@ from PIL import ImageFont +from common.fonts import font_path + from uilib.box import Box from uilib.config import Config from uilib.misc import get_text_size @@ -347,7 +349,7 @@ def __init__( super().__init__(box=Box.xywh(0, 0, _W, 240), auto_destroy=True) self._engine = engine - note_font = ImageFont.truetype("DejaVuSans-Bold.ttf", 56) + note_font = ImageFont.truetype(font_path("DejaVuSans-Bold.ttf"), 56) btn_font = Config().get_font("default") _, btn_text_h = get_text_size("Mute", btn_font) btn_v_margin = max(0, (_BTN_H - btn_text_h) // 2) diff --git a/tests/conftest.py b/tests/conftest.py index 216d41214..dbcede28e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,7 @@ import pytest from PIL import Image, ImageFont +from common.fonts import FONTS_DIR as _FONTS_DIR from uilib.panel import LcdBase PROJECT_ROOT = Path(__file__).parent.parent @@ -54,9 +55,6 @@ # --------------------------------------------------------------------------- -_FONTS_DIR = PROJECT_ROOT / "fonts" - - @pytest.fixture(autouse=True) def force_basic_layout(monkeypatch): original = ImageFont.truetype @@ -158,7 +156,7 @@ def inject(self, raw: str) -> None: def sent_values_for(self, instance_id: str, symbol: str) -> list[float]: prefix = f"param_set /graph/{instance_id}/{symbol} " - return [float(m[len(prefix):]) for m in self.sent if m.startswith(prefix)] + return [float(m[len(prefix) :]) for m in self.sent if m.startswith(prefix)] @pytest.fixture diff --git a/ui/wifi_menu.py b/ui/wifi_menu.py index e6610513a..c2a7a5bc4 100644 --- a/ui/wifi_menu.py +++ b/ui/wifi_menu.py @@ -19,6 +19,8 @@ from PIL import ImageFont +from common.fonts import font_path + import common.util as util from modalapi.wifi import ( ConnectSavedCmd, @@ -101,7 +103,7 @@ def __init__(self, ssid: str, pstack, on_submit: PasswordCallback) -> None: self._on_submit = on_submit self._curline = '' - font = ImageFont.truetype("DejaVuSans.ttf", 18) + font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 18) box = Box(0, 0, 300, 80) box = box.centre(pstack.box) super().__init__(box=box, parent=pstack, auto_destroy=True) diff --git a/uilib/config.py b/uilib/config.py index d4cc3540f..17b2df74f 100644 --- a/uilib/config.py +++ b/uilib/config.py @@ -18,6 +18,8 @@ import json from PIL import ImageFont +from common.fonts import font_path, FONTS_DIR + class Config: _instance = None @@ -40,9 +42,9 @@ def __init__(self, config_json=None): def _set_defaults(self): if "default" not in self.fonts: - add_font("default", "DejaVuSans.ttf", 16) + add_font("default", font_path("DejaVuSans.ttf"), 16) if "default_title" not in self.fonts: - add_font("default_title", "DejaVuSans-Bold.ttf", 16) + add_font("default_title", font_path("DejaVuSans-Bold.ttf"), 16) if "default_fgnd" not in self.colors: add_color("default_fgnd", (255, 255, 255)) if "default_bkgnd" not in self.colors: @@ -90,7 +92,8 @@ def load_config(self, json_file, reset_old=True): except KeyError as e: print("Error loading font:", e) else: - self.add_font(l, n, s) + candidate = FONTS_DIR / n + self.add_font(l, str(candidate) if candidate.exists() else n, s) if "colors" in data: colors = data["colors"] for color_def in colors: diff --git a/uilib/font_with_glyphs.py b/uilib/font_with_glyphs.py index e33ba253c..28da515d7 100644 --- a/uilib/font_with_glyphs.py +++ b/uilib/font_with_glyphs.py @@ -20,6 +20,8 @@ from PIL import Image, ImageDraw, ImageFont +from common.fonts import font_path + @runtime_checkable class Glyph(Protocol): @@ -32,7 +34,7 @@ class PillGlyph: def __init__(self, label: str, font_size: int = 9) -> None: self._label = label - self._font = ImageFont.truetype("DejaVuSans.ttf", font_size) + self._font = ImageFont.truetype(font_path("DejaVuSans.ttf"), font_size) bb = self._font.getbbox(label) self._text_w = int(bb[2] - bb[0]) self._text_h = int(bb[3] - bb[1]) diff --git a/uilib/text.py b/uilib/text.py index 8ea551770..a63a16fec 100644 --- a/uilib/text.py +++ b/uilib/text.py @@ -18,6 +18,8 @@ from math import log from PIL import ImageFont +from common.fonts import font_path + from uilib.panel import * from uilib.misc import * from uilib.config import * @@ -135,7 +137,7 @@ def __init__(self, widget): self.set_outline(2, (255,255,255)) self.outline = 2 self.curline = widget.text - self.font = ImageFont.truetype("DejaVuSans.ttf", 18) + self.font = ImageFont.truetype(font_path("DejaVuSans.ttf"), 18) bbox = self.font.getbbox(widget.edit_message) msg_w, msg_h = bbox[2] - bbox[0], bbox[3] msg_box = Box.xywh(10, 10, msg_w, msg_h)