Reputation: 3295
I am using a table to create a footer on every page (works in Firefox, that's enough).
A JS Fiddle: https://jsfiddle.net/j9k2xzze/
(right click on the output pane -> This Frame -> Open Frame in New Tab. Then Print Preview will function as normal)
<table id="wrapper">
<thead>
<tr>
<td id="header"></td>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="0" id="footer">
<img src="footer.jpg"/>
</td>
</tr>
</tfoot>
<tbody>
<tr>
<td id="content">
<?php echo $html; ?>
</td>
</tr>
</tbody>
</table>
But on the very last page the table footer is displayed directly below the text. If the text is shorter than the last page, the footer sticks to it.
I like the footer to be at the very bottom on the last page. Unfortunately the @page extension does not work in firefox or I am doing it wrong:
@page:last {
#footer {
position: absolute;
bottom: 0;
}
}
Upvotes: 25
Views: 32763
Reputation: 816
If anyone is looking for a solution for multi-pages PDF with Puppeteer, one can use position: absolute
with several thresholds:
const totals = document.getElementById('totals');
const offsetTop = totals.offsetTop;
const offsetHeight = totals.offsetHeight;
totals.style.position = 'absolute';
totals.style.left = '8px';
totals.style.right = '8px';
if (offsetTop + offsetHeight > 1000) {
totals.style.top = `${2000 - totals.offsetHeight}px`;
} else {
totals.style.top = `${1000 - totals.offsetHeight}px`;
}
@page {
size: a4 portrait;
margin: 20px;
}
table, th {
border-collapse: collapse;
}
tbody, th {
border: 1px solid black;
}
.footer-space {
height: 50px;
}
footer {
position: fixed;
text-align: center;
bottom: 0;
left: 0;
right: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<table>
<thead>
<tr><th>COLUMN</th></tr>
</thead>
<tbody>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
<tr><td>1</td></tr>
</tbody>
<tfoot>
<tr><td><div class="footer-space"> </div></td></tr>
</tfoot>
</table>
<table id="totals">
<tbody>
<tr>
<td>TOTALS</td>
<td>100$</td>
</tr>
</tbody>
</table>
<footer>FOOTER</footer>
</body>
</html>
Then with Puppeteer:
import puppeteer from "puppeteer";
import { readFile, writeFile } from "node:fs/promises";
async function main() {
const content = await readFile("./input.html", { encoding: "utf8" });
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(content);
await page.emulateMediaType("print");
const buffer = await page.pdf({
preferCSSPageSize: true,
});
await writeFile("./output.pdf", buffer);
await browser.close();
}
main();
Output with 1 page:
Output with 2 pages:
Upvotes: 0
Reputation: 4362
If you're only supporting Firefox, this is actually really easy. (Skip to the edit to see a technique that also works in IE but is less versatile. Chrome and the other webkit browsers didn't support repeating footers when I posted this answer, so I didn't test them).
All you have to do is add a large bottom margin at the end of your content. The exact size doesn't matter, but it must be large enough to be guaranteed to run past the end of the page. I recommend making it at least as large as the maximum paper size you think your users will use.
Don't worry, this won't add a blank page to the end of your document. Margins differ from other forms of white space (such as padding and <br>
tags) in that they get cancelled out when they exceed the page boundary (see spec, section 13.3.3). Both Firefox and IE will delete the margin, but Firefox will still generate a footer at the bottom of the page as if a page break had occurred. (IE, on the other hand, behaves as if the margin was never there, which is why this approach doesn't work in that browser.)
You can put the margin on a pseudo-element to keep your HTML tidy, and you can use @media print
to prevent it from showing on screen.
Here's the code. To see it work in Firefox, open this jsfiddle, right-click the output, select This Frame > Show Only This Frame, and do a print preview.
@media print {
#content:after {
display: block;
content: "";
margin-bottom: 594mm; /* must be larger than largest paper size you support */
}
}
<table>
<thead>
<tr>
<th>PAGE HEADER</th>
</tr>
</thead>
<tfoot>
<tr>
<td>PAGE FOOTER</td>
</tr>
</tfoot>
<tbody>
<tr>
<td id="content">
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content
</td>
</tr>
</tbody>
</table>
EDIT
There's another option that works in both Firefox and IE. All you have to do is put the footer in a separate <div>
and fix it to the bottom of the page, and then use the repeating <tfoot>
as a spacer. This approach does have some minor drawbacks, though (details below snippet).
Here's the code. To see it work in Firefox, open this jsfiddle, right-click the output, select This Frame > Show Only This Frame, and do a print preview. In IE, click in the output frame, hit CTRL+A, do a print preview, and change "As Laid Out On Screen" to "As Selected On Screen".
@media print {
#spacer {height: 2em;} /* height of footer + a little extra */
#footer {
position: fixed;
bottom: 0;
}
}
<table>
<thead>
<tr>
<th>PAGE HEADER</th>
</tr>
<thead>
<tfoot>
<tr>
<td id="spacer"></td>
</tr>
</tfoot>
<tbody>
<tr>
<td>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content
</td>
</tr>
</tbody>
</table>
<div id="footer">
PAGE FOOTER
</div>
The main limitation of this method is that it puts an identical footer on every page in the print job, which means you can't have any pages with a different footer, or no footer. Also, since the height of the spacer depends on the height of the footer, you'll have to adjust it if the footer height ever changes.
Upvotes: 12
Reputation: 1262
There is no way to have footer only on last page if you use absolute or fixed position. If you want footer on the bottom only if page in not long enough, use sticky footer technique thats doesn't require absolute/fixed position. Something like this
html, body {
height: 100%;
margin: 0;
}
.wrapper {
min-height: 100%;
margin-bottom: -50px;
}
.footer,
.push {
height: 50px;
}
If you really need footer at the bottom of each page I would recommend to generate pdf first and print it. If you need to print complex stuff you will end up generating pdfs first anyway, print css is very limited.
Upvotes: 0
Reputation: 634
Hi I just added the following media query to your CSS. And it WORKS
@media print {
#footer {
position: absolute;
bottom: 0;
}
}
Check the following fiddle
https://jsfiddle.net/jjsqgaza/
Upvotes: -5