1use super::base::{ContentType, Part, PartType};
6use crate::exc::PptxError;
7use std::path::Path;
8
9#[derive(Debug, Clone)]
11pub struct ImagePart {
12 path: String,
13 image_number: usize,
14 format: String,
15 data: Vec<u8>,
16 width: u32,
17 height: u32,
18}
19
20impl ImagePart {
21 pub fn new(image_number: usize, format: &str, data: Vec<u8>) -> Self {
23 let format_lower = format.to_lowercase();
24 let ext = match format_lower.as_str() {
25 "jpeg" => "jpg",
26 other => other,
27 };
28
29 ImagePart {
30 path: format!("ppt/media/image{}.{}", image_number, ext),
31 image_number,
32 format: format_lower,
33 data,
34 width: 0,
35 height: 0,
36 }
37 }
38
39 pub fn from_file(image_number: usize, file_path: &str) -> Result<Self, PptxError> {
41 let data = std::fs::read(file_path)?;
42 let format = Path::new(file_path)
43 .extension()
44 .and_then(|e| e.to_str())
45 .unwrap_or("png")
46 .to_lowercase();
47
48 Ok(Self::new(image_number, &format, data))
49 }
50
51 pub fn image_number(&self) -> usize {
53 self.image_number
54 }
55
56 pub fn format(&self) -> &str {
58 &self.format
59 }
60
61 pub fn data(&self) -> &[u8] {
63 &self.data
64 }
65
66 pub fn set_dimensions(&mut self, width: u32, height: u32) {
68 self.width = width;
69 self.height = height;
70 }
71
72 pub fn width(&self) -> u32 {
74 self.width
75 }
76
77 pub fn height(&self) -> u32 {
79 self.height
80 }
81
82 pub fn mime_type(&self) -> &'static str {
84 match self.format.as_str() {
85 "png" => "image/png",
86 "jpg" | "jpeg" => "image/jpeg",
87 "gif" => "image/gif",
88 "bmp" => "image/bmp",
89 "tiff" | "tif" => "image/tiff",
90 _ => "application/octet-stream",
91 }
92 }
93
94 pub fn extension(&self) -> &str {
96 match self.format.as_str() {
97 "jpeg" => "jpg",
98 other => other,
99 }
100 }
101
102 pub fn rel_target(&self) -> String {
104 format!("../media/image{}.{}", self.image_number, self.extension())
105 }
106}
107
108impl Part for ImagePart {
109 fn path(&self) -> &str {
110 &self.path
111 }
112
113 fn part_type(&self) -> PartType {
114 PartType::Image
115 }
116
117 fn content_type(&self) -> ContentType {
118 ContentType::Image(self.format.clone())
119 }
120
121 fn to_xml(&self) -> Result<String, PptxError> {
122 Err(PptxError::InvalidOperation(
124 "Images don't have XML content".to_string(),
125 ))
126 }
127
128 fn from_xml(_xml: &str) -> Result<Self, PptxError> {
129 Err(PptxError::InvalidOperation(
130 "Cannot create image from XML".to_string(),
131 ))
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_image_part_new() {
141 let data = vec![0x89, 0x50, 0x4E, 0x47]; let part = ImagePart::new(1, "png", data);
143
144 assert_eq!(part.image_number(), 1);
145 assert_eq!(part.format(), "png");
146 assert_eq!(part.path(), "ppt/media/image1.png");
147 }
148
149 #[test]
150 fn test_image_mime_type() {
151 let part = ImagePart::new(1, "jpeg", vec![]);
152 assert_eq!(part.mime_type(), "image/jpeg");
153
154 let part2 = ImagePart::new(2, "png", vec![]);
155 assert_eq!(part2.mime_type(), "image/png");
156 }
157
158 #[test]
159 fn test_image_rel_target() {
160 let part = ImagePart::new(3, "png", vec![]);
161 assert_eq!(part.rel_target(), "../media/image3.png");
162 }
163}