1use crate::objects::{Dictionary, Object};
7use crate::{PdfError, Result};
8use std::fs::File;
9use std::io::Read;
10use std::path::Path;
11
12#[derive(Debug, Clone)]
14pub struct Image {
15 data: Vec<u8>,
17 format: ImageFormat,
19 width: u32,
21 height: u32,
23 color_space: ColorSpace,
25 bits_per_component: u8,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum ImageFormat {
32 Jpeg,
34 Png,
36 Tiff,
38 Raw,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq)]
44pub enum ColorSpace {
45 DeviceGray,
47 DeviceRGB,
49 DeviceCMYK,
51}
52
53impl Image {
54 pub fn from_jpeg_file<P: AsRef<Path>>(path: P) -> Result<Self> {
56 #[cfg(feature = "external-images")]
57 {
58 Self::from_external_jpeg_file(path)
59 }
60 #[cfg(not(feature = "external-images"))]
61 {
62 let mut file = File::open(path)?;
63 let mut data = Vec::new();
64 file.read_to_end(&mut data)?;
65 Self::from_jpeg_data(data)
66 }
67 }
68
69 pub fn from_jpeg_data(data: Vec<u8>) -> Result<Self> {
71 let (width, height, color_space, bits_per_component) = parse_jpeg_header(&data)?;
73
74 Ok(Image {
75 data,
76 format: ImageFormat::Jpeg,
77 width,
78 height,
79 color_space,
80 bits_per_component,
81 })
82 }
83
84 pub fn from_png_file<P: AsRef<Path>>(path: P) -> Result<Self> {
86 #[cfg(feature = "external-images")]
87 {
88 Self::from_external_png_file(path)
89 }
90 #[cfg(not(feature = "external-images"))]
91 {
92 let mut file = File::open(path)?;
93 let mut data = Vec::new();
94 file.read_to_end(&mut data)?;
95 Self::from_png_data(data)
96 }
97 }
98
99 pub fn from_png_data(data: Vec<u8>) -> Result<Self> {
101 let (width, height, color_space, bits_per_component) = parse_png_header(&data)?;
103
104 Ok(Image {
105 data,
106 format: ImageFormat::Png,
107 width,
108 height,
109 color_space,
110 bits_per_component,
111 })
112 }
113
114 pub fn from_tiff_file<P: AsRef<Path>>(path: P) -> Result<Self> {
116 let mut file = File::open(path)?;
117 let mut data = Vec::new();
118 file.read_to_end(&mut data)?;
119 Self::from_tiff_data(data)
120 }
121
122 pub fn from_tiff_data(data: Vec<u8>) -> Result<Self> {
124 let (width, height, color_space, bits_per_component) = parse_tiff_header(&data)?;
126
127 Ok(Image {
128 data,
129 format: ImageFormat::Tiff,
130 width,
131 height,
132 color_space,
133 bits_per_component,
134 })
135 }
136
137 pub fn width(&self) -> u32 {
139 self.width
140 }
141
142 pub fn height(&self) -> u32 {
144 self.height
145 }
146
147 pub fn data(&self) -> &[u8] {
149 &self.data
150 }
151
152 pub fn format(&self) -> ImageFormat {
154 self.format
155 }
156
157 pub fn from_raw_data(
159 data: Vec<u8>,
160 width: u32,
161 height: u32,
162 color_space: ColorSpace,
163 bits_per_component: u8,
164 ) -> Self {
165 Image {
166 data,
167 format: ImageFormat::Raw,
168 width,
169 height,
170 color_space,
171 bits_per_component,
172 }
173 }
174
175 #[cfg(feature = "external-images")]
177 pub fn from_external_png_file<P: AsRef<Path>>(path: P) -> Result<Self> {
178 let img = image::ImageReader::open(path)?
179 .decode()
180 .map_err(|e| PdfError::InvalidImage(format!("Failed to decode PNG: {}", e)))?;
181
182 Self::from_dynamic_image(img)
183 }
184
185 #[cfg(feature = "external-images")]
187 pub fn from_external_jpeg_file<P: AsRef<Path>>(path: P) -> Result<Self> {
188 let img = image::ImageReader::open(path)?
189 .decode()
190 .map_err(|e| PdfError::InvalidImage(format!("Failed to decode JPEG: {}", e)))?;
191
192 Self::from_dynamic_image(img)
193 }
194
195 #[cfg(feature = "external-images")]
197 fn from_dynamic_image(img: image::DynamicImage) -> Result<Self> {
198 use image::DynamicImage;
199
200 let (width, height) = (img.width(), img.height());
201
202 let (rgb_data, color_space) = match img {
203 DynamicImage::ImageLuma8(gray_img) => (gray_img.into_raw(), ColorSpace::DeviceGray),
204 DynamicImage::ImageLumaA8(gray_alpha_img) => {
205 let rgb_data: Vec<u8> = gray_alpha_img
207 .pixels()
208 .flat_map(|p| [p[0], p[0], p[0]]) .collect();
210 (rgb_data, ColorSpace::DeviceRGB)
211 }
212 DynamicImage::ImageRgb8(rgb_img) => (rgb_img.into_raw(), ColorSpace::DeviceRGB),
213 DynamicImage::ImageRgba8(rgba_img) => {
214 let rgb_data: Vec<u8> = rgba_img
216 .pixels()
217 .flat_map(|p| [p[0], p[1], p[2]]) .collect();
219 (rgb_data, ColorSpace::DeviceRGB)
220 }
221 _ => {
222 let rgb_img = img.to_rgb8();
224 (rgb_img.into_raw(), ColorSpace::DeviceRGB)
225 }
226 };
227
228 Ok(Image {
229 data: rgb_data,
230 format: ImageFormat::Raw,
231 width,
232 height,
233 color_space,
234 bits_per_component: 8,
235 })
236 }
237
238 pub fn to_pdf_object(&self) -> Object {
240 let mut dict = Dictionary::new();
241
242 dict.set("Type", Object::Name("XObject".to_string()));
244 dict.set("Subtype", Object::Name("Image".to_string()));
245 dict.set("Width", Object::Integer(self.width as i64));
246 dict.set("Height", Object::Integer(self.height as i64));
247
248 let color_space_name = match self.color_space {
250 ColorSpace::DeviceGray => "DeviceGray",
251 ColorSpace::DeviceRGB => "DeviceRGB",
252 ColorSpace::DeviceCMYK => "DeviceCMYK",
253 };
254 dict.set("ColorSpace", Object::Name(color_space_name.to_string()));
255
256 dict.set(
258 "BitsPerComponent",
259 Object::Integer(self.bits_per_component as i64),
260 );
261
262 match self.format {
264 ImageFormat::Jpeg => {
265 dict.set("Filter", Object::Name("DCTDecode".to_string()));
266 }
267 ImageFormat::Png => {
268 dict.set("Filter", Object::Name("FlateDecode".to_string()));
269 }
270 ImageFormat::Tiff => {
271 dict.set("Filter", Object::Name("FlateDecode".to_string()));
273 }
274 ImageFormat::Raw => {
275 }
277 }
278
279 Object::Stream(dict, self.data.clone())
281 }
282}
283
284fn parse_jpeg_header(data: &[u8]) -> Result<(u32, u32, ColorSpace, u8)> {
286 if data.len() < 2 || data[0] != 0xFF || data[1] != 0xD8 {
287 return Err(PdfError::InvalidImage("Not a valid JPEG file".to_string()));
288 }
289
290 let mut pos = 2;
291 let mut width = 0;
292 let mut height = 0;
293 let mut components = 0;
294
295 while pos < data.len() - 1 {
296 if data[pos] != 0xFF {
297 return Err(PdfError::InvalidImage("Invalid JPEG marker".to_string()));
298 }
299
300 let marker = data[pos + 1];
301 pos += 2;
302
303 if marker == 0xFF {
305 continue;
306 }
307
308 if (0xC0..=0xCF).contains(&marker) && marker != 0xC4 && marker != 0xC8 && marker != 0xCC {
310 if pos + 7 >= data.len() {
312 return Err(PdfError::InvalidImage("Truncated JPEG file".to_string()));
313 }
314
315 pos += 2;
317
318 pos += 1;
320
321 height = ((data[pos] as u32) << 8) | (data[pos + 1] as u32);
323 pos += 2;
324 width = ((data[pos] as u32) << 8) | (data[pos + 1] as u32);
325 pos += 2;
326
327 components = data[pos];
329 break;
330 } else if marker == 0xD9 {
331 break;
333 } else if marker == 0xD8 || (0xD0..=0xD7).contains(&marker) {
334 continue;
336 } else {
337 if pos + 1 >= data.len() {
339 return Err(PdfError::InvalidImage("Truncated JPEG file".to_string()));
340 }
341 let length = ((data[pos] as usize) << 8) | (data[pos + 1] as usize);
342 pos += length;
343 }
344 }
345
346 if width == 0 || height == 0 {
347 return Err(PdfError::InvalidImage(
348 "Could not find image dimensions".to_string(),
349 ));
350 }
351
352 let color_space = match components {
353 1 => ColorSpace::DeviceGray,
354 3 => ColorSpace::DeviceRGB,
355 4 => ColorSpace::DeviceCMYK,
356 _ => {
357 return Err(PdfError::InvalidImage(format!(
358 "Unsupported number of components: {components}"
359 )))
360 }
361 };
362
363 Ok((width, height, color_space, 8)) }
365
366fn parse_png_header(data: &[u8]) -> Result<(u32, u32, ColorSpace, u8)> {
368 if data.len() < 8 || &data[0..8] != b"\x89PNG\r\n\x1a\n" {
370 return Err(PdfError::InvalidImage("Not a valid PNG file".to_string()));
371 }
372
373 let mut pos = 8;
375
376 while pos + 8 < data.len() {
377 let chunk_length =
379 u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
380
381 let chunk_type = &data[pos + 4..pos + 8];
383
384 if chunk_type == b"IHDR" {
385 if pos + 8 + chunk_length > data.len() || chunk_length < 13 {
387 return Err(PdfError::InvalidImage("Invalid PNG IHDR chunk".to_string()));
388 }
389
390 let ihdr_data = &data[pos + 8..pos + 8 + chunk_length];
391
392 let width =
394 u32::from_be_bytes([ihdr_data[0], ihdr_data[1], ihdr_data[2], ihdr_data[3]]);
395
396 let height =
397 u32::from_be_bytes([ihdr_data[4], ihdr_data[5], ihdr_data[6], ihdr_data[7]]);
398
399 let bit_depth = ihdr_data[8];
400 let color_type = ihdr_data[9];
401
402 let color_space = match color_type {
404 0 => ColorSpace::DeviceGray, 2 => ColorSpace::DeviceRGB, 3 => ColorSpace::DeviceRGB, 4 => ColorSpace::DeviceGray, 6 => ColorSpace::DeviceRGB, _ => {
410 return Err(PdfError::InvalidImage(format!(
411 "Unsupported PNG color type: {color_type}"
412 )))
413 }
414 };
415
416 return Ok((width, height, color_space, bit_depth));
417 }
418
419 pos += 8 + chunk_length + 4; }
422
423 Err(PdfError::InvalidImage(
424 "PNG IHDR chunk not found".to_string(),
425 ))
426}
427
428fn parse_tiff_header(data: &[u8]) -> Result<(u32, u32, ColorSpace, u8)> {
430 if data.len() < 8 {
431 return Err(PdfError::InvalidImage(
432 "Invalid TIFF file: too short".to_string(),
433 ));
434 }
435
436 let (is_little_endian, offset) = if &data[0..2] == b"II" {
438 (true, 2) } else if &data[0..2] == b"MM" {
440 (false, 2) } else {
442 return Err(PdfError::InvalidImage(
443 "Invalid TIFF byte order".to_string(),
444 ));
445 };
446
447 let magic = if is_little_endian {
449 u16::from_le_bytes([data[offset], data[offset + 1]])
450 } else {
451 u16::from_be_bytes([data[offset], data[offset + 1]])
452 };
453
454 if magic != 42 {
455 return Err(PdfError::InvalidImage(
456 "Invalid TIFF magic number".to_string(),
457 ));
458 }
459
460 let ifd_offset = if is_little_endian {
462 u32::from_le_bytes([
463 data[offset + 2],
464 data[offset + 3],
465 data[offset + 4],
466 data[offset + 5],
467 ])
468 } else {
469 u32::from_be_bytes([
470 data[offset + 2],
471 data[offset + 3],
472 data[offset + 4],
473 data[offset + 5],
474 ])
475 } as usize;
476
477 if ifd_offset + 2 > data.len() {
478 return Err(PdfError::InvalidImage(
479 "Invalid TIFF IFD offset".to_string(),
480 ));
481 }
482
483 let num_entries = if is_little_endian {
485 u16::from_le_bytes([data[ifd_offset], data[ifd_offset + 1]])
486 } else {
487 u16::from_be_bytes([data[ifd_offset], data[ifd_offset + 1]])
488 };
489
490 let mut width = 0u32;
491 let mut height = 0u32;
492 let mut bits_per_sample = 8u16;
493 let mut photometric_interpretation = 0u16;
494
495 for i in 0..num_entries {
497 let entry_offset = ifd_offset + 2 + (i as usize * 12);
498
499 if entry_offset + 12 > data.len() {
500 break;
501 }
502
503 let tag = if is_little_endian {
504 u16::from_le_bytes([data[entry_offset], data[entry_offset + 1]])
505 } else {
506 u16::from_be_bytes([data[entry_offset], data[entry_offset + 1]])
507 };
508
509 let value_offset = entry_offset + 8;
510
511 match tag {
512 256 => {
513 width = if is_little_endian {
515 u32::from_le_bytes([
516 data[value_offset],
517 data[value_offset + 1],
518 data[value_offset + 2],
519 data[value_offset + 3],
520 ])
521 } else {
522 u32::from_be_bytes([
523 data[value_offset],
524 data[value_offset + 1],
525 data[value_offset + 2],
526 data[value_offset + 3],
527 ])
528 };
529 }
530 257 => {
531 height = if is_little_endian {
533 u32::from_le_bytes([
534 data[value_offset],
535 data[value_offset + 1],
536 data[value_offset + 2],
537 data[value_offset + 3],
538 ])
539 } else {
540 u32::from_be_bytes([
541 data[value_offset],
542 data[value_offset + 1],
543 data[value_offset + 2],
544 data[value_offset + 3],
545 ])
546 };
547 }
548 258 => {
549 bits_per_sample = if is_little_endian {
551 u16::from_le_bytes([data[value_offset], data[value_offset + 1]])
552 } else {
553 u16::from_be_bytes([data[value_offset], data[value_offset + 1]])
554 };
555 }
556 262 => {
557 photometric_interpretation = if is_little_endian {
559 u16::from_le_bytes([data[value_offset], data[value_offset + 1]])
560 } else {
561 u16::from_be_bytes([data[value_offset], data[value_offset + 1]])
562 };
563 }
564 _ => {} }
566 }
567
568 if width == 0 || height == 0 {
569 return Err(PdfError::InvalidImage(
570 "TIFF dimensions not found".to_string(),
571 ));
572 }
573
574 let color_space = match photometric_interpretation {
576 0 | 1 => ColorSpace::DeviceGray, 2 => ColorSpace::DeviceRGB, 5 => ColorSpace::DeviceCMYK, _ => ColorSpace::DeviceRGB, };
581
582 Ok((width, height, color_space, bits_per_sample as u8))
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588
589 #[test]
590 fn test_parse_jpeg_header() {
591 let jpeg_data = vec![
593 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0xC8, 0x03, ];
602
603 let result = parse_jpeg_header(&jpeg_data);
604 assert!(result.is_ok());
605 let (width, height, color_space, bits) = result.unwrap();
606 assert_eq!(width, 200);
607 assert_eq!(height, 100);
608 assert_eq!(color_space, ColorSpace::DeviceRGB);
609 assert_eq!(bits, 8);
610 }
611
612 #[test]
613 fn test_invalid_jpeg() {
614 let invalid_data = vec![0x00, 0x00];
615 let result = parse_jpeg_header(&invalid_data);
616 assert!(result.is_err());
617 }
618
619 #[test]
620 fn test_parse_png_header() {
621 let mut png_data = vec![
623 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x08, 0x02, 0x00, 0x00, 0x00, ];
634
635 png_data.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
637
638 let result = parse_png_header(&png_data);
639 assert!(result.is_ok());
640 let (width, height, color_space, bits) = result.unwrap();
641 assert_eq!(width, 100);
642 assert_eq!(height, 100);
643 assert_eq!(color_space, ColorSpace::DeviceRGB);
644 assert_eq!(bits, 8);
645 }
646
647 #[test]
648 fn test_invalid_png() {
649 let invalid_data = vec![0x00, 0x00];
650 let result = parse_png_header(&invalid_data);
651 assert!(result.is_err());
652 }
653
654 #[test]
655 fn test_parse_tiff_header_little_endian() {
656 let tiff_data = vec![
658 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
664 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
666 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
668 0x00, 0x00, ];
670
671 let result = parse_tiff_header(&tiff_data);
672 assert!(result.is_ok());
673 let (width, height, color_space, bits) = result.unwrap();
674 assert_eq!(width, 100);
675 assert_eq!(height, 100);
676 assert_eq!(color_space, ColorSpace::DeviceGray);
677 assert_eq!(bits, 8);
678 }
679
680 #[test]
681 fn test_parse_tiff_header_big_endian() {
682 let tiff_data = vec![
684 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64,
690 0x01, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64,
692 0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
694 0x00, 0x00, ];
696
697 let result = parse_tiff_header(&tiff_data);
698 assert!(result.is_ok());
699 let (width, height, color_space, bits) = result.unwrap();
700 assert_eq!(width, 100);
701 assert_eq!(height, 100);
702 assert_eq!(color_space, ColorSpace::DeviceGray);
703 assert_eq!(bits, 8);
704 }
705
706 #[test]
707 fn test_invalid_tiff() {
708 let invalid_data = vec![0x00, 0x00];
709 let result = parse_tiff_header(&invalid_data);
710 assert!(result.is_err());
711 }
712
713 #[test]
714 fn test_image_format_enum() {
715 assert_eq!(ImageFormat::Jpeg, ImageFormat::Jpeg);
716 assert_eq!(ImageFormat::Png, ImageFormat::Png);
717 assert_eq!(ImageFormat::Tiff, ImageFormat::Tiff);
718 assert_ne!(ImageFormat::Jpeg, ImageFormat::Png);
719 }
720
721 mod comprehensive_tests {
723 use super::*;
724 use std::fs;
725 use tempfile::TempDir;
726
727 #[test]
728 fn test_image_format_variants() {
729 let jpeg = ImageFormat::Jpeg;
731 let png = ImageFormat::Png;
732 let tiff = ImageFormat::Tiff;
733
734 assert_eq!(jpeg, ImageFormat::Jpeg);
735 assert_eq!(png, ImageFormat::Png);
736 assert_eq!(tiff, ImageFormat::Tiff);
737
738 assert_ne!(jpeg, png);
739 assert_ne!(png, tiff);
740 assert_ne!(tiff, jpeg);
741 }
742
743 #[test]
744 fn test_image_format_debug() {
745 let jpeg = ImageFormat::Jpeg;
746 let png = ImageFormat::Png;
747 let tiff = ImageFormat::Tiff;
748
749 assert_eq!(format!("{:?}", jpeg), "Jpeg");
750 assert_eq!(format!("{:?}", png), "Png");
751 assert_eq!(format!("{:?}", tiff), "Tiff");
752 }
753
754 #[test]
755 fn test_image_format_clone_copy() {
756 let jpeg = ImageFormat::Jpeg;
757 let jpeg_clone = jpeg;
758 let jpeg_copy = jpeg;
759
760 assert_eq!(jpeg_clone, ImageFormat::Jpeg);
761 assert_eq!(jpeg_copy, ImageFormat::Jpeg);
762 }
763
764 #[test]
765 fn test_color_space_variants() {
766 let gray = ColorSpace::DeviceGray;
768 let rgb = ColorSpace::DeviceRGB;
769 let cmyk = ColorSpace::DeviceCMYK;
770
771 assert_eq!(gray, ColorSpace::DeviceGray);
772 assert_eq!(rgb, ColorSpace::DeviceRGB);
773 assert_eq!(cmyk, ColorSpace::DeviceCMYK);
774
775 assert_ne!(gray, rgb);
776 assert_ne!(rgb, cmyk);
777 assert_ne!(cmyk, gray);
778 }
779
780 #[test]
781 fn test_color_space_debug() {
782 let gray = ColorSpace::DeviceGray;
783 let rgb = ColorSpace::DeviceRGB;
784 let cmyk = ColorSpace::DeviceCMYK;
785
786 assert_eq!(format!("{:?}", gray), "DeviceGray");
787 assert_eq!(format!("{:?}", rgb), "DeviceRGB");
788 assert_eq!(format!("{:?}", cmyk), "DeviceCMYK");
789 }
790
791 #[test]
792 fn test_color_space_clone_copy() {
793 let rgb = ColorSpace::DeviceRGB;
794 let rgb_clone = rgb;
795 let rgb_copy = rgb;
796
797 assert_eq!(rgb_clone, ColorSpace::DeviceRGB);
798 assert_eq!(rgb_copy, ColorSpace::DeviceRGB);
799 }
800
801 #[test]
802 fn test_image_from_jpeg_data() {
803 let jpeg_data = vec![
805 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0xC8, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
817
818 let image = Image::from_jpeg_data(jpeg_data.clone()).unwrap();
819
820 assert_eq!(image.width(), 200);
821 assert_eq!(image.height(), 100);
822 assert_eq!(image.format(), ImageFormat::Jpeg);
823 assert_eq!(image.data(), jpeg_data);
824 }
825
826 #[test]
827 fn test_image_from_png_data() {
828 let png_data = vec![
830 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
842
843 let image = Image::from_png_data(png_data.clone()).unwrap();
844
845 assert_eq!(image.width(), 256);
846 assert_eq!(image.height(), 256);
847 assert_eq!(image.format(), ImageFormat::Png);
848 assert_eq!(image.data(), png_data);
849 }
850
851 #[test]
852 fn test_image_from_tiff_data() {
853 let tiff_data = vec![
855 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
861 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
863 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
865 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
867 0x00, 0x00, ];
869
870 let image = Image::from_tiff_data(tiff_data.clone()).unwrap();
871
872 assert_eq!(image.width(), 128);
873 assert_eq!(image.height(), 128);
874 assert_eq!(image.format(), ImageFormat::Tiff);
875 assert_eq!(image.data(), tiff_data);
876 }
877
878 #[test]
879 fn test_image_from_jpeg_file() {
880 let temp_dir = TempDir::new().unwrap();
881 let file_path = temp_dir.path().join("test.jpg");
882
883 let jpeg_data = vec![
885 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x64, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
897
898 fs::write(&file_path, &jpeg_data).unwrap();
899
900 let image = Image::from_jpeg_file(&file_path).unwrap();
901
902 assert_eq!(image.width(), 100);
903 assert_eq!(image.height(), 50);
904 assert_eq!(image.format(), ImageFormat::Jpeg);
905 assert_eq!(image.data(), jpeg_data);
906 }
907
908 #[test]
909 fn test_image_from_png_file() {
910 let temp_dir = TempDir::new().unwrap();
911 let file_path = temp_dir.path().join("test.png");
912
913 let png_data = vec![
915 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x08, 0x02, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
927
928 fs::write(&file_path, &png_data).unwrap();
929
930 let image = Image::from_png_file(&file_path).unwrap();
931
932 assert_eq!(image.width(), 80);
933 assert_eq!(image.height(), 80);
934 assert_eq!(image.format(), ImageFormat::Png);
935 assert_eq!(image.data(), png_data);
936 }
937
938 #[test]
939 fn test_image_from_tiff_file() {
940 let temp_dir = TempDir::new().unwrap();
941 let file_path = temp_dir.path().join("test.tiff");
942
943 let tiff_data = vec![
945 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
951 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
953 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
955 0x00, 0x00, ];
957
958 fs::write(&file_path, &tiff_data).unwrap();
959
960 let image = Image::from_tiff_file(&file_path).unwrap();
961
962 assert_eq!(image.width(), 96);
963 assert_eq!(image.height(), 96);
964 assert_eq!(image.format(), ImageFormat::Tiff);
965 assert_eq!(image.data(), tiff_data);
966 }
967
968 #[test]
969 fn test_image_to_pdf_object_jpeg() {
970 let jpeg_data = vec![
971 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0xC8, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
983
984 let image = Image::from_jpeg_data(jpeg_data.clone()).unwrap();
985 let pdf_obj = image.to_pdf_object();
986
987 if let Object::Stream(dict, data) = pdf_obj {
988 assert_eq!(
989 dict.get("Type").unwrap(),
990 &Object::Name("XObject".to_string())
991 );
992 assert_eq!(
993 dict.get("Subtype").unwrap(),
994 &Object::Name("Image".to_string())
995 );
996 assert_eq!(dict.get("Width").unwrap(), &Object::Integer(200));
997 assert_eq!(dict.get("Height").unwrap(), &Object::Integer(100));
998 assert_eq!(
999 dict.get("ColorSpace").unwrap(),
1000 &Object::Name("DeviceRGB".to_string())
1001 );
1002 assert_eq!(dict.get("BitsPerComponent").unwrap(), &Object::Integer(8));
1003 assert_eq!(
1004 dict.get("Filter").unwrap(),
1005 &Object::Name("DCTDecode".to_string())
1006 );
1007 assert_eq!(data, jpeg_data);
1008 } else {
1009 panic!("Expected Stream object");
1010 }
1011 }
1012
1013 #[test]
1014 fn test_image_to_pdf_object_png() {
1015 let png_data = vec![
1016 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x08, 0x06, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
1028
1029 let image = Image::from_png_data(png_data.clone()).unwrap();
1030 let pdf_obj = image.to_pdf_object();
1031
1032 if let Object::Stream(dict, data) = pdf_obj {
1033 assert_eq!(
1034 dict.get("Type").unwrap(),
1035 &Object::Name("XObject".to_string())
1036 );
1037 assert_eq!(
1038 dict.get("Subtype").unwrap(),
1039 &Object::Name("Image".to_string())
1040 );
1041 assert_eq!(dict.get("Width").unwrap(), &Object::Integer(80));
1042 assert_eq!(dict.get("Height").unwrap(), &Object::Integer(80));
1043 assert_eq!(
1044 dict.get("ColorSpace").unwrap(),
1045 &Object::Name("DeviceRGB".to_string())
1046 );
1047 assert_eq!(dict.get("BitsPerComponent").unwrap(), &Object::Integer(8));
1048 assert_eq!(
1049 dict.get("Filter").unwrap(),
1050 &Object::Name("FlateDecode".to_string())
1051 );
1052 assert_eq!(data, png_data);
1053 } else {
1054 panic!("Expected Stream object");
1055 }
1056 }
1057
1058 #[test]
1059 fn test_image_to_pdf_object_tiff() {
1060 let tiff_data = vec![
1061 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
1067 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
1069 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1071 0x00, 0x00, ];
1073
1074 let image = Image::from_tiff_data(tiff_data.clone()).unwrap();
1075 let pdf_obj = image.to_pdf_object();
1076
1077 if let Object::Stream(dict, data) = pdf_obj {
1078 assert_eq!(
1079 dict.get("Type").unwrap(),
1080 &Object::Name("XObject".to_string())
1081 );
1082 assert_eq!(
1083 dict.get("Subtype").unwrap(),
1084 &Object::Name("Image".to_string())
1085 );
1086 assert_eq!(dict.get("Width").unwrap(), &Object::Integer(64));
1087 assert_eq!(dict.get("Height").unwrap(), &Object::Integer(64));
1088 assert_eq!(
1089 dict.get("ColorSpace").unwrap(),
1090 &Object::Name("DeviceGray".to_string())
1091 );
1092 assert_eq!(dict.get("BitsPerComponent").unwrap(), &Object::Integer(8));
1093 assert_eq!(
1094 dict.get("Filter").unwrap(),
1095 &Object::Name("FlateDecode".to_string())
1096 );
1097 assert_eq!(data, tiff_data);
1098 } else {
1099 panic!("Expected Stream object");
1100 }
1101 }
1102
1103 #[test]
1104 fn test_image_clone() {
1105 let jpeg_data = vec![
1106 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x64, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
1118
1119 let image1 = Image::from_jpeg_data(jpeg_data.clone()).unwrap();
1120 let image2 = image1.clone();
1121
1122 assert_eq!(image1.width(), image2.width());
1123 assert_eq!(image1.height(), image2.height());
1124 assert_eq!(image1.format(), image2.format());
1125 assert_eq!(image1.data(), image2.data());
1126 }
1127
1128 #[test]
1129 fn test_image_debug() {
1130 let jpeg_data = vec![
1131 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x64, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
1143
1144 let image = Image::from_jpeg_data(jpeg_data).unwrap();
1145 let debug_str = format!("{:?}", image);
1146
1147 assert!(debug_str.contains("Image"));
1148 assert!(debug_str.contains("width"));
1149 assert!(debug_str.contains("height"));
1150 assert!(debug_str.contains("format"));
1151 }
1152
1153 #[test]
1154 fn test_jpeg_grayscale_image() {
1155 let jpeg_data = vec![
1156 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x64, 0x01, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD9, ];
1167
1168 let image = Image::from_jpeg_data(jpeg_data).unwrap();
1169 let pdf_obj = image.to_pdf_object();
1170
1171 if let Object::Stream(dict, _) = pdf_obj {
1172 assert_eq!(
1173 dict.get("ColorSpace").unwrap(),
1174 &Object::Name("DeviceGray".to_string())
1175 );
1176 } else {
1177 panic!("Expected Stream object");
1178 }
1179 }
1180
1181 #[test]
1182 fn test_jpeg_cmyk_image() {
1183 let jpeg_data = vec![
1184 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x64, 0x04, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
1196
1197 let image = Image::from_jpeg_data(jpeg_data).unwrap();
1198 let pdf_obj = image.to_pdf_object();
1199
1200 if let Object::Stream(dict, _) = pdf_obj {
1201 assert_eq!(
1202 dict.get("ColorSpace").unwrap(),
1203 &Object::Name("DeviceCMYK".to_string())
1204 );
1205 } else {
1206 panic!("Expected Stream object");
1207 }
1208 }
1209
1210 #[test]
1211 fn test_png_grayscale_image() {
1212 let png_data = vec![
1213 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x08, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
1225
1226 let image = Image::from_png_data(png_data).unwrap();
1227 let pdf_obj = image.to_pdf_object();
1228
1229 if let Object::Stream(dict, _) = pdf_obj {
1230 assert_eq!(
1231 dict.get("ColorSpace").unwrap(),
1232 &Object::Name("DeviceGray".to_string())
1233 );
1234 } else {
1235 panic!("Expected Stream object");
1236 }
1237 }
1238
1239 #[test]
1240 fn test_png_palette_image() {
1241 let png_data = vec![
1242 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x08, 0x03, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
1254
1255 let image = Image::from_png_data(png_data).unwrap();
1256 let pdf_obj = image.to_pdf_object();
1257
1258 if let Object::Stream(dict, _) = pdf_obj {
1259 assert_eq!(
1261 dict.get("ColorSpace").unwrap(),
1262 &Object::Name("DeviceRGB".to_string())
1263 );
1264 } else {
1265 panic!("Expected Stream object");
1266 }
1267 }
1268
1269 #[test]
1270 fn test_tiff_big_endian() {
1271 let tiff_data = vec![
1272 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80,
1278 0x01, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80,
1280 0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00,
1282 0x01, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
1284 0x00, 0x00, ];
1286
1287 let image = Image::from_tiff_data(tiff_data).unwrap();
1288
1289 assert_eq!(image.width(), 128);
1290 assert_eq!(image.height(), 128);
1291 assert_eq!(image.format(), ImageFormat::Tiff);
1292 }
1293
1294 #[test]
1295 fn test_tiff_cmyk_image() {
1296 let tiff_data = vec![
1297 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
1303 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
1305 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
1307 0x06, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
1309 0x00, 0x00, ];
1311
1312 let image = Image::from_tiff_data(tiff_data).unwrap();
1313 let pdf_obj = image.to_pdf_object();
1314
1315 if let Object::Stream(dict, _) = pdf_obj {
1316 assert_eq!(
1317 dict.get("ColorSpace").unwrap(),
1318 &Object::Name("DeviceCMYK".to_string())
1319 );
1320 } else {
1321 panic!("Expected Stream object");
1322 }
1323 }
1324
1325 #[test]
1326 fn test_error_invalid_jpeg() {
1327 let invalid_data = vec![0x00, 0x01, 0x02, 0x03]; let result = Image::from_jpeg_data(invalid_data);
1329 assert!(result.is_err());
1330 }
1331
1332 #[test]
1333 fn test_error_invalid_png() {
1334 let invalid_data = vec![0x00, 0x01, 0x02, 0x03]; let result = Image::from_png_data(invalid_data);
1336 assert!(result.is_err());
1337 }
1338
1339 #[test]
1340 fn test_error_invalid_tiff() {
1341 let invalid_data = vec![0x00, 0x01, 0x02, 0x03]; let result = Image::from_tiff_data(invalid_data);
1343 assert!(result.is_err());
1344 }
1345
1346 #[test]
1347 fn test_error_truncated_jpeg() {
1348 let truncated_data = vec![0xFF, 0xD8, 0xFF]; let result = Image::from_jpeg_data(truncated_data);
1350 assert!(result.is_err());
1351 }
1352
1353 #[test]
1354 fn test_error_truncated_png() {
1355 let truncated_data = vec![0x89, 0x50, 0x4E, 0x47]; let result = Image::from_png_data(truncated_data);
1357 assert!(result.is_err());
1358 }
1359
1360 #[test]
1361 fn test_error_truncated_tiff() {
1362 let truncated_data = vec![0x49, 0x49, 0x2A]; let result = Image::from_tiff_data(truncated_data);
1364 assert!(result.is_err());
1365 }
1366
1367 #[test]
1368 fn test_error_jpeg_unsupported_components() {
1369 let invalid_jpeg = vec![
1370 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x32, 0x00, 0x64, 0x05, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
1382
1383 let result = Image::from_jpeg_data(invalid_jpeg);
1384 assert!(result.is_err());
1385 }
1386
1387 #[test]
1388 fn test_error_png_unsupported_color_type() {
1389 let invalid_png = vec![
1390 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x08, 0x07, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
1402
1403 let result = Image::from_png_data(invalid_png);
1404 assert!(result.is_err());
1405 }
1406
1407 #[test]
1408 fn test_error_nonexistent_file() {
1409 let result = Image::from_jpeg_file("/nonexistent/path/image.jpg");
1410 assert!(result.is_err());
1411
1412 let result = Image::from_png_file("/nonexistent/path/image.png");
1413 assert!(result.is_err());
1414
1415 let result = Image::from_tiff_file("/nonexistent/path/image.tiff");
1416 assert!(result.is_err());
1417 }
1418
1419 #[test]
1420 fn test_jpeg_no_dimensions() {
1421 let jpeg_no_dims = vec![
1422 0xFF, 0xD8, 0xFF, 0xD9, ];
1425
1426 let result = Image::from_jpeg_data(jpeg_no_dims);
1427 assert!(result.is_err());
1428 }
1429
1430 #[test]
1431 fn test_png_no_ihdr() {
1432 let png_no_ihdr = vec![
1433 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x45, 0x4E, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C,
1437 0x72, 0x6E, 0x38, ];
1439
1440 let result = Image::from_png_data(png_no_ihdr);
1441 assert!(result.is_err());
1442 }
1443
1444 #[test]
1445 fn test_tiff_no_dimensions() {
1446 let tiff_no_dims = vec![
1447 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
1453 0x00, 0x00, ];
1455
1456 let result = Image::from_tiff_data(tiff_no_dims);
1457 assert!(result.is_err());
1458 }
1459
1460 #[test]
1461 fn test_different_bit_depths() {
1462 let png_16bit = vec![
1464 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x10, 0x02, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ];
1476
1477 let image = Image::from_png_data(png_16bit).unwrap();
1478 let pdf_obj = image.to_pdf_object();
1479
1480 if let Object::Stream(dict, _) = pdf_obj {
1481 assert_eq!(dict.get("BitsPerComponent").unwrap(), &Object::Integer(16));
1482 } else {
1483 panic!("Expected Stream object");
1484 }
1485 }
1486
1487 #[test]
1488 fn test_performance_large_image_data() {
1489 let mut large_jpeg = vec![
1491 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x04, 0x00, 0x04, 0x00, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, ];
1502
1503 large_jpeg.extend(vec![0x00; 10000]);
1505 large_jpeg.extend(vec![0xFF, 0xD9]); let start = std::time::Instant::now();
1508 let image = Image::from_jpeg_data(large_jpeg.clone()).unwrap();
1509 let duration = start.elapsed();
1510
1511 assert_eq!(image.width(), 1024);
1512 assert_eq!(image.height(), 1024);
1513 assert_eq!(image.data().len(), large_jpeg.len());
1514 assert!(duration.as_millis() < 100); }
1516
1517 #[test]
1518 fn test_memory_efficiency() {
1519 let jpeg_data = vec![
1520 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0xC8, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ];
1532
1533 let image = Image::from_jpeg_data(jpeg_data.clone()).unwrap();
1534
1535 assert_eq!(image.data().len(), jpeg_data.len());
1537 assert_eq!(image.data(), jpeg_data);
1538
1539 let cloned = image.clone();
1541 assert_eq!(cloned.data(), image.data());
1542 }
1543
1544 #[test]
1545 fn test_complete_workflow() {
1546 let test_cases = vec![
1548 (ImageFormat::Jpeg, "DCTDecode", "DeviceRGB"),
1549 (ImageFormat::Png, "FlateDecode", "DeviceRGB"),
1550 (ImageFormat::Tiff, "FlateDecode", "DeviceGray"),
1551 ];
1552
1553 for (expected_format, expected_filter, expected_color_space) in test_cases {
1554 let data = match expected_format {
1555 ImageFormat::Jpeg => vec![
1556 0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x64, 0x00, 0xC8, 0x03, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xD9, ],
1568 ImageFormat::Png => vec![
1569 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x64, 0x08, 0x02, 0x00, 0x00, 0x00, 0x5C, 0x72, 0x6E, 0x38, ],
1581 ImageFormat::Tiff => vec![
1582 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00,
1588 0x01, 0x01, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
1590 0x02, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
1592 0x00, 0x00, 0x00, 0x00, ],
1594 ImageFormat::Raw => Vec::new(), };
1596
1597 let image = match expected_format {
1598 ImageFormat::Jpeg => Image::from_jpeg_data(data.clone()).unwrap(),
1599 ImageFormat::Png => Image::from_png_data(data.clone()).unwrap(),
1600 ImageFormat::Tiff => Image::from_tiff_data(data.clone()).unwrap(),
1601 ImageFormat::Raw => continue, };
1603
1604 assert_eq!(image.format(), expected_format);
1606 assert_eq!(image.width(), 200);
1607 assert_eq!(image.height(), 100);
1608 assert_eq!(image.data(), data);
1609
1610 let pdf_obj = image.to_pdf_object();
1612 if let Object::Stream(dict, stream_data) = pdf_obj {
1613 assert_eq!(
1614 dict.get("Type").unwrap(),
1615 &Object::Name("XObject".to_string())
1616 );
1617 assert_eq!(
1618 dict.get("Subtype").unwrap(),
1619 &Object::Name("Image".to_string())
1620 );
1621 assert_eq!(dict.get("Width").unwrap(), &Object::Integer(200));
1622 assert_eq!(dict.get("Height").unwrap(), &Object::Integer(100));
1623 assert_eq!(
1624 dict.get("ColorSpace").unwrap(),
1625 &Object::Name(expected_color_space.to_string())
1626 );
1627 assert_eq!(
1628 dict.get("Filter").unwrap(),
1629 &Object::Name(expected_filter.to_string())
1630 );
1631 assert_eq!(dict.get("BitsPerComponent").unwrap(), &Object::Integer(8));
1632 assert_eq!(stream_data, data);
1633 } else {
1634 panic!("Expected Stream object for format {:?}", expected_format);
1635 }
1636 }
1637 }
1638 }
1639}