Skip to content
Merged
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
Binary file modified Examples/Document Configuration/report.pdf
Binary file not shown.
15 changes: 6 additions & 9 deletions Examples/Document Configuration/report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@ Customizing your document configuration and styling:

- The document template (_doc):
- >
<<<<<<< HEAD
The template controls settings such as
- _ul:
- Page size
- 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 `_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
Expand All @@ -46,7 +38,6 @@ Customizing your document configuration and styling:
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).
>>>>>>> adb6527 (feat: implement multiple named page templates)

- >
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 All @@ -72,6 +63,12 @@ Customizing your document configuration and styling:
- Font family
- Text color
- Typographic ratio (in musical intervals, e.g. "minor second" or "major third")
- Alignment (left, center, right, justify) and underline

- >
Beyond the default `body` and `headings`, you can define named `styles` — alternate text
families you switch between within the document using the `_textstyle` block. Each named
style is a sparse override that inherits everything you don't change from the defaults.
- Table styles (_tablestyle):
- >
You can control how your tables are displayed by modifying the table style. With the _tablestyle key, you can control
Expand Down
Binary file modified docs/_static/examples/document-configuration-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/_static/examples/document-configuration-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/_static/examples/text-styles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ block in the document front matter. See [Configuration](guide/configuration.md).

---

## Scoped text styles

Define named text styles under `_style.styles`, then switch the active family part-way through
the document with the `_textstyle` block. Here a `fine-print` style (smaller, grey) and a
`callout` style (larger, blue, centred) are switched in and out — each swap restyles the body,
headings, and lists that follow it, and reverts when the section ends. See
[Configuration → Named text styles](#style-named).

```{literalinclude} ../Examples/Text Styles/report.yml
:language: yaml
```

::::{container} ym-single-shot
```{image} _static/examples/text-styles.png
:alt: Scoped text styles — default, fine-print, and callout families switched within one document
:class: ym-page-shot
```
::::

---

## Document variables

Define `_vars`, render them into text with Jinja (`{{ "{{var}}" }}`), and reach into nested
Expand Down
92 changes: 91 additions & 1 deletion docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,23 @@ Report:
(cfg-style)=
## Text styles — `_style`

Controls body and heading typography.
Controls body and heading typography. Alongside the default `body` and `headings` families,
`_style` can define **named text styles** under `styles` — alternate families you switch
between within the document with the [`_textstyle`](#style-switching) block.

```yaml
_style:
headings:
font: Helvetica
color: "#222222"
ratio: Major Third # typographic scale as a musical interval
underline: false # underline every heading
body:
font: Helvetica
color: black
size: 10 # points
spacing: 1.7 # line spacing ratio
align: left # left | center | right | justify
bullets:
font: Helvetica
size: 10
Expand All @@ -148,6 +152,17 @@ _style:
spacing: 10
indent-bullet: 20
indent-text: 40
styles: # named styles (see "Named text styles" below)
fine-print:
body:
size: 8
color: "#666666"
callout:
body:
size: 12
align: center
headings:
ratio: major third
```

### Headings
Expand All @@ -157,6 +172,8 @@ _style:
| `font` | Heading font family (see [Fonts](../reference/fonts.md)). |
| `color` | Heading colour, hex or name. |
| `ratio` | Typographic scale expressed as a **musical interval** — e.g. `minor third`, `major second`, `Major Third`. Larger intervals produce a bigger jump between heading levels. |
| `align` | Text alignment — `left`, `center`, `right`, or `justify` (default `left`). |
| `underline` | `true` to underline every heading (default `false`). |

### Body

Expand All @@ -166,8 +183,81 @@ _style:
| `color` | Body text colour. |
| `size` | Body font size in points. |
| `spacing` | Line-spacing ratio. |
| `align` | Text alignment — `left`, `center`, `right`, or `justify` (default `left`). |
| `underline` | `true` to underline body paragraphs (default `false`). |
| `bullets` | Bullet styling — glyph hierarchy (`symbols`), colour, size, and indentation of the bullet (`indent-bullet`) and its text (`indent-text`). |

:::{note}
`align` and `underline` are available on `body`, `headings`, and every named style. Both are
inherited by named styles like any other field.
:::

(style-named)=
### Named text styles

Under `styles`, declare any number of **named families**. Each name maps to a **sparse
override** of the default family — you specify only what differs, and every unspecified field
(fonts, colours, bullets, alignment …) is inherited from `body`/`headings`. This mirrors the
inherit-from-defaults behaviour of the config system itself.

```yaml
_style:
body: { font: NotoSans, size: 10, color: black, spacing: 1.7 }
headings: { font: AppleGaramond, ratio: minor third, color: "#dd9922" }
styles:
fine-print: # a smaller, grey family
body:
size: 8
color: "#666666"
callout: # a larger, centred family with bigger headings
body:
size: 12
align: center
headings:
ratio: major third
```

Switching to a named style swaps the **whole family** — body paragraphs, bullet lists, and
the derived `h1`…`h6` headings all follow the active style. Because heading sizes are derived
from `body.size` (`body.size × ratio`), a named style with a smaller `body.size` also shrinks
its headings.

The special name **`default`** is always available and refers to the top-level `body`/
`headings`. Switching to an undeclared name raises an error.

(style-switching)=
### Switching text styles

Within your report content, change the active family with the `_textstyle` block:

- `_textstyle: <name>` — activate the named style (or `default`) **from that point onward
within its section**, and for every nested descendant. It renders nothing of its own.

`_textstyle` is **scoped to the list it appears in**: it restyles every following sibling in
that frame plus their descendants, and reverts automatically when the section (list) ends.
There is no need to switch back manually unless you want to change style *within* the same
section. (This differs from page templates, which persist across sections until you switch
them again.)

```yaml
Legal disclaimer:
- _textstyle: fine-print # applies from here down in THIS section
- This paragraph is fine-print.
- Sub-clause:
- Inherited fine-print (a nested descendant).
- _textstyle: default # switch back within the same section
- Back to the default style.

Next section:
- Body style again — the disclaimer's scope ended with its list.
```

:::{note}
Named styles apply to **paragraphs, headings, and bullet/numbered lists**. Other blocks
(admonitions, quotes, code, images, tables) keep their own styling regardless of the active
text style.
:::

(cfg-tablestyle)=
## Table styles — `_tablestyle`

Expand Down
39 changes: 33 additions & 6 deletions docs/reference/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,11 @@ 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. |
<<<<<<< HEAD
| [`_ul`](#block-ul) | An unordered (bulleted) list. |
| [`_ol`](#block-ol) | An ordered (numbered) list. |
| [`_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. |
>>>>>>> adb6527 (feat: implement multiple named page templates)
| [`_textstyle`](#block-textstyle) | Switch the active named text style for the rest of the section. |
| [`_hrule`](#block-hrule) | A customizable horizontal rule. |
| [`_spacer`](#block-spacer) | Insert vertical whitespace. |

Expand Down Expand Up @@ -298,7 +295,7 @@ Recommended actions:
## `_pagebreak` — Page breaks

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
[page template](#cfg-doc). Pass the **name or 0-based index** of a page
template to switch to it for the pages that follow.

```yaml
Expand All @@ -317,7 +314,7 @@ Report:
(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,
Arm the [page template](#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**.

Expand All @@ -330,6 +327,36 @@ Report:

---

(block-textstyle)=
## `_textstyle` — Switch text style

Activate a [named text style](#style-named) for the rest of the section it
appears in, and for every nested descendant. Takes a style **name** (declared under
`_style.styles`) or the special name `default`. It renders nothing of its own.

The switch is **scoped to its list**: it restyles every following sibling plus their
descendants, then reverts automatically when the section ends — no manual switch-back is
needed unless you want to change style again within the same section. Switching swaps the
whole family, so body paragraphs, bullet lists, and derived headings all follow the active
style. An undeclared style name raises an error.

```yaml
Legal disclaimer:
- _textstyle: fine-print # applies from here down in THIS section
- This paragraph is fine-print.
- Sub-clause:
- Inherited fine-print (a nested descendant).
- _textstyle: default # switch back within the same section
- Back to the default style.

Next section:
- Body style again — the disclaimer's scope ended with its list.
```

See [Switching text styles](#style-switching) for the full behaviour.

---

(block-hrule)=
## `_hrule` — Horizontal rules

Expand Down
4 changes: 3 additions & 1 deletion src/ymprint/config/config_loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def load_report_config(source_data: Optional[dict] = None, report_config_path: O
raise FileNotFoundError(f"The supplied path for the report_config_path does not exist. Here is what you passed:\n\n{str(report_config_path.resolve())}")

if report_config_path is not None and report_config_path.is_file():
config_data = load_yaml(report_config_path)
# load_yaml returns None when the file exists but is empty; treat that as
# "no config here" rather than crashing on the .get() lookups below.
config_data = load_yaml(report_config_path) or {}
config_styles = config_data.get("_style", {})
config_tablestyles = config_data.get("_tablestyle", {})
config_doctemplate = config_data.get("_doc", {})
Expand Down
17 changes: 17 additions & 0 deletions tests/test_loadconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ def test_load_report_config():
# assert doc['_doc']['first-page']['cat'] == 'here'


def test_load_report_config_empty_file(tmp_path):
"""An empty config file must be treated as 'no config', not crash.

load_yaml returns None for an empty file; load_report_config should coalesce
that to an empty mapping and fall back to the internal defaults.
"""
empty_config = tmp_path / "doc.ymprint.yml"
empty_config.write_text("")

styles, tablestyles, doc = load_report_config(None, empty_config)

# Falls back to internal defaults rather than raising AttributeError.
assert doc['_doc']['page-size'] == 'a4'
assert styles['_style']['body']['size'] == 10
assert tablestyles['_tablestyle']['headers']['text']['bold'] is True


def test_load_doc_config():
source_data = {
"_style": {
Expand Down
Loading