Search code examples
angularjspdfhtml2canvas

JSPDF and html2canvas with Angular


I am trying to generate a PDF with html2canvas but when calling the function I throw an error, my TS is the following:

  @ViewChild('content') content:ElementRef;

  generarPDF() {
    html2canvas(document.getElementById('content'), {
      // Opciones
      allowTaint: true,
      useCORS: false,
      // Calidad del PDF
      scale: 1
    }).then(function(canvas) {
      var img = canvas.toDataURL("image/png");
      var doc = new jsPDF();
      doc.addImage(img,'PNG',7, 20, 195, 105);
      doc.save('postres.pdf');
    });
  }

My html is:

div.cliente {
    border-bottom: 2px dotted;
    width: 70%;
    float: left;
}

div.fecha {
    border-bottom: 2px dotted;
    width: 28%;
    float: right;
}

ul.satisfacion {
    float: left;
    margin-top: 28px;
}
  <div #content id="content">
    <div class="contenido-pdf">
     
      <div class="cliente">
        <span>Cliente: </span>
        <div class="campo"></div>
      </div>
      <div class="fecha">
          <span>Fecha: </span>
          <div class="campo"></div>
        </div>
      <ul class="satisfacion">
        <li>El servicio es muy satisfactorio</li>
        <li>El servicio es satisfactorio</li>
        <li>El servico es normal</li>
        <li>El servicio no es satisfactorio</li>
      </ul>
    </div>
  </div>
  <button mat-raised-button (click)="generarPDF()" id="donwloadpdf">
  DESCARGAR CARTA DE CONFORMIDAD
</button>

And my error

ERROR Error: "Uncaught (in promise): Error: Supplied Data is not a valid base64-String jsPDF.convertStringToImageData 
e.convertStringToImageData

When I am going to click on generate pdf it does not generate it or do anything, it just throws that error on my console, I already tried to generate it with jsPDF but the problem is that it does not preserve the styles


Solution

  • Your css is doing something weird and making the div with id="content" have no content.

    You can see this if you put

    #content {
      outline: 1px solid red;
    }
    

    I've commented out your css and produced the following Working Stackblitz

    Uncomment the css to see the issue.

    Steps to reproduce stackblitz

    angular.json

    "scripts": [
      "node_modules/html2canvas/dist/html2canvas.min.js",
      "node_modules/jspdf/dist/jspdf.min.js"
    ]
    

    dependancies

    1. I've avoided an issue with html2canvas by using html2canvas@1.0.0-rc.1. see - https://github.com/niklasvh/html2canvas/issues/1896

    2. The latest version of jspdf is not working with some issue with file-saver so I've just gone for the version I know works jspdf@1.4.1

    3. Include types @types/html2canvas, @types/jspdf

    4. For some reason in stackblitz you need to import these using

    import jsPDF from 'jspdf';
    import html2canvas from 'html2canvas';
    

    app.component.ts

    import { Component, ViewChild, ElementRef } from '@angular/core';
    
    import jsPDF from 'jspdf';
    import html2canvas from 'html2canvas';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent  {
      name = 'Angular';
    
     @ViewChild('content', { 'static': true }) content:ElementRef;
    
      generarPDF() {
    
        const div = document.getElementById('content');
        const options = {
          background: 'white',
          scale: 3
        };
    
        html2canvas(div, options).then((canvas) => {
    
          var img = canvas.toDataURL("image/PNG");
          var doc = new jsPDF('l', 'mm', 'a4', 1);
    
          // Add image Canvas to PDF
          const bufferX = 5;
          const bufferY = 5;
          const imgProps = (<any>doc).getImageProperties(img);
          const pdfWidth = doc.internal.pageSize.getWidth() - 2 * bufferX;
          const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
          doc.addImage(img, 'PNG', bufferX, bufferY, pdfWidth, pdfHeight, undefined, 'FAST');
    
          return doc;
        }).then((doc) => {
          doc.save('postres.pdf');  
        });
      }
    
    }
    

    It anyone comes across a way to improve this answer feel free to just edit it, I'll accept any such edits.

    Update - Improved using Pdf file size too big created using jspdf