""" create_test_pdf.py — Generate a synthetic scanned medical-schedule PDF with known ground-truth for accuracy measurement. """ import json, os, random import numpy as np from PIL import Image, ImageDraw, ImageFont GROUND_TRUTH = [ ("1", "07:30", "09:15", "105", "SMITH, J", "JONES, R", "DOE, JOHN A", "45/M", "03/15/1979"), ("2", "08:00", "10:30", "150", "WILLIAMS, K", "BROWN, S", "MARTINEZ, MARIA", "67/F", "11/22/1957"), ("3", "09:00", "11:00", "120", "JOHNSON, M", "DAVIS, T", "CHEN, DAVID L", "32/M", "06/08/1992"), ("1", "10:00", "12:30", "150", "TAYLOR, P", "WILSON, A", "O'BRIEN, SARAH", "55/F", "09/04/1969"), ("4", "11:30", "13:00", "90", "ANDERSON, R", "MOORE, J", "KIM, JAMES H", "28/M", "12/01/1996"), ("2", "12:00", "14:45", "165", "THOMAS, D", "CLARK, M", "PATEL, PRIYA", "41/F", "07/19/1983"), ("3", "13:30", "15:00", "90", "JACKSON, L", "HALL, N", "RODRIGUEZ, CARLOS", "73/M", "02/28/1951"), ("5", "14:00", "16:30", "150", "WHITE, B", "LEWIS, E", "NGUYEN, THI L", "59/F", "04/10/1965"), ] COL_SPEC = [ # (header, x_start, x_end) ("ROOM", 50, 180), ("BEGIN", 180, 340), ("END", 340, 490), ("MIN", 490, 580), ("SURGEON", 580, 900), ("ANES", 900, 1180), ("PATIENT'S NAME",1180,1750), ("AGE/SEX", 1750,1900), ("DOB", 1900,2150), ] def _font(size, bold=False): name = "DejaVuSansMono-Bold.ttf" if bold else "DejaVuSansMono.ttf" for d in ["/usr/share/fonts/truetype/dejavu", "/usr/share/fonts/dejavu", "C:/Windows/Fonts"]: p = os.path.join(d, name) if os.path.isfile(p): return ImageFont.truetype(p, size) return ImageFont.load_default() def make_page(rows, W=2550, H=3300): img = Image.new("RGB", (W, H), "white") dr = ImageDraw.Draw(img) fh = _font(26, bold=True) fd = _font(20) dr.text((900, 50), "OPERATING ROOM SCHEDULE", fill="black", font=fh) dr.text((1000, 90), "DATE: 01/15/2025", fill="black", font=fd) y0, rh = 160, 55 # header for hdr, xs, xe in COL_SPEC: dr.rectangle([(xs, y0), (xe, y0 + rh)], outline="black", width=2) dr.text((xs + 8, y0 + 12), hdr, fill="black", font=fd) # data for i, vals in enumerate(rows): y = y0 + rh + i * rh for j, (_, xs, xe) in enumerate(COL_SPEC): dr.rectangle([(xs, y), (xe, y + rh)], outline="black", width=2) if j < len(vals): dr.text((xs + 8, y + 14), vals[j], fill="black", font=fd) return img def add_noise(img, level=8): a = np.array(img, dtype=np.float32) a += np.random.normal(0, level, a.shape) a = np.clip(a, 0, 255).astype(np.uint8) pil = Image.fromarray(a) return pil.rotate(random.uniform(-0.5, 0.5), fillcolor=(245,245,245)) def create_test_pdf(out_dir="./test_pdfs", pages=2): os.makedirs(out_dir, exist_ok=True) pils, gt = [], [] for pg in range(pages): pil = add_noise(make_page(GROUND_TRUTH)) pils.append(pil) for r in GROUND_TRUTH: gt.append(dict(page=pg+1, room=r[0], begin_time=r[1], end_time=r[2], minutes=r[3], surgeon=r[4], anes=r[5], patient_name=r[6], age_sex=r[7], dob=r[8])) pdf = os.path.join(out_dir, "test_schedule.pdf") pils[0].save(pdf, "PDF", resolution=300, save_all=True, append_images=pils[1:]) gt_path = os.path.join(out_dir, "ground_truth.json") with open(gt_path, "w") as f: json.dump(gt, f, indent=2) print(f"✅ PDF → {pdf} ({pages} pages × {len(GROUND_TRUTH)} rows)") print(f"✅ GT → {gt_path}") return pdf, gt_path if __name__ == "__main__": create_test_pdf()