1use crate::bindgen::{FPDF_DOCUMENT, FPDF_IMAGEOBJ_METADATA, FPDF_PAGE, FPDF_PAGEOBJECT, fpdf_page_t__};
5use crate::bindings::PdfiumLibraryBindings;
6use crate::error::{PdfiumError, PdfiumInternalError};
7use crate::pdf::bitmap::PdfBitmap;
8use crate::pdf::bitmap::Pixels;
9use crate::pdf::color_space::PdfColorSpace;
10use crate::pdf::document::PdfDocument;
11use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
12use crate::pdf::document::page::object::{PdfPageObject, PdfPageObjectOwnership};
13use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
14use crate::pdf::points::PdfPoints;
15use crate::utils::mem::create_byte_buffer;
16use crate::{create_transform_getters, create_transform_setters};
17use std::convert::TryInto;
18use std::ops::{Range, RangeInclusive};
19use std::os::raw::{c_int, c_void};
20
21#[cfg(not(target_arch = "wasm32"))]
22use {
23 crate::utils::files::get_pdfium_file_accessor_from_reader,
24 std::fs::File,
25 std::io::{Read, Seek},
26 std::path::Path,
27};
28
29#[cfg(feature = "image_025")]
30use {
31 crate::pdf::bitmap::PdfBitmapFormat,
32 crate::utils::pixels::{aligned_bgr_to_rgba, aligned_grayscale_to_unaligned, bgra_to_rgba, rgba_to_bgra},
33 image_025::{DynamicImage, EncodableLayout, GrayImage, RgbaImage},
34};
35
36#[cfg(doc)]
37use {
38 crate::pdf::document::page::PdfPage, crate::pdf::document::page::object::PdfPageObjectType,
39 crate::pdf::document::page::objects::common::PdfPageObjectsCommon,
40};
41
42pub struct PdfPageImageObject<'a> {
60 object_handle: FPDF_PAGEOBJECT,
61 ownership: PdfPageObjectOwnership,
62 bindings: &'a dyn PdfiumLibraryBindings,
63}
64
65impl<'a> PdfPageImageObject<'a> {
66 #[inline]
67 pub(crate) fn from_pdfium(
68 object_handle: FPDF_PAGEOBJECT,
69 ownership: PdfPageObjectOwnership,
70 bindings: &'a dyn PdfiumLibraryBindings,
71 ) -> Self {
72 PdfPageImageObject {
73 object_handle,
74 ownership,
75 bindings,
76 }
77 }
78
79 #[cfg(feature = "image_025")]
91 #[inline]
92 pub fn new(document: &PdfDocument<'a>, image: &DynamicImage) -> Result<Self, PdfiumError> {
93 let mut result = Self::new_from_handle(document.handle(), document.bindings());
94
95 if let Ok(result) = result.as_mut() {
96 result.set_image(image)?;
97 }
98
99 result
100 }
101
102 #[cfg(not(feature = "image_025"))]
113 pub fn new(document: &PdfDocument<'a>) -> Result<Self, PdfiumError> {
114 Self::new_from_handle(document.handle(), document.bindings())
115 }
116
117 #[cfg(not(target_arch = "wasm32"))]
129 pub fn new_from_jpeg_file(
130 document: &PdfDocument<'a>,
131 path: &(impl AsRef<Path> + ?Sized),
132 ) -> Result<Self, PdfiumError> {
133 Self::new_from_jpeg_reader(document, File::open(path).map_err(PdfiumError::IoError)?)
134 }
135
136 #[cfg(not(target_arch = "wasm32"))]
152 pub fn new_from_jpeg_reader<R: Read + Seek>(document: &PdfDocument<'a>, reader: R) -> Result<Self, PdfiumError> {
153 let object = Self::new_from_handle(document.handle(), document.bindings())?;
154
155 let mut reader = get_pdfium_file_accessor_from_reader(reader);
156
157 let result = document.bindings().FPDFImageObj_LoadJpegFileInline(
158 std::ptr::null_mut(),
159 0,
160 object.object_handle(),
161 reader.as_fpdf_file_access_mut_ptr(),
162 );
163
164 if object.bindings.is_true(result) {
165 Ok(object)
166 } else {
167 Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
168 }
169 }
170
171 pub(crate) fn new_from_handle(
172 document: FPDF_DOCUMENT,
173 bindings: &'a dyn PdfiumLibraryBindings,
174 ) -> Result<Self, PdfiumError> {
175 let handle = bindings.FPDFPageObj_NewImageObj(document);
176
177 if handle.is_null() {
178 Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
179 } else {
180 Ok(PdfPageImageObject {
181 object_handle: handle,
182 ownership: PdfPageObjectOwnership::unowned(),
183 bindings,
184 })
185 }
186 }
187
188 #[cfg(feature = "image_025")]
195 pub fn new_with_width(
196 document: &PdfDocument<'a>,
197 image: &DynamicImage,
198 width: PdfPoints,
199 ) -> Result<Self, PdfiumError> {
200 let aspect_ratio = image.height() as f32 / image.width() as f32;
201
202 let height = width * aspect_ratio;
203
204 Self::new_with_size(document, image, width, height)
205 }
206
207 #[cfg(feature = "image_025")]
214 pub fn new_with_height(
215 document: &PdfDocument<'a>,
216 image: &DynamicImage,
217 height: PdfPoints,
218 ) -> Result<Self, PdfiumError> {
219 let aspect_ratio = image.height() as f32 / image.width() as f32;
220
221 let width = height / aspect_ratio;
222
223 Self::new_with_size(document, image, width, height)
224 }
225
226 #[cfg(feature = "image_025")]
232 #[inline]
233 pub fn new_with_size(
234 document: &PdfDocument<'a>,
235 image: &DynamicImage,
236 width: PdfPoints,
237 height: PdfPoints,
238 ) -> Result<Self, PdfiumError> {
239 let mut result = Self::new(document, image)?;
240
241 result.scale(width.value, height.value)?;
242
243 Ok(result)
244 }
245
246 pub fn get_raw_bitmap(&self) -> Result<PdfBitmap<'_>, PdfiumError> {
250 Ok(PdfBitmap::from_pdfium(
251 self.bindings().FPDFImageObj_GetBitmap(self.object_handle()),
252 self.bindings(),
253 ))
254 }
255
256 #[cfg(feature = "image_025")]
262 #[inline]
263 pub fn get_raw_image(&self) -> Result<DynamicImage, PdfiumError> {
264 self.get_image_from_bitmap(&self.get_raw_bitmap()?)
265 }
266
267 #[inline]
271 pub fn get_processed_bitmap(&self, document: &PdfDocument) -> Result<PdfBitmap<'_>, PdfiumError> {
272 let (width, height) = self.get_current_width_and_height_from_metadata()?;
273
274 self.get_processed_bitmap_with_size(document, width, height)
275 }
276
277 #[cfg(feature = "image_025")]
283 #[inline]
284 pub fn get_processed_image(&self, document: &PdfDocument) -> Result<DynamicImage, PdfiumError> {
285 let (width, height) = self.get_current_width_and_height_from_metadata()?;
286
287 self.get_processed_image_with_size(document, width, height)
288 }
289
290 #[inline]
296 pub fn get_processed_bitmap_with_width(
297 &self,
298 document: &PdfDocument,
299 width: Pixels,
300 ) -> Result<PdfBitmap<'_>, PdfiumError> {
301 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
302
303 let aspect_ratio = current_width as f32 / current_height as f32;
304
305 self.get_processed_bitmap_with_size(
306 document,
307 width,
308 ((width as f32 / aspect_ratio) as u32)
309 .try_into()
310 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
311 )
312 }
313
314 #[cfg(feature = "image_025")]
322 #[inline]
323 pub fn get_processed_image_with_width(
324 &self,
325 document: &PdfDocument,
326 width: Pixels,
327 ) -> Result<DynamicImage, PdfiumError> {
328 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
329
330 let aspect_ratio = current_width as f32 / current_height as f32;
331
332 self.get_processed_image_with_size(
333 document,
334 width,
335 ((width as f32 / aspect_ratio) as u32)
336 .try_into()
337 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
338 )
339 }
340
341 #[inline]
347 pub fn get_processed_bitmap_with_height(
348 &self,
349 document: &PdfDocument,
350 height: Pixels,
351 ) -> Result<PdfBitmap<'_>, PdfiumError> {
352 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
353
354 let aspect_ratio = current_width as f32 / current_height as f32;
355
356 self.get_processed_bitmap_with_size(
357 document,
358 ((height as f32 * aspect_ratio) as u32)
359 .try_into()
360 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
361 height,
362 )
363 }
364
365 #[cfg(feature = "image_025")]
373 #[inline]
374 pub fn get_processed_image_with_height(
375 &self,
376 document: &PdfDocument,
377 height: Pixels,
378 ) -> Result<DynamicImage, PdfiumError> {
379 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
380
381 let aspect_ratio = current_width as f32 / current_height as f32;
382
383 self.get_processed_image_with_size(
384 document,
385 ((height as f32 * aspect_ratio) as u32)
386 .try_into()
387 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
388 height,
389 )
390 }
391
392 pub fn get_processed_bitmap_with_size(
399 &self,
400 document: &PdfDocument,
401 width: Pixels,
402 height: Pixels,
403 ) -> Result<PdfBitmap<'_>, PdfiumError> {
404 let mut matrix = self.matrix()?;
405
406 let original_matrix = matrix;
407
408 if matrix.a() < 0f32 {
409 matrix.set_a(-matrix.a());
410 self.reset_matrix_impl(matrix)?;
411 }
412
413 if matrix.d() < 0f32 {
414 matrix.set_d(-matrix.d());
415 self.reset_matrix_impl(matrix)?;
416 }
417
418 let page_handle = match self.ownership() {
419 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
420 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
421 _ => None,
422 };
423
424 let bitmap_handle = match page_handle {
425 Some(page_handle) => {
426 self.bindings()
427 .FPDFImageObj_GetRenderedBitmap(document.handle(), page_handle, self.object_handle())
428 }
429 None => self.bindings.FPDFImageObj_GetRenderedBitmap(
430 document.handle(),
431 std::ptr::null_mut::<fpdf_page_t__>(),
432 self.object_handle(),
433 ),
434 };
435
436 if bitmap_handle.is_null() {
437 self.reset_matrix_impl(original_matrix)?;
438 return Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown));
439 }
440
441 let result = PdfBitmap::from_pdfium(bitmap_handle, self.bindings());
442
443 if width == result.width() && height == result.height() {
444 self.reset_matrix_impl(original_matrix)?;
445
446 Ok(result)
447 } else {
448 self.transform_impl(
449 width as PdfMatrixValue / result.width() as PdfMatrixValue,
450 0.0,
451 0.0,
452 height as PdfMatrixValue / result.height() as PdfMatrixValue,
453 0.0,
454 0.0,
455 )?;
456
457 let result = PdfBitmap::from_pdfium(
458 match page_handle {
459 Some(page_handle) => self.bindings().FPDFImageObj_GetRenderedBitmap(
460 document.handle(),
461 page_handle,
462 self.object_handle(),
463 ),
464 None => self.bindings.FPDFImageObj_GetRenderedBitmap(
465 document.handle(),
466 std::ptr::null_mut::<fpdf_page_t__>(),
467 self.object_handle(),
468 ),
469 },
470 self.bindings,
471 );
472
473 self.reset_matrix_impl(original_matrix)?;
474
475 Ok(result)
476 }
477 }
478
479 #[cfg(feature = "image_025")]
488 #[inline]
489 pub fn get_processed_image_with_size(
490 &self,
491 document: &PdfDocument,
492 width: Pixels,
493 height: Pixels,
494 ) -> Result<DynamicImage, PdfiumError> {
495 self.get_processed_bitmap_with_size(document, width, height)
496 .and_then(|bitmap| self.get_image_from_bitmap(&bitmap))
497 }
498
499 #[cfg(feature = "image_025")]
500 pub(crate) fn get_image_from_bitmap(&self, bitmap: &PdfBitmap) -> Result<DynamicImage, PdfiumError> {
501 let handle = bitmap.handle();
502
503 let width = self.bindings.FPDFBitmap_GetWidth(handle);
504
505 let height = self.bindings.FPDFBitmap_GetHeight(handle);
506
507 let stride = self.bindings.FPDFBitmap_GetStride(handle);
508
509 let format = PdfBitmapFormat::from_pdfium(self.bindings.FPDFBitmap_GetFormat(handle) as u32)?;
510
511 #[cfg(not(target_arch = "wasm32"))]
512 let buffer = self.bindings.FPDFBitmap_GetBuffer_as_slice(handle);
513
514 #[cfg(target_arch = "wasm32")]
515 let buffer_vec = self.bindings.FPDFBitmap_GetBuffer_as_vec(handle);
516 #[cfg(target_arch = "wasm32")]
517 let buffer = buffer_vec.as_slice();
518
519 match format {
520 #[allow(deprecated)]
521 PdfBitmapFormat::BGRA | PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx => {
522 RgbaImage::from_raw(width as u32, height as u32, bgra_to_rgba(buffer)).map(DynamicImage::ImageRgba8)
523 }
524 PdfBitmapFormat::BGR => RgbaImage::from_raw(
525 width as u32,
526 height as u32,
527 aligned_bgr_to_rgba(buffer, width as usize, stride as usize),
528 )
529 .map(DynamicImage::ImageRgba8),
530 PdfBitmapFormat::Gray => GrayImage::from_raw(
531 width as u32,
532 height as u32,
533 aligned_grayscale_to_unaligned(buffer, width as usize, stride as usize),
534 )
535 .map(DynamicImage::ImageLuma8),
536 }
537 .ok_or(PdfiumError::ImageError)
538 }
539
540 pub fn get_raw_image_data(&self) -> Result<Vec<u8>, PdfiumError> {
545 let buffer_length = self
546 .bindings()
547 .FPDFImageObj_GetImageDataRaw(self.object_handle(), std::ptr::null_mut(), 0);
548
549 if buffer_length == 0 {
550 return Ok(Vec::new());
551 }
552
553 let mut buffer = create_byte_buffer(buffer_length as usize);
554
555 let result = self.bindings().FPDFImageObj_GetImageDataRaw(
556 self.object_handle(),
557 buffer.as_mut_ptr() as *mut c_void,
558 buffer_length,
559 );
560
561 assert_eq!(result, buffer_length);
562
563 Ok(buffer)
564 }
565
566 pub(crate) fn get_current_width_and_height_from_metadata(&self) -> Result<(Pixels, Pixels), PdfiumError> {
568 let width = self
569 .get_raw_metadata()
570 .and_then(|metadata| metadata.width.try_into().map_err(|_| PdfiumError::ImageSizeOutOfBounds))?;
571
572 let height = self.get_raw_metadata().and_then(|metadata| {
573 metadata
574 .height
575 .try_into()
576 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
577 })?;
578
579 Ok((width, height))
580 }
581
582 #[inline]
586 pub fn width(&self) -> Result<Pixels, PdfiumError> {
587 self.get_current_width_and_height_from_metadata()
588 .map(|(width, _height)| width)
589 }
590
591 #[inline]
595 pub fn height(&self) -> Result<Pixels, PdfiumError> {
596 self.get_current_width_and_height_from_metadata()
597 .map(|(_width, height)| height)
598 }
599
600 #[cfg(feature = "image_025")]
604 pub fn set_image(&mut self, image: &DynamicImage) -> Result<(), PdfiumError> {
605 let width: Pixels = image
606 .width()
607 .try_into()
608 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
609
610 let height: Pixels = image
611 .height()
612 .try_into()
613 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
614
615 let bitmap = PdfBitmap::empty(width, height, PdfBitmapFormat::BGRA, self.bindings)?;
616
617 let buffer = if let Some(image) = image.as_rgba8() {
618 rgba_to_bgra(image.as_bytes())
619 } else {
620 let image = image.to_rgba8();
621
622 rgba_to_bgra(image.as_bytes())
623 };
624
625 if !self.bindings.FPDFBitmap_SetBuffer(bitmap.handle(), buffer.as_slice()) {
626 return Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown));
627 }
628
629 self.set_bitmap(&bitmap)
630 }
631
632 pub fn set_bitmap(&mut self, bitmap: &PdfBitmap) -> Result<(), PdfiumError> {
634 if self.bindings.is_true(self.bindings().FPDFImageObj_SetBitmap(
635 std::ptr::null_mut::<FPDF_PAGE>(),
636 0,
637 self.object_handle(),
638 bitmap.handle(),
639 )) {
640 Ok(())
641 } else {
642 Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
643 }
644 }
645
646 pub(crate) fn get_raw_metadata(&self) -> Result<FPDF_IMAGEOBJ_METADATA, PdfiumError> {
648 let mut metadata = FPDF_IMAGEOBJ_METADATA {
649 width: 0,
650 height: 0,
651 horizontal_dpi: 0.0,
652 vertical_dpi: 0.0,
653 bits_per_pixel: 0,
654 colorspace: 0,
655 marked_content_id: 0,
656 };
657
658 let page_handle = match self.ownership() {
659 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
660 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
661 _ => None,
662 };
663
664 let result = self.bindings().FPDFImageObj_GetImageMetadata(
665 self.object_handle(),
666 match page_handle {
667 Some(page_handle) => page_handle,
668 None => std::ptr::null_mut::<fpdf_page_t__>(),
669 },
670 &mut metadata,
671 );
672
673 if self.bindings().is_true(result) {
674 Ok(metadata)
675 } else {
676 Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
677 }
678 }
679
680 #[inline]
684 pub fn horizontal_dpi(&self) -> Result<f32, PdfiumError> {
685 self.get_raw_metadata().map(|metadata| metadata.horizontal_dpi)
686 }
687
688 #[inline]
692 pub fn vertical_dpi(&self) -> Result<f32, PdfiumError> {
693 self.get_raw_metadata().map(|metadata| metadata.vertical_dpi)
694 }
695
696 #[inline]
700 pub fn bits_per_pixel(&self) -> Result<u8, PdfiumError> {
701 self.get_raw_metadata().map(|metadata| metadata.bits_per_pixel as u8)
702 }
703
704 #[inline]
708 pub fn color_space(&self) -> Result<PdfColorSpace, PdfiumError> {
709 self.get_raw_metadata()
710 .and_then(|metadata| PdfColorSpace::from_pdfium(metadata.colorspace as u32))
711 }
712
713 #[inline]
715 pub fn filters(&self) -> PdfPageImageObjectFilters<'_> {
716 PdfPageImageObjectFilters::new(self)
717 }
718
719 create_transform_setters!(
720 &mut Self,
721 Result<(), PdfiumError>,
722 "this [PdfPageImageObject]",
723 "this [PdfPageImageObject].",
724 "this [PdfPageImageObject],"
725 );
726
727 create_transform_getters!(
728 "this [PdfPageImageObject]",
729 "this [PdfPageImageObject].",
730 "this [PdfPageImageObject],"
731 );
732}
733
734impl<'a> PdfPageObjectPrivate<'a> for PdfPageImageObject<'a> {
735 #[inline]
736 fn object_handle(&self) -> FPDF_PAGEOBJECT {
737 self.object_handle
738 }
739
740 #[inline]
741 fn ownership(&self) -> &PdfPageObjectOwnership {
742 &self.ownership
743 }
744
745 #[inline]
746 fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
747 self.ownership = ownership;
748 }
749
750 #[inline]
751 fn bindings(&self) -> &dyn PdfiumLibraryBindings {
752 self.bindings
753 }
754
755 #[inline]
756 fn is_copyable_impl(&self) -> bool {
757 self.filters().is_empty()
758 }
759
760 #[inline]
761 fn try_copy_impl<'b>(
762 &self,
763 document: FPDF_DOCUMENT,
764 bindings: &'b dyn PdfiumLibraryBindings,
765 ) -> Result<PdfPageObject<'b>, PdfiumError> {
766 if !self.filters().is_empty() {
767 return Err(PdfiumError::ImageObjectFiltersNotCopyable);
768 }
769
770 let mut copy = PdfPageImageObject::new_from_handle(document, bindings)?;
771
772 copy.set_bitmap(&self.get_raw_bitmap()?)?;
773 copy.reset_matrix(self.matrix()?)?;
774
775 Ok(PdfPageObject::Image(copy))
776 }
777}
778
779pub type PdfPageImageObjectFilterIndex = usize;
782
783pub struct PdfPageImageObjectFilters<'a> {
785 object: &'a PdfPageImageObject<'a>,
786}
787
788impl<'a> PdfPageImageObjectFilters<'a> {
789 #[inline]
790 pub(crate) fn new(object: &'a PdfPageImageObject<'a>) -> Self {
791 PdfPageImageObjectFilters { object }
792 }
793
794 pub fn len(&self) -> usize {
796 self.object
797 .bindings()
798 .FPDFImageObj_GetImageFilterCount(self.object.object_handle()) as usize
799 }
800
801 #[inline]
803 pub fn is_empty(&self) -> bool {
804 self.len() == 0
805 }
806
807 #[inline]
809 pub fn as_range(&self) -> Range<PdfPageImageObjectFilterIndex> {
810 0..self.len()
811 }
812
813 #[inline]
815 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageImageObjectFilterIndex> {
816 if self.is_empty() { 0..=0 } else { 0..=(self.len() - 1) }
817 }
818
819 pub fn get(&self, index: PdfPageImageObjectFilterIndex) -> Result<PdfPageImageObjectFilter, PdfiumError> {
821 if index >= self.len() {
822 return Err(PdfiumError::ImageObjectFilterIndexOutOfBounds);
823 }
824
825 let buffer_length = self.object.bindings().FPDFImageObj_GetImageFilter(
826 self.object.object_handle(),
827 index as c_int,
828 std::ptr::null_mut(),
829 0,
830 );
831
832 if buffer_length == 0 {
833 return Err(PdfiumError::ImageObjectFilterIndexInBoundsButFilterUndefined);
834 }
835
836 let mut buffer = create_byte_buffer(buffer_length as usize);
837
838 let result = self.object.bindings().FPDFImageObj_GetImageFilter(
839 self.object.object_handle(),
840 index as c_int,
841 buffer.as_mut_ptr() as *mut c_void,
842 buffer_length,
843 );
844
845 assert_eq!(result, buffer_length);
846
847 Ok(PdfPageImageObjectFilter::new(
848 String::from_utf8(buffer)
849 .map(|str| str.trim_end_matches(char::from(0)).to_owned())
850 .unwrap_or_default(),
851 ))
852 }
853
854 #[inline]
857 pub fn iter(&self) -> PdfPageImageObjectFiltersIterator<'_> {
858 PdfPageImageObjectFiltersIterator::new(self)
859 }
860}
861
862pub struct PdfPageImageObjectFilter {
864 name: String,
865}
866
867impl PdfPageImageObjectFilter {
868 #[inline]
869 pub(crate) fn new(name: String) -> Self {
870 PdfPageImageObjectFilter { name }
871 }
872
873 pub fn name(&self) -> &str {
875 self.name.as_str()
876 }
877}
878
879pub struct PdfPageImageObjectFiltersIterator<'a> {
882 filters: &'a PdfPageImageObjectFilters<'a>,
883 next_index: PdfPageImageObjectFilterIndex,
884}
885
886impl<'a> PdfPageImageObjectFiltersIterator<'a> {
887 #[inline]
888 pub(crate) fn new(filters: &'a PdfPageImageObjectFilters<'a>) -> Self {
889 PdfPageImageObjectFiltersIterator { filters, next_index: 0 }
890 }
891}
892
893impl<'a> Iterator for PdfPageImageObjectFiltersIterator<'a> {
894 type Item = PdfPageImageObjectFilter;
895
896 fn next(&mut self) -> Option<Self::Item> {
897 let next = self.filters.get(self.next_index);
898
899 self.next_index += 1;
900
901 next.ok()
902 }
903}
904
905impl<'a> Drop for PdfPageImageObject<'a> {
906 fn drop(&mut self) {
908 self.drop_impl();
909 }
910}
911
912#[cfg(test)]
913mod tests {
914 use super::*;
915 use crate::prelude::*;
916 use crate::utils::test::{test_bind_to_pdfium, test_fixture_path};
917
918 #[test]
919 fn test_page_image_object_retains_format() -> Result<(), PdfiumError> {
920 let pdfium = test_bind_to_pdfium();
921
922 let image = pdfium
923 .load_pdf_from_file(&test_fixture_path("path-test.pdf"), None)?
924 .pages()
925 .get(0)?
926 .render_with_config(&PdfRenderConfig::new().set_target_width(1000))?
927 .as_image()?;
928
929 let mut document = pdfium.create_new_pdf()?;
930
931 let mut page = document.pages_mut().create_page_at_end(PdfPagePaperSize::a4())?;
932
933 let object = page.objects_mut().create_image_object(
934 PdfPoints::new(100.0),
935 PdfPoints::new(100.0),
936 &image,
937 Some(PdfPoints::new(image.width() as f32)),
938 Some(PdfPoints::new(image.height() as f32)),
939 )?;
940
941 let raw_image = object.as_image_object().unwrap().get_raw_image()?;
942
943 let processed_image = object.as_image_object().unwrap().get_processed_image(&document)?;
944
945 assert!(compare_equality_of_byte_arrays(
946 image.as_bytes(),
947 raw_image.into_rgba8().as_raw().as_slice()
948 ));
949
950 assert!(compare_equality_of_byte_arrays(
951 image.as_bytes(),
952 processed_image.into_rgba8().as_raw().as_slice()
953 ));
954
955 Ok(())
956 }
957
958 fn compare_equality_of_byte_arrays(a: &[u8], b: &[u8]) -> bool {
959 if a.len() != b.len() {
960 return false;
961 }
962
963 for index in 0..a.len() {
964 if a[index] != b[index] {
965 return false;
966 }
967 }
968
969 true
970 }
971
972 #[test]
973 fn test_image_scaling_keeps_aspect_ratio() -> Result<(), PdfiumError> {
974 let pdfium = test_bind_to_pdfium();
975
976 let mut document = pdfium.create_new_pdf()?;
977
978 let mut page = document.pages_mut().create_page_at_end(PdfPagePaperSize::a4())?;
979
980 let image = DynamicImage::new_rgb8(100, 200);
981
982 let object = page.objects_mut().create_image_object(
983 PdfPoints::new(0.0),
984 PdfPoints::new(0.0),
985 &image,
986 Some(PdfPoints::new(image.width() as f32)),
987 Some(PdfPoints::new(image.height() as f32)),
988 )?;
989
990 let image_object = object.as_image_object().unwrap();
991
992 assert_eq!(
993 image_object.get_processed_bitmap_with_width(&document, 50)?.height(),
994 100
995 );
996 assert_eq!(
997 image_object.get_processed_image_with_width(&document, 50)?.height(),
998 100
999 );
1000 assert_eq!(
1001 image_object.get_processed_bitmap_with_height(&document, 50)?.width(),
1002 25
1003 );
1004 assert_eq!(image_object.get_processed_image_with_height(&document, 50)?.width(), 25);
1005
1006 Ok(())
1007 }
1008}