I’m generating A4
invoice using HTML
, CSS
and JS
. The invoice is getting generated as expected during print preview
. However page number
is not aligned and extra empty pages are generated automatically.
Here is my code snippet:
<html>
<head>
<script>function setPrintStyles(pagesize, standardSize) {
var documentHeight = standardSize ? '' : (document.getElementsByTagName('html')[0].offsetHeight / 2) + 100 + 'px';
var bodySize = standardSize ? '' : 'body { width: ' + pagesize + '; }';
var css = `@media print { @page { size: ${pagesize} ${documentHeight}; } ${bodySize} }`,
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
head.appendChild(style);
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
}</script>
<style type="text/css">
@media print {
#section-to-print {
position: absolute;
left: 0;
top: 0;
}
body {
font-family: Calibri, monospace;
font-size: 0.8rem;
margin: 1rem;
border: 1px solid;
}
body ol {
list-style: none;
padding-left: 0;
margin: 0.5rem 0;
}
table {
width: 100%;
height: 100%;
border-collapse: collapse;
page-break-inside: auto;
}
td section div {
page-break-inside: avoid;
}
thead {
display: table-header-group;
}
tfoot {
display: table-footer-group;
}
header {
display: flex;
flex-direction: column;
font-weight: normal;
}
#brand {
border-bottom: 1px solid;
padding: 0.25rem 0.5rem;
text-transform: uppercase;
font-weight: bold;
}
#invoicedetails {
display: grid;
grid: "shop customer invoice";
grid-template-columns: repeat(3, 1fr);
padding: 0 0.5rem;
border-bottom: 1px solid;
}
#shop {
text-align: left;
}
#customer {
border: 1px solid;
border-top: 0;
border-bottom: 0;
padding-left: 0.5rem;
text-align: center;
}
#invoice {
text-align: right;
}
table tbody td {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
main {
display: block;
height: 100%;
}
#invoiceitems {
display: block;
height: 100%;
text-transform: uppercase;
}
#itemscontent {
height: 100%;
display: grid;
grid: "sno isbnno title price quantity amount";
/*Below line is added as inline style in A4 invoice component*/
/*grid-template-rows: repeat(2, minmax(1rem, max-content)) auto repeat(2, minmax(1rem, max-content));*/
/*grid-template-columns: minmax(min-content, max-content) minmax(min-content, auto) max-content max-content max-content max-content max-content max-content;*/
grid-auto-rows: minmax(1rem, max-content);
grid-column-gap: 1px;
align-items: center;
}
#itemscontent>div {
display: flex;
align-items: center;
border: 1px solid;
height: 100%;
border-top: 0;
border-right: 0;
padding: 0 0.25rem;
}
.border-left-0 {
border-left: 0 !important;
}
.border-bottom-0 {
border-bottom: 0 !important;
}
.span-4 {
grid-column-start: span 4;
}
.flex-end {
justify-content: flex-end;
}
footer {
display: flex;
flex-direction: column;
}
#itemsfooter {
display: flex;
justify-content: space-between;
align-items: center;
text-transform: uppercase;
border-top: 1px solid;
}
#itemsfooter>div {
align-self: stretch;
display: flex;
align-items: center;
padding: 0 0.5rem;
}
#amountinwords {
flex: 75%;
}
#totalamount {
flex: 25%;
text-align: right;
border-left: 1px solid black;
}
#totalamount ol {
width: 100%;
}
#totalamount ol li:last-child {
font-size: x-large;
}
#invoiceinformation {
display: flex;
justify-content: end;
padding: 0 0.5rem;
border-top: 1px solid;
border-bottom: 1px solid;
}
#signature {
padding-left: 0.5rem;
}
#message {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.5rem;
}
}
</style>
<style type="text/css">
@media print {
@page {
size: A4;
}
}
</style>
<style>
html {
height: 297mm !important;
overflow-y: scroll;
}
</style>
</head>
<body><!--!-->
<table>
<thead>
<tr>
<th>
<header>
<section id="brand"></section><!--!-->
<section id="invoicedetails">
<section id="shop">
<ol>
<li>From</li><!--!-->
<li><b>ABC</b></li><!--!-->
<li>Address:</li><!--!-->
<li>Phone:</li><!--!-->
<li>Mobile:</li>
</ol>
</section><!--!-->
<section id="customer">
<ol>
<li>To</li><!--!-->
<li><b>DEF </b></li><!--!-->
<li>Mobile: </li>
</ol>
</section><!--!-->
<section id="invoice">
<ol id="invoice-details"><!--!-->
<li>Invoice Details</li>
<li>Invoice No: <b>18/2023-24</b></li><!--!-->
<li>Invoice Date: <b>17 Nov 2023</b></li><!--!-->
<li>Payment Mode: <b>Cash</b></li><!--!-->
<li>Payment Status: Paid</li><!--!-->
<li id="page-number"><br/></li>
</ol>
</section>
</section>
</header>
</th>
</tr>
</thead><!--!-->
<tbody>
<tr>
<td>
<main>
<section id="invoiceitems">
<section id="itemscontent"
style="grid-template-rows: repeat(5, minmax(1rem, max-content)) auto repeat(2, minmax(1rem, max-content)); grid-template-columns: minmax(min-content, max-content) minmax(min-content, max-content) minmax(min-content, auto) max-content max-content max-content">
<!--!-->
<div class="border-left-0">S.No</div>
<!--!-->
<div>ISBN No</div>
<!--!-->
<div>Title</div>
<!--!-->
<div>Price</div>
<!--!-->
<div>Quantity</div>
<!--!-->
<div>Amount</div>
<div class="border-left-0 border-bottom-0">1</div><!--!-->
<div class="border-bottom-0"></div><!--!-->
<div class="border-bottom-0">Another Test</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div><!--!-->
<div class="flex-end border-bottom-0">1</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div>
<div class="border-left-0 border-bottom-0">2</div><!--!-->
<div class="border-bottom-0"></div><!--!-->
<div class="border-bottom-0">Another Test</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div><!--!-->
<div class="flex-end border-bottom-0">1</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div>
<div class="border-left-0 border-bottom-0">3</div><!--!-->
<div class="border-bottom-0"></div><!--!-->
<div class="border-bottom-0">Another Test</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div><!--!-->
<div class="flex-end border-bottom-0">1</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div>
<div class="border-left-0 border-bottom-0">4</div><!--!-->
<div class="border-bottom-0"></div><!--!-->
<div class="border-bottom-0">Another Test</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div><!--!-->
<div class="flex-end border-bottom-0">1</div><!--!-->
<div class="flex-end border-bottom-0">10.00</div><!--!-->
<div class="border-left-0"></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<!--!-->
<div class="border-left-0 flex-end span-4">Sub Total</div>
<div class="flex-end">4</div><!--!-->
<div class="flex-end">40.00</div>
<div class="border-left-0 flex-end span-4">Discount (10.00%)</div><!--!-->
<div></div>
<div class="flex-end">-3.00</div>
</section>
</section>
</main><!--!-->
<footer>
<section id="itemsfooter">
<div id="amountinwords">
<ol>
<li><small>Total Amount in Words (INR)</small></li><!--!-->
<li><b>thirty seven ONLY</b></li>
</ol>
</div><!--!-->
<div id="totalamount">
<ol><!--!-->
<li><small>Total Amount</small></li>
<li><b>₹37.00</b></li>
</ol>
</div>
</section><!--!-->
<section id="invoiceinformation">
<div id="signature"><text>for <!--!--><br><br><br>Authorized
Signatory</text></div>
</section><!--!-->
<!--!-->
<section id="message">
<div><small>THANK YOU FOR THE BUSINESS</small></div>
<div><small>THIS IS A COMPUTER GENERATED INVOICE</small></div>
</section>
</footer>
</td>
</tr>
</tbody>
</table>
<div id="dpi" style="height: 1in; left: -100%; position: absolute; top: -100%; width: 1in;"></div>
<script>window.print();</script>
<script>window.onbeforeprint = addPageNumbers;
window.onafterprint = removePageNumbers;
function removePageNumbers() {
document.querySelectorAll('.dynamic-page-number').forEach(function (li) {
li.remove();
});
}
function addPageNumbers() {
var dpi = document.getElementById("dpi").offsetHeight;
console.log(dpi);
var sheetHeightInMM = 297;
var mmPerInch = 25.4;
var pixels = (sheetHeightInMM * dpi) / mmPerInch;
var bodyHeight = document.getElementsByTagName('body')[0].scrollHeight;
console.log(bodyHeight);
var totalPages = Math.ceil(bodyHeight / pixels);
console.log(totalPages);
for (var i = 1; i <= totalPages; i++) {
var pageNumberLi = document.createElement("li");
var pageNumber = document.createTextNode("Page " + i + " of " + totalPages);
pageNumberLi.classList.add('dynamic-page-number');
pageNumberLi.style.paddingTop = '5px'; // added to adjust spacing between previous li element in dom
pageNumberLi.style.position = "absolute";
var offsetTop = document.getElementById("page-number").offsetTop + "px";
console.log(offsetTop);
pageNumberLi.style.top = `calc((${i - 1} * ${sheetHeightInMM}mm) + ${offsetTop} - ${i * 1}rem)`;
pageNumberLi.style.right = '1.5rem'; // 1rem body right margin + 0.5rem header right padding
pageNumberLi.appendChild(pageNumber);
document.getElementById("invoice-details").appendChild(pageNumberLi);
}
}</script>
</body>
</html>
Issue I’m facing:
the above code will generate single page A4 invoice. But this generates two pages in print preview. The page number can also be seen in invoice.
Page 1:
Page 2:
Script used for page number generation:
function addPageNumbers() {
var dpi = document.getElementById("dpi").offsetHeight;
console.log(dpi);
var sheetHeightInMM = 297;
var mmPerInch = 25.4;
var pixels = (sheetHeightInMM * dpi) / mmPerInch;
var bodyHeight = document.getElementsByTagName('body')[0].scrollHeight;
console.log(bodyHeight);
var totalPages = Math.ceil(bodyHeight / pixels);
console.log(totalPages);
for (var i = 1; i <= totalPages; i++) {
var pageNumberLi = document.createElement("li");
var pageNumber = document.createTextNode("Page " + i + " of " + totalPages);
pageNumberLi.classList.add('dynamic-page-number');
pageNumberLi.style.paddingTop = '5px'; // added to adjust spacing between previous li element in dom
pageNumberLi.style.position = "absolute";
var offsetTop = document.getElementById("page-number").offsetTop + "px";
console.log(offsetTop);
pageNumberLi.style.top = `calc((${i - 1} * ${sheetHeightInMM}mm) + ${offsetTop} - ${i * 1}rem)`;
pageNumberLi.style.right = '1.5rem'; // 1rem body right margin + 0.5rem header right padding
pageNumberLi.appendChild(pageNumber);
document.getElementById("invoice-details").appendChild(pageNumberLi);
}
}
Expected Behavior:
Page should be generated properly considering the height
set in html
which is 297mm
(height of A4
page).
Please try saving the code snippet as html file and open in browser. The above code snippet execution gives different output.