[ADD] поддержка изображений и рефакторинг диспетчера токенов
- registry.py: реестры BLOCK/PATTERNS/INLINE с декораторами @block/@pattern/@inline - render.py: единый диспетчер, слой паттернов, проверка контракта pos (RuntimeError), pending_toc вместо current_heading для единственной вставки СОДЕРЖАНИЯ - handlers/image.py: вики-ссылки ![[путь|ширина]], подпись Рисунок N —— ... , figure-окружение, авто-нумерация, копирование файлов в tmpdir - handlers/inline.py: табличная диспетчеризация вместо if/elif - Все block-обработчики подписаны @block, корректно продвигают pos - parser.py: препроцессинг ![[...]] → , iter_image_sources() - main.py: _copy_assets (emblem+картинки в tmpdir), фикс лога (имя .tex), cwd=tmpdir для xelatex, цикл вместо дублирования, force-include emblem - tests/test_render.py: pytest (10 тестов), pyproject dev-dep, make test - preamble.py: name=Рисунок для figure-caption (было Рис. от babel) - Исправлены баги: мёртвый обработчик, emblem не копировалась, множественный TOC, захардкожен report.log, маскировка pos - README/AGENTS.md обновлены
This commit is contained in:
+47
-13
@@ -5,7 +5,8 @@ import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from md2gost.parser import Parser
|
||||
import md2gost
|
||||
from md2gost.parser import Parser, iter_image_sources
|
||||
from md2gost.render import Renderer
|
||||
|
||||
|
||||
@@ -25,6 +26,9 @@ def run_xelatex(tex_path: Path, output_dir: Path) -> bool:
|
||||
str(output_dir),
|
||||
str(tex_path),
|
||||
],
|
||||
# Относительные пути картинок резолвятся от CWD, поэтому компилируем
|
||||
# из каталога, куда _copy_assets сложил все изображения.
|
||||
cwd=str(output_dir),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
@@ -32,6 +36,37 @@ def run_xelatex(tex_path: Path, output_dir: Path) -> bool:
|
||||
return pdf_path.exists() and result.returncode == 0
|
||||
|
||||
|
||||
def _find_emblem(input_dir: Path) -> Path | None:
|
||||
candidates = [
|
||||
input_dir / "emblem.png",
|
||||
Path(md2gost.__file__).parent / "emblem.png",
|
||||
]
|
||||
return next((p for p in candidates if p.is_file()), None)
|
||||
|
||||
|
||||
def _copy_assets(parser: Parser, input_dir: Path, cache_dir: Path) -> None:
|
||||
"""Копирует эмблему и все картинки документа в каталог компиляции."""
|
||||
emblem = _find_emblem(input_dir)
|
||||
if emblem is None:
|
||||
print(
|
||||
"предупреждение: emblem.png не найден — титульный лист без эмблемы",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
shutil.copy2(emblem, cache_dir / "emblem.png")
|
||||
|
||||
for src in iter_image_sources(parser.tokens):
|
||||
src_path = input_dir / src
|
||||
if not src_path.is_file():
|
||||
print(
|
||||
f"предупреждение: изображение не найдено: {src_path}", file=sys.stderr
|
||||
)
|
||||
continue
|
||||
dest = cache_dir / src
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src_path, dest)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
cli = argparse.ArgumentParser(
|
||||
prog="md2gost",
|
||||
@@ -70,25 +105,24 @@ def main() -> None:
|
||||
tex_path = cache_dir / tex_name
|
||||
tex_path.write_text(latex, encoding="utf-8")
|
||||
|
||||
_copy_assets(parser, args.input.parent, cache_dir)
|
||||
|
||||
final_tex = args.output / tex_name
|
||||
shutil.copy2(tex_path, final_tex)
|
||||
print(f"-> {final_tex}")
|
||||
|
||||
if not args.no_pdf:
|
||||
if not run_xelatex(tex_path, cache_dir):
|
||||
print("ошибка: xelatex не собрал PDF", file=sys.stderr)
|
||||
_print_latex_errors(cache_dir / "report.log")
|
||||
sys.exit(1)
|
||||
|
||||
if not run_xelatex(tex_path, cache_dir):
|
||||
print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr)
|
||||
_print_latex_errors(cache_dir / "report.log")
|
||||
sys.exit(1)
|
||||
|
||||
log_path = cache_dir / tex_path.with_suffix(".log").name
|
||||
pdf_name = args.input.with_suffix(".pdf").name
|
||||
pdf_path = cache_dir / pdf_name
|
||||
for attempt in (1, 2):
|
||||
if not run_xelatex(tex_path, cache_dir):
|
||||
stage = "" if attempt == 1 else " (2-й проход)"
|
||||
print(f"ошибка: xelatex{stage} не собрал PDF", file=sys.stderr)
|
||||
_print_latex_errors(log_path)
|
||||
sys.exit(1)
|
||||
|
||||
final_pdf = args.output / pdf_name
|
||||
shutil.copy2(pdf_path, final_pdf)
|
||||
shutil.copy2(cache_dir / pdf_name, final_pdf)
|
||||
print(f"-> {final_pdf}")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user