ModelsSpace / src /convert_to_pdf.py
Cihangir Emre Er
hotfix for pdf error
690b7f7
Raw
History Blame Contribute Delete
6.62 kB
"""
Markdown raporlarını PDF formatına çevirir.
Kullanım:
python convert_to_pdf.py <md_dosyasi>
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'<p class="image-label">Image</p>'
f'<img class="report-image" src="{data_uri}" alt="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 = (
'<p class="image-label">Detection Image</p>'
f'<img class="report-image" src="data:image/png;base64,{annotated_image_b64}" alt="Detection Image">'
)
else:
image_block = ""
html_body = markdown.markdown(
md_text,
extensions=["tables", "fenced_code", "nl2br", "sane_lists"],
)
full_html = f"""<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="utf-8">
<style>{CSS}{IMG_CSS}</style>
</head>
<body>
{image_block}
{html_body}
</body>
</html>"""
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ü <img> etiketlerine çevir
preprocessed = _embed_images(md_text)
html_body = markdown.markdown(
preprocessed,
extensions=["tables", "fenced_code", "nl2br", "sane_lists"],
)
full_html = f"""<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="utf-8">
<style>{CSS}{IMG_CSS}</style>
</head>
<body>
{html_body}
</body>
</html>"""
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)