From adb6527bdd5c1c20ac17b432b64f0df209b54f6a Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Thu, 13 Aug 2026 20:41:37 +0000 Subject: [PATCH 1/2] feat: implement multiple named page templates Replace the first-page/remaining-pages document model with named page templates. `_doc.templates` is a mapping of name -> {margins, background}; the first entry is the starting template. Page size and orientation remain document-wide. Add source-level template switching: - `_pagebreak` accepts an optional template name/index for following pages - `_nextpagetemplate` arms the template to switch to at the next break Template flow is managed by ReportLab's NextPageTemplate. Each PageTemplate records a page-index -> template-name map during the build so post-processing can overlay the correct background per page. Config merge treats `templates` as a whole mapping from the highest-priority layer. Migrate defaults, test configs, examples, and docs to the new schema; add tests covering template resolution, both blocks, the build-time page map, and the config merge. Co-Authored-By: Claude Opus 4.8 --- Examples/Document Configuration/report.yml | 15 +- Examples/PDF Backgrounds/report.yml | 11 +- docs/guide/configuration.md | 71 ++++--- docs/guide/pdf-backgrounds.md | 28 ++- docs/reference/blocks.md | 27 ++- src/ymprint/blocks/nextpagetemplate_block.py | 22 +++ src/ymprint/blocks/page_break_block.py | 24 ++- src/ymprint/config/config_loaders.py | 12 ++ .../config/defaults/defaults.ymprint.yml | 21 +- src/ymprint/config/doctemplate.py | 165 ++++++++-------- src/ymprint/config/pdf_postprocessing.py | 180 +++++++----------- src/ymprint/context_builder.py | 28 +-- src/ymprint/report_reader.py | 12 +- .../example_1_config/config.ymprint.yml | 12 +- .../example_2_config/config.ymprint.yml | 36 ++-- tests/test-data/example_output1.pdf | Bin 57798 -> 57800 bytes tests/test-data/example_output2.pdf | Bin 78401 -> 118694 bytes tests/test-data/example_output3.pdf | Bin 38520 -> 38521 bytes tests/test-data/filled_forms.pdf | Bin 45804 -> 45804 bytes tests/test-data/report_example_2.yml | 8 +- tests/test_multipage_templates.py | 137 +++++++++++++ 21 files changed, 516 insertions(+), 293 deletions(-) create mode 100644 src/ymprint/blocks/nextpagetemplate_block.py create mode 100644 tests/test_multipage_templates.py diff --git a/Examples/Document Configuration/report.yml b/Examples/Document Configuration/report.yml index 016bf59..0cadb24 100644 --- a/Examples/Document Configuration/report.yml +++ b/Examples/Document Configuration/report.yml @@ -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). diff --git a/Examples/PDF Backgrounds/report.yml b/Examples/PDF Backgrounds/report.yml index e1293e6..b71c507 100644 --- a/Examples/PDF Backgrounds/report.yml +++ b/Examples/PDF Backgrounds/report.yml @@ -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 diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 832b07a..2cc117e 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -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. | @@ -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 @@ -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..margins` | Page margins in points (`top`, `left`, `right`, `bottom`). | +| `templates..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: ` — 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: ` — 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` diff --git a/docs/guide/pdf-backgrounds.md b/docs/guide/pdf-backgrounds.md index 65f1639..c5e2c33 100644 --- a/docs/guide/pdf-backgrounds.md +++ b/docs/guide/pdf-backgrounds.md @@ -7,11 +7,16 @@ 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: - > @@ -19,13 +24,15 @@ Custom styling with PDF backgrounds: 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 @@ -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 diff --git a/docs/reference/blocks.md b/docs/reference/blocks.md index 6c67f44..0c50800 100644 --- a/docs/reference/blocks.md +++ b/docs/reference/blocks.md @@ -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. | @@ -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. ``` --- diff --git a/src/ymprint/blocks/nextpagetemplate_block.py b/src/ymprint/blocks/nextpagetemplate_block.py new file mode 100644 index 0000000..4512a9e --- /dev/null +++ b/src/ymprint/blocks/nextpagetemplate_block.py @@ -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) diff --git a/src/ymprint/blocks/page_break_block.py b/src/ymprint/blocks/page_break_block.py index 4f27e65..8f6e09e 100644 --- a/src/ymprint/blocks/page_break_block.py +++ b/src/ymprint/blocks/page_break_block.py @@ -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) \ No newline at end of file +register_block("_pagebreak", convert_page_break) diff --git a/src/ymprint/config/config_loaders.py b/src/ymprint/config/config_loaders.py index 70ce385..39198b0 100644 --- a/src/ymprint/config/config_loaders.py +++ b/src/ymprint/config/config_loaders.py @@ -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] diff --git a/src/ymprint/config/defaults/defaults.ymprint.yml b/src/ymprint/config/defaults/defaults.ymprint.yml index 546e9e8..b7b4f39 100644 --- a/src/ymprint/config/defaults/defaults.ymprint.yml +++ b/src/ymprint/config/defaults/defaults.ymprint.yml @@ -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: diff --git a/src/ymprint/config/doctemplate.py b/src/ymprint/config/doctemplate.py index fb27b41..3022a23 100644 --- a/src/ymprint/config/doctemplate.py +++ b/src/ymprint/config/doctemplate.py @@ -1,7 +1,7 @@ from enum import StrEnum import pathlib from typing import Optional -from pydantic import BaseModel, Field, ConfigDict +from pydantic import BaseModel, Field from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate import reportlab.lib.pagesizes as rl_pagesizes from .helpers import get_pagesize @@ -21,8 +21,8 @@ class PDFBackground(BaseModel): filepath: str relative_to: Optional[RelativeTo] = Field(alias='relative-to', default=None) -class PageConfig(BaseModel): - # model_config = ConfigDict(populate_by_name=True) +class TemplateConfig(BaseModel): + """A single named page template: its content margins and optional PDF background.""" margins: Margins background: Optional[PDFBackground] = None @@ -32,12 +32,9 @@ class PageSizeMixin: class LandscapeMixin: landscape: bool = Field(default = False) -class FirstPageMixin: - first_page: Optional[PageConfig] = Field(default = None, alias='first-page') - -class DocConfig(PageConfig, PageSizeMixin, LandscapeMixin, FirstPageMixin): - pass +class DocConfig(PageSizeMixin, LandscapeMixin, BaseModel): + templates: dict[str, TemplateConfig] @property def page_dims(self): @@ -48,97 +45,105 @@ def page_dims(self): else: final_page_dims = page_dims return final_page_dims - # page_dims = getattr(rl_pagesizes, self.page_size.upper()) else: raise ValueError(f"Page size of {self.page_size.upper()} not found. Page sizes available: {[attr for attr in dir(rl_pagesizes) if attr.isupper()]}") - def available_width(self, first_or_all: str): - if first_or_all == "first": - if getattr(self, 'first_page', None) is not None: - return self.page_dims[0] - self.first_page.margins.left - self.first_page.margins.right - else: - return self.page_dims[0] - self.margins.left - self.margins.right - else: - return self.page_dims[0] - self.margins.left - self.margins.right - - def available_height(self, first_or_all: str): - if first_or_all == "first": - if getattr(self, 'first_page', None) is not None: - return self.page_dims[1] - self.first_page.margins.top - self.first_page.margins.bottom - else: - return self.page_dims[1] - self.margins.top - self.margins.bottom - else: - return self.page_dims[1] - self.margins.top - self.margins.bottom - - def page_anchor(self, first_or_all: str): - if first_or_all == 'first': - if getattr(self, 'first_page', None) is not None: - return [self.first_page.margins.left, self.first_page.margins.bottom] - else: - return [] - else: - return [self.margins.left, self.margins.bottom] + @property + def template_names(self) -> list[str]: + """Template names in declaration order. The first is the starting template.""" + return list(self.templates.keys()) + + def resolve_template_id(self, name_or_index: str | int) -> str: + """ + Returns the template name (used as the ReportLab PageTemplate id) for a + user-supplied template reference, which may be a name or a 0-based index. + """ + names = self.template_names + # bool is an int subclass; reject it explicitly to avoid True/False -> index + if isinstance(name_or_index, bool): + raise ValueError(f"Invalid page template reference: {name_or_index!r}") + if isinstance(name_or_index, int): + try: + return names[name_or_index] + except IndexError: + raise ValueError( + f"Page template index {name_or_index} is out of range. " + f"Available templates (by index): {list(enumerate(names))}" + ) + if isinstance(name_or_index, str): + if name_or_index in self.templates: + return name_or_index + raise ValueError( + f"Page template {name_or_index!r} not found. Available templates: {names}" + ) + raise ValueError(f"Invalid page template reference: {name_or_index!r}") + + def available_width(self, template_name: str) -> float: + template = self.templates[template_name] + return self.page_dims[0] - template.margins.left - template.margins.right + + def available_height(self, template_name: str) -> float: + template = self.templates[template_name] + return self.page_dims[1] - template.margins.top - template.margins.bottom + + def page_anchor(self, template_name: str) -> list[float]: + template = self.templates[template_name] + return [template.margins.left, template.margins.bottom] + + def min_available_width(self) -> float: + """Smallest content width across all templates (safe for sizing flowables).""" + return min(self.available_width(name) for name in self.template_names) + + def min_available_height(self) -> float: + """Smallest content height across all templates (safe for sizing flowables).""" + return min(self.available_height(name) for name in self.template_names) - - def build(self, destination: str | pathlib.Path, title: str = "", author: str = ""): """ - Returns a rl object + Returns a tuple of (BaseDocTemplate, page_template_map). + + 'page_template_map' is an initially-empty dict that is populated during + the ReportLab build with {page_index (0-based): template_name}. Each + PageTemplate records the template used to render each page so that the + correct background can be overlaid in post-processing. """ page_width, page_height = self.page_dims - available_width = page_width - self.margins.left - self.margins.right - available_height = page_height - self.margins.top - self.margins.bottom - main_frame = Frame( - x1=self.margins.left, - y1=self.margins.bottom, - width = available_width, - height = available_height, - id='main', - leftPadding=0, - rightPadding=0, - topPadding=0, - bottomPadding=0 - ) - main_page_template = PageTemplate( - id='report', - pagesize=self.page_dims, - frames=[main_frame], - ) - page_templates = [main_page_template] - first_page_template = None - first_page_frame = None - if self.first_page is not None: - first_available_width = page_width - self.first_page.margins.left - self.first_page.margins.right - first_available_height = page_height - self.first_page.margins.top - self.first_page.margins.bottom - first_page_frame = Frame( - x1=self.first_page.margins.left, - y1=self.first_page.margins.bottom, - width=first_available_width, - height=first_available_height, - id='first', + page_template_map: dict[int, str] = {} + + def make_on_page(template_id: str): + def _on_page(canvas, doc): + page_template_map[canvas.getPageNumber() - 1] = template_id + return _on_page + + page_templates = [] + for name, template in self.templates.items(): + frame = Frame( + x1=template.margins.left, + y1=template.margins.bottom, + width=page_width - template.margins.left - template.margins.right, + height=page_height - template.margins.top - template.margins.bottom, + id=f'{name}_frame', leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, ) - first_page_template = PageTemplate( - id='report_first_page', - pagesize=(page_width, page_height), - frames=[first_page_frame], + page_templates.append( + PageTemplate( + id=name, + pagesize=self.page_dims, + frames=[frame], + onPage=make_on_page(name), + ) ) - page_templates.insert(0, first_page_template) + doc = BaseDocTemplate( str(destination), - pagesize = self.page_dims, + pagesize=self.page_dims, pageTemplates=page_templates, title=title, author=author, allowSplitting=1 ) - return doc - - - -# # CRITICAL: This compiles the forward reference ("Doc") now that the class is defined. -# DocConfig.model_rebuild() \ No newline at end of file + return doc, page_template_map diff --git a/src/ymprint/config/pdf_postprocessing.py b/src/ymprint/config/pdf_postprocessing.py index 94137bb..65dadc9 100644 --- a/src/ymprint/config/pdf_postprocessing.py +++ b/src/ymprint/config/pdf_postprocessing.py @@ -1,6 +1,5 @@ import io import pathlib -from typing import Optional import pymupdf as mu @@ -9,155 +8,122 @@ def overlay_pdf_background( pdf_background_streams: dict[str, io.BytesIO | None], destination_path: pathlib.Path | io.BytesIO, context: dict, + page_template_map: dict[int, str], ): """ - Overlays the pages from document_path onto the pages from - pdf_background_path and saves the new file to destination_path. - If first_page_background_path is provided, then that background - will be used for the first page and all other pages will be overlayed - onto pdf_background_path. - - If pdf_background_path is a PDF file with multiple pages, each page - in 'document_path' will be overlayed onto each page of 'pdf_background_path' - in sequence. If 'document_path' has more pages than 'pdf_background_path', - then the remaining pages of 'document_path' will not have a pdf background - applied. If 'pdf_background_path' has more pages than 'document_path', then - the final document will have the same number of pages as 'document_path' and - the remaining un-overlayed pages from 'pdf_background_path' will remain - unused in the final document. + Overlays the rendered document pages onto their page template backgrounds and + saves the result to 'destination_path'. + + 'pdf_background_streams' maps a page template name to its (form-filled) PDF + background stream, or None when that template has no background. + + 'page_template_map' maps a 0-based page index to the name of the page template + that rendered it, so each page is overlaid onto the correct template background. + + If a template's background PDF has multiple pages, each document page rendered + with that template is overlaid onto the background page at the same absolute + document index; document pages beyond the background's page count receive no + background. """ - first_data = pdf_background_streams['first'] - remaining_data = pdf_background_streams['remaining'] page_dims = context['doctemplate']['ymprint'].page_dims page_width, page_height = page_dims - first_bg = None - if first_data is not None: - first_bg = mu.open(stream=pdf_background_streams['first']) - remaining_bg = None - if remaining_data is not None: - remaining_bg = mu.open(stream=pdf_background_streams['remaining']) + + background_docs = { + name: (mu.open(stream=data) if data is not None else None) + for name, data in pdf_background_streams.items() + } + document = mu.open(stream=document_path) output = mu.open() for i in range(document.page_count): document_page = document.load_page(i) document_page.wrap_contents() out_page = output.new_page(width=page_width, height=page_height) - if i == 0 and first_bg is not None: - first_bg_page = first_bg.load_page(0) - first_bg_page.wrap_contents() - out_page.show_pdf_page(first_bg_page.rect, first_bg, pno=0) - out_page.show_pdf_page(document_page.rect, document, pno=i) - continue - if remaining_bg is not None: - if remaining_bg.page_count == 1: - background_page = remaining_bg.load_page(0) - background_page.wrap_contents() + + template_name = page_template_map.get(i) + background = background_docs.get(template_name) + if background is not None: + if background.page_count == 1: background_page_num = 0 + elif i < background.page_count: + background_page_num = i else: - try: - background_page = remaining_bg.load_page(i) - background_page.wrap_contents() - background_page_num = i - except IndexError: - background_page = None - out_page.show_pdf_page(background_page.rect, remaining_bg, pno=background_page_num) + background_page_num = None + if background_page_num is not None: + background_page = background.load_page(background_page_num) + background_page.wrap_contents() + out_page.show_pdf_page(background_page.rect, background, pno=background_page_num) + out_page.show_pdf_page(document_page.rect, document, pno=i) output.save(destination_path) - - -def fill_forms_and_bake(vars: dict, pdf_backgrounds: dict[str, io.BytesIO | None]) -> dict[str, io.BytesIO]: - first_data = pdf_backgrounds['first'] - remaining_data = pdf_backgrounds['remaining'] - first_bg = remaining_bg = None - if first_data is not None: - first_bg = mu.open(stream=first_data) - if remaining_data is not None: - remaining_bg = mu.open(stream=remaining_data) - - if first_bg is None and remaining_bg is None: - return pdf_backgrounds - docs = [first_bg, remaining_bg] - out_docs = {} - - indexes = ['first', 'remaining'] - for idx, doc in enumerate(docs): - if doc is None: - out_docs[indexes[idx]] = None + +def fill_forms_and_bake(vars: dict, pdf_backgrounds: dict[str, io.BytesIO | None]) -> dict[str, io.BytesIO | None]: + """ + For each background stream, populates any PDF form fields whose name matches a + document variable, then flattens ("bakes") the fields into the page content. + Returns a dict with the same keys, mapping to the baked stream (or None). + """ + out_docs: dict[str, io.BytesIO | None] = {} + for name, data in pdf_backgrounds.items(): + if data is None: + out_docs[name] = None continue + doc = mu.open(stream=data) for page in doc.pages(): widget = page.first_widget while widget is not None: - name = widget.field_name - widget_value = vars.get(name, None) - # THIS IS TRICKY + field_name = widget.field_name + widget_value = vars.get(field_name, None) if widget_value is not None: widget.field_value = str(widget_value) widget.update() widget = widget.next - doc.bake() doc_data = io.BytesIO() doc.save(filename=doc_data) doc_data.seek(0) - out_docs[indexes[idx]] = doc_data + out_docs[name] = doc_data return out_docs def load_pdf_backgrounds(context: dict) -> dict[str, io.BytesIO | None]: + """ + Returns a dict mapping each page template name to its background PDF as a + BytesIO stream (or None when the template has no background). Relative + background paths are resolved against the source or config directory according + to each background's 'relative-to' setting. + """ source_path = pathlib.Path(context['source_path']) source_parent = source_path.parent - print(f"{source_parent=}") if context['config_path'] is not None: - config_path = pathlib.Path(context['config_path']) - config_parent = config_path.parent + config_parent = pathlib.Path(context['config_path']).parent else: - config_path = source_path config_parent = source_parent - first_page_bg = context['doctemplate']['yaml']['_doc'].get('first-page', {}).get('background') - if isinstance(first_page_bg, dict): - print(f"{first_page.get('background', {})=}") - first_page_background_filepath = first_page_bg.get('filepath') - relative_to = first_page_bg.get('relative_to') - if relative_to == 'source': - first_page_background = source_parent / first_page_background_filepath - elif relative_to == 'config': - first_page_background = config_parent / first_page_background_filepath - else: - first_page_background = first_page_background_filepath - else: - first_page_background = None - - remaining = context['doctemplate']['yaml']['_doc'].get('background', {}) - first_page_pdf = remaining_pdf = None - first_page_data = remaining_page_data = None - if first_page_background is not None: - first_page_pdf = mu.open(first_page_background) - first_page_data = io.BytesIO() - first_page_pdf.save(first_page_data) - first_page_data.seek(0) - if remaining is not None: - relative_to = remaining.get('relative-to') - print(f"{relative_to=}") + doctemplate = context['doctemplate']['ymprint'] + backgrounds: dict[str, io.BytesIO | None] = {} + for name, template in doctemplate.templates.items(): + background = template.background + if background is None: + backgrounds[name] = None + continue + + relative_to = background.relative_to if relative_to == 'source': - remaining_page_background = source_parent / remaining.get('filepath') + background_path = source_parent / background.filepath elif relative_to == 'config': - remaining_page_background = config_parent / remaining.get('filepath') + background_path = config_parent / background.filepath else: - remaining_page_background = remaining.get('filepath') + background_path = background.filepath - print(f"{remaining_page_background=}") - remaining_pdf = mu.open(remaining_page_background) - remaining_page_data = io.BytesIO() - remaining_pdf.save(remaining_page_data) - remaining_page_data.seek(0) + pdf = mu.open(background_path) + data = io.BytesIO() + pdf.save(data) + data.seek(0) + backgrounds[name] = data - backgrounds = { - "first": first_page_data, - "remaining": remaining_page_data - } - return backgrounds \ No newline at end of file + return backgrounds diff --git a/src/ymprint/context_builder.py b/src/ymprint/context_builder.py index 1c459f7..7a224e7 100644 --- a/src/ymprint/context_builder.py +++ b/src/ymprint/context_builder.py @@ -20,7 +20,7 @@ def build_context( # This is not an appropriate merge. Need the nested chain map. combined_doctemplate = doctemplate_yaml# | inline_doctemplate doctemplate = DocConfig.model_validate(combined_doctemplate['_doc']) - rl_basedoctemplate = doctemplate.build(destination_path) + rl_basedoctemplate, _ = doctemplate.build(destination_path) report_tablestyles = TableStyle.model_validate(tablestyles_yaml['_tablestyle']) tablestyles = report_tablestyles.build() context = { @@ -49,21 +49,21 @@ def build_context( "vars": document_vars, "page_dims": doctemplate.page_dims, "frames": { - "first_page": { - "anchor": doctemplate.page_anchor('first'), - "width": doctemplate.available_width('first'), - "height": doctemplate.available_height('first'), - }, - "remaining_pages": { - "anchor": doctemplate.page_anchor('all'), - "width": doctemplate.available_width('all'), - "height": doctemplate.available_height('all'), + **{ + name: { + "anchor": doctemplate.page_anchor(name), + "width": doctemplate.available_width(name), + "height": doctemplate.available_height(name), + } + for name in doctemplate.template_names }, + # Conservative frame used by blocks to size flowables that could land + # on any page: the smallest content box across all templates. "all_pages": { - "anchor": doctemplate.page_anchor('all'), - "width": min(doctemplate.available_width('all'), doctemplate.available_width('first')), - "height": min(doctemplate.available_height('all'), doctemplate.available_height('first')), - } + "anchor": doctemplate.page_anchor(doctemplate.template_names[0]), + "width": doctemplate.min_available_width(), + "height": doctemplate.min_available_height(), + }, }, "source_path": source_path, "config_path": config_path, diff --git a/src/ymprint/report_reader.py b/src/ymprint/report_reader.py index 919a6de..5ba8ebc 100644 --- a/src/ymprint/report_reader.py +++ b/src/ymprint/report_reader.py @@ -5,6 +5,7 @@ from .blocks import admonition_block from .blocks import quote_block from .blocks import page_break_block +from .blocks import nextpagetemplate_block from .blocks import spacer_block from .blocks import hrule_block from .blocks import python_block @@ -19,7 +20,7 @@ from .config.pdf_postprocessing import load_pdf_backgrounds, fill_forms_and_bake, overlay_pdf_background -from reportlab.platypus import Spacer, NextPageTemplate +from reportlab.platypus import Spacer from reportlab.lib.units import mm from .config.font_registry import register_fonts @@ -57,20 +58,19 @@ def load_report(source_yaml: str | pathlib.Path, destination_pdf: str | pathlib. ) print(f"{doc_data=}") story = build_story(source_data, context) - if context['doctemplate']['yaml']['_doc'].get('first-page') is not None: - story = [NextPageTemplate(1)] + story - rl_doc = doctemplate.build(destination_pdf) + rl_doc, page_template_map = doctemplate.build(destination_pdf) rl_report_buffer = BytesIO() rl_doc.build(story, filename=rl_report_buffer) rl_report_buffer.seek(0) - # rl_doc.build(story, filename=str(pathlib.Path(destination_pdf).resolve())) + # page_template_map is now populated with {page_index: template_name} pdf_backgrounds = load_pdf_backgrounds(context) populated_backgrounds = fill_forms_and_bake(context['vars'], pdf_backgrounds) overlay_pdf_background( rl_report_buffer, populated_backgrounds, pathlib.Path(destination_pdf), - context + context, + page_template_map, ) diff --git a/tests/test-data/example_1_config/config.ymprint.yml b/tests/test-data/example_1_config/config.ymprint.yml index 3925ddc..f36dbe0 100644 --- a/tests/test-data/example_1_config/config.ymprint.yml +++ b/tests/test-data/example_1_config/config.ymprint.yml @@ -1,11 +1,13 @@ _doc: page-size: a4 landscape: false - margins: - top: 84 - left: 84 - right: 84 - bottom: 84 + templates: + default: + margins: + top: 84 + left: 84 + right: 84 + bottom: 84 _style: headings: diff --git a/tests/test-data/example_2_config/config.ymprint.yml b/tests/test-data/example_2_config/config.ymprint.yml index d000eaa..bfb84ce 100644 --- a/tests/test-data/example_2_config/config.ymprint.yml +++ b/tests/test-data/example_2_config/config.ymprint.yml @@ -1,23 +1,25 @@ _doc: page-size: letter landscape: true - margins: - top: 84 - left: 84 - right: 84 - bottom: 84 - background: - filepath: background_other_pages.pdf - relative-to: source - first-page: - margins: - top: 240 - left: 84 - right: 84 - bottom: 84 - background: - filepath: background_first_page.pdf - relative-to: source + templates: + first: + margins: + top: 240 + left: 84 + right: 84 + bottom: 84 + background: + filepath: background_first_page.pdf + relative-to: source + body: + margins: + top: 84 + left: 84 + right: 84 + bottom: 84 + background: + filepath: background_other_pages.pdf + relative-to: source _tablestyle: cell-padding: diff --git a/tests/test-data/example_output1.pdf b/tests/test-data/example_output1.pdf index 94418d2fb737f2c33c80340c64852dc31f0d581a..56422055ba5c198195046fb0d665034d8cc9eeea 100644 GIT binary patch delta 2010 zcmZY3dpHvc9|v&Cy~(vkZYyU|Oq<;qn%R(RYi6AclMRi`Wu(}0P30Mlq*Rz3mm=hn zl}<&aD5oJLDVOE;x(uOI?|IK(?{obA`aYlM`+I)B=WSa%+P2pI?2?D){_5Jp&mKYn z_}u|du56j&mgfe)%vWA!w7pj+1iV)oINPWp==S^oj+?&UYFO*Q%VVz37@AE@*jwH2 zum;P_ghChTjDnu`M!`HsePISr^U*rckOhO{6}mn?DI6T1X%|qo;O4Zfz%TS&K)uf# zG`>YVDgZT;6^WqGC}HF_^Lh_$fqbmpIgkoW;jcxuq+b=HnuPJ|Js29Md{_6UbDncf zcnOVE3rTr+)-pp*GO2{Z8(Yc(@Aclj5bCSB25yBEfdJ$JXHKpek*`(e9jz*1)09R` zY2YwKcj>1Qh(bDDg6Q}{6e_Pn?$krgg_gtG1tkNx_q<~}T*c5kk;1-L3)@kJlh?AQ z+qKScQ@-ME>CD$25<%OTT%Y9mV<-dVO#fQt;^m3#t!d`T5ppA`hWT$#TuxSOeN+3I zj38xTVlF1S7PngROJ%T6lJf7#^Je?7EFqA7yh2+|t^siHvk3BByYDAPR$=*I&#ddzcKOjIy=LzVs#G&D-D=tZ`RE?@seG zyX(zOsQK}Or7OWF1~S$_9L>hIs*L;&0_|x4Fvrv1X_9aIqYqZC5iv!Hv4GJsuP}kH zbpv7zMHBKHZwAlb)kA)pt?D~(v zErVCnRIAm(>C>O3d)!seslvH|SH0g{t4R%Ni4PgG#*I2y%q-~K={f6=8|6uf3Z#T1 zJSb(`q>~PIS4Gt2@u?ZY&IX+M61{5I{9{_%CB7%tQ9yfp@U$l@))RVs+mD`ssGkLK zx~6frx?g3CDi}!08v;(9{EjS_>Y<}^hET5Re|jD1F-{8dGrHIy8}>S&{9)Q_;}@C! z&V*Ow2eE%RM5cuoo=F-`Q;;awm{{~Qnl~(6YjSRSK6{*5^ViP4hqdD}K;+@hN8nvL z%@urU`enLn#CYpG#0VMwwIq63@RYhgy;5U;>XX-DsYkM=3(my?e)(+=L$Uo7Aq^cYzAb70f>A!Lq=>Nwlg;btp!J+>|!oOEnW zW9fT8h#?pW(lt5xC~2)?*Kd92jeXa=4}8Mw*o~O^4;|2Bw$BHj&a%PW$`2CT@cZFj z-1-o4!{>wQOZUpo?8vbhf&YMm6Avwo@)|qb`fBYp{b!3#{q{?zo&a2FFSkzNs$IBZFQ`Ku$xDg%toDzZrFvhA>!>V zN~WdB)9pQ>y9aL2RjGBW+vq?Bho54+8Zw$)`h587?1_yRh?jP8QP(F{2cN#IV^`H@ znaU(udlPc3M5&4UBlX-kgZRA;+?^=uQt>%?{pRaD`x=}KF_-W?aIVF+WAQ2AA*Ldn zr(|}TYq>Gp6LEWZu-PYRBhlV(C_%!O@_bVSvEuj?dVC0>|0D0ThX}BT-|s=GbjN+H za$TF_c!-eoNS29(PFz!kG#xx7$t>I654Ysrzsaag?x-#EiaJ z zfKGzc{s~9~9EsUv2y4V{u0w>#p6951J delta 2006 zcmZY7cTkgw5&&?eX#y&UH%bu#UXWsr6uyv<04CIczyS)Pw9t(dfzS*l(!0_$k0x9w zhX^5pL=oi)AqSj*iXuT#2v{fzB;ZjZaK8KN&9O5(v-6vs*`1y3<4OO)ljdv<>_!v` zM>H9O9`7RFq7@mdK9Pp9<5aS3c5(ggD*{d|?>UnzhxYxN(rC*yIUg0+7I}43gv(}i zYCnygPVBJ_5%+aWyc10?h-0J_bm;>Vm-a2qE3n@qebs{yaDvRh`>wLF5A!`kE139e zP7jLJ-62W89%kTY?yvhLkE`y^Rc3+~o-jhg0(6Pk7(3E_DEnB=tKm8;mqr#7E|F@L zhe=H755?_Hcv8vDiDT`ZC-qL7Dc-~M-Hrl;}R30X2e7Mi~v%AW+g=+7c?zbEw2|M%iY&WNq z-0$)X?RtRG$6{X>fA+=jil823{+bc5|swr zRb4YBlpCQsdV4+FkCt_wDXy{h6aPlT*{A7%2)ZgO9qRSbMDVb?Iy{0L z-2Wovxl{;*cIKLVw>^l7v2CiEfu%5YmOVl7gpKlcfj*Na9(tj8S>@LLM5h`*m$z49 z^8{-KU^<8$(D+QA(WO zD&2`be6i(yKs&Z16%+;z6*c_Ym!v0zlW*>?2YqaqHPO9Eb3!)=m9{++GAbu4d~U+%sDmxUp=$YVlvfTB$Gd0R3IuB!ET?kogPs&V#XYiu zNp6OCjx$)LyA3)D-}KQYT#v#+hklD%$?A5~PrH3UHKl9tLXm6k+S zL{=)=Q{cpFbL^HRe?g9J%;cQXte?hNep$o94ZM1KLUSP<1}#qvwRCrYmF!I-$3jP~ z%N}Fe589}C1f7B1_A@!rHxTGXv_BnE?Q?YXayet|^urPAGsNRtUAd{9<1s2rF4^>^ zo>#l!i{E;sH^9WfLDmtY*$Ra{35uiDThf8wB4WEPqr9U|z7%4a6iC6rzNdwBJgrGQ z>o;b%!Clh)wAxJQR^QHcPV`*h_s&9YJFYxfRCYF4s>;%^6TgupJ6nHutm~~$ai!?h z-C;zVE;l*;b&82ruP4*F)MhD{&$K#5U8>Us@A2m1N2mAAOAGm1X*ObWEgUuGMi_=V z^9bbY-@ZjwK6q+6Km@yP60zz3`AUS^Uf$iH^^jXH#mA>C)hbLw zFY0+^c%~G*I2ki_p;7V6P`J!Rfh3Kc17~2mu1Qd$Uc_`bEf%%qU;07gcg&DjqAvV! zwxZ$0bmk*!DoLXzX9iM9G0ufP_yQ@aG|A2z!OxIS2Ay{_*L2rNDE*os@qHT|mp`3W z5+C_o@Li#7X5{WtoKsw5Y_Pf8w{Oeqm%=`iDoZ4$Ad}(QPLSK;P!c;%G7DrO8JV8O z&b3HU{2d)(2M@@4J@C5xO5-WP*pI)k=hiCOA7uxhox5bpbIF)jx%uW5N~4zArF*=^ zwd1+oo`|0ePl2`PYU-KLHZT~S4+Pr&0xdV_^$u{4 zXieOVLm#KB`}E#Y=4<7!{Rd98KltM*uG8AVPc?cCHcz&6Xwc4aeMfxE^%*;Pq>iiA za4~N_DGriItcBUR-! diff --git a/tests/test-data/example_output2.pdf b/tests/test-data/example_output2.pdf index 0af00b74c561f3176985eb9560abfc4c5c10540a..ed2636d7b4e6c86f2ac5cd4f9b448ae6213146cf 100644 GIT binary patch delta 9399 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 delta 1661 zcmZWpO>9(E6wZC6Q~MZN(Aem-fd@EDk->TQ|GjkjZ0B_*Xh=|2pf&=b)Sxg)X^dgP zbcH}AX>%5LVcHSexWL53u$aUJp&MdM1XsGn#DyEf&Mn@1hiQ24z0Es0=iGC??>pz- z@7n`gfA3hivzz#y@169Bv_C3Ve<}}$#;#nr+E}=RJuFxM{d^F2({KQ1Q5mjojv@i4 z91N}boch2)1MtV@rG8uE{x$dMC!qRvLvwzlqhk|$Q2S5Nkn1{Ks zq6pSsDZua7BRJX~fZx{h4yLemoxyM$JD6GX%QkZ`*X9xUv%Sy3q6ZM3-8~FTH+J-S z7^})7Bxzz4bz&Fb*p0jzKcqD{ax0fx{! z4h~@wjSX7{9@UQv+HK`bLTFfHw)nE)qEZyt{IFDQOna~R`Xd{+bqMUx&AZ- zb=W~&G1an%teR@)BXq#QxT8lK^{JKp);+0~N@!A551^9KbMUK$xeKoEF`~}=iau&- z8O`Ti_FQZDJ{1~3%Et_vgpV*PMaoAB=IMHrFtgZ+vy|}QMZMN`V@d?mGNlrwuLqj) z1%5U!Gr%*WN3 z1~W08p5}y4$D~$|X(&leYHBg9HDVG~!2 diff --git a/tests/test-data/example_output3.pdf b/tests/test-data/example_output3.pdf index 6aeaefc5d28042e155e9858adb619c09dab3b466..101fe110633c8bc7ff0a0afd4d57970fefbe5bd5 100644 GIT binary patch delta 1223 zcmV;&1UUQntOEJ00F0XUN*k1v1tCbd8hEzk#wT(WDRmjw!1%h*a~*^%X>`|mp> zCHeixA^{3C7HpcF;Wr;=$l*W_17JY$Nx%6F;O7xs|H^=I7~KJrM8fC>ei;oO2A!sr zV7MV%DULHxK&Zx#f!(&A3=nN?fN`Rk_-xx}h8RUzu)ibHHdvwRm8jhn^|FCxz9{lj1ho+aNytM4Mb?WR1~dwG>2TQ- zp)n378votb9*QM2QxD6=)eX$=z(M7oVBu;vsoi24hHm_$FA->2x-o&Ds&CNve;UYL z1!E6Sb9Ysi{4ZR$RoV9ZIGi%LJoUs>mbM=!?Tc-5T7WTSt@}D z+;dz3u|}!@i%j?r6tYOA(;gPH#k_9fK_E;rW;C|Ai*YX#W~E>wO>>iIpM zkiV+cZ0zexXy zSD5jbK{+Ny!!dsxxJ!x6WK5q@aSOcfW?fRvcd~6r>V_zdB}bc!ZQqL+f@OqTmGxHg z!RNK!9!%$mZzbWL&Ua#sJ<$|`^H%Ie};?dnoQ_g1sK)hz#?ngy#1q6B-j z_i@vrzMwW_5dukbedw)RSVkxM{uevxrBhe2_=i<|BlQte8r9N@46^|jR#J76C6gO0 z0iWB!zg*RV-;+^}6tk0)MgkQ%ATTg6Ffbr)APO)rFfcGPH#s#olkSx*T|+ZMLo`G~ zGeR^&GdVLuGC43qGC?^*GB`FhG($5uGCn*;LPR+@Lq;+;H$*u@K}Iq&Ff>9&GBP+r lH$^x>H#kH-T|Pbvb97;Hba--QW(qSnFfua=B_%~qMhfF0XLH)k1v0QPf-i>&;os+$R)c5dRd^LwT!J)mK|A6y8pgI zQIg+}EE1qVVUaC{Q+*Br+qF*1_7a&g*}|zlYI1 zjIM(1Tl#L>7b#0L@?d{QWKu;|P`?tjyS!S~P|p{6c8Z{}nh^s9YhOZx=E|=yb&r1@ioEpW%Dcap-M)sd23L*iymHkyDCU`~^Le>sFe3An zU#^NeFDDWDDKCnM?G_9Fm7VmBGDm$waXsgo(o+4iiN>H?F29g`5G@Vq0f+l~K(Wy;oA_d0h zoF&%U^_*ZgCX%ILT|Xy}bGp(!kV;6Qx6#Zpf@ppe9dg%{#qOr8G7oB9BGg15E}a-$NF!>yliTD8_ZK!nG!+f@wS9Nm{7+0JphDL*lB?NAj9Nh-YEGygv})Kj`niJ?x);8(dF5-7FC% z%k}M;t?!43S>hjLQhN-Np4520{%@DO|)rosE z+i_U5uxoeL6az#GVQZ!@4q|71y4bZ8dbEzkL6`1Te~o{J=>*ePY9QO@?gObV%q z?)UlDFugTQFWoRbv!`$`-BX+@&icRFS!FNYSzX+#U0sUk-g=g|p5_13vq(*jlKj=) z$4&F!MMOd~HZn9h kGeR&kGeJIGK0XR_bYXIIcyeWC3Ntt`GBOG!B}Gq03h2BzfdBvi diff --git a/tests/test-data/filled_forms.pdf b/tests/test-data/filled_forms.pdf index fe5d2eb10ee0b57635a927b04d09ac978f34940e..5a07a1aa28d95544d1e47dc7ee68357bb1026a38 100644 GIT binary patch delta 80 zcmWN@!3}^Q3;@8yHJpML+L9O(+S>JexQJmBhv3H$^17R2n`3+LuB2QIB^V5 Here is the start of a new paragraph of {{region}}. Any text can be written Here @@ -27,7 +23,7 @@ title: - first bullet point - second bullet point - third bullet point - - _pagebreak: null + - _pagebreak: body - third topic: - First sub paragraph leading into subheading - Second sub paragraph leading into subheading diff --git a/tests/test_multipage_templates.py b/tests/test_multipage_templates.py new file mode 100644 index 0000000..3e28961 --- /dev/null +++ b/tests/test_multipage_templates.py @@ -0,0 +1,137 @@ +import pathlib + +import pytest +from reportlab.platypus import PageBreak, NextPageTemplate + +import ymprint.config as config +from ymprint.config.config_loaders import load_report_config +from ymprint.blocks.page_break_block import convert_page_break +from ymprint.blocks.nextpagetemplate_block import convert_next_page_template +from ymprint.exceptions import YMPrintSyntaxException + +TEST_DATA = pathlib.Path(__file__).parent / "test-data" + + +def make_doc_config(): + return config.DocConfig.model_validate( + { + "page-size": "a4", + "landscape": False, + "templates": { + "cover": {"margins": {"top": 200, "left": 72, "right": 72, "bottom": 72}}, + "body": {"margins": {"top": 72, "left": 72, "right": 72, "bottom": 72}}, + }, + } + ) + + +# --- DocConfig template resolution ------------------------------------------------- + +def test_template_names_preserve_order(): + doc = make_doc_config() + assert doc.template_names == ["cover", "body"] + + +def test_resolve_template_id_by_name_and_index(): + doc = make_doc_config() + assert doc.resolve_template_id("body") == "body" + assert doc.resolve_template_id(0) == "cover" + assert doc.resolve_template_id(1) == "body" + + +def test_resolve_template_id_rejects_unknown_name(): + doc = make_doc_config() + with pytest.raises(ValueError): + doc.resolve_template_id("nope") + + +def test_resolve_template_id_rejects_out_of_range_index(): + doc = make_doc_config() + with pytest.raises(ValueError): + doc.resolve_template_id(5) + + +def test_min_available_dims_use_smallest_template(): + doc = make_doc_config() + # cover has the larger top margin, so the smallest height comes from cover + assert doc.min_available_height() == doc.available_height("cover") + assert doc.min_available_width() == doc.available_width("body") + + +def test_build_records_page_template_map(): + from io import BytesIO + from reportlab.lib.styles import getSampleStyleSheet + from reportlab.platypus import Paragraph + + doc_cfg = make_doc_config() + styles = getSampleStyleSheet() + story = [ + Paragraph("p1", styles["Normal"]), + NextPageTemplate("body"), + PageBreak(), + Paragraph("p2", styles["Normal"]), + ] + doc, page_map = doc_cfg.build("/dev/null") + doc.build(story, filename=BytesIO()) + assert page_map == {0: "cover", 1: "body"} + + +# --- Block converters -------------------------------------------------------------- + +def make_context(): + return {"doctemplate": {"ymprint": make_doc_config()}} + + +def test_pagebreak_without_value_is_plain_break(): + result = convert_page_break("_pagebreak", None, make_context()) + assert len(result) == 1 + assert isinstance(result[0], PageBreak) + + +def test_pagebreak_with_name_switches_template(): + result = convert_page_break("_pagebreak", "body", make_context()) + assert isinstance(result[0], NextPageTemplate) + assert result[0].action == ("nextPageTemplate", "body") + assert isinstance(result[1], PageBreak) + + +def test_pagebreak_with_index_switches_template(): + result = convert_page_break("_pagebreak", 1, make_context()) + assert isinstance(result[0], NextPageTemplate) + assert result[0].action == ("nextPageTemplate", "body") + + +def test_pagebreak_unknown_template_raises_syntax_exception(): + with pytest.raises(YMPrintSyntaxException): + convert_page_break("_pagebreak", "missing", make_context()) + + +def test_nextpagetemplate_sets_template_without_break(): + result = convert_next_page_template("_nextpagetemplate", "body", make_context()) + assert len(result) == 1 + assert isinstance(result[0], NextPageTemplate) + assert result[0].action == ("nextPageTemplate", "body") + + +def test_nextpagetemplate_unknown_template_raises_syntax_exception(): + with pytest.raises(YMPrintSyntaxException): + convert_next_page_template("_nextpagetemplate", 9, make_context()) + + +# --- Config merge with named templates -------------------------------------------- + +def test_config_named_templates_override_default_template(): + # example_2_config defines 'first' and 'body' templates + _styles, _tablestyles, doc = load_report_config({}, TEST_DATA / "example_2_config") + templates = doc["_doc"]["templates"] + assert list(templates.keys()) == ["first", "body"] + # the default 'default' template must not leak in + assert "default" not in templates + + +def test_config_without_templates_falls_back_to_default(): + # example_1_config defines a single 'default' template + _styles, _tablestyles, doc = load_report_config({}, TEST_DATA / "example_1_config") + templates = doc["_doc"]["templates"] + assert list(templates.keys()) == ["default"] + assert templates["default"]["margins"]["top"] == 84 From 981b2c409de3521f9cf25514df1d6fd13bc2422b Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Thu, 13 Aug 2026 20:49:23 +0000 Subject: [PATCH 2/2] chore: removed dead files --- src/ymprint/config/pdf_fill_forms.py | 0 tests/test-data/example_output1.pdf | Bin 57800 -> 57800 bytes tests/test-data/example_output2.pdf | Bin 118694 -> 118694 bytes tests/test-data/example_output3.pdf | Bin 38521 -> 38521 bytes tests/test-data/filled_forms.pdf | Bin 45804 -> 45804 bytes tests/test-data/report_example_1.pdf | Bin 57426 -> 0 bytes tests/test-data/report_example_2.pdf | Bin 33967 -> 0 bytes 7 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/ymprint/config/pdf_fill_forms.py delete mode 100644 tests/test-data/report_example_1.pdf delete mode 100644 tests/test-data/report_example_2.pdf diff --git a/src/ymprint/config/pdf_fill_forms.py b/src/ymprint/config/pdf_fill_forms.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/test-data/example_output1.pdf b/tests/test-data/example_output1.pdf index 56422055ba5c198195046fb0d665034d8cc9eeea..aac4abdecc4e24f9492c77ceb71c72091a6532bc 100644 GIT binary patch delta 80 zcmWN@yA6Oa3=sDH%Ch& i3nNoYQy^*S=;Q*Vos2DvO%2_golGq)rgxoZlmP(O@D>~Z delta 83 zcmWN=%ME}a5Cy<+q6v$Dh2@hN6ZV0zEYCLJksB4*2QA4YGrAsK-=DgsSbzl9gR+29 jTme@i8#q~y-FYsFrAcX-L-oF#S&V}XwI*uzeWu!e-pdwN diff --git a/tests/test-data/example_output3.pdf b/tests/test-data/example_output3.pdf index 101fe110633c8bc7ff0a0afd4d57970fefbe5bd5..2865bb5caff3973fa0a435c90b355f6611661a71 100644 GIT binary patch delta 78 zcmWN|!3}^Q3+i=X$YjvjEtO(?QATJ9i3dvTn(Kq fOpGjC%^b~~jZ6$o4UG*949$&<+>9n)+$0SE?-&-b delta 80 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 diff --git a/tests/test-data/report_example_2.pdf b/tests/test-data/report_example_2.pdf deleted file mode 100644 index 0b4b6011868cae2c68ffe1c8dc1fb3f23dd4a995..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33967 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 NlP2ciP}NZ*{y&p*+b#eA