I am trying to convert a html div to pdf using jsPDF. With in my div I have a svg file with background image where user can draw rectangle, line, text etc. I am using d3.js for drawing. Now I want to save my div with all drawing to pdf but it only converting my text to pdf. My js code is
function htmlToPdf() {
console.log("--------------- with in demoFromHTML");
var pdf = new jsPDF('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 = $('svg.plancontainer')[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: 80,
bottom: 60,
left: 40,
width: 522
};
// 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.autoPrint();
pdf.output('dataurlnewwindow');
}, margins
);
}
and cdn is <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.debug.js"></script>
It print PRINT AREA instead of my image and text with out svg drawing.
It is my sample div's preview that I want to convert to pdf
I did not get any specific informatin that specify where it is possible using jsPDF or not.
Now my questions are
Is it possible using jsPDF or any other js library ?
If possible, would you please suggest me?
Any kind of help are appreciated. Thanks.
I am sharing my solution that may help someone. I could not manage print svg directly using jspdf instead what I have done is first convert svg to image using https://github.com/exupero/saveSvgAsPng then use that image to create pdf. Below is my code
Get base64 image uri using svgAsPngUri method of saveSvgAsPng and pass that image through callback function
svgAsPngUri(#svgObj, options, function (uri, options) {
pdf(uri, options.pdf)
});
where I am getting image uri as uri. With in my pdf function I am using this uri to make pdf
function pdf(b64Image, options) {
console.log("--------------- passing options is ", JSON.stringify(options, null, 4));
var image = new Image();
image.src = b64Image;
console.log('--------- pdf options' + JSON.stringify(options, null, 4));
var pdf = new jsPDF(options.orientation, null, options.format);
margins = {
top: 20,
bottom: 20,
left: 20,
right: 20
};
var pdfWidth = pdf.internal.pageSize.width;
var pdfHeight = pdf.internal.pageSize.height;
var footer_height = options.f_height || 30;
var htmlPageRightOffset = 0;
var outerRacBorder = 2;
var imageDrawableHeight = pdfHeight - margins.top - margins.bottom - footer_height - outerRacBorder;
var imageDrawableWidth = pdfWidth - margins.left - margins.right - outerRacBorder;
footer = {
top: margins.top + imageDrawableHeight + outerRacBorder + 10,
bottom: 20,
left: margins.left,
right: 20,
width: 100,
height: 25,
};
company_text_position = {
x: footer.left+2,
y: footer.top + 6
};
site_text_position = {
x: company_text_position.x,
y: company_text_position.y + 6
};
floor_plan_text_position = {
x: site_text_position.x,
y: site_text_position.y + 6
};
logo_text_position = {
x: pdfWidth - margins.left - 55,
y: pdfHeight - margins.bottom - 4
};
logo_image_position = {
x: logo_text_position.x +35,
y: logo_text_position.y - 4
};
/*
Image drawing on pdf
*/
imageSize = calculateAspectRatioFit(image.width, image.height, imageDrawableWidth, imageDrawableHeight);
pdf.addImage(image, 'JPEG', margins.left + 2, margins.top + 2, imageSize.width, imageSize.height);
/*
Outer rectangle
*/
pdf.rect(margins.left, margins.top, imageDrawableWidth + outerRacBorder, imageDrawableHeight + outerRacBorder);
// pdf.rect(margins.left, imageSize.height + 10, drawableWidth, (drawableWidth - imageSize.height));
pdf.rect(footer.left, footer.top, footer.width, footer.height);
console.log(footer.left);
console.log(footer.company_x);
var footer_data = getFooterInfo();
pdf.text("Company: " + footer_data.client, company_text_position.x, company_text_position.y);
pdf.text("Site: " + footer_data.site, site_text_position.x, site_text_position.y);
pdf.text("Floor Plan: " + footer_data.floor_plan, floor_plan_text_position.x, floor_plan_text_position.y);
pdf.text("Powered by: ", logo_text_position.x, logo_text_position.y);
var logo = new Image();
logo.src = $('#logo_image').val();
console.log(logo);
logoSize = calculateAspectRatioFit(logo.width, logo.height, 20, 10);
pdf.addImage(logo, 'JPEG', logo_image_position.x, logo_image_position.y, logoSize.width, logoSize.height);
pdf.autoPrint();
pdf.save(options.name + '.pdf');
}
/**
* Conserve aspect ratio of the orignal region. Useful when shrinking/enlarging
* images to fit into a certain area.
*
* #param {Number} srcWidth Source area width
* #param {Number} srcHeight Source area height
* #param {Number} maxWidth Fittable area maximum available width
* #param {Number} maxHeight Fittable area maximum available height
* #return {Object} { width, heigth }
*
*/
function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
if(srcHeight == 0 || srcWidth == 0){
return {width: maxWidth, height: maxHeight};
}
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight];
ratio = Math.min(ratio[0], ratio[1]);
return {width: srcWidth * ratio, height: srcHeight * ratio};
}
function getFooterInfo() {
var elem = $('.entityselbin .h4');
var info = {};
info.client = elem[0].innerHTML;
info.site = elem[1].innerHTML;
info.floor_plan = elem[2].innerHTML;
return info;
}
You can directly write the SVG to PDF with the latest canvg_context2d method mention in the example
https://github.com/MrRio/jsPDF/blob/master/examples/canvg_context2d/bar_graph_with_text_and_lines.html
This works for most of the svg contents.
Below is the working example of above question.
Need to include these three file refernece
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"),
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"),
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/canvg/1.5/canvg.min.js")
function createPDF() {
var svg = '';
// Provide the SVG parent div id
if (document.getElementById("ChartId") != null) {
svg = document.getElementById("ChartId").innerHTML;
}
if (svg)
svg = svg.replace(/\r?\n|\r/g, '').trim();
var pdfData = $('#htmlContainer');//main html div
html2canvas(pdfData, {
onrendered: function(canvas) {
var contentWidth = canvas.width;
var contentHeight = canvas.height;
//The height of the canvas which one pdf page can show;
var pageHeight = contentWidth / 592.28 * 841.89;
//the height of canvas that haven't render to pdf
var leftHeight = contentHeight;
//addImage y-axial offset
var position = 0;
//a4 format [595.28,841.89]
var imgWidth = 595.28;
var imgHeight = 592.28 / contentWidth * contentHeight;
var ctx = canvas.getContext('2d');
canvg(canvas, svg, {
offsetX: 10,
offsetY: 660,
ignoreMouse: true,
ignoreAnimation: true,
ignoreClear: true,
ignoreDimensions: true
});
var pageData = new Image();
pageData = canvas.toDataURL('image/jpeg', 1.0);
var pdf = new jsPDF('l', 'pt', 'a4', true);
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 100, 20, imgWidth, imgHeight);
} else {
console.log('page 2');
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight;
position -= 841.89;
//avoid blank page
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save('Test.pdf');
}
});
}
Hope this will be helpful.
Related
I have created a game in createjs. For images I am using bitmap to load them. I am able to give them positioning in my grid. I'm trying to change its width and height but its not working.
// this is where i am loading the image.
buttonSolve = new createjs.Bitmap(loader.getResult("buttonCategory"));
centerReg1(buttonSolve);
// i have tried a bunch of methods but none worked.
// buttonSolve.setDrawSize(200, 100);
// buttonSolve.sourceRect = { width: 280, height: 150 };
// buttonSolve.width = buttonSize.w;
// buttonSolve.height = buttonSize.h;
// buttonSolve.setBounds(0, 0, 250, 400);
// this is centerReg1 function:
function centerReg1(obj) {
obj.regX = 195;
obj.regY = 80;
// obj.setBounds(0, 0, 250, 100);
// obj.scaleX = -2;
// obj.x = canvasW / 2 - 120;
// obj.y = (canvasH / 100) * 75;
// obj.sourceRect = { width: 280, height: 150 };
}
kindly someone tell me how to do this!
I am not 100% sure
but you can solve this issue by using scaleX & scaleY properties of
buttonSolve bitmap
buttonSolve.scaleX = newWidth / originalWidth;
buttonSolve.scaleY = newHeight / originalHeight;
2nd method
buttonSolve.setTransform(0, 0, newWidth/originalWidth, newHeight/originalHeight);
Here
originalWidth & originalHeight are the original width & height
newWidth & newHeight are the updated width & height
How to perform resize and crop with ImageMagick using the data from cropperjs?
The user can upload a large image and zoom/pan to crop. Tried using blob but it looses too much quality and times out too often.
Example from fiddle with the following data:
Original Width: 1280
Original Height: 720
Width: 424.8717011756327
Height: 238.9903319112934
X: -155.17118867901692
Y: -1.4989251522088705
Scale: 23.82
Tried with this but it crops the wrong area. Also tried scaling the original image but that's too big for the server to handle.
convert orignial.jpg -resize "1280x720^" -gravity center -crop 424x238+-155+-1 +repage result.jpg
Example:
https://jsfiddle.net/1knw3a5e/
JS code:
$(function() {
var image = $('#crop-image');
var zoomSlider = document.getElementById('zoom-slider');
var canvasSize = null;
var pictureContainer = $('.picture-frame');
var maxPictureContainerWidth = parseFloat(pictureContainer.css('max-width')) || 450;
var maxPictureContainerHeight = parseFloat(pictureContainer.css('max-height')) || 350;
var isSliderInUse = false;
// Wall is in Cm, convert to Inches to work out pixel sizes at 300dpi
var wallWpx = (0.393700787 * pictureContainer.attr('data-width')) * 300; // data-width is the wall width in pixels
var wallHpx = (0.393700787 * pictureContainer.attr('data-height')) * 300; // data-height is the wall height in pixels
var sampleImageScaleFactor = (image.attr('width') / image.attr('original-width'));
var wallSize = {
width: wallWpx * sampleImageScaleFactor, // scaling the wall size corresponding the sample size
height: wallHpx * sampleImageScaleFactor,
originalWidth: pictureContainer.attr('data-width'),
originalHeight: pictureContainer.attr('data-height')
};
var wallAspectRatio = wallSize.originalWidth/wallSize.originalHeight;
var pictureContainerSizes = {
'width': maxPictureContainerWidth * (wallAspectRatio > 1 ? 1 : wallAspectRatio) ,
'height': maxPictureContainerHeight / (wallAspectRatio > 1 ? wallAspectRatio : 1)
};
pictureContainer.css(pictureContainerSizes).removeClass('hidden');
var zoomStep = 0.2;
var biggerSide = null;
var zoomModal = $('#modal-warning');
var handleZoomHold, handleZoomFired;
image.cropper({
zoom: 0.2,
guides: false,
cropBoxResizable: false,
cropBoxMovable: false,
//viewMode: 3,
dragMode: 'move',
left: 0,
top: 0,
//width: canvasSize.width,
//height: canvasSize.height,
//aspectRatio: 1,
toggleDragModeOnDblclick: false,
zoomOnTouch: true,
zoomOnWheel: true
});
// Event
image.on('built.cropper', function() {
image.cropper('setCropBoxData', {
left: 0,
top: 0,
width: pictureContainerSizes.width,
height: pictureContainerSizes.height
});
canvasSize = {
width: image.cropper('getCropBoxData').width,
height: image.cropper('getCropBoxData').height
};
biggerSide = canvasSize.width === image.cropper('getImageData').width ? 'width' : 'height';
var savedCropperSettings = {
sliceW: parseFloat($('input[name=sliceW]').val()),
sliceH: parseFloat($('input[name=sliceH]').val()),
sliceX: parseFloat($('input[name=sliceX]').val()),
sliceY: parseFloat($('input[name=sliceY]').val()),
scale: parseFloat($('input[name=scale]').val()) // saved adoptedZoomFactor
};
if (!savedCropperSettings.scale) {
return;
}
/* restoring saved settings */
image.cropper('zoomTo', canvasSize[biggerSide]/(wallSize[biggerSide]/savedCropperSettings.scale.toFixed(1)));
var cropboxData = image.cropper('getCropBoxData');
var scaleFactor = wallSize.originalHeight / cropboxData.height;
image.cropper('setCanvasData', {
left: savedCropperSettings.sliceX / scaleFactor + cropboxData.left,
top: savedCropperSettings.sliceY / scaleFactor + cropboxData.top
});
});
var adoptedZoomFactor = NaN;
var adoptedZoomElement = $('#adoptedZoom');
image.on('crop.cropper', function() {
var data = image.cropper('getData');
var canvasData = image.cropper('getCanvasData');
var cropboxData = image.cropper('getCropBoxData');
var scaleFactor = wallSize.originalHeight / cropboxData.height;
adoptedZoomFactor = parseFloat((wallSize[biggerSide] / data[biggerSide]).toFixed(2));
adoptedZoomElement.text(adoptedZoomFactor);
$('input[name=sliceW]').val(canvasData.width * scaleFactor);
$('input[name=sliceH]').val(canvasData.height * scaleFactor);
$('input[name=sliceX]').val((canvasData.left - cropboxData.left) * scaleFactor);
$('input[name=sliceY]').val(canvasData.top * scaleFactor);
$('input[name=scale]').val(adoptedZoomFactor);
});
});
That cropper tool does not work correctly on my Mac in Safari or Firefox or Chrome. It does not respect the scale values that are entered. It always comes out with results of scale=1. Perhaps I am doing it wrong.
But if you want to do it in ImageMagick, the correct way would be:
Original:
Cropper Screen Snap:
Cropper Result (dimensions 320x180; still scale=1):
Using ImageMagick (dimensions 640x360):
ww=320
hh=180
xx=40
yy=60
rotate=0
scale=2
scale=`convert xc: -format "%[fx:$scale*100]" info:`
convert barn.jpg -virtual-pixel white -define distort:viewport=${ww}x${hh}+${xx}+${yy} -filter point -distort SRT "$rotate" +repage -resize $scale% test.jpg
Note that ImageMagick -distort SRT permits scaling, but the scale is done before the cropping from the viewport. So I had to use the viewport crop first and then add -resize in percent (as scale=2 --> scale=200%)
The reason I used -distort SRT with the viewport crop is that it would allow offset cropping when the xx and yy values are negative. You cannot do that with a simple -crop.
So for example:
ww=320
hh=180
xx=-40
yy=-60
rotate=0
scale=1
scale=`convert xc: -format "%[fx:$scale*100]" info:`
convert barn.jpg -virtual-pixel white -define distort:viewport=${ww}x${hh}+${xx}+${yy} -filter point -distort SRT "$rotate" +repage -resize $scale% test2.jpg
If you download the image, you will see it is padded at the top and right with white, but still has a size of 320x180.
If you are cropping only within the bounds of the image, then you can use -crop and the Imagemagick command would be:
ww=320
hh=180
xx=40
yy=60
rotate=0
scale=2
scale=`convert xc: -format "%[fx:$scale*100]" info:`
convert barn.jpg -crop ${ww}x${hh}+${xx}+${yy} +repage -resize $scale% test4.jpg
Which produces the same results as my original viewport crop.
I've had success just using the data from getData without doing any math on the results.
var croppable = $('.croppable');
croppable.cropper({
autoCrop: true,
viewMode: 0,
background: false,
modal: true,
zoomable: true,
responsive: false,
crop: function(e) {
data = croppable.cropper('getData');
$('#extract_image_crop_x').val(data.x);
$('#extract_image_crop_y').val(data.y);
$('#extract_image_crop_width').val(data.width);
$('#extract_image_crop_height').val(data.height);
$('#extract_image_crop_rotate').val(data.rotate);
}
});
I gone through documentation of cropper by fengyuanchen. I want the image to be fit by default into canvas if rotated. But I couldnt find a way to achieve this. Any idea how to achieve this functionality?
I want it to be like this to be default: link
Check issue demo here: link
I fixed this behavior but for my special needs. I just needed one rotate button which rotates an image in 90° steps. For other purposes you might extend/change my fix.
It works in "strict" mode by dynamically change the cropbox dimensions.
Here my function which is called, when I want to rotate an image. Ah and additionally the misplacement bug has also been fixed.
var $image;
function initCropper() {
$image = $('.imageUploadPreviewWrap > img').cropper({
autoCrop : true,
strict: true,
background: true,
autoCropArea: 1,
crop: function(e) {
}
});
}
function rotateImage() {
//get data
var data = $image.cropper('getCropBoxData');
var contData = $image.cropper('getContainerData');
var imageData = $image.cropper('getImageData');
//set data of cropbox to avoid unwanted behavior due to strict mode
data.width = 2;
data.height = 2;
data.top = 0;
var leftNew = (contData.width / 2) - 1;
data.left = leftNew;
$image.cropper('setCropBoxData',data);
//rotate
$image.cropper('rotate', 90);
//get canvas data
var canvData = $image.cropper('getCanvasData');
//calculate new height and width based on the container dimensions
var heightOld = canvData.height;
var heightNew = contData.height;
var koef = heightNew / heightOld;
var widthNew = canvData.width * koef;
canvData.height = heightNew;
canvData.width = widthNew;
canvData.top = 0;
if (canvData.width >= contData.width) {
canvData.left = 0;
}
else {
canvData.left = (contData.width - canvData.width) / 2;
}
$image.cropper('setCanvasData', canvData);
//and now set cropper "back" to full crop
data.left = 0;
data.top = 0;
data.width = canvData.width;
data.height = canvData.height;
$image.cropper('setCropBoxData',data);
}
This is my extended code provided by AlexanderZ to avoid cuttong wider images than container :)
var contData = $image.cropper('getContainerData');
$image.cropper('setCropBoxData',{
width: 2, height: 2, top: (contData.height/ 2) - 1, left: (contData.width / 2) - 1
});
$image.cropper('rotate', 90);
var canvData = $image.cropper('getCanvasData');
var newWidth = canvData.width * (contData.height / canvData.height);
if (newWidth >= contData.width) {
var newHeight = canvData.height * (contData.width / canvData.width);
var newCanvData = {
height: newHeight,
width: contData.width,
top: (contData.height - newHeight) / 2,
left: 0
};
} else {
var newCanvData = {
height: contData.height,
width: newWidth,
top: 0,
left: (contData.width - newWidth) / 2
};
}
$image.cropper('setCanvasData', newCanvData);
$image.cropper('setCropBoxData', newCanvData);
Not a direct answer to the question ... but i'm betting many people that use this plugin will find this helpfull..
Made this after picking up #AlexanderZ code to rotate the image.
So ... If you guys want to ROTATE or FLIP a image that has already a crop box defined and if you want that cropbox to rotate or flip with the image ... use these functions:
function flipImage(r, data) {
var old_cbox = $image.cropper('getCropBoxData');
var new_cbox = $image.cropper('getCropBoxData');
var canv = $image.cropper('getCanvasData');
if (data.method == "scaleX") {
if (old_cbox.left == canv.left) {
new_cbox.left = canv.left + canv.width - old_cbox.width;
} else {
new_cbox.left = 2 * canv.left + canv.width - old_cbox.left - old_cbox.width;
}
} else {
new_cbox.top = canv.height - old_cbox.top - old_cbox.height;
}
$image.cropper('setCropBoxData', new_cbox);
/* BUG: When rotated to a perpendicular position of the original position , the user perceived axis are now inverted.
Try it yourself: GO to the demo page, rotate 90 degrees then try to flip X axis, you'll notice the image flippped vertically ... but still ... it fliped in relation to its original axis*/
if ( r == 90 || r == 270 || r == -90 || r == -270 ) {
if ( data.method == "scaleX") {
$image.cropper("scaleY", data.option);
} else {
$image.cropper("scaleX", data.option);
}
} else {
$image.cropper(data.method, data.option);
}
$image.cropper(data.method, data.option);
}
function rotateImage(rotate) {
/* var img = $image.cropper('getImageData'); */
var old_cbox = $image.cropper('getCropBoxData');
var new_cbox = $image.cropper('getCropBoxData');
var old_canv = $image.cropper('getCanvasData');
var old_cont = $image.cropper('getContainerData');
$image.cropper('rotate', rotate);
var new_canv = $image.cropper('getCanvasData');
//calculate new height and width based on the container dimensions
var heightOld = new_canv.height;
var widthOld = new_canv.width;
var heightNew = old_cont.height;
var racio = heightNew / heightOld;
var widthNew = new_canv.width * racio;
new_canv.height = Math.round(heightNew);
new_canv.width = Math.round(widthNew);
new_canv.top = 0;
if (new_canv.width >= old_cont.width) {
new_canv.left = 0;
} else {
new_canv.left = Math.round((old_cont.width - new_canv.width) / 2);
}
$image.cropper('setCanvasData', new_canv);
if (rotate == 90) {
new_cbox.height = racio * old_cbox.width;
new_cbox.width = racio * old_cbox.height;
new_cbox.top = new_canv.top + racio * (old_cbox.left - old_canv.left);
new_cbox.left = new_canv.left + racio * (old_canv.height - old_cbox.height - old_cbox.top);
}
new_cbox.width = Math.round(new_cbox.width);
new_cbox.height = Math.round(new_cbox.height);
new_cbox.top = Math.round(new_cbox.top);
new_cbox.left = Math.round(new_cbox.left);
$image.cropper('setCropBoxData', new_cbox);
}
var photoToEdit = $('.photo_container img');
$( photoToEdit ).cropper({
autoCrop : true,
crop: function(e) {}
});
$("#rotate_left_btn").click( function () {
$( photoToEdit ).cropper('rotate', -90);
var containerHeightFactor = $(".photo_container").height() / $( photoToEdit).cropper('getCanvasData').height;
if ( containerHeightFactor < 1 ) { // if canvas height is greater than the photo container height, then scale (on both x and y
// axes to maintain aspect ratio) to make canvas height fit container height
$( photoToEdit).cropper('scale', containerHeightFactor, containerHeightFactor);
} else if ( $( photoToEdit).cropper('getData').scaleX != 1 || $( photoToEdit).cropper('getData').scaleY != 1 ) { // if canvas height
// is NOT greater than container height but image is already scaled, then revert the scaling cuz the current rotation will bring
// the image back to its original orientation (landscape/portrait)
$( photoToEdit).cropper('scale', 1, 1);
}
}
I Fixed this issue hope fully. i have added or changed the option to 0 (viewMode: 0,). Now its working well.
cropper = new Cropper(image, {
dragMode: 'none',
viewMode: 0,
width: 400,
height: 500,
zoomable: true,
rotatable: true,
crop: function(e) {
}
});
document.getElementById('rotateImg').addEventListener('click', function () {
cropper.rotate(90);
});
I have a script that uses HTML2Canvas to take a screenshot of a div within the page, and then converts it to a pdf using jsPDF.
The problem is the pdf that is generated is only one page, and the screenshot requires more than one page in some instances. For example the screenshot is larger than 8.5x11. The width is fine, but I need it to create more than one page to fit the entire screenshot.
Here is my script:
var pdf = new jsPDF('portrait', 'pt', 'letter');
$('.export').click(function() {
pdf.addHTML($('.profile-expand')[0], function () {
pdf.save('bfc-schedule.pdf');
});
});
Any ideas how I could modify that to allow for multiple pages?
pdf.addHtml does not work if there are svg images on the web page.
I copy the solution here, based on the picture being in a canvas already.
Here are the numbers (paper width and height) that I found to work. It still creates a little overlap part between the pages, but good enough for me. if you can find an official number from jsPDF, use them.
var imgData = canvas.toDataURL('image/png');
var imgWidth = 210;
var pageHeight = 295;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 0;
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save( 'file.pdf');`
you can use page split option of addhtml like this:
var options = {
background: '#fff',
pagesplit: true
};
var doc = new jsPDF(orientation, 'mm', pagelayout);
doc.addHTML(source, 0, 0, options, function () {
doc.save(filename + ".pdf");
HideLoader();`enter code here`
});
Note: This will break the html on multiple pages but these pages will get stretched. Stretching is a issue in addhtml till now and i am still not able to find on internet how to solve this issue.
I was able to get it done using async functionality.
(async function loop() {
var pages = [...]
for (var i = 0; i < pages.length; i++) {
await new Promise(function(resolve) {
html2canvas(pages[i], {scale: 1}).then(function(canvas) {
var img=canvas.toDataURL("image/png");
doc.addImage(img,'JPEG', 10, 10);
if ((i+1) == pages.length) {
doc.save('menu.pdf');
} else {
doc.addPage();
}
resolve();
});
});
}
})();
This is my aproach, also with jspdf and html2canvas which did a very good job for me:
I am using a wrapper node which holds the content to be converted to pdf:
<div id="main-content">
<!-- the content goes here -->
</div>
<!-- and a button somewhere in the dom -->
<a href="javascript:genPDF()">
<i class="far fa-file-pdf"></i> Download PDF
</a>
This is the JS integration ( I only checked this versions ) and call:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.3/jspdf.min.js"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
<script type="text/javascript">
const documentSlug = "some Slug";
const documentTitle ="Some Title";
function genPDF(){
let html2pdf = new pdfView();
html2pdf.generate();
}
</script>
And here you can see the pdf generation. I encapsulated the generation part in an extra js object (you need to include this file also):
//assuming jquery is in use
let pdfView = function(){
//assuming documentSlug is a constant set earlier
this.documentSlug = documentSlug;
//assuming documentTitle is a constant set earlier
this.documentTitle = documentTitle;
//in this html node the part which shall be converted to pdf
this.nodeId = "main-content";
};
pdfView.prototype.generate = function(){
let self = this;
this.prepareDocumentToPrint();
//variables
let HTML_Width = $("#"+self.nodeId).width();
let HTML_Height = $("#"+self.nodeId).height();
let top_left_margin = 15;
let PDF_Width = HTML_Width+(top_left_margin*2);
let PDF_Height = (PDF_Width*1.5)+(top_left_margin*2);
this.pdfWidth = PDF_Width;
this.pdfHeight = PDF_Height;
let canvas_image_width = HTML_Width;
let canvas_image_height = HTML_Height;
let totalPDFPages = Math.ceil(HTML_Height/PDF_Height)-1;
html2canvas($("#"+self.nodeId)[0],{allowTaint:true, scale: 2}).then(function(canvas) {
canvas.getContext('2d');
//console.log(canvas.height+" "+canvas.width);
let imgData = canvas.toDataURL("image/jpeg", 1.0);
let pdf = new jsPDF('p', 'mm', [PDF_Width, PDF_Height]);
pdf.addImage(imgData, 'JPG', top_left_margin, top_left_margin,canvas_image_width,canvas_image_height);
pdf = self.addPdfHeader(pdf);
pdf = self.addPdfFooter(pdf, 1);
for (let i = 1; i <= totalPDFPages; i++) {
pdf.addPage(PDF_Width, PDF_Height);
pdf.addImage(imgData, 'JPG', top_left_margin, -(PDF_Height*i)+(top_left_margin*4) + top_left_margin,canvas_image_width,canvas_image_height);
pdf = self.addPdfHeader(pdf);
pdf = self.addPdfFooter(pdf, (i+1));
}
pdf.save(self.documentSlug+".pdf");
self.resetDocumentAfterPrint();
});
};
//this one sets a fixed viewport, to be able to
//get the same pdf also on a mobile device. I also
//have a css file, which holds the rules for the
//document if the body has the .printview class added
pdfView.prototype.prepareDocumentToPrint = function(){
let viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute('content', 'width=1280, initial-scale=0.1');
$('body').addClass("printview");
window.scrollTo(0,0);
};
pdfView.prototype.addPdfHeader = function(pdf){
pdf.setFillColor(255,255,255);
pdf.rect(0, 0, this.pdfWidth, 40, "F");
pdf.setFontSize(40);
pdf.text("Document: "+this.documentTitle+" - Example Lmtd. Inc. (Whatsoever)", 50, 22);
return pdf;
};
pdfView.prototype.addPdfFooter = function(pdf, pageNumber){
pdf.setFillColor(255,255,255);
pdf.rect(0, pdf.internal.pageSize.height - 40, this.pdfWidth, this.pdfHeight, "F");
pdf.setFontSize(40);
pdf.text("Seite "+pageNumber, 50, pdf.internal.pageSize.height - 20);
return pdf;
};
//and this one resets the doc back to normal
pdfView.prototype.resetDocumentAfterPrint = function(){
$('body').removeClass("printview");
let viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute('content', 'device-width, initial-scale=1, shrink-to-fit=no');
};
my css example for the .printview case:
body.printview #header,
body.printview footer
{
display: none !important;
}
body.printview{
margin-top: 0px !important;
}
body.printview #main-content{
margin-top: 0px !important;
}
body.printview h1{
margin-top: 40px !important;
}
enjoy
Contributions to #Code Spy for the Link you provided, which was the basis for this aporach.
Found out the solution to this,putting a rectangle as border for each pdf page and also starting the second page and other pages from a litte down by making difference in pageHeight,hope this will resolve for few...
html2canvas(myCanvas).then(function (canvas) {
var imgWidth = 210;
var pageHeight = 290;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 0;
var pageData = canvas.toDataURL('image/jpeg', 1.0);
var imgData = encodeURIComponent(pageData);
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
doc.setLineWidth(5);
doc.setDrawColor(255, 255, 255);
doc.rect(0, 0, 210, 295);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
doc.setLineWidth(5);
doc.setDrawColor(255, 255, 255);
doc.rect(0, 0, 210, 295);
heightLeft -= pageHeight;
}
doc.save('file.pdf');
});
};
I have a table(columns aligned differently) which is to be saved as pdf.I converted the table to image using html2canvas and then saved the image into pdf usng jspdf. It works well if the size of the image is less than or equal to page size of pdf but if the image size is bigger than the page size then it saves only first page of pdf(which has only a part of the image) and rest of the image is not displayed/saved in pdf. here the javascript code I used.
$("#btnVC_saveGLSummary").click(function () {
html2canvas($("#tblSaveAsPdf1"), {
onrendered: function (canvas) {
var myImage = canvas.toDataURL("image/jpeg");
var d = new Date().toISOString().slice(0, 19).replace(/-/g, "");
filename = 'report_' + d + '.pdf';
var doc = new jsPDF();
doc.addImage(myImage, 'JPEG', 12, 10);
doc.save(filename);
}
});
});
Any ideas how to get the remaining part of the image on the second page of pdf.
It works for html2canvas and jspdf.
var imgData = canvas.toDataURL('image/png');
var imgWidth = 210;
var pageHeight = 295;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 10; // give some top padding to first page
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position += heightLeft - imgHeight; // top padding for other pages
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save( 'file.pdf');
I use this method
function makePDF(){
var quotes = document.getElementById('container-fluid');
html2canvas(quotes, {
onrendered: function(canvas) {
//! MAKE YOUR PDF
var pdf = new jsPDF('p', 'pt', 'a4');
for (var i = 0; i <= quotes.clientHeight/980; i++) {
//! This is all just html2canvas stuff
var srcImg = canvas;
var sX = 0;
var sY = 1120*i; // start 980 pixels down for every new page
var sWidth = 778;
var sHeight = 1120;
var dX = 0;
var dY = 0;
var dWidth = 778;
var dHeight = 1120;
window.onePageCanvas = document.createElement("canvas");
onePageCanvas.setAttribute('width', 778);
onePageCanvas.setAttribute('height', 1120);
var ctx = onePageCanvas.getContext('2d');
// details on this usage of this function:
// https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Slicing
ctx.drawImage(srcImg,sX,sY,sWidth,sHeight,dX,dY,dWidth,dHeight);
// document.body.appendChild(canvas);
var canvasDataURL = onePageCanvas.toDataURL("image/png", 1.0);
var width = onePageCanvas.width;
var height = onePageCanvas.clientHeight;
//! If we're on anything other than the first page,
// add another page
if (i > 0) {
pdf.addPage(595, 842); //8.5" x 11" in pts (in*72)
}
//! now we declare that we're working on that page
pdf.setPage(i+1);
//! now we add content to that page!
pdf.addImage(canvasDataURL, 'PNG', 0, 0, (width*.72), (height*.71));
}
//! after the for loop is finished running, we save the pdf.
pdf.save('Test3.pdf');
}
});
}
I hope it will be helpful
Inspired by another stack overflow response
This was answered further up in a similar way, but I felt I wanted to give an example leaving out unnecessary code:
You can easily run through all pages and put 'watermark' texts or other items on every page like this:
let pdf = new jspdf();
// do something that creates multiple pages
for (let pageNumber = 1; pageNumber <= pdf.getNumberOfPages(); pageNumber++) {
pdf.setPage(pageNumber)
pdf.addImage('myImgOnEveryPage.png', 10,10,20,20);
pdf.text('My text on every page, 12, 12);
}
(JSPDF version 2.1.1)
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 1025, sectionWidth, 1250, 0, 0, 1800, 950);
var image2 = new Image();
image2 = Canvas2Image.convertToJPEG(canvas);
image2Data = image2.src;
doc.addImage(image2Data, 'JPEG', -105, 5, 440, 325);
Basically, you get the context of your canvas and then you can use the drawImage function to create a new Image that is a portion of your original canvas. The parameters of drawImage are:
drawImage(img, startX, startY, originalW, originalH, destX, dextY, destW, destH);
Where startX and Y are your starting locations for the clipping on the original images, original W and H are the height and width of your clipping (on the original picture), basically how much to clip, destX and Y are where on your PDF to put the new clipping and destH and W define how big the clipping is when placed on the canvas (they stretch the image once you've clipped it)Hope this helped, Cheers
You can easily do this by adding small tweak in your own code.
$("#btnVC_saveGLSummary").click(function () {
html2canvas($("#tblSaveAsPdf1"), {
onrendered: function (canvas) {
var myImage = canvas.toDataURL("image/jpeg");
var d = new Date().toISOString().slice(0, 19).replace(/-/g, "");
filename = 'report_' + d + '.pdf';
var doc = new jsPDF();
var totalPages = doc.internal.getNumberOfPages();
for(let i = 1; i < totalPages; i++) {
doc.setPage(i);
doc.addImage(myImage, 'JPEG', 12, 10);
}
doc.save(filename);
}
});
});