diff --git a/renderer/__init__.py b/renderer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/renderer/__main__.py b/renderer/__main__.py deleted file mode 100644 index b8a5ddf..0000000 --- a/renderer/__main__.py +++ /dev/null @@ -1,8 +0,0 @@ -import sys - -import mistletoe -from fifty_ohm_html_renderer import FiftyOhmHtmlRenderer - -if __name__ == "__main__": - with open(sys.argv[1]) as fin, open(sys.argv[2], "w") as fout: - fout.write(mistletoe.markdown(fin, FiftyOhmHtmlRenderer)) diff --git a/renderer/fifty_ohm_html_renderer.py b/renderer/fifty_ohm_html_renderer.py index de7bbe2..b65dea4 100644 --- a/renderer/fifty_ohm_html_renderer.py +++ b/renderer/fifty_ohm_html_renderer.py @@ -7,17 +7,35 @@ from renderer.dash import Dash from renderer.halfwidth_spaces import HalfwidthSpaces from renderer.nonbreaking_spaces import NonbreakingSpaces, NonbreakingSpacesDots from renderer.quote import Quote +from renderer.references import References from renderer.tag import Tag from renderer.underline import Underline +from renderer.unit import Unit class FiftyOhmHtmlRenderer(HtmlRenderer): - margin_anchor_id = 0 margin_id = 0 + section_url = "section.html" + ref_id = 0 def __init__(self): - super().__init__(Dash, BlockComment, SpanComment, Quote, Underline, Tag, HalfwidthSpaces, NonbreakingSpaces, NonbreakingSpacesDots) + super().__init__( + Dash, + BlockComment, + SpanComment, + Quote, + Unit, + Underline, + Tag, + HalfwidthSpaces, + NonbreakingSpaces, + NonbreakingSpacesDots, + HalfwidthSpaces, + NonbreakingSpaces, + NonbreakingSpacesDots, + References, + ) def render_dash(self, token): return " – " @@ -34,15 +52,40 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): def render_underline(self, token): return f"{self.render_inner(token)}" + @classmethod + def render_unit(cls, token: Unit): + unit = token.prefix + cls.convert_unit_helper(token.unit) + if token.unit in ["°", "%"]: + # Special cases with no space between value and unit. + return f"{token.value}{unit}" + else: + # Default case is rendered with a narrow no-break space. + return f"{token.value} {unit}" + + units = { + "Ohm": "Ω", + } + + @classmethod + def convert_unit_helper(cls, unit: str) -> str: + """Converts human-typable units to their preferred representation. + + :param str unit: The unit to convert + """ + if unit in cls.units.keys(): + return cls.units[unit] + else: + return unit + def render_thematic_break(self, token): self.margin_anchor_id += 1 return f'' @staticmethod def render_tag_helper(type, content, margin_id, margin_anchor_id): - """ This function is used to render the different types of tags. It is - used in the HtmlRenderer class and also in the test class """ - return textwrap.dedent(f'''\ + """This function is used to render the different types of tags. It is + used in the HtmlRenderer class and also in the test class""" + return textwrap.dedent(f"""\
{content}
@@ -62,33 +105,29 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): moveFunc(); }})(); - ''') + """) def render_tag(self, token): - if(token.tagtype == "latexonly"): + if token.tagtype == "latexonly": return "" - elif(token.tagtype == "webonly"): + elif token.tagtype == "webonly": return self.render_inner(token) self.margin_id += 1 - if(token.tagtype == "webmargin"): + if token.tagtype == "webmargin": type = "margin" - elif(token.tagtype == "webtip"): + elif token.tagtype == "webtip": type = "tip" - elif(token.tagtype == "webindepth"): + elif token.tagtype == "webindepth": type = "indepth" - else : + else: type = token.tagtype return self.render_tag_helper( - type, - self.render_inner(token), - self.margin_id, - self.margin_anchor_id + type, self.render_inner(token), self.margin_id, self.margin_anchor_id ) - - + def render_halfwidth_spaces(self, token): return f"{token.first}. {token.second}." @@ -96,8 +135,8 @@ class FiftyOhmHtmlRenderer(HtmlRenderer): return f"{token.first} {token.second}" def render_nonbreaking_spaces_dots(self, token): - lookup = { - "" : "", - " " : " " - } - return f"{lookup[token.first]}{token.second}{lookup[token.third]}" \ No newline at end of file + lookup = {"": "", " ": " "} + return f"{lookup[token.first]}{token.second}{lookup[token.third]}" + + def render_references(self, token): + return f'{self.ref_id}' diff --git a/renderer/halfwidth_spaces.py b/renderer/halfwidth_spaces.py index f9daf9b..dfdd374 100644 --- a/renderer/halfwidth_spaces.py +++ b/renderer/halfwidth_spaces.py @@ -8,7 +8,7 @@ class HalfwidthSpaces(SpanToken): Insert halfwidth space into span token (Z. B.) Identifies "z.B.", "d.h." and "D.h." with and without space in between. """ - pattern = re.compile(r"\b(z|d|D)\.(B|h)\.") + pattern = re.compile(r"\b(z|Z|d|D)\.\s?(B|h)\.") def __init__(self, match_object): self.first = match_object.group(1) diff --git a/renderer/nonbreaking_spaces.py b/renderer/nonbreaking_spaces.py index 932653f..08ac99d 100644 --- a/renderer/nonbreaking_spaces.py +++ b/renderer/nonbreaking_spaces.py @@ -6,7 +6,7 @@ from mistletoe.span_token import SpanToken class NonbreakingSpaces(SpanToken): """ Insert unbreakable space into span token (§ 3) - Identifies "§$number", "Abs.$number", "Klasse [A,E,N]" w/ and w\ space between. + Identifies "§$number", "Abs.$number", "Klasse [A,E,N]" w/ and without space between. """ pattern = re.compile(r"(§|Abs\.|Klasse)\s?(\d|[NEA])") diff --git a/renderer/references.py b/renderer/references.py new file mode 100644 index 0000000..e8b5f18 --- /dev/null +++ b/renderer/references.py @@ -0,0 +1,15 @@ +import re + +from mistletoe.span_token import SpanToken + + +class References(SpanToken): + """ + Resolve references for html + Identifies "[ref:$]" + """ + pattern = re.compile(r"\[ref\:(\S+)\]") + + def __init__(self, match_object): + self.first = match_object.group(1) + diff --git a/renderer/unit.py b/renderer/unit.py new file mode 100644 index 0000000..4a6b9f0 --- /dev/null +++ b/renderer/unit.py @@ -0,0 +1,55 @@ +import re + +from mistletoe.span_token import SpanToken + + +class Unit(SpanToken): + units = [ + "A", + "Ah", + "A/mm²", + "baud", + "Bit", + "Bit/s", + "dB", + "dBi", + "dBm", + "dBW", + "F", + "J", + "Hz", + "H", + "cm", + "m", + "m²", + "Ohm", + "ppm", + "pps", + "s", + "V", + "W", + "Wh", + "°", + "%", + ] + prefixes = ["p", "n", "μ", "m", "k", "M", "G"] + + pattern = re.compile( + r"(?P[0-9,]+) ?(?P" + # Group all prefixes together in a capture group + # Empty string at end of list is required so the group always exists in the resulting match + + "|".join(prefixes + [""]) + + ")(?P" + # Group all units together in a capture group. + + "|".join(units) + + r")(?!\w)" + ) + + value: str + prefix: str + unit: str + + def __init__(self, match: re.Match): + self.value = match.group("value") + self.prefix = match.group("prefix") or "" + self.unit = match.group("unit") diff --git a/test/test_halfwidth_spaces.py b/test/test_halfwidth_spaces.py index 0438674..1d3c4f9 100644 --- a/test/test_halfwidth_spaces.py +++ b/test/test_halfwidth_spaces.py @@ -3,8 +3,13 @@ import mistletoe from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer from test.util import paragraph -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) +def test_halfwidth_space_html(): + assertions = { + "Dies ist z.B. ein Test. D.h. hier sollten d.h. Spaces ersetzt worden sein." : "Dies ist z. B. ein Test. D. h. hier sollten d. h. Spaces ersetzt worden sein.", + "Dies ist z. B. ein Test. D. h. hier sollten d. h. Spaces ersetzt worden sein." : "Dies ist z. B. ein Test. D. h. hier sollten d. h. Spaces ersetzt worden sein.", + "Z. B. die D.B. wird auch ersetzt, aber das macht nix." : "Z. B. die D. B. wird auch ersetzt, aber das macht nix." + } + + for key, value in assertions.items(): + assert mistletoe.markdown(key, FiftyOhmHtmlRenderer) == paragraph(value) diff --git a/test/test_references.py b/test/test_references.py new file mode 100644 index 0000000..80dff9d --- /dev/null +++ b/test/test_references.py @@ -0,0 +1,17 @@ +import mistletoe + +from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer +from test.util import paragraph + + +def test_references_html(): + # Right now this tests only the default values. + # The correct behavior can be tested when the information is added to the renderer class + assertions = { + "[ref:e_oszilloskop_bildschirmfoto_sinus]": '0', + "[ref:n_rst_r]": '0', + "[ref:Uuuuuh]": '0', # Test the default parameters + } + + for key, value in assertions.items(): + assert mistletoe.markdown(key, FiftyOhmHtmlRenderer) == paragraph(value) diff --git a/test/test_unit.py b/test/test_unit.py new file mode 100644 index 0000000..eb736cd --- /dev/null +++ b/test/test_unit.py @@ -0,0 +1,45 @@ +import mistletoe +import pytest + +from renderer.fifty_ohm_html_renderer import FiftyOhmHtmlRenderer + +from .util import paragraph + + +@pytest.mark.html +def test_basic_units_html(): + """Test basic units: Insert non-breaking, smaller space.""" + assertions = { + "1 V": "1 V", + "0,12 V": "0,12 V", + "0,75 kW": "0,75 kW", + "33 A": "33 A", + "1 Ah": "1 Ah", # Ah can be a tricky case if the RegEx is wrong, as A might match before Ah, thus rendering the output with an "A" unit + } + + for input, output in assertions.items(): + assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output) + + +@pytest.mark.html +def test_translated_units_html(): + """Test translated units: Replace Ohm with Ω.""" + assertions = { + "50 Ohm": "50 Ω", + "100 mOhm": "100 mΩ", + } + + for input, output in assertions.items(): + assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output) + + +@pytest.mark.html +def test_special_units_html(): + """Test special units: Don't put space before ° and %.""" + assertions = { + "100%": "100%", + "42,24°": "42,24°", + } + + for input, output in assertions.items(): + assert mistletoe.markdown(input, FiftyOhmHtmlRenderer) == paragraph(output) diff --git a/test/util.py b/test/util.py index f30aa79..a86b9eb 100644 --- a/test/util.py +++ b/test/util.py @@ -1,2 +1,2 @@ -def paragraph(text) : - return f'

{text}

\n' \ No newline at end of file +def paragraph(text): + return f"

{text}

\n"