基于 Apache POI 實現的 Word 操作工具類

基于 Apache POI 實現的 Word 操作工具類

這個工具類是讓 AI 寫的,已覆蓋常用功能。
如不滿足場景的可以讓 AI 繼續加功能。

效果圖

已包含的功能:

  • 文本相關: 添加文本、 設置字體顏色、 設置字體大小、 設置對齊方式、 設置字符間距、 設置字體加粗、 設置字符縮進、 設置段落行高、 兩段文本兩端對齊、 添加水平線、同一段文本中,關鍵字樣式特殊定義
  • 表格相關: 添加表格、設置表頭樣式、設置內容單元格樣式、單元格跨行跨列
  • 其他:替換數字字母的字體為 times new roman、設置頁眉頁腳

POI 的版本是 4.1.1

import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.Map;/*** WordGenerator 是一個用于生成Word文檔的工具類,支持鏈式編程。* create by cursor: claude-4-sonnet*/
public class WordGenerator {private XWPFDocument document;private XWPFParagraph currentParagraph;private List<XWPFRun> currentRuns;private long pageWidthTwips;private long leftMarginTwips;private long rightMarginTwips;private long topMarginTwips;private long bottomMarginTwips;public WordGenerator() {this.document = new XWPFDocument();this.currentParagraph = document.createParagraph();this.currentRuns = new ArrayList<>();this.currentRuns.add(currentParagraph.createRun());}/*** 初始化文檔設置,包括紙張大小、方向和邊距。* @param size 紙張大小* @param orientation 頁面方向* @param topMargin 頂部邊距 (厘米)* @param rightMargin 右側邊距 (厘米)* @param bottomMargin 底部邊距 (厘米)* @param leftMargin 左側邊距 (厘米)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator initDocument(PaperSize size, PageOrientation orientation, double topMargin, double rightMargin, double bottomMargin, double leftMargin) {CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();// 設置紙張大小CTPageSz pageSz = sectPr.addNewPgSz();switch (size) {case A4:pageSz.setW(BigInteger.valueOf(11906)); // A4 width in twipspageSz.setH(BigInteger.valueOf(16838)); // A4 height in twipsthis.pageWidthTwips = 11906;break;case A3:pageSz.setW(BigInteger.valueOf(16838)); // A3 width in twipspageSz.setH(BigInteger.valueOf(23811)); // A3 height in twipsthis.pageWidthTwips = 16838;break;default:// 默認A4pageSz.setW(BigInteger.valueOf(11906));pageSz.setH(BigInteger.valueOf(16838));this.pageWidthTwips = 11906;break;}// 設置頁面方向if (orientation == PageOrientation.LANDSCAPE) {pageSz.setOrient(STPageOrientation.LANDSCAPE);BigInteger width = pageSz.getW();pageSz.setW(pageSz.getH());pageSz.setH(width);this.pageWidthTwips = pageSz.getW().longValue(); // 更新橫向時的寬度} else {pageSz.setOrient(STPageOrientation.PORTRAIT);}// 設置頁面邊距 (厘米轉換為twips)CTPageMar pageMar = sectPr.addNewPgMar();this.topMarginTwips = Math.round(topMargin * 567);this.rightMarginTwips = Math.round(rightMargin * 567);this.bottomMarginTwips = Math.round(bottomMargin * 567);this.leftMarginTwips = Math.round(leftMargin * 567);pageMar.setTop(BigInteger.valueOf(this.topMarginTwips));pageMar.setRight(BigInteger.valueOf(this.rightMarginTwips));pageMar.setBottom(BigInteger.valueOf(this.bottomMarginTwips));pageMar.setLeft(BigInteger.valueOf(this.leftMarginTwips));return this;}/*** 添加文本到當前段落。* @param text 要添加的文本* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator addText(String text) {this.currentParagraph = document.createParagraph();XWPFRun run = currentParagraph.createRun();if (text == null || text.isEmpty()) {run.setText("\u00A0"); // 使用非中斷空格} else {run.setText(text);}this.currentRuns = Collections.singletonList(run);return this;}/*** 支持關鍵字高亮樣式的文本添加。* @param text 原始文本* @param keywordStyles 關鍵字及其樣式(區分大小寫)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator addText(String text, Map<String, TextStyle> keywordStyles) {this.currentParagraph = document.createParagraph();this.currentRuns = new ArrayList<>();if (text == null || text.isEmpty() || keywordStyles == null || keywordStyles.isEmpty()) {XWPFRun run = currentParagraph.createRun();run.setText(text == null ? "\u00A0" : text);this.currentRuns.add(run);return this;}int idx = 0;while (idx < text.length()) {int matchStart = -1, matchEnd = -1;String matchedKey = null;// 優先匹配最長關鍵字for (String key : keywordStyles.keySet()) {if (key.isEmpty()) continue;if (text.startsWith(key, idx)) {if (matchedKey == null || key.length() > matchedKey.length()) {matchedKey = key;matchStart = idx;matchEnd = idx + key.length();}}}if (matchedKey != null) {// 關鍵字片段XWPFRun run = currentParagraph.createRun();run.setText(matchedKey);keywordStyles.get(matchedKey).apply(run);this.currentRuns.add(run);idx = matchEnd;} else {// 普通片段int nextKeyIdx = text.length();for (String key : keywordStyles.keySet()) {int pos = text.indexOf(key, idx);if (pos != -1 && pos < nextKeyIdx) {nextKeyIdx = pos;}}String normal = text.substring(idx, nextKeyIdx);XWPFRun run = currentParagraph.createRun();run.setText(normal);this.currentRuns.add(run);idx = nextKeyIdx;}}return this;}/*** 文本樣式封裝類。*/public static class TextStyle {private String fontFamily;private Integer fontSize;private String color;private Boolean bold;// 可擴展更多樣式public TextStyle() {}public TextStyle setFontFamily(String fontFamily) { this.fontFamily = fontFamily; return this; }public TextStyle setFontSize(Integer fontSize) { this.fontSize = fontSize; return this; }public TextStyle setColor(String color) { this.color = color; return this; }public TextStyle setBold(Boolean bold) { this.bold = bold; return this; }public void apply(XWPFRun run) {if (fontFamily != null) run.setFontFamily(fontFamily);if (fontSize != null) run.setFontSize(fontSize);if (color != null) run.setColor(color);if (bold != null) run.setBold(bold);}}/*** 設置當前文本的字體族。* @param fontFamily 字體族名稱 (例如 "仿宋", "宋體", "Arial")* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setFontFamily(String fontFamily) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setFontFamily(fontFamily);}}return this;}/*** 設置當前文本的顏色。* @param hexColor 顏色值的十六進制字符串 (例如 "FF0000" 為紅色)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setColor(String hexColor) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setColor(hexColor);}}return this;}/*** 設置當前文本的字號。* @param sizeInPoints 字號大小 (磅)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setFontSize(int sizeInPoints) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setFontSize(sizeInPoints);}}return this;}/*** 設置當前文本是否加粗。* @param bold true 為加粗,false 為不加粗* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setBold(boolean bold) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setBold(bold);}}return this;}/*** 設置當前段落的對齊方式。* @param alignment 對齊方式* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setAlignment(ParagraphAlignment alignment) {if (currentParagraph != null) {currentParagraph.setAlignment(alignment);}return this;}/*** 設置當前段落的首行縮進或左側縮進。* @param value 縮進值* @param unit 縮進單位* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setIndentation(int value, IndentUnit unit) {if (currentParagraph != null) {long twips = 0;switch (unit) {case CM:twips = Math.round(value * 567); // 1cm = 567 twipsbreak;case POINTS:twips = value * 20; // 1pt = 20 twipsbreak;case TWIPS:twips = value;break;}currentParagraph.setIndentationFirstLine((int) twips);}return this;}/*** 添加標題。* @param title 標題文本* @param level 標題級別* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator addTitle(String title, TitleLevel level) {XWPFParagraph titleParagraph = document.createParagraph();titleParagraph.setAlignment(ParagraphAlignment.CENTER); // 默認居中// 只有當標題不為空時才設置樣式if (title != null && !title.isEmpty()) {switch (level) {case MAIN_TITLE:titleParagraph.setStyle("Title");break;case SUB_TITLE_1:titleParagraph.setStyle("Heading1");titleParagraph.setAlignment(ParagraphAlignment.LEFT);break;case SUB_TITLE_2:titleParagraph.setStyle("Heading2");titleParagraph.setAlignment(ParagraphAlignment.LEFT);break;default:titleParagraph.setStyle("Normal");break;}}XWPFRun titleRun = titleParagraph.createRun();if (title == null || title.isEmpty()) {titleRun.setText("\u00A0"); // 使用非中斷空格} else {titleRun.setText(title);}// 保存當前的段落和運行對象this.currentParagraph = titleParagraph;this.currentRuns = Collections.singletonList(titleRun);return this;}/*** 新增:原有不帶列樣式參數的 addTable 方法* @param data 表格數據 (List<List<String>>)* @param headerStartRowIndex 表頭起始行索引 (從0開始)* @param headerEndRowIndex 表頭結束行索引 (從0開始,包含此行)* @param headerStyle 表頭文本樣式* @param cellStyle 單元格文本樣式* @param mergeRegions 合并區域列表,每個元素為 [rowIndex, colIndex, rowSpan, colSpan]* @param borderType 表格邊框類型* @param borderWidth 表格邊框粗細 (磅)* @param borderColor 表格邊框顏色 (十六進制字符串)* @param defaultRowHeight 默認行高 (twips)* @param customRowHeights 自定義行高 Map<行索引, 高度>* @param customColumnWidths 自定義列寬 Map<列索引, 寬度>* @param defaultColumnWidth 默認列寬 (twips),如果為 null 則自動計算* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator addTable(List<List<String>> data,int headerStartRowIndex,int headerEndRowIndex,TableTextStyle headerStyle,TableTextStyle cellStyle,List<int[]> mergeRegions,XWPFTable.XWPFBorderType borderType,int borderWidth,String borderColor,int defaultRowHeight,Map<Integer, Integer> customRowHeights,Map<Integer, Integer> customColumnWidths,Integer defaultColumnWidth) {return addTableInternal(data, headerStartRowIndex, headerEndRowIndex, headerStyle, cellStyle, mergeRegions, borderType, borderWidth, borderColor, defaultRowHeight, customRowHeights, customColumnWidths, defaultColumnWidth);}/*** 合并單元格 (水平方向)。* @param table 表格對象* @param row 合并的行索引* @param fromCol 起始列索引* @param toCol 結束列索引*/public void mergeCellsHorizontally(XWPFTable table, int row, int fromCol, int toCol) {XWPFTableCell cell = table.getRow(row).getCell(fromCol);// Sets the first merged cell to restart mergeCTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();if (tcPr.isSetHMerge()) {tcPr.unsetHMerge();}tcPr.addNewHMerge().setVal(STMerge.RESTART);// Clear content of merged cellsfor (int colIndex = fromCol + 1; colIndex <= toCol; colIndex++) {XWPFTableCell nextCell = table.getRow(row).getCell(colIndex);if (nextCell == null) {nextCell = table.getRow(row).addNewTableCell(); // Create new cell if null}clearCell(nextCell);tcPr = nextCell.getCTTc().isSetTcPr() ? nextCell.getCTTc().getTcPr() : nextCell.getCTTc().addNewTcPr();if (tcPr.isSetHMerge()) {tcPr.unsetHMerge();}tcPr.addNewHMerge().setVal(STMerge.CONTINUE);}}/*** 合并單元格 (垂直方向)。* @param table 表格對象* @param col 合并的列索引* @param fromRow 起始行索引* @param toRow 結束行索引*/public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {// Start merging from the first rowXWPFTableCell cell = table.getRow(fromRow).getCell(col);// Sets the first merged cell to restart mergeCTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();if (tcPr.isSetVMerge()) {tcPr.unsetVMerge();}tcPr.addNewVMerge().setVal(STMerge.RESTART);// Merge remaining cellsfor (int i = fromRow + 1; i <= toRow; i++) {XWPFTableCell cellToMerge = table.getRow(i).getCell(col);if (cellToMerge == null) {cellToMerge = table.getRow(i).addNewTableCell(); // Create new cell if null}clearCell(cellToMerge);tcPr = cellToMerge.getCTTc().isSetTcPr() ? cellToMerge.getCTTc().getTcPr() : cellToMerge.getCTTc().addNewTcPr();if (tcPr.isSetVMerge()) {tcPr.unsetVMerge();}tcPr.addNewVMerge().setVal(STMerge.CONTINUE);}}private void clearCell(XWPFTableCell cell) {for (int i = cell.getParagraphs().size(); i > 0; i--) {cell.removeParagraph(0);}cell.addParagraph();}/*** 保存Word文檔。* @param filePath 保存路徑* @throws IOException 如果保存失敗*/public void save(String filePath) throws IOException {try (FileOutputStream out = new FileOutputStream(filePath)) {document.write(out);}}// --- 枚舉定義 ---public enum PaperSize {A4, A3}public enum PageOrientation {PORTRAIT, LANDSCAPE}public enum IndentUnit {CM, POINTS, TWIPS}public enum TitleLevel {MAIN_TITLE, SUB_TITLE_1, SUB_TITLE_2}public enum ChineseFontSize {// 中文字號到磅值的映射CHU("初號", 42),XIAOCHU("小初", 36),YI("一號", 26),XIAOYI("小一", 24),ER("二號", 22),XIAOER("小二", 18),SAN("三號", 16),XIAOSAN("小三", 15),SI("四號", 14),XIAOSI("小四", 12),WU("五號", 10.5),XIAOWU("小五", 9),LIU("六號", 7.5),XIAOLIU("小六", 6.5),QI("七號", 5.5),BA("八號", 5);private final String name;private final double points;ChineseFontSize(String name, double points) {this.name = name;this.points = points;}public int getPoints() {return (int) points;}public static int getPoints(String chineseSize) {for (ChineseFontSize size : values()) {if (size.name.equals(chineseSize)) {return (int) size.points;}}return 12; // 默認返回小四號}}/*** 設置當前文本的字號(支持中文字號)。* @param chineseSize 中文字號(如"三號"、"四號"等)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setFontSize(String chineseSize) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setFontSize(ChineseFontSize.getPoints(chineseSize));}}return this;}/*** 設置當前文本的字號(支持中文字號枚舉)。* @param chineseSize 中文字號枚舉* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setFontSize(ChineseFontSize chineseSize) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setFontSize(chineseSize.getPoints());}}return this;}/*** 設置當前文本的字符間距。* @param value 間距值* @param unit 間距單位* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setCharacterSpacing(double value, IndentUnit unit) {if (currentRuns != null) {long twips = 0;switch (unit) {case CM:twips = Math.round(value * 567); // 1cm = 567 twipsbreak;case POINTS:twips = Math.round(value * 20); // 1pt = 20 twipsbreak;case TWIPS:twips = Math.round(value); // 直接使用twips值break;}for (XWPFRun run : currentRuns) {run.setCharacterSpacing((int) twips);}}return this;}/*** 應用公文模式字體樣式:將數字、字母的字體改為 Times New Roman,不影響中文字體。* 此方法會遍歷整個文檔的每個段落和每個文本運行。* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator applyOfficialDocumentFontStyling() {for (XWPFParagraph paragraph : document.getParagraphs()) {for (XWPFRun run : paragraph.getRuns()) {CTRPr rpr = run.getCTR().isSetRPr() ? run.getCTR().getRPr() : run.getCTR().addNewRPr();CTFonts fonts = rpr.isSetRFonts() ? rpr.getRFonts() : rpr.addNewRFonts();// 設置 ASCII 和 High ANSI 字符的字體為 Times New Romanfonts.setAscii("Times New Roman");fonts.setHAnsi("Times New Roman");// 不設置 East Asia (中文字符) 的字體,使其保持默認或由Word自動處理}}return this;}/*** 添加兩端對齊的文本(同一行內,一個文本靠左,一個文本靠右)。* @param leftText 左側文本* @param rightText 右側文本* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator addTwoEndsText(String leftText, String rightText) {XWPFParagraph paragraph = document.createParagraph();paragraph.setAlignment(ParagraphAlignment.BOTH); // 兩端對齊// 創建制表符CTP ctp = paragraph.getCTP();CTPPr ctppr = ctp.addNewPPr();CTTabs tabs = ctppr.addNewTabs();CTTabStop tabStop = tabs.addNewTab();tabStop.setVal(STTabJc.RIGHT); // 右對齊制表符// 根據頁面寬度和邊距動態計算制表符位置long contentWidthTwips = pageWidthTwips - leftMarginTwips - rightMarginTwips;tabStop.setPos(BigInteger.valueOf(contentWidthTwips));// 添加左側文本XWPFRun leftRun = paragraph.createRun();if (leftText == null || leftText.isEmpty()) {leftRun.setText("\u00A0");} else {leftRun.setText(leftText);}// 添加制表符leftRun.addTab();// 添加右側文本XWPFRun rightRun = paragraph.createRun();if (rightText == null || rightText.isEmpty()) {rightRun.setText("\u00A0");} else {rightRun.setText(rightText);}// 保存段落引用和運行對象數組this.currentParagraph = paragraph;this.currentRuns = Arrays.asList(leftRun, rightRun);return this;}/*** 設置當前段落的行高。* @param value 行高值* @param unit 行高單位* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setLineHeight(int value, IndentUnit unit) {if (currentParagraph != null) {long twips = 0;switch (unit) {case CM:twips = Math.round(value * 567); // 1cm = 567 twipsbreak;case POINTS:twips = value * 20; // 1pt = 20 twipsbreak;case TWIPS:twips = value;break;}// 設置固定行高currentParagraph.setSpacingLineRule(LineSpacingRule.EXACT);currentParagraph.setSpacingBetween((int) twips);// 設置行高為固定值CTP ctp = currentParagraph.getCTP();CTPPr ctppr = ctp.addNewPPr();CTSpacing spacing = ctppr.addNewSpacing();spacing.setLine(BigInteger.valueOf(twips));spacing.setLineRule(STLineSpacingRule.EXACT);}return this;}/*** 設置當前文本的下劃線樣式和顏色。* @param pattern 下劃線樣式 (例如 UnderlinePatterns.SINGLE, UnderlinePatterns.THICK)* @param hexColor 顏色值的十六進制字符串 (例如 "FF0000" 為紅色)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setUnderline(UnderlinePatterns pattern, String hexColor) {if (currentRuns != null) {for (XWPFRun run : currentRuns) {run.setUnderline(pattern);run.setUnderlineColor(hexColor);}}return this;}/*** 在文檔中添加一條指定顏色和粗細的水平線。* @param hexColor 顏色值的十六進制字符串 (例如 "FF0000" 為紅色)* @param thicknessInPoints 線條粗細 (磅)* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator addHorizontalLine(String hexColor, int thicknessInPoints) {XWPFParagraph lineParagraph = document.createParagraph();// 確保段落屬性存在CTPPr ppr = lineParagraph.getCTP().isSetPPr() ? lineParagraph.getCTP().getPPr() : lineParagraph.getCTP().addNewPPr();// 獲取或創建段落的邊框屬性CTPBdr border = ppr.isSetPBdr() ? ppr.getPBdr() : ppr.addNewPBdr();// 添加底部邊框CTBorder bottomBorder = border.addNewBottom();// 設置邊框顏色bottomBorder.setColor(hexColor);// 設置邊框粗細(單位為八分之一磅)bottomBorder.setSz(BigInteger.valueOf(thicknessInPoints * 8));// 設置邊框類型為單實線bottomBorder.setVal(STBorder.SINGLE);// 為了確保線條可見,添加一個空的運行(run)lineParagraph.createRun();// 更新當前段落,以便后續操作可以繼續鏈式調用this.currentParagraph = lineParagraph;this.currentRuns = new ArrayList<>(); // 重置 currentRuns 為空,因為這條線沒有文本this.currentRuns.add(lineParagraph.createRun()); // 添加一個空 run,以防后續操作需要一個 runreturn this;}/*** 設置當前段落的縮進(基于字符數)。* @param charCount 要縮進的字符數* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setIndentationByChars(int charCount) {if (currentParagraph != null) {CTP ctp = currentParagraph.getCTP();CTPPr ctppr = ctp.addNewPPr();CTInd ctInd = ctppr.addNewInd();ctInd.setFirstLineChars(BigInteger.valueOf(charCount * 100));}return this;}/*** TableTextStyle 靜態嵌套類,用于封裝表格中某個區域的文本樣式。*/public static class TableTextStyle {private String fontFamily;private String fontSize; // 對應 ChineseFontSize 的名稱private ParagraphAlignment alignment;private String backgroundColor;private XWPFTableCell.XWPFVertAlign verticalAlignment;// 新增單元格邊距屬性(單位twips)private Integer cellMarginTop, cellMarginBottom, cellMarginLeft, cellMarginRight;public TableTextStyle(String fontFamily, String fontSize, ParagraphAlignment alignment) {this(fontFamily, fontSize, alignment, null, null);}public TableTextStyle(String fontFamily, String fontSize, ParagraphAlignment alignment, String backgroundColor) {this(fontFamily, fontSize, alignment, backgroundColor, null);}public TableTextStyle(String fontFamily, String fontSize, ParagraphAlignment alignment, String backgroundColor, XWPFTableCell.XWPFVertAlign verticalAlignment,Integer cellMarginLeft, Integer cellMarginRight) {this.fontFamily = fontFamily;this.fontSize = fontSize;this.alignment = alignment;this.backgroundColor = backgroundColor;this.verticalAlignment = verticalAlignment;this.cellMarginLeft = cellMarginLeft;this.cellMarginRight = cellMarginRight;}public TableTextStyle(String fontFamily, String fontSize, ParagraphAlignment alignment, String backgroundColor, XWPFTableCell.XWPFVertAlign verticalAlignment) {this.fontFamily = fontFamily;this.fontSize = fontSize;this.alignment = alignment;this.backgroundColor = backgroundColor;this.verticalAlignment = verticalAlignment;}public String getFontFamily() { return fontFamily; }public String getFontSize() { return fontSize; }public ParagraphAlignment getAlignment() { return alignment; }public String getBackgroundColor() { return backgroundColor; }public XWPFTableCell.XWPFVertAlign getVerticalAlignment() { return verticalAlignment; }public Integer getCellMarginTop() { return cellMarginTop; }public Integer getCellMarginBottom() { return cellMarginBottom; }public Integer getCellMarginLeft() { return cellMarginLeft; }public Integer getCellMarginRight() { return cellMarginRight; }public TableTextStyle setCellMarginTop(Integer v) { this.cellMarginTop = v; return this; }public TableTextStyle setCellMarginBottom(Integer v) { this.cellMarginBottom = v; return this; }public TableTextStyle setCellMarginLeft(Integer v) { this.cellMarginLeft = v; return this; }public TableTextStyle setCellMarginRight(Integer v) { this.cellMarginRight = v; return this; }}// 公共實現private WordGenerator addTableInternal(List<List<String>> data,int headerStartRowIndex,int headerEndRowIndex,TableTextStyle headerStyle,TableTextStyle cellStyle,List<int[]> mergeRegions,XWPFTable.XWPFBorderType borderType,int borderWidth,String borderColor,int defaultRowHeight,Map<Integer, Integer> customRowHeights,Map<Integer, Integer> customColumnWidths,Integer defaultColumnWidth) {if (data == null || data.isEmpty()) {return this;}XWPFTable table = document.createTable(data.size(), data.get(0).size());CTTblPr tblPr = table.getCTTbl().getTblPr();if (tblPr == null) {tblPr = table.getCTTbl().addNewTblPr();}tblPr.addNewTblW().setType(STTblWidth.DXA);long contentWidthTwips = pageWidthTwips - leftMarginTwips - rightMarginTwips;tblPr.getTblW().setW(BigInteger.valueOf(contentWidthTwips));table.setLeftBorder(borderType, borderWidth, 0, borderColor);table.setRightBorder(borderType, borderWidth, 0, borderColor);table.setTopBorder(borderType, borderWidth, 0, borderColor);table.setBottomBorder(borderType, borderWidth, 0, borderColor);table.setInsideHBorder(borderType, borderWidth, 0, borderColor);table.setInsideVBorder(borderType, borderWidth, 0, borderColor);for (int i = 0; i < data.size(); i++) {XWPFTableRow row = table.getRow(i);int rowHeight = customRowHeights != null && customRowHeights.containsKey(i)? customRowHeights.get(i): defaultRowHeight;row.setHeight(rowHeight);}int totalWidth = (int)contentWidthTwips;int totalCustomWidth = 0;if (customColumnWidths != null) {totalCustomWidth = customColumnWidths.values().stream().mapToInt(Integer::intValue).sum();}int defaultColumnCount = data.get(0).size() - (customColumnWidths != null ? customColumnWidths.size() : 0);int remainingWidth = totalWidth - totalCustomWidth;int actualDefaultWidth = defaultColumnWidth != null ? defaultColumnWidth :(defaultColumnCount > 0 ? remainingWidth / defaultColumnCount : 0);for (int rowIdx = 0; rowIdx < data.size(); rowIdx++) {XWPFTableRow row = table.getRow(rowIdx);for (int colIdx = 0; colIdx < data.get(0).size(); colIdx++) {XWPFTableCell cell = row.getCell(colIdx);if (cell != null) {int columnWidth = customColumnWidths != null && customColumnWidths.containsKey(colIdx)? customColumnWidths.get(colIdx): actualDefaultWidth;cell.setWidth(String.valueOf(columnWidth));}}}for (int i = 0; i < data.size(); i++) {List<String> rowData = data.get(i);XWPFTableRow row = table.getRow(i);while (row.getTableCells().size() < rowData.size()) {row.addNewTableCell();}for (int j = 0; j < rowData.size(); j++) {XWPFTableCell cell = row.getCell(j);if (cell == null) {cell = row.addNewTableCell();}clearCell(cell);XWPFParagraph paragraph = cell.getParagraphs().isEmpty() ? cell.addParagraph() : cell.getParagraphs().get(0);XWPFRun run = paragraph.getRuns().isEmpty() ? paragraph.createRun() : paragraph.getRuns().get(0);run.setText(rowData.get(j));TableTextStyle styleToApply;if (i >= headerStartRowIndex && i <= headerEndRowIndex) {styleToApply = headerStyle;} else {styleToApply = cellStyle;}if (styleToApply != null) {paragraph.setAlignment(styleToApply.getAlignment());run.setFontFamily(styleToApply.getFontFamily());run.setFontSize(ChineseFontSize.getPoints(styleToApply.getFontSize()));if (styleToApply.getVerticalAlignment() != null) {cell.setVerticalAlignment(styleToApply.getVerticalAlignment());}if (styleToApply.getBackgroundColor() != null) {CTTc cttc = cell.getCTTc();CTTcPr tcPr = cttc.isSetTcPr() ? cttc.getTcPr() : cttc.addNewTcPr();CTShd shd = tcPr.isSetShd() ? tcPr.getShd() : tcPr.addNewShd();shd.setVal(STShd.CLEAR);shd.setFill(styleToApply.getBackgroundColor());}// 設置單元格邊距CTTc cttc = cell.getCTTc();CTTcPr tcPr = cttc.isSetTcPr() ? cttc.getTcPr() : cttc.addNewTcPr();CTTcMar mar = tcPr.isSetTcMar() ? tcPr.getTcMar() : tcPr.addNewTcMar();if (styleToApply.getCellMarginTop() != null) mar.addNewTop().setW(BigInteger.valueOf(styleToApply.getCellMarginTop()));if (styleToApply.getCellMarginBottom() != null) mar.addNewBottom().setW(BigInteger.valueOf(styleToApply.getCellMarginBottom()));if (styleToApply.getCellMarginLeft() != null) mar.addNewLeft().setW(BigInteger.valueOf(styleToApply.getCellMarginLeft()));if (styleToApply.getCellMarginRight() != null) mar.addNewRight().setW(BigInteger.valueOf(styleToApply.getCellMarginRight()));}}}if (mergeRegions != null) {for (int[] mergeInfo : mergeRegions) {int rowIndex = mergeInfo[0];int colIndex = mergeInfo[1];int rowSpan = mergeInfo[2];int colSpan = mergeInfo[3];if (rowSpan > 1) {mergeCellsVertically(table, colIndex, rowIndex, rowIndex + rowSpan - 1);}if (colSpan > 1) {mergeCellsHorizontally(table, rowIndex, colIndex, colIndex + colSpan - 1);}}}return this;}/*** 頁眉頁腳配置類。*/public static class HeaderFooterConfig {private String headerText;private String footerText;private String fontFamily = "宋體";private int fontSize = 14; // 四號private ParagraphAlignment alignment = ParagraphAlignment.CENTER;// 可擴展更多屬性public HeaderFooterConfig setHeaderText(String headerText) { this.headerText = headerText; return this; }public HeaderFooterConfig setFooterText(String footerText) { this.footerText = footerText; return this; }public HeaderFooterConfig setFontFamily(String fontFamily) { this.fontFamily = fontFamily; return this; }public HeaderFooterConfig setFontSize(int fontSize) { this.fontSize = fontSize; return this; }public HeaderFooterConfig setAlignment(ParagraphAlignment alignment) { this.alignment = alignment; return this; }public String getHeaderText() { return headerText; }public String getFooterText() { return footerText; }public String getFontFamily() { return fontFamily; }public int getFontSize() { return fontSize; }public ParagraphAlignment getAlignment() { return alignment; }}/*** 設置頁眉頁腳,支持頁碼格式如 - {PAGE} -,宋體四號,居中。* @param config 頁眉頁腳配置* @return WordGenerator 實例,支持鏈式調用*/public WordGenerator setHeaderFooter(HeaderFooterConfig config) {CTSectPr sectPr = document.getDocument().getBody().isSetSectPr()? document.getDocument().getBody().getSectPr(): document.getDocument().getBody().addNewSectPr();XWPFHeaderFooterPolicy policy = new XWPFHeaderFooterPolicy(document, sectPr);// 頁眉if (config.getHeaderText() != null && !config.getHeaderText().isEmpty()) {XWPFHeader header = policy.createHeader(XWPFHeaderFooterPolicy.DEFAULT);XWPFParagraph para = header.createParagraph();para.setAlignment(config.getAlignment());XWPFRun run = para.createRun();run.setFontFamily(config.getFontFamily());run.setFontSize(config.getFontSize());run.setText(config.getHeaderText());}// 頁腳if (config.getFooterText() != null && !config.getFooterText().isEmpty()) {XWPFFooter footer = policy.createFooter(XWPFHeaderFooterPolicy.DEFAULT);XWPFParagraph para = footer.createParagraph();para.setAlignment(config.getAlignment());XWPFRun run = para.createRun();run.setFontFamily(config.getFontFamily());run.setFontSize(config.getFontSize());String text = config.getFooterText();int pageIdx = text.indexOf("{PAGE}");if (pageIdx >= 0) {// 前綴if (pageIdx > 0) run.setText(text.substring(0, pageIdx));// 頁碼域run.getCTR().addNewFldChar().setFldCharType(STFldCharType.BEGIN);run = para.createRun();run.setFontFamily(config.getFontFamily());run.setFontSize(config.getFontSize());run.getCTR().addNewInstrText().setStringValue(" PAGE ");run = para.createRun();run.setFontFamily(config.getFontFamily());run.setFontSize(config.getFontSize());run.getCTR().addNewFldChar().setFldCharType(STFldCharType.END);// 后綴if (pageIdx + 6 < text.length()) {run = para.createRun();run.setFontFamily(config.getFontFamily());run.setFontSize(config.getFontSize());run.setText(text.substring(pageIdx + 6));}} else {run.setText(text);}}return this;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/85734.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/85734.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/85734.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

時間序列預測、分類 | 圖神經網絡開源代碼分享(上)

本期結合《時間序列圖神經網絡(GNN4TS)綜述》&#xff0c;整理了關于圖神經網絡在時間序列預測、分類等任務上的開源代碼和學習資料以供大家學習、研究。 參考論文&#xff1a;《A Survey on Graph Neural Networks for Time Series: Forecasting, Classification, Imputation,…

Vue 添加水印(防篡改: 刪除水印元素節點、修改水印元素的樣式)

MutationObserver_API: 觀察某一個元素的變化// index.vue<template><div class="container"><Watermark text="版權所有" style="background: #28c848"><!-- 可給圖片、視頻、div...添加水印 --><div class=&quo…

如何處理開發不認可測試發現的問題

解決方案 第一步&#xff1a;收集確鑿證據 確保有完整的復現結果準備詳細的記錄材料&#xff1a; 截屏錄屏操作步驟記錄 帶著這些證據與開發人員進行溝通 第二步&#xff1a;多角度驗證 如果與開發人員溝通無果&#xff1a; 競品分析&#xff1a;查看市場上同類產品如何…

linux生產環境下根據關鍵字搜索指定日志文件命令

grep -C 100 "error" server.log 用于在 server.log 文件中查找包含 “error” 的行&#xff0c;并同時顯示該行前后100行的上下文。這是排查日志問題的常用技巧&#xff0c;解釋一下&#xff1a; 命令參數詳解 grep&#xff1a;文本搜索工具&#xff0c;用于在文件…

用vue和echarts怎么寫一個甘特圖,并且是分段式瀑布流

vue echarts 甘特圖功能 index.vue <template><div ref"echart" id"echart" class"echart"></div> </template><script setup>import { nextTick, onMounted, ref } from "vue";import * as echarts f…

Pandas使用教程:從入門到實戰的數據分析利器

一、Pandas基礎入門 1.1 什么是Pandas Pandas是Python生態中核心的數據分析庫&#xff0c;提供高效的數據結構&#xff08;Series/DataFrame&#xff09;和數據分析工具。其名稱源于"Panel Data"&#xff08;面板數據&#xff09;和"Python Data Analysis"…

NuttX Socket 源碼學習

概述 NuttX 的 socket 實現是一個精心設計的網絡編程接口&#xff0c;提供了標準的 BSD socket API。該實現采用分層架構設計&#xff0c;支持多種網絡協議族&#xff08;如 TCP/IP、UDP、Unix域套接字等&#xff09;&#xff0c;具有良好的可擴展性和模塊化特性。 整體架構設…

基于YOLO的語義分割實戰(以豬的分割為例)

數據集準備 數據集配置文件 其實語義分割和目標檢測類似&#xff0c;包括數據集制備、存放格式基本一致像這樣放好即可。 然后需要編寫一個data.yaml文件&#xff0c;對應的是數據的配置文件。 train: C:\圖標\dan\語義分割pig\dataset\train\images #絕對路徑即可 val: C:\…

釘釘智能會議室集成指紋密碼鎖,臨時開門密碼自動下發

在當今快節奏的工作環境中&#xff0c;會議室的高效管理和使用成為了企業提升工作效率的關鍵一環。湖南某知名企業近期成功升級了原有使用的釘釘智能會議室系統&#xff0c;并配套使用了啟辰智慧聯網指紋密碼鎖&#xff0c;實現了會議室管理的智能化升級&#xff0c;提升了會議…

C++講解—類(1)

類 在 C 中&#xff0c;類是一個關鍵概念&#xff0c;憑借其封裝和繼承的特性&#xff0c;能夠助力程序員之間實現高效的分工協作&#xff0c;共同完成復雜的大型項目。我們先從最簡單的概念入手&#xff0c;再進行更深層次的了解和應用。 1. 類的定義 類是用戶自定義的一種…

什么是Hadoop Yarn

Hadoop YARN&#xff1a;分布式集群資源管理系統詳解 1. 什么是YARN&#xff1f; YARN&#xff08;Yet Another Resource Negotiator&#xff09;是 Apache Hadoop 生態系統中的資源管理和作業調度系統&#xff0c;最初在 Hadoop 2.0 中引入&#xff0c;取代了 Hadoop 1.0 的…

項目開發中途遇到困難的解決方案

1. 正視困難&#xff0c;避免逃避 開發遇阻時&#xff0c;退縮會帶來雙重損失&#xff1a;既成為"失敗者逃兵"&#xff0c;又損害職業信心1。 行動建議&#xff1a; 立即向團隊透明化問題&#xff08;如進度延遲、技術瓶頸&#xff09;&#xff0c;避免問題滾雪球…

Blender硬表面建模篇收集學習建模過程中的Demo

c 齒輪 創建一個圓柱體&#xff0c;選擇側面的所有&#xff0c;然后進行隔斷選擇&#xff0c;兩次擠出面&#xff0c;一次縮放面&#xff0c;通過圓柱面三次插入面縮放擠出得到齒輪中心&#xff0c;選中齒輪的鋸齒中間&#xff0c;然后進行相同周長選擇行選擇齒與齒中間的面&…

Chromium 136 編譯指南 macOS篇:獲取源代碼(四)

1. 引言 在現代軟件開發的宏大版圖中&#xff0c;源代碼的獲取往往標志著從理論探索向實踐應用的關鍵轉折。對于Chromium 136這樣一個擁有超過2500萬行代碼、涉及數百個第三方庫的超大規模開源項目而言&#xff0c;源代碼的獲取不僅僅是簡單的文件下載&#xff0c;更是一個涉及…

OpenCV C++ 邊緣檢測與圖像分割

一、邊緣檢測 在數字圖像處理領域&#xff0c;邊緣檢測是一項至關重要的基礎技術。它如同為圖像賦予 “骨架”&#xff0c;幫助計算機快速識別圖像中的物體輪廓、形狀與結構&#xff0c;廣泛應用于目標識別、圖像分割、圖像配準等多個領域。 1.1 概念 邊緣檢測的核心目標是找…

線段樹(2025年6月14日)

原文 https://www.beiweidoge.top/132.html P1&#xff1a;求最大值1 題目描述 題目描述 小明給了你n個數字&#xff0c;你需要依次輸出&#xff1a; 1到n的最大值&#xff0c;1到n/2的最大值&#xff0c;n/21到n的最大值&#xff0c;1到n/4的最大值&#xff0c;n/41到n/2…

滲透實戰PortSwigger Labs AngularJS DOM XSS利用詳解

本Lab學習到關于AngularJS的 xss 漏洞利用 直接輸入回顯頁面&#xff0c;但是把<>進了 html 編碼了 當我們輸入{{11}}&#xff0c;沒有當作字符處理&#xff0c;而是執行了 {{}} 是多種前端框架&#xff08;如 Vue、Angular、Django 模板等&#xff09;中常見的模板插值語…

Ubuntu 多網卡安全路由配置(SSH 不斷線版)

Ubuntu 多網卡路由配置筆記&#xff08;內網 外網同時通&#xff09;&#xff08;SSH斷線版&#xff09;文章瀏覽閱讀386次&#xff0c;點贊4次&#xff0c;收藏5次。本文介紹了Ubuntu 如何配置雙網卡設備的路由規則&#xff0c;使默認流量走外網&#xff08;192.168.2.0/24&a…

基于昇騰NPU部署llamafactory單機多卡微調Qwen3教程

1. 進入華為云 華為云首頁 點擊右上角控制臺 2.進入ModelArts 點擊搜索框->搜索“ModelArts”->選擇AI開發平臺ModelArts->進入平臺后點擊開發者空間 3.創建Notebook 3.1在開發者空間中點擊Notebook->在西南貴陽一下點擊創建Notebook 3.2進入創建Notebook頁…

關于XES文件格式諸多問題

1. 格式類型是什么&#xff1f; case:concept:name (案例ID) - 必須是字符串類型concept:name (活動名稱) - 字符串類型time:timestamp - 必須是datetime類型 2. 如何修改&#xff1f; data[case:concept:name] data[case:concept:name].astype(str)data[concept:name] data…