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