/**@preserve * ==================================================================== * jsPDF PNG PlugIn * Copyright (c) 2014 James Robb, https://github.com/jamesbrobb * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ==================================================================== */ (function(jsPDFAPI) { 'use strict' /* * @see http://www.w3.org/TR/PNG-Chunks.html * Color Allowed Interpretation Type Bit Depths 0 1,2,4,8,16 Each pixel is a grayscale sample. 2 8,16 Each pixel is an R,G,B triple. 3 1,2,4,8 Each pixel is a palette index; a PLTE chunk must appear. 4 8,16 Each pixel is a grayscale sample, followed by an alpha sample. 6 8,16 Each pixel is an R,G,B triple, followed by an alpha sample. */ /* * PNG filter method types * * @see http://www.w3.org/TR/PNG-Filters.html * @see http://www.libpng.org/pub/png/book/chapter09.html * * This is what the value 'Predictor' in decode params relates to * * 15 is "optimal prediction", which means the prediction algorithm can change from line to line. * In that case, you actually have to read the first byte off each line for the prediction algorthim (which should be 0-4, corresponding to PDF 10-14) and select the appropriate unprediction algorithm based on that byte. * 0 None 1 Sub 2 Up 3 Average 4 Paeth */ var doesNotHavePngJS = function() { return typeof PNG !== 'function' || typeof FlateStream !== 'function'; } , canCompress = function(value) { return value !== jsPDFAPI.image_compression.NONE && hasCompressionJS(); } , hasCompressionJS = function() { var inst = typeof Deflater === 'function'; if(!inst) throw new Error("requires deflate.js for compression") return inst; } , compressBytes = function(bytes, lineLength, colorsPerPixel, compression) { var level = 5, filter_method = filterUp; switch(compression) { case jsPDFAPI.image_compression.FAST: level = 3; filter_method = filterSub; break; case jsPDFAPI.image_compression.MEDIUM: level = 6; filter_method = filterAverage; break; case jsPDFAPI.image_compression.SLOW: level = 9; filter_method = filterPaeth;//uses to sum to choose best filter for each line break; } bytes = applyPngFilterMethod(bytes, lineLength, colorsPerPixel, filter_method); var header = new Uint8Array(createZlibHeader(level)); var checksum = adler32(bytes); var deflate = new Deflater(level); var a = deflate.append(bytes); var cBytes = deflate.flush(); var len = header.length + a.length + cBytes.length; var cmpd = new Uint8Array(len + 4); cmpd.set(header); cmpd.set(a, header.length); cmpd.set(cBytes, header.length + a.length); cmpd[len++] = (checksum >>> 24) & 0xff; cmpd[len++] = (checksum >>> 16) & 0xff; cmpd[len++] = (checksum >>> 8) & 0xff; cmpd[len++] = checksum & 0xff; return jsPDFAPI.arrayBufferToBinaryString(cmpd); } , createZlibHeader = function(bytes, level){ /* * @see http://www.ietf.org/rfc/rfc1950.txt for zlib header */ var cm = 8; var cinfo = Math.LOG2E * Math.log(0x8000) - 8; var cmf = (cinfo << 4) | cm; var hdr = cmf << 8; var flevel = Math.min(3, ((level - 1) & 0xff) >> 1); hdr |= (flevel << 6); hdr |= 0;//FDICT hdr += 31 - (hdr % 31); return [cmf, (hdr & 0xff) & 0xff]; } , adler32 = function(array, param) { var adler = 1; var s1 = adler & 0xffff, s2 = (adler >>> 16) & 0xffff; var len = array.length; var tlen; var i = 0; while (len > 0) { tlen = len > param ? param : len; len -= tlen; do { s1 += array[i++]; s2 += s1; } while (--tlen); s1 %= 65521; s2 %= 65521; } return ((s2 << 16) | s1) >>> 0; } , applyPngFilterMethod = function(bytes, lineLength, colorsPerPixel, filter_method) { var lines = bytes.length / lineLength, result = new Uint8Array(bytes.length + lines), filter_methods = getFilterMethods(), i = 0, line, prevLine, offset; for(; i < lines; i++) { offset = i * lineLength; line = bytes.subarray(offset, offset + lineLength); if(filter_method) { result.set(filter_method(line, colorsPerPixel, prevLine), offset + i); }else{ var j = 0, len = filter_methods.length, results = []; for(; j < len; j++) results[j] = filter_methods[j](line, colorsPerPixel, prevLine); var ind = getIndexOfSmallestSum(results.concat()); result.set(results[ind], offset + i); } prevLine = line; } return result; } , filterNone = function(line, colorsPerPixel, prevLine) { /*var result = new Uint8Array(line.length + 1); result[0] = 0; result.set(line, 1);*/ var result = Array.apply([], line); result.unshift(0); return result; } , filterSub = function(line, colorsPerPixel, prevLine) { var result = [], i = 0, len = line.length, left; result[0] = 1; for(; i < len; i++) { left = line[i - colorsPerPixel] || 0; result[i + 1] = (line[i] - left + 0x0100) & 0xff; } return result; } , filterUp = function(line, colorsPerPixel, prevLine) { var result = [], i = 0, len = line.length, up; result[0] = 2; for(; i < len; i++) { up = prevLine && prevLine[i] || 0; result[i + 1] = (line[i] - up + 0x0100) & 0xff; } return result; } , filterAverage = function(line, colorsPerPixel, prevLine) { var result = [], i = 0, len = line.length, left, up; result[0] = 3; for(; i < len; i++) { left = line[i - colorsPerPixel] || 0; up = prevLine && prevLine[i] || 0; result[i + 1] = (line[i] + 0x0100 - ((left + up) >>> 1)) & 0xff; } return result; } , filterPaeth = function(line, colorsPerPixel, prevLine) { var result = [], i = 0, len = line.length, left, up, upLeft, paeth; result[0] = 4; for(; i < len; i++) { left = line[i - colorsPerPixel] || 0; up = prevLine && prevLine[i] || 0; upLeft = prevLine && prevLine[i - colorsPerPixel] || 0; paeth = paethPredictor(left, up, upLeft); result[i + 1] = (line[i] - paeth + 0x0100) & 0xff; } return result; } ,paethPredictor = function(left, up, upLeft) { var p = left + up - upLeft, pLeft = Math.abs(p - left), pUp = Math.abs(p - up), pUpLeft = Math.abs(p - upLeft); return (pLeft <= pUp && pLeft <= pUpLeft) ? left : (pUp <= pUpLeft) ? up : upLeft; } , getFilterMethods = function() { return [filterNone, filterSub, filterUp, filterAverage, filterPaeth]; } ,getIndexOfSmallestSum = function(arrays) { var i = 0, len = arrays.length, sum, min, ind; while(i < len) { sum = absSum(arrays[i].slice(1)); if(sum < min || !min) { min = sum; ind = i; } i++; } return ind; } , absSum = function(array) { var i = 0, len = array.length, sum = 0; while(i < len) sum += Math.abs(array[i++]); return sum; } , logImg = function(img) { console.log("width: " + img.width); console.log("height: " + img.height); console.log("bits: " + img.bits); console.log("colorType: " + img.colorType); console.log("transparency:"); console.log(img.transparency); console.log("text:"); console.log(img.text); console.log("compressionMethod: " + img.compressionMethod); console.log("filterMethod: " + img.filterMethod); console.log("interlaceMethod: " + img.interlaceMethod); console.log("imgData:"); console.log(img.imgData); console.log("palette:"); console.log(img.palette); console.log("colors: " + img.colors); console.log("colorSpace: " + img.colorSpace); console.log("pixelBitlength: " + img.pixelBitlength); console.log("hasAlphaChannel: " + img.hasAlphaChannel); }; jsPDFAPI.processPNG = function(imageData, imageIndex, alias, compression, dataAsBinaryString) { 'use strict' var colorSpace = this.color_spaces.DEVICE_RGB, decode = this.decode.FLATE_DECODE, bpc = 8, img, dp, trns, colors, pal, smask; /* if(this.isString(imageData)) { }*/ if(this.isArrayBuffer(imageData)) imageData = new Uint8Array(imageData); if(this.isArrayBufferView(imageData)) { if(doesNotHavePngJS()) throw new Error("PNG support requires png.js and zlib.js"); img = new PNG(imageData); imageData = img.imgData; bpc = img.bits; colorSpace = img.colorSpace; colors = img.colors; //logImg(img); /* * colorType 6 - Each pixel is an R,G,B triple, followed by an alpha sample. * * colorType 4 - Each pixel is a grayscale sample, followed by an alpha sample. * * Extract alpha to create two separate images, using the alpha as a sMask */ if([4,6].indexOf(img.colorType) !== -1) { /* * processes 8 bit RGBA and grayscale + alpha images */ if(img.bits === 8) { var pixels = img.pixelBitlength == 32 ? new Uint32Array(img.decodePixels().buffer) : img.pixelBitlength == 16 ? new Uint16Array(img.decodePixels().buffer) : new Uint8Array(img.decodePixels().buffer), len = pixels.length, imgData = new Uint8Array(len * img.colors), alphaData = new Uint8Array(len), pDiff = img.pixelBitlength - img.bits, i = 0, n = 0, pixel, pbl; for(; i < len; i++) { pixel = pixels[i]; pbl = 0; while(pbl < pDiff) { imgData[n++] = ( pixel >>> pbl ) & 0xff; pbl = pbl + img.bits; } alphaData[i] = ( pixel >>> pbl ) & 0xff; } } /* * processes 16 bit RGBA and grayscale + alpha images */ if(img.bits === 16) { var pixels = new Uint32Array(img.decodePixels().buffer), len = pixels.length, imgData = new Uint8Array((len * (32 / img.pixelBitlength) ) * img.colors), alphaData = new Uint8Array(len * (32 / img.pixelBitlength) ), hasColors = img.colors > 1, i = 0, n = 0, a = 0, pixel; while(i < len) { pixel = pixels[i++]; imgData[n++] = (pixel >>> 0) & 0xFF; if(hasColors) { imgData[n++] = (pixel >>> 16) & 0xFF; pixel = pixels[i++]; imgData[n++] = (pixel >>> 0) & 0xFF; } alphaData[a++] = (pixel >>> 16) & 0xFF; } bpc = 8; } if(canCompress(compression)) { imageData = compressBytes(imgData, img.width * img.colors, img.colors, compression); smask = compressBytes(alphaData, img.width, 1, compression); }else{ imageData = imgData; smask = alphaData; decode = null; } } /* * Indexed png. Each pixel is a palette index. */ if(img.colorType === 3) { colorSpace = this.color_spaces.INDEXED; pal = img.palette; if(img.transparency.indexed) { var trans = img.transparency.indexed; var total = 0, i = 0, len = trans.length; for(; i