diff --git a/examples/src/bin/form.rs b/examples/src/bin/form.rs new file mode 100644 index 0000000..b0a29ed --- /dev/null +++ b/examples/src/bin/form.rs @@ -0,0 +1,140 @@ +extern crate pdf; + +use std::collections::HashMap; +use std::env::args; + +use pdf::content::{FormXObject, Op, serialize_ops}; +use pdf::error::PdfError; +use pdf::file::{FileOptions, Log}; +use pdf::font::{Font, FontData, TFont}; +use pdf::object::*; +use pdf::primitive::{PdfString, Primitive, Name}; + +fn main() -> Result<(), PdfError> { + let path = args().nth(1).expect("no file given"); + println!("read: {}", path); + + 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 { + if let Some(ref a) = annot.appearance_streams { + let normal = file.resolver().get(a.normal); + if let Ok(normal) = normal { + match *normal { + 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 normal2 = AppearanceStreamEntry::Single(FormXObject { stream }); + file.update(a.normal.get_inner(), normal2)?; + } + _ => {} + } + } + } + } + + if let Some(ref forms) = file.get_root().forms { + println!("Forms:"); + for field in forms.fields.iter().take(1) { + print!(" {:?} = ", field.name); + match field.value { + Primitive::String(ref s) => println!("{}", s.to_string_lossy()), + Primitive::Integer(i) => println!("{}", i), + Primitive::Name(ref s) => println!("{}", s), + ref p => println!("{:?}", p), + } + + if to_update_field.is_none() { + to_update_field = Some(field.clone()); + } + } + } + + /* + 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 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, + )?; + + file.save_to("output/out.pdf")?; + + println!("\nUpdated field:"); + //println!("{:?}\n", reference); + } + + Ok(()) +} diff --git a/pdf/src/build.rs b/pdf/src/build.rs index addae67..7f71f3f 100644 --- a/pdf/src/build.rs +++ b/pdf/src/build.rs @@ -132,6 +132,7 @@ impl CatalogBuilder { lgi: page.lgi, vp: page.vp, other: page.other, + annotations: vec![] }; update.fulfill(promise, PagesNode::Leaf(page))?; } @@ -157,8 +158,8 @@ pub struct PdfBuilder { } impl PdfBuilder where - SC: Cache>> + Default, - OC: Cache, Arc>> + Default, + SC: Cache>>, + OC: Cache, Arc>>, L: Log, { pub fn new(fileoptions: FileOptions<'_, SC, OC, L>) -> Self { diff --git a/pdf/src/file.rs b/pdf/src/file.rs index 22b391c..c28ef1a 100644 --- a/pdf/src/file.rs +++ b/pdf/src/file.rs @@ -361,7 +361,7 @@ where fn update(&mut self, old: PlainRef, obj: T) -> Result> { let r = match self.refs.get(old.id)? { XRef::Free { .. } => panic!(), - XRef::Raw { gen_nr, .. } => PlainRef { id: old.id, gen: gen_nr + 1 }, + XRef::Raw { gen_nr, .. } => PlainRef { id: old.id, gen: gen_nr }, XRef::Stream { .. } => return self.create(obj), XRef::Promised => PlainRef { id: old.id, gen: 0 }, XRef::Invalid => panic!() diff --git a/pdf/src/object/types.rs b/pdf/src/object/types.rs index 8785d56..e05fd2a 100644 --- a/pdf/src/object/types.rs +++ b/pdf/src/object/types.rs @@ -72,6 +72,20 @@ impl PageRc { self.0.get_ref() } } +impl Object for PageRc { + fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result { + let node = t!(RcRef::from_primitive(p, resolve)); + match *node { + PagesNode::Tree(_) => Err(PdfError::WrongDictionaryType {expected: "Page".into(), found: "Pages".into()}), + PagesNode::Leaf(_) => Ok(PageRc(node)) + } + } +} +impl ObjectWrite for PageRc { + fn to_primitive(&self, update: &mut impl Updater) -> Result { + self.0.to_primitive(update) + } +} /// A `PagesNode::Tree` wrapped in a `RcRef` /// @@ -278,6 +292,9 @@ pub struct Page { #[pdf(key="VP")] pub vp: Option, + #[pdf(key="Annots", default="vec![]")] + pub annotations: Vec, + #[pdf(other)] pub other: Dictionary, } @@ -307,6 +324,7 @@ impl Page { lgi: None, vp: None, other: Dictionary::new(), + annotations: vec![] } } pub fn media_box(&self) -> Result { @@ -335,6 +353,7 @@ impl Page { } impl SubType for Page {} + #[derive(Object, DataSize)] pub struct PageLabel { #[pdf(key="S")] @@ -764,7 +783,6 @@ impl RenderingIntent { } } - #[derive(Object, Debug, DataSize, DeepClone, ObjectWrite, Clone, Default)] #[pdf(Type="XObject?", Subtype="Form")] pub struct FormDict { @@ -924,6 +942,41 @@ pub struct SignatureReferenceDictionary { pub other: Dictionary } + +#[derive(Object, ObjectWrite, Debug, Clone, DataSize)] +#[pdf(Type="Annot?")] +pub struct Annot { + #[pdf(key="Subtype")] + pub subtype: Name, + + #[pdf(key="Rect")] + pub rect: Rect, + + #[pdf(key="Contents")] + pub contents: Option, + + #[pdf(key="P")] + pub page: Option, + + #[pdf(key="NM")] + pub annotation_name: Option, + + #[pdf(key="M")] + pub date: Option, + + #[pdf(key="F", default="0")] + pub annot_flags: u32, + + #[pdf(key="AP")] + pub appearance_streams: Option>, + + #[pdf(key="AS")] + pub appearance_state: Option, + + #[pdf(key="Border")] + pub border: Option, +} + #[derive(Object, ObjectWrite, Debug, DataSize, Clone)] pub struct FieldDictionary { #[pdf(key="FT")] @@ -956,15 +1009,23 @@ pub struct FieldDictionary { #[pdf(key="DV")] pub default_value: Primitive, + #[pdf(key="DR")] + pub default_resources: Option>, + #[pdf(key="AA")] pub actions: Option, - #[pdf(key="AP")] - pub appearance_streams: Option>, - #[pdf(key="Rect")] pub rect: Rect, + #[pdf(key="MaxLen")] + pub max_len: Option, + + + + #[pdf(key="Subtype")] + pub subtype: Name, + #[pdf(other)] pub other: Dictionary }