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:
parent
ce3c47e447
commit
6218ca91cf
@ -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" => {
|
||||
|
249
src/lib/book.rs
249
src/lib/book.rs
@ -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 => {
|
||||
|
Loading…
Reference in New Issue
Block a user