表格样式问题

运行的同样的代码为什么我的表格会突然变成这样子,另外一个是正常的
不正常表格:


正常表格:

代码中没有看出来任何问题

@Override
public int replacing(ReplacingArgs args) throws Exception {
    // 获取文档对象
    Document doc = (Document) args.getMatchNode().getDocument();
    DocumentBuilder builder = new DocumentBuilder(doc);
    // 打印捕获的内容
    Node matchNode = args.getMatchNode();
    System.out.println("Captured Node Text: " + matchNode.getText());
    // 定位到占位符
    builder.moveTo(args.getMatchNode());
    // 获取表头和数据
    List<String> headers = (List<String>) tableData.get("header"); // 表头
    List<List<Object>> tableDataList = (List<List<Object>>) tableData.get("tableDataList");// 数据集
    //合计数据
    Map summaryRowMap = (Map) tableData.get("summaryRowMap");
    if (summaryRowMap != null) {
        DocumentTableUtil.addTableAndAddSummaryRow(builder, headers, tableDataList, summaryRowMap);
    }else{
        DocumentTableUtil.addTable(builder, headers, tableDataList);
    }
    //删除占位符
    args.getMatchNode().remove();
    return ReplaceAction.SKIP; // 跳过默认替换
}




/**
 */
public class DocumentTableUtil {
    public static Table addTable(DocumentBuilder builder, List headers, List<List<Object>> rowData) throws Exception {
        // 开始表格
        Table table = builder.startTable();
        // 添加表头
        List<Double> columnWidths = new ArrayList<>();
        if (headers != null && !headers.isEmpty()) {
            addTableHeader(builder, headers, columnWidths);
        }
        if(CollectionUtils.isNotEmpty(rowData)){
            // 填充行数据
            for (int i = 0; i < rowData.size(); i++) {
                List<Object> list = rowData.get(i);
                //设置表体格式
                setTableBodyFormat(builder);
                if(CollectionUtils.isNotEmpty(list)){
                    for (int j = 0; j < list.size(); j++) {
                        builder.insertCell(); // 插入单元格
                        builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(columnWidths.get(j)));
                        Object object = list.get(j);
                        builder.write(object != null ? object.toString() : "");
                    }
                    builder.endRow();
                }
            }
        }
        builder.endTable();
        //返回表格对象
        return table;
    }

    //添加表格并且合计行
    public static Table addTableAndAddSummaryRow(DocumentBuilder builder, List headers, List<List<Object>> rowData, Map summaryRowMap) throws Exception {
        // 开始表格
        Table table = builder.startTable();
        // 设置表头格式添加表头
        // 添加表头
        // 添加表头
        List<Double> columnWidths = new ArrayList<>();
        if (headers != null && !headers.isEmpty()) {
            addTableHeader(builder, headers, columnWidths);
        }
        if (CollectionUtils.isNotEmpty(rowData)) {
            // 填充行数据
            for (int i = 0; i < rowData.size(); i++) {
                List<Object> list = rowData.get(i);
                //设置表体格式
                setTableBodyFormat(builder);
                if (CollectionUtils.isNotEmpty(list)) {
                    for (int j = 0; j < list.size(); j++) {
                        builder.insertCell(); // 插入单元格
                        builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(columnWidths.get(j)));
                        Object object = list.get(j);
                        builder.write(object != null ? object.toString() : "");
                    }
                    builder.endRow();
                }
            }
        }
        if (summaryRowMap != null) {
            Integer endMergeIndex = MapUtils.getInteger(summaryRowMap, "endMergeIndex", 0);
            String label = MapUtils.getString(summaryRowMap, "label", "合计");
            List<Object> dataList = (List<Object>) MapUtils.getObject(summaryRowMap, "dataList", new ArrayList<>());
            //addSummaryRow(builder, endMergeIndex, label, dataList, null);
            addSummaryRow(builder, endMergeIndex, label, dataList, null, columnWidths);
        }

        builder.endTable();
        //返回表格对象
        return table;
    }


    public static void addTableHeader(DocumentBuilder builder, List<String> headers) throws Exception {
        builder.getFont().setBold(false);
        // 设置表头字体样式
        builder.getFont().setSize(10); // 设置字体大小为10(可根据需要调整)
        //段落的对齐方式设置为居中对齐
        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
        builder.getRowFormat().setHeadingFormat(true);
        //方法将清除单元格的所有格式设置,包括字体、颜色、边框等。如果您只想清除特定的格式设置,可以使用其他适当的方法,例如clearFormatting()方法的重载版本,该版本接受一个参数,用于指定要清除的格式设置类型。
        //builder.getCellFormat().clearFormatting();
        //设置单元格的宽度  磅(points)。

        //这只单元格的高度  磅(points)。
        //builder.getRowFormat().setHeight(1);
        // 设置行的高度为自动
        builder.getRowFormat().setHeightRule(HeightRule.AUTO);
        builder.getRowFormat().setHeight(0); // 设置高度为0以便自动调整为内容高度
        //设置单元格垂直
        builder.getCellFormat().setVerticalAlignment(CellVerticalAlignment.CENTER);
        //单元格背景颜色设置
        builder.getCellFormat().getShading().setBackgroundPatternColor(new Color(128, 128, 128));
        //单元格边框颜色设置
        //builder.getRowFormat().getBorders().setColor(new Color(1, 1, 1));
        //设置字体颜色
        builder.getFont().setColor(Color.BLACK); // 设置字体颜色为黑色
        //这行代码禁用单元格中的文本自动换行功能
        //builder.getCellFormat().setWrapText(false);
        //这行代码启用单元格中文本的自动缩放以适应单元格大小。
        //builder.getCellFormat().setFitText(true);
        //这行代码设置行的高度规则为精确值。
        //builder.getRowFormat().setHeightRule(HeightRule.EXACTLY);
        //这行代码设置行的边框线样式为浮雕3D效果。
        //builder.getRowFormat().getBorders().setLineStyle(LineStyle.ENGRAVE_3_D);

        int totalCols = headers.size();
        double defaultPercent = 100.0 / totalCols;
        for (String header : headers) {
            builder.insertCell();
            String title = header;
            double percent = defaultPercent;
            // 检查是否为 header-20 形式的指定百分比宽度
            if (header.contains("-")) {
                String[] parts = header.split("-");
                if (parts.length == 2 && parts[1].matches("\\d+(\\.\\d+)?")) {
                    title = parts[0]; // 显示文字
                    percent = Double.parseDouble(parts[1]);
                }
            }
            builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(percent));
            builder.write(title);
        }
        builder.endRow();
    }

    public static void addTableHeader(DocumentBuilder builder, List<String> headers, List<Double> columnWidths) throws Exception {
        builder.getFont().setBold(false);
        // 设置表头字体样式
        builder.getFont().setSize(10); // 设置字体大小为10(可根据需要调整)
        //段落的对齐方式设置为居中对齐
        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
        builder.getRowFormat().setHeadingFormat(true);
        //方法将清除单元格的所有格式设置,包括字体、颜色、边框等。如果您只想清除特定的格式设置,可以使用其他适当的方法,例如clearFormatting()方法的重载版本,该版本接受一个参数,用于指定要清除的格式设置类型。
        //builder.getCellFormat().clearFormatting();
        //设置单元格的宽度  磅(points)。

        //这只单元格的高度  磅(points)。
        //builder.getRowFormat().setHeight(1);
        // 设置行的高度为自动
        builder.getRowFormat().setHeightRule(HeightRule.AUTO);
        builder.getRowFormat().setHeight(0); // 设置高度为0以便自动调整为内容高度
        //设置单元格垂直
        builder.getCellFormat().setVerticalAlignment(CellVerticalAlignment.CENTER);
        //单元格背景颜色设置
        builder.getCellFormat().getShading().setBackgroundPatternColor(new Color(128, 128, 128));
        //单元格边框颜色设置
        //builder.getRowFormat().getBorders().setColor(new Color(1, 1, 1));
        //设置字体颜色
        builder.getFont().setColor(Color.BLACK); // 设置字体颜色为黑色
        //这行代码禁用单元格中的文本自动换行功能
        //builder.getCellFormat().setWrapText(false);
        //这行代码启用单元格中文本的自动缩放以适应单元格大小。
        //builder.getCellFormat().setFitText(true);
        //这行代码设置行的高度规则为精确值。
        //builder.getRowFormat().setHeightRule(HeightRule.EXACTLY);
        //这行代码设置行的边框线样式为浮雕3D效果。
        //builder.getRowFormat().getBorders().setLineStyle(LineStyle.ENGRAVE_3_D);
        int totalCols = headers.size();
        double totalAssignedPercent = 0;
        for (int i = 0; i < totalCols; i++) {
            columnWidths.add(0.0); // 初始化大小
        }
        List<Integer> autoWidthIndexes = new ArrayList<>();
        for (int i = 0; i < totalCols; i++) {
            String header = headers.get(i);
            String title = header;
            double percent = -1;
            if (header.contains("-")) {
                String[] parts = header.split("-");
                if (parts.length == 2 && parts[1].matches("\\d+(\\.\\d+)?")) {
                    title = parts[0];
                    percent = Double.parseDouble(parts[1]);
                    columnWidths.set(i, percent);
                    totalAssignedPercent += percent;
                }
            }
            if (percent == -1) {
                autoWidthIndexes.add(i);
            }
            headers.set(i, title); // 替换为纯标题文本
        }

        double remainingPercent = 100.0 - totalAssignedPercent;
        double autoPercent = autoWidthIndexes.isEmpty() ? 0 : remainingPercent / autoWidthIndexes.size();
        for (Integer index : autoWidthIndexes) {
            columnWidths.set(index, autoPercent);
        }
        // 插入单元格
        for (int i = 0; i < headers.size(); i++) {
            builder.insertCell();
            builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(columnWidths.get(i)));
            builder.write(headers.get(i));
        }
        builder.endRow();
    }


    public static void setTableBodyFormat(DocumentBuilder builder) throws Exception {
        //清除格式
        builder.getCellFormat().clearFormatting();
        //设置单元格垂直
        builder.getCellFormat().setVerticalAlignment(CellVerticalAlignment.CENTER);
        //单元格背景颜色设置
        //builder.getCellFormat().getShading().setBackgroundPatternColor(new Color(209, 230, 250));
        //单元格边框颜色设置
        builder.getRowFormat().getBorders().setColor(new Color(1, 1, 1));
        //设置字体颜色
        builder.getFont().setColor(new Color(1, 1, 1));
        //拒绝字体加粗
        builder.getFont().setBold(false);
        //这行代码禁用单元格中的文本自动换行功能
        //builder.getCellFormat().setWrapText(false);
        builder.getRowFormat().setHeadingFormat(false); // 确保内容行不设置为表头
    }

    /**
     * 添加合计行(可自定义合并列和样式)
     *
     * @param builder      DocumentBuilder
     * @param mergeColumns 要合并的列数(从左往右合并)
     * @param label        合计文字,如“合计”
     * @param totalValues  合计数据列表(从未合并列开始填充)
     * @param backgroundColor 合计行背景色,可为 null 表示不设置
     * @throws Exception 异常
     */
    public static void addSummaryRow(DocumentBuilder builder, int mergeColumns, String label, List<Object> totalValues, Color backgroundColor) throws Exception {
        builder.getCellFormat().clearFormatting();
        builder.getRowFormat().setHeightRule(HeightRule.AUTO);
        builder.getCellFormat().setVerticalAlignment(CellVerticalAlignment.CENTER);
        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);

        // 设置背景色(如果指定了)
        if (backgroundColor != null) {
            builder.getCellFormat().getShading().setBackgroundPatternColor(backgroundColor);
        }
        // 合并列
        builder.insertCell();
        builder.getCellFormat().setHorizontalMerge(CellMerge.FIRST);
        builder.write(label != null ? label : "合计");
        for (int i = 1; i < mergeColumns; i++) {
            builder.insertCell();
            builder.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS);
        }
        // 插入合计的值
        if (totalValues != null) {
            for (Object val : totalValues) {
                String value = "";
                if (val instanceof Integer) {
                    value = String.valueOf(val);
                } else if (val instanceof BigDecimal) {
                    value = String.valueOf(val);
                } else if (val instanceof Double) {
                    value = String.valueOf(val);
                } else if (val instanceof Float) {
                    value = String.valueOf(val);
                } else if (val instanceof Long) {
                    value = String.valueOf(val);
                } else if (val instanceof String) {
                    value = String.valueOf(val);
                }
                builder.insertCell();
                builder.getCellFormat().setHorizontalMerge(CellMerge.NONE);
                builder.write(val != null ? value : "");
            }
        }
        builder.endRow();
    }

    //自适应列宽 添加合计行
    public static void addSummaryRow(DocumentBuilder builder, int mergeColumns, String label, List<Object> totalValues, Color backgroundColor, List<Double> columnWidths) throws Exception {
        builder.getCellFormat().clearFormatting();
        builder.getRowFormat().setHeightRule(HeightRule.AUTO);
        builder.getCellFormat().setVerticalAlignment(CellVerticalAlignment.CENTER);
        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);

        // 设置背景色(如果指定了)
        if (backgroundColor != null) {
            builder.getCellFormat().getShading().setBackgroundPatternColor(backgroundColor);
        }
        // 合并列
        builder.insertCell();
        builder.getCellFormat().setHorizontalMerge(CellMerge.FIRST);
        // 设置合并单元格宽度为前 mergeColumns 列宽度之和
        if (columnWidths != null && columnWidths.size() >= mergeColumns) {
            double totalWidth = 0;
            for (int i = 0; i < mergeColumns; i++) {
                totalWidth += columnWidths.get(i);
            }
            builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(totalWidth));
        }


        builder.write(label != null ? label : "合计");
        for (int i = 1; i < mergeColumns; i++) {
            builder.insertCell();
            builder.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS);
        }
        // 插入合计的值
        if (totalValues != null) {
            for (int j = 0; j < totalValues.size(); j++) {
                Object val = totalValues.get(j);

                String value = "";
                if (val instanceof Integer) {
                    value = String.valueOf(val);
                } else if (val instanceof BigDecimal) {
                    value = String.valueOf(val);
                } else if (val instanceof Double) {
                    value = String.valueOf(val);
                } else if (val instanceof Float) {
                    value = String.valueOf(val);
                } else if (val instanceof Long) {
                    value = String.valueOf(val);
                } else if (val instanceof String) {
                    value = String.valueOf(val);
                }
                builder.insertCell();
                builder.getCellFormat().setHorizontalMerge(CellMerge.NONE);


                // 设置当前单元格宽度
                if (columnWidths != null && columnWidths.size() > mergeColumns + j) {
                    builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(columnWidths.get(mergeColumns + j)));
                }
                builder.write(val != null ? value : "");
            }
        }
        builder.endRow();
    }


    //可以指定开始合并到结束合并的
    public static void addSummaryRowCustomize(DocumentBuilder builder, int startIndex, int endIndex, String label, List<Object> totalValues, Color backgroundColor, List<Double> columnWidths) throws Exception {
        builder.getCellFormat().clearFormatting();
        builder.getRowFormat().setHeightRule(HeightRule.AUTO);
        builder.getCellFormat().setVerticalAlignment(CellVerticalAlignment.CENTER);
        builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);

        // 设置背景色(如果指定了)
        if (backgroundColor != null) {
            builder.getCellFormat().getShading().setBackgroundPatternColor(backgroundColor);
        }
        double totalWidth = 0;
        // 设置合并单元格宽度为前 mergeColumns 列宽度之和
        if (columnWidths != null && columnWidths.size() >= endIndex) {
            for (int i = startIndex - 1; i < endIndex; i++) {
                totalWidth += columnWidths.get(i);
            }
        }

        if (totalValues != null) {
            for (int j = 0; j < totalValues.size(); j++) {
                if (j < startIndex - 1) {
                    builder.insertCell();
                    builder.write(totalValues.get(j).toString());
                    builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(columnWidths.get(j)));
                }
                //如果为要合并的那一列,就设置开始合并
                else if (j == startIndex - 1) {
                    builder.insertCell();
                    builder.getCellFormat().setHorizontalMerge(CellMerge.FIRST);
                    builder.write(totalValues.get(startIndex).toString());
                    builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(totalWidth));
                } else {
                    //到结束之前都要设置向前合并
                    if (j < endIndex) {
                        builder.insertCell();
                        builder.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS);
                    } else {
                        builder.insertCell();
                        builder.getCellFormat().setHorizontalMerge(CellMerge.NONE);
                        builder.write(totalValues.get(j).toString());
                        builder.getCellFormat().setPreferredWidth(PreferredWidth.fromPercent(columnWidths.get(j)));
                    }
                }

            }
        }
        builder.endRow();
    }


}

@JOOL 我用我的数据测试了你的代码,在创建表格时没有出现格式问题。 这可能是文档样式问题或其他可能发生的问题。 如果可能,请提供带有输入和输出文件的简单应用程序,以帮助我们重现问题。 我们将进行调查并提供更多信息。

这是我的测试模版,用来做占位符填充;
testTemplate.docx (1.8 MB)

位置如下:


这是我替换表格以后的效果:

这是我的测试主程序代码:

 public static void main(String[] args) throws Exception {

        // 1. 加载模板
        Document doc = new Document("/Users/WorkSpace/file/运营管理报告/testTemplate.docx");

        // 2. 准备表格数据
        Map<String, Object> tableData = new HashMap<>();

        // 表头(带宽度信息)
        List<String> header = Arrays.asList("序号-10", "名称-30", "金额-30", "备注-30");
        tableData.put("header", header);

        // 表体数据
        List<List<Object>> tableDataList = new ArrayList<>();
        tableDataList.add(Arrays.asList(1, "项目A", "1000", "正常"));
        tableDataList.add(Arrays.asList(2, "项目B", "2000", "正常"));
        tableDataList.add(Arrays.asList(3, "项目C", "3000", "正常"));

        tableData.put("tableDataList", tableDataList);

        // 合计行(可选)
        Map<String, Object> summaryRowMap = new HashMap<>();
        summaryRowMap.put("label","合计");
        summaryRowMap.put("dataList", Arrays.asList("3000", "-"));
        summaryRowMap.put("endMergeIndex",2);
        tableData.put("summaryRowMap", summaryRowMap);

        // 3. 执行表格占位符替换
        ReplaceWithCustomTableHandler06 processor = new ReplaceWithCustomTableHandler06();
        processor.process(doc, "{TABLE13}", tableData);
        // 4. 保存文档
        doc.save("output.docx");
    }

这是我的自定义替换类:

public class ReplaceWithCustomTableHandler06 implements CustomDocumentTableProcessor {
    private Map<String, Object> tableData;



    @Override
    public void process(Document document, String placeholderName, Map<String, Object> tableData) throws Exception {
        this.tableData = tableData;
        DocumentBuilder builder = new DocumentBuilder(document);
        // 1️⃣ 查找占位符
        FindReplaceOptions options = new FindReplaceOptions();
        options.setReplacingCallback(args -> {
            // 定位到占位符
            builder.moveTo(args.getMatchNode());
            // 开始表格
            builder.startTable();
            List<String> header = (List<String>) tableData.get("header");

            List<Double> columnWidths = new ArrayList<>();
            Map summaryRowMap = (Map) tableData.get("summaryRowMap");
            List<List<Object>> tableDataList = (List<List<Object>>) tableData.get("tableDataList");
            //DocumentTableUtil.addTable(builder, header, tableDataList);
            DocumentTableUtil.addTableAndAddSummaryRow(builder,header , tableDataList, summaryRowMap);
            //删除占位符
            args.getMatchNode().remove();
            return ReplaceAction.SKIP; // 跳过默认替换
        });
        // 5️⃣ 执行替换
        document.getRange().replace(Pattern.compile(Pattern.quote(placeholderName)), "", options);
    }
}

DocumentTableUtil:表格处理操作类和上文中一致;
拜托了,非常感谢!!!

@JOOL 替换段落的段落样式很奇怪,因此出现了这个问题。 为了获得正确的输出,我建议您在添加表格前重置段落格式,如下所示: builder.getParagraphFormat().clearFormatting();`或者为段落更换不同的样式。 更新后的代码应如下所示

FindReplaceOptions options = new FindReplaceOptions();
options.setReplacingCallback(args -> {
    // 定位到占位符
    builder.moveTo(args.getMatchNode());
    builder.getParagraphFormat().clearFormatting();
    // 开始表格
    builder.startTable();
    List<String> header = (List<String>) tableData.get("header");

    List<Double> columnWidths = new ArrayList<>();
    Map summaryRowMap = (Map) tableData.get("summaryRowMap");
    List<List<Object>> tableDataList = (List<List<Object>>) tableData.get("tableDataList");
    //DocumentTableUtil.addTable(builder, header, tableDataList);
    DocumentTableUtil.addTableAndAddSummaryRow(builder,header , tableDataList, summaryRowMap);
    //删除占位符
    args.getMatchNode().remove();
    return ReplaceAction.SKIP; // 跳过默认替换
});