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(
294 &self,
295 document: &PdfDocument,
296 ) -> Result<PdfBitmap<'_>, PdfiumError> {
297 let (width, height) = self.get_current_width_and_height_from_metadata()?;
298
299 self.get_processed_bitmap_with_size(document, width, height)
300 }
301
302 #[cfg(feature = "image_api")]
308 #[inline]
309 pub fn get_processed_image(&self, document: &PdfDocument) -> Result<DynamicImage, PdfiumError> {
310 let (width, height) = self.get_current_width_and_height_from_metadata()?;
311
312 self.get_processed_image_with_size(document, width, height)
313 }
314
315 #[inline]
321 pub fn get_processed_bitmap_with_width(
322 &self,
323 document: &PdfDocument,
324 width: Pixels,
325 ) -> Result<PdfBitmap<'_>, PdfiumError> {
326 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
327
328 let aspect_ratio = current_width as f32 / current_height as f32;
329
330 self.get_processed_bitmap_with_size(
331 document,
332 width,
333 ((width as f32 / aspect_ratio) as u32)
334 .try_into()
335 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
336 )
337 }
338
339 #[cfg(feature = "image_api")]
347 #[inline]
348 pub fn get_processed_image_with_width(
349 &self,
350 document: &PdfDocument,
351 width: Pixels,
352 ) -> Result<DynamicImage, PdfiumError> {
353 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
354
355 let aspect_ratio = current_width as f32 / current_height as f32;
356
357 self.get_processed_image_with_size(
358 document,
359 width,
360 ((width as f32 / aspect_ratio) as u32)
361 .try_into()
362 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
363 )
364 }
365
366 #[inline]
372 pub fn get_processed_bitmap_with_height(
373 &self,
374 document: &PdfDocument,
375 height: Pixels,
376 ) -> Result<PdfBitmap<'_>, PdfiumError> {
377 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
378
379 let aspect_ratio = current_width as f32 / current_height as f32;
380
381 self.get_processed_bitmap_with_size(
382 document,
383 ((height as f32 * aspect_ratio) as u32)
384 .try_into()
385 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
386 height,
387 )
388 }
389
390 #[cfg(feature = "image_api")]
398 #[inline]
399 pub fn get_processed_image_with_height(
400 &self,
401 document: &PdfDocument,
402 height: Pixels,
403 ) -> Result<DynamicImage, PdfiumError> {
404 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
405
406 let aspect_ratio = current_width as f32 / current_height as f32;
407
408 self.get_processed_image_with_size(
409 document,
410 ((height as f32 * aspect_ratio) as u32)
411 .try_into()
412 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
413 height,
414 )
415 }
416
417 pub fn get_processed_bitmap_with_size(
424 &self,
425 document: &PdfDocument,
426 width: Pixels,
427 height: Pixels,
428 ) -> Result<PdfBitmap<'_>, PdfiumError> {
429 let mut matrix = self.matrix()?;
446
447 let original_matrix = matrix; if matrix.a() < 0f32 {
452 matrix.set_a(-matrix.a());
453 self.reset_matrix_impl(matrix)?;
454 }
455
456 if matrix.d() < 0f32 {
457 matrix.set_d(-matrix.d());
458 self.reset_matrix_impl(matrix)?;
459 }
460
461 let page_handle = match self.ownership() {
462 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
463 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
464 _ => None,
465 };
466
467 let bitmap_handle = match page_handle {
468 Some(page_handle) => self.bindings().FPDFImageObj_GetRenderedBitmap(
469 document.handle(),
470 page_handle,
471 self.object_handle(),
472 ),
473 None => self.bindings.FPDFImageObj_GetRenderedBitmap(
474 document.handle(),
475 std::ptr::null_mut::<fpdf_page_t__>(),
476 self.object_handle(),
477 ),
478 };
479
480 if bitmap_handle.is_null() {
481 self.reset_matrix_impl(original_matrix)?;
485 return Err(PdfiumError::PdfiumLibraryInternalError(
486 PdfiumInternalError::Unknown,
487 ));
488 }
489
490 let result = PdfBitmap::from_pdfium(bitmap_handle, self.bindings());
491
492 if width == result.width() && height == result.height() {
493 self.reset_matrix_impl(original_matrix)?;
497
498 Ok(result)
499 } else {
500 self.transform_impl(
505 width as PdfMatrixValue / result.width() as PdfMatrixValue,
506 0.0,
507 0.0,
508 height as PdfMatrixValue / result.height() as PdfMatrixValue,
509 0.0,
510 0.0,
511 )?;
512
513 let result = PdfBitmap::from_pdfium(
516 match page_handle {
517 Some(page_handle) => self.bindings().FPDFImageObj_GetRenderedBitmap(
518 document.handle(),
519 page_handle,
520 self.object_handle(),
521 ),
522 None => self.bindings.FPDFImageObj_GetRenderedBitmap(
523 document.handle(),
524 std::ptr::null_mut::<fpdf_page_t__>(),
525 self.object_handle(),
526 ),
527 },
528 self.bindings,
529 );
530
531 self.reset_matrix_impl(original_matrix)?;
534
535 Ok(result)
536 }
537 }
538
539 #[cfg(feature = "image_api")]
548 #[inline]
549 pub fn get_processed_image_with_size(
550 &self,
551 document: &PdfDocument,
552 width: Pixels,
553 height: Pixels,
554 ) -> Result<DynamicImage, PdfiumError> {
555 self.get_processed_bitmap_with_size(document, width, height)
556 .and_then(|bitmap| self.get_image_from_bitmap(&bitmap))
557 }
558
559 #[cfg(feature = "image_api")]
560 pub(crate) fn get_image_from_bitmap(
561 &self,
562 bitmap: &PdfBitmap,
563 ) -> Result<DynamicImage, PdfiumError> {
564 let handle = bitmap.handle();
565
566 let width = self.bindings.FPDFBitmap_GetWidth(handle);
567
568 let height = self.bindings.FPDFBitmap_GetHeight(handle);
569
570 let stride = self.bindings.FPDFBitmap_GetStride(handle);
571
572 let format =
573 PdfBitmapFormat::from_pdfium(self.bindings.FPDFBitmap_GetFormat(handle) as u32)?;
574
575 #[cfg(not(target_arch = "wasm32"))]
576 let buffer = self.bindings.FPDFBitmap_GetBuffer_as_slice(handle);
577
578 #[cfg(target_arch = "wasm32")]
579 let buffer_vec = self.bindings.FPDFBitmap_GetBuffer_as_vec(handle);
580 #[cfg(target_arch = "wasm32")]
581 let buffer = buffer_vec.as_slice();
582
583 match format {
584 #[allow(deprecated)]
585 PdfBitmapFormat::BGRA | PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx => {
586 RgbaImage::from_raw(width as u32, height as u32, bgra_to_rgba(buffer))
587 .map(DynamicImage::ImageRgba8)
588 }
589 PdfBitmapFormat::BGR => RgbaImage::from_raw(
590 width as u32,
591 height as u32,
592 aligned_bgr_to_rgba(buffer, width as usize, stride as usize),
593 )
594 .map(DynamicImage::ImageRgba8),
595 PdfBitmapFormat::Gray => GrayImage::from_raw(
596 width as u32,
597 height as u32,
598 aligned_grayscale_to_unaligned(buffer, width as usize, stride as usize),
599 )
600 .map(DynamicImage::ImageLuma8),
601 }
602 .ok_or(PdfiumError::ImageError)
603 }
604
605 pub fn get_raw_image_data(&self) -> Result<Vec<u8>, PdfiumError> {
610 let buffer_length = self.bindings().FPDFImageObj_GetImageDataRaw(
611 self.object_handle(),
612 std::ptr::null_mut(),
613 0,
614 );
615
616 if buffer_length == 0 {
617 return Ok(Vec::new());
618 }
619
620 let mut buffer = create_byte_buffer(buffer_length as usize);
621
622 let result = self.bindings().FPDFImageObj_GetImageDataRaw(
623 self.object_handle(),
624 buffer.as_mut_ptr() as *mut c_void,
625 buffer_length,
626 );
627
628 assert_eq!(result, buffer_length);
629
630 Ok(buffer)
631 }
632
633 pub(crate) fn get_current_width_and_height_from_metadata(
635 &self,
636 ) -> Result<(Pixels, Pixels), PdfiumError> {
637 let width = self.get_raw_metadata().and_then(|metadata| {
638 metadata
639 .width
640 .try_into()
641 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
642 })?;
643
644 let height = self.get_raw_metadata().and_then(|metadata| {
645 metadata
646 .height
647 .try_into()
648 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
649 })?;
650
651 Ok((width, height))
652 }
653
654 #[inline]
658 pub fn width(&self) -> Result<Pixels, PdfiumError> {
659 self.get_current_width_and_height_from_metadata()
660 .map(|(width, _height)| width)
661 }
662
663 #[inline]
667 pub fn height(&self) -> Result<Pixels, PdfiumError> {
668 self.get_current_width_and_height_from_metadata()
669 .map(|(_width, height)| height)
670 }
671
672 #[cfg(feature = "image_api")]
676 pub fn set_image(&mut self, image: &DynamicImage) -> Result<(), PdfiumError> {
677 let width: Pixels = image
678 .width()
679 .try_into()
680 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
681
682 let height: Pixels = image
683 .height()
684 .try_into()
685 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
686
687 let bitmap = PdfBitmap::empty(width, height, PdfBitmapFormat::BGRA, self.bindings)?;
688
689 let buffer = if let Some(image) = image.as_rgba8() {
690 rgba_to_bgra(image.as_bytes())
693 } else {
694 let image = image.to_rgba8();
697
698 rgba_to_bgra(image.as_bytes())
699 };
700
701 if !self
702 .bindings
703 .FPDFBitmap_SetBuffer(bitmap.handle(), buffer.as_slice())
704 {
705 return Err(PdfiumError::PdfiumLibraryInternalError(
706 PdfiumInternalError::Unknown,
707 ));
708 }
709
710 self.set_bitmap(&bitmap)
711 }
712
713 pub fn set_bitmap(&mut self, bitmap: &PdfBitmap) -> Result<(), PdfiumError> {
715 if self
716 .bindings
717 .is_true(self.bindings().FPDFImageObj_SetBitmap(
718 std::ptr::null_mut::<FPDF_PAGE>(),
719 0,
720 self.object_handle(),
721 bitmap.handle(),
722 ))
723 {
724 Ok(())
725 } else {
726 Err(PdfiumError::PdfiumLibraryInternalError(
727 PdfiumInternalError::Unknown,
728 ))
729 }
730 }
731
732 pub(crate) fn get_raw_metadata(&self) -> Result<FPDF_IMAGEOBJ_METADATA, PdfiumError> {
734 let mut metadata = FPDF_IMAGEOBJ_METADATA {
735 width: 0,
736 height: 0,
737 horizontal_dpi: 0.0,
738 vertical_dpi: 0.0,
739 bits_per_pixel: 0,
740 colorspace: 0,
741 marked_content_id: 0,
742 };
743
744 let page_handle = match self.ownership() {
745 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
746 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
747 _ => None,
748 };
749
750 let result = self.bindings().FPDFImageObj_GetImageMetadata(
751 self.object_handle(),
752 match page_handle {
753 Some(page_handle) => page_handle,
754 None => std::ptr::null_mut::<fpdf_page_t__>(),
755 },
756 &mut metadata,
757 );
758
759 if self.bindings().is_true(result) {
760 Ok(metadata)
761 } else {
762 Err(PdfiumError::PdfiumLibraryInternalError(
763 PdfiumInternalError::Unknown,
764 ))
765 }
766 }
767
768 #[inline]
772 pub fn horizontal_dpi(&self) -> Result<f32, PdfiumError> {
773 self.get_raw_metadata()
774 .map(|metadata| metadata.horizontal_dpi)
775 }
776
777 #[inline]
781 pub fn vertical_dpi(&self) -> Result<f32, PdfiumError> {
782 self.get_raw_metadata()
783 .map(|metadata| metadata.vertical_dpi)
784 }
785
786 #[inline]
790 pub fn bits_per_pixel(&self) -> Result<u8, PdfiumError> {
791 self.get_raw_metadata()
792 .map(|metadata| metadata.bits_per_pixel as u8)
793 }
794
795 #[inline]
799 pub fn color_space(&self) -> Result<PdfColorSpace, PdfiumError> {
800 self.get_raw_metadata()
801 .and_then(|metadata| PdfColorSpace::from_pdfium(metadata.colorspace as u32))
802 }
803
804 #[inline]
806 pub fn filters(&self) -> PdfPageImageObjectFilters<'_> {
807 PdfPageImageObjectFilters::new(self)
808 }
809
810 create_transform_setters!(
811 &mut Self,
812 Result<(), PdfiumError>,
813 "this [PdfPageImageObject]",
814 "this [PdfPageImageObject].",
815 "this [PdfPageImageObject],"
816 );
817
818 create_transform_getters!(
822 "this [PdfPageImageObject]",
823 "this [PdfPageImageObject].",
824 "this [PdfPageImageObject],"
825 );
826
827 }
830
831impl<'a> PdfPageObjectPrivate<'a> for PdfPageImageObject<'a> {
832 #[inline]
833 fn object_handle(&self) -> FPDF_PAGEOBJECT {
834 self.object_handle
835 }
836
837 #[inline]
838 fn ownership(&self) -> &PdfPageObjectOwnership {
839 &self.ownership
840 }
841
842 #[inline]
843 fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
844 self.ownership = ownership;
845 }
846
847 #[inline]
848 fn bindings(&self) -> &dyn PdfiumLibraryBindings {
849 self.bindings
850 }
851
852 #[inline]
853 fn is_copyable_impl(&self) -> bool {
854 self.filters().is_empty()
857 }
858
859 #[inline]
860 fn try_copy_impl<'b>(
861 &self,
862 document: FPDF_DOCUMENT,
863 bindings: &'b dyn PdfiumLibraryBindings,
864 ) -> Result<PdfPageObject<'b>, PdfiumError> {
865 if !self.filters().is_empty() {
866 return Err(PdfiumError::ImageObjectFiltersNotCopyable);
869 }
870
871 let mut copy = PdfPageImageObject::new_from_handle(document, bindings)?;
872
873 copy.set_bitmap(&self.get_raw_bitmap()?)?;
874 copy.reset_matrix(self.matrix()?)?;
875
876 Ok(PdfPageObject::Image(copy))
877 }
878}
879
880pub type PdfPageImageObjectFilterIndex = usize;
883
884pub struct PdfPageImageObjectFilters<'a> {
886 object: &'a PdfPageImageObject<'a>,
887}
888
889impl<'a> PdfPageImageObjectFilters<'a> {
890 #[inline]
891 pub(crate) fn new(object: &'a PdfPageImageObject<'a>) -> Self {
892 PdfPageImageObjectFilters { object }
893 }
894
895 pub fn len(&self) -> usize {
897 self.object
898 .bindings()
899 .FPDFImageObj_GetImageFilterCount(self.object.object_handle()) as usize
900 }
901
902 #[inline]
904 pub fn is_empty(&self) -> bool {
905 self.len() == 0
906 }
907
908 #[inline]
910 pub fn as_range(&self) -> Range<PdfPageImageObjectFilterIndex> {
911 0..self.len()
912 }
913
914 #[inline]
916 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageImageObjectFilterIndex> {
917 if self.is_empty() {
918 0..=0
919 } else {
920 0..=(self.len() - 1)
921 }
922 }
923
924 pub fn get(
926 &self,
927 index: PdfPageImageObjectFilterIndex,
928 ) -> Result<PdfPageImageObjectFilter, PdfiumError> {
929 if index >= self.len() {
930 return Err(PdfiumError::ImageObjectFilterIndexOutOfBounds);
931 }
932
933 let buffer_length = self.object.bindings().FPDFImageObj_GetImageFilter(
943 self.object.object_handle(),
944 index as c_int,
945 std::ptr::null_mut(),
946 0,
947 );
948
949 if buffer_length == 0 {
950 return Err(PdfiumError::ImageObjectFilterIndexInBoundsButFilterUndefined);
953 }
954
955 let mut buffer = create_byte_buffer(buffer_length as usize);
956
957 let result = self.object.bindings().FPDFImageObj_GetImageFilter(
958 self.object.object_handle(),
959 index as c_int,
960 buffer.as_mut_ptr() as *mut c_void,
961 buffer_length,
962 );
963
964 assert_eq!(result, buffer_length);
965
966 Ok(PdfPageImageObjectFilter::new(
967 String::from_utf8(buffer)
968 .map(|str| str.trim_end_matches(char::from(0)).to_owned())
971 .unwrap_or_default(),
972 ))
973 }
974
975 #[inline]
978 pub fn iter(&self) -> PdfPageImageObjectFiltersIterator<'_> {
979 PdfPageImageObjectFiltersIterator::new(self)
980 }
981}
982
983pub struct PdfPageImageObjectFilter {
985 name: String,
986}
987
988impl PdfPageImageObjectFilter {
989 #[inline]
990 pub(crate) fn new(name: String) -> Self {
991 PdfPageImageObjectFilter { name }
992 }
993
994 pub fn name(&self) -> &str {
996 self.name.as_str()
997 }
998}
999
1000pub struct PdfPageImageObjectFiltersIterator<'a> {
1003 filters: &'a PdfPageImageObjectFilters<'a>,
1004 next_index: PdfPageImageObjectFilterIndex,
1005}
1006
1007impl<'a> PdfPageImageObjectFiltersIterator<'a> {
1008 #[inline]
1009 pub(crate) fn new(filters: &'a PdfPageImageObjectFilters<'a>) -> Self {
1010 PdfPageImageObjectFiltersIterator {
1011 filters,
1012 next_index: 0,
1013 }
1014 }
1015}
1016
1017impl<'a> Iterator for PdfPageImageObjectFiltersIterator<'a> {
1018 type Item = PdfPageImageObjectFilter;
1019
1020 fn next(&mut self) -> Option<Self::Item> {
1021 let next = self.filters.get(self.next_index);
1022
1023 self.next_index += 1;
1024
1025 next.ok()
1026 }
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031 use super::*;
1032 use crate::prelude::*;
1033 use crate::utils::test::test_bind_to_pdfium;
1034
1035 #[test]
1036 fn test_page_image_object_retains_format() -> Result<(), PdfiumError> {
1037 let pdfium = test_bind_to_pdfium();
1041
1042 let image = pdfium
1043 .load_pdf_from_file("./test/path-test.pdf", None)?
1044 .pages()
1045 .get(0)?
1046 .render_with_config(&PdfRenderConfig::new().set_target_width(1000))?
1047 .as_image();
1048
1049 let mut document = pdfium.create_new_pdf()?;
1050
1051 let mut page = document
1052 .pages_mut()
1053 .create_page_at_end(PdfPagePaperSize::a4())?;
1054
1055 let object = page.objects_mut().create_image_object(
1056 PdfPoints::new(100.0),
1057 PdfPoints::new(100.0),
1058 &image,
1059 Some(PdfPoints::new(image.width() as f32)),
1060 Some(PdfPoints::new(image.height() as f32)),
1061 )?;
1062
1063 let raw_image = object.as_image_object().unwrap().get_raw_image()?;
1071
1072 let processed_image = object
1077 .as_image_object()
1078 .unwrap()
1079 .get_processed_image(&document)?;
1080
1081 assert!(compare_equality_of_byte_arrays(
1086 image.as_bytes(),
1087 raw_image.into_rgba8().as_raw().as_slice()
1088 ));
1089
1090 assert!(compare_equality_of_byte_arrays(
1091 image.as_bytes(),
1092 processed_image.into_rgba8().as_raw().as_slice()
1093 ));
1094
1095 Ok(())
1096 }
1097
1098 fn compare_equality_of_byte_arrays(a: &[u8], b: &[u8]) -> bool {
1099 if a.len() != b.len() {
1100 return false;
1101 }
1102
1103 for index in 0..a.len() {
1104 if a[index] != b[index] {
1105 return false;
1106 }
1107 }
1108
1109 true
1110 }
1111
1112 #[test]
1113 fn test_image_scaling_keeps_aspect_ratio() -> Result<(), PdfiumError> {
1114 let pdfium = test_bind_to_pdfium();
1115
1116 let mut document = pdfium.create_new_pdf()?;
1117
1118 let mut page = document
1119 .pages_mut()
1120 .create_page_at_end(PdfPagePaperSize::a4())?;
1121
1122 let image = DynamicImage::new_rgb8(100, 200);
1123
1124 let object = page.objects_mut().create_image_object(
1125 PdfPoints::new(0.0),
1126 PdfPoints::new(0.0),
1127 &image,
1128 Some(PdfPoints::new(image.width() as f32)),
1129 Some(PdfPoints::new(image.height() as f32)),
1130 )?;
1131
1132 let image_object = object.as_image_object().unwrap();
1133
1134 assert_eq!(
1135 image_object
1136 .get_processed_bitmap_with_width(&document, 50)?
1137 .height(),
1138 100
1139 );
1140 assert_eq!(
1141 image_object
1142 .get_processed_image_with_width(&document, 50)?
1143 .height(),
1144 100
1145 );
1146 assert_eq!(
1147 image_object
1148 .get_processed_bitmap_with_height(&document, 50)?
1149 .width(),
1150 25
1151 );
1152 assert_eq!(
1153 image_object
1154 .get_processed_image_with_height(&document, 50)?
1155 .width(),
1156 25
1157 );
1158
1159 Ok(())
1160 }
1161}
1162
1163impl<'a> Drop for PdfPageImageObject<'a> {
1164 fn drop(&mut self) {
1166 self.drop_impl();
1167 }
1168}