mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add resolutionUnit to metadata and as tiff option #3023
Co-authored-by: Lovell Fuller <github@lovell.info>
This commit is contained in:
parent
7aa340232e
commit
f7bed69ffb
@ -262,6 +262,7 @@ const Sharp = function (input, options) {
|
||||
tiffTileWidth: 256,
|
||||
tiffXres: 1.0,
|
||||
tiffYres: 1.0,
|
||||
tiffResolutionUnit: 'inch',
|
||||
heifQuality: 50,
|
||||
heifLossless: false,
|
||||
heifCompression: 'av1',
|
||||
|
@ -704,6 +704,7 @@ function trySetAnimationOptions (source, target) {
|
||||
* @param {number} [options.tileHeight=256] - vertical tile size
|
||||
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
||||
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
||||
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
|
||||
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
@ -777,6 +778,14 @@ function tiff (options) {
|
||||
throw is.invalidParameterError('predictor', 'one of: none, horizontal, float', options.predictor);
|
||||
}
|
||||
}
|
||||
// resolutionUnit
|
||||
if (is.defined(options.resolutionUnit)) {
|
||||
if (is.string(options.resolutionUnit) && is.inArray(options.resolutionUnit, ['inch', 'cm'])) {
|
||||
this.options.tiffResolutionUnit = options.resolutionUnit;
|
||||
} else {
|
||||
throw is.invalidParameterError('resolutionUnit', 'one of: inch, cm', options.resolutionUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._updateFormatOut('tiff', options);
|
||||
}
|
||||
|
@ -80,7 +80,8 @@
|
||||
"Brad Parham <baparham@gmail.com>",
|
||||
"Taneli Vatanen <taneli.vatanen@gmail.com>",
|
||||
"Joris Dugué <zaruike10@gmail.com>",
|
||||
"Chris Banks <christopher.bradley.banks@gmail.com>"
|
||||
"Chris Banks <christopher.bradley.banks@gmail.com>",
|
||||
"Ompal Singh <ompal.hitm09@gmail.com>"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)",
|
||||
|
@ -77,6 +77,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
|
||||
baton->compression = image.get_string("heif-compression");
|
||||
}
|
||||
if (image.get_typeof(VIPS_META_RESOLUTION_UNIT) == VIPS_TYPE_REF_STRING) {
|
||||
baton->resolutionUnit = image.get_string(VIPS_META_RESOLUTION_UNIT);
|
||||
}
|
||||
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
|
||||
int const levels = std::stoi(image.get_string("openslide.level-count"));
|
||||
for (int l = 0; l < levels; l++) {
|
||||
@ -198,6 +201,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
||||
if (!baton->compression.empty()) {
|
||||
info.Set("compression", baton->compression);
|
||||
}
|
||||
if (!baton->resolutionUnit.empty()) {
|
||||
info.Set("resolutionUnit", baton->resolutionUnit == "in" ? "inch" : baton->resolutionUnit);
|
||||
}
|
||||
if (!baton->levels.empty()) {
|
||||
int i = 0;
|
||||
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
|
||||
|
@ -40,6 +40,7 @@ struct MetadataBaton {
|
||||
std::vector<int> delay;
|
||||
int pagePrimary;
|
||||
std::string compression;
|
||||
std::string resolutionUnit;
|
||||
std::vector<std::pair<int, int>> levels;
|
||||
int subifds;
|
||||
std::vector<double> background;
|
||||
|
@ -913,7 +913,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("tile_height", baton->tiffTileHeight)
|
||||
->set("tile_width", baton->tiffTileWidth)
|
||||
->set("xres", baton->tiffXres)
|
||||
->set("yres", baton->tiffYres)));
|
||||
->set("yres", baton->tiffYres)
|
||||
->set("resunit", baton->tiffResolutionUnit)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
area->free_fn = nullptr;
|
||||
@ -1071,7 +1072,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("tile_height", baton->tiffTileHeight)
|
||||
->set("tile_width", baton->tiffTileWidth)
|
||||
->set("xres", baton->tiffXres)
|
||||
->set("yres", baton->tiffYres));
|
||||
->set("yres", baton->tiffYres)
|
||||
->set("resunit", baton->tiffResolutionUnit));
|
||||
baton->formatOut = "tiff";
|
||||
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
||||
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
||||
@ -1542,6 +1544,10 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
||||
sharp::AttrAsStr(options, "tiffPredictor").data()));
|
||||
baton->tiffResolutionUnit = static_cast<VipsForeignTiffResunit>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_RESUNIT,
|
||||
sharp::AttrAsStr(options, "tiffResolutionUnit").data()));
|
||||
|
||||
baton->heifQuality = sharp::AttrAsUint32(options, "heifQuality");
|
||||
baton->heifLossless = sharp::AttrAsBool(options, "heifLossless");
|
||||
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
||||
@ -1550,6 +1556,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
|
||||
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
||||
|
||||
|
||||
// Raw output
|
||||
baton->rawDepth = static_cast<VipsBandFormat>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_BAND_FORMAT,
|
||||
|
@ -167,6 +167,7 @@ struct PipelineBaton {
|
||||
int tiffTileWidth;
|
||||
double tiffXres;
|
||||
double tiffYres;
|
||||
VipsForeignTiffResunit tiffResolutionUnit;
|
||||
int heifQuality;
|
||||
VipsForeignHeifCompression heifCompression;
|
||||
int heifEffort;
|
||||
@ -305,6 +306,7 @@ struct PipelineBaton {
|
||||
tiffTileWidth(256),
|
||||
tiffXres(1.0),
|
||||
tiffYres(1.0),
|
||||
tiffResolutionUnit(VIPS_FOREIGN_TIFF_RESUNIT_INCH),
|
||||
heifQuality(50),
|
||||
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
||||
heifEffort(4),
|
||||
|
@ -99,6 +99,7 @@ describe('Image metadata', function () {
|
||||
assert.strictEqual(1, metadata.orientation);
|
||||
assert.strictEqual('undefined', typeof metadata.exif);
|
||||
assert.strictEqual('undefined', typeof metadata.icc);
|
||||
assert.strictEqual('inch', metadata.resolutionUnit);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -288,6 +288,30 @@ describe('TIFF', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('TIFF resolutionUnit of inch (default)', async () => {
|
||||
const data = await sharp({ create: { width: 8, height: 8, channels: 3, background: 'red' } })
|
||||
.tiff()
|
||||
.toBuffer();
|
||||
const { resolutionUnit } = await sharp(data).metadata();
|
||||
assert.strictEqual(resolutionUnit, 'inch');
|
||||
});
|
||||
|
||||
it('TIFF resolutionUnit of inch', async () => {
|
||||
const data = await sharp({ create: { width: 8, height: 8, channels: 3, background: 'red' } })
|
||||
.tiff({ resolutionUnit: 'inch' })
|
||||
.toBuffer();
|
||||
const { resolutionUnit } = await sharp(data).metadata();
|
||||
assert.strictEqual(resolutionUnit, 'inch');
|
||||
});
|
||||
|
||||
it('TIFF resolutionUnit of cm', async () => {
|
||||
const data = await sharp({ create: { width: 8, height: 8, channels: 3, background: 'red' } })
|
||||
.tiff({ resolutionUnit: 'cm' })
|
||||
.toBuffer();
|
||||
const { resolutionUnit } = await sharp(data).metadata();
|
||||
assert.strictEqual(resolutionUnit, 'cm');
|
||||
});
|
||||
|
||||
it('TIFF deflate compression with horizontal predictor shrinks test file', function (done) {
|
||||
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||
sharp(fixtures.inputTiffUncompressed)
|
||||
@ -383,6 +407,12 @@ describe('TIFF', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('TIFF invalid resolutionUnit option throws', function () {
|
||||
assert.throws(function () {
|
||||
sharp().tiff({ resolutionUnit: 'none' });
|
||||
});
|
||||
});
|
||||
|
||||
it('TIFF horizontal predictor does not throw error', function () {
|
||||
assert.doesNotThrow(function () {
|
||||
sharp().tiff({ predictor: 'horizontal' });
|
||||
|
Loading…
x
Reference in New Issue
Block a user