plugins_addimage.js.html 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>plugins/addimage.js - Documentation</title>
  6. <script src="scripts/prettify/prettify.js"></script>
  7. <script src="scripts/prettify/lang-css.js"></script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
  13. </head>
  14. <body>
  15. <input type="checkbox" id="nav-trigger" class="nav-trigger" />
  16. <label for="nav-trigger" class="navicon-button x">
  17. <div class="navicon"></div>
  18. </label>
  19. <label for="nav-trigger" class="overlay"></label>
  20. <nav>
  21. <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="jsPDF.html">jsPDF</a></li></ul><h3>Global</h3><ul><li><a href="global.html#addFont">addFont</a></li><li><a href="global.html#addHTML">addHTML</a></li><li><a href="global.html#addMetadata">addMetadata</a></li><li><a href="global.html#addPage">addPage</a></li><li><a href="global.html#autoPrint">autoPrint</a></li><li><a href="global.html#CapJoinStyles">CapJoinStyles</a></li><li><a href="global.html#circle">circle</a></li><li><a href="global.html#ellipse">ellipse</a></li><li><a href="global.html#getFontList">getFontList</a></li><li><a href="global.html#lines">lines</a></li><li><a href="global.html#lstext">lstext</a></li><li><a href="global.html#output">output</a></li><li><a href="global.html#rect">rect</a></li><li><a href="global.html#roundedRect">roundedRect</a></li><li><a href="global.html#save">save</a></li><li><a href="global.html#setDisplayMode">setDisplayMode</a></li><li><a href="global.html#setDrawColor">setDrawColor</a></li><li><a href="global.html#setFillColor">setFillColor</a></li><li><a href="global.html#setFont">setFont</a></li><li><a href="global.html#setFontSize">setFontSize</a></li><li><a href="global.html#setFontStyle">setFontStyle</a></li><li><a href="global.html#setLineCap">setLineCap</a></li><li><a href="global.html#setLineJoin">setLineJoin</a></li><li><a href="global.html#setLineWidth">setLineWidth</a></li><li><a href="global.html#setPage">setPage</a></li><li><a href="global.html#setProperties">setProperties</a></li><li><a href="global.html#setTextColor">setTextColor</a></li><li><a href="global.html#text">text</a></li><li><a href="global.html#triangle">triangle</a></li></ul>
  22. </nav>
  23. <div id="main">
  24. <h1 class="page-title">plugins/addimage.js</h1>
  25. <section>
  26. <article>
  27. <pre class="prettyprint source linenums"><code>/** @preserve
  28. * jsPDF addImage plugin
  29. * Copyright (c) 2012 Jason Siefken, https://github.com/siefkenj/
  30. * 2013 Chris Dowling, https://github.com/gingerchris
  31. * 2013 Trinh Ho, https://github.com/ineedfat
  32. * 2013 Edwin Alejandro Perez, https://github.com/eaparango
  33. * 2013 Norah Smith, https://github.com/burnburnrocket
  34. * 2014 Diego Casorran, https://github.com/diegocr
  35. * 2014 James Robb, https://github.com/jamesbrobb
  36. *
  37. * Permission is hereby granted, free of charge, to any person obtaining
  38. * a copy of this software and associated documentation files (the
  39. * "Software"), to deal in the Software without restriction, including
  40. * without limitation the rights to use, copy, modify, merge, publish,
  41. * distribute, sublicense, and/or sell copies of the Software, and to
  42. * permit persons to whom the Software is furnished to do so, subject to
  43. * the following conditions:
  44. *
  45. * The above copyright notice and this permission notice shall be
  46. * included in all copies or substantial portions of the Software.
  47. *
  48. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  49. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  50. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  51. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  52. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  53. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  54. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  55. */
  56. ;(function(jsPDFAPI) {
  57. 'use strict'
  58. var namespace = 'addImage_',
  59. supported_image_types = ['jpeg', 'jpg', 'png'];
  60. // Image functionality ported from pdf.js
  61. var putImage = function(img) {
  62. var objectNumber = this.internal.newObject()
  63. , out = this.internal.write
  64. , putStream = this.internal.putStream
  65. img['n'] = objectNumber
  66. out('&lt;&lt;/Type /XObject')
  67. out('/Subtype /Image')
  68. out('/Width ' + img['w'])
  69. out('/Height ' + img['h'])
  70. if (img['cs'] === this.color_spaces.INDEXED) {
  71. out('/ColorSpace [/Indexed /DeviceRGB '
  72. // if an indexed png defines more than one colour with transparency, we've created a smask
  73. + (img['pal'].length / 3 - 1) + ' ' + ('smask' in img ? objectNumber + 2 : objectNumber + 1)
  74. + ' 0 R]');
  75. } else {
  76. out('/ColorSpace /' + img['cs']);
  77. if (img['cs'] === this.color_spaces.DEVICE_CMYK) {
  78. out('/Decode [1 0 1 0 1 0 1 0]');
  79. }
  80. }
  81. out('/BitsPerComponent ' + img['bpc']);
  82. if ('f' in img) {
  83. out('/Filter /' + img['f']);
  84. }
  85. if ('dp' in img) {
  86. out('/DecodeParms &lt;&lt;' + img['dp'] + '>>');
  87. }
  88. if ('trns' in img &amp;&amp; img['trns'].constructor == Array) {
  89. var trns = '',
  90. i = 0,
  91. len = img['trns'].length;
  92. for (; i &lt; len; i++)
  93. trns += (img['trns'][i] + ' ' + img['trns'][i] + ' ');
  94. out('/Mask [' + trns + ']');
  95. }
  96. if ('smask' in img) {
  97. out('/SMask ' + (objectNumber + 1) + ' 0 R');
  98. }
  99. out('/Length ' + img['data'].length + '>>');
  100. putStream(img['data']);
  101. out('endobj');
  102. // Soft mask
  103. if ('smask' in img) {
  104. var dp = '/Predictor '+ img['p'] +' /Colors 1 /BitsPerComponent ' + img['bpc'] + ' /Columns ' + img['w'];
  105. var smask = {'w': img['w'], 'h': img['h'], 'cs': 'DeviceGray', 'bpc': img['bpc'], 'dp': dp, 'data': img['smask']};
  106. if ('f' in img)
  107. smask.f = img['f'];
  108. putImage.call(this, smask);
  109. }
  110. //Palette
  111. if (img['cs'] === this.color_spaces.INDEXED) {
  112. this.internal.newObject();
  113. //out('&lt;&lt; /Filter / ' + img['f'] +' /Length ' + img['pal'].length + '>>');
  114. //putStream(zlib.compress(img['pal']));
  115. out('&lt;&lt; /Length ' + img['pal'].length + '>>');
  116. putStream(this.arrayBufferToBinaryString(new Uint8Array(img['pal'])));
  117. out('endobj');
  118. }
  119. }
  120. , putResourcesCallback = function() {
  121. var images = this.internal.collections[namespace + 'images']
  122. for ( var i in images ) {
  123. putImage.call(this, images[i])
  124. }
  125. }
  126. , putXObjectsDictCallback = function(){
  127. var images = this.internal.collections[namespace + 'images']
  128. , out = this.internal.write
  129. , image
  130. for (var i in images) {
  131. image = images[i]
  132. out(
  133. '/I' + image['i']
  134. , image['n']
  135. , '0'
  136. , 'R'
  137. )
  138. }
  139. }
  140. , checkCompressValue = function(value) {
  141. if(value &amp;&amp; typeof value === 'string')
  142. value = value.toUpperCase();
  143. return value in jsPDFAPI.image_compression ? value : jsPDFAPI.image_compression.NONE;
  144. }
  145. , getImages = function() {
  146. var images = this.internal.collections[namespace + 'images'];
  147. //first run, so initialise stuff
  148. if(!images) {
  149. this.internal.collections[namespace + 'images'] = images = {};
  150. this.internal.events.subscribe('putResources', putResourcesCallback);
  151. this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback);
  152. }
  153. return images;
  154. }
  155. , getImageIndex = function(images) {
  156. var imageIndex = 0;
  157. if (images){
  158. // this is NOT the first time this method is ran on this instance of jsPDF object.
  159. imageIndex = Object.keys ?
  160. Object.keys(images).length :
  161. (function(o){
  162. var i = 0
  163. for (var e in o){if(o.hasOwnProperty(e)){ i++ }}
  164. return i
  165. })(images)
  166. }
  167. return imageIndex;
  168. }
  169. , notDefined = function(value) {
  170. return typeof value === 'undefined' || value === null;
  171. }
  172. , generateAliasFromData = function(data) {
  173. return typeof data === 'string' &amp;&amp; jsPDFAPI.sHashCode(data);
  174. }
  175. , doesNotSupportImageType = function(type) {
  176. return supported_image_types.indexOf(type) === -1;
  177. }
  178. , processMethodNotEnabled = function(type) {
  179. return typeof jsPDFAPI['process' + type.toUpperCase()] !== 'function';
  180. }
  181. , isDOMElement = function(object) {
  182. return typeof object === 'object' &amp;&amp; object.nodeType === 1;
  183. }
  184. , createDataURIFromElement = function(element, format, angle) {
  185. //if element is an image which uses data url definition, just return the dataurl
  186. if (element.nodeName === 'IMG' &amp;&amp; element.hasAttribute('src')) {
  187. var src = ''+element.getAttribute('src');
  188. if (!angle &amp;&amp; src.indexOf('data:image/') === 0) return src;
  189. // only if the user doesn't care about a format
  190. if (!format &amp;&amp; /\.png(?:[?#].*)?$/i.test(src)) format = 'png';
  191. }
  192. if(element.nodeName === 'CANVAS') {
  193. var canvas = element;
  194. } else {
  195. var canvas = document.createElement('canvas');
  196. canvas.width = element.clientWidth || element.width;
  197. canvas.height = element.clientHeight || element.height;
  198. var ctx = canvas.getContext('2d');
  199. if (!ctx) {
  200. throw ('addImage requires canvas to be supported by browser.');
  201. }
  202. if (angle) {
  203. var x, y, b, c, s, w, h, to_radians = Math.PI/180, angleInRadians;
  204. if (typeof angle === 'object') {
  205. x = angle.x;
  206. y = angle.y;
  207. b = angle.bg;
  208. angle = angle.angle;
  209. }
  210. angleInRadians = angle*to_radians;
  211. c = Math.abs(Math.cos(angleInRadians));
  212. s = Math.abs(Math.sin(angleInRadians));
  213. w = canvas.width;
  214. h = canvas.height;
  215. canvas.width = h * s + w * c;
  216. canvas.height = h * c + w * s;
  217. if (isNaN(x)) x = canvas.width / 2;
  218. if (isNaN(y)) y = canvas.height / 2;
  219. ctx.clearRect(0,0,canvas.width, canvas.height);
  220. ctx.fillStyle = b || 'white';
  221. ctx.fillRect(0, 0, canvas.width, canvas.height);
  222. ctx.save();
  223. ctx.translate(x, y);
  224. ctx.rotate(angleInRadians);
  225. ctx.drawImage(element, -(w/2), -(h/2));
  226. ctx.rotate(-angleInRadians);
  227. ctx.translate(-x, -y);
  228. ctx.restore();
  229. } else {
  230. ctx.drawImage(element, 0, 0, canvas.width, canvas.height);
  231. }
  232. }
  233. return canvas.toDataURL((''+format).toLowerCase() == 'png' ? 'image/png' : 'image/jpeg');
  234. }
  235. ,checkImagesForAlias = function(alias, images) {
  236. var cached_info;
  237. if(images) {
  238. for(var e in images) {
  239. if(alias === images[e].alias) {
  240. cached_info = images[e];
  241. break;
  242. }
  243. }
  244. }
  245. return cached_info;
  246. }
  247. ,determineWidthAndHeight = function(w, h, info) {
  248. if (!w &amp;&amp; !h) {
  249. w = -96;
  250. h = -96;
  251. }
  252. if (w &lt; 0) {
  253. w = (-1) * info['w'] * 72 / w / this.internal.scaleFactor;
  254. }
  255. if (h &lt; 0) {
  256. h = (-1) * info['h'] * 72 / h / this.internal.scaleFactor;
  257. }
  258. if (w === 0) {
  259. w = h * info['w'] / info['h'];
  260. }
  261. if (h === 0) {
  262. h = w * info['h'] / info['w'];
  263. }
  264. return [w, h];
  265. }
  266. , writeImageToPDF = function(x, y, w, h, info, index, images) {
  267. var dims = determineWidthAndHeight.call(this, w, h, info),
  268. coord = this.internal.getCoordinateString,
  269. vcoord = this.internal.getVerticalCoordinateString;
  270. w = dims[0];
  271. h = dims[1];
  272. images[index] = info;
  273. this.internal.write(
  274. 'q'
  275. , coord(w)
  276. , '0 0'
  277. , coord(h) // TODO: check if this should be shifted by vcoord
  278. , coord(x)
  279. , vcoord(y + h)
  280. , 'cm /I'+info['i']
  281. , 'Do Q'
  282. )
  283. };
  284. /**
  285. * COLOR SPACES
  286. */
  287. jsPDFAPI.color_spaces = {
  288. DEVICE_RGB:'DeviceRGB',
  289. DEVICE_GRAY:'DeviceGray',
  290. DEVICE_CMYK:'DeviceCMYK',
  291. CAL_GREY:'CalGray',
  292. CAL_RGB:'CalRGB',
  293. LAB:'Lab',
  294. ICC_BASED:'ICCBased',
  295. INDEXED:'Indexed',
  296. PATTERN:'Pattern',
  297. SEPARATION:'Separation',
  298. DEVICE_N:'DeviceN'
  299. };
  300. /**
  301. * DECODE METHODS
  302. */
  303. jsPDFAPI.decode = {
  304. DCT_DECODE:'DCTDecode',
  305. FLATE_DECODE:'FlateDecode',
  306. LZW_DECODE:'LZWDecode',
  307. JPX_DECODE:'JPXDecode',
  308. JBIG2_DECODE:'JBIG2Decode',
  309. ASCII85_DECODE:'ASCII85Decode',
  310. ASCII_HEX_DECODE:'ASCIIHexDecode',
  311. RUN_LENGTH_DECODE:'RunLengthDecode',
  312. CCITT_FAX_DECODE:'CCITTFaxDecode'
  313. };
  314. /**
  315. * IMAGE COMPRESSION TYPES
  316. */
  317. jsPDFAPI.image_compression = {
  318. NONE: 'NONE',
  319. FAST: 'FAST',
  320. MEDIUM: 'MEDIUM',
  321. SLOW: 'SLOW'
  322. };
  323. jsPDFAPI.sHashCode = function(str) {
  324. return Array.prototype.reduce &amp;&amp; str.split("").reduce(function(a,b){a=((a&lt;&lt;5)-a)+b.charCodeAt(0);return a&amp;a},0);
  325. };
  326. jsPDFAPI.isString = function(object) {
  327. return typeof object === 'string';
  328. };
  329. /**
  330. * Strips out and returns info from a valid base64 data URI
  331. * @param {String[dataURI]} a valid data URI of format 'data:[&lt;MIME-type>][;base64],&lt;data>'
  332. * @returns an Array containing the following
  333. * [0] the complete data URI
  334. * [1] &lt;MIME-type>
  335. * [2] format - the second part of the mime-type i.e 'png' in 'image/png'
  336. * [4] &lt;data>
  337. */
  338. jsPDFAPI.extractInfoFromBase64DataURI = function(dataURI) {
  339. return /^data:([\w]+?\/([\w]+?));base64,(.+?)$/g.exec(dataURI);
  340. };
  341. /**
  342. * Check to see if ArrayBuffer is supported
  343. */
  344. jsPDFAPI.supportsArrayBuffer = function() {
  345. return typeof ArrayBuffer !== 'undefined' &amp;&amp; typeof Uint8Array !== 'undefined';
  346. };
  347. /**
  348. * Tests supplied object to determine if ArrayBuffer
  349. * @param {Object[object]}
  350. */
  351. jsPDFAPI.isArrayBuffer = function(object) {
  352. if(!this.supportsArrayBuffer())
  353. return false;
  354. return object instanceof ArrayBuffer;
  355. };
  356. /**
  357. * Tests supplied object to determine if it implements the ArrayBufferView (TypedArray) interface
  358. * @param {Object[object]}
  359. */
  360. jsPDFAPI.isArrayBufferView = function(object) {
  361. if(!this.supportsArrayBuffer())
  362. return false;
  363. if(typeof Uint32Array === 'undefined')
  364. return false;
  365. return (object instanceof Int8Array ||
  366. object instanceof Uint8Array ||
  367. (typeof Uint8ClampedArray !== 'undefined' &amp;&amp; object instanceof Uint8ClampedArray) ||
  368. object instanceof Int16Array ||
  369. object instanceof Uint16Array ||
  370. object instanceof Int32Array ||
  371. object instanceof Uint32Array ||
  372. object instanceof Float32Array ||
  373. object instanceof Float64Array );
  374. };
  375. /**
  376. * Exactly what it says on the tin
  377. */
  378. jsPDFAPI.binaryStringToUint8Array = function(binary_string) {
  379. /*
  380. * not sure how efficient this will be will bigger files. Is there a native method?
  381. */
  382. var len = binary_string.length;
  383. var bytes = new Uint8Array( len );
  384. for (var i = 0; i &lt; len; i++) {
  385. bytes[i] = binary_string.charCodeAt(i);
  386. }
  387. return bytes;
  388. };
  389. /**
  390. * @see this discussion
  391. * http://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers
  392. *
  393. * As stated, i imagine the method below is highly inefficent for large files.
  394. *
  395. * Also of note from Mozilla,
  396. *
  397. * "However, this is slow and error-prone, due to the need for multiple conversions (especially if the binary data is not actually byte-format data, but, for example, 32-bit integers or floats)."
  398. *
  399. * https://developer.mozilla.org/en-US/Add-ons/Code_snippets/StringView
  400. *
  401. * Although i'm strugglig to see how StringView solves this issue? Doesn't appear to be a direct method for conversion?
  402. *
  403. * Async method using Blob and FileReader could be best, but i'm not sure how to fit it into the flow?
  404. */
  405. jsPDFAPI.arrayBufferToBinaryString = function(buffer) {
  406. /*if('TextDecoder' in window){
  407. var decoder = new TextDecoder('ascii');
  408. return decoder.decode(buffer);
  409. }*/
  410. if(this.isArrayBuffer(buffer))
  411. buffer = new Uint8Array(buffer);
  412. var binary_string = '';
  413. var len = buffer.byteLength;
  414. for (var i = 0; i &lt; len; i++) {
  415. binary_string += String.fromCharCode(buffer[i]);
  416. }
  417. return binary_string;
  418. /*
  419. * Another solution is the method below - convert array buffer straight to base64 and then use atob
  420. */
  421. //return atob(this.arrayBufferToBase64(buffer));
  422. };
  423. /**
  424. * Converts an ArrayBuffer directly to base64
  425. *
  426. * Taken from here
  427. *
  428. * http://jsperf.com/encoding-xhr-image-data/31
  429. *
  430. * Need to test if this is a better solution for larger files
  431. *
  432. */
  433. jsPDFAPI.arrayBufferToBase64 = function(arrayBuffer) {
  434. var base64 = ''
  435. var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  436. var bytes = new Uint8Array(arrayBuffer)
  437. var byteLength = bytes.byteLength
  438. var byteRemainder = byteLength % 3
  439. var mainLength = byteLength - byteRemainder
  440. var a, b, c, d
  441. var chunk
  442. // Main loop deals with bytes in chunks of 3
  443. for (var i = 0; i &lt; mainLength; i = i + 3) {
  444. // Combine the three bytes into a single integer
  445. chunk = (bytes[i] &lt;&lt; 16) | (bytes[i + 1] &lt;&lt; 8) | bytes[i + 2]
  446. // Use bitmasks to extract 6-bit segments from the triplet
  447. a = (chunk &amp; 16515072) >> 18 // 16515072 = (2^6 - 1) &lt;&lt; 18
  448. b = (chunk &amp; 258048) >> 12 // 258048 = (2^6 - 1) &lt;&lt; 12
  449. c = (chunk &amp; 4032) >> 6 // 4032 = (2^6 - 1) &lt;&lt; 6
  450. d = chunk &amp; 63 // 63 = 2^6 - 1
  451. // Convert the raw binary segments to the appropriate ASCII encoding
  452. base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
  453. }
  454. // Deal with the remaining bytes and padding
  455. if (byteRemainder == 1) {
  456. chunk = bytes[mainLength]
  457. a = (chunk &amp; 252) >> 2 // 252 = (2^6 - 1) &lt;&lt; 2
  458. // Set the 4 least significant bits to zero
  459. b = (chunk &amp; 3) &lt;&lt; 4 // 3 = 2^2 - 1
  460. base64 += encodings[a] + encodings[b] + '=='
  461. } else if (byteRemainder == 2) {
  462. chunk = (bytes[mainLength] &lt;&lt; 8) | bytes[mainLength + 1]
  463. a = (chunk &amp; 64512) >> 10 // 64512 = (2^6 - 1) &lt;&lt; 10
  464. b = (chunk &amp; 1008) >> 4 // 1008 = (2^6 - 1) &lt;&lt; 4
  465. // Set the 2 least significant bits to zero
  466. c = (chunk &amp; 15) &lt;&lt; 2 // 15 = 2^4 - 1
  467. base64 += encodings[a] + encodings[b] + encodings[c] + '='
  468. }
  469. return base64
  470. };
  471. jsPDFAPI.createImageInfo = function(data, wd, ht, cs, bpc, f, imageIndex, alias, dp, trns, pal, smask, p) {
  472. var info = {
  473. alias:alias,
  474. w : wd,
  475. h : ht,
  476. cs : cs,
  477. bpc : bpc,
  478. i : imageIndex,
  479. data : data
  480. // n: objectNumber will be added by putImage code
  481. };
  482. if(f) info.f = f;
  483. if(dp) info.dp = dp;
  484. if(trns) info.trns = trns;
  485. if(pal) info.pal = pal;
  486. if(smask) info.smask = smask;
  487. if(p) info.p = p;// predictor parameter for PNG compression
  488. return info;
  489. };
  490. jsPDFAPI.addImage = function(imageData, format, x, y, w, h, alias, compression, rotation) {
  491. 'use strict'
  492. if(typeof format !== 'string') {
  493. var tmp = h;
  494. h = w;
  495. w = y;
  496. y = x;
  497. x = format;
  498. format = tmp;
  499. }
  500. if (typeof imageData === 'object' &amp;&amp; !isDOMElement(imageData) &amp;&amp; "imageData" in imageData) {
  501. var options = imageData;
  502. imageData = options.imageData;
  503. format = options.format || format;
  504. x = options.x || x || 0;
  505. y = options.y || y || 0;
  506. w = options.w || w;
  507. h = options.h || h;
  508. alias = options.alias || alias;
  509. compression = options.compression || compression;
  510. rotation = options.rotation || options.angle || rotation;
  511. }
  512. if (isNaN(x) || isNaN(y))
  513. {
  514. console.error('jsPDF.addImage: Invalid coordinates', arguments);
  515. throw new Error('Invalid coordinates passed to jsPDF.addImage');
  516. }
  517. var images = getImages.call(this), info;
  518. if (!(info = checkImagesForAlias(imageData, images))) {
  519. var dataAsBinaryString;
  520. if(isDOMElement(imageData))
  521. imageData = createDataURIFromElement(imageData, format, rotation);
  522. if(notDefined(alias))
  523. alias = generateAliasFromData(imageData);
  524. if (!(info = checkImagesForAlias(alias, images))) {
  525. if(this.isString(imageData)) {
  526. var base64Info = this.extractInfoFromBase64DataURI(imageData);
  527. if(base64Info) {
  528. format = base64Info[2];
  529. imageData = atob(base64Info[3]);//convert to binary string
  530. } else {
  531. if (imageData.charCodeAt(0) === 0x89 &amp;&amp;
  532. imageData.charCodeAt(1) === 0x50 &amp;&amp;
  533. imageData.charCodeAt(2) === 0x4e &amp;&amp;
  534. imageData.charCodeAt(3) === 0x47 ) format = 'png';
  535. }
  536. }
  537. format = (format || 'JPEG').toLowerCase();
  538. if(doesNotSupportImageType(format))
  539. throw new Error('addImage currently only supports formats ' + supported_image_types + ', not \''+format+'\'');
  540. if(processMethodNotEnabled(format))
  541. throw new Error('please ensure that the plugin for \''+format+'\' support is added');
  542. /**
  543. * need to test if it's more efficient to convert all binary strings
  544. * to TypedArray - or should we just leave and process as string?
  545. */
  546. if(this.supportsArrayBuffer()) {
  547. // no need to convert if imageData is already uint8array
  548. if(!(imageData instanceof Uint8Array)){
  549. dataAsBinaryString = imageData;
  550. imageData = this.binaryStringToUint8Array(imageData);
  551. }
  552. }
  553. info = this['process' + format.toUpperCase()](
  554. imageData,
  555. getImageIndex(images),
  556. alias,
  557. checkCompressValue(compression),
  558. dataAsBinaryString
  559. );
  560. if(!info)
  561. throw new Error('An unkwown error occurred whilst processing the image');
  562. }
  563. }
  564. writeImageToPDF.call(this, x, y, w, h, info, info.i, images);
  565. return this
  566. };
  567. /**
  568. * JPEG SUPPORT
  569. **/
  570. //takes a string imgData containing the raw bytes of
  571. //a jpeg image and returns [width, height]
  572. //Algorithm from: http://www.64lines.com/jpeg-width-height
  573. var getJpegSize = function(imgData) {
  574. 'use strict'
  575. var width, height, numcomponents;
  576. // Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00
  577. if (!imgData.charCodeAt(0) === 0xff ||
  578. !imgData.charCodeAt(1) === 0xd8 ||
  579. !imgData.charCodeAt(2) === 0xff ||
  580. !imgData.charCodeAt(3) === 0xe0 ||
  581. !imgData.charCodeAt(6) === 'J'.charCodeAt(0) ||
  582. !imgData.charCodeAt(7) === 'F'.charCodeAt(0) ||
  583. !imgData.charCodeAt(8) === 'I'.charCodeAt(0) ||
  584. !imgData.charCodeAt(9) === 'F'.charCodeAt(0) ||
  585. !imgData.charCodeAt(10) === 0x00) {
  586. throw new Error('getJpegSize requires a binary string jpeg file')
  587. }
  588. var blockLength = imgData.charCodeAt(4)*256 + imgData.charCodeAt(5);
  589. var i = 4, len = imgData.length;
  590. while ( i &lt; len ) {
  591. i += blockLength;
  592. if (imgData.charCodeAt(i) !== 0xff) {
  593. throw new Error('getJpegSize could not find the size of the image');
  594. }
  595. if (imgData.charCodeAt(i+1) === 0xc0 || //(SOF) Huffman - Baseline DCT
  596. imgData.charCodeAt(i+1) === 0xc1 || //(SOF) Huffman - Extended sequential DCT
  597. imgData.charCodeAt(i+1) === 0xc2 || // Progressive DCT (SOF2)
  598. imgData.charCodeAt(i+1) === 0xc3 || // Spatial (sequential) lossless (SOF3)
  599. imgData.charCodeAt(i+1) === 0xc4 || // Differential sequential DCT (SOF5)
  600. imgData.charCodeAt(i+1) === 0xc5 || // Differential progressive DCT (SOF6)
  601. imgData.charCodeAt(i+1) === 0xc6 || // Differential spatial (SOF7)
  602. imgData.charCodeAt(i+1) === 0xc7) {
  603. height = imgData.charCodeAt(i+5)*256 + imgData.charCodeAt(i+6);
  604. width = imgData.charCodeAt(i+7)*256 + imgData.charCodeAt(i+8);
  605. numcomponents = imgData.charCodeAt(i+9);
  606. return [width, height, numcomponents];
  607. } else {
  608. i += 2;
  609. blockLength = imgData.charCodeAt(i)*256 + imgData.charCodeAt(i+1)
  610. }
  611. }
  612. }
  613. , getJpegSizeFromBytes = function(data) {
  614. var hdr = (data[0] &lt;&lt; 8) | data[1];
  615. if(hdr !== 0xFFD8)
  616. throw new Error('Supplied data is not a JPEG');
  617. var len = data.length,
  618. block = (data[4] &lt;&lt; 8) + data[5],
  619. pos = 4,
  620. bytes, width, height, numcomponents;
  621. while(pos &lt; len) {
  622. pos += block;
  623. bytes = readBytes(data, pos);
  624. block = (bytes[2] &lt;&lt; 8) + bytes[3];
  625. if((bytes[1] === 0xC0 || bytes[1] === 0xC2) &amp;&amp; bytes[0] === 0xFF &amp;&amp; block > 7) {
  626. bytes = readBytes(data, pos + 5);
  627. width = (bytes[2] &lt;&lt; 8) + bytes[3];
  628. height = (bytes[0] &lt;&lt; 8) + bytes[1];
  629. numcomponents = bytes[4];
  630. return {width:width, height:height, numcomponents: numcomponents};
  631. }
  632. pos+=2;
  633. }
  634. throw new Error('getJpegSizeFromBytes could not find the size of the image');
  635. }
  636. , readBytes = function(data, offset) {
  637. return data.subarray(offset, offset+ 5);
  638. };
  639. jsPDFAPI.processJPEG = function(data, index, alias, compression, dataAsBinaryString) {
  640. 'use strict'
  641. var colorSpace = this.color_spaces.DEVICE_RGB,
  642. filter = this.decode.DCT_DECODE,
  643. bpc = 8,
  644. dims;
  645. if(this.isString(data)) {
  646. dims = getJpegSize(data);
  647. return this.createImageInfo(data, dims[0], dims[1], dims[3] == 1 ? this.color_spaces.DEVICE_GRAY:colorSpace, bpc, filter, index, alias);
  648. }
  649. if(this.isArrayBuffer(data))
  650. data = new Uint8Array(data);
  651. if(this.isArrayBufferView(data)) {
  652. dims = getJpegSizeFromBytes(data);
  653. // if we already have a stored binary string rep use that
  654. data = dataAsBinaryString || this.arrayBufferToBinaryString(data);
  655. return this.createImageInfo(data, dims.width, dims.height, dims.numcomponents == 1 ? this.color_spaces.DEVICE_GRAY:colorSpace, bpc, filter, index, alias);
  656. }
  657. return null;
  658. };
  659. jsPDFAPI.processJPG = function(/*data, index, alias, compression, dataAsBinaryString*/) {
  660. return this.processJPEG.apply(this, arguments);
  661. }
  662. })(jsPDF.API);
  663. </code></pre>
  664. </article>
  665. </section>
  666. </div>
  667. <br class="clear">
  668. <footer>
  669. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.2</a> on Sun Oct 09 2016 11:08:27 GMT+0100 (BST) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
  670. </footer>
  671. <script>prettyPrint();</script>
  672. <script src="scripts/linenumber.js"></script>
  673. </body>
  674. </html>