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