IIIF Image API
Using the IIIF Image API¶
Image Request¶
Info
See the IIIF Image API reference v2.1 or v3.0 for further details on the URI syntax (region, size, rotation, quality and format).
The IIIF Image API URI for requesting an image defines the following URI Template:
{scheme}://{server}{/prefix}/{identifier}/{region}/{size}/{rotation}/{quality}.{format}
Warning
Strong recommendation about output formats
The jpg format is the recommended output format, complying with the IIIF Image API level 2 specification and can be processed by IIIF viewers.
As our storage format is jxl format, we offer also jxl as output format. The most efficient request pattern corresponds with the sizes of the pre-generated image derivatives, as they can be delivered directly without re-encoding. Therefore we recommend this output format wherever applicable with your viewer / front end.
The png format is exposed mainly to preserve IIIF Image API level 2 compliance alongside jpg.
The webp format is also exposed, but it is strongly discouraged for regular viewer usage.
png and webp requests are generated on demand and can require very long encoding times.
For the University Library IIIF Server, the {scheme}://{server} part is https://iiif.ub.unibe.ch followed by /image/{version} as {prefix} where version is either v2.1 or v3.0. For the following examples we will use v3.0 as {version} (https://iiif.ub.unibe.ch/image/v3.0/...)
There are two ways on the University Library IIIF Server to call a specific image:
-
By image identifier
Here the ID of the image is used as identifier to call the IIIF Image API:
Example with image IDe61b22ed-3d94-4322-9633-e4b9a3df1def:
https://iiif.ub.unibe.ch/image/v3.0/e61b22ed-3d94-4322-9633-e4b9a3df1def/full/max/0/default.jpg
The ID of the image can be found in the Image View or in the CSV export created in the Collection or Sequence View. -
By project slug and image source file name
Here the project slug will be added to the{prefix}value:
Example with project slugmy-project:https://iiif.ub.unibe.ch/image/v3.0/my-project/...
As image identifier the image source file name (original file name in import, unique within a project) is used.
Example with project slughallerand source file namegga_00001_001.tif:
https://iiif.ub.unibe.ch/image/v3.0/haller/gga_00001_001.tif/full/max/0/default.jpg
The slug of the project can be found on the Project View), the source file name of the image can be found in the Image View). Both pieces of information can also be found in the CSV export created in the Collection or Sequence View).
Info
When using the variant with the project slug and image source file name, an HTTP redirect to the first variant (image identifier) will be created. This means for every such request to the image API the browser has to do a second request. This causes a minimal delay (microseconds) on each request and increases the load on the web server. We recommend to use the first variant whenever possible.
Using JXL in a Viewer¶
The IIIF Image API of this server supports jxl as output format in addition to jpg, png and webp.
For viewer integrations, the recommended strategy is:
- Load the regular IIIF
info.jsonas usual. - Keep the IIIF metadata unchanged.
- Only rewrite the generated tile image requests from
.jpgor.pngto.jxlwhen the browser supports JXL. - Fall back to the unmodified IIIF tile URLs when the browser does not support JXL.
This is the same pattern used by the administration viewer.
Recommended OpenSeadragon integration¶
For OpenSeadragon, our current recommendation is:
- Use the IIIF
v2.1info.jsonURL as the tile source. - Let OpenSeadragon build tile URLs normally.
- Patch
getTileUrl()after the tile source is available and replace the file extension only.
Example:
import OpenSeadragon from 'openseadragon';
const viewer = OpenSeadragon({
element: document.getElementById('viewer'),
tileSources: ['https://iiif.ub.unibe.ch/image/v2.1/{identifier}/info.json']
});
viewer.addHandler('open', () => {
const tiledImage = viewer.world.getItemAt(0);
const originalGetTileUrl = tiledImage.source.getTileUrl;
tiledImage.source.getTileUrl = function(level, x, y) {
const url = originalGetTileUrl.call(this, level, x, y);
return url ? url.replace(/\.(jpg|png)$/, '.jxl') : url;
};
});
Detecting browser support¶
Only enable the JXL rewrite when the browser can decode image/jxl. If the browser cannot decode JXL, keep the original IIIF tile URLs unchanged.
In our administration viewer, support is detected in two phases:
- First, the server checks the browser's
Acceptheader forimage/jxland passes that information to the Stimulus OpenSeadragon controller as an initial hint. - If that hint is not present, the client performs its own fallback check with a tiny in-memory JXL image, so no extra network request is needed.
This means the administration viewer uses the header-based signal as a fast path and the tiny image probe as a fallback verification path.
If your application can inspect the request headers on the server side, you can expose the initial Accept: image/jxl result to the frontend and use it as the fast path. In this project, the admin page passes that hint into the Stimulus controller as the jxlSupported value. If not, the client-side probe alone is still a valid implementation.
Complete example:
import OpenSeadragon from 'openseadragon';
const JXL_PROBE_DATA_URL = 'data:image/jxl;base64,/woAEBAUNwIIAgEAHABLEqVChSQM';
const JXL_TILE_EXTENSION_PATTERN = /\.(jpg|png)$/;
let jxlSupportProbePromise;
function hasInitialJxlHint(jxlSupported) {
return jxlSupported;
}
async function detectJxlSupport(jxlSupported = false) {
if (hasInitialJxlHint(jxlSupported)) {
return true;
}
if (!jxlSupportProbePromise) {
jxlSupportProbePromise = new Promise((resolve) => {
const image = new Image();
let settled = false;
const finish = (result) => {
if (settled) {
return;
}
settled = true;
resolve(result);
};
image.onload = () => finish(true);
image.onerror = () => finish(false);
image.src = JXL_PROBE_DATA_URL;
// Browsers that neither decode nor error should not block viewer startup.
window.setTimeout(() => finish(false), 1500);
});
}
return jxlSupportProbePromise;
}
function enableJxlSupportAfterDetection(viewer) {
const patchTileSources = () => {
let patchedAnySource = false;
const count = viewer.world.getItemCount();
for (let i = 0; i < count; i++) {
const tiledImage = viewer.world.getItemAt(i);
if (!tiledImage.source || !tiledImage.source.getTileUrl || tiledImage.source._iiifJxlPatched) {
continue;
}
const originalGetTileUrl = tiledImage.source.getTileUrl;
tiledImage.source.getTileUrl = function(level, x, y) {
const url = originalGetTileUrl.call(this, level, x, y);
return url ? url.replace(JXL_TILE_EXTENSION_PATTERN, '.jxl') : url;
};
tiledImage.source._iiifJxlPatched = true;
patchedAnySource = true;
}
return patchedAnySource;
};
viewer.addHandler('open', patchTileSources);
return patchTileSources();
}
const imageInfoUrl = 'https://iiif.ub.unibe.ch/image/v2.1/{identifier}/info.json';
const viewer = OpenSeadragon({
element: document.getElementById('viewer'),
tileSources: [imageInfoUrl]
});
if (hasJxlAcceptHeader()) {
enableJxlSupportAfterDetection(viewer);
} else {
detectJxlSupport().then((isJxlSupported) => {
if (!isJxlSupported) {
return;
}
const didEnableJxl = enableJxlSupportAfterDetection(viewer);
if (didEnableJxl && viewer.isOpen()) {
viewer.open(imageInfoUrl);
}
});
}
Why the rewrite happens at the tile URL level¶
The important point is that the IIIF Image API description does not need to change for JXL usage in the viewer. The client can:
- use the standard
info.json - use the standard IIIF tiling metadata
- only switch the requested tile format at request time
This keeps the integration compatible with viewers that do not support JXL and makes fallback behavior straightforward.
Image Information Request¶
The IIIF Image API URI for requesting an image defines the following URI Template:
{scheme}://{server}{/prefix}/{identifier}/info.json
The same two addressing methods apply here. The following examples show the info.json endpoint for each:
-
By image identifier — example with image ID
e61b22ed-3d94-4322-9633-e4b9a3df1def:
https://iiif.ub.unibe.ch/image/v3.0/e61b22ed-3d94-4322-9633-e4b9a3df1def/info.json -
By project slug and image source file name — example with project slug
hallerand source file namegga_00001_001.tif:
https://iiif.ub.unibe.ch/image/v3.0/haller/gga_00001_001.tif/info.json
Warning
When using the variant with the project slug and image source file name, a HTTP redirect to the first variant (image identifier) will be created. This means for every such request to the image API the browser has to do a second request. This causes a minimal delay (microseconds) on each request and increases the load on the web server. We recommend to use the first variant whenever possible.