• SVG2PNG(前台和后台将SVG转换为PNG)--amcharts导出png


      在项目中用到了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;  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=" 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转换PDFJPEGPNG
     * 
     * 
     */
    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可以模拟ChromeFirefox等浏览器请求信息。并且我在模拟的时候获取到了SvgCode,但是有一个问题是获取到的偏移量不同,导致部分svg元素不能正常显示。

  • 相关阅读:
    自动化验证文件是否更新
    设计师最常用的几个字体网站,解决你苦寻字体烦恼!
    条码生产返回结果
    精选] MySQL数据太大,你们是如何拆分数据的?
    在idea中如何将Services调出并将启动类显示在Services中
    怎么关闭笔记本的触控板
    svn钩子
    split分割
    java中multiply用法_java中BigDecimal加减乘除基本用法
    取当前年月
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/9780021.html
Copyright © 2020-2023  润新知