From d8290417969f3c2ea5dce301d58caebae64d94a5 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 29 Nov 2025 23:33:17 -0500 Subject: [PATCH] Add PDF download for interlinear chapter pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create chapter_interlinear_pdf.html template for PDF export - Add /book/{book}/chapter/{chapter}/interlinear/pdf route - Add PDF download button to interlinear page alongside print - Include Hebrew/Greek text, English translations, and Strong's numbers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- kjvstudy_org/server.py | 55 +++++- .../templates/chapter_interlinear.html | 8 + .../templates/chapter_interlinear_pdf.html | 167 ++++++++++++++++++ 3 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 kjvstudy_org/templates/chapter_interlinear_pdf.html diff --git a/kjvstudy_org/server.py b/kjvstudy_org/server.py index 9a8cea3..0b4516d 100644 --- a/kjvstudy_org/server.py +++ b/kjvstudy_org/server.py @@ -2239,6 +2239,57 @@ async def chapter_pdf(request: Request, book: str, chapter: int): ) +@app.get("/book/{book}/chapter/{chapter}/interlinear/pdf") +async def chapter_interlinear_pdf(book: str, chapter: int): + """Generate PDF export for interlinear chapter view.""" + # Redirect book name variations to canonical form + canonical_name = normalize_book_name(book) + if canonical_name: + return RedirectResponse(url=f"/book/{canonical_name}/chapter/{chapter}/interlinear/pdf", status_code=301) + + if not WEASYPRINT_AVAILABLE: + raise HTTPException( + status_code=503, + detail="PDF generation is not available. WeasyPrint system libraries are not installed." + ) + + verses = bible.get_verses_by_book_chapter(book, chapter) + chapters = bible.get_chapters_for_book(book) + + if not verses: + if not chapters: + raise HTTPException(status_code=404, detail=f"The book '{book}' was not found.") + else: + raise HTTPException(status_code=404, detail=f"Chapter {chapter} of {book} was not found.") + + # Get interlinear data for each verse + verses_with_interlinear = [] + for verse in verses: + interlinear_words = get_interlinear_data(book, chapter, verse.verse) + verses_with_interlinear.append({ + 'verse': verse, + 'interlinear_words': interlinear_words or [] + }) + + # Determine if OT or NT for language badge + is_old_testament = book in OT_BOOKS + + html_content = templates.get_template("chapter_interlinear_pdf.html").render( + book=book, + chapter=chapter, + verses_with_interlinear=verses_with_interlinear, + is_old_testament=is_old_testament + ) + pdf_buffer = await render_html_to_pdf_async(html_content) + + filename = f"{book.lower().replace(' ', '-')}-{chapter}-interlinear.pdf" + return StreamingResponse( + pdf_buffer, + media_type="application/pdf", + headers={"Content-Disposition": f"attachment; filename={filename}"} + ) + + @app.get("/book/{book}/chapter/{chapter}/interlinear", response_class=HTMLResponse) def read_chapter_interlinear(request: Request, book: str, chapter: int): """Display a chapter with interlinear Hebrew/Greek for every verse""" @@ -2302,7 +2353,9 @@ def read_chapter_interlinear(request: Request, book: str, chapter: int): "breadcrumbs": breadcrumbs, "current_book": book, "current_chapter": chapter, - "is_old_testament": is_old_testament + "is_old_testament": is_old_testament, + "pdf_available": WEASYPRINT_AVAILABLE, + "pdf_url": f"/book/{book}/chapter/{chapter}/interlinear/pdf" if WEASYPRINT_AVAILABLE else None } ) diff --git a/kjvstudy_org/templates/chapter_interlinear.html b/kjvstudy_org/templates/chapter_interlinear.html index 5a795b6..617a95a 100644 --- a/kjvstudy_org/templates/chapter_interlinear.html +++ b/kjvstudy_org/templates/chapter_interlinear.html @@ -638,6 +638,14 @@