diff --git a/renderer/document.py b/renderer/document.py index 71cad24..615a799 100644 --- a/renderer/document.py +++ b/renderer/document.py @@ -2,6 +2,9 @@ import mistletoe class Document(mistletoe.Document): - # margin_count = 0 - # margin_anchor_count = 0 - references = {} + def __init__(self, *args, **kwargs): + # references must exist before super().__init__ tokenizes the lines, + # since ReferencedToken reads it from _root_node during tokenization. + self.references: dict[str, str] = {} + + super().__init__(*args, **kwargs) diff --git a/renderer/fifty_ohm_html_renderer.py b/renderer/fifty_ohm_html_renderer.py index 5b11c6f..9e4fad2 100644 --- a/renderer/fifty_ohm_html_renderer.py +++ b/renderer/fifty_ohm_html_renderer.py @@ -8,16 +8,15 @@ from .dash import Dash from .document import Document from .formula import Formula from .halfwidth_spaces import HalfwidthSpaces +from .image import Image from .include import Include from .index import Index from .morse import Morse from .nonbreaking_spaces import NonbreakingSpaces, NonbreakingSpacesDots -from .photo import Photo -from .picture import Picture from .qso import Qso from .question import Question from .quote import Quote -from .references import References +from .reference import Reference from .table import Table, TableBody, TableCell, TableHeader, TableRow from .tag import Tag from .underline import Underline @@ -30,14 +29,6 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): margin_anchor_id = 0 margin_id = 0 - edition: str = None - chapter: str = None - section: str = None - section_url: str = None - - # TODO: Add type - references = {} - def __init__( self, *extras, @@ -62,10 +53,9 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): HalfwidthSpaces, NonbreakingSpaces, NonbreakingSpacesDots, - References, + Reference, Question, - Picture, - Photo, + Image, Table, TableHeader, TableRow, @@ -90,23 +80,21 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): self.section = section # Set section URL if provided, otherwise use default - if section_url is not None: - self.section_url = section_url + self.section_url = section_url if section_url is not None else "section.html" - # Figure map: stores ref -> hierarchical number mapping - self.figure_map = {} - - # Single unified counter for all figure types (pictures, photos, tables) - self.figure_counter = 0 + self.references: dict[str, str] = {} # Keep track of index ids already emitted in this document. self.index_anchor_ids = set() - def get_reference_label(self, marker: str): + def _format_reference_label(self, marker: str): if marker in self.references: - return f"{self.edition}-{self.chapter}.{self.section}.{self.references[marker]}", True + label = self.references[marker] + if self.edition and self.chapter and self.section: + label = f"{self.edition}-{self.chapter}.{self.section}.{label}" + return label else: - return marker, False + return None def render_dash(self, token): return " – " @@ -222,47 +210,11 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): lookup = {"": "", " ": " "} return f"{lookup[token.first]}{token.second}{lookup[token.third]}" - def collect_figures(self, document): - """First pass: collect all figures and assign hierarchical numbers.""" - self.figure_counter = 0 - self.figure_map = {} - - self._collect_figures_recursive(document) - - def _collect_figures_recursive(self, token): - """Recursively traverse the document tree to collect figures.""" - if hasattr(token, "children") and token.children is not None: - for child in token.children: - # Check if this is a Picture, Photo, or Table token - if isinstance(child, Picture | Photo | Table): - # Get the reference ID (ref for Picture/Photo, name for Table) - ref_id = getattr(child, "ref", None) or getattr(child, "name", None) - - if ref_id: # Only number figures with a reference ID - self.figure_counter += 1 - hierarchical_num = self._format_figure_number(self.figure_counter) - self.figure_map[ref_id] = hierarchical_num - child.number = hierarchical_num - else: - child.number = "" - - # Recurse into children - self._collect_figures_recursive(child) - - def _format_figure_number(self, counter): - """Format the hierarchical figure number based on context.""" - if self.edition and self.chapter and self.section: - return f"{self.edition}-{self.chapter}.{self.section}.{counter}" - else: - # Fallback for tests or when context is not provided - return str(counter) - - def render_references(self, token): + def render_reference(self, token: Reference): # Look up the figure number from the map - figure_num = self.figure_map.get(token.first, "?") + label = self._format_reference_label(token.marker) or "?" return ( - f'{figure_num}" + f'{label}' ) def render_question(self, token): @@ -282,31 +234,34 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): return ( f'
\n' f' {alt_text}\n' - f"
Abbildung {self.get_reference_label(marker)}: {text}
\n" + f"
Abbildung {label}: {text}
\n" "
\n" ) - def render_picture(self, token): - alt_text = "" - if self.picture_handler is not None: - alt_text = self.picture_handler(token.id) - - return self.render_picture_helper(token.id, token.marker, token.text, token.label, alt_text) - @staticmethod - def render_photo_helper(id, ref, text, number, alt_text): + def render_photo_helper(id, ref, text, label, alt_text): return f"""
{alt_text} -
Abbildung {number}: {text}
+
Abbildung {label}: {text}
""" - def render_photo(self, token): - alt_text = "" - if self.photo_handler is not None: - alt_text = self.photo_handler(token.id) or alt_text - return self.render_photo_helper(token.id, token.ref, token.text, token.number, alt_text) + def render_image(self, token: Image): + label = self._format_reference_label(token.marker) or "?" + + # Only supported kinds are photo and picture + if token.kind == "photo": + alt_text = "" + if self.photo_handler is not None: + alt_text = self.photo_handler(token.id) or alt_text + return self.render_photo_helper(token.id, token.marker, token.text, label, alt_text) + if token.kind == "picture": + alt_text = "" + if self.picture_handler is not None: + alt_text = self.picture_handler(token.id) + return self.render_picture_helper(token.id, token.marker, token.text, label, alt_text) + return "" def render_table(self, token: Table): # Add id and name attributes if table has a name @@ -317,10 +272,11 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): table = f'\n{self.render_inner(token)}' if token.caption != "": - # Include hierarchical number in caption if available + # Include hierarchical number in caption if the table is named. caption_text = token.caption - if hasattr(token, "number") and token.number: - caption_text = f"Tabelle {token.number}: {token.caption}" + label = self._format_reference_label(token.name) + if label: + caption_text = f"Tabelle {label}: {token.caption}" table += f"\n" table += "
{caption_text}
" diff --git a/renderer/fifty_ohm_latex_renderer.py b/renderer/fifty_ohm_latex_renderer.py index 86989ca..39c9c96 100644 --- a/renderer/fifty_ohm_latex_renderer.py +++ b/renderer/fifty_ohm_latex_renderer.py @@ -3,10 +3,9 @@ from mistletoe.latex_renderer import LaTeXRenderer from .comment import BlockComment from .dash import Dash from .halfwidth_spaces import HalfwidthSpaces +from .image import Image from .index import Index from .nonbreaking_spaces import NonbreakingSpaces, NonbreakingSpacesDots -from .photo import Photo -from .picture import Picture from .question import Question from .quote import Quote from .table import Table, TableBody, TableCell, TableHeader, TableRow @@ -26,8 +25,7 @@ class FiftyOhmLaTeXRenderer(LaTeXRenderer): NonbreakingSpaces, NonbreakingSpacesDots, Question, - Picture, - Photo, + Image, Table, TableBody, TableRow, @@ -115,17 +113,18 @@ class FiftyOhmLaTeXRenderer(LaTeXRenderer): \captionof{{figure}}{{{text}}} \label{{{ref}}}""" - def render_picture(self, token): - return self.render_picture_helper(token.id, token.ref, token.text, token.number) - @staticmethod def render_photo_helper(id, ref, text, number): return rf"""\includegraphics[width=1.0\linewidth]{{photo/{id}}} \captionof{{figure}}{{{text}}} \label{{{ref}}}""" - def render_photo(self, token): - return self.render_photo_helper(token.id, token.ref, token.text, token.number) + def render_image(self, token: Image): + if token.kind == "photo": + return self.render_photo_helper(token.id, token.marker, token.text, token.label) + if token.kind == "picture": + return self.render_picture_helper(token.id, token.marker, token.text, token.label) + return "" def render_table(self, token): table = "\\begin{DARCtabular}" + f"{self.render_inner(token)}" + "\\end{DARCtabular}" diff --git a/renderer/image.py b/renderer/image.py new file mode 100644 index 0000000..a2f0691 --- /dev/null +++ b/renderer/image.py @@ -0,0 +1,40 @@ +import re + +from mistletoe.block_tokenizer import FileWrapper + +from renderer.referenced_token import ReferencedToken + + +class Image(ReferencedToken): + """ + Block token for pictures and photos. + Examples: + [picture:727:amplitude_periode_halbwellen:Positive und negative Halbwellen einer Sinusschwingung] + [photo:123:n_wasserfalldiagramm_trx_collage:Ansichten verschiedener Transceiver] + """ + + pattern = re.compile(r"^\[(?Ppicture|photo):(?P\d+):(?P[^:\]]+):(?P[^:\]]+)\]$") + + @classmethod + def start(cls, line: str): + return cls.pattern.match(line.strip()) is not None + + @classmethod + def check_interrupts_paragraph(cls, lines: FileWrapper): + line = lines.peek() + if line is not None: + return cls.start(line) + else: + return False + + @classmethod + def read(cls, lines: FileWrapper): + match = cls.pattern.match(next(lines).strip()) + return match.group("kind"), match.group("id"), match.group("marker"), match.group("text") + + def __init__(self, match): + kind, id, marker, text = match + super().__init__(marker) + self.kind = kind + self.id = id + self.text = text diff --git a/renderer/photo.py b/renderer/photo.py deleted file mode 100644 index 5634372..0000000 --- a/renderer/photo.py +++ /dev/null @@ -1,19 +0,0 @@ -import re - -from mistletoe.span_token import SpanToken - - -class Photo(SpanToken): - """ - Span token for photos. - Example: [photo:123:n_wasserfalldiagramm_trx_collage:Ansichten verschiedener Transceiver] - """ - - parse_inner = False - pattern = re.compile(r"\[photo:(\d+):([^:\]]+):([^:\]]+)\]") - - def __init__(self, match_object): - self.id = match_object.group(1) - self.ref = match_object.group(2) - self.text = match_object.group(3) - self.number = "TODO" diff --git a/renderer/picture.py b/renderer/picture.py deleted file mode 100644 index b645126..0000000 --- a/renderer/picture.py +++ /dev/null @@ -1,33 +0,0 @@ -import re - -from mistletoe.block_tokenizer import FileWrapper - -from renderer.referenced_token import ReferencedToken - - -class Picture(ReferencedToken): - """ - Span token for pictures. - Example: [picture:727:amplitude_periode_halbwellen:Positive und negative Halbwellen einer Sinusschwingung] - """ - - pattern = re.compile(r"^\[picture:(?P\d+):(?P[^:\]]+):(?P[^:\]]+)\]$") - - @classmethod - def start(cls, line: str): - return cls.pattern.match(line.strip()) is not None - - @classmethod - def check_interrupts_paragraph(cls, lines: FileWrapper): - return cls.pattern.match(lines.peek()) - - @classmethod - def read(cls, lines: FileWrapper): - match = cls.pattern.match(next(lines)) - return match.group("id"), match.group("marker"), match.group("text") - - def __init__(self, match): - id, marker, text = match - super().__init__(marker) - self.id = id - self.text = text diff --git a/renderer/references.py b/renderer/reference.py similarity index 73% rename from renderer/references.py rename to renderer/reference.py index bb9de72..d1f7287 100644 --- a/renderer/references.py +++ b/renderer/reference.py @@ -3,7 +3,7 @@ import re from mistletoe.span_token import SpanToken -class References(SpanToken): +class Reference(SpanToken): """ Resolve references for html Identifies "[ref:$]" @@ -12,4 +12,4 @@ class References(SpanToken): pattern = re.compile(r"\[ref\:(\S+)\]") def __init__(self, match_object): - self.first = match_object.group(1) + self.marker: str = match_object.group(1) diff --git a/renderer/referenced_token.py b/renderer/referenced_token.py index 1f07b46..7a1e0d8 100644 --- a/renderer/referenced_token.py +++ b/renderer/referenced_token.py @@ -3,15 +3,16 @@ from mistletoe.block_token import BlockToken class ReferencedToken(BlockToken): - line_number: int = -1 - marker: str = None - label: str = None - def __init__(self, marker: str): - self.marker = marker - - if marker not in mistletoe.token._root_node.references: - self.label = len(mistletoe.token._root_node.references) + 1 - mistletoe.token._root_node.references[marker] = self.label + if marker != "": + self.marker = marker + references = mistletoe.token._root_node.references + if marker not in references: + label = str(len(references) + 1) + references[marker] = label + else: + label = references[marker] + self.label = label else: - self.label = mistletoe._root_node.references[marker] + self.marker = None + self.label = None diff --git a/renderer/table.py b/renderer/table.py index 7a107e6..45135e5 100644 --- a/renderer/table.py +++ b/renderer/table.py @@ -4,6 +4,8 @@ from enum import Enum from mistletoe import span_token from mistletoe.block_token import BlockToken +from renderer.referenced_token import ReferencedToken + class CellAlignment(Enum): LEFT = "l" @@ -76,7 +78,7 @@ class TableBody(BlockToken): self.children, self.header = rows, header -class Table(BlockToken): +class Table(ReferencedToken): name: str caption: str header: list[TableHeader] @@ -115,4 +117,8 @@ class Table(BlockToken): return children, name, caption def __init__(self, match): - self.children, self.name, self.caption = match + children, name, caption = match + super().__init__(name) + self.children = children + self.name = name + self.caption = caption diff --git a/src/build.py b/src/build.py index 7803885..d243be4 100644 --- a/src/build.py +++ b/src/build.py @@ -7,10 +7,10 @@ import zipfile from pathlib import Path from jinja2 import Environment, FileSystemLoader -from mistletoe import Document from rich.progress import BarColumn, Progress, TaskProgressColumn, TextColumn, TimeRemainingColumn from tqdm import tqdm +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer from renderer.index import Index @@ -222,12 +222,7 @@ class Build: section=section_num, section_url=section_filename, ) as renderer: - # First pass: collect all figures and assign hierarchical numbers - doc = Document(section["content"]) - renderer.collect_figures(doc) - - # Second pass: render with hierarchical numbers - section["content"] = renderer.render(doc) + section["content"] = renderer.render(Document(section["content"])) result = section_template.render( edition=edition, @@ -266,58 +261,51 @@ class Build: # Note: filename uses original edition (e.g., "N"), not modified edition with "S" (e.g., "NS") slide_filename = f"{edition}_slide_{chapter['ident']}.html" - with FiftyOhmHtmlSlideRenderer( - question_renderer=self.__build_question_slide, - picture_handler=self.__picture_handler, - photo_handler=self.__photo_handler, - include_handler=self.__include_handler, - edition=edition, - chapter=chapter_num, - section="0", # Will be updated per section - section_url=slide_filename, - ) as renderer: - result = "
\n" - result += f'
\n

{chapter["title"]}

\n
\n' - result += help_template.render() - result += "
\n" + result = "
\n" + result += f'
\n

{chapter["title"]}

\n
\n' + result += help_template.render() + result += "
\n" - section_counter = 0 - slides_task = progress.add_task("Rendering slides ...") - for section in progress.track(sections, task_id=slides_task): - progress.update(slides_task, description=f"Rendering slides of {section['title']}") + section_counter = 0 + slides_task = progress.add_task("Rendering slides ...") + for section in progress.track(sections, task_id=slides_task): + progress.update(slides_task, description=f"Rendering slides of {section['title']}") - if section["slide"] is None: - continue + if section["slide"] is None: + continue - section_counter += 1 - # Update section number for this section - renderer.section = str(section_counter) - # Reset figure counter for each section - renderer.figure_counter = 0 - renderer.figure_map = {} + section_counter += 1 - if not section["slide"].startswith("---"): - section["slide"] = "---\n" + section["slide"] + if not section["slide"].startswith("---"): + section["slide"] = "---\n" + section["slide"] - # First pass: collect figures - doc = Document(section["slide"]) - renderer.collect_figures(doc) - - # Second pass: render with hierarchical numbers - tmp = f'
\n

{section["title"]}

\n
\n' - tmp += renderer.render(doc) - result += f"
{tmp}
\n" - - progress.remove_task(slides_task) - - result += next_template.render( + # A fresh renderer per section keeps the figure counter and + # reference map scoped to that section. + with FiftyOhmHtmlSlideRenderer( + question_renderer=self.__build_question_slide, + picture_handler=self.__picture_handler, + photo_handler=self.__photo_handler, + include_handler=self.__include_handler, edition=edition, - next_chapter=next_chapter, - chapter=chapter, - ) + chapter=chapter_num, + section=str(section_counter), + section_url=slide_filename, + ) as renderer: + tmp = f'
\n

{section["title"]}

\n
\n' + tmp += renderer.render(Document(section["slide"])) - result = slide_template.render(content=result) - file.write(result) + result += f"
{tmp}
\n" + + progress.remove_task(slides_task) + + result += next_template.render( + edition=edition, + next_chapter=next_chapter, + chapter=chapter, + ) + + result = slide_template.render(content=result) + file.write(result) def __filter_shuffle_answers(self, seq): answers = [] diff --git a/test/test_comment.py b/test/test_comment.py index 68118f9..019c13a 100644 --- a/test/test_comment.py +++ b/test/test_comment.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -16,7 +15,7 @@ def test_comment_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == assertions[assertion] + assert render_html(assertion) == assertions[assertion] @pytest.mark.latex diff --git a/test/test_dash.py b/test/test_dash.py index 1d851e9..4d0a120 100644 --- a/test/test_dash.py +++ b/test/test_dash.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -14,7 +13,7 @@ def test_dash_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == paragraph(assertions[assertion]) + assert render_html(assertion) == paragraph(assertions[assertion]) @pytest.mark.latex diff --git a/test/test_formula.py b/test/test_formula.py index afa85a6..563fef6 100644 --- a/test/test_formula.py +++ b/test/test_formula.py @@ -1,7 +1,6 @@ import pytest -from mistletoe import Document -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer +from test.util import render_html @pytest.mark.html @@ -14,6 +13,5 @@ def test_formula_html(): r"Lorem Ipsum" + "\n" + r"$a \cdot b$": "

Lorem Ipsum

\n\n" + r"$$a \cdot b$$" + "\n\n", } - with FiftyOhmHtmlRenderer() as renderer: - for assertion in assertions: - assert renderer.render(Document(assertion)) == assertions[assertion] + for assertion in assertions: + assert render_html(assertion) == assertions[assertion] diff --git a/test/test_halfwidth_spaces.py b/test/test_halfwidth_spaces.py index b2e29c7..75453dc 100644 --- a/test/test_halfwidth_spaces.py +++ b/test/test_halfwidth_spaces.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -11,7 +10,7 @@ def test_halfwidth_space_html(): input = "Dies ist z.B. ein Test. D.h. hier sollten d.h. Spaces ersetzt worden sein." target = "Dies ist z. B. ein Test. D. h. hier sollten d. h. Spaces ersetzt worden sein." - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(target) + assert render_html(input) == paragraph(target) @pytest.mark.latex diff --git a/test/test_hierarchical_numbering.py b/test/test_hierarchical_numbering.py index 2974182..08a0376 100644 --- a/test/test_hierarchical_numbering.py +++ b/test/test_hierarchical_numbering.py @@ -1,13 +1,13 @@ -import mistletoe import pytest +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer @pytest.mark.html def test_hierarchical_picture_numbering(): - """Test that pictures get hierarchical numbers in format edition-chapter.section.counter""" + """Pictures get hierarchical numbers in format edition-chapter.section.counter""" markdown_text = """ [picture:123:pic1:First picture] @@ -20,30 +20,26 @@ Reference to second: [ref:pic2] """ renderer = FiftyOhmHtmlRenderer(edition="E", chapter="2", section="3") - doc = mistletoe.Document(markdown_text) + doc = Document(markdown_text) - # First pass: collect figures - renderer.collect_figures(doc) + # Counter is assigned in document order during parsing. + assert doc.references["pic1"] == "1" + assert doc.references["pic2"] == "2" - # Verify figure map - assert renderer.figure_map["pic1"] == "E-2.3.1" - assert renderer.figure_map["pic2"] == "E-2.3.2" - - # Second pass: render result = renderer.render(doc) - # Verify hierarchical numbers in output + # Hierarchical numbers in the figure captions. assert "Abbildung E-2.3.1:" in result assert "Abbildung E-2.3.2:" in result - # Verify references are resolved + # References are resolved to the same numbers. assert ">E-2.3.1" in result assert ">E-2.3.2" in result @pytest.mark.html def test_hierarchical_photo_numbering(): - """Test that photos get hierarchical numbers""" + """Photos get hierarchical numbers from the shared counter""" markdown_text = """ [photo:789:photo1:First photo] @@ -53,12 +49,10 @@ Reference: [ref:photo1] """ renderer = FiftyOhmHtmlRenderer(edition="N", chapter="1", section="2") - doc = mistletoe.Document(markdown_text) + doc = Document(markdown_text) - renderer.collect_figures(doc) - - assert renderer.figure_map["photo1"] == "N-1.2.1" - assert renderer.figure_map["photo2"] == "N-1.2.2" + assert doc.references["photo1"] == "1" + assert doc.references["photo2"] == "2" result = renderer.render(doc) @@ -69,7 +63,7 @@ Reference: [ref:photo1] @pytest.mark.html def test_hierarchical_table_numbering(): - """Test that tables with names get hierarchical numbers""" + """Tables with names get hierarchical numbers from the shared counter""" markdown_text = """ | Header 1 | Header 2 | | Data 1 | Data 2 | @@ -83,12 +77,10 @@ Reference: [ref:table1] """ renderer = FiftyOhmHtmlRenderer(edition="A", chapter="3", section="1") - doc = mistletoe.Document(markdown_text) + doc = Document(markdown_text) - renderer.collect_figures(doc) - - assert renderer.figure_map["table1"] == "A-3.1.1" - assert renderer.figure_map["table2"] == "A-3.1.2" + assert doc.references["table1"] == "1" + assert doc.references["table2"] == "2" result = renderer.render(doc) @@ -99,7 +91,7 @@ Reference: [ref:table1] @pytest.mark.html def test_mixed_figure_types_share_counter(): - """Test that pictures, photos, and tables share a single unified counter""" + """Pictures, photos, and tables share a single unified counter in document order""" markdown_text = """ [picture:1:pic1:Picture one] @@ -115,19 +107,16 @@ References: [ref:pic1], [ref:photo1], [ref:tab1], [ref:pic2] """ renderer = FiftyOhmHtmlRenderer(edition="E", chapter="5", section="2") - doc = mistletoe.Document(markdown_text) + doc = Document(markdown_text) - renderer.collect_figures(doc) - - # All types share the same counter - assert renderer.figure_map["pic1"] == "E-5.2.1" - assert renderer.figure_map["photo1"] == "E-5.2.2" - assert renderer.figure_map["tab1"] == "E-5.2.3" - assert renderer.figure_map["pic2"] == "E-5.2.4" + # All types share the same counter, assigned in document order. + assert doc.references["pic1"] == "1" + assert doc.references["photo1"] == "2" + assert doc.references["tab1"] == "3" + assert doc.references["pic2"] == "4" result = renderer.render(doc) - # Verify all references are resolved with correct numbers assert "Abbildung E-5.2.1:" in result # pic1 assert "Abbildung E-5.2.2:" in result # photo1 assert "Tabelle E-5.2.3:" in result # tab1 @@ -136,7 +125,7 @@ References: [ref:pic1], [ref:photo1], [ref:tab1], [ref:pic2] @pytest.mark.slide def test_slide_renderer_adds_s_suffix(): - """Test that slide renderer adds 'S' suffix to edition""" + """The slide renderer adds an 'S' suffix to the edition""" markdown_text = """ [picture:1:pic1:Slide picture] @@ -144,17 +133,11 @@ Reference: [ref:pic1] """ renderer = FiftyOhmHtmlSlideRenderer(edition="E", chapter="2", section="1") - doc = mistletoe.Document(markdown_text) - # Verify edition has S suffix + # Edition gains the S suffix. assert renderer.edition == "ES" - renderer.collect_figures(doc) - - # Verify hierarchical number includes S - assert renderer.figure_map["pic1"] == "ES-2.1.1" - - result = renderer.render(doc) + result = renderer.render(Document(markdown_text)) assert "Abbildung ES-2.1.1:" in result assert ">ES-2.1.1" in result @@ -162,49 +145,38 @@ Reference: [ref:pic1] @pytest.mark.html def test_fallback_numbering_without_context(): - """Test that numbering falls back to simple counter when context is missing""" + """Numbering falls back to a simple counter when context is missing""" markdown_text = """ [picture:1:pic1:Picture] Reference: [ref:pic1] """ - # No edition/chapter/section provided + # No edition/chapter/section provided. renderer = FiftyOhmHtmlRenderer() - doc = mistletoe.Document(markdown_text) - - renderer.collect_figures(doc) - - # Should fall back to simple counter - assert renderer.figure_map["pic1"] == "1" - - result = renderer.render(doc) + result = renderer.render(Document(markdown_text)) + # Falls back to the bare counter. assert "Abbildung 1:" in result assert ">1" in result @pytest.mark.html def test_unresolved_reference(): - """Test that references to non-existent figures show '?'""" + """References to non-existent figures show '?'""" markdown_text = """ Reference to non-existent: [ref:nonexistent] """ renderer = FiftyOhmHtmlRenderer(edition="E", chapter="1", section="1") - doc = mistletoe.Document(markdown_text) + result = renderer.render(Document(markdown_text)) - renderer.collect_figures(doc) - - result = renderer.render(doc) - - # Should show ? for unresolved reference assert ">?" in result @pytest.mark.html def test_table_without_name_not_numbered(): - """Test that tables without names are not numbered""" + """Tables without names are not numbered and do not consume a counter slot""" markdown_text = """ | Header 1 | Header 2 | | Data 1 | Data 2 | @@ -213,10 +185,12 @@ def test_table_without_name_not_numbered(): """ renderer = FiftyOhmHtmlRenderer(edition="E", chapter="1", section="1") - doc = mistletoe.Document(markdown_text) + doc = Document(markdown_text) - renderer.collect_figures(doc) + # Only the picture is registered. + assert doc.references == {"pic1": "1"} - # Only the picture should be numbered - assert renderer.figure_map["pic1"] == "E-1.1.1" - assert renderer.figure_counter == 1 + result = renderer.render(doc) + + assert "Abbildung E-1.1.1:" in result + assert "Tabelle" not in result diff --git a/test/test_html.py b/test/test_html.py index d60a04e..8d5a601 100644 --- a/test/test_html.py +++ b/test/test_html.py @@ -2,9 +2,9 @@ import json import pytest from jinja2 import Environment, FileSystemLoader -from mistletoe import Document import src.config as config +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer conf = config.Config() diff --git a/test/test_html_slides.py b/test/test_html_slides.py index 27ebbd5..f1729d1 100644 --- a/test/test_html_slides.py +++ b/test/test_html_slides.py @@ -3,9 +3,9 @@ import random import pytest from jinja2 import Environment, FileSystemLoader -from mistletoe import Document import src.config as config +from renderer.document import Document from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer conf = config.Config() diff --git a/test/test_include.py b/test/test_include.py index 4b024a4..6db910b 100644 --- a/test/test_include.py +++ b/test/test_include.py @@ -1,6 +1,6 @@ import pytest -from mistletoe import Document +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer diff --git a/test/test_index.py b/test/test_index.py index ec538fd..4984dbf 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -42,7 +41,7 @@ def test_index_html(): } for key, value in assertions.items(): - assert mistletoe.markdown(key, FiftyOhmHtmlRenderer) == paragraph(value) + assert render_html(key) == paragraph(value) @pytest.mark.latex diff --git a/test/test_morse.py b/test/test_morse.py index 8a80fc2..eb78051 100644 --- a/test/test_morse.py +++ b/test/test_morse.py @@ -1,8 +1,7 @@ -import mistletoe import pytest from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -17,4 +16,4 @@ def test_morse_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == paragraph(assertions[assertion]) + assert render_html(assertion) == paragraph(assertions[assertion]) diff --git a/test/test_nonbreaking_spaces.py b/test/test_nonbreaking_spaces.py index 68839d8..7a9d7c7 100644 --- a/test/test_nonbreaking_spaces.py +++ b/test/test_nonbreaking_spaces.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -11,7 +10,7 @@ def test_paragraph_html(): input = "Der Test befindet sich unter §2 vielleicht aber auch § 3." target = "Der Test befindet sich unter § 2 vielleicht aber auch § 3." - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(target) + assert render_html(input) == paragraph(target) @pytest.mark.html @@ -24,7 +23,7 @@ def test_three_points_html(): } for key, value in assertions.items(): - assert mistletoe.markdown(key, FiftyOhmHtmlRenderer) == paragraph(value) + assert render_html(key) == paragraph(value) @pytest.mark.html @@ -32,7 +31,7 @@ def test_absatz_html(): input = "Der Test befindet sich unter Abs.2 vielleicht aber auch Abs. 3." target = "Der Test befindet sich unter Abs. 2 vielleicht aber auch Abs. 3." - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(target) + assert render_html(input) == paragraph(target) @pytest.mark.html @@ -40,7 +39,7 @@ def test_class_html(): input = "Ich lerne für Klasse A, Klasse E und Klasse N." target = "Ich lerne für Klasse A, Klasse E und Klasse N." - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(target) + assert render_html(input) == paragraph(target) @pytest.mark.latex diff --git a/test/test_photo.py b/test/test_photo.py index 7e6d27b..5ea3e25 100644 --- a/test/test_photo.py +++ b/test/test_photo.py @@ -1,19 +1,17 @@ -import mistletoe import pytest +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph @pytest.mark.html def test_photo_html(): - assertions = { - "[photo:123:abc:Text]": FiftyOhmHtmlRenderer.render_photo_helper("123", "abc", "Text", "TODO", ""), - } + assertions = {"[photo:123:abc:Text]": ("123", "abc", "Text", "A-5.7.1", "")} - for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == paragraph(assertions[assertion]) + with FiftyOhmHtmlRenderer(edition="A", chapter="5", section="7") as renderer: + for assertion in assertions: + assert renderer.render(Document(assertion)) == renderer.render_photo_helper(*assertions[assertion]) + "\n" @pytest.mark.latex @@ -22,5 +20,6 @@ def test_photo_latex(): "[photo:123:abc:Text]": FiftyOhmLaTeXRenderer.render_photo_helper("123", "abc", "Text", "TODO"), } - for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmLaTeXRenderer) == "\n" + assertions[assertion] + "\n" + with FiftyOhmLaTeXRenderer() as renderer: + for assertion in assertions: + assert renderer.render(Document(assertion)) == assertions[assertion] diff --git a/test/test_picture.py b/test/test_picture.py index 70b3bc3..0c6d391 100644 --- a/test/test_picture.py +++ b/test/test_picture.py @@ -1,4 +1,3 @@ -import mistletoe import pytest from renderer.document import Document @@ -21,5 +20,6 @@ def test_picture_latex(): "[picture:0:abc:Text]": FiftyOhmLaTeXRenderer.render_picture_helper("0", "abc", "Text", "TODO"), } - for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmLaTeXRenderer) == "\n" + assertions[assertion] + "\n" + with FiftyOhmLaTeXRenderer() as renderer: + for assertion in assertions: + assert renderer.render(Document(assertion)) == assertions[assertion] diff --git a/test/test_qso.py b/test/test_qso.py index 50bdfa2..b34ab23 100644 --- a/test/test_qso.py +++ b/test/test_qso.py @@ -1,8 +1,7 @@ -import mistletoe import pytest from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer -from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer +from test.util import render_html, render_slide @pytest.mark.html @@ -21,7 +20,7 @@ def test_qso_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == assertions[assertion] + assert render_html(assertion) == assertions[assertion] @pytest.mark.slide @@ -31,4 +30,4 @@ def test_qso_slide(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlSlideRenderer) == assertions[assertion] + assert render_slide(assertion) == assertions[assertion] diff --git a/test/test_question.py b/test/test_question.py index 3b9fdd3..a1f8226 100644 --- a/test/test_question.py +++ b/test/test_question.py @@ -1,8 +1,8 @@ import json import pytest -from mistletoe import Document +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer from test.util import paragraph diff --git a/test/test_quote.py b/test/test_quote.py index 64ff664..d69bcb2 100644 --- a/test/test_quote.py +++ b/test/test_quote.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -18,7 +17,7 @@ def test_quote_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == paragraph(assertions[assertion]) + assert render_html(assertion) == paragraph(assertions[assertion]) @pytest.mark.latex diff --git a/test/test_references.py b/test/test_references.py index 5011685..8272059 100644 --- a/test/test_references.py +++ b/test/test_references.py @@ -1,8 +1,6 @@ -import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -15,4 +13,4 @@ def test_references_html(): } for key, value in assertions.items(): - assert mistletoe.markdown(key, FiftyOhmHtmlRenderer) == paragraph(value) + assert render_html(key) == paragraph(value) diff --git a/test/test_table.py b/test/test_table.py index bdccb15..6047571 100644 --- a/test/test_table.py +++ b/test/test_table.py @@ -1,6 +1,6 @@ import pytest -from mistletoe import Document +from renderer.document import Document from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer @@ -15,7 +15,7 @@ def test_table_html(): "| l: a |\n| b |\n| c |": '\n\n\n\n\n\n\n\n\n\n\n\n\n\n
a
b
c
\n', # noqa: E501 "| a |\n| b |\n| c |": '\n\n\n\n\n\n\n\n\n\n\n\n\n\n
a
b
c
\n', # noqa: E501 "| l: a |\n| *b* |": '\n\n\n\n\n\n\n\n\n\n\n
a
b
\n', # noqa: E501 - "| l: a |\n| b |\n[table:n_ab:A und B]": '\n\n\n\n\n\n\n\n\n\n\n\n
a
b
A und B
\n', # noqa: E501 + "| l: a |\n| b |\n[table:n_ab:A und B]": '\n\n\n\n\n\n\n\n\n\n\n\n
a
b
Tabelle 1: A und B
\n', # noqa: E501 "| l: *a* |": '\n\n\n\n\n\n
a
\n', # noqa: E501 "| l: a |\n| [morse:a] |": '\n\n\n\n\n\n\n\n\n\n\n
a
\n\n▄\n\n▄▄▄\n\n
\n', # noqa: E501 "| l: abc | r: abc | X: abc |\n| 123 | 456 | 789 | \n | 987 | 654 | 321 | ": '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
abcabcabc
123456789
987654321
\n', # noqa: E501 diff --git a/test/test_tag.py b/test/test_tag.py index f3df1cc..e1e7019 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -2,9 +2,8 @@ import mistletoe import pytest from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer -from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html, render_slide @pytest.mark.html @@ -15,7 +14,7 @@ def test_thematic_break_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == assertions[assertion] + assert render_html(assertion) == assertions[assertion] @pytest.mark.html @@ -39,7 +38,7 @@ def test_tag_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == assertions[assertion] + assert render_html(assertion) == assertions[assertion] @pytest.mark.html @@ -61,7 +60,7 @@ def test_thematic_break_and_tag_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == assertions[assertion] + assert render_html(assertion) == assertions[assertion] @pytest.mark.latex @@ -119,7 +118,7 @@ def test_tag_slide(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlSlideRenderer) == assertions[assertion] + assert render_slide(assertion) == assertions[assertion] @pytest.mark.slide @@ -133,4 +132,4 @@ def test_slide_break(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlSlideRenderer) == assertions[assertion] + assert render_slide(assertion) == assertions[assertion] diff --git a/test/test_underline.py b/test/test_underline.py index ec22438..068bb1c 100644 --- a/test/test_underline.py +++ b/test/test_underline.py @@ -1,9 +1,8 @@ import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from renderer.fifty_ohm_latex_renderer import FiftyOhmLaTeXRenderer -from test.util import paragraph +from test.util import paragraph, render_html @pytest.mark.html @@ -15,7 +14,7 @@ def test_underline_html(): } for assertion in assertions: - assert mistletoe.markdown(assertion, FiftyOhmHtmlRenderer) == paragraph(assertions[assertion]) + assert render_html(assertion) == paragraph(assertions[assertion]) @pytest.mark.latex diff --git a/test/test_unit.py b/test/test_unit.py index e6638f7..f39ecb7 100644 --- a/test/test_unit.py +++ b/test/test_unit.py @@ -1,9 +1,6 @@ -import mistletoe import pytest -from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer - -from .util import paragraph +from .util import paragraph, render_html @pytest.mark.html @@ -21,7 +18,7 @@ def test_basic_units_html(): } for input, output in assertions.items(): - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output) + assert render_html(input) == paragraph(output) @pytest.mark.html @@ -33,7 +30,7 @@ def test_translated_units_html(): } for input, output in assertions.items(): - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output) + assert render_html(input) == paragraph(output) @pytest.mark.html @@ -46,4 +43,4 @@ def test_special_units_html(): } for input, output in assertions.items(): - assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output) + assert render_html(input) == paragraph(output) diff --git a/test/util.py b/test/util.py index a86b9eb..96dac82 100644 --- a/test/util.py +++ b/test/util.py @@ -1,2 +1,19 @@ +from renderer.document import Document +from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer +from renderer.fifty_ohm_html_slide_renderer import FiftyOhmHtmlSlideRenderer + + def paragraph(text): return f"

{text}

\n" + + +def render_html(markdown): + """Render markdown with the HTML renderer through the project's Document.""" + with FiftyOhmHtmlRenderer() as renderer: + return renderer.render(Document(markdown)) + + +def render_slide(markdown): + """Render markdown with the slide renderer through the project's Document.""" + with FiftyOhmHtmlSlideRenderer() as renderer: + return renderer.render(Document(markdown))