Skip to main content

pdfium_render/pdf/document/page/
object.rs

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