mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add support for writing dz format to zip container (#402)
To enable this you can either use the `.zip` or `.szi` file extensions or use `.tile({container: 'zip'})` with the `.dzi` extension.
This commit is contained in:
parent
ef61da3051
commit
b224874332
@ -527,14 +527,17 @@ The default behaviour, when `withMetadata` is not used, is to strip all metadata
|
|||||||
|
|
||||||
#### tile(options)
|
#### tile(options)
|
||||||
|
|
||||||
The size, overlap and directory layout to use when generating square Deep Zoom image pyramid tiles.
|
The size, overlap, container and directory layout to use when generating square Deep Zoom image pyramid tiles.
|
||||||
|
|
||||||
`options` is an Object with one or more of the following attributes:
|
`options` is an Object with one or more of the following attributes:
|
||||||
|
|
||||||
* `size` is an integral Number between 1 and 8192. The default value is 256 pixels.
|
* `size` is an integral Number between 1 and 8192. The default value is 256 pixels.
|
||||||
* `overlap` is an integral Number between 0 and 8192. The default value is 0 pixels.
|
* `overlap` is an integral Number between 0 and 8192. The default value is 0 pixels.
|
||||||
|
* `container` is a String, with value `fs` or `zip`. The default value is `fs`.
|
||||||
* `layout` is a String, with value `dz`, `zoomify` or `google`. The default value is `dz`.
|
* `layout` is a String, with value `dz`, `zoomify` or `google`. The default value is `dz`.
|
||||||
|
|
||||||
|
You can also use the file extension .zip or .szi to write to a ZIP container instead of the filesystem.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp('input.tiff')
|
sharp('input.tiff')
|
||||||
.tile({
|
.tile({
|
||||||
|
8
index.js
8
index.js
@ -660,6 +660,14 @@ Sharp.prototype.tile = function(tile) {
|
|||||||
throw new Error('Invalid tile overlap (0 to 8192) ' + tile.overlap);
|
throw new Error('Invalid tile overlap (0 to 8192) ' + tile.overlap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Container
|
||||||
|
if (isDefined(tile.container)) {
|
||||||
|
if (isString(tile.container) && contains(tile.container, ['fs', 'zip'])) {
|
||||||
|
this.options.tileContainer = tile.container;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid tile container ' + tile.container);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Layout
|
// Layout
|
||||||
if (isDefined(tile.layout)) {
|
if (isDefined(tile.layout)) {
|
||||||
if (isString(tile.layout) && contains(tile.layout, ['dz', 'google', 'zoomify'])) {
|
if (isString(tile.layout) && contains(tile.layout, ['dz', 'google', 'zoomify'])) {
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "^1.5.2",
|
"async": "^1.5.2",
|
||||||
|
"bufferutil": "^1.2.1",
|
||||||
"coveralls": "^2.11.9",
|
"coveralls": "^2.11.9",
|
||||||
"exif-reader": "^1.0.0",
|
"exif-reader": "^1.0.0",
|
||||||
"icc": "^0.0.2",
|
"icc": "^0.0.2",
|
||||||
@ -66,7 +67,7 @@
|
|||||||
"mocha-jshint": "^2.3.1",
|
"mocha-jshint": "^2.3.1",
|
||||||
"node-cpplint": "^0.4.0",
|
"node-cpplint": "^0.4.0",
|
||||||
"rimraf": "^2.5.2",
|
"rimraf": "^2.5.2",
|
||||||
"bufferutil": "^1.2.1"
|
"unzip": "^0.1.11"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -52,6 +52,9 @@ namespace sharp {
|
|||||||
bool IsDz(std::string const &str) {
|
bool IsDz(std::string const &str) {
|
||||||
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
|
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
|
||||||
}
|
}
|
||||||
|
bool IsDzZip(std::string const &str) {
|
||||||
|
return EndsWith(str, ".zip") || EndsWith(str, ".ZIP") || EndsWith(str, ".szi") || EndsWith(str, ".SZI");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Provide a string identifier for the given image type.
|
Provide a string identifier for the given image type.
|
||||||
|
@ -35,6 +35,7 @@ namespace sharp {
|
|||||||
bool IsWebp(std::string const &str);
|
bool IsWebp(std::string const &str);
|
||||||
bool IsTiff(std::string const &str);
|
bool IsTiff(std::string const &str);
|
||||||
bool IsDz(std::string const &str);
|
bool IsDz(std::string const &str);
|
||||||
|
bool IsDzZip(std::string const &str);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Provide a string identifier for the given image type.
|
Provide a string identifier for the given image type.
|
||||||
|
@ -65,6 +65,7 @@ using sharp::IsPng;
|
|||||||
using sharp::IsWebp;
|
using sharp::IsWebp;
|
||||||
using sharp::IsTiff;
|
using sharp::IsTiff;
|
||||||
using sharp::IsDz;
|
using sharp::IsDz;
|
||||||
|
using sharp::IsDzZip;
|
||||||
using sharp::FreeCallback;
|
using sharp::FreeCallback;
|
||||||
using sharp::CalculateCrop;
|
using sharp::CalculateCrop;
|
||||||
using sharp::counterProcess;
|
using sharp::counterProcess;
|
||||||
@ -743,7 +744,8 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
bool isWebp = IsWebp(baton->fileOut);
|
bool isWebp = IsWebp(baton->fileOut);
|
||||||
bool isTiff = IsTiff(baton->fileOut);
|
bool isTiff = IsTiff(baton->fileOut);
|
||||||
bool isDz = IsDz(baton->fileOut);
|
bool isDz = IsDz(baton->fileOut);
|
||||||
bool matchInput = baton->formatOut == "input" && !(isJpeg || isPng || isWebp || isTiff || isDz);
|
bool isDzZip = IsDzZip(baton->fileOut);
|
||||||
|
bool matchInput = baton->formatOut == "input" && !(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip);
|
||||||
if (baton->formatOut == "jpeg" || isJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
|
if (baton->formatOut == "jpeg" || isJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
|
||||||
// Write JPEG to file
|
// Write JPEG to file
|
||||||
image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
@ -784,12 +786,16 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
);
|
);
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
baton->channels = std::min(baton->channels, 3);
|
||||||
} else if (baton->formatOut == "dz" || IsDz(baton->fileOut)) {
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||||
|
if (isDzZip) {
|
||||||
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||||
|
}
|
||||||
// Write DZ to file
|
// Write DZ to file
|
||||||
image.dzsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.dzsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("tile_size", baton->tileSize)
|
->set("tile_size", baton->tileSize)
|
||||||
->set("overlap", baton->tileOverlap)
|
->set("overlap", baton->tileOverlap)
|
||||||
|
->set("container", baton->tileContainer)
|
||||||
->set("layout", baton->tileLayout)
|
->set("layout", baton->tileLayout)
|
||||||
);
|
);
|
||||||
baton->formatOut = "dz";
|
baton->formatOut = "dz";
|
||||||
@ -1058,6 +1064,12 @@ NAN_METHOD(pipeline) {
|
|||||||
// Tile output
|
// Tile output
|
||||||
baton->tileSize = attrAs<int32_t>(options, "tileSize");
|
baton->tileSize = attrAs<int32_t>(options, "tileSize");
|
||||||
baton->tileOverlap = attrAs<int32_t>(options, "tileOverlap");
|
baton->tileOverlap = attrAs<int32_t>(options, "tileOverlap");
|
||||||
|
std::string tileContainer = attrAsStr(options, "tileContainer");
|
||||||
|
if (tileContainer == "zip") {
|
||||||
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||||
|
} else {
|
||||||
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_FS;
|
||||||
|
}
|
||||||
std::string tileLayout = attrAsStr(options, "tileLayout");
|
std::string tileLayout = attrAsStr(options, "tileLayout");
|
||||||
if (tileLayout == "google") {
|
if (tileLayout == "google") {
|
||||||
baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_GOOGLE;
|
baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_GOOGLE;
|
||||||
|
@ -81,6 +81,7 @@ struct PipelineBaton {
|
|||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
int tileSize;
|
int tileSize;
|
||||||
int tileOverlap;
|
int tileOverlap;
|
||||||
|
VipsForeignDzContainer tileContainer;
|
||||||
VipsForeignDzLayout tileLayout;
|
VipsForeignDzLayout tileLayout;
|
||||||
|
|
||||||
PipelineBaton():
|
PipelineBaton():
|
||||||
@ -130,6 +131,7 @@ struct PipelineBaton {
|
|||||||
withMetadataOrientation(-1),
|
withMetadataOrientation(-1),
|
||||||
tileSize(256),
|
tileSize(256),
|
||||||
tileOverlap(0),
|
tileOverlap(0),
|
||||||
|
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
||||||
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ) {
|
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ) {
|
||||||
background[0] = 0.0;
|
background[0] = 0.0;
|
||||||
background[1] = 0.0;
|
background[1] = 0.0;
|
||||||
|
@ -6,6 +6,7 @@ var assert = require('assert');
|
|||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var rimraf = require('rimraf');
|
var rimraf = require('rimraf');
|
||||||
|
var unzip = require('unzip');
|
||||||
|
|
||||||
var sharp = require('../../index');
|
var sharp = require('../../index');
|
||||||
var fixtures = require('../fixtures');
|
var fixtures = require('../fixtures');
|
||||||
@ -88,6 +89,26 @@ describe('Tile', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Valid container values pass', function() {
|
||||||
|
['fs', 'zip'].forEach(function(container) {
|
||||||
|
assert.doesNotThrow(function() {
|
||||||
|
sharp().tile({
|
||||||
|
container: container
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid container values fail', function() {
|
||||||
|
['zoinks', 1].forEach(function(container) {
|
||||||
|
assert.throws(function() {
|
||||||
|
sharp().tile({
|
||||||
|
container: container
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Valid layout values pass', function() {
|
it('Valid layout values pass', function() {
|
||||||
['dz', 'google', 'zoomify'].forEach(function(layout) {
|
['dz', 'google', 'zoomify'].forEach(function(layout) {
|
||||||
assert.doesNotThrow(function() {
|
assert.doesNotThrow(function() {
|
||||||
@ -190,6 +211,58 @@ describe('Tile', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Write to ZIP container using file extension', function(done) {
|
||||||
|
var container = fixtures.path('output.dz.container.zip');
|
||||||
|
var extractTo = fixtures.path('output.dz.container');
|
||||||
|
var directory = path.join(extractTo, 'output.dz.container_files');
|
||||||
|
rimraf(directory, function() {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.toFile(container, function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('dz', info.format);
|
||||||
|
fs.stat(container, function(err, stat) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, stat.isFile());
|
||||||
|
assert.strictEqual(true, stat.size > 0);
|
||||||
|
fs.createReadStream(container)
|
||||||
|
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
|
||||||
|
.on('error', function(err) { throw err; })
|
||||||
|
.on('close', function() {
|
||||||
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Write to ZIP container using container tile option', function(done) {
|
||||||
|
var container = fixtures.path('output.dz.containeropt.zip');
|
||||||
|
var extractTo = fixtures.path('output.dz.containeropt');
|
||||||
|
var directory = path.join(extractTo, 'output.dz.containeropt_files');
|
||||||
|
rimraf(directory, function() {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.tile({
|
||||||
|
container: 'zip'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.path('output.dz.containeropt.dzi'), function(err, info) {
|
||||||
|
// Vips overrides .dzi extension to .zip used by container var below
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('dz', info.format);
|
||||||
|
fs.stat(container, function(err, stat) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, stat.isFile());
|
||||||
|
assert.strictEqual(true, stat.size > 0);
|
||||||
|
fs.createReadStream(container)
|
||||||
|
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
|
||||||
|
.on('error', function(err) { throw err; })
|
||||||
|
.on('close', function() {
|
||||||
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user