Skip to main content

pdfium_render/pdf/document/page/
object.rs

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