需求:做个公文系统,需要将正文文档在某个节点点击套红按钮,实现文档套红
试了很多方法,大多数网上能查到但是实际代码不能找到关键方法,可能是跟包的版本有关系,下面记录能用的这个。
一:添加依赖
<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转pdf
doc2Pdf(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("百度");
效果: