Commit f03c4259 authored by wangqiang's avatar wangqiang

添加wordUtil 工具类

parent e0aa2c6f
...@@ -63,6 +63,12 @@ ...@@ -63,6 +63,12 @@
<version>2.0.24</version> <version>2.0.24</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.luhuiguo</groupId>
<artifactId>aspose-words</artifactId>
<version>23.1</version>
</dependency>
</dependencies> </dependencies>
......
package com.yonde.dcs.plan.common.utils;
import com.aspose.words.*;
import com.aspose.words.ParagraphAlignment;
import com.google.common.collect.Lists;
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.util.Strings;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.wp.usermodel.HeaderFooterType;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.xwpf.usermodel.Document;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.w3c.dom.Node;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.stream.Stream;
/**
* @author 954L
* @create 2021/12/9 10:23
*/
public class WordUtil {
/**
* 水印字体字体
*/
private static final String fontName = "PingFang SC";
/**
* 一个字平均长度,单位pt,用于:计算文本占用的长度(文本总个数*单字长度)
*/
private static final int widthPerWord = 10;
/**
* 替换word内容 替换文字(不替换图片) 只能替换docx的文档
*
* @param src 原word文件地址
* @param dest 转换后word文件地址
* @param params 替换参数map
* @throws Exception
*/
@SneakyThrows
public static void replace(String src, String dest, Map<String, String> params) {
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(src));
// 页眉内容替换
replaceHeader(document, params);
// replaceHeader1(document, params);
// 页脚替换
replaceFooter(document, params);
// 基础内容替换
replaceParagraph(document, params);
// 表格内容替换
replaceTable(Lists.newArrayList(document.getTablesIterator()), params);
try (FileOutputStream outStream = new FileOutputStream(dest)) {
document.write(outStream);
}
}
/**
* 校验替换key值是否存在
*
* @param is
* @param signkeys
*/
@SneakyThrows
public static void checkSignKey(InputStream is, Map<String, String> signkeys) {
com.aspose.words.Document document = new com.aspose.words.Document(is);
BookmarkCollection bookmarks = document.getRange().getBookmarks();
checkTableKeys(bookmarks, signkeys);
}
/**
* 校验文档表格替换Key是否存在
*
* @param bookmarks
* @param params
*/
private static void checkTableKeys(BookmarkCollection bookmarks, Map<String, String> params) {
// 检查集合中的元素是否都在 Map 的键集合中
for (Bookmark bookmark : bookmarks) {
String name = bookmark.getName();
if (params.containsKey(name)) {
params.remove(name);
}
}
}
/**
* 处理段落
*
* @param paragraphs
* @param params
*/
private static void dealParagraphs(List<XWPFParagraph> paragraphs, Map<String, String> params) {
if (CollectionUtils.isNotEmpty(paragraphs)) {
for (XWPFParagraph paragraph : paragraphs) {
for (String key : params.keySet()) {
replaceInParagraph(paragraph, key, params.get(key));
//TODO:测试替换
/*if (paragraph.getText().contains(key)) {
replaceInParagraph(paragraph, key, params.get(key));
}*/
}
}
}
}
/**
* 替换脚
*
* @param document
* @param params
*/
private static void replaceFooter(XWPFDocument document, Map<String, String> params) {
List<XWPFFooter> footerList = document.getFooterList();
for (XWPFFooter xwpfFooter : footerList) {
List<XWPFParagraph> paragraphs = xwpfFooter.getParagraphs();
dealParagraphs(paragraphs, params);
//TODO 处理页脚中的表格内容
List<XWPFTable> tables = xwpfFooter.getTables();
replaceTable(tables, params);
}
}
private static void replaceHeader1(XWPFDocument document, Map<String, String> params) {
List<XWPFHeader> headerList = document.getHeaderList();
for (XWPFHeader xwpfHeader : headerList) {
List<XWPFParagraph> paragraphs = xwpfHeader.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (int i = 0; i < runs.size(); i++) {
for (String s : params.keySet()) {
if (runs.get(i).getText(runs.get(i).getTextPosition()) != null && runs.get(i).getText(runs.get(i).getTextPosition()).contains(s)) {
String replace = runs.get(i).getText(runs.get(i).getTextPosition()).replace(s, params.get(s));
runs.get(i).setText(replace, 0);
}
}
}
}
}
}
/**
* 替换表头
*
* @param document
* @param params
*/
private static void replaceHeader(XWPFDocument document, Map<String, String> params) {
List<XWPFHeader> headerList = document.getHeaderList();
for (XWPFHeader xwpfHeader : headerList) {
List<XWPFParagraph> paragraphs = xwpfHeader.getParagraphs();
dealParagraphs(paragraphs, params);
//TODO 处理页脚中的表格内容
List<XWPFTable> tables = xwpfHeader.getTables();
replaceTable(tables, params);
}
}
/**
* 替换普通文本
*
* @param document
* @param params
*/
private static void replaceParagraph(XWPFDocument document, Map<String, String> params) {
List<XWPFParagraph> xwpfParagraphList = document.getParagraphs();
for (int i = 0, size = xwpfParagraphList.size(); i < size; i++) {
XWPFParagraph xwpfParagraph = xwpfParagraphList.get(i);
System.out.printf("----基础替换内容>"+xwpfParagraph.getText());
for (String key : params.keySet()) {
//TODO:测试替换
//if (xwpfParagraph.getText().contains(key))
replaceInParagraph(xwpfParagraph, key, params.get(key));
}
}
}
/**
* 替换表格文本
*
* @param tables
* @param params
*/
private static void replaceTable(List<XWPFTable> tables, Map<String, String> params) {
for (XWPFTable xwpfTable : tables) {
for (int i = 0, count = xwpfTable.getNumberOfRows(); i < count; i++) {
XWPFTableRow xwpfTableRow = xwpfTable.getRow(i);
List<XWPFTableCell> xwpfTableCellList = xwpfTableRow.getTableCells();
for (int j = 0, cellSize = xwpfTableCellList.size(); j < cellSize; j++) {
XWPFTableCell xwpfTableCell = xwpfTableCellList.get(j);
List<XWPFParagraph> paragraphList = xwpfTableCell.getParagraphs();
for (int k = 0, paragraphSize = paragraphList.size(); k < paragraphSize; k++) {
XWPFParagraph xwpfParagraph = paragraphList.get(k);
System.out.printf("----表格替换内容>"+xwpfParagraph.getText());
if (Strings.isEmpty(xwpfParagraph.getText())) continue;
for (String key : params.keySet()) {
if (xwpfParagraph.getText().contains(key))
replaceInParagraph(xwpfParagraph, key, params.get(key));
}
}
}
}
}
}
/**
* 段落内替换
*
* @param xwpfParagraph
* @param oldString
* @param newString
*/
private static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, String newString) {
List<XWPFRun> runs = xwpfParagraph.getRuns();
int runSize = runs.size();
StringBuilder textSb = new StringBuilder();
Map<Integer, String> textMap = new HashMap<>();
for (int j = 0; j < runSize; j++) {
XWPFRun xwpfRun = runs.get(j);
Integer textPosition = xwpfRun.getTextPosition();
String text = xwpfRun.getText(textPosition);
textSb.append(text);
textMap.put(j, text);
}
// 判断是否重合
if (!textSb.toString().contains(oldString)) return;
int startIndex = 0;
int mapSize = textMap.size();
int maxEndIndex = oldString.length();
Integer startPosition = null, endPosition = null;
String uuid = UUID.randomUUID().toString();
alwaysFor:
for (; ; ) {
if (startIndex > mapSize) break;
int endIndex = startIndex;
while (endIndex >= startIndex && maxEndIndex > endIndex - startIndex) {
StringBuilder strSb = new StringBuilder();
for (int i = startIndex; i <= endIndex; i++)
strSb.append(textMap.getOrDefault(i, uuid));
if (!strSb.toString().trim().equals(oldString)) ++endIndex;
else {
startPosition = startIndex;
endPosition = endIndex;
break alwaysFor;
}
}
++startIndex;
}
if (startPosition != null && endPosition != null) {
XWPFRun modelRun = runs.get(endPosition);
XWPFRun xwpfRun = xwpfParagraph.insertNewRun(endPosition + 1);
xwpfRun.setText(newString);
if (modelRun.getFontSize() != -1) {
xwpfRun.setFontSize(modelRun.getFontSize());
}
xwpfRun.setFontFamily(modelRun.getFontFamily());
//测试不删除变量
for (int i = endPosition; i >= startPosition; i--) {
xwpfParagraph.removeRun(i);
}
} else {
// 最小粒度无法匹配,此处采用下下策粗粒度替换文本
String text = xwpfParagraph.getText();
XWPFRun xwpfRun = xwpfParagraph.getRuns().get(0);
String fontFamily = xwpfRun.getFontFamily();
int fontSize = xwpfRun.getFontSize();
XWPFRun insertXwpfRun = xwpfParagraph.insertNewRun(runSize);
insertXwpfRun.setText(text.replace(oldString, newString));
insertXwpfRun.setFontFamily(fontFamily);
insertXwpfRun.setFontSize(fontSize);
//测试不删除变量
for (int i = runSize - 1; i >= 0; i--) {
xwpfParagraph.removeRun(i);
}
}
}
/**
* 根据书签名替换为图片(只支持WORD2007)
*
* @param in
* @param out
* @param bookMarkName
* @param imageUrl
*/
public static void replaceBookMarkByImage(InputStream in, OutputStream out, String bookMarkName, String imageUrl, InputStream imgIn) throws IOException, InvalidFormatException {
XWPFDocument document = new XWPFDocument(in);
List<XWPFParagraph> paragraphList = document.getParagraphs();
for (XWPFParagraph xwpfParagraph : paragraphList) {
CTP ctp = xwpfParagraph.getCTP();
for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
if (bookMarkName.equals(bookmark.getName())) {
XWPFRun run = xwpfParagraph.createRun();
Node firstNode = bookmark.getDomNode();
Node nextNode = firstNode.getNextSibling();
XWPFRun runimg = xwpfParagraph.createRun();
//FileInputStream imageStream = new FileInputStream(imageUrl);
byte[] bs = IOUtils.toByteArray(imgIn);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bs));
runimg.addPicture(new ByteArrayInputStream(bs), Document.PICTURE_TYPE_JPEG, "", Units.toEMU(image.getWidth()), Units.toEMU(image.getHeight()));
ctp.getDomNode().insertBefore(runimg.getCTR().getDomNode(), firstNode);
//imageStream.close();
imgIn.close();
}
}
}
document.write(out);
return;
}
public static void replaceBookTag(XWPFDocument document, Map<String, Object> bookTagMap) {
List<XWPFParagraph> paragraphList = document.getParagraphs();
for (XWPFParagraph xwpfParagraph : paragraphList) {
CTP ctp = xwpfParagraph.getCTP();
for (int dwI = 0; dwI < ctp.sizeOfBookmarkStartArray(); dwI++) {
CTBookmark bookmark = ctp.getBookmarkStartArray(dwI);
if (bookTagMap.containsKey(bookmark.getName())) {
XWPFRun run = xwpfParagraph.createRun();
run.setText(bookTagMap.get(bookmark.getName()).toString());
}
}
}
}
/**
* 将图片插入到word中
*
* @param inputDocPath:
* @param imagePath:
* @return void
*/
public static void imageToWord(String inputDocPath, String outputDocPath, String imagePath) {
try {
// 加载现有的 Word 文档
com.aspose.words.Document document = new com.aspose.words.Document(inputDocPath);
// 获取图片文件夹中的所有图片
File folder = new File(imagePath);
File[] files = folder.listFiles();
List<File> imageFiles = new ArrayList<File>();
for (File file : files) {
if (file.isFile() && file.getName().toLowerCase().endsWith(".jpg")) {
imageFiles.add(file);
}
}
// 插入图片并添加分页符
for (int i = 1; i <= imageFiles.size(); i++) {
if (i < imageFiles.size()) {
insertImage(document, imageFiles.get(i - 1).getAbsolutePath(), true);
document.getLastSection().getPageSetup().setSectionStart(SectionStart.NEW_PAGE);
} else {
insertImage(document, imageFiles.get(i - 1).getAbsolutePath(), false);
}
}
// 保存新的 Word 文档
document.save(outputDocPath);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 插入图片并分页
*
* @param document:
* @param imagePath:
* @param fenYe:
* @return void
*/
private static void insertImage(com.aspose.words.Document document, String imagePath, Boolean fenYe) throws Exception {
DocumentBuilder builder = new DocumentBuilder(document);
// 移动光标到文档末尾
builder.moveToDocumentEnd();
// 插入图片
builder.insertImage(imagePath);
if (fenYe) {
// 添加分页符
builder.insertBreak(com.aspose.words.BreakType.PAGE_BREAK);
}
}
private static String getOutputDocPath(String inputDocPath) {
int dotIndex = inputDocPath.lastIndexOf(".");
String fileName = inputDocPath.substring(0, dotIndex);
String extension = inputDocPath.substring(dotIndex);
long timeDifference = System.currentTimeMillis() - new File(inputDocPath).lastModified();
return fileName + "-" + timeDifference + extension;
}
/**
* 以艺术字方式加上水印(平铺)
*
* @param docx XWPFDocument对象
* @param customText 水印文字
*/
public static void makeFullWaterMarkByWordArt(XWPFDocument docx, String customText, String fontColor, String fontSize, String styleRotation) {
// 水印文字之间使用8个空格分隔
customText = customText + repeatString(" ", 16);
// 一行水印重复水印文字次数
customText = repeatString(customText, 10);
// 与顶部的间距
String styleTop = "0pt";
if (docx == null) {
return;
}
// 遍历文档,添加水印
for (int lineIndex = -10; lineIndex < 20; lineIndex++) {
styleTop = 200 * lineIndex + "pt";
waterMarkDocXDocument(docx, customText, styleTop, 1, fontColor, fontSize, styleRotation);
}
}
/**
* 将指定的字符串重复repeats次.
*
* @param pattern 字符串
* @param repeats 重复次数
* @return 生成的字符串
*/
private static String repeatString(String pattern, int repeats) {
StringBuilder buffer = new StringBuilder(pattern.length() * repeats);
Stream.generate(() -> pattern).limit(repeats).forEach(buffer::append);
return new String(buffer);
}
/**
* 为文档添加水印
* 实现参考了{@link XWPFHeaderFooterPolicy#(String, int)}
*
* @param doc 需要被处理的docx文档对象
* @param customText 水印文本
* @param type 类型:1.平铺;2.单个
*/
private static void waterMarkDocXDocument(XWPFDocument doc, String customText, String styleTop, int type, String fontColor, String fontSize, String rotation) {
//XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT); // 如果之前已经创建过 DEFAULT 的Header,将会复用之
/* XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
if (headerFooterPolicy == null) {
headerFooterPolicy = doc.createHeaderFooterPolicy();
}*/
// create default Watermark - fill color black and not rotated
//headerFooterPolicy.createWatermark(customText);
// get the default header
// Note: createWatermark also sets FIRST and EVEN headers
// but this code does not updating those other headers
//XWPFHeader header = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
if (doc.getHeaderList().size() == 0) {
XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT);
headerWatermark(doc, customText, styleTop, type, fontColor, fontSize, rotation, header);
} else {
for (XWPFHeader header : doc.getHeaderList()) {
headerWatermark(doc, customText, styleTop, type, fontColor, fontSize, rotation, header);
}
}
}
/**
* 处理Header水印
*
* @param doc
* @param customText
* @param styleTop
* @param type
* @param fontColor
* @param fontSize
* @param rotation
* @param header
*/
public static void headerWatermark(XWPFDocument doc, String customText, String styleTop, int type, String fontColor, String fontSize, String rotation, XWPFHeader header) {
int size = header.getParagraphs().size();
if (size == 0) {
header.createParagraph();
}
CTP ctp = header.getParagraphArray(0).getCTP();
byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR();
byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault();
ctp.setRsidP(rsidr);
ctp.setRsidRDefault(rsidrdefault);
CTPPr ppr = ctp.addNewPPr();
ppr.addNewPStyle().setVal("Header");
// 开始加水印
CTR ctr = ctp.addNewR();
CTRPr ctrpr = ctr.addNewRPr();
ctrpr.addNewNoProof();
CTGroup group = CTGroup.Factory.newInstance();
CTShapetype shapetype = group.addNewShapetype();
CTTextPath shapeTypeTextPath = shapetype.addNewTextpath();
shapeTypeTextPath.setOn(STTrueFalse.T);
shapeTypeTextPath.setFitshape(STTrueFalse.T);
CTLock lock = shapetype.addNewLock();
lock.setExt(STExt.VIEW);
CTShape shape = group.addNewShape();
shape.setId("PowerPlusWaterMarkObject");
shape.setSpid("_x0000_s102");
shape.setType("#_x0000_t136");
// 平铺或单个
if (type != 2) {
// 设置形状样式(旋转,位置,相对路径等参数)
shape.setStyle(getShapeStyle(customText, styleTop, rotation));
} else {
// 设置形状样式(旋转,位置,相对路径等参数)
shape.setStyle(getShapeStyle());
}
shape.setFillcolor(fontColor);
// 字体设置为实心
shape.setStroked(STTrueFalse.FALSE);
// 绘制文本的路径
CTTextPath shapeTextPath = shape.addNewTextpath();
// 设置文本字体与大小
shapeTextPath.setStyle("font-family:" + fontName + ";font-size:" + fontSize);
shapeTextPath.setString(customText);
CTPicture pict = ctr.addNewPict();
pict.set(group);
}
/**
* 构建Shape的样式参数
*
* @param customText 水印文本
* @return
*/
private static String getShapeStyle(String customText, String styleTop, String styleRotation) {
StringBuilder sb = new StringBuilder();
// 文本path绘制的定位方式
sb.append("position: ").append("absolute");
// 计算文本占用的长度(文本总个数*单字长度)
sb.append(";width: ").append(customText.length() * widthPerWord).append("pt");
// 字体高度
sb.append(";height: ").append("20pt");
sb.append(";z-index: ").append("-251654144");
sb.append(";mso-wrap-edited: ").append("f");
sb.append(";margin-top: ").append(styleTop);
sb.append(";mso-position-horizontal-relative: ").append("margin");
sb.append(";mso-position-vertical-relative: ").append("margin");
sb.append(";mso-position-vertical: ").append("left");
sb.append(";mso-position-horizontal: ").append("center");
sb.append(";rotation: ").append(styleRotation);
return sb.toString();
}
/**
* 构建Shape的样式参数
*
* @return
*/
private static String getShapeStyle() {
StringBuilder sb = new StringBuilder();
// 文本path绘制的定位方式
sb.append("position: ").append("absolute");
sb.append(";left: ").append("opt");
// 计算文本占用的长度(文本总个数*单字长度)
sb.append(";width: ").append("500pt");
// 字体高度
sb.append(";height: ").append("150pt");
sb.append(";z-index: ").append("-251654144");
sb.append(";mso-wrap-edited: ").append("f");
sb.append(";margin-left: ").append("-50pt");
sb.append(";margin-top: ").append("270pt");
sb.append(";mso-position-horizontal-relative: ").append("margin");
sb.append(";mso-position-vertical-relative: ").append("margin");
sb.append(";mso-width-relative: ").append("page");
sb.append(";mso-height-relative: ").append("page");
sb.append(";rotation: ").append("-2949120f");
return sb.toString();
}
/**
* 书签方式替换
*
* @param src
* @param dest
* @param docInfo
* @throws Exception
*/
public static void replaceBookmark(String src, String dest, Map<String, String> docInfo) throws Exception {
com.aspose.words.Document doc = new com.aspose.words.Document(src);
BookmarkCollection bookmarks = doc.getRange().getBookmarks();
for (String bookMarkName : docInfo.keySet()) {
Bookmark bookmark = bookmarks.get(bookMarkName);
if (bookmark != null) {
// 页签包含list为动态表格数据
if (bookmark.getName().contains("list")) {
// 动态插入word表格数据
Table targetTable = (Table) bookmark.getBookmarkStart().getParentNode().getParentNode().getParentNode().getParentNode();
Row targetRow = (Row) bookmark.getBookmarkStart().getParentNode().getParentNode().getParentNode();
// 复制表头后第一行
//Row cloneRow = (Row) targetTable.getRows().get(targetTable.indexOf(targetRow) + 1).deepClone(true);
List<Object> rowItem = new ArrayList<>();
Object rowObj = docInfo.get(bookMarkName);
if (rowObj instanceof List) {
rowItem = (List) rowObj;
}
for (int i = 0; i < rowItem.size(); i++) {
// 复制表头后第一行
Row cloneRow = (Row) targetTable.getRows().get(targetTable.indexOf(targetRow) + 1).deepClone(true);
List<String> cellList = (List<String>) rowItem.get(i);
for (int j = 0; j < cellList.size(); j++) {
Cell targetCell = cloneRow.getCells().get(j);
targetCell.removeAllChildren();
Paragraph p = new Paragraph(doc);
p.appendChild(new Run(doc, cellList.get(j)));
// 设置居中对齐
p.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER);
targetCell.appendChild(p);
targetCell.getCellFormat().setVerticalAlignment(VerticalAlignment.CENTER);
targetCell.getCellFormat().setHorizontalMerge(HorizontalAlignment.CENTER);
}
targetTable.getRows().insert(targetTable.indexOf(targetRow) + 1 + i, cloneRow);
}
} else {
bookmark.setText(docInfo.get(bookMarkName));
}
}
FileOutputStream out = new FileOutputStream(dest);
try {
doc.save(out, SaveFormat.DOCX);
} catch (Exception e) {
throw e;
} finally {
out.close();
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment