How to open download file dialog with QWebEngineView? - python
I'm building a pyqt5 desktop interface where I'm using QWebEngineView to show a html file where I show a Leaflet map. This is working fine.
The next step is to export all features the user added to the map. When I click on "Export Features" on the map nothing happen, but when I open the same html file on my Chromium web browser, the "Export feature" opens the download dialog fine.
PyQt5 script:
self.MainWindow.webMapViewer = QtWebEngineWidgets.QWebEngineView()
self.MainWindow.webPageLayout.addWidget(self.MainWindow.webMapViewer)
self.html_path = os.path.split(os.path.abspath(__file__))[0] + r'/html/test.html'
self.MainWindow.webMapViewer.load(QtCore.QUrl().fromLocalFile(self.html_path))
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<title>FazMaraneyRGB_transparent_mosaic_group1</title>
<!-- Leaflet -->
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>
<!-- Leaflet.draw -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.css" />
<script src="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.js"></script>
<!-- Leaflet Ajax -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>
<!-- Leaflet Measument -->
<link rel="stylesheet" href="http://ljagis.github.io/leaflet-measure/leaflet-measure.css" />
<script src="http://ljagis.github.io/leaflet-measure/leaflet-measure.min.js"></script>
<style>
body { margin:0; padding:0; }
body, table, tr, td, th, div, h1, h2, input { font-family: "Calibri", "Trebuchet MS", "Ubuntu", Serif; font-size: 11pt; }
#map { position:absolute; top:0; bottom:0; width:100%; } /* full size */
.ctl {
padding: 2px 10px 2px 10px;
background: white;
background: rgba(255,255,255,0.9);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
text-align: right;
}
.title {
font-size: 18pt;
font-weight: bold;
}
.src {
font-size: 10pt;
}
#delete, #export {
position: absolute;
top:100px;
right:10px;
z-index:100;
background:white;
color:black;
padding:6px;
border-radius:4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size:12px;
text-decoration:none;
}
#export {
top:130px;
}
</style>
</head>
<body>
<div id='map'></div>
<div id='delete'>Delete Features</div>
<a href='#' id='export'>Export Features</a>
<script>
/* **** Leaflet **** */
// Base layers
// .. OpenStreetMap
var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap contributors'});
// .. White background
var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX///+nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");
// Overlay layers (TMS)
var lyr1 = L.tileLayer('./tiles/{z}/{x}/{y}.png', {tms: true, maxZoom: 22, opacity: 0.9, attribution: ""});
// Map
var map = L.map('map', {
measureControl: true,
center: [-18.3604868606589, -52.694255477616245],
zoom: 22,
minZoom: 0,
maxZoom: 22,
layers: [osm]
});
lyr1.addTo(map);
//Geojson Layers
var basemaps = {"OpenStreetMap": osm, "Without background": white}
var overlaymaps = {"Layer 1": lyr1}
// Title
var title = L.control();
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl title');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = "FazMaraneyRGB_transparent_mosaic_group1";
};
title.addTo(map);
// Note
var src = 'Generated by Hawkit';
var title = L.control({position: 'bottomleft'});
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl src');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = src;
};
title.addTo(map);
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
}
}).addTo(map);
map.on('draw:created', function(e) {
// Each time a feaute is created, it's added to the over arching feature group
featureGroup.addLayer(e.layer);
});
// on click, clear all layers
document.getElementById('delete').onclick = function(e) {
featureGroup.clearLayers();
}
document.getElementById('export').onclick = function(e) {
// Extract GeoJson from featureGroup
var data = featureGroup.toGeoJSON();
// Stringify the GeoJson
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download','data.geojson');
}
// Add base layers
L.control.layers(basemaps, overlaymaps, {collapsed: true}).addTo(map);
// Fit to overlay bounds (SW and NE points with (lat, lon))
map.fitBounds([[-18.36827062251916, -52.6871074784942], [-18.35270287637126, -52.7014028427423]]);
</script>
</body>
</html>
That popup window is generated by the browser, in the case of QWebEngine we must create it. To start, the signal indicating the download must be detected, and this signal is downloadRequested from the QWebEngineProfile. That signal sends us a QWebEngineDownloadItem object that handles the download, in it we create a dialog window with the help of QFileDialog. For this case we will create a custom QWebEnginePage as shown below:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<title>FazMaraneyRGB_transparent_mosaic_group1</title>
<!-- Leaflet -->
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>
<!-- Leaflet.draw -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.css" />
<script src="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.js"></script>
<!-- Leaflet Ajax -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>
<!-- Leaflet Measument -->
<link rel="stylesheet" href="http://ljagis.github.io/leaflet-measure/leaflet-measure.css" />
<script src="http://ljagis.github.io/leaflet-measure/leaflet-measure.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
body,
table,
tr,
td,
th,
div,
h1,
h2,
input {
font-family: "Calibri", "Trebuchet MS", "Ubuntu", Serif;
font-size: 11pt;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
/* full size */
.ctl {
padding: 2px 10px 2px 10px;
background: white;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
border-radius: 5px;
text-align: right;
}
.title {
font-size: 18pt;
font-weight: bold;
}
.src {
font-size: 10pt;
}
#delete,
#export {
position: absolute;
top: 100px;
right: 10px;
z-index: 100;
background: white;
color: black;
padding: 6px;
border-radius: 4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size: 12px;
text-decoration: none;
}
#export {
top: 130px;
}
</style>
</head>
<body>
<div id='map'></div>
<div id='delete'>Delete Features</div>
<a href='#' id='export'>Export Features</a>
<script>
/* **** Leaflet **** */
// Base layers
// .. OpenStreetMap
var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
});
// .. White background
var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX///+nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");
// Overlay layers (TMS)
var lyr1 = L.tileLayer('./tiles/{z}/{x}/{y}.png', {
tms: true,
maxZoom: 22,
opacity: 0.9,
attribution: ""
});
// Map
var map = L.map('map', {
measureControl: true,
center: [-18.3604868606589, -52.694255477616245],
zoom: 22,
minZoom: 0,
maxZoom: 22,
layers: [osm]
});
lyr1.addTo(map);
//Geojson Layers
var basemaps = {
"OpenStreetMap": osm,
"Without background": white
}
var overlaymaps = {
"Layer 1": lyr1
}
// Title
var title = L.control();
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl title');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = "FazMaraneyRGB_transparent_mosaic_group1";
};
title.addTo(map);
// Note
var src = 'Generated by Hawkit';
var title = L.control({
position: 'bottomleft'
});
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl src');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = src;
};
title.addTo(map);
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
}
}).addTo(map);
map.on('draw:created', function(e) {
// Each time a feaute is created, it's added to the over arching feature group
featureGroup.addLayer(e.layer);
});
// on click, clear all layers
document.getElementById('delete').onclick = function(e) {
featureGroup.clearLayers();
}
document.getElementById('export').onclick = function(e) {
// Extract GeoJson from featureGroup
var data = featureGroup.toGeoJSON();
// Stringify the GeoJson
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download', 'data.geojson');
}
// Add base layers
L.control.layers(basemaps, overlaymaps, {
collapsed: true
}).addTo(map);
// Fit to overlay bounds (SW and NE points with (lat, lon))
map.fitBounds([
[-18.36827062251916, -52.6871074784942],
[-18.35270287637126, -52.7014028427423]
]);
</script>
</body>
</html>
main.py
from PyQt5 import QtWebEngineWidgets, QtWidgets, QtCore
class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
def __init__(self, *args, **kwargs):
QtWebEngineWidgets.QWebEnginePage.__init__(self, *args, **kwargs)
self.profile().downloadRequested.connect(self.on_downloadRequested)
#QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def on_downloadRequested(self, download):
old_path = download.path()
suffix = QtCore.QFileInfo(old_path).suffix()
path, _ = QtWidgets.QFileDialog.getSaveFileName(self.view(), "Save File", old_path, "*."+suffix)
if path:
download.setPath(path)
download.accept()
if __name__ == '__main__':
import sys
sys.argv.append("--remote-debugging-port=8000")
sys.argv.append("--disable-web-security")
app = QtWidgets.QApplication(sys.argv)
view = QtWebEngineWidgets.QWebEngineView()
page = WebEnginePage(view)
view.setPage(page)
path = QtCore.QDir.current().filePath("index.html")
view.load(QtCore.QUrl.fromLocalFile(path))
view.show()
sys.exit(app.exec_())
In your case:
self.MainWindow.webMapViewer = QtWebEngineWidgets.QWebEngineView()
self.MainWindow.webPageLayout.addWidget(self.MainWindow.webMapViewer)
page = WebEnginePage(self.MainWindow.webMapViewer) # create page
self.MainWindow.webMapViewer.setPage(page) # set page
self.html_path = os.path.split(os.path.abspath(__file__))[0] + r'/html/test.html'
self.MainWindow.webMapViewer.load(QtCore.QUrl().fromLocalFile(self.html_path))
Plus:
If you want to set a defined route use the following:
#QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def on_downloadRequested(self, download):
directory = "/path/of/directory"
filename = QtCore.QFileInfo(download.path()).fileName()
download.setPath(QtCore.QDir(directory).filePath(filename))
download.accept()
Related
wtf form change style on file input
I am using wtf forms with flask to create a form. I have a file input, which is styled so the default button is not shown. How can I dynamically change the style after a file is loaded? Here is my code: html: <div class="file-upload my-form"> <img src="https://i.stack.imgur.com/dy62M.png" /> {{ wtf.form_field(form.file)}} </div> css: .my-form input { margin: 0; padding: 0; height: 100%; opacity: 0; } .file-upload { margin: 40px auto; border: 1px solid #149174; border-radius: 100px; overflow: hidden; position: relative; } .file-upload input { position: absolute; width: 300px; height: 600px; left: 10px; top: 20px; } .file-upload img { height: 170px; width: 170px; margin: 60px; } how can I change the style on input? or show a label with the file name in the worst case...
In the end I dealt with it like this: function fileCheck() { var checked = document.getElementById('file') if (checked.value.length > 0) { var file_name = checked.value.split(/(\\|\/)/g).pop(); var re = /(?:\.([^.]+))?$/; var ext = re.exec(file_name)[1]; if (ext != 'pdf') { document.getElementById('file-container').style.backgroundColor = '#ca2c2c'; document.getElementById("file-desc").innerHTML = "File Loaded with illegal extension: " + ext + " !!"; document.getElementById("final-submit").disabled = true; } else { document.getElementById('file-container').style.backgroundColor = '#149174'; document.getElementById("file-desc").innerHTML = "File Loaded: " + checked.value.split(/(\\|\/)/g).pop(); document.getElementById("final-submit").disabled = false; } } else { document.getElementById("final-submit").disabled = true; } } And my html looks like this: <div class="file-upload my-form" id="file-container"> <img src="static/images/upload_icon.png" /> {{ form.file(oninput="fileCheck()") }} </div>
How can I access view random created folder in flask?
I have implement simple app in flask. I can get data and process it also but how can I get random created folder. In this app, I tried to input some data to text area. When export deck button clicked then the data post to flask. I can get data and generate deck also but unable send generated deck file or redirect to the random folder. I get the following error. raise TypeError( TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.) So, How can I implement this? Redirect to new random generated folder or send the generated file as download link. Thanks app.py from flask import Flask, render_template, request, redirect, url_for, flash, send_file, send_from_directory import image_occ_deck_export import random, os app = Flask(__name__) app.config["CACHE_TYPE"] = "null" #app.route("/", methods=["GET","POST"]) def home(): if request.method == "POST": print(request.form['notes']) notes = request.form['notes'] # random folder data = request.form['notes'] print(data) random_f = random.randrange(1 << 30, 1 << 31) create_random_folder(random_f, data) else: return render_template("index.html") def create_random_folder(random_f, data): directory = str(random_f) parent_dir = "static/uploads/" path = os.path.join(parent_dir, directory) if not os.path.exists(path): os.mkdir(path) random_file = directory + ".txt" random_deck_name = directory + ".apkg" file_loc = path + "/" + random_file deck_loc = path + "/" + random_deck_name with open(file_loc, 'w') as f: f.write(str(data)) image_occ_deck_export.exportDeck(file_loc, deck_loc) return redirect(url_for('uploaded', path=path)) #app.route('/uploaded/<path>', methods=['GET']) def uploaded(): return render_template("upload.html") if __name__ == "__main__": app.run(debug=False) index.js function export() { var textToExport = document.getElementById("noteData").value; var formData = new FormData(); formData.append("notes", textToExport); var request = new XMLHttpRequest(); request.open("POST", "/"); request.send(formData); } index.html <html> <textarea id="noteData"></textarea> <button onclick="export()">Export Deck</button> </html> sample input cordova-img-occ-note-1602529000819 <img src='art-1851483_640.jpg'></img> <img src='cordova-img-occ-ques-1602529000819.svg'></img> <img src='cordova-img-occ-ans-1602529000819.svg'></img> <img src='cordova-img-occ-orig-1602529000819.svg'></img> cordova-img-occ-note-1602529001248 <img src='art-1851483_640.jpg'></img> <img src='cordova-img-occ-ques-1602529001248.svg'></img> <img src='cordova-img-occ-ans-1602529001248.svg'></img> <img src='cordova-img-occ-orig-1602529000819.svg'></img> cordova-img-occ-note-1602529001673 <img src='art-1851483_640.jpg'></img> <img src='cordova-img-occ-ques-1602529001673.svg'></img> <img src='cordova-img-occ-ans-1602529001673.svg'></img> <img src='cordova-img-occ-orig-1602529000819.svg'></img> image_occ_deck_export.py The file use to generate anki deck from txt import random import genanki import csv import traceback anki_deck_title = "learn" anki_model_name = "image occ" model_id = random.randrange(1 << 30, 1 << 31) def exportDeck(data_filename, deck_filename): try: # front side front = """ {{#Image}} <div id="io-header">{{Header}}</div> <div id="io-wrapper"> <div id="io-overlay">{{Question Mask}}</div> <div id="io-original">{{Image}}</div> </div> <div id="io-footer">{{Footer}}</div> <script> // Prevent original image from loading before mask aFade = 50, qFade = 0; var mask = document.querySelector('#io-overlay>img'); function loaded() { var original = document.querySelector('#io-original'); original.style.visibility = "visible"; } if (mask === null || mask.complete) { loaded(); } else { mask.addEventListener('load', loaded); } </script> {{/Image}} """ style = """ /* GENERAL CARD STYLE */ .card { font-family: "Helvetica LT Std", Helvetica, Arial, Sans; font-size: 150%; text-align: center; color: black; background-color: white; } /* OCCLUSION CSS START - don't edit this */ #io-overlay { position:absolute; top:0; width:100%; z-index:3 } #io-original { position:relative; top:0; width:100%; z-index:2; visibility: hidden; } #io-wrapper { position:relative; width: 100%; } /* OCCLUSION CSS END */ /* OTHER STYLES */ #io-header{ font-size: 1.1em; margin-bottom: 0.2em; } #io-footer{ max-width: 80%; margin-left: auto; margin-right: auto; margin-top: 0.8em; font-style: italic; } #io-extra-wrapper{ /* the wrapper is needed to center the left-aligned blocks below it */ width: 80%; margin-left: auto; margin-right: auto; margin-top: 0.5em; } #io-extra{ text-align:center; display: inline-block; } .io-extra-entry{ margin-top: 0.8em; font-size: 0.9em; text-align:left; } .io-field-descr{ margin-bottom: 0.2em; font-weight: bold; font-size: 1em; } #io-revl-btn { font-size: 0.5em; } /* ADJUSTMENTS FOR MOBILE DEVICES */ .mobile .card, .mobile #content { font-size: 120%; margin: 0; } .mobile #io-extra-wrapper { width: 95%; } .mobile #io-revl-btn { font-size: 0.8em; } """ # back side back = """ {{#Image}} <div id="io-header">{{Header}}</div> <div id="io-wrapper"> <div id="io-overlay">{{Answer Mask}}</div> <div id="io-original">{{Image}}</div> </div> {{#Footer}}<div id="io-footer">{{Footer}}</div>{{/Footer}} <button id="io-revl-btn" onclick="toggle();">Toggle Masks</button> <div id="io-extra-wrapper"> <div id="io-extra"> {{#Remarks}} <div class="io-extra-entry"> <div class="io-field-descr">Remarks</div>{{Remarks}} </div> {{/Remarks}} {{#Sources}} <div class="io-extra-entry"> <div class="io-field-descr">Sources</div>{{Sources}} </div> {{/Sources}} {{#Extra 1}} <div class="io-extra-entry"> <div class="io-field-descr">Extra 1</div>{{Extra 1}} </div> {{/Extra 1}} {{#Extra 2}} <div class="io-extra-entry"> <div class="io-field-descr">Extra 2</div>{{Extra 2}} </div> {{/Extra 2}} </div> </div> <script> // Toggle answer mask on clicking the image var toggle = function() { var amask = document.getElementById('io-overlay'); if (amask.style.display === 'block' || amask.style.display === '') amask.style.display = 'none'; else amask.style.display = 'block' } // Prevent original image from loading before mask aFade = 50, qFade = 0; var mask = document.querySelector('#io-overlay>img'); function loaded() { var original = document.querySelector('#io-original'); original.style.visibility = "visible"; } if (mask === null || mask.complete) { loaded(); } else { mask.addEventListener('load', loaded); } </script> {{/Image}} """ # print(self.fields) anki_model = genanki.Model( model_id, anki_model_name, fields=[{"name": "id"},{"name": "Header"}, {"name": "Image"}, {"name": "Question Mask"}, {"name": "Footer"}, {"name": "Remarks"}, {"name": "Sources"}, {"name": "Extra 1"}, {"name": "Extra 2"}, {"name": "Answer Mask"}, {"name": "Original"}], templates=[ { "name": "Card 1", "qfmt": front, "afmt": back, }, ], css=style, ) anki_notes = [] with open(data_filename, "r", encoding="utf-8") as csv_file: csv_reader = csv.reader(csv_file, delimiter="\t") for row in csv_reader: flds = [] for i in range(len(row)): flds.append(row[i]) anki_note = genanki.Note( model=anki_model, fields=flds, ) anki_notes.append(anki_note) random.shuffle(anki_notes) anki_deck = genanki.Deck(model_id, anki_deck_title) anki_package = genanki.Package(anki_deck) for anki_note in anki_notes: anki_deck.add_note(anki_note) anki_package.write_to_file(deck_filename) print("Deck generated with {} flashcards".format( len(anki_deck.notes))) except Exception: traceback.print_exc()
create_random_folder() returns a redirect, but when you call it from your home() request handler, you don’t do anything with the returned value and you don’t return a response in that code branch of your home() handler. It seems you intend to return that redirect from your home() handler like so: return create_random_folder(random_f, data) Remember, when you return a value from a function, you’re returning the value to the calling code, not to the browser. If you call a function from a request handler and receive a return value, that doesn’t automatically get sent back to the browser; you need to return it from the request handler.
How to render a 3D object to HTML file by using pythonOCC in Django?
I have a Django application and I'm using pythonOCC package in it. I have to display the 3D .stl, .stp, .igs files in my template. Normally, when I call the render() function, the following outputs appear on my vscode console and since flask app created by pythonocc instead of django starts running in localhost, my index.html is never rendered. However I need to display the files in a Django template. That's why I have extended the X3DomRenderer Class such as below. My custom X3DomRenderer class: class CustomX3DomRenderer(x3dom_renderer.X3DomRenderer): def render_to_string(self): self.generate_html_file(self._axes_plane, self._axes_plane_zoom_factor) return open(self._html_filename, 'r').read() the HTML codes that returned from render_to_string() function: <html lang="en"> <head> <title>pythonOCC 7.4.0 x3dom renderer</title> <meta name='Author' content='Thomas Paviot - tpaviot#gmail.com'> <meta name='Keywords' content='WebGl,pythonOCC'> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="https://x3dom.org/release/x3dom.css"> <script src="https://x3dom.org/release/x3dom.js"></script> <style> body { background: linear-gradient(#ced7de, #808080); margin: 0px; overflow: hidden; } #pythonocc_rocks { padding: 5px; position: absolute; left: 1%; bottom: 2%; height: 38px; width: 280px; border-radius: 5px; border: 2px solid #f7941e; opacity: 0.7; font-family: Arial; background-color: #414042; color: #ffffff; font-size: 14px; opacity: 0.5; } #commands { padding: 5px; position: absolute; right: 1%; top: 2%; height: 65px; width: 180px; border-radius: 5px; border: 2px solid #f7941e; opacity: 0.7; font-family: Arial; background-color: #414042; color: #ffffff; font-size: 14px; opacity: 0.5; } a { color: #f7941e; text-decoration: none; } a:hover { color: #ffffff; } </style> </head> <body> <x3d id="pythonocc-x3d-scene" style="width:100%;border: none" > <Scene> <transform scale="1,1,1"> <transform id="plane_smallaxe_Id" rotation="1 0 0 -1.57079632679"> <inline url="https://rawcdn.githack.com/x3dom/component-editor/master/static/x3d/plane.x3d" mapDEFToID="true" namespaceName="plane"></inline> <inline url="https://rawcdn.githack.com/x3dom/component-editor/master/static/x3d/axesSmall.x3d" mapDEFToID="true" namespaceName="axesSmall"></inline> </transform> <inline url="https://rawcdn.githack.com/x3dom/component-editor/master/static/x3d/axes.x3d" mapDEFToID="true" namespaceName="axes"></inline> </transform> <transform id="glbal_scene_rotation_Id" rotation="1 0 0 -1.57079632679"> <Inline onload="fitCamera()" mapDEFToID="true" url="shp6b8ef6d6e61744489de16a6798cfe998.x3d"></Inline> </transform> </Scene> </x3d> <div id="pythonocc_rocks"> pythonocc-7.4.0 x3dom renderer <br>Check our blog at <a href=http://www.pythonocc.org>http://www.pythonocc.org</a> </div> <div id="commands"> <b>t</b> view/hide shape<br> <b>r</b> reset view<br> <b>a</b> show all<br> <b>u</b> upright<br> </div> <script> var selected_target_color = null; var current_selected_shape = null; var current_mat = null; function fitCamera() { var x3dElem = document.getElementById('pythonocc-x3d-scene'); x3dElem.runtime.fitAll(); } function select(the_shape) // called whenever a shape is clicked { // restore color for previous selected shape if (current_mat) { current_mat.diffuseColor = selected_target_color; } // store the shape for future process current_selected_shape = the_shape; console.log(the_shape); // store color, to be restored later appear = current_selected_shape.getElementsByTagName("Appearance")[0]; mat = appear.getElementsByTagName("Material")[0]; current_mat = mat; console.log(mat); selected_target_color = mat.diffuseColor; mat.diffuseColor = "1, 0.65, 0"; //console.log(the_shape.getElementsByTagName("Appearance"));//.getAttribute('diffuseColor')); } function onDocumentKeyPress(event) { event.preventDefault(); if (event.key=="t") { // t key if (current_selected_shape) { if (current_selected_shape.render == "true") { current_selected_shape.render = "false"; } else { current_selected_shape.render = "true"; } } } } // add events document.addEventListener('keypress', onDocumentKeyPress, false); </script> </body> </html> and here is my view: def occ_viewer(request): shape = read_step_file(os.path.join('C:/Users/imgea/desktop/bgtask/bgtask/ThreeDFile', 'splinecage.stp')) my_renderer = extend_x3dom.CustomX3DomRenderer(path='C:/Users/imgea/desktop/bgtask/bgtask/ThreeDFile') my_renderer.DisplayShape(shape) context = {'viewer': my_renderer.render_to_string()} return render(request, 'success.html', context) and I have added these HTML codes that I got from render_to_string() function to my template file. The viewer's grid has shown but the 3D object hasn't because of the error below. Page not found: http://127.0.0.1:8000/file/occview/shp6b8ef6d6e61744489de16a6798cfe998.x3d The library creates that .x3d file in the same directory with the file that I wanna render to template but I guess the viewer is looking for this .x3d file in the error which I mentioned before even I sent the directory. I couldn't find the cause of this error. Am I missing something? Thank you!!
Django easy_pdf remove margin
To create pdf from html i use easy_pdf with is connected to xhtml2pdf. Small example: in view.py from easy_pdf.rendering import render_to_pdf_response context = dict({'user_company': 'test'}) template_name = "pdf_template.html" return render_to_pdf_response(request, template_name, context) html template: <!DOCTYPE html> {% load static %} <html lang="en"> <head> <style> * { margin: 0 !important; padding: 0 !important; } </style> </head> <body> {{user_company}} <img src="/static/some_image.png" height="1000" width="1000"> </body> </html> Image and all element are placed inside margins: I have try to change and add some margins options inside side_packages/easy_pdf/templates/easy_pdf/base.html : github link <style type="text/css"> #page { // added margin: 0 !important; * { margin: 0 !important; padding: 0 !important; } body { margin: 0 !important; padding: 0 !important; } #frame { margin: 0 !important; } // changed size: {{ pagesize|default:"A4" }}; margin-left: 0; margin-right: 0; margin-top: 0; margin-bottom: 0; #frame header { -pdf-frame-content: page-header; margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; } #frame footer { -pdf-frame-content: page-footer; bottom: 0; margin-left: 0; margin-right: 0; height: 0; } } </style> But without effect Question Where to add or change css options to remove the margins from pdf?
I have found the solution: you need to add below into you template style: <style> #page { * { margin: 0; padding: 0; } } </style>
xhtml2pdf doesn't embed Helvetica
I'm creating a PDF with xhtml2pdf using Django. I'm sending that PDF to print, but then they say that some Fonts are not embed. I have a Helvetica font, but I didn't use Helvetica in the PDFs. Here you have a Screen Shot of the properties of the PDF As you see, Guilles'ComicFont and TF2Secondary are correclty embbeded, but not with Helvetica. Here you have my view that generates the PDF: def generate_pdf(request, book, order): try: book = Book.objects.get(pk=int(book)) order = Order.objects.get(identificador=order, cuento=book) except ObjectDoesNotExist: raise Http404 data = {} data = ast.literal_eval(order.datos_variables) data['order'] = order template = get_template(order.book.plantilla_html) html = template.render(Context(data)) url = '/Users/vergere/dev/media/pdfs/%s.pdf' % order.identificador fichero = open(url, "w+b") pisaStatus = pisa.CreatePDF(html.encode('utf-8'), dest=fichero, encoding='utf-8') fichero.seek(0) fichero.close() order.url_pdf = '%s.pdf' % order.identificador order.contador_libro = numero order.codigo_libro = country_code order.save() return HttpResponseRedirect(reverse('order_single', kwargs={'pedido': order.pk})) And here my HTML: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Background</title> <style> #page { background-image: url("cuento1/img/portada.jpg"); size: 213mm 216mm; } #font-face { font-family: Helvetica; src: url(pdf_generator/helvetica.ttf); } #font-face { font-family: 'Gilles'; src: url(pdf_generator/cuento1/gilles/gilles.ttf); font-weight: normal; font-style: normal; } #font-face { font-family: 'Secondary'; src: url(pdf_generator/cuento1/tf2_secondary/tf2_secondary.ttf); font-weight: normal; font-style: normal; } * { box-shadow:none !important; margin: 0; padding: 0; text-shadow: none !important; font-family: 'Gilles'; } body{ font-family: 'Gilles'; } p { color: #464439; display: block; font-weight: normal; position: absolute; } .page-1 p, .page-2 p{ font-family: 'Secondary'; font-size: 40px; line-height: 1.3em; position: absolute; } </style> </head> <body> <pdf:nextpage name="p1" /> <pdf:nextpage name="p2" /> <div class="page-6 page-dedicatoria"> {{order.dedicatoria}} <br /> {{order.de}} </div> <p> </p> </body> </html> Anyone knows why is using Helvetica? Or there is any way to embed Helvetica? I'm trying with "#font-face" but It's not working.
Helvetica is one of the standard fonts that every PDF renderer has to have available. Therefore it doesn't have to be embedded. A possible solution would be to use another sans-serif font instead of Helvetica. On windows e.g. Arial. On OSX e.g. Helvetica Neue or Avenir. They look a lot like Helvetica, but are not standard PDF fonts. In your stylesheet, specify a new font for all elements; * { font-family: Avenir; }