Add content before and after table (jsPDF autotable) - jspdf

i have a question to jsPDF autotable.
My Code:
$('#printBtn').on('click', function() {
var pdf = new jsPDF('p', 'pt', 'a4');
var res = pdf.autoTableHtmlToJson(document.getElementById("tablePrint"));
pdf.autoTable(res.columns, res.data, {
theme : 'plain',
styles: {
fontSize: 12
},
showHeader: 'never',
createdCell: function(cell, data) {
var tdElement = cell.raw;
if (tdElement.classList.contains('hrow')) {
cell.styles.fontStyle = 'bold';
}
}
});
pdf.save("test.pdf");
});
I want add Text before and after the Table from a div.
I have found this Code Snippet in jsPDF autotable examples:
var before = "text before";
pdf.text(before, 14, 30);
This code works good. I have insert that before pdf.autoTable(...});.
But i dont know what the number 14 and 30 for?
Then i have the code insert after the pdf.autoTable function call and the Text printed on the last page of pdf but on the top of the page, not on the end, why?
Sorry for my bad englisch.
Thanks for help.

If what you want is to add something before you must first move the table that you are adding with autotable, you achieve this by adding a startY: 150 attribute within doc.autotable:
pdf.autoTable(res.columns, res.data, {
theme : 'plain',
styles: {
fontSize: 12
},
startY: 150,
showHeader: 'never',
createdCell: function(cell, data) {
var tdElement = cell.raw;
if (tdElement.classList.contains('hrow')) {
cell.styles.fontStyle = 'bold';
}
}
});
150 is the value in pixels you want to move. Above this you can place the text you want with the code you placed.
var before = "text before";
pdf.text(before, 14, 30);
Now the values of 14 (Value in Y) and 30 (Value in Y) are the values that you want the text to move in pixels.
In order for you to add text after the table you must first obtain in which number of pixels your table ended and from there enter the text you want.
let finalY = pdf.previousAutoTable.finalY; //this gives you the value of the end-y-axis-position of the previous autotable.
pdf.text("Text to be shown relative to the table", 12, finalY + 10); //you use the variable and add the number of pixels you want it to move.

Here's my stab at an answer:
So this is your Autotable's function call:
var pdf = new jsPDF('p', 'pt', 'a4');
Since jsPDF Autotables is based on jsPDF, you'll have to go here:
pt is a unit of measurement called points, so 14 and 30 are points. At the first position, 14, moves elements left and right. The second position, 30, moves elements down and up. I'm guessing they're like pixels(px). Seems like you have to move your text to your desired locations by using points.

An idea that helped me is to placing a Table with no content on the position where you would like to add the new table.
autoTable(doc, {
body: [],
startY: finalY + bias
});

Related

jsPDF - practical example for a fit-to-width shopping cart HTML page?

I'm trying to implement jsPDF pdf saving in a Vue app and I'm finding it overwhelming and nearly impossible to affect any visual change in the output.
The current results are: gigantic text and huge images.
Is there a way to get it to respect any of my CSS, #mediap print or otherwise, particularly for div borders?
The docs are very deep so I'd love an example of how to take a div-based table-like layout—e.g. a shopping cart—and fit it to width. I'd also love tips on avoid content bleeding across page breaks.
I've tried doc.setFont, doc.setFontSize and other methods and nothing changes the output.
this.doc.html(document.getElementById("pdfList"), {
callback: function (doc) {
doc.save("Cycad Inventory");
},
x: 40,
y: 40,
});
exportPDF = () => {
const unit = "pt";
const size = "A4"; // Use A1, A2, A3 or A4
const orientation = "portrait"; // portrait or landscape
const marginLeft = 30;
const doc = new jsPDF(orientation, unit, size);
doc.setFontSize(14);
const title = "Report";
const headers = [["Time", "Source", "Destination", "Protocol", "Length"]];
const data = this.state.ipData.map(elt => [elt.time, elt.source, elt.destination, elt.protocol, elt.length]);
let content = {
startY: 50,
head: headers,
body: data,
}
doc.text(title, marginLeft, 40);
doc.autoTable(content);
doc.save(this.state.ipData.fileName);
}
This is a code I used in reactJS. I hope this will help you to do changes to your code. Here I have declared the height, width, font sizes margins.

yAxis resizer to change svgrenderer position also highcharts

I have a chart with some indicators below it. Each indicator area consists
of a svg renderer button. so when I use resize property to drag and resize
the panes, the series resized perfectly but the button remains in its same
position, Can we move the button with the resizer?
Here I created a sample link to regenerate
https://jsfiddle.net/q0ybpnvx/2/
Any help will be appreciated. I am having great trouble. Thank you
You can add and position the custom button in render event:
chart: {
events: {
render: function() {
var chart = this;
if (chart.customBtn) {
chart.customBtn.attr({
y: chart.yAxis[1].top,
});
} else {
chart.customBtn = chart.renderer.button(
'sometext',
5,
chart.yAxis[1].top,
function() {
console.log('some task')
}).add()
}
}
}
},
Live demo: https://jsfiddle.net/BlackLabel/b7vq4ecy/
API Reference:
https://api.highcharts.com/highcharts/chart.events.render
https://api.highcharts.com/class-reference/Highcharts.SVGElement#attr
I was able to do something by manually changing the x/y attributes of my svgRenderer label, the same should apply to buttons.
I'm in angular and have a listener for screen resizing:
Also Note that you can change the entire SVGRenderer label with the attr.text property.
this.chart = Highcharts.chart(.....);
// I used a helper method to create the label
this.chart.myLabel = this.labelCreationHelperMethod(this.chart, data);
this.windowEventService.resize$.subscribe(dimensions => {
if(dimensions.x < 500) { //this would be your charts resize breakpoint
// here I was using a specific chart series property to tell where to put my x coordinate,
// you can traverse through the chart object to find a similar number,
// or just use a hardcoded number
this.chart.myLabel.attr({ y: 15, x: this.chart.series[0].points[0].plotX + 20 });
} else {
this.chart.myLabel.attr({ y: 100, x: this.chart.series[0].points[0].plotX + 50 });
}
}
//Returns the label object that we keep a reference to in the chart object.
labelCreationHelperMethod() {
const y = screen.width > 500 ? 100 : 15; 
const x = screen.width > 500 ? this.chart.series[0].points[0].plotX + 50 :
this.chart.series[0].points[0].plotX + 20
// your label
const label = `<div style="color: blue"...> My Label Stuff</div>`
return chart.renderer.label(label, x, y, 'callout', offset + chart.plotLeft, chart.plotTop + 80, true)
.attr({
fill: '#e8e8e8',
padding: 15,
r: 5,
zIndex: 6
})
.add();
}

jsPDF fromHTML image (to large) and page margin-bottom (not working)

I just implemented jspdf in a page, where users work with an editor and then want to be able to print the result. When I tested some example content, I run in to that problem the images are not displayed in there orignial size. They are scaled/displayed so large, that they are lager then the width of the page.
Also I had a problem with the page height/margin-bottom. If the content is to large for a page, then there will be no margin at the bottom of the page. So if for what ever reason an elemtent seems to be too large for a page, this element goes until the end of the page.
I googled a lod, but I couldn't find a solution to avoid that too large elements go until the border of the page.
And I also could not find something, for how to set an image to it's original or max-size when I don't know the content of the page.
Here is js code and an example page: jsfiddle-example
$(".printPdf").click(function () {
var pdfdoc = new jsPDF("portrait");
var getPageTitle = $("#page-title");
var pageTitle = (getPageTitle != "")? getPageTitle : "Document";
var contentClass = ".page-content";
var $pageContent = $(contentClass);
var margins = {
top: 10,
bottom: 20,
left: 15,
width: 180
};
pdfdoc.setDisplayMode('original');
pdfdoc.fromHTML($pageContent.get(0), margins.left, margins.top, {
"width": margins.width,
"text-align": "unset"
}, function () { pdfdoc.save(pdfdoc.pageTitle + ".pdf") }, margins);
});

jspdf first PDF page is blank

Im trying to get my jspdf to not save a blank page PDF. I been trying many examples, but nothing works :(. My table content is saved correct, with a image on the second page of the PDF, but my first page is blank.
var pdf = new jsPDF('o', 'pt', 'a6');
//pdf.autoTable(this.columns, this.data);
//var width = pdf.internal.pageSize.width;
//var height = pdf.internal.pageSize.height;
pdf.addPage('1800','900');
pdf.addImage(imgData, 'PNG', 120, 40, 120, 100);
pdf.setTextColor(0,0,0);
pdf.text(120, 20, 'BOOKINGS');
pdf.setFontSize(22);
// 'o', 'pt', 'a4'
// 'p', 'pt', 'letter'
// source can be HTML-formatted string, or a reference
// to an actual DOM element from which the text will be scraped.
source = jQuery('.dataTables_wrapper')[0];
// we support special element handlers. Register them with jQuery-style
// ID selector for either ID or node name. ("#iAmID", "div", "span" etc.)
// There is no support for any other type of selectors
// (class, of compound) at this time.
specialElementHandlers = {
// element with id of "bypass" - jQuery style selector
'#bypassme': function (element, renderer) {
// true = "handled elsewhere, bypass text extraction"
return true
}
};
margins = {
top: 120,
bottom: 0,
left: 0,
width: 2000
};
// all coords and widths are in jsPDF instance's declared units
// 'inches' in this case
pdf.fromHTML(
source, // HTML string or DOM elem ref.
margins.left, // x coord
margins.top, { // y coord
'width': margins.width, // max width of content on PDF
'elementHandlers': specialElementHandlers
},
function (dispose) {
// dispose: object with X, Y of the last line add to the PDF
// this allow the insertion of new lines after html
pdf.save('bookings.pdf');
}, margins);
}
The first page is blank because adding a page is the first call you make after calling the jsPDF constructor. The constructor does already create a (first) blank page. To get rid of the additional empty page at the start of the document, either delete the first page calling doc.deletePage(1), or do not add a page after the constructor is being called.

How to post process with a dynamically created Google Chart?

I have a chart that looks like this:
Data comes from a web service and I want all the data to remain intact as it comes through to my model. The bars on the chart represent timespans (just a number column that displays the timespan.TotalHours).
Occasionally, as shown in the image, a time span will be negative - meaning it was ahead of schedule. When this is the case, I do not want to display that segment at all and in fact would like to subtract that time from the green segment. From what I've looked up, something that could help me with the actual subtraction of the negative number could be post processing. But how would I be able to modify my chart creation after that's done?
In case it's relavent, the code for the chart's creation is below:
<script type="text/javascript">
google.charts.load('current', { 'packages': ['corechart'] });
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
// Since every row will alwyas have this as the first column, we do not need to iterate. The first column will always be this title.
data.addColumn("string", "Category");
// This iterates through each stage (timespan) and creates a column for it.
// And display only the relavent time spans on the chart.
#foreach (var stage in Model.selectedForm.rows[0].stages)
{
#Html.Raw("data.addColumn('number', '" + Regex.Replace(stage.label, "(\\B[A-Z])", " $1") + "');") // Adding a numeric column for each timespan (stage) in that row. Using Regex to make the labels more friendly to the user.
}
#foreach (var row in Model.selectedForm.rows)
{
#Html.Raw("data.addRow(['" + row.label + "'") //The first part of our row is the label - each subsequent stage will be filled in at each iteration below
foreach (var stage in row.stages) //For each stage in the row being created ...
{
if (stage.timespan.TotalHours <= 0)
{
#Html.Raw(", " + "null") // replace the column with a null value
}
else
{
#Html.Raw(", " + stage.timespan.TotalHours) //...Continue the HTML builder adding additional timespans to eventually...
}
}
#Html.Raw("]);\n\n") //...close it here
}
var options = {
titlePosition: 'none',
width: 1200,
height: 400,
legend: { position: 'top', maxLines: 3 },
bar: { groupWidth: '75%' },
isStacked: true
}
var chart = new google.visualization.BarChart(document.getElementById('SOPChart'));
chart.draw(data, options);
}
</script>
Edit: I've added a check in my code that generates the table to account for the negative value and not drawing the purple segment. However there is still the matter of subtracting that time from the previous segment which I am unclear on how to accomplish.
Edit: Added the updated code with comments. Also added an updated image of what this produces. Now that the actual chart is being handled, I still need a way to subtract the value off of the previous (green) segment. After this edit, here is what the chart looks like now:
if you want to try javascript on the client to correct the issue...
remove the last edit and allow the negative value to come thru
then use following snippet to correct, add just above --> var options = {
for (var i = 0; i < data.getNumberOfColumns(); i++) {
if (data.getValue(i, data.getNumberOfColumns() - 1) < 0) {
// subtract from next to last column
data.setValue(i, data.getNumberOfColumns() - 2, data.getValue(i, data.getNumberOfColumns() - 2) + data.getValue(i, data.getNumberOfColumns() - 1));
// remove negative from last column
data.setValue(i, data.getNumberOfColumns() - 1, null);
}
}

Resources