1

I have a Flask web app with a download button. When I click on the button I would like to execute a python method, not a javascript method. This python method should take as input some recently modified html code from the web page.

Most precisely the following happens: the user is asked to fill the blanks in some text and when she is done clicks on the download button. When the button gets clicked, a python function render_pdf() is executed that converts the html modified text to a pdf by using pdfkit library.

In its most simple version, my project has the following structure:

 - static/sample.pdf
 - templates/index.html
 - main.py

And this is the code I have so far:

main.py

from flask import Flask, render_template
import pdfkit 

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')
    
if __name__ == "__main__":
    app.run(debug=True)

index.html

<body>

    <p class="test">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus, beatae!
        <span contenteditable="true" class="badge alert-info name" data-placeholder="Enter your name" data-focused-advice="Start typing"></span><i class="fa fa-lg fa-plus-circle"></i>
        Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perspiciatis, laboriosam?
    </p>

    <button onclick="render_pdf()"><a href="../static/sample.pdf" download="my_file">Download</a></button>
</body>

The python method I would like to execute should be this one, passing htmlstr as input:

import pdfkit

def render_pdf(htmlstr):
    pdfkit.from_string(htmlstr, './static/sample.pdf') 

I know I can get with javascript the variable htmlstr by doing document.getElementsByClassName("test")[0].innerHTML;

I feel I am super close to get there and I have checked maybe a bit old but very similar questions but I just do not know how to pass the input html to the python method. What am I missing?

AmyyazdaN
  • 303
  • 1
  • 9
G. Macia
  • 1,204
  • 3
  • 23
  • 38

1 Answers1

1
  1. Define a URL(route) that runs your script.
  2. In your JavaScript code, make an HTTP request to the URL defined in Step 1.

It's so simple, I wrote codes for you and I hope it help you:

I also made a gist at github.

main.py

from flask import Flask, render_template
from flask import request
import pdfkit


## ## ## ## ## ## ## ## ## ## ## ## 
app = Flask(__name__)


## ## ## ## ## ## ## ## ## ## ## ## 
# config wkhtmltopdf on my system
path_wkhtmltopdf = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe'
config = pdfkit.configuration(wkhtmltopdf=path_wkhtmltopdf)


# function of rendering
def render_pdf(htmlstr):
    pdfkit.from_string(htmlstr, './static/sample.pdf', configuration=config) 


## ## ## ## ## ## ## ## ## ## ## ## 
@app.route('/')
def index():
    return render_template('index.html')


@app.route('/update-file/', methods=['POST'])
def convert_string_to_pdf():
    try:
        # geting the json request
        json = request.get_json()
        
        # call render_pdf function with new value
        render_pdf(json['htmlstr'])
        
        # '0' meaning no error
        return '0'

    except Exception as error:
        return str(error)


## ## ## ## ## ## ## ## ## ## ## ## 
if __name__ == "__main__":
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Convert HTML to PDF</title>
</head>
<body>

    <p class="test">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Temporibus, beatae!
        <span contenteditable="true" class="badge alert-info name" data-placeholder="Enter your name" data-focused-advice="Start typing"></span><i class="fa fa-lg fa-plus-circle"></i>
        Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perspiciatis, laboriosam?
    </p>

    <br><br>
    <button onclick="commandServerToRender()">Convert to PDF</button>

    
    <br><br>
    <div id="downloadBox" hidden>
        <p style="color: green;"><b>File is ready to download</b></p>
        <a href="/static/sample.pdf" download="my_file">Download PDF File</a>
    </div>

    <div id="errorBox" hidden>
        <p style="color:red"><b>There was an error:</b></p>
        <p id="errorMessage" style="color:red"></p>
    </div>


    <script>
        function commandServerToRender() {
            test = document.getElementsByClassName("test")[0].innerHTML;
            downloadBox = document.getElementById('downloadBox');
            errorBox = document.getElementById('errorBox');
            errorMessage = document.getElementById('errorMessage');

            downloadBox.hidden = true;
            errorBox.hidden = true;

            // Sending data in JSON format using POST method
            //
            var xhr = new XMLHttpRequest();
            var url = "/update-file/";

            xhr.open("POST", url, true);
            xhr.setRequestHeader("Content-Type", "application/json");

            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    // if print 0 meaning that it was successfull
                    if (xhr.responseText == '0') {
                        downloadBox.hidden = false;

                    } else {
                        errorMessage.innerHTML = xhr.responseText;
                        errorBox.hidden = false;

                    }
                }
            };

            var data = JSON.stringify({"htmlstr": test});
            xhr.send(data);
        }
    </script>
</body>
</html>

EDIT

To set encoding (You asked in comments) you must modify render_pdf() and add <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> to htmlstr, like below:

(If you are adding the whole html file, you need to add this code to your html's <head> tag)

def render_pdf(htmlstr):
    pdfkit.from_string('<meta http-equiv="Content-type" content="text/html; charset=utf-8" />' + htmlstr, './static/sample.pdf', configuration=config) 
AmyyazdaN
  • 303
  • 1
  • 9
  • Works perfectly! And the errors messages are a great extra. – G. Macia Jul 12 '21 at 19:31
  • Minor point, how can I set the character encoding, I am doing in the this: but the characters are not rendered correctly in the pdf – G. Macia Jul 12 '21 at 20:02
  • I also tried xhr.setRequestHeader("Content-Type", "application/json;charset=iso-8859-1"); but the Spanish characters are still not rendering properly – G. Macia Jul 12 '21 at 20:14
  • I edited post and added encoding codes, I hope it solve your problem. – AmyyazdaN Jul 12 '21 at 21:12