pytex-preprocessor 1.0.6__tar.gz → 1.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.
Files changed (171) hide show
  1. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/PKG-INFO +195 -176
  2. pytex_preprocessor-1.2.0/README.md +359 -0
  3. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/pyproject.toml +20 -19
  4. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/__init__.py +10 -2
  5. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/__init__.py +7 -0
  6. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/biblatex.py +2 -1
  7. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/builtin.py +34 -8
  8. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/captions.py +23 -0
  9. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/cleveref.py +23 -0
  10. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/colors.py +15 -2
  11. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/conditionals.py +31 -0
  12. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/counters.py +33 -1
  13. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/definitions.py +12 -2
  14. pytex_preprocessor-1.2.0/src/pytex/commands/floats.py +160 -0
  15. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/font.py +5 -4
  16. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/fontawesome.py +27 -4
  17. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/fontspec.py +35 -0
  18. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/geometry.py +15 -0
  19. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/glossaries.py +3 -2
  20. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/graphics.py +38 -0
  21. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/hooks.py +32 -0
  22. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/hyperref.py +23 -0
  23. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/lengths.py +4 -4
  24. pytex_preprocessor-1.2.0/src/pytex/commands/listings.py +107 -0
  25. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/mdframed.py +21 -0
  26. pytex_preprocessor-1.2.0/src/pytex/commands/picture.py +50 -0
  27. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/setspace.py +15 -0
  28. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/commands/tables.py +11 -5
  29. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/helpers/__init__.py +2 -0
  30. pytex_preprocessor-1.2.0/src/pytex/helpers/coerce.py +22 -0
  31. pytex_preprocessor-1.2.0/src/pytex/helpers/parenting.py +24 -0
  32. pytex_preprocessor-1.2.0/src/pytex/helpers/sanitize.py +70 -0
  33. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/helpers/with_package.py +19 -2
  34. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/interface/__init__.py +2 -0
  35. pytex_preprocessor-1.2.0/src/pytex/interface/control_sequence.py +47 -0
  36. pytex_preprocessor-1.2.0/src/pytex/interface/package.py +70 -0
  37. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/interface/tex.py +17 -4
  38. pytex_preprocessor-1.2.0/src/pytex/model/__init__.py +26 -0
  39. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/color.py +84 -14
  40. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/comment.py +8 -4
  41. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/concat.py +16 -7
  42. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/control_sequence.py +19 -0
  43. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/document.py +45 -14
  44. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/empty.py +6 -0
  45. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/image.py +60 -21
  46. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/include.py +9 -0
  47. pytex_preprocessor-1.2.0/src/pytex/model/length.py +100 -0
  48. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/math.py +8 -6
  49. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/package.py +29 -0
  50. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/raw.py +28 -4
  51. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/packages.py +14 -5
  52. pytex_preprocessor-1.2.0/src/pytex/registry.py +80 -0
  53. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/template.py +29 -18
  54. pytex_preprocessor-1.2.0/src/pytex_analyze/__init__.py +18 -0
  55. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_analyze/analyze.py +23 -7
  56. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_analyze/optimize.py +81 -42
  57. pytex_preprocessor-1.2.0/src/pytex_api/__init__.py +223 -0
  58. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_api/_compile.py +147 -63
  59. pytex_preprocessor-1.2.0/src/pytex_api/_models.py +161 -0
  60. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_api/_policy.py +39 -29
  61. pytex_preprocessor-1.2.0/src/pytex_api/_render.py +199 -0
  62. pytex_preprocessor-1.2.0/src/pytex_api/_sandbox.py +540 -0
  63. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_api/_security.py +78 -37
  64. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_api/sandbox_init.py +70 -29
  65. pytex_preprocessor-1.2.0/src/pytex_builder/__init__.py +8 -0
  66. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/build.py +90 -66
  67. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/console.py +8 -7
  68. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/render.py +40 -24
  69. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/tectonic.py +132 -71
  70. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/tex2py.py +18 -13
  71. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/tree.py +36 -17
  72. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_builder/variants.py +125 -55
  73. pytex_preprocessor-1.2.0/src/pytex_components/__init__.py +73 -0
  74. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_components/boxes.py +81 -35
  75. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_components/citations.py +6 -4
  76. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_components/cleveref_names.py +5 -1
  77. pytex_preprocessor-1.2.0/src/pytex_components/pagebreak.py +107 -0
  78. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_components/voting.py +27 -9
  79. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_components/watermark.py +31 -4
  80. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_components/wordcount.py +11 -2
  81. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/__init__.py +5 -2
  82. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/colors.py +6 -1
  83. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/document.py +97 -42
  84. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/fonts.py +16 -10
  85. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/glossary.py +12 -7
  86. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/hyperref_config.py +11 -4
  87. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/listings.py +8 -0
  88. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/logos.py +74 -34
  89. pytex_preprocessor-1.2.0/src/pytex_hsrtreport/pagesetup.py +41 -0
  90. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/titlepage.py +28 -18
  91. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/variants.py +17 -6
  92. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_koma/document.py +13 -4
  93. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/__init__.py +24 -13
  94. pytex_preprocessor-1.2.0/src/pytex_markdown/convert.py +633 -0
  95. pytex_preprocessor-1.2.0/src/pytex_markdown/escape.py +11 -0
  96. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/frontmatter.py +39 -30
  97. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/glyphs.py +61 -52
  98. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/__init__.py +8 -7
  99. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/convert.py +38 -25
  100. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/document.py +39 -19
  101. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/entries.py +24 -10
  102. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/header.py +25 -9
  103. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/shortcodes.py +26 -13
  104. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_markdown/protocol/signatures.py +18 -11
  105. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_preprocessor.egg-info/PKG-INFO +195 -176
  106. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_protocol/__init__.py +8 -6
  107. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_protocol/frontmatter.py +1 -1
  108. pytex_preprocessor-1.0.6/README.md +0 -340
  109. pytex_preprocessor-1.0.6/src/pytex/commands/floats.py +0 -93
  110. pytex_preprocessor-1.0.6/src/pytex/commands/listings.py +0 -63
  111. pytex_preprocessor-1.0.6/src/pytex/commands/picture.py +0 -32
  112. pytex_preprocessor-1.0.6/src/pytex/helpers/coerce.py +0 -13
  113. pytex_preprocessor-1.0.6/src/pytex/helpers/parenting.py +0 -13
  114. pytex_preprocessor-1.0.6/src/pytex/helpers/sanitize.py +0 -59
  115. pytex_preprocessor-1.0.6/src/pytex/interface/control_sequence.py +0 -29
  116. pytex_preprocessor-1.0.6/src/pytex/interface/package.py +0 -52
  117. pytex_preprocessor-1.0.6/src/pytex/model/__init__.py +0 -25
  118. pytex_preprocessor-1.0.6/src/pytex/model/length.py +0 -54
  119. pytex_preprocessor-1.0.6/src/pytex/registry.py +0 -49
  120. pytex_preprocessor-1.0.6/src/pytex_analyze/__init__.py +0 -16
  121. pytex_preprocessor-1.0.6/src/pytex_api/__init__.py +0 -197
  122. pytex_preprocessor-1.0.6/src/pytex_api/_models.py +0 -125
  123. pytex_preprocessor-1.0.6/src/pytex_api/_render.py +0 -123
  124. pytex_preprocessor-1.0.6/src/pytex_api/_sandbox.py +0 -442
  125. pytex_preprocessor-1.0.6/src/pytex_builder/__init__.py +0 -8
  126. pytex_preprocessor-1.0.6/src/pytex_components/__init__.py +0 -58
  127. pytex_preprocessor-1.0.6/src/pytex_components/pagebreak.py +0 -67
  128. pytex_preprocessor-1.0.6/src/pytex_hsrtreport/pagesetup.py +0 -33
  129. pytex_preprocessor-1.0.6/src/pytex_markdown/convert.py +0 -490
  130. pytex_preprocessor-1.0.6/src/pytex_markdown/escape.py +0 -11
  131. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/LICENSE +0 -0
  132. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/setup.cfg +0 -0
  133. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/document_class.py +0 -0
  134. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex/model/environment.py +0 -0
  135. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
  136. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
  137. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
  138. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
  139. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
  140. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
  141. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
  142. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
  143. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
  144. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
  145. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
  146. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
  147. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
  148. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
  149. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
  150. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
  151. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/ASTA.svg +0 -0
  152. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
  153. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
  154. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/ECHO.svg +0 -0
  155. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
  156. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/INF.pdf +0 -0
  157. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/MAKERS-Icon.svg +0 -0
  158. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/MAKERS-RAlign.svg +0 -0
  159. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/MAKERS.svg +0 -0
  160. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
  161. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
  162. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_hsrtreport/tex/pagesetup.tex +0 -0
  163. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_koma/__init__.py +0 -0
  164. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_koma/commands.py +0 -0
  165. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_preprocessor.egg-info/SOURCES.txt +0 -0
  166. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_preprocessor.egg-info/dependency_links.txt +0 -0
  167. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_preprocessor.egg-info/entry_points.txt +0 -0
  168. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_preprocessor.egg-info/requires.txt +0 -0
  169. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_preprocessor.egg-info/top_level.txt +0 -0
  170. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_tikz/__init__.py +0 -0
  171. {pytex_preprocessor-1.0.6 → pytex_preprocessor-1.2.0}/src/pytex_tikz/tikz.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytex-preprocessor
3
- Version: 1.0.6
3
+ Version: 1.2.0
4
4
  Summary: Type-safe LaTeX document generation with Python
5
5
  Author-email: Frederik Beimgraben <frederik@beimgraben.net>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -692,62 +692,66 @@ Dynamic: license-file
692
692
 
693
693
  # PyTeX
694
694
 
695
- Type-safe LaTeX document generation with Python. Build a document as a tree of
696
- typed `TeX` nodes and render it to a `.tex` file, or drop inline Python
697
- expressions into an existing `.tex` source and have them evaluated at render
698
- time. Requires Python 3.13+.
695
+ PyTeX builds a LaTeX document from Python, and a type checker can check the
696
+ Python code. You build the document as a node tree of typed `TeX` nodes. Then
697
+ you render it to a rendered `.tex` file. You can also put inline Python
698
+ expressions into an existing `.tex` source. PyTeX evaluates them when it
699
+ renders the file. PyTeX needs Python 3.13 or later.
699
700
 
700
- A `TeX` node is an immutable dataclass with a `.rendered` property. The public
701
- API mirrors LaTeX control sequences as PascalCase factories (`Section`,
702
- `Bold`, `Frac`, `Title`, ...), so a document reads like the LaTeX it produces
703
- while staying checkable by a type checker. Nodes track their package
704
- requirements, so the preamble is assembled automatically from what the body
705
- uses.
701
+ A `TeX` node is a dataclass with a `.rendered` property. Most node types are
702
+ immutable. The types `Document`, `Raw` and `IncludeImage` stay mutable. The
703
+ public API mirrors the LaTeX control sequences as PascalCase factories
704
+ (`Section`, `Bold`, `Frac`, `Title`, ...), so a document reads like the LaTeX
705
+ it renders. A node requires a package, so PyTeX assembles the preamble from
706
+ the package requirements that the body uses.
706
707
 
707
708
  ## Install
708
709
 
709
710
  ### Prebuilt binary
710
711
 
711
- Each release attaches standalone `pytex` binaries (Linux/macOS/Windows) no
712
- Python or `pip` needed. Download one from the
712
+ Each release attaches a standalone `pytex` binary for Linux, macOS and Windows.
713
+ The binary needs neither Python nor `pip`. Download one from the
713
714
  [Releases](https://github.com/frederikbeimgraben/PyTeX-Preprocessor/releases)
714
- page, make it executable, and run it. The binary bundles its own interpreter
715
- plus common data packages (numpy, pandas, openpyxl/calamine for spreadsheets,
716
- Pillow, PyYAML), so documents can `import` those without installing anything;
717
- see [`packaging/`](packaging/). It is built on Python 3.14, so documents may use
718
- `tex(t"...")` even on machines without 3.14. (`--build` still needs `tectonic`,
719
- which pytex downloads on first use.)
715
+ page, make it executable, and run it.
716
+
717
+ The binary holds its own interpreter and common data packages. Those packages
718
+ are numpy, pandas, openpyxl and calamine for spreadsheets, Pillow, and PyYAML.
719
+ A document can `import` those without an install (see
720
+ [`packaging/`](packaging/)). PyTeX builds the binary on Python 3.14, so a
721
+ document can use `tex(t"...")` on a machine without Python 3.14. A build with
722
+ `--build` still needs the tectonic binary, which PyTeX downloads on first use.
720
723
 
721
724
  ### From PyPI
722
725
 
723
- To use the `pytex` command anywhere, install it as an isolated tool with
726
+ To get the `pytex` command everywhere, install it as an isolated tool with
724
727
  [pipx](https://pipx.pypa.io/):
725
728
 
726
729
  ```sh
727
730
  pipx install pytex-preprocessor
728
731
  ```
729
732
 
730
- It is also available via plain `pip install pytex-preprocessor`.
733
+ A plain `pip install pytex-preprocessor` also works.
731
734
 
732
- For development, work in a virtualenv with an editable install instead:
735
+ For development, work in a virtualenv and make an editable install instead:
733
736
 
734
737
  ```sh
735
738
  python -m venv venv && . venv/bin/activate
736
739
  pip install -e . # add [dev] for pytest, ruff, basedpyright
737
740
  ```
738
741
 
739
- External tools, each needed only for the matching feature:
742
+ PyTeX uses these external tools. Each tool serves one feature:
740
743
 
741
- - `tectonic` — compile to PDF (`--build`). If not on `PATH`, the build
742
- downloads a self-contained binary into a temp folder and reuses it.
743
- - `inkscape` `SVG` image conversion.
744
- - `makeindex` (from a TeX distribution, e.g. TeX Live) — resolve
745
- glossaries/acronyms.
744
+ - `tectonic` — compiles the rendered `.tex` file to PDF (`--build`). If the
745
+ tectonic binary is not on `PATH`, PyTeX downloads a self-contained binary
746
+ into `$XDG_CACHE_HOME/pytex` or `~/.cache/pytex` and reuses it.
747
+ - `inkscape` converts an `SVG` image to PDF.
748
+ - `makeindex` (from a TeX distribution, for example TeX Live) — resolves the
749
+ `glossaries` entries and the acronyms.
746
750
 
747
751
  ## Quick start
748
752
 
749
- A `.tex.py` file is plain Python exposing a module-level `__pytex__` that holds
750
- a `TeX` node:
753
+ A `.tex.py` file is plain Python that defines the `__pytex__` node at module
754
+ level. That node holds a `TeX` node:
751
755
 
752
756
  ```py
753
757
  from pytex.commands.builtin import Bold, Emph, Section, Title, MakeTitle
@@ -772,14 +776,15 @@ pytex example.tex.py # render -> build/example.out.tex
772
776
  pytex example.tex.py --build # render + compile -> build/example.out.pdf
773
777
  ```
774
778
 
775
- Bare strings are coerced to text nodes and LaTeX-escaped.
779
+ PyTeX coerces a bare string to a text node and escapes it for LaTeX.
776
780
 
777
781
  ### Template strings (Python 3.14+)
778
782
 
779
783
  On Python 3.14, `pytex.tex` accepts a [PEP 750](https://peps.python.org/pep-0750/)
780
- template string and builds a `TeX` tree from it. Static parts are literal LaTeX;
781
- interpolations are LaTeX-escaped when they are plain values and spliced as-is
782
- when they are `TeX` nodes (nested template strings and lists are handled too):
784
+ template string and builds a node tree from it. The static parts stay literal
785
+ LaTeX. For an interpolation, `tex` escapes a plain value for LaTeX, and puts a
786
+ `TeX` node into the node tree without a change. It handles a nested template
787
+ string, a list and a tuple the same way:
783
788
 
784
789
  ```py
785
790
  from pytex import tex
@@ -788,22 +793,23 @@ name = "Q&A: 50%"
788
793
  body = tex(t"{Bold('Heading')} — {name}") # node spliced; name -> "Q\&A: 50\%"
789
794
  ```
790
795
 
791
- `tex` is only exported on 3.14+; the rest of the library runs on 3.13.
796
+ PyTeX exports `tex` only on Python 3.14 and later. The rest of the library runs
797
+ on Python 3.13.
792
798
 
793
799
  ## The `pytex` command
794
800
 
795
- The input file is dispatched by extension:
801
+ PyTeX dispatches the input file by its extension:
796
802
 
797
803
  | Extension | Handling |
798
804
  | --- | --- |
799
- | `.py` | imported as a module; its `__pytex__` node is rendered. Convention: name it `<doc>.tex.py`. |
800
- | `.tex` | wrapped in `IncludeTeX`; inline `\iffalse{pytex(...)}\fi` markers are evaluated, then rendered. Convention: `<doc>.py.tex`. |
801
- | `.md` / `.markdown` | converted to nodes and wrapped in a document according to `--variant` (see below). Without `--variant` the style is auto-detected. |
805
+ | `.py` | PyTeX imports the file as a module and renders its `__pytex__` node. By convention, name the file `<doc>.tex.py`. |
806
+ | `.tex` | PyTeX wraps the file in `IncludeTeX`, evaluates each inline `pytex(...)` marker, then renders the file. By convention, name the file `<doc>.py.tex`. |
807
+ | `.md` / `.markdown` | The Markdown converter turns the file into nodes. PyTeX wraps them in a document that `--variant` picks (see below). Without `--variant`, PyTeX detects the variant. |
802
808
 
803
- ### Inline replacements in `.tex`
809
+ ### Inline `pytex(...)` markers in `.tex`
804
810
 
805
- Any registered factory is in scope inside a marker. The `\iffalse ... \fi` pair
806
- is a LaTeX no-op, so the source still compiles as-is without PyTeX:
811
+ Every registered factory is in scope inside a marker. The `\iffalse ... \fi`
812
+ pair is a LaTeX no-op, so LaTeX still compiles the source without PyTeX:
807
813
 
808
814
  ```tex
809
815
  Today is \iffalse{pytex(Today())}\fi.
@@ -815,73 +821,76 @@ Plain Python works too: $3^2 = \iffalse{pytex(3 ** 2)}\fi$.
815
821
 
816
822
  | Flag | Default | Meaning |
817
823
  | --- | --- | --- |
818
- | `-o`, `--output` | `<build-dir>/<input>.out.tex` | rendered LaTeX output path |
819
- | `-b`, `--build` | off | compile the rendered `.tex` to PDF with tectonic |
820
- | `--build-dir DIR` | `build` | directory for artifacts and tectonic output |
821
- | `--no-shell-escape` | shell-escape on | disable shell-escape |
822
- | `-t`, `--tree` | off | also print the input's `TeX`-node tree (`tree`-style) before rendering/building |
823
- | `-f`, `--force` | off | skip the optimize + analysis pass and build even if problems are found |
824
- | `--variant STYLE` | auto-detect | Markdown output style (`plain`, `report`, `report-makers`, `protocol-asta`, `protocol-stupa`) |
825
- | `--config JSON` | none | JSON object of document-class params, merged over the frontmatter |
826
- | `--untrusted` | off (trusted) | render foreign input through the trust policy (see [Security](#security-and-trust)) |
827
- | `--trust-level LEVEL` | `trusted` | `trusted`, `sandboxed`, or `untrusted` (see [Security](#security-and-trust)) |
828
-
829
- Shell-escape is on by default because inline images decode their base64
830
- payloads at compile time. The build runs tectonic, then `makeindex` (for
831
- `glossaries`/acronyms), then reruns tectonic when an index changed.
824
+ | `-o`, `--output` | `<build-dir>/<input>.out.tex` | path of the rendered `.tex` file |
825
+ | `-b`, `--build` | off | compile the rendered `.tex` file to PDF with the tectonic binary |
826
+ | `--build-dir DIR` | `build` | build directory for the artifacts and the tectonic output |
827
+ | `--no-shell-escape` | shell-escape on | turn shell-escape off |
828
+ | `-t`, `--tree` | off | also print the node tree of the input file in `tree` style before the render |
829
+ | `-f`, `--force` | off | skip the optimize pass and the analysis pass, and build even when PyTeX finds a problem |
830
+ | `--variant STYLE` | auto-detect | variant for a Markdown input file (`plain`, `report`, `report-makers`, `protocol`, `protocol-asta`, `protocol-stupa`) |
831
+ | `--config JSON` | none | JSON object of document-class parameters. `--config` overrides the frontmatter. |
832
+ | `--untrusted` | off (trusted) | render input from a source you do not trust through the trust policy (see [Security](#security-and-trust)) |
833
+ | `--trust-level LEVEL` | `trusted` | the trust level: `trusted`, `sandboxed`, or `untrusted` (see [Security](#security-and-trust)) |
834
+
835
+ Shell-escape is on by default, because an inline image decodes its base64
836
+ data during the compile pass. The build runs the tectonic binary, then the
837
+ makeindex step for `glossaries` and the acronyms. When the makeindex step
838
+ rebuilds an index, the build runs one more compile pass.
832
839
 
833
840
  ### Security and trust
834
841
 
835
- By default the CLI runs in a **trusted** context: it imports and executes `.py`
836
- inputs, evaluates `.tex` `pytex(...)` replacements and Markdown `eval`
837
- comments, and enables shell-escape. That is code execution by design it is
838
- how PyTeX documents work and is safe **only for documents you wrote
839
- yourself**. Do not run the default CLI on a file from a source you do not
840
- trust.
841
-
842
- To render input from a foreign or untrusted source, pass `--untrusted` (or
843
- `--trust-level {sandboxed,untrusted}`). These route the build through the
844
- `pytex_api` trust policy, which:
845
-
846
- - refuses `.py` / `.tex.py` inputs (no Python execution),
847
- - leaves `.tex` `pytex(...)` markers and Markdown `eval` comments inert,
848
- - forces shell-escape **off** and rejects code-/file-surface packages
849
- (`minted`, `shellesc`, `pythontex`, …) and anything off the package
850
- allowlist,
851
- - applies CPU/memory/output resource limits, and
852
- - for `sandboxed`, additionally requires the Podman OS sandbox for PDF builds.
853
-
854
- `--untrusted` is shorthand for `--trust-level untrusted`. The two flags are
855
- mutually exclusive; `trusted` is the default, so existing invocations are
856
- unchanged.
857
-
858
- Output is minimal and color-tagged (`==>`, `note:`, `warning:`, `error:`),
859
- following tectonic's style; on failure it points at the likely cause and the
860
- log file. Set `NO_COLOR` to disable color.
861
-
862
- ### Pre-flight optimize + analysis
863
-
864
- Before rendering, the builder runs two render-equivalent passes over the node
865
- tree. First `Optimize` tidies the tree (flatten nested `Concat`s, drop empty
866
- nodes, turn whole-`Raw` LaTeX constructs into native nodes) without changing
867
- the output (it also expands inline `pytex(...)` markers and turns `Raw`
868
- comments and math `\[...\]`, `\(...\)`, `$...$` into native nodes). Then
869
- `pytex_analyze` checks for problems that LaTeX would only surface later (or
870
- silently):
871
-
872
- - references (`\ref`, `\cref`, `\autoref`, ...) to a label that is never
873
- defined,
874
- - labels defined more than once,
875
- - `\includegraphics` paths that do not exist on disk.
876
-
877
- Missing-image issues are errors and abort the build; the rest are warnings.
878
- Pass `-f`/`--force` to skip both passes and build regardless.
842
+ By default the `pytex` command runs at trust level `trusted`. At that level
843
+ PyTeX imports and executes a `.py` input file. It evaluates each inline
844
+ `pytex(...)` marker of a `.tex` input file and each Markdown `eval` comment. It
845
+ also turns shell-escape on. This is code execution by design, because that is
846
+ how a PyTeX document works. **Use the default only on a document you wrote
847
+ yourself.**
848
+
849
+ If the input file comes from a source you do not trust, pass `--untrusted` (or
850
+ `--trust-level {sandboxed,untrusted}`). Both options route the build through
851
+ the `pytex_api` trust policy. The trust policy:
852
+
853
+ - refuses a `.py` or `.tex.py` input file, so PyTeX executes no Python,
854
+ - leaves each inline `pytex(...)` marker and each Markdown `eval` comment
855
+ inert,
856
+ - forces shell-escape **off**, and rejects a package that opens a
857
+ code-execution surface (`minted`, `shellesc`, `pythontex`, …) and every
858
+ package off the package allowlist,
859
+ - applies the CPU, memory and output limits, and
860
+ - needs the Podman sandbox for a PDF build at both trust levels. The level
861
+ `sandboxed` has a wider package allowlist than `untrusted`.
862
+
863
+ `--untrusted` is shorthand for `--trust-level untrusted`. The two options are
864
+ mutually exclusive. The default trust level is `trusted`, so an existing
865
+ command line does not change.
866
+
867
+ The `pytex` command prints short, color-tagged output (`==>`, `note:`,
868
+ `warning:`, `error:`) in the style of tectonic. On a failure it names the
869
+ likely cause and the log file. Set `NO_COLOR` to turn color off.
870
+
871
+ ### The optimize pass and the analysis pass
872
+
873
+ Before PyTeX renders the node tree, it runs the optimize pass and then the
874
+ analysis pass. The optimize pass tidies the node tree. It flattens a nested
875
+ `Concat`, drops an empty node, and turns a whole-`Raw` LaTeX construct into a
876
+ native node. It also expands each inline `pytex(...)` marker, and turns a `Raw`
877
+ comment and `Raw` math (`\[...\]`, `\(...\)`, `$...$`) into a native node. The
878
+ optimize pass is render-equivalent. The analysis pass then runs static checks
879
+ for problems that LaTeX reports late or not at all:
880
+
881
+ - a reference (`\ref`, `\cref`, `\autoref`, ...) to a label that no node
882
+ defines,
883
+ - a label that more than one node defines,
884
+ - an `\includegraphics` path that does not exist on disk.
885
+
886
+ A missing image is an error and stops the build. Every other issue is a
887
+ warning. To skip both passes and build anyway, pass `-f` or `--force`.
879
888
 
880
889
  ### Inspecting the node tree
881
890
 
882
- `--tree` prints the parsed `TeX`-node tree (then renders/builds as usual),
883
- useful for debugging how an input maps to nodes. Nodes that require a package
884
- are tagged with it (`[+package]`):
891
+ `--tree` prints the node tree of the input file, then renders or builds as
892
+ usual. Use it to see how PyTeX maps an input file to nodes. A node that
893
+ requires a package carries a tag with the package name (`[+package]`):
885
894
 
886
895
  ```
887
896
  $ pytex example.tex.py --tree
@@ -897,22 +906,24 @@ Document (article)
897
906
 
898
907
  ## Packages
899
908
 
900
- `pytex` is the core; the rest are optional and build on it.
909
+ `pytex` is the core package. Every other package is optional and builds on
910
+ `pytex`.
901
911
 
902
912
  | Package | Provides |
903
913
  | --- | --- |
904
- | `pytex` | core node model, `Document`, math, tables, graphics, and factories for the common LaTeX packages (biblatex, cleveref, glossaries, hyperref, listings, ...). |
905
- | `pytex_koma` | KOMA-Script classes and commands (`Addchap`, `Minisec`, `KOMAoptions`, ...). |
914
+ | `pytex` | the core node model, `Document`, math, tables, graphics, and the factories for the common LaTeX packages (biblatex, cleveref, glossaries, hyperref, listings, ...). |
915
+ | `pytex_koma` | the KOMA-Script classes and commands (`Addchap`, `Minisec`, `KOMAoptions`, ...). |
906
916
  | `pytex_tikz` | TikZ pictures and primitives (`TikzPicture`, `Draw`, `Node`, `Circle`, ...). |
907
- | `pytex_components` | reusable, template-agnostic widgets: colored callout boxes (`ColoredBox` + presets), a voting tally, draft watermark, word-count and smart-pagebreak macros, a clickable author-year citation, German cleveref labels. |
908
- | `pytex_markdown` | Markdown -> native `TeX` conversion (see below), including `pytex_markdown.protocol` (STUPA/AStA meeting minutes) and `pytex_markdown.frontmatter` (YAML frontmatter parsing). |
909
- | `pytex_analyze` | static checks over the node tree (dangling refs, duplicate labels, missing images), plus `Optimize` to simplify a tree render-equivalently. |
910
- | `pytex_hsrtreport` | HSRT report document class, title pages, logos, and HSRT colors/fonts/glossary helpers. Builds on `pytex_components` (and re-exports it for compatibility). |
911
- | `pytex_protocol` | deprecated alias for `pytex_markdown.protocol` (kept as a re-export shim). |
917
+ | `pytex_components` | the components that any document can use: colored boxes (`ColoredBox` and the presets), a voting tally, a draft watermark, word-count and smart-pagebreak macros, a clickable author-year citation, and German cleveref labels. |
918
+ | `pytex_markdown` | the Markdown converter, which turns Markdown into native `TeX` nodes (see below). It holds `pytex_markdown.protocol` for a StuPa or AStA meeting protocol, and `pytex_markdown.frontmatter` for the YAML frontmatter. |
919
+ | `pytex_analyze` | the analysis pass, which runs static checks over the node tree for a dangling reference, a duplicate label and a missing image. It also holds `Optimize`, the render-equivalent optimize pass. |
920
+ | `pytex_hsrtreport` | the HSRT report document class, the title pages, the logos, and the HSRT color, font and glossary helpers. It builds on `pytex_components` and re-exports it, so an older import keeps working. |
921
+ | `pytex_protocol` | the deprecated alias for `pytex_markdown.protocol`. It stays as a re-export shim. |
912
922
 
913
923
  ## Markdown
914
924
 
915
- `pytex_markdown` converts Markdown to native `TeX` nodes (via `marko`):
925
+ The Markdown converter in `pytex_markdown` turns Markdown into native `TeX`
926
+ nodes. It parses the source with `marko`:
916
927
 
917
928
  ```py
918
929
  from pytex_markdown import Markdown, IncludeMarkdown
@@ -921,83 +932,88 @@ body = Markdown("# Title\n\nText with **bold**, `code`, [a link](https://x).")
921
932
  body = IncludeMarkdown("notes.md", base_level=-1) # base_level=-1: # -> \chapter
922
933
  ```
923
934
 
924
- Headings, emphasis, inline/fenced code, lists, links, images, GFM tables, block
925
- quotes and thematic breaks map to the standard pytex library; text is
926
- LaTeX-escaped. Some extras on top of plain Markdown:
935
+ A heading, emphasis, inline code and fenced code map to the core `pytex`
936
+ library. A list, a link, an image, a GFM table, a block quote and a thematic
937
+ break map to it too. The Markdown converter escapes the text for LaTeX. It adds
938
+ these extras to plain Markdown:
927
939
 
928
- - **GitHub-style callouts** become colored boxes (from `pytex_components`):
940
+ - **GitHub-style callouts** become colored boxes from `pytex_components`:
929
941
  ```md
930
942
  > [!NOTE] -> InfoBox > [!IMPORTANT] -> ImportantBox
931
943
  > [!TIP] -> SuccessBox > [!WARNING] -> WarningBox
932
944
  ```
933
- - **Citations** in Pandoc syntax: `[@key]` / `[@key, p. 5]` -> `\autocite`,
934
- `[@a; @b]` -> a combined cite, and a narrative `@key` -> `\textcite`.
935
- - **Bibliography** from frontmatter `bibliography:` is either inline BibTeX (a
936
- `|` block scalar) or a path to a `.bib` file; reports print a numbered
937
- `\printbibliography`.
938
- - ASCII **math arrows** (`->`, `=>`, `<->`, ...) become inline math arrows, the
939
- **euro sign** `€` becomes a font-independent `\euro{}`, and tables get a bit of
940
- vertical breathing room.
941
-
942
- Both factories are registered, so they work in `\iffalse{pytex(...)}\fi`
943
- replacements in `.tex` sources too.
945
+ - **Citations** in Pandoc syntax. `[@key]` and `[@key, p. 5]` -> `\autocite`,
946
+ `[@a; @b]` -> one combined cite, and a narrative `@key` -> `\textcite`.
947
+ - **Bibliography** from the frontmatter. The key `bibliography:` holds either
948
+ inline BibTeX (a `|` block scalar) or a path to a `.bib` file. A report
949
+ variant prints a numbered `\printbibliography`.
950
+ - An ASCII **math arrow** (`->`, `=>`, `<->`, ...) becomes an inline math
951
+ arrow. The **euro sign** `€` becomes a font-independent `\euro{}`. A table
952
+ gets more vertical space.
953
+
954
+ PyTeX registers both factories, so they also work in an inline `pytex(...)`
955
+ marker in a `.tex` input file.
944
956
 
945
957
  ### Output variants
946
958
 
947
- When the `pytex` command renders a `.md` file it wraps the converted nodes in a
948
- document chosen by `--variant`:
959
+ When the `pytex` command renders a `.md` file, it wraps the converted nodes in
960
+ a document that `--variant` picks:
949
961
 
950
962
  | Variant | Document |
951
963
  | --- | --- |
952
- | `plain` | a bare `Document` (default class `article`); `#` -> `\section`. |
953
- | `report` | an HSRT report with title page and table of contents; `#` -> `\chapter`. |
954
- | `report-makers` | a `report` branded with the MAKERS logo (title page + footer). |
955
- | `protocol-asta` | an AStA meeting protocol (HSRT report, AStA logos). |
956
- | `protocol-stupa` | a StuPa meeting protocol (HSRT report, StuPa logos). |
964
+ | `plain` | a bare `Document`. The default document class is `article`, and `#` -> `\section`. |
965
+ | `report` | an HSRT report with a title page and a table of contents. `#` -> `\chapter`. |
966
+ | `report-makers` | a `report` with the MAKERS logo on the title page and in the footer. |
967
+ | `protocol` | a meeting protocol with no corporate design of its own. The caller names the logos with `logos` and `footer_logos`. |
968
+ | `protocol-asta` | an AStA meeting protocol. It is an HSRT report with the AStA logos. |
969
+ | `protocol-stupa` | a StuPa meeting protocol. It is an HSRT report with the StuPa logos. |
957
970
 
958
- Without `--variant`, protocol frontmatter (`gremium:` or `typ: protokoll`) picks
959
- a protocol style and everything else falls back to `plain`.
971
+ Without `--variant`, PyTeX detects the variant. Meeting-protocol frontmatter
972
+ (`gremium:` or `typ: protokoll`) picks a protocol variant. Every other input
973
+ file gets `plain`.
960
974
 
961
- Document-class parameters come from the YAML frontmatter and from `--config`
962
- (a JSON object that overrides the frontmatter), e.g.:
975
+ Document-class parameters come from the YAML frontmatter and from `--config`, a
976
+ JSON object. `--config` overrides the frontmatter. For example:
963
977
 
964
978
  ```sh
965
979
  pytex notes.md --variant plain --config '{"documentclass": "scrartcl", "classoptions": ["11pt", "twocolumn"]}'
966
980
  ```
967
981
 
968
982
  `classoptions` accepts a list (`"twocolumn"`, `"DIV=12"`) or a `{key: value}`
969
- object. For styles with a title page (`report`), the title is taken from
970
- `title:`/`--config` if given, otherwise from the first `#` heading (which is then
971
- not also rendered as a chapter).
972
-
973
- The report styles read further frontmatter keys: `author`, `abstract`,
974
- `keywords`, title-page `datalines` (a list of `"Label: value"` entries),
975
- `bibliography` (see [Markdown](#markdown)), `logos` (title-page logos — vendored
976
- names like `INF`/`MAKERS` and/or paths to custom image files), and the labels
977
- `abstract_heading` / `keywords_heading` to rename the default "Abstract" /
978
- "Keywords" sections.
983
+ object. A variant with a title page, such as `report`, takes the title from
984
+ `title:` or from `--config`. If neither one gives a title, the variant takes
985
+ the first `#` heading, and then does not also render it as a chapter.
986
+
987
+ The report variants read more frontmatter keys. These are `author`, `abstract`,
988
+ `keywords`, the title-page `datalines` (a list of `"Label: value"` entries), and
989
+ `bibliography` (see [Markdown](#markdown)). The key `logos` names the title-page
990
+ logos, and the key `footer_logos` the logos of the page footer. Each one takes
991
+ a vendored name such as `INF` or `MAKERS`, a path to a custom image file, or
992
+ both. Without these keys, the variant supplies its own logos. The keys `abstract_heading` and `keywords_heading` rename
993
+ the default "Abstract" and "Keywords" sections.
979
994
 
980
995
  ## Converting LaTeX to PyTeX
981
996
 
982
- `pytex-tex2py` turns an existing `.tex` file into an equivalent `.tex.py`
983
- source. It reads the file, runs `Optimize` over it (expanding inline
984
- `pytex(...)` markers and recognising comments and math), and serialises the
985
- result to Python that rebuilds the same tree:
997
+ `pytex-tex2py` turns an existing `.tex` file into an equivalent `.tex.py` file.
998
+ It reads the file and runs the optimize pass over it. The optimize pass expands
999
+ each inline `pytex(...)` marker and recognizes comments and math.
1000
+ `pytex-tex2py` then serializes the result to Python that rebuilds the same node
1001
+ tree:
986
1002
 
987
1003
  ```sh
988
1004
  pytex-tex2py paper.tex # -> paper.tex.py
989
1005
  pytex-tex2py paper.tex -o out.py
990
1006
  ```
991
1007
 
992
- Rendering the generated `.tex.py` reproduces the original output byte-for-byte;
993
- nodes the serialiser does not special-case fall back to a literal `Raw`, so the
994
- conversion always round-trips.
1008
+ The rendered `.tex` file of the new `.tex.py` file matches the original byte
1009
+ for byte. A node that the serializer does not handle falls back to a literal
1010
+ `Raw`, so the conversion always round-trips.
995
1011
 
996
1012
  ## Examples
997
1013
 
998
- See `examples/` for one minimal input per kind (`.tex.py`, `.py.tex`, `.md`,
999
- mixed, and a full HSRT report). Run from the repository root so relative paths
1000
- resolve:
1014
+ The `examples/` directory holds one minimal input file per kind: `.tex.py`,
1015
+ `.py.tex`, `.md`, a mixed file, and a full HSRT report. Run the commands from
1016
+ the repository root, so the relative paths resolve:
1001
1017
 
1002
1018
  ```sh
1003
1019
  pytex examples/document.tex.py --build
@@ -1007,24 +1023,27 @@ pytex examples/notes.md --build
1007
1023
 
1008
1024
  ## Stability
1009
1025
 
1010
- From 1.0 the project follows [Semantic Versioning](https://semver.org). The
1011
- public API is what each package exports through its top-level `__all__`:
1012
- everything reachable as `from pytex import X` (and the same for `pytex_koma`,
1013
- `pytex_tikz`, `pytex_components`, `pytex_markdown`, `pytex_analyze`,
1014
- `pytex_hsrtreport`). Breaking those names needs a major version bump.
1015
-
1016
- Also part of the contract: the registry keys exposed to `\iffalse{pytex(...)}\fi`
1017
- markers they are the factory names, so renaming a registered factory is a
1018
- breaking change (which is why the `\fill` length is `Fill_len`, leaving the bare
1019
- `Fill` key to the TikZ path command).
1020
-
1021
- Internal and not covered by the guarantee: any name with a leading underscore,
1022
- modules whose name starts with an underscore (e.g. `pytex_api._policy`,
1023
- `pytex_api._compile`), and anything not listed in a package's `__all__`. Import
1024
- those at your own risk.
1025
-
1026
- Deprecated shims (`pytex_protocol`, the `pytex.commands.lengths.Fill` alias) keep
1027
- working with a `DeprecationWarning` and may be removed in the next major release.
1026
+ From 1.0 on, PyTeX follows [Semantic Versioning](https://semver.org). The
1027
+ public API is what each package exports through its top-level `__all__`. That
1028
+ is every name you can reach as `from pytex import X`, and the same for
1029
+ `pytex_koma`, `pytex_tikz`, `pytex_components`, `pytex_markdown`,
1030
+ `pytex_analyze` and `pytex_hsrtreport`. A change that breaks one of those names
1031
+ needs a major version bump.
1032
+
1033
+ The contract also covers the registry keys that an inline `pytex(...)` marker
1034
+ can use. A registry key is the name of a factory, so a rename of a registered
1035
+ factory is a breaking change. For that reason the `\fill` length is `Fill_len`,
1036
+ which leaves the bare `Fill` registry key to the TikZ path command.
1037
+
1038
+ Some parts are internal, and the guarantee does not cover them. A name with a
1039
+ leading underscore is internal. A module whose name starts with an underscore
1040
+ is internal, for example `pytex_api._policy` and `pytex_api._compile`. A name
1041
+ that a package does not list in its `__all__` is also internal. Import those at
1042
+ your own risk.
1043
+
1044
+ The deprecated shims `pytex_protocol` and the `pytex.commands.lengths.Fill`
1045
+ alias keep working and raise a `DeprecationWarning`. PyTeX may remove them in
1046
+ the next major release.
1028
1047
 
1029
1048
  ## License
1030
1049