Skip to content
28 changes: 25 additions & 3 deletions jspdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -39,7 +40,7 @@
*
* Contributor(s):
* siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
* kim3er, mfo, alnorth,
* kim3er, mfo, alnorth, Flamenco
*/

/**
Expand Down Expand Up @@ -228,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);
Expand All @@ -251,7 +263,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('putPage', {pageNumber:n,page:pages[n]});
out('>>');
out('endobj');

// Page content
Expand Down Expand Up @@ -766,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');
Expand Down Expand Up @@ -918,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'
Expand Down
143 changes: 143 additions & 0 deletions jspdf.plugin.annotations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* jsPDF Annotations PlugIn
* Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/

/**
* There are many types of annotations in a PDF document. Annotations are placed
* on a page at a particular location. They are not 'attached' to an object.
* <br />
* This plugin current supports <br />
* <li> Goto Page (set pageNumber in options)
* <li> Goto URL (set url in options)
*
* <p>
* Options In PDF spec Not Implemented Yet
* <li> link border
* <li> named target
* <li> page coordinates
* <li> destination page scaling and layout
* <li> actions other than URL and GotoPage
* <li> background / hover actions
* </p>
*/

function notEmpty(obj) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make this function private to your plugin scope.

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

(function(jsPDFAPI) {
'use strict';

var annotationPlugin = {

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

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

jsPDF.API.annotationPlugin = annotationPlugin;

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

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('<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /A <</S /URI /URI (' + anno.options.url + ') >> >>')
} else if (anno.options.pageNumber) {
// first page is 0
this.internal.write('<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /Dest [' + (anno.options.pageNumber - 1) + ' /XYZ 0 ' + pageHeight + ' 0] >>')
} else {
// TODO error - should not be here
}
}
this.internal.write("]");
}
]);

/**
* valid options
* <li> 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);
//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;
};

//TODO move into external library
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than into an external library, feel free to add them both to the main/internal jsPDF API.

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

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

return this;

})(jsPDF.API);
Loading