我已经在Google colab中使用bokeh创建了一个图形,并希望将其嵌入PDF(作为jinja2模板的一部分)。 Jinja2 html输出显示为pdf,但散景图除外。
当我尝试仅转换散景图时,我只会得到空白页:
from weasyprint import HTML
HTML('bokeh-test.html').write_pdf("report3.pdf")
有没有一种方法可以使Weasyprint与Bokeh的html输出配合使用? 我使用的是google-colab,因此我可以安装的内容受到限制。 我使用https://www.sejda.com/html-to-pdf将html转换为pdf,并显示了图形。
散景图html输出:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Bokeh Plot</title>
<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.css" type="text/css" />
<script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-0.13.0.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
</head>
<body>
<div class="bk-root" id="ef113f06-aa36-4375-be30-7e11ddafa77a"></div>
<script type="application/json" id="4ee5c914-fee4-4720-8c61-acaf6a1f5e0b">
{"57a9d4e3-e61b-42d1-9e38-2d3437421187":{"roots":{"references":[{"attributes":{"bottom_units":"screen","fill_alpha":{"value":0.5},"fill_color":{"value":"lightgrey"},"left_units":"screen","level":"overlay","line_alpha":{"value":1.0},"line_color":{"value":"black"},"line_dash":[4,4],"line_width":{"value":2},"plot":null,"render_mode":"css","right_units":"screen","top_units":"screen"},"id":"517c2134-ed27-444c-915e-3ffb477d5f1c","type":"BoxAnnotation"},{"attributes":{"data_source":{"id":"5431ac6b-2caf-4a54-b371-e853e6bd7a1f","type":"ColumnDataSource"},"glyph":{"id":"7d8c79a4-27fd-4fee-ba41-e68b7308b250","type":"VBar"},"hover_glyph":null,"muted_glyph":null,"nonselection_glyph":{"id":"04df65f3-0a25-4a1f-b75b-70d459e720eb","type":"VBar"},"selection_glyph":null,"view":{"id":"f7508f8e-52c6-4a67-864a-e1d20de6373d","type":"CDSView"}},"id":"178d9ac7-fe8f-4c12-ae2b-27c0630e9bfe","type":"GlyphRenderer"},{"attributes":{"overlay":{"id":"517c2134-ed27-444c-915e-3ffb477d5f1c","type":"BoxAnnotation"}},"id":"59417e60-4616-4ceb-8cb8-0a2e8215b228","type":"BoxZoomTool"},{"attributes":{},"id":"ab2cfea9-982a-470d-a222-7b2c43be98f3","type":"CategoricalScale"},{"attributes":{"callback":null},"id":"41aa3083-6fba-495e-b0e0-5201619117c8","type":"DataRange1d"},{"attributes":{"plot":{"id":"68a2a747-e658-4f80-bc64-925e7307eafe","subtype":"Figure","type":"Plot"},"ticker":{"id":"935d6bf7-1198-4252-9ed2-6e479bd5ef62","type":"CategoricalTicker"}},"id":"b8e5a61d-76cf-4647-87d0-a6bdfc08e31c","type":"Grid"},{"attributes":{},"id":"6941c212-764a-4b31-8412-11e0b3262395","type":"CategoricalTickFormatter"},{"attributes":{},"id":"9e3d4322-4d16-4912-b0ad-79611d031253","type":"BasicTickFormatter"},{"attributes":{"callback":null,"factors":["2017-39","2017-40","2017-41","2017-42","2017-43","2017-44","2017-45","2017-46","2017-47","2017-48","2017-49","2017-50","2017-51","2017-52","2018-01","2018-02","2018-03","2018-04","2018-05","2018-06","2018-07","2018-08","2018-09","2018-10","2018-11","2018-12","2018-13","2018-14","2018-15","2018-16","2018-17","2018-18","2018-19","2018-20","2018-21","2018-22"]},"id":"d8e79791-ca6d-4f6c-ae30-d06e808dad55","type":"FactorRange"},{"attributes":{"fill_alpha":{"value":0.1},"fill_color":{"value":"#1f77b4"},"line_alpha":{"value":0.1},"line_color":{"value":"#1f77b4"},"top":{"field":"duration"},"width":{"value":0.8},"x":{"field":"index"}},"id":"04df65f3-0a25-4a1f-b75b-70d459e720eb","type":"VBar"},{"attributes":{"plot":null,"text":"Hours driven each week","text_font_size":{"value":"18pt"}},"id":"47d89887-fdc8-491c-af53-50acfc4d7385","type":"Title"},{"attributes":{"axis_label":"Hours","formatter":{"id":"9e3d4322-4d16-4912-b0ad-79611d031253","type":"BasicTickFormatter"},"plot":{"id":"68a2a747-e658-4f80-bc64-925e7307eafe","subtype":"Figure","type":"Plot"},"ticker":{"id":"0b715b2c-d93a-4787-a350-b9d270f0bd01","type":"BasicTicker"}},"id":"e70ed347-e9ca-4595-8959-9611cc88c2a3","type":"LinearAxis"},{"attributes":{"callback":null,"data":{"duration":{"__ndarray__":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAzczMzMzMF0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERERERERAJA3t3d3d3dKUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","dtype":"float64","shape":[36]},"index":["2017-39","2017-40","2017-41","2017-42","2017-43","2017-44","2017-45","2017-46","2017-47","2017-48","2017-49","2017-50","2017-51","2017-52","2018-01","2018-02","2018-03","2018-04","2018-05","2018-06","2018-07","2018-08","2018-09","2018-10","2018-11","2018-12","2018-13","2018-14","2018-15","2018-16","2018-17","2018-18","2018-19","2018-20","2018-21","2018-22"]},"selected":{"id":"a5b6187f-ffd1-43eb-92ca-2ed60bddcce5","type":"Selection"},"selection_policy":{"id":"c11dc4a7-7148-407e-90ac-86c53b64e997","type":"UnionRenderers"}},"id":"5431ac6b-2caf-4a54-b371-e853e6bd7a1f","type":"ColumnDataSource"},{"attributes":{},"id":"01c941d4-a31f-403a-9ba9-fc35388abc24","type":"WheelZoomTool"},{"attributes":{"below":[{"id":"991d44e6-2849-4b16-8b3c-67ce9f05b42c","type":"CategoricalAxis"}],"left":[{"id":"e70ed347-e9ca-4595-8959-9611cc88c2a3","type":"LinearAxis"}],"plot_height":400,"plot_width":1000,"renderers":[{"id":"991d44e6-2849-4b16-8b3c-67ce9f05b42c","type":"CategoricalAxis"},{"id":"b8e5a61d-76cf-4647-87d0-a6bdfc08e31c","type":"Grid"},{"id":"e70ed347-e9ca-4595-8959-9611cc88c2a3","type":"LinearAxis"},{"id":"00ddf32c-f48f-4cc5-ae77-64c914eb7fc5","type":"Grid"},{"id":"517c2134-ed27-444c-915e-3ffb477d5f1c","type":"BoxAnnotation"},{"id":"178d9ac7-fe8f-4c12-ae2b-27c0630e9bfe","type":"GlyphRenderer"}],"title":{"id":"47d89887-fdc8-491c-af53-50acfc4d7385","type":"Title"},"toolbar":{"id":"d7d3a411-b28c-4f3f-9c99-144f1151b83f","type":"Toolbar"},"x_range":{"id":"d8e79791-ca6d-4f6c-ae30-d06e808dad55","type":"FactorRange"},"x_scale":{"id":"ab2cfea9-982a-470d-a222-7b2c43be98f3","type":"CategoricalScale"},"y_range":{"id":"41aa3083-6fba-495e-b0e0-5201619117c8","type":"DataRange1d"},"y_scale":{"id":"40cda5bc-3a56-4c31-8fd8-14fc3c5e1880","type":"LinearScale"}},"id":"68a2a747-e658-4f80-bc64-925e7307eafe","subtype":"Figure","type":"Plot"},{"attributes":{},"id":"5de2c2ac-a957-4b3e-a38a-af1af0153811","type":"HelpTool"},{"attributes":{},"id":"a5b6187f-ffd1-43eb-92ca-2ed60bddcce5","type":"Selection"},{"attributes":{},"id":"c11dc4a7-7148-407e-90ac-86c53b64e997","type":"UnionRenderers"},{"attributes":{},"id":"935d6bf7-1198-4252-9ed2-6e479bd5ef62","type":"CategoricalTicker"},{"attributes":{"fill_color":{"value":"#1f77b4"},"line_color":{"value":"#1f77b4"},"top":{"field":"duration"},"width":{"value":0.8},"x":{"field":"index"}},"id":"7d8c79a4-27fd-4fee-ba41-e68b7308b250","type":"VBar"},{"attributes":{"source":{"id":"5431ac6b-2caf-4a54-b371-e853e6bd7a1f","type":"ColumnDataSource"}},"id":"f7508f8e-52c6-4a67-864a-e1d20de6373d","type":"CDSView"},{"attributes":{"axis_label":"Date","formatter":{"id":"6941c212-764a-4b31-8412-11e0b3262395","type":"CategoricalTickFormatter"},"major_label_orientation":45,"plot":{"id":"68a2a747-e658-4f80-bc64-925e7307eafe","subtype":"Figure","type":"Plot"},"ticker":{"id":"935d6bf7-1198-4252-9ed2-6e479bd5ef62","type":"CategoricalTicker"}},"id":"991d44e6-2849-4b16-8b3c-67ce9f05b42c","type":"CategoricalAxis"},{"attributes":{},"id":"e63ed713-5f57-4972-b2f1-b47e836113be","type":"PanTool"},{"attributes":{},"id":"40cda5bc-3a56-4c31-8fd8-14fc3c5e1880","type":"LinearScale"},{"attributes":{"active_drag":"auto","active_inspect":"auto","active_multi":null,"active_scroll":"auto","active_tap":"auto","tools":[{"id":"e63ed713-5f57-4972-b2f1-b47e836113be","type":"PanTool"},{"id":"01c941d4-a31f-403a-9ba9-fc35388abc24","type":"WheelZoomTool"},{"id":"59417e60-4616-4ceb-8cb8-0a2e8215b228","type":"BoxZoomTool"},{"id":"cd7c9a7f-62e7-407d-85e8-c80859cce5f0","type":"SaveTool"},{"id":"53d31065-2af5-417a-9aa5-b83dcc1ba3d3","type":"ResetTool"},{"id":"5de2c2ac-a957-4b3e-a38a-af1af0153811","type":"HelpTool"}]},"id":"d7d3a411-b28c-4f3f-9c99-144f1151b83f","type":"Toolbar"},{"attributes":{},"id":"cd7c9a7f-62e7-407d-85e8-c80859cce5f0","type":"SaveTool"},{"attributes":{"dimension":1,"plot":{"id":"68a2a747-e658-4f80-bc64-925e7307eafe","subtype":"Figure","type":"Plot"},"ticker":{"id":"0b715b2c-d93a-4787-a350-b9d270f0bd01","type":"BasicTicker"}},"id":"00ddf32c-f48f-4cc5-ae77-64c914eb7fc5","type":"Grid"},{"attributes":{},"id":"53d31065-2af5-417a-9aa5-b83dcc1ba3d3","type":"ResetTool"},{"attributes":{},"id":"0b715b2c-d93a-4787-a350-b9d270f0bd01","type":"BasicTicker"}],"root_ids":["68a2a747-e658-4f80-bc64-925e7307eafe"]},"title":"Bokeh Application","version":"0.13.0"}}
</script>
<script type="text/javascript">
(function() {
var fn = function() {
Bokeh.safely(function() {
(function(root) {
function embed_document(root) {
var docs_json = document.getElementById('4ee5c914-fee4-4720-8c61-acaf6a1f5e0b').textContent;
var render_items = [{"docid":"57a9d4e3-e61b-42d1-9e38-2d3437421187","roots":{"68a2a747-e658-4f80-bc64-925e7307eafe":"ef113f06-aa36-4375-be30-7e11ddafa77a"}}];
root.Bokeh.embed.embed_items(docs_json, render_items);
}
if (root.Bokeh !== undefined) {
embed_document(root);
} else {
var attempts = 0;
var timer = setInterval(function(root) {
if (root.Bokeh !== undefined) {
embed_document(root);
clearInterval(timer);
}
attempts++;
if (attempts > 100) {
console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing")
clearInterval(timer);
}
}, 10, root)
}
})(window);
});
};
if (document.readyState != "loading") fn();
else document.addEventListener("DOMContentLoaded", fn);
})();
</script>
</body>
</html>
答案 0 :(得分:0)
散景图实际上是JavaScript应用程序,可以渲染到HTML画布,并响应和处理UI事件。 PDF无法直接执行任何操作,因为它们无法运行JavaScript代码。我非常怀疑任何程序化HTML到PDF工具都可以对它做任何有用的事情。 AFAIK最好的选择是使用Bokeh's export API显式导出PNG:
from bokeh.io import export_png
export_png(plot, filename="plot.png")
还有一种在浏览器中生成SVG的模式,并且HTML-to-PDF工具可能能够转换这种输出:
plot.output_backend = "svg"
但是从Bokeh 0.13开始,Bokeh中的SVG输出存在一些限制和已知问题。您只需要尝试一下,即可看到它足以满足您的特定用例。
答案 1 :(得分:0)
我已经使用这个(非常粗略的)代码进行 HTML 到 PDF 的转换,即使在相当复杂的模板化布局中嵌入散景图,它通常也能工作。有两个引擎:
pip install requests
除外),但需要一个免费的令牌帐户,并且文档大小的上限似乎为 25 MB。< /li>
import os
import subprocess
import tempfile as tmp
import requests
def html_to_pdf(document, compress=False, engine='chromium',
url='https://api.sejda.com/v2/html-pdf', token=None,
webservice_options=None):
"""
Convert html to pdf using headless chromium or sejda webservice.
Optionally compress with ghostscript (also needs to be installed) if using chromium. For webservice_options,
see https://www.sejda.com/developers#html-pdf-api for docs.
Parameters
----------
document: string
Full html document to render.
Returns
-------
pdf: bytes
The rendered pdf document as a bytestring.
Notes
-----
Unfortunately requires disk (temporary files) when using chromium engine.
"""
if webservice_options is None:
webservice_options = {}
if engine == 'sejda':
payload = {'htmlCode': document, **webservice_options}
response = requests.post(url, json=payload, headers={'Authorization': f'Token: {token}'})
response.raise_for_status()
static_bytes = response.content
elif engine == 'chromium':
with tmp.NamedTemporaryFile('w', suffix='.html', encoding='utf-8') as interactive_file:
interactive_file.write(document)
interactive_filename = interactive_file.name
static_filename = interactive_filename.replace('.html', '.pdf')
command = 'chromium --headless --disable-gpu --audio-output-channels=0 --print-to-pdf={pdf} {html}'.format(pdf=static_filename, html=interactive_filename)
subprocess.check_call(command.split())
if compress:
# needs svg to run
small_static_filename = static_filename.replace('.pdf', '_small.pdf')
command = 'gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dNOPAUSE -dBATCH -dQUIET -sOutputFile={pdfout} {pdfin}'.format(pdfin=static_filename, pdfout=small_static_filename)
subprocess.check_call(command.split())
with open(small_static_filename if compress else static_filename, 'rb') as static_file:
static_bytes = static_file.read()
os.remove(static_filename)
if compress:
os.remove(small_static_filename)
else:
raise ValueError(f'invalid engine: {engine}')
return static_bytes