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

Memory leak with lots of images #844

Closed
VilleImmonen opened this issue Sep 21, 2016 · 21 comments
Closed

Memory leak with lots of images #844

VilleImmonen opened this issue Sep 21, 2016 · 21 comments

Comments

@VilleImmonen
Copy link

Hi!

We are creating a Phonegap app that uses jsPDF to create PDFs on the fly on users phone. We are having a problem with jsPDF documents with images. On our android app one function is creating a PDF file with lots of JPEG images, with the size of the PDF file rising to over 5 MB. It seems that there is a memory leak somewhere and if we create a few large PDF files in a row, the app crashes because it runs out of memory. I have analyzed the memory consumption with GapDebug and the problem persist even when we remove all possible references from the code after the PDF is done. The only possible solution that we could come up with that would clear the memory was to entirely restart the app...

There are maybe 10 images in our pdf. We are using Chart.js and Konva.js to create images, convert them to base64 and add to PDF, like this:

var pie = new Chart(pie_ctx).Pie(pieData, {
          animation: false,
          segmentStrokeColor : "#D6D6D6"
});

var image = pie.toBase64Image();

doc.addImage(image, 'JPEG', 310, 105, 100, 100);
@Uzlopak
Copy link
Collaborator

Uzlopak commented Oct 9, 2016

Well, I think your problem are the generated images before they get added to the pdf. I guess they are saved as bitmap-files and not e.g. as jpeg with 60% compression. So changing the scaleFactor from the canvas could reduce the size of the image itself.

http://stackoverflow.com/questions/14488849/higher-dpi-graphics-with-html5-canvas

@VilleImmonen
Copy link
Author

The problem is that we can create as many of these images as we want in a row, and the garbage collection clears them from the memory. But when we add it to pdf, suddenly the memory usage is remains super high even after the pdf and the image has been saved and otherwise discarded.

@MrRio
Copy link
Member

MrRio commented Oct 11, 2016

There's a new method of adding SVGs to PDFs which may help you out on the 'feature/svg-html' branch.

Docs: https://rawgit.com/MrRio/jsPDF/feature/svg-html/docs/global.html#svg
Example: https://rawgit.com/MrRio/jsPDF/feature/svg-html/examples/canvg_context2d/svg.html

@MrRio
Copy link
Member

MrRio commented Oct 11, 2016

Or there's actually a way to create a canvas for Chart.js to draw to (Commented out) that will convert the drawing actions into vector, using magic.

https://github.com/MrRio/jsPDF/blob/feature/svg-html/examples/canvg_context2d/bar_graph_with_text_and_lines.html#L680

@Uzlopak
Copy link
Collaborator

Uzlopak commented Oct 13, 2016

I think the problem is the addImage-Plugin.
https://github.com/MrRio/jsPDF/blob/master/plugins/addimage.js

Every Image is saved as base64-Text into an Object (line 134). So instead of loading and processing and then kicking it out of the memory, it keeps the images in the memory. There is not even a delete action after it is put into the pdf (line 108), because you dont know if it is used again in another place in the pdf.

@VilleImmonen
Copy link
Author

I think the problem is the addImage-Plugin.

We tried drawing the pictures directly to the pdf with the method found in this comment:

Or there's actually a way to create a canvas for Chart.js to draw to (Commented out) that will convert the drawing actions into vector, using magic.

Like this:

    var image = pie.toBase64Image();

    var c = doc.canvas;
    var ctx = c.getContext('2d');
    ctx.drawImage(image, 300, 105, 100, 100);

And the issue still remains, without the addImage plugin.

It is hard to say if this is a problem in jsPDF or in PhoneGap/Android memory management. We are still trying to investigate this issue further. I will report any new findings..

@Uzlopak
Copy link
Collaborator

Uzlopak commented Oct 21, 2016

What happens if you change it to

    var images = {};
    images.image = pie.toBase64Image();
    var c = doc.canvas;
    var ctx = c.getContext('2d');
    ctx.drawImage(images.image, 300, 105, 100, 100);
    delete images.image;

or

image = pie.toBase64Image();
var c = doc.canvas;
var ctx = c.getContext('2d');
ctx.drawImage(image, 300, 105, 100, 100);
delete image;

?

@VilleImmonen
Copy link
Author

This does not help..

@orodriguez2
Copy link

Hi,

I'm running into a similar issue as VilleImmonen, was this resolved at some point? If my only option is to use the SVG files I will have to convert 100's of them to be able to use them into my application.

Thanks,

@pablomaurer
Copy link

nope, never resolved..

@Uzlopak
Copy link
Collaborator

Uzlopak commented Mar 14, 2017

Try following:
find the line
jsPDFAPI.arrayBufferToBinaryString = function (buffer) {

comment out the first solution and activate the second method

return atob(this.arrayBufferToBase64(buffer));

Please give feedback about the result.

@orodriguez2
Copy link

orodriguez2 commented Mar 14, 2017

@arasabbasi,

It turns out that the problem was indeed in my code. I was not using properly the new class instance. jsPDF works as expected. Thanks for your help.

@Uzlopak
Copy link
Collaborator

Uzlopak commented Mar 14, 2017

@orodriuez2
Just to document here your original comment before you edited it:

Changing to the second method does indeed maintain the memory low (~47K using Google chrome Task Manager, with the other one went to 1.6G). And the application doesn't crash the browser anymore.

Well I guess, there is a reason, why atob or arrayBufferToBase64 is not used. Probably because atob is not supported till IE 10. So keep this in mind, that you should test it properly.

@Uzlopak
Copy link
Collaborator

Uzlopak commented Mar 16, 2017

Can somebody test if this solves the memory leak problem? I dont know how to reproduce the memory leak.

@orodriuez2
@mnewmedia
@VilleImmonen


	jsPDFAPI.arrayBufferToBinaryString = function (buffer) {
			var data = (this.isArrayBuffer(buffer)) ? buffer : new Uint8Array(buffer);
			var chunkSizeForSlice = 0x10000;
			var binary_string = '';
			var slicesCount = Math.round(data.byteLength / chunkSizeForSlice);
			for (var i = 0; i < slicesCount; i++) {
				binary_string += String.fromCharCode.apply(null, data.slice(i*chunkSizeForSlice, i*chunkSizeForSlice+chunkSizeForSlice));
			}
			return binary_string;
	};

If so, then I would recommend following change for max compatibility:


jsPDFAPI.arrayBufferToBinaryString = function (buffer) {
		if (typeof(window.atob) === "function") {
			return atob(this.arrayBufferToBase64(buffer));
		} else {
			var data = (this.isArrayBuffer(buffer)) ? buffer : new Uint8Array(buffer);
			var chunkSizeForSlice = 0x10000;
			var binary_string = '';
			var slicesCount = Math.round(data.byteLength / chunkSizeForSlice);
			for (var i = 0; i < slicesCount; i++) {
				binary_string += String.fromCharCode.apply(null, data.slice(i*chunkSizeForSlice, i*chunkSizeForSlice+chunkSizeForSlice));
			}
			return binary_string;
		}
	};

@Uzlopak
Copy link
Collaborator

Uzlopak commented Mar 16, 2017

It is maybe better to set chunkSizeForSlice = 0x5000

@Uzlopak
Copy link
Collaborator

Uzlopak commented Mar 16, 2017

#425

@orodriguez2
Copy link

@arasabbasi,

I've tested the code above and it does indeed handle the memory much better. Now every time I create a pdf document with images the memory clears up and gets back to about 50MB, when with the prior code it was getting about 1300GB. The attached images show this using Chrome's task manager. The one in blue is the memory after generating a pdf. The one in yellow is after generating the same pdf with the new code.

Great work! thanks a lot for this.
2017-03-22_9-45-20
2017-03-22_9-43-40

@Uzlopak
Copy link
Collaborator

Uzlopak commented Mar 22, 2017

The problem was that the previous code is made for maximum compatibility on all browsers because the atob Function is not implemented in all browsers. If you use the generic atob function then the browserengine loads the whole base64 string into the memory and changes it to binary code.
The alternative algorhythm loads the base64-string char by char and not all in once. Thats why you have a memory leak.... The browser allocates for every char a byte. Few and Small images are no problem, but if you load hundreds of big images the browser engine needs alot of memory and the garbage collector doesnt realize that it can dealloc the memory.
Thats why i call the apply with the null parameter so it doesnt floods the window object with garbage. And second it will load the base64 string in much bigger chunks, so it needs less iterations to read the base64 string.
Thats why in chrome with using the generic atob-function it will now work much smoother.

It can still be a problem in IE9 or less. I had before a bigger chunk size and in chrome it didnt work properly. Thats why i recommended a smaller chunk size.

But if you just use chrome it should be from now on no problem.

MrRio pushed a commit that referenced this issue Sep 2, 2017
Solution to solve memory leaks caused by big images.
Is solving the issues #844 and #425
@MrRio
Copy link
Member

MrRio commented Sep 2, 2017

This is fixed in #1115

@Uzlopak Uzlopak closed this as completed Feb 15, 2018
Uzlopak added a commit that referenced this issue Feb 22, 2018
* Update karma.conf.js

* Merge  (#7)

* Update addimage.js

Solution to solve memory leaks caused by big images.
Is solving the issues #844 and #425

* normalize Color Methods

* f2 => f3

* make it black

* make it gray again

typeof comparison was wrong...

* make typeof comparison wrong to pass test

* Update jspdf.js

d'oh

* Update jspdf.js

correct it again....

* operation fix

ch1 === ch2  === ch3 is wrong. it would process from left to right resulting in a ch1 === ch2 => true, true === ch3 => false

* update test pdf

fix it so that the test is passed

* Update rectangles.pdf

stupid letter

* Update rectangles.pdf

fix length of content

* Update rectangles.pdf

... why?

* Update jspdf.js

DRY it up
add cssColor to rgb  conversion

* Update jspdf.js

bugfix

* Update standard.spec.js

add test for colornames

* Update standard.spec.js

* Update standard.spec.js

* Update jspdf.js

convert short rgb to long form

* Update standard.spec.js

add test case for short rgb-values

* Update jspdf.js

add a setter for the CreationDate

* Update jspdf.js

some major changes to the setCreationDate Algorythm

* Update jspdf.js

bugfix

* Update jspdf.js

darn...

* Update jspdf.js

* Update jspdf.js

* Update jspdf.js

bugfix

* Update addimage.js

round is not the proper method. It has to be ceil so that the splice-method is called atleast once.

* add polyfill for Object.assign

* Add files via upload

* Add files via upload

fix small mistake

* fix tainted standardfontsmetrics

* Add files via upload

one fix

* Update acroform.js

* update references

* modify tests

* force testing

* Update acroform.js

* Add files via upload

* Update acroform.js

* update references

* Add files via upload

* Update acroform.js

* Update acroform.js

* Add files via upload

* Update acroform.js

* Add files via upload

* Add files via upload

* Update acroform.js

* Add files via upload

* addimage.js add cmyk recognition for jpg

* make it less code

* Update saucelabs.karma.conf.js

* Create standard.spec.js

* Create jpg.pdf

* Rename tests/addimage/jpg.pdf to tests/addimage/reference/jpg.pdf

* Add files via upload

* test first if jpeg is working

* Add files via upload

* Update jpeg.spec.js

* Add files via upload

* Add files via upload

* Add files via upload

* Update saucelabs.karma.conf.js

* Update jpg.b64

* Update addhtml.js

#1480

* add ability to recognize filetype by header

* remove unnecessary variable

* typo-fix

* Update addimage.js

* Update addimage.js

#966

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

* Update saucelabs.karma.conf.js

* Update compare.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

* Add files via upload

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update compare.js

D'Oh

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

why is here a done? fml

* Add files via upload

* Add files via upload

* Add files via upload

* Delete jpg.b64

* Update jpeg.spec.js

* Update compare.js

* Update saucelabs.karma.conf.js

* Update png.spec.js

* Update saucelabs.karma.conf.js

* Update addimage.js

* Create filetypeRecognition.spec.js

* Update saucelabs.karma.conf.js

* Update filetypeRecognition.spec.js

* Update addimage.js

* fix IE error

* Update standard.spec.js

HideWindowUI.pdf is misssing

* compare.js make it look good

* add tests

* Update saucelabs.karma.conf.js

* Fix for #905 #1163 #1317

adler32cs is no longer maintained, made package.json point to my fork of  adler32cs that contains the loader.js fix

* small modification

* Update outline.js

* Add files via upload

* Update standard.spec.js

* Update karma.conf.js to load outline.js

* Update saucelabs.karma.conf.js to load outline.js

* chore(package): update rollup-plugin-babel to version 3.0.3

* chore(package): update karma to version 2.0.0

* Add new method for internal, getTextColor

* Update dist files and docs to include getTextColor

* Revise getTextColor to return hex code; add new spec

* Compiled assets for getTextColor modifications

* Updates spec and documentation per feedback

* chore(package): update uglify-js to version 3.3.5

Closes #1506

* simplify

* addCreationDate

* Update standard.spec.js

* Update standard.spec.js

* Update jspdf.js

* Update standard.spec.js

* Update standard.spec.js

* Add png adam7 interlace (#6)

* Update png.js

Adapted from here 
foliojs/png.js#8

* make it look good

* fix possible merging error

* fix minor bug

* hopefull removed all dupes

* Update main.js

* Don't skip HideWindowUI

* Update addimage.js

* Update compare.js

* Add files via upload

* Update jspdf.js

* Update jspdf.js

* Update addhtml.js

* Update outline.js

* Update cell.js

* Update annotations.js

* Update from_html.js

* Update context2d.js

* Update acroform.js

* Update addimage.js

* Update karma.conf.js

* Create getImageProperties.spec.js

* typo
Uzlopak added a commit that referenced this issue May 8, 2018
* Update karma.conf.js

* Merge  (#7)

* Update addimage.js

Solution to solve memory leaks caused by big images.
Is solving the issues #844 and #425

* normalize Color Methods

* f2 => f3

* make it black

* make it gray again

typeof comparison was wrong...

* make typeof comparison wrong to pass test

* Update jspdf.js

d'oh

* Update jspdf.js

correct it again....

* operation fix

ch1 === ch2  === ch3 is wrong. it would process from left to right resulting in a ch1 === ch2 => true, true === ch3 => false

* update test pdf

fix it so that the test is passed

* Update rectangles.pdf

stupid letter

* Update rectangles.pdf

fix length of content

* Update rectangles.pdf

... why?

* Update jspdf.js

DRY it up
add cssColor to rgb  conversion

* Update jspdf.js

bugfix

* Update standard.spec.js

add test for colornames

* Update standard.spec.js

* Update standard.spec.js

* Update jspdf.js

convert short rgb to long form

* Update standard.spec.js

add test case for short rgb-values

* Update jspdf.js

add a setter for the CreationDate

* Update jspdf.js

some major changes to the setCreationDate Algorythm

* Update jspdf.js

bugfix

* Update jspdf.js

darn...

* Update jspdf.js

* Update jspdf.js

* Update jspdf.js

bugfix

* Update addimage.js

round is not the proper method. It has to be ceil so that the splice-method is called atleast once.

* add polyfill for Object.assign

* Add files via upload

* Add files via upload

fix small mistake

* fix tainted standardfontsmetrics

* Add files via upload

one fix

* Update acroform.js

* update references

* modify tests

* force testing

* Update acroform.js

* Add files via upload

* Update acroform.js

* update references

* Add files via upload

* Update acroform.js

* Update acroform.js

* Add files via upload

* Update acroform.js

* Add files via upload

* Add files via upload

* Update acroform.js

* Add files via upload

* addimage.js add cmyk recognition for jpg

* make it less code

* Update saucelabs.karma.conf.js

* Create standard.spec.js

* Create jpg.pdf

* Rename tests/addimage/jpg.pdf to tests/addimage/reference/jpg.pdf

* Add files via upload

* test first if jpeg is working

* Add files via upload

* Update jpeg.spec.js

* Add files via upload

* Add files via upload

* Add files via upload

* Update saucelabs.karma.conf.js

* Update jpg.b64

* Update addhtml.js

#1480

* add ability to recognize filetype by header

* remove unnecessary variable

* typo-fix

* Update addimage.js

* Update addimage.js

#966

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

* Update saucelabs.karma.conf.js

* Update compare.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

* Add files via upload

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update compare.js

D'Oh

* Update compare.js

* Update jpeg.spec.js

* Update compare.js

* Update compare.js

* Update compare.js

* Update jpeg.spec.js

why is here a done? fml

* Add files via upload

* Add files via upload

* Add files via upload

* Delete jpg.b64

* Update jpeg.spec.js

* Update compare.js

* Update saucelabs.karma.conf.js

* Update png.spec.js

* Update saucelabs.karma.conf.js

* Update addimage.js

* Create filetypeRecognition.spec.js

* Update saucelabs.karma.conf.js

* Update filetypeRecognition.spec.js

* Update addimage.js

* fix IE error

* Update standard.spec.js

HideWindowUI.pdf is misssing

* compare.js make it look good

* add tests

* Update saucelabs.karma.conf.js

* Fix for #905 #1163 #1317

adler32cs is no longer maintained, made package.json point to my fork of  adler32cs that contains the loader.js fix

* small modification

* Update outline.js

* Add files via upload

* Update standard.spec.js

* Update karma.conf.js to load outline.js

* Update saucelabs.karma.conf.js to load outline.js

* chore(package): update rollup-plugin-babel to version 3.0.3

* chore(package): update karma to version 2.0.0

* Add new method for internal, getTextColor

* Update dist files and docs to include getTextColor

* Revise getTextColor to return hex code; add new spec

* Compiled assets for getTextColor modifications

* Updates spec and documentation per feedback

* chore(package): update uglify-js to version 3.3.5

Closes #1506

* simplify

* addCreationDate

* Update standard.spec.js

* Update standard.spec.js

* Update jspdf.js

* Update standard.spec.js

* Update standard.spec.js

* Add png adam7 interlace (#6)

* Update png.js

Adapted from here 
foliojs/png.js#8

* make it look good

* fix possible merging error

* fix minor bug

* hopefull removed all dupes

* Update main.js

* Don't skip HideWindowUI

* Update addimage.js

* Update compare.js

* Add files via upload

* Update jspdf.js

* Update jspdf.js

* Update addhtml.js

* Update outline.js

* Update cell.js

* Update annotations.js

* Update from_html.js

* Update context2d.js

* Update acroform.js

* Update addimage.js

* Update karma.conf.js

* Create getImageProperties.spec.js

* typo
@sohilfynd
Copy link

@arasabbasi I am trying to generate pdf using html2Canvas and jspdf but after like generating 150 pdf pages, chrome memory utilization is too much. Can you tell what can be the issue?

@parallax parallax locked as spam and limited conversation to collaborators Oct 31, 2018
@Uzlopak
Copy link
Collaborator

Uzlopak commented Oct 31, 2018

Sohilfynd, well what do you expect with 150 a4 Size images?

Please don't hijack issues. issue locked.

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

No branches or pull requests

6 participants