From 94c5b74d57a6a880ac44ef5673aef8c85501670c Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Sun, 9 Nov 2014 11:53:25 -0500 Subject: [PATCH 1/9] Initial support for annotations (links, etc.) --- jspdf.js | 8 ++- jspdf.plugin.annotations.js | 124 ++++++++++++++++++++++++++++++++++++ test/test_annotation.html | 99 ++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 jspdf.plugin.annotations.js create mode 100644 test/test_annotation.html diff --git a/jspdf.js b/jspdf.js index f37206170..096208479 100644 --- a/jspdf.js +++ b/jspdf.js @@ -17,6 +17,7 @@ * 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria * 2014 James Makes, https://github.com/dollaruw * 2014 Diego Casorran, https://github.com/diegocr + * 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 @@ -39,7 +40,7 @@ * * Contributor(s): * siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango, - * kim3er, mfo, alnorth, + * kim3er, mfo, alnorth, Flamenco */ /** @@ -251,7 +252,10 @@ var jsPDF = (function(global) { out('/Parent 1 0 R'); out('/Resources 2 0 R'); out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']'); - out('/Contents ' + (objectNumber + 1) + ' 0 R>>'); + out('/Contents ' + (objectNumber + 1) + ' 0 R'); + // Added for annotation plugin + events.publish('render/page', {pageNumber:n,page:pages[n]}); + out('>>'); out('endobj'); // Page content diff --git a/jspdf.plugin.annotations.js b/jspdf.plugin.annotations.js new file mode 100644 index 000000000..cff07ccad --- /dev/null +++ b/jspdf.plugin.annotations.js @@ -0,0 +1,124 @@ +/** + * 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 in options) + *
  • Goto URL (set url in options) + * + *

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

    + */ + +(function(jsPDFAPI) { + 'use strict'; + + jsPDFAPI.initAnnotationPlugin = function() { + this.annotationPlugin = {}; + + this.annotationPlugin.annotations = []; + // TODO remove this after we find a way to subscribe before the + // first page is created. + this.annotationPlugin.annotations[1] = []; + + this.annotationPlugin.f2 = function(number) { + return number.toFixed(2); + }; + + this.internal.events.subscribe('addPage', function(info) { + this.annotationPlugin.annotations[info.pageNumber] = []; + }); + + this.internal.events.subscribe('render/page', function(info) { + var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; + + var found = false; + for (var a = 0; a < pageAnnos.length; a++) { + if (pageAnnos[a].type === 'link'){ + found = true; + break; + } + } + if (found == false){ + return; + } + + this.internal.write("/Annots ["); + for (var a = 0; a < pageAnnos.length; a++) { + var anno = pageAnnos[a]; + var k = this.internal.scaleFactor; + var pageHeight = this.internal.pageSize.height; + var rect = "/Rect [" + this.annotationPlugin.f2(anno.x * k) + " " + this.annotationPlugin.f2((pageHeight - anno.y) * k) + " " + this.annotationPlugin.f2(anno.x + anno.w * k) + " " + this.annotationPlugin.f2(pageHeight - (anno.y + anno.h) * k) + "] "; + if (anno.options.url) { + this.internal.write('<> >>') + } else if (anno.options.pageNumber) { + // first page is 0 + this.internal.write('<>') + } else { + // TODO error - not supported or not indicated + } + } + this.internal.write("]"); + }); + }; + + /** + * An array of arrays, indexed by pageNumber. + */ + // this.internal.annotations = []; + /** + * valid options + *
  • pageNumber or url [required] + */ + jsPDFAPI.link = function(x,y,w,h,options) { + 'use strict'; + this.annotationPlugin.annotations[this.internal.getNumberOfPages()].push({ + x : x, + y : y, + w : w, + h : h, + options : options, + type : 'link' + }); + }; + + /** + * Currently only supports single line text. + */ + jsPDFAPI.textWithLink = function(text,x,y,options) { + 'use strict'; + var width = this.getTextWidth(text); + var height = this.internal.getLineHeight(); + this.text(text, x, y); + this.link(x, y - height, width, height, options); + return this; + }; + + jsPDFAPI.getTextWidth = function(text) { + 'use strict'; + var fontSize = this.internal.getFontSize(); + var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor; + return txtWidth; + }; + + jsPDFAPI.getLineHeight = function() { + return this.internal.getLineHeight(); + }; + + return this; +})(jsPDF.API); diff --git a/test/test_annotation.html b/test/test_annotation.html new file mode 100644 index 000000000..7e7a2bb18 --- /dev/null +++ b/test/test_annotation.html @@ -0,0 +1,99 @@ + + + + + + + +Annotaion Test + + + + + + + + + + + + + +
    
    +	
    +
    +
    
    From 970f0fd13ca85214aa0fed9b9d3c497ca5435663 Mon Sep 17 00:00:00 2001
    From: Steven Spungin 
    Date: Mon, 10 Nov 2014 07:55:42 -0500
    Subject: [PATCH 2/9] removed hardcoded url added parameter check to link
     options
    
    ---
     jspdf.plugin.annotations.js | 27 +++++++++++++++++++--------
     1 file changed, 19 insertions(+), 8 deletions(-)
    
    diff --git a/jspdf.plugin.annotations.js b/jspdf.plugin.annotations.js
    index cff07ccad..7b53bd658 100644
    --- a/jspdf.plugin.annotations.js
    +++ b/jspdf.plugin.annotations.js
    @@ -25,6 +25,14 @@
      * 

    */ +function notEmpty(obj) { + if (typeof obj != 'undefined') { + if (obj != '') { + return true; + } + } +} + (function(jsPDFAPI) { 'use strict'; @@ -46,15 +54,18 @@ this.internal.events.subscribe('render/page', function(info) { var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; - + var found = false; for (var a = 0; a < pageAnnos.length; a++) { - if (pageAnnos[a].type === 'link'){ - found = true; - break; + var anno = pageAnnos[a]; + if (anno.type === 'link') { + if (notEmpty(anno.options.url) || notEmpty(anno.options.pageNumber)) { + found = true; + break; + } } } - if (found == false){ + if (found == false) { return; } @@ -65,12 +76,12 @@ var pageHeight = this.internal.pageSize.height; var rect = "/Rect [" + this.annotationPlugin.f2(anno.x * k) + " " + this.annotationPlugin.f2((pageHeight - anno.y) * k) + " " + this.annotationPlugin.f2(anno.x + anno.w * k) + " " + this.annotationPlugin.f2(pageHeight - (anno.y + anno.h) * k) + "] "; if (anno.options.url) { - this.internal.write('<> >>') + this.internal.write('<> >>') } else if (anno.options.pageNumber) { // first page is 0 - this.internal.write('<>') + this.internal.write('<>') } else { - // TODO error - not supported or not indicated + // TODO error - should not be here } } this.internal.write("]"); From a6d7af4f380f5a680ef10bf9684cc525717cc010 Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Tue, 11 Nov 2014 11:39:24 -0500 Subject: [PATCH 3/9] Initial support for outlines (table of contents) --- jspdf.js | 20 ++++- jspdf.plugin.outline.js | 162 ++++++++++++++++++++++++++++++++++++++++ test/test_outline.html | 88 ++++++++++++++++++++++ 3 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 jspdf.plugin.outline.js create mode 100644 test/test_outline.html diff --git a/jspdf.js b/jspdf.js index 096208479..5a85b391d 100644 --- a/jspdf.js +++ b/jspdf.js @@ -229,6 +229,17 @@ var jsPDF = (function(global) { out(objectNumber + ' 0 obj'); return objectNumber; }, + // Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data + newObjectDeferred = function() { + objectNumber++; + offsets[objectNumber] = function(){ + return content_length; + }; + return objectNumber; + }, + newObjectDeferredBegin = function(oid) { + offsets[oid] = content_length; + }, putStream = function(str) { out('stream'); out(str); @@ -770,7 +781,12 @@ var jsPDF = (function(global) { out('0 ' + (objectNumber + 1)); out(p+' 65535 f '); for (i = 1; i <= objectNumber; i++) { - out((p + offsets[i]).slice(-10) + ' 00000 n '); + var offset = offsets[i]; + if (typeof offset === 'function'){ + out((p + offsets[i]()).slice(-10) + ' 00000 n '); + }else{ + out((p + offsets[i]).slice(-10) + ' 00000 n '); + } } // Trailer out('trailer'); @@ -922,6 +938,8 @@ var jsPDF = (function(global) { }, 'collections' : {}, 'newObject' : newObject, + 'newObjectDeferred' : newObjectDeferred, + 'newObjectDeferredBegin' : newObjectDeferredBegin, 'putStream' : putStream, 'events' : events, // ratio that you use in multiplication of a given "size" number to arrive to 'point' diff --git a/jspdf.plugin.outline.js b/jspdf.plugin.outline.js new file mode 100644 index 000000000..f73081e7e --- /dev/null +++ b/jspdf.plugin.outline.js @@ -0,0 +1,162 @@ +/** + * jsPDF Outline PlugIn + * Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv + * + * Licensed under the MIT License. + * http://opensource.org/licenses/mit-license + */ + +/** + * Generates a PDF Outline + */ +; +(function(jsPDFAPI) { + 'use strict'; + + jsPDFAPI.initOutlinePlugin = function() { + + this.outline = {}; + this.outline.root = { + children : [] + }; + + this.internal.events.subscribe('postPutResources', function() { + if (this.outline.root.children.length > 0) { + var lines = this.outline.render().split(/\r\n/); + for (var int = 0; int < lines.length; int++) { + var line = lines[int]; + if (line.endsWith('obj')){ + var oid = line.split(' ')[0]; + this.internal.newObjectDeferredBegin(oid); + } + this.internal.write(line); + } + } + }); + + this.internal.events.subscribe('putCatalog', function() { + if (this.outline.root.children.length > 0) { + this.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); + } + }); + + /** + * Options: pageNumber + */ + this.outline.add = function(parent,title,options) { + var item = { + title : title, + options : options, + children : [] + }; + if (parent == null) { + parent = this.root; + } + parent.children.push(item); + return item; + } + + this.outline.render = function() { + this.outline.ctx = {}; + this.outline.nextId = 1000; + this.outline.ctx.val = ''; + this.outline.ctx.pdf = this; + + this.outline.genIds_r(this.outline.root); + this.outline.renderRoot(this.outline.root); + this.outline.renderItems(this.outline.root); + + return this.outline.ctx.val; + }.bind(this); + + this.outline.genIds_r = function(node) { + node.id = this.internal.newObjectDeferred(); + for (var i = 0; i < node.children.length; i++) { + this.outline.genIds_r(node.children[i]); + } + }.bind(this); + + this.outline.renderRoot = function(node) { + this.objStart(node); + this.line('/Type /Outlines'); + if (node.children.length > 0) { + this.line('/First ' + this.makeRef(node.children[0])); + this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); + } + this.line('/Count ' + this.count_r({ + count : 0 + }, node)); + this.objEnd(); + } + + this.outline.renderItems = function(node) { + for (var i = 0; i < node.children.length; i++) { + var item = node.children[i]; + this.objStart(item); + + this.line('/Title ' + this.makeString(item.title)); + + this.line('/Parent ' + this.makeRef(node)); + if (i > 0) { + this.line('/Prev ' + this.makeRef(node.children[i - 1])); + } + if (i < node.children.length - 1) { + this.line('/Next ' + this.makeRef(node.children[i + 1])); + } + if (item.children.length > 0) { + this.line('/First ' + this.makeRef(item.children[0])); + this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); + } + + var count = this.count = this.count_r({ + count : 0 + }, item); + if (count > 0) { + this.line('/Count ' + count); + } + + if (item.options) { + if (item.options.pageNumber) { + this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); + //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /Fit]'); + } + } + this.objEnd(); + } + for (var i = 0; i < node.children.length; i++) { + var item = node.children[i]; + this.renderItems(item); + } + } + + this.outline.line = function(text) { + this.ctx.val += text + '\r\n'; + } + + this.outline.makeRef = function(node) { + return node.id + ' 0 R'; + } + + this.outline.makeString = function(val) { + return '(' + this.internal.pdfEscape(val) + ')'; + }.bind(this); + + this.outline.objStart = function(node) { + this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; + } + + this.outline.objEnd = function(node) { + this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; + } + + this.outline.count_r = function(ctx,node) { + for (var i = 0; i < node.children.length; i++) { + ctx.count++; + this.count_r(ctx, node.children[i]); + } + return ctx.count; + } + } + + return this; +})(jsPDF.API); diff --git a/test/test_outline.html b/test/test_outline.html new file mode 100644 index 000000000..65e3af0db --- /dev/null +++ b/test/test_outline.html @@ -0,0 +1,88 @@ + + + + + + + +Outline Test + + + + + + + + + + +
    
    +	
    +
    +
    
    From a315a4d8279987afe49c6c3c86985063113ff69f Mon Sep 17 00:00:00 2001
    From: Steven Spungin 
    Date: Wed, 12 Nov 2014 11:40:52 -0500
    Subject: [PATCH 4/9] Added a plugin manager. Fire a plugin event on the static
     PubSub before the first page is added.
    
    ---
     jspdf.js                    |  18 ++-
     jspdf.plugin.annotations.js |  27 ++--
     jspdf.plugin.outline.js     | 254 +++++++++++++++++++-----------------
     test/test_annotation.html   |  13 +-
     test/test_outline.html      |   1 -
     5 files changed, 179 insertions(+), 134 deletions(-)
    
    diff --git a/jspdf.js b/jspdf.js
    index 5a85b391d..c3394c9bb 100644
    --- a/jspdf.js
    +++ b/jspdf.js
    @@ -1768,6 +1768,7 @@ var jsPDF = (function(global) {
     		// Add the first page automatically
     		addFonts();
     		activeFontKey = 'F1';
    +		jsPDF.plugins.internal.onInitialize(API);
     		_addPage(format, orientation);
     
     		events.publish('initialized');
    @@ -1802,7 +1803,6 @@ var jsPDF = (function(global) {
     	 */
     	jsPDF.API = {events:[]};
     	jsPDF.version = "1.0.0-trunk";
    -
     	if (typeof define === 'function' && define.amd) {
     		define('jsPDF', function() {
     			return jsPDF;
    @@ -1810,5 +1810,21 @@ var jsPDF = (function(global) {
     	} else {
     		global.jsPDF = jsPDF;
     	}
    +	jsPDF.plugins = {
    +		register:function(plugin){
    +			this.internal.plugins.push(plugin);
    +		},
    +		internal:{
    +			plugins:[],
    +			onInitialize: function(pdf){
    +				for (var i = 0; i < this.plugins.length; i++) {
    +					var plugin = this.plugins[i];
    +					if (typeof plugin.onInitialize === 'function'){
    +						plugin.onInitialize.call(plugin, pdf);
    +					}
    +				}
    +			},
    +		}
    +	};
     	return jsPDF;
     }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this));
    diff --git a/jspdf.plugin.annotations.js b/jspdf.plugin.annotations.js
    index 7b53bd658..3b7d94e18 100644
    --- a/jspdf.plugin.annotations.js
    +++ b/jspdf.plugin.annotations.js
    @@ -8,7 +8,7 @@
     
     /**
      * 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'
    + * on a page at a particular location. They are not 'attached' to an object.
      * 
    * This plugin current supports
    *
  • Goto Page (set pageNumber in options) @@ -36,23 +36,31 @@ function notEmpty(obj) { (function(jsPDFAPI) { 'use strict'; - jsPDFAPI.initAnnotationPlugin = function() { - this.annotationPlugin = {}; + var annotationPlugin = { + onInitialize : function(pdf) { + this.installAnnotationPlugin(pdf); + } + }; + jsPDF.API.annotationPlugin = annotationPlugin; + jsPDF.plugins.register(annotationPlugin); + + annotationPlugin.installAnnotationPlugin = function(pdf) { - this.annotationPlugin.annotations = []; + this.annotations = []; + // TODO remove this after we find a way to subscribe before the // first page is created. - this.annotationPlugin.annotations[1] = []; + //this.annotations[1] = []; - this.annotationPlugin.f2 = function(number) { + this.f2 = function(number) { return number.toFixed(2); }; - this.internal.events.subscribe('addPage', function(info) { + pdf.internal.events.subscribe('addPage', function(info) { this.annotationPlugin.annotations[info.pageNumber] = []; }); - this.internal.events.subscribe('render/page', function(info) { + pdf.internal.events.subscribe('render/page', function(info) { var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; var found = false; @@ -70,11 +78,12 @@ function notEmpty(obj) { } this.internal.write("/Annots ["); + var f2 = this.annotationPlugin.f2; for (var a = 0; a < pageAnnos.length; a++) { var anno = pageAnnos[a]; var k = this.internal.scaleFactor; var pageHeight = this.internal.pageSize.height; - var rect = "/Rect [" + this.annotationPlugin.f2(anno.x * k) + " " + this.annotationPlugin.f2((pageHeight - anno.y) * k) + " " + this.annotationPlugin.f2(anno.x + anno.w * k) + " " + this.annotationPlugin.f2(pageHeight - (anno.y + anno.h) * k) + "] "; + 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) + "] "; if (anno.options.url) { this.internal.write('<> >>') } else if (anno.options.pageNumber) { diff --git a/jspdf.plugin.outline.js b/jspdf.plugin.outline.js index f73081e7e..5a5b24daa 100644 --- a/jspdf.plugin.outline.js +++ b/jspdf.plugin.outline.js @@ -12,151 +12,165 @@ ; (function(jsPDFAPI) { 'use strict'; - - jsPDFAPI.initOutlinePlugin = function() { - - this.outline = {}; - this.outline.root = { - children : [] - }; - - this.internal.events.subscribe('postPutResources', function() { - if (this.outline.root.children.length > 0) { - var lines = this.outline.render().split(/\r\n/); - for (var int = 0; int < lines.length; int++) { - var line = lines[int]; - if (line.endsWith('obj')){ - var oid = line.split(' ')[0]; - this.internal.newObjectDeferredBegin(oid); + var outline = { + + onInitialize : function(pdf) { + + this.installOutlinePlugin(pdf); + + pdf.internal.events.subscribe('postPutResources', function() { + + var rx = /^(\d+) 0 obj$/; + + if (this.outline.root.children.length > 0) { + var lines = pdf.outline.render().split(/\r\n/); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var m = rx.exec(line); + if (m != null) { + var oid = m[1]; + pdf.internal.newObjectDeferredBegin(oid); + } + pdf.internal.write(line); } - this.internal.write(line); } - } - }); + }); + + pdf.internal.events.subscribe('putCatalog', function() { + if (pdf.outline.root.children.length > 0) { + pdf.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); + } + }); + }, + + installOutlinePlugin : function(pdf) { - this.internal.events.subscribe('putCatalog', function() { - if (this.outline.root.children.length > 0) { - this.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); - } - }); - - /** - * Options: pageNumber - */ - this.outline.add = function(parent,title,options) { - var item = { - title : title, - options : options, + pdf.outline = {}; + pdf.outline.root = { children : [] }; - if (parent == null) { - parent = this.root; + + /** + * Options: pageNumber + */ + pdf.outline.add = function(parent,title,options) { + var item = { + title : title, + options : options, + children : [] + }; + if (parent == null) { + parent = this.root; + } + parent.children.push(item); + return item; } - parent.children.push(item); - return item; - } - this.outline.render = function() { - this.outline.ctx = {}; - this.outline.nextId = 1000; - this.outline.ctx.val = ''; - this.outline.ctx.pdf = this; + pdf.outline.render = function() { + this.ctx = {}; + this.ctx.val = ''; + this.ctx.pdf = pdf; - this.outline.genIds_r(this.outline.root); - this.outline.renderRoot(this.outline.root); - this.outline.renderItems(this.outline.root); + this.genIds_r(this.root); + this.renderRoot(this.root); + this.renderItems(this.root); - return this.outline.ctx.val; - }.bind(this); + return this.ctx.val; + }; - this.outline.genIds_r = function(node) { - node.id = this.internal.newObjectDeferred(); - for (var i = 0; i < node.children.length; i++) { - this.outline.genIds_r(node.children[i]); - } - }.bind(this); - - this.outline.renderRoot = function(node) { - this.objStart(node); - this.line('/Type /Outlines'); - if (node.children.length > 0) { - this.line('/First ' + this.makeRef(node.children[0])); - this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); - } - this.line('/Count ' + this.count_r({ - count : 0 - }, node)); - this.objEnd(); - } + pdf.outline.genIds_r = function(node) { + node.id = pdf.internal.newObjectDeferred(); + for (var i = 0; i < node.children.length; i++) { + this.genIds_r(node.children[i]); + } + }; - this.outline.renderItems = function(node) { - for (var i = 0; i < node.children.length; i++) { - var item = node.children[i]; - this.objStart(item); + pdf.outline.renderRoot = function(node) { + this.objStart(node); + this.line('/Type /Outlines'); + if (node.children.length > 0) { + this.line('/First ' + this.makeRef(node.children[0])); + this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); + } + this.line('/Count ' + this.count_r({ + count : 0 + }, node)); + this.objEnd(); + }; - this.line('/Title ' + this.makeString(item.title)); + pdf.outline.renderItems = function(node) { + for (var i = 0; i < node.children.length; i++) { + var item = node.children[i]; + this.objStart(item); - this.line('/Parent ' + this.makeRef(node)); - if (i > 0) { - this.line('/Prev ' + this.makeRef(node.children[i - 1])); - } - if (i < node.children.length - 1) { - this.line('/Next ' + this.makeRef(node.children[i + 1])); - } - if (item.children.length > 0) { - this.line('/First ' + this.makeRef(item.children[0])); - this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); - } + this.line('/Title ' + this.makeString(item.title)); - var count = this.count = this.count_r({ - count : 0 - }, item); - if (count > 0) { - this.line('/Count ' + count); - } + this.line('/Parent ' + this.makeRef(node)); + if (i > 0) { + this.line('/Prev ' + this.makeRef(node.children[i - 1])); + } + if (i < node.children.length - 1) { + this.line('/Next ' + this.makeRef(node.children[i + 1])); + } + if (item.children.length > 0) { + this.line('/First ' + this.makeRef(item.children[0])); + this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); + } + + var count = this.count = this.count_r({ + count : 0 + }, item); + if (count > 0) { + this.line('/Count ' + count); + } - if (item.options) { - if (item.options.pageNumber) { - this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); - //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /Fit]'); + if (item.options) { + if (item.options.pageNumber) { + this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); + // this.line('/Dest ' + '[' + + // (item.options.pageNumber - + // 1) + ' /Fit]'); + } } + this.objEnd(); } - this.objEnd(); - } - for (var i = 0; i < node.children.length; i++) { - var item = node.children[i]; - this.renderItems(item); - } - } + for (var i = 0; i < node.children.length; i++) { + var item = node.children[i]; + this.renderItems(item); + } + }; - this.outline.line = function(text) { - this.ctx.val += text + '\r\n'; - } + pdf.outline.line = function(text) { + this.ctx.val += text + '\r\n'; + }; - this.outline.makeRef = function(node) { - return node.id + ' 0 R'; - } + pdf.outline.makeRef = function(node) { + return node.id + ' 0 R'; + }; - this.outline.makeString = function(val) { - return '(' + this.internal.pdfEscape(val) + ')'; - }.bind(this); + pdf.outline.makeString = function(val) { + return '(' + pdf.internal.pdfEscape(val) + ')'; + }; - this.outline.objStart = function(node) { - this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; - } + pdf.outline.objStart = function(node) { + this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; + }; - this.outline.objEnd = function(node) { - this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; - } + pdf.outline.objEnd = function(node) { + this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; + }; - this.outline.count_r = function(ctx,node) { - for (var i = 0; i < node.children.length; i++) { - ctx.count++; - this.count_r(ctx, node.children[i]); - } - return ctx.count; + pdf.outline.count_r = function(ctx,node) { + for (var i = 0; i < node.children.length; i++) { + ctx.count++; + this.count_r(ctx, node.children[i]); + } + return ctx.count; + }; } } + jsPDF.API.outline = outline; + jsPDF.plugins.register(outline); return this; })(jsPDF.API); diff --git a/test/test_annotation.html b/test/test_annotation.html index 7e7a2bb18..e36520026 100644 --- a/test/test_annotation.html +++ b/test/test_annotation.html @@ -27,7 +27,6 @@ var pdf = new jsPDF('p', 'pt', 'letter'); - pdf.initAnnotationPlugin(); // Create pages with a table of contents. // TOC links to each page @@ -60,8 +59,8 @@ } // generate either the pdf or source code - //TODO use query string ?source=true - if(true){ + // generate either the pdf or source code + if (getParameterByName('src') != 'true'){ var frame = document.getElementById('pdfframe'); frame.src = pdf.output('datauristring'); } @@ -88,6 +87,14 @@ return entityMap[s]; }); } + + function getParameterByName(name) { + name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); + var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), + results = regex.exec(location.search); + return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); + } + diff --git a/test/test_outline.html b/test/test_outline.html index 65e3af0db..67f04e702 100644 --- a/test/test_outline.html +++ b/test/test_outline.html @@ -32,7 +32,6 @@ pdf.addPage(); pdf.text(20, 20, 'More'); - pdf.initOutlinePlugin(); var node = pdf.outline.add(null, 'Test Pages', null); pdf.outline.add(node, 'Hello', {pageNumber:1}); pdf.outline.add(node, 'PDF', {pageNumber:2}); From b403daf98e10316e76a1d3b3c19e7eeec91e800b Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Thu, 13 Nov 2014 11:24:09 -0500 Subject: [PATCH 5/9] removed duplicate comment --- test/test_annotation.html | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_annotation.html b/test/test_annotation.html index e36520026..cf2d3c876 100644 --- a/test/test_annotation.html +++ b/test/test_annotation.html @@ -58,7 +58,6 @@ y += pdf.getLineHeight(); } - // generate either the pdf or source code // generate either the pdf or source code if (getParameterByName('src') != 'true'){ var frame = document.getElementById('pdfframe'); From 9d70421b075c56b55e88c2ca3ff2ea9106489db8 Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Thu, 13 Nov 2014 11:24:59 -0500 Subject: [PATCH 6/9] removed extra call to render --- test/test_outline.html | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_outline.html b/test/test_outline.html index 67f04e702..2d5f09412 100644 --- a/test/test_outline.html +++ b/test/test_outline.html @@ -46,7 +46,6 @@ frame.src = pdf.output('datauristring'); } else{ - var src = pdf.outline.render(); var src = pdf.output(); var content = document.getElementById('sourcecode'); raw = escapeHtml(src); From 19251b05348dda05da78287b8f2a011b6be287b1 Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Thu, 13 Nov 2014 11:25:57 -0500 Subject: [PATCH 7/9] Fix to make destinations more compatable on certain clients. Added support to generate named destination --- jspdf.plugin.outline.js | 89 +++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/jspdf.plugin.outline.js b/jspdf.plugin.outline.js index 5a5b24daa..87dcfbda2 100644 --- a/jspdf.plugin.outline.js +++ b/jspdf.plugin.outline.js @@ -13,15 +13,34 @@ (function(jsPDFAPI) { 'use strict'; var outline = { - + + createNamedDestinations : false, + onInitialize : function(pdf) { - + this.installOutlinePlugin(pdf); - + + var namesOid; + var destsGoto = []; + pdf.internal.events.subscribe('postPutResources', function() { - + var rx = /^(\d+) 0 obj$/; - + + // Write action goto objects for each page + // this.outline.destsGoto = []; + // for (var i = 0; i < totalPages; i++) { + // var id = pdf.internal.newObject(); + // this.outline.destsGoto.push(id); + // pdf.internal.write("<> endobj"); + // } + // + // for (var i = 0; i < dests.length; i++) { + // pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 + // R"); + // } + // if (this.outline.root.children.length > 0) { var lines = pdf.outline.render().split(/\r\n/); for (var i = 0; i < lines.length; i++) { @@ -34,18 +53,55 @@ pdf.internal.write(line); } } + + // This code will write named destination for each page reference + // (page_1, etc) + if (this.outline.createNamedDestinations) { + var totalPages = this.internal.pages.length; + // WARNING: this assumes jsPDF starts on page 3 and pageIDs + // follow 5, 7, 9, etc + // Write destination objects for each page + var dests = []; + for (var i = 0; i < totalPages; i++) { + var id = pdf.internal.newObject(); + dests.push(id); + pdf.internal.write("<< /D[" + (i * 2 + 3) + " 0 R /XYZ null null null]>> endobj"); + } + + // assign a name for each destination + var names2Oid = pdf.internal.newObject(); + pdf.internal.write('<< /Names [ '); + for (var i = 0; i < dests.length; i++) { + pdf.internal.write("(page_" + (i + 1) + ")" + dests[i] + " 0 R"); + } + pdf.internal.write(' ] >>', 'endobj'); + + // var kids = pdf.internal.newObject(); + // pdf.internal.write('<< /Kids [ ' + names2Oid + ' 0 R'); + // pdf.internal.write(' ] >>', 'endobj'); + + namesOid = pdf.internal.newObject(); + pdf.internal.write('<< /Dests ' + names2Oid + " 0 R"); + pdf.internal.write('>>', 'endobj'); + } + }); - + pdf.internal.events.subscribe('putCatalog', function() { if (pdf.outline.root.children.length > 0) { pdf.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); + if (this.outline.createNamedDestinations) { + pdf.internal.write("/Names " + namesOid + " 0 R"); + } + // Open with Bookmarks showing + // pdf.internal.write("/PageMode /UseOutlines"); } }); }, - + installOutlinePlugin : function(pdf) { - pdf.outline = {}; + pdf.outline = this; pdf.outline.root = { children : [] }; @@ -126,10 +182,19 @@ if (item.options) { if (item.options.pageNumber) { - this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); - // this.line('/Dest ' + '[' + - // (item.options.pageNumber - - // 1) + ' /Fit]'); + // Explicit Destination + //WARNING this assumes page ids are 3,5,7, etc. + this.line('/Dest ' + '[' + ((item.options.pageNumber - 1) * 2 + 3) + ' 0 R /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); + // this line does not work on all clients (pageNumber instead of page ref) + //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); + + // Named Destination + // this.line('/Dest (page_' + (item.options.pageNumber) + ')'); + + // Action Destination + // var id = pdf.internal.newObject(); + // pdf.internal.write('<> endobj'); + // this.line('/A ' + id + ' 0 R' ); } } this.objEnd(); From f3aa1a502ec405d1660a1e5222b078ec4180f87e Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Thu, 13 Nov 2014 12:03:33 -0500 Subject: [PATCH 8/9] hack: adjust annotation area for text baseline. --- jspdf.plugin.annotations.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jspdf.plugin.annotations.js b/jspdf.plugin.annotations.js index 3b7d94e18..6418b721d 100644 --- a/jspdf.plugin.annotations.js +++ b/jspdf.plugin.annotations.js @@ -47,7 +47,7 @@ function notEmpty(obj) { annotationPlugin.installAnnotationPlugin = function(pdf) { this.annotations = []; - + // TODO remove this after we find a way to subscribe before the // first page is created. //this.annotations[1] = []; @@ -125,6 +125,9 @@ function notEmpty(obj) { 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 this; }; From 981d2492de4ac611cebda5ca76a9f7f89e2288ce Mon Sep 17 00:00:00 2001 From: Steven Spungin Date: Mon, 17 Nov 2014 09:52:03 -0500 Subject: [PATCH 9/9] removed plugin manager changed event name to match conventions --- jspdf.js | 20 +-- jspdf.plugin.annotations.js | 102 +++++++------- jspdf.plugin.outline.js | 271 ++++++++++++++++++------------------ 3 files changed, 186 insertions(+), 207 deletions(-) diff --git a/jspdf.js b/jspdf.js index 33c8f7a1f..a1a9cab29 100644 --- a/jspdf.js +++ b/jspdf.js @@ -265,7 +265,7 @@ var jsPDF = (function(global) { out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']'); out('/Contents ' + (objectNumber + 1) + ' 0 R'); // Added for annotation plugin - events.publish('render/page', {pageNumber:n,page:pages[n]}); + events.publish('putPage', {pageNumber:n,page:pages[n]}); out('>>'); out('endobj'); @@ -1768,7 +1768,6 @@ var jsPDF = (function(global) { // Add the first page automatically addFonts(); activeFontKey = 'F1'; - jsPDF.plugins.internal.onInitialize(API); _addPage(format, orientation); events.publish('initialized'); @@ -1803,6 +1802,7 @@ var jsPDF = (function(global) { */ jsPDF.API = {events:[]}; jsPDF.version = "1.0.0-trunk"; + if (typeof define === 'function' && define.amd) { define('jsPDF', function() { return jsPDF; @@ -1810,21 +1810,5 @@ var jsPDF = (function(global) { } else { global.jsPDF = jsPDF; } - jsPDF.plugins = { - register:function(plugin){ - this.internal.plugins.push(plugin); - }, - internal:{ - plugins:[], - onInitialize: function(pdf){ - for (var i = 0; i < this.plugins.length; i++) { - var plugin = this.plugins[i]; - if (typeof plugin.onInitialize === 'function'){ - plugin.onInitialize.call(plugin, pdf); - } - } - }, - } - }; return jsPDF; }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this)); diff --git a/jspdf.plugin.annotations.js b/jspdf.plugin.annotations.js index 6418b721d..603c84f04 100644 --- a/jspdf.plugin.annotations.js +++ b/jspdf.plugin.annotations.js @@ -37,70 +37,63 @@ function notEmpty(obj) { 'use strict'; var annotationPlugin = { - onInitialize : function(pdf) { - this.installAnnotationPlugin(pdf); - } - }; - jsPDF.API.annotationPlugin = annotationPlugin; - jsPDF.plugins.register(annotationPlugin); - - annotationPlugin.installAnnotationPlugin = function(pdf) { - - this.annotations = []; - // TODO remove this after we find a way to subscribe before the - // first page is created. - //this.annotations[1] = []; + /** + * An array of arrays, indexed by pageNumber. + */ + annotations : [], - this.f2 = function(number) { + f2 : function(number) { return number.toFixed(2); - }; - - pdf.internal.events.subscribe('addPage', function(info) { - this.annotationPlugin.annotations[info.pageNumber] = []; - }); + } + }; - pdf.internal.events.subscribe('render/page', function(info) { - var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; + jsPDF.API.annotationPlugin = annotationPlugin; - var found = false; - for (var a = 0; a < pageAnnos.length; a++) { - var anno = pageAnnos[a]; - if (anno.type === 'link') { - if (notEmpty(anno.options.url) || notEmpty(anno.options.pageNumber)) { - found = true; - break; + jsPDF.API.events.push([ + 'addPage', function(info) { + this.annotationPlugin.annotations[info.pageNumber] = []; + } + ]); + + jsPDFAPI.events.push([ + 'putPage', function(info) { + var pageAnnos = this.annotationPlugin.annotations[info.pageNumber]; + + var found = false; + for (var a = 0; a < pageAnnos.length; a++) { + var anno = pageAnnos[a]; + if (anno.type === 'link') { + if (notEmpty(anno.options.url) || notEmpty(anno.options.pageNumber)) { + found = true; + break; + } } } - } - if (found == false) { - return; - } + if (found == false) { + return; + } - this.internal.write("/Annots ["); - var f2 = this.annotationPlugin.f2; - for (var a = 0; a < pageAnnos.length; a++) { - var anno = pageAnnos[a]; - var k = this.internal.scaleFactor; - var pageHeight = this.internal.pageSize.height; - 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) + "] "; - if (anno.options.url) { - this.internal.write('<> >>') - } else if (anno.options.pageNumber) { - // first page is 0 - this.internal.write('<>') - } else { - // TODO error - should not be here + this.internal.write("/Annots ["); + var f2 = this.annotationPlugin.f2; + for (var a = 0; a < pageAnnos.length; a++) { + var anno = pageAnnos[a]; + var k = this.internal.scaleFactor; + var pageHeight = this.internal.pageSize.height; + 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) + "] "; + if (anno.options.url) { + this.internal.write('<> >>') + } else if (anno.options.pageNumber) { + // first page is 0 + this.internal.write('<>') + } else { + // TODO error - should not be here + } } + this.internal.write("]"); } - this.internal.write("]"); - }); - }; + ]); - /** - * An array of arrays, indexed by pageNumber. - */ - // this.internal.annotations = []; /** * valid options *
  • pageNumber or url [required] @@ -132,6 +125,7 @@ function notEmpty(obj) { return this; }; + //TODO move into external library jsPDFAPI.getTextWidth = function(text) { 'use strict'; var fontSize = this.internal.getFontSize(); @@ -139,9 +133,11 @@ function notEmpty(obj) { return txtWidth; }; + //TODO move into external library jsPDFAPI.getLineHeight = function() { return this.internal.getLineHeight(); }; return this; + })(jsPDF.API); diff --git a/jspdf.plugin.outline.js b/jspdf.plugin.outline.js index 87dcfbda2..079b87e8b 100644 --- a/jspdf.plugin.outline.js +++ b/jspdf.plugin.outline.js @@ -12,19 +12,10 @@ ; (function(jsPDFAPI) { 'use strict'; - var outline = { - - createNamedDestinations : false, - - onInitialize : function(pdf) { - - this.installOutlinePlugin(pdf); - - var namesOid; - var destsGoto = []; - - pdf.internal.events.subscribe('postPutResources', function() { + jsPDFAPI.events.push([ + 'postPutResources', function() { + var pdf = this; var rx = /^(\d+) 0 obj$/; // Write action goto objects for each page @@ -85,9 +76,12 @@ pdf.internal.write('>>', 'endobj'); } - }); + } + ]); - pdf.internal.events.subscribe('putCatalog', function() { + jsPDFAPI.events.push([ + 'putCatalog', function() { + var pdf = this; if (pdf.outline.root.children.length > 0) { pdf.internal.write("/Outlines", this.outline.makeRef(this.outline.root)); if (this.outline.createNamedDestinations) { @@ -96,146 +90,151 @@ // Open with Bookmarks showing // pdf.internal.write("/PageMode /UseOutlines"); } - }); - }, - - installOutlinePlugin : function(pdf) { - - pdf.outline = this; - pdf.outline.root = { - children : [] - }; - - /** - * Options: pageNumber - */ - pdf.outline.add = function(parent,title,options) { - var item = { - title : title, - options : options, - children : [] + } + ]); + + jsPDFAPI.events.push([ + 'initialized', function() { + var pdf = this; + + pdf.outline = { + createNamedDestinations : false, + root : { + children : [] + } }; - if (parent == null) { - parent = this.root; + + var namesOid; + var destsGoto = []; + + /** + * Options: pageNumber + */ + pdf.outline.add = function(parent,title,options) { + var item = { + title : title, + options : options, + children : [] + }; + if (parent == null) { + parent = this.root; + } + parent.children.push(item); + return item; } - parent.children.push(item); - return item; - } - pdf.outline.render = function() { - this.ctx = {}; - this.ctx.val = ''; - this.ctx.pdf = pdf; + pdf.outline.render = function() { + this.ctx = {}; + this.ctx.val = ''; + this.ctx.pdf = pdf; - this.genIds_r(this.root); - this.renderRoot(this.root); - this.renderItems(this.root); + this.genIds_r(this.root); + this.renderRoot(this.root); + this.renderItems(this.root); - return this.ctx.val; - }; + return this.ctx.val; + }; - pdf.outline.genIds_r = function(node) { - node.id = pdf.internal.newObjectDeferred(); - for (var i = 0; i < node.children.length; i++) { - this.genIds_r(node.children[i]); - } - }; - - pdf.outline.renderRoot = function(node) { - this.objStart(node); - this.line('/Type /Outlines'); - if (node.children.length > 0) { - this.line('/First ' + this.makeRef(node.children[0])); - this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); - } - this.line('/Count ' + this.count_r({ - count : 0 - }, node)); - this.objEnd(); - }; - - pdf.outline.renderItems = function(node) { - for (var i = 0; i < node.children.length; i++) { - var item = node.children[i]; - this.objStart(item); - - this.line('/Title ' + this.makeString(item.title)); - - this.line('/Parent ' + this.makeRef(node)); - if (i > 0) { - this.line('/Prev ' + this.makeRef(node.children[i - 1])); - } - if (i < node.children.length - 1) { - this.line('/Next ' + this.makeRef(node.children[i + 1])); - } - if (item.children.length > 0) { - this.line('/First ' + this.makeRef(item.children[0])); - this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); + pdf.outline.genIds_r = function(node) { + node.id = pdf.internal.newObjectDeferred(); + for (var i = 0; i < node.children.length; i++) { + this.genIds_r(node.children[i]); } + }; - var count = this.count = this.count_r({ - count : 0 - }, item); - if (count > 0) { - this.line('/Count ' + count); + pdf.outline.renderRoot = function(node) { + this.objStart(node); + this.line('/Type /Outlines'); + if (node.children.length > 0) { + this.line('/First ' + this.makeRef(node.children[0])); + this.line('/Last ' + this.makeRef(node.children[node.children.length - 1])); } + this.line('/Count ' + this.count_r({ + count : 0 + }, node)); + this.objEnd(); + }; - if (item.options) { - if (item.options.pageNumber) { - // Explicit Destination - //WARNING this assumes page ids are 3,5,7, etc. - this.line('/Dest ' + '[' + ((item.options.pageNumber - 1) * 2 + 3) + ' 0 R /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); - // this line does not work on all clients (pageNumber instead of page ref) - //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); - - // Named Destination - // this.line('/Dest (page_' + (item.options.pageNumber) + ')'); - - // Action Destination - // var id = pdf.internal.newObject(); - // pdf.internal.write('<> endobj'); - // this.line('/A ' + id + ' 0 R' ); + pdf.outline.renderItems = function(node) { + for (var i = 0; i < node.children.length; i++) { + var item = node.children[i]; + this.objStart(item); + + this.line('/Title ' + this.makeString(item.title)); + + this.line('/Parent ' + this.makeRef(node)); + if (i > 0) { + this.line('/Prev ' + this.makeRef(node.children[i - 1])); + } + if (i < node.children.length - 1) { + this.line('/Next ' + this.makeRef(node.children[i + 1])); + } + if (item.children.length > 0) { + this.line('/First ' + this.makeRef(item.children[0])); + this.line('/Last ' + this.makeRef(item.children[item.children.length - 1])); + } + + var count = this.count = this.count_r({ + count : 0 + }, item); + if (count > 0) { + this.line('/Count ' + count); } + + if (item.options) { + if (item.options.pageNumber) { + // Explicit Destination + //WARNING this assumes page ids are 3,5,7, etc. + this.line('/Dest ' + '[' + ((item.options.pageNumber - 1) * 2 + 3) + ' 0 R /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); + // this line does not work on all clients (pageNumber instead of page ref) + //this.line('/Dest ' + '[' + (item.options.pageNumber - 1) + ' /XYZ 0 ' + this.ctx.pdf.internal.pageSize.height + ' 0]'); + + // Named Destination + // this.line('/Dest (page_' + (item.options.pageNumber) + ')'); + + // Action Destination + // var id = pdf.internal.newObject(); + // pdf.internal.write('<> endobj'); + // this.line('/A ' + id + ' 0 R' ); + } + } + this.objEnd(); } - this.objEnd(); - } - for (var i = 0; i < node.children.length; i++) { - var item = node.children[i]; - this.renderItems(item); - } - }; + for (var i = 0; i < node.children.length; i++) { + var item = node.children[i]; + this.renderItems(item); + } + }; - pdf.outline.line = function(text) { - this.ctx.val += text + '\r\n'; - }; + pdf.outline.line = function(text) { + this.ctx.val += text + '\r\n'; + }; - pdf.outline.makeRef = function(node) { - return node.id + ' 0 R'; - }; + pdf.outline.makeRef = function(node) { + return node.id + ' 0 R'; + }; - pdf.outline.makeString = function(val) { - return '(' + pdf.internal.pdfEscape(val) + ')'; - }; + pdf.outline.makeString = function(val) { + return '(' + pdf.internal.pdfEscape(val) + ')'; + }; - pdf.outline.objStart = function(node) { - this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; - }; + pdf.outline.objStart = function(node) { + this.ctx.val += '\r\n' + node.id + ' 0 obj' + '\r\n<<\r\n'; + }; - pdf.outline.objEnd = function(node) { - this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; - }; + pdf.outline.objEnd = function(node) { + this.ctx.val += '>> \r\n' + 'endobj' + '\r\n'; + }; - pdf.outline.count_r = function(ctx,node) { - for (var i = 0; i < node.children.length; i++) { - ctx.count++; - this.count_r(ctx, node.children[i]); - } - return ctx.count; - }; - } - } - jsPDF.API.outline = outline; - jsPDF.plugins.register(outline); + pdf.outline.count_r = function(ctx,node) { + for (var i = 0; i < node.children.length; i++) { + ctx.count++; + this.count_r(ctx, node.children[i]); + } + return ctx.count; + }; + } + ]); return this; })(jsPDF.API);