1
0
Fork 0
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:
Lise Henry 2023-08-09 13:30:07 +02:00
parent 86f7cd5ea9
commit fe8d98ccd7
14 changed files with 364 additions and 457 deletions

12
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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 cant 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
),
)),
}
}

View File

@ -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();

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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}"))
}
}

View File

@ -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

View File

@ -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())

View File

@ -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()?)
}
}

View File

@ -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()?)
}
}

View File

@ -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()?)
}
}

View File

@ -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.