mirror of
https://github.com/lise-henry/crowbook
synced 2024-05-13 15:46:17 +02:00
Replace mustache with upon (wip)
This compiles but probably wrecks everything since the template syntax is a bit different
This commit is contained in:
parent
86f7cd5ea9
commit
fe8d98ccd7
|
@ -361,6 +361,7 @@ dependencies = [
|
|||
"syntect",
|
||||
"tempfile",
|
||||
"textwrap",
|
||||
"upon",
|
||||
"uuid",
|
||||
"walkdir 2.3.3",
|
||||
"yaml-rust",
|
||||
|
@ -1642,6 +1643,17 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "upon"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a9260fe394dfd8ab204a8eab40f88eb9a331bb852147d24fc0aff6b30daa02"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"unicode-ident",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.5"
|
||||
|
|
|
@ -51,6 +51,7 @@ html-escape = "0.2"
|
|||
mime_guess = "2"
|
||||
comrak = "0.18"
|
||||
yaml-rust = "0.4"
|
||||
upon = "0.7"
|
||||
mustache = "0.9"
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
walkdir = "2"
|
||||
|
|
202
src/lib/book.rs
202
src/lib/book.rs
|
@ -22,11 +22,11 @@ use crate::chapter::Chapter;
|
|||
use crate::cleaner::{Cleaner, CleanerParams, Default, French, Off};
|
||||
use crate::epub::Epub;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::html_dir::{HtmlDir, ProofHtmlDir};
|
||||
use crate::html_dir::HtmlDir;
|
||||
use crate::html_if::HtmlIf;
|
||||
use crate::html_single::{HtmlSingle, ProofHtmlSingle};
|
||||
use crate::html_single::HtmlSingle;
|
||||
use crate::lang;
|
||||
use crate::latex::{Latex, Pdf, ProofLatex, ProofPdf};
|
||||
use crate::latex::{Latex, Pdf};
|
||||
use crate::misc;
|
||||
use crate::number::Number;
|
||||
use crate::parser::Features;
|
||||
|
@ -38,14 +38,14 @@ use crate::token::Token;
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::iter::IntoIterator;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use mustache::{MapBuilder, Template};
|
||||
use mustache::{Template};
|
||||
use numerals::roman::Roman;
|
||||
use rayon::prelude::*;
|
||||
use yaml_rust::{Yaml, YamlLoader};
|
||||
|
@ -120,7 +120,7 @@ impl fmt::Display for HeaderData {
|
|||
/// // Render the book as html to stdout
|
||||
/// book.render_format_to("html", &mut std::io::stdout()).unwrap();
|
||||
/// ```
|
||||
pub struct Book {
|
||||
pub struct Book<'a> {
|
||||
/// Internal structure. You should not accesss this directly except if
|
||||
/// you are writing a new renderer.
|
||||
pub chapters: Vec<Chapter>,
|
||||
|
@ -147,11 +147,14 @@ pub struct Book {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub bars: Bars,
|
||||
|
||||
/// Store the templates registry
|
||||
pub registry: upon::Engine<'a>,
|
||||
}
|
||||
|
||||
impl Book {
|
||||
impl<'a> Book<'a> {
|
||||
/// Creates a new, empty `Book`
|
||||
pub fn new() -> Book {
|
||||
pub fn new() -> Book<'a> {
|
||||
let mut book = Book {
|
||||
source: Source::empty(),
|
||||
chapters: vec![],
|
||||
|
@ -163,47 +166,26 @@ impl Book {
|
|||
formats: HashMap::new(),
|
||||
features: Features::new(),
|
||||
bars: Bars::new(),
|
||||
registry: upon::Engine::new(),
|
||||
};
|
||||
book.add_format(
|
||||
"html",
|
||||
lformat!("HTML (standalone page)"),
|
||||
Box::new(HtmlSingle {}),
|
||||
)
|
||||
.add_format(
|
||||
"proofread.html",
|
||||
lformat!("HTML (standalone page/proofreading)"),
|
||||
Box::new(ProofHtmlSingle {}),
|
||||
)
|
||||
.add_format(
|
||||
"html.dir",
|
||||
lformat!("HTML (multiple pages)"),
|
||||
Box::new(HtmlDir {}),
|
||||
)
|
||||
.add_format(
|
||||
"proofread.html.dir",
|
||||
lformat!("HTML (multiple pages/proofreading)"),
|
||||
Box::new(ProofHtmlDir {}),
|
||||
)
|
||||
.add_format("tex", lformat!("LaTeX"), Box::new(Latex {}))
|
||||
.add_format(
|
||||
"proofread.tex",
|
||||
lformat!("LaTeX (proofreading)"),
|
||||
Box::new(ProofLatex {}),
|
||||
)
|
||||
.add_format("pdf", lformat!("PDF"), Box::new(Pdf {}))
|
||||
.add_format(
|
||||
"proofread.pdf",
|
||||
lformat!("PDF (proofreading)"),
|
||||
Box::new(ProofPdf {}),
|
||||
)
|
||||
.add_format("epub", lformat!("EPUB"), Box::new(Epub {}))
|
||||
.add_format(
|
||||
"html.if",
|
||||
lformat!("HTML (interactive fiction)"),
|
||||
Box::new(HtmlIf {}),
|
||||
);
|
||||
#[cfg(feature = "odt")]
|
||||
book.add_format("odt", lformat!("ODT"), Box::new(Odt {}));
|
||||
book
|
||||
}
|
||||
|
||||
|
@ -265,9 +247,9 @@ impl Book {
|
|||
/// assert_eq!(book.options.get_str("author").unwrap(), "Foo");
|
||||
/// assert_eq!(book.options.get_str("title").unwrap(), "Bar");
|
||||
/// ```
|
||||
pub fn set_options<'a, I>(&mut self, options: I) -> &mut Book
|
||||
pub fn set_options<'b, I>(&mut self, options: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a (&'a str, &'a str)>,
|
||||
I: IntoIterator<Item = &'b (&'b str, &'b str)>,
|
||||
{
|
||||
// set options
|
||||
for (key, value) in options {
|
||||
|
@ -302,7 +284,7 @@ impl Book {
|
|||
/// let mut book = Book::new();
|
||||
/// let result = book.load_file("some.book");
|
||||
/// ```
|
||||
pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<&mut Book> {
|
||||
pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
||||
let filename = format!("{}", path.as_ref().display());
|
||||
self.source = Source::new(filename.as_str());
|
||||
self.options.source = Source::new(filename.as_str());
|
||||
|
@ -316,9 +298,8 @@ impl Book {
|
|||
self.options.root = self.root.clone();
|
||||
}
|
||||
|
||||
let result = self.read_config(&f);
|
||||
match result {
|
||||
Ok(book) => Ok(book),
|
||||
match self.read_config(&f) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
if err.is_config_parser() && path.as_ref().ends_with(".md") {
|
||||
let err = Error::default(
|
||||
|
@ -353,7 +334,7 @@ impl Book {
|
|||
/// let mut book = Book::new();
|
||||
/// book.load_markdown_file("foo.md"); // not unwraping since foo.md doesn't exist
|
||||
/// ```
|
||||
pub fn load_markdown_file<P: AsRef<Path>>(&mut self, path: P) -> Result<&mut Self> {
|
||||
pub fn load_markdown_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
||||
let filename = format!("{}", path.as_ref().display());
|
||||
self.source = Source::new(filename.as_str());
|
||||
|
||||
|
@ -372,7 +353,7 @@ impl Book {
|
|||
// Update grammar checker according to options
|
||||
self.add_chapter(Number::Hidden, &relative_path.to_string_lossy(), false)?;
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a single markdown config from a `Read`able object.
|
||||
|
@ -397,18 +378,18 @@ impl Book {
|
|||
/// book.read_markdown_config(content.as_bytes()).unwrap();
|
||||
/// assert_eq!(book.options.get_str("title").unwrap(), "Bar");
|
||||
/// ```
|
||||
pub fn read_markdown_config<R: Read>(&mut self, source: R) -> Result<&mut Self> {
|
||||
pub fn read_markdown_config<R: Read>(&mut self, source: R) -> Result<()> {
|
||||
self.options.set("tex.class", "article").unwrap();
|
||||
self.options.set("input.yaml_blocks", "true").unwrap();
|
||||
|
||||
// Update grammar checker according to options
|
||||
self.add_chapter_from_source(Number::Hidden, source, false)?;
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets options from a YAML block
|
||||
fn set_options_from_yaml(&mut self, yaml: &str) -> Result<&mut Book> {
|
||||
fn set_options_from_yaml(&mut self, yaml: &str) -> Result<&mut Self> {
|
||||
self.options.source = self.source.clone();
|
||||
match YamlLoader::load_from_str(yaml) {
|
||||
Err(err) => {
|
||||
|
@ -471,8 +452,8 @@ impl Book {
|
|||
/// let mut book = Book::new();
|
||||
/// book.read_config(content.as_bytes()); // no unwraping as `intro.md` and `chapter_01.md` don't exist
|
||||
/// ```
|
||||
pub fn read_config<R: Read>(&mut self, mut source: R) -> Result<&mut Book> {
|
||||
fn get_filename<'a>(source: &Source, s: &'a str) -> Result<&'a str> {
|
||||
pub fn read_config<R: Read>(&mut self, mut source: R) -> Result<()> {
|
||||
fn get_filename<'b>(source: &Source, s: &'b str) -> Result<&'b str> {
|
||||
let words: Vec<&str> = (s[1..]).split_whitespace().collect();
|
||||
if words.len() > 1 {
|
||||
return Err(Error::config_parser(
|
||||
|
@ -691,7 +672,7 @@ impl Book {
|
|||
|
||||
self.source.unset_line();
|
||||
self.set_chapter_template()?;
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determine whether proofreading is activated or not
|
||||
|
@ -1179,12 +1160,16 @@ impl Book {
|
|||
|
||||
/// Sets the chapter_template once and for all
|
||||
fn set_chapter_template(&mut self) -> Result<()> {
|
||||
let template = compile_str(
|
||||
self.options.get_str("rendering.chapter.template").unwrap(),
|
||||
&self.source,
|
||||
"rendering.chapter.template",
|
||||
)?;
|
||||
self.chapter_template = Some(template);
|
||||
self.registry.add_template("rendering.chapter.template",
|
||||
self.options.get_str("rendering.chapter.template").unwrap().to_owned())
|
||||
.map_err(|e| Error::template(
|
||||
&self.source,
|
||||
lformat!(
|
||||
"could not compile '{template}': {error}",
|
||||
template = "rendering.chapter.template",
|
||||
error = e
|
||||
))
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1236,7 +1221,7 @@ impl Book {
|
|||
};
|
||||
let mut data = self.get_metadata(&mut f)?;
|
||||
if !title.is_empty() {
|
||||
data = data.insert_bool(format!("has_{header_type}_title"), true);
|
||||
data.insert(format!("has_{header_type}_title"), true.into());
|
||||
}
|
||||
let number = self.get_header_number(header, n)?;
|
||||
let header_name = self
|
||||
|
@ -1245,43 +1230,24 @@ impl Book {
|
|||
.map(|s| s.to_owned())
|
||||
.unwrap_or_else(|_| lang::get_str(self.options.get_str("lang").unwrap(), header_type));
|
||||
|
||||
data = data
|
||||
.insert_str(format!("{header_type}_title"), title.clone())
|
||||
.insert_str(header_type, header_name.clone())
|
||||
.insert_str("number", number.clone());
|
||||
let data = data.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
data.insert(format!("{header_type}_title"), title.clone().into());
|
||||
data.insert(header_type.into(), header_name.clone().into());
|
||||
data.insert("number".into(), number.clone().into());
|
||||
|
||||
let opt_template = match header {
|
||||
Header::Part => &self.part_template,
|
||||
Header::Chapter => &self.chapter_template,
|
||||
};
|
||||
|
||||
if let Some(ref template) = *opt_template {
|
||||
template.render_data(&mut res, &data)?;
|
||||
} else {
|
||||
let template = compile_str(
|
||||
self.options
|
||||
.get_str(&format!("rendering.{header_type}.template"))
|
||||
.unwrap(),
|
||||
&self.source,
|
||||
&format!("rendering.{header_type}.template"),
|
||||
)?;
|
||||
template.render_data(&mut res, &data)?;
|
||||
}
|
||||
|
||||
match String::from_utf8(res) {
|
||||
Err(_) => panic!(
|
||||
"{}",
|
||||
lformat!("header generated by mustache was not valid utf-8")
|
||||
),
|
||||
Ok(res) => Ok(HeaderData {
|
||||
text: res,
|
||||
number,
|
||||
header: header_name,
|
||||
title,
|
||||
}),
|
||||
}
|
||||
let res = self.registry.get_template(&format!("rendering.{header_type}.template"))
|
||||
.unwrap()
|
||||
.render(&data)
|
||||
.to_string()?;
|
||||
Ok(HeaderData {
|
||||
text: res,
|
||||
number,
|
||||
header: header_name,
|
||||
title,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the string corresponding to a number, title, and the numbering template for chapter
|
||||
|
@ -1302,24 +1268,22 @@ impl Book {
|
|||
self.get_header(Header::Part, n, title, f)
|
||||
}
|
||||
|
||||
/// Returns a `MapBuilder` (used by `Mustache` for templating), to be used (and completed)
|
||||
/// Returns a `Map of Key/Value` (used by `Upon` for templating), to be used (and completed)
|
||||
/// by renderers. It fills it with the metadata options.
|
||||
///
|
||||
/// It also uses the lang/xx.yaml file corresponding to the language and fills
|
||||
/// `loc_xxx` fiels with it that corresponds to translated versions.
|
||||
///
|
||||
/// This method treats the metadata as Markdown and thus calls `f` to render it.
|
||||
/// This is why we can’t really cache this as it will depend on the renderer.
|
||||
#[doc(hidden)]
|
||||
pub fn get_metadata<F>(&self, mut f: F) -> Result<MapBuilder>
|
||||
pub fn get_metadata<F>(&self, mut f: F) -> Result<BTreeMap<String, upon::Value>>
|
||||
where
|
||||
F: FnMut(&str) -> Result<String>,
|
||||
{
|
||||
let mut mapbuilder = MapBuilder::new();
|
||||
mapbuilder = mapbuilder.insert_str("crowbook_version", env!("CARGO_PKG_VERSION"));
|
||||
mapbuilder = mapbuilder.insert_bool(
|
||||
format!("lang_{}", self.options.get_str("lang").unwrap()),
|
||||
true,
|
||||
);
|
||||
let mut m: BTreeMap<String, upon::Value> = BTreeMap::new();
|
||||
m.insert("crowbook_version".into(), env!("CARGO_PKG_VERSION").into());
|
||||
m.insert(format!("lang_{}", self.options.get_str("lang").unwrap()), true.into());
|
||||
|
||||
// Add metadata to mapbuilder
|
||||
for key in self.options.get_metadata() {
|
||||
|
@ -1335,12 +1299,12 @@ impl Book {
|
|||
match content {
|
||||
Ok(content) => {
|
||||
if !content.is_empty() {
|
||||
mapbuilder = mapbuilder.insert_str(format!("{key}_raw"), raw);
|
||||
mapbuilder = mapbuilder.insert_str(key.clone(), content);
|
||||
m.insert(format!("{key}_raw"), raw.into());
|
||||
m.insert(key.clone(), content.into());
|
||||
|
||||
mapbuilder = mapbuilder.insert_bool(format!("has_{key}"), true);
|
||||
m.insert(format!("has_{key}"), true.into());
|
||||
} else {
|
||||
mapbuilder = mapbuilder.insert_bool(format!("has_{key}"), false);
|
||||
m.insert(format!("has_{key}"), false.into());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
|
@ -1356,7 +1320,7 @@ impl Book {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
mapbuilder = mapbuilder.insert_bool(format!("has_{key}"), false);
|
||||
m.insert(format!("has_{key}"), false.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1365,11 +1329,32 @@ impl Book {
|
|||
for (key, value) in hash {
|
||||
let key = format!("loc_{}", key.as_str().unwrap());
|
||||
let value = value.as_str().unwrap();
|
||||
mapbuilder = mapbuilder.insert_str(key, value);
|
||||
m.insert(key, value.into());
|
||||
}
|
||||
Ok(mapbuilder)
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
/// Calls upon::engine::compile, does NOT registre the complete
|
||||
pub fn compile_str<'s, O>(&self, template: &'s str, source: O, template_name: &str) -> Result<upon::Template<'_, 's>>
|
||||
where
|
||||
O: Into<Source>,
|
||||
{
|
||||
let input: String = template.to_owned();
|
||||
let result = self.registry.compile(template);
|
||||
match result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(err) => Err(Error::template(
|
||||
source,
|
||||
lformat!(
|
||||
"could not compile '{template}': {error}",
|
||||
template = template_name,
|
||||
error = err
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Remove YAML blocks from a string and try to parse them to set options
|
||||
///
|
||||
/// YAML blocks start with
|
||||
|
@ -1495,28 +1480,9 @@ impl Book {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for Book {
|
||||
impl std::default::Default for Book<'_> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls mustache::compile_str but catches panics and returns a result
|
||||
pub fn compile_str<O>(template: &str, source: O, template_name: &str) -> Result<mustache::Template>
|
||||
where
|
||||
O: Into<Source>,
|
||||
{
|
||||
let input: String = template.to_owned();
|
||||
let result = mustache::compile_str(&input);
|
||||
match result {
|
||||
Ok(result) => Ok(result),
|
||||
Err(err) => Err(Error::template(
|
||||
source,
|
||||
lformat!(
|
||||
"could not compile '{template}': {error}",
|
||||
template = template_name,
|
||||
error = err
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2017-2022 Élisabeth HENRY.
|
||||
// Copyright (C) 2017-2023 Élisabeth HENRY.
|
||||
//
|
||||
// This file is part of Crowbook.
|
||||
//
|
||||
|
@ -63,7 +63,7 @@ impl Default for Bars {
|
|||
|
||||
/// Return the style of a bar
|
||||
|
||||
impl Book {
|
||||
impl Book<'_> {
|
||||
/// Adds a progress bar where where info should be written.
|
||||
///
|
||||
/// See [indicatif doc](https://docs.rs/indicatif) for more information.
|
||||
|
@ -270,7 +270,7 @@ impl Book {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Book {
|
||||
impl Drop for Book<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ref bar) = self.bars.secondbar {
|
||||
bar.finish_and_clear();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2017 Élisabeth HENRY.
|
||||
// Copyright (C) 2017-2023 Élisabeth HENRY.
|
||||
//
|
||||
// This file is part of Crowbook.
|
||||
//
|
||||
|
@ -28,7 +28,7 @@ impl Bars {
|
|||
}
|
||||
}
|
||||
|
||||
impl Book {
|
||||
impl Book<'_> {
|
||||
pub fn private_add_progress_bar(&mut self, _: bool) {}
|
||||
|
||||
/// Sets a finished message to the progress bar, if it is set
|
||||
|
|
|
@ -559,8 +559,7 @@ impl BookOptions {
|
|||
})?;
|
||||
let mut book = Book::new();
|
||||
book.load_file(file)?;
|
||||
let options = mem::replace(&mut book.options, BookOptions::new());
|
||||
self.merge(options)?;
|
||||
self.merge(&book.options)?;
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(self.options.insert(key, BookOption::Path(value)))
|
||||
|
@ -910,7 +909,7 @@ impl BookOptions {
|
|||
/// If option is already set in self, don't add it, unless it was the default.
|
||||
/// Option is not inserted either if new value is equal to default.
|
||||
#[doc(hidden)]
|
||||
pub fn merge(&mut self, other: BookOptions) -> Result<()> {
|
||||
pub fn merge(&mut self, other: &BookOptions) -> Result<()> {
|
||||
for (key, value) in &other.options {
|
||||
// Check if option was already set, and if it was to default or to something else
|
||||
if self.defaults.contains_key(key) {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// along with Crowbook. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::book::Header;
|
||||
use crate::book::{compile_str, Book};
|
||||
use crate::book::Book;
|
||||
use crate::book_renderer::BookRenderer;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::html::HtmlRenderer;
|
||||
|
@ -34,7 +34,7 @@ use epub_builder::{
|
|||
EpubBuilder, EpubContent, EpubVersion, ReferenceType, ZipCommand, ZipCommandOrLibrary,
|
||||
ZipLibrary,
|
||||
};
|
||||
use mustache::Template;
|
||||
use upon::Template;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::convert::{AsMut, AsRef};
|
||||
|
@ -194,8 +194,9 @@ impl<'a> EpubRenderer<'a> {
|
|||
}
|
||||
|
||||
// Write chapters
|
||||
let template_chapter = compile_str(
|
||||
self.html.book.get_template("epub.chapter.xhtml")?.as_ref(),
|
||||
let template_chapter_src = self.html.book.get_template("epub.chapter.xhtml")?;
|
||||
let template_chapter = self.html.book.compile_str(
|
||||
template_chapter_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"epub.chapter.xhtml",
|
||||
)?;
|
||||
|
@ -229,23 +230,21 @@ impl<'a> EpubRenderer<'a> {
|
|||
self.html.source = Source::empty();
|
||||
|
||||
// Render the CSS file and write it
|
||||
let template_css = compile_str(
|
||||
self.html.book.get_template("epub.css").unwrap().as_ref(),
|
||||
let template_css_src = self.html.book.get_template("epub.css").unwrap();
|
||||
let template_css = self.html.book.compile_str(
|
||||
template_css_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"epub.css",
|
||||
)?;
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into());
|
||||
if let Ok(epub_css_add) = self.html.book.options.get_str("epub.css.add") {
|
||||
data = data.insert_str("additional_code", epub_css_add);
|
||||
data.insert("additional_code".into(), epub_css_add.into());
|
||||
}
|
||||
let data = data.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template_css.render_data(&mut res, &data)?;
|
||||
let css = String::from_utf8_lossy(&res);
|
||||
let css = template_css.render(&data).to_string()?;
|
||||
maker.stylesheet(css.as_bytes())
|
||||
.map_err(|err| Error::render(Source::empty(), format!("{}", err)))?;
|
||||
|
||||
|
@ -308,25 +307,17 @@ impl<'a> EpubRenderer<'a> {
|
|||
|
||||
/// Render the titlepgae
|
||||
fn render_titlepage(&mut self) -> Result<String> {
|
||||
let template = compile_str(
|
||||
self.html
|
||||
.book
|
||||
.get_template("epub.titlepage.xhtml")?
|
||||
.as_ref(),
|
||||
let template_src = self.html.book.get_template("epub.titlepage.xhtml")?;
|
||||
let template = self.html.book.compile_str(
|
||||
template_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"epub.titlepage.xhtml",
|
||||
)?;
|
||||
let data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
match String::from_utf8(res) {
|
||||
Err(_) => panic!("generated HTML in titlepage was not utf-8 valid"),
|
||||
Ok(res) => Ok(res),
|
||||
}
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
|
||||
/// Render cover.xhtml
|
||||
|
@ -341,32 +332,22 @@ impl<'a> EpubRenderer<'a> {
|
|||
));
|
||||
}
|
||||
let epub3 = self.html.book.options.get_i32("epub.version").unwrap() == 3;
|
||||
let template = compile_str(
|
||||
let template = self.html.book.compile_str(
|
||||
if epub3 { epub3::COVER } else { COVER },
|
||||
&self.html.book.source,
|
||||
"cover.xhtml",
|
||||
)?;
|
||||
let data = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str(
|
||||
"cover",
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert(
|
||||
"cover".into(),
|
||||
self.html
|
||||
.handler
|
||||
.map_image(&self.html.source, Cow::Owned(cover))?
|
||||
.into_owned(),
|
||||
)
|
||||
.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
match String::from_utf8(res) {
|
||||
Err(_) => panic!(
|
||||
"{}",
|
||||
lformat!("generated HTML for cover.xhtml was not utf-8 valid")
|
||||
),
|
||||
Ok(res) => Ok(res),
|
||||
}
|
||||
.into());
|
||||
Ok(template.render(&data).to_string()?)
|
||||
} else {
|
||||
panic!(
|
||||
"{}",
|
||||
|
@ -421,21 +402,15 @@ impl<'a> EpubRenderer<'a> {
|
|||
}
|
||||
self.toc.push(self.chapter_title.clone());
|
||||
|
||||
let data = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("content", content)
|
||||
.insert_str("chapter_title_raw", self.chapter_title_raw.clone())
|
||||
.insert_str("chapter_title", std::mem::take(&mut self.chapter_title))
|
||||
.build();
|
||||
self.chapter_title = String::new();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
match String::from_utf8(res) {
|
||||
Err(_) => panic!("{}", lformat!("generated HTML was not utf-8 valid")),
|
||||
Ok(res) => Ok((res, std::mem::take(&mut self.chapter_title_raw))),
|
||||
}
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("content".into(), content.into());
|
||||
data.insert("chapter_title_raw".into(), self.chapter_title_raw.clone(). into());
|
||||
data.insert("chapter_title".into(), std::mem::take(&mut self.chapter_title).into());
|
||||
Ok((template.render(&data).to_string()?,
|
||||
std::mem::take(&mut self.chapter_title_raw)))
|
||||
}
|
||||
|
||||
/// Renders the header section of the book, finding the title of the chapter
|
||||
|
|
|
@ -361,8 +361,8 @@ impl fmt::Display for Error {
|
|||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Implement our Error from mustache::Error
|
||||
impl From<mustache::Error> for Error {
|
||||
fn from(err: mustache::Error) -> Error {
|
||||
impl From<upon::Error> for Error {
|
||||
fn from(err: upon::Error) -> Error {
|
||||
Error::template(Source::empty(), format!("{err}"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
use crate::book::Header;
|
||||
use crate::book::HeaderData;
|
||||
use crate::book::{compile_str, Book};
|
||||
use crate::book::Book;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::lang;
|
||||
use crate::number::Number;
|
||||
|
@ -36,8 +36,6 @@ use std::fmt::Write;
|
|||
use crowbook_text_processing::escape;
|
||||
use epub_builder::Toc;
|
||||
use epub_builder::TocElement;
|
||||
use mustache::MapBuilder;
|
||||
use mustache::Template;
|
||||
use numerals::roman::Roman;
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
|
@ -63,7 +61,7 @@ pub struct HtmlRenderer<'a> {
|
|||
filename: String,
|
||||
|
||||
/// Book that must be rendered
|
||||
pub book: &'a Book,
|
||||
pub book: &'a Book<'a>,
|
||||
|
||||
/// Proofread or not
|
||||
pub proofread: bool,
|
||||
|
@ -109,8 +107,8 @@ pub struct HtmlRenderer<'a> {
|
|||
|
||||
syntax: Option<Syntax>,
|
||||
|
||||
part_template_html: Template,
|
||||
chapter_template_html: Template,
|
||||
part_template_html: upon::Template<'a, 'a>,
|
||||
chapter_template_html: upon::Template<'a, 'a>,
|
||||
}
|
||||
|
||||
impl<'a> HtmlRenderer<'a> {
|
||||
|
@ -161,12 +159,12 @@ impl<'a> HtmlRenderer<'a> {
|
|||
proofread: false,
|
||||
syntax,
|
||||
highlight,
|
||||
part_template_html: compile_str(
|
||||
part_template_html: book.compile_str(
|
||||
book.options.get_str("html.part.template").unwrap(),
|
||||
Source::empty(),
|
||||
"html.part.template",
|
||||
)?,
|
||||
chapter_template_html: compile_str(
|
||||
chapter_template_html: book.compile_str(
|
||||
book.options.get_str("html.chapter.template").unwrap(),
|
||||
Source::empty(),
|
||||
"html.chapter.template",
|
||||
|
@ -278,17 +276,15 @@ impl<'a> HtmlRenderer<'a> {
|
|||
};
|
||||
let has_number = !data.header.is_empty();
|
||||
let has_title = !data.title.is_empty();
|
||||
let data = MapBuilder::new()
|
||||
.insert_bool("has_number", has_number)
|
||||
.insert_bool("has_title", has_title)
|
||||
.insert_str("header", data.header)
|
||||
.insert_str("number", data.number)
|
||||
.insert_str("link", format!("{}", self.link_number))
|
||||
.insert_str("title", data.title)
|
||||
.build();
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
Ok(String::from_utf8(res)?)
|
||||
let data = upon::value!{
|
||||
has_number: has_number,
|
||||
has_title: has_title,
|
||||
header: data.header,
|
||||
number: data.number,
|
||||
title: data.title,
|
||||
link: format!("{}", self.link_number)
|
||||
};
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
} else {
|
||||
Ok(format!(
|
||||
|
@ -667,12 +663,9 @@ impl<'a> HtmlRenderer<'a> {
|
|||
|
||||
/// Consider the html as a template
|
||||
fn templatize(&mut self, s: &str) -> Result<String> {
|
||||
let mapbuilder = self.book.get_metadata(|s| Ok(s.to_owned()))?;
|
||||
let data = mapbuilder.build();
|
||||
let template = compile_str(s, &self.book.source, "")?;
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
Ok(String::from_utf8_lossy(&res).into_owned())
|
||||
let data = self.book.get_metadata(|s| Ok(s.to_owned()))?;
|
||||
let template = self.book.compile_str(s, &self.book.source, "")?;
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
|
||||
/// Renders the toc name
|
||||
|
@ -680,17 +673,14 @@ impl<'a> HtmlRenderer<'a> {
|
|||
pub fn get_toc_name(&mut self) -> Result<String> {
|
||||
let data = self
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.build();
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
let template = self
|
||||
.book
|
||||
.options
|
||||
.get_str("rendering.inline_toc.name")
|
||||
.unwrap();
|
||||
let template = compile_str(template, &self.book.source, "rendering.inline_toc.name")?;
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
Ok(String::from_utf8_lossy(&res).into_owned())
|
||||
let template = self.book.compile_str(template, &self.book.source, "rendering.inline_toc.name")?;
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
|
||||
/// Render a section containing schema.org JSON-LD code
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// You should have received ba copy of the GNU Lesser General Public License
|
||||
// along with Crowbook. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::book::{compile_str, Book};
|
||||
use crate::book::Book;
|
||||
use crate::book_renderer::BookRenderer;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::html::Highlight;
|
||||
|
@ -273,8 +273,9 @@ impl<'a> HtmlDirRenderer<'a> {
|
|||
let toc = self.html.toc.render(false, false);
|
||||
|
||||
// render all chapters
|
||||
let template = compile_str(
|
||||
self.html.book.get_template("html.dir.template")?.as_ref(),
|
||||
let template_src = self.html.book.get_template("html.dir.template")?;
|
||||
let template = self.html.book.compile_str(
|
||||
template_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.dir.template",
|
||||
)?;
|
||||
|
@ -308,39 +309,37 @@ impl<'a> HtmlDirRenderer<'a> {
|
|||
};
|
||||
|
||||
// Render each HTML document
|
||||
let mut mapbuilder = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("content", content?)
|
||||
.insert_str("chapter_title", titles[i].clone())
|
||||
.insert_str("json_data", self.html.get_json_ld()?)
|
||||
.insert_str("chapter_title_raw", titles_raw[i].clone())
|
||||
.insert_str("toc", toc.clone())
|
||||
.insert_str("prev_chapter", prev_chapter)
|
||||
.insert_str("next_chapter", next_chapter)
|
||||
.insert_str("footer", HtmlRenderer::get_footer(self)?)
|
||||
.insert_str("header", HtmlRenderer::get_header(self)?)
|
||||
.insert_str("script", self.html.book.get_template("html.js").unwrap())
|
||||
.insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("content".into(), content?.into());
|
||||
data.insert("chapter_title".into(), titles[i].clone().into());
|
||||
data.insert("json_data".into(), self.html.get_json_ld()?.into());
|
||||
data.insert("chapter_title_raw".into(), titles_raw[i].clone().into());
|
||||
data.insert("toc".into(), toc.clone().into());
|
||||
data.insert("prev_chapter".into(), prev_chapter.into());
|
||||
data.insert("next_chapter".into(), next_chapter.into());
|
||||
data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into());
|
||||
data.insert("header".into(), HtmlRenderer::get_header(self)?.into());
|
||||
data.insert("script".into(), self.html.book.get_template("html.js").unwrap().into());
|
||||
data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into());
|
||||
|
||||
if let Ok(favicon) = self.html.book.options.get_path("html.icon") {
|
||||
let favicon = self
|
||||
.html
|
||||
.handler
|
||||
.map_image(&self.html.book.source, favicon)?;
|
||||
mapbuilder = mapbuilder.insert_str(
|
||||
"favicon",
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">"),
|
||||
data.insert(
|
||||
"favicon".into(),
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">").into(),
|
||||
);
|
||||
}
|
||||
if self.html.highlight == Highlight::Js {
|
||||
mapbuilder = mapbuilder.insert_bool("highlight_code", true);
|
||||
data.insert("highlight_code".into(), true.into());
|
||||
}
|
||||
let data = mapbuilder.build();
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
self.write_file(&filenamer(i), &res)?;
|
||||
let res = template.render(&data).to_string()?;
|
||||
self.write_file(&filenamer(i), res.as_bytes())?;
|
||||
}
|
||||
|
||||
let mut content = if let Ok(cover) = self.html.book.options.get_path("cover") {
|
||||
|
@ -420,38 +419,37 @@ impl<'a> HtmlDirRenderer<'a> {
|
|||
)?;
|
||||
}
|
||||
// Render index.html and write it too
|
||||
let mut mapbuilder = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("content", content)
|
||||
.insert_str("header", HtmlRenderer::get_header(self)?)
|
||||
.insert_str("footer", HtmlRenderer::get_footer(self)?)
|
||||
.insert_str("toc", toc.clone())
|
||||
.insert_str("script", self.html.book.get_template("html.js").unwrap())
|
||||
.insert_bool(self.html.book.options.get_str("lang").unwrap(), true);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("content".into(), content.into());
|
||||
data.insert("header".into(), HtmlRenderer::get_header(self)?.into());
|
||||
data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into());
|
||||
data.insert("toc".into(), toc.into());
|
||||
data.insert("script".into(), self.html.book.get_template("html.js").unwrap().into());
|
||||
data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into());
|
||||
if let Ok(favicon) = self.html.book.options.get_path("html.icon") {
|
||||
let favicon = self
|
||||
.html
|
||||
.handler
|
||||
.map_image(&self.html.book.source, favicon)?;
|
||||
mapbuilder = mapbuilder.insert_str(
|
||||
"favicon",
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">"),
|
||||
data.insert(
|
||||
"favicon".into(),
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">").into(),
|
||||
);
|
||||
}
|
||||
if self.html.highlight == Highlight::Js {
|
||||
mapbuilder = mapbuilder.insert_bool("highlight_code", true);
|
||||
data.insert("highlight_code".into(), true.into());
|
||||
}
|
||||
let data = mapbuilder.build();
|
||||
let template = compile_str(
|
||||
self.html.book.get_template("html.dir.template")?.as_ref(),
|
||||
let template_src = self.html.book.get_template("html.dir.template")?;
|
||||
let template = self.html.book.compile_str(
|
||||
template_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.dir.template",
|
||||
)?;
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
self.write_file("index.html", &res)?;
|
||||
let res = template.render(&data).to_string()?;
|
||||
self.write_file("index.html", res.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -459,20 +457,19 @@ impl<'a> HtmlDirRenderer<'a> {
|
|||
// Render the CSS file and write it
|
||||
fn write_css(&self) -> Result<()> {
|
||||
// Render the CSS
|
||||
let template_css = compile_str(
|
||||
self.html.book.get_template("html.css")?.as_ref(),
|
||||
let template_css_src = self.html.book.get_template("html.css")?;
|
||||
let template_css = self.html.book.compile_str(
|
||||
template_css_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.css",
|
||||
)?;
|
||||
let mut data = self.html.book.get_metadata(|s| Ok(s.to_owned()))?;
|
||||
data = data.insert_str("colors", self.html.book.get_template("html.css.colors")?);
|
||||
data.insert("colors".into(), self.html.book.get_template("html.css.colors")?.into());
|
||||
if let Ok(html_css_add) = self.html.book.options.get_str("html.css.add") {
|
||||
data = data.insert_str("additional_code", html_css_add);
|
||||
data.insert("additional_code".into(), html_css_add.into());
|
||||
}
|
||||
let data = data.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template_css.render_data(&mut res, &data)?;
|
||||
let css = String::from_utf8_lossy(&res);
|
||||
|
||||
let css = template_css.render(&data).to_string()?;
|
||||
|
||||
// Write it
|
||||
self.write_file("stylesheet.css", css.as_bytes())
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// You should have received ba copy of the GNU Lesser General Public License
|
||||
// along with Crowbook. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::book::{compile_str, Book};
|
||||
use crate::book::Book;
|
||||
use crate::book_renderer::BookRenderer;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::html::Highlight;
|
||||
|
@ -261,74 +261,71 @@ return crowbook_return_variable.replace(/<\\/ul><ul>/g, '');\n",
|
|||
self.html.render_end_notes(&mut content, "section", "");
|
||||
|
||||
// Render the CSS
|
||||
let template_css = compile_str(
|
||||
self.html.book.get_template("html.css")?.as_ref(),
|
||||
let template_css_src = self.html.book.get_template("html.css")?;
|
||||
let template_css = self.html.book.compile_str(
|
||||
template_css_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.css",
|
||||
)?;
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("colors", self.html.book.get_template("html.css.colors")?);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("colors".into(), self.html.book.get_template("html.css.colors")?.into());
|
||||
if let Ok(html_css_add) = self.html.book.options.get_str("html.css.add") {
|
||||
data = data.insert_str("additional_code", html_css_add);
|
||||
data.insert("additional_code".into(), html_css_add.into());
|
||||
}
|
||||
let data = data.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template_css.render_data(&mut res, &data)?;
|
||||
let css = String::from_utf8_lossy(&res);
|
||||
let css:String = template_css.render(&data).to_string()?;
|
||||
|
||||
// Render the JS
|
||||
let template_js = compile_str(
|
||||
self.html.book.get_template("html.if.js")?.as_ref(),
|
||||
let template_js_src = self.html.book.get_template("html.if.js")?;
|
||||
let template_js = self.html.book.compile_str(
|
||||
template_js_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.standalone.js",
|
||||
)?;
|
||||
let data = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| Ok(s.to_owned()))?
|
||||
.insert_bool("one_chapter", true)
|
||||
.insert_str("js_prelude", std::mem::take(&mut self.fn_defs))
|
||||
.insert_str(
|
||||
"new_game",
|
||||
self.html.book.get_template("html.if.new_game").unwrap(),
|
||||
)
|
||||
.insert_str(
|
||||
"common_script",
|
||||
self.html.book.get_template("html.js").unwrap().as_ref(),
|
||||
)
|
||||
.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template_js.render_data(&mut res, &data)?;
|
||||
let js = String::from_utf8_lossy(&res);
|
||||
.get_metadata(|s| Ok(s.to_owned()))?;
|
||||
data.insert("one_chapter".into(), true.into());
|
||||
data.insert("js_prelude".into(), self.fn_defs.clone().into());
|
||||
data.insert(
|
||||
"new_game".into(),
|
||||
self.html.book.get_template("html.if.new_game").unwrap().into(),
|
||||
);
|
||||
data.insert(
|
||||
"common_script".into(),
|
||||
self.html.book.get_template("html.js").unwrap().into(),
|
||||
);
|
||||
let js = template_js.render(&data).to_string()?;
|
||||
|
||||
|
||||
// Render the HTML document
|
||||
let mut mapbuilder = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("content", content)
|
||||
.insert_str("script", js)
|
||||
.insert_bool(self.html.book.options.get_str("lang").unwrap(), true)
|
||||
.insert_bool("one_chapter", true)
|
||||
.insert_str("style", css.as_ref())
|
||||
.insert_str(
|
||||
"print_style",
|
||||
self.html.book.get_template("html.css.print").unwrap(),
|
||||
)
|
||||
.insert_str("footer", HtmlRenderer::get_footer(self)?)
|
||||
.insert_str("header", HtmlRenderer::get_header(self)?)
|
||||
.insert_bool("has_toc", false);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("content".into(), content.into());
|
||||
data.insert("script".into(), js.into());
|
||||
data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into());
|
||||
data.insert("one_chapter".into(), true.into());
|
||||
data.insert("style".into(), css.into());
|
||||
data.insert(
|
||||
"print_style".into(),
|
||||
self.html.book.get_template("html.css.print").unwrap().into(),
|
||||
);
|
||||
data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into());
|
||||
data.insert("header".into(), HtmlRenderer::get_header(self)?.into());
|
||||
data.insert("has_toc".into(), false.into());
|
||||
if let Ok(favicon) = self.html.book.options.get_path("html.icon") {
|
||||
let favicon = self
|
||||
.html
|
||||
.handler
|
||||
.map_image(&self.html.book.source, favicon)?;
|
||||
mapbuilder = mapbuilder.insert_str(
|
||||
"favicon",
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">"),
|
||||
data.insert(
|
||||
"favicon".into(),
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">").into(),
|
||||
);
|
||||
}
|
||||
if self.html.highlight == Highlight::Js {
|
||||
|
@ -338,26 +335,20 @@ return crowbook_return_variable.replace(/<\\/ul><ul>/g, '');\n",
|
|||
.get_template("html.highlight.js")?
|
||||
.as_bytes());
|
||||
let highlight_js = format!("data:text/javascript;base64,{highlight_js}");
|
||||
mapbuilder = mapbuilder
|
||||
.insert_bool("highlight_code", true)
|
||||
.insert_str(
|
||||
"highlight_css",
|
||||
self.html.book.get_template("html.highlight.css")?,
|
||||
)
|
||||
.insert_str("highlight_js", highlight_js);
|
||||
data.insert("highlight_code".into(), true.into());
|
||||
data.insert(
|
||||
"highlight_css".into(),
|
||||
self.html.book.get_template("html.highlight.css")?.into(),
|
||||
);
|
||||
data.insert("highlight_js".into(), highlight_js.into());
|
||||
}
|
||||
let data = mapbuilder.build();
|
||||
let template = compile_str(
|
||||
self.html
|
||||
.book
|
||||
.get_template("html.standalone.template")?
|
||||
.as_ref(),
|
||||
let template_src = self.html.book.get_template("html.standalone.template")?;
|
||||
let template = self.html.book.compile_str(
|
||||
template_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.standalone.template",
|
||||
)?;
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
Ok(String::from_utf8_lossy(&res).into_owned())
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// You should have received ba copy of the GNU Lesser General Public License
|
||||
// along with Crowbook. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::book::{compile_str, Book};
|
||||
use crate::book::Book;
|
||||
use crate::book_renderer::BookRenderer;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::html::Highlight;
|
||||
|
@ -215,93 +215,91 @@ impl<'a> HtmlSingleRenderer<'a> {
|
|||
}
|
||||
|
||||
// Render the CSS
|
||||
let template_css = compile_str(
|
||||
self.html.book.get_template("html.css")?.as_ref(),
|
||||
let template_css_src = self.html.book.get_template("html.css")?;
|
||||
let template_css = self.html.book.compile_str(
|
||||
template_css_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.css",
|
||||
)?;
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("colors", self.html.book.get_template("html.css.colors")?);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("colors".into(), self.html.book.get_template("html.css.colors")?.into());
|
||||
if let Ok(html_css_add) = self.html.book.options.get_str("html.css.add") {
|
||||
data = data.insert_str("additional_code", html_css_add);
|
||||
data.insert("additional_code".into(), html_css_add.into());
|
||||
}
|
||||
let data = data.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template_css.render_data(&mut res, &data)?;
|
||||
let css = String::from_utf8_lossy(&res);
|
||||
let css = template_css.render(&data).to_string()?;
|
||||
|
||||
// Render the JS
|
||||
let template_js = compile_str(
|
||||
self.html.book.get_template("html.standalone.js")?.as_ref(),
|
||||
let template_js_src = self.html.book.get_template("html.standalone.js")?;
|
||||
let template_js = self.html.book.compile_str(
|
||||
template_js_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.standalone.js",
|
||||
)?;
|
||||
let data = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| Ok(s.to_owned()))?
|
||||
.insert_str("book_svg", book_svg.clone())
|
||||
.insert_str("pages_svg", pages_svg.clone())
|
||||
.insert_bool(
|
||||
"one_chapter",
|
||||
.get_metadata(|s| Ok(s.to_owned()))?;
|
||||
data.insert("book_svg".into(), book_svg.clone().into());
|
||||
data.insert("pages_svg".into(), pages_svg.clone().into());
|
||||
data.insert(
|
||||
"one_chapter".into(),
|
||||
self.html
|
||||
.book
|
||||
.options
|
||||
.get_bool("html.standalone.one_chapter")
|
||||
.unwrap(),
|
||||
)
|
||||
.insert_str(
|
||||
"common_script",
|
||||
self.html.book.get_template("html.js").unwrap().as_ref(),
|
||||
)
|
||||
.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template_js.render_data(&mut res, &data)?;
|
||||
let js = String::from_utf8_lossy(&res);
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
data.insert(
|
||||
"common_script".into(),
|
||||
self.html.book.get_template("html.js").unwrap().into(),
|
||||
);
|
||||
let js = template_js.render(&data).to_string()?;
|
||||
|
||||
// Render the HTML document
|
||||
let mut mapbuilder = self
|
||||
let mut data = self
|
||||
.html
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("content", content)
|
||||
.insert_str("script", js)
|
||||
.insert_bool(self.html.book.options.get_str("lang").unwrap(), true)
|
||||
.insert_bool(
|
||||
"one_chapter",
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("content".into(), content.into());
|
||||
data.insert("script".into(), js.into());
|
||||
data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into());
|
||||
data.insert(
|
||||
"one_chapter".into(),
|
||||
self.html
|
||||
.book
|
||||
.options
|
||||
.get_bool("html.standalone.one_chapter")
|
||||
.unwrap(),
|
||||
)
|
||||
.insert_str("style", css.as_ref())
|
||||
.insert_str(
|
||||
"print_style",
|
||||
self.html.book.get_template("html.css.print").unwrap(),
|
||||
)
|
||||
.insert_str("menu_svg", menu_svg)
|
||||
.insert_str("book_svg", book_svg)
|
||||
.insert_str("pages_svg", pages_svg)
|
||||
.insert_str("json_data", self.html.get_json_ld()?)
|
||||
.insert_str("footer", HtmlRenderer::get_footer(self)?)
|
||||
.insert_str("header", HtmlRenderer::get_header(self)?);
|
||||
.unwrap()
|
||||
.into(),
|
||||
);
|
||||
data.insert("style".into(), css.into());
|
||||
data.insert(
|
||||
"print_style".into(),
|
||||
self.html.book.get_template("html.css.print").unwrap().into(),
|
||||
);
|
||||
data.insert("menu_svg".into(), menu_svg.clone().into());
|
||||
data.insert("book_svg".into(), book_svg.clone().into());
|
||||
data.insert("pages_svg".into(), pages_svg.clone().into());
|
||||
data.insert("json_data".into(), self.html.get_json_ld()?.into());
|
||||
data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into());
|
||||
data.insert("header".into(), HtmlRenderer::get_header(self)?.into());
|
||||
if let Ok(favicon) = self.html.book.options.get_path("html.icon") {
|
||||
let favicon = self
|
||||
.html
|
||||
.handler
|
||||
.map_image(&self.html.book.source, favicon)?;
|
||||
mapbuilder = mapbuilder.insert_str(
|
||||
"favicon",
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">"),
|
||||
data.insert(
|
||||
"favicon".into(),
|
||||
format!("<link rel = \"icon\" href = \"{favicon}\">").into(),
|
||||
);
|
||||
}
|
||||
if !self.html.toc.is_empty() {
|
||||
mapbuilder = mapbuilder.insert_bool("has_toc", true);
|
||||
mapbuilder = mapbuilder.insert_str("toc", toc)
|
||||
data.insert("has_toc".into(), true.into());
|
||||
data.insert("toc".into(), toc.into());
|
||||
}
|
||||
if self.html.highlight == Highlight::Js {
|
||||
let highlight_js = misc::u8_to_base64(&self
|
||||
|
@ -310,26 +308,20 @@ impl<'a> HtmlSingleRenderer<'a> {
|
|||
.get_template("html.highlight.js")?
|
||||
.as_bytes());
|
||||
let highlight_js = format!("data:text/javascript;base64,{highlight_js}");
|
||||
mapbuilder = mapbuilder
|
||||
.insert_bool("highlight_code", true)
|
||||
.insert_str(
|
||||
"highlight_css",
|
||||
self.html.book.get_template("html.highlight.css")?,
|
||||
)
|
||||
.insert_str("highlight_js", highlight_js);
|
||||
data.insert("highlight_code".into(), true.into());
|
||||
data.insert(
|
||||
"highlight_css".into(),
|
||||
self.html.book.get_template("html.highlight.css")?.into(),
|
||||
);
|
||||
data.insert("highlight_js".into(), highlight_js.into());
|
||||
}
|
||||
let data = mapbuilder.build();
|
||||
let template = compile_str(
|
||||
self.html
|
||||
.book
|
||||
.get_template("html.standalone.template")?
|
||||
.as_ref(),
|
||||
let template_src = self.html.book.get_template("html.standalone.template")?;
|
||||
let template = self.html.book.compile_str(
|
||||
template_src.as_ref(),
|
||||
&self.html.book.source,
|
||||
"html.standalone.template",
|
||||
)?;
|
||||
let mut res = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
Ok(String::from_utf8_lossy(&res).into_owned())
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
104
src/lib/latex.rs
104
src/lib/latex.rs
|
@ -15,7 +15,7 @@
|
|||
// You should have received ba copy of the GNU Lesser General Public License
|
||||
// along with Crowbook. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::book::{compile_str, Book};
|
||||
use crate::book::Book;
|
||||
use crate::book_renderer::BookRenderer;
|
||||
use crate::error::{Error, Result, Source};
|
||||
use crate::number::Number;
|
||||
|
@ -39,7 +39,7 @@ use std::iter::Iterator;
|
|||
|
||||
/// LaTeX renderer
|
||||
pub struct LatexRenderer<'a> {
|
||||
book: &'a Book,
|
||||
book: &'a Book<'a>,
|
||||
current_chapter: Number,
|
||||
handler: ResourceHandler,
|
||||
source: Source,
|
||||
|
@ -210,96 +210,80 @@ impl<'a> LatexRenderer<'a> {
|
|||
}
|
||||
});
|
||||
|
||||
let template = compile_str(
|
||||
self.book.get_template("tex.template")?.as_ref(),
|
||||
let template_src = self.book.get_template("tex.template")?;
|
||||
let template = self.book.compile_str(
|
||||
template_src.as_ref(),
|
||||
&self.book.source,
|
||||
"tex.template",
|
||||
)?;
|
||||
)?;
|
||||
let mut data = self
|
||||
.book
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?
|
||||
.insert_str("content", content)
|
||||
.insert_str("class", self.book.options.get_str("tex.class").unwrap())
|
||||
.insert_bool(
|
||||
"tex_title",
|
||||
self.book.options.get_bool("tex.title").unwrap(),
|
||||
)
|
||||
.insert_str(
|
||||
"papersize",
|
||||
self.book.options.get_str("tex.paper.size").unwrap(),
|
||||
)
|
||||
.insert_bool(
|
||||
"stdpage",
|
||||
self.book.options.get_bool("tex.stdpage").unwrap(),
|
||||
)
|
||||
.insert_bool("use_url", self.book.features.url)
|
||||
.insert_bool("use_taskitem", self.book.features.taskitem)
|
||||
.insert_bool("use_tables", self.book.features.table)
|
||||
.insert_bool("use_codeblocks", self.book.features.codeblock)
|
||||
.insert_bool("use_images", self.book.features.image)
|
||||
.insert_bool("use_strikethrough", self.book.features.strikethrough)
|
||||
.insert_str("tex_lang", tex_lang);
|
||||
.get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?;
|
||||
data.insert("content".into(), content.into());
|
||||
data.insert("class".into(), self.book.options.get_str("tex.class").unwrap().into());
|
||||
data.insert("tex_title".into(), self.book.options.get_bool("tex.title").unwrap().into());
|
||||
data.insert("papersize".into(), self.book.options.get_str("tex.paper.size").unwrap().into());
|
||||
data.insert("stdpage".into(), self.book.options.get_bool("tex.stdpage").unwrap().into());
|
||||
data.insert("use_url".into(), self.book.features.url.into());
|
||||
data.insert("use_taskitem".into(), self.book.features.taskitem.into());
|
||||
data.insert("use_tables".into(), self.book.features.table.into());
|
||||
data.insert("use_codeblocks".into(), self.book.features.codeblock.into());
|
||||
data.insert("use_images".into(), self.book.features.image.into());
|
||||
data.insert("use_strikethrough".into(), self.book.features.strikethrough.into());
|
||||
data.insert("tex_lang".into(), tex_lang.into());
|
||||
if let Ok(tex_tmpl_add) = self.book.options.get_str("tex.template.add") {
|
||||
data = data.insert_str("additional_code", tex_tmpl_add);
|
||||
data.insert("additional_code".into(), tex_tmpl_add.into());
|
||||
}
|
||||
if let Ok(tex_font_size) = self.book.options.get_i32("tex.font.size") {
|
||||
data = data
|
||||
.insert_bool("has_tex_size", true)
|
||||
.insert_str("tex_size", format!("{tex_font_size}"));
|
||||
data.insert("has_tex_size".into(), true.into());
|
||||
data.insert("tex_size".into(), format!("{tex_font_size}").into());
|
||||
}
|
||||
|
||||
// If class isn't book, set open_any to true, so margins are symetric.
|
||||
let mut book = false;
|
||||
if self.book.options.get_str("tex.class").unwrap() == "book" {
|
||||
data = data.insert_bool("book", true);
|
||||
data.insert("book".into(), true.into());
|
||||
book = true;
|
||||
}
|
||||
data = data
|
||||
.insert_str(
|
||||
"margin_left",
|
||||
data.insert(
|
||||
"margin_left".into(),
|
||||
self.book
|
||||
.options
|
||||
.get_str("tex.margin.left")
|
||||
.unwrap_or(if book { "2.5cm" } else { "2cm" }),
|
||||
)
|
||||
.insert_str(
|
||||
"margin_right",
|
||||
.unwrap_or(if book { "2.5cm" } else { "2cm" }).into(),
|
||||
);
|
||||
data.insert(
|
||||
"margin_right".into(),
|
||||
self.book
|
||||
.options
|
||||
.get_str("tex.margin.right")
|
||||
.unwrap_or(if book { "1.5cm" } else { "2cm" }),
|
||||
)
|
||||
.insert_str(
|
||||
"margin_bottom",
|
||||
self.book.options.get_str("tex.margin.bottom").unwrap(),
|
||||
)
|
||||
.insert_str(
|
||||
"margin_top",
|
||||
self.book.options.get_str("tex.margin.top").unwrap(),
|
||||
);
|
||||
.unwrap_or(if book { "1.5cm" } else { "2cm" }).into(),
|
||||
);
|
||||
data.insert(
|
||||
"margin_bottom".into(),
|
||||
self.book.options.get_str("tex.margin.bottom").unwrap().into(),
|
||||
);
|
||||
data.insert(
|
||||
"margin_top".into(),
|
||||
self.book.options.get_str("tex.margin.top").unwrap().into(),
|
||||
);
|
||||
|
||||
if let Ok(chapter_name) = self.book.options.get_str("rendering.chapter") {
|
||||
data = data.insert_str("chapter_name", chapter_name);
|
||||
data.insert("chapter_name".into(), chapter_name.into());
|
||||
}
|
||||
if let Ok(part_name) = self.book.options.get_str("rendering.part") {
|
||||
data = data.insert_str("part_name", part_name);
|
||||
data.insert("part_name".into(), part_name.into());
|
||||
}
|
||||
if self.book.options.get_bool("rendering.initials") == Ok(true) {
|
||||
data = data.insert_bool("initials", true);
|
||||
data.insert("initials".into(), true.into());
|
||||
}
|
||||
// Insert xelatex if tex.command is set to xelatex or tectonic
|
||||
if (self.book.options.get_str("tex.command") == Ok("xelatex"))
|
||||
| (self.book.options.get_str("tex.command") == Ok("tectonic"))
|
||||
{
|
||||
data = data.insert_bool("xelatex", true);
|
||||
}
|
||||
let data = data.build();
|
||||
let mut res: Vec<u8> = vec![];
|
||||
template.render_data(&mut res, &data)?;
|
||||
match String::from_utf8(res) {
|
||||
Err(_) => panic!("{}", lformat!("generated LaTeX was not valid utf-8")),
|
||||
Ok(res) => Ok(res),
|
||||
data.insert("xelatex".into(), true.into());
|
||||
}
|
||||
Ok(template.render(&data).to_string()?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2016, 2017 Élisabeth HENRY.
|
||||
// Copyright (C) 2016-2023 Élisabeth HENRY.
|
||||
//
|
||||
// This file is part of Crowbook.
|
||||
//
|
||||
|
@ -7,7 +7,7 @@
|
|||
// by the Free Software Foundation, either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Caribon is distributed in the hope that it will be useful,
|
||||
// Crowbook is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
|
|
Loading…
Reference in New Issue