diff --git a/src/display/svg.js b/src/display/svg.js index 46048f503..13b264817 100644 --- a/src/display/svg.js +++ b/src/display/svg.js @@ -30,7 +30,12 @@ function createScratchSVG(width, height) { } var convertImgDataToPng = (function convertImgDataToPngClosure() { - var crcTable = []; + var PNG_HEADER = + new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); + + var CHUNK_WRAPPER_SIZE = 12; + + var crcTable = new Int32Array(256); for (var i = 0; i < 256; i++) { var c = i; for (var h = 0; h < 8; h++) { @@ -53,32 +58,32 @@ var convertImgDataToPng = (function convertImgDataToPngClosure() { return crc ^ -1; } - function createPngChunk(type, data) { - var chunk = new Uint8Array(12 + data.length); - var p = 0; + function writePngChunk(type, body, data, offset) { + var p = offset; - var len = data.length; - chunk[p] = len >> 24 & 0xff; - chunk[p + 1] = len >> 16 & 0xff; - chunk[p + 2] = len >> 8 & 0xff; - chunk[p + 3] = len & 0xff; + var len = body.length; - chunk[p + 4] = type.charCodeAt(0) & 0xff; - chunk[p + 5] = type.charCodeAt(1) & 0xff; - chunk[p + 6] = type.charCodeAt(2) & 0xff; - chunk[p + 7] = type.charCodeAt(3) & 0xff; + data[p] = len >> 24 & 0xff; + data[p + 1] = len >> 16 & 0xff; + data[p + 2] = len >> 8 & 0xff; + data[p + 3] = len & 0xff; + p += 4; - chunk.set(data, 8); + data[p] = type.charCodeAt(0) & 0xff; + data[p + 1] = type.charCodeAt(1) & 0xff; + data[p + 2] = type.charCodeAt(2) & 0xff; + data[p + 3] = type.charCodeAt(3) & 0xff; + p += 4; - p = 8 + len; + data.set(body, p); + p += body.length; - var crc = crc32(chunk, 4, p); - chunk[p] = crc >> 24 & 0xff; - chunk[p + 1] = crc >> 16 & 0xff; - chunk[p + 2] = crc >> 8 & 0xff; - chunk[p + 3] = crc & 0xff; + var crc = crc32(data, offset + 4, p); - return chunk; + data[p] = crc >> 24 & 0xff; + data[p + 1] = crc >> 16 & 0xff; + data[p + 2] = crc >> 8 & 0xff; + data[p + 3] = crc & 0xff; } function adler32(data, start, end) { @@ -119,18 +124,26 @@ var convertImgDataToPng = (function convertImgDataToPngClosure() { throw new Error('invalid format'); } + // prefix every row with predictor 0 var literals = new Uint8Array((1 + lineSize) * height); var offsetLiterals = 0, offsetBytes = 0; - for (var y = 0; y < height; ++y) { - literals[offsetLiterals++] = 0; + var y, i; + for (y = 0; y < height; ++y) { + literals[offsetLiterals++] = 0; // no prediction literals.set(bytes.subarray(offsetBytes, offsetBytes + lineSize), offsetLiterals); offsetBytes += lineSize; offsetLiterals += lineSize; } + if (kind === ImageKind.GRAYSCALE_1BPP) { - for (var i = 0, ii = bytes.length; i < ii; i++) { - bytes[i] ^= 0xFF; + // inverting for B/W + offsetLiterals = 0; + for (y = 0; y < height; y++) { + offsetLiterals++; // skipping predictor + for (i = 0; i < lineSize; i++) { + literals[offsetLiterals++] ^= 0xFF; + } } } @@ -153,14 +166,15 @@ var convertImgDataToPng = (function convertImgDataToPngClosure() { var len = literals.length; var maxBlockLength = 0xFFFF; - var idat = new Uint8Array(2 + len + - Math.ceil(len / maxBlockLength) * 5 + 4); + var deflateBlocks = Math.ceil(len / maxBlockLength); + var idat = new Uint8Array(2 + len + deflateBlocks * 5 + 4); var pi = 0; idat[pi++] = 0x78; // compression method and flags idat[pi++] = 0x9c; // flags var pos = 0; while (len > maxBlockLength) { + // writing non-final DEFLATE blocks type 0 and length of 65535 idat[pi++] = 0x00; idat[pi++] = 0xff; idat[pi++] = 0xff; @@ -172,12 +186,12 @@ var convertImgDataToPng = (function convertImgDataToPngClosure() { len -= maxBlockLength; } + // writing non-final DEFLATE blocks type 0 idat[pi++] = 0x01; idat[pi++] = len & 0xff; idat[pi++] = len >> 8 & 0xff; idat[pi++] = (~len & 0xffff) & 0xff; idat[pi++] = (~len & 0xffff) >> 8 & 0xff; - idat.set(literals.subarray(pos), pi); pi += literals.length - pos; @@ -187,28 +201,26 @@ var convertImgDataToPng = (function convertImgDataToPngClosure() { idat[pi++] = adler >> 8 & 0xff; idat[pi++] = adler & 0xff; - var chunks = [ - new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), - createPngChunk('IHDR', ihdr), - createPngChunk('IDAT', idat), - createPngChunk('IEND', new Uint8Array(0)) - ]; + // PNG will consists: header, IHDR+data, IDAT+data, and IEND. + var pngLength = PNG_HEADER.length + CHUNK_WRAPPER_SIZE * 3 + + ihdr.length + idat.length; + var data = new Uint8Array(pngLength); + var offset = 0; + data.set(PNG_HEADER, offset); + offset += PNG_HEADER.length; + writePngChunk('IHDR', ihdr, data, offset); + offset += CHUNK_WRAPPER_SIZE + ihdr.length; + writePngChunk('IDATA', idat, data, offset); + offset += CHUNK_WRAPPER_SIZE + idat.length; + writePngChunk('IEND', new Uint8Array(0), data, offset); - var data = []; - for (var j = 0; j < 3; j++) { - data.push.apply(data, chunks[j]); - } - data = new Uint8Array(data); return PDFJS.createObjectURL(data, 'image/png'); } return function convertImgDataToPng(imgData) { var kind = (imgData.kind === undefined ? ImageKind.GRAYSCALE_1BPP : imgData.kind); - var url = encode(imgData, kind); - if (url) { - return url; - } + return encode(imgData, kind); }; })();