123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /** @preserve
- jsPDF addImage plugin (JPEG only at this time)
- Copyright (c) 2012 https://github.com/siefkenj/
- */
- /**
- * 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'
- var namespace = 'addImage_'
- // 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;
- // Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00
- if (!imgData.charCodeAt(0) === 0xff ||
- !imgData.charCodeAt(1) === 0xd8 ||
- !imgData.charCodeAt(2) === 0xff ||
- !imgData.charCodeAt(3) === 0xe0 ||
- !imgData.charCodeAt(6) === 'J'.charCodeAt(0) ||
- !imgData.charCodeAt(7) === 'F'.charCodeAt(0) ||
- !imgData.charCodeAt(8) === 'I'.charCodeAt(0) ||
- !imgData.charCodeAt(9) === 'F'.charCodeAt(0) ||
- !imgData.charCodeAt(10) === 0x00) {
- throw new Error('getJpegSize requires a binary jpeg file')
- }
- 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);
- return [width, height];
- } else {
- i += 2;
- blockLength = imgData.charCodeAt(i)*256 + imgData.charCodeAt(i+1)
- }
- }
- }
- // Image functionality ported from pdf.js
- , putImage = function(img) {
- var objectNumber = this.internal.newObject()
- , out = this.internal.write
- , putStream = this.internal.putStream
- img['n'] = objectNumber
- out('<</Type /XObject')
- out('/Subtype /Image')
- out('/Width ' + img['w'])
- out('/Height ' + img['h'])
- if (img['cs'] === 'Indexed') {
- out('/ColorSpace [/Indexed /DeviceRGB '
- + (img['pal'].length / 3 - 1) + ' ' + (objectNumber + 1)
- + ' 0 R]');
- } else {
- out('/ColorSpace /' + img['cs']);
- if (img['cs'] === 'DeviceCMYK') {
- out('/Decode [1 0 1 0 1 0 1 0]');
- }
- }
- out('/BitsPerComponent ' + img['bpc']);
- if ('f' in img) {
- out('/Filter /' + img['f']);
- }
- if ('dp' in img) {
- out('/DecodeParms <<' + img['dp'] + '>>');
- }
- if ('trns' in img && img['trns'].constructor == Array) {
- var trns = '';
- for ( var i = 0; i < img['trns'].length; i++) {
- trns += (img[trns][i] + ' ' + img['trns'][i] + ' ');
- out('/Mask [' + trns + ']');
- }
- }
- if ('smask' in img) {
- out('/SMask ' + (objectNumber + 1) + ' 0 R');
- }
- out('/Length ' + img['data'].length + '>>');
- putStream(img['data']);
- out('endobj');
- }
- , putResourcesCallback = function() {
- var images = this.internal.collections[namespace + 'images']
- for ( var i in images ) {
- putImage.call(this, images[i])
- }
- }
- , putXObjectsDictCallback = function(){
- var images = this.internal.collections[namespace + 'images']
- , out = this.internal.write
- , image
- for (var i in images) {
- image = images[i]
- out(
- '/I' + image['i']
- , image['n']
- , '0'
- , 'R'
- )
- }
- }
- jsPDFAPI.addImage = function(imageData, format, x, y, w, h) {
- 'use strict'
- if (typeof imageData === 'object' && imageData.nodeType === 1) {
- var canvas = document.createElement('canvas');
- canvas.width = imageData.clientWidth;
- canvas.height = imageData.clientHeight;
- var ctx = canvas.getContext('2d');
- if (!ctx) {
- throw ('addImage requires canvas to be supported by browser.');
- }
- ctx.drawImage(imageData, 0, 0, canvas.width, canvas.height);
- imageData = canvas.toDataURL('image/jpeg');
- format = "JPEG";
- }
- if (format.toUpperCase() !== 'JPEG') {
- throw new Error('addImage currently only supports format \'JPEG\', not \''+format+'\'');
- }
- var imageIndex
- , images = this.internal.collections[namespace + 'images']
- , coord = this.internal.getCoordinateString
- , vcoord = this.internal.getVerticalCoordinateString;
- // Detect if the imageData is raw binary or Data URL
- if (imageData.substring(0, 23) === 'data:image/jpeg;base64,') {
- imageData = atob(imageData.replace('data:image/jpeg;base64,', ''));
- }
- if (images){
- // this is NOT the first time this method is ran on this instance of jsPDF object.
- imageIndex = Object.keys ?
- Object.keys(images).length :
- (function(o){
- var i = 0
- for (var e in o){if(o.hasOwnProperty(e)){ i++ }}
- return i
- })(images)
- } else {
- // this is the first time this method is ran on this instance of jsPDF object.
- imageIndex = 0
- this.internal.collections[namespace + 'images'] = images = {}
- this.internal.events.subscribe('putResources', putResourcesCallback)
- this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback)
- }
- var dims = getJpegSize(imageData);
- var info = {
- w : dims[0],
- h : dims[1],
- cs : 'DeviceRGB',
- bpc : 8,
- f : 'DCTDecode',
- i : imageIndex,
- data : imageData
- // n: objectNumber will be added by putImage code
- };
- images[imageIndex] = info
- if (!w && !h) {
- w = -96;
- h = -96;
- }
- if (w < 0) {
- w = (-1) * info['w'] * 72 / w / this.internal.scaleFactor;
- }
- if (h < 0) {
- h = (-1) * info['h'] * 72 / h / this.internal.scaleFactor;
- }
- if (w === 0) {
- w = h * info['w'] / info['h'];
- }
- if (h === 0) {
- h = w * info['h'] / info['w'];
- }
- this.internal.write(
- 'q'
- , coord(w)
- , '0 0'
- , coord(h) // TODO: check if this should be shifted by vcoord
- , coord(x)
- , vcoord(y + h)
- , 'cm /I'+info['i']
- , 'Do Q'
- )
- return this
- }
- })(jsPDF.API)
|