使用POI在图片之后插入文字
首先要知道docx文件是zip形式保存的,文档内容会保存为xml格式。我们将一个docx文件后缀改为zip并打开,会发现里面又很多文件夹以及xml文件。
内容存在word文件夹下的document.xml文件中,charts文件夹保存图表,embeddings中的xlsx文件是图表的信息,图片则保存在media中。
使用浏览器打开document.xml文件,里面就是我们在文档中输入的内容。
<w:p>:段落,Paragraph
<w:pPr>:段落样式
<w:r>:文本,Run
<w:rPr>:文本样式
<w:t>:文本内容
<w:hdr>:页眉
<w:ftr>:页脚
<w:pict>:图片
<w:drawing>:
进入正题,采用的主要想法是根据图片和文字的标签进行定位,然后
File file = new File(filePath);
in = new FileInputStream(file);
XWPFDocument doc = new XWPFDocument(in);
//获取段落
List<XWPFParagraph> paraList = doc.getParagraphs();
//run.getEmbeddedPictures()方法只能获取嵌入式的图片
//doc.getAllPictures()获取全部图片
List<XWPFPictureData> pictures = doc.getAllPictures();
//将图片的byte放到map中,方便后面获取
Map<String, byte[]> picDataMap = new HashMap<>();
for (XWPFPictureData pic : pictures) {
//relationId就是图片引用的rid
String rid = pic.getParent().getRelationId(pic);
picDataMap.put(rid, pic.getData());
}
for (XWPFParagraph para : paraList){
//run是一段文本对象,一个段落(XWPFPaaragraph)的run会根据图片进行分割
List<XWPFRun> runList = para.getRuns();
for (int i = 0; i < runList.size(); i++) {
//保存当前run需要插入的值和插入位置,在判断结束后统一插入
Map<Integer, String> insertMap = new HashMap<>();
//当前run没有文字时,需要插入的值:
String insertValue = "";
XWPFRun run = runList.get(i);
//POI对word的处理:将word以xml格式读取后进行封装
//getCTR方法获取xml信息
String runXmlText = run.getCTR().xmlText();
//判断当前文本对象<w:pict>为引用图片,<w:drawing>为嵌入图片:嵌入图片也可以通过run.getEmbeddedPictures()获取
if (runXmlText.indexOf("<w:pict>") != -1) {
//该方法利用indexOf获取当前文本包含图片的位置,如果为嵌入图片应使用r:embed获取位置。
List<Integer> indexList = getRIdIndex(0, runXmlText, "r:id");
List<Integer> wtIndexList = getWtIndex(0, runXmlText, "<w:t");
for (int j = 0; j < indexList.size(); j++){
//通过切割获取当前位置图片的rid
int rIdIndex = indexList.get(j);
int rIdEndIndex = runXmlText.indexOf("/>", rIdIndex);
String rIdText = runXmlText.substring(rIdIndex, rIdEndIndex);
String id = rIdText.split("\"")[1];
//通过获取到的rid读取图片信息
byte[] bytes = picDataMap.get(id);
//当前图片流,可以对图片进行处理
InputStream picIn = new ByteArrayInputStream(bytes);
//value为要插入的文字
String value = "要插入的值";
if (!CommonUtil.checkNull(value)){
//用于判断图片后面是否还有文本
boolean hasEndWt = false;
for (int k = 0; k < wtIndexList.size(); k++) {
int wtIndex = wtIndexList.get(k);
//当wt在pic之后进行插入
if (wtIndex > rIdIndex) {
//run.setText(value, pos);pos数量就是当前run中<w:t></w:t>的数量
//防止多图问题,先把值和位置放到map中。在当前run中所有图片值确定之后在进行插入。
if (!CommonUtil.checkNull(insertMap.get(k))){
insertMap.put(k, insertMap.get(k) + value);
} else{
insertMap.put(k, value);
}
hasEndWt = true;
break;
}
}
//如果图片后没有文本
if (!hasEndWt) {
//防止多图问题,先把值和位置放到map中。在当前run中所有图片值确定之后在进行插入。
insertValue += value;
//通过测试来看可以不做下列判断,直接在当前run插入文本,但不确定文本是否都在图片后面。
}
}
}
}
//在当前run进行插入
if (!CommonUtil.checkNullOrEmpty(insertMap)){
for (Map.Entry<Integer, String> entry : insertMap.entrySet()){
int key = entry.getKey();
String value = entry.getValue();
run.setText(value + run.getText(key), key);
}
}
if (!"".equals(insertValue)) {
XWPFRun newRun = para.createRun();
newRun.setText(insertValue);
}
}
}
out = new FileOutputStream(filePath);
doc.write(out);
————————————————
版权声明:本文为CSDN博主「学习要趁早z」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42677452/article/details/116267235