Multipage pdf with html2Canvas - jspdf

I am using react-typescript and I have successfully created a PDF file from an html page with the help of this ref
Generating a PDF file from React Components
But if we want to create a PDF with multiple pages then what to do?
with a4 size page with appropriate margins at all sides and in each new page that margin should be applied.
And here is my code.
private printDocument() {
const input = document.getElementById("pdf");
html2canvas(input)
.then((canvas) => {
const pdf = new jsPDF("p", "px", "a4");
pdf.addHTML(input, 0, 0, {
pagesplit: true,
background: "#ffffff",
}, () => {
pdf.save("download.pdf");
});
});
}
please help me its argent.
Thank you in advance

I tried to use jsPDF to workaround this problem, but i did not succeed. The way that jsPDF manage the content to split in paged are not clear to me.
So i decided to use pdfMake, another amazing js library.
I got these infos in this question: Generating PDF files with JavaScript
In the question that you metioned (Generating a PDF file from React Components), the best answer sugest a good way to handle the pagination. You make a div for each page. But in my case, my content can dinamically increase your vertical size, so i can't fix the div's vertical sizes.
So, i did like this:
printDocument() {
const divs = document.getElementsByClassName('example');
const newList = [].slice.call(inputs);
var contentArray = []
var docDefinition = {
pageSize: {width: 800, height: 1173},
content: [
{
text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Confectum ponit legam, perferendis nomine miserum, animi. Moveat nesciunt triari naturam.'
}
]
}
Promise.map(newList, async (element, index) => {
let canvas = await html2canvas(element);
const imgData = await canvas.toDataURL('image/png');
// console.log("imgData URL => ", imgData)
// margin horizontal -40 = removing white spaces
return contentArray[`${index}`] = [{ image: imgData, width: canvas.width, height: canvas.height, margin: [-40, 0] }, {
text: ` ${index} - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Confectum ponit legam, perferendis nomine miserum, animi.`
}];
}).then(
() => ( docDefinition.content.push(contentArray))
).then(
() => {
console.log("... starting download ...")
pdfMake.createPdf(docDefinition).download('examplePdf.pdf')
}
)
}
// In your react's component constructor ...
constructor(props) {
super(props);
this.printDocument = this.printDocument.bind(this)
}
// the imports below ...
import Promise from 'bluebird';
import html2canvas from 'html2canvas';
import pdfMake from 'pdfmake/build/pdfmake.js';
import pdfFonts from "pdfmake/build/vfs_fonts.js";
pdfMake.vfs = pdfFonts.pdfMake.vfs;
// i'm using these middlewares
import promise from 'redux-promise'
import multi from 'redux-multi'
import thunk from 'redux-thunk'
<div>
The approach here is: a div it's not a page. Because if the image generated by the canvas element it's bigger than the page vertical size, we'll need to control the pagination by ourselves. So, we broke our content in small elements to the pdf generator handle the pagination to us. This way we garantee that the pagination will occurs without cuts.
<div className="example" style={{ backgroundColor: '#ffffff', maxWidth: '800px', maxHeight: '1173px', borderStyle: 'groove', borderColor: 'red', margin: '0px' }} >
// any content or component here, we need maxHeight to be sure that the div's height size it's not bigger than the your PDF doc's height dimension, else your div may never be rendered inside it.
</div>
<div className="example" style={{ backgroundColor: '#ffffff', maxWidth: '800px', maxHeight: '1173px', borderStyle: 'groove', borderColor: 'red', margin: '0px' }} >
// any content or component here, we need maxHeight to be sure that the div's height size it's not bigger than the your PDF doc's height dimension, else your div may never be rendered inside it.
</div>
<div className="example" style={{ backgroundColor: '#ffffff', maxWidth: '800px', maxHeight: '1173px', borderStyle: 'groove', borderColor: 'red', margin: '0px' }} >
// any content or component here, we need maxHeight to be sure that the div's height size it's not bigger than the your PDF doc's height dimension, else your div may never be rendered inside it.
</div>
</div>
<div>
<button onClick={this.printDocument}> print using PDFMake </button>
</div>
Using the Promise.map by bluebird with the async/await resources, we can ensure that we'll wait till the end of generation of all images from canvas. This process can take a while depending of your image's size.
http://bluebirdjs.com/docs/api/promise.map.html
Take a look at pdfMake's github:
https://github.com/bpampuch/pdfmake
And his playground with excelent examples and how tos:
http://pdfmake.org/playground.html
I'll still trying to upgrade this way to solve this problem with the pagination, but it was the quickest way that i solved the problem and i hope to be useful for somebody.

Are you try?
const printDocument= () => {
const input = document.getElementById('divToPrint');
const input2 = document.getElementById('divToPrint2');
const pdf = new jsPDF();
html2canvas(input)
.then((canvas) => {
const imgData = canvas.toDataURL('image/png');
pdf.addImage(imgData, 'JPEG', 0, 0);
pdf.addPage();
html2canvas(input2)
.then((canvas2) => {
const imgData2 = canvas2.toDataURL('image/png');
pdf.addImage(imgData2, 'JPEG', 0, 0);
pdf.save("download.pdf");
})
;
})
;
}

Related

jsPDF - Print current pagenumber in footer of all pages

Im trying to print the current page numbers using this: https://github.com/MrRio/jsPDF/pull/260
But jsPDF renders a huge amount of blank pages and crashes :(
Without the footer, every thing works fine and I get a nice PDF with 27 pages, but without footers ofcause
Console errors:
my footer is:
<footer>
<div style='text-align:center;'>Page <span class="pageCounter"></span>/<span class="totalPages"></span></div>
</footer>
and heres my Jquery part:
var doc = new jsPDF();
var margins = {
top: 10,
left: 10,
right: 10,
bottom: 20,
width: 265
};
doc.setProperties({
title: 'Title',
subject: 'This is the subject',
author: 'Author Name',
keywords: 'generated, javascript, web 2.0, ajax',
creator: 'Creator Name'
});
length = doc.internal.getNumberOfPages()
doc.fromHTML(response, margins.left, margins.top,{
'width': margins.width // max width of content on PDF
},
function(){
doc.save('noter.pdf');
}, margins);
You can use this code :
var doc = jsPDF();
doc.addPage();
doc.addPage();
doc.addPage();
doc.addPage();
var pageCount = doc.internal.getNumberOfPages(); //Total Page Number
for(i = 0; i < pageCount; i++) {
doc.setPage(i);
let pageCurrent = doc.internal.getCurrentPageInfo().pageNumber; //Current Page
doc.setFontSize(12);
doc.text('page: ' + pageCurrent + '/' + pageCount, 10, doc.internal.pageSize.height - 10);
}
Very strange! but apparently, the footer needs to have a <p> tag to make it work, even though it isen´t like that in the example on the github page...
So I´ve changed
<footer>
<div style='text-align:center;'>Page <span class="pageCounter"></span>/<span class="totalPages"></span></div>
</footer>
to
<footer>
<div><p>Page <span class="pageCounter"></span>/<span class="totalPages"></span></p></div>
</footer>
and it works now

Highcharts : synchronized charts, It's possible export download image all in one?

As you can see the demo is 3 in 1 chart.
I can download it separately by navigation menu.
But that's not what I expected.
It's possible export download image all in one?
Code
<script src="https://code.highcharts.com/modules/exporting.js"></script>
navigation: {
buttonOptions: {
align: 'center'
}
}
Demo : http://jsfiddle.net/puff0211/r43w7qne/
I merged the fiddle from your answer with this example: http://jsfiddle.net/highcharts/gd7bB/
Final effect seems to work fine: http://jsfiddle.net/kkulig/pzfhnpjh/
The only modification that I did was moving width definition for every chart from CSS (for some reason it doesn't work when defined there):
.chart {
min-width: 320px;
max-width: 800px;
margin: 0 auto;
/*height: 220px;*/
}
to chart options in JS:
chart: {
marginLeft: 40, // Keep all charts left aligned
spacingTop: 20,
spacingBottom: 20,
height: 220 // added
},
You can use Html2canvas library to capture your web page (or a portion of it) and transform to a Canvas, so it can be saved as a picture:
html2canvas($("#container"), {
onrendered: function(canvas) {
saveAs(canvas.toDataURL(), 'download.png');
}
});
If you want, using jsPdf, the Canvas can be saved also as PDF.
html2canvas($("#container"), {
onrendered: function(canvas) {
var imgData = canvas.toDataURL('image/png');
var doc = new jsPDF('p', 'pt', 'a4');
doc.addImage(imgData, 'PNG', 10, 10);
doc.save('download.pdf');
}
});
Check this fiddle (update from yours): http://jsfiddle.net/beaver71/saku390u/

Fit a wider table into PDF

This is in continuation to the question asked in github, Fit a wider table into PDF #261.
I'm reusing the same method(doc.autoTable) to create PDF out of different HTML inputs.
So, If I define the column style of 0th column as columnWidth: 'wrap', then the same style will be applied for all the HTML tables that invoke this particular method.
I'm not sure If I can follow long text example, as both the column names & table body are coming from HTML page directly. Whereas in the long text example, I'm seeing the column names being declared/defined as shown below
var columnsLong = getColumns().concat([
{title: "Title with\nlinebreak", dataKey: "text2"},
{title: "Long text column", dataKey: "text"},
]);
Now there are 2 questions.
1. I dont want to apply 'WRAP' for all the columns, as the table gets cut.
2. Need to apply 'wrap' for certain columns alone by mentioning the column name that comes from HTML/GSP page.
This is my code
var res = doc.autoTableHtmlToJson($(".printReportsCaveat")[0]);
doc.autoTable(res.columns, res.data, {
columnStyles : {'Plant':{columnWidth: 'wrap'},
'Mine':{columnWidth: 'wrap'},
0:{textColor: [0,105,170]}
},
margin: {top: 55, bottom : 110},
headerStyles: {
overflow: 'linebreak',
// columnWidth: 'auto',
halign: 'center'
},
styles : {
overflow: 'linebreak',
halign: 'center',
fontSize: 8
},
createdCell: function(cell, data) {
var group = $('#groupByValue').val();
addColorToCell(group, level3Flag, level2Flag, data, cell);
},
addPageContent : function(data) {
printHeadNFoot(doc, userDtl, data);
},
drawCell: function(cell, data) {
designCell(data,doc);   
},
});
Kindly help!
If I understand your issue correctly you can try referencing a specific column by index instead of by key. I.e. columnWidth: {0: columnWidth: 'wrap'}.

Style-Binding using Computed Properties and VueX

How would I bind styles using computed properties in VueJS while integrating with VueX.
The issue I am having is in regards to my style properties not updating after a change in my VueX Store.
Code Examples:
//VueX Store
const store = new Vuex.Store({
state : {
div: [
{
offset: 0,
width: 1,
text : 'Hello World'
},
{
offset: 0,
width: 1,
text : 'Hello World As Well'
}
]
}
});
//My component
<template>
<div v-bind:style="{ width: width, left: offset}">
<p>{{text}}</p>
</div>
</template>
<script>
export default {
name: 'divBox',
computed : {
text : function() {
return this.$store.state.div[this.Id].text;
},
width : function() {
return this.$store.state.div[this.Id].width;
},
offset : function() {
return this.$store.state.div[this.Id].offset;
}
},
props : ['Id']
}
</script>
Here is a working example of how to use vuex to do what you are wanting. https://jsfiddle.net/n9jmu5v7/770/ I assume your problem is the fact that your store does not contain any mutations https://vuex.vuejs.org/en/mutations.html.
mutations: {
bgChange: state => state.bg='grey',
colorChange: state => state.color='green'
}
Also, remember that just because your using vuex doesn't mean you need to put everything into it, it is fine to keep local data in a component. For example component style information sounds like something that doesn't need to be shared with anything else (Obviously you may have a reason for storing it in vuex that does make sense).

jQuery UI Resizable + Draggable: disappearing on drop

I am trying to allow an image to be dropped into a container and enable resizing, however, when dropped it disappears until the handle is dragged. I have no errors in the console.
Does this appear to be a CSS generated issue? I have tried changing the z-index but to no avail.
Does anyone have any suggestions as to what would cause this?
Issue Seen Here:
http://jsfiddle.net/xBB5x/9662/
JS:
$(document).ready(function(){
$(".droppableShape").draggable({
helper:'clone'
});
$(".canvas").droppable({
accept: ".droppableShape",
tolerance: 'fit',
drop: function(event,ui){
// Set variables
var new_field = $(ui.helper).clone().removeClass('droppableShape');
var droppable_page = $(this);
var droppableOffset = $(this).offset();
// Check the tool type
switch(new_field.attr('class').split(" ")[0]) {
case "strokeTool":
new_field.css('top', ui.position.top - droppableOffset.top);
new_field.css('left', ui.position.left - droppableOffset.left);
break;
default:
console.log("A class type was not detected");
}
new_field.find( "img" ).resizable({
maxHeight: 47,
maxWidth: 151,
minHeight: 18,
minWidth: 60,
aspectRatio: 151 / 47
});
// Set Draggable Options
new_field.draggable({
containment: droppable_page,
});
// Add to drop area
$(this).append(new_field);
}
});
});
HTML:
<div class="container">
<div id="toolbar">
Tools:
<div class="droppableShape strokeTool">
<img width="125" src="http://41.media.tumblr.com/75b247a33fee823ac735d86270cfa813/tumblr_mp5loljbFz1s56exfo1_500.png" />
</div>
</div>
<div id="canvas_area">
<div class="canvas ui-droppable" id="page1"><img src="http://www.astrologychanneletc.com/wp-content/uploads/2014/09/blankcanvas.jpg"></div>
</div>
</div>
I'm not sure why, but calling .resizable() on your img causes a div.ui-wrapper to be created as its parent, and this new div has a width and height of 0. The solution is to call .resizable() on new_field and not the img inside of it:
new_field.resizable({
maxHeight: 47,
maxWidth: 151,
minHeight: 18,
minWidth: 60,
aspectRatio: 151 / 47
});
Then, to grow/shrink your img as its parent div is resized, add an event listener:
new_field.on("resize", function(rEvent, rUI) {
new_field.find("img").css({
"width": rUI.size.width,
"height": rUI.size.height
});
});
works for me in Chrome and Firefox on Linux.
http://jsfiddle.net/xBB5x/9663/

Resources