Html转word追加篇,关于hr标签分割线的显示

1. 又来了一个分割线的标签,哦豁,又得加代码啦。

1.1 上一篇代码中的select中最后加上hr

java 复制代码
  Elements elements = doc.select("title, p, img, h1, h2, h3, h4, h5, h6, br, table, ul, li, hr");

1.2 判断加上

java 复制代码
else if (tagName.equals("hr")) {
   drawHr(document, element);
}

1.3 然后在加上下边的常量语枚举和样式类

java 复制代码
    private static final Pattern PX_PATTERN = Pattern.compile("(\\d+)px?");
    private static final Pattern PERCENT_PATTERN = Pattern.compile("(\\d+)%?");
    // ==================== 数据类和常量 ====================

    public enum LineStyle {
        SOLID, DASHED, DOTTED, DOUBLE, NONE
    }

    public static class HrStyle {
        
        public int widthPercent = 100;
        public int thickness = 2;
        public Color color = Color.BLACK;
        public LineStyle lineStyle = LineStyle.SOLID;
        public ParagraphAlignment alignment = ParagraphAlignment.CENTER;
        public int space = 1; // 间距

        @Override
        public String toString() {
            return String.format("HrStyle{width=%d%%, thickness=%dpx, color=%s, style=%s, align=%s}",
                    widthPercent, thickness, rgbToHex(color), lineStyle, alignment);
        }
    }

    private static final java.util.Map<String, Color> COLOR_MAP = new java.util.HashMap<>();

    static {
        COLOR_MAP.put("black", Color.BLACK);
        COLOR_MAP.put("white", Color.WHITE);
        COLOR_MAP.put("red", Color.RED);
        COLOR_MAP.put("green", Color.GREEN);
        COLOR_MAP.put("blue", Color.BLUE);
        COLOR_MAP.put("yellow", Color.YELLOW);
        COLOR_MAP.put("cyan", Color.CYAN);
        COLOR_MAP.put("magenta", Color.MAGENTA);
        COLOR_MAP.put("gray", Color.GRAY);
        COLOR_MAP.put("grey", Color.GRAY);
        COLOR_MAP.put("darkgray", Color.DARK_GRAY);
        COLOR_MAP.put("lightgray", Color.LIGHT_GRAY);
        COLOR_MAP.put("orange", Color.ORANGE);
        COLOR_MAP.put("pink", Color.PINK);
    }

1.4 核心转换代码

java 复制代码
 /**
     * 主入口:从 Jsoup Element 绘制分割线
     * @param hrElement Jsoup 解析的 hr 元素
     */
    public static void drawHr(XWPFDocument document, Element hrElement) {
        HrStyle style = parseHrElement(hrElement);
        applyStyleToParagraph(document, style);
    }

    /**
     * 解析 JSoup Element 提取样式
     */
    public static HrStyle parseHrElement(Element element) {
        HrStyle style = new HrStyle();

        // 1. 解析 style 属性
        String styleAttr = element.attr("style");
        if (!styleAttr.isEmpty()) {
            parseStyleAttribute(styleAttr, style);
        }

        // 2. 解析直接属性(HTML4 遗留属性)
        // width 属性 (如 width="80%" 或 width="80")
        String widthAttr = element.attr("width");
        if (!widthAttr.isEmpty()) {
            style.widthPercent = parsePercent(widthAttr);
        }

        // size 属性 (线条高度/粗细)
        String sizeAttr = element.attr("size");
        if (!sizeAttr.isEmpty()) {
            style.thickness = parseInt(sizeAttr);
        }

        // color 属性
        String colorAttr = element.attr("color");
        if (!colorAttr.isEmpty()) {
            style.color = parseColor(colorAttr);
        }

        // noshade 属性(无阴影,实线)
        if (element.hasAttr("noshade")) {
            style.lineStyle = LineStyle.SOLID;
        }

        // align 属性(对齐方式)
        String alignAttr = element.attr("align");
        if (!alignAttr.isEmpty()) {
            style.alignment = parseAlignment(alignAttr);
        }

        // 3. 解析 class 属性(支持预设样式类)
        String classAttr = element.attr("class");
        if (!classAttr.isEmpty()) {
            applyCssClass(classAttr, style);
        }

        return style;
    }

    // ==================== 样式解析工具方法 ====================

    /**
     * 解析 style 属性中的 CSS 样式
     */
    private static void parseStyleAttribute(String styleAttr, HrStyle style) {
        // 分割多个 CSS 属性
        String[] declarations = styleAttr.split(";");

        for (String declaration : declarations) {
            declaration = declaration.trim();
            if (declaration.isEmpty()) continue;

            String[] parts = declaration.split(":", 2);
            if (parts.length != 2) continue;

            String property = parts[0].trim().toLowerCase();
            String value = parts[1].trim().toLowerCase();

            switch (property) {
                case "width":
                    style.widthPercent = parsePercent(value);
                    break;
                case "height":
                case "border-top-width":
                case "border-width":
                    style.thickness = parsePx(value);
                    break;
                case "color":
                case "border-color":
                case "border-top-color":
                    style.color = parseColor(value);
                    break;
                case "border-style":
                case "border-top-style":
                    style.lineStyle = parseLineStyle(value);
                    break;
                case "border":
                    // 简写属性:border: 2px solid red
                    parseBorderShorthand(value, style);
                    break;
                case "margin":
                case "margin-top":
                case "margin-bottom":
                    // 边距会影响间距
                    style.space = parsePx(value);
                    break;
                case "text-align":
                    style.alignment = parseAlignment(value);
                    break;
            }
        }
    }

    /**
     * 解析 border 简写属性
     */
    private static void parseBorderShorthand(String value, HrStyle style) {
        String[] parts = value.split("\\s+");
        for (String part : parts) {
            if (part.matches("\\d+px?")) {
                style.thickness = parsePx(part);
            } else if (isColor(part)) {
                style.color = parseColor(part);
            } else {
                style.lineStyle = parseLineStyle(part);
            }
        }
    }

    // ==================== 数据类型转换 ====================

    private static int parsePercent(String value) {
        Matcher m = PERCENT_PATTERN.matcher(value);
        return m.find() ? Integer.parseInt(m.group(1)) : 100;
    }

    private static int parsePx(String value) {
        Matcher m = PX_PATTERN.matcher(value);
        return m.find() ? Integer.parseInt(m.group(1)) : 2;
    }

    private static int parseInt(String value) {
        try {
            return Integer.parseInt(value.replaceAll("[^0-9]", ""));
        } catch (NumberFormatException e) {
            return 2;
        }
    }

    private static Color parseColor(String colorStr) {
        colorStr = colorStr.trim().toLowerCase();

        // 十六进制 #RRGGBB 或 #RGB
        if (colorStr.startsWith("#")) {
            return Color.decode(colorStr.length() == 4
                    ? "#" + colorStr.charAt(1) + colorStr.charAt(1)
                      + colorStr.charAt(2) + colorStr.charAt(2)
                      + colorStr.charAt(3) + colorStr.charAt(3)
                    : colorStr);
        }

        // rgb(r, g, b)
        if (colorStr.startsWith("rgb")) {
            return parseRgb(colorStr);
        }

        // 颜色名称映射
        return COLOR_MAP.getOrDefault(colorStr, Color.BLACK);
    }

    private static Color parseRgb(String rgb) {
        Pattern p = Pattern.compile("rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)");
        Matcher m = p.matcher(rgb);
        if (m.find()) {
            return new Color(
                    Integer.parseInt(m.group(1)),
                    Integer.parseInt(m.group(2)),
                    Integer.parseInt(m.group(3))
            );
        }
        return Color.BLACK;
    }

    private static LineStyle parseLineStyle(String value) {
        return switch (value) {
            case "dashed" -> LineStyle.DASHED;
            case "dotted" -> LineStyle.DOTTED;
            case "double" -> LineStyle.DOUBLE;
            case "none", "hidden" -> LineStyle.NONE;
            default -> LineStyle.SOLID;
        };
    }

    private static ParagraphAlignment parseAlignment(String value) {
        return switch (value.toLowerCase()) {
            case "left" -> ParagraphAlignment.LEFT;
            case "right" -> ParagraphAlignment.RIGHT;
            default -> ParagraphAlignment.CENTER;
        };
    }

    private static boolean isColor(String str) {
        return str.startsWith("#") || str.startsWith("rgb") || COLOR_MAP.containsKey(str);
    }

    // ==================== CSS 类预设 ====================

    private static void applyCssClass(String classNames, HrStyle style) {
        String[] classes = classNames.split("\\s+");
        for (String cls : classes) {
            switch (cls) {
                case "dashed":
                    style.lineStyle = LineStyle.DASHED;
                    break;
                case "dotted":
                    style.lineStyle = LineStyle.DOTTED;
                    break;
                case "double":
                    style.lineStyle = LineStyle.DOUBLE;
                    break;
                case "solid":
                    style.lineStyle = LineStyle.SOLID;
                    break;
                case "red":
                    style.color = Color.RED;
                    break;
                case "blue":
                    style.color = Color.BLUE;
                    break;
                case "gray":
                case "grey":
                    style.color = Color.GRAY;
                    break;
                case "thick":
                    style.thickness = 4;
                    break;
                case "thin":
                    style.thickness = 1;
                    break;
            }
        }
    }

    // ==================== 绘制实现 ====================

    /**
     * 应用样式到段落(创建分割线)
     */
    private static void applyStyleToParagraph(XWPFDocument document, HrStyle style) {
        if (style.lineStyle == LineStyle.NONE) return;

        XWPFParagraph paragraph = document.createParagraph();

        // 设置对齐
        paragraph.setAlignment(style.alignment);

        // 获取/创建段落属性
        CTP ctp = paragraph.getCTP();
        CTPPr pPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();

        // 设置段落底边边框作为分割线
        CTPBdr border = pPr.isSetPBdr() ? pPr.getPBdr() : pPr.addNewPBdr();

        CTBorder bottomBorder = CTBorder.Factory.newInstance();

        // 设置边框样式
        setBorderStyle(bottomBorder, style.lineStyle);

        // 设置颜色
        bottomBorder.setColor(rgbToHex(style.color));

        // 设置粗细 (以1/8磅为单位)
        int size = Math.max(2, style.thickness * 4);
        bottomBorder.setSz(BigInteger.valueOf(size));

        // 设置间距
        bottomBorder.setSpace(BigInteger.valueOf(style.space > 0 ? style.space : 1));

        border.setBottom(bottomBorder);

        // 设置宽度(通过缩进)
        if (style.widthPercent < 100) {
            setParagraphWidth(paragraph, style.widthPercent);
        }

        // 添加空运行保持段落
        XWPFRun run = paragraph.createRun();
        run.setText(" ");
        run.setFontSize(2);

        // 设置段落间距
        paragraph.setSpacingBefore(style.space * 50);
        paragraph.setSpacingAfter(style.space * 50);
    }

    private static void setBorderStyle(CTBorder border, LineStyle style) {
        switch (style) {
            case DASHED:
                border.setVal(STBorder.DASHED);
                break;
            case DOTTED:
                border.setVal(STBorder.DOTTED);
                break;
            case DOUBLE:
                border.setVal(STBorder.DOUBLE);
                break;
            case SOLID:
            default:
                border.setVal(STBorder.SINGLE);
                break;
        }
    }

    private static void setParagraphWidth(XWPFParagraph paragraph, int widthPercent) {
        CTPPr pPr = paragraph.getCTP().isSetPPr()
                ? paragraph.getCTP().getPPr()
                : paragraph.getCTP().addNewPPr();

        CTInd ind = pPr.isSetInd() ? pPr.getInd() : pPr.addNewInd();

        // 标准页面可写区域约 6.5 英寸 = 9360 twips
        int totalWidth = 9360;
        int indent = (totalWidth * (100 - widthPercent)) / 200;

        ind.setLeft(BigInteger.valueOf(indent));
        ind.setRight(BigInteger.valueOf(indent));
    }

    private static String rgbToHex(Color color) {
        return String.format("%02X%02X%02X",
                color.getRed(), color.getGreen(), color.getBlue());
    }

1.5 废话不多说,结束,展示一下样式

1.5.1 示例数据:
html 复制代码
<hr style=\"width:80%;height:2px;color:red;border-style:dashed;\">
<hr style=\"width:100%;color:#333;border-style:solid;\">
1.5.2 导出样式:
相关推荐
techdashen2 小时前
Go 1.25 新特性:Flight Recorder —— 像黑匣子一样捕捉线上 Bug
java·golang·bug
A_QXBlms2 小时前
企微群发消息技术实现:定时任务+模板消息
java·mybatis·企业微信
小李子呢02112 小时前
前端八股---axios封装
java·前端·javascript
斌味代码2 小时前
SpringBoot 实战总结:踩坑与解决方案全记录
java·spring boot·后端
摇滚侠2 小时前
Groovy 中如何定义集合
java·开发语言·python
0xDevNull2 小时前
Spring Boot 3.0动态多数据源切换实战教程
java·spring boot·后端
代码漫谈2 小时前
微服务 vs 单体架构:架构选型、实战拆解与决策指南
java·微服务·springboot·springcloud
神龙斗士2402 小时前
第一个Spring Boot程序
java·spring boot·java-ee·tomcat
gelald2 小时前
Spring Boot - 配置加载
java·spring boot·后端·spring