Weasyprint doesn't render images (Python) - python

I am writing a python script that generates a html file out of some images and svg files, which later should be converted into a pdf file.
For the conversion I am using weasyprint, but for some reason it doesnt render the images, only the according alt-text.
Here is a screenshot of the html file rendered in Chrome:
But the generated pdf looks like this:
Here is the code that should generate the pdf:
html = HTML(filename="path/to/file")
html.write_pdf("path/to/output/file")
And here is the according html file:
<img src="C:/Users/jonas/Documents/Dev/PythonLabel/bin/LABEL_SVG.svg" alt="SVG of Label" />
<div class="sn-number">3253343345</div>
<img class="qr-code" src="C:/Users/jonas/Documents/Dev/PythonLabel/bin/qrcode.png" alt="qrcode" />
I dont know what causes the problem, but Firefox isnt able to render the images either, but Chrome and Brave manage to do so. Maybe both problems have a similar reason.

So I managed to get it to work, but its not a pretty solution.
I figured out, that weasyprint was able to get images and SVGs from actual webpages like stackoverflow. So I made a little flask app that would only serve the one html file, and after the PDF was created I just stopped the flask server.
from flask import Flask
from multiprocessing import Process
app = Flask(
__name__, static_folder=path/to/static/folder)
#app.route("/")
def home():
with open(path/to/html, "r") as f:
html = "".join(f.readlines())
return html
def start_server():
app.run()
if __name__ == "__main__":
server = Process(target=start_server)
server.start()
sleep(4)
print("Generating PDF...")
html = HTML(
"http://localhost:5000").write_pdf(path/to/output/file)
server.terminate()
server.join()

Related

weasyprint does not load flask images

Hello friends I have a code to generate pdf with weasyprint from flask and it turns out that it works and generates the pdf but the html that I am rendering has two images and it does not render them for me I have the following code:
from weasyprint import HTML
image = "/static/images/logo-ori.png"
name = "Pepito perez"
print_html = render_template('cert/certificado.html', img=image)
Up to this point the image renders sim problem but when I go to convert it to pdf it no longer renders it
cert = os.path.join(url_direct, "certificado.pdf")
pdf = HTML(string=print_html)
pdf.write_pdf(cert)
The pdf is stored on the server which is what I want to do and with flask I redirect to start I hope they can help me I was reviewing how to use flask-weasyprint but I cannot store the pdf on the server I hope they can help me with either of the two. Thank you in advance Thank you

Prevent browsers from opening json files and instead download when sent from flask app

I have a JSON file in the flask app that i send via either send_from_directory() or via send_file() i have tried both. This triggers on button click but the browser opens the file directly instead of getting a download dialog box. I researched a bit and found out Firefox and Chrome have an inbuilt option to do this, however i want it to always download instead of opening no matter the setting in the browser.
Flask Code
def download_file():
filename = "requiredFields.json"
return send_from_directory(app.config['MAIN_FOLDER'],filename,as_attachment=True)
HTML That calls it
Download
If you set mimetype to 'application/octet-stream' then browser should save it.
send_file(..., mimetype='application/octet-stream')
send_from_directory(..., mimetype='application/octet-stream')
Doc: send_file
See also: Do I need Content-Type: application/octet-stream for file download?
EDIT:
Minimal working example - you have to only set correct filenames in index()
It doesn't matter if you have file .json, .txt, image, .pdf or other. It works for all types of files.
from flask import Flask, render_template_string, send_from_directory
app = Flask(__name__)
app.config['MAIN_FOLDER'] = '.'
#app.route('/')
def index():
all_filenames = [
'requiredFields.json',
'input.txt',
'image.jpg',
'document.pdf',
]
return render_template_string('''
{% for file in all_files %}
Download {{file}}<br/>
{% endfor %}''', all_files=all_filenames)
##app.route('/download_file/<filename>')
#app.route('/download_file/<path:filename>')
def download_file(filename):
return send_from_directory(app.config['MAIN_FOLDER'], filename, as_attachment=True, attachment_filename=filename, mimetype='application/octet-stream')
if __name__ == '__main__':
app.run() #debug=True

'Download simulation data' link on html using flask app

I am reading user inputs and sending them for processing. After processing, the results are displayed. Along with the results I want a link on webpage to be able to download the data as a csv file. My function to process inputs looks as follows.
#app.route('/process', methods=['POST'])
def process_data():
# create csv_file
return render_template("results.html", data=csv_file)
results.html has following line.
<p> <large>Download simulation data </large></p>
This link is correctly displayed on the webpage. My function to download the data looks as follows.
#app.route('/download/<filename>')
def download(filename):
response = make_response(filename)
response.headers["Content-Disposition"] = "attachment; filename=Simulation.csv"
response.headers["Content-Type"] = "text/csv"
return response
Clicking the download link, I get '414 Request-URI Too Large'.
Is there a better solution for passing data from flask to html to flask again?
I can see that my entire data are appended to url, can I somehow avoid that?
Is it possible to directly pass response while rendering results.html and make it downloadable?
UPDATE
I learnt that putting data in url is a bad idea. Instead I can use dataurl by encoding the data and then use dataurl for csv in href tag of html.
buffer = StringIO()
dataframe.to_csv(buffer, index=False)
buffer.seek(0)
data_str = base64.b64encode(buffer.getvalue().encode('utf8')).decode('ascii')
url = "data:text/csv; base64,{}".format(data_str)
html looks like as follows.
<a download="SSW_Simulation.csv" href="{{ data_url }}">download</a>
However this solution does not work in internet explorer I guess because urls for data are not supported. Should I be saving the csv file somewhere and pass that filename to html? so that when prompted, I can fetch it from the temporary location and download using make_response? I would prefer not to save the file to the disk.
Handling the data in javascript solved the problem as suggested by #Jeronimo. json string was passed to html page.
import json
#app.route('/process', methods=['POST'])
def process_data():
# create csv_file
data = json.dumps(csv_file)
return render_template("results.html", data=data)
Javascript suggested in this answer was added to html and along with download button.
<button onclick="download({{ data }}, 'myfile.csv', 'text/csv')">download data</button>

download file FROM web TO flask server

My flask app should download files from urls which are posted to my app by a json object.
The files should be downloaded for further processing. I do not get an error message. A lot of tutorials are about how a user uploads a file, but this is not my case. Can anyone help?
import os
from flask import Flask, request, jsonify
import urllib.request as web
app = Flask(__name__)
download_folder=os.path.join(app.instance_path,'downloads')
app.config['DOWNLOAD_FOLDER'] = download_folder
#app.route('/syncaudio', methods=['POST'])
def syncaudio():
files = request.get_json(force=True)
for file_url in files['syncFileUrls']:
local_file = os.path.join(app.config['DOWNLOAD_FOLDER'], file_url.rsplit('/')[-1])
web.urlretrieve(file_url, local_file)
if __name__ == '__main__':
app.run(debug=True)
urlretrieve(file_url,local_file) is the right way of doing it, there is nothing specific to Flask.
I suggest you to try in a Python interpreter to see if that works:
>>> import urllib.request
>>> urllib.request.urlretrieve('http://some/url', '/some/file/location.txt')
If that works, this means your code is never getting executed.
You may want to print the value of files to see if the list is not empty.
I found the solution!
for downloading FROM web TO flask server I do not need to create a folder like this:
download_folder=os.path.join(app.instance_path,'downloads')
app.config['DOWNLOAD_FOLDER'] = download_folder
I just have to create a download folder via:
os.makedirs('downloads')
really simpel :)

Right click, "save image as" is not working for dynamically generated PIL image (Flask)

I have some experience with Python but none with Flask or any web development. I tried deploying my first app on PythonAnywhere using Flask. It's a very simple script and the "desktop" version works perfectly well. On the site, the image is being generated and saved to file in a static folder ('/static/').
I want the script to show the user the picture automatically once it is generated. The most important thing is that the user must be able to save it. However, when I try:
return redirect("http://www.example.com/static/image.png")
the image is being displayed properly and can be saved properly using "Save Page as". But when I right click the image and click "save image as", the file it writes is corrupted. It's not even the image (the file size is much larger). The filename is wrong too. Instead of "image.png" it saves it as "enc_text.png" ("enc_text" is the name of the function in my script.
How can I get "save image" to work?
(I don't want a solution to do with embedding the image on a blank HTML page (e.g., img src="...")
Any help is much appreciated.
EDIT
#app.route('/enc_text', methods=['POST'])
def enc_text():
text = request.form['text']
text = unidecode.unidecode(text)
filepath = os.path.join(app.config['UPLOAD_FOLDER'],'steg','enc')
filename = 'image.txt'
targetname = filename.rsplit('.', 1)[0] + '.png'
target = os.path.join(app.config['UPLOAD_FOLDER'],'steg','enc',targetname)
steg.encode(text, target) #Reads text and returns PNG file
return redirect("http://www.mysite.com/static/image.png")
(Note: steg.encode is a function I wrote)
EDIT
It seems to be only a problem with Firefox (23.0.1). The problem persists even when I restart Firefox in Safe Mode. I tried it on IE, Chrome and Safari and both "Save page" and "Save image" works fine. That's weird. I opened the "enc_text.png" using notepad and it contains the following HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
Because you're redirecting the enc_text URL to an static image - It can be weird for browser -, the better solution for your return line is:
return '<img src="http://www.mysite.com/static/image.png">'
This way you're serving a page that contains an image and it will be saved correctly. Of course the best solution is to have a full HTML page not just an img tag. So you can replace this return line with a return line of rendered HTML template.

Categories

Resources