Deprecated old versions of transform, html and tex generation.
This commit is contained in:
parent
c7dd1a2e95
commit
723038a2bd
3 changed files with 0 additions and 757 deletions
|
@ -1,311 +0,0 @@
|
|||
from panflute import *
|
||||
from pygments import highlight
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.util import ClassNotFound
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from .whitespace import NBSP
|
||||
from .elements import FQuoted
|
||||
from .katex import KatexClient
|
||||
from .util import inlinify
|
||||
from .context import Group
|
||||
from .images import ImageProcessor
|
||||
|
||||
import warnings
|
||||
warnings.warn("The html function has been deprecated, is left only for reference and will be removed in future commits. HTML_generator should be used in its place.", DeprecationWarning)
|
||||
|
||||
def html(e: Union[Element, ListContainer], k: KatexClient, i: ImageProcessor, indent_level: int=0, indent_str: str="\t") -> str:
|
||||
|
||||
warnings.warn("The html function has been deprecated, is left only for reference and will be removed in future commits. HTML_generator should be used in its place.", DeprecationWarning)
|
||||
|
||||
# `only` attribute which makes transformed elements appear only in tex
|
||||
# output or html output
|
||||
if hasattr(e, "attributes") and "only" in e.attributes and e.attributes["only"] != "html":
|
||||
return ""
|
||||
|
||||
if isinstance(e, ListContainer):
|
||||
return ''.join([html(child, k, i, indent_level, indent_str) for child in e])
|
||||
|
||||
# Bits from which the final element output is built at the end of this
|
||||
# function. Most elements override this by returning their own output.
|
||||
tag = e.tag.lower()
|
||||
attributes = ""
|
||||
content_foot = ""
|
||||
content_head = ""
|
||||
|
||||
if isinstance(e, Str):
|
||||
return e.text.replace(" ", " ")
|
||||
|
||||
# Most elements fit the general template at the end of the function, just
|
||||
# need their html tag specified.
|
||||
tags = {
|
||||
BulletList: "ul",
|
||||
Doc: "main",
|
||||
Emph: "em",
|
||||
Caption: "figcaption",
|
||||
Para: "p",
|
||||
Header: "h"+str(e.level) if isinstance(e, Header) else "",
|
||||
LineBlock: "p",
|
||||
ListItem: "li",
|
||||
SmallCaps: "span",
|
||||
Strikeout: "strike",
|
||||
Subscript: "sub",
|
||||
Superscript: "sup",
|
||||
Underline: "u",
|
||||
TableBody: "tbody",
|
||||
TableHead: "thead",
|
||||
TableFoot: "tfoot",
|
||||
TableRow: "tr",
|
||||
TableCell: "td",
|
||||
}
|
||||
if type(e) in tags:
|
||||
tag = tags[type(e)]
|
||||
|
||||
# These are also disabled in pandoc so they shouldn't appear in the AST at all.
|
||||
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 -->'
|
||||
|
||||
# Elements which can be represented by a simple string
|
||||
simple_string = {
|
||||
NBSP: " ",
|
||||
Space: " ",
|
||||
Null: "",
|
||||
LineBreak: f"\n{indent_level*indent_str}<br>\n{indent_level*indent_str}",
|
||||
SoftBreak: f" ",
|
||||
HorizontalRule: f"{indent_level*indent_str}<hr>\n"
|
||||
}
|
||||
if type(e) in simple_string:
|
||||
return simple_string[type(e)]
|
||||
|
||||
if hasattr(e, "identifier") and e.identifier != "":
|
||||
attributes += f' id="{e.identifier}"'
|
||||
|
||||
if hasattr(e, "classes") and len(e.classes) != 0:
|
||||
attributes += f' class="{" ".join(e.classes)}"'
|
||||
|
||||
# Attributes are only passed down manually, because we use them internally.
|
||||
# Maybe this should be a blocklist instead of an allowlist?
|
||||
|
||||
# Overriding elements with their own returns
|
||||
if isinstance(e, CodeBlock):
|
||||
if 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:
|
||||
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)
|
||||
return f'{result}'
|
||||
else:
|
||||
return f'<pre>{e.text}</pre>'
|
||||
|
||||
if isinstance(e, Doc):
|
||||
formatter = HtmlFormatter(style=e.get_metadata("highlight-style") if e.get_metadata("highlight-style") is not None else "default")
|
||||
content_head = f'<style>{formatter.get_style_defs(".highlight")}</style>'
|
||||
|
||||
if isinstance(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 = i.process_image(url, ext, source_dir, **additional_args)
|
||||
elif ext in ["pdf", "epdf"]:
|
||||
if not "dpi" in additional_args:
|
||||
additional_args["dpi"] = 300
|
||||
url = i.process_image(url, "png", source_dir, **additional_args)
|
||||
elif ext in ["jpg"]:
|
||||
url = i.process_image(url, "jpeg", source_dir, **additional_args)
|
||||
else:
|
||||
url = i.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 = i.get_image_size(url, [i.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'{i.web_path}/{url}', f'{width}w'))
|
||||
break
|
||||
quality = size[2] if ext == "jpeg" else None
|
||||
srcset.append((f'{i.web_path}/{i.process_image(url, ext, i.public_dir, width=size[0], height=size[1], quality=quality)}', f'{size[0]}w'))
|
||||
|
||||
url = i.web_path + "/" + url
|
||||
|
||||
attributes = f'{" style=width:"+e.attributes["width"] if "width" in e.attributes else ""} alt="{e.title or html(e.content, k, i, 0, "")}"'
|
||||
if len(srcset) != 0:
|
||||
return f'<a href="{url}"><img src="{srcset[-1][0]}" srcset="{", ".join([" ".join(src) for src in srcset])}"{attributes}></a>'
|
||||
else:
|
||||
return f'<img src="{url}"{attributes}>'
|
||||
|
||||
# See https://pandoc.org/MANUAL.html#line-blocks
|
||||
if isinstance(e, LineItem):
|
||||
return indent_level*indent_str + html(e.content, k, i) + "<br>\n"
|
||||
|
||||
# Footnotes are placed into parentheses. (And not footnotes (This is how KSP did it before me))
|
||||
if isinstance(e, Note):
|
||||
content_head = "("
|
||||
content_foot = ")"
|
||||
if inlinify(e) is not None:
|
||||
return f' <note>({html(inlinify(e), k, i, 0, "")})</note>'
|
||||
|
||||
if isinstance(e, FQuoted):
|
||||
if e.style == "cs":
|
||||
if e.quote_type == "SingleQuote":
|
||||
return f'‚{html(e.content, k, i, 0, "")}‘'
|
||||
elif e.quote_type == "DoubleQuote":
|
||||
return f'„{html(e.content, k, i, 0, "")}“'
|
||||
elif e.style == "en":
|
||||
if e.quote_type == "SingleQuote":
|
||||
return f'‘{html(e.content, k, i, 0, "")}’'
|
||||
elif e.quote_type == "DoubleQuote":
|
||||
return f'“{html(e.content, k, i, 0, "")}”'
|
||||
else:
|
||||
if e.quote_type == "SingleQuote":
|
||||
return f'\'{html(e.content, k, i, 0, "")}\''
|
||||
elif e.quote_type == "DoubleQuote":
|
||||
return f'"{html(e.content, k, i, 0, "")}"'
|
||||
else:
|
||||
return f'"{html(e.content, k, i, 0, "")}"'
|
||||
|
||||
if isinstance(e, Group):
|
||||
k.begingroup()
|
||||
ret = html(e.content, k, i, indent_level, indent_str)
|
||||
k.endgroup()
|
||||
return ret
|
||||
|
||||
if isinstance(e, Math):
|
||||
formats = {
|
||||
"DisplayMath": True,
|
||||
"InlineMath": False
|
||||
}
|
||||
return indent_level*indent_str + k.render(e.text, {"displayMode": formats[e.format]})
|
||||
|
||||
if isinstance(e, RawInline):
|
||||
if e.format == "html":
|
||||
return e.text
|
||||
else:
|
||||
return ""
|
||||
|
||||
if isinstance(e, RawBlock):
|
||||
if e.format == "html":
|
||||
return f'{e.text}\n'
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
# Non-overriding elements, they get generated using the template at the end
|
||||
# of this function
|
||||
if isinstance(e, Header):
|
||||
tag = "h"+str(e.level)
|
||||
|
||||
if isinstance(e, Figure):
|
||||
content_foot = html(e.caption, k, i, indent_level+1, indent_str)
|
||||
|
||||
if isinstance(e, Caption):
|
||||
tag = "figcaption"
|
||||
|
||||
if isinstance(e, Link):
|
||||
tag = "a"
|
||||
attributes += f' href="{e.url}"'
|
||||
if e.title:
|
||||
attributes += f' title="{e.title}"'
|
||||
|
||||
if isinstance(e, OrderedList):
|
||||
tag = "ol"
|
||||
if e.start and e.start != 1:
|
||||
attributes += f' start="{e.start}"'
|
||||
html_styles = {
|
||||
"Decimal": "1",
|
||||
"LowerRoman": "i",
|
||||
"UpperRoman:": "I",
|
||||
"LowerAlpha": "a",
|
||||
"UpperAlpha": "A"
|
||||
}
|
||||
if e.style and e.style != "DefaultStyle":
|
||||
attributes += f' type="{html_styles[e.style]}"'
|
||||
# FIXME: Delimeter styles
|
||||
|
||||
if isinstance(e, Table):
|
||||
content_head = html(e.head, k, i, indent_level+1, indent_str)
|
||||
content_foot = html(e.foot, k, i, indent_level+1, indent_str)
|
||||
# FIXME: Fancy pandoc tables, using colspec
|
||||
|
||||
if isinstance(e, TableCell):
|
||||
tag = "td"
|
||||
if e.colspan != 1:
|
||||
attributes += f' colspan="{e.colspan}"'
|
||||
if e.rowspan != 1:
|
||||
attributes += f' rowspan="{e.rowspan}"'
|
||||
aligns = {
|
||||
"AlignLeft": "left",
|
||||
"AlignRight": "right",
|
||||
"AlignCenter": "center"
|
||||
}
|
||||
if e.alignment and e.alignment != "AlignDefault":
|
||||
attributes += f' style="text-align: {aligns[e.alignment]}"'
|
||||
|
||||
# The default which all non-overriding elements get generated by. This
|
||||
# includes elements, which were not explicitly mentioned in this function,
|
||||
# e. g. Strong
|
||||
|
||||
if isinstance(e, Inline):
|
||||
return f'<{tag}{attributes}>{content_head}{html(e.content, k, i, 0, "") if hasattr(e, "_content") else ""}{e.text if hasattr(e, "text") else ""}{content_foot}</{tag}>'
|
||||
|
||||
out_str = ""
|
||||
if not isinstance(e, Plain):
|
||||
out_str += f"{indent_level*indent_str}<{tag}{attributes}>\n"
|
||||
out_str += content_head
|
||||
if hasattr(e, "_content"):
|
||||
if len(e.content) > 0 and isinstance(e.content[0], Inline):
|
||||
out_str += (indent_level+1)*indent_str
|
||||
out_str += html(e.content, k, i, indent_level+1, indent_str)
|
||||
if hasattr(e, "text"):
|
||||
out_str += e.text
|
||||
out_str += f"{content_foot}\n"
|
||||
if not isinstance(e, Plain):
|
||||
out_str += f"{indent_level*indent_str}</{tag}>\n"
|
||||
|
||||
return out_str
|
||||
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
from panflute import *
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from .whitespace import NBSP
|
||||
from .elements import FQuoted
|
||||
from .util import inlinify
|
||||
from .context import Group
|
||||
from .images import ImageProcessor
|
||||
|
||||
# Heavily inspired by: git://git.ucw.cz/labsconf2022.git
|
||||
def tex(e: Union[Element, ListContainer], i: ImageProcessor, indent_level: int=0, indent_str: str="\t") -> str:
|
||||
|
||||
# `only` attribute which makes transformed elements appear only in tex
|
||||
# output or html output
|
||||
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])
|
||||
|
||||
# Bits from which the final element output is built at the end of this
|
||||
# function. Most elements override this by returning their own output.
|
||||
content_foot = ""
|
||||
content_head = ""
|
||||
|
||||
arguments = ""
|
||||
open = "{"
|
||||
close = "}"
|
||||
|
||||
tag = e.tag.lower()
|
||||
|
||||
tags = {
|
||||
Header: "h"+chr(64 + e.level) if isinstance(e, Header) else "",
|
||||
}
|
||||
if type(e) in tags:
|
||||
tag = tags[type(e)]
|
||||
|
||||
# These are also disabled in pandoc so they shouldn't appear in the AST at all.
|
||||
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'
|
||||
|
||||
# Elements which can be represented by a simple string
|
||||
simple_string = {
|
||||
NBSP: "~",
|
||||
Space: " ",
|
||||
Null: "",
|
||||
LineBreak: f"\\\\",
|
||||
SoftBreak: f" ",
|
||||
HorizontalRule: "\\hr\n\n"
|
||||
}
|
||||
if type(e) in simple_string:
|
||||
return simple_string[type(e)]
|
||||
|
||||
# Simplest basic elements
|
||||
if isinstance(e, Str):
|
||||
return e.text.replace(" ", "~")
|
||||
|
||||
if isinstance(e, Para):
|
||||
return tex(e.content, i, 0, "")+"\n\n"
|
||||
|
||||
if isinstance(e, Span) or isinstance(e, Plain):
|
||||
return tex(e.content, i, 0, "")
|
||||
|
||||
# Overriding elements with their own returns
|
||||
if isinstance(e, Image):
|
||||
url = e.url
|
||||
|
||||
# TODO: This should use OutputGenerator's get_image_processor_args
|
||||
# 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 ["pdf", "png", "jpeg"]:
|
||||
# Even supported elements have to be 'converted' because the
|
||||
# processing contains finding and moving them to the cache
|
||||
# directory.
|
||||
url = i.process_image(url, ext, source_dir, **additional_args)
|
||||
elif ext in ["svg"]:
|
||||
url = i.process_image(url, "pdf", source_dir, **additional_args)
|
||||
elif ext in ["epdf"]:
|
||||
url = i.process_image(url, "pdf", source_dir, **additional_args)
|
||||
elif ext in ["jpg"]:
|
||||
url = i.process_image(url, "jpeg", source_dir, **additional_args)
|
||||
else:
|
||||
url = i.process_image(url, "pdf", source_dir, **additional_args)
|
||||
|
||||
url = i.find_image(url, [i.cache_dir])
|
||||
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
|
||||
return f'\\image{{{width}}}{{{url}}}'
|
||||
|
||||
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, Code):
|
||||
return f'\\verb`{e.text.replace("`", "backtick")}`'
|
||||
|
||||
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'
|
||||
|
||||
# Figure caption
|
||||
if isinstance(e, Caption):
|
||||
if inlinify(e) is not None:
|
||||
return f'\\figcaption{{{tex(e.content, i, 0, "")}}}'
|
||||
|
||||
if isinstance(e, Math):
|
||||
if e.format == "DisplayMath":
|
||||
return f'$${e.text}$$\n'
|
||||
else:
|
||||
return f'${e.text}$'
|
||||
|
||||
# Footnote
|
||||
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 ""
|
||||
|
||||
# See https://pandoc.org/MANUAL.html#line-blocks
|
||||
if isinstance(e, LineBlock):
|
||||
return f'{tex(e.content, i, indent_level+1, indent_str)}\n'
|
||||
|
||||
if isinstance(e, LineItem):
|
||||
return tex(e.content, i, 0, "") + ("\\\\\n" if e.next else "\n")
|
||||
|
||||
if type(e) is 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" # Is having the \bye a bad idea here?
|
||||
|
||||
|
||||
# Non-overriding elements, they get generated using the template at the end
|
||||
# of this function
|
||||
if isinstance(e, BulletList):
|
||||
tag = "list"
|
||||
open = ""
|
||||
arguments = "{o}"
|
||||
close = "\\endlist"
|
||||
|
||||
elif 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
|
||||
|
||||
elif isinstance(e, ListItem):
|
||||
tag = ":"
|
||||
|
||||
elif 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}}}'
|
||||
|
||||
elif isinstance(e, Group):
|
||||
tag = "begingroup"
|
||||
open = ""
|
||||
if "lang" in e.metadata and e.metadata["lang"] is not None:
|
||||
open = "\\language"+e.metadata["lang"]
|
||||
close = "\\endgroup"
|
||||
|
||||
# The default which all non-overriding elements get generated by. This
|
||||
# includes elements, which were not explicitly mentioned in this function,
|
||||
# e. g. Strong, Emph...
|
||||
|
||||
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
|
|
@ -1,176 +0,0 @@
|
|||
from panflute import Element, Div, Span, Quoted, Image, CodeBlock, Str, MetaInlines, MetaString, MetaBool, RawBlock
|
||||
import re
|
||||
import os
|
||||
|
||||
# Import local files
|
||||
from .whitespace import Whitespace, NBSP, bavlna
|
||||
from .util import nullify, import_md
|
||||
from .context import Context, BlockGroup
|
||||
from .command import Command, BlockCommand, InlineCommand
|
||||
from .command_util import handle_command_define, parse_command
|
||||
from .elements import FQuoted
|
||||
|
||||
|
||||
import warnings
|
||||
warnings.warn("The transform function has been deprecated, is left only for reference and will be removed in future commits. TransformProcessor should be used in its place.", DeprecationWarning)
|
||||
|
||||
# This is where tha magic happens. This function transforms a single element,
|
||||
# to transform the entire tree, panflute's walk should be used.
|
||||
def transform(e: Element, c: Context) -> Element:
|
||||
|
||||
warnings.warn("The transform function has been deprecated, is left only for reference and will be removed in future commits. TransformProcessor should be used in its place.", DeprecationWarning)
|
||||
# Determine if this space should be non-breakable. See whitespace.py.
|
||||
if isinstance(e, Whitespace) and bavlna(e, c):
|
||||
e = NBSP()
|
||||
|
||||
if hasattr(e, "attributes"):
|
||||
# `if` attribute. Only show this element if flag is set.
|
||||
if "if" in e.attributes:
|
||||
if not c.is_flag_set(e.attributes["if"]):
|
||||
return nullify(e)
|
||||
# `ifn` attribute. Only show this element if flag is NOT set
|
||||
if "ifn" in e.attributes:
|
||||
if c.is_flag_set(e.attributes["ifn"]):
|
||||
return nullify(e)
|
||||
|
||||
# There are multiple ways to call a command so we turn it into a
|
||||
# unified element first and then call it at the end. This handles the
|
||||
# []{c=commandname} and
|
||||
# :::{c=commandname}
|
||||
# :::
|
||||
# syntax.
|
||||
if (isinstance(e, Div) or isinstance(e, Span)) and "c" in e.attributes:
|
||||
if isinstance(e, Div):
|
||||
e = BlockCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
|
||||
else:
|
||||
e = InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)
|
||||
|
||||
# Isolated subdocuments using Group and a different Context. Can be
|
||||
# separate files (using attribute `partial`) or be inline using the
|
||||
# following syntax:
|
||||
# ```markdown {.group}
|
||||
# * file content *
|
||||
# ```
|
||||
# Both can contain their own metadata in a FrontMatter (YAML header)
|
||||
if (isinstance(e, Div) and "partial" in e.attributes)\
|
||||
or (isinstance(e, CodeBlock) and "markdown" in e.classes and "group" in e.classes):
|
||||
if isinstance(e, Div):
|
||||
if not c.trusted: # If we're in an untrusted context, we shouldn't allow inclusion of files outside the PWD.
|
||||
full_path = os.path.abspath(c.dir + "/" + e.attributes["partial"])
|
||||
pwd = os.path.abspath(".")
|
||||
if os.path.commonpath([full_path, pwd]) != os.path.commonpath([pwd]):
|
||||
return nullify(e)
|
||||
text = open(c.dir + "/" + e.attributes["partial"], "r").read()
|
||||
path = c.dir + "/" + e.attributes["partial"]
|
||||
else:
|
||||
text = e.text
|
||||
path = c.path
|
||||
if "type" in e.attributes and e.attributes["type"] in ["tex", "html"]:
|
||||
e = RawBlock(text, e.attributes["type"])
|
||||
else:
|
||||
includedDoc = import_md(text)
|
||||
trusted = True
|
||||
if "untrusted" in e.attributes and (e.attributes["untrusted"] == True or e.attributes["untrusted"] == 'True'):
|
||||
trusted = False
|
||||
if not c.trusted:
|
||||
trusted = False
|
||||
nContext = Context(includedDoc, path, c, trusted=trusted)
|
||||
language = includedDoc.get_metadata("lang")
|
||||
includedDoc = includedDoc.walk(transform, nContext)
|
||||
e = BlockGroup(*includedDoc.content, context=nContext, metadata={"lang": language})
|
||||
|
||||
# Transform panflute's Quoted to custom FQuoted, see above.
|
||||
if isinstance(e, Quoted):
|
||||
quote_styles = {
|
||||
"cs": "cs",
|
||||
"en": "en",
|
||||
"sk": "cs",
|
||||
None: None
|
||||
}
|
||||
e = FQuoted(*e.content, quote_type=e.quote_type, style=quote_styles[c.get_metadata("lang")])
|
||||
|
||||
if isinstance(e, Image):
|
||||
# Pass down the directory of the current source file for finding image
|
||||
# files.
|
||||
e.attributes["source_dir"] = c.dir
|
||||
# Pass down "no-srcset" metadatum as attribute down to images.
|
||||
if not "no-srcset" in e.attributes:
|
||||
e.attributes["no-srcset"] = c.get_metadata("no-srcset") if c.get_metadata("no-srcset") is not None else False
|
||||
|
||||
# Pass down metadata 'highlight' and 'highlight_style' as attribute to CodeBlocks
|
||||
if isinstance(e, CodeBlock):
|
||||
if not "highlight" in e.attributes:
|
||||
e.attributes["highlight"] = c.get_metadata("highlight") if c.get_metadata("highlight") is not None else True
|
||||
if not "style" in e.attributes:
|
||||
e.attributes["style"] = c.get_metadata("highlight-style") if c.get_metadata("highlight-style") is not None else "default"
|
||||
e.attributes["noclasses"] = False
|
||||
# I think this is supposed to enable inline styles for highlighting when the style differs from the document, but it clearly doesn't work. a) HTML_generator never accesses it and b) Only the top-level document contains a style so you have to ask the top level context, not the current context.
|
||||
else:
|
||||
e.attributes["noclasses"] = True
|
||||
|
||||
# Execute python code inside source code block. Works the same as commands.
|
||||
# Syntax:
|
||||
# ```python {.run}
|
||||
# print("woo")
|
||||
# ```
|
||||
if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and "run" in e.classes:
|
||||
if not c.trusted:
|
||||
return nullify(e)
|
||||
command_output = parse_command(e.text)(BlockCommand(), c)
|
||||
e = BlockCommand().replaceSelf(*([] if command_output is None else command_output))
|
||||
e = e.walk(transform, c)
|
||||
|
||||
# Command defines for calling using BlockCommand and InlineCommand. If
|
||||
# redefine is used instead of define, the program doesn't check if the
|
||||
# command already exists.
|
||||
# Syntax:
|
||||
# ```python {define=commandname}
|
||||
# print(wooo)
|
||||
# ```
|
||||
if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, "attributes")\
|
||||
and ("define" in e.attributes or "redefine" in e.attributes):
|
||||
if not c.trusted:
|
||||
return nullify(e)
|
||||
e = handle_command_define(e, c)
|
||||
|
||||
## Shorthands
|
||||
# Shorter (and sometimes the only) forms of certain features
|
||||
if isinstance(e, Span) and len(e.content) == 1 and isinstance(e.content[0], Str):
|
||||
## Handle special command shorthand [!commandname]{}
|
||||
if re.match(r"^![\w.]+$", e.content[0].text):
|
||||
e = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]})
|
||||
|
||||
## Handle import [#path/file.md]{}
|
||||
# This is the exact opposite of partials. We take the commands, flags
|
||||
# and metadata but drop the content.
|
||||
elif re.match(r"^#.+$", e.content[0].text):
|
||||
importedDoc = import_md(open(c.dir + "/" + e.content[0].text[1:], "r").read())
|
||||
importedDoc.walk(transform, c)
|
||||
return nullify(e)
|
||||
|
||||
## Handle metadata print [$key1.key2]{}
|
||||
# This is a shorthand for just printing the content of some metadata.
|
||||
elif re.match(r"^\$[\w.]+$", e.content[0].text):
|
||||
val = c.get_metadata(e.content[0].text[1:], False)
|
||||
if isinstance(val, MetaInlines):
|
||||
e = Span(*val.content)
|
||||
e = e.walk(transform, c)
|
||||
elif isinstance(val, MetaString):
|
||||
e = Span(Str(val.string))
|
||||
elif isinstance(val, MetaBool):
|
||||
e = Span(Str(str(val.boolean)))
|
||||
else:
|
||||
raise TypeError(f"Cannot print value of metadatum '{e.content[0].text[1:]}' of type '{type(val)}'")
|
||||
|
||||
## Execute commands
|
||||
# panflute's walk function transforms the children first, then the root
|
||||
# element, so the content the command receives is already transformed.
|
||||
# The output from the command is then transformed manually again.
|
||||
if isinstance(e, Command):
|
||||
if not c.get_command(e.attributes["c"]):
|
||||
raise NameError(f"Command not defined '{e.attributes['c']}'.")
|
||||
command_output = c.get_command(e.attributes["c"])(e, c)
|
||||
e = e.replaceSelf(*command_output)
|
||||
e = e.walk(transform, c)
|
||||
|
||||
return e
|
Loading…
Reference in a new issue