[1]:
import pydocmaker as pyd
import os
from pathlib import Path
print(pyd.__version__)
2.6.9
Using templates (HTML, Typst, and LaTeX)
pydocmaker supports mounting Jinja2 Templates organized in folders together with (optional) default parameters and assets. Such templates are organized in folders. pydocmaker has one such template directory shipped with it by default which is part of the installation.
The pydocmaker inbuild templates
By default the following templates are available:
latex:base.tex.j2a basic minimal latex template without anything fancy (but with a list of reference and applicable documents as well as acronyms)html:base.html.j2a basic minimal html template without anything fancy (but with a list of reference and applicable documents as well as acronyms)typst:paper.typa minimal paper template with author(s), abstract and a title, whcih has been adapted from the typst documentationreport.typa more comprehensive report template with many possible parameters, headers a title page, and headers / footers. This template was adapted from the Typst ``basic-report` template in the typst Universe <https://typst.app/universe/package/basic-report/>`__
[2]:
pyd.get_registered_template_dirs()
[2]:
['C:\\Users\\tglaubach\\repos\\pydocmaker\\src\\pydocmaker\\templates']
[3]:
pyd.get_available_template_ids()
[3]:
['base', 'paper', 'report']
Definition of templates
Each template has
a name, and its format. The main name per template is then:
<name>.<format>.j2Optionally it can also have an assests folder
<name>.assets/where you can keep any assests such as images/logos etc.Optionally it can have a JSON file
<name>.params.jsonto define the parameters which can be used for this template type, since (as far as I know) Jinja2 has no possibility to reflect / track expected parameter names.
Such templates only work for text based formats, such as html and tex.
[4]:
for d in pyd.get_registered_template_dirs():
print(d)
for f in os.listdir(d):
print(' ', f)
C:\Users\tglaubach\repos\pydocmaker\src\pydocmaker\templates
assets
base.assets
base.html.j2
base.tex.j2
base.typ.j2
paper.typ.j2
report.typ.j2
word_template_with_mergefields.docx
Adding / defining you own template
Suppose you have the following folder structure with a Jinja2 template
home/jovyan/templates/
├─ assets/
│ ├─ i_can_use_this_everywhere.png
├─ fancy_tempate.assets/
│ ├─ fancy_logo.png
│ ├─ fancy_title_picture.png
├─ fancy_template.params.json
├─ fancy_template.tex.j2
├─ normal_template.params.json
├─ normal_template.tex.j2
(NOTE: you can also check out the templates folder in this repository for an example).
You can then mount this folder in pydocmaker using
[5]:
p = (Path(pyd.__file__).parent.parent.parent / 'example_templates')
print(p, p.exists())
pyd.register_new_template_dir(str(p))
C:\Users\tglaubach\repos\pydocmaker\example_templates True
[5]:
True
Which will give you two available templates to use for exporting tex and pdf documents:
[6]:
pyd.get_available_template_ids()
[6]:
['base', 'fancy_template', 'normal_template', 'paper', 'report']
Inspecting parameters
As stated before, for each template you can define a <name>.params.json file. You can then get the parameters conveniently via get_template_params.
NOTE: When a .params.json file is found for a template, its contents are used as-is. When no .params.json file exists and allow_fallback_jinja is True, parameters are inferred from the template source via find_undeclared_variables, in which case all parameter values are set to jinja2.Undefined which will be ignored on render.
This is how to get the parameters for a single template:
[7]:
pyd.get_template_params('base')
[7]:
{'acronyms': '__jinja2.Undefined,acronyms',
'references': '__jinja2.Undefined,references',
'applicables': '__jinja2.Undefined,applicables',
'title': '__jinja2.Undefined,title'}
and this is how to get the parameters for all templates.
[8]:
pyd.get_template_params()
[8]:
{'base.html.j2': {'acronyms': '__jinja2.Undefined,acronyms',
'references': '__jinja2.Undefined,references',
'applicables': '__jinja2.Undefined,applicables',
'title': '__jinja2.Undefined,title'},
'base.tex.j2': {'acronyms': '__jinja2.Undefined,acronyms',
'default': '__jinja2.Undefined,default',
'references': '__jinja2.Undefined,references',
'date': '__jinja2.Undefined,date',
'applicables': '__jinja2.Undefined,applicables',
'title': '__jinja2.Undefined,title',
'author': '__jinja2.Undefined,author'},
'base.typ.j2': {'acronyms': '__jinja2.Undefined,acronyms',
'references': '__jinja2.Undefined,references',
'applicables': '__jinja2.Undefined,applicables',
'title': '__jinja2.Undefined,title'},
'fancy_template.tex.j2': {'title': 'EMPTY',
'date': '',
'author': 'Automatically Generated',
'acronyms': {},
'references': {},
'applicables': {}},
'normal_template.tex.j2': {'title': 'EMPTY',
'date': '',
'author': 'Automatically Generated',
'acronyms': {},
'references': {},
'applicables': {}},
'paper.typ.j2': {'columns': '__jinja2.Undefined,columns',
'authors': '__jinja2.Undefined,authors',
'abstract': '__jinja2.Undefined,abstract',
'title': '__jinja2.Undefined,title',
'author': '__jinja2.Undefined,author'},
'report.typ.j2': {'subtitle': '__jinja2.Undefined,subtitle',
'logo': '__jinja2.Undefined,logo',
'acronyms': '__jinja2.Undefined,acronyms',
'hide_toc': '__jinja2.Undefined,hide_toc',
'hide_pydocmaker': '__jinja2.Undefined,hide_pydocmaker',
'title': '__jinja2.Undefined,title',
'author': '__jinja2.Undefined,author',
'logo_b64_pydocmaker': '__jinja2.Undefined,logo_b64_pydocmaker',
'version': '__jinja2.Undefined,version',
'references': '__jinja2.Undefined,references',
'footer_str_right': '__jinja2.Undefined,footer_str_right',
'doc_category': '__jinja2.Undefined,doc_category',
'abstract': '__jinja2.Undefined,abstract',
'titlepage_info_dict': '__jinja2.Undefined,titlepage_info_dict',
'date': '__jinja2.Undefined,date',
'footer_str_left': '__jinja2.Undefined,footer_str_left',
'logo_b64': '__jinja2.Undefined,logo_b64',
'hide_date': '__jinja2.Undefined,hide_date',
'applicables': '__jinja2.Undefined,applicables',
'header_str_right': '__jinja2.Undefined,header_str_right',
'header_str_left': '__jinja2.Undefined,header_str_left'}}
NOTE: variables/properties which are string and are starting with __jinja2.Undefined get treated like jinja2.Undefined while rendering, meaning they will be ignored. The decision was taken so that the properties remain serializeable to JSON.
Using your template
You can mark the templates to be used for a doc by setting it to the reports metadata:
[9]:
doc = pyd.get_example()
doc.set_template_to_meta('report', tformat=None)
doc.get_metadata()
[9]:
{'subtitle': '__jinja2.Undefined,subtitle',
'logo': '__jinja2.Undefined,logo',
'acronyms': '__jinja2.Undefined,acronyms',
'hide_toc': '__jinja2.Undefined,hide_toc',
'hide_pydocmaker': '__jinja2.Undefined,hide_pydocmaker',
'title': '__jinja2.Undefined,title',
'author': '__jinja2.Undefined,author',
'logo_b64_pydocmaker': '__jinja2.Undefined,logo_b64_pydocmaker',
'version': '__jinja2.Undefined,version',
'references': '__jinja2.Undefined,references',
'footer_str_right': '__jinja2.Undefined,footer_str_right',
'doc_category': '__jinja2.Undefined,doc_category',
'abstract': '__jinja2.Undefined,abstract',
'titlepage_info_dict': '__jinja2.Undefined,titlepage_info_dict',
'date': '__jinja2.Undefined,date',
'footer_str_left': '__jinja2.Undefined,footer_str_left',
'logo_b64': '__jinja2.Undefined,logo_b64',
'hide_date': '__jinja2.Undefined,hide_date',
'applicables': '__jinja2.Undefined,applicables',
'header_str_right': '__jinja2.Undefined,header_str_right',
'header_str_left': '__jinja2.Undefined,header_str_left',
'files_to_upload': {'i_can_use_this_everywhere.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg=='},
'template_id': 'report'}
[10]:
pdf = doc.to_pdf(verb=1)
len(pdf), pdf[:120]
[2026-06-10 16:05:41 | INFO | pydocmaker] making PDF with engine='typ'
[2026-06-10 16:05:41 | INFO | pydocmaker] found non-path content in input dict, compiling typst in temporary directory...
[2026-06-10 16:05:41 | INFO | pydocmaker] Compiling typst document to N/A format pdf with typst compiler...
[10]:
(64625,
b'%PDF-1.7\n%\x80\x80\x80\x80\n\n1 0 obj\n<<\n /Type /Pages\n /Count 3\n /Kids [160 0 R 163 0 R 165 0 R]\n>>\nendobj\n\n2 0 obj\n<<\n /Type /Ou')
[11]:
doc = pyd.get_example()
doc.set_template_to_meta('fancy_template')
[11]:
{'title': 'EMPTY',
'date': '',
'author': 'Automatically Generated',
'acronyms': {},
'references': {},
'applicables': {},
'files_to_upload': {'i_can_use_this_everywhere.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg==',
'fancy_logo.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg==',
'fancy_title_picture.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg=='},
'template_id': 'fancy_template'}
which will write all needed data to the documents “metadata”. Specifically:
template_idwill hold the id with which the specific template can be loaded. In our case it will befancy_template.files_to_uploadwill hold all assets as base64 encoded bytes in our case the following files:key:
fancy_logo.pngvalue content from.../fancy_tempate.assets/fancy_logo.pngkey:
fancy_title_picture.pngvalue content from.../fancy_tempate.assets/fancy_title_picture.pngkey:
i_can_use_this_everywhere.pngvalue content from.../assets/i_can_use_this_everywhere.png(content fromassetswill be made available shared for all templates)
and all other fields loaded from
fancy_template.params.jsonwill be loaded to the metadata dictionary directly.
If you thereafter export your document to pdf (or html if you have an html type template), pydocmaker will automatically load the template and render it with parameters, attachments and your document as the body.
you can view the template and params by
[12]:
doc.get_meta()
[12]:
{'typ': 'meta',
'children': '',
'data': {'title': 'EMPTY',
'date': '',
'author': 'Automatically Generated',
'acronyms': {},
'references': {},
'applicables': {},
'files_to_upload': {'i_can_use_this_everywhere.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg==',
'fancy_logo.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg==',
'fancy_title_picture.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg=='},
'template_id': 'fancy_template'}}
[13]:
doc.update_meta(author='Me!')
[13]:
{'title': 'EMPTY',
'date': '',
'author': 'Me!',
'acronyms': {},
'references': {},
'applicables': {},
'files_to_upload': {'i_can_use_this_everywhere.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg==',
'fancy_logo.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg==',
'fancy_title_picture.png': 'iVBORw0KGgoAAAANSUhEUgAAAH4AAAAVCAYAAACAEFoRAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAOwSURBVGhD7Zi/a9toGMc/vbW4l0LRonrQ4GDeQCFD0Q3GcAYPBwZDh3gygczpnj/BezwbjkzuYCgYOgRSCIY7kyEQqAjpoMHVYgJxIzL3Bv2I9OqVrSTOtcH6gMB69Up63+f7vM/7lZ+VSus/yFk5fpMbclaDXPgVJRd+RcmFX1GeJcydMOnXXnD64ZDONHbFR6eza2L4Zzdnh+wcu1KfHIBKtc77NwXvZDKm9dGRuzwyBfa262zOku++84pvN02MyZhWd0CrO3gaoguT/naZitz+yIyOD2l1B+yf3TNGWpnerklbbl8CKcJf4yhXewF9Deyv/3fm5jyEm6truUlR6ufilXmOBuxZ6mvBFhArbcKkX/rG/pUIS19yi5Dudy32/z5nJEz6NRh2xxwE11RtKrQyvS3Bc7kdJ3KvXw79ihwdd6VaZ4cJ0zcCA4fhETRq+m2fTPPyS/5LK1FuE3MO3y21h7ixLTi2lcTmtJhswmcIYLtpwsfgxVKCCJO+HLCYcF5/TRG0xLOAdvMdDZL7VirCpP/2u5dI0qX4s7wkWLe9cXiBhdMP/8JfdTax2P8EO1u/87k75kCel1amt1XkQvJHacLPjRlB3P13xe5MJn+lWue9MVHOUUVKqZeYnrPTHdDqjrEB+8jb31uRAR2EEwBw+DIB7VWQjf4KDiZufcPmBbrmnVaqAsO16CVEx0uuMxejpPvnOhtFl9N/Moo+F52NosMwFMSlc+Lw3NBv/cDECkW0TxRBjc5r6nDhFlh7JXdSszBmc2iXdOyj2/tHxxZ2oUjFj+kisgmfBWHS330XHo2i3CEd42UBZm4yqD6j8wk3xdeeyRGvMSJiPAitgIZOIzLufi1IsPuTVbz7x8zzWkYtMm7l1pDOcoTXyvRqeqQSDBhO5E7p2FeqlR5h6nDh6mwIP9OXai4dhv6YwyNjuUxjerlgPjw8ZsQqb3CkfYInWY7wALjMLv2fwrxD9gYr2qQj5CsBXgk23tb5c81imDCWC7j8zo2qDPoJ1Wg+fJUDVKp/sFlw+JJ5fAtiNnWZ4iV8HJeR7WLU7v+pl83chSgMiE+7GSlVrsXpTLB+5Zu1hLnS6ewKZlETJBvIwNUH53MN4GLSHbDk6iPO/NaUXbO3XWftZMDeZcRwBeYuZP5zIf7VMDdmAbF3zHP1qpilc0fhfyaKZPnZJBL66bDEUv+4eP8YLsnU5fz6Kz4sh6oyJm8PEqo/U5bKE17xv7zwOY/Dkyn1OcslF35F+Q9idByILrzS4QAAAABJRU5ErkJggg=='},
'template_id': 'fancy_template'}
Jinja2 templates from strings
you can also use Jinja2 Templates directly to make HTML or PDF (latex) documents. An example is given below:
[14]:
# This is a minimal template, the document will be written to the "body" part.
template = r'''
\documentclass[a4paper]{article}
{% if title %}\title {{ title }}{% endif %}
{% if author %}\author {{ author }}{% endif %}
\begin{document}
{{ body }}
\end{document}
'''
import pydocmaker as pyd
doc = pyd.get_example()
pdf_bytes = doc.to_pdf(template=template, template_params=dict(title='My Title', author='Me'))
len(pdf_bytes), type(pdf_bytes)
[14]:
(114267, bytes)
[15]:
tex_string, attachments = doc.to_tex(template=template, template_params=dict(title='My Title', author='Me'))
print(tex_string[:120] + '...')
\documentclass[a4paper]{article}
\title My Title
\author Me
\begin{document}
\subsection{Some Example Text}\label{so...
NOTE: If you use template strings directly and your document template has external references such as logos, you need to load them to a bytes array and pass them as a {filename:content} dictionary into the to_pdf(…) methods using the files_to_upload argument.
[16]:
assets = {}
with open(r'C:\Users\tglaubach\repos\pydocmaker\example_templates\fancy_template.assets\fancy_logo.png', 'rb') as fp:
assets = {'fancy_logo.png': fp.read()}
pdf_bytes = doc.to_pdf(template=template, template_params=dict(title='My Title', author='Me'), files_to_upload=assets, engine='tex')
len(pdf_bytes), type(pdf_bytes)
[16]:
(114267, bytes)
A note on engine(s) and templates when using to_pdf
When using doc.to_pdf with the arguments template, and engine you need to make sure that your engine matches your template.
Automatically determine engine if engine=None
If you leave engine=None the engine type will be determined automatically in the following order:
if a template is given directly: Determine engine from template format/type via:
if template is a
jinja2.Templateobject with a “filepath” use the filepath as a template and continue as stated below.if template is a string and not a file path or known template name: Try to determine the template type by looking for a few keywords in template, such as
<html>or\documentclassif tmplate is a string or a
Path: determine the template type from the filename extension
if no template is given, but the document has a template defined in its metadata. Determine engine from the metadata template format/type in the same manner as described in the last point.
If no form of template is given, use the engine as configured for pydocmaker in the config (determine from what is available).
Directly stating engine and template
If you want to give both template, and engine directly you need to match the two. e.G.
template = "report.typ.j2"withengine = "typ"template = "base.tex.j2"withengine = "tex"
[17]:
def helper_function_for_testing(engine, template):
print('-----')
print(f'{template=} {engine=}')
print('', flush=True)
try:
pdf_bytes = pyd.get_example().to_pdf(template=template, engine=engine, verb=2)
print(len(pdf_bytes), pdf_bytes[:100])
print('-----')
except Exception as err:
pyd.log.error(f'ERROR: {err}', exc_info=1)
The following will resolve base to base.tex.j2
[18]:
helper_function_for_testing(engine='tex', template="base")
-----
template='base' engine='tex'
[2026-06-10 16:05:45 | INFO | pydocmaker] making PDF with engine='tex'
[2026-06-10 16:05:45 | INFO | pydocmaker] template='base'
[2026-06-10 16:05:45 | INFO | pydocmaker] params.keys()=dict_keys([])
[2026-06-10 16:05:45 | INFO | pydocmaker] files_to_upload.keys()=dict_keys([])
[2026-06-10 16:05:45 | INFO | pydocmaker] "tex" backend creating ".tex" document with
[2026-06-10 16:05:45 | INFO | pydocmaker] template="<Template 'base.tex.j2'>"
[2026-06-10 16:05:45 | INFO | pydocmaker] -> str: '\\documentclass[a4paper]{articl...'
[2026-06-10 16:05:45 | INFO | pydocmaker] kw.keys()=dict_keys(['body'])
[2026-06-10 16:05:45 | INFO | pydocmaker] attachments.keys()=dict_keys([])
[2026-06-10 16:05:45 | INFO | pydocmaker] "tex" backend creating ".tex" document from latex string '\\documentclass[a4paper]{articl...' and attachments dict_keys(['img_1781100345702040700.png'])
87912 b'%PDF-1.5\n%\xd0\xd4\xc5\xd8\n1 0 obj\n<< /S /GoTo /D (subsection.0.1) >>\nendobj\n4 0 obj\n(\\376\\377\\000S\\000o\\000m\\00'
-----
The following will just use engine tex and template base.tex.j2 (if available) to make a PDF document.
[19]:
helper_function_for_testing(engine='tex', template="base.tex.j2")
-----
template='base.tex.j2' engine='tex'
[2026-06-10 16:05:48 | INFO | pydocmaker] making PDF with engine='tex'
[2026-06-10 16:05:48 | INFO | pydocmaker] template='base.tex.j2'
[2026-06-10 16:05:48 | INFO | pydocmaker] params.keys()=dict_keys([])
[2026-06-10 16:05:48 | INFO | pydocmaker] files_to_upload.keys()=dict_keys([])
[2026-06-10 16:05:48 | INFO | pydocmaker] "tex" backend creating ".tex" document with
[2026-06-10 16:05:48 | INFO | pydocmaker] template="<Template 'base.tex.j2'>"
[2026-06-10 16:05:48 | INFO | pydocmaker] -> str: '\\documentclass[a4paper]{articl...'
[2026-06-10 16:05:48 | INFO | pydocmaker] kw.keys()=dict_keys(['body'])
[2026-06-10 16:05:48 | INFO | pydocmaker] attachments.keys()=dict_keys([])
[2026-06-10 16:05:48 | INFO | pydocmaker] "tex" backend creating ".tex" document from latex string '\\documentclass[a4paper]{articl...' and attachments dict_keys(['img_1781100348054432700.png'])
87912 b'%PDF-1.5\n%\xd0\xd4\xc5\xd8\n1 0 obj\n<< /S /GoTo /D (subsection.0.1) >>\nendobj\n4 0 obj\n(\\376\\377\\000S\\000o\\000m\\00'
-----
The following will raise a ValueError because of template-engine mismatch
[20]:
helper_function_for_testing(engine='tex', template="base.typ.j2")
-----
template='base.typ.j2' engine='tex'
[2026-06-10 16:05:50 | ERROR | pydocmaker] ERROR: The requested template='base.typ.j2' is of format "'typ'" while the current engine is "tex" which requires "tex" for templates.
Traceback (most recent call last):
File "C:\Users\tglaubach\AppData\Local\Temp\ipykernel_8868\3294294629.py", line 6, in helper_function_for_testing
pdf_bytes = pyd.get_example().to_pdf(template=template, engine=engine, verb=2)
File "C:\Users\tglaubach\repos\pydocmaker\src\pydocmaker\core.py", line 1777, in to_pdf
raise ValueError(f'The requested {template=} is of format "{tformat!r}" while the current engine is "{engine}" which requires "{engine}" for templates.')
ValueError: The requested template='base.typ.j2' is of format "'typ'" while the current engine is "tex" which requires "tex" for templates.
The following will automatically set engine to typ
[21]:
helper_function_for_testing(engine=None, template="base.typ.j2")
-----
template='base.typ.j2' engine=None
[2026-06-10 16:05:50 | INFO | pydocmaker] Inferred engine='typ' from provided template='base.typ.j2'
[2026-06-10 16:05:50 | INFO | pydocmaker] making PDF with engine='typ'
[2026-06-10 16:05:50 | INFO | pydocmaker] template='base.typ.j2'
[2026-06-10 16:05:50 | INFO | pydocmaker] params.keys()=dict_keys([])
[2026-06-10 16:05:50 | INFO | pydocmaker] files_to_upload.keys()=dict_keys([])
[2026-06-10 16:05:50 | INFO | pydocmaker] Compiling typst document to N/A format pdf with typst compiler...
50892 b'%PDF-1.7\n%\x80\x80\x80\x80\n\n1 0 obj\n<<\n /Type /Pages\n /Count 1\n /Kids [118 0 R]\n>>\nendobj\n\n2 0 obj\n<<\n /Type'
-----
The following will automatically set engine to tex
[22]:
helper_function_for_testing(engine=None, template="base.tex.j2")
-----
template='base.tex.j2' engine=None
[2026-06-10 16:05:50 | INFO | pydocmaker] Inferred engine='tex' from provided template='base.tex.j2'
[2026-06-10 16:05:50 | INFO | pydocmaker] making PDF with engine='tex'
[2026-06-10 16:05:50 | INFO | pydocmaker] template='base.tex.j2'
[2026-06-10 16:05:50 | INFO | pydocmaker] params.keys()=dict_keys([])
[2026-06-10 16:05:50 | INFO | pydocmaker] files_to_upload.keys()=dict_keys([])
[2026-06-10 16:05:50 | INFO | pydocmaker] "tex" backend creating ".tex" document with
[2026-06-10 16:05:50 | INFO | pydocmaker] template="<Template 'base.tex.j2'>"
[2026-06-10 16:05:50 | INFO | pydocmaker] -> str: '\\documentclass[a4paper]{articl...'
[2026-06-10 16:05:50 | INFO | pydocmaker] kw.keys()=dict_keys(['body'])
[2026-06-10 16:05:50 | INFO | pydocmaker] attachments.keys()=dict_keys([])
[2026-06-10 16:05:50 | INFO | pydocmaker] "tex" backend creating ".tex" document from latex string '\\documentclass[a4paper]{articl...' and attachments dict_keys(['img_1781100350664246600.png'])
87912 b'%PDF-1.5\n%\xd0\xd4\xc5\xd8\n1 0 obj\n<< /S /GoTo /D (subsection.0.1) >>\nendobj\n4 0 obj\n(\\376\\377\\000S\\000o\\000m\\00'
-----
The following will use configured engine and try to find a matching base.<engine>.j2 template
[23]:
helper_function_for_testing(engine=None, template="base")
-----
template='base' engine=None
[2026-06-10 16:05:52 | INFO | pydocmaker] Inferred engine='typ' from provided template='base'
[2026-06-10 16:05:52 | INFO | pydocmaker] making PDF with engine='typ'
[2026-06-10 16:05:52 | INFO | pydocmaker] template='base'
[2026-06-10 16:05:52 | INFO | pydocmaker] params.keys()=dict_keys([])
[2026-06-10 16:05:52 | INFO | pydocmaker] files_to_upload.keys()=dict_keys([])
[2026-06-10 16:05:53 | INFO | pydocmaker] Compiling typst document to N/A format pdf with typst compiler...
50892 b'%PDF-1.7\n%\x80\x80\x80\x80\n\n1 0 obj\n<<\n /Type /Pages\n /Count 1\n /Kids [118 0 R]\n>>\nendobj\n\n2 0 obj\n<<\n /Type'
-----
The following will try to use report.tex.j2 as a template. If not found it will infer the engine from the first match for a template which matches report.<engine>.j2 and set engine accordingly.
[24]:
pyd.config_pdf_engine_set("tex")
helper_function_for_testing(engine=None, template="report")
-----
template='report' engine=None
[2026-06-10 16:05:53 | INFO | pydocmaker] Inferred engine='typ' from provided template='report'
[2026-06-10 16:05:53 | INFO | pydocmaker] making PDF with engine='typ'
[2026-06-10 16:05:53 | INFO | pydocmaker] template='report'
[2026-06-10 16:05:53 | INFO | pydocmaker] params.keys()=dict_keys([])
[2026-06-10 16:05:53 | INFO | pydocmaker] files_to_upload.keys()=dict_keys([])
[2026-06-10 16:05:53 | INFO | pydocmaker] Compiling typst document to N/A format pdf with typst compiler...
64625 b'%PDF-1.7\n%\x80\x80\x80\x80\n\n1 0 obj\n<<\n /Type /Pages\n /Count 3\n /Kids [160 0 R 163 0 R 165 0 R]\n>>\nendobj\n\n2 '
-----