Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
afa0550
Return don't just call process()
dannyadair Apr 13, 2025
d6382c0
Simplify docs for custom_processing
dannyadair Apr 13, 2025
3dcafe3
Standardise YAML file extension from .yml to .yaml
dannyadair Apr 15, 2025
9b252bf
Add Cursor/VSCode config
dannyadair Apr 15, 2025
93c59de
WIP Refactor for clean structure to support custom locations
dannyadair Apr 15, 2025
821cefb
Update tests - test build of all examples
dannyadair Apr 15, 2025
5cfb964
Update examples - add example of custom locations
dannyadair Apr 15, 2025
eba6a3b
Linting
dannyadair Apr 15, 2025
fa1c01f
Factor out logging into separate mixin class
dannyadair Apr 16, 2025
6630561
Make PDFBaker options its own (data)class
dannyadair Apr 16, 2025
e35f3f4
Minor tidy-up
dannyadair Apr 16, 2025
bce23a3
Better docstring
dannyadair Apr 16, 2025
86b2c78
Add link to github repo back
dannyadair Apr 16, 2025
71a6db6
Fix capitalisation
dannyadair Apr 16, 2025
d5863f2
Clarify README
dannyadair Apr 17, 2025
a10e05e
Refactor to handle custom locations properly
dannyadair Apr 18, 2025
5d47a9f
Tear down build correctly
dannyadair Apr 18, 2025
415b72f
Add diagrams for some workflow visualization
dannyadair Apr 19, 2025
47d6903
Various improvements
dannyadair Apr 19, 2025
34c85ec
Fix test debugging
dannyadair Apr 19, 2025
f9765f9
Improve use of diagrams
dannyadair Apr 19, 2025
21b5a39
Minor clarification
dannyadair Apr 19, 2025
daba607
Minor clarification
dannyadair Apr 19, 2025
409a846
Linting
dannyadair Apr 19, 2025
63f4c33
Minor clarification
dannyadair Apr 19, 2025
8cafbd7
Fix directories calculation
dannyadair Apr 19, 2025
af97e51
Catch pyyaml ScannerError for more meaningful error message
dannyadair Apr 19, 2025
6e41748
Fix CLI return code
dannyadair Apr 19, 2025
9aac8ca
Fix timing of teardown
dannyadair Apr 19, 2025
06c2c76
Fix settings inheritance
dannyadair Apr 19, 2025
651ce60
Improve logging
dannyadair Apr 19, 2025
4c1b8c8
Enable compression in the regular example
dannyadair Apr 19, 2025
a49929a
Adjust error message
dannyadair Apr 19, 2025
0a74d99
Remove only other console log handlers, not all
dannyadair Apr 19, 2025
6562adf
Handle "gs not found" specifically with own error message
dannyadair Apr 19, 2025
1d8f65a
Complete test suite
dannyadair Apr 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ wheels/
# venv
/.venv

# PyCharm
.idea/

# Backup files
**.~
*.swp
23 changes: 23 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Examples",
"type": "debugpy",
"request": "launch",
"module": "pdfbaker",
"args": ["bake", "examples/examples.yaml"],
"console": "integratedTerminal"
},
{
"name": "Debug Tests",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": ["-v", "tests"],
"justMyCode": false,
"env": {"PYTEST_ADDOPTS": "--no-cov"},
"console": "integratedTerminal"
}
]
}
52 changes: 52 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Bake",
"type": "shell",
"command": "python -m pdfbaker bake ${input:configPath}",
"group": "build",
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "Run Tests",
"type": "shell",
"command": "pytest -v tests",
"group": "test",
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "Run Coverage",
"type": "shell",
"command": "pytest --cov=pdfbaker --cov-report=html",
"group": "test",
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "View Coverage",
"type": "shell",
"command": "python -c \"import webbrowser; webbrowser.open('htmlcov/index.html')\"",
"presentation": {
"reveal": "never",
"panel": "new"
}
}
],
"inputs": [
{
"id": "configPath",
"type": "promptString",
"description": "Path to main YAML config file",
"default": "examples/examples.yaml"
}
]
}
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pipx ensurepath
sudo apt install ghostscript
```

- If you want to embed particular fonts, they need to be installed. For example for
[Roboto fonts](https://fonts.google.com/specimen/Roboto):
- If you your templates embed particular fonts, they need to be installed. For example
for [Roboto fonts](https://fonts.google.com/specimen/Roboto):
```bash
sudo apt install fonts-roboto
```
Expand All @@ -55,14 +55,22 @@ pdfbaker bake <config_file>

This will produce your PDF files in a `dist/` directory where your configuration file
lives. It will also create a `build/` directory with intermediate files, which is only
kept if you specify `--debug`.
kept if you specify `--keep-build-files` (or `--debug`).

## Examples

For working examples, see the [examples](examples) directory.<br> Create all PDFs with:
For working examples, see the [examples](examples) directory:

- [minimal](examples/minimal) - Basic usage
- [regular](examples/regular) - Standard features
- [variants](examples/variants) - Document variants
- [custom_locations](examples/custom_locations) - Custom file/directory locations
- [custom_processing](examples/custom_processing) - Custom processing with Python

Create all PDFs with:

```bash
pdfbaker bake examples/examples.yml
pdfbaker bake examples/examples.yaml
```

## Documentation
Expand All @@ -76,6 +84,8 @@ pdfbaker bake examples/examples.yml

## Development

All source code is [on GitHub](https://github.com/pythonnz/pdfbaker).

This project uses [uv](https://github.com/astral-sh/uv) for dependency management. The
`uv.lock` file ensures reproducible builds.

Expand Down
38 changes: 30 additions & 8 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,46 @@

```
project/
├── kiwipycon.yml # Main configuration
├── kiwipycon.yaml # Main configuration
├── material_specs/ # A document
│ ├── config.yml # Document configuration
│ ├── config.yaml # Document configuration
│ ├── images/ # Images
│ ├── pages/ # Page configurations
│ └── templates/ # SVG templates
└── prospectus/ # Another document
├── config.yml
├── config.yaml
├── images/
├── pages/
└── templates/
```

## Configuration Workflow

For every page, your main configuration (for all documents), document configuration (for
all pages of this document) and the page configuration are merged to form the context
provided to your page template.

```mermaid
flowchart TD
subgraph Configuration
Main[YAML Main Config] -->|inherits| Doc[YAML Document Config]
Doc -->|inherits| Page[YAML Page Config]
end

subgraph Page Processing
Template[SVG Template]
Page -->|context| Render[Template Rendering]
Template -->|jinja2| Render
Render -->|output| SVG[SVG File]
SVG -->|cairosvg| PDF[PDF File]
end
```

## Main Configuration File

| Option | Type | Default | Description |
| ----------------- | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------ |
| `documents` | array | Yes | List of document directories. Each directory must contain a `config.yml` file |
| `documents` | array | Yes | List of document directories. Each directory must contain a `config.yaml` file |
| `style` | object | No | Global style definitions that may reference `theme` values |
| `theme` | object | No | Reusable values (colors, fonts, spacing, etc.) used by `style` |
| `compress_pdf` | boolean | `false` | Whether to compress the final PDF. Requires Ghostscript. |
Expand All @@ -33,7 +55,7 @@ highlighted in the color specified by the `highlight_color` in your `style`.
Example:

```yaml
# kiwipycon.yml
# kiwipycon.yaml

documents:
- prospectus
Expand Down Expand Up @@ -74,13 +96,13 @@ for each document.
| Option | Type | Required | Description |
| ---------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `filename` | string | Yes | Filename (without extension) of the final PDF document. Can use variables, particularly `variant` (see [Variants](variants.md)) |
| `pages` | array | Yes | List of page names. Each page must have a corresponding `.yml` file in the `pages/` directory |
| `pages` | array | Yes | List of page names. Each page must have a corresponding `.yaml` file in the `pages/` directory |
| `variants` | array | No | List of document variants (see [Variants](variants.md)) |

Example:

```yaml
# prospectus/config.yml
# prospectus/config.yaml

filename: "Kiwi PyCon {{ conference.year }} - Prospectus" # Use config values in config values!
title: "Sponsorship Prospectus"
Expand All @@ -105,7 +127,7 @@ merged with this for access to all settings in your template.
Example:

```yaml
# pages/conference_schedule.yml
# pages/conference_schedule.yaml

template: list_section.svg.j2

Expand Down
80 changes: 8 additions & 72 deletions docs/custom_processing.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,86 +10,22 @@ customize the document generation process. This allows you to:

## Basic Structure

Your `bake.py` should define a `process_document` function:
As a naming convention, your `bake.py` needs to define a `process_document` function:

```python
from pdfbaker.document import PDFBakerDocument

def process_document(document: PDFBakerDocument) -> None:
# Custom processing logic here
pass
```

## Document Object

The `document` parameter provides access to:

- Document configuration
- Variant processing
- Page rendering
- File management

### Key Methods and Properties

```python
# Access configuration
config = document.config

# Process variants
for variant in document.config.get('variants', []):
# Process variant...

# Process pages
for page in document.config.get('pages', []):
# Process page...

# File management
build_dir = document.build_dir
dist_dir = document.dist_dir
```

## Example: Dynamic Pricing

Here's an example that calculates dynamic pricing based on features:

```python
def process_document(document):
# Load pricing data
with open('content/pricing_data.yaml') as f:
pricing_data = yaml.safe_load(f)

# Calculate pricing for each variant
for variant in document.config.get('variants', []):
base_price = document.config['content']['base_price']
features = len(variant['content']['features'])

# Adjust price based on features
adjusted_price = base_price * (1 + (features - 1) * 0.1)
final_price = adjusted_price * (1 - variant['content']['discount'])

# Update variant content
variant['content']['final_price'] = round(final_price, 2)

# Process as usual
document.process()
```

## Example: Content Generation

Generate content dynamically based on external data:

```python
def process_document(document):
# Fetch data from API
response = requests.get('https://api.example.com/data')
data = response.json()
You will usually just manipulate the data for your templates, and then call `.process()`
on the document to continue with the built-in stages of combining pages and compressing
the PDF as configured.

# Update document content
document.config['content'].update({
'latest_data': data,
'generated_at': datetime.now().isoformat()
})
See `examples/custom_processing/bake.py` for a simple example of how to do this.

# Process as usual
document.process()
```
If you need to fully customise the processing, make sure that your function returns a
Path or list of Path objects (the PDF files that were created) as that is the expected
type of return value for logging.
Loading