dezoomify-rs 2.9.4

Allows downloading zoomable images. Supports several different formats such as zoomify, iiif, and deep zoom images.
Documentation
use std::fmt::Debug;

use serde::Deserialize;

use crate::json_utils::number_or_string;
use crate::network::resolve_relative;
use crate::Vec2d;

use super::DziError;

#[derive(Debug, Deserialize, PartialEq, Eq)]
pub struct DziFile {
    #[serde(rename = "Overlap", deserialize_with = "number_or_string", default)]
    pub overlap: u32,
    #[serde(rename = "TileSize", deserialize_with = "number_or_string")]
    pub tile_size: u32,
    #[serde(rename = "Format")]
    pub format: String,
    #[serde(rename = "Size")]
    pub size: Size,
    #[serde(rename = "Url")]
    pub base_url: Option<String>,
}

impl DziFile {
    pub fn get_size(&self) -> Result<Vec2d, DziError> {
        Ok(Vec2d { x: self.size.width, y: self.size.height })
    }
    pub fn get_tile_size(&self) -> Vec2d {
        Vec2d::square(self.tile_size)
    }
    pub fn max_level(&self) -> u32 {
        let size = self.get_size().unwrap();
        log2(size.x.max(size.y))
    }
    pub fn base_url(&self, resource_url: &str) -> String {
        if let Some(s) = &self.base_url {
            let relative_url_str = s.trim_end_matches('/');
            resolve_relative(resource_url, relative_url_str)
        } else {
            let until_dot = if let Some(dot_pos) = resource_url.rfind('.') {
                &resource_url[0..dot_pos]
            } else { resource_url };
            format!("{}_files", until_dot)
        }
    }
}

fn log2(n: u32) -> u32 {
    32 - (n - 1).leading_zeros()
}

#[derive(Debug, Deserialize, PartialEq, Eq, Default)]
pub struct Size {
    #[serde(rename = "Width", deserialize_with = "number_or_string", default)]
    pub width: u32,
    #[serde(rename = "Height", deserialize_with = "number_or_string", default)]
    pub height: u32,
}

#[test]
fn test_dzi() {
    let dzi: DziFile = serde_xml_rs::from_str(
        r#"
        <Image
            Format="png" Overlap="2" TileSize="256">
            <Size Height="3852" Width="5393"/>
        </Image>"#,
    )
        .unwrap();
    assert_eq!(dzi.get_size().unwrap(), Vec2d { x: 5393, y: 3852 });
    assert_eq!(dzi.get_tile_size(), Vec2d { x: 256, y: 256 });
    assert_eq!(dzi.max_level(), 13);
}

#[test]
fn test_dzi_json() {
    let dzi: DziFile = serde_json::from_str(
        r#"{
            "type":  "image",
            "xmlns": "http://schemas.microsoft.com/deepzoom/2008",
	        "Url":   "http://content.example.net/images/",
            "Format":   "jpg",
            "Overlap":  "1",
            "TileSize": "254",
            "Size": { "Height": "4409", "Width": "7793" }
	    }"#,
    ).unwrap();
    assert_eq!(dzi.get_size().unwrap(), Vec2d { y: 4409, x: 7793 });
    assert_eq!(dzi.get_tile_size(), Vec2d { x: 254, y: 254 });
    assert_eq!(dzi.max_level(), 13);
}