Skip to content

Commit eb60ed1

Browse files
authored
Merge pull request #889 from blikblum/png-sync
Use sync version of zlib deflate method to compress png images
2 parents 8f1bb8c + f2208a7 commit eb60ed1

File tree

4 files changed

+210
-19
lines changed

4 files changed

+210
-19
lines changed

demo/png-benchmark.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const PDFDocument = require('../');
2+
const fs = require('fs');
3+
4+
const doc = new PDFDocument();
5+
6+
// files with alpha channel -> uses zlib.deflate
7+
const files = [
8+
'test.png',
9+
'test3.png'
10+
];
11+
12+
const filesData = files.map(fileName => {
13+
return fs.readFileSync(`images/${fileName}`);
14+
});
15+
16+
const iterationCount = 100;
17+
18+
console.time('png-bench')
19+
20+
for (let i = 0; i < iterationCount; i++) {
21+
filesData.forEach(data => {
22+
doc.image(data)
23+
doc.addPage()
24+
})
25+
}
26+
27+
doc.on('data', () => {})
28+
29+
doc.on('end', () => {
30+
console.timeEnd('png-bench');
31+
});
32+
33+
doc.end();

lib/image/png.js

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class PNGImage {
114114
const imgData = new Buffer(pixelCount * colorByteSize);
115115
const alphaChannel = new Buffer(pixelCount);
116116

117-
let i = (p = (a = 0));
117+
let i = p = a = 0;
118118
const len = pixels.length;
119119
while (i < len) {
120120
imgData[p++] = pixels[i++];
@@ -123,22 +123,13 @@ class PNGImage {
123123
alphaChannel[a++] = pixels[i++];
124124
}
125125

126-
let done = 0;
127-
zlib.deflate(imgData, (err, imgData1) => {
128-
this.imgData = imgData1;
129-
if (err) { throw err; }
130-
if (++done === 2) { return this.finalize(); }
131-
});
132-
133-
return zlib.deflate(alphaChannel, (err, alphaChannel1) => {
134-
this.alphaChannel = alphaChannel1;
135-
if (err) { throw err; }
136-
if (++done === 2) { return this.finalize(); }
137-
});
126+
this.imgData = zlib.deflateSync(imgData);
127+
this.alphaChannel = zlib.deflateSync(alphaChannel);
128+
return this.finalize();
138129
});
139130
}
140131

141-
loadIndexedAlphaChannel(fn) {
132+
loadIndexedAlphaChannel() {
142133
const transparency = this.image.transparency.indexed;
143134
return this.image.decodePixels(pixels => {
144135
const alphaChannel = new Buffer(this.width * this.height);
@@ -148,11 +139,8 @@ class PNGImage {
148139
alphaChannel[i++] = transparency[pixels[j]];
149140
}
150141

151-
return zlib.deflate(alphaChannel, (err, alphaChannel1) => {
152-
this.alphaChannel = alphaChannel1;
153-
if (err) { throw err; }
154-
return this.finalize();
155-
});
142+
this.alphaChannel = zlib.deflateSync(alphaChannel);
143+
return this.finalize();
156144
});
157145
}
158146
}

tests/images/fish.png

9.77 KB
Loading

tests/unit/png.spec.js

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
const PDFDocument = require("../../lib/document").default;
2+
const PDFReference = require("../../lib/reference").default;
3+
const PNGImage = require("../../lib/image/png").default;
4+
const fs = require("fs");
5+
6+
describe("PNGImage", () => {
7+
let document;
8+
9+
const createImage = fileName => {
10+
const img = new PNGImage(fs.readFileSync(fileName), "I1");
11+
// noop data manipulation methods
12+
img.loadIndexedAlphaChannel = () => {
13+
if (img.image.hasAlphaChannel) {
14+
img.alphaChannel = {};
15+
}
16+
};
17+
img.splitAlphaChannel = () => {
18+
if (img.image.hasAlphaChannel) {
19+
img.alphaChannel = {};
20+
}
21+
};
22+
img.embed(document);
23+
img.finalize();
24+
return img;
25+
};
26+
27+
beforeEach(() => {
28+
document = new PDFDocument();
29+
});
30+
31+
test("RGB", () => {
32+
// ImageWidth = 400
33+
// ImageHeight = 533
34+
// BitDepth = 8
35+
// ColorType = 2 (RGB)
36+
// Compression = 0
37+
// Filter = 0
38+
// Interlace = 0
39+
40+
const img = createImage("./demo/images/test2.png");
41+
42+
expect(img.obj.data).toMatchObject({
43+
BitsPerComponent: 8,
44+
ColorSpace: "DeviceRGB",
45+
Filter: "FlateDecode",
46+
Height: 533,
47+
Length: 397011,
48+
Subtype: "Image",
49+
Type: "XObject",
50+
Width: 400,
51+
DecodeParms: expect.any(PDFReference)
52+
});
53+
54+
expect(img.obj.data.DecodeParms.data).toMatchObject({
55+
BitsPerComponent: 8,
56+
Colors: 3,
57+
Columns: 400,
58+
Predictor: 15
59+
});
60+
});
61+
62+
test("RGB with Alpha", () => {
63+
// ImageWidth = 409
64+
// ImageHeight = 400
65+
// BitDepth = 8
66+
// ColorType = 6 (RGB with Alpha)
67+
// Compression = 0
68+
// Filter = 0
69+
// Interlace = 0
70+
71+
const img = createImage("./tests/images/bee.png");
72+
73+
expect(img.obj.data).toMatchObject({
74+
BitsPerComponent: 8,
75+
ColorSpace: "DeviceRGB",
76+
Filter: "FlateDecode",
77+
Height: 400,
78+
Length: 47715,
79+
Subtype: "Image",
80+
Type: "XObject",
81+
Width: 409,
82+
SMask: expect.any(PDFReference)
83+
});
84+
85+
expect(img.obj.data.SMask.data).toMatchObject({
86+
BitsPerComponent: 8,
87+
ColorSpace: "DeviceGray",
88+
Decode: [
89+
0,
90+
1
91+
],
92+
Filter: "FlateDecode",
93+
Height: 400,
94+
Length: 16,
95+
Subtype: "Image",
96+
Type: "XObject",
97+
Width: 409,
98+
});
99+
});
100+
101+
test("Pallete", () => {
102+
// ImageWidth = 980
103+
// ImageHeight = 540
104+
// BitDepth = 8
105+
// ColorType = 3 (Pallete)
106+
// Compression = 0
107+
// Filter = 0
108+
// Interlace = 0
109+
110+
const img = createImage("./demo/images/test3.png");
111+
112+
expect(img.obj.data).toMatchObject({
113+
BitsPerComponent: 8,
114+
ColorSpace: ["Indexed", "DeviceRGB", 255, expect.any(PDFReference)],
115+
Filter: "FlateDecode",
116+
Height: 540,
117+
Length: 56682,
118+
Subtype: "Image",
119+
Type: "XObject",
120+
Width: 980,
121+
DecodeParms: expect.any(PDFReference)
122+
});
123+
124+
expect(img.obj.data.DecodeParms.data).toMatchObject({
125+
BitsPerComponent: 8,
126+
Colors: 1,
127+
Columns: 980,
128+
Predictor: 15
129+
});
130+
});
131+
132+
test("Grayscale with Alpha", () => {
133+
// ImageWidth = 112
134+
// ImageHeight = 112
135+
// BitDepth = 8
136+
// ColorType = 4 (Grayscale with Alpha)
137+
// Compression = 0
138+
// Filter = 0
139+
// Interlace = 0
140+
141+
const img = createImage("./tests/images/fish.png");
142+
143+
expect(img.obj.data).toMatchObject({
144+
BitsPerComponent: 8,
145+
ColorSpace: "DeviceGray",
146+
Filter: "FlateDecode",
147+
Height: 112,
148+
Length: 9922,
149+
Subtype: "Image",
150+
Type: "XObject",
151+
Width: 112,
152+
SMask: expect.any(PDFReference)
153+
});
154+
155+
expect(img.obj.data.SMask.data).toMatchObject({
156+
BitsPerComponent: 8,
157+
ColorSpace: "DeviceGray",
158+
Decode: [
159+
0,
160+
1
161+
],
162+
Filter: "FlateDecode",
163+
Height: 112,
164+
Length: 16,
165+
Subtype: "Image",
166+
Type: "XObject",
167+
Width: 112,
168+
});
169+
});
170+
});

0 commit comments

Comments
 (0)