一步一步摸索实现
开发需求:在法院文书的【经办法院】上方插入对应法院名称的蒙文头图片
在固定位置插入文字
public static void createPicInDoc(XWPFParagraph para,String value){
try {
XWPFDocument document = para.getDocument();
XmlCursor xmlCursor = para.getCTP().newCursor();
XWPFParagraph newPara = document.insertNewParagraph(xmlCursor);
//如newPara为空,就按表格式搜索
if(ObjectUtils.isNull(newPara)){
List<XWPFTable> tables = document.getTables();
for (XWPFTable tab :tables) {
XWPFTableRow row = tab.getRow(0);
XWPFTableCell cell = row.getCell(0);
List<XWPFParagraph> paragraphs = cell.getParagraphs();
XWPFParagraph xwpfParagraph = paragraphs.get(2);
XmlCursor xmlCursor1 = xwpfParagraph.getCTP().newCursor();
newPara = cell.insertNewParagraph(xmlCursor1);
XWPFRun run = newPara.createRun();
run.setText("hjx_caozuo_ceshi");
run.setFontSize(20);
run.setFontFamily("宋体");
run.setBold(true);
}
}
byte[] bytes = Base64.decodeBase64(value);
String blipId = document.addPictureData(bytes, 5);
newPara.setAlignment(para.getAlignment());
createPicture(blipId,0, 540, 77, newPara);
} catch (Exception e) {
logger.error("向word中添加图片异常!!!",e);
}
}
以上说明光标位置正确了,但是行和列是写死的,后面再看怎么改。
但是改成插入图片还是有问题,打成zip包发现图片已经存入doc里了,但是xml里没有<pic:pic这个标签节点,说明插入图片xml还有问题
在指定位置插入游标
先改了找经办法院位置的问题,单提了一个方法出来,但是循环嵌套比较多,不知道sonar能不能通过
/**
* 从document中找【经办法院】所在位置,并插入新段落游标,返回新段落游标
* @param document
* @return XWPFParagraph
*/
public static XWPFParagraph findJBFYpicFromTable (XWPFDocument document) {
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for(XWPFTableCell cell : row.getTableCells()) {
if(cell.getText().contains("【经办法院】")){
for (XWPFParagraph para:cell.getParagraphs()) {
if(para.getText().contains("【经办法院】")){
XmlCursor xmlCursor = para.getCTP().newCursor();
return cell.insertNewParagraph(xmlCursor);
}
}
}
}
}
}
return null;
}
在游标处插入图片
public static void createPicInDoc(XWPFParagraph para,String value){
try {
XWPFDocument document = para.getDocument();
XmlCursor xmlCursor = para.getCTP().newCursor();
XWPFParagraph newPara = document.insertNewParagraph(xmlCursor);
//如newPara为空,就按表格式搜索
if(ObjectUtils.isNull(newPara)){
newPara = findJBFYpicFromTable(document);
}
byte[] bytes = Base64.decodeBase64(value);
String blipId = document.addPictureData(bytes, 5);
newPara.setAlignment(para.getAlignment());
createPicture(blipId,0, 540, 77, newPara);
} catch (Exception e) {
logger.error("向word中添加图片异常!!!",e);
}
}
在createPicture之后使用流输出一份word到桌面,此时发现图片已经插入,只是没有替换其他标签,因为后面本应该还有其他操作,只是在这里输出了文件,这样看来,图片已经插入,可能是后面替换标签时又被替换了
过程中插入成功,最终生成文件插入失败,原因排查
跟代码找到,是在这里被remove了(cell.removeParagraph(j))
private static void batchProcessTableNewLine(XWPFTableCell cell){
List<XWPFParagraph> paragraphs = cell.getParagraphs();
int orgSize = paragraphs.size();
String cellText = null;
try{
cellText =cell.getText();
}catch(Exception e){
logger.error("cell获取text失败", e);
}
if(StringUtils.isNotBlank(cellText)){
for(int j = 0; j < orgSize; j++){
XWPFParagraph paragraph = paragraphs.get(j);
processNewLine(cell, paragraph);
}
for(int j = orgSize-1; j >= 0; j--){
cell.removeParagraph(j);
}
}
}
再往后排查,是表格式的,每个单元格中的paragraph会把新生成的paragraph完全复制一份放到原来的cell格中,然后将原来的paragraphremove掉,但是复制的时候并没复制到图片内容。
private static void setNewParagraphInfos(XWPFParagraph newParagraph, XWPFParagraph baseParagraph){
newParagraph.setIndentationFirstLine(baseParagraph.getIndentationFirstLine());
newParagraph.setSpacingBefore(baseParagraph.getSpacingBefore()==NumberUtil.NUM_MINUS_1?0:baseParagraph.getSpacingBefore());
newParagraph.setSpacingAfter(baseParagraph.getSpacingAfter()==NumberUtil.NUM_MINUS_1?0:baseParagraph.getSpacingAfter());
newParagraph.setAlignment(baseParagraph.getAlignment());
boolean baseSpaceLineStyle = baseParagraph.getCTP().getPPr().getSpacing() != null &&
baseParagraph.getCTP().getPPr().getSpacing().getLineRule() != null &&
baseParagraph.getCTP().getPPr().getSpacing().getLine() != null;
if(baseSpaceLineStyle){
newParagraph.getCTP().getPPr().getSpacing().setLineRule(baseParagraph.getCTP().getPPr().getSpacing().getLineRule());
newParagraph.getCTP().getPPr().getSpacing().setLine(baseParagraph.getCTP().getPPr().getSpacing().getLine());
}
newParagraph.getCTP().getPPr().setInd(baseParagraph.getCTP().getPPr().getInd());
newParagraph.getCTP().getPPr().setNumPr(baseParagraph.getCTP().getPPr().getNumPr());
}
private static void setNewRunInfos(XWPFRun newRun, XWPFRun baseRun, String newRunTxt){
RunStyleUtil.copyRunStyle(baseRun,newRun);
newRun.setText(newRunTxt);
newRun.setSubscript(baseRun.getSubscript());
}
现在的问题就是如何把这部分set进去,两个部分,一个 <w:pPr>,一个 <w:r><w:drawing>。
<w:pPr>
<w:jc w:val="center"/>
</w:pPr>
<w:r>
<w:drawing>
<wp:inline distT="0" distB="0" distL="0" distR="0">
<wp:extent cx="5143500" cy="733425"/>
<wp:docPr id="1" name="xx"/>
<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
<pic:nvPicPr>
<pic:cNvPr id="0" name="Generated"/>
<pic:cNvPicPr/>
</pic:nvPicPr>
<pic:blipFill>
<a:blip r:embed="rId17"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
<pic:spPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="5143500" cy="733425"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</pic:spPr>
</pic:pic>
</a:graphicData>
</a:graphic>
</wp:inline>
</w:drawing>
</w:r>
新增了
newParagraph.getCTP().setPPr(baseParagraph.getCTP().getPPr());
CTR ctr = newParagraph.getCTP().addNewR();
ctr.set(baseParagraph.getCTP().getRArray(0));
现在图片不会被丢失了,但是出了新问题,表格中的文本都重复了
这部分还是加个判断吧,条件为<w:r>下有<w:drawing>节点,即这个段落有图片时这样做。
if(!Objects.isNull(baseParagraph.getCTP().getRArray(0).getDrawingArray(0))){
CTR ctr = newParagraph.getCTP().addNewR();
ctr.set(baseParagraph.getCTP().getRArray(0));
}
这样写运行中直接被catch了,后面发现应该是baseParagraph.getCTP().getRArray(0)这里就有可能已经为空了,再点的话就空指针了。
暂时改成这样,暂时解决,后面看怎么改
try{
if(baseParagraph.getCTP().getRArray(0).getDrawingArray(0)!=null){
CTR ctr = newParagraph.getCTP().addNewR();
ctr.set(baseParagraph.getCTP().getRArray(0));
}
}catch(Exception e){
}
Optional.ofNullable(baseParagraph.getCTP())
.map(p -> p.getRArray(0))
.map(a -> a.getDrawingArray(0)).ifPresent(m -> {
CTR ctr = newParagraph.getCTP().addNewR();
ctr.set(baseParagraph.getCTP().getRArray(0));
});
最终实现代码
public static void createPicInDoc(XWPFParagraph para,String value){
try {
XWPFDocument document = para.getDocument();
XmlCursor xmlCursor = para.getCTP().newCursor();
XWPFParagraph newPara = document.insertNewParagraph(xmlCursor);
//如newPara为空,就按表格式搜索
if(ObjectUtils.isNull(newPara)){
newPara = findJBFYpicFromTable(document);
}
byte[] bytes = Base64.decodeBase64(value);
String blipId = document.addPictureData(bytes, 5);
newPara.setAlignment(para.getAlignment());
createPicture(blipId,0, 540, 77, newPara);
} catch (Exception e) {
logger.error("向word中添加图片异常!!!",e);
}
}
/**
* 从document中找【经办法院】所在位置,并插入新段落游标,返回新段落游标
* @param document
* @return XWPFParagraph
*/
public static XWPFParagraph findJBFYpicFromTable (XWPFDocument document) {
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for(XWPFTableCell cell : row.getTableCells()) {
if(cell.getText().contains("【经办法院】")){
for (XWPFParagraph para:cell.getParagraphs()) {
if(para.getText().contains("【经办法院】")){
XmlCursor xmlCursor = para.getCTP().newCursor();
return cell.insertNewParagraph(xmlCursor);
}
}
}
}
}
}
return null;
}
//当段落里存在图片节点,就复制一份到新段落中
List<CTR> rList = baseParagraph.getCTP().getRList();
for (CTR c: rList) {
if(c.getDrawingList().size()>0){
CTR ctr = newParagraph.getCTP().addNewR();
ctr.set(c);
}
}
XWPFTableCell cell = document.getTables().get(0).getRows().get(0).getTableCells().get(0);
XWPFTableCell cell = Optional.of(document.getTables())
.map(a -> a.get(0))
.map(b -> b.getRows())
.map(c -> c.get(0))
.map(d -> d.getTableCells())
.map(e -> e.get(0));