# plugins/from_html.js

    /** @preserve
     * jsPDF fromHTML plugin. BETA stage. API subject to change. Needs browser
     * Copyright (c) 2012 Willow Systems Corporation, willow-systems.com
     *               2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
     *               2014 Diego Casorran, https://github.com/diegocr
     *               2014 Daniel Husar, https://github.com/danielhusar
     *               2014 Wolfgang Gassler, https://github.com/woolfg
     *               2014 Steven Spungin, https://github.com/flamenco
     *
     * Permission is hereby granted, free of charge, to any person obtaining
     * a copy of this software and associated documentation files (the
     * "Software"), to deal in the Software without restriction, including
     * without limitation the rights to use, copy, modify, merge, publish,
     * distribute, sublicense, and/or sell copies of the Software, and to
     * permit persons to whom the Software is furnished to do so, subject to
     * the following conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     * ====================================================================
     */

    (function (jsPDFAPI) {
    	var clone,
    	DrillForContent,
    	FontNameDB,
    	FontStyleMap,
    	TextAlignMap,
    	FontWeightMap,
    	FloatMap,
    	ClearMap,
    	GetCSS,
    	PurgeWhiteSpace,
    	Renderer,
    	ResolveFont,
    	ResolveUnitedNumber,
    	UnitedNumberMap,
    	elementHandledElsewhere,
    	images,
    	loadImgs,
    	checkForFooter,
    	process,
    	tableToJson;
    	clone = (function () {
    		return function (obj) {
    			Clone.prototype = obj;
    			return new Clone()
    		};
    		function Clone() {}
    	})();
    	PurgeWhiteSpace = function (array) {
    		var fragment,
    		i,
    		l,
    		lTrimmed,
    		r,
    		rTrimmed,
    		trailingSpace;
    		i = 0;
    		l = array.length;
    		fragment = void 0;
    		lTrimmed = false;
    		rTrimmed = false;
    		while (!lTrimmed && i !== l) {
    			fragment = array[i] = array[i].trimLeft();
    			if (fragment) {
    				lTrimmed = true;
    			}
    			i++;
    		}
    		i = l - 1;
    		while (l && !rTrimmed && i !== -1) {
    			fragment = array[i] = array[i].trimRight();
    			if (fragment) {
    				rTrimmed = true;
    			}
    			i--;
    		}
    		r = /\s+$/g;
    		trailingSpace = true;
    		i = 0;
    		while (i !== l) {
    			// Leave the line breaks intact
    			if (array[i] != "\u2028") {
    				fragment = array[i].replace(/\s+/g, " ");
    				if (trailingSpace) {
    					fragment = fragment.trimLeft();
    				}
    				if (fragment) {
    					trailingSpace = r.test(fragment);
    				}
    				array[i] = fragment;
    			}
    			i++;
    		}
    		return array;
    	};
    	Renderer = function (pdf, x, y, settings) {
    		this.pdf = pdf;
    		this.x = x;
    		this.y = y;
    		this.settings = settings;
    		//list of functions which are called after each element-rendering process
    		this.watchFunctions = [];
    		this.init();
    		return this;
    	};
    	ResolveFont = function (css_font_family_string) {
    		var name,
    		part,
    		parts;
    		name = void 0;
    		parts = css_font_family_string.split(",");
    		part = parts.shift();
    		while (!name && part) {
    			name = FontNameDB[part.trim().toLowerCase()];
    			part = parts.shift();
    		}
    		return name;
    	};
    	ResolveUnitedNumber = function (css_line_height_string) {

    		//IE8 issues
    		css_line_height_string = css_line_height_string === "auto" ? "0px" : css_line_height_string;
    		if (css_line_height_string.indexOf("em") > -1 && !isNaN(Number(css_line_height_string.replace("em", "")))) {
    			css_line_height_string = Number(css_line_height_string.replace("em", "")) * 18.719 + "px";
    		}
    		if (css_line_height_string.indexOf("pt") > -1 && !isNaN(Number(css_line_height_string.replace("pt", "")))) {
    			css_line_height_string = Number(css_line_height_string.replace("pt", "")) * 1.333 + "px";
    		}

    		var normal,
    		undef,
    		value;
    		undef = void 0;
    		normal = 16.00;
    		value = UnitedNumberMap[css_line_height_string];
    		if (value) {
    			return value;
    		}
    		value = {
    			"xx-small"  :  9,
    			"x-small"   : 11,
    			small       : 13,
    			medium      : 16,
    			large       : 19,
    			"x-large"   : 23,
    			"xx-large"  : 28,
    			auto        :  0
    		}[{ css_line_height_string : css_line_height_string }];

    		if (value !== undef) {
    			return UnitedNumberMap[css_line_height_string] = value / normal;
    		}
    		if (value = parseFloat(css_line_height_string)) {
    			return UnitedNumberMap[css_line_height_string] = value / normal;
    		}
    		value = css_line_height_string.match(/([\d\.]+)(px)/);
    		if (value.length === 3) {
    			return UnitedNumberMap[css_line_height_string] = parseFloat(value[1]) / normal;
    		}
    		return UnitedNumberMap[css_line_height_string] = 1;
    	};
    	GetCSS = function (element) {
    		var css,tmp,computedCSSElement;
    		computedCSSElement = (function (el) {
    			var compCSS;
    			compCSS = (function (el) {
    				if (document.defaultView && document.defaultView.getComputedStyle) {
    					return document.defaultView.getComputedStyle(el, null);
    				} else if (el.currentStyle) {
    					return el.currentStyle;
    				} else {
    					return el.style;
    				}
    			})(el);
    			return function (prop) {
    				prop = prop.replace(/-\D/g, function (match) {
    					return match.charAt(1).toUpperCase();
    				});
    				return compCSS[prop];
    			};
    		})(element);
    		css = {};
    		tmp = void 0;
    		css["font-family"] = ResolveFont(computedCSSElement("font-family")) || "times";
    		css["font-style"] = FontStyleMap[computedCSSElement("font-style")] || "normal";
    		css["text-align"] = TextAlignMap[computedCSSElement("text-align")] || "left";
    		tmp = FontWeightMap[computedCSSElement("font-weight")] || "normal";
    		if (tmp === "bold") {
    			if (css["font-style"] === "normal") {
    				css["font-style"] = tmp;
    			} else {
    				css["font-style"] = tmp + css["font-style"];
    			}
    		}
    		css["font-size"] = ResolveUnitedNumber(computedCSSElement("font-size")) || 1;
    		css["line-height"] = ResolveUnitedNumber(computedCSSElement("line-height")) || 1;
    		css["display"] = (computedCSSElement("display") === "inline" ? "inline" : "block");

    		tmp = (css["display"] === "block");
    		css["margin-top"]     = tmp && ResolveUnitedNumber(computedCSSElement("margin-top"))     || 0;
    		css["margin-bottom"]  = tmp && ResolveUnitedNumber(computedCSSElement("margin-bottom"))  || 0;
    		css["padding-top"]    = tmp && ResolveUnitedNumber(computedCSSElement("padding-top"))    || 0;
    		css["padding-bottom"] = tmp && ResolveUnitedNumber(computedCSSElement("padding-bottom")) || 0;
    		css["margin-left"]    = tmp && ResolveUnitedNumber(computedCSSElement("margin-left"))    || 0;
    		css["margin-right"]   = tmp && ResolveUnitedNumber(computedCSSElement("margin-right"))   || 0;
    		css["padding-left"]   = tmp && ResolveUnitedNumber(computedCSSElement("padding-left"))   || 0;
    		css["padding-right"]  = tmp && ResolveUnitedNumber(computedCSSElement("padding-right"))  || 0;

    		css["page-break-before"] = computedCSSElement("page-break-before") || "auto";

    		//float and clearing of floats
    		css["float"] = FloatMap[computedCSSElement("cssFloat")] || "none";
    		css["clear"] = ClearMap[computedCSSElement("clear")] || "none";

    		css["color"]  = computedCSSElement("color");

    		return css;
    	};
    	elementHandledElsewhere = function (element, renderer, elementHandlers) {
    		var handlers,
    		i,
    		isHandledElsewhere,
    		l,
    		t;
    		isHandledElsewhere = false;
    		i = void 0;
    		l = void 0;
    		t = void 0;
    		handlers = elementHandlers["#" + element.id];
    		if (handlers) {
    			if (typeof handlers === "function") {
    				isHandledElsewhere = handlers(element, renderer);
    			} else {
    				i = 0;
    				l = handlers.length;
    				while (!isHandledElsewhere && i !== l) {
    					isHandledElsewhere = handlers[i](element, renderer);
    					i++;
    				}
    			}
    		}
    		handlers = elementHandlers[element.nodeName];
    		if (!isHandledElsewhere && handlers) {
    			if (typeof handlers === "function") {
    				isHandledElsewhere = handlers(element, renderer);
    			} else {
    				i = 0;
    				l = handlers.length;
    				while (!isHandledElsewhere && i !== l) {
    					isHandledElsewhere = handlers[i](element, renderer);
    					i++;
    				}
    			}
    		}
    		return isHandledElsewhere;
    	};
    	tableToJson = function (table, renderer) {
    		var data,
    		headers,
    		i,
    		j,
    		rowData,
    		tableRow,
    		table_obj,
    		table_with,
    		cell,
    		l;
    		data = [];
    		headers = [];
    		i = 0;
    		l = table.rows[0].cells.length;
    		table_with = table.clientWidth;
    		while (i < l) {
    			cell = table.rows[0].cells[i];
    			headers[i] = {
    				name : cell.textContent.toLowerCase().replace(/\s+/g, ''),
    				prompt : cell.textContent.replace(/\r?\n/g, ''),
    				width : (cell.clientWidth / table_with) * renderer.pdf.internal.pageSize.width
    			};
    			i++;
    		}
    		i = 1;
    		while (i < table.rows.length) {
    			tableRow = table.rows[i];
    			rowData = {};
    			j = 0;
    			while (j < tableRow.cells.length) {
    				rowData[headers[j].name] = tableRow.cells[j].textContent.replace(/\r?\n/g, '');
    				j++;
    			}
    			data.push(rowData);
    			i++;
    		}
    		return table_obj = {
    			rows : data,
    			headers : headers
    		};
    	};
    	var SkipNode = {
    		SCRIPT   : 1,
    		STYLE    : 1,
    		NOSCRIPT : 1,
    		OBJECT   : 1,
    		EMBED    : 1,
    		SELECT   : 1
    	};
    	var listCount = 1;
    	DrillForContent = function (element, renderer, elementHandlers) {
    		var cn,
    		cns,
    		fragmentCSS,
    		i,
    		isBlock,
    		l,
    		px2pt,
    		table2json,
    		cb;
    		cns = element.childNodes;
    		cn = void 0;
    		fragmentCSS = GetCSS(element);
    		isBlock = fragmentCSS.display === "block";
    		if (isBlock) {
    			renderer.setBlockBoundary();
    			renderer.setBlockStyle(fragmentCSS);
    		}
    		px2pt = 0.264583 * 72 / 25.4;
    		i = 0;
    		l = cns.length;
    		while (i < l) {
    			cn = cns[i];
    			if (typeof cn === "object") {

    				//execute all watcher functions to e.g. reset floating
    				renderer.executeWatchFunctions(cn);

    				/*** HEADER rendering **/
    				if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
    					var header = cn;
    					//store old top margin
    					var oldMarginTop = renderer.pdf.margins_doc.top;
    					//subscribe for new page event and render header first on every page
    					renderer.pdf.internal.events.subscribe('addPage', function (pageInfo) {
    						//set current y position to old margin
    						renderer.y = oldMarginTop;
    						//render all child nodes of the header element
    						DrillForContent(header, renderer, elementHandlers);
    						//set margin to old margin + rendered header + 10 space to prevent overlapping
    						//important for other plugins (e.g. table) to start rendering at correct position after header
    						renderer.pdf.margins_doc.top = renderer.y + 10;
    						renderer.y += 10;
    					}, false);
    				}

    				if (cn.nodeType === 8 && cn.nodeName === "#comment") {
    					if (~cn.textContent.indexOf("ADD_PAGE")) {
    						renderer.pdf.addPage();
    						renderer.y = renderer.pdf.margins_doc.top;
    					}

    				} else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
    					/*** IMAGE RENDERING ***/
    					var cached_image;
    					if (cn.nodeName === "IMG") {
    						var url = cn.getAttribute("src");
    						cached_image = images[renderer.pdf.sHashCode(url) || url];
    					}
    					if (cached_image) {
    						if ((renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height) && (renderer.y > renderer.pdf.margins_doc.top)) {
    							renderer.pdf.addPage();
    							renderer.y = renderer.pdf.margins_doc.top;
    							//check if we have to set back some values due to e.g. header rendering for new page
    							renderer.executeWatchFunctions(cn);
    						}

    						var imagesCSS = GetCSS(cn);
    						var imageX = renderer.x;
    						var fontToUnitRatio = 12 / renderer.pdf.internal.scaleFactor;

    						//define additional paddings, margins which have to be taken into account for margin calculations
    						var additionalSpaceLeft = (imagesCSS["margin-left"] + imagesCSS["padding-left"])*fontToUnitRatio;
    						var additionalSpaceRight = (imagesCSS["margin-right"] + imagesCSS["padding-right"])*fontToUnitRatio;
    						var additionalSpaceTop = (imagesCSS["margin-top"] + imagesCSS["padding-top"])*fontToUnitRatio;
    						var additionalSpaceBottom = (imagesCSS["margin-bottom"] + imagesCSS["padding-bottom"])*fontToUnitRatio;

    						//if float is set to right, move the image to the right border
    						//add space if margin is set
    						if (imagesCSS['float'] !== undefined && imagesCSS['float'] === 'right') {
    							imageX += renderer.settings.width - cn.width - additionalSpaceRight;
    						} else {
    							imageX +=  additionalSpaceLeft;
    						}

    						renderer.pdf.addImage(cached_image, imageX, renderer.y + additionalSpaceTop, cn.width, cn.height);
    						cached_image = undefined;
    						//if the float prop is specified we have to float the text around the image
    						if (imagesCSS['float'] === 'right' || imagesCSS['float'] === 'left') {
    							//add functiont to set back coordinates after image rendering
    							renderer.watchFunctions.push((function(diffX , thresholdY, diffWidth, el) {
    								//undo drawing box adaptions which were set by floating
    								if (renderer.y >= thresholdY) {
    									renderer.x += diffX;
    									renderer.settings.width += diffWidth;
    									return true;
    								} else if(el && el.nodeType === 1 && !SkipNode[el.nodeName] && renderer.x+el.width > (renderer.pdf.margins_doc.left + renderer.pdf.margins_doc.width)) {
    									renderer.x += diffX;
    									renderer.y = thresholdY;
    									renderer.settings.width += diffWidth;
    									return true;
    								} else {
    									return false;
    								}
    							}).bind(this, (imagesCSS['float'] === 'left') ? -cn.width-additionalSpaceLeft-additionalSpaceRight : 0, renderer.y+cn.height+additionalSpaceTop+additionalSpaceBottom, cn.width));
    							//reset floating by clear:both divs
    							//just set cursorY after the floating element
    							renderer.watchFunctions.push((function(yPositionAfterFloating, pages, el) {
    								if (renderer.y < yPositionAfterFloating && pages === renderer.pdf.internal.getNumberOfPages()) {
    									if (el.nodeType === 1 && GetCSS(el).clear === 'both') {
    										renderer.y = yPositionAfterFloating;
    										return true;
    									} else {
    										return false;
    									}
    								} else {
    									return true;
    								}
    							}).bind(this, renderer.y+cn.height, renderer.pdf.internal.getNumberOfPages()));

    							//if floating is set we decrease the available width by the image width
    							renderer.settings.width -= cn.width+additionalSpaceLeft+additionalSpaceRight;
    							//if left just add the image width to the X coordinate
    							if (imagesCSS['float'] === 'left') {
    								renderer.x += cn.width+additionalSpaceLeft+additionalSpaceRight;
    							}
    						} else {
    						//if no floating is set, move the rendering cursor after the image height
    							renderer.y += cn.height + additionalSpaceTop + additionalSpaceBottom;
    						}

    					/*** TABLE RENDERING ***/
    					} else if (cn.nodeName === "TABLE") {
    						table2json = tableToJson(cn, renderer);
    						renderer.y += 10;
    						renderer.pdf.table(renderer.x, renderer.y, table2json.rows, table2json.headers, {
    							autoSize : false,
    							printHeaders: elementHandlers.printHeaders,
    							margins: renderer.pdf.margins_doc,
    							css: GetCSS(cn)
    						});
    						renderer.y = renderer.pdf.lastCellPos.y + renderer.pdf.lastCellPos.h + 20;
    					} else if (cn.nodeName === "OL" || cn.nodeName === "UL") {
    						listCount = 1;
    						if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
    							DrillForContent(cn, renderer, elementHandlers);
    						}
    						renderer.y += 10;
    					} else if (cn.nodeName === "LI") {
    						var temp = renderer.x;
    						renderer.x += 20 / renderer.pdf.internal.scaleFactor;
    						renderer.y += 3;
    						if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
    							DrillForContent(cn, renderer, elementHandlers);
    						}
    						renderer.x = temp;
    					} else if (cn.nodeName === "BR") {
    						renderer.y += fragmentCSS["font-size"] * renderer.pdf.internal.scaleFactor;
    						renderer.addText("\u2028", clone(fragmentCSS));
    					} else {
    						if (!elementHandledElsewhere(cn, renderer, elementHandlers)) {
    							DrillForContent(cn, renderer, elementHandlers);
    						}
    					}
    				} else if (cn.nodeType === 3) {
    					var value = cn.nodeValue;
    					if (cn.nodeValue && cn.parentNode.nodeName === "LI") {
    						if (cn.parentNode.parentNode.nodeName === "OL") {
    							value = listCount++ + '. ' + value;
    						} else {
    							var fontSize = fragmentCSS["font-size"];
    							var offsetX = (3 - fontSize * 0.75) * renderer.pdf.internal.scaleFactor;
    							var offsetY = fontSize * 0.75 * renderer.pdf.internal.scaleFactor;
    							var radius = fontSize * 1.74 / renderer.pdf.internal.scaleFactor;
    							cb = function (x, y) {
    								this.pdf.circle(x + offsetX, y + offsetY, radius, 'FD');
    							};
    						}
    					}
    					// Only add the text if the text node is in the body element
    					// Add compatibility with IE11
    					if(!!(cn.ownerDocument.body.compareDocumentPosition(cn) & 16)){
    						renderer.addText(value, fragmentCSS);
    					}
    				} else if (typeof cn === "string") {
    					renderer.addText(cn, fragmentCSS);
    				}
    			}
    			i++;
    		}
    		elementHandlers.outY = renderer.y;

    		if (isBlock) {
    			return renderer.setBlockBoundary(cb);
    		}
    	};
    	images = {};
    	loadImgs = function (element, renderer, elementHandlers, cb) {
    		var imgs = element.getElementsByTagName('img'),
    		l = imgs.length, found_images,
    		x = 0;
    		function done() {
    			renderer.pdf.internal.events.publish('imagesLoaded');
    			cb(found_images);
    		}
    		function loadImage(url, width, height) {
    			if (!url)
    				return;
    			var img = new Image();
    			found_images = ++x;
    			img.crossOrigin = '';
    			img.onerror = img.onload = function () {
    				if(img.complete) {
    					//to support data urls in images, set width and height
    					//as those values are not recognized automatically
    					if (img.src.indexOf('data:image/') === 0) {
    						img.width = width || img.width || 0;
    						img.height = height || img.height || 0;
    					}
    					//if valid image add to known images array
    					if (img.width + img.height) {
    						var hash = renderer.pdf.sHashCode(url) || url;
    						images[hash] = images[hash] || img;
    					}
    				}
    				if(!--x) {
    					done();
    				}
    			};
    			img.src = url;
    		}
    		while (l--)
    			loadImage(imgs[l].getAttribute("src"),imgs[l].width,imgs[l].height);
    		return x || done();
    	};
    	checkForFooter = function (elem, renderer, elementHandlers) {
    		//check if we can found a <footer> element
    		var footer = elem.getElementsByTagName("footer");
    		if (footer.length > 0) {

    			footer = footer[0];

    			//bad hack to get height of footer
    			//creat dummy out and check new y after fake rendering
    			var oldOut = renderer.pdf.internal.write;
    			var oldY = renderer.y;
    			renderer.pdf.internal.write = function () {};
    			DrillForContent(footer, renderer, elementHandlers);
    			var footerHeight = Math.ceil(renderer.y - oldY) + 5;
    			renderer.y = oldY;
    			renderer.pdf.internal.write = oldOut;

    			//add 20% to prevent overlapping
    			renderer.pdf.margins_doc.bottom += footerHeight;

    			//Create function render header on every page
    			var renderFooter = function (pageInfo) {
    				var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1;
    				//set current y position to old margin
    				var oldPosition = renderer.y;
    				//render all child nodes of the header element
    				renderer.y = renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom;
    				renderer.pdf.margins_doc.bottom -= footerHeight;

    				//check if we have to add page numbers
    				var spans = footer.getElementsByTagName('span');
    				for (var i = 0; i < spans.length; ++i) {
    					//if we find some span element with class pageCounter, set the page
    					if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1) {
    						spans[i].innerHTML = pageNumber;
    					}
    					//if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
    					if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
    						spans[i].innerHTML = '###jsPDFVarTotalPages###';
    					}
    				}

    				//render footer content
    				DrillForContent(footer, renderer, elementHandlers);
    				//set bottom margin to previous height including the footer height
    				renderer.pdf.margins_doc.bottom += footerHeight;
    				//important for other plugins (e.g. table) to start rendering at correct position after header
    				renderer.y = oldPosition;
    			};

    			//check if footer contains totalPages which should be replace at the disoposal of the document
    			var spans = footer.getElementsByTagName('span');
    			for (var i = 0; i < spans.length; ++i) {
    				if ((" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1) {
    					renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
    				}
    			}

    			//register event to render footer on every new page
    			renderer.pdf.internal.events.subscribe('addPage', renderFooter, false);
    			//render footer on first page
    			renderFooter();

    			//prevent footer rendering
    			SkipNode['FOOTER'] = 1;
    		}
    	};
    	process = function (pdf, element, x, y, settings, callback) {
    		if (!element)
    			return false;
    		if (typeof element !== "string" && !element.parentNode)
    			element = '' + element.innerHTML;
    		if (typeof element === "string") {
    			element = (function (element) {
    				var $frame,
    				$hiddendiv,
    				framename,
    				visuallyhidden;
    				framename = "jsPDFhtmlText" + Date.now().toString() + (Math.random() * 1000).toFixed(0);
    				visuallyhidden = "position: absolute !important;" + "clip: rect(1px 1px 1px 1px); /* IE6, IE7 */" + "clip: rect(1px, 1px, 1px, 1px);" + "padding:0 !important;" + "border:0 !important;" + "height: 1px !important;" + "width: 1px !important; " + "top:auto;" + "left:-100px;" + "overflow: hidden;";
    				$hiddendiv = document.createElement('div');
    				$hiddendiv.style.cssText = visuallyhidden;
    				$hiddendiv.innerHTML = "<iframe style=\"height:1px;width:1px\" name=\"" + framename + "\" />";
    				document.body.appendChild($hiddendiv);
    				$frame = window.frames[framename];
    				$frame.document.open();
    				$frame.document.writeln(element);
    				$frame.document.close();
    				return $frame.document.body;
    			})(element.replace(/<\/?script[^>]*?>/gi, ''));
    		}
    		var r = new Renderer(pdf, x, y, settings), out;

    		// 1. load images
    		// 2. prepare optional footer elements
    		// 3. render content
    		loadImgs.call(this, element, r, settings.elementHandlers, function (found_images) {
    			checkForFooter( element, r, settings.elementHandlers);
    			DrillForContent(element, r, settings.elementHandlers);
    			//send event dispose for final taks (e.g. footer totalpage replacement)
    			r.pdf.internal.events.publish('htmlRenderingFinished');
    			out = r.dispose();
    			if (typeof callback === 'function') callback(out);
    			else if (found_images) console.error('jsPDF Warning: rendering issues? provide a callback to fromHTML!');
    		});
    		return out || {x: r.x, y:r.y};
    	};
    	Renderer.prototype.init = function () {
    		this.paragraph = {
    			text : [],
    			style : []
    		};
    		return this.pdf.internal.write("q");
    	};
    	Renderer.prototype.dispose = function () {
    		this.pdf.internal.write("Q");
    		return {
    			x : this.x,
    			y : this.y,
    			ready:true
    		};
    	};

    	//Checks if we have to execute some watcher functions
    	//e.g. to end text floating around an image
    	Renderer.prototype.executeWatchFunctions = function(el) {
    		var ret = false;
    		var narray = [];
    		if (this.watchFunctions.length > 0) {
    			for(var i=0; i< this.watchFunctions.length; ++i) {
    				if (this.watchFunctions[i](el) === true) {
    					ret = true;
    				} else {
    					narray.push(this.watchFunctions[i]);
    				}
    			}
    			this.watchFunctions = narray;
    		}
    		return ret;
    	};

    	Renderer.prototype.splitFragmentsIntoLines = function (fragments, styles) {
    		var currentLineLength,
    		defaultFontSize,
    		ff,
    		fontMetrics,
    		fontMetricsCache,
    		fragment,
    		fragmentChopped,
    		fragmentLength,
    		fragmentSpecificMetrics,
    		fs,
    		k,
    		line,
    		lines,
    		maxLineLength,
    		style;
    		defaultFontSize = 12;
    		k = this.pdf.internal.scaleFactor;
    		fontMetricsCache = {};
    		ff = void 0;
    		fs = void 0;
    		fontMetrics = void 0;
    		fragment = void 0;
    		style = void 0;
    		fragmentSpecificMetrics = void 0;
    		fragmentLength = void 0;
    		fragmentChopped = void 0;
    		line = [];
    		lines = [line];
    		currentLineLength = 0;
    		maxLineLength = this.settings.width;
    		while (fragments.length) {
    			fragment = fragments.shift();
    			style = styles.shift();
    			if (fragment) {
    				ff = style["font-family"];
    				fs = style["font-style"];
    				fontMetrics = fontMetricsCache[ff + fs];
    				if (!fontMetrics) {
    					fontMetrics = this.pdf.internal.getFont(ff, fs).metadata.Unicode;
    					fontMetricsCache[ff + fs] = fontMetrics;
    				}
    				fragmentSpecificMetrics = {
    					widths : fontMetrics.widths,
    					kerning : fontMetrics.kerning,
    					fontSize : style["font-size"] * defaultFontSize,
    					textIndent : currentLineLength
    				};
    				fragmentLength = this.pdf.getStringUnitWidth(fragment, fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
    				if (fragment == "\u2028") {
    					line = [];
    					lines.push(line);
    				} else if (currentLineLength + fragmentLength > maxLineLength) {
    					fragmentChopped = this.pdf.splitTextToSize(fragment, maxLineLength, fragmentSpecificMetrics);
    					line.push([fragmentChopped.shift(), style]);
    					while (fragmentChopped.length) {
    						line = [[fragmentChopped.shift(), style]];
    						lines.push(line);
    					}
    					currentLineLength = this.pdf.getStringUnitWidth(line[0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
    				} else {
    					line.push([fragment, style]);
    					currentLineLength += fragmentLength;
    				}
    			}
    		}

    		//if text alignment was set, set margin/indent of each line
    		if (style['text-align'] !== undefined && (style['text-align'] === 'center' || style['text-align'] === 'right' || style['text-align'] === 'justify')) {
    			for (var i = 0; i < lines.length; ++i) {
    				var length = this.pdf.getStringUnitWidth(lines[i][0][0], fragmentSpecificMetrics) * fragmentSpecificMetrics.fontSize / k;
    				//if there is more than on line we have to clone the style object as all lines hold a reference on this object
    				if (i > 0) {
    					lines[i][0][1] = clone(lines[i][0][1]);
    				}
    				var space = (maxLineLength - length);

    				if (style['text-align'] === 'right') {
    					lines[i][0][1]['margin-left'] = space;
    					//if alignment is not right, it has to be center so split the space to the left and the right
    				} else if (style['text-align'] === 'center') {
    					lines[i][0][1]['margin-left'] = space / 2;
    					//if justify was set, calculate the word spacing and define in by using the css property
    				} else if (style['text-align'] === 'justify') {
    					var countSpaces = lines[i][0][0].split(' ').length - 1;
    					lines[i][0][1]['word-spacing'] = space / countSpaces;
    					//ignore the last line in justify mode
    					if (i === (lines.length - 1)) {
    						lines[i][0][1]['word-spacing'] = 0;
    					}
    				}
    			}
    		}

    		return lines;
    	};
    	Renderer.prototype.RenderTextFragment = function (text, style) {
    		var defaultFontSize,
    		font,
    		maxLineHeight;

    		maxLineHeight = 0;
    		defaultFontSize = 12;

    		if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {
    			this.pdf.internal.write("ET", "Q");
    			this.pdf.addPage();
    			this.y = this.pdf.margins_doc.top;
    			this.pdf.internal.write("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), style.color, "Td");
    			//move cursor by one line on new page
    			maxLineHeight = Math.max(maxLineHeight, style["line-height"], style["font-size"]);
    			this.pdf.internal.write(0, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
    		}

    		font = this.pdf.internal.getFont(style["font-family"], style["font-style"]);

    		// text color
    		var pdfTextColor = this.getPdfColor(style["color"]);
    		if (pdfTextColor !== this.lastTextColor)
    		{
    			this.pdf.internal.write(pdfTextColor);
    			this.lastTextColor = pdfTextColor;
    		}

    		//set the word spacing for e.g. justify style
    		if (style['word-spacing'] !== undefined && style['word-spacing'] > 0) {
    			this.pdf.internal.write(style['word-spacing'].toFixed(2), "Tw");
    		}

    		this.pdf.internal.write("/" + font.id, (defaultFontSize * style["font-size"]).toFixed(2), "Tf", "(" + this.pdf.internal.pdfEscape(text) + ") Tj");

    		//set the word spacing back to neutral => 0
    		if (style['word-spacing'] !== undefined) {
    			this.pdf.internal.write(0, "Tw");
    		}
    	};

    	// Accepts #FFFFFF, rgb(int,int,int), or CSS Color Name
    	Renderer.prototype.getPdfColor = function(style) {
    		var textColor;
    		var r,g,b;

    		var rx = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/;
    		var m = rx.exec(style);
    		if (m != null){
    			r = parseInt(m[1]);
    			g = parseInt(m[2]);
    			b = parseInt(m[3]);
    		}
    		else{
    			if (style.charAt(0) != '#') {
    				style = CssColors.colorNameToHex(style);
    				if (!style) {
    					style = '#000000';
    				}
    			}
    			r = style.substring(1, 3);
    			r = parseInt(r, 16);
    			g = style.substring(3, 5);
    			g = parseInt(g, 16);
    			b = style.substring(5, 7);
    			b = parseInt(b, 16);
    		}

    		if ((typeof r === 'string') && /^#[0-9A-Fa-f]{6}$/.test(r)) {
    			var hex = parseInt(r.substr(1), 16);
    			r = (hex >> 16) & 255;
    			g = (hex >> 8) & 255;
    			b = (hex & 255);
    		}

    		var f3 = this.f3;
    		if ((r === 0 && g === 0 && b === 0) || (typeof g === 'undefined')) {
    			textColor = f3(r / 255) + ' g';
    		} else {
    			textColor = [f3(r / 255), f3(g / 255), f3(b / 255), 'rg'].join(' ');
    		}
    		return textColor;
    	};

    	Renderer.prototype.f3 = function(number) {
    		return number.toFixed(3); // Ie, %.3f
    	},

    	Renderer.prototype.renderParagraph = function (cb) {
    		var blockstyle,
    		defaultFontSize,
    		fontToUnitRatio,
    		fragments,
    		i,
    		l,
    		line,
    		lines,
    		maxLineHeight,
    		out,
    		paragraphspacing_after,
    		paragraphspacing_before,
    		priorblockstyle,
    		styles,
    		fontSize;
    		fragments = PurgeWhiteSpace(this.paragraph.text);
    		styles = this.paragraph.style;
    		blockstyle = this.paragraph.blockstyle;
    		priorblockstyle = this.paragraph.priorblockstyle || {};
    		this.paragraph = {
    			text : [],
    			style : [],
    			blockstyle : {},
    			priorblockstyle : blockstyle
    		};
    		if (!fragments.join("").trim()) {
    			return;
    		}
    		lines = this.splitFragmentsIntoLines(fragments, styles);
    		line = void 0;
    		maxLineHeight = void 0;
    		defaultFontSize = 12;
    		fontToUnitRatio = defaultFontSize / this.pdf.internal.scaleFactor;
    		this.priorMarginBottom =  this.priorMarginBottom || 0;
    		paragraphspacing_before = (Math.max((blockstyle["margin-top"] || 0) - this.priorMarginBottom, 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
    		paragraphspacing_after = ((blockstyle["margin-bottom"] || 0) + (blockstyle["padding-bottom"] || 0)) * fontToUnitRatio;
    		this.priorMarginBottom =  blockstyle["margin-bottom"] || 0;

    		if (blockstyle['page-break-before'] === 'always'){
    			this.pdf.addPage();
    			this.y = 0;
    			paragraphspacing_before = ((blockstyle["margin-top"] || 0) + (blockstyle["padding-top"] || 0)) * fontToUnitRatio;
    		}

    		out = this.pdf.internal.write;
    		i = void 0;
    		l = void 0;
    		this.y += paragraphspacing_before;
    		out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");

    		//stores the current indent of cursor position
    		var currentIndent = 0;

    		while (lines.length) {
    			line = lines.shift();
    			maxLineHeight = 0;
    			i = 0;
    			l = line.length;
    			while (i !== l) {
    				if (line[i][0].trim()) {
    					maxLineHeight = Math.max(maxLineHeight, line[i][1]["line-height"], line[i][1]["font-size"]);
    					fontSize = line[i][1]["font-size"] * 7;
    				}
    				i++;
    			}
    			//if we have to move the cursor to adapt the indent
    			var indentMove = 0;
    			var wantedIndent = 0;
    			//if a margin was added (by e.g. a text-alignment), move the cursor
    			if (line[0][1]["margin-left"] !== undefined && line[0][1]["margin-left"] > 0) {
    				wantedIndent = this.pdf.internal.getCoordinateString(line[0][1]["margin-left"]);
    				indentMove = wantedIndent - currentIndent;
    				currentIndent = wantedIndent;
    			}
    			var indentMore = (Math.max(blockstyle["margin-left"] || 0, 0)) * fontToUnitRatio;
    			//move the cursor
    			out(indentMove + indentMore, (-1 * defaultFontSize * maxLineHeight).toFixed(2), "Td");
    			i = 0;
    			l = line.length;
    			while (i !== l) {
    				if (line[i][0]) {
    					this.RenderTextFragment(line[i][0], line[i][1]);
    				}
    				i++;
    			}
    			this.y += maxLineHeight * fontToUnitRatio;

    			//if some watcher function was executed successful, so e.g. margin and widths were changed,
    			//reset line drawing and calculate position and lines again
    			//e.g. to stop text floating around an image
    			if (this.executeWatchFunctions(line[0][1]) && lines.length > 0) {
    				var localFragments = [];
    				var localStyles = [];
    				//create fragment array of
    				lines.forEach(function(localLine) {
    					var i = 0;
    					var l = localLine.length;
    					while (i !== l) {
    						if (localLine[i][0]) {
    							localFragments.push(localLine[i][0]+' ');
    							localStyles.push(localLine[i][1]);
    						}
    						++i;
    					}
    				});
    				//split lines again due to possible coordinate changes
    				lines = this.splitFragmentsIntoLines(PurgeWhiteSpace(localFragments), localStyles);
    				//reposition the current cursor
    				out("ET", "Q");
    				out("q", "BT 0 g", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
    			}

    		}
    		if (cb && typeof cb === "function") {
    			cb.call(this, this.x - 9, this.y - fontSize / 2);
    		}
    		out("ET", "Q");
    		return this.y += paragraphspacing_after;
    	};
    	Renderer.prototype.setBlockBoundary = function (cb) {
    		return this.renderParagraph(cb);
    	};
    	Renderer.prototype.setBlockStyle = function (css) {
    		return this.paragraph.blockstyle = css;
    	};
    	Renderer.prototype.addText = function (text, css) {
    		this.paragraph.text.push(text);
    		return this.paragraph.style.push(css);
    	};
    	FontNameDB = {
    		helvetica         : "helvetica",
    		"sans-serif"      : "helvetica",
    		"times new roman" : "times",
    		serif             : "times",
    		times             : "times",
    		monospace         : "courier",
    		courier           : "courier"
    	};
    	FontWeightMap = {
    		100 : "normal",
    		200 : "normal",
    		300 : "normal",
    		400 : "normal",
    		500 : "bold",
    		600 : "bold",
    		700 : "bold",
    		800 : "bold",
    		900 : "bold",
    		normal  : "normal",
    		bold    : "bold",
    		bolder  : "bold",
    		lighter : "normal"
    	};
    	FontStyleMap = {
    		normal  : "normal",
    		italic  : "italic",
    		oblique : "italic"
    	};
    	TextAlignMap = {
    		left    : "left",
    		right   : "right",
    		center  : "center",
    		justify : "justify"
    	};
    	FloatMap = {
    		none : 'none',
    		right: 'right',
    		left: 'left'
    	};
    	ClearMap = {
    	  none : 'none',
    	  both : 'both'
    	};
    	UnitedNumberMap = {
    		normal : 1
    	};
    	/**
    	 * Converts HTML-formatted text into formatted PDF text.
    	 *
    	 * Notes:
    	 * 2012-07-18
    	 * Plugin relies on having browser, DOM around. The HTML is pushed into dom and traversed.
    	 * Plugin relies on jQuery for CSS extraction.
    	 * Targeting HTML output from Markdown templating, which is a very simple
    	 * markup - div, span, em, strong, p. No br-based paragraph separation supported explicitly (but still may work.)
    	 * Images, tables are NOT supported.
    	 *
    	 * @public
    	 * @function
    	 * @param HTML {String or DOM Element} HTML-formatted text, or pointer to DOM element that is to be rendered into PDF.
    	 * @param x {Number} starting X coordinate in jsPDF instance's declared units.
    	 * @param y {Number} starting Y coordinate in jsPDF instance's declared units.
    	 * @param settings {Object} Additional / optional variables controlling parsing, rendering.
    	 * @returns {Object} jsPDF instance
    	 */
    	jsPDFAPI.fromHTML = function (HTML, x, y, settings, callback, margins) {
    		"use strict";

    		this.margins_doc = margins || {
    			top : 0,
    			bottom : 0
    		};
    		if (!settings)
    			settings = {};
    		if (!settings.elementHandlers)
    			settings.elementHandlers = {};

    		return process(this, HTML, isNaN(x) ? 4 : x, isNaN(y) ? 4 : y, settings, callback);
    	};
    })(jsPDF.API);