Image API
Route definitions for the IIIF Image API.
Here we define our route definitions for the IIIF Image API as defined in: http://iiif.io/api/image/2.1/
The Image Request URI Syntax will work as the following example illustrate.
{scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
e.g. http://www.example.org/image-service/abcd1234/full/full/0/default.jpg
Image base
get/^(\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X)-(((\d{4}|\d{8}))|(,(\d{4}|\d{8}))|((\d{4}|\d{8}),)|((\d{4}|\d{8}),(\d{4}|\d{8}))))|(\/SNP([a-zA-Z0-9-]*){6})|(\/(HoPo)\d{3}-\d{4})|(\/([a-z0-9]*)-\d{4}))(\/)?$/i
Redirect to information page.
Handler
async ctx => { let url = ctx.request.url; // If the url ends with a slash, cut it of. if (url.length - 1 === url.lastIndexOf('/')) { url = url.slice(0, -1); } ctx.status = 302; ctx.redirect(`${url}/info.json`); }
Image information
get/^(\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X)-(((\d{4}|\d{8}))|(,(\d{4}|\d{8}))|((\d{4}|\d{8}),)|((\d{4}|\d{8}),(\d{4}|\d{8}))))|(\/SNP([a-zA-Z0-9-]*){6})|(\/(HoPo)\d{3}-\d{4})|(\/([a-z0-9]*)-\d{4}))\/info.json$/i
Full information for the image in the json format.
Handler
async ctx => { try { // Fill up the response body with the information. const id = utilsMain.getUrlParams(ctx.request.url).id; ctx.body = await imageApi.getImageInfo(id, ctx.request.url); // We set the content type after the body, otherwise it will be set // to application/json automatically. ctx.set('content-type', 'application/ld+json; charset=utf-8'); logger.info(`Deliver image info for ${id}.`); } catch (error) { /* It's possible that an Error is thrown while creating the image data. We expect a boom object here. */ logger.error(`Error while delivering image info: ${error}.`); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { /* istanbul ignore next */ throw error; } } }
Get the image.
get/^(\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X)-(((\d{4}|\d{8}))|(,(\d{4}|\d{8}))|((\d{4}|\d{8}),)|((\d{4}|\d{8}),(\d{4}|\d{8}))))|(\/SNP([a-zA-Z0-9-]*){6})|(\/(HoPo)\d{3}-\d{4})|(\/([a-z0-9]*)-\d{4}))\/(full|square|(\d+,\d+,\d+,\d+)|pct:((\d+)(?:\.\d+)?,(\d+)(?:\.\d+)?,(\d+)(?:\.\d+)?,(\d+)(?:\.\d+)?))\/(full|max|(\d+,)|(,\d+)|(pct:\d+)|(\d+,\d+)|(!\d+,\d+))\/(\d{1,3}(?:\.\d+)?|!\d{1,3}(?:\.\d+)?)\/[a-zA-Z]+\.[a-zA-Z]+$/i
Get the image with the given id with the transformations in the path.
Query: Object
Key = default | Type* (tests) in [valids] not in [invalids] | Eaxmples | Description |
---|---|---|---|
highlight | string [regex([object Object])] | ||
highlightColor | string [regex([object Object])] |
Handler
async ctx => { try { const urlParams = utilsMain.getUrlParams(ctx.request.url); const imageData = await imageApi.getImageData(ctx); const filename = utilsImage.buildImageFilename(urlParams); ctx.set('content-type', utilsImage.contentType(urlParams.format)); ctx.set('content-disposition', `inline; filename: "${filename}"`); ctx.set('content-length', new String(imageData).length); ctx.body = imageData; logger.info(`Deliver image ${filename}.`); } catch (error) { /* It's possible that an Error is thrown while creating the image data. We expect a boom object here. */ logger.error(`Error while delivering image: ${error}.`); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { /* istanbul ignore next */ throw error; } } }
Miscellaneous
Miscellaneous route definitions that are not included in the IIIF Image API.
The server have beside the implementation of the IIIF Image API some extra functionalities.
Server information
get/
A basic description what is this service for and where are more information are provide.
Handler
async ctx => { ctx.body = `This is the NGCS-Chula, an image server that implements the IIIF Image API Level 2. See <https://iiif.io/api/image/2.1/> for details and <https://ngcs-beta.staatsbibliothek-berlin.de/docs> for further implemented methods.`; }
Logo
get/^(\/dc|\/zefys|\/hopo|\/ebdb|)\/logo(\/)?$/i
Logo image of the SBB.
Handler
async ctx => { const client = utilsClients.getClient(ctx.request.url); const imageLogo = client.getImageLogo(); ctx.set('content-type', 'image/png'); ctx.set('content-disposition', `inline; filename: ${imageLogo.filename}`); ctx.set('content-length', new String(imageLogo.imageData).length); ctx.body = imageLogo.imageData; }
Download images as ZIP.
get/\/(dc\/|)download\/zip/
Get a ZIP file containing images and metadata of a specified PPN.
Query: Object
Key = default | Type* (tests) in [valids] not in [invalids] | Eaxmples | Description |
---|---|---|---|
ppn | string * [alphanum(),min(9),max(13)] | ||
range | string [length(9)] |
Handler
async ctx => { try { await zip(ctx); } catch (error) { if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Download images as PDF.
get/^(\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X)-(((\d{4}|\d{8}))|(,(\d{4}|\d{8}))|((\d{4}|\d{8}),)|((\d{4}|\d{8}),(\d{4}|\d{8}))))|(\/SNP([a-zA-Z0-9-]*){6})|(\/(HoPo)\d{3}-\d{4})|(\/([a-z0-9]*)-\d{4})).(pdf)$/
Get a PDF file containing images specified ID.
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); let headerContentDisposition = 'attachment'; let filename = 'sbb-zefys.pdf'; try { filename = await client.getPdfFilename(ctx.request.url); headerContentDisposition += `; filename: "${filename}"`; } catch (err) { logger.warn(`[ZEFYS][PDF] No method found to build attachment filename.`); } ctx.set('content-type', 'application/pdf'); ctx.set('content-disposition', headerContentDisposition); logger.info(`[ZEFYS][PDF] Deliver pdf for ${filename}.`); await client.getPdf(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); logger.error(`Error while deliver pdf: ${error}.`); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Download IIIF manifest.
get/^(\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X))|(\/(HoPo)\d{3})|(\/([a-z0-9]*)))\/manifest$/
Get a JSON file for the specified id.
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); const urlParams = utilsMain.getUrlParams(ctx.request.url); const filename = `${urlParams.id}.manifest.json`; ctx.set('content-type', 'application/ld+json; charset=utf-8'); ctx.set('content-disposition', `attachment; filename="${filename}"`); logger.info(`Deliver manifest for ${urlParams.id}.`); ctx.body = await client.getIIIFManifest(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); logger.error(`Error while deliver manifest: ${error}.`); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Delete images from the cache.
get/^((\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X)-(((\d{4}|\d{8}))|(,(\d{4}|\d{8}))|((\d{4}|\d{8}),)|((\d{4}|\d{8}),(\d{4}|\d{8}))))|(\/SNP([a-zA-Z0-9-]*){6})|(\/(HoPo)\d{3}-\d{4})|(\/([a-z0-9]*)-\d{4}))|(\/dc|\/zefys|\/hopo|\/ebdb|)((\/(PPN|)\d{8,9}(\d{1}|X))|(\/(HoPo)\d{3})|(\/([a-z0-9]*)))).delete.cache$/
Can be called for a specific image or for an object.
Handler
async ctx => { try { const urlParams = utilsMain.getUrlParams(ctx.request.url); const imageId = urlParams.id.replace('.delete.cache', ''); const app = require('../app'); utilsImage.deleteImagesFromCache(app.config.cache.directory, imageId); ctx.set('content-type', 'application/json; charset=utf-8'); ctx.body = {"ack": true}; logger.info(`Delete image cache for ${urlParams.id}.`); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); logger.error(`Error while deleting image cache: ${error}.`); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Plugins
Route definitions from the plugins.
The server can have extra routes in the extensible plugins.
Logo small
get/^\/dc\/logo-small(\/)?$/i
Small logo image of the SBB.
Handler
async ctx => { const client = utilsClients.getClient(ctx.request.url); const imageLogo = client.getImageLogoSmall(); ctx.set('content-type', 'image/png'); ctx.set('content-disposition', `inline; filename: ${imageLogo.filename}`); ctx.set('content-length', new String(imageLogo.imageData).length); ctx.body = imageLogo.imageData; }
Download METS file as xml.
get/^\/dc\/(PPN|)\d{8,9}(\d{1}|X).mets.xml$/
Get a XML (METS) file for the specified object id.
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); let filename = ctx.request.url.split('/')[2].split('.')[0]; ctx.set('content-type', 'application/xml; charset=utf-8'); ctx.set('content-disposition', `attachment; filename="sbb-mets-${filename}.xml"`); ctx.body = await client.getMetsFile(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Download OCR file as xml.
get/^\/dc\/(PPN|)\d{8,9}(\d{1}|X)-(\d{4}|\d{8}).ocr.xml$/
Get a XML (ALTO) file for the specified object id.
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); let filename = ctx.request.url.split('/')[2].split('.')[0]; ctx.set('content-type', 'application/xml; charset=utf-8'); ctx.set('content-disposition', `attachment; filename="sbb-ocr-alto-${filename}.xml"`); ctx.body = await client.getOcrFile(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Download OCR files as zip.
get/^\/dc\/(PPN|)\d{8,9}(\d{1}|X).ocr.zip$/
Get a ZIP file for the specified object id containing all ALTO files of the OCR.
Query: Object
Key = default | Type* (tests) in [valids] not in [invalids] | Eaxmples | Description |
---|---|---|---|
range | string [length(9)] |
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); await client.getOcrZip(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Download OCR files as text.
get/^\/dc\/(PPN|)\d{8,9}(\d{1}|X).ocr.txt$/
Get a TXT file for the specified object id containing all OCR as simple text.
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); let filename = ctx.request.url.split('/')[2].split('.')[0]; ctx.set('content-type', 'text/plain; charset=utf-8'); ctx.set('content-disposition', `attachment; filename="sbb-${filename}.ocr.txt"`); ctx.body = await client.getOcrTxt(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Download XML.
get/^\/zefys((\/(PPN|)\d{8,9}(\d{1}|X)-(((\d{4}|\d{8}))|(,(\d{4}|\d{8}))|((\d{4}|\d{8}),)|((\d{4}|\d{8}),(\d{4}|\d{8}))))|(\/SNP([a-zA-Z0-9-]*){6})|(\/(HoPo)\d{3}-\d{4})|(\/([a-z0-9]*)-\d{4}))(.alto)?.xml$/
Get a XML file for the specified image (ALTO) or issue (METS).
Handler
async ctx => { try { const client = utilsClients.getClient(ctx.request.url); const filename = ctx.request.url.split('/')[2]; ctx.set('content-type', 'application/xml; charset=utf-8'); ctx.set('content-disposition', `attachment; filename: "${filename}"`); await client.getXml(ctx); } catch (error) { ctx.set('content-type', 'text/plain'); ctx.remove('content-disposition'); if (error.isBoom) { ctx.status = error.output.statusCode; ctx.body = error.output.payload.message; } else { // istanbul ignore next throw error; } } }
Logo small
get/^\/hopo\/logo-small(\/)?$/i
Small logo image of the E.T.A. Hoffmann Portal.
Handler
async ctx => { const client = utilsClients.getClient(ctx.request.url); const imageLogo = client.getImageLogoSmall(); ctx.set('content-type', 'image/png'); ctx.set('content-disposition', `inline; filename: ${imageLogo.filename}`); ctx.set('content-length', new String(imageLogo.imageData).length); ctx.body = imageLogo.imageData; }