      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