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:
I needed to use
httpGetBlob<Blob>
in TaskClient.ts instead of the httpGet