pdfium_render/pdf/document/page/
object.rs

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