modules/jpeg_support.js

/* global jsPDF */
/**
 * @license
 *
 * Licensed under the MIT License.
 * http://opensource.org/licenses/mit-license
 */

/**
* jsPDF jpeg Support PlugIn
*
* @name jpeg_support
* @module
*/
(function (jsPDFAPI) {
    'use strict';

    //takes a string imgData containing the raw bytes of
    //a jpeg image and returns [width, height]
    //Algorithm from: http://www.64lines.com/jpeg-width-height
    var getJpegSize = function (imgData) {
        'use strict'
        var width, height, numcomponents;
        var blockLength = imgData.charCodeAt(4) * 256 + imgData.charCodeAt(5);
        var i = 4, len = imgData.length;
        while (i < len) {
            i += blockLength;
            if (imgData.charCodeAt(i) !== 0xff) {
                throw new Error('getJpegSize could not find the size of the image');
            }
            if (imgData.charCodeAt(i + 1) === 0xc0 || //(SOF) Huffman  - Baseline DCT
                imgData.charCodeAt(i + 1) === 0xc1 || //(SOF) Huffman  - Extended sequential DCT
                imgData.charCodeAt(i + 1) === 0xc2 || // Progressive DCT (SOF2)
                imgData.charCodeAt(i + 1) === 0xc3 || // Spatial (sequential) lossless (SOF3)
                imgData.charCodeAt(i + 1) === 0xc4 || // Differential sequential DCT (SOF5)
                imgData.charCodeAt(i + 1) === 0xc5 || // Differential progressive DCT (SOF6)
                imgData.charCodeAt(i + 1) === 0xc6 || // Differential spatial (SOF7)
                imgData.charCodeAt(i + 1) === 0xc7) {
                height = imgData.charCodeAt(i + 5) * 256 + imgData.charCodeAt(i + 6);
                width = imgData.charCodeAt(i + 7) * 256 + imgData.charCodeAt(i + 8);
                numcomponents = imgData.charCodeAt(i + 9);
                return [width, height, numcomponents];
            } else {
                i += 2;
                blockLength = imgData.charCodeAt(i) * 256 + imgData.charCodeAt(i + 1)
            }
        }
    };

    var getJpegSizeFromBytes = function (data) {

        var hdr = (data[0] << 8) | data[1];

        if (hdr !== 0xFFD8)
            throw new Error('Supplied data is not a JPEG');

        var len = data.length,
            block = (data[4] << 8) + data[5],
            pos = 4,
            bytes, width, height, numcomponents;

        while (pos < len) {
            pos += block;
            bytes = readBytes(data, pos);
            block = (bytes[2] << 8) + bytes[3];
            if ((bytes[1] === 0xC0 || bytes[1] === 0xC2) && bytes[0] === 0xFF && block > 7) {
                bytes = readBytes(data, pos + 5);
                width = (bytes[2] << 8) + bytes[3];
                height = (bytes[0] << 8) + bytes[1];
                numcomponents = bytes[4];
                return { width: width, height: height, numcomponents: numcomponents };
            }

            pos += 2;
        }

        throw new Error('getJpegSizeFromBytes could not find the size of the image');
    };

    var readBytes = function (data, offset) {
        return data.subarray(offset, offset + 5);
    };

    /**
    * @ignore
    */
    jsPDFAPI.processJPEG = function (data, index, alias, compression, dataAsBinaryString, colorSpace) {
        'use strict'
        var filter = this.decode.DCT_DECODE,
            bpc = 8,
            dims;

        if (!(typeof data === 'string') && !this.__addimage__.isArrayBuffer(data) && !this.__addimage__.isArrayBufferView(data)) {
            return null;
        }

        if (typeof data === 'string') {
            dims = getJpegSize(data);
        }

        if (this.__addimage__.isArrayBuffer(data)) {
            data = new Uint8Array(data);
        }
        if (this.__addimage__.isArrayBufferView(data)) {

            dims = getJpegSizeFromBytes(data);

            // if we already have a stored binary string rep use that
            data = dataAsBinaryString || this.__addimage__.arrayBufferToBinaryString(data);

        }

        if (colorSpace === undefined) {
            switch (dims.numcomponents) {
                case 1:
                    colorSpace = this.color_spaces.DEVICE_GRAY;
                    break;
                case 4:
                    colorSpace = this.color_spaces.DEVICE_CMYK;
                    break;
                default:
                case 3:
                    colorSpace = this.color_spaces.DEVICE_RGB;
                    break;
            }
        }

        return {data: data, width: dims.width, height: dims.height, colorSpace: colorSpace, bitsPerComponent: bpc, filter: filter, index: index, alias: alias};
    };
})(jsPDF.API);