pytex-preprocessor 0.1.3__tar.gz → 0.2.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.
- pytex_preprocessor-0.2.0/PKG-INFO +170 -0
- pytex_preprocessor-0.2.0/README.md +156 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/pyproject.toml +1 -1
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/builtin.py +16 -13
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/definitions.py +47 -8
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/glossaries.py +4 -4
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/boxes.py +3 -3
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/titlepage.py +4 -3
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/voting.py +2 -2
- pytex_preprocessor-0.2.0/src/pytex_preprocessor.egg-info/PKG-INFO +170 -0
- pytex_preprocessor-0.1.3/PKG-INFO +0 -82
- pytex_preprocessor-0.1.3/README.md +0 -68
- pytex_preprocessor-0.1.3/src/pytex_preprocessor.egg-info/PKG-INFO +0 -82
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/setup.cfg +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/biblatex.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/captions.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/cleveref.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/colors.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/conditionals.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/counters.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/floats.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/font.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/fontawesome.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/fontspec.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/geometry.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/graphics.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/hooks.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/hyperref.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/lengths.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/listings.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/mdframed.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/picture.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/setspace.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/commands/tables.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/helpers/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/helpers/coerce.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/helpers/parenting.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/helpers/sanitize.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/helpers/with_package.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/interface/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/interface/control_sequence.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/interface/package.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/interface/tex.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/color.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/concat.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/control_sequence.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/document.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/document_class.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/empty.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/environment.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/image.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/include.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/length.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/math.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/package.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/model/raw.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/packages.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex/registry.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_builder/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_builder/build.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_builder/console.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_builder/render.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_builder/tectonic.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/ASTA.svg +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/ECHO.svg +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/INF.pdf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/citations.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/cleveref_names.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/colors.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/document.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/fonts.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/glossary.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/hyperref_config.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/listings.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/logos.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/pagebreak.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/pagesetup.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/tex/pagesetup.tex +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/variants.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/watermark.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_hsrtreport/wordcount.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_koma/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_koma/commands.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_koma/document.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_markdown/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_markdown/convert.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_markdown/escape.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_preprocessor.egg-info/SOURCES.txt +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_preprocessor.egg-info/dependency_links.txt +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_preprocessor.egg-info/entry_points.txt +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_preprocessor.egg-info/requires.txt +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_preprocessor.egg-info/top_level.txt +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/convert.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/document.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/entries.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/frontmatter.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/header.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/shortcodes.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_protocol/signatures.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_tikz/__init__.py +0 -0
- {pytex_preprocessor-0.1.3 → pytex_preprocessor-0.2.0}/src/pytex_tikz/tikz.py +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytex-preprocessor
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Type-safe LaTeX document generation with Python
|
|
5
|
+
Author-email: Frederik Beimgraben <frederik@beimgraben.net>
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pydantic
|
|
9
|
+
Requires-Dist: marko
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest; extra == "dev"
|
|
12
|
+
Requires-Dist: ruff; extra == "dev"
|
|
13
|
+
Requires-Dist: basedpyright; extra == "dev"
|
|
14
|
+
|
|
15
|
+
# PyTeX
|
|
16
|
+
|
|
17
|
+
Type-safe LaTeX document generation with Python. Build a document as a tree of
|
|
18
|
+
typed `TeX` nodes and render it to a `.tex` file, or drop inline Python
|
|
19
|
+
expressions into an existing `.tex` source and have them evaluated at render
|
|
20
|
+
time. Requires Python 3.13+.
|
|
21
|
+
|
|
22
|
+
A `TeX` node is an immutable dataclass with a `.rendered` property. The public
|
|
23
|
+
API mirrors LaTeX control sequences as PascalCase factories (`Section`,
|
|
24
|
+
`Bold`, `Frac`, `Title`, ...), so a document reads like the LaTeX it produces
|
|
25
|
+
while staying checkable by a type checker. Nodes track their package
|
|
26
|
+
requirements, so the preamble is assembled automatically from what the body
|
|
27
|
+
uses.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
To use the `pytex` command anywhere, install it as an isolated tool with
|
|
32
|
+
[pipx](https://pipx.pypa.io/):
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pipx install pytex-preprocessor
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It is also available via plain `pip install pytex-preprocessor`.
|
|
39
|
+
|
|
40
|
+
For development, work in a virtualenv with an editable install instead:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
python -m venv venv && . venv/bin/activate
|
|
44
|
+
pip install -e . # add [dev] for pytest, ruff, basedpyright
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
External tools, each needed only for the matching feature:
|
|
48
|
+
|
|
49
|
+
- `tectonic` — compile to PDF (`--build`). If not on `PATH`, the build
|
|
50
|
+
downloads a self-contained binary into a temp folder and reuses it.
|
|
51
|
+
- `inkscape` — `SVG` image conversion.
|
|
52
|
+
- `makeindex` (from a TeX distribution, e.g. TeX Live) — resolve
|
|
53
|
+
glossaries/acronyms.
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
A `.tex.py` file is plain Python exposing a module-level `__pytex__` that holds
|
|
58
|
+
a `TeX` node:
|
|
59
|
+
|
|
60
|
+
```py
|
|
61
|
+
from pytex.commands.builtin import Bold, Emph, Section, Title, MakeTitle
|
|
62
|
+
from pytex.model.concat import Concat
|
|
63
|
+
from pytex.model.document import Document
|
|
64
|
+
from pytex.model.math import DisplayMath, Frac
|
|
65
|
+
|
|
66
|
+
__pytex__ = Document(
|
|
67
|
+
preamble=Title("PyTeX Example"),
|
|
68
|
+
body=Concat(
|
|
69
|
+
MakeTitle(),
|
|
70
|
+
Section("Text"),
|
|
71
|
+
"A paragraph with ", Bold("bold"), " and ", Emph("emphasised"), " words.",
|
|
72
|
+
Section("Math"),
|
|
73
|
+
DisplayMath(Concat("x = ", Frac("-b", "2a"))),
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
pytex example.tex.py # render -> build/example.out.tex
|
|
80
|
+
pytex example.tex.py --build # render + compile -> build/example.out.pdf
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Bare strings are coerced to text nodes and LaTeX-escaped.
|
|
84
|
+
|
|
85
|
+
## The `pytex` command
|
|
86
|
+
|
|
87
|
+
The input file is dispatched by extension:
|
|
88
|
+
|
|
89
|
+
| Extension | Handling |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `.py` | imported as a module; its `__pytex__` node is rendered. Convention: name it `<doc>.tex.py`. |
|
|
92
|
+
| `.tex` | wrapped in `IncludeTeX`; inline `\iffalse{pytex(...)}\fi` markers are evaluated, then rendered. Convention: `<doc>.py.tex`. |
|
|
93
|
+
| `.md` / `.markdown` | converted to nodes via `IncludeMarkdown`. Frontmatter with `gremium:` or `typ: protokoll` routes to the protocol renderer instead. |
|
|
94
|
+
|
|
95
|
+
### Inline replacements in `.tex`
|
|
96
|
+
|
|
97
|
+
Any registered factory is in scope inside a marker. The `\iffalse ... \fi` pair
|
|
98
|
+
is a LaTeX no-op, so the source still compiles as-is without PyTeX:
|
|
99
|
+
|
|
100
|
+
```tex
|
|
101
|
+
Today is \iffalse{pytex(Today())}\fi.
|
|
102
|
+
A fraction: $\iffalse{pytex(Frac("1", "2"))}\fi$.
|
|
103
|
+
Plain Python works too: $3^2 = \iffalse{pytex(3 ** 2)}\fi$.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Options
|
|
107
|
+
|
|
108
|
+
| Flag | Default | Meaning |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `-o`, `--output` | `<build-dir>/<input>.out.tex` | rendered LaTeX output path |
|
|
111
|
+
| `-b`, `--build` | off | compile the rendered `.tex` to PDF with tectonic |
|
|
112
|
+
| `--build-dir DIR` | `build` | directory for artifacts and tectonic output |
|
|
113
|
+
| `--no-shell-escape` | shell-escape on | disable shell-escape |
|
|
114
|
+
|
|
115
|
+
Shell-escape is on by default because inline images decode their base64
|
|
116
|
+
payloads at compile time. The build runs tectonic, then `makeindex` (for
|
|
117
|
+
`glossaries`/acronyms), then reruns tectonic when an index changed.
|
|
118
|
+
|
|
119
|
+
Output is minimal and color-tagged (`==>`, `note:`, `warning:`, `error:`),
|
|
120
|
+
following tectonic's style; on failure it points at the likely cause and the
|
|
121
|
+
log file. Set `NO_COLOR` to disable color.
|
|
122
|
+
|
|
123
|
+
## Packages
|
|
124
|
+
|
|
125
|
+
`pytex` is the core; the rest are optional and build on it.
|
|
126
|
+
|
|
127
|
+
| Package | Provides |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `pytex` | core node model, `Document`, math, tables, graphics, and factories for the common LaTeX packages (biblatex, cleveref, glossaries, hyperref, listings, ...). |
|
|
130
|
+
| `pytex_koma` | KOMA-Script classes and commands (`Addchap`, `Minisec`, `KOMAoptions`, ...). |
|
|
131
|
+
| `pytex_tikz` | TikZ pictures and primitives (`TikzPicture`, `Draw`, `Node`, `Circle`, ...). |
|
|
132
|
+
| `pytex_markdown` | Markdown -> native `TeX` conversion (see below). |
|
|
133
|
+
| `pytex_hsrtreport` | HSRT report document class, colored callout boxes, title pages, glossary/citation helpers. |
|
|
134
|
+
| `pytex_protocol` | STUPA/AStA meeting minutes from Markdown, built on `pytex_hsrtreport`. |
|
|
135
|
+
|
|
136
|
+
## Markdown
|
|
137
|
+
|
|
138
|
+
`pytex_markdown` converts Markdown to native `TeX` nodes (via `marko`):
|
|
139
|
+
|
|
140
|
+
```py
|
|
141
|
+
from pytex_markdown import Markdown, IncludeMarkdown
|
|
142
|
+
|
|
143
|
+
body = Markdown("# Title\n\nText with **bold**, `code`, [a link](https://x).")
|
|
144
|
+
body = IncludeMarkdown("notes.md", base_level=-1) # base_level=-1: # -> \chapter
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Headings, emphasis, inline/fenced code, lists, links, images, block quotes and
|
|
148
|
+
thematic breaks map to the standard pytex library; text is LaTeX-escaped.
|
|
149
|
+
GitHub-style callouts become HSRT colored boxes (so the module depends on
|
|
150
|
+
`pytex_hsrtreport`):
|
|
151
|
+
|
|
152
|
+
```md
|
|
153
|
+
> [!NOTE] -> InfoBox > [!IMPORTANT] -> ImportantBox
|
|
154
|
+
> [!TIP] -> SuccessBox > [!WARNING] -> WarningBox
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Both factories are registered, so they work in `\iffalse{pytex(...)}\fi`
|
|
158
|
+
replacements in `.tex` sources too.
|
|
159
|
+
|
|
160
|
+
## Examples
|
|
161
|
+
|
|
162
|
+
See `examples/` for one minimal input per kind (`.tex.py`, `.py.tex`, `.md`,
|
|
163
|
+
mixed, and a full HSRT report). Run from the repository root so relative paths
|
|
164
|
+
resolve:
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
pytex examples/document.tex.py --build
|
|
168
|
+
pytex examples/replacements.py.tex --build
|
|
169
|
+
pytex examples/notes.md --build
|
|
170
|
+
```
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# PyTeX
|
|
2
|
+
|
|
3
|
+
Type-safe LaTeX document generation with Python. Build a document as a tree of
|
|
4
|
+
typed `TeX` nodes and render it to a `.tex` file, or drop inline Python
|
|
5
|
+
expressions into an existing `.tex` source and have them evaluated at render
|
|
6
|
+
time. Requires Python 3.13+.
|
|
7
|
+
|
|
8
|
+
A `TeX` node is an immutable dataclass with a `.rendered` property. The public
|
|
9
|
+
API mirrors LaTeX control sequences as PascalCase factories (`Section`,
|
|
10
|
+
`Bold`, `Frac`, `Title`, ...), so a document reads like the LaTeX it produces
|
|
11
|
+
while staying checkable by a type checker. Nodes track their package
|
|
12
|
+
requirements, so the preamble is assembled automatically from what the body
|
|
13
|
+
uses.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
To use the `pytex` command anywhere, install it as an isolated tool with
|
|
18
|
+
[pipx](https://pipx.pypa.io/):
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pipx install pytex-preprocessor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
It is also available via plain `pip install pytex-preprocessor`.
|
|
25
|
+
|
|
26
|
+
For development, work in a virtualenv with an editable install instead:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
python -m venv venv && . venv/bin/activate
|
|
30
|
+
pip install -e . # add [dev] for pytest, ruff, basedpyright
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
External tools, each needed only for the matching feature:
|
|
34
|
+
|
|
35
|
+
- `tectonic` — compile to PDF (`--build`). If not on `PATH`, the build
|
|
36
|
+
downloads a self-contained binary into a temp folder and reuses it.
|
|
37
|
+
- `inkscape` — `SVG` image conversion.
|
|
38
|
+
- `makeindex` (from a TeX distribution, e.g. TeX Live) — resolve
|
|
39
|
+
glossaries/acronyms.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
A `.tex.py` file is plain Python exposing a module-level `__pytex__` that holds
|
|
44
|
+
a `TeX` node:
|
|
45
|
+
|
|
46
|
+
```py
|
|
47
|
+
from pytex.commands.builtin import Bold, Emph, Section, Title, MakeTitle
|
|
48
|
+
from pytex.model.concat import Concat
|
|
49
|
+
from pytex.model.document import Document
|
|
50
|
+
from pytex.model.math import DisplayMath, Frac
|
|
51
|
+
|
|
52
|
+
__pytex__ = Document(
|
|
53
|
+
preamble=Title("PyTeX Example"),
|
|
54
|
+
body=Concat(
|
|
55
|
+
MakeTitle(),
|
|
56
|
+
Section("Text"),
|
|
57
|
+
"A paragraph with ", Bold("bold"), " and ", Emph("emphasised"), " words.",
|
|
58
|
+
Section("Math"),
|
|
59
|
+
DisplayMath(Concat("x = ", Frac("-b", "2a"))),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
pytex example.tex.py # render -> build/example.out.tex
|
|
66
|
+
pytex example.tex.py --build # render + compile -> build/example.out.pdf
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Bare strings are coerced to text nodes and LaTeX-escaped.
|
|
70
|
+
|
|
71
|
+
## The `pytex` command
|
|
72
|
+
|
|
73
|
+
The input file is dispatched by extension:
|
|
74
|
+
|
|
75
|
+
| Extension | Handling |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `.py` | imported as a module; its `__pytex__` node is rendered. Convention: name it `<doc>.tex.py`. |
|
|
78
|
+
| `.tex` | wrapped in `IncludeTeX`; inline `\iffalse{pytex(...)}\fi` markers are evaluated, then rendered. Convention: `<doc>.py.tex`. |
|
|
79
|
+
| `.md` / `.markdown` | converted to nodes via `IncludeMarkdown`. Frontmatter with `gremium:` or `typ: protokoll` routes to the protocol renderer instead. |
|
|
80
|
+
|
|
81
|
+
### Inline replacements in `.tex`
|
|
82
|
+
|
|
83
|
+
Any registered factory is in scope inside a marker. The `\iffalse ... \fi` pair
|
|
84
|
+
is a LaTeX no-op, so the source still compiles as-is without PyTeX:
|
|
85
|
+
|
|
86
|
+
```tex
|
|
87
|
+
Today is \iffalse{pytex(Today())}\fi.
|
|
88
|
+
A fraction: $\iffalse{pytex(Frac("1", "2"))}\fi$.
|
|
89
|
+
Plain Python works too: $3^2 = \iffalse{pytex(3 ** 2)}\fi$.
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Options
|
|
93
|
+
|
|
94
|
+
| Flag | Default | Meaning |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `-o`, `--output` | `<build-dir>/<input>.out.tex` | rendered LaTeX output path |
|
|
97
|
+
| `-b`, `--build` | off | compile the rendered `.tex` to PDF with tectonic |
|
|
98
|
+
| `--build-dir DIR` | `build` | directory for artifacts and tectonic output |
|
|
99
|
+
| `--no-shell-escape` | shell-escape on | disable shell-escape |
|
|
100
|
+
|
|
101
|
+
Shell-escape is on by default because inline images decode their base64
|
|
102
|
+
payloads at compile time. The build runs tectonic, then `makeindex` (for
|
|
103
|
+
`glossaries`/acronyms), then reruns tectonic when an index changed.
|
|
104
|
+
|
|
105
|
+
Output is minimal and color-tagged (`==>`, `note:`, `warning:`, `error:`),
|
|
106
|
+
following tectonic's style; on failure it points at the likely cause and the
|
|
107
|
+
log file. Set `NO_COLOR` to disable color.
|
|
108
|
+
|
|
109
|
+
## Packages
|
|
110
|
+
|
|
111
|
+
`pytex` is the core; the rest are optional and build on it.
|
|
112
|
+
|
|
113
|
+
| Package | Provides |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `pytex` | core node model, `Document`, math, tables, graphics, and factories for the common LaTeX packages (biblatex, cleveref, glossaries, hyperref, listings, ...). |
|
|
116
|
+
| `pytex_koma` | KOMA-Script classes and commands (`Addchap`, `Minisec`, `KOMAoptions`, ...). |
|
|
117
|
+
| `pytex_tikz` | TikZ pictures and primitives (`TikzPicture`, `Draw`, `Node`, `Circle`, ...). |
|
|
118
|
+
| `pytex_markdown` | Markdown -> native `TeX` conversion (see below). |
|
|
119
|
+
| `pytex_hsrtreport` | HSRT report document class, colored callout boxes, title pages, glossary/citation helpers. |
|
|
120
|
+
| `pytex_protocol` | STUPA/AStA meeting minutes from Markdown, built on `pytex_hsrtreport`. |
|
|
121
|
+
|
|
122
|
+
## Markdown
|
|
123
|
+
|
|
124
|
+
`pytex_markdown` converts Markdown to native `TeX` nodes (via `marko`):
|
|
125
|
+
|
|
126
|
+
```py
|
|
127
|
+
from pytex_markdown import Markdown, IncludeMarkdown
|
|
128
|
+
|
|
129
|
+
body = Markdown("# Title\n\nText with **bold**, `code`, [a link](https://x).")
|
|
130
|
+
body = IncludeMarkdown("notes.md", base_level=-1) # base_level=-1: # -> \chapter
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Headings, emphasis, inline/fenced code, lists, links, images, block quotes and
|
|
134
|
+
thematic breaks map to the standard pytex library; text is LaTeX-escaped.
|
|
135
|
+
GitHub-style callouts become HSRT colored boxes (so the module depends on
|
|
136
|
+
`pytex_hsrtreport`):
|
|
137
|
+
|
|
138
|
+
```md
|
|
139
|
+
> [!NOTE] -> InfoBox > [!IMPORTANT] -> ImportantBox
|
|
140
|
+
> [!TIP] -> SuccessBox > [!WARNING] -> WarningBox
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Both factories are registered, so they work in `\iffalse{pytex(...)}\fi`
|
|
144
|
+
replacements in `.tex` sources too.
|
|
145
|
+
|
|
146
|
+
## Examples
|
|
147
|
+
|
|
148
|
+
See `examples/` for one minimal input per kind (`.tex.py`, `.py.tex`, `.md`,
|
|
149
|
+
mixed, and a full HSRT report). Run from the repository root so relative paths
|
|
150
|
+
resolve:
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
pytex examples/document.tex.py --build
|
|
154
|
+
pytex examples/replacements.py.tex --build
|
|
155
|
+
pytex examples/notes.md --build
|
|
156
|
+
```
|
|
@@ -33,12 +33,12 @@ __all__ = [
|
|
|
33
33
|
"Group",
|
|
34
34
|
"Hfill",
|
|
35
35
|
"Hspace",
|
|
36
|
+
"HspaceStar",
|
|
36
37
|
"Immediate",
|
|
37
38
|
"Include",
|
|
38
39
|
"IncludeOnly",
|
|
39
40
|
"Indent",
|
|
40
41
|
"Input",
|
|
41
|
-
"Inputfile",
|
|
42
42
|
"Italic",
|
|
43
43
|
"Item",
|
|
44
44
|
"Itemize",
|
|
@@ -93,6 +93,7 @@ __all__ = [
|
|
|
93
93
|
"Verse",
|
|
94
94
|
"Vfill",
|
|
95
95
|
"Vspace",
|
|
96
|
+
"VspaceStar",
|
|
96
97
|
"Whiledo",
|
|
97
98
|
"Write18",
|
|
98
99
|
]
|
|
@@ -264,15 +265,23 @@ def Pagebreak(n: int | None = None) -> TeX:
|
|
|
264
265
|
|
|
265
266
|
|
|
266
267
|
@Registry.add
|
|
267
|
-
def Hspace(amount: str
|
|
268
|
-
|
|
269
|
-
return ControlSequence(name, (Parameter(amount),))
|
|
268
|
+
def Hspace(amount: str) -> TeX:
|
|
269
|
+
return ControlSequence("hspace", (Parameter(amount),))
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
@Registry.add
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
def HspaceStar(amount: str) -> TeX:
|
|
274
|
+
return ControlSequence("hspace*", (Parameter(amount),))
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@Registry.add
|
|
278
|
+
def Vspace(amount: str) -> TeX:
|
|
279
|
+
return ControlSequence("vspace", (Parameter(amount),))
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@Registry.add
|
|
283
|
+
def VspaceStar(amount: str) -> TeX:
|
|
284
|
+
return ControlSequence("vspace*", (Parameter(amount),))
|
|
276
285
|
|
|
277
286
|
|
|
278
287
|
@Registry.add
|
|
@@ -552,12 +561,6 @@ def Verbatiminput(path: str) -> TeX:
|
|
|
552
561
|
return ControlSequence("verbatiminput", (Parameter(path),))
|
|
553
562
|
|
|
554
563
|
|
|
555
|
-
@Registry.add
|
|
556
|
-
def Inputfile(path: str) -> TeX:
|
|
557
|
-
"""Alias for `\\input{path}` — `Input` already defined above as Input(path)."""
|
|
558
|
-
return ControlSequence("input", (Parameter(path),))
|
|
559
|
-
|
|
560
|
-
|
|
561
564
|
@Registry.add
|
|
562
565
|
def Whiledo(condition: TeX | str, body: TeX | str) -> TeX:
|
|
563
566
|
return ControlSequence("whiledo", (Parameter(condition), Parameter(body)))
|
|
@@ -5,11 +5,15 @@ from ..registry import Registry
|
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"DeclareRobustCommand",
|
|
8
|
+
"DeclareRobustCommandStar",
|
|
8
9
|
"Def",
|
|
9
10
|
"Newcommand",
|
|
11
|
+
"NewcommandStar",
|
|
10
12
|
"Newenvironment",
|
|
11
13
|
"Providecommand",
|
|
14
|
+
"ProvidecommandStar",
|
|
12
15
|
"Renewcommand",
|
|
16
|
+
"RenewcommandStar",
|
|
13
17
|
"Renewenvironment",
|
|
14
18
|
]
|
|
15
19
|
|
|
@@ -38,9 +42,18 @@ def Newcommand(
|
|
|
38
42
|
body: TeX | str,
|
|
39
43
|
nargs: int | None = None,
|
|
40
44
|
default: str | None = None,
|
|
41
|
-
star: bool = False,
|
|
42
45
|
) -> TeX:
|
|
43
|
-
return _cmd("newcommand", cs, nargs, default, body, star)
|
|
46
|
+
return _cmd("newcommand", cs, nargs, default, body, star=False)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@Registry.add
|
|
50
|
+
def NewcommandStar(
|
|
51
|
+
cs: str,
|
|
52
|
+
body: TeX | str,
|
|
53
|
+
nargs: int | None = None,
|
|
54
|
+
default: str | None = None,
|
|
55
|
+
) -> TeX:
|
|
56
|
+
return _cmd("newcommand", cs, nargs, default, body, star=True)
|
|
44
57
|
|
|
45
58
|
|
|
46
59
|
@Registry.add
|
|
@@ -49,9 +62,18 @@ def Renewcommand(
|
|
|
49
62
|
body: TeX | str,
|
|
50
63
|
nargs: int | None = None,
|
|
51
64
|
default: str | None = None,
|
|
52
|
-
star: bool = False,
|
|
53
65
|
) -> TeX:
|
|
54
|
-
return _cmd("renewcommand", cs, nargs, default, body, star)
|
|
66
|
+
return _cmd("renewcommand", cs, nargs, default, body, star=False)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@Registry.add
|
|
70
|
+
def RenewcommandStar(
|
|
71
|
+
cs: str,
|
|
72
|
+
body: TeX | str,
|
|
73
|
+
nargs: int | None = None,
|
|
74
|
+
default: str | None = None,
|
|
75
|
+
) -> TeX:
|
|
76
|
+
return _cmd("renewcommand", cs, nargs, default, body, star=True)
|
|
55
77
|
|
|
56
78
|
|
|
57
79
|
@Registry.add
|
|
@@ -60,9 +82,18 @@ def Providecommand(
|
|
|
60
82
|
body: TeX | str,
|
|
61
83
|
nargs: int | None = None,
|
|
62
84
|
default: str | None = None,
|
|
63
|
-
star: bool = False,
|
|
64
85
|
) -> TeX:
|
|
65
|
-
return _cmd("providecommand", cs, nargs, default, body, star)
|
|
86
|
+
return _cmd("providecommand", cs, nargs, default, body, star=False)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@Registry.add
|
|
90
|
+
def ProvidecommandStar(
|
|
91
|
+
cs: str,
|
|
92
|
+
body: TeX | str,
|
|
93
|
+
nargs: int | None = None,
|
|
94
|
+
default: str | None = None,
|
|
95
|
+
) -> TeX:
|
|
96
|
+
return _cmd("providecommand", cs, nargs, default, body, star=True)
|
|
66
97
|
|
|
67
98
|
|
|
68
99
|
@Registry.add
|
|
@@ -70,9 +101,17 @@ def DeclareRobustCommand(
|
|
|
70
101
|
cs: str,
|
|
71
102
|
body: TeX | str,
|
|
72
103
|
nargs: int | None = None,
|
|
73
|
-
star: bool = False,
|
|
74
104
|
) -> TeX:
|
|
75
|
-
return _cmd("DeclareRobustCommand", cs, nargs, None, body, star)
|
|
105
|
+
return _cmd("DeclareRobustCommand", cs, nargs, None, body, star=False)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@Registry.add
|
|
109
|
+
def DeclareRobustCommandStar(
|
|
110
|
+
cs: str,
|
|
111
|
+
body: TeX | str,
|
|
112
|
+
nargs: int | None = None,
|
|
113
|
+
) -> TeX:
|
|
114
|
+
return _cmd("DeclareRobustCommand", cs, nargs, None, body, star=True)
|
|
76
115
|
|
|
77
116
|
|
|
78
117
|
@Registry.add
|
|
@@ -10,10 +10,10 @@ __all__ = [
|
|
|
10
10
|
"Acrlong",
|
|
11
11
|
"Acrshort",
|
|
12
12
|
"Gls",
|
|
13
|
+
"GlsUpper",
|
|
13
14
|
"Glsenablehyper",
|
|
14
15
|
"Glspl",
|
|
15
|
-
"
|
|
16
|
-
"Glsupper",
|
|
16
|
+
"GlsplUpper",
|
|
17
17
|
"Makeglossaries",
|
|
18
18
|
"Newacronym",
|
|
19
19
|
"Newglossaryentry",
|
|
@@ -80,7 +80,7 @@ def Gls(label: str) -> TeX:
|
|
|
80
80
|
|
|
81
81
|
@Registry.add
|
|
82
82
|
@with_package(GLOSSARIES)
|
|
83
|
-
def
|
|
83
|
+
def GlsUpper(label: str) -> TeX:
|
|
84
84
|
return ControlSequence("Gls", (Parameter(label),))
|
|
85
85
|
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ def Glspl(label: str) -> TeX:
|
|
|
92
92
|
|
|
93
93
|
@Registry.add
|
|
94
94
|
@with_package(GLOSSARIES)
|
|
95
|
-
def
|
|
95
|
+
def GlsplUpper(label: str) -> TeX:
|
|
96
96
|
return ControlSequence("Glspl", (Parameter(label),))
|
|
97
97
|
|
|
98
98
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from typing import Final, override
|
|
3
3
|
|
|
4
|
-
from pytex.commands.builtin import
|
|
4
|
+
from pytex.commands.builtin import HspaceStar, Noindent, VspaceStar
|
|
5
5
|
from pytex.commands.colors import SelectColor
|
|
6
6
|
from pytex.commands.floats import Minipage
|
|
7
7
|
from pytex.commands.font import Fontsize, Selectfont
|
|
@@ -105,7 +105,7 @@ class ColoredBox(TeX):
|
|
|
105
105
|
bg = round((BASE_OPACITY + PER_LEVEL * level) * 100)
|
|
106
106
|
icon_op = bg + ICON_BOOST
|
|
107
107
|
return Concat(
|
|
108
|
-
|
|
108
|
+
VspaceStar(r"0.5\baselineskip"),
|
|
109
109
|
Raw("~\\\\"),
|
|
110
110
|
Noindent(),
|
|
111
111
|
Minipage(
|
|
@@ -135,7 +135,7 @@ class ColoredBox(TeX):
|
|
|
135
135
|
),
|
|
136
136
|
),
|
|
137
137
|
),
|
|
138
|
-
|
|
138
|
+
HspaceStar("0.25cm+2pt"),
|
|
139
139
|
Minipage(
|
|
140
140
|
r"\linewidth-0.5cm-2pt",
|
|
141
141
|
self.body,
|
|
@@ -4,7 +4,7 @@ from typing import Final, override
|
|
|
4
4
|
|
|
5
5
|
from pytex.commands.builtin import (
|
|
6
6
|
Blenderfont,
|
|
7
|
-
|
|
7
|
+
HspaceStar,
|
|
8
8
|
Newline,
|
|
9
9
|
Noindent,
|
|
10
10
|
Rule,
|
|
@@ -12,6 +12,7 @@ from pytex.commands.builtin import (
|
|
|
12
12
|
Textbf,
|
|
13
13
|
Vfill,
|
|
14
14
|
Vspace,
|
|
15
|
+
VspaceStar,
|
|
15
16
|
)
|
|
16
17
|
from pytex.commands.colors import SelectColor
|
|
17
18
|
from pytex.commands.floats import Titlepage as TitlepageEnv
|
|
@@ -97,7 +98,7 @@ class TitlePage(TeX):
|
|
|
97
98
|
Concat(
|
|
98
99
|
Blenderfont(),
|
|
99
100
|
HugeBig(),
|
|
100
|
-
|
|
101
|
+
HspaceStar("-2.5pt"),
|
|
101
102
|
self.title,
|
|
102
103
|
)
|
|
103
104
|
),
|
|
@@ -121,7 +122,7 @@ class TitlePage(TeX):
|
|
|
121
122
|
yield SectionStar("Abstract")
|
|
122
123
|
yield Vspace("-1em")
|
|
123
124
|
yield self.abstract
|
|
124
|
-
yield
|
|
125
|
+
yield VspaceStar("1em")
|
|
125
126
|
if not _is_blank(self.keywords):
|
|
126
127
|
yield Raw(r"\par\noindent ")
|
|
127
128
|
yield Textbf("Keywords")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from typing import Final, override
|
|
3
3
|
|
|
4
|
-
from pytex.commands.builtin import Textbf,
|
|
4
|
+
from pytex.commands.builtin import Textbf, VspaceStar
|
|
5
5
|
from pytex.commands.floats import Columnbreak, Multicols
|
|
6
6
|
from pytex.commands.fontawesome import FaIcon
|
|
7
7
|
from pytex.helpers.parenting import attach
|
|
@@ -67,7 +67,7 @@ class VotingResults(TeX):
|
|
|
67
67
|
return ColoredBox(
|
|
68
68
|
body=Concat(
|
|
69
69
|
self.body,
|
|
70
|
-
|
|
70
|
+
VspaceStar("-2em"),
|
|
71
71
|
Multicols(
|
|
72
72
|
3,
|
|
73
73
|
Concat(
|