jspdf.js.html 83 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>jspdf.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">jspdf.js</h1>
  25. <section>
  26. <article>
  27. <pre class="prettyprint source linenums"><code>/** @preserve
  28. * jsPDF - PDF Document creation from JavaScript
  29. * Version ${versionID}
  30. * CommitID ${commitID}
  31. *
  32. * Copyright (c) 2010-2016 James Hall &lt;james@parall.ax>, https://github.com/MrRio/jsPDF
  33. * 2010 Aaron Spike, https://github.com/acspike
  34. * 2012 Willow Systems Corporation, willow-systems.com
  35. * 2012 Pablo Hess, https://github.com/pablohess
  36. * 2012 Florian Jenett, https://github.com/fjenett
  37. * 2013 Warren Weckesser, https://github.com/warrenweckesser
  38. * 2013 Youssef Beddad, https://github.com/lifof
  39. * 2013 Lee Driscoll, https://github.com/lsdriscoll
  40. * 2013 Stefan Slonevskiy, https://github.com/stefslon
  41. * 2013 Jeremy Morel, https://github.com/jmorel
  42. * 2013 Christoph Hartmann, https://github.com/chris-rock
  43. * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
  44. * 2014 James Makes, https://github.com/dollaruw
  45. * 2014 Diego Casorran, https://github.com/diegocr
  46. * 2014 Steven Spungin, https://github.com/Flamenco
  47. * 2014 Kenneth Glassey, https://github.com/Gavvers
  48. *
  49. * Permission is hereby granted, free of charge, to any person obtaining
  50. * a copy of this software and associated documentation files (the
  51. * "Software"), to deal in the Software without restriction, including
  52. * without limitation the rights to use, copy, modify, merge, publish,
  53. * distribute, sublicense, and/or sell copies of the Software, and to
  54. * permit persons to whom the Software is furnished to do so, subject to
  55. * the following conditions:
  56. *
  57. * The above copyright notice and this permission notice shall be
  58. * included in all copies or substantial portions of the Software.
  59. *
  60. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  61. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  62. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  63. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  64. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  65. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  66. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  67. *
  68. * Contributor(s):
  69. * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
  70. * kim3er, mfo, alnorth, Flamenco
  71. */
  72. /**
  73. * Creates new jsPDF document object instance.
  74. *
  75. * @class
  76. * @param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
  77. * @param unit Measurement unit to be used when coordinates are specified.
  78. * One of "pt" (points), "mm" (Default), "cm", "in"
  79. * @param format One of 'pageFormats' as shown below, default: a4
  80. * @returns {jsPDF}
  81. * @name jsPDF
  82. */
  83. var jsPDF = (function(global) {
  84. 'use strict';
  85. var pdfVersion = '1.3',
  86. pageFormats = { // Size in pt of various paper formats
  87. 'a0': [2383.94, 3370.39],
  88. 'a1': [1683.78, 2383.94],
  89. 'a2': [1190.55, 1683.78],
  90. 'a3': [841.89, 1190.55],
  91. 'a4': [595.28, 841.89],
  92. 'a5': [419.53, 595.28],
  93. 'a6': [297.64, 419.53],
  94. 'a7': [209.76, 297.64],
  95. 'a8': [147.40, 209.76],
  96. 'a9': [104.88, 147.40],
  97. 'a10': [73.70, 104.88],
  98. 'b0': [2834.65, 4008.19],
  99. 'b1': [2004.09, 2834.65],
  100. 'b2': [1417.32, 2004.09],
  101. 'b3': [1000.63, 1417.32],
  102. 'b4': [708.66, 1000.63],
  103. 'b5': [498.90, 708.66],
  104. 'b6': [354.33, 498.90],
  105. 'b7': [249.45, 354.33],
  106. 'b8': [175.75, 249.45],
  107. 'b9': [124.72, 175.75],
  108. 'b10': [87.87, 124.72],
  109. 'c0': [2599.37, 3676.54],
  110. 'c1': [1836.85, 2599.37],
  111. 'c2': [1298.27, 1836.85],
  112. 'c3': [918.43, 1298.27],
  113. 'c4': [649.13, 918.43],
  114. 'c5': [459.21, 649.13],
  115. 'c6': [323.15, 459.21],
  116. 'c7': [229.61, 323.15],
  117. 'c8': [161.57, 229.61],
  118. 'c9': [113.39, 161.57],
  119. 'c10': [79.37, 113.39],
  120. 'dl': [311.81, 623.62],
  121. 'letter': [612, 792],
  122. 'government-letter': [576, 756],
  123. 'legal': [612, 1008],
  124. 'junior-legal': [576, 360],
  125. 'ledger': [1224, 792],
  126. 'tabloid': [792, 1224],
  127. 'credit-card': [153, 243]
  128. };
  129. /**
  130. * jsPDF's Internal PubSub Implementation.
  131. * See mrrio.github.io/jsPDF/doc/symbols/PubSub.html
  132. * Backward compatible rewritten on 2014 by
  133. * Diego Casorran, https://github.com/diegocr
  134. *
  135. * @class
  136. * @name PubSub
  137. * @ignore This should not be in the public docs.
  138. */
  139. function PubSub(context) {
  140. var topics = {};
  141. this.subscribe = function(topic, callback, once) {
  142. if (typeof callback !== 'function') {
  143. return false;
  144. }
  145. if (!topics.hasOwnProperty(topic)) {
  146. topics[topic] = {};
  147. }
  148. var id = Math.random().toString(35);
  149. topics[topic][id] = [callback, !!once];
  150. return id;
  151. };
  152. this.unsubscribe = function(token) {
  153. for (var topic in topics) {
  154. if (topics[topic][token]) {
  155. delete topics[topic][token];
  156. return true;
  157. }
  158. }
  159. return false;
  160. };
  161. this.publish = function(topic) {
  162. if (topics.hasOwnProperty(topic)) {
  163. var args = Array.prototype.slice.call(arguments, 1),
  164. idr = [];
  165. for (var id in topics[topic]) {
  166. var sub = topics[topic][id];
  167. try {
  168. sub[0].apply(context, args);
  169. } catch (ex) {
  170. if (global.console) {
  171. console.error('jsPDF PubSub Error', ex.message, ex);
  172. }
  173. }
  174. if (sub[1]) idr.push(id);
  175. }
  176. if (idr.length) idr.forEach(this.unsubscribe);
  177. }
  178. };
  179. }
  180. /**
  181. * @constructor
  182. * @private
  183. */
  184. function jsPDF(orientation, unit, format, compressPdf) {
  185. var options = {};
  186. if (typeof orientation === 'object') {
  187. options = orientation;
  188. orientation = options.orientation;
  189. unit = options.unit || unit;
  190. format = options.format || format;
  191. compressPdf = options.compress || options.compressPdf || compressPdf;
  192. }
  193. // Default options
  194. unit = unit || 'mm';
  195. format = format || 'a4';
  196. orientation = ('' + (orientation || 'P')).toLowerCase();
  197. var format_as_string = ('' + format).toLowerCase(),
  198. compress = !!compressPdf &amp;&amp; typeof Uint8Array === 'function',
  199. textColor = options.textColor || '0 g',
  200. drawColor = options.drawColor || '0 G',
  201. activeFontSize = options.fontSize || 16,
  202. lineHeightProportion = options.lineHeight || 1.15,
  203. lineWidth = options.lineWidth || 0.200025, // 2mm
  204. objectNumber = 2, // 'n' Current object number
  205. outToPages = !1, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
  206. offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
  207. fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font.
  208. fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
  209. activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle
  210. k, // Scale factor
  211. tmp,
  212. page = 0,
  213. currentPage,
  214. pages = [],
  215. pagesContext = [], // same index as pages and pagedim
  216. pagedim = [],
  217. content = [],
  218. additionalObjects = [],
  219. lineCapID = 0,
  220. lineJoinID = 0,
  221. content_length = 0,
  222. pageWidth,
  223. pageHeight,
  224. pageMode,
  225. zoomMode,
  226. layoutMode,
  227. documentProperties = {
  228. 'title': '',
  229. 'subject': '',
  230. 'author': '',
  231. 'keywords': '',
  232. 'creator': ''
  233. },
  234. API = {},
  235. events = new PubSub(API),
  236. /////////////////////
  237. // Private functions
  238. /////////////////////
  239. f2 = function(number) {
  240. return number.toFixed(2); // Ie, %.2f
  241. },
  242. f3 = function(number) {
  243. return number.toFixed(3); // Ie, %.3f
  244. },
  245. padd2 = function(number) {
  246. return ('0' + parseInt(number)).slice(-2);
  247. },
  248. out = function(string) {
  249. if (outToPages) {
  250. /* set by beginPage */
  251. pages[currentPage].push(string);
  252. } else {
  253. // +1 for '\n' that will be used to join 'content'
  254. content_length += string.length + 1;
  255. content.push(string);
  256. }
  257. },
  258. newObject = function() {
  259. // Begin a new object
  260. objectNumber++;
  261. offsets[objectNumber] = content_length;
  262. out(objectNumber + ' 0 obj');
  263. return objectNumber;
  264. },
  265. // Does not output the object until after the pages have been output.
  266. // Returns an object containing the objectId and content.
  267. // All pages have been added so the object ID can be estimated to start right after.
  268. // This does not modify the current objectNumber; It must be updated after the newObjects are output.
  269. newAdditionalObject = function() {
  270. var objId = pages.length * 2 + 1;
  271. objId += additionalObjects.length;
  272. var obj = {
  273. objId: objId,
  274. content: ''
  275. };
  276. additionalObjects.push(obj);
  277. return obj;
  278. },
  279. // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
  280. newObjectDeferred = function() {
  281. objectNumber++;
  282. offsets[objectNumber] = function() {
  283. return content_length;
  284. };
  285. return objectNumber;
  286. },
  287. newObjectDeferredBegin = function(oid) {
  288. offsets[oid] = content_length;
  289. },
  290. putStream = function(str) {
  291. out('stream');
  292. out(str);
  293. out('endstream');
  294. },
  295. putPages = function() {
  296. var n, p, arr, i, deflater, adler32, adler32cs, wPt, hPt,
  297. pageObjectNumbers = [];
  298. adler32cs = global.adler32cs || jsPDF.adler32cs;
  299. if (compress &amp;&amp; typeof adler32cs === 'undefined') {
  300. compress = false;
  301. }
  302. // outToPages = false as set in endDocument(). out() writes to content.
  303. for (n = 1; n &lt;= page; n++) {
  304. pageObjectNumbers.push(newObject());
  305. wPt = (pageWidth = pagedim[n].width) * k;
  306. hPt = (pageHeight = pagedim[n].height) * k;
  307. out('&lt;&lt;/Type /Page');
  308. out('/Parent 1 0 R');
  309. out('/Resources 2 0 R');
  310. out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
  311. // Added for annotation plugin
  312. events.publish('putPage', {
  313. pageNumber: n,
  314. page: pages[n]
  315. });
  316. out('/Contents ' + (objectNumber + 1) + ' 0 R');
  317. out('>>');
  318. out('endobj');
  319. // Page content
  320. p = pages[n].join('\n');
  321. newObject();
  322. if (compress) {
  323. arr = [];
  324. i = p.length;
  325. while (i--) {
  326. arr[i] = p.charCodeAt(i);
  327. }
  328. adler32 = adler32cs.from(p);
  329. deflater = new Deflater(6);
  330. deflater.append(new Uint8Array(arr));
  331. p = deflater.flush();
  332. arr = new Uint8Array(p.length + 6);
  333. arr.set(new Uint8Array([120, 156])),
  334. arr.set(p, 2);
  335. arr.set(new Uint8Array([adler32 &amp; 0xFF, (adler32 >> 8) &amp; 0xFF, (
  336. adler32 >> 16) &amp; 0xFF, (adler32 >> 24) &amp; 0xFF]), p.length +
  337. 2);
  338. p = String.fromCharCode.apply(null, arr);
  339. out('&lt;&lt;/Length ' + p.length + ' /Filter [/FlateDecode]>>');
  340. } else {
  341. out('&lt;&lt;/Length ' + p.length + '>>');
  342. }
  343. putStream(p);
  344. out('endobj');
  345. }
  346. offsets[1] = content_length;
  347. out('1 0 obj');
  348. out('&lt;&lt;/Type /Pages');
  349. var kids = '/Kids [';
  350. for (i = 0; i &lt; page; i++) {
  351. kids += pageObjectNumbers[i] + ' 0 R ';
  352. }
  353. out(kids + ']');
  354. out('/Count ' + page);
  355. out('>>');
  356. out('endobj');
  357. events.publish('postPutPages');
  358. },
  359. putFont = function(font) {
  360. font.objectNumber = newObject();
  361. out('&lt;&lt;/BaseFont/' + font.PostScriptName + '/Type/Font');
  362. if (typeof font.encoding === 'string') {
  363. out('/Encoding/' + font.encoding);
  364. }
  365. out('/Subtype/Type1>>');
  366. out('endobj');
  367. },
  368. putFonts = function() {
  369. for (var fontKey in fonts) {
  370. if (fonts.hasOwnProperty(fontKey)) {
  371. putFont(fonts[fontKey]);
  372. }
  373. }
  374. },
  375. putXobjectDict = function() {
  376. // Loop through images, or other data objects
  377. events.publish('putXobjectDict');
  378. },
  379. putResourceDictionary = function() {
  380. out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  381. out('/Font &lt;&lt;');
  382. // Do this for each font, the '1' bit is the index of the font
  383. for (var fontKey in fonts) {
  384. if (fonts.hasOwnProperty(fontKey)) {
  385. out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
  386. }
  387. }
  388. out('>>');
  389. out('/XObject &lt;&lt;');
  390. putXobjectDict();
  391. out('>>');
  392. },
  393. putResources = function() {
  394. putFonts();
  395. events.publish('putResources');
  396. // Resource dictionary
  397. offsets[2] = content_length;
  398. out('2 0 obj');
  399. out('&lt;&lt;');
  400. putResourceDictionary();
  401. out('>>');
  402. out('endobj');
  403. events.publish('postPutResources');
  404. },
  405. putAdditionalObjects = function() {
  406. events.publish('putAdditionalObjects');
  407. for (var i = 0; i &lt; additionalObjects.length; i++) {
  408. var obj = additionalObjects[i];
  409. offsets[obj.objId] = content_length;
  410. out(obj.objId + ' 0 obj');
  411. out(obj.content);;
  412. out('endobj');
  413. }
  414. objectNumber += additionalObjects.length;
  415. events.publish('postPutAdditionalObjects');
  416. },
  417. addToFontDictionary = function(fontKey, fontName, fontStyle) {
  418. // this is mapping structure for quick font key lookup.
  419. // returns the KEY of the font (ex: "F1") for a given
  420. // pair of font name and type (ex: "Arial". "Italic")
  421. if (!fontmap.hasOwnProperty(fontName)) {
  422. fontmap[fontName] = {};
  423. }
  424. fontmap[fontName][fontStyle] = fontKey;
  425. },
  426. /**
  427. * FontObject describes a particular font as member of an instnace of jsPDF
  428. *
  429. * It's a collection of properties like 'id' (to be used in PDF stream),
  430. * 'fontName' (font's family name), 'fontStyle' (font's style variant label)
  431. *
  432. * @class
  433. * @public
  434. * @property id {String} PDF-document-instance-specific label assinged to the font.
  435. * @property PostScriptName {String} PDF specification full name for the font
  436. * @property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
  437. * @name FontObject
  438. * @ignore This should not be in the public docs.
  439. */
  440. addFont = function(PostScriptName, fontName, fontStyle, encoding) {
  441. var fontKey = 'F' + (Object.keys(fonts).length + 1).toString(10),
  442. // This is FontObject
  443. font = fonts[fontKey] = {
  444. 'id': fontKey,
  445. 'PostScriptName': PostScriptName,
  446. 'fontName': fontName,
  447. 'fontStyle': fontStyle,
  448. 'encoding': encoding,
  449. 'metadata': {}
  450. };
  451. addToFontDictionary(fontKey, fontName, fontStyle);
  452. events.publish('addFont', font);
  453. return fontKey;
  454. },
  455. addFonts = function() {
  456. var HELVETICA = "helvetica",
  457. TIMES = "times",
  458. COURIER = "courier",
  459. NORMAL = "normal",
  460. BOLD = "bold",
  461. ITALIC = "italic",
  462. BOLD_ITALIC = "bolditalic",
  463. encoding = 'StandardEncoding',
  464. ZAPF = "zapfdingbats",
  465. standardFonts = [
  466. ['Helvetica', HELVETICA, NORMAL],
  467. ['Helvetica-Bold', HELVETICA, BOLD],
  468. ['Helvetica-Oblique', HELVETICA, ITALIC],
  469. ['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC],
  470. ['Courier', COURIER, NORMAL],
  471. ['Courier-Bold', COURIER, BOLD],
  472. ['Courier-Oblique', COURIER, ITALIC],
  473. ['Courier-BoldOblique', COURIER, BOLD_ITALIC],
  474. ['Times-Roman', TIMES, NORMAL],
  475. ['Times-Bold', TIMES, BOLD],
  476. ['Times-Italic', TIMES, ITALIC],
  477. ['Times-BoldItalic', TIMES, BOLD_ITALIC],
  478. ['ZapfDingbats', ZAPF]
  479. ];
  480. for (var i = 0, l = standardFonts.length; i &lt; l; i++) {
  481. var fontKey = addFont(
  482. standardFonts[i][0],
  483. standardFonts[i][1],
  484. standardFonts[i][2],
  485. encoding);
  486. // adding aliases for standard fonts, this time matching the capitalization
  487. var parts = standardFonts[i][0].split('-');
  488. addToFontDictionary(fontKey, parts[0], parts[1] || '');
  489. }
  490. events.publish('addFonts', {
  491. fonts: fonts,
  492. dictionary: fontmap
  493. });
  494. },
  495. SAFE = function __safeCall(fn) {
  496. fn.foo = function __safeCallWrapper() {
  497. try {
  498. return fn.apply(this, arguments);
  499. } catch (e) {
  500. var stack = e.stack || '';
  501. if (~stack.indexOf(' at ')) stack = stack.split(" at ")[1];
  502. var m = "Error in function " + stack.split("\n")[0].split('&lt;')[
  503. 0] + ": " + e.message;
  504. if (global.console) {
  505. global.console.error(m, e);
  506. if (global.alert) alert(m);
  507. } else {
  508. throw new Error(m);
  509. }
  510. }
  511. };
  512. fn.foo.bar = fn;
  513. return fn.foo;
  514. },
  515. to8bitStream = function(text, flags) {
  516. /**
  517. * PDF 1.3 spec:
  518. * "For text strings encoded in Unicode, the first two bytes must be 254 followed by
  519. * 255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
  520. * with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
  521. * to be a meaningful beginning of a word or phrase.) The remainder of the
  522. * string consists of Unicode character codes, according to the UTF-16 encoding
  523. * specified in the Unicode standard, version 2.0. Commonly used Unicode values
  524. * are represented as 2 bytes per character, with the high-order byte appearing first
  525. * in the string."
  526. *
  527. * In other words, if there are chars in a string with char code above 255, we
  528. * recode the string to UCS2 BE - string doubles in length and BOM is prepended.
  529. *
  530. * HOWEVER!
  531. * Actual *content* (body) text (as opposed to strings used in document properties etc)
  532. * does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
  533. *
  534. * Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
  535. * a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
  536. * fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
  537. * code page. There, however, all characters in the stream are treated as GIDs,
  538. * including BOM, which is the reason we need to skip BOM in content text (i.e. that
  539. * that is tied to a font).
  540. *
  541. * To signal this "special" PDFEscape / to8bitStream handling mode,
  542. * API.text() function sets (unless you overwrite it with manual values
  543. * given to API.text(.., flags) )
  544. * flags.autoencode = true
  545. * flags.noBOM = true
  546. *
  547. * ===================================================================================
  548. * `flags` properties relied upon:
  549. * .sourceEncoding = string with encoding label.
  550. * "Unicode" by default. = encoding of the incoming text.
  551. * pass some non-existing encoding name
  552. * (ex: 'Do not touch my strings! I know what I am doing.')
  553. * to make encoding code skip the encoding step.
  554. * .outputEncoding = Either valid PDF encoding name
  555. * (must be supported by jsPDF font metrics, otherwise no encoding)
  556. * or a JS object, where key = sourceCharCode, value = outputCharCode
  557. * missing keys will be treated as: sourceCharCode === outputCharCode
  558. * .noBOM
  559. * See comment higher above for explanation for why this is important
  560. * .autoencode
  561. * See comment higher above for explanation for why this is important
  562. */
  563. var i, l, sourceEncoding, encodingBlock, outputEncoding, newtext,
  564. isUnicode, ch, bch;
  565. flags = flags || {};
  566. sourceEncoding = flags.sourceEncoding || 'Unicode';
  567. outputEncoding = flags.outputEncoding;
  568. // This 'encoding' section relies on font metrics format
  569. // attached to font objects by, among others,
  570. // "Willow Systems' standard_font_metrics plugin"
  571. // see jspdf.plugin.standard_font_metrics.js for format
  572. // of the font.metadata.encoding Object.
  573. // It should be something like
  574. // .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
  575. // .widths = {0:width, code:width, ..., 'fof':divisor}
  576. // .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
  577. if ((flags.autoencode || outputEncoding) &amp;&amp;
  578. fonts[activeFontKey].metadata &amp;&amp;
  579. fonts[activeFontKey].metadata[sourceEncoding] &amp;&amp;
  580. fonts[activeFontKey].metadata[sourceEncoding].encoding) {
  581. encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding;
  582. // each font has default encoding. Some have it clearly defined.
  583. if (!outputEncoding &amp;&amp; fonts[activeFontKey].encoding) {
  584. outputEncoding = fonts[activeFontKey].encoding;
  585. }
  586. // Hmmm, the above did not work? Let's try again, in different place.
  587. if (!outputEncoding &amp;&amp; encodingBlock.codePages) {
  588. outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
  589. }
  590. if (typeof outputEncoding === 'string') {
  591. outputEncoding = encodingBlock[outputEncoding];
  592. }
  593. // we want output encoding to be a JS Object, where
  594. // key = sourceEncoding's character code and
  595. // value = outputEncoding's character code.
  596. if (outputEncoding) {
  597. isUnicode = false;
  598. newtext = [];
  599. for (i = 0, l = text.length; i &lt; l; i++) {
  600. ch = outputEncoding[text.charCodeAt(i)];
  601. if (ch) {
  602. newtext.push(
  603. String.fromCharCode(ch));
  604. } else {
  605. newtext.push(
  606. text[i]);
  607. }
  608. // since we are looping over chars anyway, might as well
  609. // check for residual unicodeness
  610. if (newtext[i].charCodeAt(0) >> 8) {
  611. /* more than 255 */
  612. isUnicode = true;
  613. }
  614. }
  615. text = newtext.join('');
  616. }
  617. }
  618. i = text.length;
  619. // isUnicode may be set to false above. Hence the triple-equal to undefined
  620. while (isUnicode === undefined &amp;&amp; i !== 0) {
  621. if (text.charCodeAt(i - 1) >> 8) {
  622. /* more than 255 */
  623. isUnicode = true;
  624. }
  625. i--;
  626. }
  627. if (!isUnicode) {
  628. return text;
  629. }
  630. newtext = flags.noBOM ? [] : [254, 255];
  631. for (i = 0, l = text.length; i &lt; l; i++) {
  632. ch = text.charCodeAt(i);
  633. bch = ch >> 8; // divide by 256
  634. if (bch >> 8) {
  635. /* something left after dividing by 256 second time */
  636. throw new Error("Character at position " + i + " of string '" +
  637. text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
  638. }
  639. newtext.push(bch);
  640. newtext.push(ch - (bch &lt;&lt; 8));
  641. }
  642. return String.fromCharCode.apply(undefined, newtext);
  643. },
  644. pdfEscape = function(text, flags) {
  645. /**
  646. * Replace '/', '(', and ')' with pdf-safe versions
  647. *
  648. * Doing to8bitStream does NOT make this PDF display unicode text. For that
  649. * we also need to reference a unicode font and embed it - royal pain in the rear.
  650. *
  651. * There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
  652. * which JavaScript Strings are happy to provide. So, while we still cannot display
  653. * 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
  654. * 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
  655. * is still parseable.
  656. * This will allow immediate support for unicode in document properties strings.
  657. */
  658. return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(
  659. /\(/g, '\\(').replace(/\)/g, '\\)');
  660. },
  661. putInfo = function() {
  662. out('/Producer (jsPDF ' + jsPDF.version + ')');
  663. for (var key in documentProperties) {
  664. if (documentProperties.hasOwnProperty(key) &amp;&amp; documentProperties[
  665. key]) {
  666. out('/' + key.substr(0, 1).toUpperCase() + key.substr(1) + ' (' +
  667. pdfEscape(documentProperties[key]) + ')');
  668. }
  669. }
  670. var created = new Date(),
  671. tzoffset = created.getTimezoneOffset(),
  672. tzsign = tzoffset &lt; 0 ? '+' : '-',
  673. tzhour = Math.floor(Math.abs(tzoffset / 60)),
  674. tzmin = Math.abs(tzoffset % 60),
  675. tzstr = [tzsign, padd2(tzhour), "'", padd2(tzmin), "'"].join('');
  676. out(['/CreationDate (D:',
  677. created.getFullYear(),
  678. padd2(created.getMonth() + 1),
  679. padd2(created.getDate()),
  680. padd2(created.getHours()),
  681. padd2(created.getMinutes()),
  682. padd2(created.getSeconds()), tzstr, ')'
  683. ].join(''));
  684. },
  685. putCatalog = function() {
  686. out('/Type /Catalog');
  687. out('/Pages 1 0 R');
  688. // PDF13ref Section 7.2.1
  689. if (!zoomMode) zoomMode = 'fullwidth';
  690. switch (zoomMode) {
  691. case 'fullwidth':
  692. out('/OpenAction [3 0 R /FitH null]');
  693. break;
  694. case 'fullheight':
  695. out('/OpenAction [3 0 R /FitV null]');
  696. break;
  697. case 'fullpage':
  698. out('/OpenAction [3 0 R /Fit]');
  699. break;
  700. case 'original':
  701. out('/OpenAction [3 0 R /XYZ null null 1]');
  702. break;
  703. default:
  704. var pcn = '' + zoomMode;
  705. if (pcn.substr(pcn.length - 1) === '%')
  706. zoomMode = parseInt(zoomMode) / 100;
  707. if (typeof zoomMode === 'number') {
  708. out('/OpenAction [3 0 R /XYZ null null ' + f2(zoomMode) + ']');
  709. }
  710. }
  711. if (!layoutMode) layoutMode = 'continuous';
  712. switch (layoutMode) {
  713. case 'continuous':
  714. out('/PageLayout /OneColumn');
  715. break;
  716. case 'single':
  717. out('/PageLayout /SinglePage');
  718. break;
  719. case 'two':
  720. case 'twoleft':
  721. out('/PageLayout /TwoColumnLeft');
  722. break;
  723. case 'tworight':
  724. out('/PageLayout /TwoColumnRight');
  725. break;
  726. }
  727. if (pageMode) {
  728. /**
  729. * A name object specifying how the document should be displayed when opened:
  730. * UseNone : Neither document outline nor thumbnail images visible -- DEFAULT
  731. * UseOutlines : Document outline visible
  732. * UseThumbs : Thumbnail images visible
  733. * FullScreen : Full-screen mode, with no menu bar, window controls, or any other window visible
  734. */
  735. out('/PageMode /' + pageMode);
  736. }
  737. events.publish('putCatalog');
  738. },
  739. putTrailer = function() {
  740. out('/Size ' + (objectNumber + 1));
  741. out('/Root ' + objectNumber + ' 0 R');
  742. out('/Info ' + (objectNumber - 1) + ' 0 R');
  743. },
  744. beginPage = function(width, height) {
  745. // Dimensions are stored as user units and converted to points on output
  746. var orientation = typeof height === 'string' &amp;&amp; height.toLowerCase();
  747. if (typeof width === 'string') {
  748. var format = width.toLowerCase();
  749. if (pageFormats.hasOwnProperty(format)) {
  750. width = pageFormats[format][0] / k;
  751. height = pageFormats[format][1] / k;
  752. }
  753. }
  754. if (Array.isArray(width)) {
  755. height = width[1];
  756. width = width[0];
  757. }
  758. if (orientation) {
  759. switch (orientation.substr(0, 1)) {
  760. case 'l':
  761. if (height > width) orientation = 's';
  762. break;
  763. case 'p':
  764. if (width > height) orientation = 's';
  765. break;
  766. }
  767. if (orientation === 's') {
  768. tmp = width;
  769. width = height;
  770. height = tmp;
  771. }
  772. }
  773. outToPages = true;
  774. pages[++page] = [];
  775. pagedim[page] = {
  776. width: Number(width) || pageWidth,
  777. height: Number(height) || pageHeight
  778. };
  779. pagesContext[page] = {};
  780. _setPage(page);
  781. },
  782. _addPage = function() {
  783. beginPage.apply(this, arguments);
  784. // Set line width
  785. out(f2(lineWidth * k) + ' w');
  786. // Set draw color
  787. out(drawColor);
  788. // resurrecting non-default line caps, joins
  789. if (lineCapID !== 0) {
  790. out(lineCapID + ' J');
  791. }
  792. if (lineJoinID !== 0) {
  793. out(lineJoinID + ' j');
  794. }
  795. events.publish('addPage', {
  796. pageNumber: page
  797. });
  798. },
  799. _deletePage = function(n) {
  800. if (n > 0 &amp;&amp; n &lt;= page) {
  801. pages.splice(n, 1);
  802. pagedim.splice(n, 1);
  803. page--;
  804. if (currentPage > page) {
  805. currentPage = page;
  806. }
  807. this.setPage(currentPage);
  808. }
  809. },
  810. _setPage = function(n) {
  811. if (n > 0 &amp;&amp; n &lt;= page) {
  812. currentPage = n;
  813. pageWidth = pagedim[n].width;
  814. pageHeight = pagedim[n].height;
  815. }
  816. },
  817. /**
  818. * Returns a document-specific font key - a label assigned to a
  819. * font name + font type combination at the time the font was added
  820. * to the font inventory.
  821. *
  822. * Font key is used as label for the desired font for a block of text
  823. * to be added to the PDF document stream.
  824. * @private
  825. * @function
  826. * @param fontName {String} can be undefined on "falthy" to indicate "use current"
  827. * @param fontStyle {String} can be undefined on "falthy" to indicate "use current"
  828. * @returns {String} Font key.
  829. */
  830. getFont = function(fontName, fontStyle) {
  831. var key;
  832. fontName = fontName !== undefined ? fontName : fonts[activeFontKey]
  833. .fontName;
  834. fontStyle = fontStyle !== undefined ? fontStyle : fonts[
  835. activeFontKey].fontStyle;
  836. if (fontName !== undefined) {
  837. fontName = fontName.toLowerCase();
  838. }
  839. switch (fontName) {
  840. case 'sans-serif':
  841. case 'verdana':
  842. case 'arial':
  843. case 'helvetica':
  844. fontName = 'helvetica';
  845. break;
  846. case 'fixed':
  847. case 'monospace':
  848. case 'terminal':
  849. case 'courier':
  850. fontName = 'courier';
  851. break;
  852. case 'serif':
  853. case 'cursive':
  854. case 'fantasy':
  855. default:
  856. fontName = 'times';
  857. break;
  858. }
  859. try {
  860. // get a string like 'F3' - the KEY corresponding tot he font + type combination.
  861. key = fontmap[fontName][fontStyle];
  862. } catch (e) {}
  863. if (!key) {
  864. //throw new Error("Unable to look up font label for font '" + fontName + "', '"
  865. //+ fontStyle + "'. Refer to getFontList() for available fonts.");
  866. key = fontmap['times'][fontStyle];
  867. if (key == null) {
  868. key = fontmap['times']['normal'];
  869. }
  870. }
  871. return key;
  872. },
  873. buildDocument = function() {
  874. outToPages = false; // switches out() to content
  875. objectNumber = 2;
  876. content_length = 0;
  877. content = [];
  878. offsets = [];
  879. additionalObjects = [];
  880. // Added for AcroForm
  881. events.publish('buildDocument');
  882. // putHeader()
  883. out('%PDF-' + pdfVersion);
  884. putPages();
  885. // Must happen after putPages
  886. // Modifies current object Id
  887. putAdditionalObjects();
  888. putResources();
  889. // Info
  890. newObject();
  891. out('&lt;&lt;');
  892. putInfo();
  893. out('>>');
  894. out('endobj');
  895. // Catalog
  896. newObject();
  897. out('&lt;&lt;');
  898. putCatalog();
  899. out('>>');
  900. out('endobj');
  901. // Cross-ref
  902. var o = content_length,
  903. i, p = "0000000000";
  904. out('xref');
  905. out('0 ' + (objectNumber + 1));
  906. out(p + ' 65535 f ');
  907. for (i = 1; i &lt;= objectNumber; i++) {
  908. var offset = offsets[i];
  909. if (typeof offset === 'function') {
  910. out((p + offsets[i]()).slice(-10) + ' 00000 n ');
  911. } else {
  912. out((p + offsets[i]).slice(-10) + ' 00000 n ');
  913. }
  914. }
  915. // Trailer
  916. out('trailer');
  917. out('&lt;&lt;');
  918. putTrailer();
  919. out('>>');
  920. out('startxref');
  921. out('' + o);
  922. out('%%EOF');
  923. outToPages = true;
  924. return content.join('\n');
  925. },
  926. getStyle = function(style) {
  927. // see path-painting operators in PDF spec
  928. var op = 'S'; // stroke
  929. if (style === 'F') {
  930. op = 'f'; // fill
  931. } else if (style === 'FD' || style === 'DF') {
  932. op = 'B'; // both
  933. } else if (style === 'f' || style === 'f*' || style === 'B' ||
  934. style === 'B*') {
  935. /*
  936. Allow direct use of these PDF path-painting operators:
  937. - f fill using nonzero winding number rule
  938. - f* fill using even-odd rule
  939. - B fill then stroke with fill using non-zero winding number rule
  940. - B* fill then stroke with fill using even-odd rule
  941. */
  942. op = style;
  943. }
  944. return op;
  945. },
  946. getArrayBuffer = function() {
  947. var data = buildDocument(),
  948. len = data.length,
  949. ab = new ArrayBuffer(len),
  950. u8 = new Uint8Array(ab);
  951. while (len--) u8[len] = data.charCodeAt(len);
  952. return ab;
  953. },
  954. getBlob = function() {
  955. return new Blob([getArrayBuffer()], {
  956. type: "application/pdf"
  957. });
  958. },
  959. /**
  960. * Generates the PDF document.
  961. *
  962. * If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
  963. *
  964. * @param {String} type A string identifying one of the possible output types.
  965. * @param {Object} options An object providing some additional signalling to PDF generator.
  966. * @function
  967. * @returns {jsPDF}
  968. * @methodOf jsPDF#
  969. * @name output
  970. */
  971. output = SAFE(function(type, options) {
  972. var datauri = ('' + type).substr(0, 6) === 'dataur' ?
  973. 'data:application/pdf;base64,' + btoa(buildDocument()) : 0;
  974. switch (type) {
  975. case undefined:
  976. return buildDocument();
  977. case 'save':
  978. if (navigator.getUserMedia) {
  979. if (global.URL === undefined || global.URL.createObjectURL ===
  980. undefined) {
  981. return API.output('dataurlnewwindow');
  982. }
  983. }
  984. saveAs(getBlob(), options);
  985. if (typeof saveAs.unload === 'function') {
  986. if (global.setTimeout) {
  987. setTimeout(saveAs.unload, 911);
  988. }
  989. }
  990. break;
  991. case 'arraybuffer':
  992. return getArrayBuffer();
  993. case 'blob':
  994. return getBlob();
  995. case 'bloburi':
  996. case 'bloburl':
  997. // User is responsible of calling revokeObjectURL
  998. return global.URL &amp;&amp; global.URL.createObjectURL(getBlob()) ||
  999. void 0;
  1000. case 'datauristring':
  1001. case 'dataurlstring':
  1002. return datauri;
  1003. case 'dataurlnewwindow':
  1004. var nW = global.open(datauri);
  1005. if (nW || typeof safari === "undefined") return nW;
  1006. /* pass through */
  1007. case 'datauri':
  1008. case 'dataurl':
  1009. return global.document.location.href = datauri;
  1010. default:
  1011. throw new Error('Output type "' + type +
  1012. '" is not supported.');
  1013. }
  1014. // @TODO: Add different output options
  1015. });
  1016. switch (unit) {
  1017. case 'pt':
  1018. k = 1;
  1019. break;
  1020. case 'mm':
  1021. k = 72 / 25.4000508;
  1022. break;
  1023. case 'cm':
  1024. k = 72 / 2.54000508;
  1025. break;
  1026. case 'in':
  1027. k = 72;
  1028. break;
  1029. case 'px':
  1030. k = 96 / 72;
  1031. break;
  1032. case 'pc':
  1033. k = 12;
  1034. break;
  1035. case 'em':
  1036. k = 12;
  1037. break;
  1038. case 'ex':
  1039. k = 6;
  1040. break;
  1041. default:
  1042. throw ('Invalid unit: ' + unit);
  1043. }
  1044. //---------------------------------------
  1045. // Public API
  1046. /**
  1047. * Object exposing internal API to plugins
  1048. * @public
  1049. */
  1050. API.internal = {
  1051. 'pdfEscape': pdfEscape,
  1052. 'getStyle': getStyle,
  1053. /**
  1054. * Returns {FontObject} describing a particular font.
  1055. * @public
  1056. * @function
  1057. * @param fontName {String} (Optional) Font's family name
  1058. * @param fontStyle {String} (Optional) Font's style variation name (Example:"Italic")
  1059. * @returns {FontObject}
  1060. */
  1061. 'getFont': function() {
  1062. return fonts[getFont.apply(API, arguments)];
  1063. },
  1064. 'getFontSize': function() {
  1065. return activeFontSize;
  1066. },
  1067. 'getLineHeight': function() {
  1068. return activeFontSize * lineHeightProportion;
  1069. },
  1070. 'write': function(string1 /*, string2, string3, etc */ ) {
  1071. out(arguments.length === 1 ? string1 : Array.prototype.join.call(
  1072. arguments, ' '));
  1073. },
  1074. 'getCoordinateString': function(value) {
  1075. return f2(value * k);
  1076. },
  1077. 'getVerticalCoordinateString': function(value) {
  1078. return f2((pageHeight - value) * k);
  1079. },
  1080. 'collections': {},
  1081. 'newObject': newObject,
  1082. 'newAdditionalObject': newAdditionalObject,
  1083. 'newObjectDeferred': newObjectDeferred,
  1084. 'newObjectDeferredBegin': newObjectDeferredBegin,
  1085. 'putStream': putStream,
  1086. 'events': events,
  1087. // ratio that you use in multiplication of a given "size" number to arrive to 'point'
  1088. // units of measurement.
  1089. // scaleFactor is set at initialization of the document and calculated against the stated
  1090. // default measurement units for the document.
  1091. // If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
  1092. // through multiplication.
  1093. 'scaleFactor': k,
  1094. 'pageSize': {
  1095. get width() {
  1096. return pageWidth
  1097. },
  1098. get height() {
  1099. return pageHeight
  1100. }
  1101. },
  1102. 'output': function(type, options) {
  1103. return output(type, options);
  1104. },
  1105. 'getNumberOfPages': function() {
  1106. return pages.length - 1;
  1107. },
  1108. 'pages': pages,
  1109. 'out': out,
  1110. 'f2': f2,
  1111. 'getPageInfo': function(pageNumberOneBased) {
  1112. var objId = (pageNumberOneBased - 1) * 2 + 3;
  1113. return {
  1114. objId: objId,
  1115. pageNumber: pageNumberOneBased,
  1116. pageContext: pagesContext[pageNumberOneBased]
  1117. };
  1118. },
  1119. 'getCurrentPageInfo': function() {
  1120. var objId = (currentPage - 1) * 2 + 3;
  1121. return {
  1122. objId: objId,
  1123. pageNumber: currentPage,
  1124. pageContext: pagesContext[currentPage]
  1125. };
  1126. },
  1127. 'getPDFVersion': function() {
  1128. return pdfVersion;
  1129. }
  1130. };
  1131. /**
  1132. * Adds (and transfers the focus to) new page to the PDF document.
  1133. * @function
  1134. * @returns {jsPDF}
  1135. *
  1136. * @methodOf jsPDF#
  1137. * @name addPage
  1138. */
  1139. API.addPage = function() {
  1140. _addPage.apply(this, arguments);
  1141. return this;
  1142. };
  1143. /**
  1144. * Adds (and transfers the focus to) new page to the PDF document.
  1145. * @function
  1146. * @returns {jsPDF}
  1147. *
  1148. * @methodOf jsPDF#
  1149. * @name setPage
  1150. * @param {Number} page Switch the active page to the page number specified
  1151. * @example
  1152. * doc = jsPDF()
  1153. * doc.addPage()
  1154. * doc.addPage()
  1155. * doc.text('I am on page 3', 10, 10)
  1156. * doc.setPage(1)
  1157. * doc.text('I am on page 1', 10, 10)
  1158. */
  1159. API.setPage = function() {
  1160. _setPage.apply(this, arguments);
  1161. return this;
  1162. };
  1163. API.insertPage = function(beforePage) {
  1164. this.addPage();
  1165. this.movePage(currentPage, beforePage);
  1166. return this;
  1167. };
  1168. API.movePage = function(targetPage, beforePage) {
  1169. if (targetPage > beforePage) {
  1170. var tmpPages = pages[targetPage];
  1171. var tmpPagedim = pagedim[targetPage];
  1172. var tmpPagesContext = pagesContext[targetPage];
  1173. for (var i = targetPage; i > beforePage; i--) {
  1174. pages[i] = pages[i - 1];
  1175. pagedim[i] = pagedim[i - 1];
  1176. pagesContext[i] = pagesContext[i - 1];
  1177. }
  1178. pages[beforePage] = tmpPages;
  1179. pagedim[beforePage] = tmpPagedim;
  1180. pagesContext[beforePage] = tmpPagesContext;
  1181. this.setPage(beforePage);
  1182. } else if (targetPage &lt; beforePage) {
  1183. var tmpPages = pages[targetPage];
  1184. var tmpPagedim = pagedim[targetPage];
  1185. var tmpPagesContext = pagesContext[targetPage];
  1186. for (var i = targetPage; i &lt; beforePage; i++) {
  1187. pages[i] = pages[i + 1];
  1188. pagedim[i] = pagedim[i + 1];
  1189. pagesContext[i] = pagesContext[i + 1];
  1190. }
  1191. pages[beforePage] = tmpPages;
  1192. pagedim[beforePage] = tmpPagedim;
  1193. pagesContext[beforePage] = tmpPagesContext;
  1194. this.setPage(beforePage);
  1195. }
  1196. return this;
  1197. };
  1198. API.deletePage = function() {
  1199. _deletePage.apply(this, arguments);
  1200. return this;
  1201. };
  1202. /**
  1203. * Set the display mode options of the page like zoom and layout.
  1204. *
  1205. * @param {integer|String} zoom You can pass an integer or percentage as
  1206. * a string. 2 will scale the document up 2x, '200%' will scale up by the
  1207. * same amount. You can also set it to 'fullwidth', 'fullheight',
  1208. * 'fullpage', or 'original'.
  1209. *
  1210. * Only certain PDF readers support this, such as Adobe Acrobat
  1211. *
  1212. * @param {String} layout Layout mode can be: 'continuous' - this is the
  1213. * default continuous scroll. 'single' - the single page mode only shows one
  1214. * page at a time. 'twoleft' - two column left mode, first page starts on
  1215. * the left, and 'tworight' - pages are laid out in two columns, with the
  1216. * first page on the right. This would be used for books.
  1217. * @param {String} pmode 'UseOutlines' - it shows the
  1218. * outline of the document on the left. 'UseThumbs' - shows thumbnails along
  1219. * the left. 'FullScreen' - prompts the user to enter fullscreen mode.
  1220. *
  1221. * @function
  1222. * @returns {jsPDF}
  1223. * @name setDisplayMode
  1224. */
  1225. API.setDisplayMode = function(zoom, layout, pmode) {
  1226. zoomMode = zoom;
  1227. layoutMode = layout;
  1228. pageMode = pmode;
  1229. var validPageModes = [undefined, null, 'UseNone', 'UseOutlines', 'UseThumbs', 'FullScreen'];
  1230. if (validPageModes.indexOf(pmode) == -1) {
  1231. throw new Error('Page mode must be one of UseNone, UseOutlines, UseThumbs, or FullScreen. "' + pmode + '" is not recognized.')
  1232. }
  1233. return this;
  1234. },
  1235. /**
  1236. * Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
  1237. *
  1238. * @function
  1239. * @param {String|Array} text String or array of strings to be added to the page. Each line is shifted one line down per font, spacing settings declared before this call.
  1240. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1241. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1242. * @param {Object} flags Collection of settings signalling how the text must be encoded. Defaults are sane. If you think you want to pass some flags, you likely can read the source.
  1243. * @returns {jsPDF}
  1244. * @methodOf jsPDF#
  1245. * @name text
  1246. */
  1247. API.text = function(text, x, y, flags, angle, align) {
  1248. /**
  1249. * Inserts something like this into PDF
  1250. * BT
  1251. * /F1 16 Tf % Font name + size
  1252. * 16 TL % How many units down for next line in multiline text
  1253. * 0 g % color
  1254. * 28.35 813.54 Td % position
  1255. * (line one) Tj
  1256. * T* (line two) Tj
  1257. * T* (line three) Tj
  1258. * ET
  1259. */
  1260. function ESC(s) {
  1261. s = s.split("\t").join(Array(options.TabLen || 9).join(" "));
  1262. return pdfEscape(s, flags);
  1263. }
  1264. // Pre-August-2012 the order of arguments was function(x, y, text, flags)
  1265. // in effort to make all calls have similar signature like
  1266. // function(data, coordinates... , miscellaneous)
  1267. // this method had its args flipped.
  1268. // code below allows backward compatibility with old arg order.
  1269. if (typeof text === 'number') {
  1270. tmp = y;
  1271. y = x;
  1272. x = text;
  1273. text = tmp;
  1274. }
  1275. // If there are any newlines in text, we assume
  1276. // the user wanted to print multiple lines, so break the
  1277. // text up into an array. If the text is already an array,
  1278. // we assume the user knows what they are doing.
  1279. // Convert text into an array anyway to simplify
  1280. // later code.
  1281. if (typeof text === 'string') {
  1282. if (text.match(/[\n\r]/)) {
  1283. text = text.split(/\r\n|\r|\n/g);
  1284. } else {
  1285. text = [text];
  1286. }
  1287. }
  1288. if (typeof angle === 'string') {
  1289. align = angle;
  1290. angle = null;
  1291. }
  1292. if (typeof flags === 'string') {
  1293. align = flags;
  1294. flags = null;
  1295. }
  1296. if (typeof flags === 'number') {
  1297. angle = flags;
  1298. flags = null;
  1299. }
  1300. var xtra = '',
  1301. mode = 'Td',
  1302. todo;
  1303. if (angle) {
  1304. angle *= (Math.PI / 180);
  1305. var c = Math.cos(angle),
  1306. s = Math.sin(angle);
  1307. xtra = [f2(c), f2(s), f2(s * -1), f2(c), ''].join(" ");
  1308. mode = 'Tm';
  1309. }
  1310. flags = flags || {};
  1311. if (!('noBOM' in flags))
  1312. flags.noBOM = true;
  1313. if (!('autoencode' in flags))
  1314. flags.autoencode = true;
  1315. var strokeOption = '';
  1316. var pageContext = this.internal.getCurrentPageInfo().pageContext;
  1317. if (true === flags.stroke) {
  1318. if (pageContext.lastTextWasStroke !== true) {
  1319. strokeOption = '1 Tr\n';
  1320. pageContext.lastTextWasStroke = true;
  1321. }
  1322. } else {
  1323. if (pageContext.lastTextWasStroke) {
  1324. strokeOption = '0 Tr\n';
  1325. }
  1326. pageContext.lastTextWasStroke = false;
  1327. }
  1328. if (typeof this._runningPageHeight === 'undefined') {
  1329. this._runningPageHeight = 0;
  1330. }
  1331. if (typeof text === 'string') {
  1332. text = ESC(text);
  1333. } else if (Object.prototype.toString.call(text) ===
  1334. '[object Array]') {
  1335. // we don't want to destroy original text array, so cloning it
  1336. var sa = text.concat(),
  1337. da = [],
  1338. len = sa.length;
  1339. // we do array.join('text that must not be PDFescaped")
  1340. // thus, pdfEscape each component separately
  1341. while (len--) {
  1342. da.push(ESC(sa.shift()));
  1343. }
  1344. var linesLeft = Math.ceil((pageHeight - y - this._runningPageHeight) *
  1345. k / (activeFontSize * lineHeightProportion));
  1346. if (0 &lt;= linesLeft &amp;&amp; linesLeft &lt; da.length + 1) {
  1347. //todo = da.splice(linesLeft-1);
  1348. }
  1349. if (align) {
  1350. var left,
  1351. prevX,
  1352. maxLineLength,
  1353. leading = activeFontSize * lineHeightProportion,
  1354. lineWidths = text.map(function(v) {
  1355. return this.getStringUnitWidth(v) * activeFontSize / k;
  1356. }, this);
  1357. maxLineLength = Math.max.apply(Math, lineWidths);
  1358. // The first line uses the "main" Td setting,
  1359. // and the subsequent lines are offset by the
  1360. // previous line's x coordinate.
  1361. if (align === "center") {
  1362. // The passed in x coordinate defines
  1363. // the center point.
  1364. left = x - maxLineLength / 2;
  1365. x -= lineWidths[0] / 2;
  1366. } else if (align === "right") {
  1367. // The passed in x coordinate defines the
  1368. // rightmost point of the text.
  1369. left = x - maxLineLength;
  1370. x -= lineWidths[0];
  1371. } else {
  1372. throw new Error(
  1373. 'Unrecognized alignment option, use "center" or "right".'
  1374. );
  1375. }
  1376. prevX = x;
  1377. text = da[0];
  1378. for (var i = 1, len = da.length; i &lt; len; i++) {
  1379. var delta = maxLineLength - lineWidths[i];
  1380. if (align === "center") delta /= 2;
  1381. // T* = x-offset leading Td ( text )
  1382. text += ") Tj\n" + ((left - prevX) + delta) + " -" + leading +
  1383. " Td (" + da[i];
  1384. prevX = left + delta;
  1385. }
  1386. } else {
  1387. text = da.join(") Tj\nT* (");
  1388. }
  1389. } else {
  1390. throw new Error('Type of text must be string or Array. "' + text +
  1391. '" is not recognized.');
  1392. }
  1393. // Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
  1394. // BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET
  1395. // if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations)
  1396. // Thus, there is NO useful, *reliable* concept of "default" font for a page.
  1397. // The fact that "default" (reuse font used before) font worked before in basic cases is an accident
  1398. // - readers dealing smartly with brokenness of jsPDF's markup.
  1399. var curY;
  1400. if (todo) {
  1401. //this.addPage();
  1402. //this._runningPageHeight += y - (activeFontSize * 1.7 / k);
  1403. //curY = f2(pageHeight - activeFontSize * 1.7 /k);
  1404. } else {
  1405. curY = f2((pageHeight - y) * k);
  1406. }
  1407. //curY = f2((pageHeight - (y - this._runningPageHeight)) * k);
  1408. // if (curY &lt; 0){
  1409. // console.log('auto page break');
  1410. // this.addPage();
  1411. // this._runningPageHeight = y - (activeFontSize * 1.7 / k);
  1412. // curY = f2(pageHeight - activeFontSize * 1.7 /k);
  1413. // }
  1414. out(
  1415. 'BT\n/' +
  1416. activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size
  1417. (activeFontSize * lineHeightProportion) + ' TL\n' + // line spacing
  1418. strokeOption + // stroke option
  1419. textColor +
  1420. '\n' + xtra + f2(x * k) + ' ' + curY + ' ' + mode + '\n(' +
  1421. text +
  1422. ') Tj\nET');
  1423. if (todo) {
  1424. //this.text( todo, x, activeFontSize * 1.7 / k);
  1425. //this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7 / k));
  1426. this.text(todo, x, y); // + (activeFontSize * 1.7 / k));
  1427. }
  1428. return this;
  1429. };
  1430. /**
  1431. * Letter spacing method to print text with gaps
  1432. *
  1433. * @function
  1434. * @param {String|Array} text String to be added to the page.
  1435. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1436. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1437. * @param {Number} spacing Spacing (in units declared at inception)
  1438. * @returns {jsPDF}
  1439. * @methodOf jsPDF#
  1440. * @name lstext
  1441. * @deprecated We'll be removing this function. It doesn't take character width into account.
  1442. */
  1443. API.lstext = function(text, x, y, spacing) {
  1444. console.warn('jsPDF.lstext is deprecated');
  1445. for (var i = 0, len = text.length; i &lt; len; i++, x += spacing) this
  1446. .text(text[i], x, y);
  1447. return this;
  1448. };
  1449. API.line = function(x1, y1, x2, y2) {
  1450. return this.lines([
  1451. [x2 - x1, y2 - y1]
  1452. ], x1, y1);
  1453. };
  1454. API.clip = function() {
  1455. // By patrick-roberts, github.com/MrRio/jsPDF/issues/328
  1456. // Call .clip() after calling .rect() with a style argument of null
  1457. out('W') // clip
  1458. out('S') // stroke path; necessary for clip to work
  1459. };
  1460. /**
  1461. * This fixes the previous function clip(). Perhaps the 'stroke path' hack was due to the missing 'n' instruction?
  1462. * We introduce the fixed version so as to not break API.
  1463. * @param fillRule
  1464. */
  1465. API.clip_fixed = function(fillRule) {
  1466. // Call .clip() after calling drawing ops with a style argument of null
  1467. // W is the PDF clipping op
  1468. if ('evenodd' === fillRule) {
  1469. out('W*');
  1470. } else {
  1471. out('W');
  1472. }
  1473. // End the path object without filling or stroking it.
  1474. // This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path
  1475. // (see Section 4.4.3, “Clipping Path Operators”)
  1476. out('n');
  1477. };
  1478. /**
  1479. * Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
  1480. * All data points in `lines` are relative to last line origin.
  1481. * `x`, `y` become x1,y1 for first line / curve in the set.
  1482. * For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
  1483. * For bezier curves you need to specify [x2,y2,x3,y3,x4,y4] - vectors to control points 1, 2, ending point. All vectors are against the start of the curve - x1,y1.
  1484. *
  1485. * @example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
  1486. * @param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
  1487. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1488. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1489. * @param {Number} scale (Defaults to [1.0,1.0]) x,y Scaling factor for all vectors. Elements can be any floating number Sub-one makes drawing smaller. Over-one grows the drawing. Negative flips the direction.
  1490. * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
  1491. * @param {Boolean} closed If true, the path is closed with a straight line from the end of the last curve to the starting point.
  1492. * @function
  1493. * @returns {jsPDF}
  1494. * @methodOf jsPDF#
  1495. * @name lines
  1496. */
  1497. API.lines = function(lines, x, y, scale, style, closed) {
  1498. var scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4;
  1499. // Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
  1500. // in effort to make all calls have similar signature like
  1501. // function(content, coordinateX, coordinateY , miscellaneous)
  1502. // this method had its args flipped.
  1503. // code below allows backward compatibility with old arg order.
  1504. if (typeof lines === 'number') {
  1505. tmp = y;
  1506. y = x;
  1507. x = lines;
  1508. lines = tmp;
  1509. }
  1510. scale = scale || [1, 1];
  1511. // starting point
  1512. out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m ');
  1513. scalex = scale[0];
  1514. scaley = scale[1];
  1515. l = lines.length;
  1516. //, x2, y2 // bezier only. In page default measurement "units", *after* scaling
  1517. //, x3, y3 // bezier only. In page default measurement "units", *after* scaling
  1518. // ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
  1519. x4 = x; // last / ending point = starting point for first item.
  1520. y4 = y; // last / ending point = starting point for first item.
  1521. for (i = 0; i &lt; l; i++) {
  1522. leg = lines[i];
  1523. if (leg.length === 2) {
  1524. // simple line
  1525. x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
  1526. y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
  1527. out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l');
  1528. } else {
  1529. // bezier curve
  1530. x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
  1531. y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
  1532. x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
  1533. y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
  1534. x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
  1535. y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
  1536. out(
  1537. f3(x2 * k) + ' ' +
  1538. f3((pageHeight - y2) * k) + ' ' +
  1539. f3(x3 * k) + ' ' +
  1540. f3((pageHeight - y3) * k) + ' ' +
  1541. f3(x4 * k) + ' ' +
  1542. f3((pageHeight - y4) * k) + ' c');
  1543. }
  1544. }
  1545. if (closed) {
  1546. out(' h');
  1547. }
  1548. // stroking / filling / both the path
  1549. if (style !== null) {
  1550. out(getStyle(style));
  1551. }
  1552. return this;
  1553. };
  1554. /**
  1555. * Adds a rectangle to PDF
  1556. *
  1557. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1558. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1559. * @param {Number} w Width (in units declared at inception of PDF document)
  1560. * @param {Number} h Height (in units declared at inception of PDF document)
  1561. * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
  1562. * @function
  1563. * @returns {jsPDF}
  1564. * @methodOf jsPDF#
  1565. * @name rect
  1566. */
  1567. API.rect = function(x, y, w, h, style) {
  1568. var op = getStyle(style);
  1569. out([
  1570. f2(x * k),
  1571. f2((pageHeight - y) * k),
  1572. f2(w * k),
  1573. f2(-h * k),
  1574. 're'
  1575. ].join(' '));
  1576. if (style !== null) {
  1577. out(getStyle(style));
  1578. }
  1579. return this;
  1580. };
  1581. /**
  1582. * Adds a triangle to PDF
  1583. *
  1584. * @param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
  1585. * @param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1586. * @param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
  1587. * @param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1588. * @param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
  1589. * @param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1590. * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
  1591. * @function
  1592. * @returns {jsPDF}
  1593. * @methodOf jsPDF#
  1594. * @name triangle
  1595. */
  1596. API.triangle = function(x1, y1, x2, y2, x3, y3, style) {
  1597. this.lines(
  1598. [
  1599. [x2 - x1, y2 - y1], // vector to point 2
  1600. [x3 - x2, y3 - y2], // vector to point 3
  1601. [x1 - x3, y1 - y3] // closing vector back to point 1
  1602. ],
  1603. x1,
  1604. y1, // start of path
  1605. [1, 1],
  1606. style,
  1607. true);
  1608. return this;
  1609. };
  1610. /**
  1611. * Adds a rectangle with rounded corners to PDF
  1612. *
  1613. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1614. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1615. * @param {Number} w Width (in units declared at inception of PDF document)
  1616. * @param {Number} h Height (in units declared at inception of PDF document)
  1617. * @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
  1618. * @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
  1619. * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
  1620. * @function
  1621. * @returns {jsPDF}
  1622. * @methodOf jsPDF#
  1623. * @name roundedRect
  1624. */
  1625. API.roundedRect = function(x, y, w, h, rx, ry, style) {
  1626. var MyArc = 4 / 3 * (Math.SQRT2 - 1);
  1627. this.lines(
  1628. [
  1629. [(w - 2 * rx), 0],
  1630. [(rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry],
  1631. [0, (h - 2 * ry)],
  1632. [0, (ry * MyArc), -(rx * MyArc), ry, -rx, ry],
  1633. [(-w + 2 * rx), 0],
  1634. [-(rx * MyArc), 0, -rx, -(ry * MyArc), -rx, -ry],
  1635. [0, (-h + 2 * ry)],
  1636. [0, -(ry * MyArc), (rx * MyArc), -ry, rx, -ry]
  1637. ],
  1638. x + rx,
  1639. y, // start of path
  1640. [1, 1],
  1641. style);
  1642. return this;
  1643. };
  1644. /**
  1645. * Adds an ellipse to PDF
  1646. *
  1647. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1648. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1649. * @param {Number} rx Radius along x axis (in units declared at inception of PDF document)
  1650. * @param {Number} rx Radius along y axis (in units declared at inception of PDF document)
  1651. * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
  1652. * @function
  1653. * @returns {jsPDF}
  1654. * @methodOf jsPDF#
  1655. * @name ellipse
  1656. */
  1657. API.ellipse = function(x, y, rx, ry, style) {
  1658. var lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
  1659. ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
  1660. out([
  1661. f2((x + rx) * k),
  1662. f2((pageHeight - y) * k),
  1663. 'm',
  1664. f2((x + rx) * k),
  1665. f2((pageHeight - (y - ly)) * k),
  1666. f2((x + lx) * k),
  1667. f2((pageHeight - (y - ry)) * k),
  1668. f2(x * k),
  1669. f2((pageHeight - (y - ry)) * k),
  1670. 'c'
  1671. ].join(' '));
  1672. out([
  1673. f2((x - lx) * k),
  1674. f2((pageHeight - (y - ry)) * k),
  1675. f2((x - rx) * k),
  1676. f2((pageHeight - (y - ly)) * k),
  1677. f2((x - rx) * k),
  1678. f2((pageHeight - y) * k),
  1679. 'c'
  1680. ].join(' '));
  1681. out([
  1682. f2((x - rx) * k),
  1683. f2((pageHeight - (y + ly)) * k),
  1684. f2((x - lx) * k),
  1685. f2((pageHeight - (y + ry)) * k),
  1686. f2(x * k),
  1687. f2((pageHeight - (y + ry)) * k),
  1688. 'c'
  1689. ].join(' '));
  1690. out([
  1691. f2((x + lx) * k),
  1692. f2((pageHeight - (y + ry)) * k),
  1693. f2((x + rx) * k),
  1694. f2((pageHeight - (y + ly)) * k),
  1695. f2((x + rx) * k),
  1696. f2((pageHeight - y) * k),
  1697. 'c'
  1698. ].join(' '));
  1699. if (style !== null) {
  1700. out(getStyle(style));
  1701. }
  1702. return this;
  1703. };
  1704. /**
  1705. * Adds an circle to PDF
  1706. *
  1707. * @param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
  1708. * @param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
  1709. * @param {Number} r Radius (in units declared at inception of PDF document)
  1710. * @param {String} style A string specifying the painting style or null. Valid styles include: 'S' [default] - stroke, 'F' - fill, and 'DF' (or 'FD') - fill then stroke. A null value postpones setting the style so that a shape may be composed using multiple method calls. The last drawing method call used to define the shape should not have a null style argument.
  1711. * @function
  1712. * @returns {jsPDF}
  1713. * @methodOf jsPDF#
  1714. * @name circle
  1715. */
  1716. API.circle = function(x, y, r, style) {
  1717. return this.ellipse(x, y, r, r, style);
  1718. };
  1719. /**
  1720. * Adds a properties to the PDF document
  1721. *
  1722. * @param {Object} A property_name-to-property_value object structure.
  1723. * @function
  1724. * @returns {jsPDF}
  1725. * @methodOf jsPDF#
  1726. * @name setProperties
  1727. */
  1728. API.setProperties = function(properties) {
  1729. // copying only those properties we can render.
  1730. for (var property in documentProperties) {
  1731. if (documentProperties.hasOwnProperty(property) &amp;&amp; properties[
  1732. property]) {
  1733. documentProperties[property] = properties[property];
  1734. }
  1735. }
  1736. return this;
  1737. };
  1738. /**
  1739. * Sets font size for upcoming text elements.
  1740. *
  1741. * @param {Number} size Font size in points.
  1742. * @function
  1743. * @returns {jsPDF}
  1744. * @methodOf jsPDF#
  1745. * @name setFontSize
  1746. */
  1747. API.setFontSize = function(size) {
  1748. activeFontSize = size;
  1749. return this;
  1750. };
  1751. /**
  1752. * Sets text font face, variant for upcoming text elements.
  1753. * See output of jsPDF.getFontList() for possible font names, styles.
  1754. *
  1755. * @param {String} fontName Font name or family. Example: "times"
  1756. * @param {String} fontStyle Font style or variant. Example: "italic"
  1757. * @function
  1758. * @returns {jsPDF}
  1759. * @methodOf jsPDF#
  1760. * @name setFont
  1761. */
  1762. API.setFont = function(fontName, fontStyle) {
  1763. activeFontKey = getFont(fontName, fontStyle);
  1764. // if font is not found, the above line blows up and we never go further
  1765. return this;
  1766. };
  1767. /**
  1768. * Switches font style or variant for upcoming text elements,
  1769. * while keeping the font face or family same.
  1770. * See output of jsPDF.getFontList() for possible font names, styles.
  1771. *
  1772. * @param {String} style Font style or variant. Example: "italic"
  1773. * @function
  1774. * @returns {jsPDF}
  1775. * @methodOf jsPDF#
  1776. * @name setFontStyle
  1777. */
  1778. API.setFontStyle = API.setFontType = function(style) {
  1779. activeFontKey = getFont(undefined, style);
  1780. // if font is not found, the above line blows up and we never go further
  1781. return this;
  1782. };
  1783. /**
  1784. * Returns an object - a tree of fontName to fontStyle relationships available to
  1785. * active PDF document.
  1786. *
  1787. * @public
  1788. * @function
  1789. * @returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
  1790. * @methodOf jsPDF#
  1791. * @name getFontList
  1792. */
  1793. API.getFontList = function() {
  1794. // TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
  1795. var list = {},
  1796. fontName, fontStyle, tmp;
  1797. for (fontName in fontmap) {
  1798. if (fontmap.hasOwnProperty(fontName)) {
  1799. list[fontName] = tmp = [];
  1800. for (fontStyle in fontmap[fontName]) {
  1801. if (fontmap[fontName].hasOwnProperty(fontStyle)) {
  1802. tmp.push(fontStyle);
  1803. }
  1804. }
  1805. }
  1806. }
  1807. return list;
  1808. };
  1809. /**
  1810. * Add a custom font.
  1811. *
  1812. * @param {String} Postscript name of the Font. Example: "Menlo-Regular"
  1813. * @param {String} Name of font-family from @font-face definition. Example: "Menlo Regular"
  1814. * @param {String} Font style. Example: "normal"
  1815. * @function
  1816. * @returns the {fontKey} (same as the internal method)
  1817. * @methodOf jsPDF#
  1818. * @name addFont
  1819. */
  1820. API.addFont = function(postScriptName, fontName, fontStyle) {
  1821. addFont(postScriptName, fontName, fontStyle, 'StandardEncoding');
  1822. };
  1823. /**
  1824. * Sets line width for upcoming lines.
  1825. *
  1826. * @param {Number} width Line width (in units declared at inception of PDF document)
  1827. * @function
  1828. * @returns {jsPDF}
  1829. * @methodOf jsPDF#
  1830. * @name setLineWidth
  1831. */
  1832. API.setLineWidth = function(width) {
  1833. out((width * k).toFixed(2) + ' w');
  1834. return this;
  1835. };
  1836. /**
  1837. * Sets the stroke color for upcoming elements.
  1838. *
  1839. * Depending on the number of arguments given, Gray, RGB, or CMYK
  1840. * color space is implied.
  1841. *
  1842. * When only ch1 is given, "Gray" color space is implied and it
  1843. * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
  1844. * if values are communicated as String types, or in range from 0 (black)
  1845. * to 255 (white) if communicated as Number type.
  1846. * The RGB-like 0-255 range is provided for backward compatibility.
  1847. *
  1848. * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
  1849. * value must be in the range from 0.00 (minimum intensity) to to 1.00
  1850. * (max intensity) if values are communicated as String types, or
  1851. * from 0 (min intensity) to to 255 (max intensity) if values are communicated
  1852. * as Number types.
  1853. * The RGB-like 0-255 range is provided for backward compatibility.
  1854. *
  1855. * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
  1856. * value must be a in the range from 0.00 (0% concentration) to to
  1857. * 1.00 (100% concentration)
  1858. *
  1859. * Because JavaScript treats fixed point numbers badly (rounds to
  1860. * floating point nearest to binary representation) it is highly advised to
  1861. * communicate the fractional numbers as String types, not JavaScript Number type.
  1862. *
  1863. * @param {Number|String} ch1 Color channel value
  1864. * @param {Number|String} ch2 Color channel value
  1865. * @param {Number|String} ch3 Color channel value
  1866. * @param {Number|String} ch4 Color channel value
  1867. *
  1868. * @function
  1869. * @returns {jsPDF}
  1870. * @methodOf jsPDF#
  1871. * @name setDrawColor
  1872. */
  1873. API.setDrawColor = function(ch1, ch2, ch3, ch4) {
  1874. var color;
  1875. if (ch2 === undefined || (ch4 === undefined &amp;&amp; ch1 === ch2 === ch3)) {
  1876. // Gray color space.
  1877. if (typeof ch1 === 'string') {
  1878. color = ch1 + ' G';
  1879. } else {
  1880. color = f2(ch1 / 255) + ' G';
  1881. }
  1882. } else if (ch4 === undefined) {
  1883. // RGB
  1884. if (typeof ch1 === 'string') {
  1885. color = [ch1, ch2, ch3, 'RG'].join(' ');
  1886. } else {
  1887. color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(
  1888. ' ');
  1889. }
  1890. } else {
  1891. // CMYK
  1892. if (typeof ch1 === 'string') {
  1893. color = [ch1, ch2, ch3, ch4, 'K'].join(' ');
  1894. } else {
  1895. color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' ');
  1896. }
  1897. }
  1898. out(color);
  1899. return this;
  1900. };
  1901. /**
  1902. * Sets the fill color for upcoming elements.
  1903. *
  1904. * Depending on the number of arguments given, Gray, RGB, or CMYK
  1905. * color space is implied.
  1906. *
  1907. * When only ch1 is given, "Gray" color space is implied and it
  1908. * must be a value in the range from 0.00 (solid black) to to 1.00 (white)
  1909. * if values are communicated as String types, or in range from 0 (black)
  1910. * to 255 (white) if communicated as Number type.
  1911. * The RGB-like 0-255 range is provided for backward compatibility.
  1912. *
  1913. * When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
  1914. * value must be in the range from 0.00 (minimum intensity) to to 1.00
  1915. * (max intensity) if values are communicated as String types, or
  1916. * from 0 (min intensity) to to 255 (max intensity) if values are communicated
  1917. * as Number types.
  1918. * The RGB-like 0-255 range is provided for backward compatibility.
  1919. *
  1920. * When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
  1921. * value must be a in the range from 0.00 (0% concentration) to to
  1922. * 1.00 (100% concentration)
  1923. *
  1924. * Because JavaScript treats fixed point numbers badly (rounds to
  1925. * floating point nearest to binary representation) it is highly advised to
  1926. * communicate the fractional numbers as String types, not JavaScript Number type.
  1927. *
  1928. * @param {Number|String} ch1 Color channel value
  1929. * @param {Number|String} ch2 Color channel value
  1930. * @param {Number|String} ch3 Color channel value
  1931. * @param {Number|String} ch4 Color channel value
  1932. *
  1933. * @function
  1934. * @returns {jsPDF}
  1935. * @methodOf jsPDF#
  1936. * @name setFillColor
  1937. */
  1938. API.setFillColor = function(ch1, ch2, ch3, ch4) {
  1939. var color;
  1940. if (ch2 === undefined || (ch4 === undefined &amp;&amp; ch1 === ch2 === ch3)) {
  1941. // Gray color space.
  1942. if (typeof ch1 === 'string') {
  1943. color = ch1 + ' g';
  1944. } else {
  1945. color = f2(ch1 / 255) + ' g';
  1946. }
  1947. } else if (ch4 === undefined || typeof ch4 === 'object') {
  1948. // RGB
  1949. if (typeof ch1 === 'string') {
  1950. color = [ch1, ch2, ch3, 'rg'].join(' ');
  1951. } else {
  1952. color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(
  1953. ' ');
  1954. }
  1955. if (ch4 &amp;&amp; ch4.a === 0) {
  1956. //TODO Implement transparency.
  1957. //WORKAROUND use white for now
  1958. color = ['255', '255', '255', 'rg'].join(' ');
  1959. }
  1960. } else {
  1961. // CMYK
  1962. if (typeof ch1 === 'string') {
  1963. color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
  1964. } else {
  1965. color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
  1966. }
  1967. }
  1968. out(color);
  1969. return this;
  1970. };
  1971. /**
  1972. * Sets the text color for upcoming elements.
  1973. * If only one, first argument is given,
  1974. * treats the value as gray-scale color value.
  1975. *
  1976. * @param {Number} r Red channel color value in range 0-255 or {String} r color value in hexadecimal, example: '#FFFFFF'
  1977. * @param {Number} g Green channel color value in range 0-255
  1978. * @param {Number} b Blue channel color value in range 0-255
  1979. * @function
  1980. * @returns {jsPDF}
  1981. * @methodOf jsPDF#
  1982. * @name setTextColor
  1983. */
  1984. API.setTextColor = function(r, g, b) {
  1985. if ((typeof r === 'string') &amp;&amp; /^#[0-9A-Fa-f]{6}$/.test(r)) {
  1986. var hex = parseInt(r.substr(1), 16);
  1987. r = (hex >> 16) &amp; 255;
  1988. g = (hex >> 8) &amp; 255;
  1989. b = (hex &amp; 255);
  1990. }
  1991. if ((r === 0 &amp;&amp; g === 0 &amp;&amp; b === 0) || (typeof g === 'undefined')) {
  1992. textColor = f3(r / 255) + ' g';
  1993. } else {
  1994. textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(
  1995. ' ');
  1996. }
  1997. return this;
  1998. };
  1999. /**
  2000. * Is an Object providing a mapping from human-readable to
  2001. * integer flag values designating the varieties of line cap
  2002. * and join styles.
  2003. *
  2004. * @returns {Object}
  2005. * @fieldOf jsPDF#
  2006. * @name CapJoinStyles
  2007. */
  2008. API.CapJoinStyles = {
  2009. 0: 0,
  2010. 'butt': 0,
  2011. 'but': 0,
  2012. 'miter': 0,
  2013. 1: 1,
  2014. 'round': 1,
  2015. 'rounded': 1,
  2016. 'circle': 1,
  2017. 2: 2,
  2018. 'projecting': 2,
  2019. 'project': 2,
  2020. 'square': 2,
  2021. 'bevel': 2
  2022. };
  2023. /**
  2024. * Sets the line cap styles
  2025. * See {jsPDF.CapJoinStyles} for variants
  2026. *
  2027. * @param {String|Number} style A string or number identifying the type of line cap
  2028. * @function
  2029. * @returns {jsPDF}
  2030. * @methodOf jsPDF#
  2031. * @name setLineCap
  2032. */
  2033. API.setLineCap = function(style) {
  2034. var id = this.CapJoinStyles[style];
  2035. if (id === undefined) {
  2036. throw new Error("Line cap style of '" + style +
  2037. "' is not recognized. See or extend .CapJoinStyles property for valid styles"
  2038. );
  2039. }
  2040. lineCapID = id;
  2041. out(id + ' J');
  2042. return this;
  2043. };
  2044. /**
  2045. * Sets the line join styles
  2046. * See {jsPDF.CapJoinStyles} for variants
  2047. *
  2048. * @param {String|Number} style A string or number identifying the type of line join
  2049. * @function
  2050. * @returns {jsPDF}
  2051. * @methodOf jsPDF#
  2052. * @name setLineJoin
  2053. */
  2054. API.setLineJoin = function(style) {
  2055. var id = this.CapJoinStyles[style];
  2056. if (id === undefined) {
  2057. throw new Error("Line join style of '" + style +
  2058. "' is not recognized. See or extend .CapJoinStyles property for valid styles"
  2059. );
  2060. }
  2061. lineJoinID = id;
  2062. out(id + ' j');
  2063. return this;
  2064. };
  2065. // Output is both an internal (for plugins) and external function
  2066. API.output = output;
  2067. /**
  2068. * Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
  2069. * @param {String} filename The filename including extension.
  2070. *
  2071. * @function
  2072. * @returns {jsPDF}
  2073. * @methodOf jsPDF#
  2074. * @name save
  2075. */
  2076. API.save = function(filename) {
  2077. API.output('save', filename);
  2078. };
  2079. // applying plugins (more methods) ON TOP of built-in API.
  2080. // this is intentional as we allow plugins to override
  2081. // built-ins
  2082. for (var plugin in jsPDF.API) {
  2083. if (jsPDF.API.hasOwnProperty(plugin)) {
  2084. if (plugin === 'events' &amp;&amp; jsPDF.API.events.length) {
  2085. (function(events, newEvents) {
  2086. // jsPDF.API.events is a JS Array of Arrays
  2087. // where each Array is a pair of event name, handler
  2088. // Events were added by plugins to the jsPDF instantiator.
  2089. // These are always added to the new instance and some ran
  2090. // during instantiation.
  2091. var eventname, handler_and_args, i;
  2092. for (i = newEvents.length - 1; i !== -1; i--) {
  2093. // subscribe takes 3 args: 'topic', function, runonce_flag
  2094. // if undefined, runonce is false.
  2095. // users can attach callback directly,
  2096. // or they can attach an array with [callback, runonce_flag]
  2097. // that's what the "apply" magic is for below.
  2098. eventname = newEvents[i][0];
  2099. handler_and_args = newEvents[i][1];
  2100. events.subscribe.apply(
  2101. events, [eventname].concat(
  2102. typeof handler_and_args === 'function' ? [
  2103. handler_and_args
  2104. ] : handler_and_args));
  2105. }
  2106. }(events, jsPDF.API.events));
  2107. } else {
  2108. API[plugin] = jsPDF.API[plugin];
  2109. }
  2110. }
  2111. }
  2112. //////////////////////////////////////////////////////
  2113. // continuing initialization of jsPDF Document object
  2114. //////////////////////////////////////////////////////
  2115. // Add the first page automatically
  2116. addFonts();
  2117. activeFontKey = 'F1';
  2118. _addPage(format, orientation);
  2119. events.publish('initialized');
  2120. return API;
  2121. }
  2122. /**
  2123. * jsPDF.API is a STATIC property of jsPDF class.
  2124. * jsPDF.API is an object you can add methods and properties to.
  2125. * The methods / properties you add will show up in new jsPDF objects.
  2126. *
  2127. * One property is prepopulated. It is the 'events' Object. Plugin authors can add topics,
  2128. * callbacks to this object. These will be reassigned to all new instances of jsPDF.
  2129. * Examples:
  2130. * jsPDF.API.events['initialized'] = function(){ 'this' is API object }
  2131. * jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
  2132. *
  2133. * @static
  2134. * @public
  2135. * @memberOf jsPDF
  2136. * @name API
  2137. *
  2138. * @example
  2139. * jsPDF.API.mymethod = function(){
  2140. * // 'this' will be ref to internal API object. see jsPDF source
  2141. * // , so you can refer to built-in methods like so:
  2142. * // this.line(....)
  2143. * // this.text(....)
  2144. * }
  2145. * var pdfdoc = new jsPDF()
  2146. * pdfdoc.mymethod() // &lt;- !!!!!!
  2147. */
  2148. jsPDF.API = {
  2149. events: []
  2150. };
  2151. jsPDF.version = "1.x-master";
  2152. if (typeof define === 'function' &amp;&amp; define.amd) {
  2153. define('jsPDF', function() {
  2154. return jsPDF;
  2155. });
  2156. } else if (typeof module !== 'undefined' &amp;&amp; module.exports) {
  2157. module.exports = jsPDF;
  2158. } else {
  2159. global.jsPDF = jsPDF;
  2160. }
  2161. return jsPDF;
  2162. }(typeof self !== "undefined" &amp;&amp; self || typeof window !== "undefined" &amp;&amp;
  2163. window || this));
  2164. </code></pre>
  2165. </article>
  2166. </section>
  2167. </div>
  2168. <br class="clear">
  2169. <footer>
  2170. 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.
  2171. </footer>
  2172. <script>prettyPrint();</script>
  2173. <script src="scripts/linenumber.js"></script>
  2174. </body>
  2175. </html>