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
texsmith/smart_dashes.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Markdown extension that converts ``--``/``---`` into proper dashes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import ClassVar
|
|
7
|
+
import xml.etree.ElementTree as ElementTree
|
|
8
|
+
|
|
9
|
+
from markdown import Extension, Markdown
|
|
10
|
+
from markdown.treeprocessors import Treeprocessor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_DASH_PATTERN = re.compile(r"---|--")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _replace_dashes(text: str) -> str:
|
|
17
|
+
"""Replace ASCII dash sequences with typographic counterparts."""
|
|
18
|
+
|
|
19
|
+
def _swap(match: re.Match[str]) -> str:
|
|
20
|
+
payload = match.group(0)
|
|
21
|
+
return "\u2014" if payload == "---" else "\u2013"
|
|
22
|
+
|
|
23
|
+
return _DASH_PATTERN.sub(_swap, text)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _SmartDashesTreeprocessor(Treeprocessor):
|
|
27
|
+
"""Replace double/triple hyphens outside code with typographic dashes."""
|
|
28
|
+
|
|
29
|
+
_SKIP_TAGS: ClassVar[set[str]] = {"code", "pre", "kbd", "script", "style"}
|
|
30
|
+
|
|
31
|
+
def run(self, root: ElementTree.Element) -> None: # type: ignore[override]
|
|
32
|
+
self._process(root)
|
|
33
|
+
|
|
34
|
+
def _process(self, element: ElementTree.Element) -> None:
|
|
35
|
+
if self._is_skipped(element):
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
if element.text:
|
|
39
|
+
element.text = _replace_dashes(element.text)
|
|
40
|
+
|
|
41
|
+
for child in list(element):
|
|
42
|
+
self._process(child)
|
|
43
|
+
if child.tail:
|
|
44
|
+
child.tail = _replace_dashes(child.tail)
|
|
45
|
+
|
|
46
|
+
def _is_skipped(self, element: ElementTree.Element) -> bool:
|
|
47
|
+
tag = element.tag
|
|
48
|
+
if isinstance(tag, str):
|
|
49
|
+
tag = tag.lower()
|
|
50
|
+
return tag in self._SKIP_TAGS
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TexsmithSmartDashesExtension(Extension):
|
|
54
|
+
"""Register the smart dash tree-processor."""
|
|
55
|
+
|
|
56
|
+
def extendMarkdown(self, md: Markdown) -> None: # type: ignore[override] # noqa: N802
|
|
57
|
+
md.treeprocessors.register(
|
|
58
|
+
_SmartDashesTreeprocessor(md), "texsmith_smart_dashes", priority=16
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def makeExtension(**kwargs: object) -> TexsmithSmartDashesExtension: # noqa: N802
|
|
63
|
+
return TexsmithSmartDashesExtension(**kwargs)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
__all__ = ["TexsmithSmartDashesExtension", "makeExtension"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# TeXSmith Article Template
|
|
2
|
+
|
|
3
|
+
This directory hosts the built-in `article` layout bundled with [TeXSmith](https://github.com/yves-chevallier/texsmith). The CLI exposes it via `--template article` (or `-tarticle`), so no extra installation is required.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
texsmith intro.md --template article --output-dir build/article
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To customise the template, copy this directory (or extract it via `importlib.resources`) and adjust the manifest, assets, or LaTeX entry point before packaging it as your own distribution.
|
|
10
|
+
|
|
11
|
+
## Fonts
|
|
12
|
+
|
|
13
|
+
Markdown is usually rendered on web broswers that supports a wide range of unicode characters. In LaTeX few fonts support such a wide range of characters. This template uses the most comprehensive font available: [Noto](https://notofonts.github.io/). However you need to install the font on your system first. You can download it from [Google Fonts](https://fonts.google.com/noto) or install it through your system package manager. For example on Ubuntu:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
sudo apt update
|
|
17
|
+
sudo apt install fonts-noto-core
|
|
18
|
+
sudo apt install fonts-noto-cjk
|
|
19
|
+
sudo apt install fonts-noto-extra
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For black and white emojis, the `Symbola` font is used. For example on Ubuntu:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
sudo apt update
|
|
26
|
+
sudo apt install fonts-symbola
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Template Details
|
|
30
|
+
|
|
31
|
+
- Engine: LuaLaTeX (shell escape required)
|
|
32
|
+
- TeX Live year: 2023
|
|
33
|
+
- tlmgr packages: `babel`, `geometry`, `hyperref`, `microtype`, `fontspec`
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Article template integration for texsmith."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable, Mapping
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, ClassVar
|
|
8
|
+
import unicodedata
|
|
9
|
+
|
|
10
|
+
from texsmith.adapters.latex.utils import escape_latex_chars
|
|
11
|
+
from texsmith.core.templates import TemplateError, WrappableTemplate
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_PACKAGE_ROOT = Path(__file__).parent.resolve()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Template(WrappableTemplate):
|
|
18
|
+
"""Expose the article template as a wrappable template instance."""
|
|
19
|
+
|
|
20
|
+
_LATIN_RANGE_LIMIT: ClassVar[int] = 0x024F
|
|
21
|
+
_TEXTCOMP_CHARACTERS: ClassVar[set[str]] = {
|
|
22
|
+
"\N{EURO SIGN}",
|
|
23
|
+
"\N{POUND SIGN}",
|
|
24
|
+
"\N{YEN SIGN}",
|
|
25
|
+
"\N{SECTION SIGN}",
|
|
26
|
+
"\N{PILCROW SIGN}",
|
|
27
|
+
"\N{DEGREE SIGN}",
|
|
28
|
+
"\N{PLUS-MINUS SIGN}",
|
|
29
|
+
"\N{MICRO SIGN}",
|
|
30
|
+
"\N{MULTIPLICATION SIGN}",
|
|
31
|
+
"\N{DIVISION SIGN}",
|
|
32
|
+
"\N{COPYRIGHT SIGN}",
|
|
33
|
+
"\N{REGISTERED SIGN}",
|
|
34
|
+
"\N{TRADE MARK SIGN}",
|
|
35
|
+
"\N{VULGAR FRACTION ONE HALF}",
|
|
36
|
+
"\N{VULGAR FRACTION ONE QUARTER}",
|
|
37
|
+
"\N{VULGAR FRACTION THREE QUARTERS}",
|
|
38
|
+
"\N{PER MILLE SIGN}",
|
|
39
|
+
}
|
|
40
|
+
_ALLOWED_PUNCTUATION: ClassVar[set[str]] = {
|
|
41
|
+
"\N{LEFT-POINTING DOUBLE ANGLE QUOTATION MARK}",
|
|
42
|
+
"\N{RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK}",
|
|
43
|
+
"\N{SINGLE LEFT-POINTING ANGLE QUOTATION MARK}",
|
|
44
|
+
"\N{SINGLE RIGHT-POINTING ANGLE QUOTATION MARK}",
|
|
45
|
+
"\N{DOUBLE LOW-9 QUOTATION MARK}",
|
|
46
|
+
"\N{LEFT DOUBLE QUOTATION MARK}",
|
|
47
|
+
"\N{RIGHT DOUBLE QUOTATION MARK}",
|
|
48
|
+
"\N{RIGHT SINGLE QUOTATION MARK}",
|
|
49
|
+
"\N{SINGLE LOW-9 QUOTATION MARK}",
|
|
50
|
+
"\N{MIDDLE DOT}",
|
|
51
|
+
"\N{EN DASH}",
|
|
52
|
+
"\N{EM DASH}",
|
|
53
|
+
"\N{HORIZONTAL ELLIPSIS}",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def __init__(self) -> None:
|
|
57
|
+
try:
|
|
58
|
+
super().__init__(_PACKAGE_ROOT)
|
|
59
|
+
except TemplateError as exc:
|
|
60
|
+
raise TemplateError(f"Failed to initialise article template: {exc}") from exc
|
|
61
|
+
config_path = (self.root / "template" / "mermaid-config.json").resolve()
|
|
62
|
+
self.extras = {"mermaid_config": str(config_path)}
|
|
63
|
+
|
|
64
|
+
def prepare_context(
|
|
65
|
+
self,
|
|
66
|
+
latex_body: str,
|
|
67
|
+
*,
|
|
68
|
+
overrides: Mapping[str, Any] | None = None,
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
context = super().prepare_context(latex_body, overrides=overrides)
|
|
71
|
+
context["ts_extra_disable_hyperref"] = True
|
|
72
|
+
|
|
73
|
+
# Transform author metadata into a LaTeX-ready string while preserving defaults.
|
|
74
|
+
raw_authors = context.pop("authors", None)
|
|
75
|
+
author_value = self._format_authors(raw_authors)
|
|
76
|
+
if author_value:
|
|
77
|
+
context["author"] = author_value
|
|
78
|
+
else:
|
|
79
|
+
fallback_author = self._coerce_string(context.get("author"))
|
|
80
|
+
if fallback_author:
|
|
81
|
+
context["author"] = fallback_author
|
|
82
|
+
else:
|
|
83
|
+
context.pop("author", None)
|
|
84
|
+
|
|
85
|
+
context.pop("press", None)
|
|
86
|
+
|
|
87
|
+
engine_default = self.info.engine or "pdflatex"
|
|
88
|
+
|
|
89
|
+
context.setdefault("latex_engine", engine_default)
|
|
90
|
+
context.setdefault("requires_unicode_engine", False)
|
|
91
|
+
context.setdefault("unicode_chars", "")
|
|
92
|
+
context.setdefault("unicode_problematic_chars", "")
|
|
93
|
+
context.setdefault("pdflatex_extra_packages", [])
|
|
94
|
+
|
|
95
|
+
candidate_emoji = context.get("emoji")
|
|
96
|
+
if not candidate_emoji:
|
|
97
|
+
fonts_cfg = context.get("fonts")
|
|
98
|
+
if isinstance(fonts_cfg, Mapping):
|
|
99
|
+
candidate_emoji = fonts_cfg.get("emoji")
|
|
100
|
+
if not candidate_emoji:
|
|
101
|
+
press_cfg = context.get("press")
|
|
102
|
+
if isinstance(press_cfg, Mapping):
|
|
103
|
+
press_fonts = press_cfg.get("fonts")
|
|
104
|
+
if isinstance(press_fonts, Mapping):
|
|
105
|
+
candidate_emoji = press_fonts.get("emoji")
|
|
106
|
+
|
|
107
|
+
emoji_mode = self._normalise_emoji_mode(candidate_emoji)
|
|
108
|
+
context["emoji"] = emoji_mode
|
|
109
|
+
if emoji_mode in {"symbola", "color"}:
|
|
110
|
+
context["latex_engine"] = "lualatex"
|
|
111
|
+
context["requires_unicode_engine"] = True
|
|
112
|
+
|
|
113
|
+
return context
|
|
114
|
+
|
|
115
|
+
def _coerce_string(self, value: Any) -> str | None:
|
|
116
|
+
if value is None:
|
|
117
|
+
return None
|
|
118
|
+
candidate = value.strip() if isinstance(value, str) else str(value).strip()
|
|
119
|
+
return candidate or None
|
|
120
|
+
|
|
121
|
+
def _format_authors(self, payload: Any) -> str | None:
|
|
122
|
+
if not payload:
|
|
123
|
+
return None
|
|
124
|
+
if not isinstance(payload, Iterable) or isinstance(payload, (str, bytes)):
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
formatted: list[str] = []
|
|
128
|
+
for item in payload:
|
|
129
|
+
if not isinstance(item, Mapping):
|
|
130
|
+
continue
|
|
131
|
+
name_value = self._coerce_string(item.get("name"))
|
|
132
|
+
if not name_value:
|
|
133
|
+
continue
|
|
134
|
+
name_tex = escape_latex_chars(name_value)
|
|
135
|
+
affiliation_value = self._coerce_string(item.get("affiliation"))
|
|
136
|
+
if affiliation_value:
|
|
137
|
+
affiliation_tex = escape_latex_chars(affiliation_value)
|
|
138
|
+
formatted.append(f"{name_tex}\\thanks{{{affiliation_tex}}}")
|
|
139
|
+
else:
|
|
140
|
+
formatted.append(name_tex)
|
|
141
|
+
|
|
142
|
+
if not formatted:
|
|
143
|
+
return None
|
|
144
|
+
return " \\and ".join(formatted)
|
|
145
|
+
|
|
146
|
+
def _normalise_emoji_mode(self, value: Any) -> str:
|
|
147
|
+
candidate = self._coerce_string(value)
|
|
148
|
+
if candidate:
|
|
149
|
+
candidate_stripped = candidate.strip()
|
|
150
|
+
else:
|
|
151
|
+
default_value = self.info.get_attribute_default("emoji") or "black"
|
|
152
|
+
candidate = self._coerce_string(default_value)
|
|
153
|
+
candidate_stripped = candidate.strip() if candidate else "black"
|
|
154
|
+
|
|
155
|
+
if not candidate_stripped:
|
|
156
|
+
return "black"
|
|
157
|
+
|
|
158
|
+
lowered = candidate_stripped.lower()
|
|
159
|
+
if lowered in {"artifact", "symbola", "color", "black", "twemoji"}:
|
|
160
|
+
return lowered
|
|
161
|
+
return candidate_stripped
|
|
162
|
+
|
|
163
|
+
def wrap_document(
|
|
164
|
+
self,
|
|
165
|
+
latex_body: str,
|
|
166
|
+
*,
|
|
167
|
+
overrides: Mapping[str, Any] | None = None,
|
|
168
|
+
context: Mapping[str, Any] | None = None,
|
|
169
|
+
) -> str:
|
|
170
|
+
if context is None:
|
|
171
|
+
prepared = self.prepare_context(latex_body, overrides=overrides)
|
|
172
|
+
context_ref: dict[str, Any] | None = prepared
|
|
173
|
+
else:
|
|
174
|
+
prepared = dict(context)
|
|
175
|
+
context_ref = context if isinstance(context, dict) else None
|
|
176
|
+
|
|
177
|
+
preamble_override: str | None = None
|
|
178
|
+
if overrides:
|
|
179
|
+
press_section = overrides.get("press")
|
|
180
|
+
if isinstance(press_section, Mapping):
|
|
181
|
+
override_section = press_section.get("override")
|
|
182
|
+
if isinstance(override_section, Mapping):
|
|
183
|
+
candidate = override_section.get("preamble")
|
|
184
|
+
if isinstance(candidate, str) and candidate.strip():
|
|
185
|
+
preamble_override = candidate.strip()
|
|
186
|
+
if preamble_override is None:
|
|
187
|
+
direct_override = overrides.get("preamble")
|
|
188
|
+
if isinstance(direct_override, str) and direct_override.strip():
|
|
189
|
+
preamble_override = direct_override.strip()
|
|
190
|
+
|
|
191
|
+
if preamble_override:
|
|
192
|
+
existing_extra = prepared.get("extra_packages") or ""
|
|
193
|
+
package_parts = [existing_extra] if existing_extra else []
|
|
194
|
+
package_parts.append(preamble_override)
|
|
195
|
+
prepared["extra_packages"] = "\n".join(part for part in package_parts if part)
|
|
196
|
+
|
|
197
|
+
self._analyse_unicode_payload(prepared)
|
|
198
|
+
|
|
199
|
+
if context_ref is not prepared and context_ref is not None:
|
|
200
|
+
context_ref.clear()
|
|
201
|
+
context_ref.update(prepared)
|
|
202
|
+
|
|
203
|
+
return super().wrap_document(latex_body, overrides=overrides, context=prepared)
|
|
204
|
+
|
|
205
|
+
def _analyse_unicode_payload(self, context: dict[str, Any]) -> None:
|
|
206
|
+
unicode_chars = self._collect_unicode_characters(context)
|
|
207
|
+
problematic = []
|
|
208
|
+
extra_packages: set[str] = set()
|
|
209
|
+
|
|
210
|
+
base_engine = context.get("latex_engine") or "pdflatex"
|
|
211
|
+
|
|
212
|
+
for char in unicode_chars:
|
|
213
|
+
classification = self._classify_character(char)
|
|
214
|
+
if classification == "textcomp":
|
|
215
|
+
extra_packages.add("textcomp")
|
|
216
|
+
elif classification == "unsupported":
|
|
217
|
+
problematic.append(char)
|
|
218
|
+
|
|
219
|
+
context["unicode_chars"] = "".join(sorted(unicode_chars, key=ord))
|
|
220
|
+
context["unicode_problematic_chars"] = "".join(sorted(problematic, key=ord))
|
|
221
|
+
context["pdflatex_extra_packages"] = sorted(extra_packages)
|
|
222
|
+
|
|
223
|
+
requires_unicode_engine = bool(problematic)
|
|
224
|
+
context["requires_unicode_engine"] = requires_unicode_engine
|
|
225
|
+
context["latex_engine"] = "lualatex" if requires_unicode_engine else base_engine
|
|
226
|
+
|
|
227
|
+
def _collect_unicode_characters(self, payload: Any) -> set[str]:
|
|
228
|
+
collected: set[str] = set()
|
|
229
|
+
visited: set[int] = set()
|
|
230
|
+
|
|
231
|
+
def _walk(value: Any) -> None:
|
|
232
|
+
if isinstance(value, str):
|
|
233
|
+
for char in value:
|
|
234
|
+
if ord(char) > 0x7F:
|
|
235
|
+
collected.add(char)
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
if isinstance(value, Mapping):
|
|
239
|
+
identifier = id(value)
|
|
240
|
+
if identifier in visited:
|
|
241
|
+
return
|
|
242
|
+
visited.add(identifier)
|
|
243
|
+
for key, item in value.items():
|
|
244
|
+
if isinstance(key, str) and key == "callouts_definitions":
|
|
245
|
+
continue
|
|
246
|
+
_walk(item)
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
if isinstance(value, Iterable) and not isinstance(value, (bytes, bytearray)):
|
|
250
|
+
identifier = id(value)
|
|
251
|
+
if identifier in visited:
|
|
252
|
+
return
|
|
253
|
+
visited.add(identifier)
|
|
254
|
+
for item in value:
|
|
255
|
+
_walk(item)
|
|
256
|
+
|
|
257
|
+
_walk(payload)
|
|
258
|
+
return collected
|
|
259
|
+
|
|
260
|
+
def _classify_character(self, char: str) -> str:
|
|
261
|
+
codepoint = ord(char)
|
|
262
|
+
if codepoint <= self._LATIN_RANGE_LIMIT:
|
|
263
|
+
return "latin"
|
|
264
|
+
if char in self._ALLOWED_PUNCTUATION:
|
|
265
|
+
return "punctuation"
|
|
266
|
+
if char in self._TEXTCOMP_CHARACTERS:
|
|
267
|
+
return "textcomp"
|
|
268
|
+
|
|
269
|
+
name: str | None
|
|
270
|
+
try:
|
|
271
|
+
name = unicodedata.name(char)
|
|
272
|
+
except ValueError:
|
|
273
|
+
name = None
|
|
274
|
+
|
|
275
|
+
if name and "LATIN" in name:
|
|
276
|
+
return "latin"
|
|
277
|
+
|
|
278
|
+
return "unsupported"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
__all__ = ["Template"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
[compat]
|
|
2
|
+
texsmith = ">=0.1,<1.0"
|
|
3
|
+
|
|
4
|
+
[latex.template]
|
|
5
|
+
name = "article"
|
|
6
|
+
version = "0.1.0"
|
|
7
|
+
entrypoint = "template/template.tex"
|
|
8
|
+
engine = "lualatex"
|
|
9
|
+
shell_escape = false
|
|
10
|
+
texlive_year = 2023
|
|
11
|
+
tlmgr_packages = [
|
|
12
|
+
"babel",
|
|
13
|
+
"geometry",
|
|
14
|
+
"hyperref",
|
|
15
|
+
"microtype",
|
|
16
|
+
"lmodern",
|
|
17
|
+
"textcomp",
|
|
18
|
+
"fontspec",
|
|
19
|
+
"biblatex",
|
|
20
|
+
]
|
|
21
|
+
fragments = [
|
|
22
|
+
"ts-geometry",
|
|
23
|
+
"ts-typesetting",
|
|
24
|
+
"ts-fonts",
|
|
25
|
+
"ts-extra",
|
|
26
|
+
"ts-keystrokes",
|
|
27
|
+
"ts-callouts",
|
|
28
|
+
"ts-code",
|
|
29
|
+
"ts-glossary",
|
|
30
|
+
"ts-index",
|
|
31
|
+
"ts-bibliography",
|
|
32
|
+
"ts-todolist",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[latex.template.attributes.title]
|
|
36
|
+
default = ""
|
|
37
|
+
type = "string"
|
|
38
|
+
escape = "latex"
|
|
39
|
+
allow_empty = true
|
|
40
|
+
sources = ["title"]
|
|
41
|
+
description = "The title of the document. If empty, first heading in document will be used."
|
|
42
|
+
|
|
43
|
+
[latex.template.attributes.subtitle]
|
|
44
|
+
default = ""
|
|
45
|
+
type = "string"
|
|
46
|
+
escape = "latex"
|
|
47
|
+
sources = ["subtitle"]
|
|
48
|
+
description = "The subtitle of the document."
|
|
49
|
+
|
|
50
|
+
[latex.template.attributes.toc]
|
|
51
|
+
default = false
|
|
52
|
+
type = "boolean"
|
|
53
|
+
sources = ["toc"]
|
|
54
|
+
description = "Whether to include a table of contents."
|
|
55
|
+
|
|
56
|
+
[latex.template.attributes.authors]
|
|
57
|
+
default = []
|
|
58
|
+
type = "list"
|
|
59
|
+
sources = ["authors"]
|
|
60
|
+
description = "List of authors of the document. Use plain strings or objects with 'name', 'affiliation', and 'email' fields. You may include multiple authors."
|
|
61
|
+
|
|
62
|
+
[latex.template.attributes.date]
|
|
63
|
+
default = ""
|
|
64
|
+
type = "string"
|
|
65
|
+
escape = "latex"
|
|
66
|
+
sources = ["date"]
|
|
67
|
+
description = "The date of the document, use ISO format (YYYY-MM-DD) or any string. If empty, the current date will be used."
|
|
68
|
+
|
|
69
|
+
[latex.template.attributes.language]
|
|
70
|
+
default = "english"
|
|
71
|
+
type = "string"
|
|
72
|
+
allow_empty = false
|
|
73
|
+
normaliser = "babel_language"
|
|
74
|
+
description = "The language for hyphenation, typesettings and document terms (e.g., 'Table of Contents')."
|
|
75
|
+
|
|
76
|
+
[latex.template.attributes.columns]
|
|
77
|
+
default = 1
|
|
78
|
+
type = "integer"
|
|
79
|
+
description = "Number of columns for the document. When >1, uses the documentclass twocolumn option."
|
|
80
|
+
|
|
81
|
+
[latex.template.attributes.page_numbers]
|
|
82
|
+
default = true
|
|
83
|
+
type = "boolean"
|
|
84
|
+
sources = ["page_numbers"]
|
|
85
|
+
description = "Whether to include page numbers in the document."
|
|
86
|
+
|
|
87
|
+
[latex.template.attributes.numbering]
|
|
88
|
+
default = true
|
|
89
|
+
type = "boolean"
|
|
90
|
+
sources = ["numbering"]
|
|
91
|
+
description = "Whether to include section numbering in the document."
|
|
92
|
+
|
|
93
|
+
[latex.template.attributes.glossary_style]
|
|
94
|
+
default = "list"
|
|
95
|
+
type = "string"
|
|
96
|
+
allow_empty = false
|
|
97
|
+
sources = ["glossary_style"]
|
|
98
|
+
description = "The style used for the glossary section."
|
|
99
|
+
|
|
100
|
+
[latex.template.attributes.bibliography_style]
|
|
101
|
+
default = "numeric"
|
|
102
|
+
type = "string"
|
|
103
|
+
description = "The bibliography style to use. See BibLaTeX documentation for available styles."
|
|
104
|
+
|
|
105
|
+
# Slots definition:
|
|
106
|
+
# -----------------
|
|
107
|
+
# Each slot represents a section of the document that can be filled with content.
|
|
108
|
+
# The 'depth' parameter indicates the LaTeX sectioning command to use.
|
|
109
|
+
[latex.template.slots.mainmatter]
|
|
110
|
+
default = true
|
|
111
|
+
depth = "section"
|
|
112
|
+
description = "The main content of the document."
|
|
113
|
+
|
|
114
|
+
[latex.template.slots.abstract]
|
|
115
|
+
depth = "section"
|
|
116
|
+
strip_heading = true
|
|
117
|
+
description = "The abstract or summary of the document."
|
|
118
|
+
|
|
119
|
+
[latex.template.slots.appendix]
|
|
120
|
+
depth = "section"
|
|
121
|
+
description = "Appendix content inserted before backmatter."
|
|
122
|
+
|
|
123
|
+
[latex.template.slots.backmatter]
|
|
124
|
+
depth = "section"
|
|
125
|
+
strip_heading = true
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"theme": "base",
|
|
3
|
+
"themeVariables": {
|
|
4
|
+
"primaryColor": "white",
|
|
5
|
+
"primaryTextColor": "black",
|
|
6
|
+
"primaryBorderColor": "black",
|
|
7
|
+
"secondaryColor": "transparent",
|
|
8
|
+
"secondaryTextColor": "black",
|
|
9
|
+
"tertiaryColor": "transparent",
|
|
10
|
+
"lineColor": "black",
|
|
11
|
+
"background": "red",
|
|
12
|
+
"clusterBkg": "white",
|
|
13
|
+
"clusterBorder": "black",
|
|
14
|
+
"fontFamily": "Latin Modern Roman, Computer Modern Serif, Times New Roman, serif",
|
|
15
|
+
"edgeLabelBackground": "#ffffff"
|
|
16
|
+
},
|
|
17
|
+
"themeCSS": ".node rect, .node circle, .node ellipse, .node polygon, .node path { stroke-width: 2px; }\n .edgePath .path { stroke-width: 2px !important; }\n.packetBlock { fill: #ffffff !important; stroke: #000000 !important; }\n.packet rect, .packet polygon { fill: #ffffff !important; stroke: #000000 !important; }\n.packet text, .packetLabel, .packetByte, .packetTitle { fill: #000000 !important; }\n.node rect, .node polygon, .node ellipse { fill: white !important; }"
|
|
18
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
\BLOCK{ set base_docclass_options = documentclass_options|default('', true) }
|
|
2
|
+
\BLOCK{ set combined_docclass_options = base_docclass_options[1:-1] if base_docclass_options else '' }
|
|
3
|
+
\BLOCK{ set column_count = columns|default(1)|int }
|
|
4
|
+
\BLOCK{ set option_parts = [] }
|
|
5
|
+
\BLOCK{ if combined_docclass_options }\BLOCK{ set option_parts = option_parts + [combined_docclass_options] }\BLOCK{ endif }
|
|
6
|
+
\BLOCK{ if column_count > 1 }\BLOCK{ set option_parts = option_parts + ['twocolumn'] }\BLOCK{ endif }
|
|
7
|
+
\BLOCK{ set effective_documentclass_options = '[' ~ option_parts|join(',') ~ ']' if option_parts else '' }
|
|
8
|
+
\documentclass\VAR{effective_documentclass_options}{article}
|
|
9
|
+
|
|
10
|
+
\usepackage[svgnames,dvipsnames,x11names]{xcolor}
|
|
11
|
+
\VAR{extra_packages}
|
|
12
|
+
\usepackage{graphicx}
|
|
13
|
+
\usepackage[\VAR{language|default('english')}]{babel}
|
|
14
|
+
\usepackage[colorlinks=true, linkcolor=NavyBlue, urlcolor=NavyBlue, citecolor=NavyBlue]{hyperref}
|
|
15
|
+
|
|
16
|
+
\usepackage{lastpage} % pour le label LastPage
|
|
17
|
+
\usepackage{refcount} % pour \getpagerefnumber
|
|
18
|
+
\makeatletter
|
|
19
|
+
\def\ps@plain{%
|
|
20
|
+
\let\@oddhead\@empty
|
|
21
|
+
\let\@evenhead\@empty
|
|
22
|
+
\def\@oddfoot{%
|
|
23
|
+
\hfil
|
|
24
|
+
% Afficher le numéro seulement si le document a plus d’une page
|
|
25
|
+
\ifnum\getpagerefnumber{LastPage}>1\relax
|
|
26
|
+
\thepage
|
|
27
|
+
\fi
|
|
28
|
+
\hfil}%
|
|
29
|
+
\let\@evenfoot\@oddfoot
|
|
30
|
+
}
|
|
31
|
+
\makeatother
|
|
32
|
+
|
|
33
|
+
\BLOCK{ set has_title_metadata = title|default('')|trim }
|
|
34
|
+
\BLOCK{ set effective_date = date|default('')|trim }
|
|
35
|
+
\BLOCK{ if has_title_metadata }
|
|
36
|
+
\title{\VAR{title}\BLOCK{ if subtitle }\\\large \VAR{subtitle}\BLOCK{ endif }}
|
|
37
|
+
\BLOCK{ if author }\author{\VAR{author}}\BLOCK{ else }\author{}\BLOCK{ endif }
|
|
38
|
+
\BLOCK{ if effective_date }\date{\VAR{date}}\BLOCK{ else }\date{\today}\BLOCK{ endif }
|
|
39
|
+
\BLOCK{ endif }
|
|
40
|
+
|
|
41
|
+
\begin{document}
|
|
42
|
+
|
|
43
|
+
\BLOCK{ if has_title_metadata }
|
|
44
|
+
\maketitle
|
|
45
|
+
\BLOCK{ endif }
|
|
46
|
+
\BLOCK{ if not page_numbers }
|
|
47
|
+
\pagenumbering{gobble}
|
|
48
|
+
\thispagestyle{empty}
|
|
49
|
+
\pagestyle{empty}
|
|
50
|
+
\BLOCK{ endif }
|
|
51
|
+
\BLOCK{ if numbering is defined and not numbering }
|
|
52
|
+
\setcounter{secnumdepth}{-1}
|
|
53
|
+
\BLOCK{ endif }
|
|
54
|
+
|
|
55
|
+
\BLOCK{ if abstract }
|
|
56
|
+
\begin{abstract}
|
|
57
|
+
\VAR{abstract}
|
|
58
|
+
\end{abstract}
|
|
59
|
+
\BLOCK{ endif }
|
|
60
|
+
\BLOCK{ if toc }
|
|
61
|
+
\tableofcontents
|
|
62
|
+
\BLOCK{ endif }
|
|
63
|
+
|
|
64
|
+
\VAR{mainmatter}
|
|
65
|
+
|
|
66
|
+
\BLOCK{ if appendix }
|
|
67
|
+
\appendix
|
|
68
|
+
\VAR{appendix}
|
|
69
|
+
\BLOCK{ endif }
|
|
70
|
+
|
|
71
|
+
\VAR{backmatter}
|
|
72
|
+
\VAR{fragment_backmatter}
|
|
73
|
+
|
|
74
|
+
\end{document}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# TeXSmith Book Template
|
|
2
|
+
|
|
3
|
+
The built-in `book` template targets long-form documents with front matter, chapters, appendices, and clean back matter. It wraps `memoir` defaults, modern callouts, keystroke helpers, todo lists, glossary/index hooks, and bibliography support.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
texsmith manuscript.md --template book \
|
|
9
|
+
--slot frontmatter:intro.md \
|
|
10
|
+
--slot backmatter:appendix.md
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Slots:
|
|
14
|
+
- `frontmatter`, `preface`, `dedication`
|
|
15
|
+
- `mainmatter` (default)
|
|
16
|
+
- `appendix`
|
|
17
|
+
- `backmatter`, `colophon`
|
|
18
|
+
|
|
19
|
+
## Template Details
|
|
20
|
+
|
|
21
|
+
- Engine: LuaLaTeX
|
|
22
|
+
- TeX Live year: 2023
|
|
23
|
+
- tlmgr packages: `babel`, `babel-french`, `csquotes`, `fontspec`, `fancyvrb`, `geometry`, `hyperref`, `longtable`, `microtype`, `titlesec`, `titletoc`, `xcolor`, `xunicode`
|
|
24
|
+
- Built-in fragments: geometry, typesetting, fonts, extra, keystrokes, callouts, code, glossary, index, bibliography, todolist
|
|
25
|
+
- Attributes: title/subtitle/authors/email/date/language/documentclass, hyperref options, edition/publisher/imprint*, glossary toggles, list-of figures/tables, `part` flag for part/chapter structure.
|
|
26
|
+
- Assets: none (latexmkrc is injected by core).
|