Skip to main content

pdfium_render/pdf/document/page/
object.rs

1//! Defines the [PdfPageObject] enum, exposing functionality related to a single renderable page object.
2
3pub(crate) mod group;
4pub(crate) mod image;
5pub(crate) mod ownership;
6pub(crate) mod path;
7pub(crate) mod private; // Keep private so that the PdfPageObjectPrivate trait is not exposed.
8pub(crate) mod shading;
9pub(crate) mod text;
10pub(crate) mod unsupported;
11pub(crate) mod x_object_form;
12
13use crate::bindgen::{
14    FPDF_LINECAP_BUTT, FPDF_LINECAP_PROJECTING_SQUARE, FPDF_LINECAP_ROUND, FPDF_LINEJOIN_BEVEL,
15    FPDF_LINEJOIN_MITER, FPDF_LINEJOIN_ROUND, FPDF_PAGEOBJECT, FPDF_PAGEOBJ_FORM,
16    FPDF_PAGEOBJ_IMAGE, FPDF_PAGEOBJ_PATH, FPDF_PAGEOBJ_SHADING, FPDF_PAGEOBJ_TEXT,
17    FPDF_PAGEOBJ_UNKNOWN,
18};
19use crate::bindings::PdfiumLibraryBindings;
20use crate::error::PdfiumError;
21use crate::pdf::color::PdfColor;
22use crate::pdf::document::page::annotation::objects::PdfPageAnnotationObjects;
23use crate::pdf::document::page::annotation::private::internal::PdfPageAnnotationPrivate;
24use crate::pdf::document::page::annotation::{PdfPageAnnotation, PdfPageAnnotationCommon};
25use crate::pdf::document::page::object::image::PdfPageImageObject;
26use crate::pdf::document::page::object::path::PdfPagePathObject;
27use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
28use crate::pdf::document::page::object::shading::PdfPageShadingObject;
29use crate::pdf::document::page::object::text::PdfPageTextObject;
30use crate::pdf::document::page::object::unsupported::PdfPageUnsupportedObject;
31use crate::pdf::document::page::object::x_object_form::PdfPageXObjectFormObject;
32use crate::pdf::document::page::objects::PdfPageObjects;
33use crate::pdf::document::page::{PdfPage, PdfPageObjectOwnership};
34use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
35use crate::pdf::path::clip_path::PdfClipPath;
36use crate::pdf::points::PdfPoints;
37use crate::pdf::quad_points::PdfQuadPoints;
38use crate::pdf::rect::PdfRect;
39use crate::pdfium::PdfiumLibraryBindingsAccessor;
40use crate::{create_transform_getters, create_transform_setters};
41use std::convert::TryInto;
42use std::os::raw::{c_int, c_uint};
43
44#[cfg(any(
45    feature = "pdfium_future",
46    feature = "pdfium_7763",
47    feature = "pdfium_7543",
48    feature = "pdfium_7350"
49))]
50use crate::pdf::document::page::objects::common::PdfPageObjectIndex;
51
52#[cfg(any(
53    feature = "pdfium_future",
54    feature = "pdfium_7763",
55    feature = "pdfium_7543",
56    feature = "pdfium_7350",
57    feature = "pdfium_7215",
58    feature = "pdfium_7123",
59    feature = "pdfium_6996"
60))]
61use crate::error::PdfiumInternalError;
62
63#[cfg(doc)]
64use crate::pdf::document::PdfDocument;
65
66/// The type of a single renderable [PdfPageObject].
67///
68/// Note that Pdfium does not support or recognize all PDF page object types. For instance,
69/// Pdfium does not currently support or recognize all types of External Object ("XObject")
70/// page object types supported by Adobe Acrobat and Foxit's commercial PDF SDK. In these cases,
71/// Pdfium will return [PdfPageObjectType::Unsupported].
72#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Eq, Hash)]
73pub enum PdfPageObjectType {
74    /// Any External Object ("XObject") page object type not directly supported by Pdfium.
75    Unsupported = FPDF_PAGEOBJ_UNKNOWN as isize,
76
77    /// A page object containing renderable text.
78    Text = FPDF_PAGEOBJ_TEXT as isize,
79
80    /// A page object containing a renderable vector path.
81    Path = FPDF_PAGEOBJ_PATH as isize,
82
83    /// A page object containing a renderable bitmapped image.
84    Image = FPDF_PAGEOBJ_IMAGE as isize,
85
86    /// A page object containing a renderable geometric shape whose color is an arbitrary
87    /// function of position within the shape.
88    Shading = FPDF_PAGEOBJ_SHADING as isize,
89
90    /// A page object containing a content stream that itself may consist of multiple other page
91    /// objects. When this page object is rendered, it renders all its constituent page objects,
92    /// effectively serving as a template or stamping object.
93    ///
94    /// Despite the page object name including "form", this page object type bears no relation
95    /// to an interactive form containing form fields.
96    XObjectForm = FPDF_PAGEOBJ_FORM as isize,
97}
98
99impl PdfPageObjectType {
100    pub(crate) fn from_pdfium(value: u32) -> Result<PdfPageObjectType, PdfiumError> {
101        match value {
102            FPDF_PAGEOBJ_UNKNOWN => Ok(PdfPageObjectType::Unsupported),
103            FPDF_PAGEOBJ_TEXT => Ok(PdfPageObjectType::Text),
104            FPDF_PAGEOBJ_PATH => Ok(PdfPageObjectType::Path),
105            FPDF_PAGEOBJ_IMAGE => Ok(PdfPageObjectType::Image),
106            FPDF_PAGEOBJ_SHADING => Ok(PdfPageObjectType::Shading),
107            FPDF_PAGEOBJ_FORM => Ok(PdfPageObjectType::XObjectForm),
108            _ => Err(PdfiumError::UnknownPdfPageObjectType),
109        }
110    }
111}
112
113/// The method used to combine overlapping colors when painting one [PdfPageObject] on top of
114/// another.
115///
116/// The color being newly painted is the source color; the existing color being painted onto is the
117/// backdrop color.
118///
119/// A formal definition of these blend modes can be found in Section 7.2.4 of
120/// the PDF Reference Manual, version 1.7, on page 520.
121#[derive(Debug, Copy, Clone, PartialEq)]
122pub enum PdfPageObjectBlendMode {
123    /// Selects the source color, ignoring the backdrop.
124    Normal,
125
126    /// Multiplies the backdrop and source color values. The resulting color is always at least
127    /// as dark as either of the two constituent colors. Multiplying any color with black
128    /// produces black; multiplying with white leaves the original color unchanged.
129    /// Painting successive overlapping objects with a color other than black or white
130    /// produces progressively darker colors.
131    Multiply,
132
133    /// Multiplies the complements of the backdrop and source color values, then complements
134    /// the result.
135
136    /// The result color is always at least as light as either of the two constituent colors.
137    /// Screening any color with white produces white; screening with black leaves the original
138    /// color unchanged. The effect is similar to projecting multiple photographic slides
139    /// simultaneously onto a single screen.
140    Screen,
141
142    /// Multiplies or screens the colors, depending on the backdrop color value. Source colors
143    /// overlay the backdrop while preserving its highlights and shadows. The backdrop color is
144    /// not replaced but is mixed with the source color to reflect the lightness or darkness of
145    /// the backdrop.
146    Overlay,
147
148    /// Selects the darker of the backdrop and source colors. The backdrop is replaced with the
149    /// source where the source is darker; otherwise, it is left unchanged.
150    Darken,
151
152    /// Selects the lighter of the backdrop and source colors. The backdrop is replaced with the
153    /// source where the source is lighter; otherwise, it is left unchanged.
154    Lighten,
155
156    /// Brightens the backdrop color to reflect the source color. Painting with black produces no
157    /// changes.
158    ColorDodge,
159
160    /// Darkens the backdrop color to reflect the source color. Painting with white produces no
161    /// change.
162    ColorBurn,
163
164    /// Multiplies or screens the colors, depending on the source color value. The effect is similar
165    /// to shining a harsh spotlight on the backdrop.
166    HardLight,
167
168    /// Darkens or lightens the colors, depending on the source color value. The effect is similar
169    /// to shining a diffused spotlight on the backdrop.
170    SoftLight,
171
172    /// Subtracts the darker of the two constituent colors from the lighter color.
173    /// Painting with white inverts the backdrop color; painting with black produces no change.
174    Difference,
175
176    /// Produces an effect similar to that of the Difference mode but lower in contrast.
177    /// Painting with white inverts the backdrop color; painting with black produces no change.
178    Exclusion,
179
180    /// Preserves the luminosity of the backdrop color while adopting the hue and saturation
181    /// of the source color.
182    HSLColor,
183
184    /// Preserves the luminosity and saturation of the backdrop color while adopting the hue
185    /// of the source color.
186    HSLHue,
187
188    /// Preserves the hue and saturation of the backdrop color while adopting the luminosity
189    /// of the source color.
190    HSLLuminosity,
191
192    /// Preserves the luminosity and hue of the backdrop color while adopting the saturation
193    /// of the source color.
194    HSLSaturation,
195}
196
197impl PdfPageObjectBlendMode {
198    pub(crate) fn as_pdfium(&self) -> &str {
199        match self {
200            PdfPageObjectBlendMode::HSLColor => "Color",
201            PdfPageObjectBlendMode::ColorBurn => "ColorBurn",
202            PdfPageObjectBlendMode::ColorDodge => "ColorDodge",
203            PdfPageObjectBlendMode::Darken => "Darken",
204            PdfPageObjectBlendMode::Difference => "Difference",
205            PdfPageObjectBlendMode::Exclusion => "Exclusion",
206            PdfPageObjectBlendMode::HardLight => "HardLight",
207            PdfPageObjectBlendMode::HSLHue => "Hue",
208            PdfPageObjectBlendMode::Lighten => "Lighten",
209            PdfPageObjectBlendMode::HSLLuminosity => "Luminosity",
210            PdfPageObjectBlendMode::Multiply => "Multiply",
211            PdfPageObjectBlendMode::Normal => "Normal",
212            PdfPageObjectBlendMode::Overlay => "Overlay",
213            PdfPageObjectBlendMode::HSLSaturation => "Saturation",
214            PdfPageObjectBlendMode::Screen => "Screen",
215            PdfPageObjectBlendMode::SoftLight => "SoftLight",
216        }
217    }
218}
219
220/// The shape that should be used at the corners of stroked paths.
221///
222/// Join styles are significant only at points where consecutive segments of a path
223/// connect at an angle; segments that meet or intersect fortuitously receive no special treatment.
224///
225/// A formal definition of these styles can be found in Section 4.3.2 of
226/// the PDF Reference Manual, version 1.7, on page 216.
227#[derive(Debug, Copy, Clone, PartialEq)]
228pub enum PdfPageObjectLineJoin {
229    /// The outer edges of the strokes for the two path segments are extended
230    /// until they meet at an angle, as in a picture frame. If the segments meet at too
231    /// sharp an angle, a bevel join is used instead.
232    Miter = FPDF_LINEJOIN_MITER as isize,
233
234    /// An arc of a circle with a diameter equal to the line width is drawn
235    /// around the point where the two path segments meet, connecting the outer edges of
236    /// the strokes for the two segments. This pie-slice-shaped figure is filled in,
237    /// producing a rounded corner.
238    Round = FPDF_LINEJOIN_ROUND as isize,
239
240    /// The two path segments are finished with butt caps and the resulting notch
241    /// beyond the ends of the segments is filled with a triangle.
242    Bevel = FPDF_LINEJOIN_BEVEL as isize,
243}
244
245impl PdfPageObjectLineJoin {
246    pub(crate) fn from_pdfium(value: c_int) -> Option<Self> {
247        match value as u32 {
248            FPDF_LINEJOIN_MITER => Some(Self::Miter),
249            FPDF_LINEJOIN_ROUND => Some(Self::Round),
250            FPDF_LINEJOIN_BEVEL => Some(Self::Bevel),
251            _ => None,
252        }
253    }
254
255    pub(crate) fn as_pdfium(&self) -> u32 {
256        match self {
257            PdfPageObjectLineJoin::Miter => FPDF_LINEJOIN_MITER,
258            PdfPageObjectLineJoin::Round => FPDF_LINEJOIN_ROUND,
259            PdfPageObjectLineJoin::Bevel => FPDF_LINEJOIN_BEVEL,
260        }
261    }
262}
263
264/// The shape that should be used at the ends of open stroked paths.
265///
266/// A formal definition of these styles can be found in Section 4.3.2 of
267/// the PDF Reference Manual, version 1.7, on page 216.
268#[derive(Debug, Copy, Clone, PartialEq)]
269pub enum PdfPageObjectLineCap {
270    /// The stroke is squared off at the endpoint of the path. There is no
271    /// projection beyond the end of the path.
272    Butt = FPDF_LINECAP_BUTT as isize,
273
274    /// A semicircular arc with a diameter equal to the line width is
275    /// drawn around the endpoint and filled in.
276    Round = FPDF_LINECAP_ROUND as isize,
277
278    /// The stroke continues beyond the endpoint of the path
279    /// for a distance equal to half the line width and is squared off.
280    Square = FPDF_LINECAP_PROJECTING_SQUARE as isize,
281}
282
283impl PdfPageObjectLineCap {
284    pub(crate) fn from_pdfium(value: c_int) -> Option<Self> {
285        match value as u32 {
286            FPDF_LINECAP_BUTT => Some(Self::Butt),
287            FPDF_LINECAP_ROUND => Some(Self::Round),
288            FPDF_LINECAP_PROJECTING_SQUARE => Some(Self::Square),
289            _ => None,
290        }
291    }
292
293    pub(crate) fn as_pdfium(&self) -> u32 {
294        match self {
295            PdfPageObjectLineCap::Butt => FPDF_LINECAP_BUTT,
296            PdfPageObjectLineCap::Round => FPDF_LINECAP_ROUND,
297            PdfPageObjectLineCap::Square => FPDF_LINECAP_PROJECTING_SQUARE,
298        }
299    }
300}
301
302/// A single renderable object on a [PdfPage].
303pub enum PdfPageObject<'a> {
304    /// A page object containing renderable text.
305    Text(PdfPageTextObject<'a>),
306
307    /// A page object containing a renderable vector path.
308    Path(PdfPagePathObject<'a>),
309
310    /// A page object containing a renderable bitmapped image.
311    Image(PdfPageImageObject<'a>),
312
313    /// A page object containing a renderable geometric shape whose color is an arbitrary
314    /// function of position within the shape.
315    Shading(PdfPageShadingObject<'a>),
316
317    /// A page object containing a content stream that itself may consist of multiple other page
318    /// objects. When this page object is rendered, it renders all its constituent page objects,
319    /// effectively serving as a template or stamping object.
320    ///
321    /// Despite the page object name including "form", this page object type bears no relation
322    /// to an interactive form containing form fields.
323    XObjectForm(PdfPageXObjectFormObject<'a>),
324
325    /// Any External Object ("XObject") page object type not directly supported by Pdfium.
326    ///
327    /// Common properties shared by all [PdfPageObject] types can still be accessed for
328    /// page objects not recognized by Pdfium, but object-specific functionality
329    /// will be unavailable.
330    Unsupported(PdfPageUnsupportedObject<'a>),
331}
332
333impl<'a> PdfPageObject<'a> {
334    pub(crate) fn from_pdfium(
335        object_handle: FPDF_PAGEOBJECT,
336        ownership: PdfPageObjectOwnership,
337        bindings: &'a dyn PdfiumLibraryBindings,
338    ) -> Self {
339        match PdfPageObjectType::from_pdfium(
340            unsafe { bindings.FPDFPageObj_GetType(object_handle) } as u32
341        )
342        .unwrap_or(PdfPageObjectType::Unsupported)
343        {
344            PdfPageObjectType::Unsupported => PdfPageObject::Unsupported(
345                PdfPageUnsupportedObject::from_pdfium(object_handle, ownership),
346            ),
347            PdfPageObjectType::Text => {
348                PdfPageObject::Text(PdfPageTextObject::from_pdfium(object_handle, ownership))
349            }
350            PdfPageObjectType::Path => {
351                PdfPageObject::Path(PdfPagePathObject::from_pdfium(object_handle, ownership))
352            }
353            PdfPageObjectType::Image => {
354                PdfPageObject::Image(PdfPageImageObject::from_pdfium(object_handle, ownership))
355            }
356            PdfPageObjectType::Shading => {
357                PdfPageObject::Shading(PdfPageShadingObject::from_pdfium(object_handle, ownership))
358            }
359            PdfPageObjectType::XObjectForm => PdfPageObject::XObjectForm(
360                PdfPageXObjectFormObject::from_pdfium(object_handle, ownership),
361            ),
362        }
363    }
364
365    #[inline]
366    pub(crate) fn unwrap_as_trait(&self) -> &dyn PdfPageObjectPrivate<'a> {
367        match self {
368            PdfPageObject::Text(object) => object,
369            PdfPageObject::Path(object) => object,
370            PdfPageObject::Image(object) => object,
371            PdfPageObject::Shading(object) => object,
372            PdfPageObject::XObjectForm(object) => object,
373            PdfPageObject::Unsupported(object) => object,
374        }
375    }
376
377    #[inline]
378    pub(crate) fn unwrap_as_trait_mut(&mut self) -> &mut dyn PdfPageObjectPrivate<'a> {
379        match self {
380            PdfPageObject::Text(object) => object,
381            PdfPageObject::Path(object) => object,
382            PdfPageObject::Image(object) => object,
383            PdfPageObject::Shading(object) => object,
384            PdfPageObject::XObjectForm(object) => object,
385            PdfPageObject::Unsupported(object) => object,
386        }
387    }
388
389    /// The object type of this [PdfPageObject].
390    ///
391    /// Note that Pdfium does not support or recognize all PDF page object types. For instance,
392    /// Pdfium does not currently support or recognize the External Object ("XObject") page object
393    /// type supported by Adobe Acrobat and Foxit's commercial PDF SDK. In these cases, Pdfium
394    /// will return `PdfPageObjectType::Unsupported`.
395    #[inline]
396    pub fn object_type(&self) -> PdfPageObjectType {
397        match self {
398            PdfPageObject::Text(_) => PdfPageObjectType::Text,
399            PdfPageObject::Path(_) => PdfPageObjectType::Path,
400            PdfPageObject::Image(_) => PdfPageObjectType::Image,
401            PdfPageObject::Shading(_) => PdfPageObjectType::Shading,
402            PdfPageObject::XObjectForm(_) => PdfPageObjectType::XObjectForm,
403            PdfPageObject::Unsupported(_) => PdfPageObjectType::Unsupported,
404        }
405    }
406
407    /// Returns `true` if this [PdfPageObject] has an object type other than [PdfPageObjectType::Unsupported].
408    ///
409    /// The [PdfPageObject::as_text_object()], [PdfPageObject::as_path_object()], [PdfPageObject::as_image_object()],
410    /// [PdfPageObject::as_shading_object()], and [PdfPageObject::as_x_object_form_object()] functions
411    /// can be used to access properties and functions pertaining to a specific page object type.
412    #[inline]
413    pub fn is_supported(&self) -> bool {
414        !self.is_unsupported()
415    }
416
417    /// Returns `true` if this [PdfPageObject] has an object type of [PdfPageObjectType::Unsupported].
418    ///
419    /// Common properties shared by all [PdfPageObject] types can still be accessed for
420    /// page objects not recognized by Pdfium, but object-specific functionality
421    /// will be unavailable.
422    #[inline]
423    pub fn is_unsupported(&self) -> bool {
424        self.object_type() == PdfPageObjectType::Unsupported
425    }
426
427    /// Returns an immutable reference to the underlying [PdfPageTextObject] for this [PdfPageObject],
428    /// if this page object has an object type of [PdfPageObjectType::Text].
429    #[inline]
430    pub fn as_text_object(&self) -> Option<&PdfPageTextObject<'_>> {
431        match self {
432            PdfPageObject::Text(object) => Some(object),
433            _ => None,
434        }
435    }
436
437    /// Returns a mutable reference to the underlying [PdfPageTextObject] for this [PdfPageObject],
438    /// if this page object has an object type of [PdfPageObjectType::Text].
439    #[inline]
440    pub fn as_text_object_mut(&mut self) -> Option<&mut PdfPageTextObject<'a>> {
441        match self {
442            PdfPageObject::Text(object) => Some(object),
443            _ => None,
444        }
445    }
446
447    /// Returns an immutable reference to the underlying [PdfPagePathObject] for this [PdfPageObject],
448    /// if this page object has an object type of [PdfPageObjectType::Path].
449    #[inline]
450    pub fn as_path_object(&self) -> Option<&PdfPagePathObject<'_>> {
451        match self {
452            PdfPageObject::Path(object) => Some(object),
453            _ => None,
454        }
455    }
456
457    /// Returns a mutable reference to the underlying [PdfPagePathObject] for this [PdfPageObject],
458    /// if this page object has an object type of [PdfPageObjectType::Path].
459    #[inline]
460    pub fn as_path_object_mut(&mut self) -> Option<&mut PdfPagePathObject<'a>> {
461        match self {
462            PdfPageObject::Path(object) => Some(object),
463            _ => None,
464        }
465    }
466
467    /// Returns an immutable reference to the underlying [PdfPageImageObject] for this [PdfPageObject],
468    /// if this page object has an object type of [PdfPageObjectType::Image].
469    #[inline]
470    pub fn as_image_object(&self) -> Option<&PdfPageImageObject<'_>> {
471        match self {
472            PdfPageObject::Image(object) => Some(object),
473            _ => None,
474        }
475    }
476
477    /// Returns a mutable reference to the underlying [PdfPageImageObject] for this [PdfPageObject],
478    /// if this page object has an object type of [PdfPageObjectType::Image].
479    #[inline]
480    pub fn as_image_object_mut(&mut self) -> Option<&mut PdfPageImageObject<'a>> {
481        match self {
482            PdfPageObject::Image(object) => Some(object),
483            _ => None,
484        }
485    }
486
487    /// Returns an immutable reference to the underlying [PdfPageShadingObject] for this [PdfPageObject],
488    /// if this page object has an object type of [PdfPageObjectType::Shading].
489    #[inline]
490    pub fn as_shading_object(&self) -> Option<&PdfPageShadingObject<'_>> {
491        match self {
492            PdfPageObject::Shading(object) => Some(object),
493            _ => None,
494        }
495    }
496
497    /// Returns a mutable reference to the underlying [PdfPageShadingObject] for this [PdfPageObject],
498    /// if this page object has an object type of [PdfPageObjectType::Shading].
499    #[inline]
500    pub fn as_shading_object_mut(&mut self) -> Option<&mut PdfPageShadingObject<'a>> {
501        match self {
502            PdfPageObject::Shading(object) => Some(object),
503            _ => None,
504        }
505    }
506
507    /// Returns an immutable reference to the underlying [PdfPageXObjectFormObject] for this [PdfPageObject],
508    /// if this page object has an object type of [PdfPageObjectType::XObjectForm].
509    #[inline]
510    pub fn as_x_object_form_object(&self) -> Option<&PdfPageXObjectFormObject<'_>> {
511        match self {
512            PdfPageObject::XObjectForm(object) => Some(object),
513            _ => None,
514        }
515    }
516
517    /// Returns a mutable reference to the underlying [PdfPageXObjectFormObject] for this [PdfPageObject],
518    /// if this page object has an object type of [PdfPageObjectType::XObjectForm].
519    #[inline]
520    pub fn as_x_object_form_object_mut(&mut self) -> Option<&mut PdfPageXObjectFormObject<'a>> {
521        match self {
522            PdfPageObject::XObjectForm(object) => Some(object),
523            _ => None,
524        }
525    }
526
527    /// Returns the clip path for this object, if any.
528    pub fn get_clip_path(&self) -> Option<PdfClipPath<'_>> {
529        let path_handle = unsafe {
530            self.bindings()
531                .FPDFPageObj_GetClipPath(self.object_handle())
532        };
533
534        if path_handle.is_null() {
535            return None;
536        }
537
538        return Some(PdfClipPath::from_pdfium(
539            path_handle,
540            self.ownership().clone(),
541        ));
542    }
543
544    #[cfg(any(
545        feature = "pdfium_future",
546        feature = "pdfium_7763",
547        feature = "pdfium_7543",
548        feature = "pdfium_7350",
549        feature = "pdfium_7215",
550        feature = "pdfium_7123",
551        feature = "pdfium_6996"
552    ))]
553    /// Marks this [PdfPageObject] as active on its containing page. All page objects
554    /// start in the active state by default.
555    pub fn set_active(&mut self) -> Result<(), PdfiumError> {
556        if self.bindings().is_true(unsafe {
557            self.bindings()
558                .FPDFPageObj_SetIsActive(self.object_handle(), self.bindings().TRUE())
559        }) {
560            Ok(())
561        } else {
562            Err(PdfiumError::PdfiumLibraryInternalError(
563                PdfiumInternalError::Unknown,
564            ))
565        }
566    }
567
568    #[cfg(any(
569        feature = "pdfium_future",
570        feature = "pdfium_7763",
571        feature = "pdfium_7543",
572        feature = "pdfium_7350",
573        feature = "pdfium_7215",
574        feature = "pdfium_7123",
575        feature = "pdfium_6996"
576    ))]
577    /// Returns `true` if this [PdfPageObject] is marked as active on its containing page.
578    pub fn is_active(&self) -> Result<bool, PdfiumError> {
579        let mut result = self.bindings().FALSE();
580
581        if self.bindings().is_true(unsafe {
582            self.bindings()
583                .FPDFPageObj_GetIsActive(self.object_handle(), &mut result)
584        }) {
585            Ok(self.bindings().is_true(result))
586        } else {
587            Err(PdfiumError::PdfiumLibraryInternalError(
588                PdfiumInternalError::Unknown,
589            ))
590        }
591    }
592
593    #[cfg(any(
594        feature = "pdfium_future",
595        feature = "pdfium_7763",
596        feature = "pdfium_7543",
597        feature = "pdfium_7350",
598        feature = "pdfium_7215",
599        feature = "pdfium_7123",
600        feature = "pdfium_6996"
601    ))]
602    /// Marks this [PdfPageObject] as inactive on its containing page. The page object will
603    /// be treated as if it were not in the document, even though it exists internally.
604    pub fn set_inactive(&mut self) -> Result<(), PdfiumError> {
605        if self.bindings().is_true(unsafe {
606            self.bindings()
607                .FPDFPageObj_SetIsActive(self.object_handle(), self.bindings().FALSE())
608        }) {
609            Ok(())
610        } else {
611            Err(PdfiumError::PdfiumLibraryInternalError(
612                PdfiumInternalError::Unknown,
613            ))
614        }
615    }
616
617    #[cfg(any(
618        feature = "pdfium_future",
619        feature = "pdfium_7763",
620        feature = "pdfium_7543",
621        feature = "pdfium_7350",
622        feature = "pdfium_7215",
623        feature = "pdfium_7123",
624        feature = "pdfium_6996"
625    ))]
626    /// Returns `true` if this [PdfPageObject] is marked as inactive on its containing page.
627    #[inline]
628    pub fn is_inactive(&self) -> Result<bool, PdfiumError> {
629        self.is_active().map(|result| !result)
630    }
631
632    create_transform_setters!(
633        &mut Self,
634        Result<(), PdfiumError>,
635        "this [PdfPageObject]",
636        "this [PdfPageObject].",
637        "this [PdfPageObject],"
638    );
639
640    // The transform_impl() and reset_matrix_impl() functions required by the
641    // create_transform_setters!() macro are provided by the PdfPageObjectPrivate trait.
642
643    create_transform_getters!(
644        "this [PdfPageObject]",
645        "this [PdfPageObject].",
646        "this [PdfPageObject],"
647    );
648
649    // The get_matrix_impl() function required by the create_transform_getters!() macro
650    // is provided by the PdfPageObjectPrivate trait.
651}
652
653impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageObject<'a> {}
654
655#[cfg(feature = "thread_safe")]
656unsafe impl<'a> Send for PdfPageObject<'a> {}
657
658#[cfg(feature = "thread_safe")]
659unsafe impl<'a> Sync for PdfPageObject<'a> {}
660
661/// Functionality common to all [PdfPageObject] objects, regardless of their [PdfPageObjectType].
662pub trait PdfPageObjectCommon<'a> {
663    /// Returns `true` if this [PdfPageObject] contains transparency.
664    fn has_transparency(&self) -> bool;
665
666    /// Returns the bounding box of this [PdfPageObject] as a quadrilateral.
667    ///
668    /// For text objects, the bottom of the bounding box is set to the font baseline. Any characters
669    /// in the text object that have glyph shapes that descends below the font baseline will extend
670    /// beneath the bottom of this bounding box. To measure the distance of the maximum descent of
671    /// any glyphs, use the [PdfPageTextObject::descent()] function.
672    fn bounds(&self) -> Result<PdfQuadPoints, PdfiumError>;
673
674    /// Returns the width of this [PdfPageObject].
675    #[inline]
676    fn width(&self) -> Result<PdfPoints, PdfiumError> {
677        Ok(self.bounds()?.width())
678    }
679
680    /// Returns the height of this [PdfPageObject].
681    #[inline]
682    fn height(&self) -> Result<PdfPoints, PdfiumError> {
683        Ok(self.bounds()?.height())
684    }
685
686    /// Returns `true` if the bounds of this [PdfPageObject] lie entirely within the given rectangle.
687    #[inline]
688    fn is_inside_rect(&self, rect: &PdfRect) -> bool {
689        self.bounds()
690            .map(|bounds| bounds.to_rect().is_inside(rect))
691            .unwrap_or(false)
692    }
693
694    /// Returns `true` if the bounds of this [PdfPageObject] lie at least partially within
695    /// the given rectangle.
696    #[inline]
697    fn does_overlap_rect(&self, rect: &PdfRect) -> bool {
698        self.bounds()
699            .map(|bounds| bounds.to_rect().does_overlap(rect))
700            .unwrap_or(false)
701    }
702
703    /// Transforms this [PdfPageObject] by applying the transformation matrix read from the given [PdfPageObject].
704    ///
705    /// Any translation, rotation, scaling, or skewing transformations currently applied to the
706    /// given [PdfPageObject] will be immediately applied to this [PdfPageObject].
707    fn transform_from(&mut self, other: &PdfPageObject) -> Result<(), PdfiumError>;
708
709    /// Sets the blend mode that will be applied when painting this [PdfPageObject].
710    ///
711    /// Note that Pdfium does not currently expose a function to read the currently set blend mode.
712    fn set_blend_mode(&mut self, blend_mode: PdfPageObjectBlendMode) -> Result<(), PdfiumError>;
713
714    /// Returns the color of any filled paths in this [PdfPageObject].
715    fn fill_color(&self) -> Result<PdfColor, PdfiumError>;
716
717    /// Sets the color of any filled paths in this [PdfPageObject].
718    fn set_fill_color(&mut self, fill_color: PdfColor) -> Result<(), PdfiumError>;
719
720    /// Returns the color of any stroked paths in this [PdfPageObject].
721    fn stroke_color(&self) -> Result<PdfColor, PdfiumError>;
722
723    /// Sets the color of any stroked paths in this [PdfPageObject].
724    ///
725    /// Even if this object's path is set with a visible color and a non-zero stroke width,
726    /// the object's stroke mode must be set in order for strokes to actually be visible.
727    fn set_stroke_color(&mut self, stroke_color: PdfColor) -> Result<(), PdfiumError>;
728
729    /// Returns the width of any stroked lines in this [PdfPageObject].
730    fn stroke_width(&self) -> Result<PdfPoints, PdfiumError>;
731
732    /// Sets the width of any stroked lines in this [PdfPageObject].
733    ///
734    /// A line width of 0 denotes the thinnest line that can be rendered at device resolution:
735    /// 1 device pixel wide. However, some devices cannot reproduce 1-pixel lines,
736    /// and on high-resolution devices, they are nearly invisible. Since the results of rendering
737    /// such zero-width lines are device-dependent, their use is not recommended.
738    ///
739    /// Even if this object's path is set with a visible color and a non-zero stroke width,
740    /// the object's stroke mode must be set in order for strokes to actually be visible.
741    fn set_stroke_width(&mut self, stroke_width: PdfPoints) -> Result<(), PdfiumError>;
742
743    /// Returns the line join style that will be used when painting stroked path segments
744    /// in this [PdfPageObject].
745    fn line_join(&self) -> Result<PdfPageObjectLineJoin, PdfiumError>;
746
747    /// Sets the line join style that will be used when painting stroked path segments
748    /// in this [PdfPageObject].
749    fn set_line_join(&mut self, line_join: PdfPageObjectLineJoin) -> Result<(), PdfiumError>;
750
751    /// Returns the line cap style that will be used when painting stroked path segments
752    /// in this [PdfPageObject].
753    fn line_cap(&self) -> Result<PdfPageObjectLineCap, PdfiumError>;
754
755    /// Sets the line cap style that will be used when painting stroked path segments
756    /// in this [PdfPageObject].
757    fn set_line_cap(&mut self, line_cap: PdfPageObjectLineCap) -> Result<(), PdfiumError>;
758
759    /// Returns the line dash phase that will be used when painting stroked path segments
760    /// in this [PdfPageObject].
761    ///
762    /// A page object's line dash pattern controls the pattern of dashes and gaps used to stroke
763    /// paths, as specified by a _dash array_ and a _dash phase_. The dash array's elements are
764    /// [PdfPoints] values that specify the lengths of alternating dashes and gaps; all values
765    /// must be non-zero and non-negative. The dash phase specifies the distance into the dash pattern
766    /// at which to start the dash.
767    ///
768    /// For more information on stroked dash patterns, refer to the PDF Reference Manual,
769    /// version 1.7, pages 217 - 218.
770    ///
771    /// Note that dash pattern save support in Pdfium was not fully stabilized until release
772    /// `chromium/5772` (May 2023). Versions of Pdfium older than this can load and render
773    /// dash patterns, but will not save dash patterns to PDF files.
774    fn dash_phase(&self) -> Result<PdfPoints, PdfiumError>;
775
776    /// Sets the line dash phase that will be used when painting stroked path segments
777    /// in this [PdfPageObject].
778    ///
779    /// A page object's line dash pattern controls the pattern of dashes and gaps used to stroke
780    /// paths, as specified by a _dash array_ and a _dash phase_. The dash array's elements are
781    /// [PdfPoints] values that specify the lengths of alternating dashes and gaps; all values
782    /// must be non-zero and non-negative. The dash phase specifies the distance into the dash pattern
783    /// at which to start the dash.
784    ///
785    /// For more information on stroked dash patterns, refer to the PDF Reference Manual,
786    /// version 1.7, pages 217 - 218.
787    ///
788    /// Note that dash pattern save support in Pdfium was not fully stabilized until release
789    /// `chromium/5772` (May 2023). Versions of Pdfium older than this can load and render
790    /// dash patterns, but will not save dash patterns to PDF files.
791    fn set_dash_phase(&mut self, dash_phase: PdfPoints) -> Result<(), PdfiumError>;
792
793    /// Returns the line dash array that will be used when painting stroked path segments
794    /// in this [PdfPageObject].
795    ///
796    /// A page object's line dash pattern controls the pattern of dashes and gaps used to stroke
797    /// paths, as specified by a _dash array_ and a _dash phase_. The dash array's elements are
798    /// [PdfPoints] values that specify the lengths of alternating dashes and gaps; all values
799    /// must be non-zero and non-negative. The dash phase specifies the distance into the dash pattern
800    /// at which to start the dash.
801    ///
802    /// For more information on stroked dash patterns, refer to the PDF Reference Manual,
803    /// version 1.7, pages 217 - 218.
804    ///
805    /// Note that dash pattern save support in Pdfium was not fully stabilized until release
806    /// `chromium/5772` (May 2023). Versions of Pdfium older than this can load and render
807    /// dash patterns, but will not save dash patterns to PDF files.
808    fn dash_array(&self) -> Result<Vec<PdfPoints>, PdfiumError>;
809
810    /// Sets the line dash array that will be used when painting stroked path segments
811    /// in this [PdfPageObject].
812    ///
813    /// A page object's line dash pattern controls the pattern of dashes and gaps used to stroke
814    /// paths, as specified by a _dash array_ and a _dash phase_. The dash array's elements are
815    /// [PdfPoints] values that specify the lengths of alternating dashes and gaps; all values
816    /// must be non-zero and non-negative. The dash phase specifies the distance into the dash pattern
817    /// at which to start the dash.
818    ///
819    /// For more information on stroked dash patterns, refer to the PDF Reference Manual,
820    /// version 1.7, pages 217 - 218.
821    ///
822    /// Note that dash pattern save support in Pdfium was not fully stabilized until release
823    /// `chromium/5772` (May 2023). Versions of Pdfium older than this can load and render
824    /// dash patterns, but will not save dash patterns to PDF files.
825    fn set_dash_array(&mut self, array: &[PdfPoints], phase: PdfPoints) -> Result<(), PdfiumError>;
826
827    /// Copies this [PdfPageObject] object into a new [PdfPageXObjectFormObject], then adds
828    /// the new form object to the page objects collection of the given [PdfPage],
829    /// returning the new form object.
830    fn copy_to_page<'b>(
831        &mut self,
832        page: &mut PdfPage<'b>,
833    ) -> Result<PdfPageObject<'b>, PdfiumError>;
834
835    /// Moves the ownership of this [PdfPageObject] to the given [PdfPage], regenerating
836    /// page content as necessary.
837    ///
838    /// An error will be returned if the destination page is in a different [PdfDocument]
839    /// than this object. Pdfium only supports safely moving objects within the
840    /// same document, not across documents.
841    fn move_to_page(&mut self, page: &mut PdfPage) -> Result<(), PdfiumError>;
842
843    /// Moves the ownership of this [PdfPageObject] to the given [PdfPageAnnotation],
844    /// regenerating page content as necessary.
845    ///
846    /// An error will be returned if the destination annotation is in a different [PdfDocument]
847    /// than this object. Pdfium only supports safely moving objects within the
848    /// same document, not across documents.
849    fn move_to_annotation(&mut self, annotation: &mut PdfPageAnnotation)
850        -> Result<(), PdfiumError>;
851}
852
853// Blanket implementation for all PdfPageObject types.
854
855impl<'a, T> PdfPageObjectCommon<'a> for T
856where
857    T: PdfPageObjectPrivate<'a>,
858{
859    #[inline]
860    fn has_transparency(&self) -> bool {
861        self.has_transparency_impl()
862    }
863
864    #[inline]
865    fn bounds(&self) -> Result<PdfQuadPoints, PdfiumError> {
866        self.bounds_impl()
867    }
868
869    #[inline]
870    fn transform_from(&mut self, other: &PdfPageObject) -> Result<(), PdfiumError> {
871        self.reset_matrix_impl(other.matrix()?)
872    }
873
874    #[inline]
875    fn set_blend_mode(&mut self, blend_mode: PdfPageObjectBlendMode) -> Result<(), PdfiumError> {
876        unsafe {
877            self.bindings()
878                .FPDFPageObj_SetBlendMode(self.object_handle(), blend_mode.as_pdfium());
879        }
880
881        Ok(())
882    }
883
884    #[inline]
885    fn fill_color(&self) -> Result<PdfColor, PdfiumError> {
886        let mut r = 0;
887        let mut g = 0;
888        let mut b = 0;
889        let mut a = 0;
890
891        if self.bindings().is_true(unsafe {
892            self.bindings().FPDFPageObj_GetFillColor(
893                self.object_handle(),
894                &mut r,
895                &mut g,
896                &mut b,
897                &mut a,
898            )
899        }) {
900            Ok(PdfColor::new(
901                r.try_into()
902                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
903                g.try_into()
904                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
905                b.try_into()
906                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
907                a.try_into()
908                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
909            ))
910        } else {
911            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
912        }
913    }
914
915    #[inline]
916    fn set_fill_color(&mut self, fill_color: PdfColor) -> Result<(), PdfiumError> {
917        if self.bindings().is_true(unsafe {
918            self.bindings().FPDFPageObj_SetFillColor(
919                self.object_handle(),
920                fill_color.red() as c_uint,
921                fill_color.green() as c_uint,
922                fill_color.blue() as c_uint,
923                fill_color.alpha() as c_uint,
924            )
925        }) {
926            Ok(())
927        } else {
928            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
929        }
930    }
931
932    #[inline]
933    fn stroke_color(&self) -> Result<PdfColor, PdfiumError> {
934        let mut r = 0;
935        let mut g = 0;
936        let mut b = 0;
937        let mut a = 0;
938
939        if self.bindings().is_true(unsafe {
940            self.bindings().FPDFPageObj_GetStrokeColor(
941                self.object_handle(),
942                &mut r,
943                &mut g,
944                &mut b,
945                &mut a,
946            )
947        }) {
948            Ok(PdfColor::new(
949                r.try_into()
950                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
951                g.try_into()
952                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
953                b.try_into()
954                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
955                a.try_into()
956                    .map_err(PdfiumError::UnableToConvertPdfiumColorValueToRustu8)?,
957            ))
958        } else {
959            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
960        }
961    }
962
963    #[inline]
964    fn set_stroke_color(&mut self, stroke_color: PdfColor) -> Result<(), PdfiumError> {
965        if self.bindings().is_true(unsafe {
966            self.bindings().FPDFPageObj_SetStrokeColor(
967                self.object_handle(),
968                stroke_color.red() as c_uint,
969                stroke_color.green() as c_uint,
970                stroke_color.blue() as c_uint,
971                stroke_color.alpha() as c_uint,
972            )
973        }) {
974            Ok(())
975        } else {
976            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
977        }
978    }
979
980    #[inline]
981    fn stroke_width(&self) -> Result<PdfPoints, PdfiumError> {
982        let mut width = 0.0;
983
984        if self.bindings().is_true(unsafe {
985            self.bindings()
986                .FPDFPageObj_GetStrokeWidth(self.object_handle(), &mut width)
987        }) {
988            Ok(PdfPoints::new(width))
989        } else {
990            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
991        }
992    }
993
994    #[inline]
995    fn set_stroke_width(&mut self, stroke_width: PdfPoints) -> Result<(), PdfiumError> {
996        if self.bindings().is_true(unsafe {
997            self.bindings()
998                .FPDFPageObj_SetStrokeWidth(self.object_handle(), stroke_width.value)
999        }) {
1000            Ok(())
1001        } else {
1002            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1003        }
1004    }
1005
1006    #[inline]
1007    fn line_join(&self) -> Result<PdfPageObjectLineJoin, PdfiumError> {
1008        PdfPageObjectLineJoin::from_pdfium(unsafe {
1009            self.bindings()
1010                .FPDFPageObj_GetLineJoin(self.object_handle())
1011        })
1012        .ok_or(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1013    }
1014
1015    #[inline]
1016    fn set_line_join(&mut self, line_join: PdfPageObjectLineJoin) -> Result<(), PdfiumError> {
1017        if self.bindings().is_true(unsafe {
1018            self.bindings()
1019                .FPDFPageObj_SetLineJoin(self.object_handle(), line_join.as_pdfium() as c_int)
1020        }) {
1021            Ok(())
1022        } else {
1023            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1024        }
1025    }
1026
1027    #[inline]
1028    fn line_cap(&self) -> Result<PdfPageObjectLineCap, PdfiumError> {
1029        PdfPageObjectLineCap::from_pdfium(unsafe {
1030            self.bindings().FPDFPageObj_GetLineCap(self.object_handle())
1031        })
1032        .ok_or(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1033    }
1034
1035    #[inline]
1036    fn set_line_cap(&mut self, line_cap: PdfPageObjectLineCap) -> Result<(), PdfiumError> {
1037        if self.bindings().is_true(unsafe {
1038            self.bindings()
1039                .FPDFPageObj_SetLineCap(self.object_handle(), line_cap.as_pdfium() as c_int)
1040        }) {
1041            Ok(())
1042        } else {
1043            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1044        }
1045    }
1046
1047    #[inline]
1048    fn dash_phase(&self) -> Result<PdfPoints, PdfiumError> {
1049        let mut phase = 0.0;
1050
1051        if self.bindings().is_true(unsafe {
1052            self.bindings()
1053                .FPDFPageObj_GetDashPhase(self.object_handle(), &mut phase)
1054        }) {
1055            Ok(PdfPoints::new(phase))
1056        } else {
1057            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1058        }
1059    }
1060
1061    #[inline]
1062    fn set_dash_phase(&mut self, dash_phase: PdfPoints) -> Result<(), PdfiumError> {
1063        if self.bindings().is_true(unsafe {
1064            self.bindings()
1065                .FPDFPageObj_SetDashPhase(self.object_handle(), dash_phase.value)
1066        }) {
1067            Ok(())
1068        } else {
1069            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1070        }
1071    }
1072
1073    #[inline]
1074    fn dash_array(&self) -> Result<Vec<PdfPoints>, PdfiumError> {
1075        let dash_count = unsafe {
1076            self.bindings()
1077                .FPDFPageObj_GetDashCount(self.object_handle())
1078        } as usize;
1079
1080        let mut dash_array = vec![0.0; dash_count];
1081
1082        if self.bindings().is_true(unsafe {
1083            self.bindings().FPDFPageObj_GetDashArray(
1084                self.object_handle(),
1085                dash_array.as_mut_ptr(),
1086                dash_count,
1087            )
1088        }) {
1089            Ok(dash_array
1090                .iter()
1091                .map(|dash| PdfPoints::new(*dash))
1092                .collect())
1093        } else {
1094            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1095        }
1096    }
1097
1098    fn set_dash_array(&mut self, array: &[PdfPoints], phase: PdfPoints) -> Result<(), PdfiumError> {
1099        let dash_array = array.iter().map(|dash| dash.value).collect::<Vec<_>>();
1100
1101        if self.bindings().is_true(unsafe {
1102            self.bindings().FPDFPageObj_SetDashArray(
1103                self.object_handle(),
1104                dash_array.as_ptr(),
1105                dash_array.len(),
1106                phase.value,
1107            )
1108        }) {
1109            Ok(())
1110        } else {
1111            Err(PdfiumError::PdfiumFunctionReturnValueIndicatedFailure)
1112        }
1113    }
1114
1115    #[inline]
1116    fn copy_to_page<'b>(
1117        &mut self,
1118        page: &mut PdfPage<'b>,
1119    ) -> Result<PdfPageObject<'b>, PdfiumError> {
1120        self.copy_to_page_impl(page)
1121    }
1122
1123    fn move_to_page(&mut self, page: &mut PdfPage) -> Result<(), PdfiumError> {
1124        match self.ownership() {
1125            PdfPageObjectOwnership::Document(ownership) => {
1126                if ownership.document_handle() != page.document_handle() {
1127                    return Err(PdfiumError::CannotMoveObjectAcrossDocuments);
1128                }
1129            }
1130            PdfPageObjectOwnership::Page(_) => self.remove_object_from_page()?,
1131            PdfPageObjectOwnership::AttachedAnnotation(_)
1132            | PdfPageObjectOwnership::UnattachedAnnotation(_) => {
1133                self.remove_object_from_annotation()?
1134            }
1135            PdfPageObjectOwnership::Unowned => {}
1136        }
1137
1138        self.add_object_to_page(page.objects_mut())
1139    }
1140
1141    fn move_to_annotation(
1142        &mut self,
1143        annotation: &mut PdfPageAnnotation,
1144    ) -> Result<(), PdfiumError> {
1145        match self.ownership() {
1146            PdfPageObjectOwnership::Document(ownership) => {
1147                let annotation_document_handle = match annotation.ownership() {
1148                    PdfPageObjectOwnership::Document(ownership) => {
1149                        Some(ownership.document_handle())
1150                    }
1151                    PdfPageObjectOwnership::Page(ownership) => Some(ownership.document_handle()),
1152                    PdfPageObjectOwnership::AttachedAnnotation(ownership) => {
1153                        Some(ownership.document_handle())
1154                    }
1155                    PdfPageObjectOwnership::UnattachedAnnotation(_)
1156                    | PdfPageObjectOwnership::Unowned => None,
1157                };
1158
1159                if let Some(annotation_document_handle) = annotation_document_handle {
1160                    if ownership.document_handle() != annotation_document_handle {
1161                        return Err(PdfiumError::CannotMoveObjectAcrossDocuments);
1162                    }
1163                }
1164            }
1165            PdfPageObjectOwnership::Page(_) => self.remove_object_from_page()?,
1166            PdfPageObjectOwnership::AttachedAnnotation(_)
1167            | PdfPageObjectOwnership::UnattachedAnnotation(_) => {
1168                self.remove_object_from_annotation()?
1169            }
1170            PdfPageObjectOwnership::Unowned => {}
1171        }
1172
1173        self.add_object_to_annotation(annotation.objects())
1174    }
1175}
1176
1177impl<'a> PdfPageObjectPrivate<'a> for PdfPageObject<'a> {
1178    #[inline]
1179    fn object_handle(&self) -> FPDF_PAGEOBJECT {
1180        self.unwrap_as_trait().object_handle()
1181    }
1182
1183    #[inline]
1184    fn ownership(&self) -> &PdfPageObjectOwnership {
1185        self.unwrap_as_trait().ownership()
1186    }
1187
1188    #[inline]
1189    fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
1190        self.unwrap_as_trait_mut().set_ownership(ownership);
1191    }
1192
1193    #[inline]
1194    fn add_object_to_page(&mut self, page_objects: &mut PdfPageObjects) -> Result<(), PdfiumError> {
1195        self.unwrap_as_trait_mut().add_object_to_page(page_objects)
1196    }
1197
1198    #[cfg(any(
1199        feature = "pdfium_future",
1200        feature = "pdfium_7763",
1201        feature = "pdfium_7543",
1202        feature = "pdfium_7350"
1203    ))]
1204    #[inline]
1205    fn insert_object_on_page(
1206        &mut self,
1207        page_objects: &mut PdfPageObjects,
1208        index: PdfPageObjectIndex,
1209    ) -> Result<(), PdfiumError> {
1210        self.unwrap_as_trait_mut()
1211            .insert_object_on_page(page_objects, index)
1212    }
1213
1214    #[inline]
1215    fn remove_object_from_page(&mut self) -> Result<(), PdfiumError> {
1216        self.unwrap_as_trait_mut().remove_object_from_page()
1217    }
1218
1219    #[inline]
1220    fn add_object_to_annotation(
1221        &mut self,
1222        annotation_objects: &PdfPageAnnotationObjects,
1223    ) -> Result<(), PdfiumError> {
1224        self.unwrap_as_trait_mut()
1225            .add_object_to_annotation(annotation_objects)
1226    }
1227
1228    #[inline]
1229    fn remove_object_from_annotation(&mut self) -> Result<(), PdfiumError> {
1230        self.unwrap_as_trait_mut().remove_object_from_annotation()
1231    }
1232
1233    #[inline]
1234    fn copy_to_page_impl<'b>(
1235        &mut self,
1236        page: &mut PdfPage<'b>,
1237    ) -> Result<PdfPageObject<'b>, PdfiumError> {
1238        self.unwrap_as_trait_mut().copy_to_page_impl(page)
1239    }
1240}
1241
1242impl<'a> From<PdfPageXObjectFormObject<'a>> for PdfPageObject<'a> {
1243    #[inline]
1244    fn from(object: PdfPageXObjectFormObject<'a>) -> Self {
1245        Self::XObjectForm(object)
1246    }
1247}
1248
1249impl<'a> From<PdfPageImageObject<'a>> for PdfPageObject<'a> {
1250    #[inline]
1251    fn from(object: PdfPageImageObject<'a>) -> Self {
1252        Self::Image(object)
1253    }
1254}
1255
1256impl<'a> From<PdfPagePathObject<'a>> for PdfPageObject<'a> {
1257    #[inline]
1258    fn from(object: PdfPagePathObject<'a>) -> Self {
1259        Self::Path(object)
1260    }
1261}
1262
1263impl<'a> From<PdfPageShadingObject<'a>> for PdfPageObject<'a> {
1264    #[inline]
1265    fn from(object: PdfPageShadingObject<'a>) -> Self {
1266        Self::Shading(object)
1267    }
1268}
1269
1270impl<'a> From<PdfPageTextObject<'a>> for PdfPageObject<'a> {
1271    #[inline]
1272    fn from(object: PdfPageTextObject<'a>) -> Self {
1273        Self::Text(object)
1274    }
1275}
1276
1277impl<'a> From<PdfPageUnsupportedObject<'a>> for PdfPageObject<'a> {
1278    #[inline]
1279    fn from(object: PdfPageUnsupportedObject<'a>) -> Self {
1280        Self::Unsupported(object)
1281    }
1282}
1283
1284impl<'a> Drop for PdfPageObject<'a> {
1285    /// Closes this [PdfPageObject], releasing held memory.
1286    #[inline]
1287    fn drop(&mut self) {
1288        // The documentation for FPDFPageObj_Destroy() states that we only need
1289        // call the function for page objects created by FPDFPageObj_CreateNew*() or
1290        // FPDFPageObj_New*Obj() _and_ where the newly-created object was _not_ subsequently
1291        // added to a PdfPage or PdfPageAnnotation via a call to FPDFPage_InsertObject() or
1292        // FPDFAnnot_AppendObject().
1293
1294        // In other words, retrieving a page object that already exists in a document evidently
1295        // does not allocate any additional resources, so we don't need to free anything.
1296        // (Indeed, if we try to, Pdfium segfaults.)
1297
1298        if !self.ownership().is_owned() {
1299            unsafe {
1300                self.bindings().FPDFPageObj_Destroy(self.object_handle());
1301            }
1302        }
1303    }
1304}
1305
1306#[cfg(test)]
1307mod tests {
1308    use crate::prelude::*;
1309    use crate::utils::test::test_bind_to_pdfium;
1310
1311    #[test]
1312    fn test_apply_matrix() -> Result<(), PdfiumError> {
1313        let pdfium = test_bind_to_pdfium();
1314
1315        let mut document = pdfium.create_new_pdf()?;
1316
1317        let mut page = document
1318            .pages_mut()
1319            .create_page_at_start(PdfPagePaperSize::a4())?;
1320
1321        let font = document.fonts_mut().times_roman();
1322
1323        let mut object = page.objects_mut().create_text_object(
1324            PdfPoints::ZERO,
1325            PdfPoints::ZERO,
1326            "My new text object",
1327            font,
1328            PdfPoints::new(10.0),
1329        )?;
1330
1331        // Apply some basic transformations to the object...
1332
1333        object.translate(PdfPoints::new(100.0), PdfPoints::new(100.0))?;
1334        object.flip_vertically()?;
1335        object.rotate_clockwise_degrees(45.0)?;
1336        object.scale(3.0, 4.0)?;
1337
1338        let previous_matrix = object.matrix()?;
1339
1340        // _Applying_ the identity matrix should not alter the current matrix.
1341
1342        object.apply_matrix(PdfMatrix::IDENTITY)?;
1343
1344        assert_eq!(previous_matrix, object.matrix()?);
1345
1346        Ok(())
1347    }
1348
1349    #[test]
1350    fn test_reset_matrix_to_identity() -> Result<(), PdfiumError> {
1351        let pdfium = test_bind_to_pdfium();
1352
1353        let mut document = pdfium.create_new_pdf()?;
1354
1355        let mut page = document
1356            .pages_mut()
1357            .create_page_at_start(PdfPagePaperSize::a4())?;
1358
1359        let font = document.fonts_mut().times_roman();
1360
1361        let mut object = page.objects_mut().create_text_object(
1362            PdfPoints::ZERO,
1363            PdfPoints::ZERO,
1364            "My new text object",
1365            font,
1366            PdfPoints::new(10.0),
1367        )?;
1368
1369        // Apply some basic transformations to the object...
1370
1371        object.translate(PdfPoints::new(100.0), PdfPoints::new(100.0))?;
1372        object.flip_vertically()?;
1373        object.rotate_clockwise_degrees(45.0)?;
1374        object.scale(3.0, 4.0)?;
1375
1376        let previous_matrix = object.matrix()?;
1377
1378        // _Resetting_ the object's matrix back to the identity matrix should wipe out
1379        // the current matrix.
1380
1381        object.reset_matrix_to_identity()?;
1382
1383        assert_ne!(previous_matrix, object.matrix()?);
1384        assert_eq!(object.matrix()?, PdfMatrix::IDENTITY);
1385
1386        Ok(())
1387    }
1388
1389    #[test]
1390    fn test_transform_captured_in_content_regeneration() -> Result<(), PdfiumError> {
1391        // The purpose of the test is to confirm that object transformations are correctly
1392        // applied to the page's content streams by automatic content regeneration.
1393        // In pdfium-render versions 0.8.27 and earlier, this was not reliably the case.
1394        // See: https://github.com/ajrcarey/pdfium-render/issues/168
1395
1396        let pdfium = test_bind_to_pdfium();
1397
1398        let mut document = pdfium.create_new_pdf()?;
1399
1400        let x = PdfPoints::new(100.0);
1401        let y = PdfPoints::new(400.0);
1402
1403        let object_matrix_before_rotation = {
1404            let mut page = document
1405                .pages_mut()
1406                .create_page_at_start(PdfPagePaperSize::a4())?;
1407
1408            let font = document
1409                .fonts_mut()
1410                .new_built_in(PdfFontBuiltin::TimesRoman);
1411
1412            let mut object = page.objects_mut().create_text_object(
1413                x,
1414                y,
1415                "Hello world!",
1416                font,
1417                PdfPoints::new(20.0),
1418            )?;
1419
1420            let object_matrix_before_rotation = object.matrix()?;
1421
1422            // In pdfium-render versions 0.8.27 and earlier, the following transformation
1423            // will not be captured by automatic content regeneration. It will be lost
1424            // when the page falls out of scope.
1425            object.rotate_clockwise_degrees(45.0)?;
1426
1427            object_matrix_before_rotation
1428        };
1429
1430        // The page has now been dropped since it has fallen out of scope. Re-open it,
1431        // retrieve the page object, and test its transformation matrix. It should be
1432        // correctly rotated, indicating the transformation was correctly captured by
1433        // automatic content regeneration.
1434
1435        assert_eq!(
1436            object_matrix_before_rotation.rotate_clockwise_degrees(45.0)?,
1437            document.pages().first()?.objects().first()?.matrix()?
1438        );
1439
1440        Ok(())
1441    }
1442}