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::PdfPageObjectOwnership;
14use crate::pdf::document::PdfDocument;
15use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
16use crate::pdf::points::PdfPoints;
17use crate::pdfium::PdfiumLibraryBindingsAccessor;
18use crate::utils::mem::create_byte_buffer;
19use crate::{create_transform_getters, create_transform_setters};
20use std::convert::TryInto;
21use std::marker::PhantomData;
22use std::ops::{Range, RangeInclusive};
23use std::os::raw::{c_int, c_void};
24
25#[cfg(not(target_arch = "wasm32"))]
26use {
27 crate::utils::files::get_pdfium_file_accessor_from_reader,
28 std::fs::File,
29 std::io::{Read, Seek},
30 std::path::Path,
31};
32
33#[cfg(feature = "image_api")]
34use {
35 crate::pdf::bitmap::PdfBitmapFormat,
36 crate::utils::pixels::{
37 aligned_bgr_to_rgba, aligned_grayscale_to_unaligned, bgra_to_rgba, rgba_to_bgra,
38 },
39};
40
41#[cfg(feature = "image_025")]
42use image_025::{DynamicImage, EncodableLayout, GrayImage, RgbaImage};
43
44#[cfg(feature = "image_024")]
45use image_024::{DynamicImage, EncodableLayout, GrayImage, RgbaImage};
46
47#[cfg(feature = "image_023")]
48use image_023::{DynamicImage, EncodableLayout, GenericImageView, GrayImage, RgbaImage};
49
50#[cfg(doc)]
51use {
52 crate::pdf::document::page::object::PdfPageObject,
53 crate::pdf::document::page::object::PdfPageObjectType,
54 crate::pdf::document::page::objects::common::PdfPageObjectsCommon,
55 crate::pdf::document::page::PdfPage,
56};
57
58pub struct PdfPageImageObject<'a> {
76 object_handle: FPDF_PAGEOBJECT,
77 ownership: PdfPageObjectOwnership,
78 lifetime: PhantomData<&'a FPDF_PAGEOBJECT>,
79}
80
81impl<'a> PdfPageImageObject<'a> {
82 #[inline]
83 pub(crate) fn from_pdfium(
84 object_handle: FPDF_PAGEOBJECT,
85 ownership: PdfPageObjectOwnership,
86 ) -> Self {
87 PdfPageImageObject {
88 object_handle,
89 ownership,
90 lifetime: PhantomData,
91 }
92 }
93
94 #[cfg(feature = "image_api")]
106 #[inline]
107 pub fn new(document: &PdfDocument<'a>, image: &DynamicImage) -> Result<Self, PdfiumError> {
108 let mut result = Self::new_from_handle(document.handle(), document.bindings());
109
110 if let Ok(result) = result.as_mut() {
111 result.set_image(image)?;
112 }
113
114 result
115 }
116
117 #[cfg(not(feature = "image_api"))]
128 pub fn new(document: &PdfDocument<'a>) -> Result<Self, PdfiumError> {
129 Self::new_from_handle(document.handle(), document.bindings())
130 }
131
132 #[cfg(not(target_arch = "wasm32"))]
144 pub fn new_from_jpeg_file(
145 document: &PdfDocument<'a>,
146 path: &(impl AsRef<Path> + ?Sized),
147 ) -> Result<Self, PdfiumError> {
148 Self::new_from_jpeg_reader(document, File::open(path).map_err(PdfiumError::IoError)?)
149 }
150
151 #[cfg(not(target_arch = "wasm32"))]
167 pub fn new_from_jpeg_reader<R: Read + Seek>(
168 document: &PdfDocument<'a>,
169 reader: R,
170 ) -> Result<Self, PdfiumError> {
171 let object = Self::new_from_handle(document.handle(), document.bindings())?;
172
173 let mut reader = get_pdfium_file_accessor_from_reader(reader);
174
175 let result = unsafe {
176 document.bindings().FPDFImageObj_LoadJpegFileInline(
177 std::ptr::null_mut(),
178 0,
179 object.object_handle(),
180 reader.as_fpdf_file_access_mut_ptr(),
181 )
182 };
183
184 if object.bindings().is_true(result) {
185 Ok(object)
186 } else {
187 Err(PdfiumError::PdfiumLibraryInternalError(
188 PdfiumInternalError::Unknown,
189 ))
190 }
191 }
192
193 pub(crate) fn new_from_handle(
196 document: FPDF_DOCUMENT,
197 bindings: &'a dyn PdfiumLibraryBindings,
198 ) -> Result<Self, PdfiumError> {
199 let handle = unsafe { bindings.FPDFPageObj_NewImageObj(document) };
200
201 if handle.is_null() {
202 Err(PdfiumError::PdfiumLibraryInternalError(
203 PdfiumInternalError::Unknown,
204 ))
205 } else {
206 Ok(PdfPageImageObject {
207 object_handle: handle,
208 ownership: PdfPageObjectOwnership::unowned(),
209 lifetime: PhantomData,
210 })
211 }
212 }
213
214 #[cfg(feature = "image_api")]
221 pub fn new_with_width(
222 document: &PdfDocument<'a>,
223 image: &DynamicImage,
224 width: PdfPoints,
225 ) -> Result<Self, PdfiumError> {
226 let aspect_ratio = image.height() as f32 / image.width() as f32;
227
228 let height = width * aspect_ratio;
229
230 Self::new_with_size(document, image, width, height)
231 }
232
233 #[cfg(feature = "image_api")]
240 pub fn new_with_height(
241 document: &'a PdfDocument<'a>,
242 image: &DynamicImage,
243 height: PdfPoints,
244 ) -> Result<Self, PdfiumError> {
245 let aspect_ratio = image.height() as f32 / image.width() as f32;
246
247 let width = height / aspect_ratio;
248
249 Self::new_with_size(document, image, width, height)
250 }
251
252 #[cfg(feature = "image_api")]
258 #[inline]
259 pub fn new_with_size(
260 document: &PdfDocument<'a>,
261 image: &DynamicImage,
262 width: PdfPoints,
263 height: PdfPoints,
264 ) -> Result<Self, PdfiumError> {
265 let mut result = Self::new(document, image)?;
266
267 result.scale(width.value, height.value)?;
268
269 Ok(result)
270 }
271
272 pub fn get_raw_bitmap(&self) -> Result<PdfBitmap<'_>, PdfiumError> {
276 Ok(PdfBitmap::from_pdfium(unsafe {
277 self.bindings().FPDFImageObj_GetBitmap(self.object_handle())
278 }))
279 }
280
281 #[cfg(feature = "image_api")]
287 #[inline]
288 pub fn get_raw_image(&self) -> Result<DynamicImage, PdfiumError> {
289 self.get_image_from_bitmap(&self.get_raw_bitmap()?)
290 }
291
292 #[inline]
296 pub fn get_processed_bitmap(
297 &self,
298 document: &PdfDocument,
299 ) -> Result<PdfBitmap<'_>, PdfiumError> {
300 let (width, height) = self.get_current_width_and_height_from_metadata()?;
301
302 self.get_processed_bitmap_with_size(document, width, height)
303 }
304
305 #[cfg(feature = "image_api")]
311 #[inline]
312 pub fn get_processed_image(&self, document: &PdfDocument) -> Result<DynamicImage, PdfiumError> {
313 let (width, height) = self.get_current_width_and_height_from_metadata()?;
314
315 self.get_processed_image_with_size(document, width, height)
316 }
317
318 #[inline]
324 pub fn get_processed_bitmap_with_width(
325 &self,
326 document: &PdfDocument,
327 width: Pixels,
328 ) -> Result<PdfBitmap<'_>, PdfiumError> {
329 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
330
331 let aspect_ratio = current_width as f32 / current_height as f32;
332
333 self.get_processed_bitmap_with_size(
334 document,
335 width,
336 ((width as f32 / aspect_ratio) as u32)
337 .try_into()
338 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
339 )
340 }
341
342 #[cfg(feature = "image_api")]
350 #[inline]
351 pub fn get_processed_image_with_width(
352 &self,
353 document: &PdfDocument,
354 width: Pixels,
355 ) -> Result<DynamicImage, PdfiumError> {
356 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
357
358 let aspect_ratio = current_width as f32 / current_height as f32;
359
360 self.get_processed_image_with_size(
361 document,
362 width,
363 ((width as f32 / aspect_ratio) as u32)
364 .try_into()
365 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
366 )
367 }
368
369 #[inline]
375 pub fn get_processed_bitmap_with_height(
376 &self,
377 document: &PdfDocument,
378 height: Pixels,
379 ) -> Result<PdfBitmap<'_>, PdfiumError> {
380 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
381
382 let aspect_ratio = current_width as f32 / current_height as f32;
383
384 self.get_processed_bitmap_with_size(
385 document,
386 ((height as f32 * aspect_ratio) as u32)
387 .try_into()
388 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
389 height,
390 )
391 }
392
393 #[cfg(feature = "image_api")]
401 #[inline]
402 pub fn get_processed_image_with_height(
403 &self,
404 document: &PdfDocument,
405 height: Pixels,
406 ) -> Result<DynamicImage, PdfiumError> {
407 let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
408
409 let aspect_ratio = current_width as f32 / current_height as f32;
410
411 self.get_processed_image_with_size(
412 document,
413 ((height as f32 * aspect_ratio) as u32)
414 .try_into()
415 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
416 height,
417 )
418 }
419
420 pub fn get_processed_bitmap_with_size(
427 &self,
428 document: &PdfDocument,
429 width: Pixels,
430 height: Pixels,
431 ) -> Result<PdfBitmap<'_>, PdfiumError> {
432 let mut matrix = self.matrix()?;
449
450 let original_matrix = matrix; if matrix.a() < 0f32 {
455 matrix.set_a(-matrix.a());
456 self.reset_matrix_impl(matrix)?;
457 }
458
459 if matrix.d() < 0f32 {
460 matrix.set_d(-matrix.d());
461 self.reset_matrix_impl(matrix)?;
462 }
463
464 let page_handle = match self.ownership() {
465 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
466 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
467 _ => None,
468 };
469
470 let bitmap_handle = match page_handle {
471 Some(page_handle) => unsafe {
472 self.bindings().FPDFImageObj_GetRenderedBitmap(
473 document.handle(),
474 page_handle,
475 self.object_handle(),
476 )
477 },
478 None => unsafe {
479 self.bindings().FPDFImageObj_GetRenderedBitmap(
480 document.handle(),
481 std::ptr::null_mut::<fpdf_page_t__>(),
482 self.object_handle(),
483 )
484 },
485 };
486
487 if bitmap_handle.is_null() {
488 self.reset_matrix_impl(original_matrix)?;
492 return Err(PdfiumError::PdfiumLibraryInternalError(
493 PdfiumInternalError::Unknown,
494 ));
495 }
496
497 let result = PdfBitmap::from_pdfium(bitmap_handle);
498
499 if width == result.width() && height == result.height() {
500 self.reset_matrix_impl(original_matrix)?;
504
505 Ok(result)
506 } else {
507 self.transform_impl(
512 width as PdfMatrixValue / result.width() as PdfMatrixValue,
513 0.0,
514 0.0,
515 height as PdfMatrixValue / result.height() as PdfMatrixValue,
516 0.0,
517 0.0,
518 )?;
519
520 let result = PdfBitmap::from_pdfium(match page_handle {
523 Some(page_handle) => unsafe {
524 self.bindings().FPDFImageObj_GetRenderedBitmap(
525 document.handle(),
526 page_handle,
527 self.object_handle(),
528 )
529 },
530 None => unsafe {
531 self.bindings().FPDFImageObj_GetRenderedBitmap(
532 document.handle(),
533 std::ptr::null_mut::<fpdf_page_t__>(),
534 self.object_handle(),
535 )
536 },
537 });
538
539 self.reset_matrix_impl(original_matrix)?;
542
543 Ok(result)
544 }
545 }
546
547 #[cfg(feature = "image_api")]
556 #[inline]
557 pub fn get_processed_image_with_size(
558 &self,
559 document: &PdfDocument,
560 width: Pixels,
561 height: Pixels,
562 ) -> Result<DynamicImage, PdfiumError> {
563 self.get_processed_bitmap_with_size(document, width, height)
564 .and_then(|bitmap| self.get_image_from_bitmap(&bitmap))
565 }
566
567 #[cfg(feature = "image_api")]
568 pub(crate) fn get_image_from_bitmap(
569 &self,
570 bitmap: &PdfBitmap,
571 ) -> Result<DynamicImage, PdfiumError> {
572 let handle = bitmap.handle();
573 let width = unsafe { self.bindings().FPDFBitmap_GetWidth(handle) };
574 let height = unsafe { self.bindings().FPDFBitmap_GetHeight(handle) };
575 let stride = unsafe { self.bindings().FPDFBitmap_GetStride(handle) };
576 let format =
577 PdfBitmapFormat::from_pdfium(
578 unsafe { self.bindings().FPDFBitmap_GetFormat(handle) } as u32
579 )?;
580
581 #[cfg(not(target_arch = "wasm32"))]
582 let buffer = unsafe { self.bindings().FPDFBitmap_GetBuffer_as_slice(handle) };
583
584 #[cfg(target_arch = "wasm32")]
585 let buffer_vec = unsafe { self.bindings().FPDFBitmap_GetBuffer_as_vec(handle) };
586 #[cfg(target_arch = "wasm32")]
587 let buffer = buffer_vec.as_slice();
588
589 match format {
590 #[allow(deprecated)]
591 PdfBitmapFormat::BGRA | PdfBitmapFormat::BGRx => {
592 RgbaImage::from_raw(width as u32, height as u32, bgra_to_rgba(buffer))
593 .map(DynamicImage::ImageRgba8)
594 }
595 PdfBitmapFormat::BGR => RgbaImage::from_raw(
596 width as u32,
597 height as u32,
598 aligned_bgr_to_rgba(buffer, width as usize, stride as usize),
599 )
600 .map(DynamicImage::ImageRgba8),
601 PdfBitmapFormat::Gray => GrayImage::from_raw(
602 width as u32,
603 height as u32,
604 aligned_grayscale_to_unaligned(buffer, width as usize, stride as usize),
605 )
606 .map(DynamicImage::ImageLuma8),
607 }
608 .ok_or(PdfiumError::ImageError)
609 }
610
611 pub fn get_raw_image_data(&self) -> Result<Vec<u8>, PdfiumError> {
616 let buffer_length = unsafe {
617 self.bindings().FPDFImageObj_GetImageDataRaw(
618 self.object_handle(),
619 std::ptr::null_mut(),
620 0,
621 )
622 };
623
624 if buffer_length == 0 {
625 return Ok(Vec::new());
626 }
627
628 let mut buffer = create_byte_buffer(buffer_length as usize);
629
630 let result = unsafe {
631 self.bindings().FPDFImageObj_GetImageDataRaw(
632 self.object_handle(),
633 buffer.as_mut_ptr() as *mut c_void,
634 buffer_length,
635 )
636 };
637
638 assert_eq!(result, buffer_length);
639
640 Ok(buffer)
641 }
642
643 pub(crate) fn get_current_width_and_height_from_metadata(
645 &self,
646 ) -> Result<(Pixels, Pixels), PdfiumError> {
647 let width = self.get_raw_metadata().and_then(|metadata| {
648 metadata
649 .width
650 .try_into()
651 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
652 })?;
653
654 let height = self.get_raw_metadata().and_then(|metadata| {
655 metadata
656 .height
657 .try_into()
658 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)
659 })?;
660
661 Ok((width, height))
662 }
663
664 #[inline]
668 pub fn width(&self) -> Result<Pixels, PdfiumError> {
669 self.get_current_width_and_height_from_metadata()
670 .map(|(width, _height)| width)
671 }
672
673 #[inline]
677 pub fn height(&self) -> Result<Pixels, PdfiumError> {
678 self.get_current_width_and_height_from_metadata()
679 .map(|(_width, height)| height)
680 }
681
682 #[cfg(feature = "image_api")]
686 pub fn set_image(&mut self, image: &DynamicImage) -> Result<(), PdfiumError> {
687 let width: Pixels = image
688 .width()
689 .try_into()
690 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
691
692 let height: Pixels = image
693 .height()
694 .try_into()
695 .map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
696
697 let bitmap = PdfBitmap::empty(width, height, PdfBitmapFormat::BGRA, self.bindings())?;
698
699 let buffer = if let Some(image) = image.as_rgba8() {
700 rgba_to_bgra(image.as_bytes())
703 } else {
704 let image = image.to_rgba8();
707
708 rgba_to_bgra(image.as_bytes())
709 };
710
711 if !(unsafe {
712 self.bindings()
713 .FPDFBitmap_SetBuffer(bitmap.handle(), buffer.as_slice())
714 }) {
715 return Err(PdfiumError::PdfiumLibraryInternalError(
716 PdfiumInternalError::Unknown,
717 ));
718 }
719
720 self.set_bitmap(&bitmap)
721 }
722
723 pub fn set_bitmap(&mut self, bitmap: &PdfBitmap) -> Result<(), PdfiumError> {
725 if self.bindings().is_true(unsafe {
726 self.bindings().FPDFImageObj_SetBitmap(
727 std::ptr::null_mut::<FPDF_PAGE>(),
728 0,
729 self.object_handle(),
730 bitmap.handle(),
731 )
732 }) {
733 Ok(())
734 } else {
735 Err(PdfiumError::PdfiumLibraryInternalError(
736 PdfiumInternalError::Unknown,
737 ))
738 }
739 }
740
741 pub(crate) fn get_raw_metadata(&self) -> Result<FPDF_IMAGEOBJ_METADATA, PdfiumError> {
743 let mut metadata = FPDF_IMAGEOBJ_METADATA {
744 width: 0,
745 height: 0,
746 horizontal_dpi: 0.0,
747 vertical_dpi: 0.0,
748 bits_per_pixel: 0,
749 colorspace: 0,
750 marked_content_id: 0,
751 };
752
753 let page_handle = match self.ownership() {
754 PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
755 PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
756 _ => None,
757 };
758
759 let result = unsafe {
760 self.bindings().FPDFImageObj_GetImageMetadata(
761 self.object_handle(),
762 match page_handle {
763 Some(page_handle) => page_handle,
764 None => std::ptr::null_mut::<fpdf_page_t__>(),
765 },
766 &mut metadata,
767 )
768 };
769
770 if self.bindings().is_true(result) {
771 Ok(metadata)
772 } else {
773 Err(PdfiumError::PdfiumLibraryInternalError(
774 PdfiumInternalError::Unknown,
775 ))
776 }
777 }
778
779 #[inline]
783 pub fn horizontal_dpi(&self) -> Result<f32, PdfiumError> {
784 self.get_raw_metadata()
785 .map(|metadata| metadata.horizontal_dpi)
786 }
787
788 #[inline]
792 pub fn vertical_dpi(&self) -> Result<f32, PdfiumError> {
793 self.get_raw_metadata()
794 .map(|metadata| metadata.vertical_dpi)
795 }
796
797 #[inline]
801 pub fn bits_per_pixel(&self) -> Result<u8, PdfiumError> {
802 self.get_raw_metadata()
803 .map(|metadata| metadata.bits_per_pixel as u8)
804 }
805
806 #[inline]
810 pub fn color_space(&self) -> Result<PdfColorSpace, PdfiumError> {
811 self.get_raw_metadata()
812 .and_then(|metadata| PdfColorSpace::from_pdfium(metadata.colorspace as u32))
813 }
814
815 #[inline]
817 pub fn filters(&self) -> PdfPageImageObjectFilters<'_> {
818 PdfPageImageObjectFilters::new(self)
819 }
820
821 create_transform_setters!(
822 &mut Self,
823 Result<(), PdfiumError>,
824 "this [PdfPageImageObject]",
825 "this [PdfPageImageObject].",
826 "this [PdfPageImageObject],"
827 );
828
829 create_transform_getters!(
833 "this [PdfPageImageObject]",
834 "this [PdfPageImageObject].",
835 "this [PdfPageImageObject],"
836 );
837
838 }
841
842impl<'a> PdfPageObjectPrivate<'a> for PdfPageImageObject<'a> {
843 #[inline]
844 fn object_handle(&self) -> FPDF_PAGEOBJECT {
845 self.object_handle
846 }
847
848 #[inline]
849 fn ownership(&self) -> &PdfPageObjectOwnership {
850 &self.ownership
851 }
852
853 #[inline]
854 fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
855 self.ownership = ownership;
856 }
857}
858
859impl<'a> Drop for PdfPageImageObject<'a> {
860 fn drop(&mut self) {
862 self.drop_impl();
863 }
864}
865
866impl<'a> PdfiumLibraryBindingsAccessor<'a> for PdfPageImageObject<'a> {}
867
868#[cfg(feature = "thread_safe")]
869unsafe impl<'a> Send for PdfPageImageObject<'a> {}
870
871#[cfg(feature = "thread_safe")]
872unsafe impl<'a> Sync for PdfPageImageObject<'a> {}
873
874pub type PdfPageImageObjectFilterIndex = usize;
877
878pub struct PdfPageImageObjectFilters<'a> {
880 object: &'a PdfPageImageObject<'a>,
881}
882
883impl<'a> PdfPageImageObjectFilters<'a> {
884 #[inline]
885 pub(crate) fn new(object: &'a PdfPageImageObject<'a>) -> Self {
886 PdfPageImageObjectFilters { object }
887 }
888
889 pub fn len(&self) -> usize {
891 (unsafe {
892 self.object
893 .bindings()
894 .FPDFImageObj_GetImageFilterCount(self.object.object_handle())
895 }) as usize
896 }
897
898 #[inline]
900 pub fn is_empty(&self) -> bool {
901 self.len() == 0
902 }
903
904 #[inline]
906 pub fn as_range(&self) -> Range<PdfPageImageObjectFilterIndex> {
907 0..self.len()
908 }
909
910 #[inline]
912 pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageImageObjectFilterIndex> {
913 if self.is_empty() {
914 0..=0
915 } else {
916 0..=(self.len() - 1)
917 }
918 }
919
920 pub fn get(
922 &self,
923 index: PdfPageImageObjectFilterIndex,
924 ) -> Result<PdfPageImageObjectFilter, PdfiumError> {
925 if index >= self.len() {
926 return Err(PdfiumError::ImageObjectFilterIndexOutOfBounds);
927 }
928
929 let buffer_length = unsafe {
939 self.object.bindings().FPDFImageObj_GetImageFilter(
940 self.object.object_handle(),
941 index as c_int,
942 std::ptr::null_mut(),
943 0,
944 )
945 };
946
947 if buffer_length == 0 {
948 return Err(PdfiumError::ImageObjectFilterIndexInBoundsButFilterUndefined);
951 }
952
953 let mut buffer = create_byte_buffer(buffer_length as usize);
954
955 let result = unsafe {
956 self.object.bindings().FPDFImageObj_GetImageFilter(
957 self.object.object_handle(),
958 index as c_int,
959 buffer.as_mut_ptr() as *mut c_void,
960 buffer_length,
961 )
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}