HTML通过POI转DOCX

前端富文本编辑后转DOCX,网上查看了很多方法,转出的docx文件的document.xml都没有内容,不能被POI解析,于是通过html标签转换,实现简单样式和内容的转换。使用的时候需要根据前端标签去修改标签值

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * html转word工具类
 *
 */
@Slf4j
public class HtmlConvertWordUtil {

    /**
     * html转word的main方法
     *
     * @param html 需要转换的html
     */
    public static XWPFDocument htmlConvertWord(String html) {
        XWPFDocument doc = new XWPFDocument();
        Integer abstractNumSize = 0;
        // 通过Jsoup格式化html
        Document parse = Jsoup.parse(html);
        // 获取html中所有的标签元素
        Elements es = parse.body().getAllElements();
        // 父标签值
        Set<String> parentTagNameSet = Arrays.stream(new String[]{"div", "p", "h1", "h2", "h3", "h4", "h5", "table", "ul", "ol", "hr"}).collect(Collectors.toSet());
        // 筛选出所有的父级标签  限于p、h1、h2、h3、table
        List<Element> tag1 = es.stream().filter(x -> parentTagNameSet.contains(x.tagName())).collect(Collectors.toList());
        // 循环每个父标签 ,每个父标签是一个段落,将父标签下的内容以及子标签对应的内容放到父标签段落中
        for (Element e : tag1) {
            //创建段落)
            abstractNumSize = createXWPFParagraph(doc, e, abstractNumSize);
        }
        return doc;
    }


    /**
     * 构建段落
     *
     * @param docxDocument
     * @param e
     */
    public static Integer createXWPFParagraph(XWPFDocument docxDocument, Element e, Integer abstractNumSize) {
        // 创建段落
        XWPFParagraph paragraph = docxDocument.createParagraph();
        // 存放样式
        List<String> allStyles = new ArrayList<>();
        // 创建内容并设置样式
        abstractNumSize = createXWPFRun(docxDocument, paragraph, e, allStyles, abstractNumSize);
        return abstractNumSize;
    }

    /**
     * 创建段落内容
     *
     * @param docxDocument 目标文档
     * @param paragraph    段落
     * @param e            html中的元素
     * @param allStyles    样式
     * @param abstractNumSize 排序编号编码(最好从0开始)
     */
    public static Integer createXWPFRun(XWPFDocument docxDocument, XWPFParagraph paragraph, Element e, List<String> allStyles, Integer abstractNumSize) {
        // 父标签 style中的样式
        List<String> parentStyle = new ArrayList<>(Arrays.asList(e.attr("style") == null ? new String[0] : e.attr("style").split(";")));
        allStyles.addAll(parentStyle);
        // 父标签样式 只针对于h1、h2、h3、p
        if (e.tagName().contains("div") || e.tagName().contains("h") || "p".equals(e.tagName())) {
            allStyles.add(e.tagName() + ":");
        }
        // 父标签下的所有的子标签
        List<Node> nodes = e.childNodes();
        if (CollUtil.isNotEmpty(nodes)) {
            // 单独处理表格
            if ("table".equals(e.tagName())) {
                // 在word中创建表格
                XWPFTable table = docxDocument.createTable();
                // 设置表格宽度
                CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
                width.setType(STTblWidth.DXA);
                width.setW(BigInteger.valueOf(9072));
                nodes = nodes.stream().filter(x -> x instanceof Element).collect(Collectors.toList());
                // 获取tbody
                Element tableBody = (Element) nodes.get(0);
                // 在word中创建表格的时候 会默认创建一行
                table.removeRow(0);
                // 遍历tr 行
                tableBody.childNodes().stream().filter(x -> x instanceof Element).map(x -> (Element) x).forEach(x -> {
                    // 创建行
                    XWPFTableRow row = table.createRow();
                    // 列的下标
                    AtomicInteger i = new AtomicInteger();
                    // 遍历td 列
                    x.childNodes().stream().filter(c -> c instanceof Element).map(c -> (Element) c).forEach(c -> {
                        i.getAndIncrement();
                        // 这个创建列  多行只要创建了第一行的列  下面所有的行都会有对应创建的列  所有如果多行列是一样的 只需要创建一次就可以
                        XWPFTableCell cell = row.getCell(i.intValue() - 1);
                        if (cell == null) {
                            cell = row.createCell();
                        }
                        // 单元格内容垂直居中
                        CTTcPr tcpr = cell.getCTTc().addNewTcPr();
                        CTVerticalJc va = tcpr.addNewVAlign();
                        va.setVal(STVerticalJc.CENTER);
                        // 暂存当前标签的样式
                        List<String> tempStyles = new ArrayList<>();
                        // 获取样式
                        tempStyles = cellStyle(c, tempStyles);
                        if (tempStyles.size() != 0) {
                            allStyles.addAll(tempStyles);
                            XWPFRun run = cell.addParagraph().createRun();
                            run.setText(c.text());
                            setFontStyle(allStyles, run, paragraph, docxDocument, c);
                            allStyles.removeAll(allStyles);
                        } else {
                            cell.setText(c.text());
                        }
                    });
                });
            } else if (StringUtils.equals("ol", e.tagName())) {
                // 有序列表
                abstractNumSize = createNumberList(docxDocument, e, allStyles, true, abstractNumSize);
            } else if (StringUtils.equals("ul", e.tagName())) {
                // 无序列表
                abstractNumSize = createNumberList(docxDocument, e, allStyles, false, abstractNumSize);
            }else {
                for (Node node : nodes) {
                    // 创建段落内容
                    XWPFRun run = paragraph.createRun();
                    // 当子标签 为TextNode 的时候 说明这个子标签仅仅是文本 没有被标签修饰
                    if (node instanceof TextNode) {
                        TextNode textNode = (TextNode) node;
                        run.setText(textNode.text().replaceAll(" ", ""));
                        // 设置样式 已父标签样式为基准
                        setFontStyle(allStyles, run, paragraph, docxDocument, null);
                        // 当子标签 为Element的时候 说明这个子标签不单单是文本,还包括标签  这时候需要根据对应标签的样式 修饰当前文本
                    } else if (node instanceof Element) {
                        Element children = (Element) node;
                        // 存放子标签中style样式
                        List<String> childrenStyle = new ArrayList<>(Arrays.asList(children.attr("style") == null ? new String[0] : children.attr("style").split(";")));
                        // 子标签名称
                        String tagName = children.tagName();
                        // 添加标签对应的样式
                        childrenStyle.add(addTagStyle(tagName, children));
                        // 将父子标签样式汇总
                        allStyles.addAll(childrenStyle);
                        // 查看子标签下是否还有子标签
                        List<Node> grandsons = children.childNodes().stream().filter(x -> x instanceof Element).collect(Collectors.toList());
                        if (grandsons != null && grandsons.size() != 0) {
                            // 如果子标签下还有字标签  样式需要包括当前子标签的样式
                            createXWPFRun(docxDocument, paragraph, children, allStyles, abstractNumSize);
                        }
                        // 查看当前标签 有没有包括内容  如果不加这个直接text()方法 他会获取当前标签的子标签的内容  造成内容重复的问题
                        List<Node> childrenNodes = children.childNodes().stream().filter(x -> x instanceof TextNode).collect(Collectors.toList());
                        if (childrenNodes != null && childrenNodes.size() != 0) {
                            // 子标签内容
                            String text = children.text();
                            // 这里设置内容需要排除超链接a标签  a标签需要特殊设置内容
                            List<String> aStyle = allStyles.stream().filter(x -> x.indexOf("a:") >= 0).collect(Collectors.toList());
                            if (aStyle == null || aStyle.size() == 0) {
                                run.setText(text.replaceAll(" ", ""));
                            }
                            setFontStyle(allStyles, run, paragraph, docxDocument, children);
                        }
                        // 去除子标签样式 当前标签的内容 仅限于当前标签  下个标签不能使用
                        allStyles.removeAll(childrenStyle);
                    }
                }
            }

        } else {
            // 横线 hr标签
            if ("hr".equals(e.tagName())) {
                // 创建段落内容
                XWPFRun run = paragraph.createRun();
                run.setText("_________________________________________________________________________"); // 用足够多的下划线字符填充
                run.setUnderline(UnderlinePatterns.SINGLE);

                // 可根据需要调整对齐方式
                paragraph.setAlignment(ParagraphAlignment.CENTER);

            }
        }
        return abstractNumSize;
    }

    /**
     * 添加图片
     * @param run
     * @param pictureUrl
     * @param fileName
     * @return
     */
    public static XWPFRun addPicture(XWPFRun run, String pictureUrl,String fileName) {
        if (pictureUrl == null) {
            return run;
        }
        URL url = null;
        InputStream inputStream = null;
        try {
            pictureUrl = URLDecoder.decode(pictureUrl,"UTF-8");
            url = new URL(pictureUrl);
            inputStream = url.openConnection().getInputStream();
            // 获取 图片类型 并添加图片
            run.addPicture(inputStream, getPictureType(pictureUrl),fileName, Units.toEMU(400), Units.toEMU(256));
        } catch (MalformedURLException e) {
            throw new RuntimeException("图片url解析异常,url=" + pictureUrl);
        } catch (IOException e) {
            throw new RuntimeException("获取图片异常,url=" + pictureUrl);
        } catch (InvalidFormatException e) {
            throw new RuntimeException("添加图片异常,url=" + pictureUrl);
        }

        return run;
    }
    /**
     * 根据图片类型,取得对应的图片类型代码
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType){
        int res = XWPFDocument.PICTURE_TYPE_PICT;
        if(picType != null){
            if(picType.equalsIgnoreCase("png")){
                res = XWPFDocument.PICTURE_TYPE_PNG;
            }else if(picType.equalsIgnoreCase("dib")){
                res = XWPFDocument.PICTURE_TYPE_DIB;
            }else if(picType.equalsIgnoreCase("emf")){
                res = XWPFDocument.PICTURE_TYPE_EMF;
            }else if(picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")){
                res = XWPFDocument.PICTURE_TYPE_JPEG;
            }else if(picType.equalsIgnoreCase("wmf")){
                res = XWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }
    /**
     * 循环取出表格中的字体样式
     *
     * @param c          cell单元格
     * @param tempStyles 样式
     * @return
     */
    private static List<String> cellStyle(Element c, List<String> tempStyles) {
        List<Element> collect = c.childNodes().stream().filter(s -> s instanceof Element).map(s -> (Element) s).collect(Collectors.toList());
        for (Element element : collect) {
            tempStyles.add(addTagStyle(element.tagName(), element));
            List<Node> childs = element.childNodes().stream().filter(s -> s instanceof Element).collect(Collectors.toList());
            if (childs != null && childs.size() != 0) {
                cellStyle(element, tempStyles);
            }
        }
        return tempStyles;
    }

    /**
     * 添加标签样式
     *
     * @param tagName  标签
     * @param children 元素
     * @return
     */
    public static String addTagStyle(String tagName, Element children) {
        String style = "";
        switch (tagName) {
            // 字体相关
            case "font":
                // 字体
                if (StringUtils.isNotBlank(children.attr("font-family"))) {
                    style = "font-family:" + children.attr("font-family");
                }
                // 大小
                if (StringUtils.isNotBlank(children.attr("font-size"))) {
                    style = "font-size:" + children.attr("font-size");
                }
                // 颜色
                if (StringUtils.isNotBlank(children.attr("color"))) {
                    style = "color:" + children.attr("color");
                }
                break;
            // 删除线
            case "s":
                style = "s:";
                break;
            // br标签
            case "br":
                style = "br:";
                break;
            // u标签 下划线
            case "u":
                style = "u:";
                break;
            // i标签 斜体
            case "em":
                style = "em:";
                break;
            // b标签 加粗
            case "strong":
                style = "strong:";
                break;
            // a 标签 超链接
            case "a":
                if (StringUtils.isNotBlank(children.attr("href"))) {
                    style = "a:" + children.attr("href");
                }
                break;
            case "sup":
                style = "sup:";
                break;
            case "sub":
                style = "sub:";
                break;
        }
        return style;
    }

    /**
     * 设置内容样式
     *
     * @param styles       样式
     * @param run          需要赋予样式的对象 XWPFRun
     * @param paragraph    段落
     * @param docxDocument 目标文档
     */
    public static void setFontStyle(List<String> styles, XWPFRun run, XWPFParagraph paragraph, XWPFDocument docxDocument, Element children) {
        // 自定义样式排序 父标签样式在前 子标签样式在后  子标签样式可以覆盖父标签样式
        Collections.sort(styles, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                if (o1.contains("h") || "p".equals(o1)||o1.contains("div")) {
                    return -1;
                } else if ("p".equals(o2) || o2.contains("h")||o2.contains("div")) {
                    return 1;
                } else {
                    return 0;
                }
            }
        });
        for (String styleValue : styles) {
            if (StringUtils.isBlank(styleValue)) {
                continue;
            }
            // 获取style中的样式
            String style = styleValue.substring(0, styleValue.indexOf(":")).replaceAll(" ", "");
            // style样式对应的值
            String value = styleValue.substring(styleValue.indexOf(":") + 1).replaceAll(" ", "");
            switch (style) {
                /*--------------------------------标签对应的样式 例如p 、h1、h2、h3*/
                // 正文
                case "p":
                    //对齐方式
                    //paragraph.setAlignment(ParagraphAlignment.BOTH);
                    //首行缩进:567==1厘米
                    //paragraph.setIndentationFirstLine(567);
                    break;
                case "div":
//                    对齐方式
                    paragraph.setAlignment(ParagraphAlignment.BOTH);
//                    首行缩进:567==1厘米
                    paragraph.setIndentationFirstLine(567);
                    break;
                // h1标题
                case "h1":
                    addCustomHeadingStyle(docxDocument, "标题 1", 1);
                    paragraph.setStyle("标题 1");
                    run.setBold(true);
                    run.setColor("000000");
                    run.setFontFamily("宋体");
                    run.setFontSize(20);
                    break;
                // h2 标题
                case "h2":
                    addCustomHeadingStyle(docxDocument, "标题 2", 2);
                    paragraph.setStyle("标题 2");
                    run.setBold(true);
                    run.setColor("000000");
                    run.setFontFamily("宋体");
                    run.setFontSize(18);
                    break;
                // h3标题
                case "h3":
                    addCustomHeadingStyle(docxDocument, "标题 3", 3);
                    paragraph.setStyle("标题 3");
                    run.setBold(true);
                    run.setColor("000000");
                    run.setFontFamily("宋体");
                    run.setFontSize(16);
                    break;
                case "h4":
                    addCustomHeadingStyle(docxDocument, "标题 4", 4);
                    paragraph.setStyle("标题 4");
                    run.setBold(true);
                    run.setColor("000000");
                    run.setFontFamily("宋体");
                    run.setFontSize(14);
                    break;
                case "h5":
                    addCustomHeadingStyle(docxDocument, "标题 5", 5);
                    paragraph.setStyle("标题 5");
                    run.setBold(true);
                    run.setColor("000000");
                    run.setFontFamily("宋体");
                    run.setFontSize(12);
                    break;
                // 行高 行间距
                case "line-height":
                    double tempDouble = Double.parseDouble(value);
                    int height = (int) Math.round(tempDouble);
                    run.setTextPosition(height);

                    break;
                /* 左内边距  因为单位是em  暂不处理此标签
                case "padding-left":
                    CTSectPr sectPr = docxDocument.getDocument().getBody().addNewSectPr();
                    CTPageMar pageMar = sectPr.addNewPgMar();
                    pageMar.setLeft(BigInteger.valueOf(720L));
                    break;*/
                // 首行缩进
                case "text-indent":
                    int twips = 0;
                    if (StringUtils.endsWith(value, "pt")) {
                        String indent = value.replaceAll("pt", "");
                        Double pt = Double.parseDouble(indent) * 20;// pt=20twips
                        twips = pt.intValue();
                    } else if (StringUtils.endsWith(value, "px")) {
                        String indent = value.replaceAll("px", "");
                        Double px = Double.parseDouble(indent) * 15;// px=15twips
                        twips = px.intValue();
                    } else if (StringUtils.endsWith(value, "em")) {
                        String indent = value.replaceAll("em", "");
                        Double em = Double.parseDouble(indent);
                        int fontSize = run.getFontSize() > 0 ? run.getFontSize() : 12;
                        twips = fontSize * em.intValue() * 15;
                    }
                    paragraph.setIndentationFirstLine(twips);
                    break;
                case "font-family":
                    CTFonts ctFonts = run.getCTR().addNewRPr().addNewRFonts();
                    // 设置中文字体
                    ctFonts.setEastAsia(value);
                    // 设置英文数字字体
                    ctFonts.setAscii(value);
                    break;
                // 大小
                case "font-size":
                    if (StringUtils.isNotBlank(value)) {
                        int size = Integer.parseInt(value.replaceAll("px", ""));
                        size = (size * 72) / 96;
                        run.setFontSize(size);
                    }
//                    run.setFontSize(fontSizeConvert(value));
                    break;
                // 颜色
                case "color":
                    run.setColor(rgbToColorValue(value));
                    break;
                // 删除线
                case "s":
                    run.setStrikeThrough(true);
                    break;
                // br 换行
                case "br":
                    run.addCarriageReturn();
                    break;
                // u 下划线
                case "u":
                    run.setUnderline(UnderlinePatterns.SINGLE);
                    break;
                // em 斜体
                case "em":
                    run.setItalic(true);
                    break;
                // strong 加粗
                case "strong":
                    run.setBold(true);
                    break;
                // background-color 背景色
                case "background-color":
                    // 高亮(不使用)
//                    run.getCTR().addNewRPr().addNewHighlight().setVal(getBackground(value));
                    // 背景色
                    CTShd shd = run.getCTR().addNewRPr().addNewShd();
                    shd.setFill("auto"); // 设置填充模式
                    shd.setColor(rgbToColorValue(value)); // 自定义颜色,这里是紫色(RGB: 255, 0, 255)
                    shd.setVal(STShd.SOLID); // 设置阴影样式,例如20%透明度
                    break;
                // a 超连接
                case "a":
                    // 创建一个超链接运行并设置文本
                    XWPFHyperlinkRun hyperlinkRun = paragraph.createHyperlinkRun(value);
                    hyperlinkRun.setText((children.text()));

                    // 设置链接颜色和下划线(可选)
                    hyperlinkRun.setColor("0000FF");
                    hyperlinkRun.setUnderline(UnderlinePatterns.SINGLE);
                    break;
                // 文本对齐方式
                case "text-align":
                    if (StringUtils.equals("center", value)) {
                        paragraph.setAlignment(ParagraphAlignment.CENTER);
                    } else if (StringUtils.equals("left", value)) {
                        paragraph.setAlignment(ParagraphAlignment.LEFT);
                    } else if (StringUtils.equals("right", value)) {
                        paragraph.setAlignment(ParagraphAlignment.RIGHT);
                    } else if (StringUtils.equals("justify", value)) {
                        paragraph.setAlignment(ParagraphAlignment.BOTH);
                    }
                    break;
                // 上标
                case "sup":
                    run.setSubscript(VerticalAlign.SUPERSCRIPT);
                    break;
                // 下标
                case "sub":
                    run.setSubscript(VerticalAlign.SUBSCRIPT);
                    break;
            }
        }
    }

    /**
     * 增加自定义标题样式。这里用的是stackoverflow的源码
     *
     * @param docxDocument 目标文档
     * @param strStyleId   样式名称
     * @param headingLevel 样式级别
     */
    private static void addCustomHeadingStyle(XWPFDocument docxDocument, String strStyleId, int headingLevel) {

        CTStyle ctStyle = CTStyle.Factory.newInstance();
        ctStyle.setStyleId(strStyleId);

        CTString styleName = CTString.Factory.newInstance();
        styleName.setVal(strStyleId);
        ctStyle.setName(styleName);

        CTDecimalNumber indentNumber = CTDecimalNumber.Factory.newInstance();
        indentNumber.setVal(BigInteger.valueOf(headingLevel));

        // lower number > style is more prominent in the formats bar
        ctStyle.setUiPriority(indentNumber);

        CTOnOff onoffnull = CTOnOff.Factory.newInstance();
        ctStyle.setUnhideWhenUsed(onoffnull);

        // style shows up in the formats bar
        ctStyle.setQFormat(onoffnull);

        // style defines a heading of the given level
        CTPPr ppr = CTPPr.Factory.newInstance();
        ppr.setOutlineLvl(indentNumber);
        ctStyle.setPPr(ppr);

        XWPFStyle style = new XWPFStyle(ctStyle);

        // is a null op if already defined
        XWPFStyles styles = docxDocument.createStyles();

        style.setType(STStyleType.PARAGRAPH);
        styles.addStyle(style);

    }

    /**
     * 字体像素大小转换为word字体大小
     *
     * @param level
     * @return
     */
    public static Integer fontSizeConvert(String level) {
        Integer fontSize = null;
        if (StringUtils.isBlank(level)) {
            return fontSize;
        }
        switch (level) {
            // 对应像素大小 10px
            case "1":
                fontSize = 7;
                break;
            // 对应像素大小 13px
            case "2":
                fontSize = 8;
                break;
            // 对应像素大小 16px
            case "3":
                fontSize = 9;
                break;
            // 对应像素大小 18px
            case "4":
                fontSize = 10;
                break;
            // 对应像素大小 24px
            case "5":
                fontSize = 14;
                break;
            // 对应像素大小 32px
            case "6":
                fontSize = 18;
                break;
            // 对应像素大小 48px
            case "7":
                fontSize = 28;
                break;
            case "8":
                fontSize = 36;
                break;
            case "9":
                fontSize = 48;
                break;
            case "10":
                fontSize = 72;
                break;
            default:
                fontSize = 5;
        }
        return fontSize;
    }

    /**
     * 17 种标准色是 aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, white, yellow。
     *
     * @param color
     * @return
     * @date 2020年4月7日 下午7:16:39
     */
    public static STHighlightColor.Enum getBackground(String color) {
        color = color.replaceAll(" ", "");
        if ("yellow".equals(color) || "rgb(255,255,0)".equals(color) || "#FFFF00".equals(color)) {
            //1-黄色
            return STHighlightColor.YELLOW;
        } else if ("lime".equals(color) || "rgb(0,255,0)".equals(color) || "#00FF00".equals(color)) {
            //2-绿色
            return STHighlightColor.GREEN;
        } else if ("aqua".equals(color) || "rgb(0,255,255)".equals(color) || "#00FFFF".equals(color)) {
            //3-青色
            return STHighlightColor.CYAN;
        } else if ("fuchsia".equals(color) || "rgb(255,0,255)".equals(color) || "#FF00FF".equals(color)) {
            //4-粉红色
            return STHighlightColor.MAGENTA;
        } else if ("blue".equals(color) || "rgb(0,0,255)".equals(color) || "#0000FF".equals(color)) {
            //5-蓝色
            return STHighlightColor.BLUE;
        } else if ("red".equals(color) || "rgb(255,0,0)".equals(color) || "#FF0000".equals(color)) {
            //6-红色
            return STHighlightColor.RED;
        } else if ("navy".equals(color) || "rgb(0,0,128)".equals(color) || "#000080".equals(color)) {
            //7-深蓝色
            return STHighlightColor.DARK_BLUE;
        } else if ("teal".equals(color) || "rgb(0,128,128)".equals(color) || "#008080".equals(color)) {
            //8-深青色
            return STHighlightColor.DARK_CYAN;
        } else if ("green".equals(color) || "rgb(0,128,0)".equals(color) || "#008000".equals(color)) {
            //9-深绿色
            return STHighlightColor.DARK_GREEN;
        } else if ("purple".equals(color) || "rgb(128,0,128)".equals(color) || "#800080".equals(color)) {
            //10-深粉红色,紫色
            return STHighlightColor.DARK_MAGENTA;
        } else if ("maroon".equals(color) || "rgb(128,0,0)".equals(color) || "#800000".equals(color)) {
            //11-深红色
            return STHighlightColor.DARK_RED;
        } else if ("olive".equals(color) || "rgb(128,128,0)".equals(color) || "#808000".equals(color)) {
            //12-深黄色
            return STHighlightColor.DARK_YELLOW;
        } else if ("gray".equals(color) || "rgb(128,128,128)".equals(color) || "#808080".equals(color)) {
            //13-深灰色
            return STHighlightColor.DARK_GRAY;
        } else if ("silver".equals(color) || "rgb(192,192,192)".equals(color) || "#C0C0C0".equals(color)) {
            //14-浅灰色
            return STHighlightColor.LIGHT_GRAY;
        } else if ("black".equals(color) || "rgb(0,0,0)".equals(color) || "#000000".equals(color)) {
            //15-黑色
            return STHighlightColor.BLACK;
        } else {
            //无色
            return STHighlightColor.NONE;
        }
    }

    /**
     * rgb(245, 219, 77) 转十六进制 #F5DB4D 并去除#
     * @param rgb rgb(245, 219, 77)
     * @return F5DB4D
     */
    public static String rgbToColorValue(String rgb) {
        String[] rgbValues = rgb.substring(rgb.indexOf('(') + 1, rgb.lastIndexOf(')')).split(",");
        // 转十六进制
        String hexColor = String.format("#%02X%02X%02X", Integer.parseInt(rgbValues[0]), Integer.parseInt(rgbValues[1]), Integer.parseInt(rgbValues[2]));
        // 在POI中去掉'#'符号
        return hexColor.substring(1);
    }

    /**
     * 创建列表(有序,无序)
     */
    public static Integer createNumberList(XWPFDocument docxDocument, Element e, List<String> allStyles, boolean isOl, Integer abstractNumSize) {

        List<Node> nodes = e.childNodes();

        // 初始化文档的编号定义部分
        XWPFNumbering numbering = docxDocument.getNumbering();
        if (ObjectUtil.isNull(numbering)) {
            numbering = docxDocument.createNumbering();
        }

        while (ObjectUtil.isNotNull(numbering.getAbstractNum(BigInteger.valueOf(abstractNumSize)))) {
            abstractNumSize++;
        }

        // 创建有序列表编号定义
        CTAbstractNum ctAbstractNum = CTAbstractNum.Factory.newInstance();
        XWPFAbstractNum abstractNum = new XWPFAbstractNum(ctAbstractNum);
        abstractNum.getAbstractNum().setAbstractNumId(BigInteger.valueOf(abstractNumSize));

        // 样式
        CTLvl ctLvl = ctAbstractNum.addNewLvl();

        if (isOl) {
            // 层数
            ctLvl.setIlvl(BigInteger.ZERO);
            // 设置为十进制格式
            ctLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL);
//            // 设置起始数字(对于有序列表)
            ctLvl.addNewStart().setVal(BigInteger.valueOf(1));

            ctLvl.addNewLvlText().setVal("%1.");
        } else {
            // 设置实心圆点
            CTNumFmt ctNumFmt = CTNumFmt.Factory.newInstance();
            ctNumFmt.setVal(STNumberFormat.BULLET);
            ctLvl.setNumFmt(ctNumFmt);
            CTLevelText ctLevelText = ctLvl.addNewLvlText();
            ctLevelText.setVal("\u2022"); // 设置实心圆点作为项目符号
        }

        CTJc ctJc = ctLvl.addNewLvlJc();
        ctJc.setVal(STJc.LEFT);

        // 添加有序列表定义到文档的编号系统
        numbering.addAbstractNum(abstractNum);
        BigInteger numId = numbering.addNum(BigInteger.valueOf(abstractNumSize));

        // 创建有序列表项
        for (Node node : nodes) {
            // 创建段落并关联到有序列表
            XWPFParagraph ulParagraph = docxDocument.createParagraph();
            ulParagraph.setNumID(numId);
            // 创建文本运行并添加内容
            Element children = (Element) node;
            abstractNumSize = createXWPFRun(docxDocument, ulParagraph, children, allStyles, abstractNumSize);
        }
        return abstractNumSize++;
    }


}



  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
POI是一种常用的技术,用于将HTML换为DOCX格式的文档。然而,使用POIHTMLDOCX格式时,可能会遇到一些困难,特别是在处理图片和表格方面。有些代码只能将HTML换为DOC格式,而不是DOCX格式。此外,对于用户想要在换后的文档中自由更改HTML并重新组织的情况,POI可能不太适合。因此,POI在处理复杂的HTMLDOCX的情况下可能不是最佳选择。[1] 如果POI无法满足您的需求,您可以尝试使用docx4j。docx4j是另一个功能强大的库,它支持将HTML换为DOCX格式,并且还支持图片的base64码。docx4j还集成了全面的格式化处理功能,可以处理复杂的文档格式。但是,有时在使用docx4j生成的文档中,表格和图片的宽度可能会比文档宽度多出一部分,这可能需要进一步的调试和解决方案。[2] 另外,您还可以考虑使用JACOB库。JACOB是一个用于在Java中调用DLL的桥梁库。通过使用JACOB,您可以调用Word原生的换格式功能,将HTML换为DOCX格式。JACOB的代码简洁易用,并且换后的图片和表格不会出现错乱的情况。您可以使用jsoup对HTML代码进行格式化处理,然后将图片文件处理成与WordHTML的方式相似的文件和文件夹结构,最后调用Word原生的HTML换功能来生成最终的文档。[3] 综上所述,对于HTML换为DOCX格式的需求,您可以尝试使用POIdocx4j或JACOB库,根据您的具体需求选择最适合的解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值