1use crate::bindgen::{
4 FPDF_BITMAP, FPDFBitmap_BGR, FPDFBitmap_BGRA, FPDFBitmap_BGRx, FPDFBitmap_Gray, FPDFBitmap_Unknown,
5};
6use crate::bindings::PdfiumLibraryBindings;
7use crate::error::{PdfiumError, PdfiumInternalError};
8use crate::pdf::document::page::render_config::PdfPageRenderSettings;
9use crate::utils::pixels::{aligned_bgr_to_rgba, aligned_rgb_to_rgba, bgra_to_rgba};
10use std::os::raw::c_int;
11
12#[cfg(feature = "image_025")]
13use image_025::{DynamicImage, GrayImage, RgbaImage};
14
15#[cfg(not(target_arch = "wasm32"))]
16use std::os::raw::c_void;
17
18#[cfg(target_arch = "wasm32")]
19use {
20 js_sys::Uint8Array,
21 wasm_bindgen::{Clamped, JsValue},
22 web_sys::ImageData,
23};
24
25#[cfg(doc)]
26struct Uint8Array;
27
28#[cfg(doc)]
29struct ImageData;
30
31#[cfg(doc)]
32struct JsValue;
33
34pub type Pixels = i32;
41
42#[derive(Copy, Clone, Debug, PartialEq)]
44#[allow(clippy::manual_non_exhaustive)]
45pub enum PdfBitmapFormat {
46 Gray = FPDFBitmap_Gray as isize,
47 BGR = FPDFBitmap_BGR as isize,
48 BGRx = FPDFBitmap_BGRx as isize,
49 BGRA = FPDFBitmap_BGRA as isize,
50
51 #[deprecated(
54 since = "0.8.7",
55 note = "This variant has been renamed to correct a misspelling. Use the BGRx variant instead."
56 )]
57 #[doc(hidden)]
58 BRGx = 999,
59}
60
61impl PdfBitmapFormat {
62 #[inline]
63 #[allow(non_upper_case_globals)]
64 pub(crate) fn from_pdfium(format: u32) -> Result<Self, PdfiumError> {
65 match format {
66 FPDFBitmap_Unknown => Err(PdfiumError::UnknownBitmapFormat),
67 FPDFBitmap_Gray => Ok(PdfBitmapFormat::Gray),
68 FPDFBitmap_BGR => Ok(PdfBitmapFormat::BGR),
69 FPDFBitmap_BGRx => Ok(PdfBitmapFormat::BGRx),
70 FPDFBitmap_BGRA => Ok(PdfBitmapFormat::BGRA),
71 _ => Err(PdfiumError::UnknownBitmapFormat),
72 }
73 }
74
75 #[inline]
76 pub(crate) fn as_pdfium(&self) -> u32 {
77 match self {
78 PdfBitmapFormat::Gray => FPDFBitmap_Gray,
79 PdfBitmapFormat::BGR => FPDFBitmap_BGR,
80 #[allow(deprecated)]
81 PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx => FPDFBitmap_BGRx,
82 PdfBitmapFormat::BGRA => FPDFBitmap_BGRA,
83 }
84 }
85}
86
87#[allow(clippy::derivable_impls)]
88impl Default for PdfBitmapFormat {
89 #[inline]
90 fn default() -> Self {
91 PdfBitmapFormat::BGRA
92 }
93}
94
95pub struct PdfBitmap<'a> {
97 handle: FPDF_BITMAP,
98 was_byte_order_reversed_during_rendering: bool,
99 bindings: &'a dyn PdfiumLibraryBindings,
100}
101
102impl<'a> PdfBitmap<'a> {
103 pub(crate) fn from_pdfium(handle: FPDF_BITMAP, bindings: &'a dyn PdfiumLibraryBindings) -> Self {
105 PdfBitmap {
106 handle,
107 was_byte_order_reversed_during_rendering: false,
108 bindings,
109 }
110 }
111
112 pub fn empty(
115 width: Pixels,
116 height: Pixels,
117 format: PdfBitmapFormat,
118 bindings: &'a dyn PdfiumLibraryBindings,
119 ) -> Result<PdfBitmap<'a>, PdfiumError> {
120 let handle = bindings.FPDFBitmap_CreateEx(
121 width as c_int,
122 height as c_int,
123 format.as_pdfium() as c_int,
124 std::ptr::null_mut(),
125 0,
126 );
127
128 if handle.is_null() {
129 Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
130 } else {
131 Ok(Self::from_pdfium(handle, bindings))
132 }
133 }
134
135 #[cfg(not(target_arch = "wasm32"))]
146 pub unsafe fn from_bytes(
147 width: Pixels,
148 height: Pixels,
149 format: PdfBitmapFormat,
150 buffer: &'a mut [u8],
151 bindings: &'a dyn PdfiumLibraryBindings,
152 ) -> Result<PdfBitmap<'a>, PdfiumError> {
153 let handle = bindings.FPDFBitmap_CreateEx(
154 width as c_int,
155 height as c_int,
156 format.as_pdfium() as c_int,
157 buffer.as_mut_ptr() as *mut c_void,
158 0,
159 );
160
161 if handle.is_null() {
162 Err(PdfiumError::PdfiumLibraryInternalError(PdfiumInternalError::Unknown))
163 } else {
164 Ok(Self::from_pdfium(handle, bindings))
165 }
166 }
167
168 #[inline]
170 pub(crate) fn handle(&self) -> FPDF_BITMAP {
171 self.handle
172 }
173
174 #[inline]
179 pub(crate) fn set_byte_order_from_render_settings(&mut self, settings: &PdfPageRenderSettings) {
180 self.was_byte_order_reversed_during_rendering = settings.is_reversed_byte_order_flag_set
181 }
182
183 #[inline]
185 pub fn bindings(&self) -> &dyn PdfiumLibraryBindings {
186 self.bindings
187 }
188
189 #[inline]
191 pub fn width(&self) -> Pixels {
192 self.bindings().FPDFBitmap_GetWidth(self.handle()) as Pixels
193 }
194
195 #[inline]
197 pub fn height(&self) -> Pixels {
198 self.bindings().FPDFBitmap_GetHeight(self.handle()) as Pixels
199 }
200
201 #[inline]
203 pub fn format(&self) -> Result<PdfBitmapFormat, PdfiumError> {
204 PdfBitmapFormat::from_pdfium(self.bindings().FPDFBitmap_GetFormat(self.handle()) as u32)
205 }
206
207 #[deprecated(
211 since = "0.8.16",
212 note = "This function has been renamed to better reflect its purpose. Use the PdfBitmap::as_raw_bytes() function instead."
213 )]
214 #[doc(hidden)]
215 #[inline]
216 pub fn as_bytes(&self) -> Vec<u8> {
217 self.as_raw_bytes()
218 }
219
220 pub fn as_raw_bytes(&self) -> Vec<u8> {
227 self.bindings().FPDFBitmap_GetBuffer_as_vec(self.handle)
228 }
229
230 pub fn as_rgba_bytes(&self) -> Vec<u8> {
233 let bytes = self.as_raw_bytes();
234
235 let format = self.format().unwrap_or_default();
236
237 let width = self.width() as usize;
238
239 let stride = bytes.len() / self.height() as usize;
240
241 if self.was_byte_order_reversed_during_rendering {
242 match format {
243 #[allow(deprecated)]
244 PdfBitmapFormat::BGRA | PdfBitmapFormat::BGRx | PdfBitmapFormat::BRGx => bytes,
245 PdfBitmapFormat::BGR => aligned_rgb_to_rgba(bytes.as_slice(), width, stride),
246 PdfBitmapFormat::Gray => bytes,
247 }
248 } else {
249 match format {
250 #[allow(deprecated)]
251 PdfBitmapFormat::BGRA | PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx => bgra_to_rgba(bytes.as_slice()),
252 PdfBitmapFormat::BGR => aligned_bgr_to_rgba(bytes.as_slice(), width, stride),
253 PdfBitmapFormat::Gray => bytes,
254 }
255 }
256 }
257
258 #[cfg(feature = "image_025")]
262 pub fn as_image(&self) -> Result<DynamicImage, PdfiumError> {
263 let bytes = self.as_rgba_bytes();
264
265 let width = self.width() as u32;
266
267 let height = self.height() as u32;
268
269 match self.format().unwrap_or_default() {
270 #[allow(deprecated)]
271 PdfBitmapFormat::BGRA | PdfBitmapFormat::BRGx | PdfBitmapFormat::BGRx | PdfBitmapFormat::BGR => {
272 RgbaImage::from_raw(width, height, bytes)
273 .map(DynamicImage::ImageRgba8)
274 .ok_or(PdfiumError::ImageError)
275 }
276 PdfBitmapFormat::Gray => GrayImage::from_raw(width, height, bytes)
277 .map(DynamicImage::ImageLuma8)
278 .ok_or(PdfiumError::ImageError),
279 }
280 }
281
282 #[deprecated(
291 since = "0.7.12",
292 note = "This function is no longer necessary since all page rendering operations are now processed eagerly rather than lazily. Calls to this function can be removed."
293 )]
294 #[doc(hidden)]
295 #[inline]
296 pub fn render(&self) {}
297
298 #[cfg(any(doc, target_arch = "wasm32"))]
312 #[inline]
313 pub fn as_array(&self) -> Uint8Array {
314 self.bindings().FPDFBitmap_GetBuffer_as_array(self.handle())
315 }
316
317 #[cfg(any(doc, target_arch = "wasm32"))]
329 #[inline]
330 pub fn as_image_data(&self) -> Result<ImageData, JsValue> {
331 ImageData::new_with_u8_clamped_array_and_sh(
332 Clamped(&self.as_rgba_bytes()),
333 self.width() as u32,
334 self.height() as u32,
335 )
336 }
337
338 #[inline]
345 pub fn bytes_required_for_size(width: Pixels, height: Pixels) -> usize {
346 4 * width as usize * height as usize
347 }
348}
349
350impl<'a> Drop for PdfBitmap<'a> {
351 #[inline]
353 fn drop(&mut self) {
354 self.bindings().FPDFBitmap_Destroy(self.handle());
355 }
356}
357
358#[cfg(test)]
359mod tests {
360 use crate::prelude::*;
361 use crate::utils::mem::create_sized_buffer;
362 use crate::utils::test::test_bind_to_pdfium;
363
364 #[test]
365 fn test_from_bytes() -> Result<(), PdfiumError> {
366 let pdfium = test_bind_to_pdfium();
367
368 let test_width = 2000;
369 let test_height = 4000;
370
371 let mut buffer = create_sized_buffer(PdfBitmap::bytes_required_for_size(test_width, test_height));
372
373 let buffer_ptr = buffer.as_ptr();
374
375 let bitmap = unsafe {
376 PdfBitmap::from_bytes(
377 test_width,
378 test_height,
379 PdfBitmapFormat::BGRx,
380 buffer.as_mut_slice(),
381 pdfium.bindings(),
382 )?
383 };
384
385 assert_eq!(bitmap.width(), test_width);
386 assert_eq!(bitmap.height(), test_height);
387 assert_eq!(
388 pdfium.bindings().FPDFBitmap_GetBuffer(bitmap.handle) as usize,
389 buffer_ptr as usize
390 );
391 assert_eq!(pdfium.bindings().FPDFBitmap_GetStride(bitmap.handle), test_width * 4);
392
393 Ok(())
394 }
395}