dezoomify_rs/pff/
mod.rs

1use std::sync::Arc;
2
3use custom_error::custom_error;
4/// Dezoomer for the zoomify PFF servlet API format
5/// See: https://github.com/lovasoa/pff-extract/wiki/Zoomify-PFF-file-format-documentation
6
7use 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
17/// Dezoomer for Zoomify PFF.
18/// Takes an URL to a pff file
19pub 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}