Change html rendering to use a writer for memory efficiency
Signed-off-by: trivernis <trivernis@protonmail.com>feature/epub-rendering
parent
ee822738b4
commit
09bbabfdc2
@ -1,608 +0,0 @@
|
|||||||
use crate::elements::*;
|
|
||||||
use crate::format::PlaceholderTemplate;
|
|
||||||
use crate::references::templates::{Template, TemplateVariable};
|
|
||||||
use asciimath_rs::format::mathml::ToMathML;
|
|
||||||
use htmlescape::{encode_attribute, encode_minimal};
|
|
||||||
use minify::html::minify;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use syntect::highlighting::ThemeSet;
|
|
||||||
use syntect::html::highlighted_html_for_string;
|
|
||||||
use syntect::parsing::SyntaxSet;
|
|
||||||
|
|
||||||
macro_rules! combine_with_lb {
|
|
||||||
($a:expr, $b:expr) => {
|
|
||||||
if $a.len() > 0 {
|
|
||||||
format!("{}<br>{}", $a, $b.to_html())
|
|
||||||
} else {
|
|
||||||
$b.to_html()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ToHtml {
|
|
||||||
fn to_html(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Element {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Element::Block(block) => block.to_html(),
|
|
||||||
Element::Inline(inline) => inline.to_html(),
|
|
||||||
Element::Line(line) => line.to_html(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Line {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Line::Text(text) => text.to_html(),
|
|
||||||
Line::Ruler(ruler) => ruler.to_html(),
|
|
||||||
Line::RefLink(anchor) => anchor.to_html(),
|
|
||||||
Line::Centered(centered) => centered.to_html(),
|
|
||||||
Line::BibEntry(_) => "".to_string(),
|
|
||||||
Line::Anchor(a) => a.to_html(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Inline {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Inline::Url(url) => url.to_html(),
|
|
||||||
Inline::Monospace(mono) => mono.to_html(),
|
|
||||||
Inline::Striked(striked) => striked.to_html(),
|
|
||||||
Inline::Plain(plain) => plain.to_html(),
|
|
||||||
Inline::Italic(italic) => italic.to_html(),
|
|
||||||
Inline::Underlined(under) => under.to_html(),
|
|
||||||
Inline::Bold(bold) => bold.to_html(),
|
|
||||||
Inline::Image(img) => img.to_html(),
|
|
||||||
Inline::Placeholder(placeholder) => placeholder.read().unwrap().to_html(),
|
|
||||||
Inline::Superscript(superscript) => superscript.to_html(),
|
|
||||||
Inline::Checkbox(checkbox) => checkbox.to_html(),
|
|
||||||
Inline::Emoji(emoji) => emoji.to_html(),
|
|
||||||
Inline::Colored(colored) => colored.to_html(),
|
|
||||||
Inline::BibReference(bibref) => bibref.read().unwrap().to_html(),
|
|
||||||
Inline::TemplateVar(var) => var.read().unwrap().to_html(),
|
|
||||||
Inline::Math(m) => m.to_html(),
|
|
||||||
Inline::LineBreak => "<br>".to_string(),
|
|
||||||
Inline::CharacterCode(code) => code.to_html(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Block {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Block::Paragraph(para) => para.to_html(),
|
|
||||||
Block::List(list) => list.to_html(),
|
|
||||||
Block::Table(table) => table.to_html(),
|
|
||||||
Block::CodeBlock(code) => code.to_html(),
|
|
||||||
Block::Quote(quote) => quote.to_html(),
|
|
||||||
Block::Section(section) => section.to_html(),
|
|
||||||
Block::Import(import) => import.to_html(),
|
|
||||||
Block::Placeholder(placeholder) => placeholder.read().unwrap().to_html(),
|
|
||||||
Block::MathBlock(m) => m.to_html(),
|
|
||||||
Block::Null => "".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for MetadataValue {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
match self {
|
|
||||||
MetadataValue::String(string) => encode_minimal(string),
|
|
||||||
MetadataValue::Integer(num) => format!("{}", num),
|
|
||||||
MetadataValue::Placeholder(ph) => ph.read().unwrap().to_html(),
|
|
||||||
MetadataValue::Bool(b) => format!("{}", b),
|
|
||||||
MetadataValue::Float(f) => format!("{}", f),
|
|
||||||
MetadataValue::Template(t) => t.to_html(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Document {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let inner = self
|
|
||||||
.elements
|
|
||||||
.par_iter()
|
|
||||||
.fold(|| String::new(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
.reduce(
|
|
||||||
|| String::new(),
|
|
||||||
|mut a: String, b: String| {
|
|
||||||
a.push_str(&b);
|
|
||||||
a
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let path = if let Some(path) = &self.path {
|
|
||||||
format!("path='{}'", encode_attribute(path.as_str()))
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
if self.is_root {
|
|
||||||
let language = if let Some(entry) = self.config.get_entry("lang") {
|
|
||||||
entry.get().as_string()
|
|
||||||
} else {
|
|
||||||
"en".to_string()
|
|
||||||
};
|
|
||||||
let style = minify(std::include_str!("assets/style.css"));
|
|
||||||
format!(
|
|
||||||
"<!DOCTYPE html>\
|
|
||||||
<html lang={}>\
|
|
||||||
<head {}>\
|
|
||||||
<meta charset='UTF-8'>\
|
|
||||||
<script id='MathJax-script' async src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'></script>
|
|
||||||
<style>{}</style>\
|
|
||||||
{}\
|
|
||||||
</head>\
|
|
||||||
<body>\
|
|
||||||
<div class='content'>{}</div>\
|
|
||||||
</body>\
|
|
||||||
</html>",
|
|
||||||
encode_minimal(language.as_str()),
|
|
||||||
path, style, self.stylesheets.iter().fold("".to_string(), |a, b| format!("{}<style>{}</style>", a, encode_minimal(b))), inner
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"<div class='documentImport' document-import=true {}>{}</div>",
|
|
||||||
path, inner
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Math {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<math xmlns='http://www.w3.org/1998/Math/MathML'>{}</math>",
|
|
||||||
self.expression.to_mathml()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for MathBlock {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<math xmlns='http://www.w3.org/1998/Math/MathML' display='block'>{}</math>",
|
|
||||||
self.expression.to_mathml()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Import {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let anchor = self.anchor.read().unwrap();
|
|
||||||
if let Some(document) = &anchor.document {
|
|
||||||
document.to_html()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Section {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let inner = self
|
|
||||||
.elements
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()));
|
|
||||||
format!("<section>{}{}</section>", self.header.to_html(), inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Header {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<h{0} id='{1}'>{2}</h{0}>",
|
|
||||||
self.size,
|
|
||||||
encode_attribute(self.anchor.as_str()),
|
|
||||||
self.line.to_html()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Paragraph {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let inner = self
|
|
||||||
.elements
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| combine_with_lb!(a, b));
|
|
||||||
format!("<div class='paragraph'>{}</div>", inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for List {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let inner = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()));
|
|
||||||
if self.ordered {
|
|
||||||
format!("<ol>{}</ol>", inner)
|
|
||||||
} else {
|
|
||||||
format!("<ul>{}</ul>", inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for ListItem {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let inner = self
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()));
|
|
||||||
if let Some(first) = self.children.first() {
|
|
||||||
if first.ordered {
|
|
||||||
format!("<li>{}<ol>{}</ol></li>", self.text.to_html(), inner)
|
|
||||||
} else {
|
|
||||||
format!("<li>{}<ul>{}</ul></li>", self.text.to_html(), inner)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
format!("<li>{}</li>", self.text.to_html())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Table {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let head = self.header.cells.iter().fold("".to_string(), |a, b| {
|
|
||||||
format!("{}<th>{}</th>", a, b.text.to_html())
|
|
||||||
});
|
|
||||||
let body = self
|
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()));
|
|
||||||
format!(
|
|
||||||
"<div class='tableWrapper'><table><tr>{}<tr>{}</table></div>",
|
|
||||||
head, body
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Row {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let inner = self
|
|
||||||
.cells
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()));
|
|
||||||
format!("<tr>{}</tr>", inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Cell {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!("<td>{}</td>", self.text.to_html())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local! {static PS: RefCell<SyntaxSet> = RefCell::new(SyntaxSet::load_defaults_nonewlines());}
|
|
||||||
thread_local! {static TS: RefCell<ThemeSet> = RefCell::new(ThemeSet::load_defaults());}
|
|
||||||
|
|
||||||
impl ToHtml for CodeBlock {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
if self.language.len() > 0 {
|
|
||||||
PS.with(|ps_cell| {
|
|
||||||
let ps = ps_cell.borrow();
|
|
||||||
if let Some(syntax) = ps.find_syntax_by_token(self.language.as_str()) {
|
|
||||||
TS.with(|ts_cell| {
|
|
||||||
let ts = ts_cell.borrow();
|
|
||||||
format!(
|
|
||||||
"<div><code lang='{}'>{}</code></div>",
|
|
||||||
encode_attribute(self.language.clone().as_str()),
|
|
||||||
highlighted_html_for_string(
|
|
||||||
self.code.as_str(),
|
|
||||||
&ps,
|
|
||||||
syntax,
|
|
||||||
&ts.themes["InspiredGitHub"]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"<div><code lang='{}'><pre>{}</pre></code></div>",
|
|
||||||
encode_attribute(self.language.clone().as_str()),
|
|
||||||
encode_minimal(self.code.as_str())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"<div><code><pre>{}</pre></code></div>",
|
|
||||||
encode_minimal(self.code.as_str())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Quote {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let text = self
|
|
||||||
.text
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| combine_with_lb!(a, b));
|
|
||||||
if let Some(meta) = self.metadata.clone() {
|
|
||||||
format!(
|
|
||||||
"<div class='quote'><blockquote>{}</blockquote><span class='metadata'>{}</span></div>",
|
|
||||||
text, meta.to_html()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!("<div class='quote'><blockquote>{}</blockquote></div>", text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Ruler {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
"<hr>".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for TextLine {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
self.subtext
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Image {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
let mut style = String::new();
|
|
||||||
|
|
||||||
let url = if let Some(content) = self.get_content() {
|
|
||||||
let mime_type = mime_guess::from_path(&self.url.url).first_or(mime::IMAGE_PNG);
|
|
||||||
format!(
|
|
||||||
"data:{};base64,{}",
|
|
||||||
mime_type.to_string(),
|
|
||||||
base64::encode(content)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
encode_attribute(self.url.url.as_str())
|
|
||||||
};
|
|
||||||
if let Some(meta) = &self.metadata {
|
|
||||||
if let Some(width) = meta.data.get("width") {
|
|
||||||
style = format!("{}width: {};", style, width.to_html())
|
|
||||||
}
|
|
||||||
if let Some(height) = meta.data.get("height") {
|
|
||||||
style = format!("{}height: {};", style, height.to_html())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(description) = self.url.description.clone() {
|
|
||||||
let description = description
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{} {}", a, b.to_html()));
|
|
||||||
minify(
|
|
||||||
format!(
|
|
||||||
"<div class='figure'>\
|
|
||||||
<a href={}>\
|
|
||||||
<img src='{}' alt='{}' style='{}'/>\
|
|
||||||
</a>\
|
|
||||||
<label class='imageDescription'>{}</label>\
|
|
||||||
</div>",
|
|
||||||
encode_attribute(self.url.url.as_str()),
|
|
||||||
url,
|
|
||||||
encode_attribute(description.as_str()),
|
|
||||||
style,
|
|
||||||
description
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!("<a href={0}><img src='{0}' style='{1}'/></a>", url, style)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for BoldText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<b>{}</b>",
|
|
||||||
self.value
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for UnderlinedText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<u>{}</u>",
|
|
||||||
self.value
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for ItalicText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<i>{}</i>",
|
|
||||||
self.value
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for StrikedText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<del>{}</del>",
|
|
||||||
self.value
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for SuperscriptText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<sup>{}</sup>",
|
|
||||||
self.value
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for MonospaceText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<code class='inlineCode'>{}</code>",
|
|
||||||
encode_minimal(self.value.as_str())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Url {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
if let Some(description) = self.description.clone() {
|
|
||||||
format!(
|
|
||||||
"<a href='{}'>{}</a>",
|
|
||||||
self.url.clone(),
|
|
||||||
description
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
.as_str()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"<a href='{}'>{}</a>",
|
|
||||||
encode_attribute(self.url.clone().as_str()),
|
|
||||||
encode_minimal(self.url.clone().as_str())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for PlainText {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
encode_minimal(self.value.clone().as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Placeholder {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
if let Some(value) = &self.value {
|
|
||||||
value.to_html()
|
|
||||||
} else {
|
|
||||||
format!("Unknown placeholder '{}'!", encode_minimal(&self.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for RefLink {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<a href='#{}'>{}</a>",
|
|
||||||
encode_attribute(self.reference.as_str()),
|
|
||||||
self.description.to_html()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for InlineMetadata {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
if let Some(MetadataValue::String(format)) = self.data.get("display") {
|
|
||||||
let mut template = PlaceholderTemplate::new(format.clone());
|
|
||||||
self.data
|
|
||||||
.iter()
|
|
||||||
.for_each(|(k, v)| template.add_replacement(k, v.to_html().as_str()));
|
|
||||||
|
|
||||||
template.render()
|
|
||||||
} else {
|
|
||||||
self.data.iter().fold("".to_string(), |s, (k, v)| {
|
|
||||||
format!("{} {}={},", s, k, v.to_html())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Centered {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!("<div class='centered'>{}</div>", self.line.to_html())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Checkbox {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
if self.value {
|
|
||||||
format!("<input type='checkbox' checked disabled>")
|
|
||||||
} else {
|
|
||||||
format!("<input type='checkbox'disabled>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Emoji {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<span class='emoji' emoji-name='{}'>{}</span>",
|
|
||||||
encode_attribute(self.name.as_str()),
|
|
||||||
self.value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Colored {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<span class='colored' style='color:{};'>{}</span>",
|
|
||||||
encode_attribute(self.color.as_str()),
|
|
||||||
self.value.to_html()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for BibReference {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<sup><a href='#{}'>{}</a></sup>",
|
|
||||||
self.key.clone(),
|
|
||||||
self.get_formatted()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Template {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
self.text
|
|
||||||
.iter()
|
|
||||||
.fold("".to_string(), |a, b| format!("{}{}", a, b.to_html()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for TemplateVariable {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
if let Some(value) = &self.value {
|
|
||||||
format!(
|
|
||||||
"{}{}{}",
|
|
||||||
encode_minimal(self.prefix.as_str()),
|
|
||||||
value.to_html(),
|
|
||||||
encode_minimal(self.suffix.as_str())
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for CharacterCode {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!("&{};", encode_minimal(self.code.as_str()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToHtml for Anchor {
|
|
||||||
fn to_html(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"<div id='{}'>{}</div>",
|
|
||||||
encode_attribute(self.key.as_str()),
|
|
||||||
self.inner.to_html()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,28 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
pub struct HTMLWriter {
|
||||||
|
inner: Box<dyn Write>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HTMLWriter {
|
||||||
|
/// Creates a new writer
|
||||||
|
pub fn new(inner: Box<dyn Write>) -> Self {
|
||||||
|
Self { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a raw string
|
||||||
|
pub fn write(&mut self, html: String) -> io::Result<()> {
|
||||||
|
self.inner.write_all(html.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes an escaped string
|
||||||
|
pub fn write_escaped(&mut self, html: String) -> io::Result<()> {
|
||||||
|
self.write(htmlescape::encode_minimal(html.as_str()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes an escaped attribute
|
||||||
|
pub fn write_attribute(&mut self, attribute_value: String) -> io::Result<()> {
|
||||||
|
self.write(htmlescape::encode_attribute(attribute_value.as_str()))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
pub mod html_writer;
|
||||||
|
pub mod to_html;
|
@ -0,0 +1,631 @@
|
|||||||
|
use crate::elements::*;
|
||||||
|
use crate::format::html::html_writer::HTMLWriter;
|
||||||
|
use crate::format::PlaceholderTemplate;
|
||||||
|
use crate::references::templates::{Template, TemplateVariable};
|
||||||
|
use asciimath_rs::format::mathml::ToMathML;
|
||||||
|
use htmlescape::encode_attribute;
|
||||||
|
use minify::html::minify;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::io;
|
||||||
|
use syntect::highlighting::ThemeSet;
|
||||||
|
use syntect::html::highlighted_html_for_string;
|
||||||
|
use syntect::parsing::SyntaxSet;
|
||||||
|
|
||||||
|
pub trait ToHtml {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Element {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Element::Block(block) => block.to_html(writer),
|
||||||
|
Element::Inline(inline) => inline.to_html(writer),
|
||||||
|
Element::Line(line) => line.to_html(writer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Line {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Line::Text(text) => text.to_html(writer),
|
||||||
|
Line::Ruler(ruler) => ruler.to_html(writer),
|
||||||
|
Line::RefLink(anchor) => anchor.to_html(writer),
|
||||||
|
Line::Centered(centered) => centered.to_html(writer),
|
||||||
|
Line::Anchor(a) => a.to_html(writer),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Inline {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Inline::Url(url) => url.to_html(writer),
|
||||||
|
Inline::Monospace(mono) => mono.to_html(writer),
|
||||||
|
Inline::Striked(striked) => striked.to_html(writer),
|
||||||
|
Inline::Plain(plain) => plain.to_html(writer),
|
||||||
|
Inline::Italic(italic) => italic.to_html(writer),
|
||||||
|
Inline::Underlined(under) => under.to_html(writer),
|
||||||
|
Inline::Bold(bold) => bold.to_html(writer),
|
||||||
|
Inline::Image(img) => img.to_html(writer),
|
||||||
|
Inline::Placeholder(placeholder) => placeholder.read().unwrap().to_html(writer),
|
||||||
|
Inline::Superscript(superscript) => superscript.to_html(writer),
|
||||||
|
Inline::Checkbox(checkbox) => checkbox.to_html(writer),
|
||||||
|
Inline::Emoji(emoji) => emoji.to_html(writer),
|
||||||
|
Inline::Colored(colored) => colored.to_html(writer),
|
||||||
|
Inline::BibReference(bibref) => bibref.read().unwrap().to_html(writer),
|
||||||
|
Inline::TemplateVar(var) => var.read().unwrap().to_html(writer),
|
||||||
|
Inline::Math(m) => m.to_html(writer),
|
||||||
|
Inline::LineBreak => writer.write("<br>".to_string()),
|
||||||
|
Inline::CharacterCode(code) => code.to_html(writer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Block {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
Block::Paragraph(para) => para.to_html(writer),
|
||||||
|
Block::List(list) => list.to_html(writer),
|
||||||
|
Block::Table(table) => table.to_html(writer),
|
||||||
|
Block::CodeBlock(code) => code.to_html(writer),
|
||||||
|
Block::Quote(quote) => quote.to_html(writer),
|
||||||
|
Block::Section(section) => section.to_html(writer),
|
||||||
|
Block::Import(import) => import.to_html(writer),
|
||||||
|
Block::Placeholder(placeholder) => placeholder.read().unwrap().to_html(writer),
|
||||||
|
Block::MathBlock(m) => m.to_html(writer),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MetadataValue {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
MetadataValue::String(string) => writer.write_escaped(string.clone()),
|
||||||
|
MetadataValue::Integer(num) => writer.write(num.to_string()),
|
||||||
|
MetadataValue::Placeholder(ph) => ph.read().unwrap().to_html(writer),
|
||||||
|
MetadataValue::Bool(b) => writer.write(b.to_string()),
|
||||||
|
MetadataValue::Float(f) => writer.write(f.to_string()),
|
||||||
|
MetadataValue::Template(t) => t.to_html(writer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Document {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
let path = if let Some(path) = &self.path {
|
||||||
|
format!("path='{}'", encode_attribute(path.as_str()))
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
};
|
||||||
|
if self.is_root {
|
||||||
|
let language = if let Some(entry) = self.config.get_entry("lang") {
|
||||||
|
entry.get().as_string()
|
||||||
|
} else {
|
||||||
|
"en".to_string()
|
||||||
|
};
|
||||||
|
let style = minify(std::include_str!("assets/style.css"));
|
||||||
|
writer.write("<!DOCTYPE html".to_string())?;
|
||||||
|
writer.write("<html lang=\"".to_string())?;
|
||||||
|
writer.write_attribute(language)?;
|
||||||
|
writer.write("\"/><head ".to_string())?;
|
||||||
|
writer.write(path)?;
|
||||||
|
writer.write("/>".to_string())?;
|
||||||
|
writer.write("<meta charset='UTF-8'><script id='MathJax-script' async src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'></script>".to_string())?;
|
||||||
|
writer.write("<style>".to_string())?;
|
||||||
|
writer.write(style)?;
|
||||||
|
writer.write("</style>".to_string())?;
|
||||||
|
|
||||||
|
for stylesheet in &self.stylesheets {
|
||||||
|
writer.write("<style>".to_string())?;
|
||||||
|
writer.write(stylesheet.clone())?;
|
||||||
|
writer.write("</style>".to_string())?;
|
||||||
|
}
|
||||||
|
writer.write("</head><body><div class='content'>".to_string())?;
|
||||||
|
for element in &self.elements {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</div></body></html>".to_string())?;
|
||||||
|
} else {
|
||||||
|
writer.write("<div class='documentImport' document-import='true' ".to_string())?;
|
||||||
|
writer.write(path)?;
|
||||||
|
writer.write(">".to_string())?;
|
||||||
|
|
||||||
|
for element in &self.elements {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</div>".to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Math {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<math xmlns='http://www.w3.org/1998/Math/MathML'>".to_string())?;
|
||||||
|
writer.write(self.expression.to_mathml())?;
|
||||||
|
|
||||||
|
writer.write("</math>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MathBlock {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write(
|
||||||
|
"<math xmlns='http://www.w3.org/1998/Math/MathML' display='block'>".to_string(),
|
||||||
|
)?;
|
||||||
|
writer.write(self.expression.to_mathml())?;
|
||||||
|
|
||||||
|
writer.write("</math>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Import {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
let anchor = self.anchor.read().unwrap();
|
||||||
|
if let Some(document) = &anchor.document {
|
||||||
|
document.to_html(writer)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Section {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<section>".to_string())?;
|
||||||
|
self.header.to_html(writer)?;
|
||||||
|
for element in &self.elements {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</section>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Header {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write(format!("<h{}", self.size))?;
|
||||||
|
writer.write(" id=\"".to_string())?;
|
||||||
|
writer.write_attribute(self.anchor.clone())?;
|
||||||
|
writer.write("\">".to_string())?;
|
||||||
|
self.line.to_html(writer)?;
|
||||||
|
|
||||||
|
writer.write(format!("</h{}>", self.size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Paragraph {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<div class='paragraph'>".to_string())?;
|
||||||
|
for element in &self.elements {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
writer.write("<br>".to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</div>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for List {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
if self.ordered {
|
||||||
|
writer.write("<ol>".to_string())?;
|
||||||
|
for item in &self.items {
|
||||||
|
item.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</ol>".to_string())
|
||||||
|
} else {
|
||||||
|
writer.write("<ul>".to_string())?;
|
||||||
|
for item in &self.items {
|
||||||
|
item.to_html(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</ul>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for ListItem {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<li>".to_string())?;
|
||||||
|
self.text.to_html(writer)?;
|
||||||
|
|
||||||
|
if let Some(first) = self.children.first() {
|
||||||
|
if first.ordered {
|
||||||
|
writer.write("<ol>".to_string())?;
|
||||||
|
for item in &self.children {
|
||||||
|
item.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</ol>".to_string())?;
|
||||||
|
} else {
|
||||||
|
writer.write("<ul>".to_string())?;
|
||||||
|
for item in &self.children {
|
||||||
|
item.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</ul>".to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</li>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Table {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<div class='tableWrapper'><table><tr>".to_string())?;
|
||||||
|
|
||||||
|
for cell in &self.header.cells {
|
||||||
|
writer.write("<th>".to_string())?;
|
||||||
|
cell.text.to_html(writer)?;
|
||||||
|
writer.write("</th>".to_string())?;
|
||||||
|
}
|
||||||
|
writer.write("</tr>".to_string())?;
|
||||||
|
for row in &self.rows {
|
||||||
|
row.to_html(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</table></div>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Row {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<tr>".to_string())?;
|
||||||
|
|
||||||
|
for cell in &self.cells {
|
||||||
|
cell.to_html(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</tr>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Cell {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<td>".to_string())?;
|
||||||
|
self.text.to_html(writer)?;
|
||||||
|
|
||||||
|
writer.write("</td>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {static PS: RefCell<SyntaxSet> = RefCell::new(SyntaxSet::load_defaults_nonewlines());}
|
||||||
|
thread_local! {static TS: RefCell<ThemeSet> = RefCell::new(ThemeSet::load_defaults());}
|
||||||
|
|
||||||
|
impl ToHtml for CodeBlock {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<div><code".to_string())?;
|
||||||
|
if self.language.len() > 0 {
|
||||||
|
writer.write(" lang=\"".to_string())?;
|
||||||
|
writer.write_attribute(self.language.clone())?;
|
||||||
|
writer.write("\">".to_string())?;
|
||||||
|
PS.with(|ps_cell| {
|
||||||
|
let ps = ps_cell.borrow();
|
||||||
|
if let Some(syntax) = ps.find_syntax_by_token(self.language.as_str()) {
|
||||||
|
TS.with(|ts_cell| {
|
||||||
|
let ts = ts_cell.borrow();
|
||||||
|
|
||||||
|
let _ = writer.write(highlighted_html_for_string(
|
||||||
|
self.code.as_str(),
|
||||||
|
&ps,
|
||||||
|
syntax,
|
||||||
|
&ts.themes["InspiredGitHub"],
|
||||||
|
));
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let _ = writer.write("<pre>".to_string());
|
||||||
|
let _ = writer.write(self.code.clone());
|
||||||
|
let _ = writer.write("</pre>".to_string());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
writer.write("><pre>".to_string())?;
|
||||||
|
writer.write_escaped(self.code.clone())?;
|
||||||
|
writer.write("</pre>".to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</code></div>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Quote {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<div class='quote'><blockquote>".to_string())?;
|
||||||
|
for line in &self.text {
|
||||||
|
line.to_html(writer)?;
|
||||||
|
writer.write("<br>".to_string())?;
|
||||||
|
}
|
||||||
|
if let Some(meta) = self.metadata.clone() {
|
||||||
|
writer.write("<span class='metadata'>".to_string())?;
|
||||||
|
meta.to_html(writer)?;
|
||||||
|
writer.write("</span>".to_string())?;
|
||||||
|
}
|
||||||
|
writer.write("</blockquote></div>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Ruler {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<hr>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for TextLine {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
for text in &self.subtext {
|
||||||
|
text.to_html(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Image {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
let mut style = String::new();
|
||||||
|
|
||||||
|
let url = if let Some(content) = self.get_content() {
|
||||||
|
let mime_type = mime_guess::from_path(&self.url.url).first_or(mime::IMAGE_PNG);
|
||||||
|
format!(
|
||||||
|
"data:{};base64,{}",
|
||||||
|
mime_type.to_string(),
|
||||||
|
base64::encode(content)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
encode_attribute(self.url.url.as_str())
|
||||||
|
};
|
||||||
|
if let Some(meta) = &self.metadata {
|
||||||
|
if let Some(width) = meta.get_string("width") {
|
||||||
|
style = format!("{}width: {};", style, width)
|
||||||
|
}
|
||||||
|
if let Some(height) = meta.get_string("height") {
|
||||||
|
style = format!("{}height: {};", style, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(description) = self.url.description.clone() {
|
||||||
|
writer.write("<div class='figure'><a href='".to_string())?;
|
||||||
|
writer.write_attribute(self.url.url.clone())?;
|
||||||
|
writer.write("'><img src='".to_string())?;
|
||||||
|
writer.write(url)?;
|
||||||
|
writer.write("' style='".to_string())?;
|
||||||
|
writer.write_attribute(style)?;
|
||||||
|
writer.write("'/></a><br><label class='imageDescripton'>".to_string())?;
|
||||||
|
for item in description {
|
||||||
|
item.to_html(writer)?;
|
||||||
|
writer.write(" ".to_string())?;
|
||||||
|
}
|
||||||
|
writer.write("</label></div>".to_string())?;
|
||||||
|
} else {
|
||||||
|
writer.write("<a href='".to_string())?;
|
||||||
|
writer.write(url.clone())?;
|
||||||
|
writer.write("'><img src='".to_string())?;
|
||||||
|
writer.write(url)?;
|
||||||
|
writer.write("' style='".to_string())?;
|
||||||
|
writer.write(style)?;
|
||||||
|
writer.write("'/></a>".to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for BoldText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<b>".to_string())?;
|
||||||
|
for element in &self.value {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</b>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for UnderlinedText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<u>".to_string())?;
|
||||||
|
for element in &self.value {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</u>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for ItalicText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<i>".to_string())?;
|
||||||
|
for element in &self.value {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</i>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for StrikedText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<del>".to_string())?;
|
||||||
|
for element in &self.value {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</del>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for SuperscriptText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<sup>".to_string())?;
|
||||||
|
for element in &self.value {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
writer.write("</sup>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for MonospaceText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<code class='inlineCode'>".to_string())?;
|
||||||
|
writer.write_escaped(self.value.clone())?;
|
||||||
|
|
||||||
|
writer.write("</code>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Url {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<a href='".to_string())?;
|
||||||
|
writer.write(self.url.clone())?;
|
||||||
|
writer.write("'>".to_string())?;
|
||||||
|
if let Some(description) = self.description.clone() {
|
||||||
|
for desc in description {
|
||||||
|
desc.to_html(writer)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writer.write_escaped(self.url.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("</a>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for PlainText {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write_escaped(self.value.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Placeholder {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
if let Some(value) = &self.value {
|
||||||
|
value.to_html(writer)
|
||||||
|
} else {
|
||||||
|
writer.write("Unknown placeholder '".to_string())?;
|
||||||
|
writer.write_escaped(self.name.clone())?;
|
||||||
|
writer.write_escaped("'".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for RefLink {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<a href='#".to_string())?;
|
||||||
|
writer.write_escaped(self.reference.clone())?;
|
||||||
|
writer.write("'>".to_string())?;
|
||||||
|
self.description.to_html(writer)?;
|
||||||
|
|
||||||
|
writer.write("</a>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for InlineMetadata {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
if let Some(MetadataValue::String(format)) = self.data.get("display") {
|
||||||
|
let mut template = PlaceholderTemplate::new(format.clone());
|
||||||
|
self.data
|
||||||
|
.iter()
|
||||||
|
.for_each(|(k, v)| template.add_replacement(k, &v.to_string()));
|
||||||
|
|
||||||
|
writer.write(template.render())?;
|
||||||
|
} else {
|
||||||
|
for (k, v) in &self.data {
|
||||||
|
writer.write_escaped(format!("{}={},", k, v.to_string()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Centered {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<div class='centered'>".to_string())?;
|
||||||
|
self.line.to_html(writer)?;
|
||||||
|
|
||||||
|
writer.write("</div>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Checkbox {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<input type='checkbox' disabled ".to_string())?;
|
||||||
|
if self.value {
|
||||||
|
writer.write("checked".to_string())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write("/>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Emoji {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<span class='emoji' emoji-name='".to_string())?;
|
||||||
|
writer.write_attribute(self.name.clone())?;
|
||||||
|
writer.write("'>".to_string())?;
|
||||||
|
writer.write(self.value.to_string())?;
|
||||||
|
|
||||||
|
writer.write("</span>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Colored {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<span class='colored' style='color:".to_string())?;
|
||||||
|
writer.write_attribute(self.color.clone())?;
|
||||||
|
writer.write(";'>".to_string())?;
|
||||||
|
self.value.to_html(writer)?;
|
||||||
|
|
||||||
|
writer.write("</span>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for BibReference {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<sup><a href='#".to_string())?;
|
||||||
|
writer.write_escaped(self.key.clone())?;
|
||||||
|
writer.write("'>".to_string())?;
|
||||||
|
writer.write(self.get_formatted())?;
|
||||||
|
|
||||||
|
writer.write("</a></sup>".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Template {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
for element in &self.text {
|
||||||
|
element.to_html(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for TemplateVariable {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
if let Some(value) = &self.value {
|
||||||
|
writer.write_escaped(self.prefix.clone())?;
|
||||||
|
value.to_html(writer)?;
|
||||||
|
writer.write_escaped(self.suffix.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for CharacterCode {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("&".to_string())?;
|
||||||
|
writer.write_escaped(self.code.clone())?;
|
||||||
|
|
||||||
|
writer.write(";".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToHtml for Anchor {
|
||||||
|
fn to_html(&self, writer: &mut HTMLWriter) -> io::Result<()> {
|
||||||
|
writer.write("<div id='".to_string())?;
|
||||||
|
writer.write_attribute(self.key.clone())?;
|
||||||
|
writer.write("'>".to_string())?;
|
||||||
|
self.inner.to_html(writer)?;
|
||||||
|
|
||||||
|
writer.write("</div>".to_string())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue