0

I've been doing some googling and I am not turning up the answer I am searching for.

Essentially I have a Django web app. One of the models is a Blueprint which contains a link to a PDF stored someplace. I want to be able to write an admin action so that if the user selects one or more of these models I can print the PDFs attached one after another to a physical printer.

90% of the stuff I'm finding is about how to create PDFs. I don't want to create them, they already exist. Essentially all I need to do is allow the user to select a printer to send the pdfs to in a sequential order.

I'm a little confused on what needs to be done , but I might just be overthinking it. From inside the admin action I have access to all the selected Objects. But the actual PDF files would be in memory not sitting in disk someplace. They do exist in storage. However, this is running inside a browser so I suppose all I need to do is trigger the browser's inborn print capability and pass it some sort of reference to the PDF? Does that make sense?

I have come across a couple of libraries that involve printing in Python but they seem to involve windows specifically and/or a PDF library such as foxit or adobe being installed (somewhere). Given that I'm running inside a web app inside a browser I can't really know if those apps are installed or not so how can I know if that's going to work? What if someone's running the app on their phone - popping up a windows printer dialog isn't going to work? Is there some sort of abstraction layer I can leverage?

Hopefully I am making sense here.

Can anyone assist in what I'm attempting to do?

thanks in advance, EK

Ed Kramer
  • 131
  • 1
  • 12
  • This SO answer may help https://stackoverflow.com/questions/45782639/printing-a-pdf-file-in-django – Leshawn Rice Jul 14 '23 at 23:20
  • Not sure I fully understand what you mean by 'put all the filenames on the HTML and print one by one.' but if I'm reading the linked article right... could I not create a 'new' PDF document, iterate through the selected Objects and add their PDFs pages to the 'new PDF' document and then print it like one big document? These PDFs are essentially 1 page character sheets from an RPG. – Ed Kramer Jul 15 '23 at 22:09
  • 1
    Yes the web page would be at a public domain level but the functionality would be behind authentication. By 'PDF enabler' I assume you mean something like Adobe or Foxit. and yeah I think I get your meaning. That was the problem I was trying to get around. The web page itself doesn't really have any control over the local environment where the printer would be. So if I'm understanding you correctly the best that can be done is render the PDF to a web page and let the user decide how to print it? – Ed Kramer Jul 17 '23 at 14:34

1 Answers1

1

am using django with python hopefully this helps somebody.

ok so, the way I got this working is the following:

in my admin.py file I have the admin handler for the Object model that holds the blueprint pdf. I added an admin action. This allows you to select multiple models in the admin list and perform said action on them all by iterating through a queryset. When you click on the 'go' button in the admin panel it briefly displays an HTML file that prompts the user with a list of the selected items and a 'proceed' and 'cancel' button.

{% extends "admin/base_site.html" %}

{% block content %}
<form action="" method="post">
  {% csrf_token %}
<p>
Are you sure you want to print the PDFs for the selected items?
</p>
  {% for item in items %}
    <p>
      {{ item }}
    </p>
    <input type="hidden" name="_selected_action" value="{{ item.pk }}" />
  {% endfor %}

    <input type="hidden" name="action" value="print_pdfs" />
    <input type="submit" name="proceed" value="Proceed"/>
    <input type="submit" name="cancel" value="Cancel"/>
</form>
{% endblock %}

if you click 'proceed', the code follows as below.

    @admin.action(description="Print the PDFs of all selected items")
def print_pdfs(self, request, queryset):
    if 'proceed' in request.POST:
        output_file = PdfWriter()
        try:
            for item in queryset:
                for module_pdf in ItemPdf.objects.filter(pdf_assembly_item_id=item.pk):
                    reader = PdfReader(module_pdf.pdf.open("rb"))
                    number_of_pages = len(reader.pages)
                    for page_number in range(number_of_pages):
                        page = reader.pages[page_number]
                        output_file.add_page(page)

            with io.BytesIO() as pdf:
                output_file.write(pdf)
                response = HttpResponse(pdf.getbuffer(), content_type='application/pdf')
                response['Content-Disposition'] = 'filename=%s' % 'output.pdf'
                return response
        finally:
            output_file.close()

    elif 'cancel' in request.POST:
        self.message_user(request, level=messages.ERROR, message="Action cancelled")
        return HttpResponseRedirect(request.get_full_path())
    else:
        return render(request, 'necrotopia/admin_print_pdfs.html', context={
            "title": 'Confirm Action',
            "items": queryset,
        })

The code iterates through the selected PFS and combines them into a single PDF for printing, and then writes that to an IO stream. This io stream is put into the HttpResponse as content and then returned. Poof DJango tells your browser to render that pdf and you can print from there!

Ed Kramer
  • 131
  • 1
  • 12