mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Allow withMetadata to set density #967
This commit is contained in:
parent
8c0c01c702
commit
4237f5520f
@ -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
|
||||
|
||||
@ -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: {
|
||||
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -231,6 +231,7 @@ const Sharp = function (input, options) {
|
||||
streamOut: false,
|
||||
withMetadata: false,
|
||||
withMetadataOrientation: -1,
|
||||
withMetadataDensity: 0,
|
||||
withMetadataIcc: '',
|
||||
withMetadataStrs: {},
|
||||
resolveWithObject: false,
|
||||
|
@ -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: {
|
||||
@ -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
|
||||
*/
|
||||
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
@ -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();
|
||||
|
@ -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;
|
||||
@ -290,6 +291,7 @@ struct PipelineBaton {
|
||||
heifLossless(false),
|
||||
withMetadata(false),
|
||||
withMetadataOrientation(-1),
|
||||
withMetadataDensity(0.0),
|
||||
convKernelWidth(0),
|
||||
convKernelHeight(0),
|
||||
convKernelScale(0.0),
|
||||
|
@ -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()
|
||||
@ -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 });
|
||||
|
Loading…
x
Reference in New Issue
Block a user