Search code examples
pythondjangopdfweasyprint

Using Weasyprint to create file response


I am making a web application with Django relating to guitar chord sheets. One of the features is the ability to generate a PDF from a chord sheet and download it. I am using Weasyprint to generate the PDF, but I'm getting a problem where instead of downloading the file, the view just shows a really long sequence of digits. Here's my view function:

def download_pdf(request, song_id):
    song = get_object_or_404(Song, pk=song_id)
    song.chordsheet.open("r")
    chordsheet_html = HTML(string=chopro2html(song.chordsheet.read())) # Generates HTML from a text file, not relevant here
    chordsheet_css = CSS(string="div.chords-lyrics-line {\n"
    "   display: flex;\n"
    "   font-family: Roboto Mono, monospace;\n"
    "}\n")
    song.chordsheet.close()
    return FileResponse(chordsheet_html.write_pdf(stylesheets=[chordsheet_css]), as_attachment=True, filename=song.title + "_" + song.artist + ".pdf")

And when I run the code, all I get is an empty webpage displaying a 53,635 digit number.

For what it's worth, I have a similar view function that does the same thing except without the PDF generation (downloads the raw file) and it works fine. How can I fix this?


Solution

  • I found a solution - I needed to write the PDF to a buffer before responding with it.

    import io
    
    def download_pdf(request, song_id):
        buffer = io.BytesIO()
        
        # ...
    
        chordsheet_html.write_pdf(buffer, stylesheets=[chordsheet_css])
        buffer.seek(0)
        return FileResponse(buffer, as_attachment=True, filename=song.title + "_" + song.artist + ".pdf")