Substantial refactoring of code structure

This commit is contained in:
2026-03-23 15:20:30 +01:00
parent 4b9b5b737d
commit 012ad62382
23 changed files with 565 additions and 482 deletions

View File

@@ -1,12 +0,0 @@
/**
* @interface
*/
class IIIFResource {
id;
type;
generateId(serviceURL, filename) {}
toObject() {}
}
export default IIIFResource;

View File

@@ -1,292 +0,0 @@
'use strict';
import * as fs from 'fs';
import Manifest from './Manifest.js';
import Sequence from './Sequence.js';
import Canvas from './Canvas.js';
import Image from './Image.js';
import ManifestMetadata from './Metadata.js';
/**
* @namespace Common
*/
const Common = {};
const authors = {
DAN: 'Danilo P. Pavone',
AUR: 'Aurélie Tournié',
SAB: 'Sabrina Samelo',
SOF: 'Sofia Ceccarelli',
};
/**
* @param {string} imgFilename
* @returns {{papyrus:string,imageAuthor:string,date:string}}
*/
function extractHIROXNIRMetadata(imgFilename) {
return {
papyrus: imgFilename.split('_')[0].split('-')[1],
imageAuthor: authors[imgFilename.split('-')[0].replace(/\d{4}/,'')],
date: imgFilename.split('-')[0].match(/\d{4}/)[0],
}
}
/**
* @param {string} imgFilename
* @returns {{papyrus:string,imageAuthor:string,date:string}}
*/
function extractNIRMetadata(imgFilename) {
return {
papyrus: imgFilename.split('_')[0].split('-')[2],
imageAuthor: authors[imgFilename.split('-')[0].replace(/\d{4}/,'')],
date: imgFilename.split('-')[0].match(/\d{4}/)[0],
}
}
/**
* @param {string} imgFilename
* @returns {{papyrus:string,imageAuthor:string,date:string,copyright:string}}
*/
function extractDNMetadata(imgFilename) {
return {
papyrus: imgFilename.split('_')[1],
imageAuthor: '',
date: '',
copyright: "Ministero della Cultura (Biblioteca Nazionale 'Vittorio Emanuele III' di Napoli)",
}
}
/**
* @param {string} imgFilename
* @returns {{papyrus:string,imageAuthor:string,date:string,copyright:string}}
*/
function extractDOMetadata(imgFilename) {
return {
papyrus: imgFilename.split('_')[1],
imageAuthor: '',
date: '',
copyright: "The Bodleian Libraries, University of Oxford",
}
}
Common.TECH_NAMES = {
dn: "Disegni Napoletani",
do: "Disegni Oxoniensi",
nir: "Near Infrared Imaging 1000nm",
hsi: "SWIR Hyperspectral Imaging",
uvf: "Technical Photography UVF",
mbi: "Multispectral Imaging",
hiroxnir: "HIROX Near Infrared",
};
/**
* Retrieves available image techniques
* for all papyruses based on folder contents
* @returns {object}
*/
Common.getParamsFromFolders = async function() {
let params = [];
const papyri = await fs.promises.readdir(process.env.IMAGES_DIR);
for (let p of papyri) {
let techniques = [];
for (let tech of await fs.promises.readdir(`${process.env.IMAGES_DIR}/${p}`)) {
let files = await fs.promises.readdir(
`${process.env.IMAGES_DIR}/${p}/${tech}`
);
files = files.filter(file => /(tiff?|jpe?g|jp2|bmp)/.test(file));
if (files.length) {
techniques.push(tech.replace(/PHerc_\d+_/i, ''));
}
}
params.push({
name : p.replace('_', ' '),
techniques
});
}
return params;
}
/**
* @param {string} manifestId
* @returns {string[]}
* @throws `readdir` will thrown an ENOENT error if the images folder doesn't exist.
* The manifest route should catch it.
*/
Common.getImageList = async function (manifestId) {
// Regex to exclude images with certain patterns in filename
const regexFilter = new RegExp(/(c2r|copertin.|camice|tit)/, 'i');
let folderName = manifestId.replace(/pherc-(\d+)-(\w+)$/, function (_match, g1, g2) {
return `PHerc_${g1}_${g2.toUpperCase()}`;
});
let baseFolder = `${folderName.split('_')[0]}_${folderName.split('_')[1]}`;
let files = await fs.promises.readdir(
`${process.env.IMAGES_DIR}/${baseFolder}/${folderName}`
);
files = files.filter(file => !regexFilter.test(file) && !file.startsWith('.'));
return files;
}
/**
* @param {string} imageId The image's id as a URL to the image server
* @returns {{width: number, height: number, thumb: {width: number, height: number}}}
*/
Common.getImageSize = async function (imageId) {
let infoURL = imageId.replace(/full.*$/,'info.json');
const res = await fetch(infoURL);
let size = {};
if (res.ok) {
const infoJson = await res.json();
const maxSize = infoJson.sizes[infoJson.sizes.length - 1];
size.height = maxSize.height;
size.width = maxSize.width;
size.thumb = {
width: infoJson.sizes[1].width,
height: infoJson.sizes[1].height,
}
}
return size;
}
/**
* Get image name for given canvas
* @todo Use regex in filter!!
* @param {Canvas} canvas
* @returns {string}
*/
Common.getImageName = async function (canvas) {
const images = await this.getImageList(canvas.resourceId);
let name = canvas.name;
// Adjust canvas name for HSI with PCA...
if (/pc(1|3)/.test(name)) {
name = name.replace(
/pc((1|3))/,
function (match, group1) {
return `_HSI_PC${group1}`;
}
);
}
console.log('Canvas name: ' + name);
return images.filter(i => i.includes(name))[0];
}
/**
* Create a canvas from an image filename
* @param {Manifest} manifest
* @param {string} filename The image filename
* @returns {Canvas}
*/
Common.createCanvas = async function (manifest, filename) {
let canvas = new Canvas(
process.env.IIIF_API_VERSION,
process.env.BASE_URL
);
const namePos = {
hiroxnir: 1,
nir: 1,
hsi: 1,
do: 2,
dn: 2
};
// Remove file extension
let canvasName = filename.split('_')[namePos[manifest.technique]]
.replace(/\.\w{1,3}$/, '');
let label = canvasName.replace(
/c(\w{1,2})0+(\d+).*(\.\w{2,3})?$/i,
function (str, c, number) {
return `C${c}. ${number}`;
});
// Add PCA to canvas label for HSI images
if (manifest.technique === 'hsi') {
label += ` ${filename.split('_')[3].replace(/\..*$/,'')}`;
canvasName += `${filename.split('_')[3].replace(/\..*$/,'')}`;
}
canvas.generateID(manifest.resourceId, canvasName.toLowerCase());
canvas.label = label;
let image = new Image(canvas.id);
image.generateID(process.env.IMAGE_SERVER_URL, filename);
const imgSize = await this.getImageSize(image.id);
image.setSize(imgSize.height, imgSize.width);
canvas.setThumbnail(
imgSize.thumb.height,
imgSize.thumb.width,
image.id
);
canvas.addImage(image);
return canvas;
}
/**
* @param {Manifest} manifest The manifest object
* @param {string[]} images List of image filenames from folder
* @returns {Manifest}
*/
Common.populateCanvases = async function (manifest, images) {
const sequence = new Sequence(process.env.BASE_URL);
// There's only one sequence
sequence.generateID(manifest.resourceId, 0);
for (let img of images) {
// Skip failing images (TODO log error to file)
try {
let canvas = await this.createCanvas(manifest, img);
sequence.addCanvas(canvas);
} catch (error) {
console.error(error);
console.log(`\nAffected image: ${img}`);
continue;
}
}
// Sort them according to their ID number... (cornice, colonna, ecc...)
sequence.canvases.sort((a, b) => {
const firstId = a['@id'].slice(a['@id'].lastIndexOf('/') + 1).replace(/[a-z]+/ig,'');
const secondId = b['@id'].slice(b['@id'].lastIndexOf('/') + 1).replace(/[a-z]+/ig,'');
return Number(firstId) - Number(secondId);
});
manifest.addSequence(sequence);
return manifest;
}
/**
* @param {Manifest} manifest The Manifest object
* @param {string} imgFilename
* @returns {ManifestMetadata}
*/
Common.createMetadata = function (manifest, imgFilename) {
let metadata = this.getMetadataFromImgName(imgFilename, manifest.technique);
metadata.technique = Common.TECH_NAMES[manifest.technique];
return new ManifestMetadata(metadata);
}
/**
* @param {string} imgFilename
* @param {string} technique
* @returns {{papyrus:string,imageAuthor:string,date:string,copyright:?string}}
*/
Common.getMetadataFromImgName = function (imgFilename, technique) {
const extractor = {
nir: extractNIRMetadata,
dn: extractDNMetadata,
do: extractDOMetadata,
hsi: extractNIRMetadata,
hiroxnir: extractHIROXNIRMetadata,
}
return extractor[technique](imgFilename);
}
export default Common;

24
src/constants.js Normal file
View File

@@ -0,0 +1,24 @@
export const authors = {
DAN: 'Danilo P. Pavone',
AUR: 'Aurélie Tournié',
SAB: 'Sabrina Samelo',
SOF: 'Sofia Ceccarelli',
};
export const TECH_NAMES = {
dn: "Disegni Napoletani",
do: "Disegni Oxoniensi",
nir: "Near Infrared Imaging 1000nm",
visr: "Visible Raking Light",
hsi: "SWIR Hyperspectral Imaging",
uvf: "Technical Photography UVF",
mbi: "Multispectral Imaging",
hiroxnir: "HIROX Near Infrared",
};
export const COPYRIGHT = {
dn: "Ministero della Cultura (Biblioteca Nazionale 'Vittorio Emanuele III' di Napoli)",
do: "The Bodleian Libraries, University of Oxford",
nir: "CNR - Consiglio Nazionale delle Ricerche, Ministero della Cultura (Biblioteca Nazionale 'Vittorio Emanuele III' di Napoli)",
visr: "CNR - Consiglio Nazionale delle Ricerche, Ministero della Cultura (Biblioteca Nazionale 'Vittorio Emanuele III' di Napoli)",
};

View File

@@ -1,27 +1,30 @@
import IIIFResource from './IIIFResource.js';
import Image from './Image.js';
/**
* @implements IIIFResource
*/
class Canvas {
import ManifestMetadata from './Metadata.js';
class Canvas extends IIIFResource {
id = '';
#type = 'sc:Canvas';
#label = '';
#metadata = {};
resourceId = '';
name = '';
images = [];
thumbnail = {};
/**
* @param {Number} IIIFApiVersion
* @param {String} baseURL
*/
constructor(IIIFApiVersion, baseURL) {
this.context = `https://iiif.io/api/presentation/${IIIFApiVersion}/context.json`;
this.BASE_URL = baseURL;
super(IIIFApiVersion, baseURL);
}
/**
* @param {string} resourceId The resource ID for this canvas
* @param {int|string} name A unique name for this canvas
*/
generateID(resourceId, name) {
name = name.replace(/cr([1-9]$)/, `cr0$1`);
//name = name.replace(/cr([1-9]$)/, `cr0$1`);
this.id = `${this.BASE_URL}/${resourceId}/canvas/${name}`;
this.resourceId = resourceId;
this.name = name;
@@ -53,6 +56,12 @@ class Canvas {
width
}
}
/**
* @param {ManifestMetadata} metadata
*/
setMetadata(metadata) {
this.#metadata = metadata.toObject();
}
/**
* Object representation
* @returns {object}
@@ -63,6 +72,7 @@ class Canvas {
"@id" : this.id,
"@type" : this.#type,
"label" : this.#label,
"metadata" : this.#metadata,
"images" : this.images,
"thumbnail" : this.thumbnail
}

19
src/iiif/IIIFResource.js Normal file
View File

@@ -0,0 +1,19 @@
/**
* @interface
*/
class IIIFResource {
/**
* @param {Number} IIIFApiVersion
* @param {String} baseURL
*/
constructor(IIIFApiVersion, baseURL) {
this.context = `https://iiif.io/api/presentation/${IIIFApiVersion}/context.json`;
this.BASE_URL = baseURL;
}
generateId(serviceURL, filename) {}
toObject() {}
}
export default IIIFResource;

View File

@@ -1,53 +1,8 @@
'use strict;'
import { getImagePath } from '../service/FilenameParser.js';
import IIIFResource from './IIIFResource.js';
/**
* @todo Move to common.js?!
*/
const splitter = {
HIROXNIR: splitHIROXNIR,
NIR: splitNIR,
DN: splitDNO,
DO: splitDNO,
HSI: splitHSI
};
function splitHIROXNIR(filename) {
let splitFilename = filename.split('_');
const papyrusNum = splitFilename[0].split('-')[1];
const baseFolder = `PHerc_${papyrusNum}`;
const subfolder = `PHerc_${papyrusNum}_HIROXNIR`;
return {baseFolder, subfolder};
}
function splitNIR(filename) {
let splitFilename = filename.split('_');
const papyrusNum = splitFilename[0].split('-')[2];
const baseFolder = `PHerc_${papyrusNum}`;
const subfolder = `PHerc_${papyrusNum}_${splitFilename[2].split('-')[0]}`;
return {baseFolder, subfolder};
}
/**
* @todo Redundant...
*/
function splitHSI(filename) {
let splitFilename = filename.split('_');
const papyrusNumb = splitFilename[0].split('-')[2];
const baseFolder = `PHerc_${papyrusNumb}`;
const subfolder = `PHerc_${papyrusNumb}_${splitFilename[2]}`;
return {baseFolder, subfolder};
}
function splitDNO(filename) {
let splitFilename = filename.split('_');
const papyrusNumb = splitFilename[1];
const baseFolder = `PHerc_${papyrusNumb}`;
const subfolder = `PHerc_${papyrusNumb}_${splitFilename[0]}`;
return {baseFolder, subfolder};
}
/**
* @implements IIIFResource
*/
@@ -87,11 +42,10 @@ class Image {
* server endpoint for this image
* @param {string} serviceURL The image server base URL
* @param {string} filename The image's complete filename
* @param {string} technique The imaging technique (from the manifest)
*/
generateID(serviceURL, filename) {
let splitFn = splitter[/((HIROX)?NIR|DO|DN|HSI)/.exec(filename)[0]];
const {baseFolder, subfolder} = splitFn(filename);
generateID(serviceURL, filename, technique) {
const {baseFolder, subfolder} = getImagePath(filename, technique);
this.id = `${serviceURL}/${this.#IIIF_API_VERSION}/${baseFolder}%2F${subfolder}%2F${filename}/full/max/0/default.jpg`;
this.service['@id'] = this.id.replace(/\/full.*$/,'');

View File

@@ -2,10 +2,7 @@ import IIIFResource from './IIIFResource.js';
import Sequence from "./Sequence.js";
import ManifestMetadata from './Metadata.js';
/**
* @implements IIIFResource
*/
class Manifest {
class Manifest extends IIIFResource {
id = '';
#type = 'sc:Manifest';
#label = '';
@@ -17,9 +14,12 @@ class Manifest {
*/
sequences = [];
/**
* @param {Number} IIIFApiVersion
* @param {String} baseURL
*/
constructor(IIIFApiVersion, baseURL) {
this.context = `https://iiif.io/api/presentation/${IIIFApiVersion}/context.json`;
this.BASE_URL = baseURL;
super(IIIFApiVersion, baseURL);
}
get technique() {
return this.#technique;
@@ -46,7 +46,6 @@ class Manifest {
this.#label = `P.Herc. ${this.resourceId.split('-')[1]}`;
}
/**
*
* @param {ManifestMetadata} metadata
*/
setMetadata(metadata) {

View File

@@ -1,20 +0,0 @@
{
"source": {
"include": ["src/"],
"includePattern": ".js$",
"excludePattern": "(node_modules/|docs)"
},
"plugins": ["plugins/markdown"],
"opts": {
"encoding": "utf8",
"destination": "docs/",
"recurse": true,
"verbose": true,
"template": "/home/nicolo/.npm-global/lib/node_modules/clean-jsdoc-theme",
"theme_opts": {
"theme": "light"
}
}
}

View File

@@ -0,0 +1,206 @@
'use strict;'
import { authors, COPYRIGHT } from '../constants.js';
/**
* This module parses image filenames to extract
* relevant information, according to predefined naming
* conventions.
*
* Although `parseDNFilename()` and `parseDOFilename()` are
* functionally equivalent, they're kept separate because
* it's not clear at the moment whether the naming conventions
* for the two techniques will remain equivalent.
* @module FilenameParser
*/
/**
* @typedef ParsedMetadata
* @property {String} papyrus
* @property {String} imageAuthor
* @property {String} date
* @property {String} [copyright]
*/
/**
* @typedef ImgFolderNames
* @property {String} baseFolder
* @property {String} subfolder
*/
const namePos = {
hiroxnir: 1,
nir: 1,
visr: 1,
hsi: 1,
do: 2,
dn: 2
};
/**
* @param {string} imgFilename
* @returns {ParsedMetadata}
*/
function parseHIROXNIRFilename(imgFilename) {
const prefix = imgFilename.split('_')[0];
const parts = prefix.split('-');
return {
papyrus: parts[1],
imageAuthor: authors[parts[0].replace(/\d{4}/,'')],
date: parts[0].match(/\d{4}/)[0],
}
}
/**
* @param {string} imgFilename
* @returns {ParsedMetadata}
*/
function parseNIRFilename(imgFilename) {
const prefix = imgFilename.split('_')[0];
const parts = prefix.split('-');
return {
papyrus: parts[2],
imageAuthor: authors[parts[0].replace(/\d{4}/, '')],
date: parts[0].match(/\d{4}/)[0],
copyright: COPYRIGHT.nir,
}
}
/**
* @param {string} imgFilename
* @returns {ParsedMetadata}
*/
function parseDNFilename(imgFilename) {
return {
papyrus: imgFilename.split('_')[1],
imageAuthor: '',
date: '',
copyright: COPYRIGHT.dn,
}
}
/**
* @param {string} imgFilename
* @returns {ParsedMetadata}
*/
function parseDOFilename(imgFilename) {
return {
papyrus: imgFilename.split('_')[1],
imageAuthor: '',
date: '',
copyright: COPYRIGHT.do,
}
}
/**
* @param {string} filename The image filename
* @returns {ImgFolderNames}
*/
function splitHIROXNIR(filename) {
let splitFilename = filename.split('_');
const papyrusNum = splitFilename[0].split('-')[1];
const baseFolder = `PHerc_${papyrusNum}`;
const subfolder = `PHerc_${papyrusNum}_HIROXNIR`;
return {baseFolder, subfolder};
}
/**
* @param {string} filename The image filename
* @returns {ImgFolderNames}
*/
function splitNIR(filename) {
let splitFilename = filename.split('_');
const papyrusNum = splitFilename[0].split('-')[2];
const baseFolder = `PHerc_${papyrusNum}`;
const subfolder = `PHerc_${papyrusNum}_${splitFilename[2].split('-')[0]}`;
return {baseFolder, subfolder};
}
/**
* @todo Redundant...
* @param {string} filename The image filename
* @returns {ImgFolderNames}
*/
function splitHSI(filename) {
let splitFilename = filename.split('_');
const papyrusNumb = splitFilename[0].split('-')[2];
const baseFolder = `PHerc_${papyrusNumb}`;
const subfolder = `PHerc_${papyrusNumb}_${splitFilename[2]}`;
return {baseFolder, subfolder};
}
/**
* @param {string} filename The image filename
* @returns {ImgFolderNames}
*/
function splitDNO(filename) {
let splitFilename = filename.split('_');
const papyrusNumb = splitFilename[1];
const baseFolder = `PHerc_${papyrusNumb}`;
const subfolder = `PHerc_${papyrusNumb}_${splitFilename[0]}`;
return {baseFolder, subfolder};
}
const splitters = {
hiroxnir: splitHIROXNIR,
nir: splitNIR,
visr: splitNIR,
dn: splitDNO,
do: splitDNO,
hsi: splitHSI
};
const extractors = {
nir: parseNIRFilename,
dn: parseDNFilename,
do: parseDOFilename,
hsi: parseNIRFilename,
visr: parseNIRFilename,
hiroxnir: parseHIROXNIRFilename,
}
/**
* Return metadata object from parsed
* image filename
* @param {string} imgFilename
* @param {string} technique
* @returns {ParsedMetadata}
*/
export function parse (imgFilename, technique) {
return extractors[technique](imgFilename);
}
/**
* Extract canvas name from image filename
* @param {String} imgFilename
* @param {String} technique
* @returns {String}
*/
export function getCanvasName(imgFilename, technique) {
// Remove file extension
let canvasName = imgFilename.split('_')[namePos[technique]]
.replace(/\.\w{1,3}$/, '');
if (technique === 'hsi') {
canvasName += imgFilename.split('_')[3].replace(/\..*$/,'');
}
return canvasName;
}
/**
* Generate canvas label from canvasName
* @todo Verify for HSI filenames
* @param {String} canvasName
* @returns {String}
*/
export function getCanvasLabel(canvasName) {
return canvasName.replace(
/c(\w{1,2})0+(\d+).*(\.\w{2,3})?$/i,
(str, c, number) => `C${c}. ${number}`
);
}
/**
* Get the full path (folder name) for a given image file
* @param {String} filename
* @param {String} technique
* @returns {ImgFolderNames}
*/
export function getImagePath(filename, technique) {
return splitters[technique](filename);
}

View File

@@ -0,0 +1,61 @@
'use strict';
import * as fs from 'fs';
/**
* Handles filesystem for image repository
* @module ImageRepository
*/
/**
* @param {string} manifestId
* @returns {string[]}
* @throws `readdir` will thrown an ENOENT error if the images folder doesn't exist.
* The manifest route should catch it.
*/
export async function getImageList (manifestId) {
// Regex to exclude images with certain patterns in filename
const regexFilter = new RegExp(/(c2r|copertin.|camice|tit)/, 'i');
let folderName = manifestId.replace(/pherc-(\d+)-(\w+)$/, function (_match, g1, g2) {
return `PHerc_${g1}_${g2.toUpperCase()}`;
});
let baseFolder = `${folderName.split('_')[0]}_${folderName.split('_')[1]}`;
let files = await fs.promises.readdir(
`${process.env.IMAGES_DIR}/${baseFolder}/${folderName}`
);
files = files.filter(file => !regexFilter.test(file) && !file.startsWith('.'));
return files;
}
/**
* Retrieves available image techniques
* for all papyruses based on folder contents
* @returns {object}
*/
export async function getParamsFromFolders () {
let params = [];
const papyri = await fs.promises.readdir(process.env.IMAGES_DIR);
for (let p of papyri) {
let techniques = [];
for (let tech of await fs.promises.readdir(`${process.env.IMAGES_DIR}/${p}`)) {
let files = await fs.promises.readdir(
`${process.env.IMAGES_DIR}/${p}/${tech}`
);
files = files.filter(file => /(tiff?|jpe?g|jp2|bmp)/.test(file));
if (files.length) {
techniques.push(tech.replace(/PHerc_\d+_/i, ''));
}
}
params.push({
name : p.replace('_', ' '),
techniques
});
}
return params;
}

View File

@@ -0,0 +1,176 @@
'use strict';
import Manifest from '../iiif/Manifest.js';
import Sequence from '../iiif/Sequence.js';
import Canvas from '../iiif/Canvas.js';
import Image from '../iiif/Image.js';
import ManifestMetadata from '../iiif/Metadata.js';
import { parse, getCanvasLabel, getCanvasName } from './FilenameParser.js';
import { getImageList } from './ImageRepository.js';
import { TECH_NAMES } from '../constants.js';
/**
* Builds a manifest object based on canvases
* and available metadata
* @module ManifestBuilder
*/
const IIIF_API_VERSION = process.env.IIIF_API_VERSION;
const BASE_URL = process.env.BASE_URL;
const IMAGE_SERVER_URL = process.env.IMAGE_SERVER_URL;
/**
* Builds a Manifest object
* @param {String} manifestId
* @returns {Object}
*/
export async function buildManifest(manifestId) {
let manifest = new Manifest(IIIF_API_VERSION, BASE_URL);
manifest.generateID(manifestId);
manifest.generateLabel();
const images = await getImageList(manifestId);
manifest = await populateCanvases(manifest, images, manifestId);
manifest.setMetadata(
createMetadata(
manifest,
images[0], // A single image filename is sufficient to extract metadata
)
);
return manifest.toObject();
}
/**
* Builds a Canvas object from route parameters
* @param {String} manifestId
* @param {String} name The canvas name
*/
export async function buildCanvas(manifestId, name) {
const manifest = new Manifest(IIIF_API_VERSION, BASE_URL);
manifest.generateID(manifestId);
let filename = await getImageName(name, manifestId);
return createCanvas(manifest, filename);
}
/**
* Create a canvas from an image filename
* @param {Manifest} manifest
* @param {string} filename The image filename
* @returns {Canvas}
*/
async function createCanvas (manifest, filename) {
let canvas = new Canvas(IIIF_API_VERSION, BASE_URL);
const canvasName = getCanvasName(filename, manifest.technique);
canvas.generateID(manifest.resourceId, canvasName.toLowerCase());
canvas.label = getCanvasLabel(canvasName);
let image = new Image(canvas.id);
image.generateID(IMAGE_SERVER_URL, filename, manifest.technique);
const imgSize = await getImageSize(image.id);
image.setSize(imgSize.height, imgSize.width);
canvas.setThumbnail(
imgSize.thumb.height,
imgSize.thumb.width,
image.id
);
canvas.addImage(image);
return canvas;
}
/**
* @param {string} imageId The image's id as a URL to the image server
* @returns {{width: number, height: number, thumb: {width: number, height: number}}}
*/
async function getImageSize(imageId) {
let infoURL = imageId.replace(/full.*$/,'info.json');
const res = await fetch(infoURL);
let size = {};
if (res.ok) {
const infoJson = await res.json();
const maxSize = infoJson.sizes[infoJson.sizes.length - 1];
size.height = maxSize.height;
size.width = maxSize.width;
size.thumb = {
width: infoJson.sizes[1].width,
height: infoJson.sizes[1].height,
}
}
return size;
}
/**
* @param {Manifest} manifest The manifest object
* @param {string[]} images List of image filenames from folder
* @returns {Manifest}
*/
async function populateCanvases (manifest, images) {
const sequence = new Sequence(BASE_URL);
// There's only one sequence
sequence.generateID(manifest.resourceId, 0);
for (let img of images) {
// Skip failing images (TODO log error to file)
try {
let canvas = await createCanvas(manifest, img);
sequence.addCanvas(canvas);
} catch (error) {
console.error(error);
console.log(`\nAffected image: ${img}`);
continue;
}
}
// Sort them according to their ID number... (cornice, colonna, ecc...)
sequence.canvases.sort((a, b) => {
const firstId = a['@id'].slice(a['@id'].lastIndexOf('/') + 1).replace(/[a-z]+/ig,'');
const secondId = b['@id'].slice(b['@id'].lastIndexOf('/') + 1).replace(/[a-z]+/ig,'');
return Number(firstId) - Number(secondId);
});
manifest.addSequence(sequence);
return manifest;
}
/**
* Get image name for given canvas
* @todo Use regex in filter!!
* @param {String} name The canvas name
* @param {String} manifestId The manifest (resource) id
* @returns {string}
*/
async function getImageName(name, manifestId) {
const images = await getImageList(manifestId);
// Adjust canvas name for HSI with PCA...
if (/pc(1|3)/.test(name)) {
name = name.replace(
/pc((1|3))/,
function (match, group1) {
return `_HSI_PC${group1}`;
}
);
}
return images.filter(i => i.includes(name))[0];
}
/**
* @param {Manifest} manifest The Manifest object
* @param {string} imgFilename
* @returns {ManifestMetadata}
*/
function createMetadata(manifest, imgFilename) {
let metadata = parse(imgFilename, manifest.technique);
metadata.technique = TECH_NAMES[manifest.technique];
return new ManifestMetadata(metadata);
}