highChart导出PDF

来源:互联网 发布:怎样鉴别mac口红真假 编辑:程序博客网 时间:2024/06/06 00:58

写给读者的话^_^:

  众所周知,基于Highcharts插件生成的svg图片组(注意这里鄙人指的组是若干图有序组合,并非一张图片,具有业务意义)导出为PDF文档是有难度滴。鄙人也曾“异想天开”用前端技术拍个快照然后转换为pdf文件导出,后来因为能力有限未能完美实现。因此,参照互联网已有的经验和做法,创造出一套较为有操作性的方案,详情见下文。

  

---------------------------------------------------说正事儿分割线----------------------------------------------------

 

假设需求如下:

  1. 如图所示的复杂图表报告
  2. 对其进行PDF导出(demo中所有数据为伪造,并无任何价值)
  3. 此图仅作为demo展示,不涉及商业数据,所有数据均为构造假数据

    那么问题来了,肿么导出哩,先看下导出后的效果,卖个关子,如下图:

  4. 当然,不可否认的是图像质量会打折。但是效果终究实现了。接下来我们去看看前端怎么写,然后提交到后台又如何处理返回一个文档输出流。

    1. 前端html自定义元素属性,如下:
      <div class="timeFenBuPic" id="timeFenBuPic">                    <div class="timeFenBuOne" id="timeFenBuOne" softOrHard="hard" position="center" getSvg="true" h4="VR眼镜使用饱和度">                    </div>                </div>

      例如:其中position咱们可以定义给它三个值属性:left,center,right代表了在文档中,每一组svg图的相对位置,其余几个属性自己结合后台程序使用即可。

  5. 前端js脚本获取并且组织svg图像元素并提交给服务端(这里我们用的服务端时Java写的struts2作为控制器层的服务端接口),js写法如下:
    复制代码
    function PDFExecute(){    //循环拿到各个绘图区域id    $("#svgPDF").empty();    $.each($("[getSvg='true']"),function(index,ele){        //根据每个绘图区域的id获取svg,position,softOrHard等属性        var svg = $(this).highcharts();        if(typeof(svg)=='undefined'||svg==null){            svg = 'noData';        }else{            svg = svg.getSVG();        }        $("#svgPDF").append("<input id='SVG"+$(this).attr("id")+"' name='svg' type='hidden' value='' />");        $("#SVG"+$(this).attr("id")).val(                $(this).attr("id")+                "___"+$(this).attr("position")+                "___"+encodeURI($(this).attr("h4")+getSvgUnit($(this).parents('li').children('ul').children('li .curr').text()))+                "___"+$(this).attr("softOrHard")+                "___"+svg);    });    $("#svgPDF").append("<input name='logoT' type='hidden' value='"+encodeURI($(".logoT").text())+"' />");    //处理文本锚点异常错误//    $('[text-anchor="undefined"]').attr('text-anchor','');    $("#svgPDF").submit();}
    复制代码
  6. 服务端处理服务端处理采用itext作为pdf生成第三方工具包,然后返回一个输出流到前端
    1. pdf导出接口

      复制代码
          /**     * PDF导出入口方法     * 参数要求:     * 1.一个页面的title(encode后的)     * 2.所有highcharts的svg     * 3.页面所有查询参数(用于表格类型的数据查询,因为表格类型前端无法传给后台)     * 4.svg详述:     *         svg为一个数组     *         svg的每个数组元素为字符串,且包含多个信息,以三个连续英文半角的下划线___做split操作,得到数组,具体内容如下:     *      页面每个hicharts图的绘制id___此图在水平方向的相对位置(left还是right)___encode后的每两个图组成的title标题     *      (例如xx投放趋势)___此图为软广还是硬广(soft还是hard)___svg字符串用来转换图片输出流     *      因此 svg.split("___")结果为:     *      ["charts图id","left/right","xx趋势图","soft/hard","<svg.../>"]     * 5.使用时修改ByteArrayOutputStream方法下参数及布局规则     */    public String svgPDF(){        try {                request.setCharacterEncoding("utf-8");                response.setCharacterEncoding("utf-8");                Map<String,Object> map = new HashMap<String,Object>();                String logoT = request.getParameter("logoT");                if(StringUtils.isNotEmpty(logoT)){                    logoT = URLDecoder.decode(logoT,"utf-8");                }                                downloadFileName= URLEncoder.encode(logoT,"utf-8")+".pdf";                String[] svg = request.getParameterValues("svg");                map.put("svg", svg);                map.put("logoT", logoT);                                //实例化文档绘制工具类                ComprehensivePdfUtil cpu = new ComprehensivePdfUtil();                                ByteArrayOutputStream buff = cpu.getPDFStream(request,response,map);                inputStream = new ByteArrayInputStream(buff.toByteArray());                buff.close();            return "success";        } catch (IOException e) {            e.printStackTrace();            return null;        }    }
      复制代码

      此接口响应来自客户端的http请求并返回输出流

    2. PDF文档绘制工具类
      package com.demo.utils;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.StringReader;import java.io.UnsupportedEncodingException;import java.net.MalformedURLException;import java.net.URLDecoder;import java.util.ArrayList;import java.util.List;import java.util.Map;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.batik.transcoder.TranscoderException;import org.apache.batik.transcoder.TranscoderInput;import org.apache.batik.transcoder.TranscoderOutput;import org.apache.batik.transcoder.image.PNGTranscoder;import com.itextpdf.text.BadElementException;import com.itextpdf.text.BaseColor;import com.itextpdf.text.Document;import com.itextpdf.text.DocumentException;import com.itextpdf.text.Element;import com.itextpdf.text.Font;import com.itextpdf.text.Image;import com.itextpdf.text.Paragraph;import com.itextpdf.text.Phrase;import com.itextpdf.text.Rectangle;import com.itextpdf.text.pdf.BaseFont;import com.itextpdf.text.pdf.PdfPCell;import com.itextpdf.text.pdf.PdfPRow;import com.itextpdf.text.pdf.PdfPTable;import com.itextpdf.text.pdf.PdfWriter;/** * @Description XXX分析页面PDF导出工具方法 */public class ComprehensivePdfUtil {    /**     * 获得PDF字节输出流及pdf布局业务逻辑     * @param request     * @param response     * @param resultMap 包含参数:svg(绘图svg参数及hicharts图布局参数) logoT(页面总标题)     * @param list 页面包含植入栏目排行表格图,该list存储绘制表格所用的数据     * @param tableTh 页面包含植入栏目排行表格图,该字符串作为表格表头     * @param tableTd 页面包含植入栏目排行表格图,该字符串作为表格内容填充时,实体类反射值所用的方法名(必须与实体方法严格一致)     * @return     */    public ByteArrayOutputStream getPDFStream(HttpServletRequest request,            HttpServletResponse response,            Map<String,Object> resultMap){        try {            //图片变量定义            String noData = "/style/images/noData.png";//无数据左右图            String noDataCenter = "/style/images/noDataCenter.png";//无数据中间图            String waterMark = "/style/images/PDFSHUIYIN.png";//PDF导出文件水印图片            String [] svgName = (String[]) resultMap.get("svg");//导出PDF页面所有svg图像            Document document = new Document();            ByteArrayOutputStream buffer = new ByteArrayOutputStream();            PdfWriter pdfWriter = PdfWriter.getInstance(document, buffer);                        //设置页面大小            int pageHeight = 2000;            Rectangle rect = new Rectangle(0,0,1200,pageHeight);            rect.setBackgroundColor(new BaseColor(248,248,248));//页面背景色            document.setPageSize(rect);//页面参数                        //页边空白              document.setMargins(20, 20, 30, 20);            document.open();                        //设置页头信息            if(null!=resultMap.get("logoT")){                BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);                Font FontChinese = new Font(bfChinese,20, Font.BOLD);                 Paragraph paragraph = new Paragraph((String)resultMap.get("logoT"),FontChinese);                paragraph.setAlignment(Element.ALIGN_CENTER);                document.add(paragraph);            }                        PdfPTable table = null;            String path = request.getContextPath();            String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";                        //开始循环写入svg图像到pdf文档对象            for(String str:svgName){                //////////////////////////////////////////////////////////////////////////////////////////////////////////                //positionAndSvg数组中元素说明:                //positionAndSvg[0]表示svg图像所在页面的div的id                //positionAndSvg[1]表示svg图像在水平方向的相对位置:                    //                    1.left(水平方向两张图,居左且占比50%)                 //                    2.right(水平方向两张图,居右且占比50%)                //                    3.center(水平方向一张图,居中且占比100%)                //positionAndSvg[2]表示svg图像模块的标题如:xxx走势图                //positionAndSvg[3]表示soft/hard即软广图或者硬广图,当无数据时为无数据提示效果图提供判断依据                //positionAndSvg[4]表示svg图像元素,形如<svg...../>                //////////////////////////////////////////////////////////////////////////////////////////////////////////                String[] positionAndSvg = str.split("___");                                Image image1 = null;                boolean havaData = true;                                if("noData".equals(positionAndSvg[4])){//无数据时                    image1 = Image.getInstance(basePath+noData);                    havaData = false;                }else{//有数据                    image1 = Image.getInstance(highcharts(request,response,positionAndSvg[4]).toByteArray());                    havaData = true;                }                                if("left".equals(positionAndSvg[1])){                    String title1 = URLDecoder.decode(positionAndSvg[2],"utf-8");                    setTitleByCharts(document,30,title1,"",0,87,55,Element.ALIGN_LEFT,headfont);                    if(!"cooperateProporOne".equals(positionAndSvg[0])){                        setTitleByCharts(document,0,"左图","右图",248,248,248,Element.ALIGN_CENTER,blackTextFont);                    }else{                        setTitleByCharts(document,0,"","",248,248,248,Element.ALIGN_CENTER,blackTextFont);                    }                    table = new PdfPTable(2);                                        float[] wid ={0.50f,0.50f}; //列宽度的比例                    table.setWidths(wid);                     table = PdfPTableImage(table,image1,80f);                }else if("right".equals(positionAndSvg[1])){                    table = PdfPTableImage(table,image1,80f);                    table.setSpacingBefore(10);                    table=setTableHeightWeight(table,360f,1000);                    document.add(table);                    table = null;                }else if("center".equals(positionAndSvg[1])){//总览全局                    String title1 = URLDecoder.decode(positionAndSvg[2],"utf-8");                    setTitleByCharts(document,30,title1,"",0,87,55,Element.ALIGN_LEFT,headfont);                    setTitleByCharts(document,0,"","",248,248,248,Element.ALIGN_CENTER,blackTextFont);                    table = new PdfPTable(1);                    float[] wid ={1.00f}; //列宽度的比例                    table.setWidths(wid);                     if(havaData){                        table = PdfPTableImageTable(table,image1,1000f,600f);                    }else{                        table = PdfPTableImageTable(table,Image.getInstance(basePath+noDataCenter),1000f,600f);                    }                    table=setTableHeightWeight(table,400f,1000);                    document.add(table);                    table=null;                }            }                        //添加水印Start---------------------------------------------------------------------------------------------            PdfFileExportUtil pdfFileExportUtil = new PdfFileExportUtil();            pdfWriter.setPageEvent(pdfFileExportUtil.new PictureWaterMarkPdfPageEvent(basePath+waterMark));//            pdfWriter.setPageEvent(pdfFileExportUtil.new TextWaterMarkPdfPageEvent("xxx科技"));            //添加水印End-----------------------------------------------------------------------------------------------            document.close();            return buffer;        } catch (BadElementException e) {            e.printStackTrace();            return null;        } catch (MalformedURLException e) {            e.printStackTrace();            return null;        } catch (DocumentException e) {            e.printStackTrace();            return null;        } catch (IOException e) {            e.printStackTrace();            return null;        } catch (Exception e) {            e.printStackTrace();            return null;        }    }            /**     * 设置图片类型Cell属性     * @param table     * @param image1     * @param imgPercent     * @return     * @throws Exception     */    private PdfPTable PdfPTableImage(PdfPTable table,Image image1,float imgPercent){        table = useTable(table,Element.ALIGN_CENTER);        PdfPCell cellzr = createCellImage(image1,imgPercent);        cellzr.setBorder(0);        cellzr.setBackgroundColor(new BaseColor(248,248,248));          table.addCell(cellzr);         return table;    }    /**     * 设置图片类型Table的Cell属性     * @param table     * @param image1     * @param imgPercentWidth     * @param imgPercentHeight     * @return     * @throws Exception     */    private PdfPTable PdfPTableImageTable(PdfPTable table,Image image1,float imgPercentWidth,float imgPercentHeight){        table = useTable(table,Element.ALIGN_CENTER);        PdfPCell cellzr = createCellImageTable(image1,imgPercentWidth,imgPercentHeight);        cellzr.setBorder(0);        cellzr.setBackgroundColor(new BaseColor(248,248,248));          table.addCell(cellzr);         return table;    }        /**     * 设置表头     * @param document     * @param SpacingBefore     * @param title1     * @param title2     * @param r1     * @param r2     * @param r3     * @param ele     * @param font     * @throws Exception     */    private void setTitleByCharts(Document document,int SpacingBefore,String title1,String title2,int r1,int r2,int r3,int ele,Font font){        try {            float[] titlewidthsLeft = {0.50f,0.50f};            PdfPTable zrfbtitleTable = createTable(titlewidthsLeft);            PdfPCell cellzr = createCellLeft(title1,font,ele);              cellzr.setBorder(0);              cellzr.setBackgroundColor(new BaseColor(r1,r2,r3));              zrfbtitleTable.addCell(cellzr);                         PdfPCell cellzr1 = createCellLeft(title2,font,ele);              cellzr1.setBorder(0);              cellzr1.setBackgroundColor(new BaseColor(r1,r2,r3));              zrfbtitleTable.addCell(cellzr1);             zrfbtitleTable.setSpacingBefore(SpacingBefore);            zrfbtitleTable=setTableHeightWeight(zrfbtitleTable,30f,1000);                        document.add(zrfbtitleTable);        } catch (DocumentException e) {            e.printStackTrace();        }    }    /**     * 导出Pdf所用字体静态变量     */    private static Font headfont ;// title字体    private static Font blackTextFont ;// 黑色字体    private static Font colorfont;    int maxWidth = 500;    static{        BaseFont bfChinese;        try {              bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);              headfont = new Font(bfChinese, 15, Font.BOLD);// 设置字体大小              headfont.setColor(BaseColor.WHITE);              blackTextFont = new Font(bfChinese, 11, Font.BOLD);// 设置字体大小              blackTextFont.setColor(BaseColor.BLACK);              colorfont = new Font(bfChinese, 11, Font.NORMAL);// 设置字体大小              colorfont.setColor(BaseColor.RED);        } catch (Exception e) {                        e.printStackTrace();        }     }         /**      * 创建指定内容背景色的Table元素Cell      * @param value      * @param font      * @param c1      * @param c2      * @param c3      * @return      */     public PdfPCell createCell(String value,Font font,int c1,int c2, int c3){         PdfPCell cell = new PdfPCell();         cell.setVerticalAlignment(Element.ALIGN_MIDDLE);         cell.setHorizontalAlignment(Element.ALIGN_CENTER);             cell.setPhrase(new Phrase(value,font));         cell.setBackgroundColor(new BaseColor(c1,c2,c3));         cell.setFixedHeight(33.33f);         cell.setBorder(0);        return cell;    }     /**      * 创建指定位置的Table元素Cell      * @param value      * @param font      * @param ele      * @return      */     public PdfPCell createCellLeft(String value,Font font,int ele){         PdfPCell cell = new PdfPCell();         cell.setVerticalAlignment(Element.ALIGN_MIDDLE);         cell.setHorizontalAlignment(ele);             cell.setPaddingLeft(10);         cell.setPhrase(new Phrase(value,font));        return cell;    }     /**      * 创建内容为Image的Table元素Cell      * @param image      * @param imgPercent      * @return      */     public PdfPCell createCellImage(Image image,float imgPercent){         image.scalePercent(imgPercent);         PdfPCell cell = new PdfPCell(image,false);         cell.setUseAscender(true);         cell.setUseDescender(true);         cell.setVerticalAlignment(Element.ALIGN_MIDDLE);         cell.setHorizontalAlignment(Element.ALIGN_CENTER);             cell.setPaddingLeft(10);         return cell;     }     /**      * 创建table元素cell      * @param image      * @param imgPercentWidth      * @param imgPercentHeight      * @return      */     public PdfPCell createCellImageTable(Image image,float imgPercentWidth,float imgPercentHeight){         image.scaleAbsoluteWidth(imgPercentWidth);         if(imgPercentHeight==410f){             image.scaleAbsoluteHeight(imgPercentHeight);              }                  PdfPCell cell = new PdfPCell(image,false);         cell.setVerticalAlignment(Element.ALIGN_MIDDLE);         cell.setHorizontalAlignment(Element.ALIGN_CENTER);         return cell;     }     /**      * 创建Table      * @param widths 列宽比例      * @return      */     public PdfPTable createTable(float[] widths){             for(int i=0;i<widths.length;i++){                 widths[i] = widths[i]*maxWidth;             }            PdfPTable table = new PdfPTable(widths);            try{                table.setTotalWidth(maxWidth);                table.setLockedWidth(true);                table.setHorizontalAlignment(Element.ALIGN_CENTER);                        table.getDefaultCell().setBorder(1);            }catch(Exception e){                e.printStackTrace();            }            return table;        }     /**      * 设置table参数      * @param table      * @param position      * @return      */     public PdfPTable useTable(PdfPTable table,int position){         try{             table.setTotalWidth(maxWidth);             table.setLockedWidth(true);             table.setHorizontalAlignment(position);                     table.getDefaultCell().setBorder(0);         }catch(Exception e){             e.printStackTrace();         }         return table;     }     /**      * 设置PdfTable行高      * @param table      * @param maxHeight      * @param maxWidth      * @return      */     public PdfPTable setTableHeightWeight(PdfPTable table,float maxHeight,float maxWidth){         table.setTotalWidth(maxWidth);         List<PdfPRow> list=new ArrayList<PdfPRow>();         list=table.getRows();         for(PdfPRow pr:list){             pr.setMaxHeights(maxHeight);         }         return table;     }    /**     * 根据SVG字符串得到一个输出流     * @param request     * @param response     * @param svg     * @return     * @throws Exception     */    public ByteArrayOutputStream highcharts(HttpServletRequest request,HttpServletResponse response,String svg){        try {            request.setCharacterEncoding("utf-8");// 注意编码            //转码防止乱码            byte[] arrayStr = svg.getBytes("utf-8");            svg = new String(arrayStr, "UTF-8");                        ByteArrayOutputStream stream = new ByteArrayOutputStream();                                 try {                 stream=this.transcode(stream, svg);            } catch (Exception e) {                e.printStackTrace();            }            return  stream;        } catch (UnsupportedEncodingException e) {            e.printStackTrace();            return null;        }             }        /**     * 对svg进行转码     * @param stream     * @param svg     * @return     * @throws Exception     */    public synchronized ByteArrayOutputStream transcode(ByteArrayOutputStream stream, String svg){        try {            TranscoderInput input = new TranscoderInput(new StringReader(svg));                        TranscoderOutput transOutput = new TranscoderOutput(stream);                        PNGTranscoder  transcoder = new PNGTranscoder();                                    transcoder.transcode(input, transOutput);            return stream;        } catch (TranscoderException e) {            e.printStackTrace();            return null;        }    }}
      PDF文档绘制工具类

      此工具类可以根据前端传来的svg信息,前文中提到的自定义position等属性,布局完成所要输出的PDF文档,因时间有限,不再一一赘述,有想研究的可以下载demo,我已做了一个demo供各位交流学习,下载地址:http://yun.baidu.com/share/link?shareid=2976350494&uk=657798452

原创粉丝点击