From ed19bef94a247d3eb501ae9f3bcd9496be229d80 Mon Sep 17 00:00:00 2001 From: jelemux Date: Fri, 5 Mar 2021 20:09:25 +0100 Subject: get pdf generation to work and refactor code a little --- src/model/vcard.rs | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) (limited to 'src/model/vcard.rs') diff --git a/src/model/vcard.rs b/src/model/vcard.rs index 4670fe7..ea66224 100644 --- a/src/model/vcard.rs +++ b/src/model/vcard.rs @@ -1,9 +1,19 @@ +use vobject::vcard::VcardBuilder; +use chrono::Local; +use uuid::Uuid; +use vobject::Vcard; use crate::model::property_groups::address::Address; use crate::model::property_groups::communication::Communication; use crate::model::property_groups::name::Name; use crate::model::property_groups::organizational::Organizational; use crate::model::property_groups::other_identification::OtherIdentification; use crate::model::property_groups::telephone::Telephone; +use vobject::error::Result as VResult; +use crate::model::VCardPropertyInputGroupObject; +use boolinator::Boolinator; +use vobject::parameters; +use genpdf::Element as _; +use genpdf::{elements, fonts, style}; /// Type that represents the data structure of a vcard. #[derive(Clone, Debug)] @@ -35,10 +45,325 @@ impl VCardData { organizationals: Vec::new(), } } + make_vec_adder_fn!( fn add_name names => name: Name ); make_vec_adder_fn!( fn add_address addresses => address: Address ); make_vec_adder_fn!( fn add_telephone telephones => telephone: Telephone ); make_vec_adder_fn!( fn add_communication communications => communication: Communication ); make_vec_adder_fn!( fn add_other_identification other_identifications => other_identification: OtherIdentification ); make_vec_adder_fn!( fn add_organizational organizationals => organizational: Organizational ); + + pub fn build_vcard(&self) -> VResult { + + let vcard_data = self.clone(); + let mut builder = VcardBuilder::new(); + + for name in vcard_data.names { + if !name.is_empty() { + builder = builder.with_fullname(name.generate_fn()).with_name( + parameters!(), + (!name.last_name.is_empty()).as_some(name.last_name.clone()), + (!name.first_name.is_empty()).as_some(name.first_name.clone()), + (!name.middle_name.is_empty()).as_some(name.middle_name.clone()), + (!name.prefix.is_empty()).as_some(name.prefix.clone()), + (!name.suffix.is_empty()).as_some(name.suffix.clone()), + ); + } + } + + for address in vcard_data.addresses { + if !address.is_empty() { + let mut types = String::new(); + if address.work { + types.push_str("WORK"); + } + if address.home { + if !types.is_empty() { + types.push(','); + } + types.push_str("HOME") + } + + let params = if types.is_empty() { + parameters!() + } else { + parameters!("TYPE" => types) + }; + + builder = builder.with_adr( + params, + (!address.post_office_box.is_empty()) + .as_some(address.post_office_box.clone()), + (!address.extension.is_empty()).as_some(address.extension.clone()), + (!address.street.is_empty()).as_some(address.street.clone()), + (!address.locality.is_empty()).as_some(address.locality.clone()), + (!address.region.is_empty()).as_some(address.region.clone()), + (!address.code.is_empty()).as_some(address.code.clone()), + (!address.country.is_empty()).as_some(address.country.clone()), + ); + } + } + + for telephone in vcard_data.telephones { + if !telephone.is_empty() { + let mut types = String::new(); + if telephone.work { + types.push_str("WORK"); + } + if telephone.home { + if !types.is_empty() { + types.push(','); + } + types.push_str("HOME") + } + if telephone.text { + if !types.is_empty() { + types.push(','); + } + types.push_str("TEXT") + } + if telephone.voice { + if !types.is_empty() { + types.push(','); + } + types.push_str("VOICE") + } + if telephone.fax { + if !types.is_empty() { + types.push(','); + } + types.push_str("FAX") + } + if telephone.cell { + if !types.is_empty() { + types.push(','); + } + types.push_str("CELL") + } + if telephone.video { + if !types.is_empty() { + types.push(','); + } + types.push_str("VIDEO") + } + if telephone.pager { + if !types.is_empty() { + types.push(','); + } + types.push_str("PAGER") + } + if telephone.text_phone { + if !types.is_empty() { + types.push(','); + } + types.push_str("TEXTPHONE") + } + + let params = if types.is_empty() { + parameters!() + } else { + parameters!("TYPE" => types) + }; + + builder = builder.with_tel(params, telephone.number.clone()); + } + } + + for communication in vcard_data.communications { + if !communication.email_address.is_empty() { + builder = builder.with_email(communication.email_address.clone()); + } + if !communication.impp.is_empty() { + builder = builder.with_impp(communication.impp.clone()); + } + } + + for other_identification in vcard_data.other_identifications { + if !other_identification.nickname.is_empty() { + builder = + builder.with_nickname(parameters!(), other_identification.nickname); + } + + match other_identification.photo { + Some(file) => builder = builder.with_photo(parameters!(), file.content), + None => (), + }; + + if !other_identification.anniversary.is_empty() { + builder = builder.with_anniversary(other_identification.anniversary); + } + + if !other_identification.birthday.is_empty() { + builder = builder.with_bday(parameters!(), other_identification.birthday); + } + + if !other_identification.gender.is_empty() { + builder = builder.with_gender(parameters!(), other_identification.gender); + } + } + + for organizational in vcard_data.organizationals { + if !organizational.org.is_empty() { + builder = builder.with_org(vec![organizational.org]); + } + + match organizational.logo { + Some(file) => builder = builder.with_logo(file.content), + None => (), + }; + + if !organizational.title.is_empty() { + builder = builder.with_title(organizational.title); + } + + if !organizational.role.is_empty() { + builder = builder.with_role(organizational.role); + } + + if !organizational.member.is_empty() { + builder = builder.with_member(organizational.member); + } + + if !organizational.related.is_empty() { + builder = builder.with_related(organizational.related); + } + } + + let uid = format!("urn:uuid:{}", Uuid::new_v4()); + + let rev = Local::now().to_string(); + + builder + .with_version("4.0".to_string()) + .with_rev(rev) + .with_uid(uid) + .build() + } + + pub 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("wasm-card test"); + doc.set_minimal_conformance(); + + let mut decorator = genpdf::SimplePageDecorator::new(); + decorator.set_margins(10); + doc.set_page_decorator(decorator); + + 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)), + ); + + let mut card_data = elements::Paragraph::new(""); + + let vcard_data = self.clone(); + + if let Some(name) = vcard_data.names.get(0) { + card_data.push_styled( + name.generate_fn(), + style::Effect::Bold + ); + } + card_data.push( + "" + ); + if let Some(communication) = vcard_data.communications.get(0) { + card_data.push( + communication.email_address.clone() + ); + } + card_data.push( + "" + ); + if let Some(address) = vcard_data.addresses.get(0) { + card_data.push( + address.street.clone() + ); + card_data.push( + format!("{} {} {}", address.code, address.locality, address.extension) + ); + card_data.push( + format!("{} {}", address.region, address.country) + ); + } + card_data.push( + "" + ); + for telephone in vcard_data.telephones { + card_data.push( + telephone.number + ); + } + + let framed_card_data = elements::FramedElement::new( + card_data + ); + + let mut table = elements::TableLayout::new(vec![1,1]); + table.set_cell_decorator( + elements::FrameCellDecorator::new(false, false, false) + ); + for _i in 0..4 { + table + .row() + .element(framed_card_data.clone()) + .element(framed_card_data.clone()) + .push() + .expect("invalid table row"); + } + + doc.push(table); + + // TODO fill doc with real data + + let mut buf: Vec = Vec::new(); + match doc.render(&mut buf) { + Ok(_) => Ok(match String::from_utf8(buf) { + Ok(s) => s, + Err(_) => return Err(()), + }), + Err(_) => Err(()), + } + } } -- cgit v1.2.3