@revizly/sharp 0.33.2-revizly3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,444 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ 'use strict';
5
+
6
+ const util = require('node:util');
7
+ const stream = require('node:stream');
8
+ const is = require('./is');
9
+
10
+ require('./sharp');
11
+
12
+ // Use NODE_DEBUG=sharp to enable libvips warnings
13
+ const debuglog = util.debuglog('sharp');
14
+
15
+ /**
16
+ * Constructor factory to create an instance of `sharp`, to which further methods are chained.
17
+ *
18
+ * JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
19
+ * When using Stream based output, derived attributes are available from the `info` event.
20
+ *
21
+ * Non-critical problems encountered during processing are emitted as `warning` events.
22
+ *
23
+ * Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
24
+ *
25
+ * When loading more than one page/frame of an animated image,
26
+ * these are combined as a vertically-stacked "toilet roll" image
27
+ * where the overall height is the `pageHeight` multiplied by the number of `pages`.
28
+ *
29
+ * @constructs Sharp
30
+ *
31
+ * @emits Sharp#info
32
+ * @emits Sharp#warning
33
+ *
34
+ * @example
35
+ * sharp('input.jpg')
36
+ * .resize(300, 200)
37
+ * .toFile('output.jpg', function(err) {
38
+ * // output.jpg is a 300 pixels wide and 200 pixels high image
39
+ * // containing a scaled and cropped version of input.jpg
40
+ * });
41
+ *
42
+ * @example
43
+ * // Read image data from readableStream,
44
+ * // resize to 300 pixels wide,
45
+ * // emit an 'info' event with calculated dimensions
46
+ * // and finally write image data to writableStream
47
+ * var transformer = sharp()
48
+ * .resize(300)
49
+ * .on('info', function(info) {
50
+ * console.log('Image height is ' + info.height);
51
+ * });
52
+ * readableStream.pipe(transformer).pipe(writableStream);
53
+ *
54
+ * @example
55
+ * // Create a blank 300x200 PNG image of semi-translucent red pixels
56
+ * sharp({
57
+ * create: {
58
+ * width: 300,
59
+ * height: 200,
60
+ * channels: 4,
61
+ * background: { r: 255, g: 0, b: 0, alpha: 0.5 }
62
+ * }
63
+ * })
64
+ * .png()
65
+ * .toBuffer()
66
+ * .then( ... );
67
+ *
68
+ * @example
69
+ * // Convert an animated GIF to an animated WebP
70
+ * await sharp('in.gif', { animated: true }).toFile('out.webp');
71
+ *
72
+ * @example
73
+ * // Read a raw array of pixels and save it to a png
74
+ * const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
75
+ * const image = sharp(input, {
76
+ * // because the input does not contain its dimensions or how many channels it has
77
+ * // we need to specify it in the constructor options
78
+ * raw: {
79
+ * width: 2,
80
+ * height: 1,
81
+ * channels: 3
82
+ * }
83
+ * });
84
+ * await image.toFile('my-two-pixels.png');
85
+ *
86
+ * @example
87
+ * // Generate RGB Gaussian noise
88
+ * await sharp({
89
+ * create: {
90
+ * width: 300,
91
+ * height: 200,
92
+ * channels: 3,
93
+ * noise: {
94
+ * type: 'gaussian',
95
+ * mean: 128,
96
+ * sigma: 30
97
+ * }
98
+ * }
99
+ * }).toFile('noise.png');
100
+ *
101
+ * @example
102
+ * // Generate an image from text
103
+ * await sharp({
104
+ * text: {
105
+ * text: 'Hello, world!',
106
+ * width: 400, // max width
107
+ * height: 300 // max height
108
+ * }
109
+ * }).toFile('text_bw.png');
110
+ *
111
+ * @example
112
+ * // Generate an rgba image from text using pango markup and font
113
+ * await sharp({
114
+ * text: {
115
+ * text: '<span foreground="red">Red!</span><span background="cyan">blue</span>',
116
+ * font: 'sans',
117
+ * rgba: true,
118
+ * dpi: 300
119
+ * }
120
+ * }).toFile('text_rgba.png');
121
+ *
122
+ * @param {(Buffer|ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
123
+ * a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
124
+ * a TypedArray containing raw pixel image data, or
125
+ * a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
126
+ * JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
127
+ * @param {Object} [options] - if present, is an Object with optional attributes.
128
+ * @param {string} [options.failOn='warning'] - When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort.
129
+ * @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
130
+ * (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
131
+ * An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
132
+ * @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF).
133
+ * @param {boolean} [options.sequentialRead=true] - Set this to `false` to use random access rather than sequential read. Some operations will do this automatically.
134
+ * @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
135
+ * @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
136
+ * @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
137
+ * @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
138
+ * @param {number} [options.subifd=-1] - subIFD (Sub Image File Directory) to extract for OME-TIFF, defaults to main image.
139
+ * @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
140
+ * @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
141
+ * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
142
+ * @param {number} [options.raw.width] - integral number of pixels wide.
143
+ * @param {number} [options.raw.height] - integral number of pixels high.
144
+ * @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
145
+ * @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
146
+ * to avoid sharp premultiplying the image. (optional, default `false`)
147
+ * @param {Object} [options.create] - describes a new image to be created.
148
+ * @param {number} [options.create.width] - integral number of pixels wide.
149
+ * @param {number} [options.create.height] - integral number of pixels high.
150
+ * @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
151
+ * @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
152
+ * @param {Object} [options.create.noise] - describes a noise to be created.
153
+ * @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
154
+ * @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
155
+ * @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
156
+ * @param {Object} [options.text] - describes a new text image to be created.
157
+ * @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
158
+ * @param {string} [options.text.font] - font name to render with.
159
+ * @param {string} [options.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
160
+ * @param {number} [options.text.width=0] - Integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
161
+ * @param {number} [options.text.height=0] - Maximum integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
162
+ * @param {string} [options.text.align='left'] - Alignment style for multi-line text (`'left'`, `'centre'`, `'center'`, `'right'`).
163
+ * @param {boolean} [options.text.justify=false] - set this to true to apply justification to the text.
164
+ * @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
165
+ * @param {boolean} [options.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
166
+ * @param {number} [options.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
167
+ * @param {string} [options.text.wrap='word'] - word wrapping style when width is provided, one of: 'word', 'char', 'charWord' (prefer char, fallback to word) or 'none'.
168
+ * @returns {Sharp}
169
+ * @throws {Error} Invalid parameters
170
+ */
171
+ const Sharp = function (input, options) {
172
+ if (arguments.length === 1 && !is.defined(input)) {
173
+ throw new Error('Invalid input');
174
+ }
175
+ if (!(this instanceof Sharp)) {
176
+ return new Sharp(input, options);
177
+ }
178
+ stream.Duplex.call(this);
179
+ this.options = {
180
+ // resize options
181
+ topOffsetPre: -1,
182
+ leftOffsetPre: -1,
183
+ widthPre: -1,
184
+ heightPre: -1,
185
+ topOffsetPost: -1,
186
+ leftOffsetPost: -1,
187
+ widthPost: -1,
188
+ heightPost: -1,
189
+ width: -1,
190
+ height: -1,
191
+ canvas: 'crop',
192
+ position: 0,
193
+ resizeBackground: [0, 0, 0, 255],
194
+ useExifOrientation: false,
195
+ angle: 0,
196
+ rotationAngle: 0,
197
+ rotationBackground: [0, 0, 0, 255],
198
+ rotateBeforePreExtract: false,
199
+ flip: false,
200
+ flop: false,
201
+ extendTop: 0,
202
+ extendBottom: 0,
203
+ extendLeft: 0,
204
+ extendRight: 0,
205
+ extendBackground: [0, 0, 0, 255],
206
+ extendWith: 'background',
207
+ withoutEnlargement: false,
208
+ withoutReduction: false,
209
+ affineMatrix: [],
210
+ affineBackground: [0, 0, 0, 255],
211
+ affineIdx: 0,
212
+ affineIdy: 0,
213
+ affineOdx: 0,
214
+ affineOdy: 0,
215
+ affineInterpolator: this.constructor.interpolators.bilinear,
216
+ kernel: 'lanczos3',
217
+ fastShrinkOnLoad: true,
218
+ // operations
219
+ tint: [-1, 0, 0, 0],
220
+ flatten: false,
221
+ flattenBackground: [0, 0, 0],
222
+ unflatten: false,
223
+ negate: false,
224
+ negateAlpha: true,
225
+ medianSize: 0,
226
+ blurSigma: 0,
227
+ sharpenSigma: 0,
228
+ sharpenM1: 1,
229
+ sharpenM2: 2,
230
+ sharpenX1: 2,
231
+ sharpenY2: 10,
232
+ sharpenY3: 20,
233
+ threshold: 0,
234
+ thresholdGrayscale: true,
235
+ trimBackground: [],
236
+ trimThreshold: -1,
237
+ trimLineArt: false,
238
+ gamma: 0,
239
+ gammaOut: 0,
240
+ greyscale: false,
241
+ normalise: false,
242
+ normaliseLower: 1,
243
+ normaliseUpper: 99,
244
+ claheWidth: 0,
245
+ claheHeight: 0,
246
+ claheMaxSlope: 3,
247
+ brightness: 1,
248
+ saturation: 1,
249
+ hue: 0,
250
+ lightness: 0,
251
+ booleanBufferIn: null,
252
+ booleanFileIn: '',
253
+ joinChannelIn: [],
254
+ extractChannel: -1,
255
+ removeAlpha: false,
256
+ ensureAlpha: -1,
257
+ colourspace: 'srgb',
258
+ colourspaceInput: 'last',
259
+ composite: [],
260
+ // output
261
+ fileOut: '',
262
+ formatOut: 'input',
263
+ streamOut: false,
264
+ keepMetadata: 0,
265
+ withMetadataOrientation: -1,
266
+ withMetadataDensity: 0,
267
+ withIccProfile: '',
268
+ withExif: {},
269
+ withExifMerge: true,
270
+ resolveWithObject: false,
271
+ // output format
272
+ jpegQuality: 80,
273
+ jpegProgressive: false,
274
+ jpegChromaSubsampling: '4:2:0',
275
+ jpegTrellisQuantisation: false,
276
+ jpegOvershootDeringing: false,
277
+ jpegOptimiseScans: false,
278
+ jpegOptimiseCoding: true,
279
+ jpegQuantisationTable: 0,
280
+ pngProgressive: false,
281
+ pngCompressionLevel: 6,
282
+ pngAdaptiveFiltering: false,
283
+ pngPalette: false,
284
+ pngQuality: 100,
285
+ pngEffort: 7,
286
+ pngBitdepth: 8,
287
+ pngDither: 1,
288
+ jp2Quality: 80,
289
+ jp2TileHeight: 512,
290
+ jp2TileWidth: 512,
291
+ jp2Lossless: false,
292
+ jp2ChromaSubsampling: '4:4:4',
293
+ webpQuality: 80,
294
+ webpAlphaQuality: 100,
295
+ webpLossless: false,
296
+ webpNearLossless: false,
297
+ webpSmartSubsample: false,
298
+ webpPreset: 'default',
299
+ webpEffort: 4,
300
+ webpMinSize: false,
301
+ webpMixed: false,
302
+ gifBitdepth: 8,
303
+ gifEffort: 7,
304
+ gifDither: 1,
305
+ gifInterFrameMaxError: 0,
306
+ gifInterPaletteMaxError: 3,
307
+ gifReuse: true,
308
+ gifProgressive: false,
309
+ tiffQuality: 80,
310
+ tiffCompression: 'jpeg',
311
+ tiffPredictor: 'horizontal',
312
+ tiffPyramid: false,
313
+ tiffMiniswhite: false,
314
+ tiffBitdepth: 8,
315
+ tiffTile: false,
316
+ tiffTileHeight: 256,
317
+ tiffTileWidth: 256,
318
+ tiffXres: 1.0,
319
+ tiffYres: 1.0,
320
+ tiffResolutionUnit: 'inch',
321
+ heifQuality: 50,
322
+ heifLossless: false,
323
+ heifCompression: 'av1',
324
+ heifEffort: 4,
325
+ heifChromaSubsampling: '4:4:4',
326
+ jxlDistance: 1,
327
+ jxlDecodingTier: 0,
328
+ jxlEffort: 7,
329
+ jxlLossless: false,
330
+ rawDepth: 'uchar',
331
+ tileSize: 256,
332
+ tileOverlap: 0,
333
+ tileContainer: 'fs',
334
+ tileLayout: 'dz',
335
+ tileFormat: 'last',
336
+ tileDepth: 'last',
337
+ tileAngle: 0,
338
+ tileSkipBlanks: -1,
339
+ tileBackground: [255, 255, 255, 255],
340
+ tileCentre: false,
341
+ tileId: 'https://example.com/iiif',
342
+ tileBasename: '',
343
+ timeoutSeconds: 0,
344
+ linearA: [],
345
+ linearB: [],
346
+ // Function to notify of libvips warnings
347
+ debuglog: warning => {
348
+ this.emit('warning', warning);
349
+ debuglog(warning);
350
+ },
351
+ // Function to notify of queue length changes
352
+ queueListener: function (queueLength) {
353
+ Sharp.queue.emit('change', queueLength);
354
+ }
355
+ };
356
+ this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
357
+ return this;
358
+ };
359
+ Object.setPrototypeOf(Sharp.prototype, stream.Duplex.prototype);
360
+ Object.setPrototypeOf(Sharp, stream.Duplex);
361
+
362
+ /**
363
+ * Take a "snapshot" of the Sharp instance, returning a new instance.
364
+ * Cloned instances inherit the input of their parent instance.
365
+ * This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
366
+ *
367
+ * @example
368
+ * const pipeline = sharp().rotate();
369
+ * pipeline.clone().resize(800, 600).pipe(firstWritableStream);
370
+ * pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
371
+ * readableStream.pipe(pipeline);
372
+ * // firstWritableStream receives auto-rotated, resized readableStream
373
+ * // secondWritableStream receives auto-rotated, extracted region of readableStream
374
+ *
375
+ * @example
376
+ * // Create a pipeline that will download an image, resize it and format it to different files
377
+ * // Using Promises to know when the pipeline is complete
378
+ * const fs = require("fs");
379
+ * const got = require("got");
380
+ * const sharpStream = sharp({ failOn: 'none' });
381
+ *
382
+ * const promises = [];
383
+ *
384
+ * promises.push(
385
+ * sharpStream
386
+ * .clone()
387
+ * .jpeg({ quality: 100 })
388
+ * .toFile("originalFile.jpg")
389
+ * );
390
+ *
391
+ * promises.push(
392
+ * sharpStream
393
+ * .clone()
394
+ * .resize({ width: 500 })
395
+ * .jpeg({ quality: 80 })
396
+ * .toFile("optimized-500.jpg")
397
+ * );
398
+ *
399
+ * promises.push(
400
+ * sharpStream
401
+ * .clone()
402
+ * .resize({ width: 500 })
403
+ * .webp({ quality: 80 })
404
+ * .toFile("optimized-500.webp")
405
+ * );
406
+ *
407
+ * // https://github.com/sindresorhus/got/blob/main/documentation/3-streams.md
408
+ * got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
409
+ *
410
+ * Promise.all(promises)
411
+ * .then(res => { console.log("Done!", res); })
412
+ * .catch(err => {
413
+ * console.error("Error processing files, let's clean it up", err);
414
+ * try {
415
+ * fs.unlinkSync("originalFile.jpg");
416
+ * fs.unlinkSync("optimized-500.jpg");
417
+ * fs.unlinkSync("optimized-500.webp");
418
+ * } catch (e) {}
419
+ * });
420
+ *
421
+ * @returns {Sharp}
422
+ */
423
+ function clone () {
424
+ // Clone existing options
425
+ const clone = this.constructor.call();
426
+ clone.options = Object.assign({}, this.options);
427
+ // Pass 'finish' event to clone for Stream-based input
428
+ if (this._isStreamInput()) {
429
+ this.on('finish', () => {
430
+ // Clone inherits input data
431
+ this._flattenBufferIn();
432
+ clone.options.bufferIn = this.options.bufferIn;
433
+ clone.emit('finish');
434
+ });
435
+ }
436
+ return clone;
437
+ }
438
+ Object.assign(Sharp.prototype, { clone });
439
+
440
+ /**
441
+ * Export constructor.
442
+ * @private
443
+ */
444
+ module.exports = Sharp;