texsmith 0.0.2.dev0__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.
- texsmith/__init__.py +107 -0
- texsmith/_alias.py +59 -0
- texsmith/adapters/__init__.py +6 -0
- texsmith/adapters/docker.py +258 -0
- texsmith/adapters/handlers/__init__.py +3 -0
- texsmith/adapters/handlers/_assets.py +363 -0
- texsmith/adapters/handlers/_helpers.py +62 -0
- texsmith/adapters/handlers/_mermaid.py +122 -0
- texsmith/adapters/handlers/admonitions.py +267 -0
- texsmith/adapters/handlers/basic.py +228 -0
- texsmith/adapters/handlers/blocks.py +851 -0
- texsmith/adapters/handlers/code.py +309 -0
- texsmith/adapters/handlers/inline.py +937 -0
- texsmith/adapters/handlers/links.py +239 -0
- texsmith/adapters/handlers/media.py +380 -0
- texsmith/adapters/latex/__init__.py +10 -0
- texsmith/adapters/latex/engines/__init__.py +726 -0
- texsmith/adapters/latex/engines/latex/__init__.py +26 -0
- texsmith/adapters/latex/engines/latex/log.py +720 -0
- texsmith/adapters/latex/engines/latex/runner.py +28 -0
- texsmith/adapters/latex/engines/tectonic/__init__.py +38 -0
- texsmith/adapters/latex/formatter.py +297 -0
- texsmith/adapters/latex/latexmk.py +148 -0
- texsmith/adapters/latex/partials/acronym.tex +1 -0
- texsmith/adapters/latex/partials/add.tex +1 -0
- texsmith/adapters/latex/partials/addition.tex +1 -0
- texsmith/adapters/latex/partials/blockquote.tex +3 -0
- texsmith/adapters/latex/partials/callout.tex +3 -0
- texsmith/adapters/latex/partials/choices.tex +5 -0
- texsmith/adapters/latex/partials/citation.tex +1 -0
- texsmith/adapters/latex/partials/codeblock.tex +7 -0
- texsmith/adapters/latex/partials/codeblock_listings.tex +5 -0
- texsmith/adapters/latex/partials/codeblock_pygments.tex +5 -0
- texsmith/adapters/latex/partials/codeblock_verbatim.tex +12 -0
- texsmith/adapters/latex/partials/codeinline.tex +1 -0
- texsmith/adapters/latex/partials/codeinlinett.tex +1 -0
- texsmith/adapters/latex/partials/comment.tex +1 -0
- texsmith/adapters/latex/partials/del.tex +1 -0
- texsmith/adapters/latex/partials/deletion.tex +1 -0
- texsmith/adapters/latex/partials/description_list.tex +5 -0
- texsmith/adapters/latex/partials/enquote.tex +1 -0
- texsmith/adapters/latex/partials/epigraph.tex +1 -0
- texsmith/adapters/latex/partials/exercises_solutions.tex +7 -0
- texsmith/adapters/latex/partials/figure.tex +33 -0
- texsmith/adapters/latex/partials/figure_tcolorbox.tex +15 -0
- texsmith/adapters/latex/partials/footnote.tex +3 -0
- texsmith/adapters/latex/partials/glossary.tex +1 -0
- texsmith/adapters/latex/partials/heading.tex +14 -0
- texsmith/adapters/latex/partials/highlight.tex +25 -0
- texsmith/adapters/latex/partials/horizontal_rule.tex +1 -0
- texsmith/adapters/latex/partials/href.tex +1 -0
- texsmith/adapters/latex/partials/icon.tex +1 -0
- texsmith/adapters/latex/partials/include.tex +1 -0
- texsmith/adapters/latex/partials/index.tex +1 -0
- texsmith/adapters/latex/partials/italic.tex +1 -0
- texsmith/adapters/latex/partials/keystroke.tex +46 -0
- texsmith/adapters/latex/partials/label.tex +1 -0
- texsmith/adapters/latex/partials/list_acronyms.tex +5 -0
- texsmith/adapters/latex/partials/list_glossary.tex +8 -0
- texsmith/adapters/latex/partials/multicolumn.tex +3 -0
- texsmith/adapters/latex/partials/ordered_list.tex +5 -0
- texsmith/adapters/latex/partials/pagestyle.tex +1 -0
- texsmith/adapters/latex/partials/ref.tex +1 -0
- texsmith/adapters/latex/partials/regex.tex +1 -0
- texsmith/adapters/latex/partials/smallcaps.tex +1 -0
- texsmith/adapters/latex/partials/strikethrough.tex +1 -0
- texsmith/adapters/latex/partials/strong.tex +1 -0
- texsmith/adapters/latex/partials/subscript.tex +1 -0
- texsmith/adapters/latex/partials/substitution.tex +1 -0
- texsmith/adapters/latex/partials/superscript.tex +1 -0
- texsmith/adapters/latex/partials/tabbed.tex +3 -0
- texsmith/adapters/latex/partials/table.tex +48 -0
- texsmith/adapters/latex/partials/underline.tex +1 -0
- texsmith/adapters/latex/partials/unordered_list.tex +5 -0
- texsmith/adapters/latex/partials/url.tex +1 -0
- texsmith/adapters/latex/pygments.py +98 -0
- texsmith/adapters/latex/pyxindy.py +64 -0
- texsmith/adapters/latex/renderer.py +220 -0
- texsmith/adapters/latex/tectonic.py +366 -0
- texsmith/adapters/latex/utils.py +86 -0
- texsmith/adapters/markdown/__init__.py +342 -0
- texsmith/adapters/plugins/__init__.py +8 -0
- texsmith/adapters/plugins/material.py +174 -0
- texsmith/adapters/plugins/snippet.py +1734 -0
- texsmith/adapters/transformers/__init__.py +122 -0
- texsmith/adapters/transformers/base.py +140 -0
- texsmith/adapters/transformers/strategies.py +1456 -0
- texsmith/adapters/transformers/utils.py +59 -0
- texsmith/api/__init__.py +80 -0
- texsmith/api/_utils.py +56 -0
- texsmith/api/document.py +645 -0
- texsmith/api/pipeline.py +229 -0
- texsmith/api/service.py +648 -0
- texsmith/api/templates.py +287 -0
- texsmith/core/__init__.py +6 -0
- texsmith/core/bibliography/__init__.py +57 -0
- texsmith/core/bibliography/collection.py +354 -0
- texsmith/core/bibliography/doi.py +194 -0
- texsmith/core/bibliography/issues.py +15 -0
- texsmith/core/bibliography/parsing.py +45 -0
- texsmith/core/callouts.py +99 -0
- texsmith/core/config.py +191 -0
- texsmith/core/context.py +242 -0
- texsmith/core/conversion/__init__.py +103 -0
- texsmith/core/conversion/core.py +780 -0
- texsmith/core/conversion/debug.py +87 -0
- texsmith/core/conversion/inputs.py +474 -0
- texsmith/core/conversion/renderer.py +578 -0
- texsmith/core/conversion/templates.py +646 -0
- texsmith/core/conversion_contexts.py +95 -0
- texsmith/core/diagnostics.py +118 -0
- texsmith/core/exceptions.py +41 -0
- texsmith/core/fonts/__init__.py +3 -0
- texsmith/core/fragments/__init__.py +716 -0
- texsmith/core/fragments/base.py +117 -0
- texsmith/core/metadata.py +280 -0
- texsmith/core/mustache.py +86 -0
- texsmith/core/partials.py +20 -0
- texsmith/core/rules.py +394 -0
- texsmith/core/templates/__init__.py +58 -0
- texsmith/core/templates/base.py +343 -0
- texsmith/core/templates/builtins.py +68 -0
- texsmith/core/templates/context_usage.py +137 -0
- texsmith/core/templates/loader.py +303 -0
- texsmith/core/templates/manifest.py +861 -0
- texsmith/core/templates/runtime.py +347 -0
- texsmith/core/templates/text.py +14 -0
- texsmith/core/templates/wrapper.py +340 -0
- texsmith/core/user_dir.py +179 -0
- texsmith/devtools.py +28 -0
- texsmith/extensions/__init__.py +162 -0
- texsmith/extensions/index/__init__.py +21 -0
- texsmith/extensions/index/markdown.py +94 -0
- texsmith/extensions/index/mkdocs_plugin.py +136 -0
- texsmith/extensions/index/registry.py +57 -0
- texsmith/extensions/index/renderer.py +183 -0
- texsmith/extensions/index/templates/index.tex +1 -0
- texsmith/extensions/latex_raw.py +115 -0
- texsmith/extensions/latex_text.py +117 -0
- texsmith/extensions/mermaid.py +267 -0
- texsmith/extensions/missing_footnotes.py +125 -0
- texsmith/extensions/multi_citations.py +54 -0
- texsmith/extensions/progressbar/__init__.py +9 -0
- texsmith/extensions/progressbar/markdown.py +212 -0
- texsmith/extensions/progressbar/renderer.py +117 -0
- texsmith/extensions/smallcaps.py +48 -0
- texsmith/extensions/texlogos/__init__.py +10 -0
- texsmith/extensions/texlogos/markdown.py +236 -0
- texsmith/extensions/texlogos/renderer.py +81 -0
- texsmith/extensions/texlogos/specs.py +66 -0
- texsmith/fonts/__init__.py +57 -0
- texsmith/fonts/cache.py +46 -0
- texsmith/fonts/constants.py +60 -0
- texsmith/fonts/coverage.py +275 -0
- texsmith/fonts/downloader.py +103 -0
- texsmith/fonts/fallback.py +438 -0
- texsmith/fonts/html_scripts.py +168 -0
- texsmith/fonts/logging.py +127 -0
- texsmith/fonts/pipeline.py +338 -0
- texsmith/fonts/scripts.py +493 -0
- texsmith/fonts/ucharclasses.py +164 -0
- texsmith/fragments/__init__.py +11 -0
- texsmith/fragments/bibliography/__init__.py +65 -0
- texsmith/fragments/bibliography/fragment.toml +3 -0
- texsmith/fragments/bibliography/ts-bibliography-backmatter.jinja.tex +35 -0
- texsmith/fragments/bibliography/ts-bibliography.jinja.tex +10 -0
- texsmith/fragments/callouts/__init__.py +88 -0
- texsmith/fragments/callouts/fragment.toml +3 -0
- texsmith/fragments/callouts/ts-callouts.jinja.sty +129 -0
- texsmith/fragments/code/__init__.py +82 -0
- texsmith/fragments/code/fragment.toml +3 -0
- texsmith/fragments/code/ts-code.jinja.sty +140 -0
- texsmith/fragments/extra/__init__.py +180 -0
- texsmith/fragments/extra/fragment.toml +3 -0
- texsmith/fragments/extra/ts-extra.jinja.tex +13 -0
- texsmith/fragments/fonts/__init__.py +880 -0
- texsmith/fragments/fonts/fragment.toml +3 -0
- texsmith/fragments/fonts/ts-fonts.jinja.sty +292 -0
- texsmith/fragments/frame/__init__.py +170 -0
- texsmith/fragments/frame/fragment.toml +3 -0
- texsmith/fragments/frame/ts-frame.tex.jinja +49 -0
- texsmith/fragments/geometry/__init__.py +169 -0
- texsmith/fragments/geometry/fragment.toml +3 -0
- texsmith/fragments/geometry/paper.py +526 -0
- texsmith/fragments/geometry/ts_geometry.tex.jinja +53 -0
- texsmith/fragments/glossary/__init__.py +67 -0
- texsmith/fragments/glossary/fragment.toml +3 -0
- texsmith/fragments/glossary/ts-glossary-backmatter.jinja.tex +5 -0
- texsmith/fragments/glossary/ts-glossary.jinja.sty +28 -0
- texsmith/fragments/index/__init__.py +66 -0
- texsmith/fragments/index/fragment.toml +3 -0
- texsmith/fragments/index/ts-index-backmatter.jinja.tex +3 -0
- texsmith/fragments/index/ts-index.jinja.sty +12 -0
- texsmith/fragments/keystrokes/__init__.py +69 -0
- texsmith/fragments/keystrokes/fragment.toml +3 -0
- texsmith/fragments/keystrokes/ts-keystrokes.jinja.sty +20 -0
- texsmith/fragments/todolist/__init__.py +71 -0
- texsmith/fragments/todolist/fragment.toml +3 -0
- texsmith/fragments/todolist/ts-todolist.jinja.sty +21 -0
- texsmith/fragments/typesetting/__init__.py +214 -0
- texsmith/fragments/typesetting/fragment.toml +3 -0
- texsmith/fragments/typesetting/ts-typesetting.tex.jinja +81 -0
- texsmith/index.py +26 -0
- texsmith/plugins/__init__.py +14 -0
- texsmith/progressbar.py +8 -0
- texsmith/quotes.py +40 -0
- texsmith/smart_dashes.py +66 -0
- texsmith/templates/__init__.py +3 -0
- texsmith/templates/article/README.md +33 -0
- texsmith/templates/article/__init__.py +281 -0
- texsmith/templates/article/template/manifest.toml +125 -0
- texsmith/templates/article/template/mermaid-config.json +18 -0
- texsmith/templates/article/template/template.tex +74 -0
- texsmith/templates/book/README.md +26 -0
- texsmith/templates/book/__init__.py +75 -0
- texsmith/templates/book/overrides/codeblock.tex +7 -0
- texsmith/templates/book/overrides/codeinline.tex +1 -0
- texsmith/templates/book/template/fixtoc.sty +81 -0
- texsmith/templates/book/template/manifest.toml +198 -0
- texsmith/templates/book/template/template.tex +314 -0
- texsmith/templates/common/__init__.py +1 -0
- texsmith/templates/common/latexmkrc +46 -0
- texsmith/templates/letter/README.md +59 -0
- texsmith/templates/letter/__init__.py +495 -0
- texsmith/templates/letter/demo.md +31 -0
- texsmith/templates/letter/fonts/modernline bold.otf +0 -0
- texsmith/templates/letter/fonts/modernline.otf +0 -0
- texsmith/templates/letter/manifest.toml +197 -0
- texsmith/templates/letter/template/callouts.jinja.sty +290 -0
- texsmith/templates/letter/template/template.tex +123 -0
- texsmith/templates/snippet/README.md +21 -0
- texsmith/templates/snippet/__init__.py +80 -0
- texsmith/templates/snippet/template/manifest.toml +66 -0
- texsmith/templates/snippet/template/template.tex +47 -0
- texsmith/texlogos.py +15 -0
- texsmith/ui/__init__.py +6 -0
- texsmith/ui/cli/__init__.py +22 -0
- texsmith/ui/cli/_options.py +338 -0
- texsmith/ui/cli/app.py +65 -0
- texsmith/ui/cli/bibliography.py +300 -0
- texsmith/ui/cli/commands/__init__.py +14 -0
- texsmith/ui/cli/commands/render.py +1128 -0
- texsmith/ui/cli/commands/templates.py +397 -0
- texsmith/ui/cli/diagnostics.py +36 -0
- texsmith/ui/cli/presenter.py +663 -0
- texsmith/ui/cli/state.py +263 -0
- texsmith/ui/cli/utils.py +235 -0
- texsmith-0.0.2.dev0.dist-info/METADATA +187 -0
- texsmith-0.0.2.dev0.dist-info/RECORD +252 -0
- texsmith-0.0.2.dev0.dist-info/WHEEL +4 -0
- texsmith-0.0.2.dev0.dist-info/entry_points.txt +22 -0
- texsmith-0.0.2.dev0.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\gls{\VAR{key}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
\BLOCK{- set number_to_words = {
|
|
2
|
+
-1: 'part',
|
|
3
|
+
0: 'chapter',
|
|
4
|
+
1: 'section',
|
|
5
|
+
2: 'subsection',
|
|
6
|
+
3: 'subsubsection',
|
|
7
|
+
4: 'paragraph',
|
|
8
|
+
5: 'subparagraph'
|
|
9
|
+
} -}
|
|
10
|
+
\BLOCK{- if level in number_to_words -}
|
|
11
|
+
\\VAR{number_to_words[level]}\BLOCK{if not numbered and level <= 5}*\BLOCK{endif}{\VAR{text}}\BLOCK{if ref}\label{\VAR{ref}}\BLOCK{endif}\BLOCK{if level >= 4}\mbox{}\\\BLOCK{endif}
|
|
12
|
+
\BLOCK{else -}
|
|
13
|
+
\textbf{\VAR{text}}\BLOCK{if ref}\label{\VAR{ref}}\BLOCK{endif}
|
|
14
|
+
\BLOCK{endif}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
\ifXeTeX
|
|
2
|
+
% soul breaks with ucharclasses on XeTeX: fall back to simple colored text.
|
|
3
|
+
\providecommand{\texsmithHighlight}[1]{\textcolor{yellow}{#1}}
|
|
4
|
+
\providecommand{\hl}[1]{\textcolor{yellow}{#1}}
|
|
5
|
+
\else
|
|
6
|
+
\ifLuaTeX
|
|
7
|
+
\RequirePackage{lua-ul}
|
|
8
|
+
\providecommand{\texsmithHighlight}[1]{%
|
|
9
|
+
\begingroup
|
|
10
|
+
\setulcolor{yellow}%
|
|
11
|
+
\ul{#1}%
|
|
12
|
+
\endgroup
|
|
13
|
+
}
|
|
14
|
+
\providecommand{\hl}[1]{\texsmithHighlight{#1}}
|
|
15
|
+
\else
|
|
16
|
+
\providecommand{\texsmithHighlight}[1]{%
|
|
17
|
+
\begingroup
|
|
18
|
+
\setlength{\fboxsep}{0.8pt}%
|
|
19
|
+
\colorbox{yellow}{#1}%
|
|
20
|
+
\endgroup
|
|
21
|
+
}
|
|
22
|
+
\fi
|
|
23
|
+
\fi
|
|
24
|
+
|
|
25
|
+
\texsmithHighlight{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\par\noindent\rule{\linewidth}{0.4pt}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\href{\VAR{url}}{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\includegraphics[width=1em]{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\input{\VAR{text}} % \VAR{title}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\VAR{text}\index{\BLOCK{ if style == 'b' }\textbf{\VAR{entry}}\BLOCK{ elif style == 'i' }\textit{\VAR{entry}}\BLOCK{ elif style in ('bi', 'ib') }\textbf{\textit{\VAR{entry}}}\BLOCK{ else }\VAR{entry}\BLOCK{ endif }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\emph{\VAR{text}}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
\BLOCK{set ks = {
|
|
2
|
+
'control': 'Ctrl',
|
|
3
|
+
'alt': 'Alt',
|
|
4
|
+
'delete': 'Del',
|
|
5
|
+
'enter': '⏎ Enter',
|
|
6
|
+
'shift': '⇧ Shift',
|
|
7
|
+
'slash': '/',
|
|
8
|
+
'comma': ',',
|
|
9
|
+
'period': '.',
|
|
10
|
+
'arrow-up': '\\(\\uparrow\\)',
|
|
11
|
+
'arrow-down': '\\(\\downarrow\\)',
|
|
12
|
+
'arrow-left': '\\(\\leftarrow\\)',
|
|
13
|
+
'arrow-right': '\\(\\rightarrow\\)',
|
|
14
|
+
'backslash': '\\textbackslash',
|
|
15
|
+
'double-quote': '"',
|
|
16
|
+
'backspace': '⌫ Delete',
|
|
17
|
+
'command': '⌘',
|
|
18
|
+
'tab': 'Tab',
|
|
19
|
+
'esc': 'Esc',
|
|
20
|
+
'insert': 'Ins',
|
|
21
|
+
'home': 'Home',
|
|
22
|
+
'end': 'End',
|
|
23
|
+
'page-up': 'PgUp',
|
|
24
|
+
'page-down': 'PgDn',
|
|
25
|
+
'space': 'Space',
|
|
26
|
+
'f1': 'F1',
|
|
27
|
+
'f2': 'F2',
|
|
28
|
+
'f3': 'F3',
|
|
29
|
+
'f4': 'F4',
|
|
30
|
+
'f5': 'F5',
|
|
31
|
+
'f6': 'F6',
|
|
32
|
+
'f7': 'F7',
|
|
33
|
+
'f8': 'F8',
|
|
34
|
+
'f9': 'F9',
|
|
35
|
+
'f10': 'F10',
|
|
36
|
+
'f11': 'F11',
|
|
37
|
+
'f12': 'F12',
|
|
38
|
+
}}
|
|
39
|
+
\BLOCK{- for key in text -}
|
|
40
|
+
\BLOCK{- if key in ks -}
|
|
41
|
+
\keystroke{\VAR{ks[key]}}
|
|
42
|
+
\BLOCK{- else -}
|
|
43
|
+
\keystroke{\VAR{key|upper}}
|
|
44
|
+
\BLOCK{- endif -}
|
|
45
|
+
\BLOCK{- if not loop.last -} + \BLOCK{- endif -}
|
|
46
|
+
\BLOCK{- endfor -}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\label{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\thispagestyle{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\VAR{text} \ref{\VAR{ref}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\href{\VAR{url}}{\mintinline{text}{\VAR{text}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\textsc{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\sout{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\textbf{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\textsubscript{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\xout{\VAR{original}}\ \uline{\VAR{replacement}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\textsuperscript{\VAR{text}}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
\BLOCK{- set first_style = {
|
|
2
|
+
'left': 'l',
|
|
3
|
+
'right': 'r',
|
|
4
|
+
'center': 'c'
|
|
5
|
+
} -}
|
|
6
|
+
\BLOCK{- set rest_style = {
|
|
7
|
+
'left': 'X',
|
|
8
|
+
'right': 'Z',
|
|
9
|
+
'center': 'Y',
|
|
10
|
+
'justify': 'X'
|
|
11
|
+
} -}
|
|
12
|
+
\BLOCK{- if caption -}
|
|
13
|
+
\begin{table}[H]
|
|
14
|
+
\centering
|
|
15
|
+
\BLOCK{if label -}
|
|
16
|
+
\label{\VAR{label}}
|
|
17
|
+
\BLOCK{- endif}
|
|
18
|
+
\caption{\VAR{caption}}
|
|
19
|
+
\BLOCK{else}
|
|
20
|
+
\begin{center}
|
|
21
|
+
\BLOCK{- endif}
|
|
22
|
+
\BLOCK{- if is_large -}
|
|
23
|
+
\BLOCK{- endif}
|
|
24
|
+
\begin{tabularx}{\textwidth}{\BLOCK{- for column in columns}\BLOCK{- if loop.first }\VAR{first_style.get(column, 'l')}\BLOCK{- else }\VAR{rest_style.get(column, 'X')}\BLOCK{- endif }\BLOCK{- endfor}}
|
|
25
|
+
\toprule
|
|
26
|
+
\BLOCK{for row in rows -}
|
|
27
|
+
\BLOCK{- if loop.first -}
|
|
28
|
+
\BLOCK{- for col in row -}
|
|
29
|
+
\textbf{\VAR{col}}\BLOCK{- if not loop.last} & \BLOCK{else} \\
|
|
30
|
+
\BLOCK{endif}
|
|
31
|
+
\BLOCK{- endfor}
|
|
32
|
+
\midrule
|
|
33
|
+
\BLOCK{else -}
|
|
34
|
+
\BLOCK{- for col in row -}
|
|
35
|
+
\VAR{col}\BLOCK{- if not loop.last} & \BLOCK{else} \\
|
|
36
|
+
\BLOCK{endif}
|
|
37
|
+
\BLOCK{- endfor -}
|
|
38
|
+
\BLOCK{- endif -}
|
|
39
|
+
\BLOCK{- endfor}
|
|
40
|
+
\bottomrule
|
|
41
|
+
\end{tabularx}
|
|
42
|
+
\BLOCK{- if is_large -}
|
|
43
|
+
\BLOCK{- endif}
|
|
44
|
+
\BLOCK{- if caption -}
|
|
45
|
+
\end{table}
|
|
46
|
+
\BLOCK{- else}
|
|
47
|
+
\end{center}
|
|
48
|
+
\BLOCK{- endif}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\uline{\VAR{text}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
\href{\VAR{url}}{\VAR{text}}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Pygments integration helpers for LaTeX rendering."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
|
|
7
|
+
from pygments import highlight
|
|
8
|
+
from pygments.formatters import LatexFormatter
|
|
9
|
+
from pygments.lexers import ClassNotFound, TextLexer, get_lexer_by_name
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PygmentsLatexHighlighter:
|
|
13
|
+
"""Convert source code to LaTeX using Pygments."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
*,
|
|
18
|
+
commandprefix: str = "PY",
|
|
19
|
+
style: str = "bw",
|
|
20
|
+
verboptions: str | None = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
self.commandprefix = commandprefix
|
|
23
|
+
self.style = style
|
|
24
|
+
self.verboptions = verboptions or r"breaklines, breakanywhere, commandchars=\\\{\}"
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def style_key(self) -> str:
|
|
28
|
+
"""Identifier that groups style definitions."""
|
|
29
|
+
return f"{self.style}:{self.commandprefix}"
|
|
30
|
+
|
|
31
|
+
def render(
|
|
32
|
+
self,
|
|
33
|
+
code: str,
|
|
34
|
+
language: str,
|
|
35
|
+
*,
|
|
36
|
+
linenos: bool,
|
|
37
|
+
highlight_lines: Iterable[int] | None = None,
|
|
38
|
+
) -> tuple[str, str]:
|
|
39
|
+
"""Return the LaTeX code and style definitions for a payload."""
|
|
40
|
+
try:
|
|
41
|
+
lexer = get_lexer_by_name(language or "text")
|
|
42
|
+
except ClassNotFound:
|
|
43
|
+
lexer = TextLexer()
|
|
44
|
+
|
|
45
|
+
def _format_ranges(values: Iterable[int]) -> str:
|
|
46
|
+
sorted_vals = sorted(set(values))
|
|
47
|
+
if not sorted_vals:
|
|
48
|
+
return ""
|
|
49
|
+
ranges: list[str] = []
|
|
50
|
+
start = end = sorted_vals[0]
|
|
51
|
+
for num in sorted_vals[1:]:
|
|
52
|
+
if num == end + 1:
|
|
53
|
+
end = num
|
|
54
|
+
else:
|
|
55
|
+
ranges.append(f"{start}-{end}" if start != end else str(start))
|
|
56
|
+
start = end = num
|
|
57
|
+
ranges.append(f"{start}-{end}" if start != end else str(start))
|
|
58
|
+
return ",".join(ranges)
|
|
59
|
+
|
|
60
|
+
verb_options = self.verboptions
|
|
61
|
+
formatted_highlight = _format_ranges(highlight_lines or [])
|
|
62
|
+
if formatted_highlight:
|
|
63
|
+
verb_options = f"{verb_options},highlightlines={{{formatted_highlight}}}"
|
|
64
|
+
|
|
65
|
+
formatter = LatexFormatter(
|
|
66
|
+
full=False,
|
|
67
|
+
linenos=linenos,
|
|
68
|
+
style=self.style,
|
|
69
|
+
commandprefix=self.commandprefix,
|
|
70
|
+
linenostart=1,
|
|
71
|
+
linenostep=1,
|
|
72
|
+
verboptions=verb_options,
|
|
73
|
+
hl_lines=list(highlight_lines or []),
|
|
74
|
+
)
|
|
75
|
+
latex_code = highlight(code, lexer, formatter)
|
|
76
|
+
style_defs = formatter.get_style_defs()
|
|
77
|
+
return latex_code, style_defs
|
|
78
|
+
|
|
79
|
+
def render_inline(self, code: str, language: str) -> tuple[str, str]:
|
|
80
|
+
"""Return inline LaTeX macros for a code snippet (no Verbatim env)."""
|
|
81
|
+
try:
|
|
82
|
+
lexer = get_lexer_by_name(language or "text")
|
|
83
|
+
except ClassNotFound:
|
|
84
|
+
lexer = TextLexer()
|
|
85
|
+
|
|
86
|
+
formatter = LatexFormatter(
|
|
87
|
+
full=False,
|
|
88
|
+
linenos=False,
|
|
89
|
+
style=self.style,
|
|
90
|
+
commandprefix=self.commandprefix,
|
|
91
|
+
nowrap=True,
|
|
92
|
+
)
|
|
93
|
+
latex_code = highlight(code, lexer, formatter)
|
|
94
|
+
style_defs = formatter.get_style_defs()
|
|
95
|
+
return latex_code, style_defs
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
__all__ = ["PygmentsLatexHighlighter"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Helpers to integrate the PyXindy toolchain (Python port of xindy)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import shlex
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _python_module_available() -> bool:
|
|
13
|
+
"""Return True when the PyXindy modules are importable."""
|
|
14
|
+
return importlib.util.find_spec("xindy") is not None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_available() -> bool:
|
|
18
|
+
"""Check whether PyXindy can be invoked (entry points or module)."""
|
|
19
|
+
return _python_module_available() or shutil.which("makeindex-py") is not None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _script_path(name: str) -> Path | None:
|
|
23
|
+
"""Resolve a script living alongside the current interpreter."""
|
|
24
|
+
candidate = Path(sys.executable).with_name(name)
|
|
25
|
+
return candidate if candidate.exists() else None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def index_command_tokens() -> list[str]:
|
|
29
|
+
"""Return the argv tokens to invoke the makeindex-compatible wrapper."""
|
|
30
|
+
for candidate in ("makeindex-py", "makeindex4"):
|
|
31
|
+
resolved = shutil.which(candidate) or _script_path(candidate)
|
|
32
|
+
if resolved:
|
|
33
|
+
return [str(resolved)]
|
|
34
|
+
return [sys.executable, "-m", "xindy.tex.makeindex4"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def glossary_command_tokens() -> list[str]:
|
|
38
|
+
"""Return the argv tokens to invoke the makeglossaries helper."""
|
|
39
|
+
for candidate in ("makeglossaries-py",):
|
|
40
|
+
resolved = shutil.which(candidate) or _script_path(candidate)
|
|
41
|
+
if resolved:
|
|
42
|
+
return [str(resolved)]
|
|
43
|
+
return [sys.executable, "-m", "xindy.tex.makeglossaries"]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def latexmk_makeindex_command() -> str:
|
|
47
|
+
"""Return a makeindex command string suitable for latexmkrc."""
|
|
48
|
+
tokens = [shlex.quote(token) for token in index_command_tokens()]
|
|
49
|
+
return " ".join([*tokens, "%O", "-o", "%D", "%S"])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def latexmk_makeglossaries_command() -> str:
|
|
53
|
+
"""Return a makeglossaries command string suitable for latexmkrc."""
|
|
54
|
+
tokens = [shlex.quote(token) for token in glossary_command_tokens()]
|
|
55
|
+
return " ".join([*tokens, '"$base"'])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"glossary_command_tokens",
|
|
60
|
+
"index_command_tokens",
|
|
61
|
+
"is_available",
|
|
62
|
+
"latexmk_makeglossaries_command",
|
|
63
|
+
"latexmk_makeindex_command",
|
|
64
|
+
]
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""High-level HTML to LaTeX renderer based on the modular pipeline."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
6
|
+
from importlib import metadata
|
|
7
|
+
import inspect
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
from bs4 import BeautifulSoup, FeatureNotFound
|
|
12
|
+
|
|
13
|
+
from texsmith.core.config import BookConfig
|
|
14
|
+
from texsmith.core.context import AssetRegistry, DocumentState, RenderContext
|
|
15
|
+
from texsmith.core.diagnostics import DiagnosticEmitter, NullEmitter
|
|
16
|
+
from texsmith.core.exceptions import LatexRenderingError
|
|
17
|
+
from texsmith.core.rules import RenderEngine, RenderPhase
|
|
18
|
+
from texsmith.extensions import register_all_renderers
|
|
19
|
+
|
|
20
|
+
from .formatter import LaTeXFormatter
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING: # pragma: no cover - typing only
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LaTeXRenderer:
|
|
28
|
+
_ENTRY_POINT_GROUP = "texsmith.renderers"
|
|
29
|
+
_ENTRY_POINT_PAYLOADS: list[Any] | None = None
|
|
30
|
+
|
|
31
|
+
"""Convert HTML fragments to LaTeX using a modular pipeline."""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
config: BookConfig | None = None,
|
|
36
|
+
formatter: LaTeXFormatter | None = None,
|
|
37
|
+
output_root: Path | str = Path("build"),
|
|
38
|
+
parser: str = "lxml",
|
|
39
|
+
copy_assets: bool = True,
|
|
40
|
+
convert_assets: bool = False,
|
|
41
|
+
hash_assets: bool = False,
|
|
42
|
+
) -> None:
|
|
43
|
+
self.config = config or BookConfig()
|
|
44
|
+
self.formatter = formatter or LaTeXFormatter()
|
|
45
|
+
self.parser_backend = parser
|
|
46
|
+
self.copy_assets = copy_assets
|
|
47
|
+
self.convert_assets = convert_assets
|
|
48
|
+
self.hash_assets = hash_assets
|
|
49
|
+
|
|
50
|
+
self.output_root = Path(output_root)
|
|
51
|
+
self.assets_root = (self.output_root / "assets").resolve()
|
|
52
|
+
|
|
53
|
+
self.assets = AssetRegistry(self.assets_root, copy_assets=self.copy_assets)
|
|
54
|
+
|
|
55
|
+
# Keep formatter in sync with runtime environment
|
|
56
|
+
self.formatter.config = self.config # type: ignore[assignment]
|
|
57
|
+
self.formatter.output_path = self.assets_root # type: ignore[assignment]
|
|
58
|
+
self.formatter.legacy_latex_accents = self.config.legacy_latex_accents
|
|
59
|
+
|
|
60
|
+
self.engine = RenderEngine()
|
|
61
|
+
self._register_builtin_handlers()
|
|
62
|
+
self._register_entry_point_handlers()
|
|
63
|
+
register_all_renderers(self)
|
|
64
|
+
|
|
65
|
+
def _register_builtin_handlers(self) -> None:
|
|
66
|
+
"""Register the initial set of handlers for the renderer."""
|
|
67
|
+
from ..handlers import (
|
|
68
|
+
admonitions as admonition_handlers,
|
|
69
|
+
basic as basic_handlers,
|
|
70
|
+
blocks as block_handlers,
|
|
71
|
+
code as code_handlers,
|
|
72
|
+
inline as inline_handlers,
|
|
73
|
+
links as link_handlers,
|
|
74
|
+
media as media_handlers,
|
|
75
|
+
)
|
|
76
|
+
from ..plugins import material as material_plugins, snippet as snippet_plugin
|
|
77
|
+
|
|
78
|
+
self.engine.collect_from(basic_handlers)
|
|
79
|
+
self.engine.collect_from(inline_handlers)
|
|
80
|
+
self.engine.collect_from(code_handlers)
|
|
81
|
+
self.engine.collect_from(link_handlers)
|
|
82
|
+
self.engine.collect_from(block_handlers)
|
|
83
|
+
self.engine.collect_from(admonition_handlers)
|
|
84
|
+
self.engine.collect_from(media_handlers)
|
|
85
|
+
self.register(snippet_plugin)
|
|
86
|
+
self.register(material_plugins)
|
|
87
|
+
|
|
88
|
+
def register(self, handler: Any) -> None:
|
|
89
|
+
"""Register additional handlers on demand.
|
|
90
|
+
|
|
91
|
+
Arguments can be callables decorated with :func:`renders` or modules/classes
|
|
92
|
+
exposing decorated attributes.
|
|
93
|
+
"""
|
|
94
|
+
definition = getattr(handler, "__render_rule__", None)
|
|
95
|
+
if definition is not None:
|
|
96
|
+
self.engine.register(handler)
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
self.engine.collect_from(handler)
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def _iter_entry_point_payloads(cls) -> Iterable[Any]:
|
|
103
|
+
if cls._ENTRY_POINT_PAYLOADS is None:
|
|
104
|
+
payloads: list[Any] = []
|
|
105
|
+
try:
|
|
106
|
+
entry_points = metadata.entry_points()
|
|
107
|
+
group = entry_points.select(group=cls._ENTRY_POINT_GROUP)
|
|
108
|
+
except Exception: # pragma: no cover - defensive
|
|
109
|
+
cls._ENTRY_POINT_PAYLOADS = []
|
|
110
|
+
return ()
|
|
111
|
+
|
|
112
|
+
for entry_point in sorted(
|
|
113
|
+
group,
|
|
114
|
+
key=lambda ep: (getattr(ep, "priority", 0), getattr(ep, "name", "")),
|
|
115
|
+
):
|
|
116
|
+
try:
|
|
117
|
+
payloads.append(entry_point.load())
|
|
118
|
+
except Exception: # pragma: no cover - defensive
|
|
119
|
+
continue
|
|
120
|
+
cls._ENTRY_POINT_PAYLOADS = payloads
|
|
121
|
+
return cls._ENTRY_POINT_PAYLOADS
|
|
122
|
+
|
|
123
|
+
def _register_entry_point_handlers(self) -> None:
|
|
124
|
+
for payload in self._iter_entry_point_payloads():
|
|
125
|
+
self._apply_entry_point(payload)
|
|
126
|
+
|
|
127
|
+
def _apply_entry_point(self, payload: Any) -> None:
|
|
128
|
+
def _accepts_renderer(target: Callable[..., Any]) -> bool:
|
|
129
|
+
try:
|
|
130
|
+
signature = inspect.signature(target)
|
|
131
|
+
except (TypeError, ValueError):
|
|
132
|
+
return False
|
|
133
|
+
for parameter in signature.parameters.values():
|
|
134
|
+
if parameter.kind in (parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD):
|
|
135
|
+
return True
|
|
136
|
+
if parameter.kind in (parameter.POSITIONAL_ONLY, parameter.POSITIONAL_OR_KEYWORD):
|
|
137
|
+
return True
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
if callable(payload):
|
|
141
|
+
if _accepts_renderer(payload):
|
|
142
|
+
payload(self)
|
|
143
|
+
return
|
|
144
|
+
self.register(payload)
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
register = getattr(payload, "register", None)
|
|
148
|
+
if callable(register):
|
|
149
|
+
register(self)
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
self.register(payload)
|
|
153
|
+
|
|
154
|
+
def render(
|
|
155
|
+
self,
|
|
156
|
+
html: str,
|
|
157
|
+
*,
|
|
158
|
+
runtime: Mapping[str, Any] | None = None,
|
|
159
|
+
state: DocumentState | None = None,
|
|
160
|
+
emitter: DiagnosticEmitter | None = None,
|
|
161
|
+
) -> str:
|
|
162
|
+
"""Render an HTML fragment into LaTeX."""
|
|
163
|
+
active_emitter = emitter or NullEmitter()
|
|
164
|
+
try:
|
|
165
|
+
soup = BeautifulSoup(html, self.parser_backend)
|
|
166
|
+
except FeatureNotFound:
|
|
167
|
+
if self.parser_backend == "html.parser":
|
|
168
|
+
raise
|
|
169
|
+
# Fall back to the built-in parser when the preferred backend is missing.
|
|
170
|
+
from texsmith.core.conversion.debug import record_event
|
|
171
|
+
|
|
172
|
+
record_event(
|
|
173
|
+
active_emitter,
|
|
174
|
+
"parser_fallback",
|
|
175
|
+
{"preferred": self.parser_backend, "fallback": "html.parser"},
|
|
176
|
+
)
|
|
177
|
+
soup = BeautifulSoup(html, "html.parser")
|
|
178
|
+
self.parser_backend = "html.parser"
|
|
179
|
+
document_state = state or DocumentState()
|
|
180
|
+
|
|
181
|
+
context = RenderContext(
|
|
182
|
+
config=self.config,
|
|
183
|
+
formatter=self.formatter,
|
|
184
|
+
document=soup,
|
|
185
|
+
assets=self.assets,
|
|
186
|
+
state=document_state,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
context.attach_runtime(
|
|
190
|
+
copy_assets=self.copy_assets,
|
|
191
|
+
convert_assets=self.convert_assets,
|
|
192
|
+
hash_assets=self.hash_assets,
|
|
193
|
+
emitter=active_emitter,
|
|
194
|
+
)
|
|
195
|
+
if runtime:
|
|
196
|
+
context.attach_runtime(**runtime)
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
self.engine.run(soup, context)
|
|
200
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
201
|
+
raise LatexRenderingError("LaTeX rendering failed") from exc
|
|
202
|
+
|
|
203
|
+
return self._collect_output(soup)
|
|
204
|
+
|
|
205
|
+
def _collect_output(self, soup: BeautifulSoup) -> str:
|
|
206
|
+
"""Extract the LaTeX output from the transformed soup."""
|
|
207
|
+
return soup.get_text()
|
|
208
|
+
|
|
209
|
+
def iter_registered_rules(self) -> Iterable[tuple[RenderPhase, str]]:
|
|
210
|
+
"""Expose currently registered rules for debugging/reporting."""
|
|
211
|
+
for phase in RenderPhase:
|
|
212
|
+
for rule in self.engine.registry.iter_phase(phase):
|
|
213
|
+
yield phase, rule.name
|
|
214
|
+
|
|
215
|
+
def describe_registered_rules(self) -> list[dict[str, object]]:
|
|
216
|
+
"""Return detailed metadata about registered rules."""
|
|
217
|
+
return self.engine.registry.describe()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
__all__ = ["LaTeXRenderer"]
|