Apache POI:更改应用于错误的幻灯片

Apache POI: Changes applied to the wrong slide

Apache POI 3.17 升级到 5.x.

时,我有一个非常奇怪的行为

使用新的 Apache POI 版本,文本替换是在错误的幻灯片上完成的。它修改原始模板幻灯片,而不是在复制的模板上进行修改。

我使用现有的包含“模板”幻灯片的演示文稿。复制此模板幻灯片,然后替换某些文本。这与旧版本完美配合。

这里是我用来创建模板幻灯片副本的代码:

SlideShow slides = new XMLSlideShow(inputStream);
XSLFSlide template = slides.getSlides().get(SLIDE_INDEX_TEMPLATE);

XSLFSlide newSlide = slides.createSlide(template.getSlideLayout());
newSlide.importContent(template);

这里是文本替换的简化版本:

for (XSLFShape shape: slide) {
    if (shape instanceof XSLFTextShape) {
        XSLFTextShape textShape = (XSLFTextShape) shape;
        List<XSLFTextParagraph> textBoxParagraphs = textShape.getTextParagraphs();
        List<XSLFTextRun> textBoxParagraphTextRuns = textBoxParagraphs.stream().map(XSLFTextParagraph::getTextRuns).flatMap(List::stream).collect(Collectors.toList());
        for (XSLFTextRun r : textBoxParagraphTextRuns) {
            r.setText("Replaced!");
        }
    }
}

知道我做错了什么或 Apache POI 中的任何更改可能会破坏它吗?

自从 apache poi 4 以来,apache 开发人员决定对文本段落使用 org.apache.poi.xddf.usermodel.text.*,并且文本也在 XSLF 中运行。最多 apache poi 3.17 XSLF 有自己的 类 用于文本段落和文本运行。这些进一步存在(可能是为了向后兼容),但在大于 apache po 3.17 的版本中,当从其他幻灯片复制文本形状时,它们使用了错误的文本正文。我还没有找到发生这种情况的确切位置,但下面显示这是真的。

完整示例:

import java.io.FileOutputStream;
import java.io.FileInputStream;

import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;

import java.util.*;
import java.util.stream.*;

public class PPTXCCopyTemplateSlideAndChange {
    
 static final int SLIDE_INDEX_TEMPLATE = 0;
    
 public static void main(String[] args) throws Exception {

  XMLSlideShow slideShow = new XMLSlideShow(new FileInputStream("./PPTXIn.pptx"));

  XSLFSlide template = slideShow.getSlides().get(SLIDE_INDEX_TEMPLATE);

  XSLFSlide newSlide = slideShow.createSlide(template.getSlideLayout());
  newSlide = newSlide.importContent(template);

  for (XSLFShape shape: newSlide) {

   if (shape instanceof XSLFTextShape) {
    XSLFTextShape textShape = (XSLFTextShape) shape;
    
    //textShape.setText("Replaced!"); // this would work using apache poi 3.17 as well as using apache poi 5.2.2
    
/*  this works using apache poi 3.17 only but uses the wrong text body in version 5  
    List<XSLFTextParagraph> textBoxParagraphs = textShape.getTextParagraphs();
    List<XSLFTextRun> textBoxParagraphTextRuns = textBoxParagraphs.stream().map(XSLFTextParagraph::getTextRuns).flatMap(List::stream).collect(Collectors.toList());
    for (XSLFTextRun r : textBoxParagraphTextRuns) {
     r.setText("Replaced!");
    }
*/

///*  this works using apache poi 5.2.2 only but cannot work using version 3.17 because of usage of org.apache.poi.xddf.usermodel.text.*
    List<org.apache.poi.xddf.usermodel.text.XDDFTextParagraph> textBoxParagraphs = textShape.getTextBody().getParagraphs();
    List<org.apache.poi.xddf.usermodel.text.XDDFTextRun> textBoxParagraphTextRuns = textBoxParagraphs.stream().map(org.apache.poi.xddf.usermodel.text.XDDFTextParagraph::getTextRuns).flatMap(List::stream).collect(Collectors.toList());
    for (org.apache.poi.xddf.usermodel.text.XDDFTextRun r : textBoxParagraphTextRuns) {
     r.setText("Replaced!");
    }
//*/

   }
  }
  
  FileOutputStream out = new FileOutputStream("./PPTXOut.pptx");
  slideShow.write(out);
  out.close();
 }
}

此处 textShape.setText("Replaced!"); 使用 apache poi 3.17apache poi 5.2.2 一样有效,因为 XSLFTextShape.setText 即使在版本 5 中也直接使用基础 CTTextBody。当然使用它你无法控制不同的文本运行。

List<XSLFTextParagraph> textBoxParagraphs = textShape.getTextParagraphs(); 仅使用 apache poi 3.17 有效,但在版本 5 中使用了错误的文本正文。可能是因为 XSLFAutoShape.getTextBody(boolean create) 也使用 org.apache.poi.xddf.usermodel.text.XDDFTextBody 而不是仅使用基础 CTTextBody 直接。但不太确定。

List<org.apache.poi.xddf.usermodel.text.XDDFTextParagraph> textBoxParagraphs = textShape.getTextBody().getParagraphs(); 仅适用于 apache poi 5.2.2 但无法使用 3.17 版,因为使用了 3.17 版中未知的 org.apache.poi.xddf.usermodel.text.*

因此,您必须为不同版本的 apache poi 编写完全不同的代码。没有向后兼容性,甚至可能在这里尝试过。