diff --git a/CHANGELOG.md b/CHANGELOG.md index d2626f3..12db2ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,15 @@ Possible log types: - `[fixed]` for any bug fixes. - `[security]` to invite users to upgrade in case of vulnerabilities. +### 0.9.0 + +- [changed] `Config::add_css` now returns `Result` instead of panicking on + CSS parse errors. Errors from parsing document CSS are ignored. +- [added] Support `` when CSS is enabled. +- [added] `Config::max_wrap_width()` to wrap text to a norrower width than + the overal size available. +- [added] Add --wrap-width and --css options to html2text example. + ### 0.8.0 - [added] CSS: Support more extensive selectors diff --git a/Cargo.toml b/Cargo.toml index 559cfbc..a37ee1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "html2text" -version = "0.8.0" +version = "0.9.0" authors = ["Chris Emerson "] description = "Render HTML as plain text." repository = "https://github.com/jugglerchris/rust-html2text/" diff --git a/README.md b/README.md index 7cfab0f..a384c39 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,9 @@ let html = b" assert_eq!( config::plain() - .string_from_read(html, 20), - Ok("\ + .string_from_read(&html[..], 20) + .unwrap(), + "\ * Item one * Item two * Item three diff --git a/examples/html2text.rs b/examples/html2text.rs index 7821b5f..a9164eb 100644 --- a/examples/html2text.rs +++ b/examples/html2text.rs @@ -1,6 +1,8 @@ extern crate argparse; extern crate html2text; use argparse::{ArgumentParser, Store, StoreOption, StoreTrue}; +use html2text::config::{Config, self}; +use html2text::render::text_renderer::{TextDecorator, TrivialDecorator}; use std::io; use std::io::Write; @@ -76,31 +78,63 @@ fn default_colour_map(annotations: &[RichAnnotation], s: &str) -> String { result } -fn translate(input: R, width: usize, literal: bool, _use_colour: bool) -> String +fn update_config(mut config: Config, flags: &Flags) -> Config { + if let Some(wrap_width) = flags.wrap_width { + config = config.max_wrap_width(wrap_width); + } + #[cfg(feature = "css")] + if flags.use_css { + config = config.use_doc_css(); + } + config +} + +fn translate(input: R, flags: Flags, literal: bool) -> String where R: io::Read, { #[cfg(unix)] { - if _use_colour { - return html2text::from_read_coloured(input, width, default_colour_map).unwrap(); + if flags.use_colour { + let conf = config::rich(); + let conf = update_config(conf, &flags); + return conf.coloured(input, flags.width, default_colour_map) + .unwrap() } } if literal { - let decorator = html2text::render::text_renderer::TrivialDecorator::new(); - html2text::from_read_with_decorator(input, width, decorator) + let conf = config::with_decorator(TrivialDecorator::new()); + let conf = update_config(conf, &flags); + conf.string_from_read(input, flags.width) + .unwrap() } else { - html2text::from_read(input, width) + let conf = config::plain(); + let conf = update_config(conf, &flags); + conf.string_from_read(input, flags.width) + .unwrap() } } +struct Flags { + width: usize, + wrap_width: Option, + #[allow(unused)] + use_colour: bool, + #[cfg(feature = "css")] + use_css: bool, +} + fn main() { let mut infile: Option = None; let mut outfile: Option = None; - let mut width: usize = 80; + let mut flags = Flags { + width: 80, + wrap_width: None, + use_colour: false, + #[cfg(feature = "css")] + use_css: false, + }; let mut literal: bool = false; - #[allow(unused)] - let mut use_colour = false; { let mut ap = ArgumentParser::new(); @@ -109,11 +143,16 @@ fn main() { StoreOption, "Input HTML file (default is standard input)", ); - ap.refer(&mut width).add_option( + ap.refer(&mut flags.width).add_option( &["-w", "--width"], Store, "Column width to format to (default is 80)", ); + ap.refer(&mut flags.wrap_width).add_option( + &["-W", "--wrap-width"], + StoreOption, + "Maximum text wrap width (default same as width)", + ); ap.refer(&mut outfile).add_option( &["-o", "--output"], StoreOption, @@ -125,20 +164,23 @@ fn main() { "Output only literal text (no decorations)", ); #[cfg(unix)] - ap.refer(&mut use_colour) + ap.refer(&mut flags.use_colour) .add_option(&["--colour"], StoreTrue, "Use ANSI terminal colours"); + #[cfg(feature = "css")] + ap.refer(&mut flags.use_css) + .add_option(&["--css"], StoreTrue, "Enable CSS"); ap.parse_args_or_exit(); } let data = match infile { None => { let stdin = io::stdin(); - let data = translate(&mut stdin.lock(), width, literal, use_colour); + let data = translate(&mut stdin.lock(), flags, literal); data } Some(name) => { let mut file = std::fs::File::open(name).expect("Tried to open file"); - translate(&mut file, width, literal, use_colour) + translate(&mut file, flags, literal) } }; diff --git a/src/css.rs b/src/css.rs index d0e505c..2963b9d 100644 --- a/src/css.rs +++ b/src/css.rs @@ -179,14 +179,9 @@ pub struct StyleData { impl StyleData { /// Add some CSS source to be included. The source will be parsed /// and the relevant and supported features extracted. - pub fn add_css(&mut self, css: &str) { - let ss = match StyleSheet::parse(css, ParserOptions::default()) { - Ok(ss) => ss, - Err(_e) => { - html_trace!("failed to parse CSS: {}, [[{}]]", _e, css); - return; - } - }; + pub fn add_css(&mut self, css: &str) -> Result<()> { + let ss = StyleSheet::parse(css, ParserOptions::default()) + .map_err(|_| crate::Error::CssParseError)?; for rule in &ss.rules.0 { match rule { @@ -224,6 +219,7 @@ impl StyleData { _ => (), } } + Ok(()) } /// Merge style data from other into this one. @@ -321,7 +317,8 @@ pub fn dom_to_stylesheet(handle: Handle, err_out: &mut T) -> Result Self { - self.style.add_css(css); - self + pub fn add_css(mut self, css: &str) -> Result { + self.style.add_css(css)?; + Ok(self) } #[cfg(feature = "css")] diff --git a/src/tests.rs b/src/tests.rs index 47c9d08..9877be5 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -46,6 +46,7 @@ fn test_html_err(input: &[u8], expected: Error, width: usize) { fn test_html_style(input: &[u8], style: &str, expected: &str, width: usize) { let result = config::plain() .add_css(style) + .unwrap() .string_from_read(input, width).unwrap(); assert_eq_str!(result, expected); } @@ -937,6 +938,8 @@ Indented again ); } +// Some of the tracing output can overflow the stack when tracing some values. +#[cfg(not(feature = "html_trace"))] #[test] fn test_deeply_nested() { use ::std::iter::repeat; @@ -944,6 +947,8 @@ fn test_deeply_nested() { test_html(html.as_bytes(), "", 10); } +// Some of the tracing output can overflow the stack when tracing some values. +#[cfg(not(feature = "html_trace"))] #[test] fn test_deeply_nested_table() { use ::std::iter::repeat;