SVG2PNG(前臺和後臺將SVG轉換爲PNG)--amcharts導出png

  在項目中用到了amcharts,amcharts圖標統計插件是利用SVG實現的,其自帶下載png功能,可是不支持IE如下瀏覽器。所以研究了SVG轉換爲png,最終實現的效果是將amcharts生成一張png寫入一個excel並提供下載。(只支持IE9以上)javascript

1.SVG簡介:

SVG 意爲可縮放矢量圖形(Scalable Vector Graphics)。說白了就是利用xml定義圖形。html

SVG 使用 XML 格式定義圖像。前端

例如一個簡單的圓形:java

<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>

 

結果:node

 

 

注意:若是將SVG的父標籤去掉也是正常使用的,好比:(用瀏覽器打開後綴爲下面後綴爲SVG的文件)git

<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>

 

結果:github

 

 

 可是若是將SVG根標籤的xmlns屬性去掉是不會顯示爲圖形的,好比:web

<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"  chrome

更多的關於SVG的使用參考菜鳥教程:http://www.runoob.com/svg/svg-tutorial.htmlapache

 

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元素不能正常顯示。

相關文章
相關標籤/搜索