""" Markdown raporlarını PDF formatına çevirir. Kullanım: python convert_to_pdf.py python convert_to_pdf.py test_outputs/report.md python convert_to_pdf.py # test_outputs/ klasöründeki tüm .md dosyalarını çevirir """ import sys import os import io import re import base64 from pathlib import Path import markdown from xhtml2pdf import pisa IMAGE_PATTERN = re.compile( r'(\*\*Image\*\*:\s*)([^\n]+\.(jpg|jpeg|png|gif|bmp|webp))', re.IGNORECASE, ) IMG_CSS = """ .report-image { display: block; max-width: 100%; max-height: 300px; margin: 10px 0 16px 0; border: 1px solid #ccc; border-radius: 4px; } .image-label { font-weight: bold; color: #003366; margin-bottom: 4px; } """ def _image_to_base64(img_path: str) -> str | None: path = Path(img_path.strip()) if not path.exists(): return None mime = {".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", ".gif": "image/gif", ".bmp": "image/bmp", ".webp": "image/webp"}.get(path.suffix.lower(), "image/jpeg") data = base64.b64encode(path.read_bytes()).decode() return f"data:{mime};base64,{data}" def _embed_images(md_text: str) -> str: def replacer(m: re.Match) -> str: img_path = m.group(2).strip() data_uri = _image_to_base64(img_path) if data_uri: return ( f'

Image

' f'Detection Image' ) return m.group(0) return IMAGE_PATTERN.sub(replacer, md_text) CSS = """ @page { size: A4; margin: 2cm 2.5cm 2cm 2.5cm; } body { font-family: "Helvetica", "Arial", sans-serif; font-size: 10pt; line-height: 1.6; color: #1a1a1a; } h1 { font-size: 16pt; color: #003366; border-bottom: 2px solid #003366; padding-bottom: 6px; margin-bottom: 12px; } h2 { font-size: 13pt; color: #003366; margin-top: 18px; margin-bottom: 6px; } h3 { font-size: 11pt; color: #1a4d80; margin-top: 12px; margin-bottom: 4px; } p { margin: 6px 0; } strong { color: #111; } em { color: #333; } ul, ol { margin: 6px 0 6px 20px; padding: 0; } li { margin-bottom: 3px; } hr { border: none; border-top: 1px solid #cccccc; margin: 14px 0; } code { background: #f4f4f4; padding: 1px 4px; font-size: 9pt; border-radius: 3px; } blockquote { border-left: 3px solid #003366; margin: 8px 0 8px 16px; padding-left: 10px; color: #555; } .header-meta { font-size: 9pt; color: #666; margin-bottom: 16px; } table { width: 100%; border-collapse: collapse; margin: 10px 0; font-size: 9pt; } th { background: #003366; color: white; padding: 5px 8px; text-align: left; } td { border: 1px solid #ccc; padding: 4px 8px; } tr:nth-child(even) td { background: #f5f8ff; } """ def _resize_b64_image(b64: str, max_size: int = 800) -> str: from PIL import Image as PILImage data = base64.b64decode(b64) img = PILImage.open(io.BytesIO(data)) img.thumbnail((max_size, max_size), PILImage.LANCZOS) buf = io.BytesIO() img.save(buf, format="PNG") return base64.b64encode(buf.getvalue()).decode() def md_text_to_pdf_bytes(md_text: str, annotated_image_b64: str | None = None) -> bytes: """ Markdown metnini doğrudan PDF baytlarına çevirir (dosya I/O yok). annotated_image_b64: base64 kodlanmış PNG — rapor başına eklenir. """ if annotated_image_b64: annotated_image_b64 = _resize_b64_image(annotated_image_b64) image_block = ( '

Detection Image

' f'Detection Image' ) else: image_block = "" html_body = markdown.markdown( md_text, extensions=["tables", "fenced_code", "nl2br", "sane_lists"], ) full_html = f""" {image_block} {html_body} """ buf = io.BytesIO() result = pisa.CreatePDF(full_html, dest=buf, encoding="utf-8") if result.err: raise RuntimeError(f"PDF oluşturulurken hata: {result.err}") return buf.getvalue() def md_to_pdf(md_path: str | Path, pdf_path: str | Path | None = None) -> Path: md_path = Path(md_path) if not md_path.exists(): raise FileNotFoundError(f"Dosya bulunamadı: {md_path}") if pdf_path is None: pdf_path = md_path.with_suffix(".pdf") pdf_path = Path(pdf_path) pdf_path.parent.mkdir(parents=True, exist_ok=True) md_text = md_path.read_text(encoding="utf-8") # Görsel yollarını base64 gömülü etiketlerine çevir preprocessed = _embed_images(md_text) html_body = markdown.markdown( preprocessed, extensions=["tables", "fenced_code", "nl2br", "sane_lists"], ) full_html = f""" {html_body} """ with open(pdf_path, "wb") as f: result = pisa.CreatePDF(full_html, dest=f, encoding="utf-8") if result.err: raise RuntimeError(f"PDF oluşturulurken hata: {result.err}") print(f"PDF oluşturuldu: {pdf_path}") return pdf_path def convert_all(directory: str | Path = "test_outputs") -> list[Path]: directory = Path(directory) md_files = list(directory.glob("**/*.md")) if not md_files: print(f"'{directory}' klasöründe .md dosyası bulunamadı.") return [] results = [] for md_file in md_files: try: pdf = md_to_pdf(md_file) results.append(pdf) except Exception as e: print(f"Hata ({md_file.name}): {e}") return results if __name__ == "__main__": if len(sys.argv) == 1: convert_all("test_outputs") elif len(sys.argv) == 2: target = sys.argv[1] path = Path(target) if path.is_dir(): convert_all(path) elif path.suffix == ".md": md_to_pdf(path) else: print(f"Geçersiz dosya: {target} (.md dosyası veya klasör belirtin)") sys.exit(1) elif len(sys.argv) == 3: md_to_pdf(sys.argv[1], sys.argv[2]) else: print("Kullanım: python convert_to_pdf.py [md_dosyasi] [cikti.pdf]") sys.exit(1)