How to Generate EPUB and PDF Books from Lesson Sources Using `build_book.py`
The build_book.py script in the rohitg00/ai-engineering-from-scratch repository compiles modular markdown lessons from phases/ and certifications/ directories into professionally formatted EPUB and PDF e-books using Jinja2 templates, ebooklib, and weasyprint.
The ai-engineering-from-scratch curriculum is designed as a collection of discrete markdown lessons, but is distributed as cohesive e-book files. This transformation is handled by scripts/build_book.py, a Python utility that orchestrates the conversion pipeline from source markdown to publication-ready formats through its main() entry point. Understanding this build system allows you to regenerate course materials whenever content is updated or create custom subsets of the curriculum.
The Build Pipeline Architecture
The script executes a six-stage pipeline to convert raw lesson files into distributable e-books. Each stage handles a specific transformation, ensuring that the final book.epub and book.pdf files maintain consistent styling and navigation.
Stage 1: Source Discovery
According to the source code in scripts/build_book.py, the build process begins by scanning the repository's structured content directories. The script recursively walks through phases/ and certifications/ directories, locating lesson definitions in docs/en.md files. This discovery phase respects the repository's organizational convention where each learning module resides in its own subdirectory with standardized documentation paths.
Stage 2: Markdown Rendering
Once collected, the raw markdown content is converted to HTML using Python's markdown library. The build_book.py implementation processes each docs/en.md file with extensions for table of contents generation and fenced code blocks, preserving the front-matter metadata (such as title, type, and supported languages) while converting the instructional content to semantic HTML suitable for book formatting.
Stage 3: Template Application
The rendered HTML is then injected into a Jinja2 template located at scripts/templates/book.html. This template defines the book's structural elements including the cover page, table of contents hierarchy, and chapter layouts. The build_book.py script passes lesson-specific variables—such as chapter_title and body content—to the Jinja2 Environment, which produces a complete HTML document ready for multi-format export.
Stage 4: EPUB Assembly
For EPUB generation, the script utilizes the ebooklib library to create a standards-compliant EPUB container. The implementation in scripts/build_book.py constructs an epub.EpubBook instance, sets identifiers and metadata using methods like book.set_identifier("ai-engineering") and book.set_title("AI Engineering from Scratch"), bundles CSS stylesheets, and writes the final archive to outputs/book.epub.
Stage 5: PDF Conversion
Simultaneously, the same rendered HTML is processed by weasyprint to generate paginated PDF output. The HTML(string=full_html).write_pdf() method creates outputs/book.pdf with styling that matches the EPUB version, ensuring visual consistency across both digital and print-ready formats.
Command-Line Usage
The build_book.py script provides a flexible CLI for customizing the build process without modifying source code. You can invoke the script from the repository root to generate both formats or specific subsets of the curriculum.
Basic Execution
To generate the complete book with all phases and certifications, run:
python -m scripts.build_book \
--output-dir outputs
This command processes all available lesson sources and writes book.epub and book.pdf to the specified outputs/ directory.
Filtering Content and Formats
For targeted builds, the script accepts several key arguments:
--include-phases– Comma-separated phase numbers (e.g.,01-05) to restrict content to specific curriculum stages--include-certifications– Specific certification tracks (e.g.,claude) to append to the build--no-epub– Boolean flag to skip EPUB generation and produce only PDF--no-pdf– Boolean flag to skip PDF generation and produce only EPUB
Example: Custom Curriculum Build
python -m scripts.build_book \
--output-dir outputs \
--include-phases 01-15 \
--include-certifications claude \
--no-pdf
This invocation generates only an EPUB containing phases 1 through 15 plus the Claude certification track, excluding the PDF output.
Internal Implementation Deep Dive
The core logic inside scripts/build_book.py follows this programmatic pattern. While you typically interact with the CLI, understanding the internal API helps when extending the build system:
from markdown import markdown
from jinja2 import Environment, FileSystemLoader
from ebooklib import epub
from weasyprint import HTML
from pathlib import Path
# 1. Load lesson markdown
md_text = Path("phases/01-math-foundations/01-text-processing/docs/en.md").read_text()
html_body = markdown(md_text, extensions=["toc", "fenced_code"])
# 2. Render with Jinja2 template
env = Environment(loader=FileSystemLoader("scripts/templates"))
template = env.get_template("book.html")
full_html = template.render(chapter_title="Text Processing", body=html_body)
# 3. Build EPUB
book = epub.EpubBook()
book.set_identifier("ai-engineering")
book.set_title("AI Engineering from Scratch")
book.add_item(epub.EpubHtml(content=full_html, file_name="chapter1.html"))
epub.write_epub("outputs/book.epub", book)
# 4. Build PDF
HTML(string=full_html).write_pdf("outputs/book.pdf")
Required Dependencies
The build system requires three key Python packages installed in your environment:
markdown– Parses lesson markdown into HTML with TOC and code block supportebooklib– Constructs the EPUB container and handles metadataweasyprint– Converts HTML/CSS to PDF with proper paginationjinja2– Powers the template rendering engine
Install these via pip before running the build script:
pip install markdown ebooklib weasyprint jinja2
Summary
scripts/build_book.pyserves as the central orchestrator for converting theai-engineering-from-scratchcurriculum from modular markdown into unified e-books- The build pipeline stages include source discovery from
phases/andcertifications/, markdown-to-HTML conversion via themarkdownlibrary, Jinja2 templating usingscripts/templates/book.html, and parallel generation of EPUB (viaebooklib) and PDF (viaweasyprint) formats - Output files are written to the
outputs/directory asbook.epubandbook.pdf - Command-line flags allow filtering by phase numbers (
--include-phases) and certification tracks (--include-certifications), with options to disable specific output formats using--no-epubor--no-pdf
Frequently Asked Questions
How do I add new lessons to the book generation process?
Place your new lesson content in a subdirectory under phases/ or certifications/ following the existing structure: create a docs/ folder containing an en.md file with your markdown content. The build_book.py script automatically discovers these files during the source collection phase, requiring no changes to the build configuration.
Can I generate only the PDF or only the EPUB format?
Yes. Pass the --no-epub flag to skip EPUB generation and produce only the PDF, or use --no-pdf to generate only the EPUB file. These boolean flags allow you to reduce build time when you need only one output format.
What template engine does the build script use?
The build system uses Jinja2 for HTML template rendering. The main template lives at scripts/templates/book.html and receives variables including chapter_title and body (the HTML-rendered markdown content). You can customize the book's visual layout by modifying this template and the associated CSS styles referenced during the EPUB and PDF generation phases.
Where are the generated book files saved?
By default, the script writes book.epub and book.pdf to the outputs/ directory relative to the repository root. You can specify a different destination using the --output-dir command-line argument followed by your preferred path.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →