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