javascriptpythonvue.jsgetfpdf2

My fpdf2 PDF is blank when I try to download it from the browser after passing it to the front end


I cannot for the life of me figure out why this PDF won't display. I'm using fpdf2 to create a PDF that looks good when saving it from the Python script. However, something must be going wrong when passing the data to the front-end.

Here is the Python code responsible for creating a barebones PDF:

taskGeneratePDF.py

from fpdf import FPDF
from api.packages.utils.jsonLogger import LogFormat, get_or_create_json_logger

logger = get_or_create_json_logger(__name__, log_format=LogFormat.DEFAULT)


def generate_task_pdf(task_data: dict, output_path: str) -> bytearray:
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("helvetica", style="B", size=16)
    pdf.cell(40, 10, "Hello World!")

    logger.info(f"PDF generated and saved to {output_path}")
    result = pdf.output()
    return result

The PDF Generation gets accessed through a route like this:

TaskRoutesId.py

class RouteTaskGeneratePDF(customAuthCheckClass):
    def get(
            self,
            request: HttpRequest,
            *args,
    ) -> HttpResponse:
        try:
                # Replace './test.pdf' with your desired output path or logic
                pdf_response = generate_task_pdf({"test": "test"}, "test.pdf")

            headers = {"Content-Disposition": "attachment; filename=myfilename.pdf"}
            response = HttpResponse(bytes(pdf_response), content_type="application/pdf", headers=headers)
            return response

        except Exception as e:
            # Log the error as needed
            return HttpResponse({"error": f"Failed to generate PDF: {str(e)}"}, status=404)

Here is the snippet responsible for hitting the route:

TaskClient.ts

export async function getTaskPDF(taskId: string, payload: TaskPDFPayload) {
  try {
    const response = await httpGet<Uint8Array>(`/task/${taskId}/pdf/`)

    return response
  } catch (error) {
    return await Promise.reject(error)
  }
}

TaskMainDetails.vue

function generate_pdf() {
  const payload = {}
  const taskId = props.task?.vertexID || ""
  //returns a promise
  let pdf_response = TaskClient.getTaskPDF(taskId, payload)
  pdf_response.then((message) => {
    
    const blob = new Blob([message.data], { type: "application/pdf" })
    let link = document.createElement("a")
    link.href = window.URL.createObjectURL(blob)
    link.download = "test_pdf_name"
    link.click()
  })
}

Here is the PDF representation that I end up with:

"%PDF-1.3
1 0 obj
<<
/Count 1
/Kids [3 0 R]
/MediaBox [0 0 595.28 841.89]
/Type /Pages
>>
endobj
2 0 obj
<<
/OpenAction [3 0 R /FitH null]
/PageLayout /OneColumn
/Pages 1 0 R
/Type /Catalog
>>
endobj
3 0 obj
<<
/Contents 4 0 R
/Parent 1 0 R
/Resources 6 0 R
/Type /Page
>>
endobj
4 0 obj
<<
/Filter /FlateDecode
/Length 72
>>
stream
x�3R��2�35W(�r
Q�w3T04�30PISp
    �Z(�[����(hx����+���(j*�d��V
endstream
endobj
5 0 obj
<<
/BaseFont /Helvetica-Bold
/Encoding /WinAnsiEncoding
/Subtype /Type1
/Type /Font
>>
endobj
6 0 obj
<<
/Font <</F1 5 0 R>>
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
>>
endobj
7 0 obj
<<
/CreationDate (D:20250708064952Z)
>>
endobj
xref
0 8
0000000000 65535 f 
0000000009 00000 n 
0000000096 00000 n 
0000000199 00000 n 
0000000279 00000 n 
0000000422 00000 n 
0000000524 00000 n 
0000000611 00000 n 
trailer
<<
/Size 8
/Root 2 0 R
/Info 7 0 R
/ID [<119889D12E2230568C66D2CFBCFCE0FB><119889D12E2230568C66D2CFBCFCE0FB>]
>>
startxref
666
%%EOF
"

I can verify that this is the data getting downloaded when I change the type to text/plain. When the type is set to application/pdf, I end up with a blank PDF. I can also tell that part of it is working properly because adding more pages to the PDF results in a download with the proper amount of pages. I do not save the PDF locally to the server first.

I'm running out of ideas, so I'd really appreciate if anyone had any insight.

I've tried converting to different types, using a Blob() vs a File(), changing headers to include "Content-Disposition": "attachment; filename=myfilename.pdf" and more.

Some of these ideas came from previous posts, but none of them quite did the trick. Here are some of the posts:


Solution

  • I needed to use

    httpGetBlob<Blob>
    

    in TaskClient.ts instead of the httpGet