Z.S.K.'s Records

highcharts中的svg在服务器端自动转化成图片

Highcharts 是一个用纯JavaScript编写的一个图表库, 能够很简单便捷的在web网站或是web应用程序添加有交互性的图表,并且免费提供给个人学习、个人网站和非商业用途使用。HighCharts支持的图表类型有曲线图、区域图、柱状图、饼状图、散状点图和综合图表等各种图形,效果非常美观,被广泛的应用于图表生成,类似图表库还有百度的Echart等.

highcharts

在html上生成highcharts图表是很容易的,我们只需要根据需求选择图形,然后定义样式,最后把数据塞进highcharts的series中即可渲染,而且highcharts还提供了页面(客户端)导出功能,可以很方便的导出为jpg、png、pdf、svg等格式,但是如何在服务器端(这里使用flask)自动生成图形然后保存为图片呢?

其实,我们在网面上看到的highcharts图形,其实不是一张图片,而是svg格式,具体的名词解释请自行搜索,说白了就是一种xml语言,格式如下:

1
<svg xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="highcharts-root" ...></svg> 

那么自动生成并保存为图片思路就很清晰了:

  1. 获取数据填充模板html
  2. 从模板html中获取所有svg标签,使用Highcharts自带function-getSVG()
  3. 使用cairosvg将svg转换成png或者使用phantomjs

第1点渲染模板html没有什么好说的,无非就是按照需求组织数据的问题,我们直接从第2点开始

getSVG()

主要思路为一个按钮点击后通过getSVG获取所有图表的svg信息,再使用Highcharts.post构造post表单传入后端,当然这里也可以使用request库的xpath获取svg元素,Highcharts既然的现成的办法所以就直接用了,这里有个需要注意的地方就是如果想要保留图表原本的长宽的话,在chart.getSVG()时像下面代码所示那样使用长宽,不然得到的svg的长宽会变成默认值

模板html中getSVG()代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script type="text/javascript">
Highcharts.getSVG = function (charts) {
var svgArr = [];
Highcharts.each(charts, function (chart) {
//需要指定长宽,要不然导出svg时会长宽会变成默认值
var svg = chart.getSVG({chart:{width:chart.chartWidth,height:chart.chartHeight}});
svgArr.push(svg);
});
return svgArr;
};
</script>
<script type="text/javascript">
Highcharts.exportCharts = function (charts, options) {
// Merge the options
options = Highcharts.merge(Highcharts.getOptions().exporting, options);
// Post to export server
Highcharts.post(options.url, {
//filename: options.filename || 'chart',
type: options.type,
//width: options.width,
svg: Highcharts.getSVG(charts),
_search: window.location.search
});
};
</script>
<script type="text/javascript">
$('#exportPDF').click(function (){
Highcharts.exportCharts([chart1,chart2],
{
type: 'image/png',
//width: '600',
url: '/pdfdownload/'
});
});
</script>

这样post到后端之后再request.form.to_dict()得到所有参数即可拿到svg信息,当然这里得到的是所有图表的svg串,需要进行分割处理,这里以‘,<svg’为分割符,然后使用开源库**cairosvg**中的svg2png将svg转换成png图片.

1
2
3
4
5
6
def cairosvg2png(svg,chartlst):
field_svg = re.split(r",<svg ",svg)
#第一个svg是完整的<svg></svg>,从第二个开始就需要在开头补<svg ,
svg2png(bytestring=field_svg[0], write_to=".\\static\\img\\pdf\\" + chartlst[0] + ".png")
for x in range(len(field_svg)-1):
svg2png(bytestring="<svg " + field_svg[x+1], write_to=".\\static\\img\\pdf\\" + chartlst[x+1] + ".png")

phantomjs

除了上面的方法外,还可以使用phantomjs来自动在服务器端把svg转换成png/jpeg/pdf,这也是highcharts推荐的方式,phantomjs是一个很强大的基于WebKit的服务器端JavaScript API,也是开源库,使用场景也非常广泛.特别在自动化测试及爬虫方面,有兴趣的可以看这里

而且phantomjs支持以服务的方式一直运行,只需要把信息传递到它指定的端口即可,非常方便,这次是因为不能安装在服务器上,所以没有采纳,这里使用子进程运行phantomjs.exe,直接使用时需用-resources指定js文件:

1
2
3
def exportForm():
command = "phantomjs.exe highcharts-convert.js -infile test.json -outfile chart2.png -scale 2.5 width=1000 -constr Chart -resources highcharts.js,jquery.js"
child = subprocess.Popen(command,shell=True)

参考文章:

转载请注明原作者: 周淑科(https://izsk.me)


 wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?