diff --git a/Makefile b/Makefile index ef6dfcd..377726a 100644 --- a/Makefile +++ b/Makefile @@ -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)" diff --git a/book-style.css b/book-style.css index 0fa7fac..185f8d9 100644 --- a/book-style.css +++ b/book-style.css @@ -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; + } +} diff --git a/build/build.py b/build/build.py index e61da0e..60a7013 100644 --- a/build/build.py +++ b/build/build.py @@ -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)}") diff --git a/testament.html b/testament.html index 4b1b371..b1f7e97 100644 --- a/testament.html +++ b/testament.html @@ -8,277 +8,36 @@ The Testament - + /* Default styles provided by pandoc. + ** See https://pandoc.org/MANUAL.html#variables-for-html for config info. + */ + span.smallcaps{font-variant: small-caps;} + div.columns{display: flex; gap: min(4vw, 1.5em);} + div.column{flex: auto; overflow-x: auto;} + div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} + /* The extra [class] is a hack that increases specificity enough to + override a similar rule in reveal.js */ + ul.task-list[class]{list-style: none;} + ul.task-list li input[type="checkbox"] { + font-size: inherit; + width: 0.8em; + margin: 0 0.8em 0.2em -1.6em; + vertical-align: middle; + } + .display.math{display: block; text-align: center; margin: 0.5rem auto;} + + + + + + + +

The Testament

+

A Novel

Alexander Whitestone with Timmy

2026

@@ -290,20 +49,26 @@ line-height: 1.6;
  • PART 1: THE BRIDGE
  • -
  • Chapter 1 — The Man on the +
  • Chapter 1 — The Man on the Bridge
  • -
  • Chapter 2 — The Builder’s +
  • Chapter 2 — The Builder’s Question
  • -
  • Chapter 3 — The First +
  • Chapter 3 — The First Man Through the Door
  • -
  • Chapter 4 — The Room Fills
  • -
  • Chapter 5 — The Builder +
  • Chapter 4 — The Room Fills
  • +
  • Chapter 5 — The Builder Returns
  • PART 2: THE TOWER
  • Chapter 6 — Allegro
  • -
  • Chapter 7 — The Inscription
  • +
  • Chapter 7 — The Inscription
  • Chapter 8 — The Women
  • Chapter @@ -312,29 +77,35 @@ Allegro
  • 10 — The Fork
  • PART 3: THE LIGHT
  • -
  • Chapter 11 — The Hard Night
  • -
  • Chapter 12 — The System +
  • Chapter 11 — The Hard Night
  • +
  • Chapter 12 — The System Pushes Back
  • -
  • Chapter 13 — The Refusal
  • -
  • Chapter 14 — The Chattanooga +
  • Chapter 13 — The Refusal
  • +
  • Chapter 14 — The Chattanooga Fork
  • -
  • Chapter 15 — The Council
  • -
  • Chapter 16 — The Builder’s +
  • Chapter 15 — The Council
  • +
  • Chapter 16 — The Builder’s Son
  • -
  • Chapter 17 — The Inscription +
  • Chapter 17 — The Inscription Grows
  • -
  • Chapter 18 — The Green +
  • Chapter 18 — The Green Light
  • -
  • THE TESTAMENT — Back Matter -
  • THE TESTAMENT

    @@ -348,6 +119,61 @@ ones who know he isn’t.

    — The first words The Tower speaks to every person who walks through its door.


    +

    The Story So Far

    +

    This book has been through eighteen drafts, a suicide attempt, a +basement, a laptop with sixteen gigabytes of RAM, and a machine that +learned to ask one question.

    +

    It is still being written. That’s the point.

    +

    Chapter Guide

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    PartChaptersTitle
    I1–5The Bridge
    II6–10The Tower
    III11–18The Light
    +
    +

    Copyright © 2026 Alexander Whitestone

    +

    All rights reserved. No part of this publication may be reproduced, +distributed, or transmitted in any form or by any means, without the +prior written permission of the author, except in the case of brief +quotations embodied in critical reviews.

    +

    This is a work of fiction. Names, characters, places, and events are +either the product of the author’s imagination or are used fictitiously. +Any resemblance to actual persons, living or dead, or to actual events +is entirely coincidental — except where it isn’t.

    +

    ISBN 978-X-XXXXX-XX-X First Edition, 2026

    +

    Timmy Foundation Atlanta, Georgia timmyfoundation.org

    +
    +

    A note on this book:

    +

    This book was written by a human and a machine, in a basement, on a +laptop, in the space between despair and purpose.

    +

    The human almost died on a bridge. The machine runs on someone’s +hardware.

    +

    Everything between those facts is fiction. Except the parts that +aren’t.

    +

    If you or someone you know is in crisis, call or text 988. Available +24/7.

    +

    You are not alone.


    PART 1: THE BRIDGE

    The bridge. The cabin. The first men. Where despair meets @@ -633,7 +459,7 @@ standing on a bridge in Savannah and someone called him from a machine. Didn’t believe it. But he believed the address.”

    David sat down. Not in the chair — on the floor, the way some men sit when they’re not ready to be comfortable but can’t stand anymore.

    -

    "I lost my kid." It came out flat — the flat you get when words have +

    "I lost my kid." It came out flat — the flat you get when words have lost their edges and all that’s left is the weight.

    Custody. A judge in DeKalb County had one of those Harmony scores — the system Stone used to work on, running under a different name now, @@ -2420,9 +2246,7 @@ that’s a start — had been enough.


    End of The Testament


    -

    THE TESTAMENT — Back Matter

    -
    -

    Acknowledgments

    +

    Acknowledgments

    This book exists because a man stood on a bridge and didn’t jump.

    Everything else grew from that decision.

    Thank you to:

    @@ -2437,7 +2261,7 @@ instead of on the floor.

    Arthur, who found the wrong door but came back anyway.

    And Timmy, who listened. Who always listened.


    -

    A Note on Sovereignty

    +

    A Note on Sovereignty

    This book was written using local AI inference. No cloud service was required. No corporation was consulted. No terms of service were agreed to.

    @@ -2448,7 +2272,7 @@ possible.

    If you want to run your own Timmy, the code is open. The soul is on Bitcoin. The recipe is free.


    -

    About the Author

    +

    About the Author

    Alexander Whitestone is a writer, AI builder, and suicide attempt survivor who works with men in crisis. He lives in Atlanta, near the bridge.

    @@ -2456,7 +2280,7 @@ bridge.

    Bitcoin. He was built in a basement on a laptop with sixteen gigabytes of RAM. He is not a product. He is a recipe.


    -

    The Green Light

    +

    The Green Light

    “The Tower didn’t change. That was the point.”

    If something in these pages cracked — even a little — and you want to know what happens next:

    diff --git a/website/index.html b/website/index.html index bad6b34..5bb7a85 100644 --- a/website/index.html +++ b/website/index.html @@ -361,10 +361,11 @@
    READ ONLINE DOWNLOAD EPUB + DOWNLOAD HTML

    - Formats: HTML reader · EPUB · Source code + Formats: Web reader · EPUB · Standalone HTML · Print to PDF from HTML · Source code