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!{}          } -    } +    }*/  }  | 
