diff --git a/lib/image/png.js b/lib/image/png.js index 0e9acfb68..7338c9b25 100644 --- a/lib/image/png.js +++ b/lib/image/png.js @@ -49,10 +49,10 @@ class PNGImage { // For PNG color types 0, 2 and 3, the transparency data is stored in // a dedicated PNG chunk. - if (this.image.transparency.grayscale) { + if (this.image.transparency.grayscale != null) { // Use Color Key Masking (spec section 4.8.5) // An array with N elements, where N is two times the number of color components. - const val = this.image.transparency.greyscale; + const val = this.image.transparency.grayscale; return this.obj.data['Mask'] = [val, val]; } else if (this.image.transparency.rgb) { @@ -109,7 +109,8 @@ class PNGImage { splitAlphaChannel() { return this.image.decodePixels(pixels => { let a, p; - const colorByteSize = (this.image.colors * this.image.bits) / 8; + const colorCount = this.image.colors; + const colorByteSize = (colorCount * this.image.bits) / 8; const pixelCount = this.width * this.height; const imgData = new Buffer(pixelCount * colorByteSize); const alphaChannel = new Buffer(pixelCount); @@ -117,9 +118,9 @@ class PNGImage { let i = p = a = 0; const len = pixels.length; while (i < len) { - imgData[p++] = pixels[i++]; - imgData[p++] = pixels[i++]; - imgData[p++] = pixels[i++]; + for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) { + imgData[p++] = pixels[i++]; + } alphaChannel[a++] = pixels[i++]; } diff --git a/tests/images/glassware-noisy.png b/tests/images/glassware-noisy.png new file mode 100644 index 000000000..f42616311 Binary files /dev/null and b/tests/images/glassware-noisy.png differ diff --git a/tests/images/pngsuite-gray-transparent-black.png b/tests/images/pngsuite-gray-transparent-black.png new file mode 100644 index 000000000..39a7050d2 Binary files /dev/null and b/tests/images/pngsuite-gray-transparent-black.png differ diff --git a/tests/images/pngsuite-gray-transparent-white.png b/tests/images/pngsuite-gray-transparent-white.png new file mode 100644 index 000000000..99bdeed2b Binary files /dev/null and b/tests/images/pngsuite-gray-transparent-white.png differ diff --git a/tests/images/pngsuite-palette-transparent-white.png b/tests/images/pngsuite-palette-transparent-white.png new file mode 100644 index 000000000..eacab7a14 Binary files /dev/null and b/tests/images/pngsuite-palette-transparent-white.png differ diff --git a/tests/images/pngsuite-rgb-transparent-white.png b/tests/images/pngsuite-rgb-transparent-white.png new file mode 100644 index 000000000..dd3168e5c Binary files /dev/null and b/tests/images/pngsuite-rgb-transparent-white.png differ diff --git a/tests/unit/png.spec.js b/tests/unit/png.spec.js index 809fc113c..19dcb3089 100644 --- a/tests/unit/png.spec.js +++ b/tests/unit/png.spec.js @@ -10,7 +10,7 @@ describe("PNGImage", () => { const img = new PNGImage(fs.readFileSync(fileName), "I1"); // noop data manipulation methods img.loadIndexedAlphaChannel = () => { - if (img.image.hasAlphaChannel) { + if (img.image.transparency.indexed) { img.alphaChannel = {}; } }; @@ -59,6 +59,38 @@ describe("PNGImage", () => { }); }); + test("RGB white transparent", () => { + // ImageWidth = 32 + // ImageHeight = 32 + // BitDepth = 16 + // ColorType = 2 + // Compression = 0 + // Filter = 0 + // Interlace = 0 + + const img = createImage("./tests/images/pngsuite-rgb-transparent-white.png"); + + expect(img.obj.data).toMatchObject({ + BitsPerComponent: 16, + ColorSpace: "DeviceRGB", + Filter: "FlateDecode", + Height: 32, + Length: 1932, + Subtype: "Image", + Type: "XObject", + Width: 32, + Mask: [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + DecodeParms: expect.any(PDFReference) + }); + + expect(img.obj.data.DecodeParms.data).toMatchObject({ + BitsPerComponent: 16, + Colors: 3, + Columns: 32, + Predictor: 15 + }); + }); + test("RGB with Alpha", () => { // ImageWidth = 409 // ImageHeight = 400 @@ -129,6 +161,141 @@ describe("PNGImage", () => { }); }); + test("Pallete indexed transparency", () => { + // ImageWidth = 32 + // ImageHeight = 32 + // BitDepth = 8 + // ColorType = 3 + // Compression = 0 + // Filter = 0 + // Interlace = 0 + + const img = createImage("./tests/images/pngsuite-palette-transparent-white.png"); + + expect(img.obj.data).toMatchObject({ + BitsPerComponent: 8, + ColorSpace: ["Indexed", "DeviceRGB", 244, expect.any(PDFReference)], + Filter: "FlateDecode", + Height: 32, + Length: 650, + Subtype: "Image", + Type: "XObject", + Width: 32, + DecodeParms: expect.any(PDFReference), + SMask: expect.any(PDFReference), + }); + + expect(img.obj.data.DecodeParms.data).toMatchObject({ + BitsPerComponent: 8, + Colors: 1, + Columns: 32, + Predictor: 15 + }); + + expect(img.obj.data.SMask.data).toMatchObject({ + BitsPerComponent: 8, + ColorSpace: "DeviceGray", + Decode: [ + 0, + 1 + ], + Filter: "FlateDecode", + Height: 32, + Length: 16, + Subtype: "Image", + Type: "XObject", + Width: 32, + }); + }); + + test("Grayscale", () => { + // ImageWidth = 428 + // ImageHeight = 320 + // BitDepth = 8 + // ColorType = 0 + // Compression = 0 + // Filter = 0 + // Interlace = 0 + + const img = createImage("./tests/images/glassware-noisy.png"); + + expect(img.obj.data).toMatchObject({ + BitsPerComponent: 8, + ColorSpace: "DeviceGray", + Filter: "FlateDecode", + Height: 428, + Length: 82633, + Subtype: "Image", + Type: "XObject", + Width: 320, + DecodeParms: expect.any(PDFReference), + }); + }); + + test("Grayscale black transparent", () => { + // ImageWidth = 32 + // ImageHeight = 32 + // BitDepth = 4 + // ColorType = 0 + // Compression = 0 + // Filter = 0 + // Interlace = 0 + + const img = createImage("./tests/images/pngsuite-gray-transparent-black.png"); + + expect(img.obj.data).toMatchObject({ + BitsPerComponent: 4, + ColorSpace: "DeviceGray", + Filter: "FlateDecode", + Height: 32, + Length: 328, + Subtype: "Image", + Type: "XObject", + Width: 32, + Mask: [0, 0], + DecodeParms: expect.any(PDFReference), + }); + + expect(img.obj.data.DecodeParms.data).toMatchObject({ + BitsPerComponent: 4, + Colors: 1, + Columns: 32, + Predictor: 15 + }); + }); + + test("Grayscale white transparent", () => { + // ImageWidth = 32 + // ImageHeight = 32 + // BitDepth = 16 + // ColorType = 0 + // Compression = 0 + // Filter = 0 + // Interlace = 0 + + const img = createImage("./tests/images/pngsuite-gray-transparent-white.png"); + + expect(img.obj.data).toMatchObject({ + BitsPerComponent: 16, + ColorSpace: "DeviceGray", + Filter: "FlateDecode", + Height: 32, + Length: 1212, + Subtype: "Image", + Type: "XObject", + Width: 32, + Mask: [255, 255], + DecodeParms: expect.any(PDFReference), + }); + + expect(img.obj.data.DecodeParms.data).toMatchObject({ + BitsPerComponent: 16, + Colors: 1, + Columns: 32, + Predictor: 15 + }); + }); + test("Grayscale with Alpha", () => { // ImageWidth = 112 // ImageHeight = 112