#!/usr/bin/env python3 """ Generate a sample claim PDF for the demo: all text/identity from sample_claim.json, images extracted from a source PDF (e.g. MB (2).PDF). No real identity in the output. """ from pathlib import Path import json import sys import argparse import tempfile import io PROJECT_ROOT = Path(__file__).resolve().parent.parent DEFAULT_SOURCE_PDF = PROJECT_ROOT / "MB (2).PDF" DEFAULT_CLAIM_JSON = PROJECT_ROOT / "sample_claim.json" DEFAULT_OUTPUT = PROJECT_ROOT / "demo_assets" / "sample_claim.pdf" MIN_IMAGE_DIM = 200 PAGE_WIDTH = 612 PAGE_HEIGHT = 792 MARGIN = 36 FONT_SIZE = 11 TITLE_SIZE = 14 def load_claim_data(path: Path) -> dict: with open(path, "r", encoding="utf-8") as f: data = json.load(f) return data.get("claim_data", data) def build_claim_form_text(data: dict) -> str: c = data.get("claimant", {}) p = data.get("policy", {}) i = data.get("incident", {}) cl = data.get("claim", {}) v = data.get("vehicle", {}) m = data.get("medical", {}) lines = [ "INSURANCE CLAIM FORM", "", f"Claimant: {c.get('name', '')}", f"Address: {c.get('address', '')}", f"Phone: {c.get('phone', '')}", f"Email: {c.get('email', '')}", f"DOB: {c.get('dob', '')}", "", f"Policy: {p.get('number', '')} Type: {p.get('type', '')} Coverage: ${p.get('coverage_amount', 0):,.0f}", f"Claim #: {cl.get('number', '')} Date: {cl.get('date', '')} Amount: ${cl.get('amount', 0):,.2f} Type: {cl.get('type', '')}", "", f"Vehicle: {v.get('year', '')} {v.get('make', '')} {v.get('model', '')} VIN: {v.get('vin', '')}", "", f"Date of Loss: {i.get('date', '')} Time: {i.get('time', '')}", f"Location: {i.get('location', '')}", "", "DESCRIPTION OF INCIDENT:", (i.get("description") or "").strip(), "", "MEDICAL:", f"Injuries: {', '.join(m.get('injuries', []))}", f"Providers: {', '.join(m.get('providers', []))}", f"Costs: ${m.get('costs', 0):,.0f}", "", f"TOTAL CLAIM: ${cl.get('amount', 0):,.2f}", "", f"Signed: {c.get('name', '')} Date: {cl.get('date', '')}", ] return "\n".join(lines) def extract_images_from_pdf(source_pdf: Path) -> list: """Extract image (width, height, bytes, ext) from PDF; keep only those >= MIN_IMAGE_DIM.""" import fitz out = [] seen = set() doc = fitz.open(str(source_pdf)) try: for page_num in range(doc.page_count): page = doc[page_num] for img_info in page.get_images(full=True): xref = img_info[0] if xref in seen: continue seen.add(xref) try: info = doc.extract_image(xref) w, h = info["width"], info["height"] if w < MIN_IMAGE_DIM or h < MIN_IMAGE_DIM: continue out.append((w, h, info["image"], info.get("ext", "png"))) except Exception: continue finally: doc.close() return out def main() -> None: ap = argparse.ArgumentParser(description="Generate sample claim PDF (fake identity + images from source PDF)") ap.add_argument("--source-pdf", default=None, help=f"Source PDF for images (default: MB (2).PDF)") ap.add_argument("--claim-json", default=None, help="Claim JSON for text (default: sample_claim.json)") ap.add_argument("--output", default=None, help="Output PDF path (default: demo_assets/sample_claim.pdf)") args = ap.parse_args() source_pdf = Path(args.source_pdf) if args.source_pdf else DEFAULT_SOURCE_PDF claim_json = Path(args.claim_json) if args.claim_json else DEFAULT_CLAIM_JSON output = Path(args.output) if args.output else DEFAULT_OUTPUT if not source_pdf.is_absolute(): source_pdf = PROJECT_ROOT / source_pdf if not claim_json.is_absolute(): claim_json = PROJECT_ROOT / claim_json if not output.is_absolute(): output = PROJECT_ROOT / output if not claim_json.exists(): print(f"Error: Claim JSON not found: {claim_json}", file=sys.stderr) sys.exit(1) if not source_pdf.exists(): print(f"Error: Source PDF not found: {source_pdf}", file=sys.stderr) sys.exit(1) data = load_claim_data(claim_json) form_text = build_claim_form_text(data) images = extract_images_from_pdf(source_pdf) print(f"Loaded claim data from {claim_json.name}, extracted {len(images)} images from {source_pdf.name}") import fitz doc = fitz.open() try: # Page 1: claim form text page = doc.new_page(width=PAGE_WIDTH, height=PAGE_HEIGHT) rect = fitz.Rect(MARGIN, MARGIN, PAGE_WIDTH - MARGIN, PAGE_HEIGHT - MARGIN) # Title page.insert_text(fitz.Point(MARGIN, MARGIN + 16), "INSURANCE CLAIM FORM", fontsize=TITLE_SIZE, fontname="helv") text_rect = fitz.Rect(MARGIN, MARGIN + 28, PAGE_WIDTH - MARGIN, PAGE_HEIGHT - MARGIN) page.insert_textbox(text_rect, form_text, fontsize=FONT_SIZE, fontname="helv", align=fitz.TEXT_ALIGN_LEFT) # If text overflows we could add a second page for raw_text; for simplicity one page is enough # Following pages: one image per page, scaled to fit (aspect ratio preserved) usable_w = PAGE_WIDTH - 2 * MARGIN usable_h = PAGE_HEIGHT - 2 * MARGIN for w_px, h_px, img_bytes, ext in images: page = doc.new_page(width=PAGE_WIDTH, height=PAGE_HEIGHT) if w_px and h_px: if usable_w * h_px <= usable_h * w_px: rect_w, rect_h = usable_w, usable_w * h_px / w_px else: rect_w, rect_h = usable_h * w_px / h_px, usable_h else: rect_w, rect_h = usable_w, usable_h x0 = MARGIN + (usable_w - rect_w) / 2 y0 = MARGIN + (usable_h - rect_h) / 2 img_rect = fitz.Rect(x0, y0, x0 + rect_w, y0 + rect_h) page.insert_image(img_rect, stream=img_bytes) output.parent.mkdir(parents=True, exist_ok=True) doc.save(str(output), garbage=4, deflate=True) print(f"Saved: {output}") finally: doc.close() if __name__ == "__main__": main()