Merge pull request #62 from outis1one/clDemoe/add-moon-image-locations-ACdGH

Fix UnicodeEncodeError in caption drawing
This commit is contained in:
Outis
2026-05-01 14:08:41 -04:00
committed by GitHub
+35 -4
View File
@@ -36,7 +36,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
from PIL import Image, ImageDraw, ImageFilter from PIL import Image, ImageDraw, ImageFilter, ImageFont
# Pillow ≥ 10 renamed resampling constants # Pillow ≥ 10 renamed resampling constants
try: try:
@@ -46,6 +46,39 @@ except AttributeError: # Pillow < 10
LANCZOS = Image.LANCZOS LANCZOS = Image.LANCZOS
BICUBIC = Image.BICUBIC BICUBIC = Image.BICUBIC
# Common DejaVu Sans paths across distros; first hit wins.
_DEJAVU_CANDIDATES = [
'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
'/usr/share/fonts/dejavu/DejaVuSans.ttf',
'/usr/share/fonts/TTF/DejaVuSans.ttf',
'/Library/Fonts/DejaVuSans.ttf', # macOS homebrew
]
def _load_font(size: int = 18) -> ImageFont.ImageFont:
"""Return a Unicode-capable font at `size` pt, or the PIL default."""
for path in _DEJAVU_CANDIDATES:
try:
return ImageFont.truetype(path, size)
except (OSError, IOError):
pass
return ImageFont.load_default()
def _draw_caption(draw: ImageDraw.ImageDraw, caption: str,
xy: tuple[int, int], output_w: int) -> None:
"""Draw a drop-shadow caption, sanitising characters the default font
cannot encode when no TTF is available."""
font = _load_font(max(14, output_w // 100))
# If we fell back to the bitmap default font it only handles latin-1; strip
# anything outside that range rather than crashing.
if not hasattr(font, 'path'):
caption = caption.encode('latin-1', errors='replace').decode('latin-1')
caption = caption.replace('\x3f', '-') # '?' placeholder → dash
x, y = xy
draw.text((x + 2, y + 2), caption, fill=(0, 0, 0), font=font)
draw.text((x, y), caption, fill=(220, 220, 220), font=font)
def _square_crop_to_disk(ref: Image.Image, threshold: int = 25) -> Image.Image: def _square_crop_to_disk(ref: Image.Image, threshold: int = 25) -> Image.Image:
"""Tight-crop a reference moon image to the disk's bounding square. """Tight-crop a reference moon image to the disk's bounding square.
@@ -200,9 +233,7 @@ def composite_full_moon(
# ── Caption ── # ── Caption ──
if caption: if caption:
draw = ImageDraw.Draw(bg) draw = ImageDraw.Draw(bg)
# Drop shadow for legibility _draw_caption(draw, caption, (22, output_size[1] - 48), output_size[0])
draw.text((24, output_size[1] - 44), caption, fill=(0, 0, 0))
draw.text((22, output_size[1] - 46), caption, fill=(220, 220, 220))
Path(out_path).parent.mkdir(parents=True, exist_ok=True) Path(out_path).parent.mkdir(parents=True, exist_ok=True)
bg.save(out_path, quality=92) bg.save(out_path, quality=92)