0

I have a very simple svg

<div id="printId">
    test
    <svg height="100" width="100">
        <circle cx="50" cy="50" r="40" />
    </svg> 
</div>

and trying to print it with html2canvas and jspdf. But whatever I do I get an error

Error loading svg data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%22100%22%20width%3D%22100%22%20style%3D%22-webkit-writing-mode%3A%20horizontal-tb%3B%20-webkit-user-modify%3A%20read-only%3B%20-webkit-user-drag%3A%20auto%3B%20-web
Oe.error @ html2canvas-1.1.2.min.js:20

and test.pdf file with just word test in it.

I googled this for about 3 hours, and in every place people recommend settings width/height atributes. Which, as you can see, doesn't help in my case. I've simplified it as much as I could. Here is the full index.html I use:

<!DOCTYPE html>

<html>
    <head></head>
    <body>
    <div id="printId">
        test
        <svg height="100" width="100">
            <circle cx="50" cy="50" r="40" />
        </svg> 
    </div>

    <script src="../../../script/jsPDF/jspdf-2.3.1.umd.min.js" defer></script>
    <script src="../../../script/jsPDF/html2canvas-1.1.2.min.js" defer></script>
    <script>
        function saveAsPDF(divId, usersName, certNumber = '', type = 'old')
        {
        const { jsPDF } = window.jspdf;
        const pdf = new jsPDF();
        var source = document.getElementById("printId");

        pdf.html(
            source, {
            callback: function (pdf) {
                pdf.save("test.pdf");
            },
        });
        }
    </script>
    <input type='submit'
            onclick='saveAsPDF();'
            value='save'>
</body>
</html>

I also tried all possible versions of html2canvas:
1.1.2
1.1.5
1.2.2
1.3.2

and two versions of jspdf:
2.1.1
2.3.1

And two browsers: Chrome and Opera.

What can be my issue?

Edit: I managed to make html2canvas work on it's own with the document below. Now the question is how to make it work with jspdf together:

<html>
    <head></head>
    <body>
            <div id="printId">
                this is circle:
            <svg height="100" width="100">
                <circle cx="50" cy="50" r="40" />
            </svg> 
            </div>
            <hr>
            <h4>Image of above HTML content...</h4>
            <div id="canvasImg"></div>

    <script src="../../../script/jsPDF/html2canvas-1.3.2.min.js"></script>
    <script>
        html2canvas(document.getElementById('printId')).then(function(canvas) {
            var canvasImg = canvas.toDataURL("image/jpg");
            document.getElementById('canvasImg').innerHTML = '<img src="'+canvasImg+'" alt="">';
        });
    </script>
</body>
</html>
klm123
  • 12,105
  • 14
  • 57
  • 95

2 Answers2

1

It looks like jspdf is not able to add svg as vector graphic to a page. There are three options:

  1. Use svg2pdf instead of jspdf to convert svg to pdf on it's own.
  2. Use addSvgAsImage function of jspdf to place an svg in the pdf page manually.
  3. Convert svg to jpg first, then use jspdf.

I've chosen the 3rd option and wrote a function, which I run before saving canvas as pdf:

htmlToJpg: function()
{
    var canvas = document.querySelector('.to-print');
    screenWidth = parseFloat(window.getComputedStyle(canvas).width);

    var problematic = document.querySelectorAll('.convert-to-jpg');
    problematic.forEach(function(el) {
        html2canvas(el, 
            {
                scale: 2480/screenWidth // 2480px - size for A4 paper, 300 dpi
            }
        )
        .then(function(canvas) {
            var img = canvas.toDataURL("image/jpeg");
            el.innerHTML = '<img src="' + img + '" class="img">';
        });
    });
}
klm123
  • 12,105
  • 14
  • 57
  • 95
1
import html2canvas from 'html2canvas';
import { jsPDF as JsPDF } from 'jspdf';
import { useCallback, useEffect } from 'react';

export const useDownloadPdf = (name: string, isReady: boolean) => {
 useEffect(() => {
   if (isReady) {
    const fileName = `${name}.pdf`;
    const pdf = new JsPDF({
    orientation: 'p',
    unit: 'mm',
    format: 'a4',
    putOnlyUsedFonts: true,
    });

    const convertElements = document.querySelectorAll('.convert-on-pdf');
    const elements = Array.from(convertElements) as HTMLElement[];

    if (elements.length > 0) {
      Promise.all(
        elements.map(async (element) => {
          const canvas = await html2canvas(element);
          element.replaceWith(canvas);
      }),
      ).then(() => {
        pdf.html(document.body, {
          callback: (generatedPdf) => {
            generatedPdf.save(fileName)
          },
        });
      });
    } else {
      pdf.html(document.body, {
        callback: (generatedPdf) => {
        generatedPdf
          .save(fileName)
        },
      });
    }
  }
}, [isReady, name, setAtribute]);}
Armen
  • 11
  • 1
  • 1
    Welcome to SO! Please don't post code-only answers but add a little textual explanation about how and why your approach works and what makes it different from the other answers given. You can find out more at our ["How to write a good answer"](https://stackoverflow.com/help/how-to-answer) page. – ahuemmer Dec 16 '22 at 08:57