S SmartDocs
Série: English python 96 linhas · Atualizado 2026-08-02

build_chapters.py

English/English Grammar/build_chapters.py

#!/usr/bin/env python3
"""Build a standalone, fully styled PDF for every chapter/appendix file.

Each chapter fragment in chapters/ is wrapped in a small driver document
that loads the shared preamble, sets the correct chapter number, and pulls
cross-reference targets from main.aux (via xr-hyper) so references to other
chapters resolve to the numbers used in the full book.

Output: one PDF per chapter in pdfs/, e.g. pdfs/ch01-nouns-articles.pdf
"""

import re
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent
CHAPTERS = ROOT / "chapters"
BUILD = ROOT / "build"
OUT = ROOT / "pdfs"

TITLES = {
    "preface": "How to Use This Book",
}

# Appendix letter -> chapter counter value before the \chapter command
APPENDICES = {
    "app-irregular-verbs": 0,   # Appendix A
    "app-glossary": 1,          # Appendix B
    "app-answer-key": 2,        # Appendix C
}

DRIVER = r"""\documentclass[11pt,twoside,openany]{book}
\usepackage{xr-hyper}
\input{preamble}
\externaldocument{main}
\hypersetup{pdftitle={English Grammar Course --- %(title)s}}
\begin{document}
%(setup)s
\input{chapters/%(name)s}
\end{document}
"""


def setup_for(name: str) -> str:
    if name == "preface":
        return ""
    if name in APPENDICES:
        return "\\appendix\n\\setcounter{chapter}{%d}" % APPENDICES[name]
    m = re.match(r"ch(\d+)", name)
    if m:
        return "\\setcounter{chapter}{%d}" % (int(m.group(1)) - 1)
    raise ValueError(f"Unrecognized chapter file: {name}")


def main() -> int:
    BUILD.mkdir(exist_ok=True)
    OUT.mkdir(exist_ok=True)

    if not (ROOT / "main.aux").exists():
        print("main.aux not found -- compile main.tex first", file=sys.stderr)
        return 1

    failures = []
    files = sorted(CHAPTERS.glob("*.tex"))
    for i, tex in enumerate(files, 1):
        name = tex.stem
        title = TITLES.get(name, name)
        driver = BUILD / f"{name}.tex"
        driver.write_text(
            DRIVER % {"name": name, "setup": setup_for(name), "title": title}
        )
        print(f"[{i}/{len(files)}] {name} ...", flush=True)
        res = subprocess.run(
            [
                "latexmk", "-pdf", "-interaction=nonstopmode",
                f"-output-directory={BUILD}", str(driver),
            ],
            cwd=ROOT, capture_output=True, text=True,
        )
        pdf = BUILD / f"{name}.pdf"
        if res.returncode != 0 or not pdf.exists():
            failures.append(name)
            print(res.stdout[-2500:], file=sys.stderr)
            continue
        (OUT / f"{name}.pdf").write_bytes(pdf.read_bytes())

    if failures:
        print("FAILED:", ", ".join(failures), file=sys.stderr)
        return 1
    print(f"OK: {len(files)} PDFs written to {OUT}")
    return 0


if __name__ == "__main__":
    sys.exit(main())