diff --git a/src/ol/format/IIIFInfo.js b/src/ol/format/IIIFInfo.js index 73e1834fba..311349cfb2 100644 --- a/src/ol/format/IIIFInfo.js +++ b/src/ol/format/IIIFInfo.js @@ -94,6 +94,8 @@ import {assert} from '../asserts.js'; * image service additional to the ones indicated by the compliance level. * @property {Array} [extraFeatures] Additional supported features whose support * is not indicated by the compliance level. + * @property {Array} [preferredFormats] Image formats that should preferrably + * be used. */ /** @@ -156,15 +158,15 @@ IIIF_PROFILE_VALUES[Versions.VERSION3] = { qualities: ['default'] }, 'level1': { - supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH'], + supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH', 'sizeByWh'], formats: ['jpg'], qualities: ['default'] }, 'level2': { supports: ['regionByPx', 'regionSquare', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct', 'sizeByConfinedWh', 'sizeByWh'], - formats: ['jpg'], - qualities: ['default', 'bitonal'] + formats: ['jpg', 'png'], + qualities: ['default'] } }; IIIF_PROFILE_VALUES['none'] = { @@ -231,7 +233,16 @@ function generateVersion2Options(iiifInfo) { } function generateVersion3Options(iiifInfo) { - const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures(); + const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures(), + formats = iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats : + [...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats], + preferredFormat = iiifInfo.imageInfo.preferredFormats !== undefined && Array.isArray(iiifInfo.imageInfo.preferredFormats) && + iiifInfo.imageInfo.preferredFormats.length > 0 ? + iiifInfo.imageInfo.preferredFormats.filter(function(format) { + return ['jpg', 'png', 'gif'].includes(format); + }).reduce(function(acc, format) { + return acc === undefined && formats.includes(format) ? format : acc; + }, undefined) : undefined; return { url: iiifInfo.imageInfo['id'], sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) { @@ -251,13 +262,10 @@ function generateVersion3Options(iiifInfo) { })[0], supports: iiifInfo.imageInfo.extraFeatures === undefined ? levelProfile.supports : [...levelProfile.supports, ...iiifInfo.imageInfo.extraFeatures], - formats: iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats : - [...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats], + formats: formats, qualities: iiifInfo.imageInfo.extraQualities === undefined ? levelProfile.qualities : - [...levelProfile.supports, ...iiifInfo.imageInfo.extraQualities], - maxWidth: undefined, - maxHeight: undefined, - maxArea: undefined + [...levelProfile.qualities, ...iiifInfo.imageInfo.extraQualities], + preferredFormat: preferredFormat }; } @@ -414,7 +422,8 @@ class IIIFInfo { version: version, size: [this.imageInfo.width, this.imageInfo.height], sizes: imageOptions.sizes, - format: imageOptions.formats.includes(options.format) ? options.format : 'jpg', + format: options.format !== undefined && imageOptions.formats.includes(options.format) ? options.format : + imageOptions.preferredFormat !== undefined ? imageOptions.preferredFormat : 'jpg', supports: imageOptions.supports, quality: options.quality && imageOptions.qualities.includes(options.quality) ? options.quality : imageOptions.qualities.includes('native') ? 'native' : 'default', diff --git a/test/spec/ol/format/iiif.test.js b/test/spec/ol/format/iiif.test.js index 49f14fe561..33027bd3ba 100644 --- a/test/spec/ol/format/iiif.test.js +++ b/test/spec/ol/format/iiif.test.js @@ -219,7 +219,40 @@ describe('ol.format.IIIFInfo', function() { expect(level.supports).to.contain('sizeByDistortedWh'); expect(level.supports).to.contain('sizeByWh'); - // TODO test version 3 compliance level features once version 3 is final + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + profile: 'level0' + }); + level = iiifInfo.getComplianceLevelSupportedFeatures(); + expect(level.supports).to.be.empty(); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + profile: 'level1' + }); + level = iiifInfo.getComplianceLevelSupportedFeatures(); + expect(level.supports).to.have.length(5); + expect(level.supports).to.contain('regionByPx'); + expect(level.supports).to.contain('regionSquare'); + expect(level.supports).to.contain('sizeByW'); + expect(level.supports).to.contain('sizeByH'); + expect(level.supports).to.contain('sizeByWh'); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + profile: 'level2' + }); + level = iiifInfo.getComplianceLevelSupportedFeatures(); + expect(level.supports).to.have.length(8); + expect(level.supports).to.contain('regionByPx'); + expect(level.supports).to.contain('regionByPct'); + expect(level.supports).to.contain('regionSquare'); + expect(level.supports).to.contain('sizeByW'); + expect(level.supports).to.contain('sizeByH'); + expect(level.supports).to.contain('sizeByWh'); + expect(level.supports).to.contain('sizeByConfinedWh'); + expect(level.supports).to.contain('sizeByPct'); + }); }); @@ -290,7 +323,22 @@ describe('ol.format.IIIFInfo', function() { height: 1500, profile: ['http://iiif.io/api/image/2/level2.json'] }); - const options = iiifInfo.getTileSourceOptions({ + let options = iiifInfo.getTileSourceOptions({ + quality: 'bitonal', + format: 'png' + }); + expect(options).to.have.property('quality', 'bitonal'); + expect(options).to.have.property('format', 'png'); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + '@id': 'http://iiif.test/version3/id', + width: 2000, + height: 1500, + profile: 'level2', + extraQualities: ['gray', 'bitonal'] + }); + options = iiifInfo.getTileSourceOptions({ quality: 'bitonal', format: 'png' }); @@ -308,7 +356,21 @@ describe('ol.format.IIIFInfo', function() { height: 1500, profile: ['http://iiif.io/api/image/2/level1.json'] }); - const options = iiifInfo.getTileSourceOptions({ + let options = iiifInfo.getTileSourceOptions({ + quality: 'bitonal', + format: 'png' + }); + expect(options).to.have.property('quality', 'default'); + expect(options).to.have.property('format', 'jpg'); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + '@id': 'http://iiif.test/version3/id', + width: 2000, + height: 1500, + profile: 'level1' + }); + options = iiifInfo.getTileSourceOptions({ quality: 'bitonal', format: 'png' }); @@ -350,7 +412,8 @@ describe('ol.format.IIIFInfo', function() { expect(options.supports).to.contain('regionSquare'); expect(options.supports).to.contain('sizeByW'); expect(options.supports).to.contain('sizeByH'); - expect(options.supports).to.have.length(6); + expect(options.supports).to.contain('sizeByWh'); + expect(options.supports).to.have.length(7); }); @@ -448,6 +511,26 @@ describe('ol.format.IIIFInfo', function() { expect(options.tileSize[0]).to.be(512); expect(options.tileSize[1]).to.be(1024); + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + '@id': 'http://iiif.test/id', + profile: 'level0', + tiles: [{ + scaleFactors: [1, 2, 4, 8], + width: 512, + height: 256 + }] + }); + options = iiifInfo.getTileSourceOptions(); + expect(options.resolutions).to.have.length(4); + expect(options.resolutions).to.contain(1); + expect(options.resolutions).to.contain(2); + expect(options.resolutions).to.contain(4); + expect(options.resolutions).to.contain(8); + expect(options.tileSize).to.have.length(2); + expect(options.tileSize[0]).to.be(512); + expect(options.tileSize[1]).to.be(256); + }); }); @@ -470,7 +553,7 @@ describe('ol.format.IIIFInfo', function() { height: 250 }] }); - const options = iiifInfo.getTileSourceOptions(); + let options = iiifInfo.getTileSourceOptions(); expect(options.sizes).to.have.length(3); expect(options.sizes[0]).to.have.length(2); expect(options.sizes[0][0]).to.be(2000); @@ -482,6 +565,62 @@ describe('ol.format.IIIFInfo', function() { expect(options.sizes[2][0]).to.be(500); expect(options.sizes[2][1]).to.be(250); + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + '@id': 'http://iiif.test/id', + 'sizes': [{ + width: 1500, + height: 800 + }] + }); + options = iiifInfo.getTileSourceOptions(); + expect(options.sizes).to.have.length(1); + expect(options.sizes[0]).to.have.length(2); + expect(options.sizes[0][0]).to.be(1500); + expect(options.sizes[0][1]).to.be(800); + }); + it('respects the preferred image formats', function() { + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + 'id': 'http://iiif.test/id', + 'profile': 'level0', + 'preferredFormats': ['png', 'gif'] + }); + let options = iiifInfo.getTileSourceOptions(); + expect(options.format).to.be('jpg'); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + 'id': 'http://iiif.test/id', + 'profile': 'level1', + 'preferredFormats': ['png', 'gif'] + }); + options = iiifInfo.getTileSourceOptions(); + expect(options.format).to.be('jpg'); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + 'id': 'http://iiif.test/id', + 'profile': 'level1', + 'extraFormats': ['webp', 'gif'], + 'preferredFormats': ['webp', 'png', 'gif'] + }); + options = iiifInfo.getTileSourceOptions(); + expect(options.format).to.be('gif'); + + iiifInfo.setImageInfo({ + '@context': 'http://iiif.io/api/image/3/context.json', + 'id': 'http://iiif.test/id', + 'profile': 'level2', + 'preferredFormats': ['png', 'gif'] + }); + options = iiifInfo.getTileSourceOptions(); + expect(options.format).to.be('png'); + + }); + + }); diff --git a/test/spec/ol/source/iiif.test.js b/test/spec/ol/source/iiif.test.js index 29c1d46955..f88ae4a75f 100644 --- a/test/spec/ol/source/iiif.test.js +++ b/test/spec/ol/source/iiif.test.js @@ -201,6 +201,21 @@ describe('ol.source.IIIF', function() { expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/full/full/0/default.jpg'); expect(tileUrlFunction([3, 0, 0])).to.be(undefined); + tileUrlFunction = getSource({ + version: Versions.VERSION3, + sizes: [[2000, 1500], [1000, 750], [500, 375]] + }).getTileUrlFunction(); + + expect(tileUrlFunction([0, 0, 0])).to.be('http://iiif.test/image-id/full/500,375/0/default.jpg'); + expect(tileUrlFunction([1, 0, 0])).to.be('http://iiif.test/image-id/full/1000,750/0/default.jpg'); + expect(tileUrlFunction([2, 0, 0])).to.be('http://iiif.test/image-id/full/max/0/default.jpg'); + expect(tileUrlFunction([3, 0, 0])).to.be(undefined); + expect(tileUrlFunction([-1, 0, 0])).to.be(undefined); + expect(tileUrlFunction([0, 1, 0])).to.be(undefined); + expect(tileUrlFunction([0, 0, 1])).to.be(undefined); + expect(tileUrlFunction([1, 1, 0])).to.be(undefined); + expect(tileUrlFunction([1, 0, 1])).to.be(undefined); + }); it('cannot provide scaled tiles without provided tilesize or supported features', function() {