[FIX] рефакторинг: C1-C4 критичные + W1-W10 предупреждения
C1: LaTeX escaping спецсимволов (& % $ # _ { } ~ ^ \)
C2: text.capitalize() → first char uppercase only
C3: run_xelatex проверяет returncode
C4: парсинг лога при ошибке второго прохода
W1: Удалены мёртвые счётчики section/subsection/subsubsection
W2: text.upper() вычисляется один раз
W3: Единая сигнатура хендлеров (r, tok) для всех
W4: Джэгged строки таблицы паддятся до max колонок
W5: Пустой lang в code fence → без [language=]
W6: hr() вынесен из code.py в hr.py
W7: Валидация обязательных полей front matter
W8: Имя вуза конфигурируется через front matter
W9: -> None аннотация main()
W10: \n в конце END_DOCUMENT
+ Dockerfile: добавлен texlive-fonts-recommended
This commit is contained in:
@@ -6,6 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
texlive-xetex \
|
texlive-xetex \
|
||||||
texlive-lang-cyrillic \
|
texlive-lang-cyrillic \
|
||||||
texlive-latex-extra \
|
texlive-latex-extra \
|
||||||
|
texlive-fonts-recommended \
|
||||||
fonts-liberation \
|
fonts-liberation \
|
||||||
cabextract \
|
cabextract \
|
||||||
wget \
|
wget \
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from md2gost.handlers.code import fence, hr
|
from md2gost.handlers.code import fence
|
||||||
from md2gost.handlers.heading import heading
|
from md2gost.handlers.heading import heading
|
||||||
|
from md2gost.handlers.hr import hr
|
||||||
from md2gost.handlers.image import image
|
from md2gost.handlers.image import image
|
||||||
from md2gost.handlers.lists import bullet_list, ordered_list
|
from md2gost.handlers.lists import bullet_list, ordered_list
|
||||||
from md2gost.handlers.table import table
|
from md2gost.handlers.table import table
|
||||||
|
|||||||
@@ -10,8 +10,5 @@ if TYPE_CHECKING:
|
|||||||
def fence(r: Renderer, tok: Token) -> str:
|
def fence(r: Renderer, tok: Token) -> str:
|
||||||
lang = tok.info.strip()
|
lang = tok.info.strip()
|
||||||
code = tok.content.rstrip()
|
code = tok.content.rstrip()
|
||||||
return f"\\begin{{lstlisting}}[language={lang}]\n{code}\n\\end{{lstlisting}}"
|
opts = f"[language={lang}]" if lang else ""
|
||||||
|
return f"\\begin{{lstlisting}}{opts}\n{code}\n\\end{{lstlisting}}"
|
||||||
|
|
||||||
def hr(r: Renderer) -> str:
|
|
||||||
return r"\noindent\rule{\textwidth}{0.4pt}"
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING
|
|||||||
from md2gost.handlers.inline import render_inline
|
from md2gost.handlers.inline import render_inline
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from markdown_it.token import Token
|
||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
UNNUMBERED = {
|
UNNUMBERED = {
|
||||||
@@ -24,21 +25,22 @@ NEWPAGE_BEFORE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def heading(r: Renderer) -> str:
|
def heading(r: Renderer, tok: Token | None = None) -> str:
|
||||||
tok = r.tokens[r.pos]
|
tok = r.tokens[r.pos]
|
||||||
level = int(tok.tag[1])
|
level = int(tok.tag[1])
|
||||||
inline_tok = r.tokens[r.pos + 1]
|
inline_tok = r.tokens[r.pos + 1]
|
||||||
text = render_inline(inline_tok)
|
text = render_inline(inline_tok)
|
||||||
|
|
||||||
r.current_heading = text.upper()
|
upper = text.upper()
|
||||||
|
r.current_heading = upper
|
||||||
r.pos += 2
|
r.pos += 2
|
||||||
|
|
||||||
if text.upper() == TOC_HEADING:
|
if upper == TOC_HEADING:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
if text.upper() in UNNUMBERED:
|
if upper in UNNUMBERED:
|
||||||
prefix = ""
|
prefix = ""
|
||||||
if text.upper() in NEWPAGE_BEFORE:
|
if upper in NEWPAGE_BEFORE:
|
||||||
prefix = "\\clearpage\n"
|
prefix = "\\clearpage\n"
|
||||||
return (
|
return (
|
||||||
f"{prefix}"
|
f"{prefix}"
|
||||||
@@ -46,16 +48,10 @@ def heading(r: Renderer) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if level == 1:
|
if level == 1:
|
||||||
r.section += 1
|
|
||||||
r.subsection = 0
|
|
||||||
r.subsubsection = 0
|
|
||||||
return f"\\section{{{text}}}"
|
return f"\\section{{{text}}}"
|
||||||
elif level == 2:
|
elif level == 2:
|
||||||
r.subsection += 1
|
|
||||||
r.subsubsection = 0
|
|
||||||
return f"\\subsection{{{text}}}"
|
return f"\\subsection{{{text}}}"
|
||||||
elif level == 3:
|
elif level == 3:
|
||||||
r.subsubsection += 1
|
|
||||||
return f"\\subsubsection{{{text}}}"
|
return f"\\subsubsection{{{text}}}"
|
||||||
else:
|
else:
|
||||||
return f"\\textbf{{{text}}}"
|
return f"\\textbf{{{text}}}"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from markdown_it.token import Token
|
||||||
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
|
|
||||||
|
def hr(r: Renderer, tok: Token | None = None) -> str:
|
||||||
|
return r"\noindent\rule{\textwidth}{0.4pt}"
|
||||||
@@ -5,10 +5,27 @@ from typing import TYPE_CHECKING
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from markdown_it.token import Token
|
from markdown_it.token import Token
|
||||||
|
|
||||||
|
_LATEX_SPECIAL = str.maketrans({
|
||||||
|
"&": r"\&",
|
||||||
|
"%": r"\%",
|
||||||
|
"$": r"\$",
|
||||||
|
"#": r"\#",
|
||||||
|
"_": r"\_",
|
||||||
|
"{": r"\{",
|
||||||
|
"}": r"\}",
|
||||||
|
"~": r"\textasciitilde{}",
|
||||||
|
"^": r"\textasciicircum{}",
|
||||||
|
"\\": r"\textbackslash{}",
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def escape_latex(text: str) -> str:
|
||||||
|
return text.translate(_LATEX_SPECIAL)
|
||||||
|
|
||||||
|
|
||||||
def render_inline(tok: Token) -> str:
|
def render_inline(tok: Token) -> str:
|
||||||
if not tok.children:
|
if not tok.children:
|
||||||
return tok.content or ""
|
return escape_latex(tok.content or "")
|
||||||
return render_inline_tokens(tok.children)
|
return render_inline_tokens(tok.children)
|
||||||
|
|
||||||
|
|
||||||
@@ -16,7 +33,7 @@ def render_inline_tokens(tokens: list[Token]) -> str:
|
|||||||
parts: list[str] = []
|
parts: list[str] = []
|
||||||
for tok in tokens:
|
for tok in tokens:
|
||||||
if tok.type == "text":
|
if tok.type == "text":
|
||||||
parts.append(tok.content)
|
parts.append(escape_latex(tok.content))
|
||||||
elif tok.type == "strong_open":
|
elif tok.type == "strong_open":
|
||||||
parts.append(r"\textbf{")
|
parts.append(r"\textbf{")
|
||||||
elif tok.type == "strong_close":
|
elif tok.type == "strong_close":
|
||||||
@@ -29,11 +46,11 @@ def render_inline_tokens(tokens: list[Token]) -> str:
|
|||||||
parts.append(f"\\verb|{tok.content}|")
|
parts.append(f"\\verb|{tok.content}|")
|
||||||
elif tok.type == "link_open":
|
elif tok.type == "link_open":
|
||||||
href = tok.attrGet("href") or ""
|
href = tok.attrGet("href") or ""
|
||||||
parts.append(f"\\href{{{href}}}{{")
|
parts.append(f"\\href{{{escape_latex(href)}}}{{")
|
||||||
elif tok.type == "link_close":
|
elif tok.type == "link_close":
|
||||||
parts.append("}")
|
parts.append("}")
|
||||||
elif tok.type in ("softbreak", "hardbreak"):
|
elif tok.type in ("softbreak", "hardbreak"):
|
||||||
parts.append("~\\\\")
|
parts.append("~\\\\")
|
||||||
else:
|
else:
|
||||||
parts.append(tok.content if tok.content else "")
|
parts.append(escape_latex(tok.content) if tok.content else "")
|
||||||
return "".join(parts)
|
return "".join(parts)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
|
|||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
|
|
||||||
def bullet_list(r: Renderer) -> str:
|
def bullet_list(r: Renderer, tok: Token | None = None) -> str:
|
||||||
items = _collect_list_items(r)
|
items = _collect_list_items(r)
|
||||||
lines = []
|
lines = []
|
||||||
for item in items:
|
for item in items:
|
||||||
@@ -22,7 +22,7 @@ def bullet_list(r: Renderer) -> str:
|
|||||||
return f"\\begin{{itemize}}\n{body}\n\\end{{itemize}}"
|
return f"\\begin{{itemize}}\n{body}\n\\end{{itemize}}"
|
||||||
|
|
||||||
|
|
||||||
def ordered_list(r: Renderer) -> str:
|
def ordered_list(r: Renderer, tok: Token | None = None) -> str:
|
||||||
items = _collect_list_items(r)
|
items = _collect_list_items(r)
|
||||||
lines = []
|
lines = []
|
||||||
for item in items:
|
for item in items:
|
||||||
@@ -31,7 +31,8 @@ def ordered_list(r: Renderer) -> str:
|
|||||||
text = text.rstrip(".")
|
text = text.rstrip(".")
|
||||||
lines.append(f" \\item {text}.")
|
lines.append(f" \\item {text}.")
|
||||||
else:
|
else:
|
||||||
lines.append(f" \\item {text.capitalize()}.")
|
first = text[0].upper() if text else text
|
||||||
|
lines.append(f" \\item {first}{text[1:]}.")
|
||||||
body = "\n".join(lines)
|
body = "\n".join(lines)
|
||||||
return f"\\begin{{enumerate}}\n{body}\n\\end{{enumerate}}"
|
return f"\\begin{{enumerate}}\n{body}\n\\end{{enumerate}}"
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ if TYPE_CHECKING:
|
|||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
|
|
||||||
def table(r: Renderer) -> str:
|
def table(r: Renderer, tok: None = None) -> str:
|
||||||
rows: list[list[str]] = []
|
rows: list[list[str]] = []
|
||||||
r.pos += 1
|
r.pos += 1
|
||||||
while r.pos < len(r.tokens):
|
while r.pos < len(r.tokens):
|
||||||
@@ -40,7 +40,10 @@ def table(r: Renderer) -> str:
|
|||||||
if not rows:
|
if not rows:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
cols = len(rows[0])
|
cols = max(len(row) for row in rows)
|
||||||
|
for row in rows:
|
||||||
|
row.extend([""] * (cols - len(row)))
|
||||||
|
|
||||||
col_spec = "|".join(["l"] * cols)
|
col_spec = "|".join(["l"] * cols)
|
||||||
lines = [f"\\begin{{tabular}}{{|{col_spec}|}}", "\\hline"]
|
lines = [f"\\begin{{tabular}}{{|{col_spec}|}}", "\\hline"]
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ from typing import TYPE_CHECKING
|
|||||||
from md2gost.handlers.inline import render_inline
|
from md2gost.handlers.inline import render_inline
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from markdown_it.token import Token
|
||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
|
|
||||||
def paragraph(r: Renderer) -> str:
|
def paragraph(r: Renderer, tok: Token | None = None) -> str:
|
||||||
inline_tok = r.tokens[r.pos + 1]
|
inline_tok = r.tokens[r.pos + 1]
|
||||||
text = render_inline(inline_tok)
|
text = render_inline(inline_tok)
|
||||||
r.pos += 2
|
r.pos += 2
|
||||||
|
|||||||
@@ -93,4 +93,4 @@ PREAMBLE = r"""\documentclass[a4paper, 14pt]{extarticle}
|
|||||||
|
|
||||||
\begin{document}"""
|
\begin{document}"""
|
||||||
|
|
||||||
END_DOCUMENT = r"\end{document}"
|
END_DOCUMENT = r"\end{document}" + "\n"
|
||||||
|
|||||||
+22
-15
@@ -1,31 +1,38 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from md2gost.handlers.inline import escape_latex
|
||||||
|
|
||||||
|
|
||||||
def render_titlepage(fm: dict[str, str]) -> str:
|
def render_titlepage(fm: dict[str, str]) -> str:
|
||||||
faculty = fm.get("факультет", "")
|
required = {"название": "заголовок работы", "тип": "тип работы"}
|
||||||
department = fm.get("кафедра", "")
|
for key, desc in required.items():
|
||||||
report_type = fm.get("тип", "")
|
if key not in fm:
|
||||||
title = fm.get("название", "")
|
raise ValueError(f"front matter: отсутствует поле '{key}' ({desc})")
|
||||||
student = fm.get("студент", "")
|
|
||||||
advisor = fm.get("преподаватель", "")
|
faculty = escape_latex(fm.get("факультет", ""))
|
||||||
group = fm.get("группа", "")
|
department = escape_latex(fm.get("кафедра", ""))
|
||||||
year = fm.get("год", "2026")
|
report_type = escape_latex(fm.get("тип", ""))
|
||||||
|
title = escape_latex(fm.get("название", ""))
|
||||||
|
student = escape_latex(fm.get("студент", ""))
|
||||||
|
advisor = escape_latex(fm.get("преподаватель", ""))
|
||||||
|
group = escape_latex(fm.get("группа", ""))
|
||||||
|
year = escape_latex(fm.get("год", "2026"))
|
||||||
|
|
||||||
ministry_lines = (
|
ministry_lines = (
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\hspace*{-6pt}\\centerline{Министерство науки и высшего образования Российской Федерации}\n"
|
f"\\hspace*{{-6pt}}\\centerline{{{fm.get('министерство', 'Министерство науки и высшего образования Российской Федерации')}}}\n"
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\centerline{Федеральное государственное бюджетное образовательное учреждение }\n"
|
f"\\centerline{{{fm.get('вуз_тип', 'Федеральное государственное бюджетное образовательное учреждение ')}}}\n"
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\centerline{высшего образования}\n"
|
f"\\centerline{{{fm.get('вуз_тип2', 'высшего образования')}}}\n"
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\centerline{<<Московский государственный технический университет}\n"
|
f"\\centerline{{<<{fm.get('вуз', 'Московский государственный технический университет')}}}\n"
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\centerline{имени Н.Э. Баумана>>}\n"
|
f"\\centerline{{имени Н.Э. Баумана>>}}\n"
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\centerline{(национальный исследовательский университет)}\n"
|
f"\\centerline{{({fm.get('вуз_статус', 'национальный исследовательский университет')}}}\n"
|
||||||
"\\vspace*{-0.7ex}\n"
|
"\\vspace*{-0.7ex}\n"
|
||||||
"\\centerline{(МГТУ им. Н.Э. Баумана)}"
|
f"\\centerline{{({fm.get('вуз_аббревиатура', 'МГТУ им. Н.Э. Баумана')}}}"
|
||||||
)
|
)
|
||||||
|
|
||||||
titlepage = (
|
titlepage = (
|
||||||
|
|||||||
+11
-12
@@ -9,6 +9,13 @@ from md2gost.parser import Parser
|
|||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
|
|
||||||
|
def _print_latex_errors(log_path: Path) -> None:
|
||||||
|
if log_path.exists():
|
||||||
|
for line in log_path.read_text(errors="replace").splitlines():
|
||||||
|
if line.startswith("!"):
|
||||||
|
print(f" {line}", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def run_xelatex(tex_path: Path, output_dir: Path) -> bool:
|
def run_xelatex(tex_path: Path, output_dir: Path) -> bool:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["xelatex", "-interaction=nonstopmode",
|
["xelatex", "-interaction=nonstopmode",
|
||||||
@@ -16,10 +23,10 @@ def run_xelatex(tex_path: Path, output_dir: Path) -> bool:
|
|||||||
capture_output=True, text=True,
|
capture_output=True, text=True,
|
||||||
)
|
)
|
||||||
pdf_path = output_dir / tex_path.with_suffix(".pdf").name
|
pdf_path = output_dir / tex_path.with_suffix(".pdf").name
|
||||||
return pdf_path.exists()
|
return pdf_path.exists() and result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
cli = argparse.ArgumentParser(
|
cli = argparse.ArgumentParser(
|
||||||
prog="md2gost",
|
prog="md2gost",
|
||||||
description="Markdown → LaTeX конвертер по ГОСТ Р 7.0.5-2008",
|
description="Markdown → LaTeX конвертер по ГОСТ Р 7.0.5-2008",
|
||||||
@@ -46,36 +53,28 @@ def main():
|
|||||||
renderer = Renderer(parser.tokens, parser.front_matter)
|
renderer = Renderer(parser.tokens, parser.front_matter)
|
||||||
latex = renderer.render()
|
latex = renderer.render()
|
||||||
|
|
||||||
# Собираем во временную директорию, копируем только .tex и .pdf
|
|
||||||
with tempfile.TemporaryDirectory(prefix="md2gost-") as cache:
|
with tempfile.TemporaryDirectory(prefix="md2gost-") as cache:
|
||||||
cache_dir = Path(cache)
|
cache_dir = Path(cache)
|
||||||
|
|
||||||
# Генерируем .tex в кэш
|
|
||||||
tex_name = args.input.with_suffix(".tex").name
|
tex_name = args.input.with_suffix(".tex").name
|
||||||
tex_path = cache_dir / tex_name
|
tex_path = cache_dir / tex_name
|
||||||
tex_path.write_text(latex, encoding="utf-8")
|
tex_path.write_text(latex, encoding="utf-8")
|
||||||
|
|
||||||
# Копируем .tex в выходную директорию
|
|
||||||
final_tex = args.output / tex_name
|
final_tex = args.output / tex_name
|
||||||
shutil.copy2(tex_path, final_tex)
|
shutil.copy2(tex_path, final_tex)
|
||||||
print(f"→ {final_tex}")
|
print(f"→ {final_tex}")
|
||||||
|
|
||||||
if not args.no_pdf:
|
if not args.no_pdf:
|
||||||
# Компилируем PDF в кэше (два прохода для TOC)
|
|
||||||
if not run_xelatex(tex_path, cache_dir):
|
if not run_xelatex(tex_path, cache_dir):
|
||||||
print("ошибка: xelatex не собрал PDF", file=sys.stderr)
|
print("ошибка: xelatex не собрал PDF", file=sys.stderr)
|
||||||
log_path = cache_dir / "report.log"
|
_print_latex_errors(cache_dir / "report.log")
|
||||||
if log_path.exists():
|
|
||||||
for line in log_path.read_text(errors="replace").splitlines():
|
|
||||||
if line.startswith("!"):
|
|
||||||
print(f" {line}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not run_xelatex(tex_path, cache_dir):
|
if not run_xelatex(tex_path, cache_dir):
|
||||||
print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr)
|
print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr)
|
||||||
|
_print_latex_errors(cache_dir / "report.log")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Копируем .pdf в выходную директорию
|
|
||||||
pdf_name = args.input.with_suffix(".pdf").name
|
pdf_name = args.input.with_suffix(".pdf").name
|
||||||
pdf_path = cache_dir / pdf_name
|
pdf_path = cache_dir / pdf_name
|
||||||
final_pdf = args.output / pdf_name
|
final_pdf = args.output / pdf_name
|
||||||
|
|||||||
+6
-20
@@ -16,18 +16,6 @@ from md2gost.latex import END_DOCUMENT, PREAMBLE, render_titlepage
|
|||||||
from markdown_it.token import Token
|
from markdown_it.token import Token
|
||||||
|
|
||||||
|
|
||||||
def _fence(r, tok):
|
|
||||||
return fence(r, tok)
|
|
||||||
|
|
||||||
|
|
||||||
def _image(r, tok):
|
|
||||||
return image(r, tok)
|
|
||||||
|
|
||||||
|
|
||||||
def _inline(r, tok):
|
|
||||||
return render_inline(tok)
|
|
||||||
|
|
||||||
|
|
||||||
_BLOCK = {
|
_BLOCK = {
|
||||||
"heading_open": heading,
|
"heading_open": heading,
|
||||||
"paragraph_open": paragraph,
|
"paragraph_open": paragraph,
|
||||||
@@ -38,9 +26,9 @@ _BLOCK = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_INLINE = {
|
_INLINE = {
|
||||||
"fence": _fence,
|
"fence": fence,
|
||||||
"image": _image,
|
"image": image,
|
||||||
"inline": _inline,
|
"inline": render_inline,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,9 +37,6 @@ class Renderer:
|
|||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
self.fm = front_matter
|
self.fm = front_matter
|
||||||
self.pos = 0
|
self.pos = 0
|
||||||
self.section = 0
|
|
||||||
self.subsection = 0
|
|
||||||
self.subsubsection = 0
|
|
||||||
self.current_heading = ""
|
self.current_heading = ""
|
||||||
|
|
||||||
def render(self) -> str:
|
def render(self) -> str:
|
||||||
@@ -63,12 +48,13 @@ class Renderer:
|
|||||||
tt = tok.type
|
tt = tok.type
|
||||||
|
|
||||||
if tt in _BLOCK:
|
if tt in _BLOCK:
|
||||||
result = _BLOCK[tt](self)
|
result = _BLOCK[tt](self, tok)
|
||||||
parts.append(result)
|
parts.append(result)
|
||||||
if self.current_heading == "СОДЕРЖАНИЕ":
|
if self.current_heading == "СОДЕРЖАНИЕ":
|
||||||
parts.append("\\newpage\n\\tableofcontents\n\\newpage")
|
parts.append("\\newpage\n\\tableofcontents\n\\newpage")
|
||||||
elif tt in _INLINE:
|
elif tt in _INLINE:
|
||||||
parts.append(_INLINE[tt](self, tok))
|
result = _INLINE[tt](self, tok)
|
||||||
|
parts.append(result)
|
||||||
else:
|
else:
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user