Skip to content

Commit 1bbb8a5

Browse files
committed
header footer working
1 parent 4c69493 commit 1bbb8a5

File tree

1 file changed

+111
-8
lines changed

1 file changed

+111
-8
lines changed

jspdf.plugin.from_html.js

Lines changed: 111 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
*/
2727

2828
(function(jsPDFAPI) {
29-
var DrillForContent, FontNameDB, FontStyleMap, FontWeightMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, process, tableToJson;
29+
var DrillForContent, FontNameDB, FontStyleMap, FontWeightMap, GetCSS, PurgeWhiteSpace, Renderer, ResolveFont, ResolveUnitedNumber, UnitedNumberMap, elementHandledElsewhere, images, loadImgs, checkForFooter, process, tableToJson;
3030
PurgeWhiteSpace = function(array) {
3131
var fragment, i, l, lTrimmed, r, rTrimmed, trailingSpace;
3232
i = 0;
@@ -253,16 +253,35 @@
253253
while (i < l) {
254254
cn = cns[i];
255255
if (typeof cn === "object") {
256+
257+
/*** HEADER rendering **/
258+
if (cn.nodeType === 1 && cn.nodeName === 'HEADER') {
259+
var header = cn;
260+
//store old top margin
261+
var oldMarginTop = renderer.pdf.margins_doc.top;
262+
//subscribe for new page event and render header first on every page
263+
renderer.pdf.internal.events.subscribe('addPage', function(pageInfo) {
264+
//set current y position to old margin
265+
renderer.y = oldMarginTop;
266+
//render all child nodes of the header element
267+
DrillForContent(header,renderer,elementHandlers);
268+
//set margin to old margin + rendered header + 10 space to prevent overlapping
269+
//important for other plugins (e.g. table) to start rendering at correct position after header
270+
renderer.pdf.margins_doc.top = renderer.y+10;
271+
renderer.y += 10;
272+
}, false);
273+
}
274+
256275
if (cn.nodeType === 8 && cn.nodeName === "#comment") {
257276
if (cn.textContent.match("ADD_PAGE")) {
258277
renderer.pdf.addPage();
259278
renderer.y = renderer.pdf.margins_doc.top;
260279
}
261-
} else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
280+
} else if (cn.nodeType === 1 && !SkipNode[cn.nodeName]) {
262281
if (cn.nodeName === "IMG" && images[cn.getAttribute("src")]) {
263282
if ((renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom < renderer.y + cn.height) && (renderer.y > renderer.pdf.margins_doc.top)) {
264-
renderer.pdf.addPage();
265283
renderer.y = renderer.pdf.margins_doc.top;
284+
renderer.pdf.addPage();
266285
}
267286
renderer.pdf.addImage(images[cn.getAttribute("src")], renderer.x, renderer.y, cn.width, cn.height);
268287
renderer.y += cn.height;
@@ -296,8 +315,8 @@
296315
loadImgs = function(element, renderer, elementHandlers, cb) {
297316
var imgs = element.getElementsByTagName('img'), l = imgs.length, x = 0;
298317
function done() {
299-
DrillForContent(element, renderer, elementHandlers);
300-
cb(renderer.dispose());
318+
renderer.pdf.internal.events.publish('imagesLoaded');
319+
cb();
301320
}
302321
function loadImage(url) {
303322
if(!url) return;
@@ -315,6 +334,78 @@
315334
cb = cb || function() {};
316335
return x || done();
317336
};
337+
checkForFooter = function(elem, renderer, elementHandlers, callback) {
338+
//check if we can found a <footer> element
339+
var footer = elem.getElementsByTagName("footer");
340+
if (footer.length > 0) {
341+
342+
footer = footer[0];
343+
344+
//bad hack to get height of footer
345+
//creat dummy out and check new y after fake rendering
346+
var oldOut = renderer.pdf.internal.write;
347+
var oldY = renderer.y;
348+
renderer.pdf.internal.write = function() {};
349+
DrillForContent(footer, renderer, elementHandlers);
350+
var footerHeight = Math.ceil(renderer.y-oldY)+5;
351+
renderer.y = oldY;
352+
renderer.pdf.internal.write = oldOut;
353+
354+
//add 20% to prevent overlapping
355+
renderer.pdf.margins_doc.bottom += footerHeight;
356+
357+
//Create function render header on every page
358+
var renderFooter = function(pageInfo) {
359+
var pageNumber = pageInfo !== undefined ? pageInfo.pageNumber : 1;
360+
//set current y position to old margin
361+
var oldPosition = renderer.y;
362+
//render all child nodes of the header element
363+
renderer.y = renderer.pdf.internal.pageSize.height - renderer.pdf.margins_doc.bottom;
364+
renderer.pdf.margins_doc.bottom -= footerHeight;
365+
366+
//check if we have to add page numbers
367+
var spans = footer.getElementsByTagName('span');
368+
for(var i=0; i < spans.length; ++i) {
369+
//if we find some span element with class pageCounter, set the page
370+
if ( (" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" pageCounter ") > -1 ) {
371+
spans[i].innerHTML = pageNumber;
372+
}
373+
//if we find some span element with class totalPages, set a variable which is replaced after rendering of all pages
374+
if ( (" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1 ) {
375+
spans[i].innerHTML = '###jsPDFVarTotalPages###';
376+
}
377+
}
378+
379+
//render footer content
380+
DrillForContent(footer,renderer,elementHandlers);
381+
//set bottom margin to previous height including the footer height
382+
renderer.pdf.margins_doc.bottom += footerHeight;
383+
//important for other plugins (e.g. table) to start rendering at correct position after header
384+
renderer.y = oldPosition;
385+
};
386+
387+
//check if footer contains totalPages which shoudl be replace at the disoposal of the document
388+
var spans = footer.getElementsByTagName('span');
389+
for(var i=0; i < spans.length; ++i) {
390+
if ( (" " + spans[i].className + " ").replace(/[\n\t]/g, " ").indexOf(" totalPages ") > -1 ) {
391+
renderer.pdf.internal.events.subscribe('htmlRenderingFinished', renderer.pdf.putTotalPages.bind(renderer.pdf, '###jsPDFVarTotalPages###'), true);
392+
}
393+
}
394+
395+
//register event to render footer on every new page
396+
renderer.pdf.internal.events.subscribe('addPage', renderFooter, false);
397+
//render footer on first page
398+
renderFooter();
399+
400+
//prevent footer rendering
401+
SkipNode['FOOTER'] = 1;
402+
403+
}
404+
405+
//footer preperation finished
406+
callback();
407+
408+
};
318409
process = function(pdf, element, x, y, settings, callback) {
319410
if (!element) return false;
320411
if (!element.parentNode) element = '' + element.innerHTML;
@@ -333,7 +424,19 @@
333424
})(element.replace(/<\/?script[^>]*?>/gi,''));
334425
}
335426
var r = new Renderer(pdf, x, y, settings);
336-
loadImgs.call(this, element, r, settings.elementHandlers, callback);
427+
428+
// 1. load images
429+
// 2. prepare optional footer elements
430+
// 3. render content
431+
loadImgs.call(this, element, r, settings.elementHandlers, function() {
432+
checkForFooter.call(this, element, r, settings.elementHandlers, function() {
433+
DrillForContent(element, r, settings.elementHandlers);
434+
//send event dispose for final taks (e.g. footer totalpage replacement)
435+
r.pdf.internal.events.publish('htmlRenderingFinished');
436+
callback(r.dispose());
437+
});
438+
});
439+
337440
return r.dispose();
338441
};
339442
Renderer.prototype.init = function() {
@@ -344,7 +447,7 @@
344447
return this.pdf.internal.write("q");
345448
};
346449
Renderer.prototype.dispose = function() {
347-
this.pdf.internal.write("Q");
450+
this.pdf.internal.write("Q");
348451
return {
349452
x: this.x,
350453
y: this.y
@@ -406,7 +509,7 @@
406509
if (this.pdf.internal.pageSize.height - this.pdf.margins_doc.bottom < this.y + this.pdf.internal.getFontSize()) {
407510
this.pdf.internal.write("ET", "Q");
408511
this.pdf.addPage();
409-
this.y = this.pdf.margins_doc.top;
512+
this.y = this.pdf.margins_doc.top;
410513
this.pdf.internal.write("q", "BT", this.pdf.internal.getCoordinateString(this.x), this.pdf.internal.getVerticalCoordinateString(this.y), "Td");
411514
}
412515
defaultFontSize = 12;

0 commit comments

Comments
 (0)