diff --git a/docs/api-output.md b/docs/api-output.md index 62b35e287..73292d4e2 100644 --- a/docs/api-output.md +++ b/docs/api-output.md @@ -24,7 +24,7 @@ A `Promise` is returned when `callback` is not provided. | Param | Type | Description | | --- | --- | --- | | fileOut | string | the path to write the image data to. | -| [callback] | function | called on completion with two arguments `(err, info)`. `info` contains the output image `format`, `size` (bytes), `width`, `height`, `channels` and `premultiplied` (indicating if premultiplication was used). When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region. May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text. | +| [callback] | function | called on completion with two arguments `(err, info)`. `info` contains the output image `format`, `size` (bytes), `width`, `height`, `channels` and `premultiplied` (indicating if premultiplication was used). When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region. Animated output will also contain `pageHeight` and `pages`. May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text. | **Example** ```js @@ -59,6 +59,7 @@ See [withMetadata](#withmetadata) for control over this. - `info` contains the output image `format`, `size` (bytes), `width`, `height`, `channels` and `premultiplied` (indicating if premultiplication was used). When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. +Animated output will also contain `pageHeight` and `pages`. May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text. A `Promise` is returned when `callback` is not provided. diff --git a/docs/changelog.md b/docs/changelog.md index e4ff7dc9d..27afb7f70 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,9 @@ Requires libvips v8.15.2 ### v0.33.5 - TBD +* Add `pageHeight` and `pages` to response of multi-page output. + [#3411](https://github.com/lovell/sharp/issues/3411) + * Ensure option to force use of a globally-installed libvips works correctly. [#4111](https://github.com/lovell/sharp/pull/4111) [@project0](https://github.com/project0) diff --git a/lib/output.js b/lib/output.js index d7b071210..137932cc0 100644 --- a/lib/output.js +++ b/lib/output.js @@ -65,6 +65,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math * `channels` and `premultiplied` (indicating if premultiplication was used). * When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. * When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region. + * Animated output will also contain `pageHeight` and `pages`. * May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text. * @returns {Promise} - when no callback is provided * @throws {Error} Invalid parameters @@ -109,6 +110,7 @@ function toFile (fileOut, callback) { * - `info` contains the output image `format`, `size` (bytes), `width`, `height`, * `channels` and `premultiplied` (indicating if premultiplication was used). * When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`. + * Animated output will also contain `pageHeight` and `pages`. * May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text. * * A `Promise` is returned when `callback` is not provided. diff --git a/src/pipeline.cc b/src/pipeline.cc index b21625c5e..9dc22ed72 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -849,6 +849,11 @@ class PipelineWorker : public Napi::AsyncWorker { image = sharp::SetAnimationProperties( image, nPages, targetPageHeight, baton->delay, baton->loop); + if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) { + baton->pageHeightOut = image.get_int(VIPS_META_PAGE_HEIGHT); + baton->pagesOut = image.get_int(VIPS_META_N_PAGES); + } + // Output sharp::SetTimeout(image, baton->timeoutSeconds); if (baton->fileOut.empty()) { @@ -1284,6 +1289,10 @@ class PipelineWorker : public Napi::AsyncWorker { if (baton->input->textAutofitDpi) { info.Set("textAutofitDpi", static_cast(baton->input->textAutofitDpi)); } + if (baton->pageHeightOut) { + info.Set("pageHeight", static_cast(baton->pageHeightOut)); + info.Set("pages", static_cast(baton->pagesOut)); + } if (baton->bufferOutLength > 0) { // Add buffer size to info diff --git a/src/pipeline.h b/src/pipeline.h index 6ebf0e63f..bc79eb2cc 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -43,6 +43,8 @@ struct PipelineBaton { std::string fileOut; void *bufferOut; size_t bufferOutLength; + int pageHeightOut; + int pagesOut; std::vector composite; std::vector joinChannelIn; int topOffsetPre; @@ -226,6 +228,8 @@ struct PipelineBaton { PipelineBaton(): input(nullptr), bufferOutLength(0), + pageHeightOut(0), + pagesOut(0), topOffsetPre(-1), topOffsetPost(-1), channels(0), diff --git a/test/unit/gif.js b/test/unit/gif.js index 1e05b63db..f067dcc1c 100644 --- a/test/unit/gif.js +++ b/test/unit/gif.js @@ -39,7 +39,7 @@ describe('GIF input', () => { }) ); - it('Animated GIF first page to PNG', () => + it('Animated GIF first page to non-animated GIF', () => sharp(fixtures.inputGifAnimated) .toBuffer({ resolveWithObject: true }) .then(({ data, info }) => { @@ -49,10 +49,12 @@ describe('GIF input', () => { assert.strictEqual(80, info.width); assert.strictEqual(80, info.height); assert.strictEqual(4, info.channels); + assert.strictEqual(undefined, info.pages); + assert.strictEqual(undefined, info.pageHeight); }) ); - it('Animated GIF all pages to PNG "toilet roll"', () => + it('Animated GIF round trip', () => sharp(fixtures.inputGifAnimated, { pages: -1 }) .toBuffer({ resolveWithObject: true }) .then(({ data, info }) => { @@ -62,6 +64,8 @@ describe('GIF input', () => { assert.strictEqual(80, info.width); assert.strictEqual(2400, info.height); assert.strictEqual(4, info.channels); + assert.strictEqual(30, info.pages); + assert.strictEqual(80, info.pageHeight); }) );