Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions src/modules/addimage.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,11 @@ import { atob } from "../libs/AtobBtoa.js";
value: "<<" + image.decodeParameters + ">>"
});
}
if ("transparency" in image && Array.isArray(image.transparency)) {
if (
"transparency" in image &&
Array.isArray(image.transparency) &&
image.transparency.length > 0
) {
var transparency = "",
i = 0,
len = image.transparency.length;
Expand Down Expand Up @@ -285,20 +289,17 @@ import { atob } from "../libs/AtobBtoa.js";

// Soft mask
if ("sMask" in image && typeof image.sMask !== "undefined") {
var decodeParameters =
(image.predictor != null ? "/Predictor " + image.predictor : "") +
" /Colors 1 /BitsPerComponent 8" +
" /Columns " +
image.width;
var sMask = {
const sMaskBitsPerComponent =
image.sMaskBitsPerComponent ?? image.bitsPerComponent;
const sMask = {
width: image.width,
height: image.height,
colorSpace: "DeviceGray",
bitsPerComponent: image.bitsPerComponent,
decodeParameters: decodeParameters,
bitsPerComponent: sMaskBitsPerComponent,
data: image.sMask
};
if ("filter" in image) {
sMask.decodeParameters = `/Predictor ${image.predictor} /Colors 1 /BitsPerComponent ${sMaskBitsPerComponent} /Columns ${image.width}`;
sMask.filter = image.filter;
}
putImage.call(this, sMask);
Expand Down
81 changes: 63 additions & 18 deletions src/modules/png_support.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ jsPDF.API.processPNG = function(imageData, index, alias, compression) {
const {
colorSpace,
colorsPerPixel,
sMaskBitsPerComponent,
colorBytes,
alphaBytes,
needSMask,
Expand All @@ -94,25 +95,36 @@ jsPDF.API.processPNG = function(imageData, index, alias, compression) {
if (canCompress(compression)) {
predictor = getPredictorFromCompression(compression);
filter = this.decode.FLATE_DECODE;
decodeParameters = `/Predictor ${predictor} `;
decodeParameters = `/Predictor ${predictor} /Colors ${colorsPerPixel} /BitsPerComponent ${bitsPerComponent} /Columns ${width}`;

const rowByteLength = Math.ceil(
(width * colorsPerPixel * bitsPerComponent) / 8
);

imageData = compressBytes(
colorBytes,
width * colorsPerPixel,
rowByteLength,
colorsPerPixel,
bitsPerComponent,
compression
);
if (needSMask) {
sMask = compressBytes(alphaBytes, width, 1, compression);
const sMaskRowByteLength = Math.ceil((width * sMaskBitsPerComponent) / 8);
sMask = compressBytes(
alphaBytes,
sMaskRowByteLength,
1,
sMaskBitsPerComponent,
compression
);
}
} else {
filter = undefined;
decodeParameters = "";
decodeParameters = undefined;
imageData = colorBytes;
if (needSMask) sMask = alphaBytes;
}

decodeParameters += `/Colors ${colorsPerPixel} /BitsPerComponent ${bitsPerComponent} /Columns ${width}`;

if (
this.__addimage__.isArrayBuffer(imageData) ||
this.__addimage__.isArrayBufferView(imageData)
Expand Down Expand Up @@ -140,6 +152,7 @@ jsPDF.API.processPNG = function(imageData, index, alias, compression) {
width,
height,
bitsPerComponent,
sMaskBitsPerComponent,
colorSpace
};
};
Expand Down Expand Up @@ -169,7 +182,13 @@ function canCompress(value) {
function hasCompressionJS() {
return typeof zlibSync === "function";
}
function compressBytes(bytes, lineLength, colorsPerPixel, compression) {
function compressBytes(
bytes,
lineByteLength,
channels,
bitsPerComponent,
compression
) {
let level = 4;
let filter_method = filterUp;

Expand All @@ -190,10 +209,11 @@ function compressBytes(bytes, lineLength, colorsPerPixel, compression) {
break;
}

const bytesPerPixel = Math.ceil((channels * bitsPerComponent) / 8);
bytes = applyPngFilterMethod(
bytes,
lineLength,
colorsPerPixel,
lineByteLength,
bytesPerPixel,
filter_method
);
const dat = zlibSync(bytes, { level: level });
Expand All @@ -202,27 +222,27 @@ function compressBytes(bytes, lineLength, colorsPerPixel, compression) {

function applyPngFilterMethod(
bytes,
lineLength,
colorsPerPixel,
lineByteLength,
bytesPerPixel,
filter_method
) {
const lines = bytes.length / lineLength;
const lines = bytes.length / lineByteLength;
const result = new Uint8Array(bytes.length + lines);
const filter_methods = getFilterMethods();
let prevLine;

for (let i = 0; i < lines; i += 1) {
const offset = i * lineLength;
const line = bytes.subarray(offset, offset + lineLength);
const offset = i * lineByteLength;
const line = bytes.subarray(offset, offset + lineByteLength);

if (filter_method) {
result.set(filter_method(line, colorsPerPixel, prevLine), offset + i);
result.set(filter_method(line, bytesPerPixel, prevLine), offset + i);
} else {
const len = filter_methods.length;
const results = [];

for (let j = 0; j < len; j += 1) {
results[j] = filter_methods[j](line, colorsPerPixel, prevLine);
results[j] = filter_methods[j](line, bytesPerPixel, prevLine);
}

const ind = getIndexOfSmallestSum(results.concat());
Expand Down Expand Up @@ -384,18 +404,22 @@ function processIndexedPNG(decodedPng) {
mask = undefined;

const totalPixels = width * height;
// per PNG spec, palettes always use 8 bits per component
alphaBytes = new Uint8Array(totalPixels);
const dataView = new DataView(data.buffer);
for (let p = 0; p < totalPixels; p++) {
const paletteIndex = readSample(dataView, p, depth);
const [, , , alpha] = decodedPalette[paletteIndex];
alphaBytes[p] = alpha;
}
} else if (maskLength === 0) {
mask = undefined;
}

return {
colorSpace: "Indexed",
colorsPerPixel: 1,
sMaskBitsPerComponent: needSMask ? 8 : undefined,
colorBytes: data,
alphaBytes,
needSMask,
Expand Down Expand Up @@ -447,6 +471,7 @@ function processAlphaPNG(decodedPng) {
return {
colorSpace,
colorsPerPixel,
sMaskBitsPerComponent: needSMask ? depth : undefined,
colorBytes,
alphaBytes,
needSMask
Expand All @@ -457,11 +482,31 @@ function processOpaquePNG(decodedPng) {
const { data, channels } = decodedPng;
const colorSpace = channels === 1 ? "DeviceGray" : "DeviceRGB";
const colorsPerPixel = colorSpace === "DeviceGray" ? 1 : 3;
const colorBytes =
data instanceof Uint8Array ? data : new Uint8Array(data.buffer);

let colorBytes;
if (data instanceof Uint16Array) {
colorBytes = convertUint16ArrayToUint8Array(data);
} else {
colorBytes = data;
}

return { colorSpace, colorsPerPixel, colorBytes, needSMask: false };
}

function convertUint16ArrayToUint8Array(data) {
// PNG/PDF expect MSB-first byte order. Since EcmaScript does not specify
// the byte order of Uint16Array, we need to use a DataView to ensure the
// correct byte order.
const sampleCount = data.length;
const out = new Uint8Array(sampleCount * 2);
const outView = new DataView(out.buffer, out.byteOffset, out.byteLength);

for (let i = 0; i < sampleCount; i++) {
outView.setUint16(i * 2, data[i], false);
}
return out;
}

function readSample(view, sampleIndex, depth) {
const bitIndex = sampleIndex * depth;
const byteIndex = Math.floor(bitIndex / 8);
Expand Down
Binary file modified test/reference/colortype_1_grayscale_16_bit_png.pdf
Binary file not shown.
Binary file modified test/reference/colortype_2_rgb_16_bit_png.pdf
Binary file not shown.
Binary file modified test/reference/colortype_2_rgb_8_bit_png.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified test/reference/colortype_4_grayscale_alpha_16_bit_png.pdf
Binary file not shown.
Binary file modified test/reference/colortype_4_grayscale_alpha_8_bit_png.pdf
Binary file not shown.
Binary file modified test/reference/colortype_6_rgba_16_bit_png.pdf
Binary file not shown.
Binary file modified test/reference/colortype_6_rgba_8_bit_png.pdf
Binary file not shown.
Binary file modified test/reference/colortype_6_rgba_8_bit_png_NONE.pdf
Binary file not shown.
Binary file modified test/reference/encrypted_withImage.pdf
Binary file not shown.
Binary file modified test/reference/html-margin-page-break-image.pdf
Binary file not shown.
Binary file added test/reference/images/1channels-16bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/1channels-8bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/2channels-16bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/2channels-8bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/3channels-16bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/3channels-8bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/3channels-indexed-8bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/4channels-16bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/4channels-8bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/images/4channels-indexed-8bit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/reference/png-1channels-16bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-1channels-16bit-NONE.pdf
Binary file not shown.
Binary file added test/reference/png-1channels-8bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-1channels-8bit-NONE.pdf
Binary file not shown.
Binary file added test/reference/png-2channels-16bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-2channels-16bit-NONE.pdf
Binary file not shown.
Binary file added test/reference/png-2channels-8bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-2channels-8bit-NONE.pdf
Binary file not shown.
Binary file added test/reference/png-3channels-16bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-3channels-16bit-NONE.pdf
Binary file not shown.
Binary file added test/reference/png-3channels-8bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-3channels-8bit-NONE.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added test/reference/png-4channels-16bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-4channels-16bit-NONE.pdf
Binary file not shown.
Binary file added test/reference/png-4channels-8bit-MEDIUM.pdf
Binary file not shown.
Binary file added test/reference/png-4channels-8bit-NONE.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
59 changes: 59 additions & 0 deletions test/specs/png.spec.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.