Skip to content

Commit

Permalink
Merge pull request #889 from blikblum/png-sync
Browse files Browse the repository at this point in the history
 Use sync version of zlib deflate method to compress png images
  • Loading branch information
blikblum authored Dec 23, 2018
2 parents 8f1bb8c + f2208a7 commit eb60ed1
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 19 deletions.
33 changes: 33 additions & 0 deletions demo/png-benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const PDFDocument = require('../');
const fs = require('fs');

const doc = new PDFDocument();

// files with alpha channel -> uses zlib.deflate
const files = [
'test.png',
'test3.png'
];

const filesData = files.map(fileName => {
return fs.readFileSync(`images/${fileName}`);
});

const iterationCount = 100;

console.time('png-bench')

for (let i = 0; i < iterationCount; i++) {
filesData.forEach(data => {
doc.image(data)
doc.addPage()
})
}

doc.on('data', () => {})

doc.on('end', () => {
console.timeEnd('png-bench');
});

doc.end();
26 changes: 7 additions & 19 deletions lib/image/png.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class PNGImage {
const imgData = new Buffer(pixelCount * colorByteSize);
const alphaChannel = new Buffer(pixelCount);

let i = (p = (a = 0));
let i = p = a = 0;
const len = pixels.length;
while (i < len) {
imgData[p++] = pixels[i++];
Expand All @@ -123,22 +123,13 @@ class PNGImage {
alphaChannel[a++] = pixels[i++];
}

let done = 0;
zlib.deflate(imgData, (err, imgData1) => {
this.imgData = imgData1;
if (err) { throw err; }
if (++done === 2) { return this.finalize(); }
});

return zlib.deflate(alphaChannel, (err, alphaChannel1) => {
this.alphaChannel = alphaChannel1;
if (err) { throw err; }
if (++done === 2) { return this.finalize(); }
});
this.imgData = zlib.deflateSync(imgData);
this.alphaChannel = zlib.deflateSync(alphaChannel);
return this.finalize();
});
}

loadIndexedAlphaChannel(fn) {
loadIndexedAlphaChannel() {
const transparency = this.image.transparency.indexed;
return this.image.decodePixels(pixels => {
const alphaChannel = new Buffer(this.width * this.height);
Expand All @@ -148,11 +139,8 @@ class PNGImage {
alphaChannel[i++] = transparency[pixels[j]];
}

return zlib.deflate(alphaChannel, (err, alphaChannel1) => {
this.alphaChannel = alphaChannel1;
if (err) { throw err; }
return this.finalize();
});
this.alphaChannel = zlib.deflateSync(alphaChannel);
return this.finalize();
});
}
}
Expand Down
Binary file added tests/images/fish.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
170 changes: 170 additions & 0 deletions tests/unit/png.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
const PDFDocument = require("../../lib/document").default;
const PDFReference = require("../../lib/reference").default;
const PNGImage = require("../../lib/image/png").default;
const fs = require("fs");

describe("PNGImage", () => {
let document;

const createImage = fileName => {
const img = new PNGImage(fs.readFileSync(fileName), "I1");
// noop data manipulation methods
img.loadIndexedAlphaChannel = () => {
if (img.image.hasAlphaChannel) {
img.alphaChannel = {};
}
};
img.splitAlphaChannel = () => {
if (img.image.hasAlphaChannel) {
img.alphaChannel = {};
}
};
img.embed(document);
img.finalize();
return img;
};

beforeEach(() => {
document = new PDFDocument();
});

test("RGB", () => {
// ImageWidth = 400
// ImageHeight = 533
// BitDepth = 8
// ColorType = 2 (RGB)
// Compression = 0
// Filter = 0
// Interlace = 0

const img = createImage("./demo/images/test2.png");

expect(img.obj.data).toMatchObject({
BitsPerComponent: 8,
ColorSpace: "DeviceRGB",
Filter: "FlateDecode",
Height: 533,
Length: 397011,
Subtype: "Image",
Type: "XObject",
Width: 400,
DecodeParms: expect.any(PDFReference)
});

expect(img.obj.data.DecodeParms.data).toMatchObject({
BitsPerComponent: 8,
Colors: 3,
Columns: 400,
Predictor: 15
});
});

test("RGB with Alpha", () => {
// ImageWidth = 409
// ImageHeight = 400
// BitDepth = 8
// ColorType = 6 (RGB with Alpha)
// Compression = 0
// Filter = 0
// Interlace = 0

const img = createImage("./tests/images/bee.png");

expect(img.obj.data).toMatchObject({
BitsPerComponent: 8,
ColorSpace: "DeviceRGB",
Filter: "FlateDecode",
Height: 400,
Length: 47715,
Subtype: "Image",
Type: "XObject",
Width: 409,
SMask: expect.any(PDFReference)
});

expect(img.obj.data.SMask.data).toMatchObject({
BitsPerComponent: 8,
ColorSpace: "DeviceGray",
Decode: [
0,
1
],
Filter: "FlateDecode",
Height: 400,
Length: 16,
Subtype: "Image",
Type: "XObject",
Width: 409,
});
});

test("Pallete", () => {
// ImageWidth = 980
// ImageHeight = 540
// BitDepth = 8
// ColorType = 3 (Pallete)
// Compression = 0
// Filter = 0
// Interlace = 0

const img = createImage("./demo/images/test3.png");

expect(img.obj.data).toMatchObject({
BitsPerComponent: 8,
ColorSpace: ["Indexed", "DeviceRGB", 255, expect.any(PDFReference)],
Filter: "FlateDecode",
Height: 540,
Length: 56682,
Subtype: "Image",
Type: "XObject",
Width: 980,
DecodeParms: expect.any(PDFReference)
});

expect(img.obj.data.DecodeParms.data).toMatchObject({
BitsPerComponent: 8,
Colors: 1,
Columns: 980,
Predictor: 15
});
});

test("Grayscale with Alpha", () => {
// ImageWidth = 112
// ImageHeight = 112
// BitDepth = 8
// ColorType = 4 (Grayscale with Alpha)
// Compression = 0
// Filter = 0
// Interlace = 0

const img = createImage("./tests/images/fish.png");

expect(img.obj.data).toMatchObject({
BitsPerComponent: 8,
ColorSpace: "DeviceGray",
Filter: "FlateDecode",
Height: 112,
Length: 9922,
Subtype: "Image",
Type: "XObject",
Width: 112,
SMask: expect.any(PDFReference)
});

expect(img.obj.data.SMask.data).toMatchObject({
BitsPerComponent: 8,
ColorSpace: "DeviceGray",
Decode: [
0,
1
],
Filter: "FlateDecode",
Height: 112,
Length: 16,
Subtype: "Image",
Type: "XObject",
Width: 112,
});
});
});

0 comments on commit eb60ed1

Please sign in to comment.