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,63 @@
1
+ from typing import Final
2
+
3
+ from pytex.helpers.with_package import with_package
4
+ from pytex.interface.tex import TeX
5
+ from pytex.model.control_sequence import ControlSequence, Parameter
6
+ from pytex.model.package import DefinePackage
7
+ from pytex.model.raw import Raw
8
+ from pytex.packages import ACCSUPP, IFTHEN, PGFFOR, XCOLOR
9
+ from pytex.registry import Registry
10
+
11
+ __all__ = ["DraftWatermark", "WatermarkCounter", "WatermarkPackages"]
12
+
13
+ DRAFTWATERMARK: Final = DefinePackage("draftwatermark")
14
+
15
+
16
+ def _watermark_text(text: str) -> str:
17
+ """Build the tiled watermark grid: 100 rows × 16 cols, accessibility-friendly."""
18
+ safe = text.replace("\\", "\\\\").replace("{", "\\{").replace("}", "\\}")
19
+ return (
20
+ r"\setcounter{it}{1}"
21
+ + r"\whiledo{\theit<100}{"
22
+ + r"\foreach \col in {0,...,15}{\color{black!5}\BeginAccSupp{ActualText=}"
23
+ + f"{safe}~~"
24
+ + r"\EndAccSupp{}}\\"
25
+ + r"\stepcounter{it}}"
26
+ )
27
+
28
+
29
+ @Registry.add
30
+ @with_package(DRAFTWATERMARK)
31
+ @with_package(IFTHEN)
32
+ @with_package(PGFFOR)
33
+ @with_package(ACCSUPP)
34
+ @with_package(XCOLOR)
35
+ def DraftWatermark(
36
+ text: str,
37
+ scale: float = 0.08,
38
+ angle: float = 45,
39
+ color: str = "black!12",
40
+ ) -> TeX:
41
+ """Configure draftwatermark with tiled accessibility-safe text grid."""
42
+ body = (
43
+ f"scale={scale},angle={angle},"
44
+ + f"text={{\\begin{{tabular}}{{c}}{_watermark_text(text)}\\end{{tabular}}}},"
45
+ + f"color={color}"
46
+ )
47
+ return ControlSequence("DraftwatermarkOptions", (Parameter(Raw(body)),))
48
+
49
+
50
+ @Registry.add
51
+ def WatermarkCounter() -> TeX:
52
+ """Declares the `it` counter used by DraftWatermark. Emit once in preamble."""
53
+ return Raw("\\newcounter{it}")
54
+
55
+
56
+ @Registry.add
57
+ def WatermarkPackages() -> TeX:
58
+ """Render `\\usepackage` lines for all packages required by watermark."""
59
+ return ControlSequence(
60
+ "RequirePackage",
61
+ (Parameter("draftwatermark"),),
62
+ required_packages=frozenset({IFTHEN, PGFFOR, ACCSUPP, XCOLOR, DRAFTWATERMARK}),
63
+ )
@@ -0,0 +1,33 @@
1
+ from pytex.commands.definitions import Newcommand
2
+ from pytex.interface.tex import TeX
3
+ from pytex.model.concat import Concat
4
+ from pytex.model.raw import Raw
5
+ from pytex.registry import Registry
6
+
7
+ __all__ = ["WordcountCommands"]
8
+
9
+
10
+ @Registry.add
11
+ def WordcountCommands() -> TeX:
12
+ """Define `\\quickwordcount{<doc>}` + `\\detailtexcount{<doc>}` macros.
13
+
14
+ Both shell out to `texcount` and require `-shell-escape` build.
15
+ """
16
+ return Concat(
17
+ Newcommand(
18
+ r"\quickwordcount",
19
+ Raw(
20
+ r"\immediate\write18{texcount -1 -sum -merge -q #1.tex > Build/words.sum }" # noqa: E501
21
+ + r"\input{Build/words.sum} words"
22
+ ),
23
+ nargs=1,
24
+ ),
25
+ Newcommand(
26
+ r"\detailtexcount",
27
+ Raw(
28
+ r"\immediate\write18{texcount -merge -sum -q #1.tex > Build/.wcdetail }"
29
+ + r"\verbatiminput{Build/.wcdetail}"
30
+ ),
31
+ nargs=1,
32
+ ),
33
+ )
pytex_koma/__init__.py ADDED
@@ -0,0 +1,90 @@
1
+ from . import commands
2
+ from .commands import (
3
+ Addchap,
4
+ Addmargin,
5
+ Addpart,
6
+ Addsec,
7
+ Addtokomafont,
8
+ Appendix,
9
+ Areaset,
10
+ Automark,
11
+ Backmatter,
12
+ Captionabove,
13
+ Captionbelow,
14
+ Cfoot,
15
+ Chead,
16
+ Clearpairofpagestyles,
17
+ Clearscrheadfoot,
18
+ Dedication,
19
+ Dictum,
20
+ Extratitle,
21
+ Frontmatter,
22
+ Ifoot,
23
+ Ihead,
24
+ KOMAoption,
25
+ KOMAoptions,
26
+ Labeling,
27
+ Lowertitleback,
28
+ Mainmatter,
29
+ Marginline,
30
+ Minisec,
31
+ Ofoot,
32
+ Ohead,
33
+ Pagestyle,
34
+ Publishers,
35
+ Recalctypearea,
36
+ Setkomafont,
37
+ Subject,
38
+ Subtitle,
39
+ Titlehead,
40
+ Typearea,
41
+ Uppertitleback,
42
+ Usekomafont,
43
+ )
44
+ from .document import KOMA_CLASSES, KomaDocument
45
+
46
+ __all__ = [
47
+ "KOMA_CLASSES",
48
+ "Addchap",
49
+ "Addmargin",
50
+ "Addpart",
51
+ "Addsec",
52
+ "Addtokomafont",
53
+ "Appendix",
54
+ "Areaset",
55
+ "Automark",
56
+ "Backmatter",
57
+ "Captionabove",
58
+ "Captionbelow",
59
+ "Cfoot",
60
+ "Chead",
61
+ "Clearpairofpagestyles",
62
+ "Clearscrheadfoot",
63
+ "Dedication",
64
+ "Dictum",
65
+ "Extratitle",
66
+ "Frontmatter",
67
+ "Ifoot",
68
+ "Ihead",
69
+ "KOMAoption",
70
+ "KOMAoptions",
71
+ "KomaDocument",
72
+ "Labeling",
73
+ "Lowertitleback",
74
+ "Mainmatter",
75
+ "Marginline",
76
+ "Minisec",
77
+ "Ofoot",
78
+ "Ohead",
79
+ "Pagestyle",
80
+ "Publishers",
81
+ "Recalctypearea",
82
+ "Setkomafont",
83
+ "Subject",
84
+ "Subtitle",
85
+ "Titlehead",
86
+ "Typearea",
87
+ "Uppertitleback",
88
+ "Usekomafont",
89
+ "commands",
90
+ ]
pytex_koma/commands.py ADDED
@@ -0,0 +1,296 @@
1
+ from pytex.helpers.with_package import with_package
2
+ from pytex.interface.tex import TeX
3
+ from pytex.model.control_sequence import ControlSequence, Parameter
4
+ from pytex.model.environment import Environment
5
+ from pytex.packages import SCRLAYER_SCRPAGE, TYPEAREA
6
+ from pytex.registry import Registry
7
+
8
+ __all__ = [
9
+ "Addchap",
10
+ "Addmargin",
11
+ "Addpart",
12
+ "Addsec",
13
+ "Addtokomafont",
14
+ "Appendix",
15
+ "Areaset",
16
+ "Automark",
17
+ "Backmatter",
18
+ "Captionabove",
19
+ "Captionbelow",
20
+ "Cfoot",
21
+ "Chead",
22
+ "Clearpairofpagestyles",
23
+ "Clearscrheadfoot",
24
+ "Dedication",
25
+ "Dictum",
26
+ "Extratitle",
27
+ "Frontmatter",
28
+ "Ifoot",
29
+ "Ihead",
30
+ "KOMAoption",
31
+ "KOMAoptions",
32
+ "Labeling",
33
+ "Lowertitleback",
34
+ "Mainmatter",
35
+ "Marginline",
36
+ "Minisec",
37
+ "Ofoot",
38
+ "Ohead",
39
+ "Pagestyle",
40
+ "Publishers",
41
+ "Recalctypearea",
42
+ "Setkomafont",
43
+ "Subject",
44
+ "Subtitle",
45
+ "Titlehead",
46
+ "Typearea",
47
+ "Uppertitleback",
48
+ "Usekomafont",
49
+ ]
50
+
51
+
52
+ def _sectioning(name: str, title: TeX | str, short: TeX | str | None) -> TeX:
53
+ if short is None:
54
+ return ControlSequence(name, (Parameter(title),))
55
+ return ControlSequence(name, (Parameter(short, optional=True), Parameter(title)))
56
+
57
+
58
+ @Registry.add
59
+ def Addpart(title: TeX | str, short: TeX | str | None = None) -> TeX:
60
+ return _sectioning("addpart", title, short)
61
+
62
+
63
+ @Registry.add
64
+ def Addchap(title: TeX | str, short: TeX | str | None = None) -> TeX:
65
+ return _sectioning("addchap", title, short)
66
+
67
+
68
+ @Registry.add
69
+ def Addsec(title: TeX | str, short: TeX | str | None = None) -> TeX:
70
+ return _sectioning("addsec", title, short)
71
+
72
+
73
+ @Registry.add
74
+ def Minisec(title: TeX | str) -> TeX:
75
+ return ControlSequence("minisec", (Parameter(title),))
76
+
77
+
78
+ @Registry.add
79
+ def Frontmatter() -> TeX:
80
+ return ControlSequence("frontmatter", ())
81
+
82
+
83
+ @Registry.add
84
+ def Mainmatter() -> TeX:
85
+ return ControlSequence("mainmatter", ())
86
+
87
+
88
+ @Registry.add
89
+ def Backmatter() -> TeX:
90
+ return ControlSequence("backmatter", ())
91
+
92
+
93
+ @Registry.add
94
+ def Appendix() -> TeX:
95
+ return ControlSequence("appendix", ())
96
+
97
+
98
+ @Registry.add
99
+ def Subtitle(text: TeX | str) -> TeX:
100
+ return ControlSequence("subtitle", (Parameter(text),))
101
+
102
+
103
+ @Registry.add
104
+ def Subject(text: TeX | str) -> TeX:
105
+ return ControlSequence("subject", (Parameter(text),))
106
+
107
+
108
+ @Registry.add
109
+ def Publishers(text: TeX | str) -> TeX:
110
+ return ControlSequence("publishers", (Parameter(text),))
111
+
112
+
113
+ @Registry.add
114
+ def Titlehead(text: TeX | str) -> TeX:
115
+ return ControlSequence("titlehead", (Parameter(text),))
116
+
117
+
118
+ @Registry.add
119
+ def Dedication(text: TeX | str) -> TeX:
120
+ return ControlSequence("dedication", (Parameter(text),))
121
+
122
+
123
+ @Registry.add
124
+ def Uppertitleback(text: TeX | str) -> TeX:
125
+ return ControlSequence("uppertitleback", (Parameter(text),))
126
+
127
+
128
+ @Registry.add
129
+ def Lowertitleback(text: TeX | str) -> TeX:
130
+ return ControlSequence("lowertitleback", (Parameter(text),))
131
+
132
+
133
+ @Registry.add
134
+ def Extratitle(text: TeX | str) -> TeX:
135
+ return ControlSequence("extratitle", (Parameter(text),))
136
+
137
+
138
+ @Registry.add
139
+ def Dictum(text: TeX | str, author: TeX | str | None = None) -> TeX:
140
+ if author is None:
141
+ return ControlSequence("dictum", (Parameter(text),))
142
+ return ControlSequence(
143
+ "dictum",
144
+ (Parameter(author, optional=True), Parameter(text)),
145
+ )
146
+
147
+
148
+ @Registry.add
149
+ def KOMAoptions(options: dict[str, str]) -> TeX:
150
+ return ControlSequence("KOMAoptions", (Parameter(options),))
151
+
152
+
153
+ @Registry.add
154
+ def KOMAoption(key: str, value: str) -> TeX:
155
+ return ControlSequence("KOMAoption", (Parameter(key), Parameter(value)))
156
+
157
+
158
+ @Registry.add
159
+ def Setkomafont(element: str, definition: TeX | str) -> TeX:
160
+ return ControlSequence(
161
+ "setkomafont",
162
+ (Parameter(element), Parameter(definition)),
163
+ )
164
+
165
+
166
+ @Registry.add
167
+ def Addtokomafont(element: str, definition: TeX | str) -> TeX:
168
+ return ControlSequence(
169
+ "addtokomafont",
170
+ (Parameter(element), Parameter(definition)),
171
+ )
172
+
173
+
174
+ @Registry.add
175
+ def Usekomafont(element: str) -> TeX:
176
+ return ControlSequence("usekomafont", (Parameter(element),))
177
+
178
+
179
+ @Registry.add
180
+ def Captionabove(text: TeX | str) -> TeX:
181
+ return ControlSequence("captionabove", (Parameter(text),))
182
+
183
+
184
+ @Registry.add
185
+ def Captionbelow(text: TeX | str) -> TeX:
186
+ return ControlSequence("captionbelow", (Parameter(text),))
187
+
188
+
189
+ @Registry.add
190
+ def Marginline(text: TeX | str) -> TeX:
191
+ return ControlSequence("marginline", (Parameter(text),))
192
+
193
+
194
+ @Registry.add
195
+ def Addmargin(body: TeX | str, amount: str) -> TeX:
196
+ return Environment("addmargin", body, (Parameter(amount),))
197
+
198
+
199
+ @Registry.add
200
+ def Labeling(body: TeX | str, sep: str = "") -> TeX:
201
+ params = (Parameter(sep, optional=True),) if sep else ()
202
+ return Environment("labeling", body, params)
203
+
204
+
205
+ @Registry.add
206
+ @with_package(TYPEAREA)
207
+ def Areaset(width: str, height: str, bcor: str | None = None) -> TeX:
208
+ if bcor is None:
209
+ return ControlSequence("areaset", (Parameter(width), Parameter(height)))
210
+ return ControlSequence(
211
+ "areaset",
212
+ (Parameter(bcor, optional=True), Parameter(width), Parameter(height)),
213
+ )
214
+
215
+
216
+ @Registry.add
217
+ @with_package(TYPEAREA)
218
+ def Typearea(divisor: int | str) -> TeX:
219
+ return ControlSequence("typearea", (Parameter(str(divisor)),))
220
+
221
+
222
+ @Registry.add
223
+ @with_package(TYPEAREA)
224
+ def Recalctypearea() -> TeX:
225
+ return ControlSequence("recalctypearea", ())
226
+
227
+
228
+ @Registry.add
229
+ @with_package(SCRLAYER_SCRPAGE)
230
+ def Pagestyle(name: str) -> TeX:
231
+ return ControlSequence("pagestyle", (Parameter(name),))
232
+
233
+
234
+ @Registry.add
235
+ @with_package(SCRLAYER_SCRPAGE)
236
+ def Clearpairofpagestyles() -> TeX:
237
+ return ControlSequence("clearpairofpagestyles", ())
238
+
239
+
240
+ @Registry.add
241
+ @with_package(SCRLAYER_SCRPAGE)
242
+ def Clearscrheadfoot() -> TeX:
243
+ return ControlSequence("clearscrheadfoot", ())
244
+
245
+
246
+ @Registry.add
247
+ @with_package(SCRLAYER_SCRPAGE)
248
+ def Automark(level: str, second: str | None = None) -> TeX:
249
+ if second is None:
250
+ return ControlSequence("automark", (Parameter(level),))
251
+ return ControlSequence(
252
+ "automark",
253
+ (Parameter(second, optional=True), Parameter(level)),
254
+ )
255
+
256
+
257
+ def _scoped_head_foot(name: str, scope: str | None, body: TeX | str) -> TeX:
258
+ if scope is None:
259
+ return ControlSequence(name, (Parameter(body),))
260
+ return ControlSequence(name, (Parameter(scope, optional=True), Parameter(body)))
261
+
262
+
263
+ @Registry.add
264
+ @with_package(SCRLAYER_SCRPAGE)
265
+ def Ihead(body: TeX | str, scope: str | None = None) -> TeX:
266
+ return _scoped_head_foot("ihead", scope, body)
267
+
268
+
269
+ @Registry.add
270
+ @with_package(SCRLAYER_SCRPAGE)
271
+ def Chead(body: TeX | str, scope: str | None = None) -> TeX:
272
+ return _scoped_head_foot("chead", scope, body)
273
+
274
+
275
+ @Registry.add
276
+ @with_package(SCRLAYER_SCRPAGE)
277
+ def Ohead(body: TeX | str, scope: str | None = None) -> TeX:
278
+ return _scoped_head_foot("ohead", scope, body)
279
+
280
+
281
+ @Registry.add
282
+ @with_package(SCRLAYER_SCRPAGE)
283
+ def Ifoot(body: TeX | str, scope: str | None = None) -> TeX:
284
+ return _scoped_head_foot("ifoot", scope, body)
285
+
286
+
287
+ @Registry.add
288
+ @with_package(SCRLAYER_SCRPAGE)
289
+ def Cfoot(body: TeX | str, scope: str | None = None) -> TeX:
290
+ return _scoped_head_foot("cfoot", scope, body)
291
+
292
+
293
+ @Registry.add
294
+ @with_package(SCRLAYER_SCRPAGE)
295
+ def Ofoot(body: TeX | str, scope: str | None = None) -> TeX:
296
+ return _scoped_head_foot("ofoot", scope, body)
pytex_koma/document.py ADDED
@@ -0,0 +1,138 @@
1
+ # pyright: reportAny=false
2
+ from collections.abc import Iterator
3
+ from dataclasses import dataclass, field
4
+
5
+ from pytex.interface.package import PackageOption
6
+ from pytex.model.document import Document
7
+ from pytex.registry import Registry
8
+
9
+ __all__ = ["KomaDocument"]
10
+
11
+ KOMA_CLASSES: frozenset[str] = frozenset(
12
+ {"scrartcl", "scrreprt", "scrbook", "scrlttr2"}
13
+ )
14
+
15
+ PAPER_FLAGS: frozenset[str] = frozenset(
16
+ {"a4paper", "a5paper", "b5paper", "letterpaper", "executivepaper", "legalpaper"}
17
+ )
18
+ FONTSIZE_FLAGS: frozenset[str] = frozenset({"10pt", "11pt", "12pt"})
19
+
20
+
21
+ def _on_off(value: bool, on: str, off: str) -> str:
22
+ return on if value else off
23
+
24
+
25
+ @Registry.add
26
+ @dataclass
27
+ class KomaDocument(Document):
28
+ document_class: str = "scrartcl"
29
+
30
+ paper: str | None = None
31
+ fontsize: str | None = None
32
+ bcor: str | None = None
33
+ div: int | str | None = None
34
+ pagesize: str | None = None
35
+
36
+ two_side: bool | None = None
37
+ two_column: bool | None = None
38
+ landscape: bool | None = None
39
+ title_page: bool | None = None
40
+ draft: bool | None = None
41
+
42
+ open_at: str | None = None
43
+ chapter_prefix: bool | None = None
44
+ appendix_prefix: bool | None = None
45
+
46
+ headings: str | None = None
47
+ parskip: str | None = None
48
+ numbers: str | None = None
49
+ captions: str | None = None
50
+ toc: str | None = None
51
+ listof: str | None = None
52
+ bibliography: str | None = None
53
+ index: str | None = None
54
+ footnotes: str | None = None
55
+
56
+ head_include: bool | None = None
57
+ foot_include: bool | None = None
58
+ mp_include: bool | None = None
59
+
60
+ use_geometry: bool | None = None
61
+
62
+ extra_class_options: set[PackageOption] = field(default_factory=set)
63
+
64
+ def __post_init__(self) -> None:
65
+ super().__post_init__()
66
+ if self.document_class not in KOMA_CLASSES:
67
+ raise ValueError(
68
+ f"Unknown KOMA-Script class {self.document_class!r}; "
69
+ + f"expected one of {sorted(KOMA_CLASSES)}"
70
+ )
71
+ self.document_class_options: set[PackageOption] = (
72
+ set(self.document_class_options)
73
+ | set(self.extra_class_options)
74
+ | set(self._class_option_flags())
75
+ )
76
+
77
+ def _class_option_flags(self) -> Iterator[PackageOption]:
78
+ """Translate the typed KOMA fields into raw class options."""
79
+ # Value-bearing options: a known keyword flag, otherwise a key=value pair.
80
+ if self.paper is not None:
81
+ yield self.paper if self.paper in PAPER_FLAGS else ("paper", self.paper)
82
+ if self.fontsize is not None:
83
+ yield (
84
+ self.fontsize
85
+ if self.fontsize in FONTSIZE_FLAGS
86
+ else ("fontsize", self.fontsize)
87
+ )
88
+ if self.bcor is not None:
89
+ yield ("BCOR", self.bcor)
90
+ if self.div is not None:
91
+ yield ("DIV", str(self.div))
92
+ if self.pagesize is not None:
93
+ yield ("pagesize", self.pagesize)
94
+
95
+ # Boolean toggles mapped to their on/off keywords.
96
+ if self.two_side is not None:
97
+ yield _on_off(self.two_side, "twoside", "oneside")
98
+ if self.two_column is not None:
99
+ yield _on_off(self.two_column, "twocolumn", "onecolumn")
100
+ if self.landscape is True:
101
+ yield "landscape"
102
+ if self.title_page is not None:
103
+ yield _on_off(self.title_page, "titlepage", "notitlepage")
104
+ if self.draft is not None:
105
+ yield _on_off(self.draft, "draft", "final")
106
+
107
+ if self.open_at is not None:
108
+ yield ("open", self.open_at)
109
+ if self.chapter_prefix is not None:
110
+ yield ("chapterprefix", _on_off(self.chapter_prefix, "true", "false"))
111
+ if self.appendix_prefix is not None:
112
+ yield ("appendixprefix", _on_off(self.appendix_prefix, "true", "false"))
113
+
114
+ # Plain key=value options taken verbatim from same-named fields.
115
+ for key in (
116
+ "headings",
117
+ "parskip",
118
+ "numbers",
119
+ "captions",
120
+ "toc",
121
+ "listof",
122
+ "bibliography",
123
+ "index",
124
+ "footnotes",
125
+ ):
126
+ value = getattr(self, key)
127
+ if value is not None:
128
+ yield (key, value)
129
+
130
+ if self.head_include is not None:
131
+ yield "headinclude" if self.head_include else "headexclude"
132
+ if self.foot_include is not None:
133
+ yield "footinclude" if self.foot_include else "footexclude"
134
+ if self.mp_include is not None:
135
+ yield "mpinclude" if self.mp_include else "mpexclude"
136
+
137
+ if self.use_geometry is not None:
138
+ yield "usegeometry" if self.use_geometry else "nogeometry"
@@ -0,0 +1,62 @@
1
+ """Markdown -> native PyTeX conversion.
2
+
3
+ Exposes two registered factories:
4
+
5
+ * ``Markdown(content, ...)`` - convert a Markdown string to a ``TeX`` tree.
6
+ * ``IncludeMarkdown(path, ...)`` - read a file and convert it.
7
+
8
+ GitHub-style callouts (``> [!NOTE]`` ...) become HSRT ``ColoredBox`` presets,
9
+ so this package depends on ``pytex_hsrtreport``.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+ from typing import TYPE_CHECKING
16
+
17
+ import marko
18
+
19
+ from pytex.registry import Registry
20
+
21
+ from .convert import MarkdownConverter
22
+ from .escape import escape_latex
23
+
24
+ if TYPE_CHECKING:
25
+ from os import PathLike
26
+
27
+ from pytex.interface.tex import TeX
28
+
29
+ __all__ = ["IncludeMarkdown", "Markdown", "MarkdownConverter", "escape_latex"]
30
+
31
+ PARSER = marko.Markdown()
32
+
33
+
34
+ @Registry.add
35
+ def Markdown(
36
+ content: str,
37
+ *,
38
+ base_level: int = 0,
39
+ callouts: bool = True,
40
+ ) -> TeX:
41
+ """Convert a Markdown string to a ``TeX`` tree.
42
+
43
+ ``base_level`` shifts heading depth: ``0`` maps ``#`` to ``\\section`` (the
44
+ default), ``-1`` maps it to ``\\chapter``. ``callouts`` toggles converting
45
+ ``> [!NOTE]`` blocks into HSRT colored boxes.
46
+ """
47
+ ast = PARSER.parse(content)
48
+ converter = MarkdownConverter(base_level=base_level, callouts=callouts)
49
+ return converter.block(ast)
50
+
51
+
52
+ @Registry.add
53
+ def IncludeMarkdown(
54
+ path: str | PathLike[str],
55
+ *,
56
+ base_level: int = 0,
57
+ callouts: bool = True,
58
+ encoding: str = "utf-8",
59
+ ) -> TeX:
60
+ """Read a Markdown file and convert it (see :func:`Markdown`)."""
61
+ content = Path(path).read_text(encoding=encoding)
62
+ return Markdown(content, base_level=base_level, callouts=callouts)