git » alan.git » commit c7add94

Add optional PDF rendering via pandoc and document requirements

author Alan Dipert
2025-12-04 04:57:14 UTC
committer Alan Dipert
2025-12-04 04:57:14 UTC
parent 29b881d4f8aa78c8f1aa27e11ecddd89a5c9da1b

Add optional PDF rendering via pandoc and document requirements

.gitignore +1 -0
README.md +7 -0
render_text.py +49 -2

diff --git a/.gitignore b/.gitignore
index c1f4693..b1c4696 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ test_booklet*.txt
 answer_key*.txt
 my_answers*.txt
 answers*.txt
+*.pdf
 
 # OS/editor noise
 .DS_Store
diff --git a/README.md b/README.md
index 24ac745..b1306dd 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,13 @@ make run      # generates JSON, booklet, and key
 cat test_booklet.txt   # view the booklet
 cat answer_key.txt     # view the key
 ```
+- **Optional PDF output:** Requires `pandoc` plus a PDF engine (wkhtmltopdf or weasyprint recommended). Example:
+  ```bash
+  python3 render_text.py --in generated_test.json \
+    --test-out test_booklet.txt --key-out answer_key.txt \
+    --test-pdf test_booklet.pdf --key-pdf answer_key.pdf
+  ```
+  If no PDF engine is available, the script will skip PDF generation and report the issue.
 
 ## Generate Different Hardness Levels
 - **Standard (balanced):**
diff --git a/render_text.py b/render_text.py
index c1851e5..087d685 100644
--- a/render_text.py
+++ b/render_text.py
@@ -4,6 +4,9 @@ from __future__ import annotations
 import argparse
 import json
 from typing import Dict, Any
+import subprocess
+import shutil
+import tempfile
 
 
 def parse_args() -> argparse.Namespace:
@@ -11,6 +14,8 @@ def parse_args() -> argparse.Namespace:
     parser.add_argument("--in", dest="in_path", required=True)
     parser.add_argument("--test-out", dest="test_out", default="test_booklet.txt")
     parser.add_argument("--key-out", dest="key_out", default="answer_key.txt")
+    parser.add_argument("--test-pdf", dest="test_pdf", default=None, help="Optional PDF path for the booklet.")
+    parser.add_argument("--key-pdf", dest="key_pdf", default=None, help="Optional PDF path for the answer key.")
     return parser.parse_args()
 
 
@@ -75,14 +80,56 @@ def render_key(data: Dict[str, Any]) -> str:
     return "\n".join(lines)
 
 
+def _write_pdf(text: str, pdf_path: str, title: str) -> None:
+    """Render plain text to PDF via pandoc if available."""
+    pandoc = shutil.which("pandoc")
+    if not pandoc:
+        raise RuntimeError("pandoc not found; cannot render PDF.")
+    with tempfile.NamedTemporaryFile("w", delete=False, suffix=".txt") as tmp:
+        tmp.write(f"{title}\n\n{text}")
+        tmp_path = tmp.name
+    engines = [e for e in ["wkhtmltopdf", "weasyprint", "pdflatex"] if shutil.which(e)]
+    if not engines:
+        engines = [None]  # let pandoc pick default (may fail)
+    last_error = None
+    try:
+        for eng in engines:
+            cmd = [pandoc, tmp_path, "-o", pdf_path]
+            if eng:
+                cmd.extend(["--pdf-engine", eng])
+            try:
+                subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+                last_error = None
+                break
+            except subprocess.CalledProcessError as e:
+                last_error = e
+        if last_error:
+            raise RuntimeError(
+                "pandoc could not produce PDF; install a pdf engine such as wkhtmltopdf/weasyprint/pdflatex."
+            ) from last_error
+    finally:
+        try:
+            import os
+
+            os.remove(tmp_path)
+        except OSError:
+            pass
+
+
 def main() -> None:
     args = parse_args()
     with open(args.in_path, "r", encoding="utf-8") as f:
         data = json.load(f)
+    booklet_text = render_booklet(data)
+    key_text = render_key(data)
     with open(args.test_out, "w", encoding="utf-8") as f:
-        f.write(render_booklet(data))
+        f.write(booklet_text)
     with open(args.key_out, "w", encoding="utf-8") as f:
-        f.write(render_key(data))
+        f.write(key_text)
+    if args.test_pdf:
+        _write_pdf(booklet_text, args.test_pdf, "ALAN Booklet")
+    if args.key_pdf:
+        _write_pdf(key_text, args.key_pdf, "ALAN Answer Key")
 
 
 if __name__ == "__main__":