pytex-preprocessor 0.1.0rc1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/PKG-INFO +1 -1
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/pyproject.toml +4 -1
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_builder/build.py +17 -4
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_builder/render.py +8 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_builder/tectonic.py +83 -20
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/citations.py +4 -2
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/document.py +5 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/hyperref_config.py +5 -1
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/tex/pagesetup.tex +2 -2
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/titlepage.py +64 -52
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_markdown/convert.py +6 -3
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/PKG-INFO +1 -1
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/SOURCES.txt +8 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/top_level.txt +1 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/__init__.py +37 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/convert.py +202 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/document.py +154 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/entries.py +96 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/frontmatter.py +80 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/header.py +139 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/shortcodes.py +130 -0
- pytex_preprocessor-0.1.2/src/pytex_protocol/signatures.py +84 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/README.md +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/setup.cfg +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/biblatex.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/builtin.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/captions.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/cleveref.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/colors.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/conditionals.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/counters.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/definitions.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/floats.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/font.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/fontawesome.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/fontspec.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/geometry.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/glossaries.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/graphics.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/hooks.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/hyperref.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/lengths.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/listings.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/mdframed.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/picture.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/setspace.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/commands/tables.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/helpers/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/helpers/coerce.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/helpers/parenting.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/helpers/sanitize.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/helpers/with_package.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/interface/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/interface/control_sequence.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/interface/package.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/interface/tex.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/color.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/concat.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/control_sequence.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/document.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/document_class.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/empty.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/environment.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/image.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/include.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/length.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/math.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/package.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/model/raw.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/packages.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex/registry.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_builder/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_builder/console.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Bold.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BoldItalic.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Book.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-BookItalic.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Medium.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-MediumItalic.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Strong.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-Thin.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Blender/Blender-ThinItalic.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Black.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Bold.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-BoldItalic.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Italic.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Medium.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/DIN/DIN-Regular.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/fonts/Times New Roman.ttf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/ASTA.svg +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/DUMMY.png +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/DUMMY_FOOT.png +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/ECHO.svg +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/HSRT.pdf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/INF.pdf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/STUPA.pdf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/assets/logos/Skyline.pdf +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/boxes.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/cleveref_names.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/colors.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/fonts.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/glossary.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/listings.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/logos.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/pagebreak.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/pagesetup.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/variants.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/voting.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/watermark.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/wordcount.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_koma/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_koma/commands.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_koma/document.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_markdown/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_markdown/escape.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/dependency_links.txt +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/entry_points.txt +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/requires.txt +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_tikz/__init__.py +0 -0
- {pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_tikz/tikz.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytex-preprocessor"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Frederik Beimgraben", email="frederik@beimgraben.net" },
|
|
10
10
|
]
|
|
@@ -72,6 +72,9 @@ ignore = ["N802"]
|
|
|
72
72
|
# glossary holds raw LaTeX template constants (column types, style, labels)
|
|
73
73
|
# that exceed the line length and cannot be reflowed.
|
|
74
74
|
"src/pytex_hsrtreport/glossary.py" = ["E501"]
|
|
75
|
+
# tectonic pins a table of 64-char biber SHA256 checksums; the hash literals
|
|
76
|
+
# are unbreakable.
|
|
77
|
+
"src/pytex_builder/tectonic.py" = ["E501"]
|
|
75
78
|
|
|
76
79
|
[tool.ruff.lint.isort]
|
|
77
80
|
known-first-party = ["pytex", "pytex_builder", "pytex_koma", "pytex_tikz", "pytex_hsrtreport", "pytex_markdown"]
|
|
@@ -8,6 +8,7 @@ acronyms resolve.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
|
+
import re
|
|
11
12
|
import sys
|
|
12
13
|
from dataclasses import dataclass
|
|
13
14
|
from pathlib import Path
|
|
@@ -42,16 +43,28 @@ def _default_output(inp: Path, build_dir: Path) -> Path:
|
|
|
42
43
|
``build_dir`` so the ``.out.tex`` and its inline assets (fonts, logos,
|
|
43
44
|
images) stay out of the source tree:
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
The stem is also slugified (whitespace and shell/TeX-hostile characters
|
|
47
|
+
become ``_``) because it becomes the TeX ``\\jobname``; spaces there break
|
|
48
|
+
tectonic's biber/makeindex steps (the ``.bcf`` path cannot be opened):
|
|
49
|
+
|
|
50
|
+
* ``example.tex.py`` -> ``<build_dir>/example.out.tex``
|
|
51
|
+
* ``report.py`` -> ``<build_dir>/report.out.tex``
|
|
52
|
+
* ``2026-06-15 STUPA.md`` -> ``<build_dir>/2026-06-15_STUPA.md.out.tex``
|
|
48
53
|
"""
|
|
49
54
|
base = inp
|
|
50
55
|
if base.suffix.lower() in {".py", ".tex"}:
|
|
51
56
|
base = base.with_suffix("")
|
|
52
57
|
if base.suffix.lower() == ".tex":
|
|
53
58
|
base = base.with_suffix("")
|
|
54
|
-
return build_dir / f"{base.name}.out.tex"
|
|
59
|
+
return build_dir / f"{_slug(base.name)}.out.tex"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _slug(name: str) -> str:
|
|
63
|
+
"""Make `name` safe as a TeX jobname: collapse whitespace and drop
|
|
64
|
+
characters that confuse tectonic/biber/makeindex."""
|
|
65
|
+
name = re.sub(r"\s+", "_", name.strip())
|
|
66
|
+
name = re.sub(r"[^\w.\-]", "", name)
|
|
67
|
+
return name or "document"
|
|
55
68
|
|
|
56
69
|
|
|
57
70
|
def _parse_args(argv: list[str]) -> Config:
|
|
@@ -58,7 +58,15 @@ def _render_markdown(path: Path) -> TeX:
|
|
|
58
58
|
# Imported lazily so plain .tex/.py builds need neither marko nor hsrt.
|
|
59
59
|
from pytex.model.document import Document
|
|
60
60
|
from pytex_markdown import IncludeMarkdown
|
|
61
|
+
from pytex_protocol.frontmatter import split_frontmatter
|
|
61
62
|
|
|
63
|
+
text = path.read_text()
|
|
64
|
+
meta, _ = split_frontmatter(text)
|
|
65
|
+
# A `gremium:` frontmatter key marks a STUPA/AStA meeting protocol.
|
|
66
|
+
if "gremium" in meta or meta.get("typ") == "protokoll":
|
|
67
|
+
from pytex_protocol import render_protocol
|
|
68
|
+
|
|
69
|
+
return render_protocol(text)
|
|
62
70
|
return Document(IncludeMarkdown(path))
|
|
63
71
|
|
|
64
72
|
|
|
@@ -7,6 +7,7 @@ its working directory - we point that at the cache dir.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import hashlib
|
|
10
11
|
import os
|
|
11
12
|
import platform
|
|
12
13
|
import shutil
|
|
@@ -43,6 +44,30 @@ BIBER_RELEASE_URL = (
|
|
|
43
44
|
"biblatex-biber/{version}/binaries/{sf_dir}/{filename}/download"
|
|
44
45
|
)
|
|
45
46
|
|
|
47
|
+
# Mirror of the upstream biber binaries, hosted as release assets so builds
|
|
48
|
+
# survive SourceForge outages (it periodically gates downloads behind a
|
|
49
|
+
# Cloudflare challenge that curl cannot pass). Tried before SourceForge.
|
|
50
|
+
BIBER_MIRROR_URL = (
|
|
51
|
+
"https://github.com/frederikbeimgraben/PyTeX-Preprocessor"
|
|
52
|
+
"/releases/download/biber-binaries/{asset}"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# SHA256 of each upstream biber tarball, keyed by the versioned mirror asset
|
|
56
|
+
# name. Used to verify downloads from either source and to reject HTML error
|
|
57
|
+
# pages a CDN might serve with a 200 status. Linux x86_64 only for now; other
|
|
58
|
+
# platforms download from SourceForge without a pinned checksum.
|
|
59
|
+
BIBER_SHA256: dict[str, str] = {
|
|
60
|
+
"biber-2.11-linux_x86_64.tar.gz": "7fcb51491fb24151810a92b2e2d03b7a1291823c0f8d6fb53183af391fca42e7",
|
|
61
|
+
"biber-2.12-linux_x86_64.tar.gz": "fd0b5145cc908c400a701b583330635d533d750b73a272d1d5ea47e10b2fbf71",
|
|
62
|
+
"biber-2.13-linux_x86_64.tar.gz": "03101f418d46f4666272b68a4318d9e4b7a840d9dfa05d93ddc490d491157a75",
|
|
63
|
+
"biber-2.14-linux_x86_64.tar.gz": "dab3177f03322b5529d07d47d21d9e573a90c23d86eaaf11591b2d155316ee1b",
|
|
64
|
+
"biber-2.15-linux_x86_64.tar.gz": "653c8add18d2e94a233a6b9aae6d8144f965c2ce13fb7b4e66502b55fcd06e06",
|
|
65
|
+
"biber-2.16-linux_x86_64.tar.gz": "3afb97a42d2cf272d3c0b51663725e55339c4e6f3d594cd52e16c39fa9fcfb13",
|
|
66
|
+
"biber-2.17-linux_x86_64.tar.gz": "129d2e0332a57e985ffa253e5e9fbd28ef99af5a068d1b141145211969aa8999",
|
|
67
|
+
"biber-2.18-linux_x86_64.tar.gz": "2a6b4cd15a1139907799da0d23cd4ddcce8341af3960d2b3d1d3e4b4a9f1fb53",
|
|
68
|
+
"biber-2.19-linux_x86_64.tar.gz": "e2eda3e6ea7ac7e78d60e99a0e2aeb1096829f95791c06b768ed31a12889e58e",
|
|
69
|
+
}
|
|
70
|
+
|
|
46
71
|
|
|
47
72
|
class BuildError(RuntimeError):
|
|
48
73
|
"""Raised when an external tool is missing or exits non-zero."""
|
|
@@ -109,33 +134,67 @@ def _biber_cached(version: str) -> Path:
|
|
|
109
134
|
return CACHE_DIR / "biber" / version / "biber"
|
|
110
135
|
|
|
111
136
|
|
|
137
|
+
def _mirror_asset(version: str, filename: str) -> str:
|
|
138
|
+
"""Versioned mirror asset name, e.g. ``biber-2.17-linux_x86_64.tar.gz``."""
|
|
139
|
+
return filename.replace("biber-", f"biber-{version}-", 1)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _biber_sources(version: str) -> list[tuple[str, str | None]]:
|
|
143
|
+
"""(url, expected_sha256 or None) pairs to try in order: mirror, then SourceForge."""
|
|
144
|
+
sf_dir, filename = _biber_sf_path()
|
|
145
|
+
asset = _mirror_asset(version, filename)
|
|
146
|
+
sha = BIBER_SHA256.get(asset)
|
|
147
|
+
return [
|
|
148
|
+
(BIBER_MIRROR_URL.format(asset=asset), sha),
|
|
149
|
+
(
|
|
150
|
+
BIBER_RELEASE_URL.format(version=version, sf_dir=sf_dir, filename=filename),
|
|
151
|
+
sha,
|
|
152
|
+
),
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _download_to(url: str, dest: Path, sha: str | None, console: Console) -> bool:
|
|
157
|
+
"""Fetch *url* into *dest*; return True only on success and matching checksum."""
|
|
158
|
+
proc = subprocess.run(
|
|
159
|
+
["curl", "-fsSL", "-o", str(dest), url],
|
|
160
|
+
capture_output=True,
|
|
161
|
+
text=True,
|
|
162
|
+
)
|
|
163
|
+
if proc.returncode != 0 or not dest.exists():
|
|
164
|
+
return False
|
|
165
|
+
if sha is not None:
|
|
166
|
+
actual = hashlib.sha256(dest.read_bytes()).hexdigest()
|
|
167
|
+
if actual != sha:
|
|
168
|
+
console.warn(f"checksum mismatch from {url}; discarding")
|
|
169
|
+
dest.unlink(missing_ok=True)
|
|
170
|
+
return False
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
|
|
112
174
|
def _ensure_biber(version: str, console: Console) -> Path:
|
|
113
|
-
"""Return a path to biber *version*, downloading from
|
|
175
|
+
"""Return a path to biber *version*, downloading from the mirror or SourceForge."""
|
|
114
176
|
cached = _biber_cached(version)
|
|
115
177
|
if cached.exists():
|
|
116
178
|
return cached
|
|
117
179
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
180
|
+
if not shutil.which("curl"):
|
|
181
|
+
raise BuildError(
|
|
182
|
+
"biber is not installed and cannot be downloaded without 'curl' on PATH"
|
|
183
|
+
)
|
|
122
184
|
|
|
185
|
+
console.step(f"Downloading biber {version}")
|
|
123
186
|
cached.parent.mkdir(parents=True, exist_ok=True)
|
|
124
|
-
tmp = cached.parent /
|
|
187
|
+
tmp = cached.parent / "biber.download"
|
|
125
188
|
try:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
text=True,
|
|
134
|
-
)
|
|
135
|
-
if proc.returncode != 0 or not tmp.exists():
|
|
189
|
+
downloaded = False
|
|
190
|
+
for url, sha in _biber_sources(version):
|
|
191
|
+
console.detail(f"source: {url}")
|
|
192
|
+
if _download_to(url, tmp, sha, console):
|
|
193
|
+
downloaded = True
|
|
194
|
+
break
|
|
195
|
+
if not downloaded:
|
|
136
196
|
raise BuildError(
|
|
137
|
-
f"failed to download biber {version}
|
|
138
|
-
+ (proc.stderr.strip() or "no output from curl")
|
|
197
|
+
f"failed to download biber {version} from the mirror or SourceForge"
|
|
139
198
|
)
|
|
140
199
|
with tarfile.open(tmp) as tf:
|
|
141
200
|
member = next(
|
|
@@ -147,10 +206,14 @@ def _ensure_biber(version: str, console: Console) -> Path:
|
|
|
147
206
|
None,
|
|
148
207
|
)
|
|
149
208
|
if member is None:
|
|
150
|
-
raise BuildError(
|
|
209
|
+
raise BuildError(
|
|
210
|
+
f"biber binary not found inside the biber {version} archive"
|
|
211
|
+
)
|
|
151
212
|
src = tf.extractfile(member)
|
|
152
213
|
if src is None:
|
|
153
|
-
raise BuildError(
|
|
214
|
+
raise BuildError(
|
|
215
|
+
f"could not read biber from the biber {version} archive"
|
|
216
|
+
)
|
|
154
217
|
cached.write_bytes(src.read())
|
|
155
218
|
except Exception:
|
|
156
219
|
if cached.exists():
|
|
@@ -9,11 +9,13 @@ __all__ = ["Fcite"]
|
|
|
9
9
|
|
|
10
10
|
@Registry.add
|
|
11
11
|
def Fcite(key: str) -> TeX:
|
|
12
|
-
"""Full clickable citation: `\\hyperlink{cite.KEY}{Author, Year}`.
|
|
12
|
+
"""Full clickable citation: `\\hyperlink{cite.0@KEY}{Author, Year}`.
|
|
13
13
|
|
|
14
14
|
Mirrors HSRTReport's `\\fcite` macro — author + year in one clickable link.
|
|
15
|
+
biblatex names the citation anchor ``cite.0@KEY`` (the ``0@`` is refsection
|
|
16
|
+
0); targeting ``cite.KEY`` would dangle.
|
|
15
17
|
"""
|
|
16
18
|
return Hyperlink(
|
|
17
|
-
f"cite.{key}",
|
|
19
|
+
f"cite.0@{key}",
|
|
18
20
|
Concat(Citeauthor(key), ", ", Citeyear(key)),
|
|
19
21
|
)
|
|
@@ -295,6 +295,11 @@ class HSRTReport(KomaDocument):
|
|
|
295
295
|
def rendered(self) -> str:
|
|
296
296
|
return Concat(
|
|
297
297
|
DocumentClass(self.document_class, self.document_class_options),
|
|
298
|
+
# hyperfootnotes is only honoured as a hyperref load option, so it
|
|
299
|
+
# must be queued before \usepackage{hyperref}. The HSRT footnote
|
|
300
|
+
# setup never places hyperref's Hfootnote destination, so leaving
|
|
301
|
+
# footnote-mark links enabled produces dangling links.
|
|
302
|
+
Raw(r"\PassOptionsToPackage{hyperfootnotes=false}{hyperref}"),
|
|
298
303
|
*self.ordered_packages(),
|
|
299
304
|
self.inline_image_block,
|
|
300
305
|
self.preamble,
|
{pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/hyperref_config.py
RENAMED
|
@@ -19,7 +19,11 @@ HSRT_HYPER_OPTIONS: Final[dict[str, HyperOption]] = {
|
|
|
19
19
|
"pdfpagemode": "UseOutlines",
|
|
20
20
|
"bookmarksopen": True,
|
|
21
21
|
"bookmarksopenlevel": 0,
|
|
22
|
-
|
|
22
|
+
# plainpages=false + hypertexnames=true gives roman frontmatter pages their
|
|
23
|
+
# own named anchors (page.i, ...) so glossary/index \hyperpage links to
|
|
24
|
+
# those pages resolve instead of dangling against absolute arabic anchors.
|
|
25
|
+
"plainpages": False,
|
|
26
|
+
"hypertexnames": True,
|
|
23
27
|
"colorlinks": True,
|
|
24
28
|
"citecolor": HSRT_CITE_COLOR,
|
|
25
29
|
"linkcolor": HSRT_LINK_COLOR,
|
{pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_hsrtreport/tex/pagesetup.tex
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
\usepackage{lastpage}
|
|
1
2
|
\makeatletter
|
|
2
3
|
\def\@title{}
|
|
3
4
|
\def\@author{}
|
|
@@ -12,14 +13,13 @@
|
|
|
12
13
|
\def\chaptermark#1{\def\Chaptername{#1}\Chaptermark{#1}}
|
|
13
14
|
\let\Sectionmark\sectionmark
|
|
14
15
|
\def\sectionmark#1{\def\Sectionname{#1}\Sectionmark{#1}}
|
|
15
|
-
\AtEndDocument{\immediate\write\@auxout{\string\gdef\string\@lastpage{\thepage}}}
|
|
16
16
|
\clearpairofpagestyles
|
|
17
17
|
\setkomafont{pageheadfoot}{\color{gray}\blenderfont}
|
|
18
18
|
\setkomafont{pagenumber}{\color{gray}\blenderfont}
|
|
19
19
|
\setlength{\footskip}{20pt}
|
|
20
20
|
\ohead*{\ifHSRTBackMatter\else\ifnum\value{chapter}>0\relax\Roman{\thechapter}~–~\Chaptername\fi\fi}
|
|
21
21
|
\ifoot{\@author}
|
|
22
|
-
\cfoot{\ifHSRTBackMatter\else Seite~\thepage\if@mainmatter
|
|
22
|
+
\cfoot{\ifHSRTBackMatter\else Seite~\thepage\if@mainmatter~von~\pageref{LastPage}\fi\fi}
|
|
23
23
|
\ohead{\ifHSRTBackMatter\else\ifnum\value{chapter}>0\relax\thechapter~–~\Chaptername\fi\fi}
|
|
24
24
|
\ihead{\@title}
|
|
25
25
|
\pagestyle{scrheadings}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from collections.abc import Iterator
|
|
1
2
|
from dataclasses import dataclass, field
|
|
2
3
|
from typing import Final, override
|
|
3
4
|
|
|
@@ -20,12 +21,22 @@ from pytex.commands.tables import Tabular
|
|
|
20
21
|
from pytex.helpers.parenting import attach
|
|
21
22
|
from pytex.interface.tex import TeX
|
|
22
23
|
from pytex.model.concat import Concat
|
|
24
|
+
from pytex.model.empty import Empty
|
|
23
25
|
from pytex.model.environment import Environment
|
|
24
26
|
from pytex.model.raw import Raw
|
|
25
27
|
from pytex.registry import Registry
|
|
26
28
|
|
|
27
29
|
from .logos import titlepage_logo_overlay
|
|
28
30
|
|
|
31
|
+
|
|
32
|
+
def _is_blank(node: TeX | str) -> bool:
|
|
33
|
+
"""True for an empty string or the `Empty` node — used to skip empty
|
|
34
|
+
title-page sections (a protocol has no abstract or keywords)."""
|
|
35
|
+
if isinstance(node, str):
|
|
36
|
+
return not node.strip()
|
|
37
|
+
return node is Empty
|
|
38
|
+
|
|
39
|
+
|
|
29
40
|
__all__ = ["TitlePage", "TitlePageDataLine"]
|
|
30
41
|
|
|
31
42
|
|
|
@@ -75,62 +86,63 @@ class TitlePage(TeX):
|
|
|
75
86
|
)
|
|
76
87
|
return tuple(v for v in candidates if isinstance(v, TeX))
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
def _title_block(self) -> TeX:
|
|
90
|
+
return Environment(
|
|
91
|
+
"flushleft",
|
|
92
|
+
Concat(
|
|
93
|
+
Raw(r"\hyphenpenalty=10000\exhyphenpenalty=10000"),
|
|
94
|
+
Noindent(),
|
|
95
|
+
SelectColor("black"),
|
|
96
|
+
Textbf(
|
|
97
|
+
Concat(
|
|
98
|
+
Blenderfont(),
|
|
99
|
+
HugeBig(),
|
|
100
|
+
Hspace("-2.5pt", star=True),
|
|
101
|
+
self.title,
|
|
102
|
+
)
|
|
103
|
+
),
|
|
104
|
+
Raw(r"\par"),
|
|
105
|
+
Vspace("-0.5em"),
|
|
106
|
+
Rule(r"\textwidth", "0.5mm"),
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def _content(self) -> Iterator[TeX | str]:
|
|
81
111
|
# Tikz overlay: logos chained left-to-right from the top-left corner,
|
|
82
112
|
# matching tmp/Pages/Titlepage.tex but with the foreach unrolled here.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
113
|
+
yield Raw(titlepage_logo_overlay(self.logo_names), allow_replacements=False)
|
|
114
|
+
yield Vspace("4cm")
|
|
115
|
+
yield self._title_block()
|
|
116
|
+
yield Vspace("2em")
|
|
117
|
+
yield Setstretch("1.0")
|
|
118
|
+
# Abstract and keywords are optional — skip the labels when empty
|
|
119
|
+
# (e.g. a meeting protocol has neither).
|
|
120
|
+
if not _is_blank(self.abstract):
|
|
121
|
+
yield SectionStar("Abstract")
|
|
122
|
+
yield Vspace("-1em")
|
|
123
|
+
yield self.abstract
|
|
124
|
+
yield Vspace("1em", star=True)
|
|
125
|
+
if not _is_blank(self.keywords):
|
|
126
|
+
yield Raw(r"\par\noindent ")
|
|
127
|
+
yield Textbf("Keywords")
|
|
128
|
+
yield Raw(r"\par\noindent ")
|
|
129
|
+
yield self.keywords
|
|
130
|
+
yield Vfill()
|
|
131
|
+
yield Noindent()
|
|
132
|
+
yield Setstretch("1.0")
|
|
133
|
+
yield Tabular(
|
|
134
|
+
r"@{} p{30mm} p{\dimexpr\textwidth-30mm-2\tabcolsep\relax} @{}",
|
|
135
|
+
_data_table_body(self.data_lines),
|
|
86
136
|
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
@override
|
|
140
|
+
def rendered(self) -> str:
|
|
141
|
+
# `\HSRTTitlePagetrue` while the titlepage ships out so footer_logo_hook
|
|
142
|
+
# suppresses the bottom-right footer logos on this page only; reset
|
|
143
|
+
# after \end{titlepage} (which \clearpages while the flag is still set).
|
|
91
144
|
return Concat(
|
|
92
145
|
Raw(r"\HSRTTitlePagetrue", allow_replacements=False),
|
|
93
|
-
TitlepageEnv(
|
|
94
|
-
Concat(
|
|
95
|
-
logo_overlay,
|
|
96
|
-
Vspace("4cm"),
|
|
97
|
-
Environment(
|
|
98
|
-
"flushleft",
|
|
99
|
-
Concat(
|
|
100
|
-
Raw(r"\hyphenpenalty=10000\exhyphenpenalty=10000"),
|
|
101
|
-
Noindent(),
|
|
102
|
-
SelectColor("black"),
|
|
103
|
-
Textbf(
|
|
104
|
-
Concat(
|
|
105
|
-
Blenderfont(),
|
|
106
|
-
HugeBig(),
|
|
107
|
-
Hspace("-2.5pt", star=True),
|
|
108
|
-
self.title,
|
|
109
|
-
)
|
|
110
|
-
),
|
|
111
|
-
Raw(r"\par"),
|
|
112
|
-
Vspace("-0.5em"),
|
|
113
|
-
Rule(r"\textwidth", "0.5mm"),
|
|
114
|
-
),
|
|
115
|
-
),
|
|
116
|
-
Vspace("2em"),
|
|
117
|
-
Setstretch("1.0"),
|
|
118
|
-
SectionStar("Abstract"),
|
|
119
|
-
Vspace("-1em"),
|
|
120
|
-
self.abstract,
|
|
121
|
-
Vspace("1em", star=True),
|
|
122
|
-
Raw(r"\par\noindent "),
|
|
123
|
-
Textbf("Keywords"),
|
|
124
|
-
Raw(r"\par\noindent "),
|
|
125
|
-
self.keywords,
|
|
126
|
-
Vfill(),
|
|
127
|
-
Noindent(),
|
|
128
|
-
Setstretch("1.0"),
|
|
129
|
-
Tabular(
|
|
130
|
-
r"@{} p{30mm} p{\dimexpr\textwidth-30mm-2\tabcolsep\relax} @{}",
|
|
131
|
-
_data_table_body(self.data_lines),
|
|
132
|
-
),
|
|
133
|
-
)
|
|
134
|
-
),
|
|
146
|
+
TitlepageEnv(Concat(*self._content())),
|
|
135
147
|
Raw(r"\HSRTTitlePagefalse", allow_replacements=False),
|
|
136
148
|
).rendered
|
|
@@ -10,7 +10,7 @@ this module depends on ``pytex_hsrtreport``.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import re
|
|
13
|
-
from typing import TYPE_CHECKING, Any, Final, cast
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Final, cast
|
|
14
14
|
|
|
15
15
|
from pytex.commands.builtin import (
|
|
16
16
|
Bold,
|
|
@@ -91,9 +91,12 @@ def _text(node: object) -> str | None:
|
|
|
91
91
|
return ch if isinstance(ch, str) else None
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
@final
|
|
95
94
|
class MarkdownConverter:
|
|
96
|
-
"""Walk a marko AST, producing a single ``TeX`` tree.
|
|
95
|
+
"""Walk a marko AST, producing a single ``TeX`` tree.
|
|
96
|
+
|
|
97
|
+
Subclass to add domain-specific blocks/inlines (see
|
|
98
|
+
``pytex_protocol.convert.ProtocolConverter``).
|
|
99
|
+
"""
|
|
97
100
|
|
|
98
101
|
base_level: int
|
|
99
102
|
callouts: bool
|
{pytex_preprocessor-0.1.0rc1 → pytex_preprocessor-0.1.2}/src/pytex_preprocessor.egg-info/SOURCES.txt
RENAMED
|
@@ -110,5 +110,13 @@ src/pytex_preprocessor.egg-info/dependency_links.txt
|
|
|
110
110
|
src/pytex_preprocessor.egg-info/entry_points.txt
|
|
111
111
|
src/pytex_preprocessor.egg-info/requires.txt
|
|
112
112
|
src/pytex_preprocessor.egg-info/top_level.txt
|
|
113
|
+
src/pytex_protocol/__init__.py
|
|
114
|
+
src/pytex_protocol/convert.py
|
|
115
|
+
src/pytex_protocol/document.py
|
|
116
|
+
src/pytex_protocol/entries.py
|
|
117
|
+
src/pytex_protocol/frontmatter.py
|
|
118
|
+
src/pytex_protocol/header.py
|
|
119
|
+
src/pytex_protocol/shortcodes.py
|
|
120
|
+
src/pytex_protocol/signatures.py
|
|
113
121
|
src/pytex_tikz/__init__.py
|
|
114
122
|
src/pytex_tikz/tikz.py
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""STUPA/AStA meeting-protocol rendering, built on top of ``pytex_hsrtreport``.
|
|
2
|
+
|
|
3
|
+
Write the minutes in Obsidian-flavoured Markdown - YAML frontmatter for the
|
|
4
|
+
meeting header, ``> [!beschluss]`` / ``> [!abstimmung]`` / ``> [!aufgabe]``
|
|
5
|
+
callouts and inline ``{{shortcodes}}`` for the protocol-specific bits - and
|
|
6
|
+
render it to a PDF that matches the HSRTReport look.
|
|
7
|
+
|
|
8
|
+
from pytex_protocol import IncludeProtocol
|
|
9
|
+
__pytex__ = IncludeProtocol("sitzung.md")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .convert import ProtocolConverter
|
|
13
|
+
from .document import IncludeProtocol, Protocol, render_protocol
|
|
14
|
+
from .entries import ActionItem, Deadline, Decision, Timestamp, Vote
|
|
15
|
+
from .frontmatter import split_frontmatter
|
|
16
|
+
from .header import ProtocolHeader, header_from_meta
|
|
17
|
+
from .shortcodes import expand_inline_shortcodes, expand_shortcode
|
|
18
|
+
from .signatures import SignatureLines, signature_block_from_meta
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"ActionItem",
|
|
22
|
+
"Deadline",
|
|
23
|
+
"Decision",
|
|
24
|
+
"IncludeProtocol",
|
|
25
|
+
"Protocol",
|
|
26
|
+
"ProtocolConverter",
|
|
27
|
+
"ProtocolHeader",
|
|
28
|
+
"SignatureLines",
|
|
29
|
+
"Timestamp",
|
|
30
|
+
"Vote",
|
|
31
|
+
"expand_inline_shortcodes",
|
|
32
|
+
"expand_shortcode",
|
|
33
|
+
"header_from_meta",
|
|
34
|
+
"render_protocol",
|
|
35
|
+
"signature_block_from_meta",
|
|
36
|
+
"split_frontmatter",
|
|
37
|
+
]
|