Skip to main content

Generate PDF databooks from AsciiDoc or Markdown chapters with Jinja2 templating

Project description

databook-generator

Company Logo

PyPI version Python versions License: MIT

Generate polished PDF databooks from AsciiDoc or Markdown chapter files, with full Jinja2 templating throughout — including inside SVG diagrams.

Define your parameters once in a config.json file. Every parameter automatically becomes a variable you can reference inside your chapter content and your SVG images.


Features

  • AsciiDoc (.adoc) chapters rendered via asciidoctor-pdf
  • Markdown (.md) chapters converted to AsciiDoc via pandoc, then rendered to PDF with the same pipeline
  • Jinja2 templating in .adoc, .md, and .svg files — any key in config.json becomes a template variable
  • Logo support — drop logo.svg or logo.png in your images/ folder; it appears on the title page and in the top-right corner of every page
  • Per-page footer with document title and page numbers
  • Auto-discovery of asciidoctor-pdf binary across Homebrew, gem user-install, rbenv, and RVM
  • Clean intermediate file handling — no leftover artefacts unless you ask for them

Requirements

Python

Python 3.8 or newer.

asciidoctor-pdf (required for all PDFs)

asciidoctor-pdf is a Ruby gem and must be installed separately.

macOS

# If you have the system Ruby (not recommended — no write permission):
gem install asciidoctor-pdf --user-install

# Recommended: install Ruby via Homebrew first
brew install ruby
$(brew --prefix ruby)/bin/gem install asciidoctor-pdf

# Add Homebrew gem bin to your PATH permanently (add to ~/.zshrc or ~/.bashrc):
export PATH="$(brew --prefix ruby)/bin:$PATH"
export PATH="$($(brew --prefix ruby)/bin/gem environment gemdir)/bin:$PATH"

Linux (Debian / Ubuntu)

sudo apt update
sudo apt install ruby-full build-essential
gem install asciidoctor-pdf

Linux (Fedora / RHEL / CentOS)

sudo dnf install ruby ruby-devel
gem install asciidoctor-pdf

Windows

  1. Download and install Ruby from rubyinstaller.org (include the MSYS2 toolchain when prompted).
  2. Open a new Command Prompt or PowerShell:
gem install asciidoctor-pdf

Verify installation

asciidoctor-pdf --version
# If not found, locate the gem bin directory:
gem environment gemdir
# Then add <gemdir>/bin to your PATH

pandoc (required only for Markdown chapters)

pandoc is only needed when your chapters are .md files.

macOS

brew install pandoc

Linux (Debian / Ubuntu)

sudo apt install pandoc

Linux (Fedora / RHEL)

sudo dnf install pandoc

Windows

winget install --id JohnMacFarlane.Pandoc
# Or download the installer from https://pandoc.org/installing.html

Verify installation

pandoc --version

Installation

From PyPI

pip install databook-generator

With Markdown support

pip install "databook-generator[markdown]"

From source

git clone https://github.com/rohaansch/databook-generator.git
cd databook-generator
pip install -e .                   # AsciiDoc chapters only
pip install -e ".[markdown]"       # Include Markdown support

Quick Start

1. Create your working directory

my-databook/
├── config.json
├── chapters/
│   ├── introduction.adoc   (or introduction.md)
│   └── chapter2.adoc       (or chapter2.md)
└── images/
    ├── logo.svg            (optional — auto-detected)
    └── my_diagram.svg

2. Write your config

{
    "title": "My Databook",
    "version": "1.0",
    "author": "Your Name",
    "date": "April 2026",
    "product_name": "Widget Pro",
    "chapters": [
        "introduction",
        "chapter2"
    ]
}

3. Run

cd my-databook/
databook-generator -config config.json

The PDF is written to my-databook/My_Databook.pdf.


CLI Reference

databook-generator -config CONFIG [options]

Required:
  -config FILE        Path to the JSON config file

Optional:
  -images DIR         Images directory (default: ./images)
  -chapters DIR       Chapters directory (default: ./chapters)
  -output FILE        Output PDF path (default: <title>.pdf in CWD)
  --keep-intermediates  Keep rendered .adoc and theme files for debugging
  --version           Show version and exit
  -h, --help          Show help

Config File Reference

config.json drives both the document structure and Jinja2 template variables.

Key Required Description
title Yes Document title — used on the title page, footer, and output filename
chapters Yes Ordered list of chapter names (without extension)
author No Author name displayed on the title page
Any other key No Automatically available as a Jinja2 variable in chapters and SVGs

Example

{
    "title": "Specification Document",
    "version": "2.1",
    "author": "Rohan Chadhury",
    "date": "April 2024",
    "support_email": "support@example.com",
    "feature_flags": {
        "include_legacy_notes": false,
        "include_new_section": true
    },
    "chapters": [
        "introduction",
        "measurements",
        "api_example"
    ]
}

Jinja2 Templating

This is the core power feature of databook-generator. Every key you define in config.json is injected as a Jinja2 variable into your chapter files and SVG images at render time.

How it works

When you run databook-generator, it:

  1. Loads your config.json
  2. Passes every key (except "chapters") as a Jinja2 variable
  3. Renders each chapter file as a Jinja2 template
  4. Renders any SVG files that contain Jinja2 syntax
  5. Assembles and generates the final PDF

Variable substitution

Use {{ variable_name }} anywhere in your chapter or SVG:

# In config.json:
"author": "Rohan Chadhury",
"date": "April 2026",
"revision": "Rev1.0"

# In a chapter file (.adoc or .md):
This document covers the Measurements for revision {{ revision }} released on date {{ date }}.

Renders to:

This document covers the Measurements for revision Rev1.0 released on date April 2026

Conditional content

Use {% if %} / {% else %} / {% endif %} to include or exclude sections:

# config.json:
"feature_flags": { "include_legacy_notes": false }

# chapter.adoc:
{% if feature_flags.include_legacy_notes %}
NOTE: This section contains legacy notes for pre-{{ revision }} users.
...
{% endif %}

If include_legacy_notes is false, that entire block is omitted from the PDF.

Loops

Use {% for %} to generate repeated content from a list:

# config.json:
"layer_stack": ["M1", "M2", "M3", "M4", "M5"]

# chapter.adoc:
The following measurements are defined in {{ revision }}:

{% for timing in measurements %}
* {{ timing }}
{% endfor %}

Nested values

Access nested config keys using dot notation:

# config.json:
"timing": { "setup_margin": "50ps", "hold_margin": "20ps" }

# chapter.md:
Setup margin: **{{ timing.setup_margin }}**
Hold margin: **{{ timing.hold_margin }}**

SVG templating

SVG files in your images/ directory are also Jinja2 templates. Any SVG containing {{ or {% is rendered before embedding in the PDF.

<!-- images/product_image.svg -->
<text x="300" y="50">{{ document_name }}  {{ serial_number }}</text>
<text x="300" y="70">Rev: {{ revision }}</text>

This is useful for diagram titles, labels, and revision stamps that should always match the config — no need to manually update SVG text when values change.

Undefined variables

If a template references a variable not present in config.json, it silently renders as an empty string (no errors). This lets you write reusable chapter templates that gracefully degrade for configs that don't define every variable.


Chapter Formats

AsciiDoc (.adoc)

AsciiDoc is a rich markup format with native support for tables, cross-references, callouts, and admonitions. It is the recommended format for complex technical documents.

== Introduction

This spec covers *{{ document_name }}* revision *{{ revision }}*.

[cols="1,2", options="header"]
|===
| Field | Value
| Document Title | {{ document_name }}
| Revision | {{ revision }}
|===

[NOTE]
====
All measurements are in nanometers.
====

<<<

The <<< at the end of a chapter inserts a page break.

Markdown (.md)

Markdown chapters use standard GitHub-Flavoured Markdown (GFM). Under the hood they are converted to AsciiDoc via pandoc before PDF generation, so the same logo, theme, and footer apply.

## Introduction

This report covers **{{ product_name }}** on the **{{ model_number }}** node.

| Field | Value |
|---|---|
| Product | {{ product_name }} |
| Node | {{ model_number }} |

{% if include_appendix %}
> See the appendix for raw measurement data.
{% endif %}

Format rules

  • All chapters in a run must use the same format — either all .adoc or all .md.
  • Do not mix .adoc and .md in the same chapters list. The tool will exit with an error if mixed files are detected.
  • The extension is not included in config.json; the tool discovers it automatically.

Logo

Place one or more logo files in your images/ directory. No configuration is needed — detection is automatic. SVG is recommended for crisp rendering at all sizes.

Two-logo mode (recommended)

Use separate files optimised for each context:

File Where it appears Recommended size
main_logo.svg / main_logo.png Centred on the title page at 45% page width Wide, high-resolution
header_logo.svg / header_logo.png Top-right of every page header at 22% page width Horizontal / compact

Single-logo mode (fallback)

If dedicated files are not found, logo.svg / logo.png is used for both roles automatically.

Priority order

Title page  → main_logo.svg/png   → logo.svg/png
Header      → header_logo.svg/png → logo.svg/png

SVG logo templating

Logo SVG files can contain Jinja2 directives just like chapter files. For example, to stamp a version number on the logo:

<!-- images/header_logo.svg -->
<text x="200" y="60">Rev {{ version }}</text>

Directory Layout

The expected layout when running from a project directory:

my-databook/
├── config.json          Required
├── chapters/            Default chapter directory (override with -chapters)
│   ├── intro.adoc       or intro.md
│   └── section2.adoc    or section2.md
└── images/              Default images directory (override with -images)
    ├── logo.svg          Auto-detected logo
    └── diagram.svg       Referenced from chapters as image::diagram.svg[]

Override defaults:

databook-generator \
  -config /path/to/config.json \
  -chapters /path/to/my/chapters \
  -images /path/to/my/images \
  -output /path/to/output/report.pdf

Syntax Highlighting

Code blocks in chapters use Rouge for syntax highlighting. Install it for coloured output:

gem install rouge

Python API

DatabookBuilder lets you drive the entire build from Python code — no CLI, no config.json required.

Basic usage

from databook_generator import DatabookBuilder

builder = DatabookBuilder(
    config={
        "title": "Device Characterization Report",
        "version": "3.0",
        "product": "XR-9000",
        "process_node": "5nm FinFET",
        "chapters": ["overview", "measurements"],
    },
    chapters_dir="chapters",
    images_dir="images",
)
builder.build(output="report.pdf")

You can also pass a path to a JSON file instead of a dict:

builder = DatabookBuilder(config="config.json", chapters_dir="chapters", images_dir="images")

Injecting custom chapters

Use add_chapter() to insert a dynamically generated chapter at any position. The chapter content is a Jinja2 template; you supply the variables at call time.

builder.add_chapter(
    template_path="templates",          # directory containing the template
    template_file="cell_library.adoc",  # .adoc or .md template
    variables={                         # Jinja2 variables for this chapter only
        "cells": [
            {
                "name": "NAND2_X1",
                "description": "2-input NAND gate",
                "inputs": ["A", "B"],
                "output": "ZN",
                "area_um2": 0.42,
            },
            {
                "name": "INV_X1",
                "description": "Inverter",
                "inputs": ["A"],
                "output": "ZN",
                "area_um2": 0.21,
            },
        ]
    },
    images=[                            # images this chapter needs
        "templates/images/nand2_schematic.svg",
        "templates/images/inv_schematic.svg",
    ],
    after="overview",                   # insert after the 'overview' chapter
    name="cell_library",                # logical name for after/before lookups
)

print(builder.chapter_names)
# → ['overview', 'cell_library', 'measurements']

builder.build()

Positioning options

Parameter Behaviour
after="chapter_name" Insert immediately after the named chapter
before="chapter_name" Insert immediately before the named chapter
position=N Insert at zero-based index N
(none) Append to the end

add_chapter() returns self, so calls can be chained.

Chapter-local variables

Variables passed to add_chapter(variables=...) are merged over the global config variables — chapter-level keys override global ones for that chapter's scope only. Global variables are still available in the template.

Keeping intermediate files

Pass keep_intermediates=True to build() to retain the rendered .adoc files and theme YAML for debugging:

builder.build(output="report.pdf", keep_intermediates=True)

Examples

The example/ directory contains three complete working examples:

AsciiDoc example

cd example/adoc_example/
databook-generator -config config.json

Demonstrates:

  • Jinja2 variable substitution and conditionals in .adoc
  • SVG diagrams with Jinja2 labels rendered at build time
  • Logo on every page

Markdown example

cd example/markdown_example/
databook-generator -config config.json

Demonstrates:

  • Jinja2 variable substitution and conditionals in .md
  • GFM tables and code fences
  • Chart SVGs referenced from Markdown

Python API example

cd example/api_example/
python generate_report.py

Demonstrates the full Python API:

  • DatabookBuilder instantiated with a config dict loaded from JSON
  • A custom Cell Library chapter generated from a Jinja2 template and a list of cell dicts
  • Cell schematics injected as chapter-local images
  • Chapter inserted between overview and measurements using after="overview"

The example directory layout:

example/api_example/
├── config.json                      Standard config (overview + measurements)
├── chapters/                        Standard chapter files
│   ├── overview.adoc
│   └── measurements.adoc
├── images/                          Shared images (logo, charts)
├── templates/                       Custom chapter templates
│   ├── cell_library.adoc            Jinja2 template — loops over a list of cells
│   └── images/                      Cell-specific schematics
│       ├── nand2_schematic.svg
│       └── inv_schematic.svg
└── generate_report.py               Python script using DatabookBuilder

Releasing to PyPI

Setup (one time)

  1. Create a PyPI account
  2. Enable Trusted Publishing in your PyPI project settings, pointing to this repository and the publish.yml workflow

Release process

# Bump version in:
#   databook_generator/__init__.py  (VERSION)
#   pyproject.toml                  (version field)
#   CHANGELOG.md                    (add entry)

git add .
git commit -m "Release v1.1.0"
git tag v1.1.0
git push origin main --tags

The GitHub Actions workflow (.github/workflows/publish.yml) automatically builds and publishes to PyPI when a v*.*.* tag is pushed.


Contributing

Pull requests are welcome. For major changes, open an issue first.

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Make your changes and add tests
  4. Push and open a PR against main

License

MIT — © 2024 Rohan Chadhury

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

databook_generator-1.0.3.tar.gz (182.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

databook_generator-1.0.3-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file databook_generator-1.0.3.tar.gz.

File metadata

  • Download URL: databook_generator-1.0.3.tar.gz
  • Upload date:
  • Size: 182.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for databook_generator-1.0.3.tar.gz
Algorithm Hash digest
SHA256 6852fa77be3bb705c3b322d087f8278fb3b0c76fdf502dc6ca9db95e05f3d7b2
MD5 342b74129612f468a53f437643878d41
BLAKE2b-256 383f09b50219139e39b62ff6e358d1b0ef9a0a82d0e4f1b211125cf3edaf7a06

See more details on using hashes here.

File details

Details for the file databook_generator-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for databook_generator-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7b0552f5c232a5d42de0647e39f7370dec869817cf6950acfc779d77102c2926
MD5 494ac7e7efe1943d382f21422a22f5cc
BLAKE2b-256 51bd3aa549c69b9ccdac6adcd5328b7f12168296fb61b57ad71c68e2059a5d9e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page