Journey: real train/bus pictograms instead of letter badges
Vendor Font Awesome Free Solid (fonts/fa-solid-900.ttf, SIL OFL licensed -- see fonts/FONT-AWESOME-LICENSE.txt) for its actual train/bus glyphs. Confirmed live these stay sharp and recognizable even at the tiny size the detail line renders at (~20px), unlike a hand-drawn shape would be, and unlike emoji (not in DejaVu Sans, would've rendered as tofu). render_utils.py gets a load_icon_font() alongside the existing load_font(), plus named ICON_BUS/ICON_TRAIN codepoint constants so screens don't need to embed raw Private-Use-Area unicode literals.
This commit is contained in:
1 parent
7354517b22
commit
4380a62524
4 files changed
+197
-15
No files matched your search
+16
-15
@@ -21,7 +21,7 @@ import requests
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
from screens.base import Screen
|
||||
from screens.render_utils import load_font, text_width, truncate_to_width
|
||||
from screens.render_utils import ICON_BUS, ICON_TRAIN, load_font, load_icon_font, text_width, truncate_to_width
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -350,17 +350,19 @@ class JourneyScreen(Screen):
|
||||
|
||||
draw.text((arrow_x + arrow_w + scale * 0.02, top_line_y), journey["arrival"], font=time_font, fill=BLACK)
|
||||
|
||||
# Detail line: a small badge per leg -- colored circle (blue =
|
||||
# train, green = bus; pure panel colors, no dithering) with a
|
||||
# "Z" (Zug) or "B" (Bus) letter in it, so the mode is legible at
|
||||
# a glance rather than relying on color alone. Followed by the
|
||||
# line name, then transfer/platform/notice info. Drawn as
|
||||
# manually-placed segments instead of one joined string so each
|
||||
# leg can carry its own badge.
|
||||
# Detail line: a real train/bus pictogram per leg (Font Awesome
|
||||
# Solid, vendored in fonts/fa-solid-900.ttf -- confirmed live to
|
||||
# stay recognizable even at this render size, unlike a hand-drawn
|
||||
# shape or emoji, which either look unclear that small or render
|
||||
# as tofu since DejaVu Sans has no color-emoji glyphs). Blue for
|
||||
# train, green for bus -- pure panel colors, no dithering.
|
||||
# Followed by the line name, then transfer/platform/notice info.
|
||||
# Drawn as manually-placed segments instead of one joined string
|
||||
# so each leg can carry its own icon.
|
||||
detail_y = top_line_y + time_font.size * 1.25
|
||||
right_edge = width - pad
|
||||
icon_d = detail_font.size * 0.9
|
||||
icon_font = load_font(size=max(8, int(icon_d * 0.62)), bold=True)
|
||||
icon_d = detail_font.size * 1.05
|
||||
icon_font = load_icon_font(size=int(icon_d))
|
||||
cursor_x = pad
|
||||
|
||||
def draw_segment(text, color=BLACK, avail=None):
|
||||
@@ -373,12 +375,11 @@ class JourneyScreen(Screen):
|
||||
|
||||
def draw_mode_icon(x, mode):
|
||||
color = BLUE if mode == "train" else GREEN
|
||||
letter = "Z" if mode == "train" else "B"
|
||||
glyph = ICON_TRAIN if mode == "train" else ICON_BUS
|
||||
l, t, r, b = draw.textbbox((0, 0), glyph, font=icon_font)
|
||||
gw, gh = r - l, b - t
|
||||
top = detail_y + (detail_font.size - icon_d) / 2
|
||||
draw.ellipse((x, top, x + icon_d, top + icon_d), fill=color)
|
||||
l, t, r, b = draw.textbbox((0, 0), letter, font=icon_font)
|
||||
lw, lh = r - l, b - t
|
||||
draw.text((x + (icon_d - lw) / 2 - l, top + (icon_d - lh) / 2 - t), letter, font=icon_font, fill=(255, 255, 255))
|
||||
draw.text((x - l, top + (icon_d - gh) / 2 - t), glyph, font=icon_font, fill=color)
|
||||
|
||||
for idx, leg in enumerate(journey["legs"]):
|
||||
if cursor_x >= right_edge - icon_d:
|
||||
|
||||
@@ -23,6 +23,22 @@ def load_font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont:
|
||||
return _font_cache[key]
|
||||
|
||||
|
||||
# Font Awesome Free (Solid), vendored for real pictogram icons (train, bus,
|
||||
# etc.) where a hand-drawn shape or a plain letter would be less clear at a
|
||||
# glance than the real thing. SIL OFL licensed -- see fonts/FONT-AWESOME-LICENSE.txt.
|
||||
def load_icon_font(size: int) -> ImageFont.FreeTypeFont:
|
||||
key = ("fa-solid", size)
|
||||
if key not in _font_cache:
|
||||
_font_cache[key] = ImageFont.truetype(str(FONTS_DIR / "fa-solid-900.ttf"), size)
|
||||
return _font_cache[key]
|
||||
|
||||
|
||||
# Font Awesome Free Solid codepoints used by screens -- named here so callers
|
||||
# don't need to embed raw Private-Use-Area unicode literals.
|
||||
ICON_BUS = ""
|
||||
ICON_TRAIN = ""
|
||||
|
||||
|
||||
def text_width(draw, text: str, font: ImageFont.FreeTypeFont) -> int:
|
||||
left, _, right, _ = draw.textbbox((0, 0), text, font=font)
|
||||
return right - left
|
||||
|
||||
Reference in new issue
Block a user