Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support image compression in html() function #3178

Open
ericjames opened this issue May 29, 2021 · 9 comments · May be fixed by #3419
Open

Support image compression in html() function #3178

ericjames opened this issue May 29, 2021 · 9 comments · May be fixed by #3419

Comments

@ericjames
Copy link

Filed Stackoverflow here: https://stackoverflow.com/questions/67753141/jspdf-html-method-doesnt-recognize-the-image-quality-option

Basically something is up with the jsPDF.html() method in which any img element blows up the file size. For me I have a 50kb image that blows up the PDF to 3.3mb with nothing else in it.

So I have to selectively replace those image elements with onclone.

My workaround:

pdf.html(ele, {
    x: 10,
    y: 10,
    image: {
        format: "JPEG", 
        quality: 10, // doesn't affect it
        compression: "FAST" // Doesn't do anything
    },
    html2canvas: {
        onclone: (doc) => {
            // doc returns the entire document, not just ele
            let target = doc.querySelector("#theElement");
            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();
            });
        }
    }
});
@HackbrettXXX
Copy link
Collaborator

Thanks for reporting this. Although I think this is more of a missing feature than a bug. If I read the documentation correctly, the options.image.quality argument only applies when using the toImg function on the result of html2canvas. But I agree that this option should also apply when converting images in the HTML markup.

@HackbrettXXX HackbrettXXX changed the title jsPDF.html() creates very large PDFs with img elements, doesn't recognize image quality options or compression Support image compression in html() function Jul 13, 2021
@sanishtjppi
Copy link

Same issue. I have around 10 images in my html but very small sized PNGs. PDF size increases exponentially when converting my html to PDF.

@jackcobb
Copy link

Thanks for reporting this. Although I think this is more of a missing feature than a bug. If I read the documentation correctly, the options.image.quality argument only applies when using the toImg function on the result of html2canvas. But I agree that this option should also apply when converting images in the HTML markup.

I disagree. If its intentionally not implemented, it should be spelled out in the documentation found at http:https://raw.githack.com/MrRio/jsPDF/master/docs/module-html.html

Based on that, its not clear that certain properties are not applicable

@rajorpratyush
Copy link

@ericjames Hey your method doesn't work for me.. images positions are not maintained.

Y co-ordinates comes in negative

@Uzlopak
Copy link
Collaborator

Uzlopak commented Oct 21, 2021

I suppose the problem is that maybe images are stored in the resourcedictionary or html2canvas is drawing those images as bitmap?

@rajorpratyush
Copy link

rajorpratyush commented Nov 3, 2021

Hey i solved this problem, It's a hack but it does work very good

What i did was just load the images with FAST compression but place them outside the boundary of the page. Now what happens is the cache automatically picks them so the original images comes compressed. So there will be duplicate copies of all the images but since they are not in the view it will not be shown and the one in the view will automatically will be compressed

Use the same code as above just remove the line
img.remove();

@Uzlopak
Copy link
Collaborator

Uzlopak commented Nov 3, 2021

It can also be, that you by doing the img.remove() forced jsPDF to store the next identical image as a new resource. So then you would store the same image again and again.

gkostov added a commit to gkostov/jsPDF that referenced this issue May 15, 2022
Using "null" for the "compression" parameter causes the function's logic to not detect it as "unset" and will not apply the default setting.

resolves parallax#3178
@gkostov gkostov linked a pull request May 15, 2022 that will close this issue
@gkostov
Copy link

gkostov commented May 15, 2022

After some digging it seem that this feature is actually in place but a bug in the usage of .addImage() was crippling it. Invoking the function with null was preventing the logic at

if (compression === undefined && filter.indexOf("FlateEncode") !== -1) {

to do its job (to populate the "unset" compression variable) and the variable would move on with the invalid value of null.

With undefined now images get compressed as expected (having set {compress: "true"} on the jsPDF instance).

Cheers

@gulamudi
Copy link

Filed Stackoverflow here: https://stackoverflow.com/questions/67753141/jspdf-html-method-doesnt-recognize-the-image-quality-option

Basically something is up with the jsPDF.html() method in which any img element blows up the file size. For me I have a 50kb image that blows up the PDF to 3.3mb with nothing else in it.

So I have to selectively replace those image elements with onclone.

My workaround:

pdf.html(ele, {
    x: 10,
    y: 10,
    image: {
        format: "JPEG", 
        quality: 10, // doesn't affect it
        compression: "FAST" // Doesn't do anything
    },
    html2canvas: {
        onclone: (doc) => {
            // doc returns the entire document, not just ele
            let target = doc.querySelector("#theElement");
            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();
            });
        }
    }
});

I have used this trick and was able to reduce the size of the pdf file generated by the save() method. However, since I need to send the generated pdf to the server, when I try to get the binary using output() method either as arraybuffer or blob - the resulting binary is still larger in size and the compression has no effect.

Any directions will be appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants