ymprint 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ymprint-0.1.0/PKG-INFO +150 -0
- ymprint-0.1.0/README.md +132 -0
- ymprint-0.1.0/pyproject.toml +34 -0
- ymprint-0.1.0/src/ymprint/__init__.py +2 -0
- ymprint-0.1.0/src/ymprint/blocks/__init__.py +107 -0
- ymprint-0.1.0/src/ymprint/blocks/admonition_block.py +42 -0
- ymprint-0.1.0/src/ymprint/blocks/blockstyles.py +340 -0
- ymprint-0.1.0/src/ymprint/blocks/code_block.py +25 -0
- ymprint-0.1.0/src/ymprint/blocks/code_block_styles.py +558 -0
- ymprint-0.1.0/src/ymprint/blocks/hrule_block.py +26 -0
- ymprint-0.1.0/src/ymprint/blocks/image_block.py +59 -0
- ymprint-0.1.0/src/ymprint/blocks/json_block.py +31 -0
- ymprint-0.1.0/src/ymprint/blocks/matplotfig_block.py +68 -0
- ymprint-0.1.0/src/ymprint/blocks/page_break_block.py +10 -0
- ymprint-0.1.0/src/ymprint/blocks/python_block.py +31 -0
- ymprint-0.1.0/src/ymprint/blocks/quote_block.py +25 -0
- ymprint-0.1.0/src/ymprint/blocks/slide_block.py +25 -0
- ymprint-0.1.0/src/ymprint/blocks/spacer_block.py +10 -0
- ymprint-0.1.0/src/ymprint/cli/config.py +15 -0
- ymprint-0.1.0/src/ymprint/cli/main.py +144 -0
- ymprint-0.1.0/src/ymprint/cli/throbber.py +176 -0
- ymprint-0.1.0/src/ymprint/config/__init__.py +3 -0
- ymprint-0.1.0/src/ymprint/config/check.ipynb +213 -0
- ymprint-0.1.0/src/ymprint/config/config_loaders.py +126 -0
- ymprint-0.1.0/src/ymprint/config/defaults/defaults.ymprint.yml +68 -0
- ymprint-0.1.0/src/ymprint/config/docstyles.py +115 -0
- ymprint-0.1.0/src/ymprint/config/doctablestyles.py +115 -0
- ymprint-0.1.0/src/ymprint/config/doctemplate.py +135 -0
- ymprint-0.1.0/src/ymprint/config/font_registry.py +84 -0
- ymprint-0.1.0/src/ymprint/config/fonts/AppleGaramond/AppleGaramond-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/AppleGaramond/AppleGaramond-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/AppleGaramond/AppleGaramond-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/AppleGaramond/AppleGaramond.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSans/DejaVuSans-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSans/DejaVuSans-BoldOblique.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSans/DejaVuSans-Oblique.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSans/DejaVuSans.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansCondensed/DejaVuSansCondensed-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansCondensed/DejaVuSansCondensed-BoldOblique.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansCondensed/DejaVuSansCondensed-Oblique.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansCondensed/DejaVuSansCondensed.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansMono/DejaVuSansMono-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansMono/DejaVuSansMono-Oblique.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSansMono/DejaVuSansMono.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerif/DejaVuSerif-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerif/DejaVuSerif-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerif/DejaVuSerif-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerif/DejaVuSerif.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerifCondensed/DejaVuSerifCondensed-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerifCondensed/DejaVuSerifCondensed-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerifCondensed/DejaVuSerifCondensed-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/DejaVuSerifCondensed/DejaVuSerifCondensed.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Inter/Inter-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Inter/Inter-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Inter/Inter-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Inter/Inter.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Montserrat/Montserrat-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Montserrat/Montserrat-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Montserrat/Montserrat.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSans/NotoSans-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSans/NotoSans-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSans/NotoSans-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSans/NotoSans.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSerif/NotoSerif-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSerif/NotoSerif-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSerif/NotoSerif-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/NotoSerif/NotoSerif.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Playfair/Playfair-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Playfair/Playfair-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Playfair/Playfair-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Playfair/Playfair.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Poppins/Poppins-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Poppins/Poppins-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Poppins/Poppins-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Poppins/Poppins.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Roboto/Roboto-Bold.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Roboto/Roboto-BoldItalic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Roboto/Roboto-Italic.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/fonts/Roboto/Roboto.ttf +0 -0
- ymprint-0.1.0/src/ymprint/config/helpers.py +36 -0
- ymprint-0.1.0/src/ymprint/config/pdf_fill_forms.py +0 -0
- ymprint-0.1.0/src/ymprint/config/pdf_postprocessing.py +139 -0
- ymprint-0.1.0/src/ymprint/content_checks.py +141 -0
- ymprint-0.1.0/src/ymprint/content_converters.py +107 -0
- ymprint-0.1.0/src/ymprint/context_builder.py +70 -0
- ymprint-0.1.0/src/ymprint/exceptions.py +2 -0
- ymprint-0.1.0/src/ymprint/markdown/inline.py +33 -0
- ymprint-0.1.0/src/ymprint/notes.yml +20 -0
- ymprint-0.1.0/src/ymprint/report_reader.py +84 -0
- ymprint-0.1.0/src/ymprint/story_builder.py +72 -0
- ymprint-0.1.0/src/ymprint/yaml_loader.py +13 -0
ymprint-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ymprint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Desktop publishing in YAML with Python
|
|
5
|
+
Author: Connor Ferster
|
|
6
|
+
Author-email: Connor Ferster <connor@structuralpython.com>
|
|
7
|
+
Requires-Dist: jinja2>=3.1.6
|
|
8
|
+
Requires-Dist: pydantic>=2.13.4
|
|
9
|
+
Requires-Dist: pygments>=2.20.0
|
|
10
|
+
Requires-Dist: pymupdf>=1.27.2.3
|
|
11
|
+
Requires-Dist: pypdf>=6.14.0
|
|
12
|
+
Requires-Dist: reportlab>=4.5.1
|
|
13
|
+
Requires-Dist: ruamel-yaml>=0.19.1
|
|
14
|
+
Requires-Dist: typer>=0.26.7
|
|
15
|
+
Requires-Dist: wenmode>=0.8.0
|
|
16
|
+
Requires-Python: >=3.14
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="ymprintlogolarge.png" alt="YMPrint" width="420">
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<h3 align="center">Desktop publishing with YAML</h3>
|
|
24
|
+
<p align="center"><em>The technology of the year 2000…today!</em></p>
|
|
25
|
+
|
|
26
|
+
YMPrint is a Python-based PDF authoring application geared towards professionals who need to
|
|
27
|
+
generate lots of PDF documents. You write the content of your report in YAML (as opposed to
|
|
28
|
+
Markdown) and YMPrint renders it to PDF with near-instant speeds. YMPrint lets you use Python
|
|
29
|
+
scripting within your document, create variables, render variable values, and pass live
|
|
30
|
+
Python objects between report **blocks** to create a truly expressive authoring
|
|
31
|
+
system—the likes of which have not been created before.
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
Site inspection report:
|
|
35
|
+
- >
|
|
36
|
+
This is the first paragraph. The `>` character tells YAML you are entering a
|
|
37
|
+
multi-line string that should be word-wrapped. Leave a blank line to start a
|
|
38
|
+
new paragraph.
|
|
39
|
+
- Findings:
|
|
40
|
+
- Bullets:
|
|
41
|
+
- The handrail is loose on the north stair.
|
|
42
|
+
- Two ceiling tiles are water-stained in the lobby.
|
|
43
|
+
- Items observed:
|
|
44
|
+
- Item Number: 12.01
|
|
45
|
+
Description: There is a problem here. This report documents it.
|
|
46
|
+
Location: Under the stairs
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ym convert report.yml
|
|
51
|
+
# ✍️ .... 📝 ... PDF created: report.pdf
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Why YMPrint?
|
|
55
|
+
|
|
56
|
+
- **Readable source.** Your document *is* the outline. YAML nesting is the document
|
|
57
|
+
hierarchy — no markup soup, no LaTeX, no HTML.
|
|
58
|
+
- **Batteries included.** Bundled fonts, sensible default styles, and a set of blocks for
|
|
59
|
+
the content markdown can't express.
|
|
60
|
+
- **Dynamic content.** Interpolate variables with Jinja, execute `_py` blocks, load JSON,
|
|
61
|
+
embed matplotlib figures, and auto-fill PDF form fields.
|
|
62
|
+
- **Custom templates.** Overlay your document onto a designed PDF background and
|
|
63
|
+
auto-populate its form fields from document variables.
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
Requires **Python 3.14+**.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# With uv (recommended) — installs the `ym` command
|
|
71
|
+
uv tool install ymprint
|
|
72
|
+
|
|
73
|
+
# Or with pip
|
|
74
|
+
pip install ymprint
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
From source:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone https://github.com/StructuralPython/yamlreports.git
|
|
81
|
+
cd yamlreports
|
|
82
|
+
uv sync
|
|
83
|
+
uv run ym --help
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
YMPrint installs a single command, `ym`, with two subcommands.
|
|
89
|
+
|
|
90
|
+
### `ym convert` — render once
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
ym convert report.yml # writes report.pdf next to the source
|
|
94
|
+
ym convert report.yml out/doc.pdf # choose an output path
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `ym live` — hot-reload preview
|
|
98
|
+
|
|
99
|
+
Renders the PDF, opens it in the [Okular](https://okular.kde.org/) viewer, and rebuilds on
|
|
100
|
+
every save. Great for drafting.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
ym live report.yml
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Core concepts
|
|
107
|
+
|
|
108
|
+
- **Keys are headings.** A mapping key whose value is content becomes a heading; the value
|
|
109
|
+
is laid out underneath it. Lists render in order, lists of mappings render as tables.
|
|
110
|
+
- **Configuration** lives in underscore-prefixed front matter — `_doc` (page template),
|
|
111
|
+
`_style` (text styles), and `_tablestyle` (table styles) — resolved across three
|
|
112
|
+
inheriting priority levels: internal defaults → project config → document front matter.
|
|
113
|
+
- **Variables** are defined in `_vars`, interpolated into text with Jinja (`{{name}}`), and
|
|
114
|
+
passed as real Python objects into blocks with the `$name` syntax.
|
|
115
|
+
- **Blocks** are underscore-prefixed keys that expand into custom content:
|
|
116
|
+
|
|
117
|
+
| Block | Purpose |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `_img` | Embed an image with a caption |
|
|
120
|
+
| `_matplotfig` | Embed a matplotlib figure |
|
|
121
|
+
| `_info` / `_warning` / `_danger` / `_tip` / `_note` | Admonition callouts |
|
|
122
|
+
| `_blockquote` | A quotation with attribution |
|
|
123
|
+
| `_code` | A non-executable, syntax-highlighted code block |
|
|
124
|
+
| `_py` | Execute Python and optionally show the source |
|
|
125
|
+
| `_loadjson` | Load variables from a JSON file |
|
|
126
|
+
| `_pagebreak` | Force a page break |
|
|
127
|
+
| `_hrule` | A configurable horizontal rule |
|
|
128
|
+
| `_spacer` | Insert vertical whitespace |
|
|
129
|
+
|
|
130
|
+
## Examples
|
|
131
|
+
|
|
132
|
+
The [`Examples/`](Examples) directory contains a runnable report for each major feature —
|
|
133
|
+
a simple document, document configuration, variables, PDF backgrounds, Python execution,
|
|
134
|
+
and the full set of blocks. Render any of them with `ym convert`.
|
|
135
|
+
|
|
136
|
+
## Documentation
|
|
137
|
+
|
|
138
|
+
The full documentation site lives in [`docs/`](docs) (Sphinx + the Shibuya theme). Build it
|
|
139
|
+
locally with:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uv run --with-requirements docs/requirements.txt \
|
|
143
|
+
sphinx-build -b html docs docs/_build/html
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Then open `docs/_build/html/index.html`. See [`docs/README.md`](docs/README.md) for details.
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
See [LICENSE](LICENSE).
|
ymprint-0.1.0/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="ymprintlogolarge.png" alt="YMPrint" width="420">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h3 align="center">Desktop publishing with YAML</h3>
|
|
6
|
+
<p align="center"><em>The technology of the year 2000…today!</em></p>
|
|
7
|
+
|
|
8
|
+
YMPrint is a Python-based PDF authoring application geared towards professionals who need to
|
|
9
|
+
generate lots of PDF documents. You write the content of your report in YAML (as opposed to
|
|
10
|
+
Markdown) and YMPrint renders it to PDF with near-instant speeds. YMPrint lets you use Python
|
|
11
|
+
scripting within your document, create variables, render variable values, and pass live
|
|
12
|
+
Python objects between report **blocks** to create a truly expressive authoring
|
|
13
|
+
system—the likes of which have not been created before.
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
Site inspection report:
|
|
17
|
+
- >
|
|
18
|
+
This is the first paragraph. The `>` character tells YAML you are entering a
|
|
19
|
+
multi-line string that should be word-wrapped. Leave a blank line to start a
|
|
20
|
+
new paragraph.
|
|
21
|
+
- Findings:
|
|
22
|
+
- Bullets:
|
|
23
|
+
- The handrail is loose on the north stair.
|
|
24
|
+
- Two ceiling tiles are water-stained in the lobby.
|
|
25
|
+
- Items observed:
|
|
26
|
+
- Item Number: 12.01
|
|
27
|
+
Description: There is a problem here. This report documents it.
|
|
28
|
+
Location: Under the stairs
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ym convert report.yml
|
|
33
|
+
# ✍️ .... 📝 ... PDF created: report.pdf
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Why YMPrint?
|
|
37
|
+
|
|
38
|
+
- **Readable source.** Your document *is* the outline. YAML nesting is the document
|
|
39
|
+
hierarchy — no markup soup, no LaTeX, no HTML.
|
|
40
|
+
- **Batteries included.** Bundled fonts, sensible default styles, and a set of blocks for
|
|
41
|
+
the content markdown can't express.
|
|
42
|
+
- **Dynamic content.** Interpolate variables with Jinja, execute `_py` blocks, load JSON,
|
|
43
|
+
embed matplotlib figures, and auto-fill PDF form fields.
|
|
44
|
+
- **Custom templates.** Overlay your document onto a designed PDF background and
|
|
45
|
+
auto-populate its form fields from document variables.
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Requires **Python 3.14+**.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# With uv (recommended) — installs the `ym` command
|
|
53
|
+
uv tool install ymprint
|
|
54
|
+
|
|
55
|
+
# Or with pip
|
|
56
|
+
pip install ymprint
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
From source:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/StructuralPython/yamlreports.git
|
|
63
|
+
cd yamlreports
|
|
64
|
+
uv sync
|
|
65
|
+
uv run ym --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
YMPrint installs a single command, `ym`, with two subcommands.
|
|
71
|
+
|
|
72
|
+
### `ym convert` — render once
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
ym convert report.yml # writes report.pdf next to the source
|
|
76
|
+
ym convert report.yml out/doc.pdf # choose an output path
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `ym live` — hot-reload preview
|
|
80
|
+
|
|
81
|
+
Renders the PDF, opens it in the [Okular](https://okular.kde.org/) viewer, and rebuilds on
|
|
82
|
+
every save. Great for drafting.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
ym live report.yml
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Core concepts
|
|
89
|
+
|
|
90
|
+
- **Keys are headings.** A mapping key whose value is content becomes a heading; the value
|
|
91
|
+
is laid out underneath it. Lists render in order, lists of mappings render as tables.
|
|
92
|
+
- **Configuration** lives in underscore-prefixed front matter — `_doc` (page template),
|
|
93
|
+
`_style` (text styles), and `_tablestyle` (table styles) — resolved across three
|
|
94
|
+
inheriting priority levels: internal defaults → project config → document front matter.
|
|
95
|
+
- **Variables** are defined in `_vars`, interpolated into text with Jinja (`{{name}}`), and
|
|
96
|
+
passed as real Python objects into blocks with the `$name` syntax.
|
|
97
|
+
- **Blocks** are underscore-prefixed keys that expand into custom content:
|
|
98
|
+
|
|
99
|
+
| Block | Purpose |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `_img` | Embed an image with a caption |
|
|
102
|
+
| `_matplotfig` | Embed a matplotlib figure |
|
|
103
|
+
| `_info` / `_warning` / `_danger` / `_tip` / `_note` | Admonition callouts |
|
|
104
|
+
| `_blockquote` | A quotation with attribution |
|
|
105
|
+
| `_code` | A non-executable, syntax-highlighted code block |
|
|
106
|
+
| `_py` | Execute Python and optionally show the source |
|
|
107
|
+
| `_loadjson` | Load variables from a JSON file |
|
|
108
|
+
| `_pagebreak` | Force a page break |
|
|
109
|
+
| `_hrule` | A configurable horizontal rule |
|
|
110
|
+
| `_spacer` | Insert vertical whitespace |
|
|
111
|
+
|
|
112
|
+
## Examples
|
|
113
|
+
|
|
114
|
+
The [`Examples/`](Examples) directory contains a runnable report for each major feature —
|
|
115
|
+
a simple document, document configuration, variables, PDF backgrounds, Python execution,
|
|
116
|
+
and the full set of blocks. Render any of them with `ym convert`.
|
|
117
|
+
|
|
118
|
+
## Documentation
|
|
119
|
+
|
|
120
|
+
The full documentation site lives in [`docs/`](docs) (Sphinx + the Shibuya theme). Build it
|
|
121
|
+
locally with:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
uv run --with-requirements docs/requirements.txt \
|
|
125
|
+
sphinx-build -b html docs docs/_build/html
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Then open `docs/_build/html/index.html`. See [`docs/README.md`](docs/README.md) for details.
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ymprint"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Desktop publishing in YAML with Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Connor Ferster", email = "connor@structuralpython.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.14"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"jinja2>=3.1.6",
|
|
12
|
+
"pydantic>=2.13.4",
|
|
13
|
+
"pygments>=2.20.0",
|
|
14
|
+
"pymupdf>=1.27.2.3",
|
|
15
|
+
"pypdf>=6.14.0",
|
|
16
|
+
"reportlab>=4.5.1",
|
|
17
|
+
"ruamel-yaml>=0.19.1",
|
|
18
|
+
"typer>=0.26.7",
|
|
19
|
+
"wenmode>=0.8.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
ym = "ymprint.cli.main:app"
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["uv_build>=0.11.16,<0.12.0"]
|
|
27
|
+
build-backend = "uv_build"
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
dev = [
|
|
31
|
+
"matplotlib>=3.11.0",
|
|
32
|
+
"pytest>=9.0.3",
|
|
33
|
+
"rich>=15.0.0",
|
|
34
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from typing import Callable, Optional, Union, TypeAlias, Any
|
|
4
|
+
|
|
5
|
+
from reportlab.platypus import (
|
|
6
|
+
Paragraph,
|
|
7
|
+
Spacer,
|
|
8
|
+
Table,
|
|
9
|
+
Image,
|
|
10
|
+
HRFlowable,
|
|
11
|
+
KeepTogether,
|
|
12
|
+
)
|
|
13
|
+
from reportlab.lib.units import mm
|
|
14
|
+
from ymprint.config.docstyles import ReportStyles
|
|
15
|
+
from ..content_checks import check_for_variable
|
|
16
|
+
|
|
17
|
+
RLFlowables: TypeAlias = Union[Paragraph, Spacer, Table, KeepTogether, Image]
|
|
18
|
+
|
|
19
|
+
YAML_Values: TypeAlias =Union[str, list, dict, float, int, None]
|
|
20
|
+
|
|
21
|
+
# TODO: Create a block registration function and a singleton block registry
|
|
22
|
+
|
|
23
|
+
class BlockExistsError(Exception):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def create_block_registry() -> tuple[Callable, Callable, Callable]:
|
|
27
|
+
"""
|
|
28
|
+
Creates the block registry
|
|
29
|
+
"""
|
|
30
|
+
_BLOCK_REGISTRY = {}
|
|
31
|
+
|
|
32
|
+
def register_block(block_code: str, block_convert: Callable) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Returns None. Adds a new block to the block registry.
|
|
35
|
+
|
|
36
|
+
'block_code': a str of the form '_{code}' where 'code' is an alphanumeric code used
|
|
37
|
+
to identify the block.
|
|
38
|
+
'block_convert':a function with the following signature:
|
|
39
|
+
my_func(obj: dict, context: dict) -> list[Flowable]
|
|
40
|
+
|
|
41
|
+
Where:
|
|
42
|
+
obj: a dict which has a key that starts with the block code and a value
|
|
43
|
+
which is a user-defined data structure that contains the data needed
|
|
44
|
+
to generate the block as a ReportLab Flowable.
|
|
45
|
+
context: a dict that contains all of the internal state of this program
|
|
46
|
+
at the time the conversion is executed. This will get passed to your
|
|
47
|
+
function automatically and gives your conversion function access to
|
|
48
|
+
anything and everything it needs to render your custom block.
|
|
49
|
+
Feel free to explore the context dict by using a print(context) call
|
|
50
|
+
in your function.
|
|
51
|
+
|
|
52
|
+
Return:
|
|
53
|
+
A list of ReportLab Flowable from the reportlab.platypus module. Your custom
|
|
54
|
+
block may be just one flowable (like a custom-populated table) or it can
|
|
55
|
+
be a list of many flowable.
|
|
56
|
+
"""
|
|
57
|
+
_BLOCK_REGISTRY.update({block_code: block_convert})
|
|
58
|
+
|
|
59
|
+
def list_blocks():
|
|
60
|
+
return list(_BLOCK_REGISTRY.keys())
|
|
61
|
+
|
|
62
|
+
def get_block_callable(block_code: str) -> Optional[Callable]:
|
|
63
|
+
return _BLOCK_REGISTRY.get(block_code)
|
|
64
|
+
|
|
65
|
+
return list_blocks, get_block_callable, register_block
|
|
66
|
+
|
|
67
|
+
list_blocks, get_block_callable, register_block = create_block_registry()
|
|
68
|
+
|
|
69
|
+
def convert_blocks(block_key: str, block_value: YAML_Values, context: dict) -> list[RLFlowables]:
|
|
70
|
+
block_code_pattern = re.compile(r"(^_[a-zA-Z0-9]+)")
|
|
71
|
+
matches = block_code_pattern.match(block_key)
|
|
72
|
+
if matches is not None:
|
|
73
|
+
block_code = matches.groups()[0]
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError(f"Block code not found within block key: {block_key=}")
|
|
76
|
+
block_converter = get_block_callable(block_code)
|
|
77
|
+
block_value_w_python_objects = retrieve_block_variables(block_value, context)
|
|
78
|
+
flowables = block_converter(block_key, block_value_w_python_objects, context)
|
|
79
|
+
return flowables
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def retrieve_block_variables(block_value: YAML_Values, context: dict) -> YAML_Values:
|
|
83
|
+
"""
|
|
84
|
+
Returns 'block_value' but with list values or dictionary values that have
|
|
85
|
+
the "$VAR" syntax substituted with the object values
|
|
86
|
+
"""
|
|
87
|
+
if isinstance(block_value, str) and check_for_variable(block_value, context):
|
|
88
|
+
var_name = get_variable_name(block_value)
|
|
89
|
+
return context['vars'].get(var_name, block_value)
|
|
90
|
+
elif isinstance(block_value, list):
|
|
91
|
+
acc = []
|
|
92
|
+
for elem in block_value:
|
|
93
|
+
new_elem = retrieve_block_variables(elem, context)
|
|
94
|
+
acc.append(new_elem)
|
|
95
|
+
return acc
|
|
96
|
+
elif isinstance(block_value, dict):
|
|
97
|
+
acc = {}
|
|
98
|
+
for k, v in block_value.items():
|
|
99
|
+
new_v = retrieve_block_variables(v, context)
|
|
100
|
+
acc.update({k: new_v})
|
|
101
|
+
return acc
|
|
102
|
+
else:
|
|
103
|
+
return block_value
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_variable_name(var_string: str) -> str:
|
|
107
|
+
return var_string.lstrip('$')
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from reportlab.platypus import Table, Paragraph, Spacer, KeepTogether
|
|
2
|
+
from . import register_block
|
|
3
|
+
from typing import Callable, Any
|
|
4
|
+
from . import blockstyles
|
|
5
|
+
|
|
6
|
+
def generate_admonition_block(kind: str) -> Callable:
|
|
7
|
+
"""
|
|
8
|
+
Returns a callable to render that particular admonition type
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def convert_admonition_block(block_key: str, block_value: Any, context: dict) -> list[KeepTogether | Spacer]:
|
|
13
|
+
# Need an admonition block style or style modification
|
|
14
|
+
available_width = context['frames']['all_pages']['width']
|
|
15
|
+
text_spacing = context['styles']['ymprint'].body.spacing
|
|
16
|
+
text_size = context['styles']['ymprint'].body.size
|
|
17
|
+
space_around = text_spacing * text_size
|
|
18
|
+
width_ratio = 0.8
|
|
19
|
+
block_width = width_ratio * available_width
|
|
20
|
+
value = block_value
|
|
21
|
+
tablestyle = blockstyles.get_table_style(kind)
|
|
22
|
+
body_textstyle = blockstyles.get_text_styles().get(f'admonition_{kind}_body')
|
|
23
|
+
title_textstyle = blockstyles.get_text_styles().get(f'admonition_{kind}_title')
|
|
24
|
+
notice = Paragraph(text=blockstyles.admonition_title_text(kind), style=title_textstyle)
|
|
25
|
+
content = Paragraph(text=value, style=body_textstyle)
|
|
26
|
+
table = Table(
|
|
27
|
+
data=[[notice], [content]],
|
|
28
|
+
colWidths=[block_width],
|
|
29
|
+
style=tablestyle,
|
|
30
|
+
spaceBefore=space_around,
|
|
31
|
+
spaceAfter=space_around
|
|
32
|
+
)
|
|
33
|
+
return [KeepTogether(table), Spacer(1, 10)]
|
|
34
|
+
|
|
35
|
+
return convert_admonition_block
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
register_block("_info", generate_admonition_block("info"))
|
|
39
|
+
register_block("_warning", generate_admonition_block("warning"))
|
|
40
|
+
register_block("_danger", generate_admonition_block("danger"))
|
|
41
|
+
register_block("_tip", generate_admonition_block("tip"))
|
|
42
|
+
register_block("_note", generate_admonition_block("note"))
|