I am trying to convert my html page to pdf using jsPDF. Essentially i am using the html method of jsPDF, giving it a source, and options and then in the callback function i would save the document.
But i am having a problem when it comes to dividing the single html document into mulitple divs and saving each div in a page. I am trying the below code and it renders all the pages blank.
My html looks like
<div class = "resultpage" >
<div class = "print-section-1">
//some content
</div>
<div class = "print-section-2">
//some content again
</div>
<div class = "print-section-3">
//content...
</div>
</div>
My js looks like :
window.jsPDF = window.jspdf.jsPDF;
let doc = new jsPDF({
orientation : "portrait",
unit : 'px',
format : 'a4',
hotfixes : ["px_scaling"],
putOnlyUsedFonts : true
})
doc.html($(".prints-section-1")[0], {
x: 10,
y : 10,
margin : [50, 200, 50, 200],
autoPaging : "text"
})
doc.addPage()
doc.html($(".print-section-2")[0], {
x: 10,
y : 10,
margin : [50, 200, 50, 200],
autoPaging : "text"
})
doc.addPage()
doc.html($(".print-section-3")[0], {
x: 10,
y : 10,
margin : [50, 200, 50, 200],
autoPaging : "text"
})
doc.save("test")
This renders all the pages empty.
If i modify the js, to have a chaining of callbacks like below, i am able to get the last div (print-side-2 in this case) printed but the pages previous to it are blank.
doc.html($(".print-section-1")[0], {
callback : function(doc) {
doc.addPage();
doc.html($(".print-section-2")[0], {
callback : function(doc) {
doc.save("test.pdf")
}
x: 10,
y : 10,
margin : [50, 200, 50, 200],
autoPaging : "text"
})
}
x: 10,
y : 10,
margin : [50, 200, 50, 200],
autoPaging : "text"
})
Can anyone point out what i am doing wrong ? I searched for solutions but many use deprecated methods like addFromHTtml, and some suggested using line breaks like "<!--ADD_PAGE>" _ and
style = "page-break-before : always" but both don't work. I looked into the documentation and it hasn't been great support. Please help me.
Referencing this answer (refrencing)
Just use await and make sure to return doc inside the callback
something like this
var doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'letter'
});
var field = "<b>html test </b>";
doc.text(10, 10, "test");
//add first html
await doc.html(field, {
callback: function (doc) {
return doc;
},
width: 210,
windowWidth: 210,
html2canvas: {
backgroundColor: 'lightyellow',
width: 210,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 50,
autoPaging: 'text'
});
window.open(doc.output('bloburl'));
now you can call doc.html as many times as you want for that same document
Originally posted by #bakuur in https://github.com/parallax/jsPDF/issues/3074#issuecomment-1328427528
Related
To my understanding after going through GitHub, documentation and stack overflow:
doc.html() is supposed to be able to take in a string with html formatting (AKA rich text)
There has been work done to ensure that doc.html() is able to produce multiple html snippets within the same pdf document
To produce more than one html in the same document, one is supposed to utilize the callback function and nest any further edits to the pdf.
doc.html() does not behave in the same sense as the other functions (text, rect, etc.) where you can add as many as you want.
var pageWidth = 1000,
lineHeight = 1,
margin = 20,
maxLineWidth = pageWidth - margin * 2,
fontSize = 11,
ptsPerMM = 72 / 25.6,
oneLineHeight = (fontSize * lineHeight) / ptsPerMM;
var doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'letter',
lineHeight: lineHeight
});
var field1 = "<b>this is field 1 </b>";
var field2 = "<b>this is field 2 </b>";
//add first html
doc.html("<body>" + field1 + "</body>", {
callback: function (doc) {
//do nothing
},
width: maxLineWidth ,
windowWidth: maxLineWidth ,
html2canvas: {
backgroundColor: 'lightyellow',
width: maxLineWidth ,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 10,
autoPaging: 'text'
});
//add second html
doc.html("<body>" + field2 + "</body>", {
callback: function (doc) {
//do nothing
},
width: maxLineWidth ,
windowWidth: maxLineWidth ,
html2canvas: {
backgroundColor: 'lightyellow',
width: maxLineWidth ,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 10,
autoPaging: 'text'
});
//export pdf
window.open(doc.output('bloburl'));
The code above does not work. how can I fix this issue where I cannot export multiple html snippets in the same pdf document?
I went over GitHub, documentation and stack overflow and for the first time ever, whenever someone asks a question related to this topic, they don't seem to get an answer.
I would really appreciate it if someone could help me figure this out.
I've tried returning doc within the callback and reusing that but that didn't seem to work
Found it!
just use await and make sure to return doc inside the callback
something like this
var doc = new jspdf.jsPDF({
orientation: 'p',
unit: 'pt',
format: 'letter'
});
var field = "<b>html test </b>";
doc.text(10, 10, "test");
//add first html
await doc.html(field, {
callback: function (doc) {
return doc;
},
width: 210,
windowWidth: 210,
html2canvas: {
backgroundColor: 'lightyellow',
width: 210,
height: 150
},
backgroundColor: 'lightblue',
x: 10,
y: 50,
autoPaging: 'text'
});
window.open(doc.output('bloburl'));
I have this code :
const createBrandedPresentation = () => {
let pptx = new pptxgen();
let slide = pptx.addSlide({ sectionTitle: 'ZG test' });
slide.background = { color: '#1c1c21' };
slide.addText('Title', {
y: 1.67,
fontSize: 88,
bold: true,
color: '#eb34a8',
isTextBox: true,
align: 'center',
});
slide.addImage ({
path: "./Logosvg.svg",
w: 1.5,
h: 1.5,
x: 7.5,
y: 2
})
return pptx;
};
but every time I generate a new presentation, the photo is empty. I'm calling this function from a react component, (dev server) and I can't understand why it can't add images even tho thse images are served statically with my app. This question goes double for PNG (which don't work either)
The docs say that when the chart is not bound, it'll start observing the chart.element property. I've tried not setting the bindto property for the options object passed to generate, and also setting it to null.
But if I later set the chart.element property to anything i.e. chart.element = document.getElementById("#chart-here"), nothing happens.
What is the correct approach of doing this? Or is it something that I misunderstood?
Thanks.
You just need to use setTimeout https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
var generateChart = function () {
bb.generate({
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 50, 20, 10, 40, 15, 25]
]
},
bindto: "#chart-here"
});
};
setTimeout(generateChart, 2000)
<title>billboard.js</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/billboard.js/dist/billboard.min.css">
Chart will appear in 2 seconds...
<div id="chart-here"></div>
I'm wondering whether it's possible to target multiple series in the same rule using tokens. In essence, what I am aiming for is "if the value from series 1 is greater than the value in the same position in series 2, change some styles".
Zingchart config:
var config = {
// ...
'type': 'area',
'plot': {
'rules': [
{
'rule': '', // %v from series 1 > %v from series 2
'background-color': '#ccc'
}
]
},
'series': [
{
'text': 'Series 1',
'values': [36, 40, 38, 47, 49, 45, 48, 54, 58, 65, 74, 79, 85, 83, 79, 71, 61, 55]
},
{
'text': 'Series 2',
'values': [40, 40, 40, 50, 50, 50, 50, 60, 60, 80, 80, 80, 80, 80, 80, 80, 60, 60]
}
]
}
If that's not possible, is there another way to achieve the same outcome? I'm aiming to change the style of an individual point when the series 1 value is greater than the series 2 value.
At this time of writing (v2.1.4 and below), there is no way to apply rule logic across different series values. The best way to approach this would be to reference each series value array within each series object with a "data-" as the key value. (See below for a working example).
With that being said, I have submitted a ticket to look into implementing cross series logic into the rules syntax!-- I'm on the developer team on ZingChart, feel free to reach out with any further questions.
var ruleSet =
[
//Controls the series 0 coloring
{
rule : "%data-series-1 > %v",
backgroundColor : "#007c9b",
borderColor : "#006a84",
size : "8px"
},
//Controls the series 1 coloring
{
rule : "%data-series-0 > %v",
backgroundColor : "#188f00",
borderColor : "#188f00",
size : "8px"
}
];
var series0Values = [10,10,20,10,10,10,10,10];
var series1Values = [20,20,10,20,20,20,20,20];
var myConfig = {
type: "scatter",
plot :{
marker : {
rules : ruleSet
}
},
series : [
{
values : series0Values,
"data-series-1" : series1Values,
marker : {
backgroundColor : "#00c0ef",
borderColor : "#00c0ef"
}
},
{
values : series1Values,
"data-series-0" : series0Values,
marker : {
backgroundColor : "#26de00",
borderColor : "#26de00"
}
},
]
};
zingchart.render({
id : 'myChart',
data : myConfig,
height: 400,
width: 400
});
<!DOCTYPE html>
<html>
<head>
<script src= "https://cdn.zingchart.com/zingchart.min.js"></script>
<script> zingchart.MODULESDIR = "https://cdn.zingchart.com/modules/";
</head>
<body>
<div id='myChart'></div>
</body>
</html>
I'm creating a AngularJS directive that is supposed to have a C3.js-based chart in it. Problem is that the C3 library does not see the DOM element it's supposed to attach to. The directive's link function looks something like this:
link: function(scope, element, attrs) {
scope.someid = 'id';
scope.chart = c3.generate({
bindto: "#somechart"+scope.someid,
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
console.log($("#somechart"+scope.someid).size()); // this is a test, outputs 0
}
The template for the directive has this in it:
<div id="#somechart{{ scope.someid }}">...</div>
The problem is that the c3.generate()'s bindto does not see the #somechartid. The console.log() I've put in outputs 0 which means the element is not present in the DOM at the moment when the link function is called.
If I call the same chart-generating code from the browser's console or even from some ng-click the chart gets rendered.
Is there a way to overcome this problem without using a solution like $timeout?
UPDATE 2014-07-15 15:33 Changed the code sample and added relevant line from directive's template.
Use $timeout function in your link function if you want to manipulate dom, which is not yet generated. See this
Have you tried something like this
link: function(scope, element, attrs) {
scope.chart = c3.generate({
bindto: element[0],
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
}
Maybe usage of element.find('#id') will help:
link: function(scope, element, attrs) {
var item = element.find('#somechartid');
scope.chart = c3.generate({
bindto: item,
data: {
columns: [
['data1', 30, 200, 100, 400, 150, 250],
['data2', 50, 20, 10, 40, 15, 25]
]
}
});
}