From b4d21fcea6d6ceec0c1269efc1fd5a20e69ed89a Mon Sep 17 00:00:00 2001 From: Sebastian K Date: Mon, 11 Dec 2023 20:25:02 +0100 Subject: [PATCH] some improvments on forms --- examples/src/bin/form.rs | 107 ++++++++++++++++----------------------- pdf/src/file.rs | 26 +++++++++- pdf/src/object/types.rs | 2 +- pdf/src/primitive.rs | 4 +- 4 files changed, 71 insertions(+), 68 deletions(-) diff --git a/examples/src/bin/form.rs b/examples/src/bin/form.rs index b0a29edd..d50e1968 100644 --- a/examples/src/bin/form.rs +++ b/examples/src/bin/form.rs @@ -17,12 +17,34 @@ fn main() -> Result<(), PdfError> { let mut file = FileOptions::cached().open(&path).unwrap(); let mut to_update_field: Option<_> = None; - let page0 = file.get_page(0).unwrap(); - let ops = page0 - .contents.as_ref().unwrap() - .operations(&file.resolver()).unwrap(); - for annot in &page0.annotations { + let font = Font { + data: FontData::TrueType(TFont{ + base_font: Some(Name::from("Helvetica")), + first_char: None, + font_descriptor: None, + last_char: None, + widths: None, + }), + encoding: Some(pdf::encoding::Encoding::standard()), + name: None, + subtype: pdf::font::FontType::TrueType, + to_unicode: None, + _other: Default::default() + }; + let font_name = Name::from("Helvetica"); + let font = file.create(font)?; + let mut fonts = HashMap::new(); + fonts.insert("Helvetica".into(), font.into()); + let resources = Resources { + fonts, + .. Default::default() + }; + let resources = file.create(resources)?; + + let page0 = file.get_page(0).unwrap(); + for &annot in &page0.annotations { + let annot = file.resolver().get(annot)?; if let Some(ref a) = annot.appearance_streams { let normal = file.resolver().get(a.normal); if let Ok(normal) = normal { @@ -30,19 +52,22 @@ fn main() -> Result<(), PdfError> { AppearanceStreamEntry::Single(ref s) => { //dbg!(&s.stream.resources); - let mut ops = s.operations(&file.resolver())?; - for op in ops.iter_mut() { - match op { - Op::TextDraw { text } => { - println!("{}", text.to_string_lossy()); - *text = PdfString::from("helloo"); - } - _ => {} - } - } - let stream = Stream::new(s.stream.info.info.clone(), serialize_ops(&ops)?); + let form_dict = FormDict { + resources: Some(resources.clone().into()), + .. (**s.stream).clone() + }; + + let ops = vec![ + Op::Save, + Op::TextFont { name: font_name.clone(), size: 14.0 }, + Op::TextDraw { text: PdfString::from("Hello World!") }, + Op::EndText, + Op::Restore + ]; + let stream = Stream::new(form_dict, serialize_ops(&ops)?); let normal2 = AppearanceStreamEntry::Single(FormXObject { stream }); + file.update(a.normal.get_inner(), normal2)?; } _ => {} @@ -68,63 +93,17 @@ fn main() -> Result<(), PdfError> { } } - /* - let font = Font { - data: FontData::TrueType(TFont{ - base_font: Some(Name::from("Helvetica")), - first_char: None, - font_descriptor: None, - last_char: None, - widths: None, - }), - encoding: Some(pdf::encoding::Encoding::standard()), - name: None, - subtype: pdf::font::FontType::TrueType, - to_unicode: None, - _other: Default::default() - }; - let font_name = Name::from("Helvetica"); - let font = file.create(font)?; - let mut fonts = HashMap::new(); - fonts.insert("Helvetica".into(), font.into()); - let resources = Resources { - fonts, - .. Default::default() - }; - let resources = file.create(resources)?; - */ - if let Some(to_update_field) = to_update_field { println!("\nUpdating field:"); println!("{:?}\n", to_update_field); - let text = "helloo"; + let text = "Hello World!"; let new_value: PdfString = PdfString::new(text.into()); let mut updated_field = (*to_update_field).clone(); updated_field.value = Primitive::String(new_value); - /* - let mut form_dict = FormDict { - bbox: updated_field.rect, - resources: Some(resources.into()), - .. Default::default() - }; - let content = format!("q BT /Helvetica 14 Tf ({text}) ET Q"); - - let form = FormXObject { - stream: Stream::new(form_dict, content.into_bytes()) - }; - //dbg!(&form); - let normal = AppearanceStreamEntry::Single(form); - let apperance = AppearanceStreams { - normal: file.create(normal)?.into(), - down: None, - rollover: None - }; - //updated_field.appearance_streams = Some(apperance.into()); - //updated_field.appearance_state = Some("N".into()); //dbg!(&updated_field); - */ + let reference = file.update( to_update_field.get_ref().get_inner(), updated_field, diff --git a/pdf/src/file.rs b/pdf/src/file.rs index c28ef1a8..2b454c6f 100644 --- a/pdf/src/file.rs +++ b/pdf/src/file.rs @@ -324,7 +324,15 @@ where } }); match res { - Ok(any) => Ok(RcRef::new(key, any.downcast()?)), + Ok(any) => { + match any.downcast() { + Ok(val) => Ok(RcRef::new(key, val)), + Err(_) => { + let p = self.resolve(key)?; + Ok(RcRef::new(key, T::from_primitive(p, self)?.into())) + } + } + } Err(e) => Err(PdfError::Shared { source: e.clone()}), } } @@ -359,6 +367,8 @@ where Ok(RcRef::new(r, rc)) } fn update(&mut self, old: PlainRef, obj: T) -> Result> { + use std::collections::hash_map::Entry; + let r = match self.refs.get(old.id)? { XRef::Free { .. } => panic!(), XRef::Raw { gen_nr, .. } => PlainRef { id: old.id, gen: gen_nr }, @@ -367,7 +377,19 @@ where XRef::Invalid => panic!() }; let primitive = obj.to_primitive(self)?; - self.changes.insert(old.id, (primitive, r.gen)); + match self.changes.entry(old.id) { + Entry::Vacant(e) => { + e.insert((primitive, r.gen)); + } + Entry::Occupied(mut e) => match (e.get_mut(), primitive) { + ((Primitive::Dictionary(ref mut dict), _), Primitive::Dictionary(new)) => { + dict.append(new); + } + (old, new) => { + *old = (new, r.gen); + } + } + } let rc = Shared::new(obj); Ok(RcRef::new(r, rc)) diff --git a/pdf/src/object/types.rs b/pdf/src/object/types.rs index e05fd2a6..0896824c 100644 --- a/pdf/src/object/types.rs +++ b/pdf/src/object/types.rs @@ -293,7 +293,7 @@ pub struct Page { pub vp: Option, #[pdf(key="Annots", default="vec![]")] - pub annotations: Vec, + pub annotations: Vec>, #[pdf(other)] pub other: Dictionary, diff --git a/pdf/src/primitive.rs b/pdf/src/primitive.rs index 2f0734de..df9c396d 100644 --- a/pdf/src/primitive.rs +++ b/pdf/src/primitive.rs @@ -167,6 +167,9 @@ impl Dictionary { None => Ok(()) } } + pub fn append(&mut self, other: Dictionary) { + self.dict.extend(other.dict); + } } impl DataSize for Dictionary { const IS_DYNAMIC: bool = true; @@ -849,7 +852,6 @@ mod tests { } #[test] - #[should_panic] fn utf16be_invalid_bytelen() { let s = PdfString::new([0xfe, 0xff, 0xd8, 0x34, 0x20].as_slice().into()); let repl_ch = String::from(std::char::REPLACEMENT_CHARACTER);