Skip to main content

pdfium_render/pdf/document/page/
objects.rs

1//! Defines the [PdfPageObjects] struct, exposing functionality related to the
2//! page objects contained within a single [PdfPage].
3
4pub mod common;
5pub(crate) mod private; // Keep private so that the PdfPageObjectsPrivate trait is not exposed.
6
7use crate::bindgen::{FPDF_DOCUMENT, FPDF_PAGE};
8use crate::error::{PdfiumError, PdfiumInternalError};
9use crate::pdf::document::page::object::group::PdfPageGroupObject;
10use crate::pdf::document::page::object::ownership::PdfPageObjectOwnership;
11use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
12use crate::pdf::document::page::object::x_object_form::PdfPageXObjectFormObject;
13use crate::pdf::document::page::object::PdfPageObject;
14use crate::pdf::document::page::objects::common::{
15    PdfPageObjectIndex, PdfPageObjectsCommon, PdfPageObjectsIterator,
16};
17use crate::pdf::document::page::objects::private::internal::PdfPageObjectsPrivate;
18use crate::pdf::document::page::PdfPageIndexCache;
19use crate::pdf::document::PdfDocument;
20use crate::pdfium::PdfiumLibraryBindingsAccessor;
21use std::marker::PhantomData;
22use std::os::raw::c_int;
23
24#[cfg(doc)]
25use {
26    crate::pdf::document::page::object::PdfPageObjectType, crate::pdf::document::page::PdfPage,
27    crate::pdf::document::page::PdfPageContentRegenerationStrategy,
28};
29
30/// The page objects contained within a single [PdfPage].
31///
32/// Content on a page is structured as a stream of [PdfPageObject] objects of different types:
33/// text objects, image objects, path objects, and so on.
34///
35/// Note that Pdfium does not support or recognize all PDF page object types. For instance,
36/// Pdfium does not currently support or recognize the External Object ("XObject") page object type
37/// supported by Adobe Acrobat and Foxit's commercial PDF SDK. In these cases, Pdfium will return
38/// [PdfPageObjectType::Unsupported].
39pub struct PdfPageObjects<'a> {
40    document_handle: FPDF_DOCUMENT,
41    page_handle: FPDF_PAGE,
42    ownership: PdfPageObjectOwnership,
43    lifetime: PhantomData<&'a FPDF_PAGE>,
44}
45
46impl<'a> PdfPageObjects<'a> {
47    #[inline]
48    pub(crate) fn from_pdfium(document_handle: FPDF_DOCUMENT, page_handle: FPDF_PAGE) -> Self {
49        Self {
50            document_handle,
51            page_handle,
52            ownership: PdfPageObjectOwnership::owned_by_page(document_handle, page_handle),
53            lifetime: PhantomData,
54        }
55    }
56
57    /// Returns the internal `FPDF_DOCUMENT` handle for this page objects collection.
58    #[inline]
59    pub(crate) fn document_handle(&self) -> FPDF_DOCUMENT {
60        self.document_handle
61    }
62
63    /// Returns the internal `FPDF_PAGE` handle for this page objects collection.
64    #[inline]
65    pub(crate) fn page_handle(&self) -> FPDF_PAGE {
66        self.page_handle
67    }
68
69    /// Creates a new [PdfPageGroupObject] object group that includes any page objects in this
70    /// [PdfPageObjects] collection matching the given predicate function.
71    pub fn create_group<F>(&'a self, predicate: F) -> Result<PdfPageGroupObject<'a>, PdfiumError>
72    where
73        F: Fn(&PdfPageObject) -> bool,
74    {
75        let mut result = self.create_empty_group();
76
77        for mut object in self.iter().filter(predicate) {
78            result.push(&mut object)?;
79        }
80
81        Ok(result)
82    }
83
84    /// Creates a new [PdfPageGroupObject] object group that can accept any [PdfPageObject]
85    /// in this [PdfPageObjects] collection. The newly created group will be empty;
86    /// you will need to manually add to it the objects you want to manipulate.
87    #[inline]
88    pub fn create_empty_group(&self) -> PdfPageGroupObject<'a> {
89        PdfPageGroupObject::from_pdfium(self.document_handle(), self.page_handle())
90    }
91
92    /// Creates a new [PdfPageXObjectFormObject] object from the page objects on this [PdfPage],
93    /// ready to use in the given destination [PdfDocument].
94    pub fn copy_into_x_object_form_object(
95        &self,
96        destination: &mut PdfDocument<'a>,
97    ) -> Result<PdfPageObject<'a>, PdfiumError> {
98        let page_index =
99            PdfPageIndexCache::get_index_for_page(self.document_handle(), self.page_handle());
100
101        match page_index {
102            Some(page_index) => {
103                let x_object = unsafe {
104                    self.bindings().FPDF_NewXObjectFromPage(
105                        destination.handle(),
106                        self.document_handle(),
107                        page_index as c_int,
108                    )
109                };
110
111                let object_handle =
112                    unsafe { self.bindings().FPDF_NewFormObjectFromXObject(x_object) };
113                if object_handle.is_null() {
114                    return Err(PdfiumError::PdfiumLibraryInternalError(
115                        crate::error::PdfiumInternalError::Unknown,
116                    ));
117                }
118
119                let object = PdfPageXObjectFormObject::from_pdfium(
120                    object_handle,
121                    PdfPageObjectOwnership::owned_by_document(destination.handle()),
122                );
123
124                unsafe {
125                    self.bindings().FPDF_CloseXObject(x_object);
126                }
127
128                Ok(PdfPageObject::XObjectForm(object))
129            }
130            None => Err(PdfiumError::SourcePageIndexNotInCache),
131        }
132    }
133
134    #[cfg(any(
135        feature = "pdfium_future",
136        feature = "pdfium_7763",
137        feature = "pdfium_7543",
138        feature = "pdfium_7350"
139    ))]
140    /// Adds the given [PdfPageObject] to this page objects collection, inserting it into
141    /// the collection at the given index. The object's memory ownership will be transferred
142    /// to the [PdfPage] containing this page objects collection, and the updated page object
143    /// will be returned.
144    ///
145    /// If the containing [PdfPage] has a content regeneration strategy of
146    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
147    /// will be triggered on the page.
148    #[inline]
149    pub fn insert_object_at_index(
150        &mut self,
151        index: PdfPageObjectIndex,
152        mut object: PdfPageObject<'a>,
153    ) -> Result<PdfPageObject<'a>, PdfiumError> {
154        object.insert_object_on_page(self, index).map(|_| object)
155    }
156}
157
158impl<'a> PdfPageObjectsPrivate<'a> for PdfPageObjects<'a> {
159    #[inline]
160    fn ownership(&self) -> &PdfPageObjectOwnership {
161        &self.ownership
162    }
163
164    #[inline]
165    fn len_impl(&self) -> PdfPageObjectIndex {
166        (unsafe { self.bindings().FPDFPage_CountObjects(self.page_handle) }) as PdfPageObjectIndex
167    }
168
169    fn get_impl(&self, index: PdfPageObjectIndex) -> Result<PdfPageObject<'a>, PdfiumError> {
170        let object_handle = unsafe {
171            self.bindings()
172                .FPDFPage_GetObject(self.page_handle, index as c_int)
173        };
174
175        if object_handle.is_null() {
176            if index >= self.len() {
177                Err(PdfiumError::PageObjectIndexOutOfBounds)
178            } else {
179                Err(PdfiumError::PdfiumLibraryInternalError(
180                    PdfiumInternalError::Unknown,
181                ))
182            }
183        } else {
184            Ok(PdfPageObject::from_pdfium(
185                object_handle,
186                *self.ownership(),
187                self.bindings(),
188            ))
189        }
190    }
191
192    #[inline]
193    fn iter_impl(&'a self) -> PdfPageObjectsIterator<'a> {
194        PdfPageObjectsIterator::new(self)
195    }
196
197    #[inline]
198    fn add_object_impl(
199        &mut self,
200        mut object: PdfPageObject<'a>,
201    ) -> Result<PdfPageObject<'a>, PdfiumError> {
202        object.add_object_to_page(self).map(|_| object)
203    }
204
205    #[inline]
206    fn remove_object_impl(
207        &mut self,
208        mut object: PdfPageObject<'a>,
209    ) -> Result<PdfPageObject<'a>, PdfiumError> {
210        object.remove_object_from_page().map(|_| object)
211    }
212}
213
214impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageObjects<'a> {}
215
216#[cfg(feature = "thread_safe")]
217unsafe impl<'a> Send for PdfPageObjects<'a> {}
218
219#[cfg(feature = "thread_safe")]
220unsafe impl<'a> Sync for PdfPageObjects<'a> {}