Skip to content

Commit 73722b5

Browse files
committed
context2d updates for html2canvas.js integration
1 parent bdcaaa4 commit 73722b5

4 files changed

Lines changed: 248 additions & 30 deletions

File tree

jspdf.js

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,10 @@ var jsPDF = (function(global) {
11451145
pageContext.lastTextWasStroke = false;
11461146
}
11471147

1148+
if (typeof this._runningPageHeight === 'undefined'){
1149+
this._runningPageHeight = 0;
1150+
}
1151+
11481152
if (typeof text === 'string') {
11491153
text = ESC(text);
11501154
} else if (text instanceof Array) {
@@ -1155,9 +1159,9 @@ var jsPDF = (function(global) {
11551159
while (len--) {
11561160
da.push(ESC(sa.shift()));
11571161
}
1158-
var linesLeft = Math.ceil((pageHeight - y) * k / (activeFontSize * lineHeightProportion));
1162+
var linesLeft = Math.ceil((pageHeight - y - this._runningPageHeight) * k / (activeFontSize * lineHeightProportion));
11591163
if (0 <= linesLeft && linesLeft < da.length + 1) {
1160-
todo = da.splice(linesLeft-1);
1164+
//todo = da.splice(linesLeft-1);
11611165
}
11621166

11631167
if( align ) {
@@ -1210,19 +1214,39 @@ var jsPDF = (function(global) {
12101214
// Thus, there is NO useful, *reliable* concept of "default" font for a page.
12111215
// The fact that "default" (reuse font used before) font worked before in basic cases is an accident
12121216
// - readers dealing smartly with brokenness of jsPDF's markup.
1217+
1218+
var curY;
1219+
1220+
if (todo){
1221+
//this.addPage();
1222+
//this._runningPageHeight += y - (activeFontSize * 1.7 / k);
1223+
//curY = f2(pageHeight - activeFontSize * 1.7 /k);
1224+
}else{
1225+
//curY = f2((pageHeight - (y - this._runningPageHeight)) * k);
1226+
}
1227+
curY = f2((pageHeight - (y - this._runningPageHeight)) * k);
1228+
1229+
if (curY < 0){
1230+
console.log('auto page break');
1231+
this.addPage();
1232+
this._runningPageHeight = y - (activeFontSize * 1.7 / k);
1233+
curY = f2(pageHeight - activeFontSize * 1.7 /k);
1234+
}
1235+
12131236
out(
12141237
'BT\n/' +
12151238
activeFontKey + ' ' + activeFontSize + ' Tf\n' + // font face, style, size
12161239
(activeFontSize * lineHeightProportion) + ' TL\n' + // line spacing
12171240
strokeOption +// stroke option
12181241
textColor +
1219-
'\n' + xtra + f2(x * k) + ' ' + f2((pageHeight - y) * k) + ' ' + mode + '\n(' +
1242+
'\n' + xtra + f2(x * k) + ' ' + curY + ' ' + mode + '\n(' +
12201243
text +
12211244
') Tj\nET');
12221245

12231246
if (todo) {
1224-
this.addPage();
1225-
this.text( todo, x, activeFontSize * 1.7 / k);
1247+
//this.text( todo, x, activeFontSize * 1.7 / k);
1248+
//this.text( todo, x, this._runningPageHeight + (activeFontSize * 1.7 / k));
1249+
this.text( todo, x, y);// + (activeFontSize * 1.7 / k));
12261250
}
12271251

12281252
return this;
@@ -1724,20 +1748,25 @@ var jsPDF = (function(global) {
17241748
} else {
17251749
color = f2(ch1 / 255) + ' g';
17261750
}
1727-
} else if (ch4 === undefined) {
1751+
} else if (ch4 === undefined || typeof ch4 === 'object') {
17281752
// RGB
17291753
if (typeof ch1 === 'string') {
17301754
color = [ch1, ch2, ch3, 'rg'].join(' ');
17311755
} else {
17321756
color = [f2(ch1 / 255), f2(ch2 / 255), f2(ch3 / 255), 'rg'].join(' ');
17331757
}
1758+
if (ch4 && ch4.a === 0){
1759+
//TODO Implement transparency.
1760+
//WORKAROUND use white for now
1761+
color = ['255', '255', '255', 'rg'].join(' ');
1762+
}
17341763
} else {
17351764
// CMYK
17361765
if (typeof ch1 === 'string') {
17371766
color = [ch1, ch2, ch3, ch4, 'k'].join(' ');
17381767
} else {
17391768
color = [f2(ch1), f2(ch2), f2(ch3), f2(ch4), 'k'].join(' ');
1740-
}
1769+
}
17411770
}
17421771

17431772
out(color);

jspdf.plugin.canvas.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* jsPDF Canvas PlugIn
3+
* Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
4+
*
5+
* Licensed under the MIT License.
6+
* http://opensource.org/licenses/mit-license
7+
*/
8+
9+
/**
10+
* This plugin mimicks the HTML5 Canvas
11+
*
12+
* The goal is to provide a way for current canvas users to print directly to a PDF.
13+
*/
14+
15+
(function(jsPDFAPI) {
16+
'use strict';
17+
var pdf2 = this;
18+
jsPDFAPI.canvas = {
19+
pdf : pdf2,
20+
getContext : function(name) {
21+
return pdf.context2d;
22+
},
23+
style : {}
24+
}
25+
26+
Object.defineProperty(jsPDFAPI.canvas, 'width', {
27+
get : function() {
28+
return this._width;
29+
},
30+
set : function(value) {
31+
this._width = value;
32+
}
33+
});
34+
35+
Object.defineProperty(jsPDFAPI.canvas, 'height', {
36+
get : function() {
37+
return this._height;
38+
},
39+
set : function(value) {
40+
this._height = value;
41+
}
42+
});
43+
44+
return this;
45+
})(jsPDF.API);

jspdf.plugin.context2d.js

Lines changed: 162 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,55 @@
7676
},
7777

7878
setFillStyle : function(style) {
79-
if (style.charAt(0) != '#') {
80-
style = this.internal.colorNameToHex(style);
81-
if (!style) {
82-
style = '#000000';
79+
80+
// get the decimal values of r, g, and b;
81+
var r, g, b, a;
82+
83+
var m = this.internal.rxRgb.exec(style);
84+
if (m != null) {
85+
r = parseInt(m[1]);
86+
g = parseInt(m[2]);
87+
b = parseInt(m[3]);
88+
} else {
89+
m = this.internal.rxRgba.exec(style);
90+
if (m != null) {
91+
r = parseInt(m[1]);
92+
g = parseInt(m[2]);
93+
b = parseInt(m[3]);
94+
a = parseInt(m[4]);
95+
} else {
96+
if (style.charAt(0) != '#') {
97+
style = this.internal.colorNameToHex(style);
98+
if (!style) {
99+
style = '#000000';
100+
}
101+
} else {
102+
}
103+
this.ctx.fillStyle = style;
104+
105+
if (style.length === 4) {
106+
r = this.ctx.fillStyle.substring(1, 2);
107+
r += r;
108+
g = this.ctx.fillStyle.substring(2, 3);
109+
g += g;
110+
b = this.ctx.fillStyle.substring(3, 4);
111+
b += b;
112+
} else {
113+
r = this.ctx.fillStyle.substring(1, 3);
114+
g = this.ctx.fillStyle.substring(3, 5);
115+
b = this.ctx.fillStyle.substring(5, 7);
116+
}
117+
r = parseInt(r, 16);
118+
g = parseInt(g, 16);
119+
b = parseInt(b, 16);
83120
}
84121
}
85-
this.ctx.fillStyle = style;
86-
var r = this.ctx.fillStyle.substring(1, 3);
87-
r = parseInt(r, 16);
88-
var g = this.ctx.fillStyle.substring(3, 5);
89-
g = parseInt(g, 16);
90-
var b = this.ctx.fillStyle.substring(5, 7);
91-
b = parseInt(b, 16);
92-
this.pdf.setFillColor(r, g, b);
93-
this.pdf.setTextColor(r, g, b);
122+
this.pdf.setFillColor(r, g, b, {
123+
a : a
124+
});
125+
this.pdf.setTextColor(r, g, b, {
126+
a : a
127+
});
94128
},
95129

96130
setStrokeStyle : function(style) {
@@ -122,16 +156,63 @@
122156

123157
setFont : function(font) {
124158
this.ctx.font = font;
125-
var rx = /(\d+)pt\s+(\w+)\s*(\w+)?/;
126-
var m = rx.exec(font);
127-
var size = m[1];
128-
var name = m[2];
129-
var style = m[3];
130-
if (!style) {
131-
style = 'normal';
159+
160+
var rx = /\s*(\w+)\s+(\w+)\s+(\w+)\s+(\d+)(px|pt|em)\s+(\w+)/;
161+
m = rx.exec(font);
162+
if (m != null) {
163+
var fontStyle = m[1];
164+
var fontVariant = m[2];
165+
var fontWeight = m[3];
166+
var fontSize = m[4];
167+
var fontSizeUnit = m[5];
168+
var fontFamily = m[6];
169+
170+
if ('px' === fontSizeUnit) {
171+
fontSize = parseInt(fontSize);
172+
//fontSize = fontSize * 1.25;
173+
} else if ('em' === fontSizeUnit) {
174+
fontSize = parseInt(fontSize) * this.pdf.getFontSize();
175+
} else {
176+
fontSize = parseInt(fontSize);
177+
}
178+
179+
this.pdf.setFontSize(fontSize);
180+
181+
if (fontWeight === 'bold' || fontWeight === '700') {
182+
this.pdf.setFontStyle('bold');
183+
} else {
184+
if (fontStyle === 'italic') {
185+
this.pdf.setFontStyle('italic');
186+
} else {
187+
this.pdf.setFontStyle('normal');
188+
}
189+
}
190+
// I am disabling changing the font until the font API is more robust
191+
name = "times";
192+
this.pdf.setFont(name, style);
193+
194+
} else {
195+
var rx = /(\d+)(pt|px|em)\s+(\w+)\s*(\w+)?/;
196+
var m = rx.exec(font);
197+
if (m != null) {
198+
var size = m[1];
199+
var unit = m[2];
200+
var name = m[3];
201+
var style = m[4];
202+
if (!style) {
203+
style = 'normal';
204+
}
205+
if ('em' === fontSizeUnit) {
206+
size = parseInt(fontSize) * this.pdf.getFontSize();
207+
}else{
208+
size = parseInt(size);
209+
}
210+
this.pdf.setFontSize(size);
211+
// I am disabling changing the font until the font API is more robust
212+
name = 'times';
213+
this.pdf.setFont(name, style);
214+
}
132215
}
133-
this.pdf.setFontSize(size);
134-
this.pdf.setFont(name, style);
135216
},
136217

137218
setTextBaseline : function(baseline) {
@@ -176,6 +257,7 @@
176257
},
177258

178259
arc : function(x,y,radius,startAngle,endAngle,anticlockwise) {
260+
y = y - this.pdf._runningPageHeight;
179261
var obj = {
180262
type : 'arc',
181263
x : x,
@@ -290,6 +372,27 @@
290372
this.path = [];
291373
},
292374

375+
clip : function() {
376+
//TODO not implemented
377+
},
378+
379+
translate : function(x,y) {
380+
this.ctx._translate = {
381+
x : x,
382+
y : y
383+
};
384+
//TODO use translate in other drawing methods.
385+
},
386+
measureText : function(text) {
387+
return {
388+
getWidth : function() {
389+
'use strict';
390+
var fontSize = pdf.internal.getFontSize();
391+
var txtWidth = pdf.getStringUnitWidth(text) * fontSize / pdf.internal.scaleFactor;
392+
return txtWidth;
393+
}
394+
}
395+
},
293396
_getBaseline : function(y) {
294397
var height = parseInt(this.pdf.internal.getFontSize());
295398
//TODO Get descent from font descriptor
@@ -315,8 +418,37 @@
315418

316419
var c2d = jsPDFAPI.context2d;
317420

421+
// accessor methods
422+
Object.defineProperty(c2d, 'fillStyle', {
423+
set : function(value) {
424+
this.setFillStyle(value);
425+
},
426+
get : function() {
427+
return this.ctx.fillStyle;
428+
}
429+
});
430+
Object.defineProperty(c2d, 'textBaseline', {
431+
set : function(value) {
432+
this.setTextBaseline(value);
433+
},
434+
get : function() {
435+
return this.getTextBaseline();
436+
}
437+
});
438+
Object.defineProperty(c2d, 'font', {
439+
set : function(value) {
440+
this.setFont(value);
441+
},
442+
get : function() {
443+
return this.getFont();
444+
}
445+
});
446+
318447
c2d.internal = {};
319448

449+
c2d.internal.rxRgb = /rgb\s*\(\s*(\d+),\s*(\d+),\s*(\d+\s*)\)/;
450+
c2d.internal.rxRgba = /rgba\s*\(\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)\s*\)/;
451+
320452
// http://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html
321453
c2d.internal.arc = function(xc,yc,r,a1,a2,anticlockwise,style) {
322454

@@ -601,6 +733,10 @@
601733
this.lineWidth = 1;
602734
this.lineJoin = 'miter'; //round, bevel, miter
603735
this.lineCap = 'butt'; //butt, round, square
736+
this._translate = {
737+
x : 0,
738+
y : 0
739+
};
604740
//TODO miter limit //default 10
605741

606742
this.copy = function(ctx) {
@@ -612,6 +748,10 @@
612748
this.lineCap = ctx.lineCap;
613749
this.textBaseline = ctx.textBaseline;
614750
this._fontSize = ctx._fontSize;
751+
this._translate = {
752+
x : ctx._translate.x,
753+
y : ctx._translate.y
754+
};
615755
};
616756
}
617757

test/test_context2d.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@
5050
y += textHeight + pad;
5151

5252
ctx.setFont("10pt courier bold");
53-
ctx.fillText("Hello PDF", 20, y + textHeight);
53+
ctx.fillText("Hello Bold PDF", 20, y + textHeight);
54+
y += textHeight + pad;
55+
56+
ctx.setFont("10pt courier italic");
57+
ctx.fillText("Hello Italic PDF", 20, y + textHeight);
5458
y += textHeight + pad;
5559

5660
ctx.setFont("50pt courier bold");

0 commit comments

Comments
 (0)