pdfium_render/pdf/document/page/objects/
common.rs

1//! Defines the [PdfPageObjectsCommon] trait, providing functionality common to all
2//! containers of multiple [PdfPageObject] objects.
3
4use crate::error::{PdfiumError, PdfiumInternalError};
5use crate::pdf::color::PdfColor;
6use crate::pdf::document::fonts::ToPdfFontToken;
7use crate::pdf::document::page::object::image::PdfPageImageObject;
8use crate::pdf::document::page::object::path::PdfPagePathObject;
9use crate::pdf::document::page::object::text::PdfPageTextObject;
10use crate::pdf::document::page::object::x_object_form::PdfPageXObjectFormObject;
11use crate::pdf::document::page::object::{PdfPageObject, PdfPageObjectCommon};
12use crate::pdf::document::page::objects::private::internal::PdfPageObjectsPrivate;
13use crate::pdf::document::page::PdfPageObjectOwnership;
14use crate::pdf::points::PdfPoints;
15use crate::pdf::rect::PdfRect;
16use std::ops::{Range, RangeInclusive};
17
18#[cfg(feature = "image_025")]
19use image_025::DynamicImage;
20
21#[cfg(feature = "image_024")]
22use image_024::DynamicImage;
23
24#[cfg(feature = "image_023")]
25use image_023::{DynamicImage, GenericImageView};
26
27#[cfg(doc)]
28use {
29    crate::pdf::document::page::PdfPage,
30    crate::pdf::document::page::PdfPageContentRegenerationStrategy,
31    crate::pdf::document::page::PdfPageObjects,
32};
33
34/// The zero-based index of a single [PdfPageObject] inside its containing [PdfPageObjects] collection.
35pub type PdfPageObjectIndex = usize;
36
37/// Functionality common to all containers of multiple [PdfPageObject] objects.
38/// Both pages and annotations can contain page objects.
39pub trait PdfPageObjectsCommon<'a> {
40    /// Returns the total number of page objects in the collection.
41    fn len(&self) -> PdfPageObjectIndex;
42
43    /// Returns true if this page objects collection is empty.
44    #[inline]
45    fn is_empty(&self) -> bool {
46        self.len() == 0
47    }
48
49    /// Returns a Range from `0..(number of objects)` for this page objects collection.
50    #[inline]
51    fn as_range(&self) -> Range<PdfPageObjectIndex> {
52        0..self.len()
53    }
54
55    /// Returns an inclusive Range from `0..=(number of objects - 1)` for this page objects collection.
56    #[inline]
57    fn as_range_inclusive(&self) -> RangeInclusive<PdfPageObjectIndex> {
58        if self.is_empty() {
59            0..=0
60        } else {
61            0..=(self.len() - 1)
62        }
63    }
64
65    /// Returns a single [PdfPageObject] from this page objects collection.
66    fn get(&self, index: PdfPageObjectIndex) -> Result<PdfPageObject<'a>, PdfiumError>;
67
68    /// Returns the first [PdfPageObject] in this page objects collection.
69    #[inline]
70    fn first(&self) -> Result<PdfPageObject<'a>, PdfiumError> {
71        if !self.is_empty() {
72            self.get(0)
73        } else {
74            Err(PdfiumError::NoPageObjectsInCollection)
75        }
76    }
77
78    /// Returns the last [PdfPageObject] in this page objects collection.
79    #[inline]
80    fn last(&self) -> Result<PdfPageObject<'a>, PdfiumError> {
81        if !self.is_empty() {
82            self.get(self.len() - 1)
83        } else {
84            Err(PdfiumError::NoPageObjectsInCollection)
85        }
86    }
87
88    /// Returns an iterator over all the [PdfPageObject] objects in this page objects collection.
89    fn iter(&'a self) -> PdfPageObjectsIterator<'a>;
90
91    /// Returns the smallest bounding box that contains all the [PdfPageObject] objects in this
92    /// page objects collection.
93    fn bounds(&'a self) -> PdfRect {
94        let mut bottom: f32 = 0.0;
95        let mut top: f32 = 0.0;
96        let mut left: f32 = 0.0;
97        let mut right: f32 = 0.0;
98
99        for object in self.iter() {
100            if let Ok(bounds) = object.bounds() {
101                bottom = bottom.min(bounds.bottom().value);
102                top = top.max(bounds.top().value);
103                left = left.min(bounds.left().value);
104                right = right.max(bounds.right().value);
105            }
106        }
107
108        PdfRect::new_from_values(bottom, left, top, right)
109    }
110
111    /// Adds the given [PdfPageObject] to this page objects collection. The object's
112    /// memory ownership will be transferred to the [PdfPage] containing this page objects
113    /// collection, and the updated page object will be returned.
114    ///
115    /// If the containing [PdfPage] has a content regeneration strategy of
116    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
117    /// will be triggered on the page.
118    fn add_object(&mut self, object: PdfPageObject<'a>) -> Result<PdfPageObject<'a>, PdfiumError>;
119
120    /// Adds the given [PdfPageTextObject] to this page objects collection,
121    /// returning the text object wrapped inside a generic [PdfPageObject] wrapper.
122    ///
123    /// If the containing [PdfPage] has a content regeneration strategy of
124    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
125    /// will be triggered on the page.
126    #[inline]
127    fn add_text_object(
128        &mut self,
129        object: PdfPageTextObject<'a>,
130    ) -> Result<PdfPageObject<'a>, PdfiumError> {
131        self.add_object(PdfPageObject::Text(object))
132    }
133
134    /// Creates a new [PdfPageTextObject] at the given x and y page co-ordinates
135    /// from the given arguments and adds it to this page objects collection,
136    /// returning the text object wrapped inside a generic [PdfPageObject] wrapper.
137    ///
138    /// If the containing [PdfPage] has a content regeneration strategy of
139    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
140    /// will be triggered on the page.
141    fn create_text_object(
142        &mut self,
143        x: PdfPoints,
144        y: PdfPoints,
145        text: impl ToString,
146        font: impl ToPdfFontToken,
147        font_size: PdfPoints,
148    ) -> Result<PdfPageObject<'a>, PdfiumError>;
149
150    /// Adds the given [PdfPagePathObject] to this page objects collection,
151    /// returning the path object wrapped inside a generic [PdfPageObject] wrapper.
152    ///
153    /// If the containing [PdfPage] has a content regeneration strategy of
154    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
155    /// will be triggered on the page.
156    #[inline]
157    fn add_path_object(
158        &mut self,
159        object: PdfPagePathObject<'a>,
160    ) -> Result<PdfPageObject<'a>, PdfiumError> {
161        self.add_object(PdfPageObject::Path(object))
162    }
163
164    /// Adds the given [PdfPageXObjectFormObject] to this page objects collection,
165    /// returning the XObject form object wrapped inside a generic [PdfPageObject] wrapper.
166    ///
167    /// If the containing `PdfPage` has a content regeneration strategy of
168    /// `PdfPageContentRegenerationStrategy::AutomaticOnEveryChange` then content regeneration
169    /// will be triggered on the page.
170    #[inline]
171    fn add_x_object_form_object(
172        &mut self,
173        object: PdfPageXObjectFormObject<'a>,
174    ) -> Result<PdfPageObject<'a>, PdfiumError> {
175        self.add_object(PdfPageObject::XObjectForm(object))
176    }
177
178    /// Creates a new [PdfPagePathObject] for the given line, with the given
179    /// stroke settings applied. The new path object will be added to this page objects collection
180    /// and then returned, wrapped inside a generic [PdfPageObject] wrapper.
181    ///
182    /// If the containing [PdfPage] has a content regeneration strategy of
183    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
184    /// will be triggered on the page.
185    fn create_path_object_line(
186        &mut self,
187        x1: PdfPoints,
188        y1: PdfPoints,
189        x2: PdfPoints,
190        y2: PdfPoints,
191        stroke_color: PdfColor,
192        stroke_width: PdfPoints,
193    ) -> Result<PdfPageObject<'a>, PdfiumError>;
194
195    /// Creates a new [PdfPagePathObject] for the given cubic Bézier curve, with the given
196    /// stroke settings applied. The new path object will be added to this page objects collection
197    /// and then returned, wrapped inside a generic [PdfPageObject] wrapper.
198    ///
199    /// If the containing [PdfPage] has a content regeneration strategy of
200    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
201    /// will be triggered on the page.
202    #[allow(clippy::too_many_arguments)]
203    fn create_path_object_bezier(
204        &mut self,
205        x1: PdfPoints,
206        y1: PdfPoints,
207        x2: PdfPoints,
208        y2: PdfPoints,
209        control1_x: PdfPoints,
210        control1_y: PdfPoints,
211        control2_x: PdfPoints,
212        control2_y: PdfPoints,
213        stroke_color: PdfColor,
214        stroke_width: PdfPoints,
215    ) -> Result<PdfPageObject<'a>, PdfiumError>;
216
217    /// Creates a new [PdfPagePathObject] for the given rectangle, with the given
218    /// fill and stroke settings applied. Both the stroke color and the stroke width must be
219    /// provided for the rectangle to be stroked. The new path object will be added to
220    /// this page objects collection and then returned, wrapped inside a generic
221    /// [PdfPageObject] wrapper.
222    ///
223    /// If the containing [PdfPage] has a content regeneration strategy of
224    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
225    /// will be triggered on the page.
226    fn create_path_object_rect(
227        &mut self,
228        rect: PdfRect,
229        stroke_color: Option<PdfColor>,
230        stroke_width: Option<PdfPoints>,
231        fill_color: Option<PdfColor>,
232    ) -> Result<PdfPageObject<'a>, PdfiumError>;
233
234    /// Creates a new [PdfPagePathObject]. The new path will be created with a circle that fills
235    /// the given rectangle, with the given fill and stroke settings applied. Both the stroke color
236    /// and the stroke width must be provided for the circle to be stroked. The new path object
237    /// will be added to this page objects collection and then returned, wrapped inside a generic
238    /// [PdfPageObject] wrapper.
239    ///
240    /// If the containing [PdfPage] has a content regeneration strategy of
241    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
242    /// will be triggered on the page.
243    fn create_path_object_circle(
244        &mut self,
245        rect: PdfRect,
246        stroke_color: Option<PdfColor>,
247        stroke_width: Option<PdfPoints>,
248        fill_color: Option<PdfColor>,
249    ) -> Result<PdfPageObject<'a>, PdfiumError>;
250
251    /// Creates a new [PdfPagePathObject]. The new path will be created with a circle centered
252    /// at the given coordinates, with the given radius, and with the given fill and stroke settings
253    /// applied. Both the stroke color and the stroke width must be provided for the circle to be
254    /// stroked. The new path object will be added to this page objects collection and then
255    /// returned, wrapped inside a generic [PdfPageObject] wrapper.
256    ///
257    /// If the containing [PdfPage] has a content regeneration strategy of
258    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
259    /// will be triggered on the page.
260    fn create_path_object_circle_at(
261        &mut self,
262        center_x: PdfPoints,
263        center_y: PdfPoints,
264        radius: PdfPoints,
265        stroke_color: Option<PdfColor>,
266        stroke_width: Option<PdfPoints>,
267        fill_color: Option<PdfColor>,
268    ) -> Result<PdfPageObject<'a>, PdfiumError>;
269
270    /// Creates a new [PdfPagePathObject]. The new path will be created with an ellipse that fills
271    /// the given rectangle, with the given fill and stroke settings applied. Both the stroke color
272    /// and the stroke width must be provided for the ellipse to be stroked. The new path object
273    /// will be added to this page objects collection and then returned, wrapped inside a generic
274    /// [PdfPageObject] wrapper.
275    ///
276    /// If the containing [PdfPage] has a content regeneration strategy of
277    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
278    /// will be triggered on the page.
279    fn create_path_object_ellipse(
280        &mut self,
281        rect: PdfRect,
282        stroke_color: Option<PdfColor>,
283        stroke_width: Option<PdfPoints>,
284        fill_color: Option<PdfColor>,
285    ) -> Result<PdfPageObject<'a>, PdfiumError>;
286
287    /// Creates a new [PdfPagePathObject]. The new path will be created with an ellipse centered
288    /// at the given coordinates, with the given radii, and with the given fill and stroke settings
289    /// applied. Both the stroke color and the stroke width must be provided for the ellipse to be
290    /// stroked. The new path object will be added to this page objects collection and then
291    /// returned, wrapped inside a generic [PdfPageObject] wrapper.
292    ///
293    /// If the containing [PdfPage] has a content regeneration strategy of
294    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
295    /// will be triggered on the page.
296    #[allow(clippy::too_many_arguments)]
297    fn create_path_object_ellipse_at(
298        &mut self,
299        center_x: PdfPoints,
300        center_y: PdfPoints,
301        x_radius: PdfPoints,
302        y_radius: PdfPoints,
303        stroke_color: Option<PdfColor>,
304        stroke_width: Option<PdfPoints>,
305        fill_color: Option<PdfColor>,
306    ) -> Result<PdfPageObject<'a>, PdfiumError>;
307
308    /// Adds the given [PdfPageImageObject] to this page objects collection,
309    /// returning the image object wrapped inside a generic [PdfPageObject] wrapper.
310    ///
311    /// If the containing [PdfPage] has a content regeneration strategy of
312    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
313    /// will be triggered on the page.
314    #[inline]
315    fn add_image_object(
316        &mut self,
317        object: PdfPageImageObject<'a>,
318    ) -> Result<PdfPageObject<'a>, PdfiumError> {
319        self.add_object(PdfPageObject::Image(object))
320    }
321
322    /// Creates a new [PdfPageImageObject] at the given x and y page co-ordinates
323    /// from the given arguments and adds it to this page objects collection,
324    /// returning the image object wrapped inside a generic [PdfPageObject] wrapper.
325    ///
326    /// By default, new image objects have their width and height both set to 1.0 points.
327    /// If provided, the given width and/or height will be applied to the newly created object to
328    /// scale its size.
329    ///
330    /// If the containing [PdfPage] has a content regeneration strategy of
331    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
332    /// will be triggered on the page.
333    ///
334    /// This function is only available when this crate's `image` feature is enabled.
335    #[cfg(feature = "image_api")]
336    fn create_image_object(
337        &mut self,
338        x: PdfPoints,
339        y: PdfPoints,
340        image: &DynamicImage,
341        width: Option<PdfPoints>,
342        height: Option<PdfPoints>,
343    ) -> Result<PdfPageObject<'a>, PdfiumError>;
344
345    /// Removes the given [PdfPageObject] from this page objects collection. The object's
346    /// memory ownership will be removed from the [PdfPage] containing this page objects
347    /// collection, and the updated page object will be returned. It can be added back to a
348    /// page objects collection or dropped, at which point the memory owned by the object will
349    /// be freed.
350    ///
351    /// If the containing [PdfPage] has a content regeneration strategy of
352    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
353    /// will be triggered on the page.
354    fn remove_object(
355        &mut self,
356        object: PdfPageObject<'a>,
357    ) -> Result<PdfPageObject<'a>, PdfiumError>;
358
359    /// Removes the [PdfPageObject] at the given index from this page objects collection.
360    /// The object's memory ownership will be removed from the [PdfPage] containing this page objects
361    /// collection, and the updated page object will be returned. It can be added back into a
362    /// page objects collection or discarded, at which point the memory owned by the object will
363    /// be freed.
364    ///
365    /// If the containing [PdfPage] has a content regeneration strategy of
366    /// [PdfPageContentRegenerationStrategy::AutomaticOnEveryChange] then content regeneration
367    /// will be triggered on the page.
368    fn remove_object_at_index(
369        &mut self,
370        index: PdfPageObjectIndex,
371    ) -> Result<PdfPageObject<'a>, PdfiumError>;
372}
373
374// Blanket implementation for all PdfPageObjects collection types.
375
376impl<'a, T> PdfPageObjectsCommon<'a> for T
377where
378    T: PdfPageObjectsPrivate<'a>,
379{
380    #[inline]
381    fn len(&self) -> PdfPageObjectIndex {
382        self.len_impl()
383    }
384
385    #[inline]
386    fn get(&self, index: PdfPageObjectIndex) -> Result<PdfPageObject<'a>, PdfiumError> {
387        self.get_impl(index)
388    }
389
390    #[inline]
391    fn iter(&'a self) -> PdfPageObjectsIterator<'a> {
392        self.iter_impl()
393    }
394
395    #[inline]
396    fn add_object(&mut self, object: PdfPageObject<'a>) -> Result<PdfPageObject<'a>, PdfiumError> {
397        self.add_object_impl(object)
398    }
399
400    #[inline]
401    fn create_text_object(
402        &mut self,
403        x: PdfPoints,
404        y: PdfPoints,
405        text: impl ToString,
406        font: impl ToPdfFontToken,
407        font_size: PdfPoints,
408    ) -> Result<PdfPageObject<'a>, PdfiumError> {
409        let document_handle = match self.ownership() {
410            PdfPageObjectOwnership::Page(ownership) => Some(ownership.document_handle()),
411            PdfPageObjectOwnership::AttachedAnnotation(ownership) => {
412                Some(ownership.document_handle())
413            }
414            PdfPageObjectOwnership::UnattachedAnnotation(ownership) => {
415                Some(ownership.document_handle())
416            }
417            _ => None,
418        };
419
420        if let Some(document_handle) = document_handle {
421            let mut object = PdfPageTextObject::new_from_handles(
422                document_handle,
423                text,
424                font.token().handle(),
425                font_size,
426                self.bindings(),
427            )?;
428
429            object.translate(x, y)?;
430
431            self.add_text_object(object)
432        } else {
433            Err(PdfiumError::OwnershipNotAttachedToPage)
434        }
435    }
436
437    #[inline]
438    fn create_path_object_line(
439        &mut self,
440        x1: PdfPoints,
441        y1: PdfPoints,
442        x2: PdfPoints,
443        y2: PdfPoints,
444        stroke_color: PdfColor,
445        stroke_width: PdfPoints,
446    ) -> Result<PdfPageObject<'a>, PdfiumError> {
447        let object = PdfPagePathObject::new_line_from_bindings(
448            self.bindings(),
449            x1,
450            y1,
451            x2,
452            y2,
453            stroke_color,
454            stroke_width,
455        )?;
456
457        self.add_path_object(object)
458    }
459
460    #[inline]
461    fn create_path_object_bezier(
462        &mut self,
463        x1: PdfPoints,
464        y1: PdfPoints,
465        x2: PdfPoints,
466        y2: PdfPoints,
467        control1_x: PdfPoints,
468        control1_y: PdfPoints,
469        control2_x: PdfPoints,
470        control2_y: PdfPoints,
471        stroke_color: PdfColor,
472        stroke_width: PdfPoints,
473    ) -> Result<PdfPageObject<'a>, PdfiumError> {
474        let object = PdfPagePathObject::new_bezier_from_bindings(
475            self.bindings(),
476            x1,
477            y1,
478            x2,
479            y2,
480            control1_x,
481            control1_y,
482            control2_x,
483            control2_y,
484            stroke_color,
485            stroke_width,
486        )?;
487
488        self.add_path_object(object)
489    }
490
491    #[inline]
492    fn create_path_object_rect(
493        &mut self,
494        rect: PdfRect,
495        stroke_color: Option<PdfColor>,
496        stroke_width: Option<PdfPoints>,
497        fill_color: Option<PdfColor>,
498    ) -> Result<PdfPageObject<'a>, PdfiumError> {
499        let object = PdfPagePathObject::new_rect_from_bindings(
500            self.bindings(),
501            rect,
502            stroke_color,
503            stroke_width,
504            fill_color,
505        )?;
506
507        self.add_path_object(object)
508    }
509
510    #[inline]
511    fn create_path_object_circle(
512        &mut self,
513        rect: PdfRect,
514        stroke_color: Option<PdfColor>,
515        stroke_width: Option<PdfPoints>,
516        fill_color: Option<PdfColor>,
517    ) -> Result<PdfPageObject<'a>, PdfiumError> {
518        let object = PdfPagePathObject::new_circle_from_bindings(
519            self.bindings(),
520            rect,
521            stroke_color,
522            stroke_width,
523            fill_color,
524        )?;
525
526        self.add_path_object(object)
527    }
528
529    #[inline]
530    fn create_path_object_circle_at(
531        &mut self,
532        center_x: PdfPoints,
533        center_y: PdfPoints,
534        radius: PdfPoints,
535        stroke_color: Option<PdfColor>,
536        stroke_width: Option<PdfPoints>,
537        fill_color: Option<PdfColor>,
538    ) -> Result<PdfPageObject<'a>, PdfiumError> {
539        let object = PdfPagePathObject::new_circle_at_from_bindings(
540            self.bindings(),
541            center_x,
542            center_y,
543            radius,
544            stroke_color,
545            stroke_width,
546            fill_color,
547        )?;
548
549        self.add_path_object(object)
550    }
551
552    #[inline]
553    fn create_path_object_ellipse(
554        &mut self,
555        rect: PdfRect,
556        stroke_color: Option<PdfColor>,
557        stroke_width: Option<PdfPoints>,
558        fill_color: Option<PdfColor>,
559    ) -> Result<PdfPageObject<'a>, PdfiumError> {
560        let object = PdfPagePathObject::new_ellipse_from_bindings(
561            self.bindings(),
562            rect,
563            stroke_color,
564            stroke_width,
565            fill_color,
566        )?;
567
568        self.add_path_object(object)
569    }
570
571    #[inline]
572    fn create_path_object_ellipse_at(
573        &mut self,
574        center_x: PdfPoints,
575        center_y: PdfPoints,
576        x_radius: PdfPoints,
577        y_radius: PdfPoints,
578        stroke_color: Option<PdfColor>,
579        stroke_width: Option<PdfPoints>,
580        fill_color: Option<PdfColor>,
581    ) -> Result<PdfPageObject<'a>, PdfiumError> {
582        let object = PdfPagePathObject::new_ellipse_at_from_bindings(
583            self.bindings(),
584            center_x,
585            center_y,
586            x_radius,
587            y_radius,
588            stroke_color,
589            stroke_width,
590            fill_color,
591        )?;
592
593        self.add_path_object(object)
594    }
595
596    #[cfg(feature = "image_api")]
597    fn create_image_object(
598        &mut self,
599        x: PdfPoints,
600        y: PdfPoints,
601        image: &DynamicImage,
602        width: Option<PdfPoints>,
603        height: Option<PdfPoints>,
604    ) -> Result<PdfPageObject<'a>, PdfiumError> {
605        let document_handle = match self.ownership() {
606            PdfPageObjectOwnership::Page(ownership) => Some(ownership.document_handle()),
607            PdfPageObjectOwnership::AttachedAnnotation(ownership) => {
608                Some(ownership.document_handle())
609            }
610            PdfPageObjectOwnership::UnattachedAnnotation(ownership) => {
611                Some(ownership.document_handle())
612            }
613            _ => None,
614        };
615
616        if let Some(document_handle) = document_handle {
617            let image_width = image.width();
618
619            let image_height = image.height();
620
621            let mut object = PdfPageImageObject::new_from_handle(document_handle, self.bindings())?;
622
623            object.set_image(image)?;
624
625            // Apply specified dimensions, if provided.
626
627            match (width, height) {
628                (Some(width), Some(height)) => {
629                    object.scale(width.value, height.value)?;
630                }
631                (Some(width), None) => {
632                    let aspect_ratio = image_height as f32 / image_width as f32;
633
634                    let height = width * aspect_ratio;
635
636                    object.scale(width.value, height.value)?;
637                }
638                (None, Some(height)) => {
639                    let aspect_ratio = image_height as f32 / image_width as f32;
640
641                    let width = height / aspect_ratio;
642
643                    object.scale(width.value, height.value)?;
644                }
645                (None, None) => {}
646            }
647
648            object.translate(x, y)?;
649
650            self.add_image_object(object)
651        } else {
652            Err(PdfiumError::OwnershipNotAttachedToPage)
653        }
654    }
655
656    #[inline]
657    fn remove_object(
658        &mut self,
659        object: PdfPageObject<'a>,
660    ) -> Result<PdfPageObject<'a>, PdfiumError> {
661        self.remove_object_impl(object)
662    }
663
664    fn remove_object_at_index(
665        &mut self,
666        index: PdfPageObjectIndex,
667    ) -> Result<PdfPageObject<'a>, PdfiumError> {
668        if index >= self.len() {
669            return Err(PdfiumError::PageObjectIndexOutOfBounds);
670        }
671
672        if let Ok(object) = self.get(index) {
673            self.remove_object(object)
674        } else {
675            Err(PdfiumError::PdfiumLibraryInternalError(
676                PdfiumInternalError::Unknown,
677            ))
678        }
679    }
680}
681
682/// An iterator over all the [PdfPageObject] objects in a page objects collection.
683pub struct PdfPageObjectsIterator<'a> {
684    objects: &'a dyn PdfPageObjectsPrivate<'a>,
685    next_index: PdfPageObjectIndex,
686}
687
688impl<'a> PdfPageObjectsIterator<'a> {
689    #[inline]
690    pub(crate) fn new(objects: &'a dyn PdfPageObjectsPrivate<'a>) -> Self {
691        PdfPageObjectsIterator {
692            objects,
693            next_index: 0,
694        }
695    }
696}
697
698impl<'a> Iterator for PdfPageObjectsIterator<'a> {
699    type Item = PdfPageObject<'a>;
700
701    fn next(&mut self) -> Option<Self::Item> {
702        let next = self.objects.get_impl(self.next_index);
703
704        self.next_index += 1;
705
706        next.ok()
707    }
708}