feat: bookmark replace
This commit is contained in:
parent
431a467c8a
commit
2f8a2aafba
@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import com.wmyun.farmwork.word.core.enums.BookmarkType;
|
||||
import com.wmyun.farmwork.word.core.enums.PictureProvideNameType;
|
||||
import com.wmyun.farmwork.word.core.model.AbstractExData;
|
||||
import com.wmyun.farmwork.word.core.model.BookmarkReplaceDataModel;
|
||||
import com.wmyun.farmwork.word.core.model.ext.PictureExData;
|
||||
@ -35,7 +36,8 @@ public class BookmarkExec {
|
||||
public final static String tmpDir = System.getProperty("java.io.tmpdir") + File.separator;
|
||||
|
||||
public static File replace(List<BookmarkReplaceDataModel> data, byte[] word, File file) {
|
||||
Map<String, BookmarkReplaceDataModel> map = data.stream().collect(Collectors.toMap(BookmarkReplaceDataModel::getName, b -> b));
|
||||
Map<String, BookmarkReplaceDataModel> dataMap = data.stream()
|
||||
.collect(Collectors.toMap(BookmarkReplaceDataModel::getName, b -> b));
|
||||
try (XWPFDocument doc = new XWPFDocument(IoUtil.toStream(word))) {
|
||||
// 收集所有段落
|
||||
|
||||
@ -76,62 +78,82 @@ public class BookmarkExec {
|
||||
}
|
||||
|
||||
for (XWPFParagraph paragraph : allParagraphs) {
|
||||
List<XWPFRun> runs = paragraph.getRuns();
|
||||
List<CTBookmark> bookmarkStartList = paragraph.getCTP().getBookmarkStartList();
|
||||
for (CTBookmark bookmark : bookmarkStartList) {
|
||||
String name = bookmark.getName();
|
||||
if (map.containsKey(name)) {
|
||||
List<XWPFRun> runs = paragraph.getRuns();
|
||||
XWPFRun run = null;
|
||||
int idx = 0;
|
||||
boolean doR = false;
|
||||
for (int i = 0; i < runs.size(); i++) {
|
||||
Node previousSibling = runs.get(i).getCTR().getDomNode().getPreviousSibling();
|
||||
if (previousSibling != null) {
|
||||
String nodeName = previousSibling.getNodeName();
|
||||
if ("w:bookmarkEnd".equals(nodeName)) {
|
||||
// end前一个是start
|
||||
Node bookmarkStartNode = previousSibling.getPreviousSibling();
|
||||
if (bookmarkStartNode != null) {
|
||||
Node nameAttribute = bookmarkStartNode.getAttributes().getNamedItem("w:name");
|
||||
// 获取书签名称,确保同一行内多个书签情况能正确处理
|
||||
if (nameAttribute != null && name.equals(nameAttribute.getNodeValue())) {
|
||||
idx = i - 1;
|
||||
doR = true;
|
||||
break;
|
||||
}
|
||||
if (!dataMap.containsKey(name)) {
|
||||
continue;
|
||||
}
|
||||
XWPFRun run = null;
|
||||
int idx = 0;
|
||||
boolean doR = false;
|
||||
for (int i = 0; i < runs.size(); i++) {
|
||||
Node previousSibling = runs.get(i).getCTR().getDomNode().getPreviousSibling();
|
||||
if (previousSibling != null) {
|
||||
String nodeName = previousSibling.getNodeName();
|
||||
if ("w:bookmarkEnd".equals(nodeName)) {
|
||||
// end前一个是start
|
||||
Node bookmarkStartNode = previousSibling.getPreviousSibling();
|
||||
if (bookmarkStartNode != null) {
|
||||
Node nameAttribute = bookmarkStartNode.getAttributes().getNamedItem("w:name");
|
||||
// 获取书签名称,确保同一行内多个书签情况能正确处理
|
||||
if (nameAttribute != null && name.equals(nameAttribute.getNodeValue())) {
|
||||
idx = i - 1;
|
||||
doR = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (idx < runs.size() && doR) {
|
||||
if (idx < 0) {// 如果是-1,说明这一段的开头有书签
|
||||
// 在本段开头位置插入新的run
|
||||
run = paragraph.insertNewRun(0);
|
||||
// 并复制原有开头的run的所有内容,后续只修改文本内容,不改变样式
|
||||
run.getCTR().set(runs.get(0).getCTR().copy());
|
||||
} else {
|
||||
run = runs.get(idx);
|
||||
}
|
||||
}
|
||||
if (idx < runs.size() && doR) {
|
||||
if (idx < 0) {// 如果是-1,说明这一段的开头有书签
|
||||
// 在本段开头位置插入新的run
|
||||
run = paragraph.insertNewRun(0);
|
||||
// 并复制原有开头的run的所有内容,后续只修改文本内容,不改变样式
|
||||
run.getCTR().set(runs.get(0).getCTR().copy());
|
||||
} else {
|
||||
// 处理一段末尾的书签(一般不存在这种情况)
|
||||
run = runs.get(runs.size() - 1);
|
||||
run = runs.get(idx);
|
||||
}
|
||||
BookmarkReplaceDataModel model = map.get(name);
|
||||
BookmarkType type = model.getType();
|
||||
// 处理文本
|
||||
if (BookmarkType.TEXT.equals(type)) {
|
||||
run.setText(model.getValue());
|
||||
} else if (!runs.isEmpty()){
|
||||
// 处理一段末尾的书签(一般不存在这种情况)
|
||||
run = runs.get(runs.size() - 1);
|
||||
} else {
|
||||
run = paragraph.createRun();
|
||||
}
|
||||
if (run == null) {
|
||||
continue;
|
||||
}
|
||||
BookmarkReplaceDataModel model = dataMap.get(name);
|
||||
BookmarkType type = model.getType();
|
||||
// 处理文本
|
||||
if (BookmarkType.TEXT.equals(type)) {
|
||||
run.setText(model.getValue());
|
||||
}
|
||||
// 处理图片,以嵌入式插入图片
|
||||
if (BookmarkType.PICTURE.equals(type)) {
|
||||
AbstractExData extData = model.getExtData();
|
||||
if (extData instanceof PictureExData picture && PictureProvideNameType.BOOKMARK.equals(picture.getNameType())) {
|
||||
try {
|
||||
run.addPicture(new ByteArrayInputStream(picture.readAsByteArray()), XWPFDocument.PICTURE_TYPE_PNG, picture.getPictureName(), Units.toEMU(picture.getWidth()), Units.toEMU(picture.getHeight()));
|
||||
} catch (InvalidFormatException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// 处理图片,以嵌入式插入图片
|
||||
if (BookmarkType.PICTURE.equals(type)) {
|
||||
AbstractExData extData = model.getExtData();
|
||||
if (extData instanceof PictureExData picture) {
|
||||
try {
|
||||
run.addPicture(new ByteArrayInputStream(picture.readAsByteArray()), XWPFDocument.PICTURE_TYPE_PNG, picture.getPictureName(), Units.toEMU(picture.getWidth()), Units.toEMU(picture.getHeight()));
|
||||
} catch (InvalidFormatException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (XWPFRun run : runs) {
|
||||
List<XWPFPicture> pictures = run.getEmbeddedPictures();
|
||||
for (XWPFPicture picture : pictures) {
|
||||
String description = picture.getDescription();
|
||||
if (dataMap.containsKey(description)) {
|
||||
BookmarkReplaceDataModel model = dataMap.get(description);
|
||||
if (model.getExtData() instanceof PictureExData pic) {
|
||||
String picId = doc.addPictureData(pic.readAsByteArray(), pic.readPictureType());
|
||||
picture.getCTPicture().getBlipFill().getBlip().setEmbed(picId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,7 +169,7 @@ public class BookmarkExec {
|
||||
doc.write(out);
|
||||
}
|
||||
return outFile;
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | InvalidFormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package com.wmyun.farmwork.word.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @Classname PictureProvideNameType
|
||||
* @Description TODO
|
||||
* @Date 2025/3/4 10:06
|
||||
* @Created by violet
|
||||
*/
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PictureProvideNameType {
|
||||
|
||||
BOOKMARK("BOOKMARK"),
|
||||
|
||||
DESCRIPTOR("DESCRIPTOR");
|
||||
|
||||
private final String type;
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
package com.wmyun.farmwork.word.core.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.wmyun.farmwork.word.core.enums.BookmarkType;
|
||||
import com.wmyun.farmwork.word.core.model.ext.PictureExData;
|
||||
import com.wmyun.farmwork.word.core.model.ext.TextExData;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@ -9,6 +14,14 @@ import lombok.Data;
|
||||
*/
|
||||
|
||||
@Data
|
||||
@JsonTypeInfo(
|
||||
use = JsonTypeInfo.Id.NAME,
|
||||
property = "type"
|
||||
)
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = TextExData.class, name = "TEXT"),
|
||||
@JsonSubTypes.Type(value = PictureExData.class, name = "PICTURE")
|
||||
})
|
||||
public class AbstractExData {
|
||||
|
||||
private BookmarkType type;
|
||||
}
|
||||
|
@ -3,10 +3,12 @@ package com.wmyun.farmwork.word.core.model.ext;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.wmyun.farmwork.word.core.enums.PictureProvideDataType;
|
||||
import com.wmyun.farmwork.word.core.enums.PictureProvideNameType;
|
||||
import com.wmyun.farmwork.word.core.model.AbstractExData;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.poi.xwpf.usermodel.Document;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Base64;
|
||||
@ -28,6 +30,8 @@ public class PictureExData extends AbstractExData {
|
||||
|
||||
private PictureProvideDataType dataType;
|
||||
|
||||
private PictureProvideNameType nameType;
|
||||
|
||||
private String data;
|
||||
|
||||
@SneakyThrows
|
||||
@ -43,4 +47,29 @@ public class PictureExData extends AbstractExData {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int readPictureType() {
|
||||
// 获取文件名,假设 pictureName 已经赋值
|
||||
String name = this.pictureName;
|
||||
if (name == null || !name.contains(".")) {
|
||||
throw new IllegalArgumentException("无效的图片名称:" + name);
|
||||
}
|
||||
// 获取扩展名,转换为小写
|
||||
String ext = name.substring(name.lastIndexOf('.') + 1).toLowerCase();
|
||||
// 根据扩展名返回对应的类型ID
|
||||
return switch (ext) {
|
||||
case "emf" -> Document.PICTURE_TYPE_EMF;
|
||||
case "wmf" -> Document.PICTURE_TYPE_WMF;
|
||||
case "pict" -> Document.PICTURE_TYPE_PICT;
|
||||
case "jpeg", "jpg" -> Document.PICTURE_TYPE_JPEG;
|
||||
case "png" -> Document.PICTURE_TYPE_PNG;
|
||||
case "dib" -> Document.PICTURE_TYPE_DIB;
|
||||
case "gif" -> Document.PICTURE_TYPE_GIF;
|
||||
case "tiff", "tif" -> Document.PICTURE_TYPE_TIFF;
|
||||
case "eps" -> Document.PICTURE_TYPE_EPS;
|
||||
case "bmp" -> Document.PICTURE_TYPE_BMP;
|
||||
case "wpg" -> Document.PICTURE_TYPE_WPG;
|
||||
default -> throw new IllegalArgumentException("不支持的图片类型: " + ext);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user