vuejs3pdf-generationfirebase-storagejspdfhtml2canvas

How to include an Image URL from Firebase Storage that is being blocked by cors, as part of a PDF file


I have component that renders an invoice part of the html includes an image that the src is a url from Firebase Storage. I am using jspdf & html2canvas to create a pdf file.

The image renders fine in Vue component but does not render in pdf created with jspdf & html2 canvas.

I suspect the image is not being allowed because of cors issues.

Trying to figure a way to show the image in the pdf file. I see in html2canvas docs you need to use a proxy not sure how to do this in my app or if it is necessary.

EDIT My application is deployed on Netlify, so not sure if running an express server would be practical. In the end just trying to download a pdf from the generated html that includes a dynamic image in my Vue component.

Here is a working demo

Notice the image 200 x 200 shown in demo is not in the pdf

Note the image src url can change is not meant to be hard coded.

Here is my HTML

<template>
  <div ref="invoiceContent">
    <div>
      <AppAvatarImage :src="invoice.avatar" />
    </div>
  </div>
  ..... other stuff that renders fine
</template>

Here is my method to save pdf file.

<script setup>
import { ref } from 'vue';
import { jsPDF } from 'jspdf';
import html2canvas from 'html2canvas';
const invoiceContent = ref(null);

const props = defineProps({
  invoice: {
    type: Object,
    required: true,
  },
});

const savePdf = () => {
  const doc = new jsPDF({
    orientation: 'p',
    unit: 'px',
    format: 'legal',
    hotfixes: ['px_scaling'],
  });

  html2canvas(invoiceContent.value, {
    width: doc.internal.pageSize.getWidth(),
    height: doc.internal.pageSize.getHeight(),
  }).then((canvas) => {
    const img = canvas.toDataURL('image/jpeg', 0.5);
    if (screen.width < 700) {
      doc.addImage(img, 'JPEG', 50, 50, screen.width * 3, doc.internal.pageSize.getHeight());
    } else {
      doc.addImage(img, 'JPEG', 150, 50, doc.internal.pageSize.getWidth(), doc.internal.pageSize.getHeight());
    }

    doc.save('Invoice.pdf');
  });
};
</script>

Here is my method to get image url from Firebase Storage.

async uploadAvatar({ file }) {
      if (!file) return null;
      let filename = file.name;
      try {
        const storeAuth = useStoreAuth();
        let name = `${Date.now()}-${filename}`;
        let path = `uploads/${storeAuth.user.id}/profileImgs/${name}`;
        const storageRef = ref(storage, path);

        await uploadBytes(storageRef, file);

        const avatarURL = await getDownloadURL(storageRef);

        let data = {
          id: storeAuth.user.id,
          avatar: avatarURL,
        };

        await this.updateUserAvatar(data);

        return avatarURL;
      } catch (error) {
        console.log('File Upload Error');
        console.error(error);
      }
    }

My AppAvatarImage component.

<template>
  <img :src="src || profileImg" />
</template>
<script setup>
import profileImg from '@/assets/images/all-img/user.svg';

const props = defineProps({
  src: {
    type: String,
  },
});
</script>

Solution

  • You can configure CORS for Firebase Cloud Storage to allow your frontend URL access.

    1. Download the gsutil CLI. Download link
    2. Create a file cors.json with the following data:
    [
      {
        "origin": ["https://example.netlify.app"], // ["*"] to allow all origins
        "method": ["GET"],
        "maxAgeSeconds": 3600
      }
    ]
    
    1. Run the command gsutil cors set cors.json gs://<your-cloud-storage-bucket>

    It should now work. All of the above information can also be found in the Firebase Documentation