1use crate::bindgen::{
5 fpdf_page_t__, FPDF_DOCUMENT, FPDF_IMAGEOBJ_METADATA, FPDF_PAGE, FPDF_PAGEOBJECT,
6};
7use crate::bindings::PdfiumLibraryBindings;
8use crate::error::{PdfiumError, PdfiumInternalError};
9use crate::pdf::bitmap::PdfBitmap;
10use crate::pdf::bitmap::Pixels;
11use crate::pdf::color_space::PdfColorSpace;
12use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
13use crate::pdf::document::page::object::{PdfPageObject, PdfPageObjectOwnership};
14use crate::pdf::document::PdfDocument;
15use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
16use crate::pdf::points::PdfPoints;
17use crate::utils::mem::create_byte_buffer;
18use crate::{create_transform_getters, create_transform_setters};
19use std::convert::TryInto;
20use std::ops::{Range, RangeInclusive};
21use std::os::raw::{c_int, c_void};
22
23#[cfg(not(target_arch = "wasm32"))]
24use {
25 crate::utils::files::get_pdfium_file_accessor_from_reader,
26 std::fs::File,
27 std::io::{Read, Seek},
28 std::path::Path,
29};
30
31#[cfg(feature = "image_api")]
32use {
33 crate::pdf::bitmap::PdfBitmapFormat,
34 crate::utils::pixels::{
35 aligned_bgr_to_rgba, aligned_grayscale_to_unaligned, bgra_to_rgba, rgba_to_bgra,
36 },
37};
38
39#[cfg(feature = "image_025")]
40use image_025::{DynamicImage, EncodableLayout, GrayImage, RgbaImage};
41
42#[cfg(feature = "image_024")]
43use image_024::{DynamicImage, EncodableLayout, GrayImage, RgbaImage};
44
45#[cfg(feature = "image_023")]
46use image_023::{DynamicImage, EncodableLayout, GenericImageView, GrayImage, RgbaImage};
47
48#[cfg(doc)]
49use {
50 crate::pdf::document::page::object::PdfPageObjectType,
51 crate::pdf::document::page::objects::common::PdfPageObjectsCommon,
52 crate::pdf::document::page::PdfPage,
53};
54
55pub struct PdfPageImageObject<'a> {
73 object_handle: FPDF_PAGEOBJECT,
74 ownership: PdfPageObjectOwnership,
75 bindings: &'a dyn PdfiumLibraryBindings,
76}
77
78impl<'a> PdfPageImageObject<'a> {
79 #[inline]
80 pub(crate) fn from_pdfium(
81 object_handle: FPDF_PAGEOBJECT,
82 ownership: PdfPageObjectOwnership,
83 bindings: &'a dyn PdfiumLibraryBindings,
84 ) -> Self {
85 PdfPageImageObject {
86 object_handle,
87 ownership,
88 bindings,
89 }
90 }
91
92 #[cfg(feature = "image_api")]
104 #[inline]
105 pub fn new(document: &PdfDocument<'a>, image: &DynamicImage) -> Result<Self, PdfiumError> {
106 let mut result = Self::new_from_handle(document.handle(), document.bindings());
107
108 if let Ok(result) = result.as_mut() {
109 result.set_image(image)?;
110 }
111
112 result
113 }
114
115 #[cfg(not(feature = "image_api"))]
126 pub fn new(document: &PdfDocument<'a>) -> Result<Self, PdfiumError> {
127 Self::new_from_handle(document.handle(), document.bindings())
128 }
129
130 #[cfg(not(target_arch = "wasm32"))]
142 pub fn new_from_jpeg_file(
143 document: &PdfDocument<'a>,
144 path: &(impl AsRef<Path> + ?Sized),
145 ) -> Result<Self, PdfiumError> {
146 Self::new_from_jpeg_reader(document, File::open(path).map_err(PdfiumError::IoError)?)
147 }
148
149 #[cfg(not(target_arch = "wasm32"))]
165 pub fn new_from_jpeg_reader<R: Read + Seek>(
166 document: &PdfDocument<'a>,
167 reader: R,
168 ) -> Result<Self, PdfiumError> {
169 let object = Self::new_from_handle(document.handle(), document.bindings())?;
170
171 let mut reader = get_pdfium_file_accessor_from_reader(reader);
172
173 let result = document.bindings().FPDFImageObj_LoadJpegFileInline(
174 std::ptr::null_mut(),
175 0,
176 object.object_handle(),
177 reader.as_fpdf_file_access_mut_ptr(),
178 );
179
180 if object.bindings.is_true(result) {
181 Ok(object)
182 } else {
183 Err(PdfiumError::PdfiumLibraryInternalError(
184 PdfiumInternalError::Unknown,
185 ))
186 }
187 }
188
189 pub(crate) fn new_from_handle(
192 document: FPDF_DOCUMENT,
193 bindings: &'a dyn PdfiumLibraryBindings,
194 ) -> Result<Self, PdfiumError> {
195 let handle = bindings.FPDFPageObj_NewImageObj(document);
196
197 if handle.is_null() {
198 Err(PdfiumError::PdfiumLibraryInternalError(
199 PdfiumInternalError::Unknown,
200 ))
201 } else {
202 Ok(PdfPageImageObject {
203 object_handle: handle,
204 ownership: PdfPageObjectOwnership::unowned(),
205 bindings,
206 })
207 }
208 }
209
210 #[cfg(feature = "image_api")]
217 pub fn new_with_width(
218 document: &PdfDocument<'a>,
219 image: &DynamicImage,
220 width: PdfPoints,
221 ) -> Result<Self, PdfiumError> {
222 let aspect_ratio = image.height() as f32 / image.width() as f32;
223
224 let height = width * aspect_ratio;
225
226 Self::new_with_size(document, image, width, height)
227 }
228
229 #[cfg(feature = "image_api")]
236 pub fn new_with_height(
237 document: &PdfDocument<'a>,
238 image: &DynamicImage,
239 height: PdfPoints,
240 ) -> Result<Self, PdfiumError> {
241 let aspect_ratio = image.height() as f32 / image.width() as f32;
242
243 let width = height / aspect_ratio;
244
245 Self::new_with_size(document, image, width, height)
246 }
247
248 #[cfg(feature = "image_api")]
254 #[inline]
255 pub fn new_with_size(
256 document: &PdfDocument<'a>,
257 image: &DynamicImage,
258 width: PdfPoints,
259 height: PdfPoints,
260 ) -> Result<Self, PdfiumError> {
261 let mut result = Self::new(document, image)?;
262
263 result.scale(width.value, height.value)?;
264
265 Ok(result)
266 }
267
268 pub fn get_raw_bitmap(&self) -> Result<PdfBitmap, PdfiumError> {
272 Ok(PdfBitmap::from_pdfium(
273 self.bindings().FPDFImageObj_GetBitmap(self.object_handle()),
274 self.bindings(),
275 ))
276 }
277
278 #[cfg(feature = "image_api")]
284 #[inline]
285 pub fn get_raw_image(&self) -> Result<DynamicImage, PdfiumError> {
286 self.get_image_from_bitmap(&self.get_raw_bitmap()?)
287 }
288
289 #[inline]
293 pub fn get_processed_bitmap(&self, document: &PdfDocument) -> Result<PdfBitmap, PdfiumError> {
294 let (width, height) = self.get_current_width_and_height_from_metadata()?;
295
296 self.get_processed_bitmap_with_size(document, width, height)
297 }
298
299 #[cfg(feature = "image_api")]
305 #[inline]
306 pub fn get_processed_image(&self, document: &PdfDocument) -> Result<DynamicImage, PdfiumError> {
307 let (width, height) = self.get_current_width_and_height_from_metadata()?;
308
309 self.get_processed_image_with_size(document, width, height)
310 }
311
312 #[inline]
318 pub fn get_processed_bitmap_with_width(
319 &self,
320 document: &PdfDocument,
321 width: Pixels,
322 ) -> Result<PdfBitmap, PdfiumError> {
323 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
324
325 let aspect_ratio = current_width as f32 / current_height as f32;
326
327 self.get_processed_bitmap_with_size(
328 document,
329 width,
330 ((width as f32 / aspect_ratio) as u32)
331 .try_into()
332 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
333 )
334 }
335
336 #[cfg(feature = "image_api")]
344 #[inline]
345 pub fn get_processed_image_with_width(
346 &self,
347 document: &PdfDocument,
348 width: Pixels,
349 ) -> Result<DynamicImage, PdfiumError> {
350 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
351
352 let aspect_ratio = current_width as f32 / current_height as f32;
353
354 self.get_processed_image_with_size(
355 document,
356 width,
357 ((width as f32 / aspect_ratio) as u32)
358 .try_into()
359 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
360 )
361 }
362
363 #[inline]
369 pub fn get_processed_bitmap_with_height(
370 &self,
371 document: &PdfDocument,
372 height: Pixels,
373 ) -> Result<PdfBitmap, PdfiumError> {
374 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
375
376 let aspect_ratio = current_width as f32 / current_height as f32;
377
378 self.get_processed_bitmap_with_size(
379 document,
380 ((height as f32 * aspect_ratio) as u32)
381 .try_into()
382 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
383 height,
384 )
385 }
386
387 #[cfg(feature = "image_api")]
395 #[inline]
396 pub fn get_processed_image_with_height(
397 &self,
398 document: &PdfDocument,
399 height: Pixels,
400 ) -> Result<DynamicImage, PdfiumError> {
401 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
402
403 let aspect_ratio = current_width as f32 / current_height as f32;
404
405 self.get_processed_image_with_size(
406 document,
407 ((height as f32 * aspect_ratio) as u32)
408 .try_into()
409 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
410 height,
411 )
412 }
413
414 pub fn get_processed_bitmap_with_size(
421 &self,
422 document: &PdfDocument,
423 width: Pixels,
424 height: Pixels,
425 ) -> Result<PdfBitmap, PdfiumError> {
426 let mut matrix = self.matrix()?;
443
444 let original_matrix = matrix; if matrix.a() < 0f32 {
449 matrix.set_a(-matrix.a());
450 self.reset_matrix_impl(matrix)?;
451 }
452
453 if matrix.d() < 0f32 {
454 matrix.set_d(-matrix.d());
455 self.reset_matrix_impl(matrix)?;
456 }
457
458 let page_handle = match self.ownership() {
459 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
460 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
461 _ => None,
462 };
463
464 let bitmap_handle = match page_handle {
465 Some(page_handle) => self.bindings().FPDFImageObj_GetRenderedBitmap(
466 document.handle(),
467 page_handle,
468 self.object_handle(),
469 ),
470 None => self.bindings.FPDFImageObj_GetRenderedBitmap(
471 document.handle(),
472 std::ptr::null_mut::<fpdf_page_t__>(),
473 self.object_handle(),
474 ),
475 };
476
477 if bitmap_handle.is_null() {
478 self.reset_matrix_impl(original_matrix)?;
482 return Err(PdfiumError::PdfiumLibraryInternalError(
483 PdfiumInternalError::Unknown,
484 ));
485 }
486
487 let result = PdfBitmap::from_pdfium(bitmap_handle, self.bindings());
488
489 if width == result.width() && height == result.height() {
490 self.reset_matrix_impl(original_matrix)?;
494
495 Ok(result)
496 } else {
497 self.transform_impl(
502 width as PdfMatrixValue / result.width() as PdfMatrixValue,
503 0.0,
504 0.0,
505 height as PdfMatrixValue / result.height() as PdfMatrixValue,
506 0.0,
507 0.0,
508 )?;
509
510 let result = PdfBitmap::from_pdfium(
513 match page_handle {
514 Some(page_handle) => self.bindings().FPDFImageObj_GetRenderedBitmap(
515 document.handle(),
516 page_handle,
517 self.object_handle(),
518 ),
519 None => self.bindings.FPDFImageObj_GetRenderedBitmap(
520 document.handle(),
521 std::ptr::null_mut::<fpdf_page_t__>(),
522 self.object_handle(),
523 ),
524 },
525 self.bindings,
526 );
527
528 self.reset_matrix_impl(original_matrix)?;
531
532 Ok(result)
533 }
534 }
535
536 #[cfg(feature = "image_api")]
545 #[inline]
546 pub fn get_processed_image_with_size(
547 &self,
548 document: &PdfDocument,
549 width: Pixels,
550 height: Pixels,
551 ) -> Result<DynamicImage, PdfiumError> {
552 self.get_processed_bitmap_with_size(document, width, height)
553 .and_then(|bitmap| self.get_image_from_bitmap(&bitmap))
554 }
555
556 #[cfg(feature = "image_api")]
557 pub(crate) fn get_image_from_bitmap(
558 &self,
559 bitmap: &PdfBitmap,
560 ) -> Result<DynamicImage, PdfiumError> {
561 let handle = bitmap.handle();
562
563 let width = self.bindings.FPDFBitmap_GetWidth(handle);
564
565 let height = self.bindings.FPDFBitmap_GetHeight(handle);
566
567 let stride = self.bindings.FPDFBitmap_GetStride(handle);
568
569 let format =
570 PdfBitmapFormat::from_pdfium(self.bindings.FPDFBitmap_GetFormat(handle) as u32)?;
571
572 #[cfg(not(target_arch = "wasm32"))]
573 let buffer = self.bindings.FPDFBitmap_GetBuffer_as_slice(handle);
574
575 #[cfg(target_arch = "wasm32")]
576 let buffer_vec = self.bindings.FPDFBitmap_GetBuffer_as_vec(handle);
577 #[cfg(target_arch = "wasm32")]
578 let buffer = buffer_vec.as_slice();
579
580 match format {
581 #[allow(deprecated)]
582 PdfBitmapFormat::BGRA | PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx => {
583 RgbaImage::from_raw(width as u32, height as u32, bgra_to_rgba(buffer))
584 .map(DynamicImage::ImageRgba8)
585 }
586 PdfBitmapFormat::BGR => RgbaImage::from_raw(
587 width as u32,
588 height as u32,
589 aligned_bgr_to_rgba(buffer, width as usize, stride as usize),
590 )
591 .map(DynamicImage::ImageRgba8),
592 PdfBitmapFormat::Gray => GrayImage::from_raw(
593 width as u32,
594 height as u32,
595 aligned_grayscale_to_unaligned(buffer, width as usize, stride as usize),
596 )
597 .map(DynamicImage::ImageLuma8),
598 }
599 .ok_or(PdfiumError::ImageError)
600 }
601
602 pub(crate) fn get_current_width_and_height_from_metadata(
604 &self,
605 ) -> Result<(Pixels, Pixels), PdfiumError> {
606 let width = self.get_raw_metadata().and_then(|metadata| {
607 metadata
608 .width
609 .try_into()
610 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
611 })?;
612
613 let height = self.get_raw_metadata().and_then(|metadata| {
614 metadata
615 .height
616 .try_into()
617 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
618 })?;
619
620 Ok((width, height))
621 }
622
623 #[inline]
627 pub fn width(&self) -> Result<Pixels, PdfiumError> {
628 self.get_current_width_and_height_from_metadata()
629 .map(|(width, _height)| width)
630 }
631
632 #[inline]
636 pub fn height(&self) -> Result<Pixels, PdfiumError> {
637 self.get_current_width_and_height_from_metadata()
638 .map(|(_width, height)| height)
639 }
640
641 #[cfg(feature = "image_api")]
645 pub fn set_image(&mut self, image: &DynamicImage) -> Result<(), PdfiumError> {
646 let width: Pixels = image
647 .width()
648 .try_into()
649 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
650
651 let height: Pixels = image
652 .height()
653 .try_into()
654 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
655
656 let bitmap = PdfBitmap::empty(width, height, PdfBitmapFormat::BGRA, self.bindings)?;
657
658 let buffer = if let Some(image) = image.as_rgba8() {
659 rgba_to_bgra(image.as_bytes())
662 } else {
663 let image = image.to_rgba8();
666
667 rgba_to_bgra(image.as_bytes())
668 };
669
670 if !self
671 .bindings
672 .FPDFBitmap_SetBuffer(bitmap.handle(), buffer.as_slice())
673 {
674 return Err(PdfiumError::PdfiumLibraryInternalError(
675 PdfiumInternalError::Unknown,
676 ));
677 }
678
679 self.set_bitmap(&bitmap)
680 }
681
682 pub fn set_bitmap(&mut self, bitmap: &PdfBitmap) -> Result<(), PdfiumError> {
684 if self
685 .bindings
686 .is_true(self.bindings().FPDFImageObj_SetBitmap(
687 std::ptr::null_mut::<FPDF_PAGE>(),
688 0,
689 self.object_handle(),
690 bitmap.handle(),
691 ))
692 {
693 Ok(())
694 } else {
695 Err(PdfiumError::PdfiumLibraryInternalError(
696 PdfiumInternalError::Unknown,
697 ))
698 }
699 }
700
701 pub(crate) fn get_raw_metadata(&self) -> Result<FPDF_IMAGEOBJ_METADATA, PdfiumError> {
703 let mut metadata = FPDF_IMAGEOBJ_METADATA {
704 width: 0,
705 height: 0,
706 horizontal_dpi: 0.0,
707 vertical_dpi: 0.0,
708 bits_per_pixel: 0,
709 colorspace: 0,
710 marked_content_id: 0,
711 };
712
713 let page_handle = match self.ownership() {
714 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
715 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
716 _ => None,
717 };
718
719 let result = self.bindings().FPDFImageObj_GetImageMetadata(
720 self.object_handle(),
721 match page_handle {
722 Some(page_handle) => page_handle,
723 None => std::ptr::null_mut::<fpdf_page_t__>(),
724 },
725 &mut metadata,
726 );
727
728 if self.bindings().is_true(result) {
729 Ok(metadata)
730 } else {
731 Err(PdfiumError::PdfiumLibraryInternalError(
732 PdfiumInternalError::Unknown,
733 ))
734 }
735 }
736
737 #[inline]
741 pub fn horizontal_dpi(&self) -> Result<f32, PdfiumError> {
742 self.get_raw_metadata()
743 .map(|metadata| metadata.horizontal_dpi)
744 }
745
746 #[inline]
750 pub fn vertical_dpi(&self) -> Result<f32, PdfiumError> {
751 self.get_raw_metadata()
752 .map(|metadata| metadata.vertical_dpi)
753 }
754
755 #[inline]
759 pub fn bits_per_pixel(&self) -> Result<u8, PdfiumError> {
760 self.get_raw_metadata()
761 .map(|metadata| metadata.bits_per_pixel as u8)
762 }
763
764 #[inline]
768 pub fn color_space(&self) -> Result<PdfColorSpace, PdfiumError> {
769 self.get_raw_metadata()
770 .and_then(|metadata| PdfColorSpace::from_pdfium(metadata.colorspace as u32))
771 }
772
773 #[inline]
775 pub fn filters(&self) -> PdfPageImageObjectFilters {
776 PdfPageImageObjectFilters::new(self)
777 }
778
779 create_transform_setters!(
780 &mut Self,
781 Result<(), PdfiumError>,
782 "this [PdfPageImageObject]",
783 "this [PdfPageImageObject].",
784 "this [PdfPageImageObject],"
785 );
786
787 create_transform_getters!(
791 "this [PdfPageImageObject]",
792 "this [PdfPageImageObject].",
793 "this [PdfPageImageObject],"
794 );
795
796 }
799
800impl<'a> PdfPageObjectPrivate<'a> for PdfPageImageObject<'a> {
801 #[inline]
802 fn object_handle(&self) -> FPDF_PAGEOBJECT {
803 self.object_handle
804 }
805
806 #[inline]
807 fn ownership(&self) -> &PdfPageObjectOwnership {
808 &self.ownership
809 }
810
811 #[inline]
812 fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
813 self.ownership = ownership;
814 }
815
816 #[inline]
817 fn bindings(&self) -> &dyn PdfiumLibraryBindings {
818 self.bindings
819 }
820
821 #[inline]
822 fn is_copyable_impl(&self) -> bool {
823 self.filters().is_empty()
826 }
827
828 #[inline]
829 fn try_copy_impl<'b>(
830 &self,
831 document: FPDF_DOCUMENT,
832 bindings: &'b dyn PdfiumLibraryBindings,
833 ) -> Result<PdfPageObject<'b>, PdfiumError> {
834 if !self.filters().is_empty() {
835 return Err(PdfiumError::ImageObjectFiltersNotCopyable);
838 }
839
840 let mut copy = PdfPageImageObject::new_from_handle(document, bindings)?;
841
842 copy.set_bitmap(&self.get_raw_bitmap()?)?;
843 copy.reset_matrix(self.matrix()?)?;
844
845 Ok(PdfPageObject::Image(copy))
846 }
847}
848
849pub type PdfPageImageObjectFilterIndex = usize;
852
853pub struct PdfPageImageObjectFilters<'a> {
855 object: &'a PdfPageImageObject<'a>,
856}
857
858impl<'a> PdfPageImageObjectFilters<'a> {
859 #[inline]
860 pub(crate) fn new(object: &'a PdfPageImageObject<'a>) -> Self {
861 PdfPageImageObjectFilters { object }
862 }
863
864 pub fn len(&self) -> usize {
866 self.object
867 .bindings()
868 .FPDFImageObj_GetImageFilterCount(self.object.object_handle()) as usize
869 }
870
871 #[inline]
873 pub fn is_empty(&self) -> bool {
874 self.len() == 0
875 }
876
877 #[inline]
879 pub fn as_range(&self) -> Range<PdfPageImageObjectFilterIndex> {
880 0..self.len()
881 }
882
883 #[inline]
885 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageImageObjectFilterIndex> {
886 if self.is_empty() {
887 0..=0
888 } else {
889 0..=(self.len() - 1)
890 }
891 }
892
893 pub fn get(
895 &self,
896 index: PdfPageImageObjectFilterIndex,
897 ) -> Result<PdfPageImageObjectFilter, PdfiumError> {
898 if index >= self.len() {
899 return Err(PdfiumError::ImageObjectFilterIndexOutOfBounds);
900 }
901
902 let buffer_length = self.object.bindings().FPDFImageObj_GetImageFilter(
912 self.object.object_handle(),
913 index as c_int,
914 std::ptr::null_mut(),
915 0,
916 );
917
918 if buffer_length == 0 {
919 return Err(PdfiumError::ImageObjectFilterIndexInBoundsButFilterUndefined);
922 }
923
924 let mut buffer = create_byte_buffer(buffer_length as usize);
925
926 let result = self.object.bindings().FPDFImageObj_GetImageFilter(
927 self.object.object_handle(),
928 index as c_int,
929 buffer.as_mut_ptr() as *mut c_void,
930 buffer_length,
931 );
932
933 assert_eq!(result, buffer_length);
934
935 Ok(PdfPageImageObjectFilter::new(
936 String::from_utf8(buffer)
937 .map(|str| str.trim_end_matches(char::from(0)).to_owned())
940 .unwrap_or_default(),
941 ))
942 }
943
944 #[inline]
947 pub fn iter(&self) -> PdfPageImageObjectFiltersIterator {
948 PdfPageImageObjectFiltersIterator::new(self)
949 }
950}
951
952pub struct PdfPageImageObjectFilter {
954 name: String,
955}
956
957impl PdfPageImageObjectFilter {
958 #[inline]
959 pub(crate) fn new(name: String) -> Self {
960 PdfPageImageObjectFilter { name }
961 }
962
963 pub fn name(&self) -> &str {
965 self.name.as_str()
966 }
967}
968
969pub struct PdfPageImageObjectFiltersIterator<'a> {
972 filters: &'a PdfPageImageObjectFilters<'a>,
973 next_index: PdfPageImageObjectFilterIndex,
974}
975
976impl<'a> PdfPageImageObjectFiltersIterator<'a> {
977 #[inline]
978 pub(crate) fn new(filters: &'a PdfPageImageObjectFilters<'a>) -> Self {
979 PdfPageImageObjectFiltersIterator {
980 filters,
981 next_index: 0,
982 }
983 }
984}
985
986impl<'a> Iterator for PdfPageImageObjectFiltersIterator<'a> {
987 type Item = PdfPageImageObjectFilter;
988
989 fn next(&mut self) -> Option<Self::Item> {
990 let next = self.filters.get(self.next_index);
991
992 self.next_index += 1;
993
994 next.ok()
995 }
996}
997
998#[cfg(test)]
999mod tests {
1000 use super::*;
1001 use crate::prelude::*;
1002 use crate::utils::test::test_bind_to_pdfium;
1003
1004 #[test]
1005 fn test_page_image_object_retains_format() -> Result<(), PdfiumError> {
1006 let pdfium = test_bind_to_pdfium();
1010
1011 let image = pdfium
1012 .load_pdf_from_file("./test/path-test.pdf", None)?
1013 .pages()
1014 .get(0)?
1015 .render_with_config(&PdfRenderConfig::new().set_target_width(1000))?
1016 .as_image();
1017
1018 let mut document = pdfium.create_new_pdf()?;
1019
1020 let mut page = document
1021 .pages_mut()
1022 .create_page_at_end(PdfPagePaperSize::a4())?;
1023
1024 let object = page.objects_mut().create_image_object(
1025 PdfPoints::new(100.0),
1026 PdfPoints::new(100.0),
1027 &image,
1028 Some(PdfPoints::new(image.width() as f32)),
1029 Some(PdfPoints::new(image.height() as f32)),
1030 )?;
1031
1032 let raw_image = object.as_image_object().unwrap().get_raw_image()?;
1040
1041 let processed_image = object
1046 .as_image_object()
1047 .unwrap()
1048 .get_processed_image(&document)?;
1049
1050 assert!(compare_equality_of_byte_arrays(
1055 image.as_bytes(),
1056 raw_image.into_rgba8().as_raw().as_slice()
1057 ));
1058
1059 assert!(compare_equality_of_byte_arrays(
1060 image.as_bytes(),
1061 processed_image.into_rgba8().as_raw().as_slice()
1062 ));
1063
1064 Ok(())
1065 }
1066
1067 fn compare_equality_of_byte_arrays(a: &[u8], b: &[u8]) -> bool {
1068 if a.len() != b.len() {
1069 return false;
1070 }
1071
1072 for index in 0..a.len() {
1073 if a[index] != b[index] {
1074 return false;
1075 }
1076 }
1077
1078 true
1079 }
1080
1081 #[test]
1082 fn test_image_scaling_keeps_aspect_ratio() -> Result<(), PdfiumError> {
1083 let pdfium = test_bind_to_pdfium();
1084
1085 let mut document = pdfium.create_new_pdf()?;
1086
1087 let mut page = document
1088 .pages_mut()
1089 .create_page_at_end(PdfPagePaperSize::a4())?;
1090
1091 let image = DynamicImage::new_rgb8(100, 200);
1092
1093 let object = page.objects_mut().create_image_object(
1094 PdfPoints::new(0.0),
1095 PdfPoints::new(0.0),
1096 &image,
1097 Some(PdfPoints::new(image.width() as f32)),
1098 Some(PdfPoints::new(image.height() as f32)),
1099 )?;
1100
1101 let image_object = object.as_image_object().unwrap();
1102
1103 assert_eq!(
1104 image_object
1105 .get_processed_bitmap_with_width(&document, 50)?
1106 .height(),
1107 100
1108 );
1109 assert_eq!(
1110 image_object
1111 .get_processed_image_with_width(&document, 50)?
1112 .height(),
1113 100
1114 );
1115 assert_eq!(
1116 image_object
1117 .get_processed_bitmap_with_height(&document, 50)?
1118 .width(),
1119 25
1120 );
1121 assert_eq!(
1122 image_object
1123 .get_processed_image_with_height(&document, 50)?
1124 .width(),
1125 25
1126 );
1127
1128 Ok(())
1129 }
1130}