from panflute import * import os from whitespace import NBSP from transform import FQuoted from util import inlinify from group import Group from images import ImageProcessor # Heavily inspired by: git://git.ucw.cz/labsconf2022.git def tex(e: Element, i: ImageProcessor, indent_level: int=0, indent_str: str="\t") -> str: if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "tex": return "" if isinstance(e, ListContainer): return ''.join([tex(child, i, indent_level, indent_str) for child in e]) content_foot = "" content_head = "" arguments = "" open = "{" close = "}" tag = e.tag.lower() tags = { Header: "h"+chr(64 + e.level) if hasattr(e, "level") else "", } if type(e) in tags: tag = tags[type(e)] not_implemented = { Citation: True, Cite: True, Definition: True, DefinitionItem: True, DefinitionList: True } if type(e) in not_implemented: return f'% FIXME: {type(e)}s not implemented \n' simple_string = { NBSP: "~", Space: " ", Null: "", LineBreak: f"\\\\", SoftBreak: f" ", HorizontalRule: "\\hr\n\n" } if type(e) in simple_string: return simple_string[type(e)] if isinstance(e, Str): return e.text.replace(" ", "~").replace(" ", "~") if isinstance(e, Para): return tex(e.content, i, 0, "")+"\n\n" if isinstance(e, FQuoted): if e.style == "cs": if e.quote_type == "SingleQuote": return f'‚{tex(e.content, i, 0, "")}‘' elif e.quote_type == "DoubleQuote": return f'„{tex(e.content, i, 0, "")}“' elif e.style == "en": if e.quote_type == "SingleQuote": return f'‘{tex(e.content, i, 0, "")}’' elif e.quote_type == "DoubleQuote": return f'“{tex(e.content, i, 0, "")}”' else: if e.quote_type == "SingleQuote": return f'\'{tex(e.content, i, 0, "")}\'' elif e.quote_type == "DoubleQuote": return f'"{tex(e.content, i, 0, "")}"' else: return f'"{tex(e.content, i, 0, "")}"' if isinstance(e, BulletList): tag = "list" open = "" arguments = "{o}" close = "\\endlist" if isinstance(e, OrderedList): tag = "list" open = "" 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] arguments = f"{{{style}}}" close = "\\endlist" # FIXME: Starting number of list if isinstance(e, Image): url = e.url _, ext = os.path.splitext(url) ext = ext[1:] if ext in ["pdf", "png", "jpeg"]: url = i.process_image(url, ext, relative=False) elif ext in ["svg"]: url = i.process_image(url, "pdf", relative=False) elif ext in ["epdf"]: url = i.process_image(url, "pdf", relative=False) elif ext in ["jpg"]: url = i.process_image(url, "jpeg", relative=False) else: url = i.process_image(url, "pdf", relative=False) width = "" if "width" in e.attributes: width = e.attributes["width"] if e.attributes["width"][-1] == "%": width = str(int(e.attributes["width"][:-1])/100) + "\\hsize" width = "width " + width return f'\\image{{{width}}}{{{url}}}' if isinstance(e, Figure): return f'\\figure{{{tex(e.content, i, indent_level+1, indent_str)}}}{{{tex(e.caption, i, indent_level+1, indent_str)}}}\n\n' if isinstance(e, Caption): if inlinify(e) is not None: return f'\\caption{{{tex(e.content, i, 0, "")}}}' if isinstance(e, ListItem): tag = ":" if isinstance(e, Link): if len(e.content) == 1 and isinstance(e.content[0], Str) and e.content[0].text == e.url: tag = "url" else: tag = "linkurl" arguments = f'{{{e.url}}}' if isinstance(e, Math): if e.format == "DisplayMath": return f'$${e.text}$$\n' else: return f'${e.text}$' if isinstance(e, Note): tag = "fn" if inlinify(e) is not None: return f'\\fn{{{tex(inlinify(e), i, 0, "")}}}' if isinstance(e, Table): aligns = { "AlignLeft": "\\quad#\\quad\\hfil", "AlignRight": "\\quad\\hfil#\\quad", "AlignCenter": "\\quad\\hfil#\\hfil\\quad", "AlignDefault": "\\quad#\\quad\\hfil" } text = "\strut"+"&".join([aligns[col[0]] for col in e.colspec])+"\cr\n" text += tex(e.head.content, i, 0, "") text += "\\noalign{\\hrule}\n" text += tex(e.content[0].content, i, 0, "") text += "\\noalign{\\hrule}\n" text += tex(e.foot.content, i, 0, "") return "\\vskip1em\n\\halign{"+text+"}\n\\vskip1em\n" # FIXME: Implement rowspan if isinstance(e, TableRow): return "&".join([("\\multispan"+str(cell.colspan)+" " if cell.colspan > 1 else "")+tex(cell.content, i, 0, "") for cell in e.content])+"\cr\n" if isinstance(e, RawInline): if e.format == "tex": return e.text else: return "" if isinstance(e, RawBlock): if e.format == "tex": return f'{e.text}\n' else: return "" if isinstance(e, Span) or isinstance(e, Plain): return tex(e.content, i, 0, "") if isinstance(e, LineItem): return tex(e.content, i, 0, "") + ("\\\\\n" if e.next else "\n") if isinstance(e, LineBlock): return f'{tex(e.content, i, indent_level+1, indent_str)}\n' if isinstance(e, Group): tag = "begingroup" open = "" if "language" in e.metadata and e.metadata["language"] is not None: open = "\\language"+e.metadata["language"] close = "\\endgroup" if isinstance(e, Div): return f'{tex(e.content, i, indent_level+1, indent_str)}' if isinstance(e, Doc): return tex(e.content, i, indent_level, indent_str)+"\n\\bye" if isinstance(e, Inline): return f'\\{tag}{arguments}{open}{content_head}{tex(e.content, i, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}{close}' out_str = "" out_str = f"\\{tag}{arguments}{open}\n" out_str += content_head if hasattr(e, "_content"): out_str += tex(e.content, i, indent_level+1, indent_str) if hasattr(e, "text"): out_str += e.text out_str += f"{content_foot}\n{close}\n\n" return out_str