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 panflute import MetaValue from typing import Union import os from .output_generator import OutputGenerator from .images import ImageProcessor, ImageProcessorNamespaceSearcher from .whitespace import NBSP from .elements import FQuoted from .context import Group, InlineGroup, BlockGroup, Context from .util import inlinify class UCWTexGenerator(OutputGenerator): imageProcessor: ImageProcessor _bold: int _italic: int def __init__(self, output_file, imageProcessor: ImageProcessor, *args, **kwargs): self.imageProcessor = imageProcessor self._bold = 0 self._italic = 0 super().__init__(output_file, *args, **kwargs) def escape_special_chars(self, text: str) -> str: if '\\' in text: print("ESCAPE", text) out = "" for char in text: out += { '&': r"\&", '%': r"\%", '$': r"\$", '#': r"\#", '_': r"\_", '{': r"\{", '}': r"\}", '~': r"\textasciitilde{}", '^': r"\textasciicircum{}", '\\': r"\textbackslash{}", ' ': r"~", '​': r"", }.get(char, char) return out 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 writepar(self, text: str): self.ensure_empty(2) self.writeln(text) self.ensure_empty(2) def generate_Null(self, e: Null): pass def generate_LineBreak(self, e: LineBreak): self.write(r"\\") self.endln() def generate_Para(self, e: Para): self.ensure_empty(2) self.generate(e.content) self.ensure_empty(2) def generate_HorizontalRule(self, e: HorizontalRule): self.writepar(r"\vskip5pt\hrule\hfil\vskip5pt{}") def generate_Doc(self, e: Doc): self.writeln(r"\input formatitko.tex") self.generate(e.content) self.writeln(r"\bye") def get_language_macro(self, lang: str): if lang == "cs": return r"\uselanguage{czech}\frenchspacing\lefthyphenmin=2\righthyphenmin=2{}" elif lang == "sk": return r"\uselanguage{slovak}\frenchspacing\lefthyphenmin=2\righthyphenmin=2{}" elif lang == "en": return r"\uselanguage{english}\nofrenchspacing\lefthyphenmin=2\righthyphenmin=2{}" else: return "" def generate_InlineGroup(self, e: InlineGroup): self.write(r"{") self.write(self.get_language_macro(self.context.get_metadata("lang"))) self.generate(e.content) self.write(r"}") def generate_BlockGroup(self, e: BlockGroup): self.writeln(r"\begingroup") self.indent_more() self.writeln(self.get_language_macro(self.context.get_metadata("lang"))) self.generate(e.content) self.indent_less() self.writeln(r"\endgroup") def generate_Header(self, e: Header): self.ensure_empty(2) self.write("\\"+"sub"*(e.level-1)+"section{"+".".join(map(str, e.attributes["number"]))+"}{") self.generate(e.content) self.write(r"}") self.ensure_empty(2) def generate_Image(self, e: Image): url = e.url additional_args = self.get_image_processor_args(e.attributes) additional_args["context"] = self.context # The directory of the current file relative to the current working directory source_dir = self.context.dir # The directory of the current file relative to the md file we were called on rel_dir = self.context.rel_dir searcher = self.imageProcessor.get_searcher_by_path(url, rel_dir, source_dir) url = self.imageProcessor.get_path_without_namespace(url) _, ext = os.path.splitext(url) ext = ext[1:] # Conversions between various formats. if ext in ["pdf", "png", "jpeg"]: # Even supported elements have to be 'converted' because the # processing contains finding and moving them to the cache # directory. url = self.imageProcessor.process_image(url, ext, searcher, **additional_args) elif ext in ["svg"]: # FIXME url = self.imageProcessor.process_image(url, "pdf", searcher, **additional_args) elif ext in ["epdf"]: url = self.imageProcessor.process_image(url, "pdf", searcher, **additional_args) elif ext in ["jpg"]: url = self.imageProcessor.process_image(url, "jpeg", searcher, **additional_args) else: url = self.imageProcessor.process_image(url, "pdf", searcher, **additional_args) url = searcher.get_cache_searcher().find_image(url) width = "" if "width" in e.attributes: width = e.attributes["width"] # 50% → 0.5\hsize if e.attributes["width"][-1] == "%": width = str(int(e.attributes["width"][:-1])/100) + "\\hsize" width = "width " + width if isinstance(e.parent.parent, Figure): self.writeln(f'\\putimage{{{width}}}{{{url}}}') else: self.writepar(f'\\putimage{{{width}}}{{{url}}}') def generate_Code(self, e: Code): self.write(r"\verb`") self.write(e.text) self.write(r"`") def generate_Figure(self, e: Figure): self.ensure_empty(2) self.writeln(r"\float{") self.indent_more() self.generate(e.content) self.indent_less() self.writeln(r"}{") self.indent_more() if 'number' in e.attributes: self.writeln(f"Obrázek {e.attributes['number']}:") self.generate(e.caption) if 'number' in e.attributes: self.writeln(r"\addtoc\tocpicture{"+str(e.attributes['number'])+"}{}{") self.indent_more() self.generate(e.caption.content) self.indent_less() self.writeln("}") self.indent_less() self.writeln(r"}{}") self.ensure_empty(2) def generate_Emph(self, e: Emph): if self._bold > 0: self.write(r"{\bi{}") else: self.write(r"{\I{}") self._italic+=1 self.generate(e.content) self._italic-=1 self.write(r"}") def generate_Slanted(self, e: Emph): self.write(r"{\sl{}") self.generate(e.content) self.write(r"}") def generate_Strong(self, e: Strong): if self._italic > 0: self.write(r"{\bi{}") else: self.write(r"{\bf{}") self._bold+=1 self.generate(e.content) self._bold-=1 self.write(r"}") def generate_Caption(self, e: Caption): self.generate_Slanted(e) def generate_Math(self, e: Math): if e.format == "DisplayMath": self.ensure_empty(1) self.writeraw("$$") self.writeraw(e.text.strip()) self.writeraw("$$") self.ensure_empty(1) else: self.write("$") self.write(e.text) self.write("$") def generate_Note(self, e: Note): self.write(r"\fn{") self.generate(inlinify(e)) self.write(r"}") def generate_Table(self, e: Table): aligns = { "AlignLeft": r"\quad#\quad\hfil", "AlignRight": r"\quad\hfil#\quad", "AlignCenter": r"\quad\hfil#\hfil\quad", "AlignDefault": r"\quad#\quad\hfil" } self.writeln(r"\vskip1em") self.writeln(r"\halign{\strut"+"&".join([aligns[col[0]] for col in e.colspec])+r"\cr") self.indent_more() self.generate(e.head.content) self.writeln(r"\noalign{\hrule}") self.generate(e.content[0].content) self.writeln(r"\noalign{\hrule}") self.generate(e.foot.content) self.indent_less() self.writeln("}") self.writeln(r"\vskip1em") def generate_TableRow(self, e: TableRow): for cell in e.content: if cell.colspan > 1: self.write(r"\multispan"+str(cell.colspan)+"{} ") self.generate(cell.content) if cell.next: self.write(" & ") self.write(r"\cr") self.endln() def generate_RawInline(self, e: RawInline): if e.format == "tex": self.write(e.text) def generate_RawBlock(self, e: RawBlock): if e.format == "tex": self.writeraw(e.text) def generate_Plain(self, e: Plain): self.generate(e.content) def generate_Span(self, e: Span): self.generate(e.content) def generate_CodeBlock(self, e: CodeBlock): self.writeln(r"\verbatim{") self.writeraw(e.text) self.writeln(r"}") def generate_Div(self, e: Div): self.generate(e.content) def generate_LineBlock(self, e: LineBlock): self.writeln() self.generate(e.content) self.writeln() def generate_LineItem(self, e: LineItem): self.generate(e.content) if e.next: self.write(r"\\") self.endln() def generate_BulletList(self, e: BulletList): self.ensure_empty(2) self.writeln(r"\list{o}") self.indent_more() self.generate(e.content) self.indent_less() self.write(r"\endlist") self.ensure_empty(2) def generate_OrderedList(self, e: OrderedList): self.ensure_empty(2) styles = { "DefaultStyle": "n", "Decimal": "n", "LowerRoman": "i", "UpperRoman:": "I", "LowerAlpha": "a", "UpperAlpha": "A" } style = styles[e.style] delimiters = { "DefaultDelim": f"{style}.", "Period": f"{style}.", "OneParen": f"{style})", "TwoParens": f"({style})" } style = delimiters[e.delimiter] self.writeln(r"\list{"+style+r"}") self.indent_more() self.generate(e.content) self.indent_less() self.writeln(r"\endlist") self.ensure_empty(2) def generate_ListItem(self, e: ListItem): self.endln() self.write(r"\:") self.generate(e.content) self.endln() def generate_BlockQuote(self, e: BlockQuote): self.writeln(r"\blockquote{") self.indent_more() self.generate(e.content) self.indent_less() self.writeln(r"}") def generate_Link(self, e: Link): if len(e.content) == 0: if e.url.startswith('#'): obj = e.obj_map[e.url[1:]] self.write(str(obj.attributes["number"])) return if len(e.content) == 1 and isinstance(e.content[0], Str) and e.content[0].text == e.url: self.write(r"\url{") else: self.write(r"\linkurl{"+e.url+r"}{") self.generate(e.content) self.write(r"}") # } def generate_Subscript(self, e: Subscript): self.write(r"\subscript{") self.generate(e.content) self.write(r"}") def generate_Superscript(self, e: Superscript): self.write(r"\superscript{") self.generate(e.content) self.write(r"}") def generate_simple_tag(self, e: Union[Element, None] = None, tag: str = "", attributes: Union[dict[str, str], None] = None, content: Union[ListContainer, Element, list[Union[Element, ListContainer]], str, None] = None, inline: Union[bool, None] = None): print("dumbass: ", type(e)) # 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.generate(e.content) 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") def generate_Underline(self, e: Underline): self.writeln("% FIXME: Underlines not implemented") def generate_Strikeout(self, e: Strikeout): self.writeln("% FIXME: Strikeouts not implemented") def generate_SmallCaps(self, e: Strikeout): self.write(r"{\csc{}") self.generate(e.content) self.write(r"}")