dezoomify_rs/zoomify/
mod.rs1use std::sync::Arc;
2
3use custom_error::custom_error;
4use image_properties::{ImageProperties, ZoomLevelInfo};
5
6use crate::dezoomer::*;
7
8mod image_properties;
9
10#[derive(Default)]
13pub struct ZoomifyDezoomer;
14
15impl Dezoomer for ZoomifyDezoomer {
16 fn name(&self) -> &'static str {
17 "zoomify"
18 }
19
20 fn zoom_levels(&mut self, data: &DezoomerInput) -> Result<ZoomLevels, DezoomerError> {
21 self.assert(data.uri.contains("/ImageProperties.xml"))?;
22 let DezoomerInputWithContents { uri, contents } = data.with_contents()?;
23 let levels = load_from_properties(uri, contents)?;
24 Ok(levels)
25 }
26}
27
28custom_error! {pub ZoomifyError
29 XmlError{source: serde_xml_rs::Error} = "Unable to parse ImageProperties.xml: {source}"
30}
31
32impl From<ZoomifyError> for DezoomerError {
33 fn from(err: ZoomifyError) -> Self {
34 DezoomerError::Other { source: err.into() }
35 }
36}
37
38fn load_from_properties(url: &str, contents: &[u8]) -> Result<ZoomLevels, ZoomifyError> {
39 let image_properties: ImageProperties = serde_xml_rs::from_reader(contents)?;
40 let base_url_string = url.split("/ImageProperties.xml").next().unwrap().to_string();
41 let base_url = &Arc::from(base_url_string);
42 let levels: Vec<ZoomLevelInfo> = image_properties.levels();
43 let levels: ZoomLevels = levels.into_iter().enumerate()
44 .map(move |(level, level_info)| ZoomifyLevel {
45 base_url: Arc::clone(base_url),
46 level_info,
47 level,
48 })
49 .into_zoom_levels();
50 Ok(levels)
51}
52
53struct ZoomifyLevel {
54 base_url: Arc<str>,
55 level_info: ZoomLevelInfo,
56 level: usize,
57}
58
59impl TilesRect for ZoomifyLevel {
60 fn size(&self) -> Vec2d {
61 self.level_info.size
62 }
63
64 fn tile_size(&self) -> Vec2d {
65 self.level_info.tile_size
66 }
67
68 fn tile_url(&self, pos: Vec2d) -> String {
69 format!(
70 "{base}/TileGroup{group}/{z}-{x}-{y}.jpg",
71 base = self.base_url,
72 group = self.level_info.tile_group(pos),
73 x = pos.x,
74 y = pos.y,
75 z = self.level
76 )
77 }
78}
79
80impl std::fmt::Debug for ZoomifyLevel {
81 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
82 write!(f, "Zoomify Image")
83 }
84}
85
86#[test]
87fn test_panorama() {
88 let url = "http://x.fr/y/ImageProperties.xml?t";
89 let contents = br#"
90 <IMAGE_PROPERTIES
91 WIDTH="174550" HEIGHT="16991" NUMTILES="61284"
92 NUMIMAGES="1" VERSION="1.8" TILESIZE="256"/>"#;
93 let mut props = load_from_properties(url, contents).unwrap();
94 assert_eq!(props.len(), 11);
95 let level = &mut props[3];
96 let tiles: Vec<String> = level.next_tiles(None).into_iter().map(|t| t.url).collect();
97 assert_eq!(
98 tiles,
99 vec![
100 "http://x.fr/y/TileGroup0/3-0-0.jpg",
101 "http://x.fr/y/TileGroup0/3-1-0.jpg",
102 "http://x.fr/y/TileGroup0/3-2-0.jpg",
103 "http://x.fr/y/TileGroup0/3-3-0.jpg",
104 "http://x.fr/y/TileGroup0/3-4-0.jpg",
105 "http://x.fr/y/TileGroup0/3-5-0.jpg"
106 ]
107 );
108}
109
110#[test]
111fn test_tilegroups() {
112 use std::collections::HashSet;
113 let url = "http://x.fr/y/ImageProperties.xml?t";
114 let contents = br#"<IMAGE_PROPERTIES WIDTH="12000" HEIGHT="9788"
115 NUMTILES="2477" NUMIMAGES="1" VERSION="1.8" TILESIZE="256"/>"#;
116 let mut props = load_from_properties(url, contents).unwrap();
117 let level = &mut props[5];
118 let tiles: HashSet<String> = level.next_tiles(None).into_iter().map(|t| t.url).collect();
119 assert!(tiles.contains("http://x.fr/y/TileGroup1/5-0-14.jpg"));
120 assert!(tiles.contains("http://x.fr/y/TileGroup2/5-0-15.jpg"));
121}