Skip to content

Commit

Permalink
Added IIIF_VERSION server directive and support for v3 info.json
Browse files Browse the repository at this point in the history
  • Loading branch information
ruven committed Jan 23, 2020
1 parent e06941f commit 1d6c0d5
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 18 deletions.
1 change: 1 addition & 0 deletions ChangeLog
@@ -1,5 +1,6 @@
23/01/2020:
- Refactored and corrected resolution calculation code in View class
- Added IIIF_VERSION server directive and support for v3 info.json


21/01/2020:
Expand Down
3 changes: 3 additions & 0 deletions README
Expand Up @@ -234,6 +234,9 @@ threads are used by default.
KAKADU_READMODE: Set the Kakadu JPEG2000 read-mode. 0 for 'fast' mode with minimal error checking (default), 1 for 'fussy' mode with no error
recovery, 2 for 'resilient' mode with maximum recovery from codestream errors. See the Kakadu documentation for further details.

IIIF_VERSION: Set the major IIIF Image API version. Values should be a single digit. For example: 2 for versions 2 or 2.1 etc.
3 for IIIF version 3.x. If not set, defaults to version IIIF 2.x

DECODER_MODULES: Comma separated list of external modules for decoding
other image formats. This is only necessary if you have activated
--enable-modules for ./configure and written your own image format
Expand Down
14 changes: 14 additions & 0 deletions src/Environment.h
Expand Up @@ -46,6 +46,7 @@
#define URI_MAP ""
#define EMBED_ICC true
#define KAKADU_READMODE 0
#define IIIF_VERSION 2


#include <string>
Expand Down Expand Up @@ -299,6 +300,19 @@ class Environment {
return readmode;
}


static unsigned int getIIIFVersion(){
unsigned int version;
char* envpara = getenv( "IIIF_VERSION" );
if( envpara ){
version = atoi( envpara );
if( version < 1 ) version = IIIF_VERSION;
}
else version = IIIF_VERSION;
return version;
}


};


Expand Down
78 changes: 60 additions & 18 deletions src/IIIF.cc
Expand Up @@ -2,7 +2,7 @@
IIIF Request Command Handler Class Member Function
Copyright (C) 2014-2019 Ruven Pillay
Copyright (C) 2014-2020 Ruven Pillay
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -31,9 +31,11 @@

// Define several IIIF strings
#define IIIF_SYNTAX "IIIF syntax is {identifier}/{region}/{size}/{rotation}/{quality}{.format}"
#define IIIF_PROFILE "http://iiif.io/api/image/2/level1.json"
#define IIIF_CONTEXT "http://iiif.io/api/image/2/context.json"
#define IIIF_CONTEXT "http://iiif.io/api/image/%d/context.json"
#define IIIF_PROTOCOL "http://iiif.io/api/image"
#define IIIF_PROFILE_2 "http://iiif.io/api/image/2/level1.json"
#define IIIF_PROFILE_3 "level1"


using namespace std;

Expand Down Expand Up @@ -96,11 +98,12 @@ void IIIF::run( Session* session, const string& src )
else{
string request_uri = session->headers["REQUEST_URI"];
request_uri.erase( request_uri.length() - suffix.length(), string::npos );
id = "http://" + session->headers["HTTP_HOST"] + request_uri;
id = "//" + session->headers["HTTP_HOST"] + request_uri;
}
string header = string( "Status: 303 See Other\r\n" )
+ "Location: " + id + "/info.json\r\n"
+ "Server: iipsrv/" + VERSION + "\r\n"
+ "X-Powered-By: IIPImage\r\n"
+ "\r\n";
session->out->printf( (const char*) header.c_str() );
session->response->setImageSent();
Expand Down Expand Up @@ -147,8 +150,8 @@ void IIIF::run( Session* session, const string& src )
id = host + query;
}
else{
string request_uri = session->headers["REQUEST_URI"];

string request_uri = session->headers["REQUEST_URI"];
string scheme = session->headers["HTTPS"].empty() ? "http://" : "https://";

if (request_uri.empty()){
Expand All @@ -168,9 +171,17 @@ void IIIF::run( Session* session, const string& src )
*(session->logfile) << "IIIF :: ID is set to " << iiif_id << endl;
}


// Set some parameters depending on the IIIF version
unsigned int iiif_version = session->codecOptions["IIIF_VERSION"];

// Set the context URL string
char iiif_context[48];
snprintf( iiif_context, 48, IIIF_CONTEXT, iiif_version );


infoStringStream << "{" << endl
<< " \"@context\" : \"" << IIIF_CONTEXT << "\"," << endl
<< " \"@id\" : \"" << iiif_id << "\"," << endl
<< " \"@context\" : \"" << iiif_context << "\"," << endl
<< " \"protocol\" : \"" << IIIF_PROTOCOL << "\"," << endl
<< " \"width\" : " << width << "," << endl
<< " \"height\" : " << height << "," << endl
Expand Down Expand Up @@ -202,16 +213,32 @@ void IIIF::run( Session* session, const string& src )
}

infoStringStream << " ] }" << endl
<< " ]," << endl
<< " \"profile\" : [" << endl
<< " \"" << IIIF_PROFILE << "\"," << endl
<< " { \"formats\" : [ \"jpg\" ]," << endl
<< " \"qualities\" : [ \"native\",\"color\",\"gray\",\"bitonal\" ]," << endl
<< " \"supports\" : [\"regionByPct\",\"regionSquare\",\"sizeByForcedWh\",\"sizeByWh\",\"sizeAboveFull\",\"rotationBy90s\",\"mirroring\"]," << endl
<< " \"maxWidth\" : " << max << "," << endl
<< " \"maxHeight\" : " << max << "\n }" << endl
<< " ]" << endl
<< "}";
<< " ]," << endl;

// Profile for IIIF version 3
if( iiif_version == 3 ){
infoStringStream << " \"id\" : \"" << iiif_id << "\"," << endl
<< " \"type\": \"ImageService3\"," << endl
<< " \"profile\" : \"" << IIIF_PROFILE_3 << "\"," << endl
<< " \"maxWidth\" : " << max << "," << endl
<< " \"maxHeight\" : " << max << "," << endl
<< " \"extraQualities\": [\"color\",\"gray\",\"bitonal\"]," << endl
<< " \"extraFeatures\": [\"regionByPct\",\"sizeByForcedWh\",\"sizeByWh\",\"sizeAboveFull\",\"sizeUpscaling\",\"rotationBy90s\",\"mirroring\"]"
<< endl << "}" << endl;
}
// Profile for IIIF versions 1 and 2
else{
infoStringStream << " \"@id\" : \"" << iiif_id << "\"," << endl
<< " \"profile\" : [" << endl
<< " \"" << IIIF_PROFILE_2 << "\"," << endl
<< " { \"formats\" : [ \"jpg\" ]," << endl
<< " \"qualities\" : [\"native\",\"color\",\"gray\",\"bitonal\"]," << endl
<< " \"supports\" : [\"regionByPct\",\"regionSquare\",\"sizeByForcedWh\",\"sizeByWh\",\"sizeAboveFull\",\"sizeUpscaling\",\"rotationBy90s\",\"mirroring\"]," << endl
<< " \"maxWidth\" : " << max << "," << endl
<< " \"maxHeight\" : " << max << "\n }" << endl
<< " ]" << endl
<< "}";
}

// Get our Access-Control-Allow-Origin value, if any
string cors = session->response->getCORS();
Expand Down Expand Up @@ -340,6 +367,12 @@ void IIIF::run( Session* session, const string& src )
float ratio = (float)requested_width / (float)requested_height;
unsigned int max_size = session->view->getMaxSize();

// ^ request prefix (upscaling) - remove ^ symbol and continue usual parsing
if( session->codecOptions["IIIF_VERSION"] >= 3 ){
if ( sizeString.substr(0, 1) == "^" ) sizeString.erase(0, 1);
else session->view->allow_upscaling = false;
}

// "full" or "max" request
if ( sizeString == "full" || sizeString == "max" ){
// No need to do anything
Expand All @@ -359,11 +392,12 @@ void IIIF::run( Session* session, const string& src )
// "w,h", "w,", ",h", "!w,h" requests
else{

// !w,h request - remove !, remember it and continue as if w,h request
// !w,h request (do not break aspect ratio) - remove the !, store the info and continue usual parsing
if ( sizeString.substr(0, 1) == "!" ) sizeString.erase(0, 1);
// Otherwise tell our view to break aspect ratio
else session->view->maintain_aspect = false;


size_t pos = sizeString.find_first_of(",");

// If no comma, size is invalid
Expand Down Expand Up @@ -405,6 +439,14 @@ void IIIF::run( Session* session, const string& src )
throw invalid_argument( "IIIF: invalid size" );
}

// Check for malformed upscaling request
if( session->codecOptions["IIIF_VERSION"] >= 3 ){
if( session->view->allow_upscaling == false &&
( requested_width > width || requested_height > height ) ){
throw invalid_argument( "IIIF: upscaling should be prefixed with ^" );
}
}

// Limit our requested size to the maximum allowable size if necessary
if( requested_width > max_size || requested_height > max_size ){
if( ratio > 1.0 ){
Expand Down
6 changes: 6 additions & 0 deletions src/Main.cc
Expand Up @@ -322,6 +322,10 @@ int main( int argc, char *argv[] )
bool embed_icc = Environment::getEmbedICC();


// Set our IIIF version
unsigned int iiif_version = Environment::getIIIFVersion();


// Create our image processing engine
Transform* processor = new Transform();

Expand All @@ -341,6 +345,7 @@ int main( int argc, char *argv[] )
logfile << "Setting maximum CVT size to " << max_CVT << endl;
logfile << "Setting HTTP Cache-Control header to '" << cache_control << "'" << endl;
logfile << "Setting 3D file sequence name pattern to '" << filename_pattern << "'" << endl;
logfile << "Setting IIIF version to " << iiif_version << endl;
if( !cors.empty() ) logfile << "Setting Cross Origin Resource Sharing to '" << cors << "'" << endl;
if( !base_url.empty() ) logfile << "Setting base URL to '" << base_url << "'" << endl;
if( max_layers != 0 ){
Expand Down Expand Up @@ -585,6 +590,7 @@ int main( int argc, char *argv[] )
session.watermark = &watermark;
session.headers.clear();
session.processor = processor;
session.codecOptions["IIIF_VERSION"] = iiif_version;
#ifdef HAVE_KAKADU
session.codecOptions["KAKADU_READMODE"] = kdu_readmode;
#endif
Expand Down

0 comments on commit 1d6c0d5

Please sign in to comment.