From 303dcfaa1f5dd869a0809125a00268c1dc5643c7 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Mon, 6 Feb 2023 01:00:45 +0100 Subject: [PATCH] Main file cleanup, image processing. --- .gitignore | 9 ++ formatitko.py | 32 +++-- html.py | 50 ++++--- images.py | 65 ++++++++++ katex.py | 2 +- test.md | 2 +- test/Makefile | 13 ++ test/logo.jpg | Bin 0 -> 17149 bytes test-import.md => test/test-import.md | 0 test-partial.md => test/test-partial.md | 0 test/test-top.html | 8 ++ test/test-top.tex | 1 + test/test.md | 166 ++++++++++++++++++++++++ tex.py | 71 ++++++---- 14 files changed, 362 insertions(+), 57 deletions(-) create mode 100644 images.py create mode 100644 test/Makefile create mode 100644 test/logo.jpg rename test-import.md => test/test-import.md (100%) rename test-partial.md => test/test-partial.md (100%) create mode 100644 test/test-top.html create mode 120000 test/test-top.tex create mode 100644 test/test.md diff --git a/.gitignore b/.gitignore index 96223e3..8aa3d05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,11 @@ **/__pycache__ output.* +*.log +*.aux +test/test.pdf +test/test.tex +public/ +*.png +*.pdf +*.jpeg +*.svg diff --git a/formatitko.py b/formatitko.py index 72dbb7c..f2e54df 100755 --- a/formatitko.py +++ b/formatitko.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 - +import argparse import re import sys from typing import List +import os # Import local files from transform import transform @@ -13,21 +14,30 @@ from group import Group from katex import KatexClient from html import html from tex import tex +from images import ImageProcessor from mj_show import show -doc = import_md(open(sys.argv[1], "r").read()) +parser = argparse.ArgumentParser() +parser.add_argument("-l", "--img-lookup-dirs", help="Image lookup directories. When processing images, the program will try to find the image in them first. By default contains the directory of the MarkDown file.", nargs="+", default=[]) +parser.add_argument("-p", "--img-public-dir", help="Directory to put processed images into. The program will not overwrite existing images.", nargs=1, default="public") +parser.add_argument("-w", "--output-html", help="The HTML file (for Web) to write into.", nargs=1, default="output.html") +parser.add_argument("-t", "--output-tex", help="The TEX file to write into.", nargs=1, default="output.tex") +parser.add_argument("input_filename", help="The MarkDown file to process.") +args = parser.parse_args() + +doc = import_md(open(args.input_filename, "r").read()) + language = doc.get_metadata("language", None, True) -print(show(doc)) context = Context(doc, sys.argv[1]) + doc = doc.walk(transform, context) + doc.content = [Group(*doc.content, metadata={"language":language})] -#print("---------------------") -#print(show(doc)) -#print(convert_text(doc, input_format="panflute", output_format="markdown")) + katexClient = KatexClient() -#print(katexClient.render("\\def\\Bruh{K^A\\TeX}")) -#print(katexClient.render("\\Bruh")) -open("output.html", "w").write(" " + html(doc, katexClient)) -open("output.tex", "w").write("\input formatitko.tex\n" + tex(doc)) -#print(tex(doc)) +doc_dir = os.path.dirname(args.input_filename) if os.path.dirname(args.input_filename) != "" else "." +imageProcessor = ImageProcessor(args.img_public_dir, doc_dir, *args.img_lookup_dirs) + +open(args.output_html, "w").write(html(doc, katexClient, imageProcessor)) +open(args.output_tex, "w").write(tex(doc, imageProcessor)) diff --git a/html.py b/html.py index 655250c..e9eb063 100644 --- a/html.py +++ b/html.py @@ -3,20 +3,22 @@ from pygments import highlight from pygments.lexers import get_lexer_by_name from pygments.formatters import HtmlFormatter from pygments.util import ClassNotFound +import os from whitespace import NBSP from transform import FQuoted from katex import KatexClient from util import inlinify from group import Group +from images import ImageProcessor -def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") -> str: +def html(e: Element, k: KatexClient, i: ImageProcessor, indent_level: int=0, indent_str: str="\t") -> str: if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "html": return "" if isinstance(e, ListContainer): - return ''.join([html(child, k, indent_level, indent_str) for child in e]) + return ''.join([html(child, k, i, indent_level, indent_str) for child in e]) tag = e.tag.lower() attributes = "" @@ -95,14 +97,24 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") return f'
{e.text}
' if isinstance(e, Figure): - content_foot = html(e.caption, k, indent_level+1, indent_str) + content_foot = html(e.caption, k, i, indent_level+1, indent_str) if isinstance(e, Caption): tag = "figcaption" if isinstance(e, Image): - # TODO: Image processing - return f'{e.title or html(e.content, k, 0, ' + url = e.url + _, ext = os.path.splitext(url) + ext = ext[1:] + if ext in ["svg", "png", "jpeg", "gif"]: + url = i.process_image(url, ext) + elif ext in ["pdf", "epdf"]: + url = i.process_image(url, "png", dpi=300) + elif ext in ["jpg"]: + url = i.process_image(url, "jpeg") + else: + url = i.process_image(url, ".png") + return f'{e.title or html(e.content, k, i, 0, ' if isinstance(e, Header): tag = "h"+str(e.level) @@ -114,13 +126,13 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") attributes += f' title="{e.title}"' if isinstance(e, LineItem): - return indent_level*indent_str + html(e.content, k) + "
\n" + return indent_level*indent_str + html(e.content, k, i) + "
\n" if isinstance(e, Note): content_head = "(" content_foot = ")" if inlinify(e) is not None: - return f' ({html(inlinify(e), k, 0, "")})' + return f' ({html(inlinify(e), k, i, 0, "")})' if isinstance(e, OrderedList): tag = "ol" @@ -138,8 +150,8 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") # FIXME: Delimeter styles if isinstance(e, Table): - content_head = html(e.head, k, indent_level+1, indent_str) - content_foot = html(e.foot, k, indent_level+1, indent_str) + content_head = html(e.head, k, i, indent_level+1, indent_str) + content_foot = html(e.foot, k, i, indent_level+1, indent_str) # FIXME: Fancy pandoc tables, using colspec if isinstance(e, TableCell): @@ -159,25 +171,25 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") if isinstance(e, FQuoted): if e.style == "cs": if e.quote_type == "SingleQuote": - return f'‚{html(e.content, k, 0, "")}‘' + return f'‚{html(e.content, k, i, 0, "")}‘' elif e.quote_type == "DoubleQuote": - return f'„{html(e.content, k, 0, "")}“' + return f'„{html(e.content, k, i, 0, "")}“' elif e.style == "en": if e.quote_type == "SingleQuote": - return f'‘{html(e.content, k, 0, "")}’' + return f'‘{html(e.content, k, i, 0, "")}’' elif e.quote_type == "DoubleQuote": - return f'“{html(e.content, k, 0, "")}”' + return f'“{html(e.content, k, i, 0, "")}”' else: if e.quote_type == "SingleQuote": - return f'\'{html(e.content, k, 0, "")}\'' + return f'\'{html(e.content, k, i, 0, "")}\'' elif e.quote_type == "DoubleQuote": - return f'"{html(e.content, k, 0, "")}"' + return f'"{html(e.content, k, i, 0, "")}"' else: - return f'"{html(e.content, k, 0, "")}"' + return f'"{html(e.content, k, i, 0, "")}"' if isinstance(e, Group): k.begingroup() - ret = html(e.content, k, indent_level, indent_str) + ret = html(e.content, k, i, indent_level, indent_str) k.endgroup() return ret @@ -204,7 +216,7 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") return "" if isinstance(e, Inline): - return f'<{tag}{attributes}>{content_head}{html(e.content, k, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}' + return f'<{tag}{attributes}>{content_head}{html(e.content, k, i, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}' out_str = "" if not isinstance(e, Plain): @@ -213,7 +225,7 @@ def html(e: Element, k: KatexClient, indent_level: int=0, indent_str: str="\t") if hasattr(e, "_content"): if len(e.content) > 0 and isinstance(e.content[0], Inline): out_str += (indent_level+1)*indent_str - out_str += html(e.content, k, indent_level+1, indent_str) + out_str += html(e.content, k, i, indent_level+1, indent_str) if hasattr(e, "text"): out_str += e.text out_str += f"{content_foot}\n" diff --git a/images.py b/images.py new file mode 100644 index 0000000..0444aba --- /dev/null +++ b/images.py @@ -0,0 +1,65 @@ +from typing import List +import os +import shutil +import subprocess + +class ImageProcessor: + def __init__(self, public_dir: str, *lookup_dirs: List[str]): + self.public_dir = public_dir + self.lookup_dirs = lookup_dirs + if not os.path.exists(self.public_dir): + os.mkdir(self.public_dir) + + def process_image(self, input_filename: str, format: str, relative=True, width: int=None, height:int=None, quality: int=None, dpi: int=None) -> str: + name = os.path.basename(input_filename) + base, ext = os.path.splitext(name) + ext = ext[1:] + full_path = self.find_image(input_filename) + if full_path is None: + raise FileNotFoundError(f'Image {input_filename} not found.') + + suffix = "" + geometry = None + if width is not None or height is not None: + geometry = f'{width if width is not None else ""}x{height if height is not None else ""}' + suffix += "_"+geometry + if quality is not None: + suffix += f'_q{quality}' + if quality is not None: + suffix += f'_d{dpi}' + target_name = base+suffix+"."+format + target_path = self.public_dir + "/" + target_name + + if not os.path.isfile(target_path): + if (((ext == format and width) + or (ext == "epdf" and format == "pdf") + or (ext == "jpg" and format == "jpeg")) + and width is None and height is None and quality is None and dpi is None): + shutil.copyfile(full_path, target_path) + + elif self.find_image(target_name): + shutil.copyfile(self.find_image(target_name), target_path) + + elif ext == "svg": + width_arg = ['--export-width', str(width)] if width is not None else [] + height_arg = ['--export-height', str(height)] if height is not None else [] + dpi_arg = ['--export-dpi', str(dpi)] if dpi is not None else [] + if subprocess.run(['inkscape', full_path, '-o', target_path, *width_arg, *height_arg, *dpi_arg]).returncode != 0: + raise Exception(f"Could not convert '{full_path}' to '{format}'") + + else: + resize_arg = ['-resize', str(geometry)] if geometry is not None else [] + density_arg = ['-density', str(dpi)] if dpi is not None else [] + quality_arg = ['-quality', str(quality)] if quality is not None else [] + if subprocess.run(['convert', full_path, *resize_arg, *density_arg, *quality_arg, target_path]).returncode != 0: + raise Exception(f"Could not convert '{full_path}' to '{format}'") + + return target_name if relative else target_path + + + + + def find_image(self, input_filename) -> str: + for dir in self.lookup_dirs: + if os.path.isfile(dir + "/" + input_filename): + return dir + "/" + input_filename diff --git a/katex.py b/katex.py index cac875a..7879e7e 100644 --- a/katex.py +++ b/katex.py @@ -14,7 +14,7 @@ class KatexClient: self._client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self._temp_dir = tempfile.TemporaryDirectory(prefix='formatitko') self._socket_file = self._temp_dir.name + "/katex-socket" - self._server_process = subprocess.Popen(["node", "./katex-server/index.mjs", self._socket_file]) + self._server_process = subprocess.Popen(["node", os.path.dirname(os.path.realpath(__file__)) + "/katex-server/index.mjs", self._socket_file]) while not os.path.exists(self._socket_file): pass while True: diff --git a/test.md b/test.md index b4eb439..692aaa1 100644 --- a/test.md +++ b/test.md @@ -47,7 +47,7 @@ This should only be shown to cats the second time # [$are_we_there_yet]{} -![This is a figure, go figure...](/tmp/logo.pdf){width=10em} +![This is a figure, go figure...](/tmp/logo.pdf) ![This is a figure, go figure...](/tmp/logo.jpg){width=10em} diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..455c41e --- /dev/null +++ b/test/Makefile @@ -0,0 +1,13 @@ +all: test.pdf public/test.html + +output.tex output.html: + ../formatitko.py test.md + +public/test.html: output.html + cat test-top.html output.html > public/test.html + +test.tex: output.tex + cat test-top.tex output.tex > test.tex + +test.pdf: test.tex + TEXINPUTS=.:../ucwmac:${TEXINPUTS} luatex -halt-on-error -interaction nonstopmode test.tex diff --git a/test/logo.jpg b/test/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..544393e74434c623cf8cdf1fa70a947c3477fc55 GIT binary patch literal 17149 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 + + + + + + + diff --git a/test/test-top.tex b/test/test-top.tex new file mode 120000 index 0000000..1bae400 --- /dev/null +++ b/test/test-top.tex @@ -0,0 +1 @@ +../formatitko.tex \ No newline at end of file diff --git a/test/test.md b/test/test.md new file mode 100644 index 0000000..9fa68a7 --- /dev/null +++ b/test/test.md @@ -0,0 +1,166 @@ +--- +title: 'Wooooo a title' +subtitle: 'A subtitle' +are_we_there_yet: False +language: "en" +--- +[#test-import.md]{} + +# Hello world! + +This is an *example* **yay**! + +This is *very **strongly** emphasised* + +Příliš žluťoučký kůň pěl dábelské ódy. *Příliš žluťoučký kůň pěl dábelské ódy.* **Příliš žluťoučký kůň pěl dábelské ódy.** ***Příliš žluťoučký kůň pěl dábelské ódy.*** + + +:::{partial=test-partial.md} +::: + +:::{if=cat} +This should only be shown to cats +::: + + +```python {.run} +ctx.set_flag("cat", True) +``` + +```python {.run} +println(f"The main document's title is '{ctx.get_metadata('title')}'") +ctx.set_metadata("a", {}) +ctx.set_metadata("a.b", {}) +ctx.set_metadata("a.b.c", "Bruh **bruh** bruh") +``` + +```python {style=native} +def bruh(no): + wat +``` + +Inline `code` + +::::{if=cat} +This should only be shown to cats the second time +:::: + +# [$are_we_there_yet]{} + +![This is a figure, go figure...](logo.svg){width=50%} + +![This is a figure, go figure...](logo.pdf){width=50%} + +![This is a figure, go figure...](logo.jpg){width=50%} + +![This is a figure, go figure...](logo.png){width=10em} + +![Fakt epesní reproduktor](reproduktor.jpeg){width=10em} + +![Fakt epesní reproduktor](reproduktor.png){width=10em} + +```python {.run} +ctx.set_metadata("language", "cs") +``` +[!opendatatask]{} +```python {.run} +ctx.set_metadata("language","en") +``` +[This too!]{if=cat} + +[What]{.co} + +[An inline command with contents and **bold** and another [!nop]{} inside!]{c=nop} + +[!nop]{a=b} + +> OOO a blockquote mate init +> +>> Nesting?? +>> Woah + +A non-breakable space bro + +A lot of spaces + +A text with some inline math: $\sum_{i=1}^nn^2$. Plus some display math: + +A link with the link in the link: + +H~2~O is a liquid. 2^10^ is 1024. + +[Underline]{.underline} + +:::{only=html} +$$ +\def\eqalign#1{\begin{align*}#1\end{align*}} +$$ +::: + +$$ +\eqalign{ + 2 x_2 + 6 x_3 &= 14 \cr + x_1 - 3 x_2 + 2 x_3 &= 5 \cr + -x_1 + 4 x_2 + \phantom{1} x_3 &= 2 +} +$$ + +:::{partial=test-partial.md} +::: + +--- + +This should be seen by all.^[This is a footnote] + +| Matematicko-fyzikální fakulta University Karlovy +| Malostranské nám. 2/25 +| 118 00 Praha 1 + +More footnotes.^[I am a foot] + +To Do: + +- buy eggs +- buy milk +- ??? +- profit + - also create sublists preferrably + +1. Woah +2. Wooo +3. no + +4) WOO + +``` {=html} +
+ +
This is indeed a video
+
+``` + +#. brum +#. BRUHHH +#. woah + +i. bro +ii. wym bro + + ++---------------------+-----------------------+ +| Location | Temperature 1961-1990 | +| | in degree Celsius | ++---------------------+-------+-------+-------+ +| | min | mean | max | ++=====================+=======+=======+======:+ +| Antarctica | -89.2 | N/A | 19.8 | ++---------------------+-------+-------+-------+ +| Earth | -89.2 | 14 | 56.7 | ++---------------------+-------+-------+-------+ + +------- ------ ---------- ------- + 12 12 12 12 + 123 123 123 123 + 1 1 1 1 +------- ------ ---------- ------- + diff --git a/tex.py b/tex.py index e884e24..ff7b950 100644 --- a/tex.py +++ b/tex.py @@ -1,18 +1,20 @@ from panflute import * +import os from whitespace import NBSP from transform import FQuoted from util import inlinify from group import Group +from images import ImageProcessor # Heavily inspired by: git://git.ucw.cz/labsconf2022.git -def tex(e, indent_level: int=0, indent_str: str="\t") -> str: +def tex(e: Element, i: ImageProcessor, indent_level: int=0, indent_str: str="\t") -> str: if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "tex": return "" if isinstance(e, ListContainer): - return ''.join([tex(child, indent_level, indent_str) for child in e]) + return ''.join([tex(child, i, indent_level, indent_str) for child in e]) content_foot = "" content_head = "" @@ -55,26 +57,26 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str: return e.text.replace(" ", "~").replace(" ", "~") if isinstance(e, Para): - return tex(e.content, 0, "")+"\n\n" + return tex(e.content, i, 0, "")+"\n\n" if isinstance(e, FQuoted): if e.style == "cs": if e.quote_type == "SingleQuote": - return f'‚{tex(e.content, 0, "")}‘' + return f'‚{tex(e.content, i, 0, "")}‘' elif e.quote_type == "DoubleQuote": - return f'„{tex(e.content, 0, "")}“' + return f'„{tex(e.content, i, 0, "")}“' elif e.style == "en": if e.quote_type == "SingleQuote": - return f'‘{tex(e.content, 0, "")}’' + return f'‘{tex(e.content, i, 0, "")}’' elif e.quote_type == "DoubleQuote": - return f'“{tex(e.content, 0, "")}”' + return f'“{tex(e.content, i, 0, "")}”' else: if e.quote_type == "SingleQuote": - return f'\'{tex(e.content, 0, "")}\'' + return f'\'{tex(e.content, i, 0, "")}\'' elif e.quote_type == "DoubleQuote": - return f'"{tex(e.content, 0, "")}"' + return f'"{tex(e.content, i, 0, "")}"' else: - return f'"{tex(e.content, 0, "")}"' + return f'"{tex(e.content, i, 0, "")}"' if isinstance(e, BulletList): tag = "list" @@ -106,14 +108,33 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str: # FIXME: Starting number of list if isinstance(e, Image): - return f'\\image{{width {e.attributes["width"] if "width" in e.attributes else ""}}}{{{e.url}}}' + url = e.url + _, ext = os.path.splitext(url) + ext = ext[1:] + if ext in ["pdf", "png", "jpeg"]: + url = i.process_image(url, ext, relative=False) + elif ext in ["svg"]: + url = i.process_image(url, "pdf", relative=False) + elif ext in ["epdf"]: + url = i.process_image(url, "pdf", relative=False) + elif ext in ["jpg"]: + url = i.process_image(url, "jpeg", relative=False) + else: + url = i.process_image(url, "pdf", relative=False) + width = "" + if "width" in e.attributes: + width = e.attributes["width"] + if e.attributes["width"][-1] == "%": + width = str(int(e.attributes["width"][:-1])/100) + "\\hsize" + width = "width " + width + return f'\\image{{{width}}}{{{url}}}' if isinstance(e, Figure): - return f'\\figure{{{tex(e.content, indent_level+1, indent_str)}}}{{{tex(e.caption, indent_level+1, indent_str)}}}\n\n' + return f'\\figure{{{tex(e.content, i, indent_level+1, indent_str)}}}{{{tex(e.caption, i, indent_level+1, indent_str)}}}\n\n' if isinstance(e, Caption): if inlinify(e) is not None: - return f'\\caption{{{tex(e.content, 0, "")}}}' + return f'\\caption{{{tex(e.content, i, 0, "")}}}' if isinstance(e, ListItem): tag = ":" @@ -134,7 +155,7 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str: if isinstance(e, Note): tag = "fn" if inlinify(e) is not None: - return f'\\fn{{{tex(inlinify(e), 0, "")}}}' + return f'\\fn{{{tex(inlinify(e), i, 0, "")}}}' if isinstance(e, Table): aligns = { @@ -144,16 +165,16 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str: "AlignDefault": "\\quad#\\quad\\hfil" } text = "\strut"+"&".join([aligns[col[0]] for col in e.colspec])+"\cr\n" - text += tex(e.head.content, 0, "") + text += tex(e.head.content, i, 0, "") text += "\\noalign{\\hrule}\n" - text += tex(e.content[0].content, 0, "") + text += tex(e.content[0].content, i, 0, "") text += "\\noalign{\\hrule}\n" - text += tex(e.foot.content, 0, "") + text += tex(e.foot.content, i, 0, "") return "\\vskip1em\n\\halign{"+text+"}\n\\vskip1em\n" # FIXME: Implement rowspan if isinstance(e, TableRow): - return "&".join([("\\multispan"+str(cell.colspan)+" " if cell.colspan > 1 else "")+tex(cell.content, 0, "") for cell in e.content])+"\cr\n" + return "&".join([("\\multispan"+str(cell.colspan)+" " if cell.colspan > 1 else "")+tex(cell.content, i, 0, "") for cell in e.content])+"\cr\n" if isinstance(e, RawInline): if e.format == "tex": @@ -168,13 +189,13 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str: return "" if isinstance(e, Span) or isinstance(e, Plain): - return tex(e.content, 0, "") + return tex(e.content, i, 0, "") if isinstance(e, LineItem): - return tex(e.content, 0, "") + ("\\\\\n" if e.next else "\n") + return tex(e.content, i, 0, "") + ("\\\\\n" if e.next else "\n") if isinstance(e, LineBlock): - return f'{tex(e.content, indent_level+1, indent_str)}\n' + return f'{tex(e.content, i, indent_level+1, indent_str)}\n' if isinstance(e, Group): tag = "begingroup" @@ -184,19 +205,19 @@ def tex(e, indent_level: int=0, indent_str: str="\t") -> str: close = "\\endgroup" if isinstance(e, Div): - return f'{tex(e.content, indent_level+1, indent_str)}' + return f'{tex(e.content, i, indent_level+1, indent_str)}' if isinstance(e, Doc): - return tex(e.content, indent_level, indent_str)+"\n\\bye" + return tex(e.content, i, indent_level, indent_str)+"\n\\bye" if isinstance(e, Inline): - return f'\\{tag}{arguments}{open}{content_head}{tex(e.content, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}{close}' + return f'\\{tag}{arguments}{open}{content_head}{tex(e.content, i, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}{close}' out_str = "" out_str = f"\\{tag}{arguments}{open}\n" out_str += content_head if hasattr(e, "_content"): - out_str += tex(e.content, indent_level+1, indent_str) + out_str += tex(e.content, i, indent_level+1, indent_str) if hasattr(e, "text"): out_str += e.text out_str += f"{content_foot}\n{close}\n\n"