Skip to main content

sheetkit_core/workbook/
drawing.rs

1use super::*;
2
3impl Workbook {
4    /// Add a chart to a sheet, anchored between two cells.
5    ///
6    /// The chart spans from `from_cell` (e.g., `"B2"`) to `to_cell`
7    /// (e.g., `"J15"`). The `config` specifies the chart type, series data,
8    /// title, and legend visibility.
9    pub fn add_chart(
10        &mut self,
11        sheet: &str,
12        from_cell: &str,
13        to_cell: &str,
14        config: &ChartConfig,
15    ) -> Result<()> {
16        let sheet_idx =
17            crate::sheet::find_sheet_index(&self.worksheets, sheet).ok_or_else(|| {
18                Error::SheetNotFound {
19                    name: sheet.to_string(),
20                }
21            })?;
22
23        // Parse cell references to marker coordinates (0-based).
24        let (from_col, from_row) = cell_name_to_coordinates(from_cell)?;
25        let (to_col, to_row) = cell_name_to_coordinates(to_cell)?;
26
27        let from_marker = MarkerType {
28            col: from_col - 1,
29            col_off: 0,
30            row: from_row - 1,
31            row_off: 0,
32        };
33        let to_marker = MarkerType {
34            col: to_col - 1,
35            col_off: 0,
36            row: to_row - 1,
37            row_off: 0,
38        };
39
40        // Allocate chart part.
41        let chart_num = self.charts.len() + 1;
42        let chart_path = format!("xl/charts/chart{}.xml", chart_num);
43        let chart_space = crate::chart::build_chart_xml(config);
44        self.charts.push((chart_path, chart_space));
45
46        // Get or create drawing for this sheet.
47        let drawing_idx = self.ensure_drawing_for_sheet(sheet_idx);
48
49        // Add chart reference to the drawing's relationships.
50        let chart_rid = self.next_drawing_rid(drawing_idx);
51        let chart_rel_target = format!("../charts/chart{}.xml", chart_num);
52
53        let dr_rels = self
54            .drawing_rels
55            .entry(drawing_idx)
56            .or_insert_with(|| Relationships {
57                xmlns: sheetkit_xml::namespaces::PACKAGE_RELATIONSHIPS.to_string(),
58                relationships: vec![],
59            });
60        dr_rels.relationships.push(Relationship {
61            id: chart_rid.clone(),
62            rel_type: rel_types::CHART.to_string(),
63            target: chart_rel_target,
64            target_mode: None,
65        });
66
67        // Build the chart anchor and add it to the drawing.
68        let drawing = &mut self.drawings[drawing_idx].1;
69        let anchor = crate::chart::build_drawing_with_chart(&chart_rid, from_marker, to_marker);
70        drawing.two_cell_anchors.extend(anchor.two_cell_anchors);
71
72        // Add content type for the chart.
73        self.content_types.overrides.push(ContentTypeOverride {
74            part_name: format!("/xl/charts/chart{}.xml", chart_num),
75            content_type: mime_types::CHART.to_string(),
76        });
77
78        Ok(())
79    }
80
81    /// Add an image to a sheet from bytes.
82    ///
83    /// The image is anchored to the cell specified in `config.from_cell`.
84    /// Dimensions are specified in pixels via `config.width_px` and
85    /// `config.height_px`.
86    pub fn add_image(&mut self, sheet: &str, config: &ImageConfig) -> Result<()> {
87        crate::image::validate_image_config(config)?;
88
89        let sheet_idx =
90            crate::sheet::find_sheet_index(&self.worksheets, sheet).ok_or_else(|| {
91                Error::SheetNotFound {
92                    name: sheet.to_string(),
93                }
94            })?;
95
96        // Allocate image media part.
97        let image_num = self.images.len() + 1;
98        let image_path = format!("xl/media/image{}.{}", image_num, config.format.extension());
99        self.images.push((image_path, config.data.clone()));
100
101        // Ensure the image extension has a default content type.
102        let ext = config.format.extension().to_string();
103        if !self
104            .content_types
105            .defaults
106            .iter()
107            .any(|d| d.extension == ext)
108        {
109            self.content_types.defaults.push(ContentTypeDefault {
110                extension: ext,
111                content_type: config.format.content_type().to_string(),
112            });
113        }
114
115        // Get or create drawing for this sheet.
116        let drawing_idx = self.ensure_drawing_for_sheet(sheet_idx);
117
118        // Add image reference to the drawing's relationships.
119        let image_rid = self.next_drawing_rid(drawing_idx);
120        let image_rel_target = format!("../media/image{}.{}", image_num, config.format.extension());
121
122        let dr_rels = self
123            .drawing_rels
124            .entry(drawing_idx)
125            .or_insert_with(|| Relationships {
126                xmlns: sheetkit_xml::namespaces::PACKAGE_RELATIONSHIPS.to_string(),
127                relationships: vec![],
128            });
129        dr_rels.relationships.push(Relationship {
130            id: image_rid.clone(),
131            rel_type: rel_types::IMAGE.to_string(),
132            target: image_rel_target,
133            target_mode: None,
134        });
135
136        // Count existing objects in the drawing to assign a unique ID.
137        let drawing = &mut self.drawings[drawing_idx].1;
138        let pic_id = (drawing.one_cell_anchors.len() + drawing.two_cell_anchors.len() + 2) as u32;
139
140        // Add image anchor to the drawing.
141        crate::image::add_image_to_drawing(drawing, &image_rid, config, pic_id)?;
142
143        Ok(())
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use tempfile::TempDir;
151
152    #[test]
153    fn test_add_chart_basic() {
154        use crate::chart::{ChartConfig, ChartSeries, ChartType};
155        let mut wb = Workbook::new();
156        let config = ChartConfig {
157            chart_type: ChartType::Col,
158            title: Some("Test Chart".to_string()),
159            series: vec![ChartSeries {
160                name: "Sales".to_string(),
161                categories: "Sheet1!$A$1:$A$5".to_string(),
162                values: "Sheet1!$B$1:$B$5".to_string(),
163                x_values: None,
164                bubble_sizes: None,
165            }],
166            show_legend: true,
167            view_3d: None,
168        };
169        wb.add_chart("Sheet1", "E1", "L15", &config).unwrap();
170
171        assert_eq!(wb.charts.len(), 1);
172        assert_eq!(wb.drawings.len(), 1);
173        assert!(wb.worksheet_drawings.contains_key(&0));
174        assert!(wb.drawing_rels.contains_key(&0));
175        assert!(wb.worksheets[0].1.drawing.is_some());
176    }
177
178    #[test]
179    fn test_add_chart_sheet_not_found() {
180        use crate::chart::{ChartConfig, ChartSeries, ChartType};
181        let mut wb = Workbook::new();
182        let config = ChartConfig {
183            chart_type: ChartType::Line,
184            title: None,
185            series: vec![ChartSeries {
186                name: String::new(),
187                categories: "Sheet1!$A$1:$A$5".to_string(),
188                values: "Sheet1!$B$1:$B$5".to_string(),
189                x_values: None,
190                bubble_sizes: None,
191            }],
192            show_legend: false,
193            view_3d: None,
194        };
195        let result = wb.add_chart("NoSheet", "A1", "H10", &config);
196        assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
197    }
198
199    #[test]
200    fn test_add_multiple_charts_same_sheet() {
201        use crate::chart::{ChartConfig, ChartSeries, ChartType};
202        let mut wb = Workbook::new();
203        let config1 = ChartConfig {
204            chart_type: ChartType::Col,
205            title: Some("Chart 1".to_string()),
206            series: vec![ChartSeries {
207                name: "S1".to_string(),
208                categories: "Sheet1!$A$1:$A$3".to_string(),
209                values: "Sheet1!$B$1:$B$3".to_string(),
210                x_values: None,
211                bubble_sizes: None,
212            }],
213            show_legend: true,
214            view_3d: None,
215        };
216        let config2 = ChartConfig {
217            chart_type: ChartType::Line,
218            title: Some("Chart 2".to_string()),
219            series: vec![ChartSeries {
220                name: "S2".to_string(),
221                categories: "Sheet1!$A$1:$A$3".to_string(),
222                values: "Sheet1!$C$1:$C$3".to_string(),
223                x_values: None,
224                bubble_sizes: None,
225            }],
226            show_legend: false,
227            view_3d: None,
228        };
229        wb.add_chart("Sheet1", "A1", "F10", &config1).unwrap();
230        wb.add_chart("Sheet1", "A12", "F22", &config2).unwrap();
231
232        assert_eq!(wb.charts.len(), 2);
233        assert_eq!(wb.drawings.len(), 1);
234        assert_eq!(wb.drawings[0].1.two_cell_anchors.len(), 2);
235    }
236
237    #[test]
238    fn test_add_charts_different_sheets() {
239        use crate::chart::{ChartConfig, ChartSeries, ChartType};
240        let mut wb = Workbook::new();
241        wb.new_sheet("Sheet2").unwrap();
242
243        let config = ChartConfig {
244            chart_type: ChartType::Pie,
245            title: None,
246            series: vec![ChartSeries {
247                name: String::new(),
248                categories: "Sheet1!$A$1:$A$3".to_string(),
249                values: "Sheet1!$B$1:$B$3".to_string(),
250                x_values: None,
251                bubble_sizes: None,
252            }],
253            show_legend: true,
254            view_3d: None,
255        };
256        wb.add_chart("Sheet1", "A1", "F10", &config).unwrap();
257        wb.add_chart("Sheet2", "A1", "F10", &config).unwrap();
258
259        assert_eq!(wb.charts.len(), 2);
260        assert_eq!(wb.drawings.len(), 2);
261    }
262
263    #[test]
264    fn test_save_with_chart() {
265        use crate::chart::{ChartConfig, ChartSeries, ChartType};
266        let dir = TempDir::new().unwrap();
267        let path = dir.path().join("with_chart.xlsx");
268
269        let mut wb = Workbook::new();
270        let config = ChartConfig {
271            chart_type: ChartType::Bar,
272            title: Some("Bar Chart".to_string()),
273            series: vec![ChartSeries {
274                name: "Data".to_string(),
275                categories: "Sheet1!$A$1:$A$3".to_string(),
276                values: "Sheet1!$B$1:$B$3".to_string(),
277                x_values: None,
278                bubble_sizes: None,
279            }],
280            show_legend: true,
281            view_3d: None,
282        };
283        wb.add_chart("Sheet1", "E2", "L15", &config).unwrap();
284        wb.save(&path).unwrap();
285
286        let file = std::fs::File::open(&path).unwrap();
287        let mut archive = zip::ZipArchive::new(file).unwrap();
288
289        assert!(archive.by_name("xl/charts/chart1.xml").is_ok());
290        assert!(archive.by_name("xl/drawings/drawing1.xml").is_ok());
291        assert!(archive
292            .by_name("xl/worksheets/_rels/sheet1.xml.rels")
293            .is_ok());
294        assert!(archive
295            .by_name("xl/drawings/_rels/drawing1.xml.rels")
296            .is_ok());
297    }
298
299    #[test]
300    fn test_add_image_basic() {
301        use crate::image::{ImageConfig, ImageFormat};
302        let mut wb = Workbook::new();
303        let config = ImageConfig {
304            data: vec![0x89, 0x50, 0x4E, 0x47],
305            format: ImageFormat::Png,
306            from_cell: "B2".to_string(),
307            width_px: 400,
308            height_px: 300,
309        };
310        wb.add_image("Sheet1", &config).unwrap();
311
312        assert_eq!(wb.images.len(), 1);
313        assert_eq!(wb.drawings.len(), 1);
314        assert_eq!(wb.drawings[0].1.one_cell_anchors.len(), 1);
315        assert!(wb.worksheet_drawings.contains_key(&0));
316    }
317
318    #[test]
319    fn test_add_image_sheet_not_found() {
320        use crate::image::{ImageConfig, ImageFormat};
321        let mut wb = Workbook::new();
322        let config = ImageConfig {
323            data: vec![0x89],
324            format: ImageFormat::Png,
325            from_cell: "A1".to_string(),
326            width_px: 100,
327            height_px: 100,
328        };
329        let result = wb.add_image("NoSheet", &config);
330        assert!(matches!(result.unwrap_err(), Error::SheetNotFound { .. }));
331    }
332
333    #[test]
334    fn test_add_image_invalid_config() {
335        use crate::image::{ImageConfig, ImageFormat};
336        let mut wb = Workbook::new();
337        let config = ImageConfig {
338            data: vec![],
339            format: ImageFormat::Png,
340            from_cell: "A1".to_string(),
341            width_px: 100,
342            height_px: 100,
343        };
344        assert!(wb.add_image("Sheet1", &config).is_err());
345
346        let config = ImageConfig {
347            data: vec![1],
348            format: ImageFormat::Jpeg,
349            from_cell: "A1".to_string(),
350            width_px: 0,
351            height_px: 100,
352        };
353        assert!(wb.add_image("Sheet1", &config).is_err());
354    }
355
356    #[test]
357    fn test_save_with_image() {
358        use crate::image::{ImageConfig, ImageFormat};
359        let dir = TempDir::new().unwrap();
360        let path = dir.path().join("with_image.xlsx");
361
362        let mut wb = Workbook::new();
363        let config = ImageConfig {
364            data: vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A],
365            format: ImageFormat::Png,
366            from_cell: "C3".to_string(),
367            width_px: 200,
368            height_px: 150,
369        };
370        wb.add_image("Sheet1", &config).unwrap();
371        wb.save(&path).unwrap();
372
373        let file = std::fs::File::open(&path).unwrap();
374        let mut archive = zip::ZipArchive::new(file).unwrap();
375
376        assert!(archive.by_name("xl/media/image1.png").is_ok());
377        assert!(archive.by_name("xl/drawings/drawing1.xml").is_ok());
378        assert!(archive
379            .by_name("xl/worksheets/_rels/sheet1.xml.rels")
380            .is_ok());
381        assert!(archive
382            .by_name("xl/drawings/_rels/drawing1.xml.rels")
383            .is_ok());
384    }
385
386    #[test]
387    fn test_save_with_jpeg_image() {
388        use crate::image::{ImageConfig, ImageFormat};
389        let dir = TempDir::new().unwrap();
390        let path = dir.path().join("with_jpeg.xlsx");
391
392        let mut wb = Workbook::new();
393        let config = ImageConfig {
394            data: vec![0xFF, 0xD8, 0xFF, 0xE0],
395            format: ImageFormat::Jpeg,
396            from_cell: "A1".to_string(),
397            width_px: 640,
398            height_px: 480,
399        };
400        wb.add_image("Sheet1", &config).unwrap();
401        wb.save(&path).unwrap();
402
403        let file = std::fs::File::open(&path).unwrap();
404        let mut archive = zip::ZipArchive::new(file).unwrap();
405        assert!(archive.by_name("xl/media/image1.jpeg").is_ok());
406    }
407
408    #[test]
409    fn test_save_with_new_image_formats() {
410        use crate::image::{ImageConfig, ImageFormat};
411
412        let formats = [
413            (ImageFormat::Bmp, "bmp"),
414            (ImageFormat::Ico, "ico"),
415            (ImageFormat::Tiff, "tiff"),
416            (ImageFormat::Svg, "svg"),
417            (ImageFormat::Emf, "emf"),
418            (ImageFormat::Emz, "emz"),
419            (ImageFormat::Wmf, "wmf"),
420            (ImageFormat::Wmz, "wmz"),
421        ];
422
423        for (format, ext) in &formats {
424            let dir = TempDir::new().unwrap();
425            let path = dir.path().join(format!("with_{ext}.xlsx"));
426
427            let mut wb = Workbook::new();
428            let config = ImageConfig {
429                data: vec![0x00, 0x01, 0x02, 0x03],
430                format: format.clone(),
431                from_cell: "A1".to_string(),
432                width_px: 100,
433                height_px: 100,
434            };
435            wb.add_image("Sheet1", &config).unwrap();
436            wb.save(&path).unwrap();
437
438            let file = std::fs::File::open(&path).unwrap();
439            let mut archive = zip::ZipArchive::new(file).unwrap();
440            let media_path = format!("xl/media/image1.{ext}");
441            assert!(
442                archive.by_name(&media_path).is_ok(),
443                "expected {media_path} in archive for format {ext}"
444            );
445        }
446    }
447
448    #[test]
449    fn test_add_image_new_format_content_type_default() {
450        use crate::image::{ImageConfig, ImageFormat};
451        let mut wb = Workbook::new();
452        let config = ImageConfig {
453            data: vec![0x42, 0x4D],
454            format: ImageFormat::Bmp,
455            from_cell: "A1".to_string(),
456            width_px: 100,
457            height_px: 100,
458        };
459        wb.add_image("Sheet1", &config).unwrap();
460
461        let has_bmp_default = wb
462            .content_types
463            .defaults
464            .iter()
465            .any(|d| d.extension == "bmp" && d.content_type == "image/bmp");
466        assert!(has_bmp_default, "content types should have bmp default");
467    }
468
469    #[test]
470    fn test_add_image_svg_content_type_default() {
471        use crate::image::{ImageConfig, ImageFormat};
472        let mut wb = Workbook::new();
473        let config = ImageConfig {
474            data: vec![0x3C, 0x73, 0x76, 0x67],
475            format: ImageFormat::Svg,
476            from_cell: "B3".to_string(),
477            width_px: 200,
478            height_px: 200,
479        };
480        wb.add_image("Sheet1", &config).unwrap();
481
482        let has_svg_default = wb
483            .content_types
484            .defaults
485            .iter()
486            .any(|d| d.extension == "svg" && d.content_type == "image/svg+xml");
487        assert!(has_svg_default, "content types should have svg default");
488    }
489
490    #[test]
491    fn test_add_image_emf_content_type_and_path() {
492        use crate::image::{ImageConfig, ImageFormat};
493        let dir = TempDir::new().unwrap();
494        let path = dir.path().join("with_emf.xlsx");
495
496        let mut wb = Workbook::new();
497        let config = ImageConfig {
498            data: vec![0x01, 0x00, 0x00, 0x00],
499            format: ImageFormat::Emf,
500            from_cell: "A1".to_string(),
501            width_px: 150,
502            height_px: 150,
503        };
504        wb.add_image("Sheet1", &config).unwrap();
505
506        let has_emf_default = wb
507            .content_types
508            .defaults
509            .iter()
510            .any(|d| d.extension == "emf" && d.content_type == "image/x-emf");
511        assert!(has_emf_default);
512
513        wb.save(&path).unwrap();
514        let file = std::fs::File::open(&path).unwrap();
515        let mut archive = zip::ZipArchive::new(file).unwrap();
516        assert!(archive.by_name("xl/media/image1.emf").is_ok());
517    }
518
519    #[test]
520    fn test_add_multiple_new_format_images() {
521        use crate::image::{ImageConfig, ImageFormat};
522        let mut wb = Workbook::new();
523
524        wb.add_image(
525            "Sheet1",
526            &ImageConfig {
527                data: vec![0x42, 0x4D],
528                format: ImageFormat::Bmp,
529                from_cell: "A1".to_string(),
530                width_px: 100,
531                height_px: 100,
532            },
533        )
534        .unwrap();
535
536        wb.add_image(
537            "Sheet1",
538            &ImageConfig {
539                data: vec![0x3C, 0x73],
540                format: ImageFormat::Svg,
541                from_cell: "C1".to_string(),
542                width_px: 100,
543                height_px: 100,
544            },
545        )
546        .unwrap();
547
548        wb.add_image(
549            "Sheet1",
550            &ImageConfig {
551                data: vec![0x01, 0x00],
552                format: ImageFormat::Wmf,
553                from_cell: "E1".to_string(),
554                width_px: 100,
555                height_px: 100,
556            },
557        )
558        .unwrap();
559
560        assert_eq!(wb.images.len(), 3);
561        assert_eq!(wb.drawings[0].1.one_cell_anchors.len(), 3);
562
563        let ext_defaults: Vec<&str> = wb
564            .content_types
565            .defaults
566            .iter()
567            .map(|d| d.extension.as_str())
568            .collect();
569        assert!(ext_defaults.contains(&"bmp"));
570        assert!(ext_defaults.contains(&"svg"));
571        assert!(ext_defaults.contains(&"wmf"));
572    }
573
574    #[test]
575    fn test_add_chart_and_image_same_sheet() {
576        use crate::chart::{ChartConfig, ChartSeries, ChartType};
577        use crate::image::{ImageConfig, ImageFormat};
578
579        let mut wb = Workbook::new();
580
581        let chart_config = ChartConfig {
582            chart_type: ChartType::Col,
583            title: Some("My Chart".to_string()),
584            series: vec![ChartSeries {
585                name: "Series 1".to_string(),
586                categories: "Sheet1!$A$1:$A$3".to_string(),
587                values: "Sheet1!$B$1:$B$3".to_string(),
588                x_values: None,
589                bubble_sizes: None,
590            }],
591            show_legend: true,
592            view_3d: None,
593        };
594        wb.add_chart("Sheet1", "E1", "L10", &chart_config).unwrap();
595
596        let image_config = ImageConfig {
597            data: vec![0x89, 0x50, 0x4E, 0x47],
598            format: ImageFormat::Png,
599            from_cell: "E12".to_string(),
600            width_px: 300,
601            height_px: 200,
602        };
603        wb.add_image("Sheet1", &image_config).unwrap();
604
605        assert_eq!(wb.drawings.len(), 1);
606        assert_eq!(wb.drawings[0].1.two_cell_anchors.len(), 1);
607        assert_eq!(wb.drawings[0].1.one_cell_anchors.len(), 1);
608        assert_eq!(wb.charts.len(), 1);
609        assert_eq!(wb.images.len(), 1);
610    }
611
612    #[test]
613    fn test_save_with_chart_roundtrip_drawing_ref() {
614        use crate::chart::{ChartConfig, ChartSeries, ChartType};
615        let dir = TempDir::new().unwrap();
616        let path = dir.path().join("chart_drawref.xlsx");
617
618        let mut wb = Workbook::new();
619        let config = ChartConfig {
620            chart_type: ChartType::Col,
621            title: None,
622            series: vec![ChartSeries {
623                name: "Series 1".to_string(),
624                categories: "Sheet1!$A$1:$A$3".to_string(),
625                values: "Sheet1!$B$1:$B$3".to_string(),
626                x_values: None,
627                bubble_sizes: None,
628            }],
629            show_legend: false,
630            view_3d: None,
631        };
632        wb.add_chart("Sheet1", "A1", "F10", &config).unwrap();
633        wb.save(&path).unwrap();
634
635        let wb2 = Workbook::open(&path).unwrap();
636        let ws = wb2.worksheet_ref("Sheet1").unwrap();
637        assert!(ws.drawing.is_some());
638    }
639
640    #[test]
641    fn test_open_save_preserves_existing_drawing_chart_and_image_parts() {
642        use crate::chart::{ChartConfig, ChartSeries, ChartType};
643        use crate::image::{ImageConfig, ImageFormat};
644        let dir = TempDir::new().unwrap();
645        let path1 = dir.path().join("source_with_parts.xlsx");
646        let path2 = dir.path().join("resaved_with_parts.xlsx");
647
648        let mut wb = Workbook::new();
649        wb.add_chart(
650            "Sheet1",
651            "E1",
652            "L10",
653            &ChartConfig {
654                chart_type: ChartType::Col,
655                title: Some("Chart".to_string()),
656                series: vec![ChartSeries {
657                    name: "Series 1".to_string(),
658                    categories: "Sheet1!$A$1:$A$3".to_string(),
659                    values: "Sheet1!$B$1:$B$3".to_string(),
660                    x_values: None,
661                    bubble_sizes: None,
662                }],
663                show_legend: true,
664                view_3d: None,
665            },
666        )
667        .unwrap();
668        wb.add_image(
669            "Sheet1",
670            &ImageConfig {
671                data: vec![0x89, 0x50, 0x4E, 0x47],
672                format: ImageFormat::Png,
673                from_cell: "E12".to_string(),
674                width_px: 120,
675                height_px: 80,
676            },
677        )
678        .unwrap();
679        wb.save(&path1).unwrap();
680
681        let wb2 = Workbook::open(&path1).unwrap();
682        assert_eq!(wb2.charts.len() + wb2.raw_charts.len(), 1);
683        assert_eq!(wb2.drawings.len(), 1);
684        assert_eq!(wb2.images.len(), 1);
685        assert_eq!(wb2.drawing_rels.len(), 1);
686        assert_eq!(wb2.worksheet_drawings.len(), 1);
687
688        wb2.save(&path2).unwrap();
689
690        let file = std::fs::File::open(&path2).unwrap();
691        let mut archive = zip::ZipArchive::new(file).unwrap();
692        assert!(archive.by_name("xl/charts/chart1.xml").is_ok());
693        assert!(archive.by_name("xl/drawings/drawing1.xml").is_ok());
694        assert!(archive.by_name("xl/media/image1.png").is_ok());
695        assert!(archive
696            .by_name("xl/worksheets/_rels/sheet1.xml.rels")
697            .is_ok());
698        assert!(archive
699            .by_name("xl/drawings/_rels/drawing1.xml.rels")
700            .is_ok());
701    }
702}