"""Render annotated PDF with color-coded symbol match highlights.""" import logging from pathlib import Path from PIL import Image, ImageDraw, ImageFont logger = logging.getLogger(__name__) # Distinct visually-separable colors (RGB) for up to 12 symbol types PALETTE = [ (228, 26, 28), # red (55, 126, 184), # blue (77, 175, 74), # green (152, 78, 163), # purple (255, 127, 0), # orange (0, 184, 184), # teal (247, 129, 191), # pink (153, 153, 0), # olive (166, 86, 40), # brown (102, 102, 102), # grey (0, 0, 0), # black (190, 81, 51), # rust ] def _load_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: for path in ( "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", ): try: return ImageFont.truetype(path, size) except OSError: continue return ImageFont.load_default() def render_annotated_pdf( drawing_path: Path, results: list[dict], out_path: Path, filename: str = "drawing", ) -> Path: """Build a multi-page PDF: page 1 = legend, page 2 = annotated drawing. results: [{id, description, count, matches: [{x,y,w,h,score,rot}, ...]}, ...] """ base = Image.open(drawing_path).convert("RGB") annotated = base.copy() draw = ImageDraw.Draw(annotated) # Scale stroke width by image size so boxes are visible at any zoom stroke = max(2, min(annotated.size) // 800) label_font = _load_font(max(14, min(annotated.size) // 200)) for idx, r in enumerate(results): color = PALETTE[idx % len(PALETTE)] for m in r.get("matches", []): x, y, w, h = m["x"], m["y"], m["w"], m["h"] draw.rectangle([x, y, x + w, y + h], outline=color, width=stroke) # Legend page legend_w = annotated.size[0] row_h = max(36, legend_w // 30) legend_h = row_h * (len(results) + 4) legend = Image.new("RGB", (legend_w, legend_h), "white") ldraw = ImageDraw.Draw(legend) title_font = _load_font(max(28, legend_w // 50)) ldraw.text((40, 30), f"Počítání symbolů — {filename}", font=title_font, fill="black") ldraw.text( (40, 30 + row_h), f"Nalezeno {sum(r['count'] for r in results)} symbolů v {len(results)} kategoriích.", font=label_font, fill="black", ) y = 30 + 3 * row_h swatch = row_h - 10 for idx, r in enumerate(results): color = PALETTE[idx % len(PALETTE)] ldraw.rectangle([40, y, 40 + swatch, y + swatch], fill=color, outline="black", width=2) text = f"{r.get('description', r['id'])} — {r['count']}×" ldraw.text((40 + swatch + 16, y), text, font=label_font, fill="black") y += row_h # Save as multi-page PDF legend.save( out_path, "PDF", resolution=150.0, save_all=True, append_images=[annotated], ) return out_path