diff --git a/src/formatitko/formatitko.py b/src/formatitko/formatitko.py index 913d36a..c0ec74d 100755 --- a/src/formatitko/formatitko.py +++ b/src/formatitko/formatitko.py @@ -12,6 +12,7 @@ from .html import html from .tex import tex from .images import ImageProcessor from .output_generator import Output_generator +from .html_generator import HTML_generator from .mj_show import show diff --git a/src/formatitko/html_generator.py b/src/formatitko/html_generator.py new file mode 100644 index 0000000..ee5aa5c --- /dev/null +++ b/src/formatitko/html_generator.py @@ -0,0 +1,299 @@ +from panflute import Cite, Emph, Image, LineBreak, Link, Math, Note, RawInline, SmallCaps, Str, Strikeout, Subscript, Superscript, Underline +from panflute import BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Header, HorizontalRule, LineBlock, LineItem, ListItem, Null, OrderedList, Para, Plain, RawBlock, TableBody, TableFoot, TableHead +from panflute import TableRow, TableCell, Caption, Doc +from panflute import ListContainer, Element +from typing import Union, Dict + +import re +import os +import io + +from pygments import highlight +from pygments.lexers import get_lexer_by_name +from pygments.formatters import HtmlFormatter +from pygments.util import ClassNotFound + +from .whitespace import NBSP +from .context import Group +from .output_generator import Output_generator +from .katex import KatexClient +from .images import ImageProcessor +from .util import inlinify + +class HTML_generator(Output_generator): + def __init__(self, output_file, katex_client: KatexClient, image_processor:ImageProcessor, *args, **kwargs): + self.katex_client = katex_client + self.image_processor = image_processor + super().__init__(output_file, *args, **kwargs) + + def generate(self, e: Union[Element, ListContainer]): + if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "html": + return + super().generate(e) + + def htmlescapespecialchars(self, text: str) -> str: + text = re.sub(re.compile(r"&"), "&", text) + text = re.sub(re.compile(r"<"), "<", text) + text = re.sub(re.compile(r">"), "&rt;", text) + text = re.sub(re.compile(r"\""), """, text) + text = re.sub(re.compile(r"'"), "'", text) + # text = re.sub(re.compile(r" '), " ", text) # Don't replace no-break spaces with HTML escapes, because we trust unicode? + return text + + def stag(self, tag: str, attributes: Dict[str,str]={}) -> str: + words = [tag] + for key, value in attributes.items(): + words.append(f"{key}=\"{self.htmlescapespecialchars(value)}\"") + return "<" + " ".join(words) + ">" + + def etag(self, tag: str, attributes: Dict[str,str]={}) -> str: + return "" + + def ntag(self, tag: str, attributes: Dict[str,str]={}) -> str: + return self.stag(tag, attributes) + + def tagname(self, e) -> str: + if isinstance(e, Header): + return "h" + str(e.level) + try: + return { + BulletList: "ul", + Doc: "main", + Emph: "em", + Caption: "figcaption", + Para: "p", + LineBlock: "p", + LineBreak: "br", + Link: "a", + ListItem: "li", + HorizontalRule: "hr", + OrderedList: "ol", + SmallCaps: "span", + Strikeout: "strike", + Subscript: "sub", + Superscript: "sup", + Underline: "u", + TableBody: "tbody", + TableHead: "thead", + TableFoot: "tfoot", + TableRow: "tr", + TableCell: "td", + }[type(e)] + except KeyError: + return type(e).__name__.lower() + + def common_attributes(self, e) -> Dict[str,str]: + attributes = {} + if hasattr(e, "identifier") and e.identifier != "": + attributes["id"] = e.identifier + if hasattr(e, "classes") and len(e.classes) != 0: + attributes["class"] = " ".join(e.classes) + return attributes + + def generate_Str(self, e: Str): + self.write(self.htmlescapespecialchars(e.text)) + + def generate_NBSP(self, e: NBSP): + self.write(" ") # Unicode no-break space, because we trust unicode? + + def generate_Null(self, e: Null): + pass + + def generate_Doc(self, e: Doc): + formatter = HtmlFormatter(style=e.get_metadata("highlight-style") if e.get_metadata("highlight-style") is not None else "default") + self.writeln("") + self.generate_simple_block_tag(e, "main", self.common_attributes(e)) + + def generate_CodeBlock(self, e: CodeBlock): + if e.classes and len(e.classes) > 0 and (e.attributes["highlight"] == True or e.attributes["highlight"] == 'True'): + # Syntax highlighting using pygments + for cl in e.classes: + try: + lexer = get_lexer_by_name(cl) + except ClassNotFound: + continue + break + else: + lexer = None + print(f"WARN: Syntax highligher does not have lexer for element with these classes: {e.classes}") + formatter = HtmlFormatter(style=e.attributes["style"]) + result = highlight(e.text, lexer, formatter) + self.writeraw(result) + else: + self.generate_raw_block_tag(e, "pre", self.common_attributes(e)) + + def generate_Image(self, e: Image): + url = e.url + + # Attributes → image processor args + additional_args = {} + if "file-width" in e.attributes: + additional_args["width"] = int(e.attributes["file-width"]) + if "file-height" in e.attributes: + additional_args["height"] = int(e.attributes["file-height"]) + if "file-quality" in e.attributes: + additional_args["quality"] = int(e.attributes["file-quality"]) + if "file-dpi" in e.attributes: + additional_args["dpi"] = int(e.attributes["file-dpi"]) + + # The directory of the current file, will also look for images there. + source_dir = e.attributes["source_dir"] + + _, ext = os.path.splitext(url) + ext = ext[1:] + + # Conversions between various formats. + if ext in ["svg", "png", "jpeg", "gif"]: + # Even supported elements have to be 'converted' because the + # processing contains finding and moving them to the output + # directory. + url = self.image_processor.process_image(url, ext, source_dir, **additional_args) + elif ext in ["pdf", "epdf"]: + if not "dpi" in additional_args: + additional_args["dpi"] = 300 + url = self.image_processor.process_image(url, "png", source_dir, **additional_args) + elif ext in ["jpg"]: + url = self.image_processor.process_image(url, "jpeg", source_dir, **additional_args) + else: + url = self.image_processor.process_image(url, "png", source_dir, **additional_args) + + # Srcset generation - multiple alternative sizes of images browsers can + # choose from. + _, ext = os.path.splitext(url) + ext = ext[1:] + srcset = [] + if ext in ["png", "jpeg"] and (not "no-srcset" in e.attributes or e.attributes["no-srcset"] == False or e.attributes["no-srcset"] == 'False'): + # This is inspired by @vojta001's blogPhoto shortcode he made for + # patek.cz: + # https://gitlab.com/patek-devs/patek.cz/-/blob/master/themes/patek/layouts/shortcodes/blogPhoto.html + width, height = self.image_processor.get_image_size(url, [self.image_processor.public_dir]) + sizes = [(640, 360, 85), (1280, 720, 85), (1920, 1080, 90)] # (widht, height, quality) + for size in sizes: + if width <= size[0] and height <= size[1]: + srcset.append((f'{self.image_processor.web_path}/{url}', f'{width}w')) + break + quality = size[2] if ext == "jpeg" else None + srcset.append((f'{self.image_processor.web_path}/{self.image_processor.process_image(url, ext, self.image_processor.public_dir, width=size[0], height=size[1], quality=quality)}', f'{size[0]}w')) + + url = self.image_processor.web_path + "/" + url + + attributes = self.common_attributes(e) + if "width" in e.attributes: + attributes["width"] = e.attributes["width"] + + if e.title: + attributes["alt"] = e.title + else: + fake_out = io.StringIO() + HTML_generator(fake_out, self.katex_client, self.image_processor).generate(e.content) + attributes["alt"] = fake_out.getvalue() + + if len(srcset) != 0: + attributes["src"] = srcset[-1][0] + attributes["srcset"] = ", ".join([" ".join(src) for src in srcset]) + else: + attributes["src"] = url + + img = RawInline(self.ntag("img", attributes)) + link = Link(img, url=url) + + self.generate(link) + + def generate_Group(self, e: Group): + self.katex_client.begingroup() + self.generate(e.content) + self.katex_client.endgroup() + + def generate_Plain(self, e: Plain): + self.generate(e.content) + + def generate_LineItem(self, e: LineItem): + self.generate(e.content) + self.write("
") + self.endln() + + # Footnotes are placed into parentheses. (And not footnotes (This is how KSP did it before)) + def generate_Note(self, e: Note): + inline = inlinify(e) + tag = self.tagname(e) + if inline is not None: + self.write(self.stag(tag)+" (") + self.generate(inline) + self.write(") "+self.etag(tag)) + else: + self.writeln(self.stag(tag) + "(") + self.iup() + self.generate(e.content) + self.ido() + self.writeln(self.etag(tag) + ")") + + def generate_Math(self, e: Math): + formats = { + "DisplayMath": True, + "InlineMath": False + } + self.writeln(self.katex_client.render(e.text, {"displayMode": formats[e.format]})) + + def generate_RawInline(self, e: RawInline): + if e.format == "html": + self.write(e.text) + + def generate_RawBlock(self, e: RawBlock): + if e.format == "html": + self.writeraw(e.text) + + def generate_Link(self, e: Link): + attributes = {} + attributes["href"] = e.url + if e.title: + attributes["title"] = e.title + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e) | attributes) + + def generate_OrderedList(self, e: OrderedList): + attributes = {} + if e.start and e.start != 1: + attributes["start"] = str(e.start) + html_styles = { + "Decimal": "1", + "LowerRoman": "i", + "UpperRoman:": "I", + "LowerAlpha": "a", + "UpperAlpha": "A" + } + if e.style and e.style != "DefaultStyle": + attributes["type"] = html_styles[e.style] + # FIXME: Delimeter styles: 1. 1) (1) + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e) | attributes) + + def generate_TableCell(self, e: TableCell): + attributes = self.common_attributes(e) + if e.colspan != 1: + attributes["colspan"] = str(e.colspan) + if e.rowspan != 1: + attributes["rowspan"] = str(e.rowspan) + aligns = { + "AlignLeft": "left", + "AlignRight": "right", + "AlignCenter": "center" + } + if e.alignment and e.alignment != "AlignDefault": + attributes["style"] = attributes.get("style", "")+f"text-align: {aligns[e.alignment]};" + self.generate_simple_block_tag(e, self.tagname(e), attributes) + + # These are also disabled in pandoc so they shouldn't appear in the AST at all. + def generate_Citation(self, e: Citation): + self.writeln("") + + def generate_Cite(self, e: Cite): + self.writeln("") + + def generate_Definition(self, e: Definition): + self.writeln("") + + def generate_DefinitionItem(self, e: DefinitionItem): + self.writeln("") + + def generate_DefinitionList(self, e: DefinitionList): + self.writeln("") diff --git a/src/formatitko/output_generator.py b/src/formatitko/output_generator.py index 4f93311..6b39320 100644 --- a/src/formatitko/output_generator.py +++ b/src/formatitko/output_generator.py @@ -2,7 +2,7 @@ from panflute import Element, ListContainer, Inline, Block from panflute import Cite, Code, Emph, Image, LineBreak, Link, Math, Note, Quoted, RawInline, SmallCaps, SoftBreak, Space, Span, Str, Strikeout, Strong, Subscript, Superscript, Underline from panflute import BlockQuote, BulletList, Citation, CodeBlock, Definition, DefinitionItem, DefinitionList, Div, Figure, Header, HorizontalRule, LineBlock, LineItem, ListItem, MetaBlocks, MetaBool, MetaInlines, MetaList, MetaMap, MetaString, Null, OrderedList, Para, Plain, RawBlock, Table, TableBody, TableFoot, TableHead from panflute import TableRow, TableCell, Caption, Doc -from typing import Union +from typing import Union, Dict from .whitespace import NBSP from .transform import FQuoted @@ -52,15 +52,21 @@ class Output_generator: self.output_file.write("\n") self._at_start_of_line = True - def stag(self, tag: str) -> str: + def stag(self, tag: str, attributes: Dict[str,str]={}) -> str: return tag - def etag(self, tag: str) -> str: + def etag(self, tag: str, attributes: Dict[str,str]={}) -> str: return "/" + tag - def ntag(self, tag: str) -> str: + def ntag(self, tag: str, attributes: Dict[str,str]={}) -> str: return "/" + tag + "/" + def tagname(self, e) -> str: + return type(e).__name__ + + def common_attributes(self, e) -> Dict[str,str]: + return {} + def generate(self, e: Union[Element, ListContainer]): if isinstance(e, ListContainer): self.generate_ListContainer(e) @@ -111,81 +117,108 @@ class Output_generator: FQuoted: self.generate_FQuoted }[type(e)](e) - def generate_Str(self, e): + def generate_Str(self, e: Str): self.write(e.text) - def generate_Space(self, e): + def generate_Space(self, e: Space): self.write(" ") - def generate_NBSP(self, e): + def generate_NBSP(self, e: NBSP): self.write("~") - def generate_SoftBreak(self, e): + def generate_SoftBreak(self, e: SoftBreak): self.endln() - def generate_Cite(self, e): - self.generate_simple_inline_tag(e, "Cite") + def generate_FQuoted(self, e: FQuoted): + if e.style == "cs": + if e.quote_type == "SingleQuote": + self.write("‚") + self.generate(e.content) + self.write("‘") + elif e.quote_type == "DoubleQuote": + self.write("„") + self.generate(e.content) + self.write("“") + elif e.style == "en": + if e.quote_type == "SingleQuote": + self.write("‘") + self.generate(e.content) + self.write("’") + elif e.quote_type == "DoubleQuote": + self.write("“") + self.generate(e.content) + self.write("”") + else: + if e.quote_type == "SingleQuote": + self.write("'") + self.generate(e.content) + self.write("'") + elif e.quote_type == "DoubleQuote": + self.write("\"") + self.generate(e.content) + self.write("\"") + else: + self.write("\"") + self.generate(e.content) + self.write("\"") - def generate_Emph(self, e): - self.generate_simple_inline_tag(e, "Emph") + def generate_Cite(self, e: Cite): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Image(self, e): - self.generate_simple_inline_tag(e, "Image") + def generate_Emph(self, e: Emph): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_LineBreak(self, e): - self.generate_simple_inline_tag(e, "LineBreak") + def generate_Image(self, e: Image): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Link(self, e): - self.generate_simple_inline_tag(e, "Link") + def generate_Link(self, e: Link): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Note(self, e): - self.generate_simple_inline_tag(e, "Note") + def generate_Note(self, e: Note): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Quoted(self, e): - self.generate_simple_inline_tag(e, "Quoted") + def generate_Quoted(self, e: Quoted): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_SmallCaps(self, e): - self.generate_simple_inline_tag(e, "SmallCaps") + def generate_SmallCaps(self, e: SmallCaps): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Span(self, e): - self.generate_simple_inline_tag(e, "Span") + def generate_Span(self, e: Span): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Strikeout(self, e): - self.generate_simple_inline_tag(e, "Strikeout") + def generate_Strikeout(self, e: Strikeout): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Strong(self, e): - self.generate_simple_inline_tag(e, "Strong") + def generate_Strong(self, e: Strong): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Subscript(self, e): - self.generate_simple_inline_tag(e, "Subscript") + def generate_Subscript(self, e: Subscript): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Superscript(self, e): - self.generate_simple_inline_tag(e, "Superscript") + def generate_Superscript(self, e: Superscript): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Underline(self, e): - self.generate_simple_inline_tag(e, "Underline") + def generate_Underline(self, e: Underline): + self.generate_simple_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_FQuoted(self, e): - self.generate_simple_inline_tag(e, "FQuoted") - - def generate_simple_inline_tag(self, e, tag: str): - self.write(self.stag(tag)+" ") + def generate_simple_inline_tag(self, e, tag: str, attributes: Dict[str,str]={}): + self.write(self.stag(tag, attributes)) self.generate(e.content) - self.write(" "+self.etag(tag)) + self.write(self.etag(tag)) - def generate_Math(self, e): - self.generate_raw_inline_tag(e, "Math") + def generate_Math(self, e: Math): + self.generate_raw_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Code(self, e): - self.generate_raw_inline_tag(e, "Code") + def generate_Code(self, e: Code): + self.generate_raw_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_RawInline(self, e): - self.generate_raw_inline_tag(e, "RawInline") + def generate_RawInline(self, e: RawInline): + self.generate_raw_inline_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_raw_inline_tag(self, e, tag: str): - self.write(self.stag(tag)+" ") + def generate_raw_inline_tag(self, e, tag: str, attributes: Dict[str,str]={}): + self.write(self.stag(tag, attributes)) self.write(e.text) - self.write(" "+self.etag(tag)) + self.write(self.etag(tag)) def generate_Block(self, e: Block): { @@ -219,119 +252,135 @@ class Output_generator: Group: self.generate_Group }[type(e)](e) - def generate_BlockQuote(self, e): - self.generate_simple_block_tag(e, "BlockQuote") - - def generate_BulletList(self, e): - self.generate_simple_block_tag(e, "BulletList") + def generate_BlockQuote(self, e: BlockQuote): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Citation(self, e): - self.generate_simple_block_tag(e, "Citation") + def generate_BulletList(self, e: BulletList): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Definition(self, e): - self.generate_simple_block_tag(e, "Definition") + def generate_Citation(self, e: Citation): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_DefinitionItem(self, e): - self.generate_simple_block_tag(e, "DefinitionItem") + def generate_Definition(self, e: Definition): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_DefinitionList(self, e): - self.generate_simple_block_tag(e, "DefinitionList") + def generate_DefinitionItem(self, e: DefinitionItem): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Div(self, e): - self.generate_simple_block_tag(e, "Div") + def generate_DefinitionList(self, e: DefinitionList): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Figure(self, e): - self.generate_simple_block_tag(e, "Figure") + def generate_Div(self, e: Div): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Header(self, e): - self.generate_simple_block_tag(e, "Header") + def generate_Header(self, e: Header): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_LineBlock(self, e): - self.generate_simple_block_tag(e, "LineBlock") + def generate_LineBlock(self, e: LineBlock): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_LineItem(self, e): - self.generate_simple_block_tag(e, "LineItem") + def generate_LineItem(self, e: LineItem): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_ListItem(self, e): - self.generate_simple_block_tag(e, "ListItem") + def generate_ListItem(self, e: ListItem): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_MetaBlocks(self, e): - self.generate_simple_block_tag(e, "MetaBlocks") + def generate_MetaBlocks(self, e: MetaBlocks): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_MetaBool(self, e): - self.generate_simple_block_tag(e, "MetaBool") + def generate_MetaBool(self, e: MetaBool): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_MetaInlines(self, e): - self.generate_simple_block_tag(e, "MetaInlines") + def generate_MetaInlines(self, e: MetaInlines): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_MetaList(self, e): - self.generate_simple_block_tag(e, "MetaList") + def generate_MetaList(self, e: MetaList): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_MetaMap(self, e): - self.generate_simple_block_tag(e, "MetaMap") + def generate_MetaMap(self, e: MetaMap): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_MetaString(self, e): - self.generate_simple_block_tag(e, "MetaString") + def generate_MetaString(self, e: MetaString): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_OrderedList(self, e): - self.generate_simple_block_tag(e, "OrderedList") + def generate_OrderedList(self, e: OrderedList): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Para(self, e): - self.generate_simple_block_tag(e, "Para") + def generate_Para(self, e: Para): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Plain(self, e): - self.generate_simple_block_tag(e, "Plain") + def generate_Plain(self, e: Plain): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Caption(self, e): - self.generate_simple_block_tag(e, "Caption") + def generate_Caption(self, e: Caption): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Table(self, e): - self.generate_simple_block_tag(e, "Table") + def generate_TableBody(self, e: TableBody): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_TableBody(self, e): - self.generate_simple_block_tag(e, "TableBody") + def generate_TableCell(self, e: TableCell): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_TableCell(self, e): - self.generate_simple_block_tag(e, "TableCell") + def generate_TableFoot(self, e: TableFoot): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_TableFoot(self, e): - self.generate_simple_block_tag(e, "TableFoot") + def generate_TableHead(self, e: TableHead): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_TableHead(self, e): - self.generate_simple_block_tag(e, "TableHead") + def generate_TableRow(self, e: TableRow): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_TableRow(self, e): - self.generate_simple_block_tag(e, "TableRow") - - def generate_Doc(self, e): - self.generate_simple_block_tag(e, "Doc") + def generate_Doc(self, e: Doc): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_Group(self, e): - self.generate_simple_block_tag(e, "Group") + def generate_Group(self, e: Group): + self.generate_simple_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_simple_block_tag(self, e, tag: str): - self.writeln(self.stag(tag)) + def generate_simple_block_tag(self, e, tag: str, attributes: Dict[str,str]={}): + self.writeln(self.stag(tag, attributes)) self.iup() self.generate(e.content) self.ido() self.writeln(self.etag(tag)) - def generate_Null(self, e): - self.generate_empty_block_tag(e, "Null") + def generate_Table(self, e: Table): + tag = self.tagname(e) + self.writeln(self.stag(tag, self.common_attributes(e))) + self.iup() + self.generate(e.head) + self.generate(e.content) + self.generate(e.foot) + self.ido() + self.writeln(self.etag(tag)) + + def generate_Figure(self, e: Figure): + tag = self.tagname(e) + self.writeln(self.stag(tag, self.common_attributes(e))) + self.iup() + self.generate(e.content) + self.generate(e.caption) + self.ido() + self.writeln(self.etag(tag)) + + def generate_Null(self, e: Null): + self.generate_empty_block_tag(e, self.tagname(e), self.common_attributes(e)) + + def generate_HorizontalRule(self, e: HorizontalRule): + self.generate_empty_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_HorizontalRule(self, e): - self.generate_empty_block_tag(e, "HorizontalRule") + def generate_LineBreak(self, e: LineBreak): + self.generate_empty_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_empty_block_tag(self, e, tag: str): - self.writeln(self.ntag(tag)) + def generate_empty_block_tag(self, e, tag: str, attributes: Dict[str,str]={}): + self.writeln(self.ntag(tag, attributes)) - def generate_CodeBlock(self, e): - self.generate_raw_block_tag(e, "CodeBlock") + def generate_CodeBlock(self, e: CodeBlock): + self.generate_raw_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_RawBlock(self, e): - self.generate_raw_block_tag(e, "RawBlock") + def generate_RawBlock(self, e: RawBlock): + self.generate_raw_block_tag(e, self.tagname(e), self.common_attributes(e)) - def generate_raw_block_tag(self, e, tag: str): - self.writeln(self.stag(tag)+"\n") + def generate_raw_block_tag(self, e, tag: str, attributes: Dict[str,str]={}): + self.writeln(self.stag(tag, attributes)) self.writeraw(e.text) self.writeln(self.etag(tag))