Open Fahad-AQ opened 5 years ago
This is my javascript code.
function addScript(url) {
var script = document.createElement('script');
script.type = 'application/javascript';
script.src = url;
document.head.appendChild(script);
} addScript('https://raw.githack.com/eKoopmans/html2pdf/master/dist/html2pdf.bundle.js');
var element = document.getElementById('paginations'); var opt = { filename: 'abc.pdf' };
// New Promise-based usage:
html2pdf().set(opt).from(element).save();
@Fahad-AQ Are you been able to fix this?
@eKoopmans I'm running into a similar issue. Any solutions would be of great help.
I did use the given page break options but the page-break solution only works for 1st page and not others. So, if I add
page-break-inside: avoid
It does avoid doing that slicing only in the first page of the PDF and not on subsequent pages
I encountered page break alignment issues as well, and it seemed like I had to change at least two different things to get it to work.
I had a div within a div, and I was trying to use "break-before" on the parent div (newBox) but it was having issues and only breaking after the child div.
Example, with issue :
<div class='newBox'>
<div class='soBox text-end'>
SO# 1234
</div>
nextBox [Box 1 of 5]
</div>
I ended up just adding two divs before it (with some content inside--a non-breaking space--since empty divs don't seem to page break properly either), and changed my break-before target to the second new div (newBoxBefore2). Example (working):
<div class='newBoxBefore1'>
</div>
<div class='newBoxBefore2'>
</div>
<div class='newBox'>
<div class='soBox text-end'>
SO# 1234
</div>
nextBox [Box 1 of 5]
</div>
Note: This may affect the layout of your page, and if you try to reduce the height of the div, the page break location may mess up again. You could try reducing the div height on page load then call a custom function just before saving as pdf to restore the normal div height if you have issues.
The second issue I noticed after testing saving a pdf with lots of pages is that the page content started further and further up each page, until it was starting on the previous page. I ended up adding padding-top (via javascript) to each of my "newBox" divs, and made the padding equal in pixels to the page number (for example, page 2 has 2px padding-top, page 3 has 3px padding-top, page 40 has 40px, etc). Example (working):
elements = document.getElementsByClassName("newBox");
for (var i = 0; i < elements.length; i++) {
elements[i].style.paddingTop = elements[i].style.paddingTop = i + "px";
};
This code gets each element with the class name "newBox", then goes through them one by one, and changes the CSS style on each to essentially padding-top = "##px"
where ## = the page number (really, the number of times you've looped through this FOR loop, which is the number of times you've encountered a div with class name "newBox". But those divs are at the top of my pages so it's essentially the same thing as page number).
This is my CSS in the <head>
(but I think you could also just use the html2pdf options instead of defining it in CSS):
.newBoxBefore2 {
break-before: page;
}
Another thing I did was added a function to run (when clicking the Save button) which will load custom CSS for styling the page for print/save, and running a function afterward to put the CSS back to normal. I actually just duplicated my @media print
CSS rules and added another class in front of them so they only run when they see the printCss
class in a parent div, so I'm adding the printCSS
class to the <html>
tag before saving the pdf, and removing it afterward.
Example:
/* Duplicating PRINT OPTIONS for html2pdf saving*/
.printCss .newBox {
padding-left: 20px;
}
/* Hiding elements with bootstrap d-print-none class, so it works with html2pdf */
.printCss .d-print-none {
display: none;
}
/* Defines colors for printing as: white background, black text */
/* HISTORY: It was printing with gray text as default in dark mode */
.printCss html,
.printCss,
.printCss .newBox, {
color: black;
background-color: white;
}
(using some classes from the bootstrap 5 library)
<!-- Save pdf button -->
<input type="button" class="btn btn-secondary float-end printButton"
id="saveButton" value="🖫 Save">
<!-- SAVE PDF. html2pdf library for generating pdf. https://github.com/eKoopmans/html2pdf.js -->
<script src="./js/html2pdf.bundle.min.js"></script>
<script type="text/javascript">
// RUNNING HTML2PDF
document.getElementById("saveButton").addEventListener("click", html2pdfAndPrintCss);
html2pdfAndPrintCss();
// FUNCTION: Calling the html2pdf function and the add/remove print css functions
function html2pdfAndPrintCss() {
// Targeting a parent div containing all my elements I want saved/printed as pdf
var html2pdfElement = document.getElementById('resultsBox');
// Html2pdf.js options
var html2pdfOpt = {
margin: 0.2,
filename: 'savedweb.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 4 }, // Doubled the scale to 4 to increase print quality on thermal printer
// pagebreak: { before: '.newBox' }, // Not needed since I'm defining it in CSS
jsPDF: { unit: 'in', format: [4, 6], orientation: 'portrait' }
};
// FUNCTION: Call the function to add printCss class to body tag, which triggers css rules before saving
addPrintCss();
// FUNCTION: Add extra padding to the top of each page to offset the incorrect positioning that html2pdf applies
changeCSSAddPadding();
// Uses .then (promises) to only remove the css after the async saving task is done,
// then sets padding-top on each page back to a standard value
// REFERENCE: https://github.com/eKoopmans/html2pdf.js/issues/79#issuecomment-552126785
html2pdf().set(html2pdfOpt).from(html2pdfElement).save().then(removePrintCss).then(changeCSSRemovePadding);
});
};
// FUNCTION: Adding @media print css options to html2pdf output
// by duplicating those rules, and loading them when divs/classes have a parent div with the printCss class
// USAGE: Call this function before running html2pdf
function addPrintCss() {
// SOURCE: https://stackoverflow.com/a/46980378
document.documentElement.classList.add("printCss"); // Use this to directly add to html tag
};
// USAGE: Call this function after running html2pdf (might need to use .then(function))
function removePrintCss() {
// SOURCE: https://stackoverflow.com/a/46980378
document.documentElement.classList.remove("printCss"); // Use this to directly affect html tag
};
// FUNCTION: Change CSS dynamically
// REFERENCE: https://stackoverflow.com/a/10071316/1263035
function changeCSSAddPadding() {
// Add X pixels (X is page number) to the padding-top CSS style per page to correct html2pdf incorrect alignment offset
elements = document.getElementsByClassName("newBox");
for (var i = 0; i < elements.length; i++) {
elements[i].style.paddingTop = elements[i].style.paddingTop = i + "px";
};
};
// FUNCTION: Change CSS dynamically
// REFERENCE: https://stackoverflow.com/a/10071316/1263035
function changeCSSRemovePadding() {
// Remove the pixels added to the padding-top CSS style per page for correcting html2pdf alignment offset (set to 20px)
elements = document.getElementsByClassName("newBox");
for (var i = 0; i < elements.length; i++) {
elements[i].style.paddingTop = elements[i].style.paddingTop = "20px";
};
};
</script>
I'm kinda tired right now so sorry if I made any errors while reducing my code in an attempt to make it a bit easier to understand. (My issue may have not even been the same issue you were having, lol. But I was searching through every "page break" issue I could find on here when I was trying to figure out a solution.)
I also faced this problem in my project. Having experimented a little with the input data, it turned out that overriding the width parameter for html2canvas also breaks page breaks. A simplified example of the problem causing code is attached below.
<div class="dragdroppable-row">
<div class="grid-row background--transparent">
<div class="dragdroppable dragdroppable-column">
<div class="resizable-container" style="position: relative; user-select: auto; height: 176px; max-height: 3.40282e+38px; min-width: 135.417px; min-height: 40px; box-sizing: border-box; flex-shrink: 0; max-width: 1801px !important; margin-left: 16px !important;">
<div class="dashboard-component dashboard-component-chart-holder dashboard-chart-id-2 fade-out">
<div class="chart-slice css-v2o9ep">
<div class="dashboard-chart">
<div class="chart-container css-1wgwgod">
<div>
<div id="chart-id-2" class="handlebars">
<div height="144" width="1769" class="css-8i8h0o">
<h1>some h1</h1>
<h3>some h3</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class='newBoxBefore1'>
</div>
<div class='newBoxBefore2'>
</div>
<div class="dragdroppable-row newBox">
<div class="grid-row background--transparent">
<div class="dragdroppable dragdroppable-column">
<div class="resizable-container" style="position: relative; user-select: auto; height: 176px; max-height: 3.40282e+38px; min-width: 135.417px; min-height: 40px; box-sizing: border-box; flex-shrink: 0; width: 1801px !important; max-width: 1801px !important; margin-left: 16px !important;">
<div class="dashboard-component dashboard-component-chart-holder dashboard-chart-id-2 fade-out">
<div class="chart-slice css-v2o9ep">
<div class="dashboard-chart">
<div class="chart-container css-1wgwgod">
<div>
<div id="chart-id-2" class="handlebars">
<div height="144" width="1769" class="css-8i8h0o">
<h1>some h1</h1>
<h3>
some h3
</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class='newBoxBefore1'>
</div>
<div class='newBoxBefore2'>
</div>
<div class='newBox'>
<div class='soBox text-end'>
SO# 1234
</div>
nextBox [Box 1 of 5]
</div>
<script>
html2pdfAndPrintCss();
</script>
* {
box-sizing: border-box;
}
html, body {
margin:0;
padding:0;
background-color: #cccccc;
font-size: 14px;
line-height: 1.4;
}
body {
font-family: 'Inter', Helvetica, Arial;
font-size: 14px;
line-height: 1.4;
color: #333333;
background-color: #cccccc;
}
.dragdroppable-row {width: 100%;}
.grid-row {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: flex-start;
width: 100%;
height: fit-content;
}
.background--transparent {
background-color: transparent;
}
.dashboard-component-chart-holder.fade-out {
border-radius: 4px;
box-shadow: none;
transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out, border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.dashboard-component-chart-holder {
width: 100%;
height: 100%;
color: #333333;
background-color: #ffffff;
position: relative;
padding: 16px;
overflow-y: visible;
transition: opacity 0.2s, border-color 0.2s, box-shadow 0.2s;
border: 2px solid transparent;
}
.css-v2o9ep {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
max-height: 100%;
}
[data-test-chart-id="2"] .chart-header {
display: none !important;
visibility: hidden !important;
}
.dashboard-chart {
overflow: hidden;
position: relative;
}
.css-1wgwgod {
min-height: 144px;
position: relative;
}
#chart-id-2 div h1 {
font-size: 40px;
}
body h1 {
font-weight: 600;
line-height: 1.4;
font-size: 28px;
letter-spacing: -0.2px;
margin-top: 12px;
margin-bottom: 12px;
}
.css-1wgwgod .slice_container {
height: 144px;
}
#chart-id-2 div {
text-align: center;
position: relative !important;
width: 100vw !important;
}
function html2pdfAndPrintCss() {
var html2pdfElement = document.body;
var html2pdfOpt = {
margin: 0.2,
filename: 'savedweb.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: {
scale: 3,
width: 1850 // broken here
},
pagebreak: { before: '.newBox' },
jsPDF: { unit: 'pt', format: 'a4', orientation: 'l' }
};
html2pdf().set(html2pdfOpt).from(html2pdfElement).save();
};
The problem may be due to the timing of page break rules before scaling.
I found the following solution, which allows you to consistently convert regardless of the content of the page. It consists of simply adding spacers to create breaks on the page itself. In my case, it was necessary to generate strictly pdf A4 landscape format. If necessary, the page format and its aspect ratio can be moved to a separate input variable, determined based on the main jsPDF parameters.
function addSpacers(target){
// NOTE a4 fromat in portrait(width=height/Math.sqrt(2)) and in landscape(width=height*Math.sqrt(2))
var pageH = document.body.clientWidth/ Math.sqrt(2);
var spacer = document.createElement('div');
spacer.style.width = '100%';
spacer.style.backgroundColor = 'transparent';
spacer.class = 'html2pdf__spacer';
for (const row of target.children){
if ($(row).css('display') !== 'none'){
var rect = row.getBoundingClientRect();
// IMPORTANT first page always is 1.
var startBlockPage = Math.ceil(rect.top/pageH) == 0 ? 1 : Math.ceil(rect.top/pageH);
var endBlockPage = Math.ceil((rect.bottom)/pageH);
// block bigger than page need recurse for slove it problem and definition for tables
if (rect.bottom - rect.top >= pageH){
// TODO need tests. Be careful with css.
addSpacers(row);
console.log('Recurcy ', row);
}
else{
if(startBlockPage == endBlockPage){
// all OK
}
else{
// add spacer
spacer.style.height = (pageH * startBlockPage) - rect.top + 16 + 'px';
row.parentNode.insertBefore(spacer.cloneNode(true), row);
}
}
}
}
};
Then, after saving you can delete all $('.html2pdf__spacer') elements. But you may run into problems with specific CSS or indivisible content that is larger than the page. In this case, you will have to look for an addition to this solution yourself.
Hi ekoopmans , kindly give me solution of page break issue ,