Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 10 additions & 5 deletions Examples/Document Configuration/report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ Customizing your document configuration and styling:

Using a combination of configuration levels, you can customize the style of your documents with a minimum amount of configuration text.

- The document template (_doc):
- The document template (_doc):
- >
The template controls settings such as
- - Page size
- Margins
The `_doc` configuration controls the document-wide page size and orientation, along with
one or more named `page templates`. Each page template controls settings such as
- - Margins
- PDF background file path
- And whether or not you have a separate set of the above settings applying to the first page of your document or not.

- >
The first page template listed is the one your document starts on. Within your report you
can switch between page templates using the `_pagebreak` block (pass it the name or index of
the template to use for the following pages) or the `_nextpagetemplate` block (which arms the
template to switch to at the next page break).

- >
Currently, the document template is internally configured to only use a single content frame. This limitation would only affect you if you wanted to do something like a two-column layout (where text flows into the second column on the same page once the first one is full).
Expand Down
11 changes: 10 additions & 1 deletion Examples/PDF Backgrounds/report.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
_doc:
background: background.pdf
templates:
default:
margins:
top: 72.0
left: 72.0
right: 72.0
bottom: 72.0
background:
filepath: background.pdf
relative-to: source
_vars:
field_a: 1645
field_b: 99 Sycamore St, Canada
Expand Down
71 changes: 48 additions & 23 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ above it.

| Key | Category | Controls |
| --- | --- | --- |
| `_doc` | Document template | Page size, orientation, margins, PDF background, first-page overrides. |
| `_doc` | Document template | Page size, orientation, and one or more named page templates (margins + PDF background). |
| `_style` | Text styles | Body and heading fonts, colours, size, line spacing, bullet styling. |
| `_tablestyle` | Table styles | Cell padding, header text, row colours and rule lines. |

Expand All @@ -31,7 +31,7 @@ internal defaults ─► project config ─► document front matter
:::{note}
Currently the document template uses a **single content frame**. This only matters if you
want something like a two-column layout where text flows into a second column on the same
page. Everything else — margins, backgrounds, first-page overrides — is fully configurable.
page. Everything else — page templates, margins, backgrounds — is fully configurable.
:::

## Inline (document) configuration
Expand Down Expand Up @@ -70,34 +70,59 @@ inherits from the internal defaults.
(cfg-doc)=
## Document template — `_doc`

Controls the page itself.
Controls the page itself. Page size and orientation are document-wide; margins and PDF
backgrounds are set per **named page template**. Declare one or more templates under
`templates` — the **first one listed is the template your document starts on**.

```yaml
_doc:
page-size: a4 # a4, letter, etc.
landscape: false
margins:
top: 72.0 # points (72 pt = 1 inch)
left: 72.0
right: 72.0
bottom: 72.0
background: null # path to a PDF to overlay onto — see PDF backgrounds
first-page: # optional: different margins / background for page 1
margins:
top: 72.0
left: 72.0
right: 72.0
bottom: 72.0
background: null
page-size: a4 # a4, letter, etc. (document-wide)
landscape: false # document-wide
templates:
cover: # first listed → the document starts here
margins:
top: 144.0 # points (72 pt = 1 inch)
left: 72.0
right: 72.0
bottom: 72.0
background: # path to a PDF to overlay onto — see PDF backgrounds
filepath: cover.pdf
relative-to: source
body: # switch to this with _pagebreak / _nextpagetemplate
margins:
top: 72.0
left: 72.0
right: 72.0
bottom: 72.0
background: null
```

| Key | Meaning |
| --- | --- |
| `page-size` | Named page size, e.g. `a4`, `letter`. |
| `landscape` | `true` to rotate to landscape. |
| `margins` | Page margins in points (`top`, `left`, `right`, `bottom`). |
| `background` | Path to a PDF whose pages are used as a background. See [PDF backgrounds](pdf-backgrounds.md). |
| `first-page` | Optional block giving the first page its own `margins` and `background`. |
| `page-size` | Named page size, e.g. `a4`, `letter`. Document-wide. |
| `landscape` | `true` to rotate to landscape. Document-wide. |
| `templates` | Mapping of template name → page template. The first entry is the starting template. |
| `templates.<name>.margins` | Page margins in points (`top`, `left`, `right`, `bottom`). |
| `templates.<name>.background` | Path to a PDF whose pages are used as a background. See [PDF backgrounds](pdf-backgrounds.md). |

### Switching page templates

Within your report content, switch templates with two blocks:

- `_pagebreak: <name-or-index>` — insert a page break and use the named (or 0-indexed)
template for the following pages. Called with no value (`_pagebreak:`), it just breaks the
page and keeps the current template.
- `_nextpagetemplate: <name-or-index>` — arm the template to switch to at the **next** page
break, without inserting a break itself.

```yaml
Report:
- The cover page uses the "cover" template.
- _pagebreak: body # break, and use "body" from here on
- Now on the body template.
- _nextpagetemplate: 0 # arm a switch back to the first template (by index)
- _pagebreak: # the break applies the armed template
```

(cfg-style)=
## Text styles — `_style`
Expand Down
28 changes: 20 additions & 8 deletions docs/guide/pdf-backgrounds.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,32 @@ illusion of a sophisticated page-layout workflow while you keep writing plain YA

## Using a background

Point `_doc.background` at a PDF file:
Give a [page template](#cfg-doc) a `background` pointing at a PDF file:

```yaml
_doc:
background: background.pdf
templates:
default:
margins: {top: 72.0, left: 72.0, right: 72.0, bottom: 72.0}
background:
filepath: background.pdf
relative-to: source

Custom styling with PDF backgrounds:
- >
Your content is overlaid on top of the existing background content. The background
can carry your letterhead, borders, watermarks, and form fields.
```

The path is resolved relative to the report file. Each page of your document is composited
over the corresponding page of the background.
`relative-to` may be `source` (resolve the path against the report file) or `config`
(against the project config directory). Each page rendered with a template is composited
over its background; when a background PDF has multiple pages, they are matched by page
number.

:::{tip}
Give the first page its own background using `_doc.first-page.background` — useful for a
title page or a cover sheet that differs from the body pages. See
[Configuration → Document template](#cfg-doc).
Give a cover or title page its own background by defining a **separate page template** with
its own `background` and switching to the body template with `_pagebreak` — useful when the
first page differs from the rest. See [Configuration → Document template](#cfg-doc).
:::

## Auto-populated form fields
Expand All @@ -37,7 +44,12 @@ the values look like normal, static PDF content.

```yaml
_doc:
background: background.pdf
templates:
default:
margins: {top: 72.0, left: 72.0, right: 72.0, bottom: 72.0}
background:
filepath: background.pdf
relative-to: source
_vars:
field_a: 1645
field_b: 99 Sycamore St, Canada
Expand Down
27 changes: 24 additions & 3 deletions docs/reference/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ The suffix does not affect how the block is executed. It is simply an optional i
| [`_code`](#block-code) | A non-executable code block. |
| [`_py`](#block-py) | Execute Python and optionally show the syntax-highlighted source. |
| [`_loadjson`](#block-loadjson) | Load variables into the document from a JSON file. |
| [`_pagebreak`](#block-pagebreak) | Force a page break. |
| [`_pagebreak`](#block-pagebreak) | Force a page break, optionally switching page template. |
| [`_nextpagetemplate`](#block-nextpagetemplate) | Arm the page template to switch to at the next break. |
| [`_hrule`](#block-hrule) | A customizable horizontal rule. |
| [`_spacer`](#block-spacer) | Insert vertical whitespace. |

Expand Down Expand Up @@ -251,15 +252,35 @@ _loadjson:
(block-pagebreak)=
## `_pagebreak` — Page breaks

Force a page break. Takes `null` as its value (an empty block).
Force a page break. Called with no value, it just breaks the page and keeps the current
[page template](configuration.md#cfg-doc). Pass the **name or 0-based index** of a page
template to switch to it for the pages that follow.

```yaml
Report:
- >
This content ends the page.
- _pagebreak:
- _pagebreak: # break, keep the current template
- >
This content starts a new page.
- _pagebreak: body # break AND switch to the "body" template
- _pagebreak: 1 # equivalently, by index
```

---

(block-nextpagetemplate)=
## `_nextpagetemplate` — Switch template at the next break

Arm the [page template](configuration.md#cfg-doc) to switch to at the **next** page break,
without inserting a break itself. Useful when the break is produced elsewhere (for example,
by content overflowing the page). Takes the template **name or 0-based index**.

```yaml
Report:
- _nextpagetemplate: body # the next page break will switch to "body"
- >
Long content that flows onto a second page, which will use the "body" template.
```

---
Expand Down
22 changes: 22 additions & 0 deletions src/ymprint/blocks/nextpagetemplate_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Any

from reportlab.platypus import NextPageTemplate

from . import register_block
from ..exceptions import YMPrintSyntaxException


def convert_next_page_template(block_key: str, block_value: Any, context: dict) -> list:
"""
Sets the page template to switch to at the next page break, without inserting
a break itself. The value is the name or 0-based index of the page template.
"""
doctemplate = context['doctemplate']['ymprint']
try:
template_id = doctemplate.resolve_template_id(block_value)
except ValueError as exc:
raise YMPrintSyntaxException(str(exc)) from exc
return [NextPageTemplate(template_id)]


register_block("_nextpagetemplate", convert_next_page_template)
24 changes: 19 additions & 5 deletions src/ymprint/blocks/page_break_block.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
from reportlab.platypus import PageBreak
from . import register_block
from typing import Any

from reportlab.platypus import PageBreak, NextPageTemplate

from . import register_block
from ..exceptions import YMPrintSyntaxException


def convert_page_break(block_key: str, block_value: Any, context: dict) -> list[PageBreak]:
return [PageBreak()]
def convert_page_break(block_key: str, block_value: Any, context: dict) -> list:
"""
Inserts a page break. If a value is supplied, it is the name or 0-based index
of the page template to use for the pages that follow the break.
"""
if block_value is None or block_value == "":
return [PageBreak()]
doctemplate = context['doctemplate']['ymprint']
try:
template_id = doctemplate.resolve_template_id(block_value)
except ValueError as exc:
raise YMPrintSyntaxException(str(exc)) from exc
return [NextPageTemplate(template_id), PageBreak()]


register_block("_pagebreak", convert_page_break)
register_block("_pagebreak", convert_page_break)
12 changes: 12 additions & 0 deletions src/ymprint/config/config_loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ def build_current_config(default_config: dict, config_data: DeepChainMap):
"""
style_map = {}
for key in default_config:
# 'templates' is a user-extensible mapping whose keys are arbitrary
# template names, so it must not be merged key-by-key against the
# defaults (which would drop any user-named templates and inject the
# default one). Instead, take the whole mapping from the highest-priority
# layer that defines a non-empty 'templates'.
if key == 'templates':
for mapping in config_data.maps:
templates = mapping.get('templates')
if templates:
style_map['templates'] = templates
break
continue
value = config_data[key]
if isinstance(value, DeepChainMap):
default_value = default_config[key]
Expand Down
21 changes: 8 additions & 13 deletions src/ymprint/config/defaults/defaults.ymprint.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
_doc:
page-size: a4
landscape: false
margins:
top: 72.0
left: 72.0
right: 72.0
bottom: 72.0
background: null
first-page:
margins:
top: 72.0
left: 72.0
right: 72.0
bottom: 72.0
background: null
templates:
default:
margins:
top: 72.0
left: 72.0
right: 72.0
bottom: 72.0
background: null

_style:
headings:
Expand Down
Loading
Loading