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.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.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.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
|
### Examples
|
||||||
|
|
||||||
@ -132,7 +133,7 @@ sharp('input.jpg')
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Set "IFD0-Copyright" in output EXIF metadata
|
// Set "IFD0-Copyright" in output EXIF metadata
|
||||||
await sharp(input)
|
const data = await sharp(input)
|
||||||
.withMetadata({
|
.withMetadata({
|
||||||
exif: {
|
exif: {
|
||||||
IFD0: {
|
IFD0: {
|
||||||
@ -141,6 +142,12 @@ await sharp(input)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.toBuffer();
|
.toBuffer();
|
||||||
|
|
||||||
|
* @example
|
||||||
|
// Set output metadata to 96 DPI
|
||||||
|
const data = await sharp(input)
|
||||||
|
.withMetadata({ density: 96 })
|
||||||
|
.toBuffer();
|
||||||
```
|
```
|
||||||
|
|
||||||
- Throws **[Error][4]** Invalid parameters
|
- Throws **[Error][4]** Invalid parameters
|
||||||
|
@ -6,6 +6,9 @@ Requires libvips v8.10.6
|
|||||||
|
|
||||||
### v0.28.2 - TBD
|
### 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.
|
* Skip shrink-on-load where one dimension <4px.
|
||||||
[#2653](https://github.com/lovell/sharp/issues/2653)
|
[#2653](https://github.com/lovell/sharp/issues/2653)
|
||||||
|
|
||||||
|
@ -231,6 +231,7 @@ const Sharp = function (input, options) {
|
|||||||
streamOut: false,
|
streamOut: false,
|
||||||
withMetadata: false,
|
withMetadata: false,
|
||||||
withMetadataOrientation: -1,
|
withMetadataOrientation: -1,
|
||||||
|
withMetadataDensity: 0,
|
||||||
withMetadataIcc: '',
|
withMetadataIcc: '',
|
||||||
withMetadataStrs: {},
|
withMetadataStrs: {},
|
||||||
resolveWithObject: false,
|
resolveWithObject: false,
|
||||||
|
@ -150,7 +150,7 @@ function toBuffer (options, callback) {
|
|||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Set "IFD0-Copyright" in output EXIF metadata
|
* // Set "IFD0-Copyright" in output EXIF metadata
|
||||||
* await sharp(input)
|
* const data = await sharp(input)
|
||||||
* .withMetadata({
|
* .withMetadata({
|
||||||
* exif: {
|
* exif: {
|
||||||
* IFD0: {
|
* IFD0: {
|
||||||
@ -160,10 +160,17 @@ function toBuffer (options, callback) {
|
|||||||
* })
|
* })
|
||||||
* .toBuffer();
|
* .toBuffer();
|
||||||
*
|
*
|
||||||
|
* * @example
|
||||||
|
* // Set output metadata to 96 DPI
|
||||||
|
* const data = await sharp(input)
|
||||||
|
* .withMetadata({ density: 96 })
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
* @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 {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 {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}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@ -177,6 +184,13 @@ function withMetadata (options) {
|
|||||||
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
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.defined(options.icc)) {
|
||||||
if (is.string(options.icc)) {
|
if (is.string(options.icc)) {
|
||||||
this.options.withMetadataIcc = options.icc;
|
this.options.withMetadataIcc = options.icc;
|
||||||
|
@ -130,7 +130,7 @@
|
|||||||
"async": "^3.2.0",
|
"async": "^3.2.0",
|
||||||
"cc": "^3.0.1",
|
"cc": "^3.0.1",
|
||||||
"decompress-zip": "^0.3.3",
|
"decompress-zip": "^0.3.3",
|
||||||
"documentation": "^13.2.0",
|
"documentation": "^13.2.1",
|
||||||
"exif-reader": "^1.0.3",
|
"exif-reader": "^1.0.3",
|
||||||
"icc": "^2.0.0",
|
"icc": "^2.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
|
@ -520,9 +520,8 @@ namespace sharp {
|
|||||||
VImage SetDensity(VImage image, const double density) {
|
VImage SetDensity(VImage image, const double density) {
|
||||||
const double pixelsPerMm = density / 25.4;
|
const double pixelsPerMm = density / 25.4;
|
||||||
VImage copy = image.copy();
|
VImage copy = image.copy();
|
||||||
copy.set("Xres", pixelsPerMm);
|
copy.get_image()->Xres = pixelsPerMm;
|
||||||
copy.set("Yres", pixelsPerMm);
|
copy.get_image()->Yres = pixelsPerMm;
|
||||||
copy.set(VIPS_META_RESOLUTION_UNIT, "in");
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +722,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
||||||
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
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
|
// Metadata key/value pairs, e.g. EXIF
|
||||||
if (!baton->withMetadataStrs.empty()) {
|
if (!baton->withMetadataStrs.empty()) {
|
||||||
image = image.copy();
|
image = image.copy();
|
||||||
@ -1385,6 +1389,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
||||||
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
||||||
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
||||||
|
baton->withMetadataDensity = sharp::AttrAsDouble(options, "withMetadataDensity");
|
||||||
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
||||||
Napi::Object mdStrs = options.Get("withMetadataStrs").As<Napi::Object>();
|
Napi::Object mdStrs = options.Get("withMetadataStrs").As<Napi::Object>();
|
||||||
Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
|
Napi::Array mdStrKeys = mdStrs.GetPropertyNames();
|
||||||
|
@ -168,6 +168,7 @@ struct PipelineBaton {
|
|||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
|
double withMetadataDensity;
|
||||||
std::string withMetadataIcc;
|
std::string withMetadataIcc;
|
||||||
std::unordered_map<std::string, std::string> withMetadataStrs;
|
std::unordered_map<std::string, std::string> withMetadataStrs;
|
||||||
std::unique_ptr<double[]> convKernel;
|
std::unique_ptr<double[]> convKernel;
|
||||||
@ -290,6 +291,7 @@ struct PipelineBaton {
|
|||||||
heifLossless(false),
|
heifLossless(false),
|
||||||
withMetadata(false),
|
withMetadata(false),
|
||||||
withMetadataOrientation(-1),
|
withMetadataOrientation(-1),
|
||||||
|
withMetadataDensity(0.0),
|
||||||
convKernelWidth(0),
|
convKernelWidth(0),
|
||||||
convKernelHeight(0),
|
convKernelHeight(0),
|
||||||
convKernelScale(0.0),
|
convKernelScale(0.0),
|
||||||
|
@ -623,6 +623,44 @@ describe('Image metadata', function () {
|
|||||||
assert.strictEqual(parsedExif.exif.ExposureTime, 0.2);
|
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 () {
|
it('chromaSubsampling 4:4:4:4 CMYK JPEG', function () {
|
||||||
return sharp(fixtures.inputJpgWithCmykProfile)
|
return sharp(fixtures.inputJpgWithCmykProfile)
|
||||||
.metadata()
|
.metadata()
|
||||||
@ -736,6 +774,16 @@ describe('Image metadata', function () {
|
|||||||
sharp().withMetadata({ orientation: 9 });
|
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 () {
|
it('Non string icc', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().withMetadata({ icc: true });
|
sharp().withMetadata({ icc: true });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user