extern crate console_error_panic_hook; use genpdf::Element as _; use genpdf::{elements, style, fonts}; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; use wasm_bindgen::prelude::*; use js_sys; use yew::prelude::*; use vcard::{VCard, VCardError}; use std::panic; fn init() { panic::set_hook(Box::new(console_error_panic_hook::hook)); } struct Download { file_name: String, content: Box>, 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, error: Vec, formatted_name: String, download: Option, } pub enum Msg { UpdateFormattedName(String), GenerateVCard, GeneratePdf, GenerateQrCode, Nope, } impl Component for Form { type Message = Msg; type Properties = (); fn create(_props: Self::Properties, link: ComponentLink) -> Self { Self { link, error: vec![], formatted_name: String::new(), download: None, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { self.error.clear(); match msg { Msg::UpdateFormattedName(value) => self.formatted_name = String::from(value), Msg::GenerateVCard => { match self.generate_vcard() { 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) => 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 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!")), }; } } Msg::Nope => return false, }; if self.error.len() > 0 { self.download = None; } true } fn change(&mut self, _props: Self::Properties) -> ShouldRender { false } fn view(&self) -> Html { let formatted_name_input = self.link.batch_callback(|e: InputData| vec![Msg::UpdateFormattedName(e.value), Msg::GenerateVCard] ); let download_options = self.link.callback(|e: ChangeData| match e { ChangeData::Select(v) => match v.value().as_str() { "vcard" => Msg::GenerateVCard, "pdf" => Msg::GeneratePdf, "qrcode" => Msg::GenerateQrCode, _ => Msg::Nope, }, _ => Msg::Nope, } ); html!{
{ self.render_error() }

{ "Name" }




{ self.render_download() }
} } } impl Form { fn generate_vcard(&self) -> Result { match VCard::from_formatted_name_str(&self.formatted_name) { Ok(vcard) => Ok(vcard), Err(err) => Err(err), } } fn generate_pdf(&self) -> Result, ()>{ 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 = Vec::new(); match doc.render(&mut buf) { Ok(_) => Ok(buf), Err(_) => Err(()), } } fn render_error(&self) -> Html { html!{ <> { for self.error.iter().map(|err| html!{

{ err }

} ) } } } fn render_download(&self) -> Html { if self.download.is_some() { let download = self.download.as_ref().unwrap(); html!{ { "Download" } } } else { html!{} } }/* fn render_qrcode(&self) -> Html { if self.qr_code.is_some() { let data = base64::encode(self.qr_code.as_ref().unwrap()); let uri_component: String = js_sys::encode_uri_component(&data).into(); let src = format!("data:image/svg+xml;base64,{}", uri_component); html!{ QR Code } } else { html!{} } }*/ } #[wasm_bindgen(start)] pub fn run_app() { init(); App::
::new().mount_to_body(); }