0

I have 12 graphs and I want to generate pdf with 2 pages each page has 6 graphs. However, when I convert svg to canvas, then the jspdf can only see part of both sub-dives.

$('#downloadx2').click(function() {

      var svgElements = $("#body_id").find('svg');

      //replace all svgs with a temp canvas
      svgElements.each(function() {
        var canvas, xml;

        // canvg doesn't cope very well with em font sizes so find the calculated size in pixels and replace it in the element.
        $.each($(this).find('[style*=em]'), function(index, el) {
          $(this).css('font-size', getStylex(el, 'font-size'));
        });

        canvas = document.createElement("canvas");
        canvas.className = "screenShotTempCanvas";
        //convert SVG into a XML string
        xml = (new XMLSerializer()).serializeToString(this);

        // Removing the name space as IE throws an error
        xml = xml.replace(/xmlns=\"http:\/\/www\.w3\.org\/2000\/svg\"/, '');

        //draw the SVG onto a canvas
        canvg(canvas, xml);
        $(canvas).insertAfter(this);
        //hide the SVG element
        ////this.className = "tempHide";
        $(this).attr('class', 'tempHide');
        $(this).hide();
      });

      var doc = new jsPDF("p", "mm");
      var width = doc.internal.pageSize.width;    
      var height = doc.internal.pageSize.height;

      html2canvas($("#div_pdf1"), {
        onrendered: function(canvas) {
          var imgData = canvas.toDataURL(
            'image/png', 0.1);

            doc.addImage(imgData, 'PNG', 5, 0, width, height/2,'','FAST');
            doc.addPage();

        }
      });


      html2canvas($("#div_pdf2"), {
        onrendered: function(canvas2) {
          var imgData2 = canvas2.toDataURL(
            'image/png', 0.1);


            doc.addImage(imgData2, 'PNG', 5, 0, width, height/2,'','FAST');
            doc.save('.pdf');
        }
      });
    });




<body id="body_id">
   <div id="div_pdf1" >
     <svg></svg>
     <svg></svg>
     <svg></svg>
    </div>

   <div id="div_pdf1" >
     <svg></svg>
     <svg></svg>
     <svg></svg>
   </div>
</body>

When I run this code, the generated pdf will view two pages with same canvas the first one (div_pdf1) div. So how to get both of them appearing in pdf as two pages.

Hana90
  • 943
  • 5
  • 17
  • 37
  • Do you have a url where I can inspect the page and see the problem? I suspect you have a timing issue. Remember js is asynchronous by default and does not block code execution. So after the svgElements.each the code goes to html2canvas BEFORE completing the execution of the code inside the svgElements.each. – Ami Heines Mar 18 '18 at 09:39
  • I am working locally, however, all of svg elements are converted to canvas since when I combine all svg into single div and generate a single page pdf will show all svgs – Hana90 Mar 18 '18 at 09:42

1 Answers1

0

You seem to be trying to run 2 parts in sequence but that's not how javascript works and actually runs your code.

No big deal, just a small misunderstanding between your mental model and the engine that executes the code.

A quick temporary debugging tool to see what's going on and verify that there is a discrepancy is to add console.log to key points and check the sequence of their printout once you run the code.

console.log('[1] just before: svgElements.each');
svgElements.each(function() {
  console.log('[2] just after: svgElements.each');

And also around this part of the code:

console.log('[3] just before html2canvas-div_pdf1');
html2canvas($("#div_pdf1"), {
  console.log('[4] just after html2canvas-div_pdf1');

Finally around this part of the code:

console.log('[5] just before html2canvas-div_pdf2');
html2canvas($("#div_pdf2"), {
  console.log('[6] just after html2canvas-div_pdf2');

I suspect you'll see the code doesn't print the log lines in the order you think they will.

Next, you can try wrapping the 2 calls to html2canvas with one setTimeout function and force a delay in the execution of that code by an arbitrary amount of milliseconds.

Note that this is not the recommended final production quality solution but it will make the code output what you want.

Ami Heines
  • 480
  • 7
  • 17
  • [1] just before: svgElements.each (11 times )[2] just after: svgElements.each [3] just before html2canvas-div_pdf1 [5] just before html2canvas-div_pdf2 [4] just after html2canvas-div_pdf1 [6] just after html2canvas-div_pdf2 – Hana90 Mar 18 '18 at 10:07
  • How to force event to continue execution before going to another event ? – Hana90 Mar 18 '18 at 10:10
  • In javascript, the thinking is reversed. You don't force to block execution, you need the loop to fire an event after it completes all the execution of the items. The cleanest way to do it is with the async library, http://caolan.github.io/async/docs.html – Ami Heines Mar 18 '18 at 10:14
  • I put each of these events in onclick for separately buttons, So I control execution, However, I have the right sequence of logs [1],[2],[3],[4],[5],[6], the pdf still show the first part of div_pdf1 – Hana90 Mar 18 '18 at 10:20
  • I think that there is a problem in canvas that make code consider a single of canvas to consider in its work – Hana90 Mar 18 '18 at 10:21
  • You have a similar problem to this one, it is not related to canvas specifically but to calling a function inside a loop: https://stackoverflow.com/questions/13343340/calling-an-asynchronous-function-within-a-for-loop-in-javascript – Ami Heines Mar 18 '18 at 10:37
  • still stuck with the problem – Hana90 Mar 18 '18 at 12:38
  • try giving the tags unique identifiers so you can control which one is handled in the loop. Something like: ... To make it easier for others to help, upload the minimum code to a public page, maybe https://jsfiddle.net/ – Ami Heines Mar 18 '18 at 13:13