Search code examples
angularwebpackjhipsterjspdf

jsPDF not generating PDF in production ( maybe an issue with webpack conf ? )


In an Angular app generated by JHipster, I managed to produce PDFs via jsPDF in dev mode. But when I build it and push it on a VPS, adding images in the doc crashes the webApp.

717.1183bdcffdb5d922.js:1 ERROR TypeError: Cannot read properties of undefined (reading 'data')
    at i.__addimage__.getImageFileTypeByImageData (641.074d6c566f08a36e.js:1:287740)
    at Object.Zt (641.074d6c566f08a36e.js:1:295400)
    at i.addImage (641.074d6c566f08a36e.js:1:295162)
    at m.addImageEntete (287.1ee6c0684b572bd4.js:1:800)
    at M.getPDF (641.074d6c566f08a36e.js:1:47054)
    at M.generatePDFComplet (641.074d6c566f08a36e.js:1:115186)
    at 641.074d6c566f08a36e.js:1:139516
    at d7 (717.1183bdcffdb5d922.js:1:217724)
    at c (717.1183bdcffdb5d922.js:1:217891)
    at HTMLButtonElement.<anonymous> (717.1183bdcffdb5d922.js:1:330709)

I can display them in the address bar whith their URL though.

Here is a bit of relevant code :

import { jsPDF, jsPDFOptions } from 'jspdf';
import autoTable, { RowInput } from 'jspdf-autotable';

export const ENTETE_PATH_ABMS = '/content/images/prints/abms_entete.jpg';

@Injectable({
  providedIn: 'root',
})
export class PrintService {
  constructor() {}

  getPDF() {
    const options: jsPDFOptions = {
      orientation: 'p',
      unit: 'mm',
      format: 'a4',
      putOnlyUsedFonts: true,
      floatPrecision: 16, // or "smart", default is 16
    };
    const doc = new jsPDF(options); 
    doc.addImage(ENTETE_PATH_ABMS, 'jpg', posX, posY, w, h);
 }
}

Solution

  • I figured out that the issue was one that is recurrent with jsPDF : I was adding image before loading it completely , and the issue appeared in production because of his lesser performance than my dev computer.

    With the help of a famous chatbot, I came up with this solution, featuring observables and html2canvas.

    npm install html2cavas rxjs
    

    In a utility class named CommonUtils :

      export type ImageWrapper = {
       imagePath : string,
       x : number,
       y : number,
       width : number,
       height : number,
     }
    
      private static loadImage(url: string): Observable<HTMLImageElement> {
    return new Observable((observer) => {
      const img = new Image();
      img.crossOrigin = 'Anonymous'; // Allow loading images from external URLs
      img.onload = () => {
        observer.next(img);
        observer.complete();
      };
      img.src = url;
    });
    

    }

     private static convertImageToCanvas(img: HTMLImageElement, width: number, height: number): Observable<HTMLCanvasElement> {
    return from(html2canvas(img, { width, height }));
    

    }

     public static addImagesAndSave(images: Array<ImageWrapper>, doc : jsPDF, docname:string): void {
    const imageObservables = images.map(({ imagePath }) => this.loadImage(imagePath));
    
    forkJoin(imageObservables)
      .pipe(
        map((loadedImages) => {
    
          return loadedImages.map((img, index) => {
            document.body.appendChild(img);
            return this.convertImageToCanvas(img, images[index].width, images[index].height)
                        
          }
          );
        }),
        switchMap((canvasObservables) => forkJoin(canvasObservables))
      )
      .subscribe((canvases) => {
        canvases.forEach((canvas, index) => {
          const imgData = canvas.toDataURL('image/jpeg');
          const { x, y, width, height } = images[index];
          console.log('add image ' + ' ' + x + ' ' +  y + ' ' + width + ' ' + height  + ' ' +  imgData  )
          doc.addImage(imgData, 'PNG', x, y, width, height);
        });
        doc.save(docname);
      });
    

    }

    This service is called like this

     var imageList : ImageWrapper[] = []
     // set your path , height, width, x and y accordingly
     imageList.push({
      imagePath : '/assets/images.jpeg',
      height : h,
      width : w,
      x : posX,
      y : posY
     })
     CommonUtils.addImagesAndSave(imageList , doc, 'pdfname.pdf')
    

    I have still to do some tuning because of issues with images' size but they are loaded and displayed in the pdf