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?

1 Answer 1

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) 
Sign up to request clarification or add additional context in comments.

4 Comments

Works perfectly! And the errors messages are a great extra.
Minor point, how can I set the character encoding, I am doing in the <head> this: <meta charset="ISO-8859-1"> but the characters are not rendered correctly in the pdf
I also tried xhr.setRequestHeader("Content-Type", "application/json;charset=iso-8859-1"); but the Spanish characters are still not rendering properly
I edited post and added encoding codes, I hope it solve your problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.