/** * 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. *
* This plugin current supports
*
  • Goto Page (set pageNumber and top in options) *
  • Goto Name (set name and top in options) *
  • Goto URL (set url in options) *

    * 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. *

    *

    * Links, Text, Popup, and FreeText are supported. *

    *

    * Options In PDF spec Not Implemented Yet *

  • link border *
  • named target *
  • page coordinates *
  • destination page scaling and layout *
  • actions other than URL and GotoPage *
  • background / hover actions *

    */ /* 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 pageNumber. */ 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 = '<>'; 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 = '<>'; } else if (anno.options.pageNumber) { // first page is 0 var info = this.internal.getPageInfo(anno.options.pageNumber); line = '< pageNumber or url [required] *

    If pageNumber is specified, top and zoom may also be specified

    */ 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' }); }; /** * valid options *
  • pageNumber or url [required] *

    If pageNumber is specified, top and zoom may also be specified

    */ 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.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);