Skip to content

Commit

Permalink
Allow withMetadata to set density #967
Browse files Browse the repository at this point in the history
  • Loading branch information
lovell committed Apr 17, 2021
1 parent 8c0c01c commit 4237f55
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 6 deletions.
9 changes: 8 additions & 1 deletion docs/api-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ sRGB colour space and strip all metadata, including the removal of any ICC profi
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
- `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
- `options.exif` **[Object][6]<[Object][6]>** Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. (optional, default `{}`)
- `options.density` **[number][9]?** Number of pixels per inch (DPI).

### Examples

Expand All @@ -132,7 +133,7 @@ sharp('input.jpg')

```javascript
// Set "IFD0-Copyright" in output EXIF metadata
await sharp(input)
const data = await sharp(input)
.withMetadata({
exif: {
IFD0: {
Expand All @@ -141,6 +142,12 @@ await sharp(input)
}
})
.toBuffer();

* @example
// Set output metadata to 96 DPI
const data = await sharp(input)
.withMetadata({ density: 96 })
.toBuffer();
```

- Throws **[Error][4]** Invalid parameters
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Requires libvips v8.10.6

### v0.28.2 - TBD

* Allow `withMetadata` to set `density`.
[#967](https://github.com/lovell/sharp/issues/967)

* Skip shrink-on-load where one dimension <4px.
[#2653](https://github.com/lovell/sharp/issues/2653)

Expand Down
1 change: 1 addition & 0 deletions lib/constructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ const Sharp = function (input, options) {
streamOut: false,
withMetadata: false,
withMetadataOrientation: -1,
withMetadataDensity: 0,
withMetadataIcc: '',
withMetadataStrs: {},
resolveWithObject: false,
Expand Down
16 changes: 15 additions & 1 deletion lib/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function toBuffer (options, callback) {
*
* @example
* // Set "IFD0-Copyright" in output EXIF metadata
* await sharp(input)
* const data = await sharp(input)
* .withMetadata({
* exif: {
* IFD0: {
Expand All @@ -160,10 +160,17 @@ function toBuffer (options, callback) {
* })
* .toBuffer();
*
* * @example
* // Set output metadata to 96 DPI
* const data = await sharp(input)
* .withMetadata({ density: 96 })
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
* @param {Object<Object>} [options.exif={}] Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
* @param {number} [options.density] Number of pixels per inch (DPI).
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
Expand All @@ -177,6 +184,13 @@ function withMetadata (options) {
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
}
}
if (is.defined(options.density)) {
if (is.number(options.density) && options.density > 0) {
this.options.withMetadataDensity = options.density;
} else {
throw is.invalidParameterError('density', 'positive number', options.density);
}
}
if (is.defined(options.icc)) {
if (is.string(options.icc)) {
this.options.withMetadataIcc = options.icc;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@
"async": "^3.2.0",
"cc": "^3.0.1",
"decompress-zip": "^0.3.3",
"documentation": "^13.2.0",
"documentation": "^13.2.1",
"exif-reader": "^1.0.3",
"icc": "^2.0.0",
"license-checker": "^25.0.1",
Expand Down
5 changes: 2 additions & 3 deletions src/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,8 @@ namespace sharp {
VImage SetDensity(VImage image, const double density) {
const double pixelsPerMm = density / 25.4;
VImage copy = image.copy();
copy.set("Xres", pixelsPerMm);
copy.set("Yres", pixelsPerMm);
copy.set(VIPS_META_RESOLUTION_UNIT, "in");
copy.get_image()->Xres = pixelsPerMm;
copy.get_image()->Yres = pixelsPerMm;
return copy;
}

Expand Down
5 changes: 5 additions & 0 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,10 @@ class PipelineWorker : public Napi::AsyncWorker {
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
}
// Override pixel density
if (baton->withMetadataDensity > 0) {
image = sharp::SetDensity(image, baton->withMetadataDensity);
}
// Metadata key/value pairs, e.g. EXIF
if (!baton->withMetadataStrs.empty()) {
image = image.copy();
Expand Down Expand Up @@ -1385,6 +1389,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
Napi::Object mdStrs = options.Get("withMetadataStrs").As<Napi::Object>();
Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
Expand Down
2 changes: 2 additions & 0 deletions src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ struct PipelineBaton {
std::string err;
bool withMetadata;
int withMetadataOrientation;
double withMetadataDensity;
std::string withMetadataIcc;
std::unordered_map<std::string, std::string> withMetadataStrs;
std::unique_ptr<double[]> convKernel;
Expand Down Expand Up @@ -290,6 +291,7 @@ struct PipelineBaton {
heifLossless(false),
withMetadata(false),
withMetadataOrientation(-1),
withMetadataDensity(0.0),
convKernelWidth(0),
convKernelHeight(0),
convKernelScale(0.0),
Expand Down
48 changes: 48 additions & 0 deletions test/unit/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,44 @@ describe('Image metadata', function () {
assert.strictEqual(parsedExif.exif.ExposureTime, 0.2);
});

it('Set density of JPEG', async () => {
const data = await sharp({
create: {
width: 8,
height: 8,
channels: 3,
background: 'red'
}
})
.withMetadata({
density: 300
})
.jpeg()
.toBuffer();

const { density } = await sharp(data).metadata();
assert.strictEqual(density, 300);
});

it('Set density of PNG', async () => {
const data = await sharp({
create: {
width: 8,
height: 8,
channels: 3,
background: 'red'
}
})
.withMetadata({
density: 96
})
.png()
.toBuffer();

const { density } = await sharp(data).metadata();
assert.strictEqual(density, 96);
});

it('chromaSubsampling 4:4:4:4 CMYK JPEG', function () {
return sharp(fixtures.inputJpgWithCmykProfile)
.metadata()
Expand Down Expand Up @@ -736,6 +774,16 @@ describe('Image metadata', function () {
sharp().withMetadata({ orientation: 9 });
});
});
it('Non-numeric density', function () {
assert.throws(function () {
sharp().withMetadata({ density: '1' });
});
});
it('Negative density', function () {
assert.throws(function () {
sharp().withMetadata({ density: -1 });
});
});
it('Non string icc', function () {
assert.throws(function () {
sharp().withMetadata({ icc: true });
Expand Down

0 comments on commit 4237f55

Please sign in to comment.