Skip to content

Commit adb4e49

Browse files
committed
Merge pull request #390 from Flamenco/master
Initial support for annotations (links, etc.)
2 parents 4600508 + 981d249 commit adb4e49

File tree

5 files changed

+599
-3
lines changed

5 files changed

+599
-3
lines changed

jspdf.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
1818
* 2014 James Makes, https://github.com/dollaruw
1919
* 2014 Diego Casorran, https://github.com/diegocr
20+
* 2014 Steven Spungin, https://github.com/Flamenco
2021
*
2122
* Permission is hereby granted, free of charge, to any person obtaining
2223
* a copy of this software and associated documentation files (the
@@ -39,7 +40,7 @@
3940
*
4041
* Contributor(s):
4142
* siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
42-
* kim3er, mfo, alnorth,
43+
* kim3er, mfo, alnorth, Flamenco
4344
*/
4445

4546
/**
@@ -228,6 +229,17 @@ var jsPDF = (function(global) {
228229
out(objectNumber + ' 0 obj');
229230
return objectNumber;
230231
},
232+
// Does not output the object. The caller must call newObjectDeferredBegin(oid) before outputing any data
233+
newObjectDeferred = function() {
234+
objectNumber++;
235+
offsets[objectNumber] = function(){
236+
return content_length;
237+
};
238+
return objectNumber;
239+
},
240+
newObjectDeferredBegin = function(oid) {
241+
offsets[oid] = content_length;
242+
},
231243
putStream = function(str) {
232244
out('stream');
233245
out(str);
@@ -251,7 +263,10 @@ var jsPDF = (function(global) {
251263
out('/Parent 1 0 R');
252264
out('/Resources 2 0 R');
253265
out('/MediaBox [0 0 ' + f2(wPt) + ' ' + f2(hPt) + ']');
254-
out('/Contents ' + (objectNumber + 1) + ' 0 R>>');
266+
out('/Contents ' + (objectNumber + 1) + ' 0 R');
267+
// Added for annotation plugin
268+
events.publish('putPage', {pageNumber:n,page:pages[n]});
269+
out('>>');
255270
out('endobj');
256271

257272
// Page content
@@ -766,7 +781,12 @@ var jsPDF = (function(global) {
766781
out('0 ' + (objectNumber + 1));
767782
out(p+' 65535 f ');
768783
for (i = 1; i <= objectNumber; i++) {
769-
out((p + offsets[i]).slice(-10) + ' 00000 n ');
784+
var offset = offsets[i];
785+
if (typeof offset === 'function'){
786+
out((p + offsets[i]()).slice(-10) + ' 00000 n ');
787+
}else{
788+
out((p + offsets[i]).slice(-10) + ' 00000 n ');
789+
}
770790
}
771791
// Trailer
772792
out('trailer');
@@ -918,6 +938,8 @@ var jsPDF = (function(global) {
918938
},
919939
'collections' : {},
920940
'newObject' : newObject,
941+
'newObjectDeferred' : newObjectDeferred,
942+
'newObjectDeferredBegin' : newObjectDeferredBegin,
921943
'putStream' : putStream,
922944
'events' : events,
923945
// ratio that you use in multiplication of a given "size" number to arrive to 'point'

jspdf.plugin.annotations.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* jsPDF Annotations PlugIn
3+
* Copyright (c) 2014 Steven Spungin (TwelveTone LLC) [email protected]
4+
*
5+
* Licensed under the MIT License.
6+
* http://opensource.org/licenses/mit-license
7+
*/
8+
9+
/**
10+
* There are many types of annotations in a PDF document. Annotations are placed
11+
* on a page at a particular location. They are not 'attached' to an object.
12+
* <br />
13+
* This plugin current supports <br />
14+
* <li> Goto Page (set pageNumber in options)
15+
* <li> Goto URL (set url in options)
16+
*
17+
* <p>
18+
* Options In PDF spec Not Implemented Yet
19+
* <li> link border
20+
* <li> named target
21+
* <li> page coordinates
22+
* <li> destination page scaling and layout
23+
* <li> actions other than URL and GotoPage
24+
* <li> background / hover actions
25+
* </p>
26+
*/
27+
28+
function notEmpty(obj) {
29+
if (typeof obj != 'undefined') {
30+
if (obj != '') {
31+
return true;
32+
}
33+
}
34+
}
35+
36+
(function(jsPDFAPI) {
37+
'use strict';
38+
39+
var annotationPlugin = {
40+
41+
/**
42+
* An array of arrays, indexed by <em>pageNumber</em>.
43+
*/
44+
annotations : [],
45+
46+
f2 : function(number) {
47+
return number.toFixed(2);
48+
}
49+
};
50+
51+
jsPDF.API.annotationPlugin = annotationPlugin;
52+
53+
jsPDF.API.events.push([
54+
'addPage', function(info) {
55+
this.annotationPlugin.annotations[info.pageNumber] = [];
56+
}
57+
]);
58+
59+
jsPDFAPI.events.push([
60+
'putPage', function(info) {
61+
var pageAnnos = this.annotationPlugin.annotations[info.pageNumber];
62+
63+
var found = false;
64+
for (var a = 0; a < pageAnnos.length; a++) {
65+
var anno = pageAnnos[a];
66+
if (anno.type === 'link') {
67+
if (notEmpty(anno.options.url) || notEmpty(anno.options.pageNumber)) {
68+
found = true;
69+
break;
70+
}
71+
}
72+
}
73+
if (found == false) {
74+
return;
75+
}
76+
77+
this.internal.write("/Annots [");
78+
var f2 = this.annotationPlugin.f2;
79+
for (var a = 0; a < pageAnnos.length; a++) {
80+
var anno = pageAnnos[a];
81+
var k = this.internal.scaleFactor;
82+
var pageHeight = this.internal.pageSize.height;
83+
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) + "] ";
84+
if (anno.options.url) {
85+
this.internal.write('<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /A <</S /URI /URI (' + anno.options.url + ') >> >>')
86+
} else if (anno.options.pageNumber) {
87+
// first page is 0
88+
this.internal.write('<</Type /Annot /Subtype /Link ' + rect + '/Border [0 0 0] /Dest [' + (anno.options.pageNumber - 1) + ' /XYZ 0 ' + pageHeight + ' 0] >>')
89+
} else {
90+
// TODO error - should not be here
91+
}
92+
}
93+
this.internal.write("]");
94+
}
95+
]);
96+
97+
/**
98+
* valid options
99+
* <li> pageNumber or url [required]
100+
*/
101+
jsPDFAPI.link = function(x,y,w,h,options) {
102+
'use strict';
103+
this.annotationPlugin.annotations[this.internal.getNumberOfPages()].push({
104+
x : x,
105+
y : y,
106+
w : w,
107+
h : h,
108+
options : options,
109+
type : 'link'
110+
});
111+
};
112+
113+
/**
114+
* Currently only supports single line text.
115+
*/
116+
jsPDFAPI.textWithLink = function(text,x,y,options) {
117+
'use strict';
118+
var width = this.getTextWidth(text);
119+
var height = this.internal.getLineHeight();
120+
this.text(text, x, y);
121+
//TODO We really need the text baseline height to do this correctly.
122+
// Or ability to draw text on top, bottom, center, or baseline.
123+
y += height * .2;
124+
this.link(x, y - height, width, height, options);
125+
return this;
126+
};
127+
128+
//TODO move into external library
129+
jsPDFAPI.getTextWidth = function(text) {
130+
'use strict';
131+
var fontSize = this.internal.getFontSize();
132+
var txtWidth = this.getStringUnitWidth(text) * fontSize / this.internal.scaleFactor;
133+
return txtWidth;
134+
};
135+
136+
//TODO move into external library
137+
jsPDFAPI.getLineHeight = function() {
138+
return this.internal.getLineHeight();
139+
};
140+
141+
return this;
142+
143+
})(jsPDF.API);

0 commit comments

Comments
 (0)