Skip to content

Commit b38a73b

Browse files
committed
Merge pull request #260 from woolfg/footerheader
fromHTML PlugIn Support of <header> and <footer> elements.
2 parents 769c140 + ee4ba08 commit b38a73b

File tree

1 file changed

+108
-5
lines changed

1 file changed

+108
-5
lines changed

jspdf.plugin.from_html.js

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
*/
2828

2929
(function(jsPDFAPI) {
30-
var clone,DrillForContent, FontNameDB, FontStyleMap, FontWeightMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, process, tableToJson;
30+
var clone,DrillForContent, FontNameDB, FontStyleMap, FontWeightMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, checkForFooter, process, tableToJson;
3131
clone = (function(){
3232
return function (obj) { Clone.prototype=obj; return new Clone() };
3333
function Clone(){}
@@ -270,6 +270,25 @@
270270
while (i < l) {
271271
cn = cns[i];
272272
if (typeof cn === "object") {
273+
274+
/*** HEADER rendering **/
275+
if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
276+
var header = cn;
277+
//store old top margin
278+
var oldMarginTop = renderer.pdf.margins_doc.top;
279+
//subscribe for new page event and render header first on every page
280+
renderer.pdf.internal.events.subscribe('addPage', function(pageInfo) {
281+
//set current y position to old margin
282+
renderer.y = oldMarginTop;
283+
//render all child nodes of the header element
284+
DrillForContent(header,renderer,elementHandlers);
285+
//set margin to old margin + rendered header + 10 space to prevent overlapping
286+
//important for other plugins (e.g. table) to start rendering at correct position after header
287+
renderer.pdf.margins_doc.top = renderer.y+10;
288+
renderer.y += 10;
289+
}, false);
290+
}
291+
273292
if (cn.nodeType === 8 && cn.nodeName === "#comment") {
274293
if (cn.textContent.match("ADD_PAGE")) {
275294
renderer.pdf.addPage();
@@ -343,8 +362,8 @@
343362
loadImgs = function(element, renderer, elementHandlers, cb) {
344363
var imgs = element.getElementsByTagName('img'), l = imgs.length, x = 0;
345364
function done() {
346-
DrillForContent(element, renderer, elementHandlers);
347-
cb(renderer.dispose());
365+
renderer.pdf.internal.events.publish('imagesLoaded');
366+
cb();
348367
}
349368
function loadImage(url) {
350369
if(!url) return;
@@ -362,6 +381,78 @@
362381
cb = cb || function() {};
363382
return x || done();
364383
};
384+
checkForFooter = function(elem, renderer, elementHandlers, callback) {
385+
//check if we can found a <footer> element
386+
var footer = elem.getElementsByTagName("footer");
387+
if (footer.length > 0) {
388+
389+
footer = footer[0];
390+
391+
//bad hack to get height of footer
392+
//creat dummy out and check new y after fake rendering
393+
var oldOut = renderer.pdf.internal.write;
394+
var oldY = renderer.y;
395+
renderer.pdf.internal.write = function() {};
396+
DrillForContent(footer, renderer, elementHandlers);
397+
var footerHeight = Math.ceil(renderer.y-oldY)+5;
398+
renderer.y = oldY;
399+
renderer.pdf.internal.write = oldOut;
400+
401+
//add 20% to prevent overlapping
402+
renderer.pdf.margins_doc.bottom += footerHeight;
403+
404+
//Create function render header on every page
405+
var renderFooter = function(pageInfo) {
406+
var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1;
407+
//set current y position to old margin
408+
var oldPosition = renderer.y;
409+
//render all child nodes of the header element
410+
renderer.y = renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom;
411+
renderer.pdf.margins_doc.bottom -= footerHeight;
412+
413+
//check if we have to add page numbers
414+
var spans = footer.getElementsByTagName('span');
415+
for(var i=0; i < spans.length; ++i) {
416+
//if we find some span element with class pageCounter, set the page
417+
if ( (" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1 ) {
418+
spans[i].innerHTML = pageNumber;
419+
}
420+
//if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
421+
if ( (" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1 ) {
422+
spans[i].innerHTML = '###jsPDFVarTotalPages###';
423+
}
424+
}
425+
426+
//render footer content
427+
DrillForContent(footer,renderer,elementHandlers);
428+
//set bottom margin to previous height including the footer height
429+
renderer.pdf.margins_doc.bottom += footerHeight;
430+
//important for other plugins (e.g. table) to start rendering at correct position after header
431+
renderer.y = oldPosition;
432+
};
433+
434+
//check if footer contains totalPages which shoudl be replace at the disoposal of the document
435+
var spans = footer.getElementsByTagName('span');
436+
for(var i=0; i < spans.length; ++i) {
437+
if ( (" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1 ) {
438+
renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
439+
}
440+
}
441+
442+
//register event to render footer on every new page
443+
renderer.pdf.internal.events.subscribe('addPage', renderFooter, false);
444+
//render footer on first page
445+
renderFooter();
446+
447+
//prevent footer rendering
448+
SkipNode['FOOTER'] = 1;
449+
450+
}
451+
452+
//footer preperation finished
453+
callback();
454+
455+
};
365456
process = function(pdf, element, x, y, settings, callback) {
366457
if (!element) return false;
367458
if (!element.parentNode) element = '' + element.innerHTML;
@@ -380,7 +471,19 @@
380471
})(element.replace(/<\/?script[^>]*?>/gi,''));
381472
}
382473
var r = new Renderer(pdf, x, y, settings);
383-
loadImgs.call(this, element, r, settings.elementHandlers, callback);
474+
475+
// 1. load images
476+
// 2. prepare optional footer elements
477+
// 3. render content
478+
loadImgs.call(this, element, r, settings.elementHandlers, function() {
479+
checkForFooter.call(this, element, r, settings.elementHandlers, function() {
480+
DrillForContent(element, r, settings.elementHandlers);
481+
//send event dispose for final taks (e.g. footer totalpage replacement)
482+
r.pdf.internal.events.publish('htmlRenderingFinished');
483+
callback(r.dispose());
484+
});
485+
});
486+
384487
return r.dispose();
385488
};
386489
Renderer.prototype.init = function() {
@@ -475,7 +578,7 @@
475578
}
476579

477580
return lines;
478-
};
581+
};
479582
Renderer.prototype.RenderTextFragment = function(text, style) {
480583
var defaultFontSize, font;
481584
if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {

0 commit comments

Comments
 (0)