Add support for IIIF v3 tile-based output

This commit is contained in:
Lovell Fuller 2022-01-07 20:45:48 +00:00
parent 4c3a8a7007
commit d67e09ba7c
4 changed files with 41 additions and 7 deletions

View File

@ -546,10 +546,10 @@ Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed arc
* `options.depth` **[string][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout. * `options.depth` **[string][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* `options.skipBlanks` **[number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`) * `options.skipBlanks` **[number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
* `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`) * `options.container` **[string][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
* `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. (optional, default `'dz'`) * `options.layout` **[string][2]** filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`. (optional, default `'dz'`)
* `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`) * `options.centre` **[boolean][7]** centre image in tile. (optional, default `false`)
* `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`) * `options.center` **[boolean][7]** alternative spelling of centre. (optional, default `false`)
* `options.id` **[string][2]** when `layout` is `iiif`, sets the `@id` attribute of `info.json` (optional, default `'https://example.com/iiif'`) * `options.id` **[string][2]** when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json` (optional, default `'https://example.com/iiif'`)
### Examples ### Examples

View File

@ -16,6 +16,8 @@ Requires libvips v8.12.1
* Standardise HEIF `effort` option name, deprecate `speed`. * Standardise HEIF `effort` option name, deprecate `speed`.
* Add support for IIIF v3 tile-based output.
* Expose control over CPU effort for palette-based PNG output. * Expose control over CPU effort for palette-based PNG output.
[#2541](https://github.com/lovell/sharp/issues/2541) [#2541](https://github.com/lovell/sharp/issues/2541)

View File

@ -930,10 +930,10 @@ function raw (options) {
* @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout. * @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images * @param {number} [options.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file). * @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `zoomify` or `google`. * @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`.
* @param {boolean} [options.centre=false] centre image in tile. * @param {boolean} [options.centre=false] centre image in tile.
* @param {boolean} [options.center=false] alternative spelling of centre. * @param {boolean} [options.center=false] alternative spelling of centre.
* @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`, sets the `@id` attribute of `info.json` * @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json`
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@ -968,10 +968,10 @@ function tile (options) {
} }
// Layout // Layout
if (is.defined(options.layout)) { if (is.defined(options.layout)) {
if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'iiif', 'zoomify'])) { if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'iiif', 'iiif3', 'zoomify'])) {
this.options.tileLayout = options.layout; this.options.tileLayout = options.layout;
} else { } else {
throw is.invalidParameterError('layout', 'one of: dz, google, iiif, zoomify', options.layout); throw is.invalidParameterError('layout', 'one of: dz, google, iiif, iiif3, zoomify', options.layout);
} }
} }
// Angle of rotation, // Angle of rotation,

View File

@ -830,7 +830,7 @@ describe('Tile', function () {
}); });
}); });
it('IIIF layout', function (done) { it('IIIFv2 layout', function (done) {
const name = 'output.iiif.info'; const name = 'output.iiif.info';
const directory = fixtures.path(name); const directory = fixtures.path(name);
rimraf(directory, function () { rimraf(directory, function () {
@ -848,6 +848,7 @@ describe('Tile', function () {
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size); assert.strictEqual('number', typeof info.size);
const infoJson = require(path.join(directory, 'info.json')); const infoJson = require(path.join(directory, 'info.json'));
assert.strictEqual('http://iiif.io/api/image/2/context.json', infoJson['@context']);
assert.strictEqual(`${id}/${name}`, infoJson['@id']); assert.strictEqual(`${id}/${name}`, infoJson['@id']);
fs.stat(path.join(directory, '0,0,256,256', '256,', '0', 'default.jpg'), function (err, stat) { fs.stat(path.join(directory, '0,0,256,256', '256,', '0', 'default.jpg'), function (err, stat) {
if (err) throw err; if (err) throw err;
@ -859,6 +860,37 @@ describe('Tile', function () {
}); });
}); });
it('IIIFv3 layout', function (done) {
const name = 'output.iiif3.info';
const directory = fixtures.path(name);
rimraf(directory, function () {
const id = 'https://sharp.test.com/iiif3';
sharp(fixtures.inputJpg)
.tile({
layout: 'iiif3',
id
})
.toFile(directory, function (err, info) {
if (err) throw err;
assert.strictEqual('dz', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('number', typeof info.size);
const infoJson = require(path.join(directory, 'info.json'));
assert.strictEqual('http://iiif.io/api/image/3/context.json', infoJson['@context']);
assert.strictEqual('ImageService3', infoJson.type);
assert.strictEqual(`${id}/${name}`, infoJson.id);
fs.stat(path.join(directory, '0,0,256,256', '256,256,', '0', 'default.jpg'), function (err, stat) {
if (err) throw err;
assert.strictEqual(true, stat.isFile());
assert.strictEqual(true, stat.size > 0);
done();
});
});
});
});
it('Write to ZIP container using file extension', function (done) { it('Write to ZIP container using file extension', function (done) {
const container = fixtures.path('output.dz.container.zip'); const container = fixtures.path('output.dz.container.zip');
const extractTo = fixtures.path('output.dz.container'); const extractTo = fixtures.path('output.dz.container');