1use crate::error::{PdfiumError, PdfiumInternalError};
5use crate::pdf::color::PdfColor;
6use crate::pdf::document::fonts::ToPdfFontToken;
7use crate::pdf::document::page::object::image::PdfPageImageObject;
8use crate::pdf::document::page::object::path::PdfPagePathObject;
9use crate::pdf::document::page::object::text::PdfPageTextObject;
10use crate::pdf::document::page::object::x_object_form::PdfPageXObjectFormObject;
11use crate::pdf::document::page::object::{PdfPageObject, PdfPageObjectCommon};
12use crate::pdf::document::page::objects::private::internal::PdfPageObjectsPrivate;
13use crate::pdf::document::page::PdfPageObjectOwnership;
14use crate::pdf::points::PdfPoints;
15use crate::pdf::rect::PdfRect;
16use std::ops::{Range, RangeInclusive};
17
18#[cfg(feature = "image_025")]
19use image_025::DynamicImage;
20
21#[cfg(feature = "image_024")]
22use image_024::DynamicImage;
23
24#[cfg(feature = "image_023")]
25use image_023::{DynamicImage, GenericImageView};
26
27#[cfg(doc)]
28use {
29 crate::pdf::document::page::PdfPage,
30 crate::pdf::document::page::PdfPageContentRegenerationStrategy,
31 crate::pdf::document::page::PdfPageObjects,
32};
33
34pub type PdfPageObjectIndex = usize;
36
37pub trait PdfPageObjectsCommon<'a> {
40 fn len(&self) -> PdfPageObjectIndex;
42
43 #[inline]
45 fn is_empty(&self) -> bool {
46 self.len() == 0
47 }
48
49 #[inline]
51 fn as_range(&self) -> Range<PdfPageObjectIndex> {
52 0..self.len()
53 }
54
55 #[inline]
57 fn as_range_inclusive(&self) -> RangeInclusive<PdfPageObjectIndex> {
58 if self.is_empty() {
59 0..=0
60 } else {
61 0..=(self.len() - 1)
62 }
63 }
64
65 fn get(&self, index: PdfPageObjectIndex) -> Result<PdfPageObject<'a>, PdfiumError>;
67
68 #[inline]
70 fn first(&self) -> Result<PdfPageObject<'a>, PdfiumError> {
71 if !self.is_empty() {
72 self.get(0)
73 } else {
74 Err(PdfiumError::NoPageObjectsInCollection)
75 }
76 }
77
78 #[inline]
80 fn last(&self) -> Result<PdfPageObject<'a>, PdfiumError> {
81 if !self.is_empty() {
82 self.get(self.len() - 1)
83 } else {
84 Err(PdfiumError::NoPageObjectsInCollection)
85 }
86 }
87
88 fn iter(&'a self) -> PdfPageObjectsIterator<'a>;
90
91 fn bounds(&'a self) -> PdfRect {
94 let mut bottom: f32 = 0.0;
95 let mut top: f32 = 0.0;
96 let mut left: f32 = 0.0;
97 let mut right: f32 = 0.0;
98
99 for object in self.iter() {
100 if let Ok(bounds) = object.bounds() {
101 bottom = bottom.min(bounds.bottom().value);
102 top = top.max(bounds.top().value);
103 left = left.min(bounds.left().value);
104 right = right.max(bounds.right().value);
105 }
106 }
107
108 PdfRect::new_from_values(bottom, left, top, right)
109 }
110
111 fn add_object(&mut self, object: PdfPageObject<'a>) -> Result<PdfPageObject<'a>, PdfiumError>;
119
120 #[inline]
127 fn add_text_object(
128 &mut self,
129 object: PdfPageTextObject<'a>,
130 ) -> Result<PdfPageObject<'a>, PdfiumError> {
131 self.add_object(PdfPageObject::Text(object))
132 }
133
134 fn create_text_object(
142 &mut self,
143 x: PdfPoints,
144 y: PdfPoints,
145 text: impl ToString,
146 font: impl ToPdfFontToken,
147 font_size: PdfPoints,
148 ) -> Result<PdfPageObject<'a>, PdfiumError>;
149
150 #[inline]
157 fn add_path_object(
158 &mut self,
159 object: PdfPagePathObject<'a>,
160 ) -> Result<PdfPageObject<'a>, PdfiumError> {
161 self.add_object(PdfPageObject::Path(object))
162 }
163
164 #[inline]
171 fn add_x_object_form_object(
172 &mut self,
173 object: PdfPageXObjectFormObject<'a>,
174 ) -> Result<PdfPageObject<'a>, PdfiumError> {
175 self.add_object(PdfPageObject::XObjectForm(object))
176 }
177
178 fn create_path_object_line(
186 &mut self,
187 x1: PdfPoints,
188 y1: PdfPoints,
189 x2: PdfPoints,
190 y2: PdfPoints,
191 stroke_color: PdfColor,
192 stroke_width: PdfPoints,
193 ) -> Result<PdfPageObject<'a>, PdfiumError>;
194
195 #[allow(clippy::too_many_arguments)]
203 fn create_path_object_bezier(
204 &mut self,
205 x1: PdfPoints,
206 y1: PdfPoints,
207 x2: PdfPoints,
208 y2: PdfPoints,
209 control1_x: PdfPoints,
210 control1_y: PdfPoints,
211 control2_x: PdfPoints,
212 control2_y: PdfPoints,
213 stroke_color: PdfColor,
214 stroke_width: PdfPoints,
215 ) -> Result<PdfPageObject<'a>, PdfiumError>;
216
217 fn create_path_object_rect(
227 &mut self,
228 rect: PdfRect,
229 stroke_color: Option<PdfColor>,
230 stroke_width: Option<PdfPoints>,
231 fill_color: Option<PdfColor>,
232 ) -> Result<PdfPageObject<'a>, PdfiumError>;
233
234 fn create_path_object_circle(
244 &mut self,
245 rect: PdfRect,
246 stroke_color: Option<PdfColor>,
247 stroke_width: Option<PdfPoints>,
248 fill_color: Option<PdfColor>,
249 ) -> Result<PdfPageObject<'a>, PdfiumError>;
250
251 fn create_path_object_circle_at(
261 &mut self,
262 center_x: PdfPoints,
263 center_y: PdfPoints,
264 radius: PdfPoints,
265 stroke_color: Option<PdfColor>,
266 stroke_width: Option<PdfPoints>,
267 fill_color: Option<PdfColor>,
268 ) -> Result<PdfPageObject<'a>, PdfiumError>;
269
270 fn create_path_object_ellipse(
280 &mut self,
281 rect: PdfRect,
282 stroke_color: Option<PdfColor>,
283 stroke_width: Option<PdfPoints>,
284 fill_color: Option<PdfColor>,
285 ) -> Result<PdfPageObject<'a>, PdfiumError>;
286
287 #[allow(clippy::too_many_arguments)]
297 fn create_path_object_ellipse_at(
298 &mut self,
299 center_x: PdfPoints,
300 center_y: PdfPoints,
301 x_radius: PdfPoints,
302 y_radius: PdfPoints,
303 stroke_color: Option<PdfColor>,
304 stroke_width: Option<PdfPoints>,
305 fill_color: Option<PdfColor>,
306 ) -> Result<PdfPageObject<'a>, PdfiumError>;
307
308 #[inline]
315 fn add_image_object(
316 &mut self,
317 object: PdfPageImageObject<'a>,
318 ) -> Result<PdfPageObject<'a>, PdfiumError> {
319 self.add_object(PdfPageObject::Image(object))
320 }
321
322 #[cfg(feature = "image_api")]
336 fn create_image_object(
337 &mut self,
338 x: PdfPoints,
339 y: PdfPoints,
340 image: &DynamicImage,
341 width: Option<PdfPoints>,
342 height: Option<PdfPoints>,
343 ) -> Result<PdfPageObject<'a>, PdfiumError>;
344
345 fn remove_object(
355 &mut self,
356 object: PdfPageObject<'a>,
357 ) -> Result<PdfPageObject<'a>, PdfiumError>;
358
359 fn remove_object_at_index(
369 &mut self,
370 index: PdfPageObjectIndex,
371 ) -> Result<PdfPageObject<'a>, PdfiumError>;
372}
373
374impl<'a, T> PdfPageObjectsCommon<'a> for T
377where
378 T: PdfPageObjectsPrivate<'a>,
379{
380 #[inline]
381 fn len(&self) -> PdfPageObjectIndex {
382 self.len_impl()
383 }
384
385 #[inline]
386 fn get(&self, index: PdfPageObjectIndex) -> Result<PdfPageObject<'a>, PdfiumError> {
387 self.get_impl(index)
388 }
389
390 #[inline]
391 fn iter(&'a self) -> PdfPageObjectsIterator<'a> {
392 self.iter_impl()
393 }
394
395 #[inline]
396 fn add_object(&mut self, object: PdfPageObject<'a>) -> Result<PdfPageObject<'a>, PdfiumError> {
397 self.add_object_impl(object)
398 }
399
400 #[inline]
401 fn create_text_object(
402 &mut self,
403 x: PdfPoints,
404 y: PdfPoints,
405 text: impl ToString,
406 font: impl ToPdfFontToken,
407 font_size: PdfPoints,
408 ) -> Result<PdfPageObject<'a>, PdfiumError> {
409 let document_handle = match self.ownership() {
410 PdfPageObjectOwnership::Page(ownership) => Some(ownership.document_handle()),
411 PdfPageObjectOwnership::AttachedAnnotation(ownership) => {
412 Some(ownership.document_handle())
413 }
414 PdfPageObjectOwnership::UnattachedAnnotation(ownership) => {
415 Some(ownership.document_handle())
416 }
417 _ => None,
418 };
419
420 if let Some(document_handle) = document_handle {
421 let mut object = PdfPageTextObject::new_from_handles(
422 document_handle,
423 text,
424 font.token().handle(),
425 font_size,
426 self.bindings(),
427 )?;
428
429 object.translate(x, y)?;
430
431 self.add_text_object(object)
432 } else {
433 Err(PdfiumError::OwnershipNotAttachedToPage)
434 }
435 }
436
437 #[inline]
438 fn create_path_object_line(
439 &mut self,
440 x1: PdfPoints,
441 y1: PdfPoints,
442 x2: PdfPoints,
443 y2: PdfPoints,
444 stroke_color: PdfColor,
445 stroke_width: PdfPoints,
446 ) -> Result<PdfPageObject<'a>, PdfiumError> {
447 let object = PdfPagePathObject::new_line_from_bindings(
448 self.bindings(),
449 x1,
450 y1,
451 x2,
452 y2,
453 stroke_color,
454 stroke_width,
455 )?;
456
457 self.add_path_object(object)
458 }
459
460 #[inline]
461 fn create_path_object_bezier(
462 &mut self,
463 x1: PdfPoints,
464 y1: PdfPoints,
465 x2: PdfPoints,
466 y2: PdfPoints,
467 control1_x: PdfPoints,
468 control1_y: PdfPoints,
469 control2_x: PdfPoints,
470 control2_y: PdfPoints,
471 stroke_color: PdfColor,
472 stroke_width: PdfPoints,
473 ) -> Result<PdfPageObject<'a>, PdfiumError> {
474 let object = PdfPagePathObject::new_bezier_from_bindings(
475 self.bindings(),
476 x1,
477 y1,
478 x2,
479 y2,
480 control1_x,
481 control1_y,
482 control2_x,
483 control2_y,
484 stroke_color,
485 stroke_width,
486 )?;
487
488 self.add_path_object(object)
489 }
490
491 #[inline]
492 fn create_path_object_rect(
493 &mut self,
494 rect: PdfRect,
495 stroke_color: Option<PdfColor>,
496 stroke_width: Option<PdfPoints>,
497 fill_color: Option<PdfColor>,
498 ) -> Result<PdfPageObject<'a>, PdfiumError> {
499 let object = PdfPagePathObject::new_rect_from_bindings(
500 self.bindings(),
501 rect,
502 stroke_color,
503 stroke_width,
504 fill_color,
505 )?;
506
507 self.add_path_object(object)
508 }
509
510 #[inline]
511 fn create_path_object_circle(
512 &mut self,
513 rect: PdfRect,
514 stroke_color: Option<PdfColor>,
515 stroke_width: Option<PdfPoints>,
516 fill_color: Option<PdfColor>,
517 ) -> Result<PdfPageObject<'a>, PdfiumError> {
518 let object = PdfPagePathObject::new_circle_from_bindings(
519 self.bindings(),
520 rect,
521 stroke_color,
522 stroke_width,
523 fill_color,
524 )?;
525
526 self.add_path_object(object)
527 }
528
529 #[inline]
530 fn create_path_object_circle_at(
531 &mut self,
532 center_x: PdfPoints,
533 center_y: PdfPoints,
534 radius: PdfPoints,
535 stroke_color: Option<PdfColor>,
536 stroke_width: Option<PdfPoints>,
537 fill_color: Option<PdfColor>,
538 ) -> Result<PdfPageObject<'a>, PdfiumError> {
539 let object = PdfPagePathObject::new_circle_at_from_bindings(
540 self.bindings(),
541 center_x,
542 center_y,
543 radius,
544 stroke_color,
545 stroke_width,
546 fill_color,
547 )?;
548
549 self.add_path_object(object)
550 }
551
552 #[inline]
553 fn create_path_object_ellipse(
554 &mut self,
555 rect: PdfRect,
556 stroke_color: Option<PdfColor>,
557 stroke_width: Option<PdfPoints>,
558 fill_color: Option<PdfColor>,
559 ) -> Result<PdfPageObject<'a>, PdfiumError> {
560 let object = PdfPagePathObject::new_ellipse_from_bindings(
561 self.bindings(),
562 rect,
563 stroke_color,
564 stroke_width,
565 fill_color,
566 )?;
567
568 self.add_path_object(object)
569 }
570
571 #[inline]
572 fn create_path_object_ellipse_at(
573 &mut self,
574 center_x: PdfPoints,
575 center_y: PdfPoints,
576 x_radius: PdfPoints,
577 y_radius: PdfPoints,
578 stroke_color: Option<PdfColor>,
579 stroke_width: Option<PdfPoints>,
580 fill_color: Option<PdfColor>,
581 ) -> Result<PdfPageObject<'a>, PdfiumError> {
582 let object = PdfPagePathObject::new_ellipse_at_from_bindings(
583 self.bindings(),
584 center_x,
585 center_y,
586 x_radius,
587 y_radius,
588 stroke_color,
589 stroke_width,
590 fill_color,
591 )?;
592
593 self.add_path_object(object)
594 }
595
596 #[cfg(feature = "image_api")]
597 fn create_image_object(
598 &mut self,
599 x: PdfPoints,
600 y: PdfPoints,
601 image: &DynamicImage,
602 width: Option<PdfPoints>,
603 height: Option<PdfPoints>,
604 ) -> Result<PdfPageObject<'a>, PdfiumError> {
605 let document_handle = match self.ownership() {
606 PdfPageObjectOwnership::Page(ownership) => Some(ownership.document_handle()),
607 PdfPageObjectOwnership::AttachedAnnotation(ownership) => {
608 Some(ownership.document_handle())
609 }
610 PdfPageObjectOwnership::UnattachedAnnotation(ownership) => {
611 Some(ownership.document_handle())
612 }
613 _ => None,
614 };
615
616 if let Some(document_handle) = document_handle {
617 let image_width = image.width();
618
619 let image_height = image.height();
620
621 let mut object = PdfPageImageObject::new_from_handle(document_handle, self.bindings())?;
622
623 object.set_image(image)?;
624
625 match (width, height) {
628 (Some(width), Some(height)) => {
629 object.scale(width.value, height.value)?;
630 }
631 (Some(width), None) => {
632 let aspect_ratio = image_height as f32 / image_width as f32;
633
634 let height = width * aspect_ratio;
635
636 object.scale(width.value, height.value)?;
637 }
638 (None, Some(height)) => {
639 let aspect_ratio = image_height as f32 / image_width as f32;
640
641 let width = height / aspect_ratio;
642
643 object.scale(width.value, height.value)?;
644 }
645 (None, None) => {}
646 }
647
648 object.translate(x, y)?;
649
650 self.add_image_object(object)
651 } else {
652 Err(PdfiumError::OwnershipNotAttachedToPage)
653 }
654 }
655
656 #[inline]
657 fn remove_object(
658 &mut self,
659 object: PdfPageObject<'a>,
660 ) -> Result<PdfPageObject<'a>, PdfiumError> {
661 self.remove_object_impl(object)
662 }
663
664 fn remove_object_at_index(
665 &mut self,
666 index: PdfPageObjectIndex,
667 ) -> Result<PdfPageObject<'a>, PdfiumError> {
668 if index >= self.len() {
669 return Err(PdfiumError::PageObjectIndexOutOfBounds);
670 }
671
672 if let Ok(object) = self.get(index) {
673 self.remove_object(object)
674 } else {
675 Err(PdfiumError::PdfiumLibraryInternalError(
676 PdfiumInternalError::Unknown,
677 ))
678 }
679 }
680}
681
682pub struct PdfPageObjectsIterator<'a> {
684 objects: &'a dyn PdfPageObjectsPrivate<'a>,
685 next_index: PdfPageObjectIndex,
686}
687
688impl<'a> PdfPageObjectsIterator<'a> {
689 #[inline]
690 pub(crate) fn new(objects: &'a dyn PdfPageObjectsPrivate<'a>) -> Self {
691 PdfPageObjectsIterator {
692 objects,
693 next_index: 0,
694 }
695 }
696}
697
698impl<'a> Iterator for PdfPageObjectsIterator<'a> {
699 type Item = PdfPageObject<'a>;
700
701 fn next(&mut self) -> Option<Self::Item> {
702 let next = self.objects.get_impl(self.next_index);
703
704 self.next_index += 1;
705
706 next.ok()
707 }
708}