jsPDF Server Side html() function generates empty pdf

Created on 9 Jul 2020  路  15Comments  路  Source: MrRio/jsPDF

I have a scenario, where I need to create a pdf file from html source code on server side. I tried the solution from issue #2248 , and the doc.text() function worked, but the doc.html() function creates an empty pdf and I got the following error message:

html2canvas not loaded.
jspdf.node.min.js:150

(node:7560) UnhandledPromiseRejectionWarning: ReferenceError: document is not defined
warning.js:27
    at r (\node_modules\jspdf\dist\jspdf.node.min.js:150:335)
    at Promise.<anonymous> (\dist\node_modules\jspdf\dist\jspdf.node.min.js:150:1413)
    at processTicksAndRejections (internal/process/task_queues.js:94:5)
(node:7560) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

I found this solution, that solved the "html2canvas not loaded" error, but I still get an empty pdf and an UnhandledPromiseRejectionWarning:

(node:7136) UnhandledPromiseRejectionWarning: ReferenceError: document is not defined
warning.js:27
    at r (\node_modules\jspdf\dist\jspdf.node.min.js:150:335)
    at Promise.<anonymous> (\node_modules\jspdf\dist\jspdf.node.min.js:150:1413)
    at processTicksAndRejections (internal/process/task_queues.js:94:5)
(node:7136) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
warning.js:27
(node:7136) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

This is the code that I tried to run:

global.window = {document: {createElementNS: () => {return {}} }};
global.navigator = {};
global.btoa = () => {};

const fs = require('fs')
const html2canvas = require('html2canvas');
window.html2canvas = html2canvas;
const jsPDF = require('jspdf/dist/jspdf.node.min')

// Default export is a4 paper, portrait, using milimeters for units
var doc = new jsPDF()
doc.html('<p>Hello world!</p>');

fs.writeFileSync('./output.pdf', doc.output())

delete global.window;
delete global.navigator;
delete global.btoa;

Is there any way to solve this issue?

no-issue-activity

Most helpful comment

I am still facing this issue, do we have a solution now?

All 15 comments

With the new release planned, running jsPDF in node will work much more seamless. You can try the pull request #2804.

However, html2canvas requires some DOM APIs in order to work. You could try jsdom. It would be very great, if you could test the pull request together with jsdom and see if that works. If so, we can add a node/html2canvas example to the examples.

Thanks, I'll try it.

I tried the pull request and jsdom, but they didn't solve the issue.

Using jsdom gave the following error (even though I manually imported html2canvas):
(node:20068) UnhandledPromiseRejectionWarning: ReferenceError: html2canvas is not defined warning.js:32 at Promise.<anonymous> (\node_modules\jspdf\dist\jspdf.node.min.js:150:4891) at processTicksAndRejections (internal/process/task_queues.js:97:5) (node:20068) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) warning.js:32 (node:20068) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

While the jspdf from the pull request gave an error (despite the global.btoa = () => {}; line): Error: Cannot find module 'btoa'

Did you install the dependencies? The dependencies are now

  "dependencies": {
    "atob": "^2.1.2",
    "btoa": "^1.2.1"
  },
  "optionalDependencies": {
    "canvg": "^3.0.6",
    "core-js": "^3.6.0",
    "dompurify": "^2.0.12",
    "html2canvas": "^1.0.0-rc.5"
  },

You need to include dompurify and html2canvas in your own package.json in order to be installed. In theory, you can reduce your snippet to this:

const { jsPDF } = require("jspdf")
const doc = new jsPDF()
doc.html('<p>Hello world!</p>');

doc.save('./output.pdf)

I'm also trying to add html to pdf on the server side with jspdf v2.1.0 and node v14.9.0. Is this supposed to work out of the box, or do I still need to add jsdom?

[I get UnhandledPromiseRejectionWarning: ReferenceError: document is not defined.]

I tried wrapping my html with jsdom, and then passing it to the html function, but I get UnhandledPromiseRejectionWarning: Error: Unknown source type.:

const html = '...';
var domhtml = new JSDOM(html, { contentType: "text/html" });

var doc = new jsPDF();
doc.html(domhtml, {
    callback: function(d) {
      d.save();
    }
});

Thanks for any input!

You need to set the document globally:

global.window = domhtml.window
global.document = window.document

And then use something like document.body for the doc.html call.

Thanks for the answer!

Unfortunately I get more errors from jsdom: Error: Not implemented: window.computedStyle(elt, pseudoElt), Error: Not implemented: window.scrollTo

This is my code:

const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { jsPDF } = require("jspdf");

var myhtml = `<div><p>test me</p></div>`;
const htmldom = new JSDOM(myhtml);

global.window = htmldom.window;
global.document = window.document;
global.Node = window.Node;

var doc = new jsPDF('p', 'mm', 'a4');
doc.html(document.body, {
    callback: function(d) {
        d.save();
    }
});
doc.save("a4.pdf");

Well, seems like jsdom doesn't implement all required APIs. What you might try is running jsPDF in a headless browser on the backend instead.

@manuelamaria @HackbrettXXX Hi! Have you managed to solve this? I cant make the html function to work. I am using the latest version of jspdf. Thank you!

@mountiny we ended up creating a node service where we use puppeteer to render html and then save as pdf. Something like this: https://blog.risingstack.com/pdf-from-html-node-js-puppeteer/#option3

@manuelamaria Thank you very much, that seems quite complicated for us now since we are in time/budget pressure.

This issue is stale because it has been open 90 days with no activity. It will be closed soon. Please comment/reopen if this issue is still relevant.

I am still facing this issue, do we have a solution now?

Yep, me too. Would be interested in an answer.

Well, seems like jsdom doesn't implement all required APIs. What you might try is running jsPDF in a headless browser on the backend instead.

There is simply no easy way to convert html to PDF without a working DOM API. So a headless browser on the backend is probably the best you can do.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yankeeBrit picture yankeeBrit  路  3Comments

centurianii picture centurianii  路  4Comments

MaxCodeDE picture MaxCodeDE  路  4Comments

BarathArivazhagan picture BarathArivazhagan  路  4Comments

mackersD picture mackersD  路  4Comments