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