Skip to content

Commit e78200c

Browse files
authored
Increase control over output metadata (lovell#3856)
Add withX and keepX functions to take advantage of libvips 8.15.0 new 'keep' metadata feature.
1 parent 3f7313d commit e78200c

13 files changed

+689
-174
lines changed

docs/api-colour.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ An alpha channel may be present and will be unchanged by the operation.
1212

1313
| Param | Type | Description |
1414
| --- | --- | --- |
15-
| tint | <code>String</code> \| <code>Object</code> | Parsed by the [color](https://www.npmjs.org/package/color) module. |
15+
| tint | <code>string</code> \| <code>Object</code> | Parsed by the [color](https://www.npmjs.org/package/color) module. |
1616

1717
**Example**
1818
```js

docs/api-output.md

+149-31
Original file line numberDiff line numberDiff line change
@@ -111,56 +111,174 @@ await sharp(pixelArray, { raw: { width, height, channels } })
111111
```
112112

113113

114-
## withMetadata
115-
> withMetadata([options]) ⇒ <code>Sharp</code>
114+
## keepExif
115+
> keepExif() ⇒ <code>Sharp</code>
116116
117-
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
118-
This will also convert to and add a web-friendly sRGB ICC profile if appropriate,
119-
unless a custom output profile is provided.
120-
121-
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
122-
sRGB colour space and strip all metadata, including the removal of any ICC profile.
117+
Keep all EXIF metadata from the input image in the output image.
123118

124119
EXIF metadata is unsupported for TIFF output.
125120

126121

122+
**Since**: 0.33.0
123+
**Example**
124+
```js
125+
const outputWithExif = await sharp(inputWithExif)
126+
.keepExif()
127+
.toBuffer();
128+
```
129+
130+
131+
## withExif
132+
> withExif(exif) ⇒ <code>Sharp</code>
133+
134+
Set EXIF metadata in the output image, ignoring any EXIF in the input image.
135+
136+
137+
**Throws**:
138+
139+
- <code>Error</code> Invalid parameters
140+
141+
**Since**: 0.33.0
142+
143+
| Param | Type | Description |
144+
| --- | --- | --- |
145+
| exif | <code>Object.&lt;string, Object.&lt;string, string&gt;&gt;</code> | Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. |
146+
147+
**Example**
148+
```js
149+
const dataWithExif = await sharp(input)
150+
.withExif({
151+
IFD0: {
152+
Copyright: 'The National Gallery'
153+
},
154+
IFD3: {
155+
GPSLatitudeRef: 'N',
156+
GPSLatitude: '51/1 30/1 3230/100',
157+
GPSLongitudeRef: 'W',
158+
GPSLongitude: '0/1 7/1 4366/100'
159+
}
160+
})
161+
.toBuffer();
162+
```
163+
164+
165+
## withExifMerge
166+
> withExifMerge(exif) ⇒ <code>Sharp</code>
167+
168+
Update EXIF metadata from the input image in the output image.
169+
170+
171+
**Throws**:
172+
173+
- <code>Error</code> Invalid parameters
174+
175+
**Since**: 0.33.0
176+
177+
| Param | Type | Description |
178+
| --- | --- | --- |
179+
| exif | <code>Object.&lt;string, Object.&lt;string, string&gt;&gt;</code> | Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. |
180+
181+
**Example**
182+
```js
183+
const dataWithMergedExif = await sharp(inputWithExif)
184+
.withExifMerge({
185+
IFD0: {
186+
Copyright: 'The National Gallery'
187+
}
188+
})
189+
.toBuffer();
190+
```
191+
192+
193+
## keepIccProfile
194+
> keepIccProfile() ⇒ <code>Sharp</code>
195+
196+
Keep ICC profile from the input image in the output image.
197+
198+
Where necessary, will attempt to convert the output colour space to match the profile.
199+
200+
201+
**Since**: 0.33.0
202+
**Example**
203+
```js
204+
const outputWithIccProfile = await sharp(inputWithIccProfile)
205+
.keepIccProfile()
206+
.toBuffer();
207+
```
208+
209+
210+
## withIccProfile
211+
> withIccProfile(icc, [options]) ⇒ <code>Sharp</code>
212+
213+
Transform using an ICC profile and attach to the output image.
214+
215+
This can either be an absolute filesystem path or
216+
built-in profile name (`srgb`, `p3`, `cmyk`).
217+
218+
127219
**Throws**:
128220

129221
- <code>Error</code> Invalid parameters
130222

223+
**Since**: 0.33.0
131224

132225
| Param | Type | Default | Description |
133226
| --- | --- | --- | --- |
227+
| icc | <code>string</code> | | Absolute filesystem path to output ICC profile or built-in profile name (srgb, p3, cmyk). |
134228
| [options] | <code>Object</code> | | |
135-
| [options.orientation] | <code>number</code> | | value between 1 and 8, used to update the EXIF `Orientation` tag. |
136-
| [options.icc] | <code>string</code> | <code>&quot;&#x27;srgb&#x27;&quot;</code> | Filesystem path to output ICC profile, relative to `process.cwd()`, defaults to built-in sRGB. |
137-
| [options.exif] | <code>Object.&lt;Object&gt;</code> | <code>{}</code> | Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. |
138-
| [options.density] | <code>number</code> | | Number of pixels per inch (DPI). |
229+
| [options.attach] | <code>number</code> | <code>true</code> | Should the ICC profile be included in the output image metadata? |
139230

140231
**Example**
141232
```js
142-
sharp('input.jpg')
143-
.withMetadata()
144-
.toFile('output-with-metadata.jpg')
145-
.then(info => { ... });
233+
const outputWithP3 = await sharp(input)
234+
.withIccProfile('p3')
235+
.toBuffer();
146236
```
237+
238+
239+
## keepMetadata
240+
> keepMetadata() ⇒ <code>Sharp</code>
241+
242+
Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
243+
244+
The default behaviour, when `keepMetadata` is not used, is to convert to the device-independent
245+
sRGB colour space and strip all metadata, including the removal of any ICC profile.
246+
247+
248+
**Since**: 0.33.0
147249
**Example**
148250
```js
149-
// Set output EXIF metadata
150-
const data = await sharp(input)
151-
.withMetadata({
152-
exif: {
153-
IFD0: {
154-
Copyright: 'The National Gallery'
155-
},
156-
IFD3: {
157-
GPSLatitudeRef: 'N',
158-
GPSLatitude: '51/1 30/1 3230/100',
159-
GPSLongitudeRef: 'W',
160-
GPSLongitude: '0/1 7/1 4366/100'
161-
}
162-
}
163-
})
251+
const outputWithMetadata = await sharp(inputWithMetadata)
252+
.keepMetadata()
253+
.toBuffer();
254+
```
255+
256+
257+
## withMetadata
258+
> withMetadata([options]) ⇒ <code>Sharp</code>
259+
260+
Keep most metadata (EXIF, XMP, IPTC) from the input image in the output image.
261+
262+
This will also convert to and add a web-friendly sRGB ICC profile if appropriate.
263+
264+
Allows orientation and density to be set or updated.
265+
266+
267+
**Throws**:
268+
269+
- <code>Error</code> Invalid parameters
270+
271+
272+
| Param | Type | Description |
273+
| --- | --- | --- |
274+
| [options] | <code>Object</code> | |
275+
| [options.orientation] | <code>number</code> | Used to update the EXIF `Orientation` tag, integer between 1 and 8. |
276+
| [options.density] | <code>number</code> | Number of pixels per inch (DPI). |
277+
278+
**Example**
279+
```js
280+
const outputSrgbWithMetadata = await sharp(inputRgbWithMetadata)
281+
.withMetadata()
164282
.toBuffer();
165283
```
166284
**Example**

docs/changelog.md

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Requires libvips v8.15.0
1414

1515
* Remove `sharp.vendor`.
1616

17+
* Partially deprecate `withMetadata()`, use `withExif()` and `withIccProfile()`.
18+
1719
* Add experimental support for WebAssembly-based runtimes.
1820
[@RReverser](https://github.com/RReverser)
1921

@@ -41,6 +43,9 @@ Requires libvips v8.15.0
4143
[#3823](https://github.com/lovell/sharp/pull/3823)
4244
[@uhthomas](https://github.com/uhthomas)
4345

46+
* Add more fine-grained control over output metadata.
47+
[#3824](https://github.com/lovell/sharp/issues/3824)
48+
4449
* Ensure multi-page extract remains sequential.
4550
[#3837](https://github.com/lovell/sharp/issues/3837)
4651

docs/search-index.json

+1-1
Large diffs are not rendered by default.

lib/constructor.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,12 @@ const Sharp = function (input, options) {
257257
fileOut: '',
258258
formatOut: 'input',
259259
streamOut: false,
260-
withMetadata: false,
260+
keepMetadata: 0,
261261
withMetadataOrientation: -1,
262262
withMetadataDensity: 0,
263-
withMetadataIcc: '',
264-
withMetadataStrs: {},
263+
withIccProfile: '',
264+
withExif: {},
265+
withExifMerge: true,
265266
resolveWithObject: false,
266267
// output format
267268
jpegQuality: 80,

lib/index.d.ts

+65-6
Original file line numberDiff line numberDiff line change
@@ -633,14 +633,51 @@ declare namespace sharp {
633633
*/
634634
toBuffer(options: { resolveWithObject: true }): Promise<{ data: Buffer; info: OutputInfo }>;
635635

636+
/**
637+
* Keep all EXIF metadata from the input image in the output image.
638+
* EXIF metadata is unsupported for TIFF output.
639+
* @returns A sharp instance that can be used to chain operations
640+
*/
641+
keepExif(): Sharp;
642+
643+
/**
644+
* Set EXIF metadata in the output image, ignoring any EXIF in the input image.
645+
* @param {Exif} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
646+
* @returns A sharp instance that can be used to chain operations
647+
* @throws {Error} Invalid parameters
648+
*/
649+
withExif(exif: Exif): Sharp;
650+
651+
/**
652+
* Update EXIF metadata from the input image in the output image.
653+
* @param {Exif} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
654+
* @returns A sharp instance that can be used to chain operations
655+
* @throws {Error} Invalid parameters
656+
*/
657+
withExifMerge(exif: Exif): Sharp;
658+
659+
/**
660+
* Keep ICC profile from the input image in the output image where possible.
661+
* @returns A sharp instance that can be used to chain operations
662+
*/
663+
keepIccProfile(): Sharp;
664+
665+
/**
666+
* Transform using an ICC profile and attach to the output image.
667+
* @param {string} icc - Absolute filesystem path to output ICC profile or built-in profile name (srgb, p3, cmyk).
668+
* @returns A sharp instance that can be used to chain operations
669+
* @throws {Error} Invalid parameters
670+
*/
671+
withIccProfile(icc: string, options?: WithIccProfileOptions): Sharp;
672+
636673
/**
637674
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
638675
* The default behaviour, when withMetadata is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
639676
* This will also convert to and add a web-friendly sRGB ICC profile.
640677
* @param withMetadata
641678
* @throws {Error} Invalid parameters.
642679
*/
643-
withMetadata(withMetadata?: boolean | WriteableMetadata): Sharp;
680+
withMetadata(withMetadata?: WriteableMetadata): Sharp;
644681

645682
/**
646683
* Use these JPEG options for output image.
@@ -978,15 +1015,32 @@ declare namespace sharp {
9781015
wrap?: TextWrap;
9791016
}
9801017

1018+
interface ExifDir {
1019+
[k: string]: string;
1020+
}
1021+
1022+
interface Exif {
1023+
'IFD0'?: ExifDir;
1024+
'IFD1'?: ExifDir;
1025+
'IFD2'?: ExifDir;
1026+
'IFD3'?: ExifDir;
1027+
}
1028+
9811029
interface WriteableMetadata {
1030+
/** Number of pixels per inch (DPI) */
1031+
density?: number | undefined;
9821032
/** Value between 1 and 8, used to update the EXIF Orientation tag. */
9831033
orientation?: number | undefined;
984-
/** Filesystem path to output ICC profile, defaults to sRGB. */
1034+
/**
1035+
* Filesystem path to output ICC profile, defaults to sRGB.
1036+
* @deprecated Use `withIccProfile()` instead.
1037+
*/
9851038
icc?: string | undefined;
986-
/** Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data. (optional, default {}) */
987-
exif?: Record<string, any> | undefined;
988-
/** Number of pixels per inch (DPI) */
989-
density?: number | undefined;
1039+
/**
1040+
* Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
1041+
* @deprecated Use `withExif()` or `withExifMerge()` instead.
1042+
*/
1043+
exif?: Exif | undefined;
9901044
}
9911045

9921046
interface Metadata {
@@ -1096,6 +1150,11 @@ declare namespace sharp {
10961150
force?: boolean | undefined;
10971151
}
10981152

1153+
interface WithIccProfileOptions {
1154+
/** Should the ICC profile be included in the output image metadata? (optional, default true) */
1155+
attach?: boolean | undefined;
1156+
}
1157+
10991158
interface JpegOptions extends OutputOptions {
11001159
/** Quality, integer 1-100 (optional, default 80) */
11011160
quality?: number | undefined;

0 commit comments

Comments
 (0)