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