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