diff --git a/idriz/assistant.py b/idriz/assistant.py
index 9c83205..4489202 100644
--- a/idriz/assistant.py
+++ b/idriz/assistant.py
@@ -5,6 +5,7 @@ import datetime as dt
import json
import logging
import re
+from pathlib import Path
from openai import AsyncOpenAI
@@ -85,12 +86,35 @@ class Assistant:
article = await asyncio.to_thread(wiki.find_article, topic)
if not article:
return f"Nisam našao članak o temi '{topic_bs}' na Wikipediji."
- title, body = await textgen.summarize(self.client, self.cfg.text_model, article, topic_bs, age)
stamp = dt.datetime.now().strftime("%Y%m%d-%H%M%S")
- out = self.cfg.out_dir / f"{stamp}-sazetak-{_slug(article.title)}.pdf"
- await asyncio.to_thread(render.render_summary, title, body, article.title, out, self.cfg.max_pages)
+ base = self.cfg.out_dir / f"{stamp}-sazetak-{_slug(article.title)}"
+ # sažetak i slika sa Wikipedije idu paralelno
+ summary_task = asyncio.create_task(textgen.summarize(self.client, self.cfg.text_model, article, topic_bs, age))
+ image_task = asyncio.create_task(asyncio.to_thread(self._wiki_image, article.title, base))
+ summary = await summary_task
+ image = await image_task
+ if image is None:
+ try:
+ raw = await coloring.generate_illustration(self.client, self.cfg.image_model, summary.image_prompt,
+ base.with_suffix(".gen.png"))
+ image = render.prepare_image(raw, base.with_suffix(".img.png"))
+ except Exception: # noqa: BLE001
+ log.exception("ilustracija nije uspjela, štampam bez slike")
+ out = base.with_suffix(".pdf")
+ await asyncio.to_thread(render.render_summary, summary.title, summary.html, summary.facts, summary.caption,
+ image, article.title, out, self.cfg.max_pages)
await printer.print_file(out, self.cfg.printer, self.cfg.dry_run, fit=False)
- return f"Sažetak '{title}' (po članku '{article.title}') je odštampan."
+ return f"Sažetak '{summary.title}' (po članku '{article.title}') je odštampan."
+
+ @staticmethod
+ def _wiki_image(title: str, base):
+ url = wiki.page_image_url(title)
+ if not url:
+ return None
+ raw = base.with_suffix(".wiki" + Path(url).suffix.lower())
+ if not wiki.download(url, raw):
+ return None
+ return render.prepare_image(raw, base.with_suffix(".img.png"))
async def tool_bojanka(self, args: dict) -> str:
subject = args.get("opis") or args.get("opis_bosanski") or "a cute animal"
diff --git a/idriz/coloring.py b/idriz/coloring.py
index 7507ca6..e67ad3d 100644
--- a/idriz/coloring.py
+++ b/idriz/coloring.py
@@ -35,3 +35,22 @@ async def generate(client: AsyncOpenAI, model: str, subject: str, out: Path) ->
page.save(out.with_suffix(".png"))
log.info("bojanka spremljena: %s", out)
return out
+
+
+ILLUSTRATION_PROMPT = (
+ "A simple, friendly black and white line drawing for a children's encyclopedia page: {subject}. "
+ "Clean black outlines on white, light hatching allowed, no text, no watermark, landscape orientation."
+)
+
+
+async def generate_illustration(client: AsyncOpenAI, model: str, subject: str, out: Path) -> Path:
+ """Crtež za sažetak kad Wikipedija nema sliku (PNG, vodoravno)."""
+ log.info("generišem ilustraciju: %r", subject)
+ resp = await client.images.generate(model=model, prompt=ILLUSTRATION_PROMPT.format(subject=subject),
+ size="1536x1024", quality="medium", n=1)
+ b64 = resp.data[0].b64_json
+ if not b64:
+ raise RuntimeError("model nije vratio sliku")
+ img = Image.open(io.BytesIO(base64.b64decode(b64))).convert("L")
+ ImageOps.autocontrast(img).save(out, "PNG")
+ return out
diff --git a/idriz/render.py b/idriz/render.py
index bbe01ba..13cf93f 100644
--- a/idriz/render.py
+++ b/idriz/render.py
@@ -1,55 +1,85 @@
-"""Pravljenje PDF-a od HTML sažetka (WeasyPrint) uz ograničenje broja stranica."""
+"""Pravljenje PDF-a od sažetka (WeasyPrint): slika, prozračan tekst, okvir sa zanimljivostima, najviše 2 stranice."""
import datetime as dt
import html
import logging
from pathlib import Path
+from PIL import Image, ImageOps
from weasyprint import HTML
log = logging.getLogger("idriz.render")
CSS = """
-@page {{ size: A4; margin: 16mm 18mm 18mm 18mm;
+@page {{ size: A4; margin: 15mm 17mm 17mm 17mm;
@bottom-center {{ content: "Idriz • Izvor: engleska Wikipedija ({source}) • {date} • strana " counter(page) " / " counter(pages);
- font-family: "DejaVu Sans", sans-serif; font-size: 8pt; color: #666; }} }}
-body {{ font-family: "DejaVu Sans", "Noto Sans", sans-serif; font-size: {size}pt; line-height: 1.38; color: #111; }}
-h1 {{ font-size: {h1}pt; margin: 0 0 6pt 0; border-bottom: 2pt solid #333; padding-bottom: 4pt; }}
-h2 {{ font-size: {h2}pt; margin: 10pt 0 3pt 0; }}
-p {{ margin: 0 0 6pt 0; text-align: justify; }}
-ul {{ margin: 0 0 6pt 0; padding-left: 16pt; }}
-li {{ margin-bottom: 2pt; }}
-.meta {{ font-size: {meta}pt; color: #555; margin-bottom: 8pt; }}
+ font-family: "DejaVu Sans", sans-serif; font-size: 8pt; color: #777; }} }}
+body {{ font-family: "DejaVu Sans", "Noto Sans", sans-serif; font-size: {size}pt; line-height: 1.5; color: #111; }}
+h1 {{ font-size: {h1}pt; line-height: 1.15; margin: 0 0 4pt 0; }}
+.meta {{ font-size: {meta}pt; color: #666; margin: 0 0 8pt 0; }}
+h2 {{ font-size: {h2}pt; margin: 12pt 0 3pt 0; color: #222; }}
+p {{ margin: 0 0 7pt 0; }}
+figure {{ margin: 4pt 0 10pt 0; text-align: center; page-break-inside: avoid; }}
+figure img {{ max-width: 100%; max-height: {imgh}mm; border: 1.5pt solid #333; }}
+figcaption {{ font-size: {meta}pt; color: #555; margin-top: 3pt; font-style: italic; }}
+.facts {{ border: 2pt dashed #444; border-radius: 8pt; padding: 8pt 12pt; margin: 12pt 0 0 0; page-break-inside: avoid; }}
+.facts h2 {{ margin: 0 0 4pt 0; }}
+.facts ul {{ margin: 0; padding-left: 16pt; }}
+.facts li {{ margin-bottom: 3pt; }}
"""
-def _document(title: str, body_html: str, source: str, size: float) -> str:
+def prepare_image(src: Path, dst: Path, max_w: int = 1400) -> Path | None:
+ """Pretvori sliku u sivu, pojačaj kontrast (mono laser) i smanji."""
+ try:
+ img = Image.open(src)
+ img.seek(0)
+ img = ImageOps.exif_transpose(img).convert("L")
+ img = ImageOps.autocontrast(img, cutoff=1)
+ if img.width > max_w:
+ img = img.resize((max_w, int(img.height * max_w / img.width)))
+ img.save(dst, "PNG")
+ return dst
+ except Exception: # noqa: BLE001
+ log.exception("slika %s se ne može obraditi", src)
+ return None
+
+
+def _document(title: str, body_html: str, facts: list[str], caption: str, image: Path | None,
+ source: str, size: float, imgh: int) -> str:
date = dt.date.today().strftime("%d.%m.%Y.")
- css = CSS.format(source=html.escape(source), date=date, size=size, h1=size * 1.8, h2=size * 1.25, meta=size * 0.85)
+ css = CSS.format(source=html.escape(source), date=date, size=size, h1=size * 2.1, h2=size * 1.3,
+ meta=size * 0.85, imgh=imgh)
+ fig = ""
+ if image:
+ fig = f"