| 9 |
lars |
1 |
/** @preserve jsPDF 0.9.0rc2 ( ${buildDate} ${commitID} )
|
|
|
2 |
Copyright (c) 2010-2012 James Hall, james@snapshotmedia.co.uk, https://github.com/MrRio/jsPDF
|
|
|
3 |
Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
|
|
|
4 |
MIT license.
|
|
|
5 |
*/
|
|
|
6 |
|
|
|
7 |
/*
|
|
|
8 |
* Permission is hereby granted, free of charge, to any person obtaining
|
|
|
9 |
* a copy of this software and associated documentation files (the
|
|
|
10 |
* "Software"), to deal in the Software without restriction, including
|
|
|
11 |
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
12 |
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
13 |
* permit persons to whom the Software is furnished to do so, subject to
|
|
|
14 |
* the following conditions:
|
|
|
15 |
*
|
|
|
16 |
* The above copyright notice and this permission notice shall be
|
|
|
17 |
* included in all copies or substantial portions of the Software.
|
|
|
18 |
*
|
|
|
19 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
20 |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
21 |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
22 |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
23 |
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
24 |
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
25 |
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
26 |
* ====================================================================
|
|
|
27 |
*/
|
|
|
28 |
|
|
|
29 |
|
|
|
30 |
/**
|
|
|
31 |
Creates new jsPDF document object instance
|
|
|
32 |
@class
|
|
|
33 |
@param orientation One of "portrait" or "landscape" (or shortcuts "p" (Default), "l")
|
|
|
34 |
@param unit Measurement unit to be used when coordinates are specified. One of "pt" (points), "mm" (Default), "cm", "in"
|
|
|
35 |
@param format One of 'a3', 'a4' (Default),'a5' ,'letter' ,'legal'
|
|
|
36 |
@returns {jsPDF}
|
|
|
37 |
@name jsPDF
|
|
|
38 |
*/
|
|
|
39 |
var jsPDF = (function () {
|
|
|
40 |
'use strict';
|
|
|
41 |
/*jslint browser:true, plusplus: true, bitwise: true, nomen: true */
|
|
|
42 |
/*global document: false, btoa, atob, zpipe, Uint8Array, ArrayBuffer, Blob, saveAs, adler32cs, Deflater */
|
|
|
43 |
|
|
|
44 |
// this will run on <=IE9, possibly some niche browsers
|
|
|
45 |
// new webkit-based, FireFox, IE10 already have native version of this.
|
|
|
46 |
if (typeof btoa === 'undefined') {
|
|
|
47 |
window.btoa = function (data) {
|
|
|
48 |
// DO NOT ADD UTF8 ENCODING CODE HERE!!!!
|
|
|
49 |
|
|
|
50 |
// UTF8 encoding encodes bytes over char code 128
|
|
|
51 |
// and, essentially, turns an 8-bit binary streams
|
|
|
52 |
// (that base64 can deal with) into 7-bit binary streams.
|
|
|
53 |
// (by default server does not know that and does not recode the data back to 8bit)
|
|
|
54 |
// You destroy your data.
|
|
|
55 |
|
|
|
56 |
// binary streams like jpeg image data etc, while stored in JavaScript strings,
|
|
|
57 |
// (which are 16bit arrays) are in 8bit format already.
|
|
|
58 |
// You do NOT need to char-encode that before base64 encoding.
|
|
|
59 |
|
|
|
60 |
// if you, by act of fate
|
|
|
61 |
// have string which has individual characters with code
|
|
|
62 |
// above 255 (pure unicode chars), encode that BEFORE you base64 here.
|
|
|
63 |
// you can use absolutely any approch there, as long as in the end,
|
|
|
64 |
// base64 gets an 8bit (char codes 0 - 255) stream.
|
|
|
65 |
// when you get it on the server after un-base64, you must
|
|
|
66 |
// UNencode it too, to get back to 16, 32bit or whatever original bin stream.
|
|
|
67 |
|
|
|
68 |
// Note, Yes, JavaScript strings are, in most cases UCS-2 -
|
|
|
69 |
// 16-bit character arrays. This does not mean, however,
|
|
|
70 |
// that you always have to UTF8 it before base64.
|
|
|
71 |
// it means that if you have actual characters anywhere in
|
|
|
72 |
// that string that have char code above 255, you need to
|
|
|
73 |
// recode *entire* string from 16-bit (or 32bit) to 8-bit array.
|
|
|
74 |
// You can do binary split to UTF16 (BE or LE)
|
|
|
75 |
// you can do utf8, you can split the thing by hand and prepend BOM to it,
|
|
|
76 |
// but whatever you do, make sure you mirror the opposite on
|
|
|
77 |
// the server. If server does not expect to post-process un-base64
|
|
|
78 |
// 8-bit binary stream, think very very hard about messing around with encoding.
|
|
|
79 |
|
|
|
80 |
// so, long story short:
|
|
|
81 |
// DO NOT ADD UTF8 ENCODING CODE HERE!!!!
|
|
|
82 |
|
|
|
83 |
/* @preserve
|
|
|
84 |
====================================================================
|
|
|
85 |
base64 encoder
|
|
|
86 |
MIT, GPL
|
|
|
87 |
|
|
|
88 |
version: 1109.2015
|
|
|
89 |
discuss at: http://phpjs.org/functions/base64_encode
|
|
|
90 |
+ original by: Tyler Akins (http://rumkin.com)
|
|
|
91 |
+ improved by: Bayron Guevara
|
|
|
92 |
+ improved by: Thunder.m
|
|
|
93 |
+ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
|
94 |
+ bugfixed by: Pellentesque Malesuada
|
|
|
95 |
+ improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
|
96 |
+ improved by: Rafal Kukawski (http://kukawski.pl)
|
|
|
97 |
+ Daniel Dotsenko, Willow Systems Corp, willow-systems.com
|
|
|
98 |
====================================================================
|
|
|
99 |
*/
|
|
|
100 |
|
|
|
101 |
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
|
|
102 |
b64a = b64.split(''),
|
|
|
103 |
o1,
|
|
|
104 |
o2,
|
|
|
105 |
o3,
|
|
|
106 |
h1,
|
|
|
107 |
h2,
|
|
|
108 |
h3,
|
|
|
109 |
h4,
|
|
|
110 |
bits,
|
|
|
111 |
i = 0,
|
|
|
112 |
ac = 0,
|
|
|
113 |
enc = "",
|
|
|
114 |
tmp_arr = [],
|
|
|
115 |
r;
|
|
|
116 |
|
|
|
117 |
do { // pack three octets into four hexets
|
|
|
118 |
o1 = data.charCodeAt(i++);
|
|
|
119 |
o2 = data.charCodeAt(i++);
|
|
|
120 |
o3 = data.charCodeAt(i++);
|
|
|
121 |
|
|
|
122 |
bits = o1 << 16 | o2 << 8 | o3;
|
|
|
123 |
|
|
|
124 |
h1 = bits >> 18 & 0x3f;
|
|
|
125 |
h2 = bits >> 12 & 0x3f;
|
|
|
126 |
h3 = bits >> 6 & 0x3f;
|
|
|
127 |
h4 = bits & 0x3f;
|
|
|
128 |
|
|
|
129 |
// use hexets to index into b64, and append result to encoded string
|
|
|
130 |
tmp_arr[ac++] = b64a[h1] + b64a[h2] + b64a[h3] + b64a[h4];
|
|
|
131 |
} while (i < data.length);
|
|
|
132 |
|
|
|
133 |
enc = tmp_arr.join('');
|
|
|
134 |
r = data.length % 3;
|
|
|
135 |
return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
|
|
|
136 |
// end of base64 encoder MIT, GPL
|
|
|
137 |
};
|
|
|
138 |
}
|
|
|
139 |
|
|
|
140 |
if (typeof atob === 'undefined') {
|
|
|
141 |
window.atob = function (data) {
|
|
|
142 |
// http://kevin.vanzonneveld.net
|
|
|
143 |
// + original by: Tyler Akins (http://rumkin.com)
|
|
|
144 |
// + improved by: Thunder.m
|
|
|
145 |
// + input by: Aman Gupta
|
|
|
146 |
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
|
147 |
// + bugfixed by: Onno Marsman
|
|
|
148 |
// + bugfixed by: Pellentesque Malesuada
|
|
|
149 |
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
|
150 |
// + input by: Brett Zamir (http://brett-zamir.me)
|
|
|
151 |
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
|
152 |
// * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
|
|
|
153 |
// * returns 1: 'Kevin van Zonneveld'
|
|
|
154 |
// mozilla has this native
|
|
|
155 |
// - but breaks in 2.0.0.12!
|
|
|
156 |
//if (typeof this.window['atob'] == 'function') {
|
|
|
157 |
// return atob(data);
|
|
|
158 |
//}
|
|
|
159 |
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
|
|
160 |
o1,
|
|
|
161 |
o2,
|
|
|
162 |
o3,
|
|
|
163 |
h1,
|
|
|
164 |
h2,
|
|
|
165 |
h3,
|
|
|
166 |
h4,
|
|
|
167 |
bits,
|
|
|
168 |
i = 0,
|
|
|
169 |
ac = 0,
|
|
|
170 |
dec = "",
|
|
|
171 |
tmp_arr = [];
|
|
|
172 |
|
|
|
173 |
if (!data) {
|
|
|
174 |
return data;
|
|
|
175 |
}
|
|
|
176 |
|
|
|
177 |
data += '';
|
|
|
178 |
|
|
|
179 |
do { // unpack four hexets into three octets using index points in b64
|
|
|
180 |
h1 = b64.indexOf(data.charAt(i++));
|
|
|
181 |
h2 = b64.indexOf(data.charAt(i++));
|
|
|
182 |
h3 = b64.indexOf(data.charAt(i++));
|
|
|
183 |
h4 = b64.indexOf(data.charAt(i++));
|
|
|
184 |
|
|
|
185 |
bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
|
|
|
186 |
|
|
|
187 |
o1 = bits >> 16 & 0xff;
|
|
|
188 |
o2 = bits >> 8 & 0xff;
|
|
|
189 |
o3 = bits & 0xff;
|
|
|
190 |
|
|
|
191 |
if (h3 === 64) {
|
|
|
192 |
tmp_arr[ac++] = String.fromCharCode(o1);
|
|
|
193 |
} else if (h4 === 64) {
|
|
|
194 |
tmp_arr[ac++] = String.fromCharCode(o1, o2);
|
|
|
195 |
} else {
|
|
|
196 |
tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
|
|
|
197 |
}
|
|
|
198 |
} while (i < data.length);
|
|
|
199 |
dec = tmp_arr.join('');
|
|
|
200 |
return dec;
|
|
|
201 |
};
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
var getObjectLength = typeof Object.keys === 'function' ?
|
|
|
205 |
function (object) {
|
|
|
206 |
return Object.keys(object).length;
|
|
|
207 |
} :
|
|
|
208 |
function (object) {
|
|
|
209 |
var i = 0, e;
|
|
|
210 |
for (e in object) {
|
|
|
211 |
if (object.hasOwnProperty(e)) {
|
|
|
212 |
i++;
|
|
|
213 |
}
|
|
|
214 |
}
|
|
|
215 |
return i;
|
|
|
216 |
},
|
|
|
217 |
|
|
|
218 |
/**
|
|
|
219 |
PubSub implementation
|
|
|
220 |
|
|
|
221 |
@class
|
|
|
222 |
@name PubSub
|
|
|
223 |
*/
|
|
|
224 |
PubSub = function (context) {
|
|
|
225 |
/** @preserve
|
|
|
226 |
-----------------------------------------------------------------------------------------------
|
|
|
227 |
JavaScript PubSub library
|
|
|
228 |
2012 (c) ddotsenko@willowsystems.com
|
|
|
229 |
based on Peter Higgins (dante@dojotoolkit.org)
|
|
|
230 |
Loosely based on Dojo publish/subscribe API, limited in scope. Rewritten blindly.
|
|
|
231 |
Original is (c) Dojo Foundation 2004-2010. Released under either AFL or new BSD, see:
|
|
|
232 |
http://dojofoundation.org/license for more information.
|
|
|
233 |
-----------------------------------------------------------------------------------------------
|
|
|
234 |
*/
|
|
|
235 |
/**
|
|
|
236 |
@private
|
|
|
237 |
@fieldOf PubSub
|
|
|
238 |
*/
|
|
|
239 |
this.topics = {};
|
|
|
240 |
/**
|
|
|
241 |
Stores what will be `this` within the callback functions.
|
|
|
242 |
|
|
|
243 |
@private
|
|
|
244 |
@fieldOf PubSub#
|
|
|
245 |
*/
|
|
|
246 |
this.context = context;
|
|
|
247 |
/**
|
|
|
248 |
Allows caller to emit an event and pass arguments to event listeners.
|
|
|
249 |
@public
|
|
|
250 |
@function
|
|
|
251 |
@param topic {String} Name of the channel on which to voice this event
|
|
|
252 |
@param args Any number of arguments you want to pass to the listeners of this event.
|
|
|
253 |
@methodOf PubSub#
|
|
|
254 |
@name publish
|
|
|
255 |
*/
|
|
|
256 |
this.publish = function (topic, args) {
|
|
|
257 |
if (this.topics[topic]) {
|
|
|
258 |
var currentTopic = this.topics[topic],
|
|
|
259 |
toremove = [],
|
|
|
260 |
fn,
|
|
|
261 |
i,
|
|
|
262 |
l,
|
|
|
263 |
pair,
|
|
|
264 |
emptyFunc = function () {};
|
|
|
265 |
args = Array.prototype.slice.call(arguments, 1);
|
|
|
266 |
for (i = 0, l = currentTopic.length; i < l; i++) {
|
|
|
267 |
pair = currentTopic[i]; // this is a [function, once_flag] array
|
|
|
268 |
fn = pair[0];
|
|
|
269 |
if (pair[1]) { /* 'run once' flag set */
|
|
|
270 |
pair[0] = emptyFunc;
|
|
|
271 |
toremove.push(i);
|
|
|
272 |
}
|
|
|
273 |
fn.apply(this.context, args);
|
|
|
274 |
}
|
|
|
275 |
for (i = 0, l = toremove.length; i < l; i++) {
|
|
|
276 |
currentTopic.splice(toremove[i], 1);
|
|
|
277 |
}
|
|
|
278 |
}
|
|
|
279 |
};
|
|
|
280 |
/**
|
|
|
281 |
Allows listener code to subscribe to channel and be called when data is available
|
|
|
282 |
@public
|
|
|
283 |
@function
|
|
|
284 |
@param topic {String} Name of the channel on which to voice this event
|
|
|
285 |
@param callback {Function} Executable (function pointer) that will be ran when event is voiced on this channel.
|
|
|
286 |
@param once {Boolean} (optional. False by default) Flag indicating if the function is to be triggered only once.
|
|
|
287 |
@returns {Object} A token object that cen be used for unsubscribing.
|
|
|
288 |
@methodOf PubSub#
|
|
|
289 |
@name subscribe
|
|
|
290 |
*/
|
|
|
291 |
this.subscribe = function (topic, callback, once) {
|
|
|
292 |
if (!this.topics[topic]) {
|
|
|
293 |
this.topics[topic] = [[callback, once]];
|
|
|
294 |
} else {
|
|
|
295 |
this.topics[topic].push([callback, once]);
|
|
|
296 |
}
|
|
|
297 |
return {
|
|
|
298 |
"topic": topic,
|
|
|
299 |
"callback": callback
|
|
|
300 |
};
|
|
|
301 |
};
|
|
|
302 |
/**
|
|
|
303 |
Allows listener code to unsubscribe from a channel
|
|
|
304 |
@public
|
|
|
305 |
@function
|
|
|
306 |
@param token {Object} A token object that was returned by `subscribe` method
|
|
|
307 |
@methodOf PubSub#
|
|
|
308 |
@name unsubscribe
|
|
|
309 |
*/
|
|
|
310 |
this.unsubscribe = function (token) {
|
|
|
311 |
if (this.topics[token.topic]) {
|
|
|
312 |
var currentTopic = this.topics[token.topic], i, l;
|
|
|
313 |
|
|
|
314 |
for (i = 0, l = currentTopic.length; i < l; i++) {
|
|
|
315 |
if (currentTopic[i][0] === token.callback) {
|
|
|
316 |
currentTopic.splice(i, 1);
|
|
|
317 |
}
|
|
|
318 |
}
|
|
|
319 |
}
|
|
|
320 |
};
|
|
|
321 |
};
|
|
|
322 |
|
|
|
323 |
|
|
|
324 |
/**
|
|
|
325 |
@constructor
|
|
|
326 |
@private
|
|
|
327 |
*/
|
|
|
328 |
function jsPDF(orientation, unit, format, compressPdf) { /** String orientation, String unit, String format, Boolean compressed */
|
|
|
329 |
|
|
|
330 |
// Default parameter values
|
|
|
331 |
if (typeof orientation === 'undefined') {
|
|
|
332 |
orientation = 'p';
|
|
|
333 |
} else {
|
|
|
334 |
orientation = orientation.toString().toLowerCase();
|
|
|
335 |
}
|
|
|
336 |
if (typeof unit === 'undefined') { unit = 'mm'; }
|
|
|
337 |
if (typeof format === 'undefined') { format = 'a4'; }
|
|
|
338 |
if (typeof compressPdf === 'undefined' && typeof zpipe === 'undefined') { compressPdf = false; }
|
|
|
339 |
|
|
|
340 |
var format_as_string = format.toString().toLowerCase(),
|
|
|
341 |
version = '0.9.0rc2',
|
|
|
342 |
content = [],
|
|
|
343 |
content_length = 0,
|
|
|
344 |
compress = compressPdf,
|
|
|
345 |
pdfVersion = '1.3', // PDF Version
|
|
|
346 |
pageFormats = { // Size in pt of various paper formats
|
|
|
347 |
'a3': [841.89, 1190.55],
|
|
|
348 |
'a4': [595.28, 841.89],
|
|
|
349 |
'a5': [420.94, 595.28],
|
|
|
350 |
'letter': [612, 792],
|
|
|
351 |
'legal': [612, 1008]
|
|
|
352 |
},
|
|
|
353 |
textColor = '0 g',
|
|
|
354 |
drawColor = '0 G',
|
|
|
355 |
page = 0,
|
|
|
356 |
pages = [],
|
|
|
357 |
objectNumber = 2, // 'n' Current object number
|
|
|
358 |
outToPages = false, // switches where out() prints. outToPages true = push to pages obj. outToPages false = doc builder content
|
|
|
359 |
offsets = [], // List of offsets. Activated and reset by buildDocument(). Pupulated by various calls buildDocument makes.
|
|
|
360 |
fonts = {}, // collection of font objects, where key is fontKey - a dynamically created label for a given font.
|
|
|
361 |
fontmap = {}, // mapping structure fontName > fontStyle > font key - performance layer. See addFont()
|
|
|
362 |
activeFontSize = 16,
|
|
|
363 |
activeFontKey, // will be string representing the KEY of the font as combination of fontName + fontStyle
|
|
|
364 |
lineWidth = 0.200025, // 2mm
|
|
|
365 |
pageHeight,
|
|
|
366 |
pageWidth,
|
|
|
367 |
k, // Scale factor
|
|
|
368 |
documentProperties = {'title': '', 'subject': '', 'author': '', 'keywords': '', 'creator': ''},
|
|
|
369 |
lineCapID = 0,
|
|
|
370 |
lineJoinID = 0,
|
|
|
371 |
API = {},
|
|
|
372 |
events = new PubSub(API),
|
|
|
373 |
tmp,
|
|
|
374 |
plugin,
|
|
|
375 |
/////////////////////
|
|
|
376 |
// Private functions
|
|
|
377 |
/////////////////////
|
|
|
378 |
// simplified (speedier) replacement for sprintf's %.2f conversion
|
|
|
379 |
f2 = function (number) {
|
|
|
380 |
return number.toFixed(2);
|
|
|
381 |
},
|
|
|
382 |
// simplified (speedier) replacement for sprintf's %.3f conversion
|
|
|
383 |
f3 = function (number) {
|
|
|
384 |
return number.toFixed(3);
|
|
|
385 |
},
|
|
|
386 |
// simplified (speedier) replacement for sprintf's %02d
|
|
|
387 |
padd2 = function (number) {
|
|
|
388 |
var n = (number).toFixed(0);
|
|
|
389 |
if (number < 10) {
|
|
|
390 |
return '0' + n;
|
|
|
391 |
} else {
|
|
|
392 |
return n;
|
|
|
393 |
}
|
|
|
394 |
},
|
|
|
395 |
// simplified (speedier) replacement for sprintf's %02d
|
|
|
396 |
padd10 = function (number) {
|
|
|
397 |
var n = (number).toFixed(0);
|
|
|
398 |
if (n.length < 10) {
|
|
|
399 |
return new Array( 11 - n.length ).join('0') + n;
|
|
|
400 |
} else {
|
|
|
401 |
return n;
|
|
|
402 |
}
|
|
|
403 |
},
|
|
|
404 |
out = function (string) {
|
|
|
405 |
if (outToPages) { /* set by beginPage */
|
|
|
406 |
pages[page].push(string);
|
|
|
407 |
} else {
|
|
|
408 |
content.push(string);
|
|
|
409 |
content_length += string.length + 1; // +1 is for '\n' that will be used to join contents of content
|
|
|
410 |
}
|
|
|
411 |
},
|
|
|
412 |
newObject = function () {
|
|
|
413 |
// Begin a new object
|
|
|
414 |
objectNumber++;
|
|
|
415 |
offsets[objectNumber] = content_length;
|
|
|
416 |
out(objectNumber + ' 0 obj');
|
|
|
417 |
return objectNumber;
|
|
|
418 |
},
|
|
|
419 |
putStream = function (str) {
|
|
|
420 |
out('stream');
|
|
|
421 |
out(str);
|
|
|
422 |
out('endstream');
|
|
|
423 |
},
|
|
|
424 |
wPt,
|
|
|
425 |
hPt,
|
|
|
426 |
kids,
|
|
|
427 |
i,
|
|
|
428 |
putPages = function () {
|
|
|
429 |
wPt = pageWidth * k;
|
|
|
430 |
hPt = pageHeight * k;
|
|
|
431 |
|
|
|
432 |
// outToPages = false as set in endDocument(). out() writes to content.
|
|
|
433 |
|
|
|
434 |
var n, p, arr, uint, i, deflater, adler32;
|
|
|
435 |
for (n = 1; n <= page; n++) {
|
|
|
436 |
newObject();
|
|
|
437 |
out('<</Type /Page');
|
|
|
438 |
out('/Parent 1 0 R');
|
|
|
439 |
out('/Resources 2 0 R');
|
|
|
440 |
out('/Contents ' + (objectNumber + 1) + ' 0 R>>');
|
|
|
441 |
out('endobj');
|
|
|
442 |
|
|
|
443 |
// Page content
|
|
|
444 |
p = pages[n].join('\n');
|
|
|
445 |
newObject();
|
|
|
446 |
if (compress) {
|
|
|
447 |
arr = [];
|
|
|
448 |
for (i = 0; i < p.length; ++i) {
|
|
|
449 |
arr[i] = p.charCodeAt(i);
|
|
|
450 |
}
|
|
|
451 |
adler32 = adler32cs.from(p);
|
|
|
452 |
deflater = new Deflater(6);
|
|
|
453 |
deflater.append(new Uint8Array(arr));
|
|
|
454 |
p = deflater.flush();
|
|
|
455 |
arr = [new Uint8Array([120, 156]), new Uint8Array(p),
|
|
|
456 |
new Uint8Array([adler32 & 0xFF, (adler32 >> 8) & 0xFF, (adler32 >> 16) & 0xFF, (adler32 >> 24) & 0xFF])];
|
|
|
457 |
p = '';
|
|
|
458 |
for (i in arr) {
|
|
|
459 |
if (arr.hasOwnProperty(i)) {
|
|
|
460 |
p += String.fromCharCode.apply(null, arr[i]);
|
|
|
461 |
}
|
|
|
462 |
}
|
|
|
463 |
out('<</Length ' + p.length + ' /Filter [/FlateDecode]>>');
|
|
|
464 |
} else {
|
|
|
465 |
out('<</Length ' + p.length + '>>');
|
|
|
466 |
}
|
|
|
467 |
putStream(p);
|
|
|
468 |
out('endobj');
|
|
|
469 |
}
|
|
|
470 |
offsets[1] = content_length;
|
|
|
471 |
out('1 0 obj');
|
|
|
472 |
out('<</Type /Pages');
|
|
|
473 |
kids = '/Kids [';
|
|
|
474 |
for (i = 0; i < page; i++) {
|
|
|
475 |
kids += (3 + 2 * i) + ' 0 R ';
|
|
|
476 |
}
|
|
|
477 |
out(kids + ']');
|
|
|
478 |
out('/Count ' + page);
|
|
|
479 |
out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
|
|
|
480 |
out('>>');
|
|
|
481 |
out('endobj');
|
|
|
482 |
},
|
|
|
483 |
putFont = function (font) {
|
|
|
484 |
font.objectNumber = newObject();
|
|
|
485 |
out('<</BaseFont/' + font.PostScriptName + '/Type/Font');
|
|
|
486 |
if (typeof font.encoding === 'string') {
|
|
|
487 |
out('/Encoding/' + font.encoding);
|
|
|
488 |
}
|
|
|
489 |
out('/Subtype/Type1>>');
|
|
|
490 |
out('endobj');
|
|
|
491 |
},
|
|
|
492 |
putFonts = function () {
|
|
|
493 |
var fontKey;
|
|
|
494 |
for (fontKey in fonts) {
|
|
|
495 |
if (fonts.hasOwnProperty(fontKey)) {
|
|
|
496 |
putFont(fonts[fontKey]);
|
|
|
497 |
}
|
|
|
498 |
}
|
|
|
499 |
},
|
|
|
500 |
putXobjectDict = function () {
|
|
|
501 |
// Loop through images, or other data objects
|
|
|
502 |
events.publish('putXobjectDict');
|
|
|
503 |
},
|
|
|
504 |
putResourceDictionary = function () {
|
|
|
505 |
out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
|
|
|
506 |
out('/Font <<');
|
|
|
507 |
// Do this for each font, the '1' bit is the index of the font
|
|
|
508 |
var fontKey;
|
|
|
509 |
for (fontKey in fonts) {
|
|
|
510 |
if (fonts.hasOwnProperty(fontKey)) {
|
|
|
511 |
out('/' + fontKey + ' ' + fonts[fontKey].objectNumber + ' 0 R');
|
|
|
512 |
}
|
|
|
513 |
}
|
|
|
514 |
out('>>');
|
|
|
515 |
out('/XObject <<');
|
|
|
516 |
putXobjectDict();
|
|
|
517 |
out('>>');
|
|
|
518 |
},
|
|
|
519 |
putResources = function () {
|
|
|
520 |
putFonts();
|
|
|
521 |
events.publish('putResources');
|
|
|
522 |
// Resource dictionary
|
|
|
523 |
offsets[2] = content_length;
|
|
|
524 |
out('2 0 obj');
|
|
|
525 |
out('<<');
|
|
|
526 |
putResourceDictionary();
|
|
|
527 |
out('>>');
|
|
|
528 |
out('endobj');
|
|
|
529 |
events.publish('postPutResources');
|
|
|
530 |
},
|
|
|
531 |
addToFontDictionary = function (fontKey, fontName, fontStyle) {
|
|
|
532 |
// this is mapping structure for quick font key lookup.
|
|
|
533 |
// returns the KEY of the font (ex: "F1") for a given pair of font name and type (ex: "Arial". "Italic")
|
|
|
534 |
var undef;
|
|
|
535 |
if (fontmap[fontName] === undef) {
|
|
|
536 |
fontmap[fontName] = {}; // fontStyle is a var interpreted and converted to appropriate string. don't wrap in quotes.
|
|
|
537 |
}
|
|
|
538 |
fontmap[fontName][fontStyle] = fontKey;
|
|
|
539 |
},
|
|
|
540 |
/**
|
|
|
541 |
FontObject describes a particular font as member of an instnace of jsPDF
|
|
|
542 |
|
|
|
543 |
It's a collection of properties like 'id' (to be used in PDF stream),
|
|
|
544 |
'fontName' (font's family name), 'fontStyle' (font's style variant label)
|
|
|
545 |
|
|
|
546 |
@class
|
|
|
547 |
@public
|
|
|
548 |
@property id {String} PDF-document-instance-specific label assinged to the font.
|
|
|
549 |
@property PostScriptName {String} PDF specification full name for the font
|
|
|
550 |
@property encoding {Object} Encoding_name-to-Font_metrics_object mapping.
|
|
|
551 |
@name FontObject
|
|
|
552 |
*/
|
|
|
553 |
FontObject = {},
|
|
|
554 |
addFont = function (PostScriptName, fontName, fontStyle, encoding) {
|
|
|
555 |
var fontKey = 'F' + (getObjectLength(fonts) + 1).toString(10),
|
|
|
556 |
// This is FontObject
|
|
|
557 |
font = fonts[fontKey] = {
|
|
|
558 |
'id': fontKey,
|
|
|
559 |
// , 'objectNumber': will be set by putFont()
|
|
|
560 |
'PostScriptName': PostScriptName,
|
|
|
561 |
'fontName': fontName,
|
|
|
562 |
'fontStyle': fontStyle,
|
|
|
563 |
'encoding': encoding,
|
|
|
564 |
'metadata': {}
|
|
|
565 |
};
|
|
|
566 |
|
|
|
567 |
addToFontDictionary(fontKey, fontName, fontStyle);
|
|
|
568 |
|
|
|
569 |
events.publish('addFont', font);
|
|
|
570 |
|
|
|
571 |
return fontKey;
|
|
|
572 |
},
|
|
|
573 |
addFonts = function () {
|
|
|
574 |
|
|
|
575 |
var HELVETICA = "helvetica",
|
|
|
576 |
TIMES = "times",
|
|
|
577 |
COURIER = "courier",
|
|
|
578 |
NORMAL = "normal",
|
|
|
579 |
BOLD = "bold",
|
|
|
580 |
ITALIC = "italic",
|
|
|
581 |
BOLD_ITALIC = "bolditalic",
|
|
|
582 |
encoding = 'StandardEncoding',
|
|
|
583 |
standardFonts = [
|
|
|
584 |
['Helvetica', HELVETICA, NORMAL],
|
|
|
585 |
['Helvetica-Bold', HELVETICA, BOLD],
|
|
|
586 |
['Helvetica-Oblique', HELVETICA, ITALIC],
|
|
|
587 |
['Helvetica-BoldOblique', HELVETICA, BOLD_ITALIC],
|
|
|
588 |
['Courier', COURIER, NORMAL],
|
|
|
589 |
['Courier-Bold', COURIER, BOLD],
|
|
|
590 |
['Courier-Oblique', COURIER, ITALIC],
|
|
|
591 |
['Courier-BoldOblique', COURIER, BOLD_ITALIC],
|
|
|
592 |
['Times-Roman', TIMES, NORMAL],
|
|
|
593 |
['Times-Bold', TIMES, BOLD],
|
|
|
594 |
['Times-Italic', TIMES, ITALIC],
|
|
|
595 |
['Times-BoldItalic', TIMES, BOLD_ITALIC]
|
|
|
596 |
],
|
|
|
597 |
i,
|
|
|
598 |
l,
|
|
|
599 |
fontKey,
|
|
|
600 |
parts;
|
|
|
601 |
for (i = 0, l = standardFonts.length; i < l; i++) {
|
|
|
602 |
fontKey = addFont(
|
|
|
603 |
standardFonts[i][0],
|
|
|
604 |
standardFonts[i][1],
|
|
|
605 |
standardFonts[i][2],
|
|
|
606 |
encoding
|
|
|
607 |
);
|
|
|
608 |
|
|
|
609 |
// adding aliases for standard fonts, this time matching the capitalization
|
|
|
610 |
parts = standardFonts[i][0].split('-');
|
|
|
611 |
addToFontDictionary(fontKey, parts[0], parts[1] || '');
|
|
|
612 |
}
|
|
|
613 |
|
|
|
614 |
events.publish('addFonts', {'fonts': fonts, 'dictionary': fontmap});
|
|
|
615 |
},
|
|
|
616 |
/**
|
|
|
617 |
|
|
|
618 |
@public
|
|
|
619 |
@function
|
|
|
620 |
@param text {String}
|
|
|
621 |
@param flags {Object} Encoding flags.
|
|
|
622 |
@returns {String} Encoded string
|
|
|
623 |
*/
|
|
|
624 |
to8bitStream = function (text, flags) {
|
|
|
625 |
/* PDF 1.3 spec:
|
|
|
626 |
"For text strings encoded in Unicode, the first two bytes must be 254 followed by
|
|
|
627 |
255, representing the Unicode byte order marker, U+FEFF. (This sequence conflicts
|
|
|
628 |
with the PDFDocEncoding character sequence thorn ydieresis, which is unlikely
|
|
|
629 |
to be a meaningful beginning of a word or phrase.) The remainder of the
|
|
|
630 |
string consists of Unicode character codes, according to the UTF-16 encoding
|
|
|
631 |
specified in the Unicode standard, version 2.0. Commonly used Unicode values
|
|
|
632 |
are represented as 2 bytes per character, with the high-order byte appearing first
|
|
|
633 |
in the string."
|
|
|
634 |
|
|
|
635 |
In other words, if there are chars in a string with char code above 255, we
|
|
|
636 |
recode the string to UCS2 BE - string doubles in length and BOM is prepended.
|
|
|
637 |
|
|
|
638 |
HOWEVER!
|
|
|
639 |
Actual *content* (body) text (as opposed to strings used in document properties etc)
|
|
|
640 |
does NOT expect BOM. There, it is treated as a literal GID (Glyph ID)
|
|
|
641 |
|
|
|
642 |
Because of Adobe's focus on "you subset your fonts!" you are not supposed to have
|
|
|
643 |
a font that maps directly Unicode (UCS2 / UTF16BE) code to font GID, but you could
|
|
|
644 |
fudge it with "Identity-H" encoding and custom CIDtoGID map that mimics Unicode
|
|
|
645 |
code page. There, however, all characters in the stream are treated as GIDs,
|
|
|
646 |
including BOM, which is the reason we need to skip BOM in content text (i.e. that
|
|
|
647 |
that is tied to a font).
|
|
|
648 |
|
|
|
649 |
To signal this "special" PDFEscape / to8bitStream handling mode,
|
|
|
650 |
API.text() function sets (unless you overwrite it with manual values
|
|
|
651 |
given to API.text(.., flags) )
|
|
|
652 |
flags.autoencode = true
|
|
|
653 |
flags.noBOM = true
|
|
|
654 |
|
|
|
655 |
*/
|
|
|
656 |
|
|
|
657 |
/*
|
|
|
658 |
`flags` properties relied upon:
|
|
|
659 |
.sourceEncoding = string with encoding label.
|
|
|
660 |
"Unicode" by default. = encoding of the incoming text.
|
|
|
661 |
pass some non-existing encoding name
|
|
|
662 |
(ex: 'Do not touch my strings! I know what I am doing.')
|
|
|
663 |
to make encoding code skip the encoding step.
|
|
|
664 |
.outputEncoding = Either valid PDF encoding name
|
|
|
665 |
(must be supported by jsPDF font metrics, otherwise no encoding)
|
|
|
666 |
or a JS object, where key = sourceCharCode, value = outputCharCode
|
|
|
667 |
missing keys will be treated as: sourceCharCode === outputCharCode
|
|
|
668 |
.noBOM
|
|
|
669 |
See comment higher above for explanation for why this is important
|
|
|
670 |
.autoencode
|
|
|
671 |
See comment higher above for explanation for why this is important
|
|
|
672 |
*/
|
|
|
673 |
|
|
|
674 |
var i, l, undef, sourceEncoding, encodingBlock, outputEncoding, newtext, isUnicode, ch, bch;
|
|
|
675 |
|
|
|
676 |
if (flags === undef) {
|
|
|
677 |
flags = {};
|
|
|
678 |
}
|
|
|
679 |
|
|
|
680 |
sourceEncoding = flags.sourceEncoding ? sourceEncoding : 'Unicode';
|
|
|
681 |
|
|
|
682 |
outputEncoding = flags.outputEncoding;
|
|
|
683 |
|
|
|
684 |
// This 'encoding' section relies on font metrics format
|
|
|
685 |
// attached to font objects by, among others,
|
|
|
686 |
// "Willow Systems' standard_font_metrics plugin"
|
|
|
687 |
// see jspdf.plugin.standard_font_metrics.js for format
|
|
|
688 |
// of the font.metadata.encoding Object.
|
|
|
689 |
// It should be something like
|
|
|
690 |
// .encoding = {'codePages':['WinANSI....'], 'WinANSI...':{code:code, ...}}
|
|
|
691 |
// .widths = {0:width, code:width, ..., 'fof':divisor}
|
|
|
692 |
// .kerning = {code:{previous_char_code:shift, ..., 'fof':-divisor},...}
|
|
|
693 |
if ((flags.autoencode || outputEncoding) &&
|
|
|
694 |
fonts[activeFontKey].metadata &&
|
|
|
695 |
fonts[activeFontKey].metadata[sourceEncoding] &&
|
|
|
696 |
fonts[activeFontKey].metadata[sourceEncoding].encoding
|
|
|
697 |
) {
|
|
|
698 |
encodingBlock = fonts[activeFontKey].metadata[sourceEncoding].encoding;
|
|
|
699 |
|
|
|
700 |
// each font has default encoding. Some have it clearly defined.
|
|
|
701 |
if (!outputEncoding && fonts[activeFontKey].encoding) {
|
|
|
702 |
outputEncoding = fonts[activeFontKey].encoding;
|
|
|
703 |
}
|
|
|
704 |
|
|
|
705 |
// Hmmm, the above did not work? Let's try again, in different place.
|
|
|
706 |
if (!outputEncoding && encodingBlock.codePages) {
|
|
|
707 |
outputEncoding = encodingBlock.codePages[0]; // let's say, first one is the default
|
|
|
708 |
}
|
|
|
709 |
|
|
|
710 |
if (typeof outputEncoding === 'string') {
|
|
|
711 |
outputEncoding = encodingBlock[outputEncoding];
|
|
|
712 |
}
|
|
|
713 |
// we want output encoding to be a JS Object, where
|
|
|
714 |
// key = sourceEncoding's character code and
|
|
|
715 |
// value = outputEncoding's character code.
|
|
|
716 |
if (outputEncoding) {
|
|
|
717 |
isUnicode = false;
|
|
|
718 |
newtext = [];
|
|
|
719 |
for (i = 0, l = text.length; i < l; i++) {
|
|
|
720 |
ch = outputEncoding[text.charCodeAt(i)];
|
|
|
721 |
if (ch) {
|
|
|
722 |
newtext.push(
|
|
|
723 |
String.fromCharCode(ch)
|
|
|
724 |
);
|
|
|
725 |
} else {
|
|
|
726 |
newtext.push(
|
|
|
727 |
text[i]
|
|
|
728 |
);
|
|
|
729 |
}
|
|
|
730 |
|
|
|
731 |
// since we are looping over chars anyway, might as well
|
|
|
732 |
// check for residual unicodeness
|
|
|
733 |
if (newtext[i].charCodeAt(0) >> 8) { /* more than 255 */
|
|
|
734 |
isUnicode = true;
|
|
|
735 |
}
|
|
|
736 |
}
|
|
|
737 |
text = newtext.join('');
|
|
|
738 |
}
|
|
|
739 |
}
|
|
|
740 |
|
|
|
741 |
i = text.length;
|
|
|
742 |
// isUnicode may be set to false above. Hence the triple-equal to undefined
|
|
|
743 |
while (isUnicode === undef && i !== 0) {
|
|
|
744 |
if (text.charCodeAt(i - 1) >> 8) { /* more than 255 */
|
|
|
745 |
isUnicode = true;
|
|
|
746 |
}
|
|
|
747 |
i--;
|
|
|
748 |
}
|
|
|
749 |
if (!isUnicode) {
|
|
|
750 |
return text;
|
|
|
751 |
} else {
|
|
|
752 |
newtext = flags.noBOM ? [] : [254, 255];
|
|
|
753 |
for (i = 0, l = text.length; i < l; i++) {
|
|
|
754 |
ch = text.charCodeAt(i);
|
|
|
755 |
bch = ch >> 8; // divide by 256
|
|
|
756 |
if (bch >> 8) { /* something left after dividing by 256 second time */
|
|
|
757 |
throw new Error("Character at position " + i.toString(10) + " of string '" + text + "' exceeds 16bits. Cannot be encoded into UCS-2 BE");
|
|
|
758 |
}
|
|
|
759 |
newtext.push(bch);
|
|
|
760 |
newtext.push(ch - (bch << 8));
|
|
|
761 |
}
|
|
|
762 |
return String.fromCharCode.apply(undef, newtext);
|
|
|
763 |
}
|
|
|
764 |
},
|
|
|
765 |
// Replace '/', '(', and ')' with pdf-safe versions
|
|
|
766 |
pdfEscape = function (text, flags) {
|
|
|
767 |
// doing to8bitStream does NOT make this PDF display unicode text. For that
|
|
|
768 |
// we also need to reference a unicode font and embed it - royal pain in the rear.
|
|
|
769 |
|
|
|
770 |
// There is still a benefit to to8bitStream - PDF simply cannot handle 16bit chars,
|
|
|
771 |
// which JavaScript Strings are happy to provide. So, while we still cannot display
|
|
|
772 |
// 2-byte characters property, at least CONDITIONALLY converting (entire string containing)
|
|
|
773 |
// 16bit chars to (USC-2-BE) 2-bytes per char + BOM streams we ensure that entire PDF
|
|
|
774 |
// is still parseable.
|
|
|
775 |
// This will allow immediate support for unicode in document properties strings.
|
|
|
776 |
return to8bitStream(text, flags).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
|
|
777 |
},
|
|
|
778 |
putInfo = function () {
|
|
|
779 |
out('/Producer (jsPDF ' + version + ')');
|
|
|
780 |
if (documentProperties.title) {
|
|
|
781 |
out('/Title (' + pdfEscape(documentProperties.title) + ')');
|
|
|
782 |
}
|
|
|
783 |
if (documentProperties.subject) {
|
|
|
784 |
out('/Subject (' + pdfEscape(documentProperties.subject) + ')');
|
|
|
785 |
}
|
|
|
786 |
if (documentProperties.author) {
|
|
|
787 |
out('/Author (' + pdfEscape(documentProperties.author) + ')');
|
|
|
788 |
}
|
|
|
789 |
if (documentProperties.keywords) {
|
|
|
790 |
out('/Keywords (' + pdfEscape(documentProperties.keywords) + ')');
|
|
|
791 |
}
|
|
|
792 |
if (documentProperties.creator) {
|
|
|
793 |
out('/Creator (' + pdfEscape(documentProperties.creator) + ')');
|
|
|
794 |
}
|
|
|
795 |
var created = new Date();
|
|
|
796 |
out('/CreationDate (D:' +
|
|
|
797 |
[
|
|
|
798 |
created.getFullYear(),
|
|
|
799 |
padd2(created.getMonth() + 1),
|
|
|
800 |
padd2(created.getDate()),
|
|
|
801 |
padd2(created.getHours()),
|
|
|
802 |
padd2(created.getMinutes()),
|
|
|
803 |
padd2(created.getSeconds())
|
|
|
804 |
].join('') +
|
|
|
805 |
')'
|
|
|
806 |
);
|
|
|
807 |
},
|
|
|
808 |
putCatalog = function () {
|
|
|
809 |
out('/Type /Catalog');
|
|
|
810 |
out('/Pages 1 0 R');
|
|
|
811 |
// @TODO: Add zoom and layout modes
|
|
|
812 |
out('/OpenAction [3 0 R /FitH null]');
|
|
|
813 |
out('/PageLayout /OneColumn');
|
|
|
814 |
events.publish('putCatalog');
|
|
|
815 |
},
|
|
|
816 |
putTrailer = function () {
|
|
|
817 |
out('/Size ' + (objectNumber + 1));
|
|
|
818 |
out('/Root ' + objectNumber + ' 0 R');
|
|
|
819 |
out('/Info ' + (objectNumber - 1) + ' 0 R');
|
|
|
820 |
},
|
|
|
821 |
beginPage = function () {
|
|
|
822 |
page++;
|
|
|
823 |
// Do dimension stuff
|
|
|
824 |
outToPages = true;
|
|
|
825 |
pages[page] = [];
|
|
|
826 |
},
|
|
|
827 |
_addPage = function () {
|
|
|
828 |
beginPage();
|
|
|
829 |
// Set line width
|
|
|
830 |
out(f2(lineWidth * k) + ' w');
|
|
|
831 |
// Set draw color
|
|
|
832 |
out(drawColor);
|
|
|
833 |
// resurrecting non-default line caps, joins
|
|
|
834 |
if (lineCapID !== 0) {
|
|
|
835 |
out(lineCapID.toString(10) + ' J');
|
|
|
836 |
}
|
|
|
837 |
if (lineJoinID !== 0) {
|
|
|
838 |
out(lineJoinID.toString(10) + ' j');
|
|
|
839 |
}
|
|
|
840 |
events.publish('addPage', {'pageNumber': page});
|
|
|
841 |
},
|
|
|
842 |
/**
|
|
|
843 |
Returns a document-specific font key - a label assigned to a
|
|
|
844 |
font name + font type combination at the time the font was added
|
|
|
845 |
to the font inventory.
|
|
|
846 |
|
|
|
847 |
Font key is used as label for the desired font for a block of text
|
|
|
848 |
to be added to the PDF document stream.
|
|
|
849 |
@private
|
|
|
850 |
@function
|
|
|
851 |
@param fontName {String} can be undefined on "falthy" to indicate "use current"
|
|
|
852 |
@param fontStyle {String} can be undefined on "falthy" to indicate "use current"
|
|
|
853 |
@returns {String} Font key.
|
|
|
854 |
*/
|
|
|
855 |
getFont = function (fontName, fontStyle) {
|
|
|
856 |
var key, undef;
|
|
|
857 |
|
|
|
858 |
if (fontName === undef) {
|
|
|
859 |
fontName = fonts[activeFontKey].fontName;
|
|
|
860 |
}
|
|
|
861 |
if (fontStyle === undef) {
|
|
|
862 |
fontStyle = fonts[activeFontKey].fontStyle;
|
|
|
863 |
}
|
|
|
864 |
|
|
|
865 |
try {
|
|
|
866 |
key = fontmap[fontName][fontStyle]; // returns a string like 'F3' - the KEY corresponding tot he font + type combination.
|
|
|
867 |
} catch (e) {
|
|
|
868 |
key = undef;
|
|
|
869 |
}
|
|
|
870 |
if (!key) {
|
|
|
871 |
throw new Error("Unable to look up font label for font '" + fontName + "', '" + fontStyle + "'. Refer to getFontList() for available fonts.");
|
|
|
872 |
}
|
|
|
873 |
|
|
|
874 |
return key;
|
|
|
875 |
},
|
|
|
876 |
buildDocument = function () {
|
|
|
877 |
|
|
|
878 |
outToPages = false; // switches out() to content
|
|
|
879 |
content = [];
|
|
|
880 |
offsets = [];
|
|
|
881 |
|
|
|
882 |
// putHeader()
|
|
|
883 |
out('%PDF-' + pdfVersion);
|
|
|
884 |
|
|
|
885 |
putPages();
|
|
|
886 |
|
|
|
887 |
putResources();
|
|
|
888 |
|
|
|
889 |
// Info
|
|
|
890 |
newObject();
|
|
|
891 |
out('<<');
|
|
|
892 |
putInfo();
|
|
|
893 |
out('>>');
|
|
|
894 |
out('endobj');
|
|
|
895 |
|
|
|
896 |
// Catalog
|
|
|
897 |
newObject();
|
|
|
898 |
out('<<');
|
|
|
899 |
putCatalog();
|
|
|
900 |
out('>>');
|
|
|
901 |
out('endobj');
|
|
|
902 |
|
|
|
903 |
// Cross-ref
|
|
|
904 |
var o = content_length, i;
|
|
|
905 |
out('xref');
|
|
|
906 |
out('0 ' + (objectNumber + 1));
|
|
|
907 |
out('0000000000 65535 f ');
|
|
|
908 |
for (i = 1; i <= objectNumber; i++) {
|
|
|
909 |
out(padd10(offsets[i]) + ' 00000 n ');
|
|
|
910 |
}
|
|
|
911 |
// Trailer
|
|
|
912 |
out('trailer');
|
|
|
913 |
out('<<');
|
|
|
914 |
putTrailer();
|
|
|
915 |
out('>>');
|
|
|
916 |
out('startxref');
|
|
|
917 |
out(o);
|
|
|
918 |
out('%%EOF');
|
|
|
919 |
|
|
|
920 |
outToPages = true;
|
|
|
921 |
|
|
|
922 |
return content.join('\n');
|
|
|
923 |
},
|
|
|
924 |
getStyle = function (style) {
|
|
|
925 |
// see Path-Painting Operators of PDF spec
|
|
|
926 |
var op = 'S'; // stroke
|
|
|
927 |
if (style === 'F') {
|
|
|
928 |
op = 'f'; // fill
|
|
|
929 |
} else if (style === 'FD' || style === 'DF') {
|
|
|
930 |
op = 'B'; // both
|
|
|
931 |
}
|
|
|
932 |
return op;
|
|
|
933 |
},
|
|
|
934 |
|
|
|
935 |
/**
|
|
|
936 |
Generates the PDF document.
|
|
|
937 |
Possible values:
|
|
|
938 |
datauristring (alias dataurlstring) - Data-Url-formatted data returned as string.
|
|
|
939 |
datauri (alias datauri) - Data-Url-formatted data pushed into current window's location (effectively reloading the window with contents of the PDF).
|
|
|
940 |
|
|
|
941 |
If `type` argument is undefined, output is raw body of resulting PDF returned as a string.
|
|
|
942 |
|
|
|
943 |
@param {String} type A string identifying one of the possible output types.
|
|
|
944 |
@param {Object} options An object providing some additional signalling to PDF generator.
|
|
|
945 |
@function
|
|
|
946 |
@returns {jsPDF}
|
|
|
947 |
@methodOf jsPDF#
|
|
|
948 |
@name output
|
|
|
949 |
*/
|
|
|
950 |
output = function (type, options) {
|
|
|
951 |
var undef, data, length, array, i, blob;
|
|
|
952 |
switch (type) {
|
|
|
953 |
case undef:
|
|
|
954 |
return buildDocument();
|
|
|
955 |
case 'save':
|
|
|
956 |
if (navigator.getUserMedia) {
|
|
|
957 |
if (window.URL === undefined) {
|
|
|
958 |
return API.output('dataurlnewwindow');
|
|
|
959 |
} else if (window.URL.createObjectURL === undefined) {
|
|
|
960 |
return API.output('dataurlnewwindow');
|
|
|
961 |
}
|
|
|
962 |
}
|
|
|
963 |
data = buildDocument();
|
|
|
964 |
|
|
|
965 |
// Need to add the file to BlobBuilder as a Uint8Array
|
|
|
966 |
length = data.length;
|
|
|
967 |
array = new Uint8Array(new ArrayBuffer(length));
|
|
|
968 |
|
|
|
969 |
for (i = 0; i < length; i++) {
|
|
|
970 |
array[i] = data.charCodeAt(i);
|
|
|
971 |
}
|
|
|
972 |
|
|
|
973 |
blob = new Blob([array], {type: "application/pdf"});
|
|
|
974 |
|
|
|
975 |
saveAs(blob, options);
|
|
|
976 |
break;
|
|
|
977 |
case 'datauristring':
|
|
|
978 |
case 'dataurlstring':
|
|
|
979 |
return 'data:application/pdf;base64,' + btoa(buildDocument());
|
|
|
980 |
case 'datauri':
|
|
|
981 |
case 'dataurl':
|
|
|
982 |
document.location.href = 'data:application/pdf;base64,' + btoa(buildDocument());
|
|
|
983 |
break;
|
|
|
984 |
case 'dataurlnewwindow':
|
|
|
985 |
window.open('data:application/pdf;base64,' + btoa(buildDocument()));
|
|
|
986 |
break;
|
|
|
987 |
default:
|
|
|
988 |
throw new Error('Output type "' + type + '" is not supported.');
|
|
|
989 |
}
|
|
|
990 |
// @TODO: Add different output options
|
|
|
991 |
};
|
|
|
992 |
|
|
|
993 |
if (unit === 'pt') {
|
|
|
994 |
k = 1;
|
|
|
995 |
} else if (unit === 'mm') {
|
|
|
996 |
k = 72 / 25.4;
|
|
|
997 |
} else if (unit === 'cm') {
|
|
|
998 |
k = 72 / 2.54;
|
|
|
999 |
} else if (unit === 'in') {
|
|
|
1000 |
k = 72;
|
|
|
1001 |
} else {
|
|
|
1002 |
throw ('Invalid unit: ' + unit);
|
|
|
1003 |
}
|
|
|
1004 |
|
|
|
1005 |
// Dimensions are stored as user units and converted to points on output
|
|
|
1006 |
if (pageFormats.hasOwnProperty(format_as_string)) {
|
|
|
1007 |
pageHeight = pageFormats[format_as_string][1] / k;
|
|
|
1008 |
pageWidth = pageFormats[format_as_string][0] / k;
|
|
|
1009 |
} else {
|
|
|
1010 |
try {
|
|
|
1011 |
pageHeight = format[1];
|
|
|
1012 |
pageWidth = format[0];
|
|
|
1013 |
} catch (err) {
|
|
|
1014 |
throw ('Invalid format: ' + format);
|
|
|
1015 |
}
|
|
|
1016 |
}
|
|
|
1017 |
|
|
|
1018 |
if (orientation === 'p' || orientation === 'portrait') {
|
|
|
1019 |
orientation = 'p';
|
|
|
1020 |
if (pageWidth > pageHeight) {
|
|
|
1021 |
tmp = pageWidth;
|
|
|
1022 |
pageWidth = pageHeight;
|
|
|
1023 |
pageHeight = tmp;
|
|
|
1024 |
}
|
|
|
1025 |
} else if (orientation === 'l' || orientation === 'landscape') {
|
|
|
1026 |
orientation = 'l';
|
|
|
1027 |
if (pageHeight > pageWidth) {
|
|
|
1028 |
tmp = pageWidth;
|
|
|
1029 |
pageWidth = pageHeight;
|
|
|
1030 |
pageHeight = tmp;
|
|
|
1031 |
}
|
|
|
1032 |
} else {
|
|
|
1033 |
throw ('Invalid orientation: ' + orientation);
|
|
|
1034 |
}
|
|
|
1035 |
|
|
|
1036 |
|
|
|
1037 |
|
|
|
1038 |
//---------------------------------------
|
|
|
1039 |
// Public API
|
|
|
1040 |
|
|
|
1041 |
/*
|
|
|
1042 |
Object exposing internal API to plugins
|
|
|
1043 |
@public
|
|
|
1044 |
*/
|
|
|
1045 |
API.internal = {
|
|
|
1046 |
'pdfEscape': pdfEscape,
|
|
|
1047 |
'getStyle': getStyle,
|
|
|
1048 |
/**
|
|
|
1049 |
Returns {FontObject} describing a particular font.
|
|
|
1050 |
@public
|
|
|
1051 |
@function
|
|
|
1052 |
@param fontName {String} (Optional) Font's family name
|
|
|
1053 |
@param fontStyle {String} (Optional) Font's style variation name (Example:"Italic")
|
|
|
1054 |
@returns {FontObject}
|
|
|
1055 |
*/
|
|
|
1056 |
'getFont': function () { return fonts[getFont.apply(API, arguments)]; },
|
|
|
1057 |
'getFontSize': function () { return activeFontSize; },
|
|
|
1058 |
'btoa': btoa,
|
|
|
1059 |
'write': function (string1, string2, string3, etc) {
|
|
|
1060 |
out(
|
|
|
1061 |
arguments.length === 1 ? string1 : Array.prototype.join.call(arguments, ' ')
|
|
|
1062 |
);
|
|
|
1063 |
},
|
|
|
1064 |
'getCoordinateString': function (value) {
|
|
|
1065 |
return f2(value * k);
|
|
|
1066 |
},
|
|
|
1067 |
'getVerticalCoordinateString': function (value) {
|
|
|
1068 |
return f2((pageHeight - value) * k);
|
|
|
1069 |
},
|
|
|
1070 |
'collections': {},
|
|
|
1071 |
'newObject': newObject,
|
|
|
1072 |
'putStream': putStream,
|
|
|
1073 |
'events': events,
|
|
|
1074 |
// ratio that you use in multiplication of a given "size" number to arrive to 'point'
|
|
|
1075 |
// units of measurement.
|
|
|
1076 |
// scaleFactor is set at initialization of the document and calculated against the stated
|
|
|
1077 |
// default measurement units for the document.
|
|
|
1078 |
// If default is "mm", k is the number that will turn number in 'mm' into 'points' number.
|
|
|
1079 |
// through multiplication.
|
|
|
1080 |
'scaleFactor': k,
|
|
|
1081 |
'pageSize': {'width': pageWidth, 'height': pageHeight},
|
|
|
1082 |
'output': function (type, options) {
|
|
|
1083 |
return output(type, options);
|
|
|
1084 |
}
|
|
|
1085 |
};
|
|
|
1086 |
|
|
|
1087 |
/**
|
|
|
1088 |
Adds (and transfers the focus to) new page to the PDF document.
|
|
|
1089 |
@function
|
|
|
1090 |
@returns {jsPDF}
|
|
|
1091 |
|
|
|
1092 |
@methodOf jsPDF#
|
|
|
1093 |
@name addPage
|
|
|
1094 |
*/
|
|
|
1095 |
API.addPage = function () {
|
|
|
1096 |
_addPage();
|
|
|
1097 |
return this;
|
|
|
1098 |
};
|
|
|
1099 |
|
|
|
1100 |
/**
|
|
|
1101 |
Adds text to page. Supports adding multiline text when 'text' argument is an Array of Strings.
|
|
|
1102 |
@function
|
|
|
1103 |
@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.
|
|
|
1104 |
@param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1105 |
@param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1106 |
@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.
|
|
|
1107 |
@returns {jsPDF}
|
|
|
1108 |
@methodOf jsPDF#
|
|
|
1109 |
@name text
|
|
|
1110 |
*/
|
|
|
1111 |
API.text = function (text, x, y, flags) {
|
|
|
1112 |
/**
|
|
|
1113 |
* Inserts something like this into PDF
|
|
|
1114 |
BT
|
|
|
1115 |
/F1 16 Tf % Font name + size
|
|
|
1116 |
16 TL % How many units down for next line in multiline text
|
|
|
1117 |
|
|
|
1118 |
28.35 813.54 Td % position
|
|
|
1119 |
(line one) Tj
|
|
|
1120 |
T* (line two) Tj
|
|
|
1121 |
T* (line three) Tj
|
|
|
1122 |
ET
|
|
|
1123 |
*/
|
|
|
1124 |
|
|
|
1125 |
var undef, _first, _second, _third, newtext, str, i;
|
|
|
1126 |
// Pre-August-2012 the order of arguments was function(x, y, text, flags)
|
|
|
1127 |
// in effort to make all calls have similar signature like
|
|
|
1128 |
// function(data, coordinates... , miscellaneous)
|
|
|
1129 |
// this method had its args flipped.
|
|
|
1130 |
// code below allows backward compatibility with old arg order.
|
|
|
1131 |
if (typeof text === 'number') {
|
|
|
1132 |
_first = y;
|
|
|
1133 |
_second = text;
|
|
|
1134 |
_third = x;
|
|
|
1135 |
|
|
|
1136 |
text = _first;
|
|
|
1137 |
x = _second;
|
|
|
1138 |
y = _third;
|
|
|
1139 |
}
|
|
|
1140 |
|
|
|
1141 |
// If there are any newlines in text, we assume
|
|
|
1142 |
// the user wanted to print multiple lines, so break the
|
|
|
1143 |
// text up into an array. If the text is already an array,
|
|
|
1144 |
// we assume the user knows what they are doing.
|
|
|
1145 |
if (typeof text === 'string' && text.match(/[\n\r]/)) {
|
|
|
1146 |
text = text.split(/\r\n|\r|\n/g);
|
|
|
1147 |
}
|
|
|
1148 |
|
|
|
1149 |
if (typeof flags === 'undefined') {
|
|
|
1150 |
flags = {'noBOM': true, 'autoencode': true};
|
|
|
1151 |
} else {
|
|
|
1152 |
|
|
|
1153 |
if (flags.noBOM === undef) {
|
|
|
1154 |
flags.noBOM = true;
|
|
|
1155 |
}
|
|
|
1156 |
|
|
|
1157 |
if (flags.autoencode === undef) {
|
|
|
1158 |
flags.autoencode = true;
|
|
|
1159 |
}
|
|
|
1160 |
|
|
|
1161 |
}
|
|
|
1162 |
|
|
|
1163 |
if (typeof text === 'string') {
|
|
|
1164 |
str = pdfEscape(text, flags);
|
|
|
1165 |
} else if (text instanceof Array) { /* Array */
|
|
|
1166 |
// we don't want to destroy original text array, so cloning it
|
|
|
1167 |
newtext = text.concat();
|
|
|
1168 |
// we do array.join('text that must not be PDFescaped")
|
|
|
1169 |
// thus, pdfEscape each component separately
|
|
|
1170 |
for (i = newtext.length - 1; i !== -1; i--) {
|
|
|
1171 |
newtext[i] = pdfEscape(newtext[i], flags);
|
|
|
1172 |
}
|
|
|
1173 |
str = newtext.join(") Tj\nT* (");
|
|
|
1174 |
} else {
|
|
|
1175 |
throw new Error('Type of text must be string or Array. "' + text + '" is not recognized.');
|
|
|
1176 |
}
|
|
|
1177 |
// Using "'" ("go next line and render text" mark) would save space but would complicate our rendering code, templates
|
|
|
1178 |
|
|
|
1179 |
// BT .. ET does NOT have default settings for Tf. You must state that explicitely every time for BT .. ET
|
|
|
1180 |
// if you want text transformation matrix (+ multiline) to work reliably (which reads sizes of things from font declarations)
|
|
|
1181 |
// Thus, there is NO useful, *reliable* concept of "default" font for a page.
|
|
|
1182 |
// The fact that "default" (reuse font used before) font worked before in basic cases is an accident
|
|
|
1183 |
// - readers dealing smartly with brokenness of jsPDF's markup.
|
|
|
1184 |
out(
|
|
|
1185 |
'BT\n/' +
|
|
|
1186 |
activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size
|
|
|
1187 |
activeFontSize + ' TL\n' + // line spacing
|
|
|
1188 |
textColor +
|
|
|
1189 |
'\n' + f2(x * k) + ' ' + f2((pageHeight - y) * k) + ' Td\n(' +
|
|
|
1190 |
str +
|
|
|
1191 |
') Tj\nET'
|
|
|
1192 |
);
|
|
|
1193 |
return this;
|
|
|
1194 |
};
|
|
|
1195 |
|
|
|
1196 |
API.line = function (x1, y1, x2, y2) {
|
|
|
1197 |
out(
|
|
|
1198 |
f2(x1 * k) + ' ' + f2((pageHeight - y1) * k) + ' m ' +
|
|
|
1199 |
f2(x2 * k) + ' ' + f2((pageHeight - y2) * k) + ' l S'
|
|
|
1200 |
);
|
|
|
1201 |
return this;
|
|
|
1202 |
};
|
|
|
1203 |
|
|
|
1204 |
/**
|
|
|
1205 |
Adds series of curves (straight lines or cubic bezier curves) to canvas, starting at `x`, `y` coordinates.
|
|
|
1206 |
All data points in `lines` are relative to last line origin.
|
|
|
1207 |
`x`, `y` become x1,y1 for first line / curve in the set.
|
|
|
1208 |
For lines you only need to specify [x2, y2] - (ending point) vector against x1, y1 starting point.
|
|
|
1209 |
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.
|
|
|
1210 |
|
|
|
1211 |
@example .lines([[2,2],[-2,2],[1,1,2,2,3,3],[2,1]], 212,110, 10) // line, line, bezier curve, line
|
|
|
1212 |
@param {Array} lines Array of *vector* shifts as pairs (lines) or sextets (cubic bezier curves).
|
|
|
1213 |
@param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1214 |
@param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1215 |
@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.
|
|
|
1216 |
@function
|
|
|
1217 |
@returns {jsPDF}
|
|
|
1218 |
@methodOf jsPDF#
|
|
|
1219 |
@name lines
|
|
|
1220 |
*/
|
|
|
1221 |
API.lines = function (lines, x, y, scale, style) {
|
|
|
1222 |
var undef, _first, _second, _third, scalex, scaley, i, l, leg, x2, y2, x3, y3, x4, y4;
|
|
|
1223 |
|
|
|
1224 |
// Pre-August-2012 the order of arguments was function(x, y, lines, scale, style)
|
|
|
1225 |
// in effort to make all calls have similar signature like
|
|
|
1226 |
// function(content, coordinateX, coordinateY , miscellaneous)
|
|
|
1227 |
// this method had its args flipped.
|
|
|
1228 |
// code below allows backward compatibility with old arg order.
|
|
|
1229 |
if (typeof lines === 'number') {
|
|
|
1230 |
_first = y;
|
|
|
1231 |
_second = lines;
|
|
|
1232 |
_third = x;
|
|
|
1233 |
|
|
|
1234 |
lines = _first;
|
|
|
1235 |
x = _second;
|
|
|
1236 |
y = _third;
|
|
|
1237 |
}
|
|
|
1238 |
|
|
|
1239 |
style = getStyle(style);
|
|
|
1240 |
scale = scale === undef ? [1, 1] : scale;
|
|
|
1241 |
|
|
|
1242 |
// starting point
|
|
|
1243 |
out(f3(x * k) + ' ' + f3((pageHeight - y) * k) + ' m ');
|
|
|
1244 |
|
|
|
1245 |
scalex = scale[0];
|
|
|
1246 |
scaley = scale[1];
|
|
|
1247 |
l = lines.length;
|
|
|
1248 |
//, x2, y2 // bezier only. In page default measurement "units", *after* scaling
|
|
|
1249 |
//, x3, y3 // bezier only. In page default measurement "units", *after* scaling
|
|
|
1250 |
// ending point for all, lines and bezier. . In page default measurement "units", *after* scaling
|
|
|
1251 |
x4 = x; // last / ending point = starting point for first item.
|
|
|
1252 |
y4 = y; // last / ending point = starting point for first item.
|
|
|
1253 |
|
|
|
1254 |
for (i = 0; i < l; i++) {
|
|
|
1255 |
leg = lines[i];
|
|
|
1256 |
if (leg.length === 2) {
|
|
|
1257 |
// simple line
|
|
|
1258 |
x4 = leg[0] * scalex + x4; // here last x4 was prior ending point
|
|
|
1259 |
y4 = leg[1] * scaley + y4; // here last y4 was prior ending point
|
|
|
1260 |
out(f3(x4 * k) + ' ' + f3((pageHeight - y4) * k) + ' l');
|
|
|
1261 |
} else {
|
|
|
1262 |
// bezier curve
|
|
|
1263 |
x2 = leg[0] * scalex + x4; // here last x4 is prior ending point
|
|
|
1264 |
y2 = leg[1] * scaley + y4; // here last y4 is prior ending point
|
|
|
1265 |
x3 = leg[2] * scalex + x4; // here last x4 is prior ending point
|
|
|
1266 |
y3 = leg[3] * scaley + y4; // here last y4 is prior ending point
|
|
|
1267 |
x4 = leg[4] * scalex + x4; // here last x4 was prior ending point
|
|
|
1268 |
y4 = leg[5] * scaley + y4; // here last y4 was prior ending point
|
|
|
1269 |
out(
|
|
|
1270 |
f3(x2 * k) + ' ' +
|
|
|
1271 |
f3((pageHeight - y2) * k) + ' ' +
|
|
|
1272 |
f3(x3 * k) + ' ' +
|
|
|
1273 |
f3((pageHeight - y3) * k) + ' ' +
|
|
|
1274 |
f3(x4 * k) + ' ' +
|
|
|
1275 |
f3((pageHeight - y4) * k) + ' c'
|
|
|
1276 |
);
|
|
|
1277 |
}
|
|
|
1278 |
}
|
|
|
1279 |
// stroking / filling / both the path
|
|
|
1280 |
out(style);
|
|
|
1281 |
return this;
|
|
|
1282 |
};
|
|
|
1283 |
|
|
|
1284 |
/**
|
|
|
1285 |
Adds a rectangle to PDF
|
|
|
1286 |
|
|
|
1287 |
@param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1288 |
@param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1289 |
@param {Number} w Width (in units declared at inception of PDF document)
|
|
|
1290 |
@param {Number} h Height (in units declared at inception of PDF document)
|
|
|
1291 |
@param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
|
|
|
1292 |
@function
|
|
|
1293 |
@returns {jsPDF}
|
|
|
1294 |
@methodOf jsPDF#
|
|
|
1295 |
@name rect
|
|
|
1296 |
*/
|
|
|
1297 |
API.rect = function (x, y, w, h, style) {
|
|
|
1298 |
var op = getStyle(style);
|
|
|
1299 |
out([
|
|
|
1300 |
f2(x * k),
|
|
|
1301 |
f2((pageHeight - y) * k),
|
|
|
1302 |
f2(w * k),
|
|
|
1303 |
f2(-h * k),
|
|
|
1304 |
're',
|
|
|
1305 |
op
|
|
|
1306 |
].join(' '));
|
|
|
1307 |
return this;
|
|
|
1308 |
};
|
|
|
1309 |
|
|
|
1310 |
/**
|
|
|
1311 |
Adds a triangle to PDF
|
|
|
1312 |
|
|
|
1313 |
@param {Number} x1 Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1314 |
@param {Number} y1 Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1315 |
@param {Number} x2 Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1316 |
@param {Number} y2 Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1317 |
@param {Number} x3 Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1318 |
@param {Number} y3 Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1319 |
@param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
|
|
|
1320 |
@function
|
|
|
1321 |
@returns {jsPDF}
|
|
|
1322 |
@methodOf jsPDF#
|
|
|
1323 |
@name triangle
|
|
|
1324 |
*/
|
|
|
1325 |
API.triangle = function (x1, y1, x2, y2, x3, y3, style) {
|
|
|
1326 |
this.lines(
|
|
|
1327 |
[
|
|
|
1328 |
[ x2 - x1, y2 - y1 ], // vector to point 2
|
|
|
1329 |
[ x3 - x2, y3 - y2 ], // vector to point 3
|
|
|
1330 |
[ x1 - x3, y1 - y3 ] // closing vector back to point 1
|
|
|
1331 |
],
|
|
|
1332 |
x1,
|
|
|
1333 |
y1, // start of path
|
|
|
1334 |
[1, 1],
|
|
|
1335 |
style
|
|
|
1336 |
);
|
|
|
1337 |
return this;
|
|
|
1338 |
};
|
|
|
1339 |
|
|
|
1340 |
/**
|
|
|
1341 |
Adds a rectangle with rounded corners to PDF
|
|
|
1342 |
|
|
|
1343 |
@param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1344 |
@param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1345 |
@param {Number} w Width (in units declared at inception of PDF document)
|
|
|
1346 |
@param {Number} h Height (in units declared at inception of PDF document)
|
|
|
1347 |
@param {Number} rx Radius along x axis (in units declared at inception of PDF document)
|
|
|
1348 |
@param {Number} rx Radius along y axis (in units declared at inception of PDF document)
|
|
|
1349 |
@param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
|
|
|
1350 |
@function
|
|
|
1351 |
@returns {jsPDF}
|
|
|
1352 |
@methodOf jsPDF#
|
|
|
1353 |
@name roundedRect
|
|
|
1354 |
*/
|
|
|
1355 |
API.roundedRect = function (x, y, w, h, rx, ry, style) {
|
|
|
1356 |
var MyArc = 4 / 3 * (Math.SQRT2 - 1);
|
|
|
1357 |
this.lines(
|
|
|
1358 |
[
|
|
|
1359 |
[ (w - 2 * rx), 0 ],
|
|
|
1360 |
[ (rx * MyArc), 0, rx, ry - (ry * MyArc), rx, ry ],
|
|
|
1361 |
[ 0, (h - 2 * ry) ],
|
|
|
1362 |
[ 0, (ry * MyArc), -(rx * MyArc), ry, -rx, ry],
|
|
|
1363 |
[ (-w + 2 * rx), 0],
|
|
|
1364 |
[ -(rx * MyArc), 0, -rx, -(ry * MyArc), -rx, -ry],
|
|
|
1365 |
[ 0, (-h + 2 * ry)],
|
|
|
1366 |
[ 0, -(ry * MyArc), (rx * MyArc), -ry, rx, -ry]
|
|
|
1367 |
],
|
|
|
1368 |
x + rx,
|
|
|
1369 |
y, // start of path
|
|
|
1370 |
[1, 1],
|
|
|
1371 |
style
|
|
|
1372 |
);
|
|
|
1373 |
return this;
|
|
|
1374 |
};
|
|
|
1375 |
|
|
|
1376 |
/**
|
|
|
1377 |
Adds an ellipse to PDF
|
|
|
1378 |
|
|
|
1379 |
@param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1380 |
@param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1381 |
@param {Number} rx Radius along x axis (in units declared at inception of PDF document)
|
|
|
1382 |
@param {Number} rx Radius along y axis (in units declared at inception of PDF document)
|
|
|
1383 |
@param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
|
|
|
1384 |
@function
|
|
|
1385 |
@returns {jsPDF}
|
|
|
1386 |
@methodOf jsPDF#
|
|
|
1387 |
@name ellipse
|
|
|
1388 |
*/
|
|
|
1389 |
API.ellipse = function (x, y, rx, ry, style) {
|
|
|
1390 |
var op = getStyle(style),
|
|
|
1391 |
lx = 4 / 3 * (Math.SQRT2 - 1) * rx,
|
|
|
1392 |
ly = 4 / 3 * (Math.SQRT2 - 1) * ry;
|
|
|
1393 |
|
|
|
1394 |
out([
|
|
|
1395 |
f2((x + rx) * k),
|
|
|
1396 |
f2((pageHeight - y) * k),
|
|
|
1397 |
'm',
|
|
|
1398 |
f2((x + rx) * k),
|
|
|
1399 |
f2((pageHeight - (y - ly)) * k),
|
|
|
1400 |
f2((x + lx) * k),
|
|
|
1401 |
f2((pageHeight - (y - ry)) * k),
|
|
|
1402 |
f2(x * k),
|
|
|
1403 |
f2((pageHeight - (y - ry)) * k),
|
|
|
1404 |
'c'
|
|
|
1405 |
].join(' '));
|
|
|
1406 |
out([
|
|
|
1407 |
f2((x - lx) * k),
|
|
|
1408 |
f2((pageHeight - (y - ry)) * k),
|
|
|
1409 |
f2((x - rx) * k),
|
|
|
1410 |
f2((pageHeight - (y - ly)) * k),
|
|
|
1411 |
f2((x - rx) * k),
|
|
|
1412 |
f2((pageHeight - y) * k),
|
|
|
1413 |
'c'
|
|
|
1414 |
].join(' '));
|
|
|
1415 |
out([
|
|
|
1416 |
f2((x - rx) * k),
|
|
|
1417 |
f2((pageHeight - (y + ly)) * k),
|
|
|
1418 |
f2((x - lx) * k),
|
|
|
1419 |
f2((pageHeight - (y + ry)) * k),
|
|
|
1420 |
f2(x * k),
|
|
|
1421 |
f2((pageHeight - (y + ry)) * k),
|
|
|
1422 |
'c'
|
|
|
1423 |
].join(' '));
|
|
|
1424 |
out([
|
|
|
1425 |
f2((x + lx) * k),
|
|
|
1426 |
f2((pageHeight - (y + ry)) * k),
|
|
|
1427 |
f2((x + rx) * k),
|
|
|
1428 |
f2((pageHeight - (y + ly)) * k),
|
|
|
1429 |
f2((x + rx) * k),
|
|
|
1430 |
f2((pageHeight - y) * k),
|
|
|
1431 |
'c',
|
|
|
1432 |
op
|
|
|
1433 |
].join(' '));
|
|
|
1434 |
return this;
|
|
|
1435 |
};
|
|
|
1436 |
|
|
|
1437 |
/**
|
|
|
1438 |
Adds an circle to PDF
|
|
|
1439 |
|
|
|
1440 |
@param {Number} x Coordinate (in units declared at inception of PDF document) against left edge of the page
|
|
|
1441 |
@param {Number} y Coordinate (in units declared at inception of PDF document) against upper edge of the page
|
|
|
1442 |
@param {Number} r Radius (in units declared at inception of PDF document)
|
|
|
1443 |
@param {String} style (Defaults to active fill/stroke style) A string signalling if stroke, fill or both are to be applied.
|
|
|
1444 |
@function
|
|
|
1445 |
@returns {jsPDF}
|
|
|
1446 |
@methodOf jsPDF#
|
|
|
1447 |
@name circle
|
|
|
1448 |
*/
|
|
|
1449 |
API.circle = function (x, y, r, style) {
|
|
|
1450 |
return this.ellipse(x, y, r, r, style);
|
|
|
1451 |
};
|
|
|
1452 |
|
|
|
1453 |
/**
|
|
|
1454 |
Adds a properties to the PDF document
|
|
|
1455 |
|
|
|
1456 |
@param {Object} A property_name-to-property_value object structure.
|
|
|
1457 |
@function
|
|
|
1458 |
@returns {jsPDF}
|
|
|
1459 |
@methodOf jsPDF#
|
|
|
1460 |
@name setProperties
|
|
|
1461 |
*/
|
|
|
1462 |
API.setProperties = function (properties) {
|
|
|
1463 |
// copying only those properties we can render.
|
|
|
1464 |
var property;
|
|
|
1465 |
for (property in documentProperties) {
|
|
|
1466 |
if (documentProperties.hasOwnProperty(property) && properties[property]) {
|
|
|
1467 |
documentProperties[property] = properties[property];
|
|
|
1468 |
}
|
|
|
1469 |
}
|
|
|
1470 |
return this;
|
|
|
1471 |
};
|
|
|
1472 |
|
|
|
1473 |
/**
|
|
|
1474 |
Sets font size for upcoming text elements.
|
|
|
1475 |
|
|
|
1476 |
@param {Number} size Font size in points.
|
|
|
1477 |
@function
|
|
|
1478 |
@returns {jsPDF}
|
|
|
1479 |
@methodOf jsPDF#
|
|
|
1480 |
@name setFontSize
|
|
|
1481 |
*/
|
|
|
1482 |
API.setFontSize = function (size) {
|
|
|
1483 |
activeFontSize = size;
|
|
|
1484 |
return this;
|
|
|
1485 |
};
|
|
|
1486 |
|
|
|
1487 |
/**
|
|
|
1488 |
Sets text font face, variant for upcoming text elements.
|
|
|
1489 |
See output of jsPDF.getFontList() for possible font names, styles.
|
|
|
1490 |
|
|
|
1491 |
@param {String} fontName Font name or family. Example: "times"
|
|
|
1492 |
@param {String} fontStyle Font style or variant. Example: "italic"
|
|
|
1493 |
@function
|
|
|
1494 |
@returns {jsPDF}
|
|
|
1495 |
@methodOf jsPDF#
|
|
|
1496 |
@name setFont
|
|
|
1497 |
*/
|
|
|
1498 |
API.setFont = function (fontName, fontStyle) {
|
|
|
1499 |
activeFontKey = getFont(fontName, fontStyle);
|
|
|
1500 |
// if font is not found, the above line blows up and we never go further
|
|
|
1501 |
return this;
|
|
|
1502 |
};
|
|
|
1503 |
|
|
|
1504 |
/**
|
|
|
1505 |
Switches font style or variant for upcoming text elements,
|
|
|
1506 |
while keeping the font face or family same.
|
|
|
1507 |
See output of jsPDF.getFontList() for possible font names, styles.
|
|
|
1508 |
|
|
|
1509 |
@param {String} style Font style or variant. Example: "italic"
|
|
|
1510 |
@function
|
|
|
1511 |
@returns {jsPDF}
|
|
|
1512 |
@methodOf jsPDF#
|
|
|
1513 |
@name setFontStyle
|
|
|
1514 |
*/
|
|
|
1515 |
API.setFontStyle = API.setFontType = function (style) {
|
|
|
1516 |
var undef;
|
|
|
1517 |
activeFontKey = getFont(undef, style);
|
|
|
1518 |
// if font is not found, the above line blows up and we never go further
|
|
|
1519 |
return this;
|
|
|
1520 |
};
|
|
|
1521 |
|
|
|
1522 |
/**
|
|
|
1523 |
Returns an object - a tree of fontName to fontStyle relationships available to
|
|
|
1524 |
active PDF document.
|
|
|
1525 |
|
|
|
1526 |
@public
|
|
|
1527 |
@function
|
|
|
1528 |
@returns {Object} Like {'times':['normal', 'italic', ... ], 'arial':['normal', 'bold', ... ], ... }
|
|
|
1529 |
@methodOf jsPDF#
|
|
|
1530 |
@name getFontList
|
|
|
1531 |
*/
|
|
|
1532 |
API.getFontList = function () {
|
|
|
1533 |
// TODO: iterate over fonts array or return copy of fontmap instead in case more are ever added.
|
|
|
1534 |
var list = {},
|
|
|
1535 |
fontName,
|
|
|
1536 |
fontStyle,
|
|
|
1537 |
tmp;
|
|
|
1538 |
|
|
|
1539 |
for (fontName in fontmap) {
|
|
|
1540 |
if (fontmap.hasOwnProperty(fontName)) {
|
|
|
1541 |
list[fontName] = tmp = [];
|
|
|
1542 |
for (fontStyle in fontmap[fontName]) {
|
|
|
1543 |
if (fontmap[fontName].hasOwnProperty(fontStyle)) {
|
|
|
1544 |
tmp.push(fontStyle);
|
|
|
1545 |
}
|
|
|
1546 |
}
|
|
|
1547 |
}
|
|
|
1548 |
}
|
|
|
1549 |
|
|
|
1550 |
return list;
|
|
|
1551 |
};
|
|
|
1552 |
|
|
|
1553 |
/**
|
|
|
1554 |
Sets line width for upcoming lines.
|
|
|
1555 |
|
|
|
1556 |
@param {Number} width Line width (in units declared at inception of PDF document)
|
|
|
1557 |
@function
|
|
|
1558 |
@returns {jsPDF}
|
|
|
1559 |
@methodOf jsPDF#
|
|
|
1560 |
@name setLineWidth
|
|
|
1561 |
*/
|
|
|
1562 |
API.setLineWidth = function (width) {
|
|
|
1563 |
out((width * k).toFixed(2) + ' w');
|
|
|
1564 |
return this;
|
|
|
1565 |
};
|
|
|
1566 |
|
|
|
1567 |
/**
|
|
|
1568 |
Sets the stroke color for upcoming elements.
|
|
|
1569 |
|
|
|
1570 |
Depending on the number of arguments given, Gray, RGB, or CMYK
|
|
|
1571 |
color space is implied.
|
|
|
1572 |
|
|
|
1573 |
When only ch1 is given, "Gray" color space is implied and it
|
|
|
1574 |
must be a value in the range from 0.00 (solid black) to to 1.00 (white)
|
|
|
1575 |
if values are communicated as String types, or in range from 0 (black)
|
|
|
1576 |
to 255 (white) if communicated as Number type.
|
|
|
1577 |
The RGB-like 0-255 range is provided for backward compatibility.
|
|
|
1578 |
|
|
|
1579 |
When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
|
|
|
1580 |
value must be in the range from 0.00 (minimum intensity) to to 1.00
|
|
|
1581 |
(max intensity) if values are communicated as String types, or
|
|
|
1582 |
from 0 (min intensity) to to 255 (max intensity) if values are communicated
|
|
|
1583 |
as Number types.
|
|
|
1584 |
The RGB-like 0-255 range is provided for backward compatibility.
|
|
|
1585 |
|
|
|
1586 |
When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
|
|
|
1587 |
value must be a in the range from 0.00 (0% concentration) to to
|
|
|
1588 |
1.00 (100% concentration)
|
|
|
1589 |
|
|
|
1590 |
Because JavaScript treats fixed point numbers badly (rounds to
|
|
|
1591 |
floating point nearest to binary representation) it is highly advised to
|
|
|
1592 |
communicate the fractional numbers as String types, not JavaScript Number type.
|
|
|
1593 |
|
|
|
1594 |
@param {Number|String} ch1 Color channel value
|
|
|
1595 |
@param {Number|String} ch2 Color channel value
|
|
|
1596 |
@param {Number|String} ch3 Color channel value
|
|
|
1597 |
@param {Number|String} ch4 Color channel value
|
|
|
1598 |
|
|
|
1599 |
@function
|
|
|
1600 |
@returns {jsPDF}
|
|
|
1601 |
@methodOf jsPDF#
|
|
|
1602 |
@name setDrawColor
|
|
|
1603 |
*/
|
|
|
1604 |
API.setDrawColor = function (ch1, ch2, ch3, ch4) {
|
|
|
1605 |
var color;
|
|
|
1606 |
if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
|
|
|
1607 |
// Gray color space.
|
|
|
1608 |
if (typeof ch1 === 'string') {
|
|
|
1609 |
color = ch1 + ' G';
|
|
|
1610 |
} else {
|
|
|
1611 |
color = f2(ch1 / 255) + ' G';
|
|
|
1612 |
}
|
|
|
1613 |
} else if (ch4 === undefined) {
|
|
|
1614 |
// RGB
|
|
|
1615 |
if (typeof ch1 === 'string') {
|
|
|
1616 |
color = [ch1, ch2, ch3, 'RG'].join(' ');
|
|
|
1617 |
} else {
|
|
|
1618 |
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'RG'].join(' ');
|
|
|
1619 |
}
|
|
|
1620 |
} else {
|
|
|
1621 |
// CMYK
|
|
|
1622 |
if (typeof ch1 === 'string') {
|
|
|
1623 |
color = [ch1, ch2, ch3, ch4, 'K'].join(' ');
|
|
|
1624 |
} else {
|
|
|
1625 |
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'K'].join(' ');
|
|
|
1626 |
}
|
|
|
1627 |
}
|
|
|
1628 |
|
|
|
1629 |
out(color);
|
|
|
1630 |
return this;
|
|
|
1631 |
};
|
|
|
1632 |
|
|
|
1633 |
/**
|
|
|
1634 |
Sets the fill color for upcoming elements.
|
|
|
1635 |
|
|
|
1636 |
Depending on the number of arguments given, Gray, RGB, or CMYK
|
|
|
1637 |
color space is implied.
|
|
|
1638 |
|
|
|
1639 |
When only ch1 is given, "Gray" color space is implied and it
|
|
|
1640 |
must be a value in the range from 0.00 (solid black) to to 1.00 (white)
|
|
|
1641 |
if values are communicated as String types, or in range from 0 (black)
|
|
|
1642 |
to 255 (white) if communicated as Number type.
|
|
|
1643 |
The RGB-like 0-255 range is provided for backward compatibility.
|
|
|
1644 |
|
|
|
1645 |
When only ch1,ch2,ch3 are given, "RGB" color space is implied and each
|
|
|
1646 |
value must be in the range from 0.00 (minimum intensity) to to 1.00
|
|
|
1647 |
(max intensity) if values are communicated as String types, or
|
|
|
1648 |
from 0 (min intensity) to to 255 (max intensity) if values are communicated
|
|
|
1649 |
as Number types.
|
|
|
1650 |
The RGB-like 0-255 range is provided for backward compatibility.
|
|
|
1651 |
|
|
|
1652 |
When ch1,ch2,ch3,ch4 are given, "CMYK" color space is implied and each
|
|
|
1653 |
value must be a in the range from 0.00 (0% concentration) to to
|
|
|
1654 |
1.00 (100% concentration)
|
|
|
1655 |
|
|
|
1656 |
Because JavaScript treats fixed point numbers badly (rounds to
|
|
|
1657 |
floating point nearest to binary representation) it is highly advised to
|
|
|
1658 |
communicate the fractional numbers as String types, not JavaScript Number type.
|
|
|
1659 |
|
|
|
1660 |
@param {Number|String} ch1 Color channel value
|
|
|
1661 |
@param {Number|String} ch2 Color channel value
|
|
|
1662 |
@param {Number|String} ch3 Color channel value
|
|
|
1663 |
@param {Number|String} ch4 Color channel value
|
|
|
1664 |
|
|
|
1665 |
@function
|
|
|
1666 |
@returns {jsPDF}
|
|
|
1667 |
@methodOf jsPDF#
|
|
|
1668 |
@name setFillColor
|
|
|
1669 |
*/
|
|
|
1670 |
API.setFillColor = function (ch1, ch2, ch3, ch4) {
|
|
|
1671 |
var color;
|
|
|
1672 |
|
|
|
1673 |
if (ch2 === undefined || (ch4 === undefined && ch1 === ch2 === ch3)) {
|
|
|
1674 |
// Gray color space.
|
|
|
1675 |
if (typeof ch1 === 'string') {
|
|
|
1676 |
color = ch1 + ' g';
|
|
|
1677 |
} else {
|
|
|
1678 |
color = f2(ch1 / 255) + ' g';
|
|
|
1679 |
}
|
|
|
1680 |
} else if (ch4 === undefined) {
|
|
|
1681 |
// RGB
|
|
|
1682 |
if (typeof ch1 === 'string') {
|
|
|
1683 |
color = [ch1, ch2, ch3, 'rg'].join(' ');
|
|
|
1684 |
} else {
|
|
|
1685 |
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' ');
|
|
|
1686 |
}
|
|
|
1687 |
} else {
|
|
|
1688 |
// CMYK
|
|
|
1689 |
if (typeof ch1 === 'string') {
|
|
|
1690 |
color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
|
|
|
1691 |
} else {
|
|
|
1692 |
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
|
|
|
1693 |
}
|
|
|
1694 |
}
|
|
|
1695 |
|
|
|
1696 |
out(color);
|
|
|
1697 |
return this;
|
|
|
1698 |
};
|
|
|
1699 |
|
|
|
1700 |
/**
|
|
|
1701 |
Sets the text color for upcoming elements.
|
|
|
1702 |
If only one, first argument is given,
|
|
|
1703 |
treats the value as gray-scale color value.
|
|
|
1704 |
|
|
|
1705 |
@param {Number} r Red channel color value in range 0-255
|
|
|
1706 |
@param {Number} g Green channel color value in range 0-255
|
|
|
1707 |
@param {Number} b Blue channel color value in range 0-255
|
|
|
1708 |
@function
|
|
|
1709 |
@returns {jsPDF}
|
|
|
1710 |
@methodOf jsPDF#
|
|
|
1711 |
@name setTextColor
|
|
|
1712 |
*/
|
|
|
1713 |
API.setTextColor = function (r, g, b) {
|
|
|
1714 |
if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
|
|
|
1715 |
textColor = f3(r / 255) + ' g';
|
|
|
1716 |
} else {
|
|
|
1717 |
textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
|
|
|
1718 |
}
|
|
|
1719 |
return this;
|
|
|
1720 |
};
|
|
|
1721 |
|
|
|
1722 |
/**
|
|
|
1723 |
Is an Object providing a mapping from human-readable to
|
|
|
1724 |
integer flag values designating the varieties of line cap
|
|
|
1725 |
and join styles.
|
|
|
1726 |
|
|
|
1727 |
@returns {Object}
|
|
|
1728 |
@fieldOf jsPDF#
|
|
|
1729 |
@name CapJoinStyles
|
|
|
1730 |
*/
|
|
|
1731 |
API.CapJoinStyles = {
|
|
|
1732 |
0: 0,
|
|
|
1733 |
'butt': 0,
|
|
|
1734 |
'but': 0,
|
|
|
1735 |
'bevel': 0,
|
|
|
1736 |
1: 1,
|
|
|
1737 |
'round': 1,
|
|
|
1738 |
'rounded': 1,
|
|
|
1739 |
'circle': 1,
|
|
|
1740 |
2: 2,
|
|
|
1741 |
'projecting': 2,
|
|
|
1742 |
'project': 2,
|
|
|
1743 |
'square': 2,
|
|
|
1744 |
'milter': 2
|
|
|
1745 |
};
|
|
|
1746 |
|
|
|
1747 |
/**
|
|
|
1748 |
Sets the line cap styles
|
|
|
1749 |
See {jsPDF.CapJoinStyles} for variants
|
|
|
1750 |
|
|
|
1751 |
@param {String|Number} style A string or number identifying the type of line cap
|
|
|
1752 |
@function
|
|
|
1753 |
@returns {jsPDF}
|
|
|
1754 |
@methodOf jsPDF#
|
|
|
1755 |
@name setLineCap
|
|
|
1756 |
*/
|
|
|
1757 |
API.setLineCap = function (style) {
|
|
|
1758 |
var id = this.CapJoinStyles[style];
|
|
|
1759 |
if (id === undefined) {
|
|
|
1760 |
throw new Error("Line cap style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
|
|
|
1761 |
}
|
|
|
1762 |
lineCapID = id;
|
|
|
1763 |
out(id.toString(10) + ' J');
|
|
|
1764 |
|
|
|
1765 |
return this;
|
|
|
1766 |
};
|
|
|
1767 |
|
|
|
1768 |
/**
|
|
|
1769 |
Sets the line join styles
|
|
|
1770 |
See {jsPDF.CapJoinStyles} for variants
|
|
|
1771 |
|
|
|
1772 |
@param {String|Number} style A string or number identifying the type of line join
|
|
|
1773 |
@function
|
|
|
1774 |
@returns {jsPDF}
|
|
|
1775 |
@methodOf jsPDF#
|
|
|
1776 |
@name setLineJoin
|
|
|
1777 |
*/
|
|
|
1778 |
API.setLineJoin = function (style) {
|
|
|
1779 |
var id = this.CapJoinStyles[style];
|
|
|
1780 |
if (id === undefined) {
|
|
|
1781 |
throw new Error("Line join style of '" + style + "' is not recognized. See or extend .CapJoinStyles property for valid styles");
|
|
|
1782 |
}
|
|
|
1783 |
lineJoinID = id;
|
|
|
1784 |
out(id.toString(10) + ' j');
|
|
|
1785 |
|
|
|
1786 |
return this;
|
|
|
1787 |
};
|
|
|
1788 |
|
|
|
1789 |
// Output is both an internal (for plugins) and external function
|
|
|
1790 |
API.output = output;
|
|
|
1791 |
|
|
|
1792 |
/**
|
|
|
1793 |
* Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf')
|
|
|
1794 |
* @param {String} filename The filename including extension.
|
|
|
1795 |
*
|
|
|
1796 |
* @function
|
|
|
1797 |
* @returns {jsPDF}
|
|
|
1798 |
* @methodOf jsPDF#
|
|
|
1799 |
* @name save
|
|
|
1800 |
*/
|
|
|
1801 |
API.save = function (filename) {
|
|
|
1802 |
API.output('save', filename);
|
|
|
1803 |
};
|
|
|
1804 |
|
|
|
1805 |
// applying plugins (more methods) ON TOP of built-in API.
|
|
|
1806 |
// this is intentional as we allow plugins to override
|
|
|
1807 |
// built-ins
|
|
|
1808 |
for (plugin in jsPDF.API) {
|
|
|
1809 |
if (jsPDF.API.hasOwnProperty(plugin)) {
|
|
|
1810 |
if (plugin === 'events' && jsPDF.API.events.length) {
|
|
|
1811 |
(function (events, newEvents) {
|
|
|
1812 |
|
|
|
1813 |
// jsPDF.API.events is a JS Array of Arrays
|
|
|
1814 |
// where each Array is a pair of event name, handler
|
|
|
1815 |
// Events were added by plugins to the jsPDF instantiator.
|
|
|
1816 |
// These are always added to the new instance and some ran
|
|
|
1817 |
// during instantiation.
|
|
|
1818 |
|
|
|
1819 |
var eventname, handler_and_args, i;
|
|
|
1820 |
|
|
|
1821 |
for (i = newEvents.length - 1; i !== -1; i--) {
|
|
|
1822 |
// subscribe takes 3 args: 'topic', function, runonce_flag
|
|
|
1823 |
// if undefined, runonce is false.
|
|
|
1824 |
// users can attach callback directly,
|
|
|
1825 |
// or they can attach an array with [callback, runonce_flag]
|
|
|
1826 |
// that's what the "apply" magic is for below.
|
|
|
1827 |
eventname = newEvents[i][0];
|
|
|
1828 |
handler_and_args = newEvents[i][1];
|
|
|
1829 |
events.subscribe.apply(
|
|
|
1830 |
events,
|
|
|
1831 |
[eventname].concat(
|
|
|
1832 |
typeof handler_and_args === 'function' ?
|
|
|
1833 |
[ handler_and_args ] :
|
|
|
1834 |
handler_and_args
|
|
|
1835 |
)
|
|
|
1836 |
);
|
|
|
1837 |
}
|
|
|
1838 |
}(events, jsPDF.API.events));
|
|
|
1839 |
} else {
|
|
|
1840 |
API[plugin] = jsPDF.API[plugin];
|
|
|
1841 |
}
|
|
|
1842 |
}
|
|
|
1843 |
}
|
|
|
1844 |
|
|
|
1845 |
/////////////////////////////////////////
|
|
|
1846 |
// continuing initilisation of jsPDF Document object
|
|
|
1847 |
/////////////////////////////////////////
|
|
|
1848 |
|
|
|
1849 |
|
|
|
1850 |
// Add the first page automatically
|
|
|
1851 |
addFonts();
|
|
|
1852 |
activeFontKey = 'F1';
|
|
|
1853 |
_addPage();
|
|
|
1854 |
|
|
|
1855 |
events.publish('initialized');
|
|
|
1856 |
|
|
|
1857 |
return API;
|
|
|
1858 |
}
|
|
|
1859 |
|
|
|
1860 |
/**
|
|
|
1861 |
jsPDF.API is a STATIC property of jsPDF class.
|
|
|
1862 |
jsPDF.API is an object you can add methods and properties to.
|
|
|
1863 |
The methods / properties you add will show up in new jsPDF objects.
|
|
|
1864 |
|
|
|
1865 |
One property is prepopulated. It is the 'events' Object. Plugin authors can add topics, callbacks to this object. These will be reassigned to all new instances of jsPDF.
|
|
|
1866 |
Examples:
|
|
|
1867 |
jsPDF.API.events['initialized'] = function(){ 'this' is API object }
|
|
|
1868 |
jsPDF.API.events['addFont'] = function(added_font_object){ 'this' is API object }
|
|
|
1869 |
|
|
|
1870 |
@static
|
|
|
1871 |
@public
|
|
|
1872 |
@memberOf jsPDF
|
|
|
1873 |
@name API
|
|
|
1874 |
|
|
|
1875 |
@example
|
|
|
1876 |
jsPDF.API.mymethod = function(){
|
|
|
1877 |
// 'this' will be ref to internal API object. see jsPDF source
|
|
|
1878 |
// , so you can refer to built-in methods like so:
|
|
|
1879 |
// this.line(....)
|
|
|
1880 |
// this.text(....)
|
|
|
1881 |
}
|
|
|
1882 |
var pdfdoc = new jsPDF()
|
|
|
1883 |
pdfdoc.mymethod() // <- !!!!!!
|
|
|
1884 |
*/
|
|
|
1885 |
jsPDF.API = {'events': []};
|
|
|
1886 |
|
|
|
1887 |
return jsPDF;
|
|
|
1888 |
}());
|