#!/usr/bin/env python3 """Génère le rapport PDF à partir de report/rapport.html, avec PyMuPDF (fitz). Aucune dépendance externe : ni pandoc, ni LaTeX, ni navigateur. On utilise l'API fitz.Story (rendu HTML/CSS -> PDF multi-pages) puis une seconde passe pour le pied de page et les numéros de page. python tools/make_report_pdf.py """ import os import sys import fitz # PyMuPDF ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) HTML = os.path.join(ROOT, "report", "rapport.html") OUT = os.path.join(ROOT, "dist", "Puyaubreau_Russac_rapport.pdf") # Feuille de style : mise en page A4 sobre, titres colorés, tables et blocs
# en Courier pour aligner diagrammes et carte des liserés.
CSS = """
* { font-family: serif; }
body { font-size: 10.5pt; line-height: 1.45; color: #1a1a1a; }

h1, h2, h3, .cover-title, .cover-course, .cover-sub, th { font-family: sans-serif; }

h2 { font-size: 15pt; color: #1c3d5a; margin: 18pt 0 6pt 0;
     border-bottom: 1.5px solid #1c3d5a; padding-bottom: 2pt; }
h3 { font-size: 12pt; color: #2a6f97; margin: 12pt 0 3pt 0; }
p  { margin: 5pt 0; text-align: justify; }
ul, ol { margin: 4pt 0 4pt 0; }
li { margin: 2pt 0; }

code { font-family: monospace; font-size: 9.5pt; color: #8a2846; }

pre.grid { font-family: monospace; font-size: 9pt; line-height: 1.25;
           background: #f4f6f8; border: 1px solid #d3dae0; border-radius: 3px;
           padding: 7pt; margin: 6pt 0; color: #14213d; white-space: pre; }

table { border-collapse: collapse; width: 100%; margin: 7pt 0; font-size: 9.5pt; }
th { background: #1c3d5a; color: #ffffff; text-align: left; padding: 4pt 6pt; }
td { border: 1px solid #c5ccd3; padding: 4pt 6pt; vertical-align: top; }
tr:nth-child(even) td { background: #f4f6f8; }

p.note { background: #fff8e6; border-left: 3px solid #e0a526;
         padding: 5pt 8pt; margin: 7pt 0; font-size: 9.8pt; }

/* Sommaire */
.toc-title { border: none; color: #1c3d5a; margin-bottom: 4pt; }
ol.toc { font-size: 11pt; line-height: 1.7; color: #1a1a1a; }
ol.toc li { margin: 1pt 0; }

/* Éviter qu'un bloc préformaté ou une table soit coupé entre deux pages. */
pre.grid, table, tr { page-break-inside: avoid; }
h2, h3 { page-break-after: avoid; }
.cover { page-break-after: always; }
ol.toc { page-break-after: always; }

/* Page de titre */
.cover { text-align: center; padding-top: 40pt; }
.cover-univ   { font-size: 10pt; color: #555; margin-bottom: 30pt; }
.cover-course { font-size: 13pt; color: #2a6f97; letter-spacing: 1pt; }
.cover-title  { font-size: 30pt; color: #1c3d5a; margin: 8pt 0 0 0; }
.cover-sub    { font-size: 12pt; color: #333; margin: 4pt 0; }
.cover-authors{ font-size: 15pt; color: #14213d; margin-top: 34pt; font-family: sans-serif; }
.cover-date   { font-size: 11pt; color: #555; margin-top: 6pt; }
.cover-meta   { font-size: 9.5pt; color: #555; margin-top: 34pt; line-height: 1.6; }
"""

MARGIN = 48          # marge en points (1pt = 1/72")
FOOTER = "Escampe — Puyaubreau / Russac — version finale"


def build():
    with open(HTML, "r", encoding="utf-8") as f:
        html = f.read()

    os.makedirs(os.path.dirname(OUT), exist_ok=True)

    mediabox = fitz.paper_rect("a4")
    where = mediabox + (MARGIN, MARGIN, -MARGIN, -MARGIN)

    # 1) Rendu du flux HTML en pages PDF via Story.
    writer = fitz.DocumentWriter(OUT)
    story = fitz.Story(html=html, user_css=CSS)
    more = 1
    pages = 0
    while more:
        dev = writer.begin_page(mediabox)
        more, _ = story.place(where)
        story.draw(dev)
        writer.end_page()
        pages += 1
    writer.close()

    # 2) Seconde passe : pied de page + numéros « page X / N ».
    doc = fitz.open(OUT)
    n = doc.page_count
    for i, page in enumerate(doc, start=1):
        y = page.rect.height - 26
        page.insert_text((MARGIN, y), FOOTER, fontname="helv",
                         fontsize=7.5, color=(0.45, 0.45, 0.45))
        label = f"page {i} / {n}"
        w = fitz.get_text_length(label, fontname="helv", fontsize=7.5)
        page.insert_text((page.rect.width - MARGIN - w, y), label,
                         fontname="helv", fontsize=7.5, color=(0.45, 0.45, 0.45))
    doc.saveIncr()
    doc.close()
    return pages, n


def verify():
    """Contrôle que les accents survivent au rendu (round-trip texte)."""
    doc = fitz.open(OUT)
    full = "".join(p.get_text() for p in doc)
    doc.close()
    # Mots accentués stables, présents dans report/rapport.html (titres de section).
    probes = ["liseré", "Présentation", "Modélisation", "Intégration",
              "Heuristique", "Puyaubreau"]
    missing = [s for s in probes if s not in full]
    return missing, len(full)


if __name__ == "__main__":
    pages, n = build()
    missing, chars = verify()
    print(f"PDF écrit : {OUT}")
    print(f"Pages : {n}  |  texte extrait : {chars} caractères")
    if missing:
        print("ATTENTION, chaînes accentuées introuvables après rendu :", missing)
        sys.exit(1)
    print("Accents vérifiés (round-trip OK).")