Skip to content

Commit

Permalink
Add resolutionUnit to metadata and as tiff option #3023
Browse files Browse the repository at this point in the history
Co-authored-by: Lovell Fuller <github@lovell.info>
  • Loading branch information
ompal-sisodiya and lovell committed Jan 29, 2022
1 parent 7aa3402 commit f7bed69
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 3 deletions.
1 change: 1 addition & 0 deletions lib/constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
9 changes: 9 additions & 0 deletions lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
Expand Down
6 changes: 6 additions & 0 deletions src/metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -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()));
Expand Down
1 change: 1 addition & 0 deletions src/metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 9 additions & 2 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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>(
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ struct PipelineBaton {
int tiffTileWidth;
double tiffXres;
double tiffYres;
VipsForeignTiffResunit tiffResolutionUnit;
int heifQuality;
VipsForeignHeifCompression heifCompression;
int heifEffort;
Expand Down Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions test/unit/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
Expand Down
30 changes: 30 additions & 0 deletions test/unit/tiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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' });
Expand Down

0 comments on commit f7bed69

Please sign in to comment.