UNPKG

cozy-iiif

Version:

A developer-friendly collection of abstractions and utilities built on top of @iiif/presentation-3 and @iiif/parser

121 lines (97 loc) 3.86 kB
import type { ImageService2, ImageService3, Service } from '@iiif/presentation-3'; import { getPropertyValue } from './resource'; import type { Bounds, CozyImageResource, GetRegionURLOpts } from '../types'; type ImageService = ImageService2 | ImageService3; export const isImageService = (data: any): data is ImageService => { const t = getPropertyValue<string>(data, 'type'); return t.startsWith('ImageService') || ( data.profile && data.profile.toString().includes('iiif.io/api/image/') ); } export const parseImageService = (service: Service) => { const t = getPropertyValue<string>(service, 'type'); const context = getPropertyValue<string>(service, 'context'); if (t === 'ImageService2' || context?.includes('image/2')) { const service2 = service as ImageService2; const labels = ['level0', 'level1', 'level2']; const profiles = Array.isArray(service2.profile) ? service2.profile : [service2.profile]; const levels = profiles .map(profile => labels.findIndex(level => profile.toString().includes(level))) .filter(l => l > -1) .sort((a, b) => b - a); // Sort descending return { majorVersion: 2, profileLevel: levels[0] }; } else if (t || context) { // Image API 3 const service3 = service as ImageService3; return { majorVersion: 3, profileLevel: parseInt(service3.profile)} } } export const getImageURLFromService = ( service: Service, width: number, height: number ): string => { const id = getPropertyValue(service, 'id'); const compliance = service.profile || ''; const isLevel0 = typeof compliance === 'string' && (compliance.includes('level0') || compliance.includes('level:0')); if (isLevel0) { // For level 0, find the closest pre-defined size if ('sizes' in service && Array.isArray(service.sizes)) { const suitableSize = service.sizes .sort((a, b) => (b.width * b.height) - (a.width * a.height)) .filter(s => (s.width * s.height) >= width * height)[0]; if (suitableSize) return `${id}/full/${suitableSize.width},${suitableSize.height}/0/default.jpg`; } // Fallback: full image return `${id}/full/full/0/default.jpg`; } return `${id}/full/!${width},${height}/0/default.jpg`; } export const getRegionURLFromService = ( service: Service, bounds: Bounds, opts: GetRegionURLOpts = { minSize: 400 } ): string | undefined => { const id = getPropertyValue(service, 'id'); const compliance = service.profile || ''; const isLevel0 = typeof compliance === 'string' && (compliance.includes('level0') || compliance.includes('level:0')); if (isLevel0) { console.warn(`Level 0 image service does not support custom region URLs: ${id}`); return; } const { x, y, w , h } = bounds; const { minSize = 400, maxSize } = opts; const aspect = w / h; const isPortrait = aspect < 1; let height = Math.ceil(isPortrait ? minSize / aspect : minSize); let width = Math.ceil(isPortrait ? minSize : minSize / aspect); // Apply maxSize constraint if specified if (maxSize) { if (width > maxSize || height > maxSize) { if (isPortrait) { height = Math.min(height, maxSize); width = Math.ceil(height * aspect); } else { width = Math.min(width, maxSize); height = Math.ceil(width / aspect); } } } const regionParam = `${Math.round(x)},${Math.round(y)},${Math.round(w)},${Math.round(h)}`; return `${id}/${regionParam}/!${width},${height}/0/default.jpg`; } export const getRegionURL = ( image: CozyImageResource ) => ( bounds: Bounds, opts: GetRegionURLOpts = { minSize: 400 } ): string | undefined => { if (image.type === 'dynamic') { return getRegionURLFromService(image.service, bounds, opts); } else { console.error('Level 0 or static image canvas: unsupported'); } }