plugins_from_html.js.html 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>plugins/from_html.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/from_html.js</h1>
  25. <section>
  26. <article>
  27. <pre class="prettyprint source linenums"><code>/** @preserve
  28. * jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser
  29. * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
  30. * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
  31. * 2014 Diego Casorran, https://github.com/diegocr
  32. * 2014 Daniel Husar, https://github.com/danielhusar
  33. * 2014 Wolfgang Gassler, https://github.com/woolfg
  34. * 2014 Steven Spungin, https://github.com/flamenco
  35. *
  36. * Permission is hereby granted, free of charge, to any person obtaining
  37. * a copy of this software and associated documentation files (the
  38. * "Software"), to deal in the Software without restriction, including
  39. * without limitation the rights to use, copy, modify, merge, publish,
  40. * distribute, sublicense, and/or sell copies of the Software, and to
  41. * permit persons to whom the Software is furnished to do so, subject to
  42. * the following conditions:
  43. *
  44. * The above copyright notice and this permission notice shall be
  45. * included in all copies or substantial portions of the Software.
  46. *
  47. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  48. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  49. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  50. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  51. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  52. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  53. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  54. * ====================================================================
  55. */
  56. (function (jsPDFAPI) {
  57. var clone,
  58. DrillForContent,
  59. FontNameDB,
  60. FontStyleMap,
  61. TextAlignMap,
  62. FontWeightMap,
  63. FloatMap,
  64. ClearMap,
  65. GetCSS,
  66. PurgeWhiteSpace,
  67. Renderer,
  68. ResolveFont,
  69. ResolveUnitedNumber,
  70. UnitedNumberMap,
  71. elementHandledElsewhere,
  72. images,
  73. loadImgs,
  74. checkForFooter,
  75. process,
  76. tableToJson;
  77. clone = (function () {
  78. return function (obj) {
  79. Clone.prototype = obj;
  80. return new Clone()
  81. };
  82. function Clone() {}
  83. })();
  84. PurgeWhiteSpace = function (array) {
  85. var fragment,
  86. i,
  87. l,
  88. lTrimmed,
  89. r,
  90. rTrimmed,
  91. trailingSpace;
  92. i = 0;
  93. l = array.length;
  94. fragment = void 0;
  95. lTrimmed = false;
  96. rTrimmed = false;
  97. while (!lTrimmed &amp;&amp; i !== l) {
  98. fragment = array[i] = array[i].trimLeft();
  99. if (fragment) {
  100. lTrimmed = true;
  101. }
  102. i++;
  103. }
  104. i = l - 1;
  105. while (l &amp;&amp; !rTrimmed &amp;&amp; i !== -1) {
  106. fragment = array[i] = array[i].trimRight();
  107. if (fragment) {
  108. rTrimmed = true;
  109. }
  110. i--;
  111. }
  112. r = /\s+$/g;
  113. trailingSpace = true;
  114. i = 0;
  115. while (i !== l) {
  116. // Leave the line breaks intact
  117. if (array[i] != "\u2028") {
  118. fragment = array[i].replace(/\s+/g, " ");
  119. if (trailingSpace) {
  120. fragment = fragment.trimLeft();
  121. }
  122. if (fragment) {
  123. trailingSpace = r.test(fragment);
  124. }
  125. array[i] = fragment;
  126. }
  127. i++;
  128. }
  129. return array;
  130. };
  131. Renderer = function (pdf, x, y, settings) {
  132. this.pdf = pdf;
  133. this.x = x;
  134. this.y = y;
  135. this.settings = settings;
  136. //list of functions which are called after each element-rendering process
  137. this.watchFunctions = [];
  138. this.init();
  139. return this;
  140. };
  141. ResolveFont = function (css_font_family_string) {
  142. var name,
  143. part,
  144. parts;
  145. name = void 0;
  146. parts = css_font_family_string.split(",");
  147. part = parts.shift();
  148. while (!name &amp;&amp; part) {
  149. name = FontNameDB[part.trim().toLowerCase()];
  150. part = parts.shift();
  151. }
  152. return name;
  153. };
  154. ResolveUnitedNumber = function (css_line_height_string) {
  155. //IE8 issues
  156. css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string;
  157. if (css_line_height_string.indexOf("em") > -1 &amp;&amp; !isNaN(Number(css_line_height_string.replace("em", "")))) {
  158. css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px";
  159. }
  160. if (css_line_height_string.indexOf("pt") > -1 &amp;&amp; !isNaN(Number(css_line_height_string.replace("pt", "")))) {
  161. css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px";
  162. }
  163. var normal,
  164. undef,
  165. value;
  166. undef = void 0;
  167. normal = 16.00;
  168. value = UnitedNumberMap[css_line_height_string];
  169. if (value) {
  170. return value;
  171. }
  172. value = {
  173. "xx-small" : 9,
  174. "x-small" : 11,
  175. small : 13,
  176. medium : 16,
  177. large : 19,
  178. "x-large" : 23,
  179. "xx-large" : 28,
  180. auto : 0
  181. }[{ css_line_height_string : css_line_height_string }];
  182. if (value !== undef) {
  183. return UnitedNumberMap[css_line_height_string] = value / normal;
  184. }
  185. if (value = parseFloat(css_line_height_string)) {
  186. return UnitedNumberMap[css_line_height_string] = value / normal;
  187. }
  188. value = css_line_height_string.match(/([\d\.]+)(px)/);
  189. if (value.length === 3) {
  190. return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal;
  191. }
  192. return UnitedNumberMap[css_line_height_string] = 1;
  193. };
  194. GetCSS = function (element) {
  195. var css,tmp,computedCSSElement;
  196. computedCSSElement = (function (el) {
  197. var compCSS;
  198. compCSS = (function (el) {
  199. if (document.defaultView &amp;&amp; document.defaultView.getComputedStyle) {
  200. return document.defaultView.getComputedStyle(el, null);
  201. } else if (el.currentStyle) {
  202. return el.currentStyle;
  203. } else {
  204. return el.style;
  205. }
  206. })(el);
  207. return function (prop) {
  208. prop = prop.replace(/-\D/g, function (match) {
  209. return match.charAt(1).toUpperCase();
  210. });
  211. return compCSS[prop];
  212. };
  213. })(element);
  214. css = {};
  215. tmp = void 0;
  216. css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times";
  217. css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal";
  218. css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left";
  219. tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal";
  220. if (tmp === "bold") {
  221. if (css["font-style"] === "normal") {
  222. css["font-style"] = tmp;
  223. } else {
  224. css["font-style"] = tmp + css["font-style"];
  225. }
  226. }
  227. css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1;
  228. css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1;
  229. css["display"] = (computedCSSElement("display") === "inline" ? "inline" : "block");
  230. tmp = (css["display"] === "block");
  231. css["margin-top"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("margin-top")) || 0;
  232. css["margin-bottom"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("margin-bottom")) || 0;
  233. css["padding-top"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("padding-top")) || 0;
  234. css["padding-bottom"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0;
  235. css["margin-left"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("margin-left")) || 0;
  236. css["margin-right"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("margin-right")) || 0;
  237. css["padding-left"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("padding-left")) || 0;
  238. css["padding-right"] = tmp &amp;&amp; ResolveUnitedNumber(computedCSSElement("padding-right")) || 0;
  239. css["page-break-before"] = computedCSSElement("page-break-before") || "auto";
  240. //float and clearing of floats
  241. css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none";
  242. css["clear"] = ClearMap[computedCSSElement("clear")] || "none";
  243. css["color"] = computedCSSElement("color");
  244. return css;
  245. };
  246. elementHandledElsewhere = function (element, renderer, elementHandlers) {
  247. var handlers,
  248. i,
  249. isHandledElsewhere,
  250. l,
  251. t;
  252. isHandledElsewhere = false;
  253. i = void 0;
  254. l = void 0;
  255. t = void 0;
  256. handlers = elementHandlers["#" + element.id];
  257. if (handlers) {
  258. if (typeof handlers === "function") {
  259. isHandledElsewhere = handlers(element, renderer);
  260. } else {
  261. i = 0;
  262. l = handlers.length;
  263. while (!isHandledElsewhere &amp;&amp; i !== l) {
  264. isHandledElsewhere = handlers[i](element, renderer);
  265. i++;
  266. }
  267. }
  268. }
  269. handlers = elementHandlers[element.nodeName];
  270. if (!isHandledElsewhere &amp;&amp; handlers) {
  271. if (typeof handlers === "function") {
  272. isHandledElsewhere = handlers(element, renderer);
  273. } else {
  274. i = 0;
  275. l = handlers.length;
  276. while (!isHandledElsewhere &amp;&amp; i !== l) {
  277. isHandledElsewhere = handlers[i](element, renderer);
  278. i++;
  279. }
  280. }
  281. }
  282. return isHandledElsewhere;
  283. };
  284. tableToJson = function (table, renderer) {
  285. var data,
  286. headers,
  287. i,
  288. j,
  289. rowData,
  290. tableRow,
  291. table_obj,
  292. table_with,
  293. cell,
  294. l;
  295. data = [];
  296. headers = [];
  297. i = 0;
  298. l = table.rows[0].cells.length;
  299. table_with = table.clientWidth;
  300. while (i &lt; l) {
  301. cell = table.rows[0].cells[i];
  302. headers[i] = {
  303. name : cell.textContent.toLowerCase().replace(/\s+/g, ''),
  304. prompt : cell.textContent.replace(/\r?\n/g, ''),
  305. width : (cell.clientWidth / table_with) * renderer.pdf.internal.pageSize.width
  306. };
  307. i++;
  308. }
  309. i = 1;
  310. while (i &lt; table.rows.length) {
  311. tableRow = table.rows[i];
  312. rowData = {};
  313. j = 0;
  314. while (j &lt; tableRow.cells.length) {
  315. rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, '');
  316. j++;
  317. }
  318. data.push(rowData);
  319. i++;
  320. }
  321. return table_obj = {
  322. rows : data,
  323. headers : headers
  324. };
  325. };
  326. var SkipNode = {
  327. SCRIPT : 1,
  328. STYLE : 1,
  329. NOSCRIPT : 1,
  330. OBJECT : 1,
  331. EMBED : 1,
  332. SELECT : 1
  333. };
  334. var listCount = 1;
  335. DrillForContent = function (element, renderer, elementHandlers) {
  336. var cn,
  337. cns,
  338. fragmentCSS,
  339. i,
  340. isBlock,
  341. l,
  342. px2pt,
  343. table2json,
  344. cb;
  345. cns = element.childNodes;
  346. cn = void 0;
  347. fragmentCSS = GetCSS(element);
  348. isBlock = fragmentCSS.display === "block";
  349. if (isBlock) {
  350. renderer.setBlockBoundary();
  351. renderer.setBlockStyle(fragmentCSS);
  352. }
  353. px2pt = 0.264583 * 72 / 25.4;
  354. i = 0;
  355. l = cns.length;
  356. while (i &lt; l) {
  357. cn = cns[i];
  358. if (typeof cn === "object") {
  359. //execute all watcher functions to e.g. reset floating
  360. renderer.executeWatchFunctions(cn);
  361. /*** HEADER rendering **/
  362. if (cn.nodeType === 1 &amp;&amp; cn.nodeName === 'HEADER') {
  363. var header = cn;
  364. //store old top margin
  365. var oldMarginTop = renderer.pdf.margins_doc.top;
  366. //subscribe for new page event and render header first on every page
  367. renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) {
  368. //set current y position to old margin
  369. renderer.y = oldMarginTop;
  370. //render all child nodes of the header element
  371. DrillForContent(header, renderer, elementHandlers);
  372. //set margin to old margin + rendered header + 10 space to prevent overlapping
  373. //important for other plugins (e.g. table) to start rendering at correct position after header
  374. renderer.pdf.margins_doc.top = renderer.y + 10;
  375. renderer.y += 10;
  376. }, false);
  377. }
  378. if (cn.nodeType === 8 &amp;&amp; cn.nodeName === "#comment") {
  379. if (~cn.textContent.indexOf("ADD_PAGE")) {
  380. renderer.pdf.addPage();
  381. renderer.y = renderer.pdf.margins_doc.top;
  382. }
  383. } else if (cn.nodeType === 1 &amp;&amp; !SkipNode[cn.nodeName]) {
  384. /*** IMAGE RENDERING ***/
  385. var cached_image;
  386. if (cn.nodeName === "IMG") {
  387. var url = cn.getAttribute("src");
  388. cached_image = images[renderer.pdf.sHashCode(url) || url];
  389. }
  390. if (cached_image) {
  391. if ((renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom &lt; renderer.y + cn.height) &amp;&amp; (renderer.y > renderer.pdf.margins_doc.top)) {
  392. renderer.pdf.addPage();
  393. renderer.y = renderer.pdf.margins_doc.top;
  394. //check if we have to set back some values due to e.g. header rendering for new page
  395. renderer.executeWatchFunctions(cn);
  396. }
  397. var imagesCSS = GetCSS(cn);
  398. var imageX = renderer.x;
  399. var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor;
  400. //define additional paddings, margins which have to be taken into account for margin calculations
  401. var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"])*fontToUnitRatio;
  402. var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"])*fontToUnitRatio;
  403. var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"])*fontToUnitRatio;
  404. var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"])*fontToUnitRatio;
  405. //if float is set to right, move the image to the right border
  406. //add space if margin is set
  407. if (imagesCSS['float'] !== undefined &amp;&amp; imagesCSS['float'] === 'right') {
  408. imageX += renderer.settings.width - cn.width - additionalSpaceRight;
  409. } else {
  410. imageX += additionalSpaceLeft;
  411. }
  412. renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height);
  413. cached_image = undefined;
  414. //if the float prop is specified we have to float the text around the image
  415. if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') {
  416. //add functiont to set back coordinates after image rendering
  417. renderer.watchFunctions.push((function(diffX , thresholdY, diffWidth, el) {
  418. //undo drawing box adaptions which were set by floating
  419. if (renderer.y >= thresholdY) {
  420. renderer.x += diffX;
  421. renderer.settings.width += diffWidth;
  422. return true;
  423. } else if(el &amp;&amp; el.nodeType === 1 &amp;&amp; !SkipNode[el.nodeName] &amp;&amp; renderer.x+el.width > (renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width)) {
  424. renderer.x += diffX;
  425. renderer.y = thresholdY;
  426. renderer.settings.width += diffWidth;
  427. return true;
  428. } else {
  429. return false;
  430. }
  431. }).bind(this, (imagesCSS['float'] === 'left') ? -cn.width-additionalSpaceLeft-additionalSpaceRight : 0, renderer.y+cn.height+additionalSpaceTop+additionalSpaceBottom, cn.width));
  432. //reset floating by clear:both divs
  433. //just set cursorY after the floating element
  434. renderer.watchFunctions.push((function(yPositionAfterFloating, pages, el) {
  435. if (renderer.y &lt; yPositionAfterFloating &amp;&amp; pages === renderer.pdf.internal.getNumberOfPages()) {
  436. if (el.nodeType === 1 &amp;&amp; GetCSS(el).clear === 'both') {
  437. renderer.y = yPositionAfterFloating;
  438. return true;
  439. } else {
  440. return false;
  441. }
  442. } else {
  443. return true;
  444. }
  445. }).bind(this, renderer.y+cn.height, renderer.pdf.internal.getNumberOfPages()));
  446. //if floating is set we decrease the available width by the image width
  447. renderer.settings.width -= cn.width+additionalSpaceLeft+additionalSpaceRight;
  448. //if left just add the image width to the X coordinate
  449. if (imagesCSS['float'] === 'left') {
  450. renderer.x += cn.width+additionalSpaceLeft+additionalSpaceRight;
  451. }
  452. } else {
  453. //if no floating is set, move the rendering cursor after the image height
  454. renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom;
  455. }
  456. /*** TABLE RENDERING ***/
  457. } else if (cn.nodeName === "TABLE") {
  458. table2json = tableToJson(cn, renderer);
  459. renderer.y += 10;
  460. renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, {
  461. autoSize : false,
  462. printHeaders: elementHandlers.printHeaders,
  463. margins: renderer.pdf.margins_doc,
  464. css: GetCSS(cn)
  465. });
  466. renderer.y = renderer.pdf.lastCellPos.y + renderer.pdf.lastCellPos.h + 20;
  467. } else if (cn.nodeName === "OL" || cn.nodeName === "UL") {
  468. listCount = 1;
  469. if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
  470. DrillForContent(cn, renderer, elementHandlers);
  471. }
  472. renderer.y += 10;
  473. } else if (cn.nodeName === "LI") {
  474. var temp = renderer.x;
  475. renderer.x += 20 / renderer.pdf.internal.scaleFactor;
  476. renderer.y += 3;
  477. if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
  478. DrillForContent(cn, renderer, elementHandlers);
  479. }
  480. renderer.x = temp;
  481. } else if (cn.nodeName === "BR") {
  482. renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor;
  483. renderer.addText("\u2028", clone(fragmentCSS));
  484. } else {
  485. if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
  486. DrillForContent(cn, renderer, elementHandlers);
  487. }
  488. }
  489. } else if (cn.nodeType === 3) {
  490. var value = cn.nodeValue;
  491. if (cn.nodeValue &amp;&amp; cn.parentNode.nodeName === "LI") {
  492. if (cn.parentNode.parentNode.nodeName === "OL") {
  493. value = listCount++ + '. ' + value;
  494. } else {
  495. var fontSize = fragmentCSS["font-size"];
  496. var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor;
  497. var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor;
  498. var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor;
  499. cb = function (x, y) {
  500. this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD');
  501. };
  502. }
  503. }
  504. // Only add the text if the text node is in the body element
  505. // Add compatibility with IE11
  506. if(!!(cn.ownerDocument.body.compareDocumentPosition(cn) &amp; 16)){
  507. renderer.addText(value, fragmentCSS);
  508. }
  509. } else if (typeof cn === "string") {
  510. renderer.addText(cn, fragmentCSS);
  511. }
  512. }
  513. i++;
  514. }
  515. elementHandlers.outY = renderer.y;
  516. if (isBlock) {
  517. return renderer.setBlockBoundary(cb);
  518. }
  519. };
  520. images = {};
  521. loadImgs = function (element, renderer, elementHandlers, cb) {
  522. var imgs = element.getElementsByTagName('img'),
  523. l = imgs.length, found_images,
  524. x = 0;
  525. function done() {
  526. renderer.pdf.internal.events.publish('imagesLoaded');
  527. cb(found_images);
  528. }
  529. function loadImage(url, width, height) {
  530. if (!url)
  531. return;
  532. var img = new Image();
  533. found_images = ++x;
  534. img.crossOrigin = '';
  535. img.onerror = img.onload = function () {
  536. if(img.complete) {
  537. //to support data urls in images, set width and height
  538. //as those values are not recognized automatically
  539. if (img.src.indexOf('data:image/') === 0) {
  540. img.width = width || img.width || 0;
  541. img.height = height || img.height || 0;
  542. }
  543. //if valid image add to known images array
  544. if (img.width + img.height) {
  545. var hash = renderer.pdf.sHashCode(url) || url;
  546. images[hash] = images[hash] || img;
  547. }
  548. }
  549. if(!--x) {
  550. done();
  551. }
  552. };
  553. img.src = url;
  554. }
  555. while (l--)
  556. loadImage(imgs[l].getAttribute("src"),imgs[l].width,imgs[l].height);
  557. return x || done();
  558. };
  559. checkForFooter = function (elem, renderer, elementHandlers) {
  560. //check if we can found a &lt;footer> element
  561. var footer = elem.getElementsByTagName("footer");
  562. if (footer.length > 0) {
  563. footer = footer[0];
  564. //bad hack to get height of footer
  565. //creat dummy out and check new y after fake rendering
  566. var oldOut = renderer.pdf.internal.write;
  567. var oldY = renderer.y;
  568. renderer.pdf.internal.write = function () {};
  569. DrillForContent(footer, renderer, elementHandlers);
  570. var footerHeight = Math.ceil(renderer.y - oldY) + 5;
  571. renderer.y = oldY;
  572. renderer.pdf.internal.write = oldOut;
  573. //add 20% to prevent overlapping
  574. renderer.pdf.margins_doc.bottom += footerHeight;
  575. //Create function render header on every page
  576. var renderFooter = function (pageInfo) {
  577. var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1;
  578. //set current y position to old margin
  579. var oldPosition = renderer.y;
  580. //render all child nodes of the header element
  581. renderer.y = renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom;
  582. renderer.pdf.margins_doc.bottom -= footerHeight;
  583. //check if we have to add page numbers
  584. var spans = footer.getElementsByTagName('span');
  585. for (var i = 0; i &lt; spans.length; ++i) {
  586. //if we find some span element with class pageCounter, set the page
  587. if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) {
  588. spans[i].innerHTML = pageNumber;
  589. }
  590. //if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
  591. if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
  592. spans[i].innerHTML = '###jsPDFVarTotalPages###';
  593. }
  594. }
  595. //render footer content
  596. DrillForContent(footer, renderer, elementHandlers);
  597. //set bottom margin to previous height including the footer height
  598. renderer.pdf.margins_doc.bottom += footerHeight;
  599. //important for other plugins (e.g. table) to start rendering at correct position after header
  600. renderer.y = oldPosition;
  601. };
  602. //check if footer contains totalPages which should be replace at the disoposal of the document
  603. var spans = footer.getElementsByTagName('span');
  604. for (var i = 0; i &lt; spans.length; ++i) {
  605. if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
  606. renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
  607. }
  608. }
  609. //register event to render footer on every new page
  610. renderer.pdf.internal.events.subscribe('addPage', renderFooter, false);
  611. //render footer on first page
  612. renderFooter();
  613. //prevent footer rendering
  614. SkipNode['FOOTER'] = 1;
  615. }
  616. };
  617. process = function (pdf, element, x, y, settings, callback) {
  618. if (!element)
  619. return false;
  620. if (typeof element !== "string" &amp;&amp; !element.parentNode)
  621. element = '' + element.innerHTML;
  622. if (typeof element === "string") {
  623. element = (function (element) {
  624. var $frame,
  625. $hiddendiv,
  626. framename,
  627. visuallyhidden;
  628. framename = "jsPDFhtmlText" + Date.now().toString() + (Math.random() * 1000).toFixed(0);
  629. visuallyhidden = "position: absolute !important;" + "clip: rect(1px 1px 1px 1px); /* IE6, IE7 */" + "clip: rect(1px, 1px, 1px, 1px);" + "padding:0 !important;" + "border:0 !important;" + "height: 1px !important;" + "width: 1px !important; " + "top:auto;" + "left:-100px;" + "overflow: hidden;";
  630. $hiddendiv = document.createElement('div');
  631. $hiddendiv.style.cssText = visuallyhidden;
  632. $hiddendiv.innerHTML = "&lt;iframe style=\"height:1px;width:1px\" name=\"" + framename + "\" />";
  633. document.body.appendChild($hiddendiv);
  634. $frame = window.frames[framename];
  635. $frame.document.open();
  636. $frame.document.writeln(element);
  637. $frame.document.close();
  638. return $frame.document.body;
  639. })(element.replace(/&lt;\/?script[^>]*?>/gi, ''));
  640. }
  641. var r = new Renderer(pdf, x, y, settings), out;
  642. // 1. load images
  643. // 2. prepare optional footer elements
  644. // 3. render content
  645. loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) {
  646. checkForFooter( element, r, settings.elementHandlers);
  647. DrillForContent(element, r, settings.elementHandlers);
  648. //send event dispose for final taks (e.g. footer totalpage replacement)
  649. r.pdf.internal.events.publish('htmlRenderingFinished');
  650. out = r.dispose();
  651. if (typeof callback === 'function') callback(out);
  652. else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!');
  653. });
  654. return out || {x: r.x, y:r.y};
  655. };
  656. Renderer.prototype.init = function () {
  657. this.paragraph = {
  658. text : [],
  659. style : []
  660. };
  661. return this.pdf.internal.write("q");
  662. };
  663. Renderer.prototype.dispose = function () {
  664. this.pdf.internal.write("Q");
  665. return {
  666. x : this.x,
  667. y : this.y,
  668. ready:true
  669. };
  670. };
  671. //Checks if we have to execute some watcher functions
  672. //e.g. to end text floating around an image
  673. Renderer.prototype.executeWatchFunctions = function(el) {
  674. var ret = false;
  675. var narray = [];
  676. if (this.watchFunctions.length > 0) {
  677. for(var i=0; i&lt; this.watchFunctions.length; ++i) {
  678. if (this.watchFunctions[i](el) === true) {
  679. ret = true;
  680. } else {
  681. narray.push(this.watchFunctions[i]);
  682. }
  683. }
  684. this.watchFunctions = narray;
  685. }
  686. return ret;
  687. };
  688. Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) {
  689. var currentLineLength,
  690. defaultFontSize,
  691. ff,
  692. fontMetrics,
  693. fontMetricsCache,
  694. fragment,
  695. fragmentChopped,
  696. fragmentLength,
  697. fragmentSpecificMetrics,
  698. fs,
  699. k,
  700. line,
  701. lines,
  702. maxLineLength,
  703. style;
  704. defaultFontSize = 12;
  705. k = this.pdf.internal.scaleFactor;
  706. fontMetricsCache = {};
  707. ff = void 0;
  708. fs = void 0;
  709. fontMetrics = void 0;
  710. fragment = void 0;
  711. style = void 0;
  712. fragmentSpecificMetrics = void 0;
  713. fragmentLength = void 0;
  714. fragmentChopped = void 0;
  715. line = [];
  716. lines = [line];
  717. currentLineLength = 0;
  718. maxLineLength = this.settings.width;
  719. while (fragments.length) {
  720. fragment = fragments.shift();
  721. style = styles.shift();
  722. if (fragment) {
  723. ff = style["font-family"];
  724. fs = style["font-style"];
  725. fontMetrics = fontMetricsCache[ff + fs];
  726. if (!fontMetrics) {
  727. fontMetrics = this.pdf.internal.getFont(ff, fs).metadata.Unicode;
  728. fontMetricsCache[ff + fs] = fontMetrics;
  729. }
  730. fragmentSpecificMetrics = {
  731. widths : fontMetrics.widths,
  732. kerning : fontMetrics.kerning,
  733. fontSize : style["font-size"] * defaultFontSize,
  734. textIndent : currentLineLength
  735. };
  736. fragmentLength = this.pdf.getStringUnitWidth(fragment, fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
  737. if (fragment == "\u2028") {
  738. line = [];
  739. lines.push(line);
  740. } else if (currentLineLength + fragmentLength > maxLineLength) {
  741. fragmentChopped = this.pdf.splitTextToSize(fragment, maxLineLength, fragmentSpecificMetrics);
  742. line.push([fragmentChopped.shift(), style]);
  743. while (fragmentChopped.length) {
  744. line = [[fragmentChopped.shift(), style]];
  745. lines.push(line);
  746. }
  747. currentLineLength = this.pdf.getStringUnitWidth(line[0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
  748. } else {
  749. line.push([fragment, style]);
  750. currentLineLength += fragmentLength;
  751. }
  752. }
  753. }
  754. //if text alignment was set, set margin/indent of each line
  755. if (style['text-align'] !== undefined &amp;&amp; (style['text-align'] === 'center' || style['text-align'] === 'right' || style['text-align'] === 'justify')) {
  756. for (var i = 0; i &lt; lines.length; ++i) {
  757. var length = this.pdf.getStringUnitWidth(lines[i][0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
  758. //if there is more than on line we have to clone the style object as all lines hold a reference on this object
  759. if (i > 0) {
  760. lines[i][0][1] = clone(lines[i][0][1]);
  761. }
  762. var space = (maxLineLength - length);
  763. if (style['text-align'] === 'right') {
  764. lines[i][0][1]['margin-left'] = space;
  765. //if alignment is not right, it has to be center so split the space to the left and the right
  766. } else if (style['text-align'] === 'center') {
  767. lines[i][0][1]['margin-left'] = space / 2;
  768. //if justify was set, calculate the word spacing and define in by using the css property
  769. } else if (style['text-align'] === 'justify') {
  770. var countSpaces = lines[i][0][0].split(' ').length - 1;
  771. lines[i][0][1]['word-spacing'] = space / countSpaces;
  772. //ignore the last line in justify mode
  773. if (i === (lines.length - 1)) {
  774. lines[i][0][1]['word-spacing'] = 0;
  775. }
  776. }
  777. }
  778. }
  779. return lines;
  780. };
  781. Renderer.prototype.RenderTextFragment = function (text, style) {
  782. var defaultFontSize,
  783. font,
  784. maxLineHeight;
  785. maxLineHeight = 0;
  786. defaultFontSize = 12;
  787. if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom &lt; this.y + this.pdf.internal.getFontSize()) {
  788. this.pdf.internal.write("ET", "Q");
  789. this.pdf.addPage();
  790. this.y = this.pdf.margins_doc.top;
  791. this.pdf.internal.write("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), style.color, "Td");
  792. //move cursor by one line on new page
  793. maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]);
  794. this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
  795. }
  796. font = this.pdf.internal.getFont(style["font-family"], style["font-style"]);
  797. // text color
  798. var pdfTextColor = this.getPdfColor(style["color"]);
  799. if (pdfTextColor !== this.lastTextColor)
  800. {
  801. this.pdf.internal.write(pdfTextColor);
  802. this.lastTextColor = pdfTextColor;
  803. }
  804. //set the word spacing for e.g. justify style
  805. if (style['word-spacing'] !== undefined &amp;&amp; style['word-spacing'] > 0) {
  806. this.pdf.internal.write(style['word-spacing'].toFixed(2), "Tw");
  807. }
  808. this.pdf.internal.write("/" + font.id, (defaultFontSize * style["font-size"]).toFixed(2), "Tf", "(" + this.pdf.internal.pdfEscape(text) + ") Tj");
  809. //set the word spacing back to neutral => 0
  810. if (style['word-spacing'] !== undefined) {
  811. this.pdf.internal.write(0, "Tw");
  812. }
  813. };
  814. // Accepts #FFFFFF, rgb(int,int,int), or CSS Color Name
  815. Renderer.prototype.getPdfColor = function(style) {
  816. var textColor;
  817. var r,g,b;
  818. var rx = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/;
  819. var m = rx.exec(style);
  820. if (m != null){
  821. r = parseInt(m[1]);
  822. g = parseInt(m[2]);
  823. b = parseInt(m[3]);
  824. }
  825. else{
  826. if (style.charAt(0) != '#') {
  827. style = CssColors.colorNameToHex(style);
  828. if (!style) {
  829. style = '#000000';
  830. }
  831. }
  832. r = style.substring(1, 3);
  833. r = parseInt(r, 16);
  834. g = style.substring(3, 5);
  835. g = parseInt(g, 16);
  836. b = style.substring(5, 7);
  837. b = parseInt(b, 16);
  838. }
  839. if ((typeof r === 'string') &amp;&amp; /^#[0-9A-Fa-f]{6}$/.test(r)) {
  840. var hex = parseInt(r.substr(1), 16);
  841. r = (hex >> 16) &amp; 255;
  842. g = (hex >> 8) &amp; 255;
  843. b = (hex &amp; 255);
  844. }
  845. var f3 = this.f3;
  846. if ((r === 0 &amp;&amp; g === 0 &amp;&amp; b === 0) || (typeof g === 'undefined')) {
  847. textColor = f3(r / 255) + ' g';
  848. } else {
  849. textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
  850. }
  851. return textColor;
  852. };
  853. Renderer.prototype.f3 = function(number) {
  854. return number.toFixed(3); // Ie, %.3f
  855. },
  856. Renderer.prototype.renderParagraph = function (cb) {
  857. var blockstyle,
  858. defaultFontSize,
  859. fontToUnitRatio,
  860. fragments,
  861. i,
  862. l,
  863. line,
  864. lines,
  865. maxLineHeight,
  866. out,
  867. paragraphspacing_after,
  868. paragraphspacing_before,
  869. priorblockstyle,
  870. styles,
  871. fontSize;
  872. fragments = PurgeWhiteSpace(this.paragraph.text);
  873. styles = this.paragraph.style;
  874. blockstyle = this.paragraph.blockstyle;
  875. priorblockstyle = this.paragraph.priorblockstyle || {};
  876. this.paragraph = {
  877. text : [],
  878. style : [],
  879. blockstyle : {},
  880. priorblockstyle : blockstyle
  881. };
  882. if (!fragments.join("").trim()) {
  883. return;
  884. }
  885. lines = this.splitFragmentsIntoLines(fragments, styles);
  886. line = void 0;
  887. maxLineHeight = void 0;
  888. defaultFontSize = 12;
  889. fontToUnitRatio = defaultFontSize / this.pdf.internal.scaleFactor;
  890. this.priorMarginBottom = this.priorMarginBottom || 0;
  891. paragraphspacing_before = (Math.max((blockstyle["margin-top"] || 0) - this.priorMarginBottom, 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
  892. paragraphspacing_after = ((blockstyle["margin-bottom"] || 0) + (blockstyle["padding-bottom"] || 0)) * fontToUnitRatio;
  893. this.priorMarginBottom = blockstyle["margin-bottom"] || 0;
  894. if (blockstyle['page-break-before'] === 'always'){
  895. this.pdf.addPage();
  896. this.y = 0;
  897. paragraphspacing_before = ((blockstyle["margin-top"] || 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
  898. }
  899. out = this.pdf.internal.write;
  900. i = void 0;
  901. l = void 0;
  902. this.y += paragraphspacing_before;
  903. out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
  904. //stores the current indent of cursor position
  905. var currentIndent = 0;
  906. while (lines.length) {
  907. line = lines.shift();
  908. maxLineHeight = 0;
  909. i = 0;
  910. l = line.length;
  911. while (i !== l) {
  912. if (line[i][0].trim()) {
  913. maxLineHeight = Math.max(maxLineHeight, line[i][1]["line-height"], line[i][1]["font-size"]);
  914. fontSize = line[i][1]["font-size"] * 7;
  915. }
  916. i++;
  917. }
  918. //if we have to move the cursor to adapt the indent
  919. var indentMove = 0;
  920. var wantedIndent = 0;
  921. //if a margin was added (by e.g. a text-alignment), move the cursor
  922. if (line[0][1]["margin-left"] !== undefined &amp;&amp; line[0][1]["margin-left"] > 0) {
  923. wantedIndent = this.pdf.internal.getCoordinateString(line[0][1]["margin-left"]);
  924. indentMove = wantedIndent - currentIndent;
  925. currentIndent = wantedIndent;
  926. }
  927. var indentMore = (Math.max(blockstyle["margin-left"] || 0, 0)) * fontToUnitRatio;
  928. //move the cursor
  929. out(indentMove + indentMore, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
  930. i = 0;
  931. l = line.length;
  932. while (i !== l) {
  933. if (line[i][0]) {
  934. this.RenderTextFragment(line[i][0], line[i][1]);
  935. }
  936. i++;
  937. }
  938. this.y += maxLineHeight * fontToUnitRatio;
  939. //if some watcher function was executed successful, so e.g. margin and widths were changed,
  940. //reset line drawing and calculate position and lines again
  941. //e.g. to stop text floating around an image
  942. if (this.executeWatchFunctions(line[0][1]) &amp;&amp; lines.length > 0) {
  943. var localFragments = [];
  944. var localStyles = [];
  945. //create fragment array of
  946. lines.forEach(function(localLine) {
  947. var i = 0;
  948. var l = localLine.length;
  949. while (i !== l) {
  950. if (localLine[i][0]) {
  951. localFragments.push(localLine[i][0]+' ');
  952. localStyles.push(localLine[i][1]);
  953. }
  954. ++i;
  955. }
  956. });
  957. //split lines again due to possible coordinate changes
  958. lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles);
  959. //reposition the current cursor
  960. out("ET", "Q");
  961. out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
  962. }
  963. }
  964. if (cb &amp;&amp; typeof cb === "function") {
  965. cb.call(this, this.x - 9, this.y - fontSize / 2);
  966. }
  967. out("ET", "Q");
  968. return this.y += paragraphspacing_after;
  969. };
  970. Renderer.prototype.setBlockBoundary = function (cb) {
  971. return this.renderParagraph(cb);
  972. };
  973. Renderer.prototype.setBlockStyle = function (css) {
  974. return this.paragraph.blockstyle = css;
  975. };
  976. Renderer.prototype.addText = function (text, css) {
  977. this.paragraph.text.push(text);
  978. return this.paragraph.style.push(css);
  979. };
  980. FontNameDB = {
  981. helvetica : "helvetica",
  982. "sans-serif" : "helvetica",
  983. "times new roman" : "times",
  984. serif : "times",
  985. times : "times",
  986. monospace : "courier",
  987. courier : "courier"
  988. };
  989. FontWeightMap = {
  990. 100 : "normal",
  991. 200 : "normal",
  992. 300 : "normal",
  993. 400 : "normal",
  994. 500 : "bold",
  995. 600 : "bold",
  996. 700 : "bold",
  997. 800 : "bold",
  998. 900 : "bold",
  999. normal : "normal",
  1000. bold : "bold",
  1001. bolder : "bold",
  1002. lighter : "normal"
  1003. };
  1004. FontStyleMap = {
  1005. normal : "normal",
  1006. italic : "italic",
  1007. oblique : "italic"
  1008. };
  1009. TextAlignMap = {
  1010. left : "left",
  1011. right : "right",
  1012. center : "center",
  1013. justify : "justify"
  1014. };
  1015. FloatMap = {
  1016. none : 'none',
  1017. right: 'right',
  1018. left: 'left'
  1019. };
  1020. ClearMap = {
  1021. none : 'none',
  1022. both : 'both'
  1023. };
  1024. UnitedNumberMap = {
  1025. normal : 1
  1026. };
  1027. /**
  1028. * Converts HTML-formatted text into formatted PDF text.
  1029. *
  1030. * Notes:
  1031. * 2012-07-18
  1032. * Plugin relies on having browser, DOM around. The HTML is pushed into dom and traversed.
  1033. * Plugin relies on jQuery for CSS extraction.
  1034. * Targeting HTML output from Markdown templating, which is a very simple
  1035. * markup - div, span, em, strong, p. No br-based paragraph separation supported explicitly (but still may work.)
  1036. * Images, tables are NOT supported.
  1037. *
  1038. * @public
  1039. * @function
  1040. * @param HTML {String or DOM Element} HTML-formatted text, or pointer to DOM element that is to be rendered into PDF.
  1041. * @param x {Number} starting X coordinate in jsPDF instance's declared units.
  1042. * @param y {Number} starting Y coordinate in jsPDF instance's declared units.
  1043. * @param settings {Object} Additional / optional variables controlling parsing, rendering.
  1044. * @returns {Object} jsPDF instance
  1045. */
  1046. jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins) {
  1047. "use strict";
  1048. this.margins_doc = margins || {
  1049. top : 0,
  1050. bottom : 0
  1051. };
  1052. if (!settings)
  1053. settings = {};
  1054. if (!settings.elementHandlers)
  1055. settings.elementHandlers = {};
  1056. return process(this, HTML, isNaN(x) ? 4 : x, isNaN(y) ? 4 : y, settings, callback);
  1057. };
  1058. })(jsPDF.API);
  1059. </code></pre>
  1060. </article>
  1061. </section>
  1062. </div>
  1063. <br class="clear">
  1064. <footer>
  1065. 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.
  1066. </footer>
  1067. <script>prettyPrint();</script>
  1068. <script src="scripts/linenumber.js"></script>
  1069. </body>
  1070. </html>