导出富文本到Word

pom层

<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.13.1</version>
</dependency>
<dependency>
    <groupId>org.docx4j</groupId>
    <artifactId>docx4j</artifactId>
    <version>3.3.6</version>
</dependency>
<!--word操作工具类-->
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.6.0-beta1</version>
</dependency>

application-dev.yml

# 模板导出相关  本地环境
word:
  temDir: E:/test # 临时文件夹
  titleUrl: word/content.docx   # 富文本模板地址

在这里插入图片描述

模板:
在这里插入图片描述

controller层
可以自定义<html> 在Enum层 和Util配置好对应的格式

public class ExcelController {

    @Value("${word.temDir}")
    private String exportWordTemDir;
    
    @Value("${word.titleUrl}")
    private String exportWordTitleUrl;

    @Autowired
    private AdminApproveService adminApproveService;

    @Autowired
    private AdminTitleService adminTitleService;

    /**
     * 批量导出正文内容
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @ApiOperation("批量导出标题正文")
    @PostMapping("/conteneWrod")
    public CommonResult conteneWrod(@RequestBody Integer[] ids, HttpServletRequest request, HttpServletResponse response) throws Exception {
        InputStream in = null;
        XWPFDocument doc = null;
        in = Thread.currentThread().getContextClassLoader().getResourceAsStream(exportWordTitleUrl);
        OPCPackage srcPackage = OPCPackage.open(in);
        doc = new XWPFDocument(srcPackage);
        List<ApproveVo> titleApprovalList = adminApproveService.listByIds(ids);
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (ApproveVo approveVo : titleApprovalList) {
            List<TitleVo> titlesList = adminTitleService.queryApprovalById(approveVo.getId());
            for (TitleVo titleVo : titlesList) {
                Map<String, Object> param = new HashMap<>();
                String format = DateUtils.format(approveVo.getReleaseTime());
                String content = "<h2>" + titleVo.getTitleName() + "</h2>" + "<h7>" + format + "审批表标题正文内容" + "</h7>" + titleVo.getContent() + "<br>" + "<br>" + "<br>" + "<br>";
                String contents = RichTextToDocxutil.txt2String(content);
                param.put("content", contents);
                mapList.add(param);
            }
        }
        XWPFDocumentUtil.wordInsertRitchText(doc, mapList);
        // 将doc输出到指定目录
        ExcelUtils.exportWordList(doc, exportWordTemDir, "标题表.docx", request, response);
        return CommonResult.successMsg("导出成功");
    }

}

Util层
RichTextToDocxutil工具类:

package com.jshop.common.utils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author corey
 * @version 1.0
 * @date 2020/5/3 5:51 下午
 * @Desc 将富文本txt导出成word
 */
public class RichTextToDocxutil {
    /**
     * 导出富本框到docx
     */
    public static void outRichTextToDocx(String contents ,String outFilePath) {
        String content = txt2String(contents);
        InputStream inputStream=null;
        OutputStream out = null;
        try {
            // 输入富文本内容,返回字节数组
            byte[] result = HtmlToWord.resolveHtml(content);
            //输出文件
            out = new FileOutputStream(outFilePath);
            out.write(result);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * 读取html文件的内容
     *
     * @param content 读取富文本
     * @return 返回文件内容
     */
    public static String txt2String(String content) {
        StringBuilder result = new StringBuilder();
        try {
            // 构造一个BufferedReader类来读取富文本
            result.append(System.lineSeparator()+content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }
}

XWPFDocumentUtil工具类:

package com.jshop.common.utils;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;

import java.util.List;
import java.util.Map;

/**
 * @author corey
 * @version 1.0
 * @date 2020/5/5 2:04 下午
 */
public class XWPFDocumentUtil {


    /**
     * 往doc的标记位置插入富文本内容 注意:目前支持富文本里面带url的图片,不支持base64编码的图片
     *
     * @param doc          需要插入内容的Word
     * @param ritchtextMap 标记位置对应的富文本内容
     * @param
     */
    public static void wordInsertRitchText(XWPFDocument doc, List<Map<String, Object>> ritchtextMap) {
        try {
            int i = 0;
            long beginTime = System.currentTimeMillis();
            // 如果需要替换多份富文本,通过Map来操作,key:要替换的标记,value:要替换的富文本内容
            for (Map<String, Object> mapList : ritchtextMap) {
                for (Map.Entry<String, Object> entry : mapList.entrySet()) {
                    i++;
                    for (XWPFParagraph paragraph : doc.getParagraphs()) {
                        if (entry.getKey().equals(paragraph.getText().trim())) {
                            // 在标记处插入指定富文本内容
                            HtmlUtils.resolveHtml(entry.getValue().toString(), doc, paragraph);
                            if (i == ritchtextMap.size()) {
                                //当导出最后一个富文本时 删除需要替换的标记
                                doc.removeBodyElement(doc.getPosOfParagraph(paragraph));
                            }
                            break;
                        }
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

HtmlUtils工具类:

package com.jshop.common.utils;


import com.jshop.common.constant.CommonConStant;
import com.jshop.common.enums.ElementEnum;
import com.jshop.common.enums.TitleFontEnum;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;

public class HtmlUtils {

    /**
     * 给document添加指定元素
     *
     * @param document
     */
    public static void addElement(Document document) {
        if (ObjectUtils.isEmpty(document)) {
            throw new NullPointerException("不允许为空的对象添加元素");
        }
        Elements elements = document.getAllElements();
        for (Element e : elements) {
            String attrName = ElementEnum.getValueByCode(e.tag().getName());
            if (!StringUtils.isEmpty(attrName)) {
                e.attr(CommonConStant.COMMONATTR, attrName);
            }
        }
    }


    /**
     * 将富文本内容写入到Word
     * 因富文本样式种类繁多,不能一一枚举,目前实现了H1、H2、H3、段落、图片、表格枚举
     *
     * @param ritchText 富文本内容
     * @param doc       需要写入富文本内容的Word 写入图片和表格需要用到
     * @param paragraph
     * @param
     */
    public static void resolveHtml(String ritchText, XWPFDocument doc, XWPFParagraph paragraph) {
        Document document = Jsoup.parseBodyFragment(ritchText, "UTF-8");
        try {
            // 添加固定元素
            HtmlUtils.addElement(document);
            Elements elements = document.select("[" + CommonConStant.COMMONATTR + "]");
            for (Element em : elements) {
                XmlCursor xmlCursor = paragraph.getCTP().newCursor();
                switch (em.attr(CommonConStant.COMMONATTR)) {
                    case "title":
                        break;
                    case "subtitle":
                        break;
                    case "imgurl":
                        String src = em.attr("src");
                        URL url = new URL(src);
                        URLConnection uc = url.openConnection();
                        InputStream inputStream = uc.getInputStream();
                        XWPFParagraph imgurlparagraph = doc.insertNewParagraph(xmlCursor);
                        ParagraphStyleUtil.setImageCenter(imgurlparagraph);
                        imgurlparagraph.createRun().addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, "图片.jpeg", Units.toEMU(150), Units.toEMU(150));
                        closeStream(inputStream);
                        File file = new File("picture.jpg");
                        boolean exists = file.exists();
                        if (exists) {
                            file.delete();
                        }
                        break;
                    case "imgbase64":
                        break;
                    case "table":
                        XWPFTable xwpfTable = doc.insertNewTbl(xmlCursor);
                        addTable(xwpfTable, em);
                        // 设置表格居中
                        ParagraphStyleUtil.setTableLocation(xwpfTable, "center");
                        // 设置内容居中
                        ParagraphStyleUtil.setCellLocation(xwpfTable, "CENTER", "center");
                        break;
                    case "h1":
                        XWPFParagraph h1paragraph = doc.insertNewParagraph(xmlCursor);
                        XWPFRun xwpfRun_1 = h1paragraph.createRun();
                        xwpfRun_1.setText(em.text());
                        //居中
                        ParagraphStyleUtil.setImageCenter(h1paragraph);
                        // 设置字体
                        ParagraphStyleUtil.setTitle(xwpfRun_1, TitleFontEnum.H1.getTitle());
                        break;
                    case "h2":
                        XWPFParagraph h2paragraph = doc.insertNewParagraph(xmlCursor);
                        XWPFRun xwpfRun_2 = h2paragraph.createRun();
                        xwpfRun_2.setText(em.text());
                        //居中
                        ParagraphStyleUtil.setImageCenter(h2paragraph);
                        // 设置字体
                        ParagraphStyleUtil.setTitle(xwpfRun_2, TitleFontEnum.H2.getTitle());
                        break;
                    case "h3":
                        XWPFParagraph h3paragraph = doc.insertNewParagraph(xmlCursor);
                        XWPFRun xwpfRun_3 = h3paragraph.createRun();
                        xwpfRun_3.setText(em.text());
                        // 设置字体
                        ParagraphStyleUtil.setTitle(xwpfRun_3, TitleFontEnum.H3.getTitle());
                        break;
                    case "paragraph":
                        XWPFParagraph paragraphd = doc.insertNewParagraph(xmlCursor);
                        // 设置段落缩进 4个空格
                        paragraphd.createRun().setText("    " + em.text());
                        break;
                    case "br":
                        XWPFParagraph br = doc.insertNewParagraph(xmlCursor);
                        XWPFRun run = br.createRun();
                        run.addBreak(BreakType.TEXT_WRAPPING);
                    case "h7":
                        XWPFParagraph h7paragraph = doc.insertNewParagraph(xmlCursor);
                        XWPFRun xwpfRun_7 = h7paragraph.createRun();
                        xwpfRun_7.setText(em.text());
                        //居左
                        ParagraphStyleUtil.AlignmentRight(h7paragraph);
                        // 设置字体
                        ParagraphStyleUtil.setTitle(xwpfRun_7, TitleFontEnum.H7.getTitle());
                    default:
                        break;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭输入流
     *
     * @param closeables
     */
    public static void closeStream(Closeable... closeables) {
        for (Closeable c : closeables) {
            if (c != null) {
                try {
                    c.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 将富文本的表格转换为Word里面的表格
     */
    private static void addTable(XWPFTable xwpfTable, Element table) {
        Elements trs = table.getElementsByTag("tr");
        // XWPFTableRow 第0行特殊处理
        int rownum = 0;
        for (Element tr : trs) {
            addTableTr(xwpfTable, tr, rownum);
            rownum++;
        }
    }


    /**
     * 将元素里面的tr 提取到 xwpfTabel
     */
    private static void addTableTr(XWPFTable xwpfTable, Element tr, int rownum) {
        Elements tds = tr.getElementsByTag("th").isEmpty() ? tr.getElementsByTag("td") : tr.getElementsByTag("th");
        XWPFTableRow row_1 = null;
        for (int i = 0, j = tds.size(); i < j; i++) {
            if (0 == rownum) {
                // XWPFTableRow 第0行特殊处理,
                XWPFTableRow row_0 = xwpfTable.getRow(0);
                if (i == 0) {
                    row_0.getCell(0).setText(tds.get(i).text());
                } else {
                    row_0.addNewTableCell().setText(tds.get(i).text());
                }
            } else {
                if (i == 0) {
                    // 换行需要创建一个新行
                    row_1 = xwpfTable.createRow();
                    row_1.getCell(i).setText(tds.get(i).text());
                } else {
                    row_1.getCell(i).setText(tds.get(i).text());
                }
            }
        }

    }
}

ParagraphStyleUtil工具类

package com.jshop.common.utils;

import com.jshop.common.enums.TitleFontEnum;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.util.List;

/**
 * 设置文本样式工具,因为Word样式种类繁多,不能一一枚举
 * @author corey
 * @version 1.0
 * @date 2020/5/5 9:36 下午
 */
public class ParagraphStyleUtil {

    /**
     * 段落缩进
     * @param paragraph
     */
    public static void setIndentationFirstLine(XWPFParagraph paragraph){
        paragraph.setFirstLineIndent(400);
    }

    /**
     * 设置标题 根据富文本的tag来判断
     * @param run
     * @param title
     */
    public static void setTitle(XWPFRun run, String title){
        // 加粗
        run.setBold(true);
        run.setFontSize(TitleFontEnum.getFontByTitle(title));
    }

    /**
     * 设置单元格水平位置和垂直位置
     *
     * @param xwpfTable
     * @param verticalLoction    单元格中内容垂直上TOP,下BOTTOM,居中CENTER,BOTH两端对齐
     * @param horizontalLocation 单元格中内容水平居中center,left居左,right居右,both两端对齐
     */
    public static void setCellLocation(XWPFTable xwpfTable, String verticalLoction, String horizontalLocation) {
        List<XWPFTableRow> rows = xwpfTable.getRows();
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                CTTc cttc = cell.getCTTc();
                CTP ctp = cttc.getPList().get(0);
                CTPPr ctppr = ctp.getPPr();
                if (ctppr == null) {
                    ctppr = ctp.addNewPPr();
                }
                CTJc ctjc = ctppr.getJc();
                if (ctjc == null) {
                    ctjc = ctppr.addNewJc();
                }
                ctjc.setVal(STJc.Enum.forString(horizontalLocation)); //水平居中
                cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.valueOf(verticalLoction));//垂直居中
            }
        }
    }

    /**
     * 设置表格位置
     *
     * @param xwpfTable
     * @param location  整个表格居中center,left居左,right居右,both两端对齐
     */
    public static void setTableLocation(XWPFTable xwpfTable, String location) {
        CTTbl cttbl = xwpfTable.getCTTbl();
        CTTblPr tblpr = cttbl.getTblPr() == null ? cttbl.addNewTblPr() : cttbl.getTblPr();
        CTJc cTJc = tblpr.addNewJc();
        cTJc.setVal(STJc.Enum.forString(location));
    }

    /**
     * 设置图片居中
     * @param xwpfParagraph
     */
    public static void setImageCenter(XWPFParagraph xwpfParagraph){
        //居中
        xwpfParagraph.setAlignment(ParagraphAlignment.CENTER);
    }

    /**
     *居左
     */
    public static void AlignmentLeft(XWPFParagraph xwpfParagraph){
        //左
        xwpfParagraph.setAlignment(ParagraphAlignment.LEFT);
    }

    /**
     *居右
     */
    public static void AlignmentRight(XWPFParagraph xwpfParagraph){
        //右
        xwpfParagraph.setAlignment(ParagraphAlignment.RIGHT);
    }
}

HtmlToWordUtil工具类:

package com.jshop.common.utils;

import com.jshop.common.constant.CommonConStant;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.jaxb.Context;
import org.docx4j.model.structure.SectionWrapper;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.*;
import org.docx4j.wml.PPrBase.Ind;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.*;
import java.math.BigInteger;
import java.util.List;

/**
 * @program: htmltoword
 * @description: html 转 docx
 * @author: corey
 * @create: 2020-04-29 14:10
 **/
public class HtmlToWord {
    private static ObjectFactory factory;
    private static WordprocessingMLPackage wordMLPackage;

    /**
     * 将一段富文本字符串转为一个字节数组
     * @param data
     * @return
     */
    public static byte[] resolveHtml(String data) {
        Document document = Jsoup.parseBodyFragment(data, "UTF-8");
        ByteArrayOutputStream out = null;
        try {
            wordMLPackage = WordprocessingMLPackage.createPackage();
            factory = Context.getWmlObjectFactory();
            Relationship relationship = createFooterPart();
            createFooterReference(relationship);
            MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
            alterStyleSheet();
            // 添加固定元素
            HtmlUtils.addElement(document);
            Elements elements = document.select("["+ CommonConStant.COMMONATTR+"]");
            for (Element em : elements) {
                switch (em.attr(CommonConStant.COMMONATTR)) {
                    case "title":
                        documentPart.addStyledParagraphOfText("Title", em.text());
                        break;
                    case "subtitle":
                        documentPart.addStyledParagraphOfText("Subtitle", em.text());
                        break;
                    case "imgurl":
                        String imgSrc = em.attr("src");
                        File file = new File(imgSrc);
                        byte[] bytes = convertImageToByteArray(file);
                        addImageToPackage(wordMLPackage, bytes);
                        break;
                    case "imgbase64":
                        break;
                    case "table":
                        Tbl table = addTable(em);
                        documentPart.addObject(table);
                        break;
                    case "h1":
                        documentPart.addStyledParagraphOfText("Heading1", em.text());
                        break;
                    case "h2":
                        documentPart.addStyledParagraphOfText("Heading2", em.text());
                        break;
                    case "h3":
                        documentPart.addStyledParagraphOfText("Heading3", em.text());
                        break;
                    case "paragraph":
                        P p = addParapraph(em.text());
                        //设置首行缩进
                        setFirstLine(p,"400");
                        documentPart.getContent().add(p);
                        break;
                    default:
                        documentPart.addParagraphOfText(em.text());
                        break;
                }
            }
            addPageBreak(documentPart);
             out = new ByteArrayOutputStream();
            wordMLPackage.save(out);
            return out.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            if(out!=null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @Title: addParapraph
     * @Description: (文本转段落)
     * @param @param text
     * @param @return    设定文件
     * @return P    返回类型
     * @throws
     */
    private static P addParapraph(String text) {
        factory= Context.getWmlObjectFactory();
        P paragraph = factory.createP();
        Text t = factory.createText();
        t.setValue(text);
        R run = factory.createR();
        run.getContent().add(t);
        paragraph.getContent().add(run);
        RPr runProperties = factory.createRPr();
        run.setRPr(runProperties);
        return paragraph;
    }

    /**
     * @Title: setFirstLine
     * @param @param p
     * @param @param str    设定文件
     * @return void    返回类型
     * @throws
     */
    private static void setFirstLine(P p , String str) {
        PPr ppr = getPPr(p);
        Ind ind = ppr.getInd();
        if (ind == null) {
            ind = new Ind();
            ppr.setInd(ind);
        }
        ind.setFirstLine(new BigInteger(str));
    };

    private static PPr getPPr(P p) {
        PPr ppr = p.getPPr();
        if (ppr == null) {
            ppr = new PPr();
            p.setPPr(ppr);
        }
        return ppr;
    }

    /**
     *         table @param @return 设定文件 @return Tbl 返回类型 @throws
     */
    private static Tbl addTable(Element table) {
        factory = Context.getWmlObjectFactory();
        Tbl tbl = factory.createTbl();
        addBorders(tbl);
        Elements trs = table.getElementsByTag("tr");
        for (Element tr : trs) {
            Tr fTr = addTableTr(tr);
            tbl.getContent().add(fTr);
        }
        return tbl;
    }

    /**
     *         tr @param @return 设定文件 @return Tr 返回类型 @throws
     */
    private static Tr addTableTr(Element tr) {
        Elements tds = tr.getElementsByTag("th").isEmpty() ? tr.getElementsByTag("td") : tr.getElementsByTag("th");
        Tr ftr = factory.createTr();
        for (int i = 0, j = tds.size(); i < j; i++) {
            Tc ftd = factory.createTc();
            setCellWidth(ftd, 1000);
            ftd.getContent().add(wordMLPackage.getMainDocumentPart().createParagraphOfText(tds.get(i).text()));
            ftr.getContent().add(ftd);
        }
        return ftr;
    }

    /**
     * 本方法创建一个单元格属性集对象和一个表格宽度对象. 将给定的宽度设置到宽度对象然后将其添加到 属性集对象. 最后将属性集对象设置到单元格中.
     */
    private static void setCellWidth(Tc tableCell, int width) {
        TcPr tableCellProperties = new TcPr();
        TblWidth tableWidth = new TblWidth();
        tableWidth.setW(BigInteger.valueOf(width));
        tableCellProperties.setTcW(tableWidth);
        tableCell.setTcPr(tableCellProperties);
    }

    /**
     * 本方法为表格添加边框
     */
    private static void addBorders(Tbl table) {
        table.setTblPr(new TblPr());
        CTBorder border = new CTBorder();
        border.setColor("auto");
        border.setSz(new BigInteger("4"));
        border.setSpace(new BigInteger("0"));
        border.setVal(STBorder.SINGLE);

        TblBorders borders = new TblBorders();
        borders.setBottom(border);
        borders.setLeft(border);
        borders.setRight(border);
        borders.setTop(border);
        borders.setInsideH(border);
        borders.setInsideV(border);
        table.getTblPr().setTblBorders(borders);
    }

    /**
     * 将图片从文件对象转换成字节数组.
     *
     * @param file
     *            将要转换的文件
     * @return 包含图片字节数据的字节数组
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static byte[] convertImageToByteArray(File file) throws FileNotFoundException, IOException {
        InputStream is = new FileInputStream(file);
        long length = file.length();
        // 不能使用long类型创建数组, 需要用int类型.
        if (length > Integer.MAX_VALUE) {
            System.out.println("File too large!!");
        }
        byte[] bytes = new byte[(int) length];
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }
        // 确认所有的字节都没读取
        if (offset < bytes.length) {
            System.out.println("Could not completely read file " + file.getName());
        }
        is.close();
        return bytes;
    }

    /**
     * Docx4j拥有一个由字节数组创建图片部件的工具方法, 随后将其添加到给定的包中. 为了能将图片添加 到一个段落中, 我们需要将图片转换成内联对象.
     * 这也有一个方法, 方法需要文件名提示, 替换文本, 两个id标识符和一个是嵌入还是链接到的指示作为参数. 一个id用于文档中绘图对象不可见的属性,
     * 另一个id用于图片本身不可见的绘制属性. 最后我们将内联 对象添加到段落中并将段落添加到包的主文档部件.
     *
     * @param wordMLPackage
     *            要添加图片的包
     * @param bytes
     *            图片对应的字节数组
     * @throws Exception
     *             不幸的createImageInline方法抛出一个异常(没有更多具体的异常类型)
     */
    private static void addImageToPackage(WordprocessingMLPackage wordMLPackage, byte[] bytes) throws Exception {
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);

        int docPrId = 1;
        int cNvPrId = 2;
        Inline inline = imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);

        P paragraph = addInlineImageToParagraph(inline);

        wordMLPackage.getMainDocumentPart().addObject(paragraph);
    }

    /**
     * 创建一个对象工厂并用它创建一个段落和一个可运行块R. 然后将可运行块添加到段落中. 接下来创建一个图画并将其添加到可运行块R中. 最后我们将内联
     * 对象添加到图画中并返回段落对象.
     *
     * @param inline
     *            包含图片的内联对象.
     * @return 包含图片的段落
     */
    private static P addInlineImageToParagraph(Inline inline) {
        // 添加内联对象到一个段落中
        ObjectFactory factory = new ObjectFactory();
        P paragraph = factory.createP();
        R run = factory.createR();
        paragraph.getContent().add(run);
        Drawing drawing = factory.createDrawing();
        run.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);
        return paragraph;
    }

    /**
     * This method alters the default style sheet that is part of each document.
     *
     * To do this, we first retrieve the style sheet from the package and then get
     * the Styles object from it. From this object, we get the list of actual styles
     * and iterate over them. We check against all styles we want to alter and apply
     * the alterations if applicable.
     *
     * @param
     */
    public static void alterStyleSheet() {
        StyleDefinitionsPart styleDefinitionsPart = wordMLPackage.getMainDocumentPart().getStyleDefinitionsPart();
        Styles styles = null;
        try {
            styles = styleDefinitionsPart.getContents();
        } catch (Docx4JException e) {
            e.printStackTrace();
        }

        List<Style> stylesList = styles.getStyle();
        for (Style style : stylesList) {
            if (style.getStyleId().equals("Normal")) {
                alterNormalStyle(style);
            } else if (style.getStyleId().equals("Heading1")) {
                alterHeading1Style(style);
            } else if (style.getStyleId().equals("Heading2")) {
                alterHeading2Style(style);
            } else if (style.getStyleId().equals("Title") || style.getStyleId().equals("Subtitle")) {
                getRunPropertiesAndRemoveThemeInfo(style);
            }
        }
    }

    /**
     * First we create a run properties object as we want to remove nearly all of
     * the existing styling. Then we change the font and font size and set the run
     * properties on the given style. As in previous examples, the font size is
     * defined to be in half-point size.
     */
    private static void alterNormalStyle(Style style) {
        // we want to change (or remove) almost all the run properties of the
        // normal style, so we create a new one.
        RPr rpr = new RPr();
        changeFontToArial(rpr);
        changeFontSize(rpr, 20);
        style.setRPr(rpr);
    }

    /**
     * For this style, we get the existing run properties from the style and remove
     * the theme font information from them. Then we also remove the bold styling,
     * change the font size (half-points) and add an underline.
     */
    private static void alterHeading1Style(Style style) {
        RPr rpr = getRunPropertiesAndRemoveThemeInfo(style);
        removeBoldStyle(rpr);
        changeFontSize(rpr, 28);
        /* addUnderline(rpr); */
    }

    private static void alterHeading2Style(Style style) {
        RPr rpr = getRunPropertiesAndRemoveThemeInfo(style);
        removeBoldStyle(rpr);
        changeFontSize(rpr, 24);

        /* addUnderline(rpr); */
    }

    private static RPr getRunPropertiesAndRemoveThemeInfo(Style style) {
        // We only want to change some settings, so we get the existing run
        // properties from the style.
        RPr rpr = style.getRPr();
        removeThemeFontInformation(rpr);
        return rpr;
    }

    /**
     * Change the font of the given run properties to Arial.
     *
     * A run font specifies the fonts which shall be used to display the contents of
     * the run. Of the four possible types of content, we change the styling of two
     * of them: ASCII and High ANSI. Finally we add the run font to the run
     * properties.
     *
     * @param runProperties
     */
    private static void changeFontToArial(RPr runProperties) {
        RFonts runFont = new RFonts();
        runFont.setAscii("Arial");
        runFont.setHAnsi("Arial");
        runProperties.setRFonts(runFont);
    }

    /**
     * Change the font size of the given run properties to the given value.
     *
     * @param runProperties
     * @param fontSize
     *            Twice the size needed, as it is specified as half-point value
     */
    private static void changeFontSize(RPr runProperties, int fontSize) {
        HpsMeasure size = new HpsMeasure();
        size.setVal(BigInteger.valueOf(fontSize));
        runProperties.setSz(size);
    }

    /**
     * Removes the theme font information from the run properties. If this is not
     * removed then the styles based on the normal style won't inherit the Arial
     * font from the normal style.
     *
     * @param runProperties
     */
    private static void removeThemeFontInformation(RPr runProperties) {
        runProperties.getRFonts().setAsciiTheme(null);
        runProperties.getRFonts().setHAnsiTheme(null);
    }

    /**
     * Removes the Bold styling from the run properties.
     *
     * @param runProperties
     */
    private static void removeBoldStyle(RPr runProperties) {
        runProperties.getB().setVal(false);
    }



    /**
     * As in the previous example, this method creates a footer part and adds it to
     * the main document and then returns the corresponding relationship.
     *
     * @return
     * @throws InvalidFormatException
     */
    private static Relationship createFooterPart() throws InvalidFormatException {
        FooterPart footerPart = new FooterPart();
        footerPart.setPackage(wordMLPackage);

        footerPart.setJaxbElement(createFooterWithPageNr());

        return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);
    }

    /**
     * As in the previous example, we create a footer and a paragraph object. But
     * this time, instead of adding text to a run, we add a field. And just as with
     * the table of content, we have to add a begin and end character around the
     * actual field with the page number. Finally we add the paragraph to the
     * content of the footer and then return it.
     *
     * @return
     */
    public static Ftr createFooterWithPageNr() {
        Ftr ftr = factory.createFtr();
        P paragraph = factory.createP();

        addFieldBegin(paragraph);
        addPageNumberField(paragraph);
        addFieldEnd(paragraph);

        ftr.getContent().add(paragraph);
        return ftr;
    }

    /**
     * Creating the page number field is nearly the same as creating the field in
     * the TOC example. The only difference is in the value. We use the PAGE
     * command, which prints the number of the current page, together with the
     * MERGEFORMAT switch, which indicates that the current formatting should be
     * preserved when the field is updated.
     *
     * @param paragraph
     */
    private static void addPageNumberField(P paragraph) {
        R run = factory.createR();
        Text txt = new Text();
        txt.setSpace("preserve");
        txt.setValue(" PAGE   \\* MERGEFORMAT ");
        run.getContent().add(factory.createRInstrText(txt));
        paragraph.getContent().add(run);
    }

    /**
     * Every fields needs to be delimited by complex field characters. This method
     * adds the delimiter that precedes the actual field to the given paragraph.
     *
     * @param paragraph
     */
    private static void addFieldBegin(P paragraph) {
        R run = factory.createR();
        FldChar fldchar = factory.createFldChar();
        fldchar.setFldCharType(STFldCharType.BEGIN);
        run.getContent().add(fldchar);
        paragraph.getContent().add(run);
    }

    /**
     * Every fields needs to be delimited by complex field characters. This method
     * adds the delimiter that follows the actual field to the given paragraph.
     *
     * @param paragraph
     */
    private static void addFieldEnd(P paragraph) {
        FldChar fldcharend = factory.createFldChar();
        fldcharend.setFldCharType(STFldCharType.END);
        R run3 = factory.createR();
        run3.getContent().add(fldcharend);
        paragraph.getContent().add(run3);
    }

    /**
     * This method fetches the document final section properties, and adds a newly
     * created footer reference to them.
     *
     * @param relationship
     */
    public static void createFooterReference(Relationship relationship) {

        List<SectionWrapper> sections = wordMLPackage.getDocumentModel().getSections();

        SectPr sectPr = sections.get(sections.size() - 1).getSectPr();
        // There is always a section wrapper, but it might not contain a sectPr
        if (sectPr == null) {
            sectPr = factory.createSectPr();
            wordMLPackage.getMainDocumentPart().addObject(sectPr);
            sections.get(sections.size() - 1).setSectPr(sectPr);
        }

        FooterReference footerReference = factory.createFooterReference();
        footerReference.setId(relationship.getId());
        footerReference.setType(HdrFtrRef.DEFAULT);
        sectPr.getEGHdrFtrReferences().add(footerReference);
    }

    /**
     * Adds a page break to the document.
     *
     * @param documentPart
     */
    private static void addPageBreak(MainDocumentPart documentPart) {
        Br breakObj = new Br();
        breakObj.setType(STBrType.PAGE);

        P paragraph = factory.createP();
        paragraph.getContent().add(breakObj);
        try {
            documentPart.getContents().getBody().getContent().add(paragraph);
        } catch (Docx4JException e) {
            e.printStackTrace();
        }
    }
}

ExcelUtils工具类:


```java
/**
 * 导出word
 * <p>第一步生成替换后的word文件,只支持docx</p>
 * <p>第二步下载生成的文件</p>
 * <p>第三步删除生成的临时文件</p>
 * 模版变量中变量格式:{{foo}}
 *
 * @param doc           word模板地址
 * @param temDir       生成临时文件存放地址
 * @param fileName     文件名
 * @param params       替换的参数
 * @param request      HttpServletRequest
 * @param response     HttpServletResponse
 */
public static void exportWordList(XWPFDocument doc,String temDir, String fileName, HttpServletRequest request, HttpServletResponse response) {

    Assert.notNull(temDir, "临时文件路径不能为空");
    Assert.notNull(fileName, "导出文件名不能为空");
    Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式");
    if (!temDir.endsWith("/")) {
        temDir = temDir + File.separator;
    }
    File dir = new File(temDir);
    if (!dir.exists()) {
        dir.mkdirs();
    }
    try {
        String userAgent = request.getHeader("user-agent").toLowerCase();
        if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
            fileName = URLEncoder.encode(fileName, "UTF-8");
        } else {
            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
        }
        String tmpPath = temDir + fileName;
        FileOutputStream fos = new FileOutputStream(tmpPath);
        doc.write(fos);
        // 设置强制下载不打开
        response.setContentType("application/force-download");
        // 设置文件名
        response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
        OutputStream out = response.getOutputStream();
        doc.write(out);
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

ConStant层
CommonConStant类:

package com.jshop.common.constant;

/**
 * 公共常量
 */
public class CommonConStant {
    // 固定元素节点
    public static final String COMMONATTR = "data-class";
}

Enums层
ElementEnum 类:

package com.jshop.common.enums;

/**
 * html 元素枚举映射类
 */
public enum ElementEnum {
    H1("h1","h1","一级标题"),
    H2("h2","h2","二级标题"),
    H3("h3","h3","三级标题"),
    H7("h7","h7","小标题"),
    P("p", "paragraph", "段落"),
    STRONG("strong","","加粗"),
    I("i","","斜体"),
    U("u", "", "字体下划线"),
    IMG("img", "imgurl", "base64图片"),
    TABLE("table","table","表格"),
    BR("br","br","换行")



    ;

    private String code;
    private String value;
    private String desc;

    public String getCode() {
        return code;
    }

    public String getValue() {
        return value;
    }

    public String getDesc() {
        return desc;
    }

    ElementEnum(String code, String value, String desc) {
        this.code = code;
        this.value = value;
        this.desc = desc;
    }

    public static String getValueByCode(String code) {
        for (ElementEnum e : ElementEnum.values()) {
            if (e.getCode().equalsIgnoreCase(code)) {
                return e.getValue();
            }
        }
        return null;
    }
}

TitleFontEnum类:

package com.jshop.common.enums;

/**
 * @desc 设置标题字体大小
 * @author corey
 * @version 1.0
 * @date 2020/5/5 9:48 下午
 */
public enum TitleFontEnum {
    H1("h1", 24),
    H2("h2", 22),
    H3("h3", 12),
    H7("h7",12)
    ;
    private String title;
    private Integer font;

    public String getTitle() {
        return title;
    }

    public Integer getFont() {
        return font;
    }

    TitleFontEnum(String title, Integer font) {
        this.title = title;
        this.font = font;
    }

    public static Integer getFontByTitle(String title){
        for (TitleFontEnum e : TitleFontEnum.values()) {
            if (title.equals(e.getTitle())) {
                return e.getFont();
            }
        }
        return null;
    }
}

效果图:

在这里插入图片描述

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值