Method jsPDF.html() has an option to set the image quality (HTMLOptionImage) but this does not seem to affect the image quality or inevitably the document size at all.
My goal is to get the PDF document size down to a reasonable level, but anytime I add images, it blows it up way larger than it needs to be. Like even a 50kb image file on the document turns it into a 1 MB PDF.
It also seems compress doesn't really affect it, the PDF is the same size either way.
Example code using an external image (may have to run this on your local).
package.json running "react": "^17.0.2", and "jspdf": "^2.3.1",
import { jsPDF } from "jspdf";
const stackoverflow = () => {
function downloadMe() {
const ele = document.querySelector('#stackoverflowexample');
const pdf = new jsPDF({
orientation: 'p',
unit: 'px',
format: 'letter',
compress: true,
putOnlyUsedFonts: true,
hotfixes: ['px_scaling'] // an array of hotfix strings to enable
});
pdf.html(ele, {
image: { quality: 10, type: "jpeg" },
x: 10,
y: 10,
callback: function (doc) {
doc.save(); // File size is huge even though images are small kb
// attachment = doc.output('datauristring');
},
});
}
return (
<div id="stackoverflowexample">
<h1>PDF</h1>
<button onClick={downloadMe}>Download Button</button>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Borobudur-Temple-Park_Elephant-cage-01.jpg/320px-Borobudur-Temple-Park_Elephant-cage-01.jpg" />
<p>Image makes PDF larger than it should have to be</p>
</div>)
}
export default stackoverflow;
So I can solve this file size issue by selectively removing and re-adding those image elements with an addImage but it doesn't address the underlying problem that the .html method is blowing up the img element data sizes.
html2canvas: {
onclone: (doc) => {
let target = doc.querySelector("#stackoverflowexample");
let images = target.querySelectorAll("img");
let targetRect = target.getBoundingClientRect();
images.forEach((img) => {
let rect = img.getBoundingClientRect();
pdf.addImage(img, "JPEG", targetRect.x - rect.x, targetRect.y - rect.y, rect.width, rect.height);
img.remove();
});
}
The image object has a multiplier property, try using that, it will effect the quality of the image and this would result in a greater or lesser file size depending upon what value you set of multiplier
image: { quality: 10, type: "jpeg", multiplier: .5 }
Related
I am trying to use html2canvas and jsPDF to get a PDF of the content of the page,
The idea is that someone could fill data and then print the CV created, but i want to be able to create a A4 pdf from any device, i tried to read the documentation of both libraries but i can't seem to make it work:
This is the output i am getting in the mobile and desktop versions:
Link to screenshots
EDIT:
I tried to configure both html2canvas and jsPDF in PT size:
const printDocument = () => {
setPrintable(true)
const input = document.getElementById('final-section');
html2canvas(input, {
height: 2970,
width: 2100,
scale: 1,
onclone: (cloned) => {
let toPrint = cloned.querySelector("#final-section");
toPrint.style.height = '841.88pt';
toPrint.style.width = '595.27pt';
toPrint.style.position = 'absolute';
toPrint.style.top = 0;
toPrint.style.left = 0;
}
})
.then((canvas) => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({
unit: 'pt',
format: 'a4'
});
pdf.addImage(imgData, 'JPEG', 0, 0, 841.88, 595.27);
// pdf.output('dataurlnewwindow');
pdf.save("download.pdf");
})
;
}
The markup of the html is a content div that contains a modifiable section and the final section that is the printable, in desktop the modifiable section has 45vw and the final section 55 vw,
In mobile each section occupy 100vh and 100vw,
I am working about draggable objects on Konva stage. I want to the canvas object layer turn to PDF. I use toDataURL. Like this;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
id: 'stage',
});
var grid_layer = new Konva.Layer();
var object_layer = new Konva.Layer();
stage.add(grid_layer);
stage.add(object_layer);
function updateScreen() {
object_layer.batchDraw()
}
function downloadURI(uri, name) {
var link = document.createElement('a');
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
document.getElementById('save').addEventListener(
'click',
function() {
var dataURL = stage.toDataURL({ pixelRatio: 3 });
downloadURI(dataURL, 'stage.png');
},
false
);
Save button work without objects and save canvas image. But when i run the code with objects on stage, the page reloads and the button doesn't work. doesn't save canvas image
I would strongly recommend to doing it like shown in the Konva Wiki.
// Code form KonvaJS wiki
var pdf = new jsPDF('l', 'px', [stage.width(), stage.height()]);
pdf.setTextColor('#000000');
// first add texts
stage.find('Text').forEach((text) => {
const size = text.fontSize() / 0.75; // convert pixels to points
pdf.setFontSize(size);
pdf.text(text.text(), text.x(), text.y(), {
baseline: 'top',
angle: -text.getAbsoluteRotation(),
});
});
// then put image on top of texts (so texts are not visible)
pdf.addImage(
stage.toDataURL({ pixelRatio: 2 }),
0,
0,
stage.width(),
stage.height()
);
pdf.save('canvas.pdf');
This should add all text and other objects to a pdf.
From the comments, I can conclude that you don't host the images using the server you use for the konva stuff. You can't access the images from your website as they are not from the same origin. This helps secure the files on your computer from the access of websites like yours. So you would have to move the images to the server and access them from there.
I need to create nicely formatted PDFs from website pages that vary greatly in layout and content. The closest I've come to success is with extensive #media print {} CSS on a page-by-page basis, using the browser's print dialog, then choosing 'Save As PDF' in the print dialog.
I'd like to do all that natively in the app without relying on the print dialog, and I've spent hours trying to beat jsPDF into submission with no luck. Most of my pages include jpgs and svgs as well as text and I have tried html2canvas, dompurify & canvg in conjunction with jsPDF.
I don't mind having to use lots of custom CSS to get it looking good in the PDF, but I can't even get all the elements to appear in the downloaded PDF. Most of the time it's just blank, or one random element.
Doesn't work:
downloadPDF() {
const doc = new jsPDF();
const contentHtml = this.html;
doc.fromHTML(contentHtml, 15, 15, { // error: fromHTML is not a function
width: 170,
});
doc.save("sample.pdf");
},
Also doesn't work:
downloadPDF() {
const doc = new jsPDF();
/** WITH CSS */
var canvasElement = document.createElement("canvas");
html2canvas(this.$refs.printArea, { canvas: canvasElement }).then(function (canvas) {
const img = canvas.toDataURL("image/jpeg", 0.8);
doc.addImage(img, "JPEG", 20, 20);
doc.save("sample.pdf");
});
},
Also also doesn't work:
downloadPDF() {
const pdf = new jsPDF({
orientation: "p",
unit: "pt",
format: "letter",
putOnlyUsedFonts: true,
});
pdf.setFontSize(11);
pdf.html(this.html, {
callback: function (pdf) {
pdf.save("download.pdf");
},
});
},
Using cropper js to attempt to crop an img on the page, however,
it's actually appending 2 more of the same images on the page that I can "scroll" to change their aspect ratio.
const cropper = new Cropper(image, {
aspectRatio: 16 / 9,
crop(event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
}
});
I also tried to do this to set the crop sizes;
viewMode: 1,
setData() {
x: dimensions.left;
y: dimensions.top;
width: dimensions.width;
height: dimensions.height;
}
However, it's proving to be difficult. Am I missing something?
Switched to using the Jimp library. Was much easier to figure out how to crop using that.
I want to be able to set a minimum width and height for images uploaded via the FilePicker Upload dialog box (Minimum size: 1200 x 960 pixels).
Ideally there would be an option for this but I cannot seem to find it.
While we don't have a method to limit uploads based on image dimensions, we did add the ability to resize images upon upload in May of 2015.
The follow parameters govern this behavior:
These functions work for local files picked from your computer and images captured using the webcam service.
Image dimensions
{imageDim: [width, height]}
Specify image dimenions. e.g. : {imageDim: [800, 600]}. Local images will be resized (upscaled or downscaled) to the specified dimensions before uploading. The original height to width ratio is maintained. To resize all images based on the width, set [width, null], eg. [800, null]. For the height set [null, height], eg [null, 600].
Max image dimensions
{imageMax: [width, height]}
Specify maximum image dimenions. e.g. : {imageMax: [800, 600]}. Images bigger than the specified dimensions will be resized to the max size.
Min image dimensions
{imageMin: [width, height]}
Specify minimum image dimenions. e.g. : {imageMin: [800, 600]}. Images smaller than the specified dimensions will be upscaled to the minimum size.
Note: client side resizing relies on the dialog being opened to work. If a user utilizes the drop-pane functionality (meaning no modal dialog window opens) to upload their images, their images will not be modified.
Filestack does provide a picker option to set a callback onFileSelected. This is an example from a tutorial on their web site:
function checkImgSize (file) {
return new Promise(function (resolve, reject) {
const maxWidth = 1300;
const maxHeight = 1300;
const blob = file.originalFile;
// Get an object URL for the local file
const url = URL.createObjectURL(blob);
// Create an image and load the object URL
const img = new Image();
img.src = url;
img.onload = function () {
URL.revokeObjectURL(url);
if (this.naturalWidth > maxWidth) {
reject('File is too wide');
}
if (this.naturalHeight > maxHeight) {
reject('File is too tall');
}
// If we made it here then the file was approved
resolve();
}
});
}
const picker = client.picker({
exposeOriginalFile: true,
onFileSelected: checkImgSize,
});