Files
resourcespace/api/iiif/handler2.php
2025-07-18 16:20:14 +07:00

682 lines
37 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
$suppress_headers = true;
if (!$iiif_enabled || !isset($iiif_identifier_field) || !is_numeric($iiif_identifier_field) || !isset($iiif_userid) || !is_numeric($iiif_userid) || !isset($iiif_description_field)) {
exit($lang["iiif_disabled"]);
}
$iiif_user = get_user($iiif_userid);
if ($iiif_user === false) {
iiif_error(500, ['Invalid $iiif_userid.']);
}
// Creating $userdata for use in do_search()
$userdata[0] = $iiif_user;
setup_user($iiif_user);
$rootlevel = $baseurl_short . "iiif/";
$rooturl = $baseurl . "/iiif/";
$rootimageurl = $baseurl . "/iiif/image/";
$request_url = strtok($_SERVER["REQUEST_URI"], '?');
$path = substr($request_url, strpos($request_url, $rootlevel) + strlen($rootlevel));
$xpath = explode("/", $path);
$getext = "";
$response = array();
$validrequest = false;
$iiif_headers = array();
$errors = array();
if (count($xpath) == 1 && $xpath[0] == "") {
# Root level request - send information file only
$response["@context"] = "http://iiif.io/api/presentation/2/context.json";
$response["@id"] = $rooturl;
$response["@type"] = "sc:Manifest";
$response["@label"] = "";
$response["width"] = 6000;
$response["height"] = 4000;
$response["tiles"] = array();
$response["tiles"][] = array("width" => $preview_tile_size, "height" => $preview_tile_size, "scaleFactors" => $preview_tile_scale_factors);
$response["profile"] = array("http://iiif.io/api/image/2/level0.json");
$validrequest = true;
} else {
if (strtolower($xpath[0]) == "image") {
// IMAGE REQUEST (http://iiif.io/api/image/2.1/)
// The IIIF Image API URI for requesting an image must conform to the following URI Template:
// {scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
$identifier = trim($xpath[1] ?? '');
if ($identifier === '') {
iiif_error(400, ['Missing identifier']);
}
if (!isset($xpath[2]) || $xpath[2] == "") {
// Redirect to image information document
$redirurl = $rootimageurl . $identifier . '/info.json';
if (function_exists("http_response_code")) {
http_response_code(303);
}
header("Location: " . $redirurl);
exit();
}
$resourceid = $xpath[1];
if (is_numeric($resourceid)) {
$resource = get_resource_data($resourceid);
$resource_access = get_resource_access($resourceid);
} else {
$resource_access = 2;
}
if ($resource_access == 0 && !in_array($resource["file_extension"], config_merge_non_image_types())) {
// Check resource actually exists and is active
$fulljpgsize = is_jpeg_extension($resource["file_extension"] ?? "") ? "" : "hpr";
$useextension = strtolower($resource["file_extension"]) == "jpeg" ? $resource["file_extension"] : "jpg";
$img_path = get_resource_path($resourceid, true, $fulljpgsize, false, $useextension);
$image_size = get_original_imagesize($resourceid, $img_path, $useextension);
if ($image_size === false) {
$errors[] = "No image available for this identifier";
iiif_error(404, $errors);
}
$imageWidth = (int) $image_size[1];
$imageHeight = (int) $image_size[2];
// Get all available sizes
$sizes = get_image_sizes($resourceid, true, "jpg", false);
$availsizes = array();
if ($imageWidth > 0 && $imageHeight > 0) {
foreach ($sizes as $size) {
if (
$size['width'] > 0
&& $size['height'] > 0
&& $size['width'] <= $iiif_max_width
&& $size['height'] <= $iiif_max_height
&& (
!$iiif_only_power_of_two_sizes
|| (is_power_of_two($size['width']) && is_power_of_two($size['height']))
|| $size['id'] == 'pre'
)
) {
$availsizes[] = [
'id' => $size['id'],
'width' => $size['width'],
'height' => $size['height'],
];
}
}
}
if ($xpath[2] == "info.json") {
// Image information request. Only fullsize available in this initial version
$response["@context"] = "http://iiif.io/api/image/2/context.json";
$response["@id"] = $rootimageurl . $resourceid;
$response["height"] = $imageHeight;
$response["width"] = $imageWidth;
$response["profile"] = array();
$response["profile"][] = "http://iiif.io/api/image/2/level0.json";
if ($iiif_custom_sizes) {
$response["profile"][] = array(
"formats" => array("jpg"),
"qualities" => array("default"),
"maxWidth" => $iiif_max_width,
"maxHeight" => $iiif_max_height,
"supports" => array("sizeByH","sizeByW")
);
} else {
$response["profile"][] = array(
"formats" => array("jpg"),
"qualities" => array("default"),
"maxWidth" => $iiif_max_width,
"maxHeight" => $iiif_max_height
);
}
$response["protocol"] = "http://iiif.io/api/image";
$response["sizes"] = $availsizes;
if ($preview_tiles) {
$response["tiles"] = array();
$response["tiles"][] = array("height" => $preview_tile_size, "width" => $preview_tile_size, "scaleFactors" => $preview_tile_scale_factors);
}
$iiif_headers[] = 'Link: <http://iiif.io/api/image/2/level0.json>;rel="profile"';
$validrequest = true;
} elseif (!isset($xpath[3]) || !isset($xpath[4]) || !isset($xpath[5]) || !isset($xpath[5]) || $xpath[5] != "default.jpg") {
// Not request for image information document and no sizes specified
$errors[] = "Invalid image request format.";
iiif_error(400, $errors);
} else {
// Check the request parameters
$region = $xpath[2];
$size = $xpath[3];
$rotation = $xpath[4];
$formatparts = explode(".", $xpath[5]);
if (count($formatparts) != 2) {
// Format. As we only support IIIF Image level 0 a value of 'jpg' is required
$errors[] = "Invalid quality or format requested. Try using 'default.jpg'";
iiif_error(400, $errors);
} else {
$quality = $formatparts[0];
$format = $formatparts[1];
}
// Process requested region
if (!isset($errorcode) && $region != "full" && $region != "max" && $preview_tiles) {
// If the request specifies a region which extends beyond the dimensions reported in the image information document,
// then the service should return an image cropped at the images edge, rather than adding empty space.
// If the requested regions height or width is zero, or if the region is entirely outside the bounds
// of the reported dimensions, then the server should return a 400 status code.
$regioninfo = explode(",", $region);
$region_filtered = array_filter($regioninfo, 'is_numeric');
if (count($region_filtered) != 4) {
// Invalid region
$errors[] = "Invalid region requested. Use 'full' or 'x,y,w,h'";
iiif_error(400, $errors);
} else {
$regionx = (int)$region_filtered[0];
$regiony = (int)$region_filtered[1];
$regionw = (int)$region_filtered[2];
$regionh = (int)$region_filtered[3];
debug("IIIF region requested: x:" . $regionx . ", y:" . $regiony . ", w:" . $regionw . ", h:" . $regionh);
if (fmod($regionx, $preview_tile_size) != 0 || fmod($regiony, $preview_tile_size) != 0) {
// Invalid region
$errors[] = "Invalid region requested. Supported tiles are " . $preview_tile_size . "x" . $preview_tile_size . " at scale factors " . implode(",", $preview_tile_scale_factors) . ".";
iiif_error(400, $errors);
} else {
$tile_request = true;
}
}
} else {
// Full image requested
$tile_request = false;
}
// Process size
if (strpos($size, ",") !== false) {
// Currently support 'w,' and ',h' syntax requests
$getdims = explode(",", $size);
$getwidth = (int)$getdims[0];
$getheight = (int)$getdims[1];
if ($tile_request) {
if (($regionx + $regionw) >= $imageWidth || ($regiony + $regionh) >= $imageHeight) {
// Size specified is not the standard tile width, may be right or bottom edge of image
$validtileh = false;
$validtilew = false;
if ($getwidth > 0 && $getheight == 0) {
$scale = ceil($regionw / $getwidth);
} elseif ($getheight > 0 && $getwidth == 0) {
$scale = ceil($regionh / $getheight);
} else {
$errors[] = "Invalid tile size requested";
iiif_error(501, $errors);
}
if (!in_array($scale, $preview_tile_scale_factors)) {
$errors[] = "Invalid tile size requested";
iiif_error(501, $errors);
}
} elseif (
($getwidth == $preview_tile_size && $getheight == 0) ||
($getheight == $preview_tile_size && $getwidth == 0) ||
($getheight == $preview_tile_size && $getwidth == $preview_tile_size)
) {
$valid_tile = true;
if ($getheight === 0) {
$scale = ceil($regionw / $getwidth);
} else {
$scale = ceil($regionh / $getheight);
}
} else {
$errors[] = "Invalid tile size requested";
iiif_error(400, $errors);
}
$getsize = "tile_" . $scale . "_" . $regionx . "_" . $regiony . "_" . $regionw . "_" . $regionh;
$getext = "jpg";
debug("IIIF" . $regionx . "_" . $regiony . "_" . $regionw . "_" . $regionh);
} else {
if ($getheight == 0) {
$getheight = floor($getwidth * ($imageHeight / $imageWidth));
} elseif ($getwidth == 0) {
$getwidth = floor($getheight * ($imageWidth / $imageHeight));
}
// Establish which preview size this request relates to
foreach ($availsizes as $availsize) {
debug("IIIF - checking available size for resource " . $resource["ref"] . ". Size '" . $availsize["id"] . "': " . $availsize["width"] . "x" . $availsize["height"] . ". Requested size: " . $getwidth . "x" . $getheight);
if ($availsize["width"] == $getwidth && $availsize["height"] == $getheight) {
$getsize = $availsize["id"];
}
}
if (!isset($getsize)) {
if (!$iiif_custom_sizes || $getwidth > $iiif_max_width || $getheight > $iiif_max_height) {
// Invalid size requested
$errors[] = "Invalid size requested";
iiif_error(400, $errors);
} else {
$getsize = "resized_" . $getwidth . "_" . $getheight;
$getext = "jpg";
}
}
}
} elseif ($size == "full" || $size == "max" || $size == "thm") {
if ($tile_request) {
if ($size == "full" || $size == "max") {
$getsize = "tile_" . $regionx . "_" . $regiony . "_" . $regionw . "_" . $regionh;
$getext = "jpg";
} else {
$errors[] = "Invalid tile size requested";
iiif_error(501, $errors);
}
} else {
// Full/max image region requested
if ($iiif_max_width >= $imageWidth && $iiif_max_height >= $imageHeight) {
$isjpeg = in_array(strtolower($resource["file_extension"]), array("jpg","jpeg"));
$getext = strtolower($resource["file_extension"]) == "jpeg" ? "jpeg" : "jpg";
$getsize = $isjpeg ? "" : "hpr";
} else {
$getext = "jpg";
$getsize = count($availsizes) > 0 ? $availsizes[0]["id"] : "thm";
}
}
} else {
$errors[] = "Invalid size requested";
iiif_error(400, $errors);
}
if ($rotation != 0) {
// Rotation. As we only support IIIF Image level 0 only a rotation value of 0 is accepted
$errors[] = "Invalid rotation requested. Only '0' is permitted.";
iiif_error(404, $errors);
}
if (isset($quality) && $quality != "default" && $quality != "color") {
// Quality. As we only support IIIF Image level 0 only a quality value of 'default' or 'color' is accepted
$errors[] = "Invalid quality requested. Only 'default' is permitted";
iiif_error(404, $errors);
}
if (isset($format) && strtolower($format) != "jpg") {
// Format. As we only support IIIF Image level 0 only a value of 'jpg' is accepted
$errors[] = "Invalid format requested. Only 'jpg' is permitted.";
iiif_error(404, $errors);
}
if (!isset($errorcode)) {
// Request is supported, send the image
$imgpath = get_resource_path($resourceid, true, $getsize, false, $getext);
if ($tile_request && !file_exists($imgpath)) {
// Support older tiles without scale factor in ID that may not have been recreated
$imgpath = preg_replace("/(tile_\\d+_)/", "tile_", $imgpath);
}
debug("IIIF: image path: " . $imgpath);
if (file_exists($imgpath)) {
$imgfound = true;
} else {
if ($region != "full" && $region != "max") {
// Tiles have not yet been created
if (is_process_lock('create_previews_' . $resource["ref"] . "_tiles")) {
$errors[] = "Requested image is not currently available";
iiif_error(503, $errors);
}
set_process_lock('create_previews_' . $resource["ref"] . "_tiles");
$imgfound = @create_previews($resourceid, false, "jpg", false, true, -1, true, false, false, array("tiles"));
clear_process_lock('create_previews_' . $resource["ref"] . "_tiles");
} else {
if (is_process_lock('create_previews_' . $resource["ref"] . "_" . $getsize)) {
$errors[] = "Requested image is not currently available";
iiif_error(503, $errors);
}
$imgfound = @create_previews($resourceid, false, "jpg", false, true, -1, true, false, false, array($getsize));
clear_process_lock('create_previews_' . $resource["ref"] . "_" . $getsize);
}
}
if ($imgfound) {
$validrequest = true;
$response_image = $imgpath;
} else {
$errors[] = "No image available for this identifier";
iiif_error(404, $errors);
}
}
}
/* IMAGE REQUEST END */
} else {
$errors[] = "Missing or invalid identifier";
iiif_error(404, $errors);
}
} // End of image API
else {
// Presentation API
$identifier = $xpath[0];
if ($identifier != "" && !isset($xpath[1])) {
// Redirect to image information document
$redirurl = $_SERVER["REQUEST_URI"] . (!isset($xpath[2]) ? "/" : "") . "manifest";
if (function_exists("http_response_code")) {
http_response_code(303); # Send error status
}
header("Location: " . $redirurl);
exit();
}
$iiif_field = get_resource_type_field($iiif_identifier_field);
$iiif_search = $iiif_field["name"] . ":" . $identifier;
$iiif_results = do_search($iiif_search);
if (is_array($iiif_results) && count($iiif_results) > 0) {
if (!isset($xpath[1])) {
$errors[] = "Bad request. Valid options are 'manifest', 'sequence' or 'canvas' e.g. ";
$errors[] = "For the manifest: " . $rooturl . $xpath[0] . "/manifest";
$errors[] = "For a sequence : " . $rooturl . $xpath[0] . "/sequence";
$errors[] = "For a canvas : " . $rooturl . $xpath[0] . "/canvas/<identifier>";
iiif_error(404, $errors);
} else {
if (!is_array($iiif_results) || count($iiif_results) == 0) {
$errors[] = "Invalid identifier: " . $identifier;
iiif_error(404, $errors);
} else {
// Add sequence position information
$resultcount = count($iiif_results);
$iiif_results_with_position = array();
$iiif_results_without_position = array();
for ($n = 0; $n < $resultcount; $n++) {
if (isset($iiif_sequence_field)) {
if (isset($iiif_results[$n]["field" . $iiif_sequence_field])) {
$sequenceid = $iiif_results[$n]["field" . $iiif_sequence_field];
} else {
$sequenceid = get_data_by_field($iiif_results[$n]["ref"], $iiif_sequence_field);
}
$sequence_field = get_resource_type_field($iiif_sequence_field);
$sequence_prefix = $sequence_field["name"] . " ";
if (!isset($sequenceid) || trim($sequenceid) == "") {
// Processing resources without a sequence position separately
debug("IIIF: position empty for resource ref " . $iiif_results[$n]["ref"]);
$iiif_results_without_position[] = $iiif_results[$n];
continue;
}
debug("IIIF: position $sequenceid found in resource ref " . $iiif_results[$n]["ref"]);
$iiif_results[$n]["iiif_position"] = $sequenceid;
$iiif_results_with_position[] = $iiif_results[$n];
} else {
$sequenceid = $n;
debug("IIIF: position $position assigned to resource ref " . $iiif_results[$n]["ref"]);
$iiif_results[$n]["iiif_position"] = $sequenceid;
$iiif_results_with_position[] = $iiif_results[$n];
}
}
// Sort by user supplied position (handle blanks and duplicates)
if (isset($iiif_sequence_field)) {
# First sort by ref. Any duplicate positions will then be sorted oldest resource first.
usort($iiif_results_with_position, function ($a, $b) {
return $a['ref'] - $b['ref'];
});
# Sort resources with user supplied position.
usort($iiif_results_with_position, function ($a, $b) {
if (is_int_loose($a['iiif_position']) && is_int_loose($b['iiif_position'])) {
return $a['iiif_position'] - $b['iiif_position'];
} elseif (is_int_loose($a['iiif_position']) || is_int_loose($b['iiif_position'])) {
return is_int_loose($a['iiif_position']) ? 1 : -1; // Put strings before numbers
}
return strcmp($a['iiif_position'], $b['iiif_position']);
});
if (count($iiif_results_without_position) > 0 && count($iiif_results_with_position) > 0) {
# Sort resources without a user supplied position by resource reference.
# These will appear at the end of the sequence after those with a user supplied position.
# Only applies if some resources have a sequence position else return in search results order per earlier behaviour.
usort($iiif_results_without_position, function ($a, $b) {
return $a['ref'] - $b['ref'];
});
}
$iiif_results = array_merge($iiif_results_with_position, $iiif_results_without_position);
$sorted_final = [];
$maxid = 0;
foreach ($iiif_results as $index => $resource) {
# Update iiif_position after sorting using unique array key, removing potential user entered duplicates in sequence field.
# iiif_get_canvases() requires unique iiif_position values.
$resourcepos = $resource['iiif_position'] ?? ($maxid + 1);
while (isset($sorted_final[$resourcepos])) {
$resourcepos++;
}
debug("IIIF: final position $index given for resource ref " . $resource["ref"] . " sequence id: " . $resourcepos);
$sorted_final[$index] = $resource;
$sorted_final[$index]["iiif_position"] = $resourcepos;
$maxid = max((int) $resourcepos, $maxid);
}
$iiif_results = $sorted_final;
}
if ($xpath[1] == "manifest" || $xpath[1] == "") {
/* MANIFEST REQUEST - see http://iiif.io/api/presentation/2.1/#manifest */
$response["@context"] = "http://iiif.io/api/presentation/2/context.json";
$response["@id"] = $rooturl . $identifier . "/manifest";
$response["@type"] = "sc:Manifest";
// Descriptive metadata about the object/work
// The manifest data should be the same for all resources that are returned.
// This is the default when using the tms_link plugin for TMS integration.
// Therefore we use the data from the first returned result.
$iiif_data = get_resource_field_data($iiif_results[0]["ref"]);
// Label property
foreach ($iiif_results as $iiif_result) {
// Keep on until we find a label
$iiif_label = get_data_by_field($iiif_results[0]["ref"], $view_title_field);
if (trim($iiif_label) != "") {
$response["label"] = $iiif_label;
break;
}
}
if (!$iiif_label) {
$response["label"] = $lang["notavailableshort"];
}
$response["description"] = get_data_by_field($iiif_results[0]["ref"], $iiif_description_field);
// Construct metadata array from resource field data
$response["metadata"] = array();
$n = 0;
foreach ($iiif_data as $iiif_data_row) {
if (in_array($iiif_data_row["type"], $FIXED_LIST_FIELD_TYPES)) {
// Don't use the data as this has already concatentated the translations, add an entry for each node translation by building up a new array
$resnodes = get_resource_nodes($iiif_results[0]["ref"], $iiif_data_row["resource_type_field"], true);
if (count($resnodes) == 0) {
continue;
}
$langentries = array();
$nodecount = 0;
unset($def_lang);
foreach ($resnodes as $resnode) {
debug("IIIF: translating " . $resnode["name"] . " from field '" . $iiif_data_row["title"] . "'");
$node_langs = i18n_get_translations($resnode["name"]);
$transcount = 0;
$defaulttrans = "";
foreach ($node_langs as $nlang => $nltext) {
if (!isset($langentries[$nlang])) {
// This is the first translated node entry for this language. If we already have translations copy the default language array to make sure no nodes with missing translations are lost
debug("IIIF: Adding a new translation entry for language '" . $nlang . "', field '" . $iiif_data_row["title"] . "'");
$langentries[$nlang] = isset($def_lang) ? $def_lang : array();
}
// Add the node text to the array for this language;
debug("IIIF: Adding node translation for language '" . $nlang . "', field '" . $iiif_data_row["title"] . "': " . $nltext);
$langentries[$nlang][] = $nltext;
// Set default text for any translations
if ($nlang == $defaultlanguage || $defaulttrans == "") {
$defaulttrans = $nltext;
}
$transcount++;
}
$nodecount++;
// There may not be translations for all nodes, fill any arrays that don't have an entry with the untranslated versions
foreach ($langentries as $mdlang => $mdtrans) {
debug("IIIF: entry count for " . $mdlang . ":" . count($mdtrans));
debug("IIIF: node count: " . $nodecount);
if (count($mdtrans) != $nodecount) {
debug("IIIF: No translation found for " . $mdlang . ". Adding default translation to language array for field '" . $iiif_data_row["title"] . "': " . $mdlang . ": " . $defaulttrans);
$langentries[$mdlang][] = $defaulttrans;
}
}
// To ensure that no nodes are lost due to missing translations,
// Save the default language array to make sure we include any untranslated nodes that may be missing when/if we find new languages for the next node
debug("IIIF: Saving default language array for field '" . $iiif_data_row["title"] . "': " . implode(",", $langentries[$defaultlanguage]));
// Default language is the ideal, but if no default language entries for this node have been found copy the first language we have
reset($langentries);
$def_lang = isset($langentries[$defaultlanguage]) ? $langentries[$defaultlanguage] : $langentries[key($langentries)];
}
$response["metadata"][$n] = array();
$response["metadata"][$n]["label"] = $iiif_data_row["title"];
$response["metadata"][$n]["value"] = array();
// Add each tag
$o = 0;
foreach ($langentries as $mdlang => $mdtrans) {
debug("IIIF: adding to metadata language array: " . $mdlang . ": " . implode(",", $mdtrans));
$response["metadata"][$n]["value"][$o]["@value"] = implode(",", array_values($mdtrans));
$response["metadata"][$n]["value"][$o]["@language"] = $mdlang;
$o++;
}
$n++;
} elseif (trim((string) $iiif_data_row["value"]) != "") {
$response["metadata"][$n] = array();
$response["metadata"][$n]["label"] = $iiif_data_row["title"];
$response["metadata"][$n]["value"] = $iiif_data_row["value"];
$n++;
}
}
$response["description"] = get_data_by_field($iiif_results[0]["ref"], $iiif_description_field);
if (isset($iiif_license_field)) {
$response["license"] = get_data_by_field($iiif_results[0]["ref"], $iiif_license_field);
}
// Thumbnail property
foreach ($iiif_results as $iiif_result) {
// Keep on until we find an image
$iiif_thumb = iiif_get_thumbnail($iiif_results[0]["ref"]);
if ($iiif_thumb) {
$response["thumbnail"] = $iiif_thumb;
break;
}
}
if (!$iiif_thumb) {
$response["thumbnail"] = $baseurl . "/gfx/no_preview/default.png";
}
// Sequences
$response["sequences"] = array();
$response["sequences"][0]["@id"] = $rooturl . $identifier . "/sequence/normal";
$response["sequences"][0]["@type"] = "sc:Sequence";
$response["sequences"][0]["label"] = "Default order";
$response["sequences"][0]["canvases"] = iiif_get_canvases($identifier, $iiif_results, false);
$validrequest = true;
/* MANIFEST REQUEST END */
} elseif ($xpath[1] == "canvas") {
// This is essentially a resource
// {scheme}://{host}/{prefix}/{identifier}/canvas/{name}
$canvasid = $xpath[2];
$allcanvases = iiif_get_canvases($identifier, $iiif_results, true);
$response["@context"] = "http://iiif.io/api/presentation/2/context.json";
$response = array_merge($response, $allcanvases[$canvasid]);
$validrequest = true;
} elseif ($xpath[1] == "sequence") {
if (isset($xpath[2]) && $xpath[2] == "normal") {
$response["@context"] = "http://iiif.io/api/presentation/2/context.json";
$response["@id"] = $rooturl . $identifier . "/sequence/normal";
$response["@type"] = "sc:Sequence";
$response["label"] = "Default order";
$response["canvases"] = iiif_get_canvases($identifier, $iiif_results);
$validrequest = true;
}
} elseif ($xpath[1] == "annotation") {
// See http://iiif.io/api/presentation/2.1/#image-resources
$annotationid = $xpath[2];
// Need to find the resourceid the annotation is linked to
if (isset($iiif_results[$annotationid])) {
$resourceid = $iiif_results[$annotationid]["ref"];
$size_info = array(
'identifier' => (strtolower($iiif_results[$annotationid]['file_extension']) != 'jpg') ? 'hpr' : '',
'return_height_width' => false,
);
$validrequest = true;
}
if ($validrequest) {
$response["@context"] = "http://iiif.io/api/presentation/2/context.json";
$response["@id"] = $rooturl . $identifier . "/annotation/" . $annotationid;
$response["@type"] = "oa:Annotation";
$response["motivation"] = "sc:painting";
$response["resource"] = iiif_get_image($identifier, $resourceid, $annotationid, $size_info);
$response["on"] = $rooturl . $identifier . "/canvas/" . $annotationid;
} else {
$errors[] = "Invalid annotation identifier: " . $annotationid;
iiif_error(404, $errors);
}
}
}
}
} // End of valid $identifier check based on search results
else {
$errors[] = "Invalid identifier: " . $identifier;
iiif_error(404, $errors);
}
}
}
// Send the data
if ($validrequest) {
if (function_exists("http_response_code")) {
http_response_code(200); # Send OK
}
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Accept");
if (isset($response_image) && file_exists($response_image)) {
// Send the image
$file_size = filesize_unlimited($response_image);
$file_handle = fopen($response_image, 'rb');
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Accept");
header('Content-Disposition: inline;');
header('Content-Transfer-Encoding: binary');
$mime = get_mime_type($response_image)[0];
header("Content-Type: {$mime}");
$sent = 0;
while ($sent < $file_size) {
echo fread($file_handle, $download_chunk_size);
ob_flush();
flush();
$sent += $download_chunk_size;
if (0 != connection_status()) {
break;
}
}
fclose($file_handle);
} else {
header("Content-Type: application/ld+json");
foreach ($iiif_headers as $iiif_header) {
header($iiif_header);
}
if (defined('JSON_PRETTY_PRINT')) {
echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
} else {
echo json_encode($response);
}
}
exit();
}