Skip to content

Commit

Permalink
Add resolution option for PNG format
Browse files Browse the repository at this point in the history
  • Loading branch information
zbjornson committed Jun 11, 2018
1 parent a11b55e commit 086947a
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ canvas.createJPEGStream() // new
* Support for `canvas.toBuffer("image/jpeg")`
* Unified configuration options for `canvas.toBuffer()`, `canvas.pngStream()`
and `canvas.jpegStream()`
* Added `resolution` option for `canvas.toBuffer("image/png")` and
`canvas.createPNGStream()`

1.6.x (unreleased)
==================
Expand Down
12 changes: 8 additions & 4 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,14 @@ image contained in the canvas.
`{quality: 0.75, progressive: false, chromaSubsampling: true}`. All
properties are optional.
* For `image/png`, an object specifying the ZLIB compression level (between 0
and 9), the compression filter(s), the palette (indexed PNGs only) and/or
the background palette index (indexed PNGs only):
`{compressionLevel: 6, filters: canvas.PNG_ALL_FILTERS, palette: undefined, backgroundIndex: 0}`.
and 9), the compression filter(s), the palette (indexed PNGs only), the
the background palette index (indexed PNGs only) and/or the resolution (ppi):
`{compressionLevel: 6, filters: canvas.PNG_ALL_FILTERS, palette: undefined, backgroundIndex: 0, resolution: undefined}`.
All properties are optional.

Note that the PNG format encodes the resolution in pixels per meter, so if
you specify `96`, the file will encode 3780 ppm (~96.01 ppi). The resolution
is undefined by default to match common browser behavior.

**Return value**

Expand Down Expand Up @@ -211,7 +215,7 @@ that emits PNG-encoded data.
* `config` An object specifying the ZLIB compression level (between 0 and 9),
the compression filter(s), the palette (indexed PNGs only) and/or the
background palette index (indexed PNGs only):
`{compressionLevel: 6, filters: canvas.PNG_ALL_FILTERS, palette: undefined, backgroundIndex: 0}`.
`{compressionLevel: 6, filters: canvas.PNG_ALL_FILTERS, palette: undefined, backgroundIndex: 0, resolution: undefined}`.
All properties are optional.

#### Examples
Expand Down
6 changes: 5 additions & 1 deletion src/PNG.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <png.h>
#include <pngconf.h>
#include <cairo.h>

#include <cmath> // round
#include "closure.h"

#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
Expand Down Expand Up @@ -166,6 +166,10 @@ static cairo_status_t canvas_write_png(cairo_surface_t *surface, png_rw_ptr writ
png_set_write_fn(png, closure, write_func, canvas_png_flush);
png_set_compression_level(png, closure->closure->compressionLevel);
png_set_filter(png, 0, closure->closure->filters);
if (closure->closure->resolution != 0) {
uint32_t res = static_cast<uint32_t>(round(static_cast<double>(closure->closure->resolution) * 39.3701));
png_set_pHYs(png, info, res, res, PNG_RESOLUTION_METER);
}

cairo_format_t format = cairo_image_surface_get_format(surface);

Expand Down
1 change: 1 addition & 0 deletions src/closure.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct PdfSvgClosure : Closure {
struct PngClosure : Closure {
uint32_t compressionLevel = 6;
uint32_t filters = PNG_ALL_FILTERS;
uint32_t resolution = 0; // 0 = unspecified
// Indexed PNGs:
uint32_t nPaletteColors = 0;
uint8_t* palette = NULL;
Expand Down
20 changes: 20 additions & 0 deletions test/canvas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,26 @@ describe('Canvas', function () {
var buf = createCanvas(200,200).toBuffer('image/png');
assert.equal('PNG', buf.slice(1,4).toString());
});

it('Canvas#toBuffer("image/png", {resolution: 96})', function () {
const buf = createCanvas(200, 200).toBuffer('image/png', {resolution: 96});
// 3780 ppm ~= 96 ppi
for (let i = 0; i < buf.length - 12; i++) {
if (buf[i] === 0x70 &&
buf[i + 1] === 0x48 &&
buf[i + 2] === 0x59 &&
buf[i + 3] === 0x73) { // pHYs
assert.equal(buf[i + 4], 0);
assert.equal(buf[i + 5], 0);
assert.equal(buf[i + 6], 0x0e);
assert.equal(buf[i + 7], 0xc4); // x
assert.equal(buf[i + 8], 0);
assert.equal(buf[i + 9], 0);
assert.equal(buf[i + 10], 0x0e);
assert.equal(buf[i + 11], 0xc4); // y
}
}
})

it('Canvas#toBuffer("image/png", {compressionLevel: 5})', function () {
var buf = createCanvas(200,200).toBuffer('image/png', {compressionLevel: 5});
Expand Down

0 comments on commit 086947a

Please sign in to comment.