mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
Add PDF download for interlinear chapter pages
- 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 <noreply@anthropic.com>
This commit is contained in:
+54
-1
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -638,6 +638,14 @@
|
||||
</div>
|
||||
|
||||
<div class="print-actions">
|
||||
{% if pdf_available and pdf_url %}
|
||||
<a href="{{ pdf_url }}" class="print-btn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
Download PDF
|
||||
</a>
|
||||
{% endif %}
|
||||
<button onclick="window.print()" class="print-btn">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ book }} {{ chapter }} - Interlinear (KJV)</title>
|
||||
<style>
|
||||
@page {
|
||||
size: letter;
|
||||
margin: 0.75in;
|
||||
@bottom-center {
|
||||
content: counter(page);
|
||||
font-family: "et-book", Georgia, serif;
|
||||
font-size: 10pt;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "et-book", Georgia, "Times New Roman", serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 22pt;
|
||||
margin: 0 0 0.05in 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
margin-bottom: 0.25in;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.language-note {
|
||||
font-size: 9pt;
|
||||
color: #666;
|
||||
margin-bottom: 0.3in;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 0.1in;
|
||||
}
|
||||
|
||||
.verse-section {
|
||||
margin-bottom: 0.3in;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.verse-header {
|
||||
margin-bottom: 0.1in;
|
||||
}
|
||||
|
||||
.verse-number {
|
||||
font-size: 11pt;
|
||||
font-weight: 700;
|
||||
color: #4a7c59;
|
||||
margin-right: 0.1in;
|
||||
}
|
||||
|
||||
.verse-text {
|
||||
font-size: 11pt;
|
||||
font-style: italic;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.interlinear-flow {
|
||||
margin: 0.1in 0;
|
||||
}
|
||||
|
||||
.word-unit {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
margin: 0 0.03in 0.12in 0.03in;
|
||||
vertical-align: top;
|
||||
padding: 0.03in 0.05in;
|
||||
}
|
||||
|
||||
.word-original {
|
||||
display: block;
|
||||
font-size: 14pt;
|
||||
font-weight: 400;
|
||||
color: #222;
|
||||
margin-bottom: 0.02in;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.word-original.hebrew {
|
||||
direction: rtl;
|
||||
font-family: "SBL Hebrew", "Ezra SIL", "Times New Roman", serif;
|
||||
font-size: 15pt;
|
||||
}
|
||||
|
||||
.word-original.greek {
|
||||
font-family: "SBL Greek", "Gentium Plus", "Times New Roman", serif;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
.word-english {
|
||||
display: block;
|
||||
font-size: 8pt;
|
||||
color: #4a7c59;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.word-strongs {
|
||||
display: block;
|
||||
font-size: 6pt;
|
||||
color: #888;
|
||||
font-family: monospace;
|
||||
margin-top: 0.01in;
|
||||
}
|
||||
|
||||
.no-interlinear {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 0.4in;
|
||||
font-size: 9pt;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ book }} {{ chapter }}</h1>
|
||||
<p class="subtitle">Interlinear Bible — King James Version</p>
|
||||
<p class="language-note">
|
||||
{% if is_old_testament %}Hebrew{% else %}Greek{% endif %} text with English translation and Strong's numbers
|
||||
</p>
|
||||
|
||||
{% for item in verses_with_interlinear %}
|
||||
<div class="verse-section">
|
||||
<div class="verse-header">
|
||||
<span class="verse-number">{{ item.verse.verse }}</span>
|
||||
<span class="verse-text">{{ item.verse.text }}</span>
|
||||
</div>
|
||||
|
||||
{% if item.interlinear_words %}
|
||||
<div class="interlinear-flow">
|
||||
{% for word in item.interlinear_words %}
|
||||
<span class="word-unit">
|
||||
<span class="word-original {% if is_old_testament %}hebrew{% else %}greek{% endif %}">{{ word.original }}</span>
|
||||
<span class="word-english">{{ word.english }}</span>
|
||||
{% if word.strongs %}
|
||||
<span class="word-strongs">{{ word.strongs }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="no-interlinear">Interlinear data not available for this verse.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="footer">
|
||||
From KJV Study • kjvstudy.org
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user