pytex-preprocessor 0.1.0__py3-none-any.whl

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 (119) hide show
  1. pytex/__init__.py +87 -0
  2. pytex/commands/__init__.py +51 -0
  3. pytex/commands/biblatex.py +98 -0
  4. pytex/commands/builtin.py +598 -0
  5. pytex/commands/captions.py +56 -0
  6. pytex/commands/cleveref.py +43 -0
  7. pytex/commands/colors.py +60 -0
  8. pytex/commands/conditionals.py +62 -0
  9. pytex/commands/counters.py +85 -0
  10. pytex/commands/definitions.py +109 -0
  11. pytex/commands/floats.py +93 -0
  12. pytex/commands/font.py +138 -0
  13. pytex/commands/fontawesome.py +88 -0
  14. pytex/commands/fontspec.py +75 -0
  15. pytex/commands/geometry.py +25 -0
  16. pytex/commands/glossaries.py +126 -0
  17. pytex/commands/graphics.py +68 -0
  18. pytex/commands/hooks.py +58 -0
  19. pytex/commands/hyperref.py +57 -0
  20. pytex/commands/lengths.py +200 -0
  21. pytex/commands/listings.py +63 -0
  22. pytex/commands/mdframed.py +43 -0
  23. pytex/commands/picture.py +32 -0
  24. pytex/commands/setspace.py +38 -0
  25. pytex/commands/tables.py +123 -0
  26. pytex/helpers/__init__.py +3 -0
  27. pytex/helpers/coerce.py +13 -0
  28. pytex/helpers/parenting.py +13 -0
  29. pytex/helpers/sanitize.py +54 -0
  30. pytex/helpers/with_package.py +61 -0
  31. pytex/interface/__init__.py +3 -0
  32. pytex/interface/control_sequence.py +29 -0
  33. pytex/interface/package.py +52 -0
  34. pytex/interface/tex.py +41 -0
  35. pytex/model/__init__.py +25 -0
  36. pytex/model/color.py +203 -0
  37. pytex/model/concat.py +31 -0
  38. pytex/model/control_sequence.py +72 -0
  39. pytex/model/document.py +120 -0
  40. pytex/model/document_class.py +29 -0
  41. pytex/model/empty.py +19 -0
  42. pytex/model/environment.py +30 -0
  43. pytex/model/image.py +137 -0
  44. pytex/model/include.py +21 -0
  45. pytex/model/length.py +54 -0
  46. pytex/model/math.py +401 -0
  47. pytex/model/package.py +132 -0
  48. pytex/model/raw.py +61 -0
  49. pytex/packages.py +221 -0
  50. pytex/registry.py +49 -0
  51. pytex_builder/__init__.py +8 -0
  52. pytex_builder/build.py +175 -0
  53. pytex_builder/console.py +77 -0
  54. pytex_builder/render.py +90 -0
  55. pytex_builder/tectonic.py +370 -0
  56. pytex_hsrtreport/__init__.py +116 -0
  57. pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
  58. pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
  59. pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
  60. pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
  61. pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
  62. pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
  63. pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
  64. pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
  65. pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
  66. pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
  67. pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
  68. pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
  69. pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
  70. pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
  71. pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
  72. pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
  73. pytex_hsrtreport/assets/logos/ASTA.svg +79 -0
  74. pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
  75. pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
  76. pytex_hsrtreport/assets/logos/ECHO.svg +226 -0
  77. pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
  78. pytex_hsrtreport/assets/logos/INF.pdf +0 -0
  79. pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
  80. pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
  81. pytex_hsrtreport/boxes.py +215 -0
  82. pytex_hsrtreport/citations.py +21 -0
  83. pytex_hsrtreport/cleveref_names.py +47 -0
  84. pytex_hsrtreport/colors.py +30 -0
  85. pytex_hsrtreport/document.py +307 -0
  86. pytex_hsrtreport/fonts.py +66 -0
  87. pytex_hsrtreport/glossary.py +61 -0
  88. pytex_hsrtreport/hyperref_config.py +49 -0
  89. pytex_hsrtreport/listings.py +90 -0
  90. pytex_hsrtreport/logos.py +234 -0
  91. pytex_hsrtreport/pagebreak.py +67 -0
  92. pytex_hsrtreport/pagesetup.py +33 -0
  93. pytex_hsrtreport/tex/pagesetup.tex +76 -0
  94. pytex_hsrtreport/titlepage.py +136 -0
  95. pytex_hsrtreport/variants.py +24 -0
  96. pytex_hsrtreport/voting.py +96 -0
  97. pytex_hsrtreport/watermark.py +63 -0
  98. pytex_hsrtreport/wordcount.py +33 -0
  99. pytex_koma/__init__.py +90 -0
  100. pytex_koma/commands.py +296 -0
  101. pytex_koma/document.py +138 -0
  102. pytex_markdown/__init__.py +62 -0
  103. pytex_markdown/convert.py +271 -0
  104. pytex_markdown/escape.py +11 -0
  105. pytex_preprocessor-0.1.0.dist-info/METADATA +82 -0
  106. pytex_preprocessor-0.1.0.dist-info/RECORD +119 -0
  107. pytex_preprocessor-0.1.0.dist-info/WHEEL +5 -0
  108. pytex_preprocessor-0.1.0.dist-info/entry_points.txt +2 -0
  109. pytex_preprocessor-0.1.0.dist-info/top_level.txt +7 -0
  110. pytex_protocol/__init__.py +37 -0
  111. pytex_protocol/convert.py +202 -0
  112. pytex_protocol/document.py +91 -0
  113. pytex_protocol/entries.py +96 -0
  114. pytex_protocol/frontmatter.py +80 -0
  115. pytex_protocol/header.py +139 -0
  116. pytex_protocol/shortcodes.py +130 -0
  117. pytex_protocol/signatures.py +84 -0
  118. pytex_tikz/__init__.py +25 -0
  119. pytex_tikz/tikz.py +272 -0
@@ -0,0 +1,307 @@
1
+ from collections.abc import Iterator
2
+ from dataclasses import dataclass, field
3
+ from pathlib import Path
4
+ from typing import Final, override
5
+
6
+ from pytex.commands.colors import Definecolor
7
+ from pytex.commands.fontspec import Setmainfont, Setsansfont
8
+ from pytex.commands.geometry import Geometry
9
+ from pytex.helpers.coerce import coerce_tex
10
+ from pytex.interface.package import PackageProtocol
11
+ from pytex.interface.tex import TeX
12
+ from pytex.model.color import Color, collect_colors
13
+ from pytex.model.concat import Concat
14
+ from pytex.model.document_class import DocumentClass
15
+ from pytex.model.empty import Empty
16
+ from pytex.model.environment import Environment
17
+ from pytex.model.raw import Raw
18
+ from pytex.packages import (
19
+ ARRAY,
20
+ BABEL,
21
+ BIBLATEX,
22
+ CLEVEREF,
23
+ CSQUOTES,
24
+ ETOOLBOX,
25
+ FONTAWESOME,
26
+ GEOMETRY,
27
+ GLOSSARIES,
28
+ GRAPHICX,
29
+ HYPERREF,
30
+ LISTINGS,
31
+ LMODERN,
32
+ LONGTABLE,
33
+ MDFRAMED,
34
+ NEEDSPACE,
35
+ PGF,
36
+ PGFFOR,
37
+ RAGGED2E,
38
+ SCRLAYER_SCRPAGE,
39
+ SETSPACE,
40
+ TIKZ,
41
+ XCOLOR,
42
+ )
43
+ from pytex.registry import Registry
44
+ from pytex_koma.document import KomaDocument
45
+
46
+ from .cleveref_names import GermanCrefNames
47
+ from .colors import HSRTColors
48
+ from .fonts import HSRTFontSetup
49
+ from .glossary import AcrShortcut, HSRTGlossarySetup
50
+ from .hyperref_config import (
51
+ HSRT_CITE_COLOR,
52
+ HSRT_LINK_COLOR,
53
+ HSRT_URL_COLOR,
54
+ HSRTHyperref,
55
+ )
56
+ from .listings import HSRTListingStyles
57
+ from .logos import DefaultLogos, footer_logo_hook
58
+ from .pagesetup import HSRTPageSetup
59
+ from .titlepage import TitlePage, TitlePageDataLine
60
+ from .variants import Variant, default_logo_names
61
+
62
+ __all__ = ["HSRTReport"]
63
+
64
+ BASE_PACKAGES: Final[frozenset[PackageProtocol]] = frozenset(
65
+ {
66
+ LMODERN,
67
+ GEOMETRY,
68
+ GRAPHICX,
69
+ XCOLOR,
70
+ TIKZ,
71
+ PGF,
72
+ PGFFOR,
73
+ LISTINGS,
74
+ HYPERREF,
75
+ CLEVEREF,
76
+ BIBLATEX,
77
+ CSQUOTES,
78
+ GLOSSARIES,
79
+ MDFRAMED,
80
+ FONTAWESOME,
81
+ SCRLAYER_SCRPAGE,
82
+ ETOOLBOX,
83
+ SETSPACE,
84
+ RAGGED2E,
85
+ ARRAY,
86
+ LONGTABLE,
87
+ NEEDSPACE,
88
+ BABEL,
89
+ }
90
+ )
91
+
92
+ DEFAULT_GEOMETRY: Final[dict[str, str]] = {
93
+ "a4paper": "",
94
+ "top": "2cm",
95
+ "bottom": "2cm",
96
+ "left": "2cm",
97
+ "right": "2cm",
98
+ }
99
+
100
+ # Back-matter print commands. \printglossary/\printbibliography emit their own
101
+ # \chapter* heading (and page break), so no manual \clearpage precedes them.
102
+ BACKMATTER_HEADER = r"\newpage\appendix\backmatter\HSRTBackMattertrue"
103
+ GLOSSARY_PRINT = r"\renewcommand*{\entryname}{Wort}\printglossary"
104
+ ACRONYM_PRINT = r"\renewcommand*{\entryname}{Abkürzung}\printglossary[type=\acronymtype,title=Abkürzungen]" # noqa: E501
105
+ BIBLIOGRAPHY_PRINT = r"\clearpage\chapter*{Literaturverzeichnis}\label{chap:bibliography}\printbibliography[heading=none,title={}]" # noqa: E501
106
+
107
+
108
+ def _emit(dest: Path, data: bytes) -> str:
109
+ """Write `data` to `dest` (creating parent dirs) and return its posix path."""
110
+ dest.parent.mkdir(parents=True, exist_ok=True)
111
+ dest.write_bytes(data)
112
+ return dest.as_posix()
113
+
114
+
115
+ @Registry.add
116
+ @dataclass
117
+ class HSRTReport(KomaDocument):
118
+ """HSRT report: scrbook + KomaDocument + auto preamble + colour-collector."""
119
+
120
+ document_class: str = "scrbook"
121
+
122
+ variant: Variant = Variant.INF
123
+ show_toc: bool = True
124
+ show_titlepage: bool = True
125
+ show_glossary: bool = False
126
+ show_acronyms: bool = False
127
+ show_bibliography: bool = False
128
+ show_footer_logos: bool = False
129
+
130
+ title: TeX | str | None = None
131
+ author: TeX | str | None = None
132
+ abstract: TeX | str | None = None
133
+ keywords: TeX | str | None = None
134
+ data_lines: tuple[TitlePageDataLine, ...] = ()
135
+
136
+ inline_logos: bool = True
137
+ inline_fonts: bool = True
138
+ main_font: str | None = None
139
+ sans_font: str | None = None
140
+ geometry_options: dict[str, str] = field(
141
+ default_factory=lambda: dict(DEFAULT_GEOMETRY)
142
+ )
143
+ user_preamble: TeX | str = Empty
144
+
145
+ def __post_init__(self) -> None:
146
+ super().__post_init__()
147
+ if self.document_class != "scrbook":
148
+ raise ValueError(
149
+ "HSRTReport requires document_class='scrbook', "
150
+ + f"got {self.document_class!r}"
151
+ )
152
+ self.extra_packages: frozenset[PackageProtocol] = (
153
+ frozenset(self.extra_packages) | BASE_PACKAGES
154
+ )
155
+ self.preamble: TeX | str = self._build_preamble()
156
+
157
+ def discovered_colors(self) -> tuple[Color, ...]:
158
+ """Walk body + preamble, return every `Color` instance needing `\\definecolor`.
159
+
160
+ Also includes HSRT hyperref colours (stored as Python data inside the
161
+ hypersetup options dict, so unreachable via the tree walk).
162
+ """
163
+ seen: dict[str, Color] = {}
164
+ for c in (HSRT_CITE_COLOR, HSRT_LINK_COLOR, HSRT_URL_COLOR):
165
+ seen.setdefault(c.name, c)
166
+ for root in (self.body, self.user_preamble):
167
+ for color in collect_colors(coerce_tex(root)):
168
+ seen.setdefault(color.name, color)
169
+ return tuple(seen.values())
170
+
171
+ def _color_definitions(self) -> TeX:
172
+ return Concat(
173
+ *(
174
+ Definecolor(c.name, c.spec.model, c.spec.value)
175
+ for c in self.discovered_colors()
176
+ if c.spec is not None
177
+ )
178
+ )
179
+
180
+ def _build_preamble(self) -> TeX:
181
+ return Concat(*self._preamble_parts())
182
+
183
+ def _preamble_parts(self) -> Iterator[TeX | str]:
184
+ yield Raw(r"\KOMAoptions{open=any,twoside=false}")
185
+ yield Geometry(self.geometry_options)
186
+ yield HSRTColors()
187
+ yield self._color_definitions()
188
+ yield HSRTHyperref()
189
+ yield GermanCrefNames()
190
+ if self.show_glossary or self.show_acronyms:
191
+ yield HSRTGlossarySetup()
192
+ yield HSRTListingStyles()
193
+ yield AcrShortcut()
194
+ # Page setup first — provides \providecommand{\blenderfont} fallback
195
+ # that HSRTFontSetup's \renewcommand{\blenderfont} requires.
196
+ yield HSRTPageSetup()
197
+ # The skyline is drawn on every page; footer logos only when requested.
198
+ logo_names = default_logo_names(self.variant) if self.show_footer_logos else ()
199
+ yield Raw(footer_logo_hook(logo_names), allow_replacements=False)
200
+ if self.inline_fonts:
201
+ yield HSRTFontSetup()
202
+ if self.main_font is not None:
203
+ yield Setmainfont(self.main_font)
204
+ if self.sans_font is not None:
205
+ yield Setsansfont(self.sans_font)
206
+ # \title / \author for running headers
207
+ if self.title is not None:
208
+ yield Raw(f"\\title{{{coerce_tex(self.title).rendered}}}")
209
+ if self.author is not None:
210
+ yield Raw(f"\\author{{{coerce_tex(self.author).rendered}}}")
211
+ if self.user_preamble is not Empty:
212
+ yield self.user_preamble
213
+
214
+ def _build_full_body(self) -> TeX:
215
+ """Wrap user body with front/main/back matter, ToC, glossary, bibliography."""
216
+ return Concat(*self._body_parts())
217
+
218
+ def _body_parts(self) -> Iterator[TeX | str]:
219
+ # -- Front matter --
220
+ yield Raw(r"\frontmatter")
221
+ if self.show_titlepage and self.title is not None:
222
+ yield TitlePage(
223
+ title=self.title,
224
+ abstract=self.abstract or "",
225
+ keywords=self.keywords or "",
226
+ data_lines=self.data_lines,
227
+ logo_names=default_logo_names(self.variant),
228
+ )
229
+ if self.show_toc:
230
+ yield Raw(r"\newpage\tableofcontents")
231
+
232
+ # -- Main matter --
233
+ yield Raw(r"\mainmatter")
234
+ yield coerce_tex(self.body)
235
+
236
+ # -- Back matter --
237
+ # The header is only emitted when there is actual back-matter content:
238
+ # \backmatter calls hyperref's \bookmarksetup which fires \@ in vertical
239
+ # mode and crashes, so skip it entirely when there is nothing to show.
240
+ if self.show_glossary or self.show_acronyms or self.show_bibliography:
241
+ yield Raw(BACKMATTER_HEADER)
242
+ if self.show_glossary:
243
+ yield Raw(GLOSSARY_PRINT)
244
+ if self.show_acronyms:
245
+ yield Raw(ACRONYM_PRINT)
246
+ if self.show_bibliography:
247
+ yield Raw(BIBLIOGRAPHY_PRINT)
248
+
249
+ def write_inline_fonts(self, target_dir: str = ".") -> tuple[str, ...]:
250
+ """Write bundled font TTF files to ``<target_dir>/fonts/`` for compilation.
251
+
252
+ Call this before the TeX run so fontspec can resolve the font paths
253
+ embedded in the preamble by `HSRTFontSetup`.
254
+ """
255
+ if not self.inline_fonts:
256
+ return ()
257
+ from .fonts import FONT_OUTPUT_DIR, all_font_paths, rel
258
+
259
+ base = Path(target_dir)
260
+ return tuple(
261
+ _emit(base / FONT_OUTPUT_DIR / rel(font_path), font_path.read_bytes())
262
+ for font_path in all_font_paths()
263
+ )
264
+
265
+ def write_inline_logos(self, target_dir: str = ".") -> tuple[str, ...]:
266
+ """Write the logos used by the tikz overlays to ``<target_dir>/logos/``.
267
+
268
+ The titlepage overlay and footer hook reference logos by relative path
269
+ (``logos/<file>``); this materialises those files next to the .tex so
270
+ tectonic can read them without tripping its absolute-path restrictions.
271
+ SVG sources are converted to PDF via `IncludeImage`.
272
+ """
273
+ from pytex.model.image import IncludeImage
274
+
275
+ from .logos import LOGO_OUTPUT_DIR, logo_output_name, logo_path
276
+
277
+ # Titlepage overlay + footer hook use the variant defaults; the footer
278
+ # skyline is emitted on every page regardless of show_footer_logos.
279
+ names = sorted(set(default_logo_names(self.variant)) | {"Skyline"})
280
+ base = Path(target_dir)
281
+ return tuple(
282
+ # IncludeImage.read_bytes converts svg -> pdf on the fly.
283
+ _emit(
284
+ base / LOGO_OUTPUT_DIR / logo_output_name(name),
285
+ IncludeImage(path=logo_path(name), inline_base64=False).read_bytes(),
286
+ )
287
+ for name in names
288
+ )
289
+
290
+ def default_logos(self) -> TeX:
291
+ return DefaultLogos(self.variant, inline_base64=self.inline_logos)
292
+
293
+ @property
294
+ @override
295
+ def rendered(self) -> str:
296
+ return Concat(
297
+ DocumentClass(self.document_class, self.document_class_options),
298
+ # hyperfootnotes is only honoured as a hyperref load option, so it
299
+ # must be queued before \usepackage{hyperref}. The HSRT footnote
300
+ # setup never places hyperref's Hfootnote destination, so leaving
301
+ # footnote-mark links enabled produces dangling links.
302
+ Raw(r"\PassOptionsToPackage{hyperfootnotes=false}{hyperref}"),
303
+ *self.ordered_packages(),
304
+ self.inline_image_block,
305
+ self.preamble,
306
+ Environment("document", self._build_full_body()),
307
+ ).rendered
@@ -0,0 +1,66 @@
1
+ from importlib.resources import files
2
+ from pathlib import Path
3
+ from typing import Final
4
+
5
+ from pytex.commands.fontspec import Newfontfamily, Setmainfont, Setsansfont
6
+ from pytex.interface.tex import TeX
7
+ from pytex.model.concat import Concat
8
+ from pytex.model.raw import Raw
9
+ from pytex.registry import Registry
10
+
11
+ FONT_DIR: Final[Path] = Path(str(files("pytex_hsrtreport").joinpath("assets/fonts")))
12
+
13
+ # Output dir for the bundled TTFs, relative to the .tex file. `HSRTReport`'s
14
+ # `write_inline_fonts` copies the real font files here; fontspec's Path= option
15
+ # (see `_font_opts`) loads them from <this>/<subfamily>/.
16
+ FONT_OUTPUT_DIR: Final[str] = "fonts"
17
+
18
+
19
+ def all_font_paths() -> tuple[Path, ...]:
20
+ """Return all bundled TTF paths, sorted for reproducible output."""
21
+ return tuple(sorted(FONT_DIR.rglob("*.ttf")))
22
+
23
+
24
+ def rel(font_path: Path) -> str:
25
+ """Path of a font file relative to `FONT_DIR`."""
26
+ return font_path.relative_to(FONT_DIR).as_posix()
27
+
28
+
29
+ def _font_opts(subfamily: str, upright: str, italic: str) -> dict[str, str]:
30
+ return {
31
+ "Path": f"{FONT_OUTPUT_DIR}/{subfamily}/",
32
+ "Extension": ".ttf",
33
+ "UprightFont": upright,
34
+ "BoldFont": "*-Bold",
35
+ "ItalicFont": italic,
36
+ "BoldItalicFont": "*-BoldItalic",
37
+ }
38
+
39
+
40
+ @Registry.add
41
+ def HSRTFontSetup() -> TeX:
42
+ """fontspec setup for bundled DIN (main) and Blender (sans) fonts.
43
+
44
+ Mirrors ``Config/Fonts.tex`` from the original template. The font files
45
+ are expected at ``fonts/DIN/`` and ``fonts/Blender/`` relative to the
46
+ output ``.tex`` file, which is where ``HSRTReport.write_inline_fonts``
47
+ copies the bundled TTFs before the TeX run.
48
+ """
49
+ blender_opts = _font_opts("Blender", "*-Medium", "*-MediumItalic")
50
+ din_opts = _font_opts("DIN", "*-Regular", "*-Italic")
51
+ return Concat(
52
+ Newfontfamily("\\BlenderFont", "Blender", options=blender_opts),
53
+ Newfontfamily("\\DINFont", "DIN", options=din_opts),
54
+ Raw(r"\renewcommand{\blenderfont}{\BlenderFont}"),
55
+ Raw(r"\renewcommand{\dinfont}{\DINFont}"),
56
+ Setsansfont("Blender", options=blender_opts),
57
+ Setmainfont("DIN", options=din_opts),
58
+ )
59
+
60
+
61
+ __all__ = [
62
+ "FONT_OUTPUT_DIR",
63
+ "HSRTFontSetup",
64
+ "all_font_paths",
65
+ "rel",
66
+ ]
@@ -0,0 +1,61 @@
1
+ from pytex.commands.glossaries import (
2
+ Glsenablehyper,
3
+ Makeglossaries,
4
+ Setglossarystyle,
5
+ )
6
+ from pytex.interface.tex import TeX
7
+ from pytex.model.concat import Concat
8
+ from pytex.model.raw import Raw
9
+ from pytex.registry import Registry
10
+
11
+ __all__ = ["AcrShortcut", "HSRTGlossarySetup"]
12
+
13
+ # Custom L/C/R column types used by the manualfixedwidth glossary style. Each
14
+ # fixes a horizontal alignment while still allowing manual \newline breaks.
15
+ COLUMN_TYPES = (
16
+ r"\newcolumntype{L}[1]{>{\raggedright\let\newline\\\arraybackslash\hspace{0pt}}p{#1}}"
17
+ + r"\newcolumntype{C}[1]{>{\centering\let\newline\\\arraybackslash\hspace{0pt}}p{#1}}"
18
+ + r"\newcolumntype{R}[1]{>{\raggedleft\let\newline\\\arraybackslash\hspace{0pt}}p{#1}}"
19
+ )
20
+
21
+ # A three-column glossary style with fixed widths (30%/58%/10% of \textwidth).
22
+ GLOSSARY_STYLE = (
23
+ r"\newglossarystyle{manualfixedwidth}{"
24
+ + r"\setglossarystyle{long3colheader}"
25
+ + r"\renewenvironment{theglossary}{\begin{longtable}{@{}"
26
+ + r"L{\dimexpr0.30\textwidth-\tabcolsep\relax}"
27
+ + r" p{\dimexpr0.58\textwidth-\tabcolsep\relax}"
28
+ + r" L{\dimexpr0.10\textwidth-\tabcolsep\relax}@{}}}{\end{longtable}}"
29
+ + r"\renewcommand*{\glsgroupskip}{}"
30
+ + r"\renewcommand{\arraystretch}{1.1}}"
31
+ )
32
+
33
+ # German column/section labels for the glossary and acronym lists.
34
+ GLOSSARY_LABELS = (
35
+ r"\renewcommand*{\entryname}{Wort/Abkürzung}"
36
+ + r"\renewcommand*{\descriptionname}{Bedeutung}"
37
+ + r"\renewcommand*{\pagelistname}{Seite(n)}"
38
+ + r"\renewcommand{\acronymname}{Abkürzungsverzeichnis}"
39
+ )
40
+
41
+
42
+ @Registry.add
43
+ def HSRTGlossarySetup() -> TeX:
44
+ """Standard HSRT glossary setup.
45
+
46
+ makeglossaries, manualfixedwidth style, German labels.
47
+ """
48
+ return Concat(
49
+ Makeglossaries(),
50
+ Raw(COLUMN_TYPES),
51
+ Raw(GLOSSARY_STYLE),
52
+ Setglossarystyle("manualfixedwidth"),
53
+ Glsenablehyper(),
54
+ Raw(GLOSSARY_LABELS),
55
+ )
56
+
57
+
58
+ @Registry.add
59
+ def AcrShortcut() -> TeX:
60
+ """Shortcut: `\\acr` aliases `\\acrshort`."""
61
+ return Raw(r"\newcommand{\acr}{\acrshort}")
@@ -0,0 +1,49 @@
1
+ from typing import Final
2
+
3
+ from pytex.interface.tex import TeX
4
+ from pytex.model.color import Color
5
+ from pytex.model.control_sequence import ControlSequence, Parameter
6
+ from pytex.model.raw import Raw
7
+ from pytex.registry import Registry
8
+
9
+ __all__ = ["HSRTHyperref"]
10
+
11
+ type HyperOption = bool | int | str | TeX
12
+
13
+ HSRT_CITE_COLOR: Final[Color] = Color.rgb(0.286, 0.427, 0.537, name="hsrtcite")
14
+ HSRT_LINK_COLOR: Final[Color] = Color.rgb(0.161, 0.310, 0.427, name="hsrtlink")
15
+ HSRT_URL_COLOR: Final[Color] = Color.rgb(0.071, 0.212, 0.322, name="hsrturl")
16
+
17
+
18
+ HSRT_HYPER_OPTIONS: Final[dict[str, HyperOption]] = {
19
+ "pdfpagemode": "UseOutlines",
20
+ "bookmarksopen": True,
21
+ "bookmarksopenlevel": 0,
22
+ # plainpages=false + hypertexnames=true gives roman frontmatter pages their
23
+ # own named anchors (page.i, ...) so glossary/index \hyperpage links to
24
+ # those pages resolve instead of dangling against absolute arabic anchors.
25
+ "plainpages": False,
26
+ "hypertexnames": True,
27
+ "colorlinks": True,
28
+ "citecolor": HSRT_CITE_COLOR,
29
+ "linkcolor": HSRT_LINK_COLOR,
30
+ "urlcolor": HSRT_URL_COLOR,
31
+ "pdfstartview": "FitV",
32
+ "unicode": True,
33
+ "breaklinks": True,
34
+ }
35
+
36
+
37
+ def _format(value: HyperOption) -> str:
38
+ if isinstance(value, bool):
39
+ return "true" if value else "false"
40
+ if isinstance(value, TeX):
41
+ return value.rendered
42
+ return str(value)
43
+
44
+
45
+ @Registry.add
46
+ def HSRTHyperref() -> TeX:
47
+ """Hypersetup using `HSRT_HYPER_OPTIONS` (structured Python, not a TeX blob)."""
48
+ body = ",".join(f"{k}={_format(v)}" for k, v in HSRT_HYPER_OPTIONS.items())
49
+ return ControlSequence("hypersetup", (Parameter(Raw(body)),))
@@ -0,0 +1,90 @@
1
+ from typing import Final
2
+
3
+ from pytex.commands.colors import SelectColor
4
+ from pytex.commands.font import (
5
+ Bfseries,
6
+ Footnotesize,
7
+ Scriptsize,
8
+ Ttfamily,
9
+ )
10
+ from pytex.commands.listings import Lstdefinestyle, Lstset
11
+ from pytex.interface.tex import TeX
12
+ from pytex.model.concat import Concat
13
+ from pytex.registry import Registry
14
+
15
+ __all__ = ["HSRTListingStyles", "style_options"]
16
+
17
+
18
+ def _font(*parts: TeX) -> TeX:
19
+ return Concat(*parts)
20
+
21
+
22
+ HSRT_LISTING_BASE: Final[dict[str, TeX | str]] = {
23
+ "basicstyle": _font(Footnotesize(), Ttfamily()),
24
+ "breaklines": "true",
25
+ "numbers": "left",
26
+ "frame": "single",
27
+ "float": "H",
28
+ }
29
+
30
+
31
+ HSRT_LISTING_STYLES: Final[dict[str, dict[str, TeX | str]]] = {
32
+ "htmlCode": {
33
+ "language": "html",
34
+ "basicstyle": _font(Scriptsize(), Ttfamily()),
35
+ "keywordstyle": _font(SelectColor("blue"), Bfseries(), Ttfamily()),
36
+ "commentstyle": _font(SelectColor("gray"), Ttfamily()),
37
+ "escapechar": "|",
38
+ },
39
+ "phpCode": {
40
+ "language": "php",
41
+ "morekeywords": "{php}",
42
+ "basicstyle": _font(Footnotesize(), Ttfamily()),
43
+ "keywordstyle": _font(SelectColor("blue"), Bfseries(), Ttfamily()),
44
+ "commentstyle": _font(SelectColor("gray"), Ttfamily()),
45
+ "escapechar": "|",
46
+ },
47
+ "jsCode": {
48
+ "language": "javascript",
49
+ "basicstyle": _font(Scriptsize(), Ttfamily()),
50
+ "keywordstyle": _font(SelectColor("blue"), Bfseries(), Ttfamily()),
51
+ "commentstyle": _font(SelectColor("gray"), Ttfamily()),
52
+ "escapechar": "|",
53
+ },
54
+ "shellCodeNOPASSWD": {
55
+ "language": "sh",
56
+ "deletekeywords": "{for,kill,cat}",
57
+ "morekeywords": "{sudo}",
58
+ "basicstyle": _font(Scriptsize(), Ttfamily()),
59
+ "keywordstyle": _font(SelectColor("blue"), Bfseries(), Ttfamily()),
60
+ "commentstyle": _font(SelectColor("gray"), Ttfamily()),
61
+ "escapechar": "|",
62
+ "numbers": "none",
63
+ },
64
+ "shellCode": {
65
+ "language": "sh",
66
+ "morekeywords": "{sudo,chmod,chown,cp,su,rm,python}",
67
+ "basicstyle": _font(Scriptsize(), Ttfamily()),
68
+ "keywordstyle": _font(SelectColor("blue"), Bfseries(), Ttfamily()),
69
+ "commentstyle": _font(SelectColor("gray"), Ttfamily()),
70
+ "escapechar": "|",
71
+ },
72
+ "URL": {
73
+ "basicstyle": _font(Footnotesize(), Ttfamily()),
74
+ "commentstyle": _font(SelectColor("gray"), Ttfamily()),
75
+ "escapechar": "|",
76
+ "numbers": "none",
77
+ },
78
+ }
79
+
80
+
81
+ @Registry.add
82
+ def HSRTListingStyles() -> TeX:
83
+ return Concat(
84
+ Lstset(HSRT_LISTING_BASE),
85
+ *(Lstdefinestyle(name, opts) for name, opts in HSRT_LISTING_STYLES.items()),
86
+ )
87
+
88
+
89
+ def style_options(name: str) -> dict[str, TeX | str]:
90
+ return dict(HSRT_LISTING_STYLES[name])