天天看點

java poi根據模闆生成docx

一、實作功能 

1、支援表格、圖像、插入頁腳,其中圖像包含base64 位和 隻傳入圖像路徑

2、要求使用對應的模闆 故隻能作為參考

3、裡面的以下判斷時根據 模闆檔案裡面的key進行的特殊處理

二、代碼(版本 3.15)

package com.hyzs.szcg.doc.utils;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.hyzs.gz.common.core.exception.CommonException;
import com.hyzs.szcg.doc.vo.DocWordUtilVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import javax.imageio.ImageIO;



/**
 *
 * @Title: WordExport.java
 * @Description:  導出word文檔
 * @author zzh
 * @version 1.0
 */
@Slf4j
@Component
public class PoiExportWord {
    @Value("${apiKey}")
    private String apiKey;
    private String templatePath;
    private XWPFDocument doc = null;
    private FileInputStream is = null;
    private OutputStream os = null;
    private DocWordUtilVO docWordUtilVO=new DocWordUtilVO();
    public DocWordUtilVO init(Map<String,Object> params,String templatePath,String targetPath) throws IOException {
        docWordUtilVO.setWordFlag(false);
        this.templatePath = templatePath;
        File file=new File(this.templatePath);
        File fileTarget=new File(targetPath);
        if (!fileTarget.getParentFile().exists()){
            //目錄不存在則建立目錄
            fileTarget.getParentFile().mkdirs();
        }
        //插入目前時間  年月日
        insertDate(params);
        if(file.exists()){
            is = new FileInputStream(file);
            doc = new XWPFDocument(is);
            try {
                if(export(params)){
                    docWordUtilVO.setWordFlag(generate(targetPath));
                    docWordUtilVO.setWordPath(targetPath);
                }
            } catch (Exception e) {
                throw CommonException.exception("文書生成失敗!",e);
            }
        }else{
           // log.error("io error -> 檔案模闆不存在》"+this.templatePath);
            throw CommonException.exception("io error 模闆檔案不存在");
        }
        return docWordUtilVO;
    }
    /**
     * 替換掉占位符
     * @param params
     * @return
     * @throws Exception
     */
    public boolean export(Map<String,Object> params) throws Exception{
        this.replaceInPara(doc, params);
        export(params, 0);//表格資料
        return true;
    }

    /**
     * 替換掉表格中的占位符
     * @param params
     * @param tableIndex
     * @return
     * @throws Exception
     */
    public boolean export(Map<String,Object> params,int tableIndex) throws Exception{
        //動态插入表格清單資料
        this.insertTable(params);
        this.replaceInTable(doc, params,tableIndex);
        return true;
    }
    /**
     * 循環生成表格
     * @param params
     * @return
     * @throws Exception
     */
    public boolean insertTable(Map<String,Object> params) throws Exception{
        this.insertValueToTable(doc, params);
        return true;
    }
    private void insertValueToTable(XWPFDocument doc,Map<String, Object> params) throws Exception {
        List<XWPFTable> tableList = doc.getTables();
        for (int t=0;t<tableList.size();t++){
            //循環表格
            XWPFTable table = tableList.get(t);
            List<XWPFTableRow> rows = table.getRows();
            int temRowNum=0;
            String key="";//表格 清單資料的 key
            for (int j = 0; j <rows.size() ; j++) {
                String rowText = rows.get(j).getCell(0).getText();// 擷取到表格行的第一個單元格
                //确定辨別行的位置 辨別行的下一行就是模闆行  模闆行 以 foreachRows_開頭   列 ${foreachRows_giveDocList} giveDocList為表單的key
                boolean begin = rowText.indexOf("$")>-1;
                boolean end = rowText.indexOf("}")>-1;
                if(begin&&end){
                    Matcher matcher = this.matcher(rowText);
                    if(matcher.find()){
                        String keyText = matcher.group().substring(2,matcher.group().length()-1);
                        if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("foreachRows_")){
                            temRowNum=j;
                            key=StringUtils.removeStart(keyText,"foreachRows_");
                            break;
                        }

                    }
                }

            }
            if(rows.size()<2){
               break;
            }
            if(temRowNum==0&&"".equals(key)){
                //該表格不需要動态生成
                break;
            }
            //模闆行
            XWPFTableRow tmpRow = rows.get(temRowNum+1);
            //擷取清單資料
            Object value=params.get(key);
            List<Map<String,Object>> listData=new ArrayList<>();
            if(value instanceof List){
                listData=(List<Map<String,Object>>)value;
            }
            Map m=new HashMap();
            m.putAll(params);
            m.remove(key);
            for (int i = 0; i < listData.size(); i++) {
                Map<String,Object> map = listData.get(i);
                map.putAll(m);
                map.put("no",i+1);//添加表格序号
                //------------------------------------
                /* 循環生成模闆行 */
                //在表格指定位置新增一行
                XWPFTableRow targetRow = table.insertNewTableRow(temRowNum+2+i);
                copyRow(targetRow,tmpRow);
                List<XWPFTableCell> cells;
                List<XWPFParagraph> paras;
                cells = targetRow.getTableCells();
                for (XWPFTableCell cell : cells) {
                    paras = cell.getParagraphs();
                    for (XWPFParagraph para : paras) {
                        this.replaceInPara(para, map);
                    }
                }
            }
            table.removeRow(temRowNum);// 删除辨別行
            table.removeRow(temRowNum);// 删除模版行
        }

    }
  /**
     * 生成word文檔
     * @param outDocPath
     * @return
     * @throws IOException
     */
    public boolean generate(String outDocPath){
        try {
            createFooter();
            os = new FileOutputStream(outDocPath);
            doc.write(os);
        } catch (FileNotFoundException e) {
            throw CommonException.exception("文書生成失敗!",e);
        }catch (IOException e) {
            throw CommonException.exception("文書生成失敗!",e);
        }finally {
            try {
                doc.close();
                this.close(os);
                this.close(is);
            } catch (IOException e) {
                throw CommonException.exception("文書IO流關閉失敗!",e);
            }
        }
        return true;
    }

    /**
     * 替換表格裡面的變量
     *
     * @param doc
     *            要替換的文檔
     * @param params
     *            參數
     * @throws Exception
     */
    private void replaceInTable(XWPFDocument doc, Map<String, Object> params,int tableIndex) throws Exception {
        List<XWPFTable> tableList = doc.getTables();
        if(tableList.size()>tableIndex){
            for (int i=0;i<tableList.size();i++){
                XWPFTable table = tableList.get(i);
               List<XWPFTableRow> rows;
                List<XWPFTableCell> cells;
                List<XWPFParagraph> paras;
                rows = table.getRows();
                for (XWPFTableRow row : rows) {
                    cells = row.getTableCells();
                    for (XWPFTableCell cell : cells) {
                        paras = cell.getParagraphs();
                        for (XWPFParagraph para : paras) {
                            this.replaceInPara(para, params);
                        }
                    }
                }
            }

        }
    }

    /**
     * 替換段落裡面的變量
     *
     * @param doc
     *            要替換的文檔
     * @param params
     *            參數
     * @throws Exception
     */
    private void replaceInPara(XWPFDocument doc, Map<String, Object> params) throws Exception {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            this.replaceInPara(para, params);
        }
    }

    /**
     * 替換段落裡面的變量
     *
     * @param para
     *            要替換的段落
     * @param params
     *            參數
     * @throws Exception
     * @throws IOException
     * @throws
     */
    private boolean replaceInPara(XWPFParagraph para, Map<String, Object> params) throws Exception {
        boolean data = false;
        List<XWPFRun> runs;
        //有符合條件的占位符
        if (this.matcher(para.getParagraphText()).find()) {
            runs = para.getRuns();
            data = true;
            Map<Integer,String> tempMap = new TreeMap<>();
            for (int i = 0; i < runs.size(); i++) {
                XWPFRun run = runs.get(i);
                String runText = run.toString();
                //以"$"開頭
                boolean begin = runText.indexOf("$")>-1;
                boolean end = runText.indexOf("}")>-1;
                if(begin && end){
                    tempMap.put(i, runText);
                    fillBlock(para, params, tempMap, i);
                    continue;
                }else if(begin && !end){
                    tempMap.put(i, runText);
                    continue;
                }else if(!begin && end){
                    tempMap.put(i, runText);
                    fillBlock(para, params, tempMap, i);
                    continue;
                }else{
                    if(tempMap.size()>0){
                        tempMap.put(i, runText);
                        continue;
                    }
                    continue;
                }
            }
        } else if (this.matcherRow(para.getParagraphText())) {
            runs = para.getRuns();
            data = true;
        }
        return data;
    }

    /**
     * 填充run内容
     * @param para
     * @param params
     * @param tempMap
     * @param
     * @param
     * @throws
     * @throws IOException
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private void fillBlock(XWPFParagraph para, Map<String, Object> params,
                           Map<Integer, String> tempMap, int index)
            throws InvalidFormatException, IOException, Exception {
        Matcher matcher;
        if(tempMap!=null&&tempMap.size()>0){
            String wholeText = "";
            List<Integer> tempIndexList = new ArrayList<Integer>();
            for(Map.Entry<Integer, String> entry :tempMap.entrySet()){
                tempIndexList.add(entry.getKey());
                wholeText+=entry.getValue();
            }
            if(wholeText.equals("")){
                return;
            }
            matcher = this.matcher(wholeText);
            if (matcher.find()) {
                boolean isPic = false;//圖檔辨別
                boolean isBasePic = false;//base64圖檔辨別
                boolean isTencentPic = false;//騰訊地圖圖檔
                boolean isUnderLine=false;//下劃線辨別
                boolean checkbox=false;//複選框辨別
                boolean isPicList=false;//傳入多張圖檔辨別
                List<Map<String,Object>> images=new ArrayList<>();
                ByteArrayInputStream inputStream=null;
                int width = 0;
                int height = 0;
                int picType = 0;
                String path = null;
                String keyText = matcher.group().substring(2,matcher.group().length()-1);
                String newRunText = "";
                Object value = params.get(keyText);
                if(value instanceof String){
                    newRunText = matcher.replaceFirst(String.valueOf(value));
                }else if(value instanceof Map){
                    Map pic = (Map)value;
                    if(pic.containsKey("fileSuffix")&&pic.containsKey("filePath")){
                        //插入圖檔
                        isPic = true;
                        path = pic.get("filePath").toString();
                        //擷取圖檔大小
                        BufferedImage sourceImg = ImageIO.read(getPicStream(path));
                        width=sourceImg.getWidth();
                        height=sourceImg.getHeight();
                        if(width>200){
                            width=200;
                        }
                        if(height>200){
                            height=200;
                        }
                        if(keyText.endsWith("Sign")||keyText.endsWith("_sign")){//簽名限制大小
                            width=50;
                            height=30;
                        }
                        picType=sourceImg.getType();
                    }else if(pic.containsKey("personalSignature")){
                        // base64字元串的 圖檔形式
                        isBasePic=true;
                        //将字元串轉換為byte數組
                        String file= (String) pic.get("personalSignature");
                        file=file.replaceFirst("data:image/png;base64,","");
                        byte[] bytes = new BASE64Decoder().decodeBuffer(file.trim());
                        //轉化為輸入流
                        inputStream = new ByteArrayInputStream(bytes);
                        //擷取圖檔大小
                        BufferedImage sourceImg = ImageIO.read(new ByteArrayInputStream(bytes));
                       width = sourceImg.getWidth();
                        height = sourceImg.getHeight();
                        if(width>200){
                            width=200;
                        }
                        if(height>200){
                            height=200;
                        }
                        if(keyText.endsWith("Sign")||keyText.endsWith("_sign")){//簽名限制大小
                            width=50;
                            height=30;
                        }
                        picType=sourceImg.getType();
                    }else if(pic.containsKey("name")&&pic.containsKey("address")&&pic.containsKey("latitude")){
                        //是一個 位址
                        newRunText = matcher.replaceFirst(String.valueOf(pic.get("name")));
                    }
                }else if(value instanceof List){
                    //在表單中為下拉選擇  在模闆中為文本框
                    List list=(List)value;
                    if(list.size()>0){
                        if(list.get(0) instanceof String){
                            String listStr=String.join(",",list);
                            newRunText = matcher.replaceFirst(String.valueOf(listStr));
                        }else if(list.get(0) instanceof Map){
                            Map m=(Map) list.get(0);
                            if(m.containsKey("filePath")&&m.containsKey("fileSuffix")&&m.containsKey("fileName")) {
                                //是圖檔
                                //插入圖檔
                                isPicList=true;
                                //images
                                for (int i = 0; i < list.size(); i++) {
                                    Map image=(Map) list.get(i);
                                    //擷取圖檔大小
                                    BufferedImage sourceImg = ImageIO.read(getPicStream(image.get("filePath").toString()));
                                    Map<String,Object> map=new HashMap<>();
                                    map.put("path",image.get("filePath").toString());
                                    map.put("width",400);
                                    map.put("height",250);
                                    map.put("picType",sourceImg.getType());
                                    images.add(map);
                                }
                            }else if(m.containsKey("personalSignature")){
                                // base64字元串的 圖檔形式
                                isBasePic=true;
                                //将字元串轉換為byte數組
                                String file= (String) m.get("personalSignature");
                                byte[] bytes = new BASE64Decoder().decodeBuffer(file.trim());
                                //轉化為輸入流
                                inputStream = new ByteArrayInputStream(bytes);
                                //擷取圖檔大小
                                BufferedImage sourceImg = ImageIO.read(new ByteArrayInputStream(bytes));
                                width = sourceImg.getWidth();
                                height = sourceImg.getHeight();
                                if(width>200){
                                    width=200;
                                }
                                if(height>200){
                                    height=200;
                                }
                                if(keyText.endsWith("Sign")||keyText.endsWith("_sign")){//簽名限制大小
                                    width=50;
                                    height=30;
                                }
                                picType=sourceImg.getType();
                            }else if(m.containsKey("q")&&m.containsKey("a")){
                                int size=68;
                                //問答形式
                                StringBuffer str=new StringBuffer();
                                for (int i=0;i<list.size();i++){
                                    Map map=(Map) list.get(i);
                                    if(i!=0){
                                        str.append("\n");
                                    }
                                    int lengthQ=getLength(map.get("q").toString());
                                    str.append("問:").append(map.get("q"));
                                    int t;
                                    if(lengthQ>size){
                                        t=3;
                                        if(lengthQ%2==0){
                                            t++;
                                        }
                                    }else{
                                        t=3;
                                        if(lengthQ%2==1){
                                            //t--;
                                        }
                                    }
                                    for (int j = t; (j <size-lengthQ%size)&&(lengthQ%size!=0); j++) {
                                        if(j ==((size-1)-lengthQ%size)){
                                            str.append("_");
                                        }else{
                                            str.append(" ");
                                        }
                                    }
                                    int lengthA=getLength(map.get("a").toString());
                                    if(lengthA>size){
                                        t=3;
                                        if(lengthA%2==0){
                                            t++;
                                        }
                                    }else{
                                        t=3;
                                        if(lengthA%2==1){
                                           // t--;
                                        }
                                    }
                                    str.append("\n答:").append(map.get("a"));
                                    for (int j = t; (j <size-lengthA%size)&&(lengthA%size!=0); j++) {
                                        if(j ==((size-1)-lengthA%size)){
                                            str.append("_");
                                        }else{
                                            str.append(" ");
                                        }
                                    }
                                }
                                if(str.length()>0){
                                    isUnderLine=true;
                                    newRunText = matcher.replaceFirst(str.toString());
                                }
                            }else{
                                StringBuffer listStr=new StringBuffer();
                                for (int i = 0; i <list.size() ; i++) {
                                    Map map=(Map) list.get(i);
                                    if(map.containsKey(keyText+"Label")){
                                        listStr.append(map.get(keyText+"Label")).append(",");
                                    }
                                }
                                if(listStr.length()>0){
                                    newRunText = matcher.replaceFirst(listStr.toString().substring(0,listStr.length()-1));
                                }
                            }

                        }

                    }

                }else if(value == null){
                    //如果value為null 則說明在from表單中沒有傳該值
                    //判斷 keyText 是否是複選框  key 以 flag 作為字首
                    if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("flag_")){
                        //判斷  params 是否包含  keyText這個Key 若 包含
                        //截取字元串 列如 flag_sourceType_1  ---> sourceType
                        //在表單中為下拉選擇  在模闆中為複選框
                        String str[]=keyText.split("_");
                        boolean flag=false;
                        checkbox=true;
                        if(str.length>2){
                            //String key=str[2];//StringUtils.substringBetween(keyText,"_","_");
                            String s[]=new String[str.length-2];
                            int t=0;
                            for (int i = 0; i <str.length ; i++) {
                                if(i!=0&&i!=str.length-1){
                                    s[t]=str[i];
                                    t++;
                                }
                            }
                            String key=StringUtils.join(s, "_");
                            Object v=params.get(key);
                            if(v instanceof List){
                                List list=(List)v;
                                for (int i = 0; i <list.size() ; i++) {
                                    if(list.get(i) instanceof Map){
                                        Map map=(Map) list.get(i);
                                        if(map.containsKey(key)){
                                            if(map.get(key).toString().equals(str[str.length-1])){
                                                //說明此值被選中
                                                flag=true;
                                                break;
                                            }
                                        }
                                    }else{
                                        if (list.get(i).toString().equals(str[str.length-1])){
                                            //說明此值被選中
                                            flag=true;
                                            break;
                                        }
                                    }
                                }
                            }else if(v instanceof String){
                                if(v.toString().equals(str[str.length-1])){
                                    flag=true;
                                }
                            }
                        }
                        if (flag){
                            newRunText = matcher.replaceFirst("■");//■
                        }else{
                            newRunText = matcher.replaceFirst("□");//
                        }
                    }else if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("signDate_")){
                        //以signDate_作為字首的是日期 表單裡面的日期時間
                        String key=StringUtils.substring(keyText,9);//取出日期對應的圖檔的key
                        if(params.get(key)!=null){
                            Object o=params.get(key);
                            if(o instanceof List){
                                String date = new SimpleDateFormat("yyyy年MM月dd日").format(new Date());
                                newRunText = matcher.replaceFirst(date);
                                params.put(keyText,date);
                            }
                            if(o instanceof Map){
                                String date = new SimpleDateFormat("yyyy年MM月dd日").format(new Date());
                                newRunText = matcher.replaceFirst(date);
                                params.put(keyText,date);
                            }
                            if(o instanceof String){//非圖檔簽名
                                String date = new SimpleDateFormat("yyyy年MM月dd日").format(new Date());
                                newRunText = matcher.replaceFirst(date);
                                params.put(keyText,date);
                            }
                        }
                    }else if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("isNull_")){
                        //以isNull_作為字首的是當事人證件類型号碼
                        String key=StringUtils.substring(keyText,7);//取出号碼的key
                        String[] keys=key.split("_");
                        if(keys.length>1){
                            boolean flag=false;
                            for (int i = 0; i <keys.length ; i++) {
                                String k=keys[i];
                                if(params.get(k)!=null){
                                    Object o=params.get(k);
                                    if(o instanceof String){
                                        flag=true;
                                        break;
                                    }

                                }
                            }
                            if(flag){
                                newRunText = matcher.replaceFirst("■");
                            }else{
                                newRunText = matcher.replaceFirst("□");
                            }
                        }else{
                        if(params.get(key)!=null){
                            Object o=params.get(key);
                            if(o instanceof String){
                                newRunText = matcher.replaceFirst("■");
                            }
                        }else{
                            newRunText = matcher.replaceFirst("□");
                        }
                        }
                    }else if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("isTime_")){
                        //轉化時間格式
                        String key=StringUtils.substring(keyText,7);//取出時間的key
                        //格式轉化
                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日HH時mm分");
                        SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        SimpleDateFormat formatter2 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                        SimpleDateFormat formatter3 = new SimpleDateFormat("HH:mm");
                        SimpleDateFormat formatter4 = new SimpleDateFormat("HH時mm分");
                        SimpleDateFormat formatter5 = new SimpleDateFormat("yyyy年MM月dd日");
                        SimpleDateFormat formatter6 = new SimpleDateFormat("yyyy-MM-dd");
                        if(params.get(key)!=null&&!"".equals(params.get(key))&&isValidDate(params.get(key).toString(),"yyyy-MM-dd HH:mm:ss")){
                            //判斷時間格式是否正确
                            Date date = null;
                            try {
                                date = formatter1.parse(params.get(key).toString());
                                newRunText = matcher.replaceFirst(formatter.format(date));
                            } catch (ParseException e) {
                                newRunText = matcher.replaceFirst(params.get(key)==null?"":params.get(key).toString());
                            }
                        }else if(params.get(key)!=null&&!"".equals(params.get(key))&&isValidDate(params.get(key).toString(),"yyyy-MM-dd HH:mm")){
                            //判斷時間格式是否正确
                            Date date = null;
                            try {
                                date = formatter2.parse(params.get(key).toString());
                                newRunText = matcher.replaceFirst(formatter.format(date));
                            } catch (ParseException e) {
                                newRunText = matcher.replaceFirst(params.get(key)==null?"":params.get(key).toString());
                            }
                        }else if(params.get(key)!=null&&!"".equals(params.get(key))&&isValidDate(params.get(key).toString(),"yyyy-MM-dd")){
                            //判斷時間格式是否正确
                            Date date = null;
                            try {
                                date = formatter6.parse(params.get(key).toString());
                                newRunText = matcher.replaceFirst(formatter5.format(date));
                            } catch (ParseException e) {
                                newRunText = matcher.replaceFirst(params.get(key)==null?"":params.get(key).toString());
                            }
                        }else if(params.get(key)!=null&&!"".equals(params.get(key))&&isValidDate(params.get(key).toString(),"HH:mm")){
                            //判斷時間格式是否正确
                            Date date = null;
                            try {
                                date = formatter3.parse(params.get(key).toString());
                                newRunText = matcher.replaceFirst(formatter4.format(date));
                            } catch (ParseException e) {
                                newRunText = matcher.replaceFirst(params.get(key)==null?"":params.get(key).toString());
                            }
                        }
                        else if(key!=null&&!"".equals(key)&&(key.endsWith("_yyyy")||key.endsWith("_MM")||key.endsWith("_dd"))){
                            //把前端傳入的日期(yyyy-MM-dd)拆為  yyyy MM dd等
                            String dateStr="yyyy";
                            if(key.endsWith("_yyyy")){
                                key=StringUtils.removeEnd(key,"_yyyy");
                                dateStr="yyyy";
                            }else if(key.endsWith("_MM")){
                                key=StringUtils.removeEnd(key,"_MM");
                                dateStr="MM";
                            }else if(key.endsWith("_dd")){
                                key=StringUtils.removeEnd(key,"_dd");
                                dateStr="dd";
                            }
                            SimpleDateFormat formatter7 = new SimpleDateFormat(dateStr);
                            if(params.get(key)!=null&&!"".equals(params.get(key))) {
                                Date date=null;
                                try{
                                    date=formatter6.parse(params.get(key).toString());
                                    newRunText = matcher.replaceFirst(formatter7.format(date));
                                }catch (ParseException e){
                                    newRunText = matcher.replaceFirst("");
                                }
                            }
                        }
                        else{
                            newRunText = matcher.replaceFirst(params.get(key)==null?"":params.get(key).toString());
                        }
                    }else if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("isTencentApi_")){
                        //以isTencentApi_作為字首的是 小程式給坐标點 背景調用騰訊api擷取圖檔寫入文書
                        String key=StringUtils.removeStart(keyText,"isTencentApi_");
                        if(params.get(key)!=null&&!"".equals(params.get(key))){
                             Object v=params.get(key);
                             if (v instanceof Map){
                                 Map m=(Map)v;
                                 if(m.containsKey("name")&&m.containsKey("address")&&m.containsKey("latitude")&&m.containsKey("longitude")){
                                     StringBuilder stringBuffer = new StringBuilder();
                                     String latitude=m.get("latitude").toString();
                                     String longitude=m.get("longitude").toString();
                                     try {
                                         stringBuffer.append("zoom=18");
                                         stringBuffer.append("&markers=color:blue"+ URLEncoder.encode("|", "utf-8")+"label:A"+URLEncoder.encode("|", "utf-8")+latitude+","+longitude);
                                         stringBuffer.append("&key="+apiKey);
                                         stringBuffer.append("&size=400*300");
                                         stringBuffer.append("&format=png");
                                         stringBuffer.append("&maptype=roadmap");
                                     } catch (UnsupportedEncodingException e) {
                                         log.error("文書調用騰訊api失敗");
                                         throw CommonException.exception("文書調用騰訊api失敗!",e);
                                     }
                                        byte[] bytes=TencentApiUtil.doGetHttps(stringBuffer.toString());
                                        if(bytes!=null&&bytes.length>0){
                                            //轉化為輸入流
                                            inputStream = new ByteArrayInputStream(bytes);
                                            isTencentPic=true;
                                            width=400;
                                            height=300;
                                            picType=XWPFDocument.PICTURE_TYPE_PNG;
                                        }
                                 }
                             }
                        }
                    }else if (keyText!=null&&!"".equals(keyText)&&keyText.startsWith("isUnderLine_")){
                        //isUnderLine_ 開頭的需要一整行下劃線   列 :  isUnderLine_4_isAvoid
                        String keyS="isUnderLine_";
                        String key=StringUtils.removeStart(keyText,keyS);//  列 4_isAvoid
                        //取出變量前面有幾個位元組
                        String str1=key.substring(0, key.indexOf("_"));
                        int count=0;
                        if(StringUtils.isNumeric(str1)){
                             count=Integer.parseInt(str1);//列如  4
                        }
                        key=StringUtils.removeStart(key,count+"_");
                        if(params.get(key)!=null&&!"".equals(params.get(key))){
                            Object v=params.get(key);
                            String contextStr="";
                            if (v instanceof String || v instanceof Map){
                                int size=68;
                                StringBuffer str=new StringBuffer();
                                if (v instanceof String){
                                    contextStr=v.toString();
                                    str.append(v.toString());
                                }else if(v instanceof Map){
                                    //前端傳入的位址
                                    Map m=(Map)v;
                                    if(m.containsKey("name")&&m.containsKey("address")&&m.containsKey("latitude")&&m.containsKey("longitude")){
                                        str.append(m.get("address").toString());
                                        contextStr=m.get("address").toString();
                                    }
                                }
                                int length=getLength(contextStr)+count;
                                int t=0;
                                for (int j = t; (j <size-length%size)&&(length%size!=0); j++) {
                                    if(j ==((size-1)-length%size)){
                                        str.append("_");
                                    }else{
                                        str.append(" ");
                                    }
                                }
                                if(str.length()>0){
                                    isUnderLine=true;
                                    newRunText = matcher.replaceFirst(str.toString());
                                }
                            }
                        }
                    }else if (keyText!=null&&!"".equals(keyText)&&keyText.startsWith("currentTime")){
                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日HH時mm分");
                        String dateStr=formatter.format(new Date());
                        newRunText = matcher.replaceFirst(dateStr);
                    }else if (keyText!=null&&!"".equals(keyText)&&keyText.startsWith("currentDate")){
                        SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日");
                        String dateStr=formatter.format(new Date());
                        newRunText = matcher.replaceFirst(dateStr);
                    }else if (keyText!=null&&!"".equals(keyText)&&keyText.startsWith("signature")){
                        docWordUtilVO.setSignFlag(true);
                        docWordUtilVO.setKeyWord(".sign~");
                        newRunText = matcher.replaceFirst(".sign~");
                    }else if (keyText!=null&&!"".equals(keyText)&&keyText.equals("other")){
                        newRunText = matcher.replaceFirst("       _");
                    }
                }
                //模闆樣式
                XWPFRun tempRun = null;
                // 直接調用XWPFRun的setText()方法設定文本時,在底層會重新建立一個XWPFRun,把文本附加在目前文本後面,
                // 是以我們不能直接設值,需要先删除目前run,然後再自己手動插入一個新的run。
                for(Integer pos : tempIndexList){
                    tempRun = para.getRuns().get(pos);
                    tempRun.setText("", 0);
                }
                if(isPic){
                    //addPicture方法的最後兩個參數必須用Units.toEMU轉化一下
                    //para.insertNewRun(index).addPicture(getPicStream(path), picType, "測試",Units.toEMU(width), Units.toEMU(height));
                    tempRun.addPicture(getPicStream(path), picType, "圖檔",Units.toEMU(width), Units.toEMU(height));
                }else if(isPicList){
                    for (int i = 0; i <images.size() ; i++) {
                        tempRun.addPicture(getPicStream(images.get(i).get("path").toString()), Integer.parseInt(images.get(i).get("picType").toString()), "圖檔",Units.toEMU(Integer.parseInt(images.get(i).get("width").toString())), Units.toEMU(Integer.parseInt(images.get(i).get("height").toString())));
                        tempRun.addBreak();
                    }
                }else if(isBasePic){
                    //base64圖檔加載
                    tempRun.addPicture(inputStream, picType, "圖檔",Units.toEMU(width), Units.toEMU(height));
                }else if(isTencentPic){
                    //地圖選點通過騰訊api擷取的圖檔
                    tempRun.addPicture(inputStream, picType, "圖檔",Units.toEMU(width), Units.toEMU(height));
                }else{
                    //樣式繼承
                    if(newRunText.indexOf("\n")>-1){
                        String[] textArr = newRunText.split("\n");
                        if(textArr.length>0){
                            //設定字型資訊
                            String fontFamily = tempRun.getFontFamily();
                            int fontSize = tempRun.getFontSize();
                            //logger.info("------------------"+fontSize);
                            for(int i=0;i<textArr.length;i++){
                                if(i==0){
                                    tempRun.setText(textArr[0],0);
                                }else{
                                    if(StringUtils.isNotEmpty(textArr[i])){
                                        XWPFRun newRun=para.createRun();
                                        //設定新的run的字型資訊
                                        newRun.setFontFamily(fontFamily);
                                        if(isUnderLine){//設定下滑線
                                            newRun.setUnderline(UnderlinePatterns.SINGLE);
                                        }
                                        if(fontSize==-1){
                                            newRun.setFontSize(10);
                                        }else{
                                            newRun.setFontSize(fontSize);
                                        }
                                        if(checkbox){
                                            newRun.setFontSize(20);
                                        }
                                        newRun.addBreak();
                                        newRun.setText(textArr[i], 0);
                                    }
                                }
                            }
                        }
                    }else{
                        tempRun.setText(newRunText,0);
                    }
                }
            }
            tempMap.clear();
        }
    }
    /**
     * 正則比對字元串
     *
     * @param str
     * @return
     */
    private Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}",
                Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 正則比對字元串
     *
     * @param str
     * @return
     */
    private boolean matcherRow(String str) {
        Pattern pattern = Pattern.compile("\\$\\[(.+?)\\]",
                Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher.find();
    }

    private InputStream getPicStream(String picPath) throws Exception{
        //FileInputStream fis = new FileInputStream(new File(picPath));
       URL url = new URL(picPath);
        //打開連結
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        //設定請求方式為"GET"
        conn.setRequestMethod("GET");
        //逾時響應時間為20秒
        conn.setConnectTimeout(20 * 1000);
        //通過輸入流擷取圖檔資料
        InputStream fis = conn.getInputStream();

        return fis;
    }

    /**
     * 關閉輸入流
     *
     * @param is
     */
    private void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                log.error("文書關閉輸入流失敗");
                throw CommonException.exception("文書關閉輸入流失敗!",e);            }
        }
    }

    /**
     * 關閉輸出流
     *
     * @param os
     */
    private void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                log.error("文書關閉輸出流失敗");
                throw CommonException.exception("文書關閉輸入流失敗!",e);                }
        }
    }
    /**
     * 判斷時間格式是否正确
     * @param
     * @return
     */
    public static boolean isValidDate(String strDate,String strFormat) {
        SimpleDateFormat formatter = new SimpleDateFormat(strFormat);
        try{
            Date date = formatter.parse(strDate);
            String s=formatter.format(date);
            return strDate.equals(formatter.format(date));
        }catch(Exception e){
            return false;
        }
    }

    public void copyRow(XWPFTableRow targetRow,XWPFTableRow sourceRow){
        //複制行屬性
        targetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr());
        List<XWPFTableCell> cellList = sourceRow.getTableCells();
        if (null == cellList) {
            return;
        }
        //複制列及其屬性和内容
        XWPFTableCell targetCell = null;
        for (XWPFTableCell sourceCell : cellList) {
            targetCell = targetRow.addNewTableCell();
            //列屬性
            targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
            //段落屬性
            if(sourceCell.getParagraphs()!=null&&sourceCell.getParagraphs().size()>0){
                targetCell.getParagraphs().get(0).getCTP().setPPr(sourceCell.getParagraphs().get(0).getCTP().getPPr());
                if(sourceCell.getParagraphs().get(0).getRuns()!=null&&sourceCell.getParagraphs().get(0).getRuns().size()>0){
                    XWPFRun cellR = targetCell.getParagraphs().get(0).createRun();
                    cellR.setText(sourceCell.getText());
                    cellR.setBold(sourceCell.getParagraphs().get(0).getRuns().get(0).isBold());
                }else{
                    targetCell.setText(sourceCell.getText());
                }
            }else{
                targetCell.setText(sourceCell.getText());
            }
        }
    }
    public static int getLength(String s) {
        int length = 0;
        for (int i = 0; i < s.length(); i++) {
            int ascii = Character.codePointAt(s, i);
            if (ascii >= 0 && ascii <= 255) {
                length++;
            } else {
                length += 2;
            }
        }
        return length;
    }
    //插入年月日
    private void insertDate(Map<String, Object> params) {
        Calendar calendar = Calendar.getInstance();
        params.put("nowYear",calendar.get(Calendar.YEAR)+"");
        params.put("nowMonth",(calendar.get(Calendar.MONTH+1)<10)?"0"+calendar.get(Calendar.MONTH+1):calendar.get(Calendar.MONTH+1)+"");
        params.put("nowDay",(calendar.get(Calendar.DAY_OF_MONTH)<10)?"0"+calendar.get(Calendar.DAY_OF_MONTH):calendar.get(Calendar.DAY_OF_MONTH)+"");
        params.put("nowHour",(calendar.get(Calendar.HOUR_OF_DAY)<10)?"0"+calendar.get(Calendar.HOUR_OF_DAY):calendar.get(Calendar.HOUR_OF_DAY)+"");
        params.put("nowMinute",(calendar.get(Calendar.MINUTE)<10)?"0"+calendar.get(Calendar.MINUTE):calendar.get(Calendar.MINUTE)+"");
        params.put("nowSecond",(calendar.get(Calendar.SECOND)<10)?"0"+calendar.get(Calendar.SECOND):calendar.get(Calendar.SECOND)+"");
    }
    //生成頁腳
    public void createFooter(){
        /*
         * 生成頁腳段落
         * 給段落設定寬度為占滿一行
         * */
        CTSectPr sectPr=doc.getDocument().getBody().addNewSectPr();
        XWPFHeaderFooterPolicy headerFooterPolicy=new XWPFHeaderFooterPolicy(doc,sectPr);
        try {
            XWPFFooter footer=headerFooterPolicy.createFooter(STHdrFtr.DEFAULT);
            XWPFParagraph paragraph = footer.getParagraphArray(0);
            paragraph.setAlignment(ParagraphAlignment.LEFT);
            paragraph.setVerticalAlignment(TextAlignment.CENTER);
            paragraph.setBorderTop(Borders.THICK);
            CTTabStop tabStop = paragraph.getCTP().getPPr().addNewTabs().addNewTab();
            tabStop.setVal(STTabJc.RIGHT);
            int twipsPerInch =  1440;
            tabStop.setPos(BigInteger.valueOf(6 * twipsPerInch));
            /*
             * 給段落建立元素
             * */
            XWPFRun run = paragraph.createRun();
            setXWPFRunStyle(run,"宋體",8);
            run.addTab();
            String s=run.getFontFamily();
            /*
             * 生成頁碼
             * 頁碼右對齊
             * */
            run = paragraph.createRun();
            run.setText("第");
            setXWPFRunStyle(run,"宋體",8);
            run = paragraph.createRun();
            CTFldChar fldChar = run.getCTR().addNewFldChar();
            fldChar.setFldCharType(STFldCharType.Enum.forString("begin"));
            setXWPFRunStyle(run,"宋體",8);
            run = paragraph.createRun();
            CTText ctText = run.getCTR().addNewInstrText();
            ctText.setStringValue("PAGE  \\* MERGEFORMAT");
            ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve"));
            setXWPFRunStyle(run,"宋體",8);

            fldChar = run.getCTR().addNewFldChar();
            fldChar.setFldCharType(STFldCharType.Enum.forString("end"));

            run = paragraph.createRun();
            run.setText("頁/共");
            setXWPFRunStyle(run,"宋體",8);

            run = paragraph.createRun();
            fldChar = run.getCTR().addNewFldChar();
            fldChar.setFldCharType(STFldCharType.Enum.forString("begin"));

            run = paragraph.createRun();
            ctText = run.getCTR().addNewInstrText();
            ctText.setStringValue("NUMPAGES  \\* MERGEFORMAT ");
            ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve"));
            setXWPFRunStyle(run,"宋體",8);

            fldChar = run.getCTR().addNewFldChar();
            fldChar.setFldCharType(STFldCharType.Enum.forString("end"));

            run = paragraph.createRun();
            run.setText("頁");
            setXWPFRunStyle(run,"宋體",8);
        } catch (IOException e) {
            throw CommonException.exception("頁腳設定失敗!",e);
        }
    }
    /**
     * 設定頁腳的字型樣式
     *
     * @param r1 段落元素
     */
    private void setXWPFRunStyle(XWPFRun r1,String font,int fontSize) {
        r1.setFontSize(fontSize);
        CTRPr rpr = r1.getCTR().isSetRPr() ? r1.getCTR().getRPr() : r1.getCTR().addNewRPr();
        CTFonts fonts = rpr.isSetRFonts() ? rpr.getRFonts() : rpr.addNewRFonts();
        fonts.setAscii(font);
        fonts.setEastAsia(font);
        fonts.setHAnsi(font);
    }
}