docx4j转换HTML并生成word文档实践

一、背景

在项目开发中,有一个需求需要将富文本编辑器中的内容转换为word文档。在网上看了很多开源第三方工具包的对比,最终选择了docx4j,主要原因有一下几点:

  1. 可以将html转换为word
  2. 对word操作功能强大,相对于开源工具
  3. docx4j官网提供在线word转Java代码功能。主要方便对比排查生成样式问题

二、遇到的问题

在使用docx4j对html进行转换,遇到了下面这些问题:

  1. docx4j生成table表格样式不正确。例如:在word中表格边框不展示、表格对齐方式不正确
  2. 对一些样式复杂或错误的html标签,无法处理会直接报错

三、docx4j问题使用办法

在介绍如何生成word之前,先介绍下,如何通过docx4j官网提供的在线word文档转为Java代码功能,当我们将html转为word文档后发现样式不正确或想单独设置样式是,这个功能可以帮助我们来如何进行设置,好我们先开始。

第一步:

  • 你需要进入官网这个地址就是进行word转换的地址

第二步:

  • 上传word文档。有必要说明下:这里上传的文档,是你要参考的word文档,也就是你想生成最终效果的文档。正常都是基于需求部门提供的一个文档模板,告诉IT生成出来的要长这样子。
  • 上传之后点击【Process】按钮进行转换

第三步:

  • 转换成功后,可以看到该word文档的XHTML标签,点击【/word/document.xml】标签,查看文档主体结构,该结构展示了整个文档的内如文字、内容样式属性,想了解每个标签含义可以复制发给大模型帮助解答。点击结构中的某一个标签,就可以看到该标签对于的Java代码。

  • 这里分享下,我什么情况看代码什么情况看标签:

    当我想要生成【模板文档】中某个样式效果时,我看去看代码,通过代码我可以知道文档样式如何设置的,我一般会直接把这段代码copy到项目中

    当我发现生成的word样式与【模板文档】中的样式不一致,但不知道为什么,这时我会把生成的word文档XHTML标签与【模板文档】XHTML标签进行对比,查看标签样式属性哪里有区别。

四、实践过程

第一步,导入依赖包

<dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.15.3</version>
    </dependency>
	<!-- HTML 转 word文档XHTML -->
    <dependency>
        <groupId>org.docx4j</groupId>
        <artifactId>docx4j-ImportXHTML</artifactId>
        <version>8.3.11</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
            <exclusion>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-compress</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.docx4j</groupId>
        <artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
        <version>8.3.11</version>
    </dependency>
	<!-- 修复异常的HTML标签 -->
    <dependency>
        <groupId>net.sf.jtidy</groupId>
        <artifactId>jtidy</artifactId>
        <version>r938</version>
    </dependency>

第二步,创建word文档对象,设置文档样式。

  1. 我这里提供的代码,主要是基于我在实际开发中遇到的问题所写的,供大家作为参考。
  2. 代码中体现了我在【遇到的问题】章节中做提出的
  3. 代码中使用了大量【docx4j】包中的对象,因为我要对html转换后的XHTML标签样式进行重新设置,基本不使用html中自带样式。相当于重写了word样式
  4. 【docx4j】包对象操作代码,来自于我将需求部门提供的【模板文档】上传到【docx4j】官网进行转换,将转换生成的代码copy到项目进行稍微删减修改即可使用

package com.test.common;

import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Entities;
import org.w3c.tidy.Tidy;

import javax.xml.bind.JAXBElement;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigInteger;
import java.net.URI;
import java.util.List;


public class test {


    /**
     *  word文档 对象工厂类
     */
    public static final org.docx4j.wml.ObjectFactory WML_OBJECT_FACTORY = new org.docx4j.wml.ObjectFactory();
    public static void generateWord(String title, String html) throws Exception {

        // 创建一个空白Word文档
        WordprocessingMLPackage wordMlPackage = WordprocessingMLPackage.createPackage();

        /*设置字体,把字体文件添加项目中,不依赖服务器*/
        Mapper fontMapper = new IdentityPlusMapper();
        URI uri = ClausesWordUtil.class.getResource("/fonts/simsun.ttc").toURI();
        // 加载本地字体
        PhysicalFonts.addPhysicalFonts("SimSunLocal", uri);
        fontMapper.put("SimSun", PhysicalFonts.get("SimSunLocal"));
        wordMlPackage.setFontMapper(fontMapper);


        MainDocumentPart mainPart = wordMlPackage.getMainDocumentPart();

        // 获取或创建 SectPr(Section Properties)
        SectPr sectPr = wordMlPackage.getDocumentModel().getSections().get(0).getSectPr();
        if (sectPr == null) {
            sectPr = new SectPr();
            mainPart.getJaxbElement().getBody().setSectPr(sectPr);
        }

        SectPr.PgSz sectprpgsz = WML_OBJECT_FACTORY.createSectPrPgSz();
        sectPr.setPgSz(sectprpgsz);
        sectprpgsz.setH( BigInteger.valueOf( 16838) );
        sectprpgsz.setW( BigInteger.valueOf( 11906) );


        // 创建并设置页面边距。 ⚠️文档页边距数值来源于模板文件的数值
        SectPr.PgMar pgMar = WML_OBJECT_FACTORY.createSectPrPgMar();
        pgMar.setTop(BigInteger.valueOf(1440));    // 上边距:1440 Twips = 1 inch
        pgMar.setRight(BigInteger.valueOf(1800));  // 右边距:1800 Twips = 1.25 inch
        pgMar.setBottom(BigInteger.valueOf(1440)); // 下边距:1440 Twips = 1 inch
        pgMar.setLeft(BigInteger.valueOf(1800));   // 左边距:1800 Twips = 1.25 inch
        pgMar.setHeader(BigInteger.valueOf(851));  // 页眉:851 Twips
        pgMar.setFooter(BigInteger.valueOf(992));  // 页脚:992 Twips
        pgMar.setGutter(BigInteger.valueOf(0));    // 装订线宽度:0
        // 将 pgMar 设置到 sectPr 中
        sectPr.setPgMar(pgMar);

    }



    /**
     *  word文档 标题 设置方法
     * @param wordMlPackage 文档对象
     * @param title 标题
     */
    public static void addTitle(WordprocessingMLPackage wordMlPackage, String title) {

        P p = WML_OBJECT_FACTORY.createP();
        // Create object for pPr
        PPr ppr = WML_OBJECT_FACTORY.createPPr();
        p.setPPr(ppr);
        // Create object for rPr
        ParaRPr pararpr = WML_OBJECT_FACTORY.createParaRPr();
        ppr.setRPr(pararpr);
        // Create object for rFonts
        RFonts rfonts = WML_OBJECT_FACTORY.createRFonts();
        pararpr.setRFonts(rfonts);
        rfonts.setAscii( "SimSun");
        rfonts.setCs( "SimSun");
        rfonts.setEastAsia( "SimSun");
        rfonts.setHAnsi( "SimSun");
        // Create object for spacing
        CTSignedTwipsMeasure signedtwipsmeasure = WML_OBJECT_FACTORY.createCTSignedTwipsMeasure();
        pararpr.setSpacing(signedtwipsmeasure);
        signedtwipsmeasure.setVal( BigInteger.valueOf( 6) );
        // Create object for sz
        HpsMeasure hpsmeasure = WML_OBJECT_FACTORY.createHpsMeasure();
        pararpr.setSz(hpsmeasure);
        hpsmeasure.setVal( BigInteger.valueOf( 32) );
        // Create object for bCs
        BooleanDefaultTrue booleandefaulttrue = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
        pararpr.setBCs(booleandefaulttrue);
        // Create object for szCs
        HpsMeasure hpsmeasure2 = WML_OBJECT_FACTORY.createHpsMeasure();
        pararpr.setSzCs(hpsmeasure2);
        hpsmeasure2.setVal( BigInteger.valueOf( 32) );
        // Create object for b
        BooleanDefaultTrue booleandefaulttrue2 = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
        pararpr.setB(booleandefaulttrue2);
        // Create object for adjustRightInd
        BooleanDefaultTrue booleandefaulttrue3 = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
        ppr.setAdjustRightInd(booleandefaulttrue3);
        // Create object for snapToGrid
        BooleanDefaultTrue booleandefaulttrue4 = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
        ppr.setSnapToGrid(booleandefaulttrue4);
        // Create object for spacing
        PPrBase.Spacing pprbasespacing = WML_OBJECT_FACTORY.createPPrBaseSpacing();
        ppr.setSpacing(pprbasespacing);
        pprbasespacing.setAfter( BigInteger.valueOf( 156) );
        pprbasespacing.setAfterLines( BigInteger.valueOf( 50) );
        // Create object for jc
        Jc jc = WML_OBJECT_FACTORY.createJc();
        ppr.setJc(jc);
        jc.setVal(org.docx4j.wml.JcEnumeration.CENTER);
//        p.setParaId( "2399AB22");
        // Create object for r
        R r = WML_OBJECT_FACTORY.createR();
        p.getContent().add( r);
        // Create object for rPr
        RPr rpr = WML_OBJECT_FACTORY.createRPr();
        r.setRPr(rpr);
        // Create object for rFonts
        RFonts rfonts2 = WML_OBJECT_FACTORY.createRFonts();
        rpr.setRFonts(rfonts2);
        rfonts2.setAscii( "SimSun");
        rfonts2.setCs( "SimSun");
        rfonts2.setEastAsia( "SimSun");
        rfonts2.setHint(org.docx4j.wml.STHint.EAST_ASIA);
        rfonts2.setHAnsi( "SimSun");
        // Create object for spacing
        CTSignedTwipsMeasure signedtwipsmeasure2 = WML_OBJECT_FACTORY.createCTSignedTwipsMeasure();
        rpr.setSpacing(signedtwipsmeasure2);
        signedtwipsmeasure2.setVal( BigInteger.valueOf( 6) );
        // Create object for sz
        HpsMeasure hpsmeasure3 = WML_OBJECT_FACTORY.createHpsMeasure();
        rpr.setSz(hpsmeasure3);
        hpsmeasure3.setVal( BigInteger.valueOf( 32) );
        // Create object for bCs
        BooleanDefaultTrue booleandefaulttrue5 = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
        rpr.setBCs(booleandefaulttrue5);
        // Create object for szCs
        HpsMeasure hpsmeasure4 = WML_OBJECT_FACTORY.createHpsMeasure();
        rpr.setSzCs(hpsmeasure4);
        hpsmeasure4.setVal( BigInteger.valueOf( 32) );
        // Create object for b
        BooleanDefaultTrue booleandefaulttrue6 = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
        rpr.setB(booleandefaulttrue6);
        // Create object for t (wrapped in JAXBElement)
        Text text = WML_OBJECT_FACTORY.createText();
        JAXBElement<Text> textWrapped = WML_OBJECT_FACTORY.createRT(text);
        r.getContent().add( textWrapped);
        text.setValue(title);
        wordMlPackage.getMainDocumentPart().addObject( p);
    }

    /**
     *  添加二级标题, 该方法用于添加不存在标题。 示例: 第x条
     *  没有标题的二级条目数据处理需求: 将条目正文 与 二级标题序号 同时渲染在一行,示例:
     *   第x条 xxxx正文内容
     *
     * @param wordMlPackage      word文档对象
     * @param entryLevel2TitleOrderIndex   word二级序号
     * @param entryContent                 word条目正文
     * @throws Docx4JException              docx4j异常
     */
    public static void addLevel2TitleAndRichTextToWord(WordprocessingMLPackage wordMlPackage, String entryLevel2TitleOrderIndex, String entryContent) throws Docx4JException {
        // 条目正文Html转为 word文档段落集合,一个段落为一个P对象
        List<Object> htmlToXhtmlList = htmlToXhtmlImporter(wordMlPackage, entryContent);

        if (htmlToXhtmlList.isEmpty()) {
            return;
        }
        for (int i = 0; i < htmlToXhtmlList.size(); i++) {
            Object item = htmlToXhtmlList.get(i);
            if (item instanceof P) {
                P htmlP = (P) item;
                // Create object for pPr
                PPr ppr = WML_OBJECT_FACTORY.createPPr();
                htmlP.setPPr(ppr);
                // Create object for ind
                PPrBase.Ind pprbaseind = WML_OBJECT_FACTORY.createPPrBaseInd();
                ppr.setInd(pprbaseind);
                pprbaseind.setFirstLineChars(BigInteger.valueOf(200));
                pprbaseind.setFirstLine(BigInteger.valueOf(444));
                // Create object for snapToGrid
                BooleanDefaultTrue booleandefaulttrue = WML_OBJECT_FACTORY.createBooleanDefaultTrue();
                ppr.setSnapToGrid(booleandefaulttrue);
                // Create object for spacing
                PPrBase.Spacing pprbasespacing = WML_OBJECT_FACTORY.createPPrBaseSpacing();
                ppr.setSpacing(pprbasespacing);
                pprbasespacing.setAfter(BigInteger.valueOf(156));
                pprbasespacing.setAfterLines(BigInteger.valueOf(50));
                htmlP.getContent().removeIf(rObj -> ((R) rObj).getRPr() == null);
                for (int j = 0; j < htmlP.getContent().size(); j++) {
                    R htmlR = (R) htmlP.getContent().get(j);
                    RPr htmlRpr = htmlR.getRPr();
                    // Create object for rFonts
                    RFonts rfonts = WML_OBJECT_FACTORY.createRFonts();
                    htmlRpr.setRFonts(rfonts);
                    rfonts.setAscii("SimSun");
                    rfonts.setCs("SimSun");
                    rfonts.setEastAsia("SimSun");
                    rfonts.setHint(org.docx4j.wml.STHint.EAST_ASIA);
                    rfonts.setHAnsi("SimSun");
                    // Create object for spacing
                    CTSignedTwipsMeasure signedtwipsmeasure = WML_OBJECT_FACTORY.createCTSignedTwipsMeasure();
                    htmlRpr.setSpacing(signedtwipsmeasure);
                    signedtwipsmeasure.setVal(BigInteger.valueOf(6));
                    // Create object for szCs
                    HpsMeasure hpsmeasure2 = WML_OBJECT_FACTORY.createHpsMeasure();
                    htmlRpr.setSzCs(hpsmeasure2);
                    htmlRpr.setSz(hpsmeasure2);
                    hpsmeasure2.setVal(BigInteger.valueOf(21));
                }
                wordMlPackage.getMainDocumentPart().addObject(htmlP);
            }
            else if (item instanceof Tbl) {
                Tbl tbl = (Tbl) item;
                TblPr tblpr = tbl.getTblPr();
                setTblPr(tblpr);
                tbl.setTblPr(tblpr);
                CTTblPrBase.TblStyle tblStyle = Context.getWmlObjectFactory().createCTTblPrBaseTblStyle();
                tblStyle.setVal("TableGrid");
                tblpr.setTblStyle(tblStyle);

                TblGrid tblgrid = tbl.getTblGrid();
//                setTblGrid(tblgrid);
                tbl.setTblGrid(tblgrid);
                // 循环表格每行
                tbl.getContent().forEach(trObj -> {
                    Tr tr = (Tr) trObj;
                    CTTblPrEx tblprex = WML_OBJECT_FACTORY.createCTTblPrEx();
                    setTblPrEx(tblprex);
                    tr.setTblPrEx(tblprex);
                    TrPr trPr = tr.getTrPr() == null ? WML_OBJECT_FACTORY.createTrPr() : tr.getTrPr();
                    setTrPr(trPr);
                    tr.setTrPr(trPr);
                    // 循环表格每列
                    tr.getContent().forEach(tcObj -> {
                        Tc tc = (Tc) tcObj;
                        TcPr tcPr = tc.getTcPr();
                        if (tcPr == null) {
                            tcPr = WML_OBJECT_FACTORY.createTcPr();
                            tc.setTcPr(tcPr);
                        }
                        tcPr.setTcMar(null);
                        CTVerticalJc tcPrValign = WML_OBJECT_FACTORY.createCTVerticalJc();
                        tcPrValign.setVal(STVerticalJc.CENTER);
                        // 默认设置单元格内容【垂直居中】
                        tcPr.setVAlign(tcPrValign);
                        TcPrInner.TcBorders tcBorders = WML_OBJECT_FACTORY.createTcPrInnerTcBorders();
                        setTcBorders(tcBorders);
                        tcPr.setTcBorders(tcBorders);
                        // 处理单元格内容
                        tc.getContent().forEach(pObj -> {
                            P p = (P) pObj;
                            PPr pPr = p.getPPr();
                            if (pPr != null) {
                                // 设置单元格文本 前后间距为0。 主要体现在单元格高度
                                PPrBase.Spacing sp = WML_OBJECT_FACTORY.createPPrBaseSpacing();
                                sp.setBefore(BigInteger.valueOf(0));
                                sp.setAfter(BigInteger.valueOf(0));
                                pPr.setSpacing(sp);
                                pPr.setInd(null);
                            }
                            p.getContent().removeIf(rObj -> ((R) rObj).getRPr() == null);
                            p.getContent().forEach(rObj -> {
                                R r = (R) rObj;
                                RPr rpr = r.getRPr();
                                // Create object for rFonts
                                RFonts rfonts = WML_OBJECT_FACTORY.createRFonts();
                                rpr.setRFonts(rfonts);
                                rfonts.setAscii("SimSun");
                                rfonts.setCs("SimSun");
                                rfonts.setEastAsia("SimSun");
                                rfonts.setHint(org.docx4j.wml.STHint.EAST_ASIA);
                                rfonts.setHAnsi("SimSun");
                                CTVerticalAlignRun pPrRprVertAlign = WML_OBJECT_FACTORY.createCTVerticalAlignRun();
                                // 设置文本在行内的垂直对齐方式
                                pPrRprVertAlign.setVal(STVerticalAlignRun.BASELINE);
                                rpr.setVertAlign(pPrRprVertAlign);
                                HpsMeasure hpsmeasure2 = WML_OBJECT_FACTORY.createHpsMeasure();
                                rpr.setSzCs(hpsmeasure2);
                                rpr.setSz(hpsmeasure2);
                                hpsmeasure2.setVal(BigInteger.valueOf(21));
                            });
                        });
                    });

                });
                wordMlPackage.getMainDocumentPart().addObject(item);
            }else {
                // 表示当前内容不是文本段落。 不是文本段落,默认不与二级标题 合并一行
                wordMlPackage.getMainDocumentPart().addObject(item);
            }
        }
    }
 /**
     *   富文本 html 转 xhtml
     * @param wordMlPackage  Word文档对象
     * @param richText  html字符串
     * @return  word文档对象标签集合
     * @throws Docx4JException 转换word文档标签异常类型
     */
    public static List<Object> htmlToXhtmlImporter(WordprocessingMLPackage wordMlPackage, String richText) throws Docx4JException {
        Document htmlDocument = Jsoup.parse(richText);
        htmlDocument.select("a").unwrap();
        htmlDocument.select("script").remove();
        htmlDocument.select("th").tagName("td");
        htmlDocument.select("input").remove();

        String html = htmlDocument.html().replace("&nbsp;", "\u00A0").replace("<br>", "<br/>");
        log.info("打印富文本 html: {}", html);
        XHTMLImporterImpl xhtmlImporter = new XHTMLImporterImpl(wordMlPackage);
        try {
            return xhtmlImporter.convert(html, null);
        }catch (Exception e) {
            log.error("htmlToXhtmlImporter html转换word文档标签异常", e);;
            try {
                String cleanHtml = fixHtmlByTidy(html);
                log.info("Tidy 修复后的html: {}", cleanHtml);
                return xhtmlImporter.convert(cleanHtml, null);
            }catch (Exception e1) {
                log.error("htmlToXhtmlImporter Tidy修复html转换word文档标签异常", e1);
                // 转义文本中特殊字符
                String escapeText = Entities.escape(htmlDocument.text());
                return xhtmlImporter.convert("<html><head></head><body>文本生成失败,由于内容样式过于复杂导致word转换异常,请对该内容样式进系统调整:<p> <span style=\"background-color: #ffff00;\">"+escapeText+"</span></p> </body></html>", null);
            }

        }
    }

    /**
     *   通过TIdy三方工具表,用于修复不符合规范的HTML富文本标签,不规范的HTML,会导致转换XHTML异常。
     * @param html  富文本HTML
     * @return  修复后的HTML
     */
    public static String fixHtmlByTidy(String html) {
        // 创建一个 Tidy 实例
        Tidy tidy = new Tidy();
        tidy.setXHTML(true);
        tidy.setShowWarnings(true);
        tidy.setQuiet(false);
        tidy.setForceOutput(true); // 即使存在错误,仍然输出修复后的HTML
        tidy.setWraplen(0); // 禁用换行
        tidy.setFixComments(true); // 修复HTML注释
        tidy.setFixBackslash(true); // 修复反斜杠问题
        tidy.setDropEmptyParas(true); // 删除空的<p>标签
        tidy.setFixUri(true); // 修复URL中的非法字符
        // 将输入的 HTML 内容封装到 StringReader 中
        StringReader reader = new StringReader(html);

        // 使用 StringWriter 来存储修复后的 HTML
        StringWriter writer = new StringWriter();

        // 解析并修复 HTML,输出结果到 StringWriter
        tidy.parse(reader, writer);
        return writer.toString();
    }

    /**
     *  设置表格行属性:
     *<w:trPr>:
     *      表示表格行(<w:tr>)的属性标签。所有子元素定义了这一行的样式和布局属性。
     *      <w:trHeight w:val="326" w:hRule="atLeast"/>:
     *           w:val="326":指定了行高为 326 twips,即二十分之一点。326 twips 大约等于 16.3 磅(~22像素)。
     *           w:hRule="atLeast":表示行高的规则为“至少”,即行的高度至少为 326 twips,但如果内容需要更多空间,行高可以增加。这种设置常用于允许表格行根据内容的高度自动扩展,但不会低于指定的最小高度。
     *      <w:jc w:val="center"/>:
     *           w:val="center":定义了行内内容的对齐方式为居中对齐。这影响的是整行中的文本和其他元素的水平对齐方式。
     * @param trPr 当前表格行属性对象
     */
    public static void setTrPr(TrPr trPr) {
        trPr.getCnfStyleOrDivIdOrGridBefore().clear();
        // Create object for trHeight (wrapped in JAXBElement)
        CTHeight height = WML_OBJECT_FACTORY.createCTHeight();
        JAXBElement<CTHeight> heightWrapped = WML_OBJECT_FACTORY.createCTTrPrBaseTrHeight(height);
        trPr.getCnfStyleOrDivIdOrGridBefore().add(heightWrapped);
        height.setVal(BigInteger.valueOf(326));
        height.setHRule(STHeightRule.AT_LEAST);

        Jc jc2 = WML_OBJECT_FACTORY.createJc();
        JAXBElement<Jc> jcWrapped = WML_OBJECT_FACTORY.createCTTrPrBaseJc(jc2);
        trPr.getCnfStyleOrDivIdOrGridBefore().add(jcWrapped);
        jc2.setVal(JcEnumeration.CENTER);
    }


    /**
     *设置单元格边框属性
     * 上下左右
     * insideH 定义表格内部水平边框(即行与行之间的水平线
     * insideV 定义表格内部垂直边框(即列与列之间的垂直线
     * w:val(Value):
     *      指定边框的样式。常见的值包括:
     *      single:单线边框。
     *      double:双线边框。
     *      dotted:点状边框。
     *      dashed:虚线边框。
     *      none:无边框。
     * w:color:
     *      指定边框的颜色。auto 表示自动颜色,通常是黑色或基于文档主题的颜色。也可以使用十六进制颜色代码,如 w:color="FF0000" 表示红色。
     * w:sz(Size):
     *      定义边框的粗细,单位为 eights of a point(八分之一磅)。例如,w:sz="4" 表示 4/8 = 0.5 磅。
     * w:space:
     *      定义边框与内容之间的间距,单位为 eights of a point。w:space="0" 表示无间隙。
     * @param tcBorders 单元格边框对象
     */
    public static void setTcBorders(TcPrInner.TcBorders tcBorders) {
//        CTBorder border7 = WML_OBJECT_FACTORY.createCTBorder();
//        tcBorders.setLeft(border7);
//        border7.setVal(STBorder.SINGLE);
//        border7.setColor( "000000");
//        border7.setSz( BigInteger.valueOf( 4) );
//        border7.setSpace( BigInteger.valueOf( 0) );
//        // Create object for right
        CTBorder border8 = WML_OBJECT_FACTORY.createCTBorder();
        tcBorders.setRight(border8);
        border8.setVal(STBorder.SINGLE);
        border8.setColor( "000000");
        border8.setSz( BigInteger.valueOf( 4) );
        border8.setSpace( BigInteger.valueOf( 0) );
//        // Create object for top
//        CTBorder border9 = WML_OBJECT_FACTORY.createCTBorder();
//        tcBorders.setTop(border9);
//        border9.setVal(STBorder.SINGLE);
//        border9.setColor( "000000");
//        border9.setSz( BigInteger.valueOf( 4) );
//        border9.setSpace( BigInteger.valueOf( 0) );
//        // Create object for bottom
        CTBorder border10 = WML_OBJECT_FACTORY.createCTBorder();
        tcBorders.setBottom(border10);
        border10.setVal(STBorder.SINGLE);
        border10.setColor( "000000");
        border10.setSz( BigInteger.valueOf( 4) );
        border10.setSpace( BigInteger.valueOf( 0) );
//        // Create object for insideH
        CTBorder border11 = WML_OBJECT_FACTORY.createCTBorder();
        tcBorders.setInsideH(border11);
        border11.setVal(STBorder.NONE);
        border11.setColor( "000000");
        border11.setSz( BigInteger.valueOf( 4) );
        border11.setSpace( BigInteger.valueOf( 0) );
        // Create object for insideV
        CTBorder border12 = WML_OBJECT_FACTORY.createCTBorder();
        tcBorders.setInsideV(border12);
        border12.setVal(STBorder.SINGLE);
        border12.setColor( "000000");
        border12.setSz( BigInteger.valueOf( 4) );
        border12.setSpace( BigInteger.valueOf( 0) );
    }


    /**
     * 设置表格行扩展属性
     * <w:tblCellMar> 作用:定义单元格内容与单元格边框之间的边距(即单元格的内边距)。
     *      子元素:
     *      <w:top>:设置单元格顶部的内边距。
     *      <w:left>:设置单元格左侧的内边距。
     *      <w:bottom>:设置单元格底部的内边距。
     *      <w:right>:设置单元格右侧的内边距。
     *
     *
     * @param tblprex 表格的扩展属性(table properties extension),在这里它用于定义表格行的额外属性
     */
    public static void setTblPrEx(CTTblPrEx tblprex) {
        // Create object for tblBorders
//        TblBorders tblborders2 = WML_OBJECT_FACTORY.createTblBorders();
//        tblprex.setTblBorders(tblborders2);
        // Create object for left
//        CTBorder border7 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders2.setLeft(border7);
//        border7.setVal(STBorder.SINGLE);
//        border7.setColor( "000000");
//        border7.setSz( BigInteger.valueOf( 4) );
//        border7.setSpace( BigInteger.valueOf( 0) );
//        // Create object for right
//        CTBorder border8 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders2.setRight(border8);
//        border8.setVal(STBorder.SINGLE);
//        border8.setColor( "000000");
//        border8.setSz( BigInteger.valueOf( 4) );
//        border8.setSpace( BigInteger.valueOf( 0) );
//        // Create object for top
//        CTBorder border9 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders2.setTop(border9);
//        border9.setVal(STBorder.SINGLE);
//        border9.setColor( "000000");
//        border9.setSz( BigInteger.valueOf( 4) );
//        border9.setSpace( BigInteger.valueOf( 0) );
//        // Create object for bottom
//        CTBorder border10 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders2.setBottom(border10);
//        border10.setVal(STBorder.SINGLE);
//        border10.setColor( "000000");
//        border10.setSz( BigInteger.valueOf( 4) );
//        border10.setSpace( BigInteger.valueOf( 0) );
//        // Create object for insideH
//        CTBorder border11 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders2.setInsideH(border11);
//        border11.setVal(STBorder.SINGLE);
//        border11.setColor( "000000");
//        border11.setSz( BigInteger.valueOf( 4) );
//        border11.setSpace( BigInteger.valueOf( 0) );
        // Create object for insideV
//        CTBorder border12 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders2.setInsideV(border12);
//        border12.setVal(STBorder.SINGLE);
//        border12.setColor( "000000");
//        border12.setSz( BigInteger.valueOf( 4) );
//        border12.setSpace( BigInteger.valueOf( 0) );
        // Create object for tblCellMar
        CTTblCellMar tblcellmar2 = WML_OBJECT_FACTORY.createCTTblCellMar();
        tblprex.setTblCellMar(tblcellmar2);
        // Create object for left
        TblWidth tblwidth6 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar2.setLeft(tblwidth6);
        tblwidth6.setType( "dxa");
        tblwidth6.setW( BigInteger.valueOf( 5) );
        // Create object for right
        TblWidth tblwidth7 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar2.setRight(tblwidth7);
        tblwidth7.setType( "dxa");
        tblwidth7.setW( BigInteger.valueOf( 10) );
        // Create object for top
        TblWidth tblwidth8 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar2.setTop(tblwidth8);
        tblwidth8.setType( "dxa");
        tblwidth8.setW( BigInteger.valueOf( 51) );
        // Create object for bottom
        TblWidth tblwidth9 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar2.setBottom(tblwidth9);
        tblwidth9.setType( "dxa");
        tblwidth9.setW( BigInteger.valueOf( 0) );
    }


    /**
     *设置表格属性
     * <w:tblW>
     *      作用:设置表格的宽度。
     *      w:w="7508":宽度值,单位为 twips(twentieths of a point,磅的二十分之一)。7508 twips 约等于 375.4 points(磅),大约是 5.2 英寸(13.2 厘米)。
     *      w:type="dxa":指定宽度单位为 twips。
     * <w:jc>
     *      作用:设置表格的对齐方式(table justification)。
     *      w:val="center":将表格水平居中对齐。可选值还有 "left"(左对齐)、"right"(右对齐)、"both"(两端对齐)。
     * <w:tblCellSpacing>
     *      作用:设置表格单元格之间的间距。
     *      w:w="0":单元格间距为 0 twips,即单元格之间没有间距。
     *      w:type="dxa":指定单位为 twips。
     * <w:tblLayout>
     *      作用:定义表格的布局方式(table layout)。
     *      w:type="autofit":表格自动调整布局,以适应内容。"autofit" 表示表格的列宽会根据内容自动调整。
     *<w:tblLook>
     *      作用:定义表格的视觉样式,通常与样式表相关联,用于指定特定的表格外观。
     *      w:val="04A0":指定表格的视觉样式为 04A0,通常用于更加丰富多彩的表格布局。
     * <w:tblCellMar>
     *      作用:定义单元格的内边距(cell margins),即单元格内容与单元格边框之间的距离
     *<w:tblBorders>
     *      作用:定义表格的边框样式,包括表格外部边框和内部单元格之间的边框
     *
     * @param tblpr 定义表格的属性(table properties),如宽度、对齐方式、单元格间距等。
     */
    public static void setTblPr(TblPr tblpr) {
        if (tblpr == null) {
            tblpr = WML_OBJECT_FACTORY.createTblPr();
        }
        TblWidth tblWidth = WML_OBJECT_FACTORY.createTblWidth();
        tblWidth.setType( "dxa");
        tblWidth.setW( BigInteger.valueOf( 0) );
        tblpr.setTblCellSpacing(tblWidth);
        CTTblLook tbllook = WML_OBJECT_FACTORY.createCTTblLook();
        tblpr.setTblLook(tbllook);
        tbllook.setVal( "04A0");
        // Create object for jc
        Jc jc = WML_OBJECT_FACTORY.createJc();
        tblpr.setJc(jc);
        jc.setVal(JcEnumeration.CENTER);
        // Create object for tblStyle
//        CTTblPrBase.TblStyle tblprbasetblstyle = WML_OBJECT_FACTORY.createCTTblPrBaseTblStyle();
//        tblpr.setTblStyle(tblprbasetblstyle);
//        tblprbasetblstyle.setVal( "TableGrid");
        /*
        *   TableNormal: 应用表格的默认样式,没有特殊的边框或背景色。
            TableGrid: 应用网格线样式,表格中每个单元格都有实线边框。
            ColorfulGrid: 多彩的网格样式,常用于更加丰富多彩的表格布局。
        * */
        // Create object for tblW
        TblWidth tblwidth = WML_OBJECT_FACTORY.createTblWidth();
        tblpr.setTblW(tblwidth);
        tblwidth.setType( "dxa");
        tblwidth.setW( BigInteger.valueOf( 7508) );
        // Create object for tblBorders
        TblBorders tblborders = WML_OBJECT_FACTORY.createTblBorders();
        setTblBorders(tblborders);
        tblpr.setTblBorders(tblborders);
        CTTblLayoutType tbllayouttype = WML_OBJECT_FACTORY.createCTTblLayoutType();
        tblpr.setTblLayout(tbllayouttype);
        tbllayouttype.setType(STTblLayoutType.AUTOFIT);
        // Create object for tblCellMar
        CTTblCellMar tblcellmar = WML_OBJECT_FACTORY.createCTTblCellMar();
        setTblCTTblCellMar(tblcellmar);
        tblpr.setTblCellMar(tblcellmar);
    }



    /**
     *在表格层级设置 单元格内容与单元格边框之间的距离
     *<w:top w:w="51" w:type="dxa"/>
     *      作用:设置单元格内容与单元格顶部边框之间的距离。
     * <w:left w:w="5" w:type="dxa"/>
     *      作用:设置单元格内容与单元格左侧边框之间的距离。
     * <w:bottom w:w="0" w:type="dxa"/>
     *      作用:设置单元格内容与单元格底部边框之间的距离。
     * <w:right w:w="10" w:type="dxa"/>
     *      作用:设置单元格内容与单元格右侧边框之间的距离。
     *
     * @param tblcellmar 定义单元格的内容与边框之间的内边距
     */
    public static void setTblCTTblCellMar(CTTblCellMar tblcellmar) {
        // Create object for left
        TblWidth tblwidth2 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar.setLeft(tblwidth2);
        tblwidth2.setType( "dxa");
        tblwidth2.setW( BigInteger.valueOf( 5) );
        // Create object for right
        TblWidth tblwidth3 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar.setRight(tblwidth3);
        tblwidth3.setType( "dxa");
        tblwidth3.setW( BigInteger.valueOf( 10) );
        // Create object for top
        TblWidth tblwidth4 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar.setTop(tblwidth4);
        tblwidth4.setType( "dxa");
        tblwidth4.setW( BigInteger.valueOf( 51) );
        // Create object for bottom
        TblWidth tblwidth5 = WML_OBJECT_FACTORY.createTblWidth();
        tblcellmar.setBottom(tblwidth5);
        tblwidth5.setType( "dxa");
        tblwidth5.setW( BigInteger.valueOf( 0) );
    }


    /**
     * 设置表格边框属性
     * 这里主要设置了: 表格的左、上、水平、垂直边框, 另外两个方向有单元格设置。
     * insideH 定义表格内部水平边框(即行与行之间的水平线
     * insideV 定义表格内部垂直边框(即列与列之间的垂直线
     * w:val(Value):
     *      指定边框的样式。常见的值包括:
     *      single:单线边框。
     *      double:双线边框。
     *      dotted:点状边框。
     *      dashed:虚线边框。
     *      none:无边框。
     * w:color:
     *      指定边框的颜色。auto 表示自动颜色,通常是黑色或基于文档主题的颜色。也可以使用十六进制颜色代码,如 w:color="FF0000" 表示红色。
     * w:sz(Size):
     *      定义边框的粗细,单位为 eights of a point(八分之一磅)。例如,w:sz="4" 表示 4/8 = 0.5 磅。
     * w:space:
     *      定义边框与内容之间的间距,单位为 eights of a point。w:space="0" 表示无间隙。
     *
     * @param tblborders  定义表格的边框样式,包括表格外部边框和内部单元格之间的边框。
     */
    public static void setTblBorders(TblBorders tblborders) {
        // Create object for left
        CTBorder border = WML_OBJECT_FACTORY.createCTBorder();
        tblborders.setLeft(border);
        border.setVal(STBorder.SINGLE);
        border.setColor( "000000");
        border.setSz( BigInteger.valueOf( 4) );
        border.setSpace( BigInteger.valueOf( 0) );
//        // Create object for right
//        CTBorder border2 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders.setRight(border2);
//        border2.setVal(STBorder.SINGLE);
//        border2.setColor( "000000");
//        border2.setSz( BigInteger.valueOf( 4) );
//        border2.setSpace( BigInteger.valueOf( 0) );
//        // Create object for top
        CTBorder border3 = WML_OBJECT_FACTORY.createCTBorder();
        tblborders.setTop(border3);
        border3.setVal(STBorder.SINGLE);
        border3.setColor( "000000");
        border3.setSz( BigInteger.valueOf( 4) );
        border3.setSpace( BigInteger.valueOf( 0) );
//        // Create object for bottom
//        CTBorder border4 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders.setBottom(border4);
//        border4.setVal(STBorder.SINGLE);
//        border4.setColor( "000000");
//        border4.setSz( BigInteger.valueOf( 4) );
//        border4.setSpace( BigInteger.valueOf( 0) );
        // Create object for insideH
//        CTBorder border5 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders.setInsideH(border5);
//        border5.setVal(STBorder.SINGLE);
//        border5.setColor( "000000");
//        border5.setSz( BigInteger.valueOf( 4) );
//        border5.setSpace( BigInteger.valueOf( 0) );
////         Create object for insideV
//        CTBorder border6 = WML_OBJECT_FACTORY.createCTBorder();
//        tblborders.setInsideV(border6);
//        border6.setVal(STBorder.SINGLE);
//        border6.setColor( "000000");
//        border6.setSz( BigInteger.valueOf( 4) );
//        border6.setSpace( BigInteger.valueOf( 0) );
    }
}



posted @   Easn°  阅读(1144)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示