# plugins/annotations.js

    /**
     * jsPDF Annotations PlugIn
     * Copyright (c) 2014 Steven Spungin (TwelveTone LLC)  steven@twelvetone.tv
     *
     * Licensed under the MIT License.
     * http://opensource.org/licenses/mit-license
     */

    /**
     * There are many types of annotations in a PDF document. Annotations are placed
     * on a page at a particular location. They are not 'attached' to an object.
     * <br />
     * This plugin current supports <br />
     * <li> Goto Page (set pageNumber and top in options)
     * <li> Goto Name (set name and top in options)
     * <li> Goto URL (set url in options)
     * <p>
     * 	The destination magnification factor can also be specified when goto is a page number or a named destination. (see documentation below)
     *  (set magFactor in options).  XYZ is the default.
     * </p>
     * <p>
     *  Links, Text, Popup, and FreeText are supported.
     * </p>
     * <p>
     * Options In PDF spec Not Implemented Yet
     * <li> link border
     * <li> named target
     * <li> page coordinates
     * <li> destination page scaling and layout
     * <li> actions other than URL and GotoPage
     * <li> background / hover actions
     * </p>
     */

    /*
        Destination Magnification Factors
        See PDF 1.3 Page 386 for meanings and options

        [supported]
    	XYZ (options; left top zoom)
    	Fit (no options)
    	FitH (options: top)
    	FitV (options: left)

    	[not supported]
    	FitR
    	FitB
    	FitBH
    	FitBV
     */

    (function(jsPDFAPI) {
    	'use strict';

    	var annotationPlugin = {

    		/**
    		 * An array of arrays, indexed by <em>pageNumber</em>.
    		 */
    		annotations : [],

    		f2 : function(number) {
    			return number.toFixed(2);
    		},

    		notEmpty : function(obj) {
    			if (typeof obj != 'undefined') {
    				if (obj != '') {
    					return true;
    				}
    			}
    		}
    	};

    	jsPDF.API.annotationPlugin = annotationPlugin;

    	jsPDF.API.events.push([ 'addPage', function(info) {
    		this.annotationPlugin.annotations[info.pageNumber] = [];
    	} ]);

    	jsPDFAPI.events.push([ 'putPage', function(info) {
    		//TODO store annotations in pageContext so reorder/remove will not affect them.
    		var pageAnnos = this.annotationPlugin.annotations[info.pageNumber];

    		var found = false;
    		for (var a = 0; a < pageAnnos.length && !found; a++) {
    			var anno = pageAnnos[a];
    			switch (anno.type) {
    			case 'link':
    				if (annotationPlugin.notEmpty(anno.options.url) || annotationPlugin.notEmpty(anno.options.pageNumber)) {
    					found = true;
    					break;
    				}
                case 'reference':
    			case 'text':
    			case 'freetext':
    				found = true;
    				break;
    			}
    		}
    		if (found == false) {
    			return;
    		}

    		this.internal.write("/Annots [");
    		var f2 = this.annotationPlugin.f2;
    		var k = this.internal.scaleFactor;
    		var pageHeight = this.internal.pageSize.height;
    		var pageInfo = this.internal.getPageInfo(info.pageNumber);
    		for (var a = 0; a < pageAnnos.length; a++) {
    			var anno = pageAnnos[a];

    			switch (anno.type) {
                case 'reference':
                    // References to Widget Anotations (for AcroForm Fields)
                    this.internal.write(' ' + anno.object.objId + ' 0 R ');
    				break;
    			case 'text':
    				// Create a an object for both the text and the popup
    				var objText = this.internal.newAdditionalObject();
    				var objPopup = this.internal.newAdditionalObject();

    				var title = anno.title || 'Note';
    				var rect = "/Rect [" + f2(anno.bounds.x * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + " " + f2((anno.bounds.x + anno.bounds.w) * k) + " " + f2((pageHeight - anno.bounds.y) * k) + "] ";
    				line = '<</Type /Annot /Subtype /' + 'Text' + ' ' + rect + '/Contents (' + anno.contents + ')';
    				line += ' /Popup ' + objPopup.objId + " 0 R";
    				line += ' /P ' + pageInfo.objId + " 0 R";
    				line += ' /T (' + title + ') >>';
    				objText.content = line;

    				var parent = objText.objId + ' 0 R';
    				var popoff = 30;
    				var rect = "/Rect [" + f2((anno.bounds.x + popoff) * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + " " + f2((anno.bounds.x + anno.bounds.w + popoff) * k) + " " + f2((pageHeight - anno.bounds.y) * k) + "] ";
    				//var rect2 = "/Rect [" + f2(anno.bounds.x * k) + " " + f2((pageHeight - anno.bounds.y) * k) + " " + f2(anno.bounds.x + anno.bounds.w * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + "] ";
    				line = '<</Type /Annot /Subtype /' + 'Popup' + ' ' + rect + ' /Parent ' + parent;
    				if (anno.open) {
    					line += ' /Open true';
    				}
    				line += ' >>';
    				objPopup.content = line;

    				this.internal.write(objText.objId, '0 R', objPopup.objId, '0 R');

    				break;
    			case 'freetext':
    				var rect = "/Rect [" + f2(anno.bounds.x * k) + " " + f2((pageHeight - anno.bounds.y) * k) + " " + f2(anno.bounds.x + anno.bounds.w * k) + " " + f2(pageHeight - (anno.bounds.y + anno.bounds.h) * k) + "] ";
    				var color = anno.color || '#000000';
    				line = '<</Type /Annot /Subtype /' + 'FreeText' + ' ' + rect + '/Contents (' + anno.contents + ')';
    				line += ' /DS(font: Helvetica,sans-serif 12.0pt; text-align:left; color:#' + color + ')';
    				line += ' /Border [0 0 0]';
    				line += ' >>';
    				this.internal.write(line);
    				break;
    			case 'link':
    				if (anno.options.name) {
    					var loc = this.annotations._nameMap[anno.options.name];
    					anno.options.pageNumber = loc.page;
    					anno.options.top = loc.y;
    				} else {
    					if (!anno.options.top) {
    						anno.options.top = 0;
    					}
    				}

    				var rect = "/Rect [" + f2(anno.x * k) + " " + f2((pageHeight - anno.y) * k) + " " + f2((anno.x + anno.w) * k) + " " + f2((pageHeight - (anno.y + anno.h)) * k) + "] ";

    				var line = '';
    				if (anno.options.url) {
    					line = '<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /A <</S /URI /URI (' + anno.options.url + ') >>';
    				} else if (anno.options.pageNumber) {
    					// first page is 0
    					var info = this.internal.getPageInfo(anno.options.pageNumber);
    					line = '<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /Dest [' + info.objId + " 0 R";
    					anno.options.magFactor = anno.options.magFactor || "XYZ";
    					switch (anno.options.magFactor) {
    					case 'Fit':
    						line += ' /Fit]';
    						break;
    					case 'FitH':
    						//anno.options.top = anno.options.top || f2(pageHeight * k);
    						line += ' /FitH ' + anno.options.top + ']';
    						break;
    					case 'FitV':
    						anno.options.left = anno.options.left || 0;
    						line += ' /FitV ' + anno.options.left + ']';
    						break;
    					case 'XYZ':
    					default:
    						var top = f2((pageHeight - anno.options.top) * k);// || f2(pageHeight * k);
    						anno.options.left = anno.options.left || 0;
    						// 0 or null zoom will not change zoom factor
    						if (typeof anno.options.zoom === 'undefined') {
    							anno.options.zoom = 0;
    						}
    						line += ' /XYZ ' + anno.options.left + ' ' + top + ' ' + anno.options.zoom + ']';
    						break;
    					}
    				} else {
    					// TODO error - should not be here
    				}
    				if (line != '') {
    					line += " >>";
    					this.internal.write(line);
    				}
    				break;
    			}

    		}
    		this.internal.write("]");
    	} ]);

    	jsPDFAPI.createAnnotation = function(options) {
    		switch (options.type) {
    		case 'link':
    			this.link(options.bounds.x, options.bounds.y, options.bounds.w, options.bounds.h, options);
    			break;
    		case 'text':
    		case 'freetext':
    			this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push(options);
    			break;
    		}
    	}

    	/**
    	 * valid options
    	 * <li> pageNumber or url [required]
    	 * <p>If pageNumber is specified, top and zoom may also be specified</p>
    	 */
    	jsPDFAPI.link = function(x,y,w,h,options) {
    		'use strict';
    		this.annotationPlugin.annotations[this.internal.getCurrentPageInfo().pageNumber].push({
    			x : x,
    			y : y,
    			w : w,
    			h : h,
    			options : options,
    			type : 'link'
    		});
    	};

    	/**
    	 * Currently only supports single line text.
    	 * Returns the width of the text/link
    	 */
    	jsPDFAPI.textWithLink = function(text,x,y,options) {
    		'use strict';
    		var width = this.getTextWidth(text);
    		var height = this.internal.getLineHeight() / this.internal.scaleFactor;
    		this.text(text, x, y);
    		//TODO We really need the text baseline height to do this correctly.
    		// Or ability to draw text on top, bottom, center, or baseline.
    		y += height * .2;
    		this.link(x, y - height, width, height, options);
    		return width;
    	};

    	//TODO move into external library
    	jsPDFAPI.getTextWidth = function(text) {
    		'use strict';
    		var fontSize = this.internal.getFontSize();
    		var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor;
    		return txtWidth;
    	};

    	//TODO move into external library
    	jsPDFAPI.getLineHeight = function() {
    		return this.internal.getLineHeight();
    	};

    	return this;

    })(jsPDF.API);