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);