Comments (11)
Alright, so here's the thing.
I spent about 2 weeks on this and I've managed to get it working.
But I had to custom build jsPDF to get what I needed. Allow me to explain.
Web Workers don't have a window object. That's their restriction.
I've seen in other threads that you can just set var window ={}
or var window = self
and it just magically works, but for me, it literally had no affect. Keep in my mind, I'm using webpack's worker-loader
to require the libraries I need. webpack seem to eval()
each module separately, so defining the window prior to your require doesn't do anything.
So where's the actual problem?
the base of jsPDF.js
actually works fine with web workers.
The issue comes from its plugins and some libraries it uses.
When you import jsPDF
from npm
, jsPDF publishes a compiled version of jsPDF with ALL its plugins. For most cases, there's no problem with this, other than webpack being mad that it's pre-compiled.
Not all plugins are bad however. Only the plugins acroform and png_support use DOM object methods in their code and these won't work with web workers. Anything window or DOM related won't work.
On top of that, there are 3 other libraries that jsPDF uses don't work with web workers. Specifically Blob.js, Filesaver.js and png.js
So what do we do?
Well ideally, we'd have different package just for jsPDF web-worker support or rewrite these packages so they can work with web workers.
But in the interim, you'll need custom version of jsPDF.
In most cases you can use just the jspdf.js
file used in the root of the project but because the published npm
version of jspdf
only includes the compiled version, you can't just do an import jsPDF from 'jspdf/jspdf.js'
and be done with it.
Also if you need any of the other plugins in your web workers, you'll need to build your own version anyway.
If you modify the build.js
file, this is the maximum of jspdf you can have in your web worker.
import './jspdf';
// import './plugins/acroform';
import './plugins/addhtml';
import './plugins/addimage';
import './plugins/annotations';
import './plugins/autoprint';
import './plugins/canvas';
import './plugins/cell';
import './plugins/context2d';
import './plugins/from_html';
import './plugins/javascript';
import './plugins/outline';
// import './plugins/png_support';
import './plugins/prevent_scale_to_fit';
import './plugins/split_text_to_size';
import './plugins/standard_fonts_metrics';
import './plugins/svg';
import './plugins/total_pages';
import './plugins/viewerpreferences';
import './plugins/xmp_metadata';
// import './node_modules/cf-blob.js/Blob.js';
// import './node_modules/file-saver/FileSaver.js';
import './node_modules/adler32cs/adler32cs.js';
import './libs/css_colors.js';
import './libs/deflate.js';
import './libs/html2canvas/dist/html2canvas.js';
// import './libs/png_support/png.js';
import './libs/png_support/zlib.js';
import './libs/polyfill.js';
export default jsPDF;
That's great but how do I actually save my stuff now?
So because we can't use filesaver.js
directly in our web worker, we'll need to pass the generated pdf back to our DOM and save it there.
jsPDF has a few output options that aren't supported in web workers but luckily the 'blob'
option will work. We can use that, pass it back to our app and convert it into a buffer so we can save.
There's probably some improvements I can make but this is the basic idea.
Example code below to follow.
If you have any questions or just want to add to my post, feel free.
// Worker.js
const jsPDF = require ('../custom/jspdf');
let onmessage = e => {
const doc = new jsPDF({});
...
const blob = doc.output( 'blob' );
postMessage( blob, [blob] );
}
// Main.js
import saveAs from 'file-saver';
import dataURItoBlob from './dataURItoBlob`;
worker.addEventListener('message', e => {
const blob = e.data;
saveAs( blob, 'mypdf.pdf' );
worker.terminate(); // Terminates the worker.
}, false);
from jspdf.
@FerryDewe and @diegocr Does this still work? I get window is not defined
error in the worker
from jspdf.
window is not defined +1
Is this solution doesn't work now or could u provide a simple demo?
I tried a lot of times, keeping thowing 'window is not defined error'.
Thank u
from jspdf.
In latest builds it should be as easy as changing 'this' by 'self' afaict, but considering you posted that a year ago i'm not sure if you still need it.. :-/
from jspdf.
Hi diegocr,
can you please elaborate your comment further ?
You are right that, with the recent build (Version 1.0.262), I can pass the JSON.stringifiy(jspdf_doc) to the web worker, and then use JSON.parse to bring it back as an object in the worker. However, with the specification of the web worker right now, the method and the function of the jspdf object is gone. So it does not work.
I tried also used the importScripts('/js/jsPDF.js'); directly in web worker, however the worker can't also work with DOM element, and giving me an error "Cannot read property 'createElementNS' of undefined", in FileSaver.js ( in this line ---> save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") )
Is there any workaround to make the jsPDF work with the Web Worker ?
It would be Perfect !
from jspdf.
@FerryDewe You're correct, in a worker you cannot use jsPDF functions which involves dealing with DOM nodes/functions.
As for a workaround for that specific FileSaver issue, try this:
var saveAs = function(blob, filename)
{
// send blob from worker to main process, where you can
// then save it using the saveAs() function defined there.
postMessage(blob);
};
importScripts('jspdf.debug.js');
from jspdf.
Thanks @diegocr. The workaround for FileSaver does remove the previous error because of the DOM Node.
However after I integrate that in my worker, it makes the other library not recognized. I get the following error : Uncaught Error: PNG support requires png.js and zlib.js (jspdf.debug.js:4455)
this is what I have in my worker.js
var saveAs = function(blob, filename)
{
// send blob from worker to main process, where you can
// then save it using the saveAs() function defined there.
postMessage(blob);
};
importScripts('/js/pdfPrint/jspdf.debug.js');
self.addEventListener('message', function(e) {
var data= e.data;
var doc = new jsPDF(orientation, unit, [widthDoc, heightDoc]);
------ Do Some Stuff Here: doc.addImage, doc.setFontSize, doc.text, doc.addPage -----
self.postMessage({'status':"complete", 'value':doc)});
}, false);
and in MainThread
worker.addEventListener('message', function(e) {
var data = e.data;
switch (data.status) {
case 'progress':
updateProgressMeter();
break;
case 'complete':
var doc=JSON.parse(data.value);
doc.save("yourDoc.pdf");
worker.terminate(); // Terminates the worker.
break;
};
}, false);
I have tried, toimportScripts('/js/pdfPrint/jspdf.debug.js', '/js/pdfPrint/png.js');
but it still throw the same error. And I also tried to copy the script in png js directly in my worker, but still have no luck.
Do you have any other idea ? Thanks before..
from jspdf.
PNG requires working with DOM canvas, so don't use this format, rather JPEG and passing the image to addImage as raw data or a data URI.
Also, on complete don't send the doc
since i think that would cause some circular issue, calling doc.save() in the worker will use the saveAs() function declared in this scope, so:
Replace:
self.postMessage({'status':"complete", 'value':doc)});
By:
doc.save('dummy.pdf')
And:
case 'complete':
....
break;
By:
default:
if(data instanceof Blob) saveAs(data, "yourDoc.pdf");
from jspdf.
AWESOME ! @diegocr You are the Man ! Thanks..
from jspdf.
This solution works great for me as well.
Unfortunately it's critical for my application to be able to draw PNG with its transparency support. @diegocr is there a simple workaround to support PNG images in web worker context?
from jspdf.
This issue isnt fixed!
I need to stringify the jsPDF-doc object in the same context as @jmorel does and the solution from @FerryDewe and @diegocr does not fix the main problem that there are circular structures in the jsPDF object. Its just workaround, not a fix! It would improve the handling of jsPDF by ALOT if there were no circular structures in it so then you can easily use web workers by stringify the jsPDF-object and send it to the worker which can addImages
and do other stuff with it.
How much is the effort for doing that from your perspective? It would worth its weight in gold for me!
The workaround does not work especially when you have to use libraries for webworkers such as catilinejs where you don't have the opportunity to import whole libraries into the worker.
from jspdf.
Related Issues (20)
- RangeError: Maximum call stack size exceeded at E.y.roundToPrecision.y.__private__.roundToPrecision
- Impossible to add image in NodeJS HOT 1
- Custom fonts display correctly in Firefox but incorrectly in Edge and Adobe Acrobat Reader HOT 1
- Unable to Render HTML from External File into PDF using Angular 12.
- Transparency using html() method and doc.addImage() for the background. HOT 2
- Merged PDF is blur as compared to the source images used
- Digital Signature
- after migrating to VITE from CREATE-REACT-APP, I am getting r3.writeFileSync is not a function error on export pdf HOT 1
- textWithLink doesn't support multiline links
- jspdf :2.5.1 Error Property 'setHeight' does not exist
- Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds. HOT 1
- Request support for Thai language HOT 1
- The problem of exporting PDFs is occurring only in the Safari browser. HOT 1
- generating a PDF with multiple HTML div elements on separate pages using JS? HOT 3
- garbled only in textField
- addImage y property
- Part of the text at the pagination point is missing。 doc.html()
- Open in other tab textWithLink
- I am using jsPDF to print the interface, but sometimes the html2canvas_overlay doesn't disappear. but console is not error HOT 1
- charSpace affects centering, how can I solve it?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jspdf.