modules/addimage.js

  1. /* global jsPDF */
  2. /** @license
  3. * jsPDF addImage plugin
  4. * Copyright (c) 2012 Jason Siefken, https://github.com/siefkenj/
  5. * 2013 Chris Dowling, https://github.com/gingerchris
  6. * 2013 Trinh Ho, https://github.com/ineedfat
  7. * 2013 Edwin Alejandro Perez, https://github.com/eaparango
  8. * 2013 Norah Smith, https://github.com/burnburnrocket
  9. * 2014 Diego Casorran, https://github.com/diegocr
  10. * 2014 James Robb, https://github.com/jamesbrobb
  11. *
  12. * Permission is hereby granted, free of charge, to any person obtaining
  13. * a copy of this software and associated documentation files (the
  14. * "Software"), to deal in the Software without restriction, including
  15. * without limitation the rights to use, copy, modify, merge, publish,
  16. * distribute, sublicense, and/or sell copies of the Software, and to
  17. * permit persons to whom the Software is furnished to do so, subject to
  18. * the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be
  21. * included in all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. */
  31. /**
  32. * @name addImage
  33. * @module
  34. */
  35. (function (jsPDFAPI) {
  36. 'use strict'
  37. var namespace = 'addImage_';
  38. jsPDFAPI.__addimage__ = {};
  39. var UNKNOWN = 'UNKNOWN';
  40. var imageFileTypeHeaders = {
  41. PNG: [[0x89, 0x50, 0x4e, 0x47]],
  42. TIFF: [
  43. [0x4D, 0x4D, 0x00, 0x2A], //Motorola
  44. [0x49, 0x49, 0x2A, 0x00] //Intel
  45. ],
  46. JPEG: [
  47. [0xFF, 0xD8, 0xFF, 0xE0, undefined, undefined, 0x4A, 0x46, 0x49, 0x46, 0x00], //JFIF
  48. [0xFF, 0xD8, 0xFF, 0xE1, undefined, undefined, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00], //Exif
  49. [0xFF, 0xD8, 0xFF, 0xDB], //JPEG RAW
  50. [0xFF, 0xD8, 0xFF, 0xEE] //EXIF RAW
  51. ],
  52. JPEG2000: [[0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20]],
  53. GIF87a: [[0x47, 0x49, 0x46, 0x38, 0x37, 0x61]],
  54. GIF89a: [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61]],
  55. WEBP: [[0x52, 0x49, 0x46, 0x46, undefined, undefined, undefined, undefined, 0x57, 0x45, 0x42, 0x50]],
  56. BMP: [
  57. [0x42, 0x4D], //BM - Windows 3.1x, 95, NT, ... etc.
  58. [0x42, 0x41], //BA - OS/2 struct bitmap array
  59. [0x43, 0x49], //CI - OS/2 struct color icon
  60. [0x43, 0x50], //CP - OS/2 const color pointer
  61. [0x49, 0x43], //IC - OS/2 struct icon
  62. [0x50, 0x54] //PT - OS/2 pointer
  63. ]
  64. };
  65. /**
  66. * Recognize filetype of Image by magic-bytes
  67. *
  68. * https://en.wikipedia.org/wiki/List_of_file_signatures
  69. *
  70. * @name getImageFileTypeByImageData
  71. * @public
  72. * @function
  73. * @param {string|arraybuffer} imageData imageData as binary String or arraybuffer
  74. * @param {string} format format of file if filetype-recognition fails, e.g. 'JPEG'
  75. *
  76. * @returns {string} filetype of Image
  77. */
  78. var getImageFileTypeByImageData = jsPDFAPI.__addimage__.getImageFileTypeByImageData = function (imageData, fallbackFormat) {
  79. fallbackFormat = fallbackFormat || UNKNOWN;
  80. var i;
  81. var j;
  82. var result = UNKNOWN;
  83. var headerSchemata;
  84. var compareResult;
  85. var fileType;
  86. if (isArrayBufferView(imageData)) {
  87. for (fileType in imageFileTypeHeaders) {
  88. headerSchemata = imageFileTypeHeaders[fileType];
  89. for (i = 0; i < headerSchemata.length; i += 1) {
  90. compareResult = true;
  91. for (j = 0; j < headerSchemata[i].length; j += 1) {
  92. if (headerSchemata[i][j] === undefined) {
  93. continue;
  94. }
  95. if (headerSchemata[i][j] !== imageData[j]) {
  96. compareResult = false;
  97. break;
  98. }
  99. }
  100. if (compareResult === true) {
  101. result = fileType;
  102. break;
  103. }
  104. }
  105. }
  106. } else {
  107. for (fileType in imageFileTypeHeaders) {
  108. headerSchemata = imageFileTypeHeaders[fileType];
  109. for (i = 0; i < headerSchemata.length; i += 1) {
  110. compareResult = true;
  111. for (j = 0; j < headerSchemata[i].length; j += 1) {
  112. if (headerSchemata[i][j] === undefined) {
  113. continue;
  114. }
  115. if (headerSchemata[i][j] !== imageData.charCodeAt(j)) {
  116. compareResult = false;
  117. break;
  118. }
  119. }
  120. if (compareResult === true) {
  121. result = fileType;
  122. break;
  123. }
  124. }
  125. }
  126. }
  127. if (result === UNKNOWN && fallbackFormat !== UNKNOWN) {
  128. result = fallbackFormat;
  129. }
  130. return result;
  131. };
  132. // Image functionality ported from pdf.js
  133. var putImage = function (image) {
  134. var out = this.internal.write;
  135. var putStream = this.internal.putStream;
  136. var getFilters = this.internal.getFilters;
  137. var filter = getFilters();
  138. while (filter.indexOf('FlateEncode') !== -1) {
  139. filter.splice(filter.indexOf('FlateEncode'), 1);
  140. }
  141. image.objectId = this.internal.newObject()
  142. var additionalKeyValues = [];
  143. additionalKeyValues.push({ key: 'Type', value: '/XObject' });
  144. additionalKeyValues.push({ key: 'Subtype', value: '/Image' });
  145. additionalKeyValues.push({ key: 'Width', value: image.width });
  146. additionalKeyValues.push({ key: 'Height', value: image.height });
  147. if (image.colorSpace === color_spaces.INDEXED) {
  148. additionalKeyValues.push({
  149. key: 'ColorSpace', value: '[/Indexed /DeviceRGB '
  150. // if an indexed png defines more than one colour with transparency, we've created a sMask
  151. + (image.palette.length / 3 - 1) + ' ' + ('sMask' in image && typeof image.sMask !== "undefined" ? image.objectId + 2 : image.objectId + 1)
  152. + ' 0 R]'
  153. });
  154. } else {
  155. additionalKeyValues.push({ key: 'ColorSpace', value: '/' + image.colorSpace });
  156. if (image.colorSpace === color_spaces.DEVICE_CMYK) {
  157. additionalKeyValues.push({ key: 'Decode', value: '[1 0 1 0 1 0 1 0]' });
  158. }
  159. }
  160. additionalKeyValues.push({ key: 'BitsPerComponent', value: image.bitsPerComponent });
  161. if ('decodeParameters' in image && typeof image.decodeParameters !== "undefined") {
  162. additionalKeyValues.push({ key: 'DecodeParms', value: '<<' + image.decodeParameters + '>>' });
  163. }
  164. if ('transparency' in image && Array.isArray(image.transparency)) {
  165. var transparency = '',
  166. i = 0,
  167. len = image.transparency.length;
  168. for (; i < len; i++)
  169. transparency += (image.transparency[i] + ' ' + image.transparency[i] + ' ');
  170. additionalKeyValues.push({ key: 'Mask', value: '[' + transparency + ']' });
  171. }
  172. if (typeof image.sMask !== "undefined") {
  173. additionalKeyValues.push({ key: 'SMask', value: (image.objectId + 1) + ' 0 R' });
  174. }
  175. var alreadyAppliedFilters = (typeof image.filter !== "undefined") ? ['/' + image.filter] : undefined;
  176. putStream({ data: image.data, additionalKeyValues: additionalKeyValues, alreadyAppliedFilters: alreadyAppliedFilters });
  177. out('endobj');
  178. // Soft mask
  179. if ('sMask' in image && typeof image.sMask !== "undefined") {
  180. var decodeParameters = '/Predictor ' + image.predictor + ' /Colors 1 /BitsPerComponent ' + image.bitsPerComponent + ' /Columns ' + image.width;
  181. var sMask = { width: image.width, height: image.height, colorSpace: 'DeviceGray', bitsPerComponent: image.bitsPerComponent, decodeParameters: decodeParameters, data: image.sMask };
  182. if ('filter' in image) {
  183. sMask.filter = image.filter;
  184. }
  185. putImage.call(this, sMask);
  186. }
  187. //Palette
  188. if (image.colorSpace === color_spaces.INDEXED) {
  189. this.internal.newObject();
  190. //out('<< /Filter / ' + img['f'] +' /Length ' + img['pal'].length + '>>');
  191. //putStream(zlib.compress(img['pal']));
  192. putStream({ data: arrayBufferToBinaryString(new Uint8Array(image.palette)) });
  193. out('endobj');
  194. }
  195. };
  196. var putResourcesCallback = function () {
  197. var images = this.internal.collections[namespace + 'images'];
  198. for (var i in images) {
  199. putImage.call(this, images[i]);
  200. }
  201. };
  202. var putXObjectsDictCallback = function () {
  203. var images = this.internal.collections[namespace + 'images']
  204. , out = this.internal.write
  205. , image;
  206. for (var i in images) {
  207. image = images[i];
  208. out(
  209. '/I' + image.index
  210. , image.objectId
  211. , '0'
  212. , 'R'
  213. );
  214. }
  215. };
  216. var checkCompressValue = function (value) {
  217. if (value && typeof value === 'string')
  218. value = value.toUpperCase();
  219. return value in jsPDFAPI.image_compression ? value : image_compression.NONE;
  220. };
  221. var initialize = function () {
  222. if (!this.internal.collections[namespace + 'images']) {
  223. this.internal.collections[namespace + 'images'] = {};
  224. this.internal.events.subscribe('putResources', putResourcesCallback);
  225. this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback);
  226. }
  227. };
  228. var getImages = function () {
  229. var images = this.internal.collections[namespace + 'images'];
  230. initialize.call(this);
  231. return images;
  232. };
  233. var getImageIndex = function () {
  234. return Object.keys(this.internal.collections[namespace + 'images']).length;
  235. };
  236. var notDefined = function (value) {
  237. return typeof value === 'undefined' || value === null || value.length === 0;
  238. };
  239. var generateAliasFromImageData = function (imageData) {
  240. if (typeof imageData === 'string' || isArrayBufferView(imageData)) {
  241. return sHashCode(imageData);
  242. }
  243. return null;
  244. };
  245. var isImageTypeSupported = function (type) {
  246. return (typeof jsPDFAPI["process" + type.toUpperCase()] === "function");
  247. };
  248. var isDOMElement = function (object) {
  249. return typeof object === 'object' && object.nodeType === 1;
  250. };
  251. var getImageDataFromElement = function (element) {
  252. //if element is an image which uses data url definition, just return the dataurl
  253. if (element.nodeName === 'IMG' && element.hasAttribute('src')) {
  254. var src = '' + element.getAttribute('src');
  255. //is base64 encoded dataUrl, directly process it
  256. if (src.indexOf('data:image/') === 0) {
  257. return atob(unescape(src).split('base64,').pop());
  258. }
  259. //it is probably an url, try to load it
  260. var tmpImageData = jsPDFAPI.loadFile(src, true);
  261. if (tmpImageData !== undefined) {
  262. return tmpImageData
  263. }
  264. }
  265. if (element.nodeName === 'CANVAS') {
  266. return atob(element.toDataURL('image/jpeg', 1.0).split('base64,').pop());
  267. }
  268. };
  269. var checkImagesForAlias = function (alias) {
  270. var images = this.internal.collections[namespace + 'images'];
  271. if (images) {
  272. for (var e in images) {
  273. if (alias === images[e].alias) {
  274. return images[e];
  275. }
  276. }
  277. }
  278. };
  279. var determineWidthAndHeight = function (width, height, image) {
  280. if (!width && !height) {
  281. width = -96;
  282. height = -96;
  283. }
  284. if (width < 0) {
  285. width = (-1) * image.width * 72 / width / this.internal.scaleFactor;
  286. }
  287. if (height < 0) {
  288. height = (-1) * image.height * 72 / height / this.internal.scaleFactor;
  289. }
  290. if (width === 0) {
  291. width = height * image.width / image.height;
  292. }
  293. if (height === 0) {
  294. height = width * image.height / image.width;
  295. }
  296. return [width, height];
  297. };
  298. var writeImageToPDF = function (x, y, width, height, image, rotation) {
  299. var dims = determineWidthAndHeight.call(this, width, height, image),
  300. coord = this.internal.getCoordinateString,
  301. vcoord = this.internal.getVerticalCoordinateString;
  302. var images = getImages.call(this);
  303. width = dims[0];
  304. height = dims[1];
  305. images[image.index] = image;
  306. if (rotation) {
  307. rotation *= (Math.PI / 180);
  308. var c = Math.cos(rotation);
  309. var s = Math.sin(rotation);
  310. //like in pdf Reference do it 4 digits instead of 2
  311. var f4 = function (number) {
  312. return number.toFixed(4);
  313. }
  314. var rotationTransformationMatrix = [f4(c), f4(s), f4(s * -1), f4(c), 0, 0, 'cm'];
  315. }
  316. this.internal.write('q'); //Save graphics state
  317. if (rotation) {
  318. this.internal.write([1, '0', '0', 1, coord(x), vcoord(y + height), 'cm'].join(' ')); //Translate
  319. this.internal.write(rotationTransformationMatrix.join(' ')); //Rotate
  320. this.internal.write([coord(width), '0', '0', coord(height), '0', '0', 'cm'].join(' ')); //Scale
  321. } else {
  322. this.internal.write([coord(width), '0', '0', coord(height), coord(x), vcoord(y + height), 'cm'].join(' ')); //Translate and Scale
  323. }
  324. this.internal.write('/I' + image.index + ' Do'); //Paint Image
  325. this.internal.write('Q'); //Restore graphics state
  326. };
  327. /**
  328. * COLOR SPACES
  329. */
  330. var color_spaces = jsPDFAPI.color_spaces = {
  331. DEVICE_RGB: 'DeviceRGB',
  332. DEVICE_GRAY: 'DeviceGray',
  333. DEVICE_CMYK: 'DeviceCMYK',
  334. CAL_GREY: 'CalGray',
  335. CAL_RGB: 'CalRGB',
  336. LAB: 'Lab',
  337. ICC_BASED: 'ICCBased',
  338. INDEXED: 'Indexed',
  339. PATTERN: 'Pattern',
  340. SEPARATION: 'Separation',
  341. DEVICE_N: 'DeviceN'
  342. };
  343. /**
  344. * DECODE METHODS
  345. */
  346. jsPDFAPI.decode = {
  347. DCT_DECODE: 'DCTDecode',
  348. FLATE_DECODE: 'FlateDecode',
  349. LZW_DECODE: 'LZWDecode',
  350. JPX_DECODE: 'JPXDecode',
  351. JBIG2_DECODE: 'JBIG2Decode',
  352. ASCII85_DECODE: 'ASCII85Decode',
  353. ASCII_HEX_DECODE: 'ASCIIHexDecode',
  354. RUN_LENGTH_DECODE: 'RunLengthDecode',
  355. CCITT_FAX_DECODE: 'CCITTFaxDecode'
  356. };
  357. /**
  358. * IMAGE COMPRESSION TYPES
  359. */
  360. var image_compression = jsPDFAPI.image_compression = {
  361. NONE: 'NONE',
  362. FAST: 'FAST',
  363. MEDIUM: 'MEDIUM',
  364. SLOW: 'SLOW'
  365. };
  366. /**
  367. * @name sHashCode
  368. * @function
  369. * @param {string} data
  370. * @returns {string}
  371. */
  372. var sHashCode = jsPDFAPI.__addimage__.sHashCode = function (data) {
  373. var hash = 0, i, chr, len;
  374. if (typeof data === "string") {
  375. len = data.length;
  376. for (i = 0; i < len; i++) {
  377. chr = data.charCodeAt(i);
  378. hash = ((hash << 5) - hash) + chr;
  379. hash |= 0; // Convert to 32bit integer
  380. }
  381. } else if (isArrayBufferView(data)) {
  382. len = data.byteLength / 2;
  383. for (i = 0; i < len; i++) {
  384. chr = data[i];
  385. hash = ((hash << 5) - hash) + chr;
  386. hash |= 0; // Convert to 32bit integer
  387. }
  388. }
  389. return hash;
  390. };
  391. /**
  392. * Validates if given String is a valid Base64-String
  393. *
  394. * @name validateStringAsBase64
  395. * @public
  396. * @function
  397. * @param {String} possible Base64-String
  398. *
  399. * @returns {boolean}
  400. */
  401. var validateStringAsBase64 = jsPDFAPI.__addimage__.validateStringAsBase64 = function (possibleBase64String) {
  402. possibleBase64String = possibleBase64String || '';
  403. possibleBase64String.toString().trim();
  404. var result = true;
  405. if (possibleBase64String.length === 0) {
  406. result = false;
  407. }
  408. if (possibleBase64String.length % 4 !== 0) {
  409. result = false;
  410. }
  411. if (/^[A-Za-z0-9+/]+$/.test(possibleBase64String.substr(0, possibleBase64String.length - 2)) === false) {
  412. result = false;
  413. }
  414. if (/^[A-Za-z0-9/][A-Za-z0-9+/]|[A-Za-z0-9+/]=|==$/.test(possibleBase64String.substr(-2)) === false) {
  415. result = false;
  416. }
  417. return result;
  418. };
  419. /**
  420. * Strips out and returns info from a valid base64 data URI
  421. *
  422. * @name extractImageFromDataUrl
  423. * @function
  424. * @param {string} dataUrl a valid data URI of format 'data:[<MIME-type>][;base64],<data>'
  425. * @returns {Array}an Array containing the following
  426. * [0] the complete data URI
  427. * [1] <MIME-type>
  428. * [2] format - the second part of the mime-type i.e 'png' in 'image/png'
  429. * [4] <data>
  430. */
  431. var extractImageFromDataUrl = jsPDFAPI.__addimage__.extractImageFromDataUrl = function (dataUrl) {
  432. dataUrl = dataUrl || '';
  433. var dataUrlParts = dataUrl.split('base64,');
  434. var result = null;
  435. if (dataUrlParts.length === 2) {
  436. var extractedInfo = /^data:(\w*\/\w*);*(charset=[\w=-]*)*;*$/.exec(dataUrlParts[0]);
  437. if (Array.isArray(extractedInfo)) {
  438. result = {
  439. mimeType: extractedInfo[1],
  440. charset: extractedInfo[2],
  441. data: dataUrlParts[1]
  442. };
  443. }
  444. }
  445. return result;
  446. };
  447. /**
  448. * Check to see if ArrayBuffer is supported
  449. *
  450. * @name supportsArrayBuffer
  451. * @function
  452. * @returns {boolean}
  453. */
  454. var supportsArrayBuffer = jsPDFAPI.__addimage__.supportsArrayBuffer = function () {
  455. return typeof ArrayBuffer !== 'undefined' && typeof Uint8Array !== 'undefined';
  456. };
  457. /**
  458. * Tests supplied object to determine if ArrayBuffer
  459. *
  460. * @name isArrayBuffer
  461. * @function
  462. * @param {Object} object an Object
  463. *
  464. * @returns {boolean}
  465. */
  466. jsPDFAPI.__addimage__.isArrayBuffer = function (object) {
  467. return supportsArrayBuffer() && object instanceof ArrayBuffer;
  468. };
  469. /**
  470. * Tests supplied object to determine if it implements the ArrayBufferView (TypedArray) interface
  471. *
  472. * @name isArrayBufferView
  473. * @function
  474. * @param {Object} object an Object
  475. * @returns {boolean}
  476. */
  477. var isArrayBufferView = jsPDFAPI.__addimage__.isArrayBufferView = function (object) {
  478. return (supportsArrayBuffer() && typeof Uint32Array !== 'undefined') &&
  479. (object instanceof Int8Array ||
  480. object instanceof Uint8Array ||
  481. (typeof Uint8ClampedArray !== 'undefined' && object instanceof Uint8ClampedArray) ||
  482. object instanceof Int16Array ||
  483. object instanceof Uint16Array ||
  484. object instanceof Int32Array ||
  485. object instanceof Uint32Array ||
  486. object instanceof Float32Array ||
  487. object instanceof Float64Array);
  488. };
  489. /**
  490. * Convert Binary String to ArrayBuffer
  491. *
  492. * @name binaryStringToUint8Array
  493. * @public
  494. * @function
  495. * @param {string} BinaryString with ImageData
  496. * @returns {Uint8Array}
  497. */
  498. var binaryStringToUint8Array = jsPDFAPI.__addimage__.binaryStringToUint8Array = function (binary_string) {
  499. var len = binary_string.length;
  500. var bytes = new Uint8Array(len);
  501. for (var i = 0; i < len; i++) {
  502. bytes[i] = binary_string.charCodeAt(i);
  503. }
  504. return bytes;
  505. };
  506. /**
  507. * Convert the Buffer to a Binary String
  508. *
  509. * @name arrayBufferToBinaryString
  510. * @public
  511. * @function
  512. * @param {ArrayBuffer} ArrayBuffer with ImageData
  513. *
  514. * @returns {String}
  515. */
  516. var arrayBufferToBinaryString = jsPDFAPI.__addimage__.arrayBufferToBinaryString = function (buffer) {
  517. try {
  518. return atob(btoa(String.fromCharCode.apply(null, buffer)));
  519. } catch (e) {
  520. if (typeof Uint8Array !== 'undefined' && typeof Uint8Array.prototype.reduce !== 'undefined') {
  521. return new Uint8Array(buffer).reduce(function (data, byte) {
  522. return data.push(String.fromCharCode(byte)), data;
  523. }, []).join('');
  524. }
  525. }
  526. };
  527. /**
  528. * Adds an Image to the PDF.
  529. *
  530. * @name addImage
  531. * @public
  532. * @function
  533. * @param {string|HTMLImageElement|HTMLCanvasElement|Uint8Array} imageData imageData as base64 encoded DataUrl or Image-HTMLElement or Canvas-HTMLElement
  534. * @param {string} format format of file if filetype-recognition fails, e.g. 'JPEG'
  535. * @param {number} x x Coordinate (in units declared at inception of PDF document) against left edge of the page
  536. * @param {number} y y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  537. * @param {number} width width of the image (in units declared at inception of PDF document)
  538. * @param {number} height height of the Image (in units declared at inception of PDF document)
  539. * @param {string} alias alias of the image (if used multiple times)
  540. * @param {string} compression compression of the generated JPEG, can have the values 'NONE', 'FAST', 'MEDIUM' and 'SLOW'
  541. * @param {number} rotation rotation of the image in degrees (0-359)
  542. *
  543. * @returns jsPDF
  544. */
  545. jsPDFAPI.addImage = function (imageData, format, x, y, w, h, alias, compression, rotation) {
  546. // backwards compatibility
  547. if (typeof format !== 'string') {
  548. var tmp = h;
  549. h = w;
  550. w = y;
  551. y = x;
  552. x = format;
  553. format = tmp;
  554. }
  555. if (typeof imageData === 'object' && !isDOMElement(imageData) && "imageData" in imageData) {
  556. var options = imageData;
  557. imageData = options.imageData;
  558. format = options.format || format || UNKNOWN;
  559. x = options.x || x || 0;
  560. y = options.y || y || 0;
  561. w = options.w || options.width || w;
  562. h = options.h || options.height || h;
  563. alias = options.alias || alias;
  564. compression = options.compression || compression;
  565. rotation = options.rotation || options.angle || rotation;
  566. }
  567. //If compression is not explicitly set, determine if we should use compression
  568. var filter = this.internal.getFilters();
  569. if (compression === undefined && filter.indexOf('FlateEncode') !== -1) {
  570. compression = 'SLOW';
  571. }
  572. if (isNaN(x) || isNaN(y)) {
  573. throw new Error('Invalid coordinates passed to jsPDF.addImage');
  574. }
  575. initialize.call(this);
  576. var image = processImageData.call(this, imageData, format, alias, compression);
  577. writeImageToPDF.call(this, x, y, w, h, image, rotation);
  578. return this;
  579. };
  580. var processImageData = function (imageData, format, alias, compression) {
  581. var result, dataAsBinaryString;
  582. if (typeof imageData === "string" && getImageFileTypeByImageData(imageData) === UNKNOWN) {
  583. imageData = unescape(imageData);
  584. }
  585. if (typeof imageData === 'string') {
  586. var tmpImageData = convertBase64ToBinaryString(imageData, false);
  587. if (tmpImageData !== '') {
  588. imageData = tmpImageData;
  589. } else {
  590. tmpImageData = jsPDFAPI.loadFile(imageData, true);
  591. if (tmpImageData !== undefined) {
  592. imageData = tmpImageData;
  593. }
  594. }
  595. }
  596. if (isDOMElement(imageData)) {
  597. imageData = getImageDataFromElement(imageData);
  598. }
  599. format = getImageFileTypeByImageData(imageData, format);
  600. if (!isImageTypeSupported(format)) {
  601. throw new Error('addImage does not support files of type \'' + format + '\', please ensure that a plugin for \'' + format + '\' support is added.');
  602. }
  603. // now do the heavy lifting
  604. if (notDefined(alias)) {
  605. alias = generateAliasFromImageData(imageData);
  606. }
  607. result = checkImagesForAlias.call(this, alias);
  608. if (!result) {
  609. if (supportsArrayBuffer()) {
  610. // no need to convert if imageData is already uint8array
  611. if (!(imageData instanceof Uint8Array)) {
  612. dataAsBinaryString = imageData;
  613. imageData = binaryStringToUint8Array(imageData);
  614. }
  615. }
  616. result = this['process' + format.toUpperCase()](
  617. imageData,
  618. getImageIndex.call(this),
  619. alias,
  620. checkCompressValue(compression),
  621. dataAsBinaryString
  622. );
  623. }
  624. if (!result) {
  625. throw new Error('An unknown error occurred whilst processing the image.');
  626. }
  627. return result;
  628. };
  629. /**
  630. * @name convertBase64ToBinaryString
  631. * @function
  632. * @param {string} stringData
  633. * @returns {string} binary string
  634. */
  635. var convertBase64ToBinaryString = jsPDFAPI.__addimage__.convertBase64ToBinaryString = function (stringData, throwError) {
  636. throwError = typeof throwError === "boolean" ? throwError : true;
  637. var base64Info;
  638. var imageData = '';
  639. var rawData;
  640. if (typeof stringData === 'string') {
  641. base64Info = extractImageFromDataUrl(stringData);
  642. rawData = (base64Info !== null) ? base64Info.data : stringData;
  643. try {
  644. imageData = atob(rawData);
  645. } catch (e) {
  646. if (throwError) {
  647. if (!validateStringAsBase64(rawData)) {
  648. throw new Error('Supplied Data is not a valid base64-String jsPDF.convertBase64ToBinaryString ');
  649. } else {
  650. throw new Error('atob-Error in jsPDF.convertBase64ToBinaryString ' + e.message);
  651. }
  652. }
  653. }
  654. }
  655. return imageData;
  656. };
  657. /**
  658. * @name getImageProperties
  659. * @function
  660. * @param {Object} imageData
  661. * @returns {Object}
  662. */
  663. jsPDFAPI.getImageProperties = function (imageData) {
  664. var image;
  665. var tmpImageData = '';
  666. var format;
  667. if (isDOMElement(imageData)) {
  668. imageData = getImageDataFromElement(imageData);
  669. }
  670. if (typeof imageData === "string" && getImageFileTypeByImageData(imageData) === UNKNOWN) {
  671. tmpImageData = convertBase64ToBinaryString(imageData, false);
  672. if (tmpImageData === '') {
  673. tmpImageData = jsPDFAPI.loadFile(imageData) || '';
  674. }
  675. imageData = tmpImageData;
  676. }
  677. format = getImageFileTypeByImageData(imageData);
  678. if (!isImageTypeSupported(format)) {
  679. throw new Error('addImage does not support files of type \'' + format + '\', please ensure that a plugin for \'' + format + '\' support is added.');
  680. }
  681. if (supportsArrayBuffer() && !(imageData instanceof Uint8Array)) {
  682. imageData = binaryStringToUint8Array(imageData);
  683. }
  684. image = this['process' + format.toUpperCase()](imageData);
  685. if (!image) {
  686. throw new Error('An unknown error occurred whilst processing the image');
  687. }
  688. image.fileType = format;
  689. return image;
  690. };
  691. })(jsPDF.API);