-
-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add preliminary support for the IIIF format
- Loading branch information
Showing
10 changed files
with
309 additions
and
82 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
use std::sync::Arc; | ||
|
||
use custom_error::custom_error; | ||
use tile_info::ImageInfo; | ||
|
||
use crate::dezoomer::*; | ||
|
||
mod tile_info; | ||
|
||
#[derive(Default)] | ||
pub struct IIIF; | ||
|
||
custom_error! {pub IIIFError | ||
JsonError{source: serde_json::Error} = "Invalid IIIF info.json file: {source}" | ||
} | ||
|
||
impl From<IIIFError> for DezoomerError { | ||
fn from(err: IIIFError) -> Self { | ||
DezoomerError::Other { source: err.into() } | ||
} | ||
} | ||
|
||
impl Dezoomer for IIIF { | ||
fn name(&self) -> &'static str { | ||
"iiif" | ||
} | ||
|
||
fn zoom_levels(&mut self, data: &DezoomerInput) -> Result<ZoomLevels, DezoomerError> { | ||
self.assert(data.uri.ends_with("/info.json"))?; | ||
let contents = data.with_contents()?.contents; | ||
Ok(zoom_levels(contents)?) | ||
} | ||
} | ||
|
||
fn zoom_levels(raw_info: &[u8]) -> Result<ZoomLevels, IIIFError> { | ||
let image_info: ImageInfo = serde_json::from_slice(raw_info)?; | ||
let img = Arc::new(image_info); | ||
let default_tiles = vec![Default::default()]; | ||
let tiles = img.tiles.as_ref().unwrap_or(&default_tiles); | ||
let levels = tiles | ||
.iter() | ||
.flat_map(|tile_info| { | ||
let tile_size = Vec2d { | ||
x: tile_info.width, | ||
y: tile_info.height.unwrap_or(tile_info.width), | ||
}; | ||
let page_info = &img; // Required to allow the move | ||
tile_info | ||
.scale_factors | ||
.iter() | ||
.map(move |&scale_factor| IIIFZoomLevel { | ||
scale_factor, | ||
tile_size, | ||
page_info: Arc::clone(page_info), | ||
}) | ||
}) | ||
.into_zoom_levels(); | ||
Ok(levels) | ||
} | ||
|
||
struct IIIFZoomLevel { | ||
scale_factor: u32, | ||
tile_size: Vec2d, | ||
page_info: Arc<ImageInfo>, | ||
} | ||
|
||
impl TilesRect for IIIFZoomLevel { | ||
fn size(&self) -> Vec2d { | ||
Vec2d { | ||
x: self.page_info.width / self.scale_factor, | ||
y: self.page_info.height / self.scale_factor, | ||
} | ||
} | ||
|
||
fn tile_size(&self) -> Vec2d { | ||
self.tile_size | ||
} | ||
|
||
fn tile_url(&self, col_and_row_pos: Vec2d) -> String { | ||
let scaled_tile_size = self.tile_size * self.scale_factor; | ||
let xy_pos = col_and_row_pos * scaled_tile_size; | ||
let scaled_tile_size = max_size_in_rect(xy_pos, scaled_tile_size, self.size() * self.scale_factor); | ||
let tile_size = scaled_tile_size / self.scale_factor; | ||
format!( | ||
"{base}/{x},{y},{img_w},{img_h}/{tile_w},{tile_h}/{rotation}/{quality}.{format}", | ||
base = self.page_info.id, | ||
x = xy_pos.x, | ||
y = xy_pos.y, | ||
img_w = scaled_tile_size.x, | ||
img_h = scaled_tile_size.y, | ||
tile_w = tile_size.x, | ||
tile_h = tile_size.y, | ||
rotation = 0, | ||
quality = "default", | ||
format = "jpg" | ||
) | ||
} | ||
} | ||
|
||
impl std::fmt::Debug for IIIFZoomLevel { | ||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
write!( | ||
f, | ||
"IIIF image with {}x{} tiles", | ||
self.tile_size.x, self.tile_size.y | ||
) | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_tiles() { | ||
let data = br#"{ | ||
"@context" : "http://iiif.io/api/image/2/context.json", | ||
"@id" : "http://www.asmilano.it/fast/iipsrv.fcgi?IIIF=/opt/divenire/files/./tifs/05/36/536765.tif", | ||
"protocol" : "http://iiif.io/api/image", | ||
"width" : 15001, | ||
"height" : 48002, | ||
"tiles" : [ | ||
{ "width" : 512, "height" : 512, "scaleFactors" : [ 1, 2, 4, 8, 16, 32, 64, 128 ] } | ||
], | ||
"profile" : [ | ||
"http://iiif.io/api/image/2/level1.json", | ||
{ "formats" : [ "jpg" ], | ||
"qualities" : [ "native","color","gray" ], | ||
"supports" : ["regionByPct","sizeByForcedWh","sizeByWh","sizeAboveFull","rotationBy90s","mirroring","gray"] } | ||
] | ||
}"#; | ||
let levels = zoom_levels(data).unwrap(); | ||
let tiles: Vec<String> = levels[6].tiles().into_iter() | ||
.map(|t| t.unwrap().url) | ||
.collect(); | ||
assert_eq!(tiles, vec![ | ||
"", | ||
]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
use serde::Deserialize; | ||
|
||
#[derive(Debug, Deserialize, PartialEq)] | ||
pub struct ImageInfo { | ||
#[serde(rename = "@id")] | ||
pub id: String, | ||
pub width: u32, | ||
pub height: u32, | ||
pub tiles: Option<Vec<TileInfo>>, | ||
} | ||
|
||
#[derive(Debug, Deserialize, PartialEq)] | ||
pub struct TileInfo { | ||
pub width: u32, | ||
pub height: Option<u32>, | ||
#[serde(rename = "scaleFactors")] | ||
pub scale_factors: Vec<u32>, | ||
} | ||
|
||
impl Default for TileInfo { | ||
fn default() -> Self { | ||
TileInfo { | ||
width: 512, | ||
height: None, | ||
scale_factors: vec![1], | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_deserialisation() { | ||
let _: ImageInfo = serde_json::from_str( | ||
r#"{ | ||
"@context" : "http://iiif.io/api/image/2/context.json", | ||
"@id" : "http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C", | ||
"protocol" : "http://iiif.io/api/image", | ||
"width" : 6000, | ||
"height" : 4000, | ||
"sizes" : [ | ||
{"width" : 150, "height" : 100}, | ||
{"width" : 600, "height" : 400}, | ||
{"width" : 3000, "height": 2000} | ||
], | ||
"tiles": [ | ||
{"width" : 512, "scaleFactors" : [1,2,4,8,16]} | ||
], | ||
"profile" : [ "http://iiif.io/api/image/2/level2.json" ] | ||
}"#, | ||
) | ||
.unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.