File size: 4,244 Bytes
882e444 5eb924e 882e444 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | import base64
import io
from pathlib import Path
from PIL import Image
from src.generation import ConsolidatedSummary, FlashCard
def image_to_base64(img: Image.Image) -> str:
buffer = io.BytesIO()
img.save(buffer, format="PNG")
encoded = base64.b64encode(buffer.getvalue()).decode("utf-8")
return f"data:image/png;base64,{encoded}"
def flashcard_to_html(card: FlashCard, reference_image: Image.Image = None) -> str:
key_points_html = "".join(f"<li>{p}</li>" for p in card.key_points)
examples_html = "".join(f"<li>{e}</li>" for e in card.examples)
if reference_image is not None:
img_src = image_to_base64(reference_image)
img_html = f'<img src="{img_src}" style="max-width:100%; border-radius:8px;">'
else:
img_html = f'<p style="color:#888; font-style:italic;">{card.suggested_image}</p>'
return f"""
<html>
<head>
<style>
body {{
font-family: Arial, sans-serif;
margin: 20px;
background: #ffffff;
}}
.card {{
display: flex;
border: 2px solid #2ecc71;
border-radius: 12px;
padding: 20px;
gap: 20px;
}}
.image-col {{
width: 35%;
display: flex;
align-items: center;
justify-content: center;
}}
.content-col {{
width: 65%;
}}
h1 {{
color: #2ecc71;
font-size: 22px;
margin-bottom: 8px;
}}
h3 {{
color: #555;
font-size: 14px;
margin: 12px 0 4px 0;
}}
ul {{
margin: 4px 0;
padding-left: 20px;
}}
li {{
font-size: 13px;
margin-bottom: 3px;
}}
p {{
font-size: 13px;
line-height: 1.5;
}}
</style>
</head>
<body>
<div class="card">
<div class="image-col">{img_html}</div>
<div class="content-col">
<h1>{card.concept}</h1>
<p>{card.definition}</p>
<h3>Key Points</h3>
<ul>{key_points_html}</ul>
<h3>Examples</h3>
<ul>{examples_html}</ul>
</div>
</div>
</body>
</html>
"""
def summary_to_html(summary: ConsolidatedSummary) -> str:
concepts_html = "".join(
f"<li><strong>{c['name']}:</strong> {c['description']}</li>"
for c in summary.concepts
)
return f"""
<html>
<head>
<style>
body {{
font-family: Arial, sans-serif;
margin: 20px;
background: #ffffff;
}}
.summary {{
border: 2px solid #3498db;
border-radius: 12px;
padding: 20px;
}}
h1 {{
color: #3498db;
font-size: 22px;
margin-bottom: 8px;
}}
h3 {{
color: #555;
font-size: 14px;
margin: 12px 0 4px 0;
}}
ul {{
margin: 4px 0;
padding-left: 20px;
}}
li {{
font-size: 13px;
margin-bottom: 6px;
}}
p {{
font-size: 13px;
line-height: 1.5;
}}
</style>
</head>
<body>
<div class="summary">
<h1>{summary.topic}</h1>
<p>{summary.overview}</p>
<h3>Concepts</h3>
<ul>{concepts_html}</ul>
</div>
</body>
</html>
"""
def export_to_pdf(
content: "FlashCard | ConsolidatedSummary",
output_path: Path,
reference_image: Image.Image = None,
) -> Path:
if isinstance(content, FlashCard):
html = flashcard_to_html(content, reference_image)
else:
html = summary_to_html(content)
try:
from weasyprint import HTML
except Exception as e:
raise RuntimeError(
"No se puede exportar PDF: faltan las librerias GTK en este sistema. "
"Ejecuta install.bat como administrador para instalarlas."
) from e
HTML(string=html).write_pdf(str(output_path))
return output_path
|