1use std::sync::Arc;
2
3use custom_error::custom_error;
4use serde_urlencoded as urlencoded;
8
9use image_properties::PffHeader;
10use image_properties::Reply;
11
12use crate::dezoomer::*;
13use crate::pff::image_properties::{HeaderInfo, ImageInfo, InitialServletRequestParams, RequestType, TileIndices};
14
15mod image_properties;
16
17pub enum PFF {
20 Init,
21 WithHeader(HeaderInfo),
22}
23
24impl Default for PFF { fn default() -> Self { PFF::Init } }
25
26custom_error! {pub PffError
27 DecodeError{source: serde_urlencoded::de::Error} = "Invalid meta information file: {source}",
28 EncodeError{source: serde_urlencoded::ser::Error} = "Unable to generate URL: {source}",
29}
30
31impl From<PffError> for DezoomerError {
32 fn from(err: PffError) -> Self {
33 DezoomerError::Other { source: err.into() }
34 }
35}
36
37impl Dezoomer for PFF {
38 fn name(&self) -> &'static str {
39 "pff"
40 }
41
42 fn zoom_levels(&mut self, data: &DezoomerInput) -> Result<ZoomLevels, DezoomerError> {
43 let mut parts = data.uri.splitn(2, '?');
44 let base_url = parts.next().ok_or_else(|| self.wrong_dezoomer())?.to_string();
45 let params_str = parts.next().ok_or_else(|| self.wrong_dezoomer())?;
46 match self {
47 PFF::Init => {
48 let init_params: InitialServletRequestParams =
49 urlencoded::from_str(params_str).map_err(PffError::from)?;
50 let file = init_params.file;
51 if init_params.request_type != RequestType::Metadata as u8 {
52 let uri = format!("{}?file={}&requestType={}", base_url, file, RequestType::Metadata as u8);
53 return Err(DezoomerError::NeedsData { uri });
54 }
55 let DezoomerInputWithContents { contents, .. } = data.with_contents()?;
56 let reply: Reply<PffHeader> =
57 serde_urlencoded::from_bytes(contents).map_err(PffError::from)?;
58 let header_info = HeaderInfo { base_url, file, header: reply.reply_data };
59 let uri = header_info.tiles_index_url();
60 *self = PFF::WithHeader(header_info);
61 Err(DezoomerError::NeedsData { uri })
62 },
63 PFF::WithHeader(header_info) => {
64 let DezoomerInputWithContents { contents, .. } = data.with_contents()?;
65 let reply: Reply<TileIndices> =
66 urlencoded::from_bytes(contents).map_err(PffError::from)?;
67 Ok(zoom_levels(ImageInfo {
68 header_info: header_info.clone(),
69 tiles: reply.reply_data,
70 }))
71 }
72 }
73 }
74}
75
76fn zoom_levels(info: ImageInfo) -> ZoomLevels {
77 let info = Arc::new(info);
78 let header = &info.header_info.header;
79 let mut size = Vec2d { x: header.width, y: header.height };
80 let mut tiles_before = 0;
81 let mut levels = vec![];
82 while size.x >= header.tile_size && size.y >= header.tile_size {
83 let level = PffZoomLevel {
84 image_info: Arc::clone(&info),
85 tiles_before,
86 size,
87 };
88 tiles_before += level.tile_count();
89 size = size.ceil_div(Vec2d { x: 2, y: 2 });
90 levels.push(Box::new(level) as ZoomLevel);
91 }
92 levels
93}
94
95struct PffZoomLevel {
96 image_info: Arc<ImageInfo>,
97 tiles_before: u32,
98 size: Vec2d,
99}
100
101impl TilesRect for PffZoomLevel {
102 fn size(&self) -> Vec2d { self.size }
103
104 fn tile_size(&self) -> Vec2d {
105 let size = self.image_info.header_info.header.tile_size;
106 Vec2d { x: size, y: size }
107 }
108
109 fn tile_url(&self, pos: Vec2d) -> String {
110 let num_tiles_x = (self.size().ceil_div(self.tile_size())).x;
111 let i = self.tiles_before + pos.x + pos.y * num_tiles_x;
112 self.image_info.tile_url(i as usize)
113 }
114}
115
116impl std::fmt::Debug for PffZoomLevel {
117 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
118 f.write_str("Zoomify PFF")
119 }
120}