From 6b60c313be1ff589e26f0769845e6be799faa7e6 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Wed, 19 Jul 2023 23:25:06 +0200 Subject: [PATCH] =?UTF-8?q?Velmi=20velmi=20nedod=C4=9Blan=C3=A9=20generov?= =?UTF-8?q?=C3=A1n=C3=AD=20pandoc-like=20LaTeXu,=20see=20#22.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/formatitko/html_generator.py | 7 +-- src/formatitko/latex_generator.py | 95 ++++++++++++++++++++++++++++++ src/formatitko/output_generator.py | 7 ++- 3 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 src/formatitko/latex_generator.py diff --git a/src/formatitko/html_generator.py b/src/formatitko/html_generator.py index ec11a87..31399f5 100644 --- a/src/formatitko/html_generator.py +++ b/src/formatitko/html_generator.py @@ -32,7 +32,7 @@ class HTMLGenerator(OutputGenerator): return super().generate(e) - def htmlescapespecialchars(self, text: str) -> str: + def escape_special_chars(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) @@ -44,7 +44,7 @@ class HTMLGenerator(OutputGenerator): 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)}\"") + words.append(f"{key}=\"{self.escape_special_chars(value)}\"") return "<" + " ".join(words) + ">" def etag(self, tag: str, attributes: Dict[str,str]={}) -> str: @@ -91,9 +91,6 @@ class HTMLGenerator(OutputGenerator): 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? diff --git a/src/formatitko/latex_generator.py b/src/formatitko/latex_generator.py new file mode 100644 index 0000000..34f680c --- /dev/null +++ b/src/formatitko/latex_generator.py @@ -0,0 +1,95 @@ +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, Dict + +from .whitespace import NBSP +from .transform import FQuoted +from .context import Group +from .output_generator import OutputGenerator +from .images import ImageProcessor + +import re + +class LaTeXGenerator(OutputGenerator): + def __init__(self, output_file, imageProcessor: ImageProcessor, *args, **kwargs): + self.imageProcessor = imageProcessor + super().__init__(outpout_file, *args, **kwargs) + + def generate(self, e: Union[Element, ListContainer]): + if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "tex": + return + super().generate(e) + + def escape_special_chars(self, text: str) -> str: + text = re.sub(re.compile(r"&"), "\\&", text) + text = re.sub(re.compile(r"%"), "\\%", text) + text = re.sub(re.compile(r"\$"), "\\$", text) + text = re.sub(re.compile(r"#"), "\\#", text) + text = re.sub(re.compile(r"_"), "\\_", text) + text = re.sub(re.compile(r"\{"), "\\{", text) + text = re.sub(re.compile(r"\}"), "\\}", text) + text = re.sub(re.compile(r"~"), "\\textasciitilde{}", text) + text = re.sub(re.compile(r"\^"), "\\textasciicircum{}", text) + text = re.sub(re.compile(r"\\"), "\\textbackslash{}", text) + text = re.sub(re.compile(r" "), "~", text) # We use unicode no-break spaces to force nbsp in output + return text + + def stag(self, tag: str, attributes: Dict[str,str]={}) -> str: + return "\\" + tag + "{" + + def etag(self, tag: str, attributes: Dict[str,str]={}) -> str: + return "}" + + def ntag(self, tag: str, attributes: Dict[str,str]={}) -> str: + return "\\" + tag + "{}" + + + + + + def generate_NBSP(self, e: NBSP): + self.write("~") + + def generate_Null(self, e: Null): + pass + + def generate_LineBreak(self, e: LineBreak): + self.write("\\\\") + self.endln() + + def generate_Para(self, e: Para): + self.generate(e) + self.endln() + + def generate_Header(self, e: Header): + tag = { + 1: "section", + 2: "subsection", + 3: "subsubsection", + 4: "paragraph", + 5: "subparagraph", + 6: "textbf" + } + self.generate_simple_block_tag(e, tag[e.level]) + + def generate_HorizontalRule(self, e: HorizontalRule): + self.writeln("\\begin{center}\\rule{0.5\\linewidth}{0.5pt}\\end{center}") + + # These are also disabled in pandoc so they shouldn't appear in the AST at all. + def generate_Citation(self, e: Citation): + self.writeln("% FIXME: Citations not implemented") + + def generate_Cite(self, e: Cite): + self.writeln("% FIXME: Cites not implemented") + + def generate_Definition(self, e: Definition): + self.writeln("% FIXME: Definitions not implemented") + + def generate_DefinitionItem(self, e: DefinitionItem): + self.writeln("% FIXME: DefinitionItems not implemented") + + def generate_DefinitionList(self, e: DefinitionList): + self.writeln("% FIXME: DefinitionLists not implemented") + diff --git a/src/formatitko/output_generator.py b/src/formatitko/output_generator.py index 562ece1..7566e02 100644 --- a/src/formatitko/output_generator.py +++ b/src/formatitko/output_generator.py @@ -8,6 +8,8 @@ from .whitespace import NBSP from .transform import FQuoted from .context import Group +import re + class UnknownElementError(Exception): "An unknown Element has been passed to the Output_generator, probably because panflute introduced a new one." pass @@ -19,6 +21,9 @@ class OutputGenerator: self.indent_level = initial_indent_level self._at_start_of_line = True + def escape_special_chars(self, text: str) -> str: + return text + def indent(self) -> str: return self.indent_str*self.indent_level @@ -118,7 +123,7 @@ class OutputGenerator: }[type(e)](e) def generate_Str(self, e: Str): - self.write(e.text) + self.write(self.escape_special_chars(e.text)) def generate_Space(self, e: Space): self.write(" ")