diff options
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/pdfgen.rs | 52 | ||||
-rw-r--r-- | src/view.rs | 151 |
3 files changed, 114 insertions, 92 deletions
@@ -1,4 +1,3 @@ #![recursion_limit="1024"] -mod view; -mod pdfgen;
\ No newline at end of file +mod view;
\ No newline at end of file diff --git a/src/pdfgen.rs b/src/pdfgen.rs deleted file mode 100644 index 0bca467..0000000 --- a/src/pdfgen.rs +++ /dev/null @@ -1,52 +0,0 @@ -use genpdf::Element as _; -use genpdf::{elements, style, fonts}; - -pub fn genpdf() -> Vec<u8> { - let regular_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Regular.ttf"); - let regular_font_data = fonts::FontData::new(regular_bytes.to_vec(), Some(printpdf::BuiltinFont::Helvetica)).expect("font data should be correct"); - - let bold_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Bold.ttf"); - let bold_font_data = fonts::FontData::new(bold_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaBold)).expect("font data should be correct"); - - let italic_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Italic.ttf"); - let italic_font_data = fonts::FontData::new(italic_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaOblique)).expect("font data should be correct"); - - let bold_italic_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf"); - let bold_italic_font_data = fonts::FontData::new(bold_italic_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaBoldOblique)).expect("font data should be correct"); - - let font_family = fonts::FontFamily{ - regular: regular_font_data, - bold: bold_font_data, - italic: italic_font_data, - bold_italic: bold_italic_font_data - }; - - let mut doc = genpdf::Document::new(font_family); - - doc.set_title("BCard test"); - doc.set_minimal_conformance(); - doc.set_margins(10); - doc.set_line_spacing(1.25); - - #[cfg(feature = "hyphenation")] - { - use hyphenation::Load; - - doc.set_hyphenator( - hyphenation::Standard::from_embedded(hyphenation::Language::EnglishUS) - .expect("Failed to load hyphenation data"), - ); - } - - doc.push( - elements::Paragraph::new("genpdf Demo Document") - .aligned(elements::Alignment::Center) - .styled(style::Style::new().bold().with_font_size(20)), - ); - - // TODO fill doc with real data - - let mut buf: Vec<u8> = Vec::new(); - doc.render(&mut buf).expect("should render pdf"); - buf -}
\ No newline at end of file diff --git a/src/view.rs b/src/view.rs index 8da1a6c..9557dc5 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,7 +1,8 @@ extern crate console_error_panic_hook; +use genpdf::Element as _; +use genpdf::{elements, style, fonts}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; -use crate::pdfgen; use wasm_bindgen::prelude::*; use js_sys; use yew::prelude::*; @@ -12,12 +13,42 @@ fn init() { panic::set_hook(Box::new(console_error_panic_hook::hook)); } +struct Download { + file_name: String, + content: Box<dyn AsRef<[u8]>>, + mime_type: MimeType, +} + +impl Download { + fn as_data_link(&self) -> String { + let data = base64::encode(&*self.content); + let uri_component: String = js_sys::encode_uri_component(&data).into(); + + format!("data:{};base64,{}", self.mime_type.as_text(), uri_component) + } +} + +enum MimeType { + PDF, + VCard, + SVG, +} + +impl MimeType { + fn as_text(&self) -> &str { + match self { + MimeType::PDF => "application/pdf", + MimeType::VCard => "text/vcard", + MimeType::SVG => "image/svg+xml", + } + } +} + pub struct Form { link: ComponentLink<Self>, error: Vec<String>, formatted_name: String, - vcard: Option<String>, - qr_code: Option<String>, + download: Option<Download>, } pub enum Msg { @@ -33,7 +64,7 @@ impl Component for Form { type Properties = (); fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self { - Self { link, error: vec![], formatted_name: String::new(), vcard: None, qr_code: None } + Self { link, error: vec![], formatted_name: String::new(), download: None, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { @@ -42,23 +73,45 @@ impl Component for Form { Msg::UpdateFormattedName(value) => self.formatted_name = String::from(value), Msg::GenerateVCard => { match self.generate_vcard() { - Ok(vcard) => self.vcard = Some(vcard.to_string()), + Ok(vcard) => self.download = Some( + Download { + file_name: format!("{}.vcs",self.formatted_name), + content: Box::new(vcard.to_string()), + mime_type: MimeType::VCard, + } + ), Err(VCardError::FormatError(err)) => self.error.push(err.to_string()), Err(VCardError::EmptyFormatName) => self.error.push(String::from("A VCard should have at least one formatted name.")), }; } Msg::GeneratePdf => { - + match self.generate_pdf() { + Ok(pdf) => self.download = Some( + Download { + file_name: format!("Visitenkarten {}.pdf", self.formatted_name), + content: Box::new(pdf), + mime_type: MimeType::PDF, + } + ), + Err(_) => self.error.push(String::from("Unexpected error while generating the PDF. Please contact me about it.")), + } } Msg::GenerateQrCode => { + let mut vcard_content = None; match self.generate_vcard() { - Ok(vcard) => self.vcard = Some(vcard.to_string()), + Ok(vcard) => vcard_content = Some(vcard.to_string()), Err(VCardError::FormatError(err)) => self.error.push(err.to_string()), Err(VCardError::EmptyFormatName) => self.error.push(String::from("A VCard should have at least one formatted name.")), }; - if self.vcard.is_some() { - match QrCode::encode_text(self.vcard.as_ref().unwrap(), QrCodeEcc::Low) { - Ok(qr) => self.qr_code = Some(qr.to_svg_string(4)), + if vcard_content.is_some() { + match QrCode::encode_text(vcard_content.as_ref().unwrap(), QrCodeEcc::Low) { + Ok(qr) => self.download = Some( + Download { + file_name: format!("QR-Code VCard {}.svg", self.formatted_name), + content: Box::new(qr.to_svg_string(4)), + mime_type: MimeType::SVG, + } + ), Err(_) => self.error.push(String::from("Sorry, VCard is too long!")), }; } @@ -66,8 +119,7 @@ impl Component for Form { Msg::Nope => return false, }; if self.error.len() > 0 { - self.vcard = None; - self.qr_code = None; + self.download = None; } true } @@ -124,17 +176,13 @@ impl Component for Form { <input id="suffix" type="text"/> </form> <select id="download_options" onchange=download_options> - <option value="">{ "" }</option> <option value="vcard">{ "VCard (.vcf)" }</option> <option value="pdf">{ "Print-ready PDF" }</option> <option value="qrcode">{ "QR Code" }</option> </select> - { self.render_pdf() } { self.render_vcard() } + { self.render_download() } - <div class="row"> - { self.render_qrcode() } - </div> </div> } } @@ -147,6 +195,47 @@ impl Form { Err(err) => Err(err), } } + fn generate_pdf(&self) -> Result<Vec<u8>, ()>{ + let regular_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Regular.ttf"); + let regular_font_data = fonts::FontData::new(regular_bytes.to_vec(), Some(printpdf::BuiltinFont::Helvetica)).expect("font data should be correct"); + + let bold_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Bold.ttf"); + let bold_font_data = fonts::FontData::new(bold_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaBold)).expect("font data should be correct"); + + let italic_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-Italic.ttf"); + let italic_font_data = fonts::FontData::new(italic_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaOblique)).expect("font data should be correct"); + + let bold_italic_bytes = include_bytes!("/usr/share/fonts/liberation/LiberationSans-BoldItalic.ttf"); + let bold_italic_font_data = fonts::FontData::new(bold_italic_bytes.to_vec(), Some(printpdf::BuiltinFont::HelveticaBoldOblique)).expect("font data should be correct"); + + let font_family = fonts::FontFamily{ + regular: regular_font_data, + bold: bold_font_data, + italic: italic_font_data, + bold_italic: bold_italic_font_data + }; + + let mut doc = genpdf::Document::new(font_family); + + doc.set_title("BCard test"); + doc.set_minimal_conformance(); + doc.set_margins(10); + doc.set_line_spacing(1.25); + + doc.push( + elements::Paragraph::new("genpdf Demo Document") + .aligned(elements::Alignment::Center) + .styled(style::Style::new().bold().with_font_size(20)), + ); + + // TODO fill doc with real data + + let mut buf: Vec<u8> = Vec::new(); + match doc.render(&mut buf) { + Ok(_) => Ok(buf), + Err(_) => Err(()), + } + } fn render_error(&self) -> Html { html!{ <> @@ -162,33 +251,19 @@ impl Form { </> } } - fn render_pdf(&self) -> Html { - let raw = pdfgen::genpdf(); - let data = base64::encode(&raw); - let uri_component: String = js_sys::encode_uri_component(&data).into(); - let href = format!{"data:application/pdf;base64,{}", uri_component }; - - html!{ - <a href=href download="demo.pdf" class="button success small" > - { "Download PDF" } - </a> - } - } - fn render_vcard(&self) -> Html { - if self.vcard.is_some() { - let data = base64::encode(self.vcard.as_ref().unwrap()); - let uri_component: String = js_sys::encode_uri_component(&data).into(); - let href = format!("data:text/vcard;base64,{}", uri_component); + fn render_download(&self) -> Html { + if self.download.is_some() { + let download = self.download.as_ref().unwrap(); html!{ - <a href=href download=format!("{}.vcs",self.formatted_name) class="button success small"> - { "Download vCard" } + <a href=download.as_data_link() download=download.file_name class="button success small" > + { "Download" } </a> } } else { html!{} } - } + }/* fn render_qrcode(&self) -> Html { if self.qr_code.is_some() { let data = base64::encode(self.qr_code.as_ref().unwrap()); @@ -201,7 +276,7 @@ impl Form { } else { html!{} } - } + }*/ } |