0

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);
 }
}
  • 1
    Probably your image reference in PrintService cannot be modified by WebPack when packaging and so it's not resolved at runtime. If your image never changes you could inline it as a base64 encoded image string. Have you read https://stackoverflow.com/questions/19065562/add-image-in-pdf-using-jspdf – Gaël Marziou Jul 11 '23 at 04:48
  • 1
    @GaëlMarziou thanks for your input. I had read this topic, I tried several answer, the only thing that worked was to put the base64 encoded image in a string constant. So 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. – Manolo de la Vega Aug 02 '23 at 06:25

1 Answers1

1

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