html2pdf.js icon indicating copy to clipboard operation
html2pdf.js copied to clipboard

blank pdf generated for large documents

Open kirtipriya opened this issue 4 years ago • 17 comments

When a document is big spanning 150-200 pages , blank pdf is generated. v0.9.0 is this a limitation of canvas element ? Can this be fixed ?

kirtipriya avatar Apr 23 '20 14:04 kirtipriya

I encounter the same issue in Chrome, and not only for big documents, also for tinny docs

edbras avatar Apr 24 '20 07:04 edbras

@eKoopmans is there an error callback that will say that the limit has exceeded the canvas size. We can show an error message to user in that case then

kirtipriya avatar Apr 24 '20 11:04 kirtipriya

Same here. I have a pdf consisting of 93 blank pages. In another pdf with 81 pages some elements that should be in the pdf are only present on the first pages then disappear.

image

config:

      margin: [8, 8, 8, 8],
      filename: fileName,
      image: { type: 'jpeg', quality: 0.98 },
      html2canvas: { scale: 1 },
      jsPDF: { unit: 'mm', format: 'a4', orientation: 'landscape' },
      pagebreak: { after: '.page-break' }    

using version 0.9.2

ErikJungnickel avatar Apr 28 '20 08:04 ErikJungnickel

What code and config are you guys using?

edbras avatar Apr 28 '20 11:04 edbras

Same issue here - not consistently though, sometimes same input will print to PDF just fine, sometimes it is a semi-blank or completely blank output. Using v0.9.1 and config is:

html2pdf()
      .set({
        margin: [10, 10, 20, 10],
        filename: fileName,
        image: {type: 'jpeg', quality: 1},
        html2canvas: {dpi: 192, letterRendering: true},
        jsPDF: {unit: 'mm', format: 'letter', orientation: 'landscape'},
        pageBreak: {mode: ['avoid-all', 'css'], avoid: ['.pi-row']},
      })
      .from(container[0].innerHTML)
      .toPdf()
      .save();```

simonpinn avatar May 05 '20 02:05 simonpinn

So this is a known but hard to fix issue. See #19

ErikJungnickel avatar May 08 '20 06:05 ErikJungnickel

@eKoopmans Yes , i understand it is hard to fix , but can there be a way to atleast generate an error . I ask this because , in Firefox , if the document is huge , the screen gets frozen, and there is nothing that the user can do . He has to close the browser and restart it Chrome is working because it is atleast able to generate a pdf , eventhough it is blank. Screen is not frozen. Wheras Firefox is unable to handle a canas size that exceeds its limit. The screen gets frozen and i can see errors in the console of Firefox . i have reprted this issue here with the errors received --> https://github.com/eKoopmans/html2pdf.js/issues/351

kirtipriya avatar Sep 30 '20 16:09 kirtipriya

@eKoopmans Any update on this ?

kirtipriya avatar Jan 11 '21 12:01 kirtipriya

@kirtipriya the only way to fix is as suggested here you'll have to divide up your html content into different "pages" so that a new canvas is created for each "page", preventing the content height from exceeding canvas upper limit.

i did it this way in my code and it works totally fine albeit a bit slower:

// ---------- HTML output for pdf export ----------
<div aria-label="pdf-page-1">...content</div>
<div aria-label="pdf-page-2">...content</div>
...

// ---------- Javascript ----------
// pages => individual html blocks to print
// select all elements marked with 'pdf-page-*' to be printed into PDF 
const pages = Array.from(document.querySelectorAll('div[aria-label^="pdf-page-"]'))
const pdfOptions = { ... pdf options here }
await downloadPDF(pages, pdfOptions)

const downloadPDF = (elements, pdfOptions) => {
  let worker = html2pdf()
    .set(options)
    .from(elements[0])

  if (elements.length > 1) {
    worker = worker.toPdf() // worker is now a jsPDF instance

    // add each element/page individually to the PDF render process
    elements.slice(1).forEach((element, index) => {
      worker = worker
        .get('pdf')
        .then(pdf => {
          pdf.addPage()
        })
        .from(element)
        .toContainer()
        .toCanvas()
        .toPdf()
    })
  }

  worker = worker.save()
}

shan-du avatar Feb 05 '21 17:02 shan-du

@kirtipriya the only way to fix is as suggested here you'll have to divide up your html content into different "pages" so that a new canvas is created for each "page", preventing the content height from exceeding canvas upper limit.

i did it this way in my code and it works totally fine albeit a bit slower:

// ---------- HTML output for pdf export ----------
<div aria-label="pdf-page-1">...content</div>
<div aria-label="pdf-page-2">...content</div>
...

// ---------- Javascript ----------
// pages => individual html blocks to print
// select all elements marked with 'pdf-page-*' to be printed into PDF 
const pages = Array.from(document.querySelectorAll('div[aria-label^="pdf-page-"]'))
const pdfOptions = { ... pdf options here }
await downloadPDF(pages, pdfOptions)

const downloadPDF = (elements, pdfOptions) => {
  let worker = html2pdf()
    .set(options)
    .from(elements[0])

  if (elements.length > 1) {
    worker = worker.toPdf() // worker is now a jsPDF instance

    // add each element/page individually to the PDF render process
    elements.slice(1).forEach((element, index) => {
      worker = worker
        .get('pdf')
        .then(pdf => {
          pdf.addPage()
        })
        .from(element)
        .toContainer()
        .toCanvas()
        .toPdf()
    })
  }

  worker = worker.save()
}

@shan-du : Thank you for the suggestion. The content is dynamic in my case. It might be difficult to for me to identify which div tags i have to treat as pages - div[aria-label^="pdf-page-"], or do you have a solution for that too ?

kirtipriya avatar Feb 15 '21 13:02 kirtipriya

@kirtipriya the only way to fix is as suggested here you'll have to divide up your html content into different "pages" so that a new canvas is created for each "page", preventing the content height from exceeding canvas upper limit. i did it this way in my code and it works totally fine albeit a bit slower:

// ---------- HTML output for pdf export ----------
<div aria-label="pdf-page-1">...content</div>
<div aria-label="pdf-page-2">...content</div>
...

// ---------- Javascript ----------
// pages => individual html blocks to print
// select all elements marked with 'pdf-page-*' to be printed into PDF 
const pages = Array.from(document.querySelectorAll('div[aria-label^="pdf-page-"]'))
const pdfOptions = { ... pdf options here }
await downloadPDF(pages, pdfOptions)

const downloadPDF = (elements, pdfOptions) => {
  let worker = html2pdf()
    .set(options)
    .from(elements[0])

  if (elements.length > 1) {
    worker = worker.toPdf() // worker is now a jsPDF instance

    // add each element/page individually to the PDF render process
    elements.slice(1).forEach((element, index) => {
      worker = worker
        .get('pdf')
        .then(pdf => {
          pdf.addPage()
        })
        .from(element)
        .toContainer()
        .toCanvas()
        .toPdf()
    })
  }

  worker = worker.save()
}

@shan-du : Thank you for the suggestion. The content is dynamic in my case. It might be difficult to for me to identify which div tags i have to treat as pages - div[aria-label^="pdf-page-"], or do you have a solution for that too ?

not sure what your requirements are, but in that case, i would probably still try to divide the print content, not by page, but every X pages. for example: for every 10 <div> content blocks, or every 3000px, start a new page

shan-du avatar Apr 06 '21 21:04 shan-du

@kirtipriya the only way to fix is as suggested here you'll have to divide up your html content into different "pages" so that a new canvas is created for each "page", preventing the content height from exceeding canvas upper limit. i did it this way in my code and it works totally fine albeit a bit slower:

// ---------- HTML output for pdf export ----------
<div aria-label="pdf-page-1">...content</div>
<div aria-label="pdf-page-2">...content</div>
...

// ---------- Javascript ----------
// pages => individual html blocks to print
// select all elements marked with 'pdf-page-*' to be printed into PDF 
const pages = Array.from(document.querySelectorAll('div[aria-label^="pdf-page-"]'))
const pdfOptions = { ... pdf options here }
await downloadPDF(pages, pdfOptions)

const downloadPDF = (elements, pdfOptions) => {
  let worker = html2pdf()
    .set(options)
    .from(elements[0])

  if (elements.length > 1) {
    worker = worker.toPdf() // worker is now a jsPDF instance

    // add each element/page individually to the PDF render process
    elements.slice(1).forEach((element, index) => {
      worker = worker
        .get('pdf')
        .then(pdf => {
          pdf.addPage()
        })
        .from(element)
        .toContainer()
        .toCanvas()
        .toPdf()
    })
  }

  worker = worker.save()
}

@shan-du : Thank you for the suggestion. The content is dynamic in my case. It might be difficult to for me to identify which div tags i have to treat as pages - div[aria-label^="pdf-page-"], or do you have a solution for that too ?

not sure what your requirements are, but in that case, i would probably still try to divide the print content, not by page, but every X pages. for example: for every 10 <div> content blocks, or every 3000px, start a new page

can it work with css?

alviando98 avatar Jul 10 '21 05:07 alviando98

I'm also getting a blank document, but am trying to generate a very large PDF of 48"x69". Is this just not going to be possible?

ThePixelPixie avatar May 18 '22 20:05 ThePixelPixie

How can canvas be exhausted if space between "legacy" with <br class="html2pdf__page-break"> is max 500px height (300pages)

yomajo avatar Apr 13 '23 13:04 yomajo

@kirtipriya the only way to fix is as suggested here you'll have to divide up your html content into different "pages" so that a new canvas is created for each "page", preventing the content height from exceeding canvas upper limit.

i did it this way in my code and it works totally fine albeit a bit slower:

// ---------- HTML output for pdf export ----------
<div aria-label="pdf-page-1">...content</div>
<div aria-label="pdf-page-2">...content</div>
...

// ---------- Javascript ----------
// pages => individual html blocks to print
// select all elements marked with 'pdf-page-*' to be printed into PDF 
const pages = Array.from(document.querySelectorAll('div[aria-label^="pdf-page-"]'))
const pdfOptions = { ... pdf options here }
await downloadPDF(pages, pdfOptions)

const downloadPDF = (elements, pdfOptions) => {
  let worker = html2pdf()
    .set(options)
    .from(elements[0])

  if (elements.length > 1) {
    worker = worker.toPdf() // worker is now a jsPDF instance

    // add each element/page individually to the PDF render process
    elements.slice(1).forEach((element, index) => {
      worker = worker
        .get('pdf')
        .then(pdf => {
          pdf.addPage()
        })
        .from(element)
        .toContainer()
        .toCanvas()
        .toPdf()
    })
  }

  worker = worker.save()
}

code is working but hyperlink is not able to click in exported pdf

Prvn-IG avatar Jul 13 '23 13:07 Prvn-IG

@shan-du Your code is useful to me, but if you need to wait for all pages to be generated before doing something, you can change the code to the following:

const downloadPDF = (elements, pdfOptions) => {
      return new Promise((resolve, reject) => {
        try {
          let worker = html2pdf().set(pdfOptions).from(elements[0])
          let pageCount = 1

          if (elements.length > 1) {
            worker = worker.toPdf() // worker is now a jsPDF instance

            // add each element/page individually to the PDF render process
            elements.slice(1).forEach((element, index) => {
              worker = worker
                  .get('pdf')
                  .then(pdf => {
                    pageCount++
                    pdf.addPage()

                    if (pageCount === elements.length) {
                      resolve()
                    }
                  })
                  .from(element)
                  .toContainer()
                  .toCanvas()
                  .toPdf()
            })
          }

          worker = worker.save()

          if (elements.length === 1) {
            resolve()
          }
        } catch (e) {
          reject()
        }
      })
    }

// ...

loading = true
await downloadPDF(pages, pdfOptions)
loading = false

suguoyao avatar Apr 28 '24 07:04 suguoyao