在项目中用到了amcharts,amcharts图标统计插件是利用SVG实现的,其自带下载png功能,但是不支持IE以下浏览器。因此研究了SVG转换为png,最终实现的效果是将amcharts生成一张png写入一个excel并提供下载。(只支持IE9以上)

1.SVG简介:

SVG 意为可缩放矢量图形(Scalable Vector Graphics)。说白了就是利用xml定义图形。

SVG 使用 XML 格式定义图像。

例如一个简单的圆形:

<html>
<body>
<h1>My first SVG</h1>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red" />
</svg>
</body>
</html>

结果:

注意:如果将SVG的父标签去掉也是正常使用的,比如:(用浏览器打开后缀为下面后缀为SVG的文件)

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red" />
</svg>

结果:

但是如果将SVG根标签的xmlns属性去掉是不会显示为图形的,比如:

<svg version="1.1">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red" />
</svg>

总结:SVG如果正常显示为图形,需要在SVG根标签引入   xmlns="http://www.w3.org/2000/svg"

更多的关于SVG的使用参考菜鸟教程:http://www.runoob.com/svg/svg-tutorial.html

2.SVG转换为PNG

  会研究前台JS生成和后台利用batik生成png。所有用到的JS以及lib或者其他会在最后提供github连接。

2.1前台转换(不支持IE)

需要的JS:saveSvgAsPng.js   ,前台下载也比较简单。支持chrome、firefox等主流浏览器(Ie就不主流了。。。。。)

简单的测试:

<html>
<body>
<h1>My first SVG</h1>
<div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="testSvg">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red" />
</svg>
</div>
<button onclick="downloadSvg()">download</button>
</body>
<script src="saveSvgAsPng.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
function downloadSvg(){
//下载的方法--第一个参数是SVG的顶级元素,第二个参数是文件名字
saveSvgAsPng(document.getElementById("testSvg"), "diagram.png");
}
</script>
</html>

2.2后台将SVG转换为PNG

  (后台转换的时候svgCode的第一个元素必须是svg,而且必须有xmlns属性,而且有一个坑是SVG自带的clippath是小写,导致在裁剪转换的时候识别不了,所以必须将clippath转换为clipPath)

  后台转换也就是将SVGCODE转换为PNG,注意SVGCODE是需要xmlns属性的,否则会转换失败。

  采用的是batik1.7+JDK7(刚开始采用JDK8+batik1.8的时候转换pdf报错)。

  batik官网下载地址:https://xmlgraphics.apache.org/batik/download.html

1.依赖的jar包:(commons-io包是为了读取svgcode)

2.工程结构

3. 需要转换的SVGCODE:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red" />
</svg>

直接浏览器打开效果:

 4.转换的代码以及测试:

package cn.qlq.svg2png;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream; import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.FileUtils; /**
* 将svg转换为png格式的图片
*
*
*/
public abstract class SVG2PNGUtils { /**
* 将svg字符串转换为png
*
* @param svgCode
* svg代码
* @param pngFilePath
* 保存的路径
* @throws TranscoderException
* svg代码异常
* @throws IOException
* io错误
*/
public static void convertToPng(String svgCode, String pngFilePath) throws IOException, TranscoderException { File file = new File(pngFilePath); FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file);
convertToPng(svgCode, outputStream);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
* 将svgCode转换成png文件,直接输出到流中
*
* @param svgCode
* svg代码
* @param outputStream
* 输出流
* @throws TranscoderException
* 异常
* @throws IOException
* io异常
*/
public static void convertToPng(String svgCode, OutputStream outputStream) throws TranscoderException, IOException {
try {
byte[] bytes = svgCode.getBytes("utf-8");
PNGTranscoder t = new PNGTranscoder();
TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(bytes));
TranscoderOutput output = new TranscoderOutput(outputStream);
// 增加图片的属性设置(单位是像素)---下面是写死了,实际应该是根据SVG的大小动态设置,默认宽高都是400
t.addTranscodingHint(ImageTranscoder.KEY_WIDTH, new Float(941));
t.addTranscodingHint(ImageTranscoder.KEY_HEIGHT, new Float(800));
t.transcode(input, output);
outputStream.flush();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) throws IOException, TranscoderException {
ClassLoader classLoader = SVG2PNGUtils.class.getClassLoader();
String filePath = classLoader.getResource("cn/qlq/svg2png/svgtest.svg").getPath();
String svgCode = FileUtils.readFileToString(new File(filePath), "UTF-8");
convertToPng(svgCode, "e:/test.png");
}
}

 结果会生成PNG。(再次强调SVG文件的xmlns一定要写)

SVG也可以转换为pdf与jpeg,下面是写的一个通用方法:

package cn.qlq.svg2png;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream; import org.apache.batik.transcoder.Transcoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.FileUtils;
import org.apache.fop.svg.PDFTranscoder; /**
* 通用的转换工具类,可以转换PDF、JPG、PNG
*
* @author Administrator
*
*/
public class SVGConvertUtils { /**
*
* @param svgCode
* svgcode
* @param pngFilePath
* 文件名称
* @param convertType
* 转换类型
* @throws IOException
* @throws TranscoderException
*/
public static void convertToPng(String svgCode, String pngFilePath, String convertType)
throws IOException, TranscoderException { File file = new File(pngFilePath);
FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file);
convertToPng(svgCode, outputStream, convertType);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
*
* @param svgCode
* @param outputStream
* @param convertType
* 转换类型
* @throws TranscoderException
* @throws IOException
*/
public static void convertToPng(String svgCode, OutputStream outputStream, String convertType)
throws TranscoderException, IOException {
try {
byte[] bytes = svgCode.getBytes("utf-8");
Transcoder t = null;
if ("png".equals(convertType)) {
t = new PNGTranscoder();
} else if ("pdf".equals(convertType)) {
t = new PDFTranscoder();
} else if ("jpeg".equals(convertType)) {
t = new JPEGTranscoder();
} TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(bytes));
TranscoderOutput output = new TranscoderOutput(outputStream);
// 增加图片的属性设置(单位是像素)---下面是写死了,实际应该是根据SVG的大小动态设置,默认宽高都是400
t.addTranscodingHint(ImageTranscoder.KEY_WIDTH, new Float(941));
t.addTranscodingHint(ImageTranscoder.KEY_HEIGHT, new Float(800));
t.transcode(input, output);
outputStream.flush();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) throws IOException, TranscoderException {
ClassLoader classLoader = SVG2PNGUtils.class.getClassLoader();
String filePath = classLoader.getResource("cn/qlq/svg2png/svgtest.svg").getPath();
String svgCode = FileUtils.readFileToString(new File(filePath), "UTF-8");
convertToPng(svgCode, "e:/test.png", "png");
convertToPng(svgCode, "e:/test.pdf", "pdf");
convertToPng(svgCode, "e:/test.jpeg", "jpeg");
}
}

至此就实现了SVG转换为PNG、PDF、JPEG,在web应用中我们可以将SVGCODE传到后台处理之后生成一个PNG并提供下载,再深入一点可以将图片再写入excel中提供下载。

3.amcharts生成图片(后台将SVG生成图片到Excel提供下载)

  其实amcharts自带JS下载功能,但是对于IE8却不兼容,在这里研究也主要是为了IE8浏览器的兼容性问题。

  前提需要明白amcharts使用的是SVG方式生成的图形,所以在有了上面的基础之后我们可以将SVG生成图片写入Excel提供下载。先理清自己的思路:

     1.接受前台的SVGCode参数
     2.采用JSoup处理code(加上xmlns属性,并且获取宽度和高度属性)
    3.后台生成图片
    4.图片写入Excel
    5.excel提供下载

  接下来我们按照上面的思路开始编写代码。

 

3.1前台界面的SVG预览图:

    

  右键查看元素我们发现amcharts生成的SVG的根元素不带xmlns属性,如下:

    

  我们将上面代码保存下来并且在SVG的根元素加    xmlns="http://www.w3.org/2000/svg"  即可正常显示为图形,如下:(也可以自己保存下来通过网页打开)

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" style="position: absolute; width: 500px; height: 552px;"><desc>JavaScript chart by amCharts 3.4.2</desc><g><path cs="100,100" d="M0.5,0.5 L499.5,0.5 L499.5,551.5 L0.5,551.5 Z" fill="#FFFFFF" stroke="#000000" fill-opacity="0" stroke-width="1" stroke-opacity="0"></path><path cs="100,100" d="M0.5,0.5 L424.5,0.5 L424.5,476.5 L0.5,476.5 Z" fill="#FFFFFF" stroke="#000000" fill-opacity="0" stroke-width="1" stroke-opacity="0" transform="translate(55,55)" class="amChartsPlotArea"></path></g><g><g transform="translate(55,55)"><g><path cs="100,100" d="M0.5,48.5 L5.5,48.5" fill="none" stroke-width="1" stroke-opacity="1" stroke="#DADADA" transform="translate(-6,0)"></path><path cs="100,100" d="M0.5,0.5 L0.5,0.5 L424.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M0.5,143.5 L5.5,143.5" fill="none" stroke-width="1" stroke-opacity="1" stroke="#DADADA" transform="translate(-6,0)"></path><path cs="100,100" d="M0.5,95.5 L0.5,95.5 L424.5,95.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M0.5,238.5 L5.5,238.5" fill="none" stroke-width="1" stroke-opacity="1" stroke="#DADADA" transform="translate(-6,0)"></path><path cs="100,100" d="M0.5,190.5 L0.5,190.5 L424.5,190.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M0.5,333.5 L5.5,333.5" fill="none" stroke-width="1" stroke-opacity="1" stroke="#DADADA" transform="translate(-6,0)"></path><path cs="100,100" d="M0.5,285.5 L0.5,285.5 L424.5,285.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M0.5,428.5 L5.5,428.5" fill="none" stroke-width="1" stroke-opacity="1" stroke="#DADADA" transform="translate(-6,0)"></path><path cs="100,100" d="M0.5,380.5 L0.5,380.5 L424.5,380.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M0.5,476.5 L0.5,476.5 L424.5,476.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g></g><g transform="translate(55,55)" visibility="visible"><g><path cs="100,100" d="M0.5,0.5 L0.5,5.5" fill="none" stroke-width="1" stroke-opacity="0.2" stroke="#000000" transform="translate(0,-5)"></path><path cs="100,100" d="M0.5,476.5 L0.5,476.5 L0.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M21.5,476.5 L21.5,476.5 L21.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M42.5,476.5 L42.5,476.5 L42.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M64.5,476.5 L64.5,476.5 L64.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M85.5,476.5 L85.5,476.5 L85.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M106.5,0.5 L106.5,5.5" fill="none" stroke-width="1" stroke-opacity="0.2" stroke="#000000" transform="translate(0,-5)"></path><path cs="100,100" d="M106.5,476.5 L106.5,476.5 L106.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M127.5,476.5 L127.5,476.5 L127.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M148.5,476.5 L148.5,476.5 L148.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M170.5,476.5 L170.5,476.5 L170.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M191.5,476.5 L191.5,476.5 L191.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M212.5,0.5 L212.5,5.5" fill="none" stroke-width="1" stroke-opacity="0.2" stroke="#000000" transform="translate(0,-5)"></path><path cs="100,100" d="M212.5,476.5 L212.5,476.5 L212.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M233.5,476.5 L233.5,476.5 L233.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M254.5,476.5 L254.5,476.5 L254.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M276.5,476.5 L276.5,476.5 L276.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M297.5,476.5 L297.5,476.5 L297.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M318.5,0.5 L318.5,5.5" fill="none" stroke-width="1" stroke-opacity="0.2" stroke="#000000" transform="translate(0,-5)"></path><path cs="100,100" d="M318.5,476.5 L318.5,476.5 L318.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g><g><path cs="100,100" d="M339.5,476.5 L339.5,476.5 L339.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M360.5,476.5 L360.5,476.5 L360.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M382.5,476.5 L382.5,476.5 L382.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M403.5,476.5 L403.5,476.5 L403.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.08" stroke="#000000"></path></g><g><path cs="100,100" d="M424.5,0.5 L424.5,5.5" fill="none" stroke-width="1" stroke-opacity="0.2" stroke="#000000" transform="translate(0,-5)"></path><path cs="100,100" d="M424.5,476.5 L424.5,476.5 L424.5,0.5" fill="none" stroke-width="1" stroke-dasharray="3" stroke-opacity="0.15" stroke="#000000"></path></g></g></g><g></g><g></g><g><g transform="translate(55,55)"><g transform="translate(0,389)" visibility="visible"><path cs="100,100" d="M0.5,0.5 L0.5,76.5 L204.5,76.5 L204.5,0.5 L0.5,0.5 Z" fill="#ADD981" stroke="#FF6600" fill-opacity="0.8" stroke-width="1" stroke-opacity="0"></path></g><g transform="translate(0,294)" visibility="visible"><path cs="100,100" d="M0.5,0.5 L0.5,76.5 L307.5,76.5 L307.5,0.5 L0.5,0.5 Z" fill="#ADD981" stroke="#FF6600" fill-opacity="0.8" stroke-width="1" stroke-opacity="0"></path></g><g transform="translate(0,199)" visibility="visible"><path cs="100,100" d="M0.5,0.5 L0.5,76.5 L320.5,76.5 L320.5,0.5 L0.5,0.5 Z" fill="#ADD981" stroke="#FF6600" fill-opacity="0.8" stroke-width="1" stroke-opacity="0"></path></g><g transform="translate(0,104)" visibility="visible"><path cs="100,100" d="M0.5,0.5 L0.5,76.5 L237.5,76.5 L237.5,0.5 L0.5,0.5 Z" fill="#ADD981" stroke="#FF6600" fill-opacity="0.8" stroke-width="1" stroke-opacity="0"></path></g><g transform="translate(0,9)" visibility="visible"><path cs="100,100" d="M0.5,0.5 L0.5,76.5 L180.5,76.5 L180.5,0.5 L0.5,0.5 Z" fill="#ADD981" stroke="#FF6600" fill-opacity="0.8" stroke-width="1" stroke-opacity="0"></path></g></g></g><g><g transform="translate(55,55)"><g></g></g><g transform="translate(55,55)" opacity="1" visibility="visible"><g></g><g></g><g clip-path="url(#AmChartsEl-3)"><path cs="100,100" d="M66.5,48.5 L165.5,143.5 L189.5,238.5 L214.5,333.5 L212.5,428.5 M0,0 L0,0" fill="none" stroke-width="2" stroke-opacity="1" stroke="#27c5ff"></path></g><clipPath id="AmChartsEl-3"><rect x="0" y="0" width="426" height="478" rx="0" ry="0" stroke-width="0"></rect></clipPath></g></g><g clip-path="url(#AmChartsEl-2)"></g><g><g transform="translate(55,55)"><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="end" transform="translate(-12,46)"><tspan y="6" x="0">2005</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="end" transform="translate(-12,141)"><tspan y="6" x="0">2006</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="end" transform="translate(-12,236)"><tspan y="6" x="0">2007</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="end" transform="translate(-12,331)"><tspan y="6" x="0">2008</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="end" transform="translate(-12,426)"><tspan y="6" x="0">2009</tspan></text></g><g transform="translate(55,55)" visibility="visible"><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="middle" transform="translate(0,-17)"><tspan y="6" x="0">15</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(24,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(45,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(67,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(88,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="middle" transform="translate(106,-17)"><tspan y="6" x="0">20</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(130,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(151,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(173,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(194,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="middle" transform="translate(212,-17)"><tspan y="6" x="0">25</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(236,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(257,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(279,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(300,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="middle" transform="translate(318,-17)"><tspan y="6" x="0">30</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(342,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(363,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(385,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(406,-12)"><tspan y="6" x="0"></tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="middle" transform="translate(424,-17)"><tspan y="6" x="0">35</tspan></text><text y="6" fill="#000000" font-family="Verdana" font-size="12" opacity="1" font-weight="bold" text-anchor="middle" transform="translate(212,-39)"><tspan y="6" x="0">Million USD</tspan></text></g></g><g><path cs="100,100" d="M0.5,0.5 L0.5,476.5" fill="none" stroke-width="1" stroke-opacity="1" stroke="#DADADA" transform="translate(55,55)"></path><path cs="100,100" d="M0.5,0.5 L424.5,0.5" fill="none" stroke-width="1" stroke-opacity="0.2" stroke="#000000" transform="translate(55,55)" visibility="visible"></path></g><g></g><g></g><g><g transform="translate(55,55)"></g><g transform="translate(55,55)" opacity="1" visibility="visible"><circle r="4" cx="0" cy="0" fill="#FFFFFF" stroke="#27c5ff" fill-opacity="1" stroke-width="2" stroke-opacity="1" transform="translate(66,48)"></circle><circle r="4" cx="0" cy="0" fill="#FFFFFF" stroke="#27c5ff" fill-opacity="1" stroke-width="2" stroke-opacity="1" transform="translate(165,143)"></circle><circle r="4" cx="0" cy="0" fill="#FFFFFF" stroke="#27c5ff" fill-opacity="1" stroke-width="2" stroke-opacity="1" transform="translate(189,238)"></circle><circle r="4" cx="0" cy="0" fill="#FFFFFF" stroke="#27c5ff" fill-opacity="1" stroke-width="2" stroke-opacity="1" transform="translate(214,333)"></circle><circle r="4" cx="0" cy="0" fill="#FFFFFF" stroke="#27c5ff" fill-opacity="1" stroke-width="2" stroke-opacity="1" transform="translate(212,428)"></circle></g></g><g><g></g></g><g id="balloons"></g><g><g transform="translate(420,63)" visibility="hidden"><rect x="0.5" y="0.5" width="68" height="27" rx="0" ry="0" stroke-width="1" fill="#e5e5e5" stroke="#e5e5e5" fill-opacity="1" stroke-opacity="1" opacity="0" transform="translate(-8,-8)"></rect><text y="6" fill="#000000" font-family="Verdana" font-size="11" opacity="1" text-anchor="start" transform="translate(0,7)"><tspan y="6" x="0">Show all</tspan></text></g></g><g></g><clipPath id="AmChartsEl-2"><rect x="55" y="55" width="424" height="476" rx="0" ry="0" stroke-width="0"></rect></clipPath></svg>

2.后台servlet的编写(需要接受svgCode,并且解析之后生成图片、写入excel并且提供下载)

1.所有用到的包 :

2.项目结构

3.前端主要JS代码:(带着svgCode去提交表单)

<body>
<div id="chartdiv" style="width: 500px; height: 600px;"></div>
<form action="/ExportSVG2Excel" method="post">
<input type="hidden" name="svgCode" />
</form>
<button onclick="downloadChart()">download</button>
</body>
<script>
//下载的函数(设置值,提交表单)
function downloadChart() {
var svgCode = $("#chartdiv").find("svg:first").prop("outerHTML");//会返回包括自己在内的内容
alert(svgCode);
$("[name='svgCode']").val(svgCode);
$("form").submit();
}
</script>

4.后台生成png的工具类和Servlet代码

package cn.qlq.svg2png;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream; import org.apache.batik.transcoder.Transcoder;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.fop.svg.PDFTranscoder; /**
* 将svg转换PDF\JPEG\PNG
*
*
*/
public abstract class SVGConvertUtils { /**
*
* @param svgCode
* @param pngFilePath
* 文件路径
* @param fileType
* 文件名称
* @param width
* 宽度
* @param height
* 高度
* @throws IOException
* @throws TranscoderException
*/
public static void convertToPngOrOthers(String svgCode, String pngFilePath, String fileType, int width, int height)
throws IOException, TranscoderException { File file = new File(pngFilePath);
FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file);
convertToPngOrOthers(svgCode, outputStream, fileType, width, height);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} public static void convertToPngOrOthers(String svgCode, OutputStream outputStream, String fileType, int width,
int height) throws TranscoderException, IOException {
try {
byte[] bytes = svgCode.getBytes("utf-8");
Transcoder t = null;
if ("png".equals(fileType)) {
t = new PNGTranscoder();
} else if ("pdf".equals(fileType)) {
t = new PDFTranscoder();
} else if ("jpeg".equals(fileType)) {
t = new JPEGTranscoder();
} TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(bytes));
TranscoderOutput output = new TranscoderOutput(outputStream);
// 增加图片的属性设置(单位是像素)---下面是写死了,实际应该是根据SVG的大小动态设置,默认宽高都是400
t.addTranscodingHint(ImageTranscoder.KEY_WIDTH, new Float(width));
t.addTranscodingHint(ImageTranscoder.KEY_HEIGHT, new Float(height));
t.transcode(input, output);
outputStream.flush();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package cn.qlq.Servlet;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder; import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.batik.transcoder.TranscoderException;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import cn.qlq.svg2png.SVGConvertUtils; /**
* Servlet implementation class ExportSVG2Excel
*/
@WebServlet("/ExportSVG2Excel")
public class ExportSVG2Excel extends HttpServlet {
private static final long serialVersionUID = 1L; public ExportSVG2Excel() {
} protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1.接受前台的SVGCode参数
String svgCode = request.getParameter("svgCode");
if (StringUtils.isEmpty(svgCode)) {
return;
}
// 2.采用JSoup处理code(加上xmlns属性,并且获取宽度和高度属性)
svgCode = disposeSvgCode(svgCode);
// 3.后台生成图片(暂时不做异常处理)
Document document = Jsoup.parse(svgCode);
Element element = document.select("svg").get(0);
String style = element.attr("style");
String width = extractCssAttr(style, "width");// 提取style的宽度属性
String height = extractCssAttr(style, "height");// 提取style的宽度属性
String pngFileName = "e:/amchartspng";
try {
// 最终的SVGCode必须根元素是是svg,而且带xmlns属性,而且必须将clippath替换为clipPath,否则识别不了会报错
String finallySvgCode = element.outerHtml().replace("clippath", "clipPath");
SVGConvertUtils.convertToPngOrOthers(finallySvgCode, pngFileName, "png", Integer.valueOf(width),
Integer.valueOf(height));
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (TranscoderException e) {
e.printStackTrace();
}
String excelName = "e:/amchartsexcel";
// 3.图片写入Excel
writePng2Excel(pngFileName, excelName);
// 4.excel提供下载
// 若名字为中文名字,需要用URL编码
response.setHeader("content-disposition",
"attachment;filename=" + URLEncoder.encode(excelName + ".xls", "UTF-8"));
InputStream in = in = new FileInputStream(excelName);
OutputStream out = null;
int len = 0;
byte buffer[] = new byte[1024];
out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
in.close();
} private void writePng2Excel(String pngFileName, String excelName) {
FileOutputStream fileOut = null;
BufferedImage bufferImg = null;
try {
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
// 加载图片
bufferImg = ImageIO.read(new File(pngFileName));
ImageIO.write(bufferImg, "png", byteArrayOut);
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet1 = wb.createSheet("sheet1");
HSSFPatriarch patriarch = sheet1.createDrawingPatriarch();
/**
* dx1 - the x coordinate within the first
* cell.//定义了图片在第一个cell内的偏移x坐标,既左上角所在cell的偏移x坐标,一般可设0 dy1 - the y
* coordinate within the first
* cell.//定义了图片在第一个cell的偏移y坐标,既左上角所在cell的偏移y坐标,一般可设0 dx2 - the x
* coordinate within the second
* cell.//定义了图片在第二个cell的偏移x坐标,既右下角所在cell的偏移x坐标,一般可设0 dy2 - the y
* coordinate within the second
* cell.//定义了图片在第二个cell的偏移y坐标,既右下角所在cell的偏移y坐标,一般可设0 col1 - the
* column (0 based) of the first cell.//第一个cell所在列,既图片左上角所在列 row1 -
* the row (0 based) of the first cell.//图片左上角所在行 col2 - the column
* (0 based) of the second cell.//图片右下角所在列 row2 - the row (0 based)
* of the second cell.//图片右下角所在行
*/
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) 2, 2, (short) 5, 8);
// 插入图片
patriarch.createPicture(anchor, wb.addPicture(byteArrayOut.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG));
// 输出文件
fileOut = new FileOutputStream(excelName);
wb.write(fileOut);
fileOut.close();
} catch (Exception e) {
e.printStackTrace();
}
} // excel转换单位(未用上)
private int parsrUnit(String value, String unit) {
int v = 0;
if (StringUtils.isBlank(unit) || unit.equals("px")) {
v = Math.round(Float.parseFloat(value) * 37F);
} else if ("em".endsWith(unit)) {
v = Math.round(Float.parseFloat(value) * 267.5F);
}
return v;
} private String extractCssAttr(String style, String extract) {
if (style.contains(extract)) {
style = style.substring(style.indexOf(extract));
style = style.substring(0, style.indexOf(";"));
String attr = style.substring(style.indexOf(":") + 2);
return attr.substring(0, attr.indexOf("px"));
}
return "";
} private String disposeSvgCode(String svgCode) {
Document document = Jsoup.parseBodyFragment(svgCode);
Element element = document.select("svg").get(0);
element.attr("xmlns", "http://www.w3.org/2000/svg");// 添加属性
return document.html();
} protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
} }

  至此完成了全部功能,如果是下载pdf或者svg更简单,直接将svgCode传到后台生成pdf或者svg就可以了,省去将图片写入excel的步骤。总结以下文中用到的技术:

    1.Jsoup后台解析xml

    2.batik将svg转换为png

    3.amcharts生成图标(SVG插件)

    4.poi导出图片到excel

    5.servlet文件下载

附上github地址:https://github.com/qiao-zhi/SVG2PNG

在IE8的下载问题:

  IE8不支持SVG,(amcharts在IE8下通过Div实现图表显示)我想出来的一种下载PNG思路是:用htmlunit模拟爬虫获取解析JS后的页面,后台采用Jsoup解析返回的XML提取SVGCode进行下载,因为Htmlunit可以模拟Chrome\Firefox等浏览器请求信息。并且我在模拟的时候获取到了SvgCode,但是有一个问题是获取到的偏移量不同,导致部分svg元素不能正常显示。

SVG2PNG(前台和后台将SVG转换为PNG)--amcharts导出png的更多相关文章

  1. Ext分页实现(前台与后台)

    Ext分页实现(前台与后台)Spring+Mybatis 一.项目背景 关于Ext的分页网上有很多博客都有提到,但是作为Ext新手来说,并不能很容易的在自己的项目中得以应用.因为,大多数教程以及博客基 ...

  2. MVC 前台向后台传输数据

    今天,我们一起来学习下.MVC如何在前台给后台传输数据 (1)前台传输数据到后台 具体思路:前台拼凑json字符串,然后通过 get 或 post 方式,传递到后台 Action 方法中 我现在前台展 ...

  3. 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——实践篇(二)

    在由ASP.NET所谓前台调用后台.后台调用前台想到HTTP——理论篇中描述了一下ASP.NET新手的三个问题及相关的HTTP协议内容,在由ASP.NET所谓前台调用后台.后台调用前台想到HTTP—— ...

  4. 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——理论篇

    工作两年多了,我会经常尝试给公司小伙伴儿们解决一些问题,几个月下来我发现初入公司的小朋友最爱问的问题就三个 1. 我想前台调用后台的XXX方法怎么弄啊? 2. 我想后台调用前台的XXX JavaScr ...

  5. C#和JavaScript交互(asp.net前台和后台互调)总结 (转)

    http://www.cnblogs.com/poleices/archive/2011/02/24/1963727.html C#代码与javaScript函数的相互调用: 1.如何在JavaScr ...

  6. shell下,进程的前台与后台运行

    跟系统任务相关的几个命令:fg.bg.jobs.&.ctrl+z1. & 最经常被用到   这个用在一个命令的最后,可以把这个命令放到后台执行2. ctrl + z     可以将一个 ...

  7. [转] 在Asp.net前台和后台弹出提示框

    一.在前台弹出提示框 1.点击"A"标记或者"控件按钮"弹出提示框 <asp:LinkButton ID="lbtnDel" runa ...

  8. android判断当前应用程序处于前台还是后台

    /**     *判断当前应用程序处于前台还是后台     *      * @param context * @return         */    public static boolean ...

  9. js前台与后台数据交互-前台调后台

    转自:http://blog.csdn.net/wang379275614/article/details/17033981   网站是围绕数据库来编程的,以数据库中的数据为中心,通过后台来操作这些数 ...

随机推荐

  1. Codeforces965E Short Code 【启发式合并】【堆】

    题目大意: 给出总长度不超过1E+5的不重复字符串集,给每个字符串选一个前缀使得可以区分它. 题目分析: KAN出的DIV2难度一般不高,想升Ranting的可以试试. 简单的树上启发式合并,建出Tr ...

  2. HDU1069 最长上升子序列

    emm....矩形嵌套 还记得吗....就是它... 直接贴代码了.... import java.util.ArrayList; import java.util.Arrays; import ja ...

  3. POJ P3352 Road Construction 解题报告

    P3352 Road Construction 描述 这几乎是夏季,这意味着它几乎是夏季施工时间!今年,负责岛屿热带岛屿天堂道路的优秀人士,希望修复和升级岛上各个旅游景点之间的各种道路. 道路本身也很 ...

  4. A1091. Acute Stroke

    One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the re ...

  5. Class对象、反射机制、获取Constructor构造方法

    1.Class对象的三种创建方法(Class首字母大写) public class Demo { public static void main(String[] args){ Object obj= ...

  6. 【Python】统计个人新浪微博词频并给出相应的柱状图

    Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spm=a1z38n.10677092.0.0.482434a6E ...

  7. Tomcat的配置文件详解

    Tomcat的配置文件详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Tomcat的配置文件 Tomcat的配置文件默认存放在$CATALINA_HOME/conf目录中, ...

  8. 《高性能Mysql》讲聚簇索引

    <高性能Mysql>原文 聚簇索引如下图为聚簇所有的存储方式,聚簇实际不是一种索引,而是一种数据的存储方式,InnoDB的聚簇事假在同一个结构中保存了B-Tree索引和数据行. 当表有聚簇 ...

  9. 【1】【JUC】JDK1.8源码分析之ReentrantLock

    概要: ReentrantLock类内部总共存在Sync.NonfairSync.FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQ ...

  10. java实现《剑指offer》(一)1~10

    每段代码都是在牛客网在线编程通过后,然后拷过来的,准确性可以保证. 1.二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入 ...