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