@allmaps/iiif-parser
Version:
Allmaps IIIF parser
159 lines (158 loc) • 6.11 kB
JavaScript
import { Manifest2Schema, Manifest3Schema, ManifestSchema } from '../schemas/iiif.js';
import { Image, EmbeddedImage } from './image.js';
import { Canvas } from './canvas.js';
import { parseVersion2String, parseVersion3String, parseVersion2Metadata, parseVersion3Metadata, parseVersion2Attribution, parseVersion2Thumbnail, parseVersion2Rendering, parseVersion2Related } from '../lib/convert.js';
const ManifestTypeString = 'manifest';
/**
* Parsed IIIF Manifest, embedded in a Collection
* @property embedded - Whether the Manifest is embedded in a Collection
* @property uri - URI of Manifest
* @property label - Label of Manifest
* @property majorVersion - IIIF API version of Manifest
* @property type - Resource type, equals 'manifest'
*/
export class EmbeddedManifest {
embedded = true;
uri;
type = ManifestTypeString;
label;
majorVersion;
constructor(parsedManifest) {
if ('@type' in parsedManifest) {
// IIIF Presentation API 2.0
this.uri = parsedManifest['@id'];
this.majorVersion = 2;
this.label = parseVersion2String(parsedManifest.label);
}
else if ('type' in parsedManifest) {
// IIIF Presentation API 3.0
this.uri = parsedManifest.id;
this.majorVersion = 3;
this.label = parseVersion3String(parsedManifest.label);
}
else {
throw new Error('Unsupported Manifest');
}
}
}
/**
* Parsed IIIF Manifest
*
* @property canvases - Array of parsed canvases
* @property description - Description of Manifest
* @property metadata - Metadata of Manifest
*/
export class Manifest extends EmbeddedManifest {
canvases = [];
description;
metadata;
navDate;
navPlace;
homepage;
thumbnail;
rendering;
seeAlso;
summary;
requiredStatement;
annotations;
embedded = false;
constructor(parsedManifest) {
super(parsedManifest);
if ('@type' in parsedManifest) {
// IIIF Presentation API 2.0
this.description = parseVersion2String(parsedManifest.description);
this.metadata = parseVersion2Metadata(parsedManifest.metadata);
const sequence = parsedManifest.sequences[0];
this.canvases = sequence.canvases.map((canvas) => new Canvas(canvas));
this.navDate = parsedManifest.navDate;
this.navPlace = parsedManifest.navPlace;
this.requiredStatement = parseVersion2Attribution(parsedManifest.attribution);
this.thumbnail = parseVersion2Thumbnail(parsedManifest.thumbnail);
this.rendering = parseVersion2Rendering(parsedManifest.rendering);
this.homepage = parseVersion2Related(parsedManifest.related);
}
else if ('type' in parsedManifest) {
// IIIF Presentation API 3.0
this.description = parseVersion3String(parsedManifest.description);
this.metadata = parseVersion3Metadata(parsedManifest.metadata);
this.metadata = parseVersion3Metadata(parsedManifest.metadata);
this.navDate = parsedManifest.navDate;
this.navPlace = parsedManifest.navPlace;
this.homepage = parsedManifest.homepage;
this.thumbnail = parsedManifest.thumbnail;
this.rendering = parsedManifest.rendering;
this.seeAlso = parsedManifest.seeAlso;
this.summary = parsedManifest.summary;
this.requiredStatement = parsedManifest.requiredStatement;
this.annotations = parsedManifest.annotations;
this.canvases = parsedManifest.items.map((canvas) => new Canvas(canvas));
}
else {
throw new Error('Unsupported Manifest');
}
}
/**
* Parses a IIIF resource and returns a [Manifest](#manifest) containing the parsed version
* @param iiifManifest - Source data of IIIF Manifest
* @param majorVersion - IIIF API version of Manifest. If not provided, it will be determined automatically
* @returns Parsed IIIF Manifest
*/
static parse(iiifManifest, majorVersion = null) {
let parsedManifest;
if (majorVersion === 2) {
parsedManifest = Manifest2Schema.parse(iiifManifest);
}
else if (majorVersion === 3) {
parsedManifest = Manifest3Schema.parse(iiifManifest);
}
else {
parsedManifest = ManifestSchema.parse(iiifManifest);
}
return new Manifest(parsedManifest);
}
get images() {
return this.canvases.map((canvas) => canvas.image);
}
async #fetchImage(image, fetchFn) {
if (image instanceof EmbeddedImage) {
const url = `${image.uri}/info.json`;
const iiifImage = await fetchFn(url).then((response) => response.json());
const fetchedImage = Image.parse(iiifImage);
return fetchedImage;
}
else {
return image;
}
}
async fetchAll(fetchFn = globalThis.fetch) {
const results = [];
for await (const next of this.fetchNext(fetchFn)) {
results.push(next);
}
return results;
}
async *fetchNext(fetchFn = globalThis.fetch, depth = 0) {
for (const canvas of this.canvases) {
const fetchedImage = await this.#fetchImage(canvas.image, fetchFn);
canvas.image = fetchedImage;
yield {
item: fetchedImage,
depth,
parent: {
uri: this.uri,
type: this.type
}
};
}
}
// TODO: implement fetchByUri function, also for collections
async fetchImageByUri(imageUri, fetchFn = globalThis.fetch) {
for (const canvas of this.canvases) {
if (canvas.image.uri === imageUri) {
const fetchedImage = await this.#fetchImage(canvas.image, fetchFn);
canvas.image = fetchedImage;
return fetchedImage;
}
}
}
}