需求:做個公文系統,需要將正文文檔在某個節點點擊套紅按鈕,實現文檔套紅
試了很多方法,大多數網上能查到但是實際代碼不能找到關鍵方法,可能是跟包的版本有關系,下面記錄能用的這個。
一:添加依賴
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.2</version></dependency><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.9.1</version><exclusions><exclusion><artifactId>slf4j-log4j12</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency><dependency><groupId>e-iceblue</groupId><artifactId>spire.doc</artifactId><version>12.6.2</version></dependency>
二、文檔
模板:{{date}}是可以獲取到的變量
文檔:
三、代碼
public static void word2RedDocument(String content, Map<String, Object> data, String destDocx) throws Exception {//模板文件地址String model = "D:\\套紅模板.docx";//模板文件 參數填寫XWPFTemplate template = XWPFTemplate.compile(model).render(data);//獲取模板文件 公文NiceXWPFDocument main = template.getXWPFDocument();//正文文檔NiceXWPFDocument sub = new NiceXWPFDocument(new FileInputStream(content));List<XWPFParagraph> paragraphs = main.getParagraphs();NiceXWPFDocument newDoc = new NiceXWPFDocument();for (XWPFParagraph p:paragraphs) {if( null != p && p.getText().contains("正文")){//這里是要去掉正文兩個字,自己debug看了索引,為了保險起見應該遍歷run判斷p.removeRun(0);XWPFRun run = p.createRun();// 合并兩個文檔到指定位置newDoc = main.merge(Arrays.asList(sub),run);break;}}// 設置頁碼--開始--沒有需求可以刪掉XWPFFooter footer = newDoc.createFooter(HeaderFooterType.DEFAULT);//創建一個新的XWPFFooter對象XWPFParagraph paragraph = footer.createParagraph();//創建新的XWPFParagraph對象paragraph.setAlignment(ParagraphAlignment.CENTER);//設置樣式居中//設置段落對象XWPFRun runPre = paragraph.createRun();//新的段落對象runPre.setText("- ");XWPFRun run = paragraph.createRun();//新的段落對象CTFldChar fldChar = run.getCTR().addNewFldChar();//新的CTFldChar對象fldChar.setFldCharType(STFldCharType.Enum.forString("begin"));CTText ctText = run.getCTR().addNewInstrText();ctText.setStringValue("PAGE \\* MERGEFORMAT");ctText.setSpace(SpaceAttribute.Space.Enum.forString("preserve"));fldChar = run.getCTR().addNewFldChar();fldChar.setFldCharType(STFldCharType.Enum.forString("end"));//設置段落對象XWPFRun runSuf = paragraph.createRun();//新的段落對象runSuf.setText(" -");// 將頁腳添加到所有的頁面XWPFHeaderFooterPolicy headerFooterPolicy = new XWPFHeaderFooterPolicy(newDoc);headerFooterPolicy.createFooter(STHdrFtr.DEFAULT, new XWPFParagraph[]{paragraph});
// 設置頁碼--結束--沒有需求可以刪掉//可以是生成新文檔,也可以生成到原來的正文content = "D:\\新文檔.docx";// 生成新文檔FileOutputStream out = new FileOutputStream(content);newDoc.write(out);newDoc.close();out.close();//doc轉pdfdoc2Pdf(content);
// ByteArrayOutputStream os = new ByteArrayOutputStream();
// newDoc.write(os);
// InputStream is = new ByteArrayInputStream(os.toByteArray());
// os.close();}
調用方法測試
public static void main(String[] args) throws Exception {String sourceFile = "D:\\模板.docx";String targetFile = "D:\\測試.docx";Map<String, Object> data = new HashMap<>(2);List<String> list = Arrays.asList("技術", "測試", "評選結果", "測試", "評選結果", "測試", "評選結果");StringBuilder builder = new StringBuilder();for (int i =0;i<list.size();i++) {builder.append(list.get(i));if (i != list.size() -1){builder.append(" ");}}data.put("num", "931");data.put("year", "2024");data.put("name", "銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心銷售中心");data.put("keyword",builder );
// data.put("keyword", Arrays.asList("技術","測試","評選結果","測試","評選結果","測試","評選結果"));
// data.put("keyword", Arrays.asList("技術","測試"));data.put("user", "李斯");
// data.put("company", "股份有限公司技術股份有限公司");data.put("date", getChineseDate());word2RedDocument(targetFile,data,"新文檔.docx");}
轉換時間的方法,寫稀碎,拋磚引玉吧
public static String getChineseDate() {Calendar cal = Calendar.getInstance();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日", Locale.CHINA);String date = sdf.format(cal.getTime());// 將數字轉換為漢字String[] chineseNumbers = {"〇", "一", "二", "三", "四", "五", "六", "七", "八", "九"};StringBuilder chineseDate = new StringBuilder();for (int i = 0; i < date.length(); i++) {if(i==4 || i==7 || i==10){chineseDate.append(date.charAt(i));} else {int number = Character.getNumericValue(date.charAt(i));if(i==5 || i==8 ){if(number==0){continue;}else if(number==1){chineseDate.append("十");continue;}else {chineseDate.append(chineseNumbers[number]);chineseDate.append("十");continue;}}if(i==6 || i==9 ){if(number==0){continue;}}chineseDate.append(chineseNumbers[number]);}}return chineseDate.toString();}
執行方法:
合并后文檔如下:
XWPFDocument類相關:
四、換行:addBreak/addCarriageReturn
場景:如下圖。想要一個倒三角樣式的標題,且會議紀要四個字不能拆開
分析需求:
1.模板設置居中
2.會議紀要跟前邊內容分成兩個變量字段
3.行數多于1行就把類型字段換行
這里有個問題,沒有找到能判斷出行數的方法,嘗試了很多,基本上都是根據"\n"判斷,但是打斷點會發現Paragraph.getText()的結果沒有換行符,這里先采用了一個不算辦法的辦法,根據長度判斷,假設這樣的標題格式一行10個左右。
現在判斷條件有了,那么實現3就是添加換行
XWPFParagraph xwpfParagraph = newDoc.getParagraphs().get(0);//判斷長度大于10(分行,至少兩行)添加換行,解決無法判斷幾行且類型內容不切分的問題if (xwpfParagraph.getText().length()>10){XWPFRun existingRun = xwpfParagraph.getRuns().get(0);
// XWPFParagraph paragraph1 = newDoc.insertNewParagraph(existingRun.getCTR().newCursor());--這個是null很不理解,這段純屬沒用//------ 方法一 --------existingRun.addBreak();//------ 方法二 --------existingRun .addCarriageReturn();}
模板:
效果:
五:設置頁面間距
還是為了四的效果,leader說想想辦法要一個在套紅時能設置一行幾個字的功能還不能影響模板設置的字號
查來查去,勉強能通過設置頁面間距實現(如果這樣,為啥不打印或者轉pdf之類的時候自己頁面設置呢還直觀還能隨心所欲的調整)
添加依賴:
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-full</artifactId><version>5.0.0</version></dependency>
這個真的給我找哭了,因為CTPageMar報找不到
代碼:
CTSectPr sectPr = newDoc.getDocument().getBody().addNewSectPr();CTPageMar pageMar = sectPr.addNewPgMar();pageMar.setLeft(BigInteger.valueOf(127L));pageMar.setTop(BigInteger.valueOf(127L));pageMar.setRight(BigInteger.valueOf(127L));pageMar.setBottom(BigInteger.valueOf(127L));
通過設置不同值得頁面效果:(pdf能看出來,word受wps工具設置有影響只會根據你當前wps的設置值展示–看不出來區別)
127的:
pageMar.setLeft(BigInteger.valueOf(318L));pageMar.setTop(BigInteger.valueOf(254L));pageMar.setRight(BigInteger.valueOf(318L));pageMar.setBottom(BigInteger.valueOf(254L));
不設置的:
六、鏈接
代碼:
XWPFParagraphWrapper wrapper = new XWPFParagraphWrapper(xwpfParagraph);XWPFRun hyperRun = wrapper.insertNewHyperLinkRun(0, "https://www.baidu.com/");hyperRun.setText("百度");
效果: