testament-burn: Enhanced build system with HTML output, weasyprint PDF fallback, and print CSS

- build.py: Added weasyprint fallback for PDF when xelatex unavailable
- build.py: Added --html flag for standalone pandoc HTML book output
- book-style.css: Added @media print rules for browser Print-to-PDF
- Makefile: Added 'html' target and weasyprint check to 'make check'
- website/index.html: Added HTML download link and Print to PDF option
- Regenerated testament-complete.md, testament.epub, testament.html
This commit is contained in:
Alexander Whitestone
2026-04-10 18:20:58 -04:00
parent a5560b7bd3
commit d4ccef9c24
5 changed files with 248 additions and 316 deletions

View File

@@ -1,9 +1,9 @@
# THE TESTAMENT — Build System
# Usage: make all | make pdf | make epub | make md | make clean
# Usage: make all | make pdf | make epub | make html | make md | make clean
.PHONY: all pdf epub md clean check
.PHONY: all pdf epub html md clean check
all: md epub
all: md epub html
md:
python3 build/build.py --md
@@ -14,6 +14,9 @@ epub: md
pdf: md
python3 build/build.py --pdf
html: md
python3 build/build.py --html
clean:
rm -f testament-complete.md
rm -f build/output/*.epub build/output/*.pdf
@@ -22,3 +25,4 @@ clean:
check:
@which pandoc >/dev/null 2>&1 && echo "✓ pandoc" || echo "✗ pandoc (brew install pandoc)"
@which xelatex >/dev/null 2>&1 && echo "✓ xelatex" || echo "✗ xelatex (install MacTeX)"
@python3 -c "import weasyprint" 2>/dev/null && echo "✓ weasyprint" || echo "— weasyprint (optional, PDF fallback)"

View File

@@ -226,3 +226,45 @@ a {
line-height: 1.6;
}
}
/* Print media — for browser Print-to-PDF */
@media print {
body {
font-size: 11pt;
line-height: 1.6;
color: #000;
}
@page {
size: 5.5in 8.5in;
margin: 0.75in 0.85in;
}
h1 {
page-break-before: always;
font-size: 20pt;
margin-top: 3em;
}
h1:first-of-type {
margin-top: 5em;
}
h2 {
page-break-before: always;
}
a {
color: #000;
text-decoration: none;
}
.green {
color: #000;
}
/* Hide any nav/TOC in print */
nav#TOC {
page-break-after: always;
}
}

View File

@@ -11,7 +11,8 @@ Usage:
python3 build/build.py # all formats
python3 build/build.py --md # markdown only
python3 build/build.py --epub # EPUB only
python3 build/build.py --pdf # PDF only (requires xelatex)
python3 build/build.py --pdf # PDF (xelatex or weasyprint fallback)
python3 build/build.py --html # standalone HTML book
Requirements:
- pandoc (brew install pandoc)
@@ -124,31 +125,85 @@ def compile_epub():
def compile_pdf():
"""Generate PDF via pandoc + xelatex."""
"""Generate PDF via pandoc + xelatex, or weasyprint fallback."""
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
if not shutil.which("xelatex"):
print(" PDF SKIPPED: xelatex not found (install MacTeX)")
return False
# Try xelatex first (best quality)
if shutil.which("xelatex"):
cmd = [
"pandoc", str(OUT_MD),
"-o", str(OUT_PDF),
"--pdf-engine=xelatex",
"--toc", "--toc-depth=2",
]
if METADATA.exists():
cmd.extend(["--metadata-file", str(METADATA)])
print(" Building PDF (xelatex)... this takes a minute")
r = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if r.returncode == 0:
size = OUT_PDF.stat().st_size
print(f" PDF: {OUT_PDF.name} ({size:,} bytes, {size/(1024*1024):.1f} MB)")
return True
else:
print(f" PDF (xelatex) FAILED: {r.stderr[:300]}")
cmd = [
"pandoc", str(OUT_MD),
"-o", str(OUT_PDF),
"--pdf-engine=xelatex",
"--toc", "--toc-depth=2",
]
# Fallback: pandoc HTML + weasyprint
try:
import weasyprint
html_tmp = OUTPUT_DIR / "the-testament-print.html"
cmd = [
"pandoc", str(OUT_MD),
"-o", str(html_tmp),
"--standalone",
"--toc", "--toc-depth=2",
"--css", str(STYLESHEET),
"--metadata", "title=The Testament",
]
if METADATA.exists():
cmd.extend(["--metadata-file", str(METADATA)])
print(" Building PDF (weasyprint)...")
r = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
if r.returncode != 0:
print(f" PDF (pandoc HTML) FAILED: {r.stderr[:200]}")
return False
if METADATA.exists():
cmd.extend(["--metadata-file", str(METADATA)])
print(" Building PDF (xelatex)... this takes a minute")
r = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if r.returncode == 0:
doc = weasyprint.HTML(filename=str(html_tmp))
doc.write_pdf(str(OUT_PDF))
html_tmp.unlink(missing_ok=True)
size = OUT_PDF.stat().st_size
print(f" PDF: {OUT_PDF.name} ({size:,} bytes, {size/(1024*1024):.1f} MB)")
return True
except Exception as e:
print(f" PDF FAILED: {e}")
print(" PDF SKIPPED: no PDF engine found (install MacTeX or fix weasyprint)")
return False
def compile_html():
"""Generate a standalone HTML book for the web reader."""
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
OUT_HTML = REPO / "testament.html"
cmd = [
"pandoc", str(OUT_MD),
"-o", str(OUT_HTML),
"--standalone",
"--toc", "--toc-depth=2",
"--css", "book-style.css",
"--metadata", "title=The Testament",
"--variable", "pagetitle=The Testament",
]
if METADATA.exists():
cmd.extend(["--metadata-file", str(METADATA)])
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode == 0:
size = OUT_HTML.stat().st_size
print(f" HTML: {OUT_HTML.name} ({size:,} bytes, {size/1024:.0f} KB)")
return True
else:
print(f" PDF FAILED: {r.stderr[:300]}")
print(f" HTML FAILED: {r.stderr[:200]}")
return False
@@ -158,13 +213,14 @@ def main():
do_md = "--md" in args or do_all
do_epub = "--epub" in args or do_all
do_pdf = "--pdf" in args or do_all
do_html = "--html" in args or do_all
print("=" * 50)
print(" THE TESTAMENT — Build System")
print("=" * 50)
# Step 1: Always compile markdown first
if do_md or do_epub or do_pdf:
if do_md or do_epub or do_pdf or do_html:
compile_markdown()
# Step 2: EPUB
@@ -175,11 +231,16 @@ def main():
if do_pdf:
compile_pdf()
# Step 4: Standalone HTML
if do_html:
compile_html()
print("=" * 50)
print(" Build complete.")
print("=" * 50)
for f in [OUT_MD, OUT_EPUB, OUT_PDF]:
OUT_HTML = REPO / "testament.html"
for f in [OUT_MD, OUT_EPUB, OUT_PDF, OUT_HTML]:
if f.exists():
print(f"{f.relative_to(REPO)}")

File diff suppressed because one or more lines are too long

View File

@@ -361,10 +361,11 @@
<div style="display: flex; flex-wrap: wrap; gap: 1rem; margin: 2rem 0; justify-content: center;">
<a href="reader.html" class="cta">READ ONLINE</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/raw/branch/main/build/output/the-testament.epub" class="cta" style="background: transparent; border: 1px solid var(--green); color: var(--green);">DOWNLOAD EPUB</a>
<a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament/raw/branch/main/testament.html" class="cta" style="background: transparent; border: 1px solid var(--green); color: var(--green);">DOWNLOAD HTML</a>
</div>
<p style="text-align: center; color: var(--grey); font-size: 0.9rem; margin-top: 1rem;">
Formats: HTML reader &middot; EPUB &middot; <a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament" style="color: var(--green);">Source code</a>
Formats: Web reader &middot; EPUB &middot; Standalone HTML &middot; Print to PDF from HTML &middot; <a href="https://forge.alexanderwhitestone.com/Timmy_Foundation/the-testament" style="color: var(--green);">Source code</a>
</p>
</section>