1
0
mirror of https://github.com/lise-henry/crowbook synced 2024-11-18 00:13:55 +01:00

refactored Book to use a map instead of 42 fields

This commit is contained in:
Elisabeth Henry 2016-02-22 23:21:51 +01:00
parent ce3c47e447
commit 6218ca91cf
7 changed files with 170 additions and 190 deletions

@ -31,35 +31,33 @@ use std::io;
/// Render a book to specific format
fn render_format(book: &mut Book, matches: &ArgMatches, format: &str) -> ! {
if let Some(file) = matches.value_of("output") {
let value = Some(file.to_owned());
match format {
"epub" => book.output_epub = value,
"tex" => book.output_tex = value,
"html" => book.output_html = value,
"pdf" => book.output_pdf = value,
"odt" => book.output_odt = value,
"epub" => book.set_option("output.epub", file).unwrap(),
"tex" => book.set_option("output.tex", file).unwrap(),
"html" => book.set_option("output.html", file).unwrap(),
"pdf" => book.set_option("output.pdf", file).unwrap(),
"odt" => book.set_option("output.odt", file).unwrap(),
_ => unreachable!()
}
}
let option = match format {
// if let &Some(ref file) = match format {
"epub" => &book.output_epub,
"tex" => &book.output_tex,
"html" => &book.output_html,
"pdf" => &book.output_pdf,
"odt" => &book.output_odt,
"epub" => book.get_str("output.epub"),
"tex" => book.get_str("output.tex"),
"html" => book.get_str("output.html"),
"pdf" => book.get_str("output.pdf"),
"odt" => book.get_str("output.odt"),
_ => unreachable!()
};
let result = match *option {
None => {
let result = match option {
Err(_) => {
match format {
"html" => book.render_html(&mut io::stdout()),
"tex" => book.render_tex(&mut io::stdout()),
_ => print_error(&format!("No output file specified, and book doesn't specify an output file for {}", format)),
}
},
Some(ref file) => {
Ok(file) => {
match format {
"epub" => book.render_epub(),
"tex" => {

@ -1,5 +1,6 @@
use error::{Error,Result};
use cleaner::{Cleaner, French};
use bookoption::BookOption;
use parser::Parser;
use token::Token;
use epub::EpubRenderer;
@ -14,6 +15,7 @@ use std::io::{self, Write,Read};
use std::env;
use std::path::Path;
use std::borrow::Cow;
use std::collections::HashMap;
use mustache;
use mustache::MapBuilder;
@ -26,84 +28,46 @@ pub enum Number {
Default, // chapter follows books numbering, number is given automatically
Specified(i32), //chapter number set to specified number
}
static VALID_BOOLS:&'static [&'static str] = &["numbering", "autoclean", "verbose", "tex.links_as_footnotes"];
static VALID_CHARS:&'static [&'static str] = &["nb_char"];
static VALID_INTS:&'static [&'static str] = &["epub.version"];
static VALID_STRINGS:&'static [&'static str] = &["lang", "author", "description", "title", "subject", "cover", "output.epub",
"output.html", "output.pdf", "output.tex", "output.odt", "temp_dir",
"numbering_template", "tex.command", "tex.template",
"epub.css", "epub.template", "epub.version", "html.template", "html.css"];
static DEFAULT_OPTIONS: &'static[(&'static str, &'static str)] = &[("verbose", "false"), ("numbering", "true"),
("autoclean", "true"), ("lang", "fr"),
("author", "Anonymous"), ("title", "Untitled"),
("nb_char", "''"), ("tex.command", "pdflatex"),
("numbering_template", "{{number}}. {{title}}"),
("temp_dir", "."), ("tex.links_as_footnotes", "true"),
("epub.version", "2")];
// Configuration of the book
#[derive(Debug)]
pub struct Book {
// internal structure
pub chapters: Vec<(Number, Vec<Token>)>,
// Metadata
pub lang: String,
pub author: String,
pub title: String,
pub description: Option<String>,
pub subject: Option<String>,
pub cover: Option<String>,
// Output files
pub output_epub: Option<String>,
pub output_html: Option<String>,
pub output_pdf: Option<String>,
pub output_tex: Option<String>,
pub output_odt: Option<String>,
pub temp_dir: String,
// options
pub numbering: bool, // turns on/off chapter numbering (individual chapters may still avoid it)
pub autoclean: bool,
pub nb_char: char,
pub numbering_template: String, // template for chapter numbering
pub verbose: bool,
// for latex
pub tex_command: String,
pub tex_links_as_footnotes: bool,
pub tex_template: Option<String>,
// for epub
pub epub_css: Option<String>,
pub epub_template: Option<String>,
pub epub_version: u8,
// for HTML
pub html_template: Option<String>,
pub html_css: Option<String>,
/// book options
options: HashMap<String, BookOption>,
}
impl Book {
// Creates a new Book with default options
pub fn new() -> Book {
Book {
verbose: false,
numbering: true,
autoclean: true,
let mut book = Book {
chapters: vec!(),
lang: String::from("en"),
author: String::from("Anonymous"),
title: String::from("Untitled"),
description: None,
subject: None,
cover: None,
nb_char: '',
numbering_template: String::from("{{number}}. {{title}}"),
temp_dir: String::from("."),
output_epub: None,
output_html: None,
output_pdf: None,
output_tex: None,
output_odt: None,
tex_command: String::from("pdflatex"),
tex_links_as_footnotes: true,
tex_template: None,
epub_css: None,
epub_template: None,
epub_version: 2,
html_template: None,
html_css: None,
options: HashMap::new(),
};
for &(key, value) in DEFAULT_OPTIONS {
book.set_option(key, value).unwrap();
}
book
}
/// Prints to stderr
@ -113,7 +77,7 @@ impl Book {
/// Prints to stderr but only if verbose is set to true
pub fn debug(&self, s:&str) {
if self.verbose {
if self.get_bool("verbose").unwrap() {
writeln!(&mut io::stderr(), "{}", s).unwrap();
}
}
@ -141,7 +105,9 @@ impl Book {
try!(f.read_to_string(&mut s).map_err(|_| Error::ConfigParser("file contains invalid UTF-8, could not parse it",
String::from(filename))));
let mut book = Book::new();
book.verbose = verbose;
if verbose {
book.set_option("verbose", "true").unwrap();
}
try!(book.set_from_config(&s));
Ok(book)
}
@ -158,9 +124,9 @@ impl Book {
_ => panic!("get mapbuilder called with invalid escape format")
};
MapBuilder::new()
.insert_str("author", f(&self.author))
.insert_str("title", f(&self.title))
.insert_str("lang", self.lang.clone())
.insert_str("author", f(self.get_str("author").unwrap()))
.insert_str("title", f(&self.get_str("title").unwrap()))
.insert_str("lang", self.get_str("lang").unwrap().to_owned())
}
/// Either clean a string or does nothing
@ -173,10 +139,10 @@ impl Book {
/// Return a Box<Cleaner> corresponding to the appropriate cleaning method, or None
pub fn get_cleaner(&self) -> Option<Box<Cleaner>> {
if self.autoclean {
let lang = self.lang.to_lowercase();
if self.get_bool("autoclean").unwrap() {
let lang = self.get_str("lang").unwrap().to_lowercase();
if lang.starts_with("fr") {
Some(Box::new(French::new(self.nb_char)))
Some(Box::new(French::new(self.get_char("nb_char").unwrap())))
} else {
Some(Box::new(()))
}
@ -187,7 +153,7 @@ impl Book {
/// Returns the string corresponding to a number, title, and the numbering template
pub fn get_header(&self, n: i32, title: &str) -> Result<String> {
let template = mustache::compile_str(&self.numbering_template);
let template = mustache::compile_str(self.get_str("numbering_template").unwrap());
let data = MapBuilder::new()
.insert_str("title", String::from(title))
.insert_str("number", format!("{}", n))
@ -200,61 +166,70 @@ impl Book {
}
}
/// get an option
pub fn get_option(&self, key: &str) -> Result<&BookOption> {
self.options.get(key).ok_or(Error::InvalidOption(format!("option {} is not present", key)))
}
/// gets a string option as str
pub fn get_str(&self, key: &str) -> Result<&str> {
try!(self.get_option(key)).as_str()
}
/// gets a bool option
pub fn get_bool(&self, key: &str) -> Result<bool> {
try!(self.get_option(key)).as_bool()
}
/// gets a char option
pub fn get_char(&self, key: &str) -> Result<char> {
try!(self.get_option(key)).as_char()
}
/// gets an int option
pub fn get_i32(&self, key: &str) -> Result<i32> {
try!(self.get_option(key)).as_i32()
}
/// Sets an option
pub fn set_option(&mut self, key: &str, value: &str) -> Result<()> {
// checks that str is a char and returns it
fn get_char(s: &str) -> Result<char> {
let words: Vec<_> = s.trim().split('\'').collect();
if VALID_STRINGS.contains(&key) {
self.options.insert(key.to_owned(), BookOption::String(value.to_owned()));
Ok(())
} else if VALID_CHARS.contains(&key) {
let words: Vec<_> = value.trim().split('\'').collect();
if words.len() != 3 {
return Err(Error::ConfigParser("could not parse char", String::from(s)));
return Err(Error::ConfigParser("could not parse char", String::from(value)));
}
let chars: Vec<_> = words[1].chars().collect();
if chars.len() != 1 {
return Err(Error::ConfigParser("could not parse char", String::from(s)));
return Err(Error::ConfigParser("could not parse char", String::from(value)));
}
Ok(chars[0])
self.options.insert(key.to_owned(), BookOption::Char(chars[0]));
Ok(())
} else if VALID_BOOLS.contains(&key) {
match value.parse::<bool>() {
Ok(b) => {
self.options.insert(key.to_owned(), BookOption::Bool(b));
()
},
Err(_) => return Err(Error::ConfigParser("could not parse bool", format!("{}:{}", key, value))),
}
Ok(())
} else if VALID_INTS.contains(&key) {
match value.parse::<i32>() {
Ok(i) => {
self.options.insert(key.to_owned(), BookOption::Int(i));
}
Err(_) => return Err(Error::ConfigParser("could not parse int", format!("{}:{}", key, value))),
}
Ok(())
} else {
Err(Error::ConfigParser("unrecognized key", String::from(key)))
}
// convert an error on parsing bool to a Crowbook::Error
let bool_error = |_| {
Error::ConfigParser("could not parse bool", format!("{}:{}", key, value))
};
match key {
"nb-char" | "nb_char" => self.nb_char = try!(get_char(value)),
"numbering-template" | "numbering_template" => self.numbering_template = String::from(value),
"numbering" => self.numbering = try!(value.parse::<bool>().map_err(bool_error)),
"autoclean" => self.autoclean = try!(value.parse::<bool>().map_err(bool_error)),
"temp_dir" | "temp-dir" => self.temp_dir = String::from(value),
"output.epub" |"output_epub" | "output-epub" => self.output_epub = Some(String::from(value)),
"output.html"| "output_html" | "output-html" => self.output_html = Some(String::from(value)),
"output.tex" |"output_tex" | "output-tex" => self.output_tex = Some(String::from(value)),
"output.pdf" | "output_pdf" | "output-pdf" => self.output_pdf = Some(String::from(value)),
"output.odt" | "output_odt" | "output-odt" => self.output_odt = Some(String::from(value)),
"tex.command" | "tex_command" | "tex-command" => self.tex_command = String::from(value),
"tex.links-as-footnotes" | "tex.links_as_footnotes" => self.tex_links_as_footnotes = try!(value.parse::<bool>().map_err(bool_error)),
"tex.template" => self.tex_template = Some(String::from(value)),
"author" => self.author = String::from(value),
"title" => self.title = String::from(value),
"cover" => self.cover = Some(String::from(value)),
"lang" => self.lang = String::from(value),
"description" => self.description = Some(String::from(value)),
"subject" => self.subject = Some(String::from(value)),
"epub.css" | "epub_css" | "epub-css" => self.epub_css = Some(String::from(value)),
"epub.template" | "epub_template" | "epub-template" => self.epub_template = Some(String::from(value)),
"epub.version" | "epub_version" | "epub-version" => self.epub_version = match value {
"2" => 2,
"3" => 3,
_ => return Err(Error::ConfigParser("epub_version must either be 2 or 3", String::from(value))),
},
"html.template" | "html_template" | "html-template" => self.html_template = Some(String::from(value)),
"html.css" | "html_css" | "html-css" => self.html_css = Some(String::from(value)),
_ => return Err(Error::ConfigParser("unrecognized key", String::from(key))),
}
Ok(())
}
/// Sets options according to configuration file
/// Sets options and load chapters according to configuration file
///
/// A line with "option: value" sets the option to value
/// + chapter_name.md adds the (default numbered) chapter
@ -319,7 +294,7 @@ impl Book {
let mut latex = LatexRenderer::new(&self);
let result = try!(latex.render_pdf());
self.debug(&result);
self.println(&format!("Successfully generated pdf file: {}", self.output_pdf.as_ref().unwrap()));
self.println(&format!("Successfully generated pdf file: {}", self.get_str("output.pdf").unwrap()));
Ok(())
}
@ -329,7 +304,7 @@ impl Book {
let mut epub = EpubRenderer::new(&self);
let result = try!(epub.render_book());
self.debug(&result);
self.println(&format!("Successfully generated epub file: {}", self.output_epub.as_ref().unwrap()));
self.println(&format!("Successfully generated epub file: {}", self.get_str("output.epub").unwrap()));
Ok(())
}
@ -339,7 +314,7 @@ impl Book {
let mut odt = OdtRenderer::new(&self);
let result = try!(odt.render_book());
self.debug(&result);
self.println(&format!("Successfully generated odt file: {}", self.output_odt.as_ref().unwrap()));
self.println(&format!("Successfully generated odt file: {}", self.get_str("output.odt").unwrap()));
Ok(())
}
@ -367,28 +342,28 @@ impl Book {
/// Generates output files acccording to book options
pub fn render_all(&self) -> Result<()> {
let mut did_some_stuff = false;
if self.output_epub.is_some() {
if self.get_option("output_epub").is_ok() {
did_some_stuff = true;
try!(self.render_epub());
}
if let Some(ref file) = self.output_html {
if let Ok(file) = self.get_str("output.html") {
did_some_stuff = true;
let mut f = try!(File::create(file).map_err(|_| Error::Render("could not create HTML file")));
try!(self.render_html(&mut f));
}
if let Some(ref file) = self.output_tex {
if let Ok(file) = self.get_str("output.tex") {
did_some_stuff = true;
let mut f = try!(File::create(file).map_err(|_| Error::Render("could not create LaTeX file")));
try!(self.render_tex(&mut f));
}
if self.output_pdf.is_some() {
if self.get_str("output.pdf").is_ok() {
did_some_stuff = true;
try!(self.render_pdf());
}
if self.output_odt.is_some() {
if self.get_str("output.odt").is_ok() {
did_some_stuff = true;
try!(self.render_odt());
}
@ -411,15 +386,15 @@ impl Book {
/// Returns the template (default or modified version)
pub fn get_template(&self, template: &str) -> Result<Cow<'static, str>> {
let (option, fallback) = match template {
"epub_css" => (&self.epub_css, epub::CSS),
"epub_template" => (&self.epub_template,
if self.epub_version == 3 {epub3::TEMPLATE} else {epub::TEMPLATE}),
"html_css" => (&self.html_css, html::CSS),
"html_template" => (&self.html_template, html::TEMPLATE),
"tex_template" => (&self.tex_template, latex::TEMPLATE),
_ => return Err(Error::ConfigParser("invalid template", template.to_owned())),
"epub.css" => (self.get_str("epub.css"), epub::CSS),
"epub.template" => (self.get_str("epub.template"),
if try!(self.get_i32("epub.version")) == 3 {epub3::TEMPLATE} else {epub::TEMPLATE}),
"html.css" => (self.get_str("html.css"), html::CSS),
"html.template" => (self.get_str("html.template"), html::TEMPLATE),
"tex.template" => (self.get_str("tex.template"), latex::TEMPLATE),
_ => panic!("")//return Err(Error::ConfigParser("invalid template", template.to_owned())),
};
if let Some (ref s) = *option {
if let Ok (s) = option {
let mut f = try!(File::open(s).map_err(|_| Error::FileNotFound(s.to_owned())));
let mut res = String::new();
try!(f.read_to_string(&mut res)

@ -52,7 +52,7 @@ impl<'a> EpubRenderer<'a> {
/// Render a book
pub fn render_book(&mut self) -> Result<String> {
let mut zipper = try!(Zipper::new(&self.book.temp_dir));
let mut zipper = try!(Zipper::new(&self.book.get_str("temp_dir").unwrap()));
// Write mimetype
try!(zipper.write("mimetype", b"application/epub+zip", true));
@ -60,11 +60,12 @@ impl<'a> EpubRenderer<'a> {
// Write chapters
for (i, &(n, ref v)) in self.book.chapters.iter().enumerate() {
self.html.current_hide = false;
let book_numbering = self.book.get_bool("numbering").unwrap();
match n {
Number::Unnumbered => self.html.current_numbering = false,
Number::Default => self.html.current_numbering = self.book.numbering,
Number::Default => self.html.current_numbering = book_numbering,
Number::Specified(n) => {
self.html.current_numbering = self.book.numbering;
self.html.current_numbering = book_numbering;
self.html.current_chapter = n;
},
Number::Hidden => {
@ -78,7 +79,7 @@ impl<'a> EpubRenderer<'a> {
// Write CSS file
try!(zipper.write("stylesheet.css",
&try!(self.book.get_template("epub_css")).as_bytes(), true));
&try!(self.book.get_template("epub.css")).as_bytes(), true));
// Write titlepage
try!(zipper.write("title_page.xhtml", &try!(self.render_titlepage()).as_bytes(), true));
@ -99,7 +100,7 @@ impl<'a> EpubRenderer<'a> {
try!(zipper.write("toc.ncx", &try!(self.render_toc()).as_bytes(), true));
// Write the cover (if needs be)
if let Some(ref cover) = self.book.cover {
if let Ok(cover) = self.book.get_str("cover") {
let s: &str = &*cover;
let mut f = try!(File::open(s).map_err(|_| Error::FileNotFound(String::from(s))));
let mut content = vec!();
@ -110,7 +111,7 @@ impl<'a> EpubRenderer<'a> {
try!(zipper.write("cover.xhtml", &try!(self.render_cover()).as_bytes(), true));
}
if let Some(ref epub_file) = self.book.output_epub {
if let Ok(epub_file) = self.book.get_str("output.epub") {
let res = try!(zipper.generate_epub(epub_file));
Ok(res)
} else {
@ -120,7 +121,7 @@ impl<'a> EpubRenderer<'a> {
/// Render the titlepgae
fn render_titlepage(&self) -> Result<String> {
let template = mustache::compile_str(if self.book.epub_version == 3 {epub3::TITLE} else {TITLE});
let template = mustache::compile_str(if self.book.get_i32("epub.version").unwrap() == 3 {epub3::TITLE} else {TITLE});
let data = self.book.get_mapbuilder("none")
.build();
let mut res:Vec<u8> = vec!();
@ -163,13 +164,13 @@ impl<'a> EpubRenderer<'a> {
// Optional metadata
let mut cover_xhtml = String::new();
let mut optional = String::new();
if let Some(ref s) = self.book.description {
if let Ok(s) = self.book.get_str("description") {
optional.push_str(&format!("<dc:description>{}</dc:description>\n", s));
}
if let Some(ref s) = self.book.subject {
if let Ok(s) = self.book.get_str("subject") {
optional.push_str(&format!("<dc:subject>{}</dc:subject>\n", s));
}
if let Some(ref s) = self.book.cover {
if let Ok(s) = self.book.get_str("cover") {
optional.push_str(&format!("<meta name = \"cover\" content = \"{}\" />\n", s));
cover_xhtml.push_str(&format!("<reference type=\"cover\" title=\"Cover\" href=\"cover.xhtml\" />"));
}
@ -183,7 +184,7 @@ impl<'a> EpubRenderer<'a> {
let mut items = String::new();
let mut itemrefs = String::new();
let mut coverref = String::new();
if let Some(_) = self.book.cover {
if self.book.get_str("cover").is_ok() {
items.push_str("<item id = \"cover_xhtml\" href = \"cover.xhtml\" media-type = \"application/xhtml+xml\" />\n");
coverref.push_str("<itemref idref = \"cover_xhtml\" />");
}
@ -195,7 +196,7 @@ impl<'a> EpubRenderer<'a> {
itemrefs.push_str(&format!("<itemref idref=\"{}\" />\n", to_id(&filename)));
}
// oh we must put cover in the manifest too
if let Some(ref s) = self.book.cover {
if let Ok(s) = self.book.get_str("cover") {
let format = if let Some(ext) = Path::new(s).extension() {
if let Some(extension) = ext.to_str() {
match extension {
@ -216,13 +217,13 @@ impl<'a> EpubRenderer<'a> {
"png"
};
items.push_str(&format!("<item {} media-type = \"image/{}\" id =\"{}\" href = \"{}\" />\n",
if self.book.epub_version == 3 { "properties=\"cover-image\"" } else { "" },
if self.book.get_i32("epub.version").unwrap() == 3 { "properties=\"cover-image\"" } else { "" },
format,
to_id(s),
s));
}
let template = mustache::compile_str(if self.book.epub_version == 3 {epub3::OPF} else {OPF});
let template = mustache::compile_str(if self.book.get_i32("epub.version").unwrap() == 3 {epub3::OPF} else {OPF});
let data = self.book.get_mapbuilder("none")
.insert_str("optional", optional)
.insert_str("items", items)
@ -242,8 +243,8 @@ impl<'a> EpubRenderer<'a> {
/// Render cover.xhtml
fn render_cover(&self) -> Result<String> {
if let Some(ref cover) = self.book.cover {
let template = mustache::compile_str(if self.book.epub_version == 3 {epub3::COVER} else {COVER});
if let Ok(ref cover) = self.book.get_str("cover") {
let template = mustache::compile_str(if self.book.get_i32("epub.version").unwrap() == 3 {epub3::COVER} else {COVER});
let data = self.book.get_mapbuilder("none")
.insert_str("cover", cover.clone())
.build();
@ -268,7 +269,7 @@ impl<'a> EpubRenderer<'a> {
title));
}
let template = mustache::compile_str(if self.book.epub_version == 3 {epub3::NAV} else {NAV});
let template = mustache::compile_str(if self.book.get_i32("epub.version").unwrap() == 3 {epub3::NAV} else {NAV});
let data = self.book.get_mapbuilder("none")
.insert_str("content", content)
.build();
@ -301,7 +302,7 @@ impl<'a> EpubRenderer<'a> {
}
self.toc.push(title.clone());
let template = mustache::compile_str(try!(self.book.get_template("epub_template")).as_ref());
let template = mustache::compile_str(try!(self.book.get_template("epub.template")).as_ref());
let data = self.book.get_mapbuilder("none")
.insert_str("content", content)
.insert_str("chapter_title", title)

@ -27,14 +27,15 @@ pub enum Error {
FileNotFound(String), //file
Render(&'static str),
Zipper(String),
BookOption(String)
BookOption(String),
InvalidOption(String),
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::ConfigParser(ref s, _) | Error::Render(ref s) => s,
Error::Parser(ref s) | Error::Zipper(ref s) | Error::BookOption(ref s)=> s,
Error::Parser(ref s) | Error::Zipper(ref s) | Error::BookOption(ref s) | Error::InvalidOption(ref s) => s,
Error::FileNotFound(_) => "File not found",
}
}
@ -68,7 +69,11 @@ impl fmt::Display for Error {
Error::BookOption(ref s) => {
try!(f.write_str("Error converting BookOption: "));
f.write_str(s)
}
},
Error::InvalidOption(ref s) => {
try!(f.write_str("Error accessing book option: "));
f.write_str(s)
},
}
}
}

@ -41,7 +41,7 @@ impl<'a> HtmlRenderer<'a> {
HtmlRenderer {
book: book,
current_chapter: 1,
current_numbering: book.numbering,
current_numbering: book.get_bool("numbering").unwrap(),
current_hide: false,
table_head: false,
footnote_number: 0,
@ -55,11 +55,12 @@ impl<'a> HtmlRenderer<'a> {
for &(n, ref v) in &self.book.chapters {
self.current_hide = false;
let book_numbering = self.book.get_bool("numbering").unwrap();
match n {
Number::Unnumbered => self.current_numbering = false,
Number::Default => self.current_numbering = self.book.numbering,
Number::Default => self.current_numbering = book_numbering,
Number::Specified(n) => {
self.current_numbering = self.book.numbering;
self.current_numbering = book_numbering;
self.current_chapter = n;
},
Number::Hidden => {
@ -70,11 +71,11 @@ impl<'a> HtmlRenderer<'a> {
content.push_str(&self.render_html(v));
}
let template = mustache::compile_str(try!(self.book.get_template("html_template")).as_ref());
let template = mustache::compile_str(try!(self.book.get_template("html.template")).as_ref());
let data = self.book.get_mapbuilder("none")
.insert_str("content", content)
.insert_str("style",
&try!(self.book.get_template("html_css")))
&try!(self.book.get_template("html.css")))
.build();
let mut res:Vec<u8> = vec!();

@ -43,13 +43,13 @@ impl<'a> LatexRenderer<'a> {
/// Render pdf in a file
pub fn render_pdf(&mut self) -> Result<String> {
if let Some(ref pdf_file) = self.book.output_pdf {
if let Ok(pdf_file) = self.book.get_str("output.pdf") {
let base_file = try!(Path::new(pdf_file).file_stem().ok_or(Error::Render("could not stem pdf filename")));
let tex_file = format!("{}.tex", base_file.to_str().unwrap());
let content = try!(self.render_book());
let mut zipper = try!(Zipper::new(&self.book.temp_dir));
let mut zipper = try!(Zipper::new(self.book.get_str("temp_dir").unwrap()));
try!(zipper.write(&tex_file, &content.as_bytes(), false));
zipper.generate_pdf(&self.book.tex_command, &tex_file, pdf_file)
zipper.generate_pdf(&self.book.get_str("tex.command").unwrap(), &tex_file, pdf_file)
} else {
Err(Error::Render("no output pdf file specified in book config"))
}
@ -64,16 +64,16 @@ impl<'a> LatexRenderer<'a> {
}
let tex_lang = String::from(match &*self.book.lang {
let tex_lang = String::from(match self.book.get_str("lang").unwrap() {
"en" => "english",
"fr" => "francais",
_ => {
self.book.debug(&format!("Warning: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.lang));
self.book.debug(&format!("Warning: can't find a tex equivalent for lang '{}', fallbacking on english", self.book.get_str("lang").unwrap()));
"english"
}
});
let template = mustache::compile_str(try!(self.book.get_template("tex_template")).as_ref());
let template = mustache::compile_str(try!(self.book.get_template("tex.template")).as_ref());
let data = self.book.get_mapbuilder("tex")
.insert_str("content", content)
.insert_str("tex_lang", tex_lang)
@ -140,7 +140,7 @@ impl<'a> LatexRenderer<'a> {
Token::List(ref vec) => format!("\\begin{{itemize}}\n{}\\end{{itemize}}", self.render_vec(vec, escape)),
Token::OrderedList(_, ref vec) => format!("\\begin{{enumerate}}\n{}\\end{{enumerate}}\n", self.render_vec(vec, escape)),
Token::Item(ref vec) => format!("\\item {}\n", self.render_vec(vec, escape)),
Token::Link(ref url, _, ref vec) => if self.book.tex_links_as_footnotes {
Token::Link(ref url, _, ref vec) => if self.book.get_bool("tex.links_as_footnotes").unwrap() {
format!("\\href{{{}}}{{{}}}\\footnote{{\\url{{{}}}}}", escape_tex(url), self.render_vec(vec, escape), escape_tex(url))
} else {
format!("\\href{{{}}}{{{}}}", escape_tex(url), self.render_vec(vec, escape))

@ -23,7 +23,7 @@ impl<'a> OdtRenderer<'a> {
OdtRenderer {
book: book,
current_chapter: 1,
current_numbering: book.numbering,
current_numbering: book.get_bool("numbering").unwrap(),
current_hide: false,
automatic_styles: String::from("
<style:style style:name=\"T1\" style:family=\"text\">
@ -39,7 +39,7 @@ impl<'a> OdtRenderer<'a> {
pub fn render_book(&mut self) -> Result<String> {
let content = try!(self.render_content());
let mut zipper = try!(Zipper::new(&self.book.temp_dir));
let mut zipper = try!(Zipper::new(self.book.get_str("temp_dir").unwrap()));
// Write template.odt there
try!(zipper.write("template.odt", odt::ODT, false));
@ -48,7 +48,7 @@ impl<'a> OdtRenderer<'a> {
// Complete it with content.xml
try!(zipper.write("content.xml", &content.as_bytes(), false));
// Zip and copy
if let Some(ref file) = self.book.output_odt {
if let Ok(file) = self.book.get_str("output.odt") {
zipper.generate_odt(file)
} else {
panic!("odt.render_book called while book.output_odt is not set");
@ -63,9 +63,9 @@ impl<'a> OdtRenderer<'a> {
self.current_hide = false;
match n {
Number::Unnumbered => self.current_numbering = false,
Number::Default => self.current_numbering = self.book.numbering,
Number::Default => self.current_numbering = self.book.get_bool("numbering").unwrap(),
Number::Specified(n) => {
self.current_numbering = self.book.numbering;
self.current_numbering = self.book.get_bool("numbering").unwrap();
self.current_chapter = n;
},
Number::Hidden => {