iworld
iworld

Reputation: 1

How can I use pdf.js to display the first page of every user-uploaded pdf file as a preview in Django?

Up to now i have successfully display the first page preview for one pdf file but it doesn't work for remaining others.

models.py

import uuid
from django.db import models


class PdfUploader(models.Model):
    uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'pdf_uploader'
        ordering = ['-uploaded_at']

    @property
    def filename(self):
        return self.docfile.name.split("/")[4].replace('_',' ').replace('-',' ')

views.py

class PdfUploadView(CreateView):
    def get(self, request, *args, **kwargs):
        context = {'form': PdfUploadForm()}
        return render(request, 'partials/pdf_upload_form.htm', context)

    def post(self, request, *args, **kwargs):
        form = PdfUploadForm(request.POST, request.FILES)
        files = request.FILES.getlist('docfile')
        if form.is_valid():
            for f in files:
                file_instance = PdfUploader(docfile=f)
                file_instance.save()
            return HttpResponseRedirect(reverse_lazy('pdf-list'))
        return render(request, 'partials/pdf_upload_form.htm', {'form': form})

pdf_upload_form.htm

{% block "content" %}
<div role="main" class="main">
    <section class="section section-default pt-5 m-0">
        <div class="container">
            <form method="post" enctype="multipart/form-data">
                {% csrf_token %}
                {{ form.as_p }}
                <button type="submit">Upload</button>
            </form>
        </div>
    </section>
</div>
{% endblock %}

pdf_lists.htm By following official official django docs, I am passing a context variable as JSON to the pdf.js.

{% for obj in pdfs %}
    <tr>
         <td>
              {{ forloop.counter }}
         </td>
         <td>
             <a href="{{ obj.docfile.url }}" target="_blank" rel="noopener noreferrer">{{obj.filename}}</a>
         </td>
         <td>
              {{ obj.uploaded_at|date:"d-M-Y" }}
         </td>
         <td>
             <a href="{{obj.docfile.url}}" target="_blank" rel="noopener noreferrer">
                 <canvas id="the-canvas" style="height:250px;">
                 </canvas>
                 {{obj.docfile.url|json_script:'mydata'}}
             </a>
         </td>
    </tr>
{% endfor %}

pdf.js Now I'm reading the previously passed JSON, which contains the path to the user's submitted pdf file to further process it using JS to display the first page of pdf as preview.

const mydata = JSON.parse(document.getElementById('mydata').textContent);
console.log(mydata);

// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';

// Asynchronous download of PDF

var loadingTask = pdfjsLib.getDocument(mydata);
loadingTask.promise.then(function (pdf) {
    console.log('PDF loaded');

    // Fetch the first page
    var pageNumber = 1;
    pdf.getPage(pageNumber).then(function (page) {
        console.log('Page loaded');

        var scale = 0.5;
        var viewport = page.getViewport({ scale: scale });

        // Prepare canvas using PDF page dimensions
        var canvas = document.getElementById('the-canvas');
        var context = canvas.getContext('2d');
        canvas.height = viewport.height;
        canvas.width = viewport.width;

        // Render PDF page into canvas context
        var renderContext = {
            canvasContext: context,
            viewport: viewport
        };
        var renderTask = page.render(renderContext);
        renderTask.promise.then(function () {
            console.log('Page rendered');
        });
    });
}, function (reason) {
    // PDF loading error
    console.error(reason);
});

screenshot of the result: As you can see, the first pdf file displays a preview, while the rest do not. enter image description here

Upvotes: 0

Views: 4027

Answers (2)

Raju Sah
Raju Sah

Reputation: 600

It can be achieved by creating specific canvas for each PDF file. Please replace the PDF file with your server files.

Here is the js code

function LoadAndPrint()
{
    var files = [{name:'sample1.pdf',url:'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf'},{name:'sample2.pdf',url:'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf'}];

    var pdfjsLib = window['pdfjs-dist/build/pdf'];
    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.js';

    files.forEach(myFunc);
    function myFunc(file,i){

        idContainer.innerHTML += 
        '<span>'+file.name+'</span><canvas id="the-canvas'+i+'"></canvas><hr><br>';

        var loadingTask = pdfjsLib.getDocument(file.url);
        loadingTask.promise.then(function(pdf) {
            console.log('PDF loaded');

            var pageNumber = 1;
            pdf.getPage(pageNumber).then(function(page) {
            console.log('Page loaded');

                var scale = 1.1;
                var viewport = page.getViewport({scale: scale});

                var canvas = document.getElementById("the-canvas"+i);
                var context = canvas.getContext('2d');
                canvas.height = viewport.height;
                canvas.width = viewport.width;

                var renderContext = {
                  canvasContext: context,
                  viewport: viewport
                };

                var renderTask = page.render(renderContext);
                renderTask.promise.then(function () {
                  console.log('Page rendered');
                });
            });
        }, function (reason) {
          console.error(reason);
        });
    }
}

.html

<!DOCTYPE html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
    </head>

    <body>
        <button id="idPrint" onclick="LoadAndPrint()">Load and Print</button><br>
        <div id="idContainer"></div>
    </body>
</html>

And here is the JSFiffle

Upvotes: 2

Yuri Nudelman
Yuri Nudelman

Reputation: 2943

The problem is that you only call your script 1 time. Its input is 1 object with id 'mydata' and one canvas with id 'the-canvas' that serves as output.

What you should do:

First, assign a unique ID to each data and to each canvas element.

For canvas it is simple:

  <canvas id="the-canvas{{ forloop.counter }}" style="height:250px;">
  </canvas>

For data it is a little more complex due to json_script filter, something like this:

{% with mydata_id="mydata"|add:forloop.counter %}
    {{obj.docfile.url|json_script:mydata_id}}
{% endwith %}

Then you also have to know the length of pdfs, so maybe add something like this after the for loop, quick and dirty:

<script> const mypdfslength = {{ pdfs | length }}; </script>

Just make sure to place this before your JS.

Next, in your js, you have to put all your code inside a for loop from 0 to mypdfslength.

And, of course, when resolving mydata and canvas, make sure to reference them by their new ID, which would be, considering i is your for loop index:

const mydata = JSON.parse(document.getElementById(`mydata{i}`).textContent);

and

var canvas = document.getElementById(`the-canvas{i}`);

That's it.

Disclaimer: I didn't really test it, but that is definitely the direction.

Upvotes: 0

Related Questions