Java ApachePOI:在文本前面插入图像

2022-06-15 00:00:00 docx ms-word java apache-poi xwpf
我的docx文件中有一个占位符图像,我想用新图像替换它。问题是--占位符图像在文本前面有一个属性,而新图像没有。因此,对齐方式会中断。下面是我的代码片段以及带有占位符的docx和结果docx。

            .......
            replaceImage(doc, "Рисунок 1", qr, 50, 50);

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            doc.write(out);
            out.close();
            return out.toByteArray();
        }
    }

    public XWPFDocument replaceImage(XWPFDocument document, String imageOldName, byte[] newImage, int newImageWidth, int newImageHeight) throws Exception {
        try {
            int imageParagraphPos = -1;
            XWPFParagraph imageParagraph = null;
            List<IBodyElement> documentElements = document.getBodyElements();
            for (IBodyElement documentElement : documentElements) {
                imageParagraphPos++;
                if (documentElement instanceof XWPFParagraph) {
                    imageParagraph = (XWPFParagraph) documentElement;
                    if (imageParagraph.getCTP() != null && imageParagraph.getCTP().toString().trim().contains(imageOldName)) {
                        break;
                    }
                }
            }

            if (imageParagraph == null) {
                throw new Exception("Unable to replace image data due to the exception:
"
                        + "'" + imageOldName + "' not found in in document.");
            }
            ParagraphAlignment oldImageAlignment = imageParagraph.getAlignment();

            // remove old image
            boolean isDeleted = document.removeBodyElement(imageParagraphPos);
            // now add new image
            XWPFParagraph newImageParagraph = document.createParagraph();
            XWPFRun newImageRun = newImageParagraph.createRun();
            newImageParagraph.setAlignment(oldImageAlignment);
            try (InputStream is = new ByteArrayInputStream(newImage)) {
                newImageRun.addPicture(is, XWPFDocument.PICTURE_TYPE_JPEG, "qr",
                        Units.toEMU(newImageWidth), Units.toEMU(newImageHeight));
            }

            // set new image at the old image position
            document.setParagraph(newImageParagraph, imageParagraphPos);

            // NOW REMOVE REDUNDANT IMAGE FORM THE END OF DOCUMENT
            document.removeBodyElement(document.getBodyElements().size() - 1);

            return document;
        } catch (Exception e) {
            throw new Exception("Unable to replace image '" + imageOldName + "' due to the exception:
" + e);
        }
    }

带占位符的图像:

enter image description here

生成的图像:

enter image description here


解决方案

若要替换Microsoft Word中的图片模板,无需将其删除。

存储是这样的: 嵌入的媒体以二进制文件的形式存储。这是图片数据(XWPFPictureData)。在文档中,图片元素(XWPFPicture)链接到该图片数据。

XWPFPicture具有位置、大小和文本流动设置。不需要更改这些设置。

需要在XWPFPictureData中更改。在那里,用户可以用新的二进制内容替换旧的二进制内容。

因此,需要在文档中找到XWPFPicture。在文档中插入图片时存储了一个非视觉图片名称。因此,如果一个人知道这个名字,那么这可能是找到这张照片的标准。

如果找到,则可以从找到的XWPFPicture获得XWPFPictureData。有方法XWPFPicture.getPictureData可以这样做。然后,可以用新的二进制内容替换旧的二进制内容XWPFPictureDataXWPFPictureData是一个包部件。因此它必须PackagePart.getOutputStream获取要写入的输出流。

下面的完整示例显示了全部。

source.docx需要具有名为&qot;QRTemplate.jpg";的嵌入图片。这是使用Word图形用户界面将图片插入Word文档时使用的源文件的名称。并且需要包含新内容的文件QR.jpg

然后result.docx将名为";QRTemplate.jpg";的所有图片替换为给定文件QR.jpg的内容。

import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import org.apache.poi.xwpf.usermodel.*;

public class WordReplacePictureData {
    
 static XWPFPicture getPictureByName(XWPFRun run, String pictureName) {
  if (pictureName == null) return null;
  for (XWPFPicture picture : run.getEmbeddedPictures()) {
   String nonVisualPictureName = picture.getCTPicture().getNvPicPr().getCNvPr().getName();
   if (pictureName.equals(nonVisualPictureName)) {
    return picture;   
   }
  }
  return null;
 }
 
 static void replacePictureData(XWPFPictureData source, String pictureResultPath) {
  try ( FileInputStream in = new FileInputStream(pictureResultPath); 
        OutputStream out = source.getPackagePart().getOutputStream();
       ) {
   byte[] buffer = new byte[2048];
   int length;
   while ((length = in.read(buffer)) > 0) {
    out.write(buffer, 0, length);
   }
  } catch (Exception ex) {
   ex.printStackTrace();  
  }
 }
 
 static void replacePicture(XWPFRun run, String pictureName, String pictureResultPath) {
  XWPFPicture picture = getPictureByName(run, pictureName);
  if (picture != null) {
   XWPFPictureData source = picture.getPictureData();
   replacePictureData(source, pictureResultPath);
  }   
 }

 public static void main(String[] args) throws Exception {
  String templatePath = "./source.docx";
  String resultPath = "./result.docx";
  String pictureTemplateName = "QRTemplate.jpg";
  String pictureResultPath = "./QR.jpg";
  
  try ( XWPFDocument document = new XWPFDocument(new FileInputStream(templatePath));
        FileOutputStream out = new FileOutputStream(resultPath);
       ) {
   
   for (IBodyElement bodyElement : document.getBodyElements()) {
    if (bodyElement instanceof XWPFParagraph) {
     XWPFParagraph paragraph = (XWPFParagraph)bodyElement;
     for (XWPFRun run : paragraph.getRuns()) {
      replacePicture(run, pictureTemplateName, pictureResultPath);
     }
    }
   }       
   document.write(out);
  }    
 }
}

相关文章