Giter VIP home page Giter VIP logo

Comments (11)

jwmann avatar jwmann commented on June 15, 2024 7

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.

needforspeed avatar needforspeed commented on June 15, 2024 3

@FerryDewe and @diegocr Does this still work? I get window is not defined error in the worker

from jspdf.

Nomia avatar Nomia commented on June 15, 2024 1

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.

diegocr avatar diegocr commented on June 15, 2024

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.

FerryDewe avatar FerryDewe commented on June 15, 2024

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.

diegocr avatar diegocr commented on June 15, 2024

@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.

FerryDewe avatar FerryDewe commented on June 15, 2024

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.

diegocr avatar diegocr commented on June 15, 2024

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.

FerryDewe avatar FerryDewe commented on June 15, 2024

AWESOME ! @diegocr You are the Man ! Thanks..

from jspdf.

korya avatar korya commented on June 15, 2024

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.

Rubinum avatar Rubinum commented on June 15, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.