use crate::bindgen::{FPDF_DOCUMENT, FPDF_IMAGEOBJ_METADATA, FPDF_PAGE, FPDF_PAGEOBJECT, fpdf_page_t__};
use crate::bindings::PdfiumLibraryBindings;
use crate::error::{PdfiumError, PdfiumInternalError};
use crate::pdf::bitmap::PdfBitmap;
use crate::pdf::bitmap::Pixels;
use crate::pdf::color_space::PdfColorSpace;
use crate::pdf::document::PdfDocument;
use crate::pdf::document::page::object::private::internal::PdfPageObjectPrivate;
use crate::pdf::document::page::object::{PdfPageObject, PdfPageObjectOwnership};
use crate::pdf::matrix::{PdfMatrix, PdfMatrixValue};
use crate::pdf::points::PdfPoints;
use crate::utils::mem::create_byte_buffer;
use crate::{create_transform_getters, create_transform_setters};
use std::convert::TryInto;
use std::ops::{Range, RangeInclusive};
use std::os::raw::{c_int, c_void};
#[cfg(not(target_arch = "wasm32"))]
use {
crate::utils::files::get_pdfium_file_accessor_from_reader,
std::fs::File,
std::io::{Read, Seek},
std::path::Path,
};
#[cfg(feature = "image_025")]
use {
crate::pdf::bitmap::PdfBitmapFormat,
crate::utils::pixels::{aligned_bgr_to_rgba, aligned_grayscale_to_unaligned, bgra_to_rgba, rgba_to_bgra},
image_025::{DynamicImage, EncodableLayout, GrayImage, RgbaImage},
};
#[cfg(doc)]
use {
crate::pdf::document::page::PdfPage, crate::pdf::document::page::object::PdfPageObjectType,
crate::pdf::document::page::objects::common::PdfPageObjectsCommon,
};
pub struct PdfPageImageObject<'a> {
object_handle: FPDF_PAGEOBJECT,
ownership: PdfPageObjectOwnership,
bindings: &'a dyn PdfiumLibraryBindings,
}
impl<'a> PdfPageImageObject<'a> {
#[inline]
pub(crate) fn from_pdfium(
object_handle: FPDF_PAGEOBJECT,
ownership: PdfPageObjectOwnership,
bindings: &'a dyn PdfiumLibraryBindings,
) -> Self {
PdfPageImageObject {
object_handle,
ownership,
bindings,
}
}
#[cfg(feature = "image_025")]
#[inline]
pub fn new(document: &PdfDocument<'a>, image: &DynamicImage) -> Result<Self, PdfiumError> {
let mut result = Self::new_from_handle(document.handle(), document.bindings());
if let Ok(result) = result.as_mut() {
result.set_image(image)?;
}
result
}
#[cfg(not(feature = "image_025"))]
pub fn new(document: &PdfDocument<'a>) -> Result<Self, PdfiumError> {
Self::new_from_handle(document.handle(), document.bindings())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_from_jpeg_file(
document: &PdfDocument<'a>,
path: &(impl AsRef<Path> + ?Sized),
) -> Result<Self, PdfiumError> {
Self::new_from_jpeg_reader(document, File::open(path).map_err(PdfiumError::IoError)?)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new_from_jpeg_reader<R: Read + Seek>(document: &PdfDocument<'a>, reader: R) -> Result<Self, PdfiumError> {
let object = Self::new_from_handle(document.handle(), document.bindings())?;
let mut reader = get_pdfium_file_accessor_from_reader(reader);
let result = document.bindings().FPDFImageObj_LoadJpegFileInline(
std::ptr::null_mut(),
0,
object.object_handle(),
reader.as_fpdf_file_access_mut_ptr(),
);
if object.bindings.is_true(result) {
Ok(object)
} else {
Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
}
}
pub(crate) fn new_from_handle(
document: FPDF_DOCUMENT,
bindings: &'a dyn PdfiumLibraryBindings,
) -> Result<Self, PdfiumError> {
let handle = bindings.FPDFPageObj_NewImageObj(document);
if handle.is_null() {
Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
} else {
Ok(PdfPageImageObject {
object_handle: handle,
ownership: PdfPageObjectOwnership::unowned(),
bindings,
})
}
}
#[cfg(feature = "image_025")]
pub fn new_with_width(
document: &PdfDocument<'a>,
image: &DynamicImage,
width: PdfPoints,
) -> Result<Self, PdfiumError> {
let aspect_ratio = image.height() as f32 / image.width() as f32;
let height = width * aspect_ratio;
Self::new_with_size(document, image, width, height)
}
#[cfg(feature = "image_025")]
pub fn new_with_height(
document: &PdfDocument<'a>,
image: &DynamicImage,
height: PdfPoints,
) -> Result<Self, PdfiumError> {
let aspect_ratio = image.height() as f32 / image.width() as f32;
let width = height / aspect_ratio;
Self::new_with_size(document, image, width, height)
}
#[cfg(feature = "image_025")]
#[inline]
pub fn new_with_size(
document: &PdfDocument<'a>,
image: &DynamicImage,
width: PdfPoints,
height: PdfPoints,
) -> Result<Self, PdfiumError> {
let mut result = Self::new(document, image)?;
result.scale(width.value, height.value)?;
Ok(result)
}
pub fn get_raw_bitmap(&self) -> Result<PdfBitmap<'_>, PdfiumError> {
Ok(PdfBitmap::from_pdfium(
self.bindings().FPDFImageObj_GetBitmap(self.object_handle()),
self.bindings(),
))
}
#[cfg(feature = "image_025")]
#[inline]
pub fn get_raw_image(&self) -> Result<DynamicImage, PdfiumError> {
self.get_image_from_bitmap(&self.get_raw_bitmap()?)
}
#[inline]
pub fn get_processed_bitmap(&self, document: &PdfDocument) -> Result<PdfBitmap<'_>, PdfiumError> {
let (width, height) = self.get_current_width_and_height_from_metadata()?;
self.get_processed_bitmap_with_size(document, width, height)
}
#[cfg(feature = "image_025")]
#[inline]
pub fn get_processed_image(&self, document: &PdfDocument) -> Result<DynamicImage, PdfiumError> {
let (width, height) = self.get_current_width_and_height_from_metadata()?;
self.get_processed_image_with_size(document, width, height)
}
#[inline]
pub fn get_processed_bitmap_with_width(
&self,
document: &PdfDocument,
width: Pixels,
) -> Result<PdfBitmap<'_>, PdfiumError> {
let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
let aspect_ratio = current_width as f32 / current_height as f32;
self.get_processed_bitmap_with_size(
document,
width,
((width as f32 / aspect_ratio) as u32)
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
)
}
#[cfg(feature = "image_025")]
#[inline]
pub fn get_processed_image_with_width(
&self,
document: &PdfDocument,
width: Pixels,
) -> Result<DynamicImage, PdfiumError> {
let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
let aspect_ratio = current_width as f32 / current_height as f32;
self.get_processed_image_with_size(
document,
width,
((width as f32 / aspect_ratio) as u32)
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
)
}
#[inline]
pub fn get_processed_bitmap_with_height(
&self,
document: &PdfDocument,
height: Pixels,
) -> Result<PdfBitmap<'_>, PdfiumError> {
let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
let aspect_ratio = current_width as f32 / current_height as f32;
self.get_processed_bitmap_with_size(
document,
((height as f32 * aspect_ratio) as u32)
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
height,
)
}
#[cfg(feature = "image_025")]
#[inline]
pub fn get_processed_image_with_height(
&self,
document: &PdfDocument,
height: Pixels,
) -> Result<DynamicImage, PdfiumError> {
let (current_width, current_height) = self.get_current_width_and_height_from_metadata()?;
let aspect_ratio = current_width as f32 / current_height as f32;
self.get_processed_image_with_size(
document,
((height as f32 * aspect_ratio) as u32)
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)?,
height,
)
}
pub fn get_processed_bitmap_with_size(
&self,
document: &PdfDocument,
width: Pixels,
height: Pixels,
) -> Result<PdfBitmap<'_>, PdfiumError> {
let mut matrix = self.matrix()?;
let original_matrix = matrix;
if matrix.a() < 0f32 {
matrix.set_a(-matrix.a());
self.reset_matrix_impl(matrix)?;
}
if matrix.d() < 0f32 {
matrix.set_d(-matrix.d());
self.reset_matrix_impl(matrix)?;
}
let page_handle = match self.ownership() {
PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
_ => None,
};
let bitmap_handle = match page_handle {
Some(page_handle) => {
self.bindings()
.FPDFImageObj_GetRenderedBitmap(document.handle(), page_handle, self.object_handle())
}
None => self.bindings.FPDFImageObj_GetRenderedBitmap(
document.handle(),
std::ptr::null_mut::<fpdf_page_t__>(),
self.object_handle(),
),
};
if bitmap_handle.is_null() {
self.reset_matrix_impl(original_matrix)?;
return Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown));
}
let result = PdfBitmap::from_pdfium(bitmap_handle, self.bindings());
if width == result.width() && height == result.height() {
self.reset_matrix_impl(original_matrix)?;
Ok(result)
} else {
self.transform_impl(
width as PdfMatrixValue / result.width() as PdfMatrixValue,
0.0,
0.0,
height as PdfMatrixValue / result.height() as PdfMatrixValue,
0.0,
0.0,
)?;
let result = PdfBitmap::from_pdfium(
match page_handle {
Some(page_handle) => self.bindings().FPDFImageObj_GetRenderedBitmap(
document.handle(),
page_handle,
self.object_handle(),
),
None => self.bindings.FPDFImageObj_GetRenderedBitmap(
document.handle(),
std::ptr::null_mut::<fpdf_page_t__>(),
self.object_handle(),
),
},
self.bindings,
);
self.reset_matrix_impl(original_matrix)?;
Ok(result)
}
}
#[cfg(feature = "image_025")]
#[inline]
pub fn get_processed_image_with_size(
&self,
document: &PdfDocument,
width: Pixels,
height: Pixels,
) -> Result<DynamicImage, PdfiumError> {
self.get_processed_bitmap_with_size(document, width, height)
.and_then(|bitmap| self.get_image_from_bitmap(&bitmap))
}
#[cfg(feature = "image_025")]
pub(crate) fn get_image_from_bitmap(&self, bitmap: &PdfBitmap) -> Result<DynamicImage, PdfiumError> {
let handle = bitmap.handle();
let width = self.bindings.FPDFBitmap_GetWidth(handle);
let height = self.bindings.FPDFBitmap_GetHeight(handle);
let stride = self.bindings.FPDFBitmap_GetStride(handle);
let format = PdfBitmapFormat::from_pdfium(self.bindings.FPDFBitmap_GetFormat(handle) as u32)?;
#[cfg(not(target_arch = "wasm32"))]
let buffer = self.bindings.FPDFBitmap_GetBuffer_as_slice(handle);
#[cfg(target_arch = "wasm32")]
let buffer_vec = self.bindings.FPDFBitmap_GetBuffer_as_vec(handle);
#[cfg(target_arch = "wasm32")]
let buffer = buffer_vec.as_slice();
match format {
#[allow(deprecated)]
PdfBitmapFormat::BGRA | PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx => {
RgbaImage::from_raw(width as u32, height as u32, bgra_to_rgba(buffer)).map(DynamicImage::ImageRgba8)
}
PdfBitmapFormat::BGR => RgbaImage::from_raw(
width as u32,
height as u32,
aligned_bgr_to_rgba(buffer, width as usize, stride as usize),
)
.map(DynamicImage::ImageRgba8),
PdfBitmapFormat::Gray => GrayImage::from_raw(
width as u32,
height as u32,
aligned_grayscale_to_unaligned(buffer, width as usize, stride as usize),
)
.map(DynamicImage::ImageLuma8),
}
.ok_or(PdfiumError::ImageError)
}
pub fn get_raw_image_data(&self) -> Result<Vec<u8>, PdfiumError> {
let buffer_length = self
.bindings()
.FPDFImageObj_GetImageDataRaw(self.object_handle(), std::ptr::null_mut(), 0);
if buffer_length == 0 {
return Ok(Vec::new());
}
let mut buffer = create_byte_buffer(buffer_length as usize);
let result = self.bindings().FPDFImageObj_GetImageDataRaw(
self.object_handle(),
buffer.as_mut_ptr() as *mut c_void,
buffer_length,
);
assert_eq!(result, buffer_length);
Ok(buffer)
}
pub(crate) fn get_current_width_and_height_from_metadata(&self) -> Result<(Pixels, Pixels), PdfiumError> {
let width = self
.get_raw_metadata()
.and_then(|metadata| metadata.width.try_into().map_err(|_| PdfiumError::ImageSizeOutOfBounds))?;
let height = self.get_raw_metadata().and_then(|metadata| {
metadata
.height
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)
})?;
Ok((width, height))
}
#[inline]
pub fn width(&self) -> Result<Pixels, PdfiumError> {
self.get_current_width_and_height_from_metadata()
.map(|(width, _height)| width)
}
#[inline]
pub fn height(&self) -> Result<Pixels, PdfiumError> {
self.get_current_width_and_height_from_metadata()
.map(|(_width, height)| height)
}
#[cfg(feature = "image_025")]
pub fn set_image(&mut self, image: &DynamicImage) -> Result<(), PdfiumError> {
let width: Pixels = image
.width()
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
let height: Pixels = image
.height()
.try_into()
.map_err(|_| PdfiumError::ImageSizeOutOfBounds)?;
let bitmap = PdfBitmap::empty(width, height, PdfBitmapFormat::BGRA, self.bindings)?;
let buffer = if let Some(image) = image.as_rgba8() {
rgba_to_bgra(image.as_bytes())
} else {
let image = image.to_rgba8();
rgba_to_bgra(image.as_bytes())
};
if !self.bindings.FPDFBitmap_SetBuffer(bitmap.handle(), buffer.as_slice()) {
return Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown));
}
self.set_bitmap(&bitmap)
}
pub fn set_bitmap(&mut self, bitmap: &PdfBitmap) -> Result<(), PdfiumError> {
if self.bindings.is_true(self.bindings().FPDFImageObj_SetBitmap(
std::ptr::null_mut::<FPDF_PAGE>(),
0,
self.object_handle(),
bitmap.handle(),
)) {
Ok(())
} else {
Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
}
}
pub(crate) fn get_raw_metadata(&self) -> Result<FPDF_IMAGEOBJ_METADATA, PdfiumError> {
let mut metadata = FPDF_IMAGEOBJ_METADATA {
width: 0,
height: 0,
horizontal_dpi: 0.0,
vertical_dpi: 0.0,
bits_per_pixel: 0,
colorspace: 0,
marked_content_id: 0,
};
let page_handle = match self.ownership() {
PdfPageObjectOwnership::Page(ownership) => Some(ownership.page_handle()),
PdfPageObjectOwnership::AttachedAnnotation(ownership) => Some(ownership.page_handle()),
_ => None,
};
let result = self.bindings().FPDFImageObj_GetImageMetadata(
self.object_handle(),
match page_handle {
Some(page_handle) => page_handle,
None => std::ptr::null_mut::<fpdf_page_t__>(),
},
&mut metadata,
);
if self.bindings().is_true(result) {
Ok(metadata)
} else {
Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
}
}
#[inline]
pub fn horizontal_dpi(&self) -> Result<f32, PdfiumError> {
self.get_raw_metadata().map(|metadata| metadata.horizontal_dpi)
}
#[inline]
pub fn vertical_dpi(&self) -> Result<f32, PdfiumError> {
self.get_raw_metadata().map(|metadata| metadata.vertical_dpi)
}
#[inline]
pub fn bits_per_pixel(&self) -> Result<u8, PdfiumError> {
self.get_raw_metadata().map(|metadata| metadata.bits_per_pixel as u8)
}
#[inline]
pub fn color_space(&self) -> Result<PdfColorSpace, PdfiumError> {
self.get_raw_metadata()
.and_then(|metadata| PdfColorSpace::from_pdfium(metadata.colorspace as u32))
}
#[inline]
pub fn filters(&self) -> PdfPageImageObjectFilters<'_> {
PdfPageImageObjectFilters::new(self)
}
create_transform_setters!(
&mut Self,
Result<(), PdfiumError>,
"this [PdfPageImageObject]",
"this [PdfPageImageObject].",
"this [PdfPageImageObject],"
);
create_transform_getters!(
"this [PdfPageImageObject]",
"this [PdfPageImageObject].",
"this [PdfPageImageObject],"
);
}
impl<'a> PdfPageObjectPrivate<'a> for PdfPageImageObject<'a> {
#[inline]
fn object_handle(&self) -> FPDF_PAGEOBJECT {
self.object_handle
}
#[inline]
fn ownership(&self) -> &PdfPageObjectOwnership {
&self.ownership
}
#[inline]
fn set_ownership(&mut self, ownership: PdfPageObjectOwnership) {
self.ownership = ownership;
}
#[inline]
fn bindings(&self) -> &dyn PdfiumLibraryBindings {
self.bindings
}
#[inline]
fn is_copyable_impl(&self) -> bool {
self.filters().is_empty()
}
#[inline]
fn try_copy_impl<'b>(
&self,
document: FPDF_DOCUMENT,
bindings: &'b dyn PdfiumLibraryBindings,
) -> Result<PdfPageObject<'b>, PdfiumError> {
if !self.filters().is_empty() {
return Err(PdfiumError::ImageObjectFiltersNotCopyable);
}
let mut copy = PdfPageImageObject::new_from_handle(document, bindings)?;
copy.set_bitmap(&self.get_raw_bitmap()?)?;
copy.reset_matrix(self.matrix()?)?;
Ok(PdfPageObject::Image(copy))
}
}
pub type PdfPageImageObjectFilterIndex = usize;
pub struct PdfPageImageObjectFilters<'a> {
object: &'a PdfPageImageObject<'a>,
}
impl<'a> PdfPageImageObjectFilters<'a> {
#[inline]
pub(crate) fn new(object: &'a PdfPageImageObject<'a>) -> Self {
PdfPageImageObjectFilters { object }
}
pub fn len(&self) -> usize {
self.object
.bindings()
.FPDFImageObj_GetImageFilterCount(self.object.object_handle()) as usize
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn as_range(&self) -> Range<PdfPageImageObjectFilterIndex> {
0..self.len()
}
#[inline]
pub fn as_range_inclusive(&self) -> RangeInclusive<PdfPageImageObjectFilterIndex> {
if self.is_empty() { 0..=0 } else { 0..=(self.len() - 1) }
}
pub fn get(&self, index: PdfPageImageObjectFilterIndex) -> Result<PdfPageImageObjectFilter, PdfiumError> {
if index >= self.len() {
return Err(PdfiumError::ImageObjectFilterIndexOutOfBounds);
}
let buffer_length = self.object.bindings().FPDFImageObj_GetImageFilter(
self.object.object_handle(),
index as c_int,
std::ptr::null_mut(),
0,
);
if buffer_length == 0 {
return Err(PdfiumError::ImageObjectFilterIndexInBoundsButFilterUndefined);
}
let mut buffer = create_byte_buffer(buffer_length as usize);
let result = self.object.bindings().FPDFImageObj_GetImageFilter(
self.object.object_handle(),
index as c_int,
buffer.as_mut_ptr() as *mut c_void,
buffer_length,
);
assert_eq!(result, buffer_length);
Ok(PdfPageImageObjectFilter::new(
String::from_utf8(buffer)
.map(|str| str.trim_end_matches(char::from(0)).to_owned())
.unwrap_or_default(),
))
}
#[inline]
pub fn iter(&self) -> PdfPageImageObjectFiltersIterator<'_> {
PdfPageImageObjectFiltersIterator::new(self)
}
}
pub struct PdfPageImageObjectFilter {
name: String,
}
impl PdfPageImageObjectFilter {
#[inline]
pub(crate) fn new(name: String) -> Self {
PdfPageImageObjectFilter { name }
}
pub fn name(&self) -> &str {
self.name.as_str()
}
}
pub struct PdfPageImageObjectFiltersIterator<'a> {
filters: &'a PdfPageImageObjectFilters<'a>,
next_index: PdfPageImageObjectFilterIndex,
}
impl<'a> PdfPageImageObjectFiltersIterator<'a> {
#[inline]
pub(crate) fn new(filters: &'a PdfPageImageObjectFilters<'a>) -> Self {
PdfPageImageObjectFiltersIterator { filters, next_index: 0 }
}
}
impl<'a> Iterator for PdfPageImageObjectFiltersIterator<'a> {
type Item = PdfPageImageObjectFilter;
fn next(&mut self) -> Option<Self::Item> {
let next = self.filters.get(self.next_index);
self.next_index += 1;
next.ok()
}
}
impl<'a> Drop for PdfPageImageObject<'a> {
fn drop(&mut self) {
self.drop_impl();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
use crate::utils::test::{test_bind_to_pdfium, test_fixture_path};
#[test]
fn test_page_image_object_retains_format() -> Result<(), PdfiumError> {
let pdfium = test_bind_to_pdfium();
let image = pdfium
.load_pdf_from_file(&test_fixture_path("path-test.pdf"), None)?
.pages()
.get(0)?
.render_with_config(&PdfRenderConfig::new().set_target_width(1000))?
.as_image()?;
let mut document = pdfium.create_new_pdf()?;
let mut page = document.pages_mut().create_page_at_end(PdfPagePaperSize::a4())?;
let object = page.objects_mut().create_image_object(
PdfPoints::new(100.0),
PdfPoints::new(100.0),
&image,
Some(PdfPoints::new(image.width() as f32)),
Some(PdfPoints::new(image.height() as f32)),
)?;
let raw_image = object.as_image_object().unwrap().get_raw_image()?;
let processed_image = object.as_image_object().unwrap().get_processed_image(&document)?;
assert!(compare_equality_of_byte_arrays(
image.as_bytes(),
raw_image.into_rgba8().as_raw().as_slice()
));
assert!(compare_equality_of_byte_arrays(
image.as_bytes(),
processed_image.into_rgba8().as_raw().as_slice()
));
Ok(())
}
fn compare_equality_of_byte_arrays(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
for index in 0..a.len() {
if a[index] != b[index] {
return false;
}
}
true
}
#[test]
fn test_image_scaling_keeps_aspect_ratio() -> Result<(), PdfiumError> {
let pdfium = test_bind_to_pdfium();
let mut document = pdfium.create_new_pdf()?;
let mut page = document.pages_mut().create_page_at_end(PdfPagePaperSize::a4())?;
let image = DynamicImage::new_rgb8(100, 200);
let object = page.objects_mut().create_image_object(
PdfPoints::new(0.0),
PdfPoints::new(0.0),
&image,
Some(PdfPoints::new(image.width() as f32)),
Some(PdfPoints::new(image.height() as f32)),
)?;
let image_object = object.as_image_object().unwrap();
assert_eq!(
image_object.get_processed_bitmap_with_width(&document, 50)?.height(),
100
);
assert_eq!(
image_object.get_processed_image_with_width(&document, 50)?.height(),
100
);
assert_eq!(
image_object.get_processed_bitmap_with_height(&document, 50)?.width(),
25
);
assert_eq!(image_object.get_processed_image_with_height(&document, 50)?.width(), 25);
Ok(())
}
}