Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django HTML & CSS render to pdf

I'm trying to convert HTML & CSS into a pdf page using Django-weasyprint but I'm kind of stuck with their tutorial because I want the PDF to render the current page when the user clicks on the download button and the pdf downloads automatically with read-only privileges. This whole process kind of feels painful to do.

Currently, weasyprint just converts a URL in Django to pdf, but I don't know how to set the button to look at the weasyprint view.

Maybe I am looking past it and over complicating it, any assistance would be appreciated.

Weasyprints example code:

from django.conf import settings
from django.views.generic import DetailView

from django_weasyprint import WeasyTemplateResponseMixin
from django_weasyprint.views import CONTENT_TYPE_PNG


class MyModelView(DetailView):
    # vanilla Django DetailView
    model = MyModel
    template_name = 'mymodel.html'


class MyModelPrintView(WeasyTemplateResponseMixin, MyModelView):
    # output of MyModelView rendered as PDF with hardcoded CSS
    pdf_stylesheets = [
        settings.STATIC_ROOT + 'css/app.css',
    ]
    # show pdf in-line (default: True, show download dialog)
    pdf_attachment = False
    # suggested filename (is required for attachment!)
    pdf_filename = 'foo.pdf'


class MyModelImageView(WeasyTemplateResponseMixin, MyModelView):
    # generate a PNG image instead
    content_type = CONTENT_TYPE_PNG

    # dynamically generate filename
    def get_pdf_filename(self):
        return 'bar-{at}.pdf'.format(
            at=timezone.now().strftime('%Y%m%d-%H%M'),
        )

I made a virtual env on my pc and it's setup exactly like the example. Currently using Boostrap 4.

*Edit if there is a better way of doing it, you are more than welcome to share it :)

Also I want to target just the body tags so that it converts only that section to pdf and not the ENTIRE page.

The solution I used before this is: https://codepen.io/AshikNesin/pen/KzgeYX but this doesn't work very well.

*EDIT 2.0

I've moved on to js and I'm stuck with this script where it doesn't want to create the pdf form on click function also is there a way to set the js function to ONLY download the selected Id in the div and not on certain scale? (afraid that it's going to use the resolution instead of the actual content that needs to be rendered)

https://jsfiddle.net/u4ko9pzs/18/

Any suggestions would be greatly appreciated.

like image 248
Faziki Avatar asked Dec 04 '25 21:12

Faziki


1 Answers

I was tried django-wkhtmltopdf and weasyprint. For shure weasyprint allow more in subject of css but still it isn't one to one to browser version. I wasn't happy with results. It is ok for tables, and all word/excel like data. But in my case i have to made px to px 'certificate' like pdf with svg shaped background Then some lack of features starts to be not convenient problem.

So if you need use the same css with minimal modification to get effect like in browswer then puppeter solving all compabitlity problems. But this force to use Node to run puppeteer. (in my case i already use it to compile assets)

Cli for puppetere:

npm i- puppeteer-pdf

then install wrapper

pip install django-puppeteer-pdf

add node puppeteer cli start command in settings:

PUPPETEER_PDF_CMD = 'npx puppeteer-pdf'

and create view

from puppeteer_pdf import render_pdf_from_template
from django.http import HttpResponse, 
import os

# Rendering code 
context = {
    'data': 'test data'
}

output_temp_file = os.path.join(settings.BASE_DIR, 'static', 'temp.pdf')

pdf = render_pdf_from_template(
    input_template='path to template in templates dir'
    header_template='',
    footer_template='',
    context=context,
    cmd_options={
        'format': 'A4',
        'scale': '1',
        'marginTop': '0',
        'marginLeft': '0',
        'marginRight': '0',
        'marginBottom': '0',
        'printBackground': True,
        'preferCSSPageSize': True,
        'output': output_temp_file,
        'pageRanges': 1
    }
)
#you can remove temp file now or collecte them and run some crone task.(performance)
if os.path.exists(output_temp_file):
    os.remove(output_temp_file)
else:
    # raise error or something

filename = f'filename={'your donwload file name'}.pdf'

response = HttpResponse(pdf, content_type='application/pdf;')
response['Content-Disposition'] = filename

To keep original color you have to add

html{
  -webkit-print-color-adjust: exact;
}

controlling page size and margin when you choice 'preferCSSPageSize'.

@page {
  size: A4;
  margin: 0 0 0 0;
}
like image 151
Robert Avatar answered Dec 07 '25 11:12

Robert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!