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