2

I am trying to produce several reports (i.e. N PPTX files) based on different inputs/for different users on the same PPTX template I created.

I have several preformatted XSLFTextShape on the PPTX template that contains a single XSLFTextParagraph already formatted (i.e. both the shape and the text). Each shape contains a particular placeholder that I need to substitute with a dynimic value. I have this value in a Map (placeholder,newValue). I am successful in updating the placeholder with the new value using:

textShape.clearText();
XSLFTextRun run = paragraph.addNewTextRun();
run.setText(newText);

So, when I produce the PPTX in output the text is updated but font color, font formatting, font size are changed compared to those I defined in the template. How can I keep the same formatting?

Any solutions to simply change the text while keeping original formatting?

Thanks in advance!

IceSte86
  • 51
  • 8
  • Use XWPFRun It's provides you getStyle() and setStyle() to Run with text for words –  Oct 03 '17 at 10:34
  • Hi! Thanks for the answer. Can you elaborate a little bit? How do I istantiate `XWPFRun` starting from my object `XSLFTextShape`? Once I `get` the style from the template then how do I apply the `set`? Thanks – IceSte86 Oct 04 '17 at 07:20
  • I tried to implement something but it seems the object you mentioned is specific for Word Document - does not work with PPT: `XWPFDocument doc = new XWPFDocument(OPCPackage.open(pptxAbsolutePath));` `Element presentation@http://schemas.openxmlformats.org/presentationml/2006/main is not a valid document@http://schemas.openxmlformats.org/wordprocessingml/2006/main document or a valid substitution.` – IceSte86 Oct 04 '17 at 07:36
  • This link may help's a bit https://stackoverflow.com/questions/22268898/replacing-a-text-in-apache-poi-xwpf –  Oct 04 '17 at 07:42
  • Yep, already visited after your hint. I reproduced the same code, but as you can see from previous comment when I try to create the first object, I get an error because I try to assign a PPT while, probably (from what I can understand from the error note) the object just accepts DOC files. `XWPFDocument doc = new XWPFDocument(OPCPackage.open(pptxAbsolutePath));` So, I guess both `XWPFDocument` and `XWPFRun` are not suitable for PPT. – IceSte86 Oct 04 '17 at 07:53
  • Yes, PPT not support, Can you take a look **docx4j** with much more features then POI. –  Oct 04 '17 at 08:02
  • Actually, there is something similar to the classes you mentioned, starting from the` XSLFTextShape` (textShape) I need to modify preserving formatting. There is a `XSLFTextRun` that can be grab from each Paragraph (currentParagraph.getTextRuns()), which I try to use: `for (XSLFTextRun run : allCurrentTextRun) { XSLFTextRun runCurrent = paragraph.addNewTextRun(); runCurrent.setFontColor(run.getFontColor()); }` But no method for getStyle and setStlye...lots of method for color, font ...but I am not able to apply it without getting errors.. – IceSte86 Oct 04 '17 at 08:28
  • getStyle and setStlye methods in XWPFRun class, In my case I worked previously with doc, docx & pdf. Not PPT, So dude you have to RND for It or post your complete code with POI version. –  Oct 04 '17 at 08:41
  • Tried to find some workaround - No (complete) luck. However, `textBox.appendText(newText, true)` add the desired text formatted as the one previously in the TextBox. Second params will eventually create a new Paragraph (if true). Still I am unable to delete the old content. Tried to create a new parag and delete the previous or re-run TextRun without any luck. – IceSte86 Oct 16 '17 at 09:51

2 Answers2

1

For everybody which may be interested in this topic in the future - I post the solution (working if one TextBox has a single Paragraph). This solution loops on all text boxes and in the case one contain one of the vales specified in the Placeholder->newValue map, it will update it maintaining the formatting.

    public static void updateTextBoxesWithDesiredValues(XMLSlideShow ppt, Map<String, String> placeHolderDefinedValue) {
    logger.info("ElapsedTime: " + tM.getTimeElapsedReadableFormat() + " ########## Updating single text box content...");
    List<XSLFSlide> allSlides = ppt.getSlides();
    int updatedElements = 0;
    for (XSLFSlide currentSlide : allSlides) {
        for (XSLFShape shape : currentSlide.getShapes()) {
            if (shape instanceof XSLFTextShape) {
                XSLFTextShape textBox = (XSLFTextShape) shape;
                String elementTextContent = textBox.getText();
                for (Object key : placeHolderDefinedValue.keySet()) {
                    if (elementTextContent.equals(key)) {
                        List<XSLFTextParagraph> textBoxParagraphs = textBox.getTextParagraphs();
                        List<XSLFTextRun> textBoxParagraphTextRuns = textBoxParagraphs.get(0).getTextRuns();
                        //System.out.println("########################## check paragraph number in textbox: " + textBoxParagraphs.size() + " - TextRuns: " + textBoxParagraphs.get(0).getTextRuns().size());
                        logger.info("ElapsedTime: " + tM.getTimeElapsedReadableFormat() + updatedElements + ") Updating: " + textBox.getText() + " --> " + placeHolderDefinedValue.get(key));
                        for (XSLFTextRun r : textBoxParagraphTextRuns) {
                            r.setText(placeHolderDefinedValue.get(key));
                        }
                        updatedElements++;
                        //break;
                    }
                }
            }

        }
    }
    logger.info("ElapsedTime: " + tM.getTimeElapsedReadableFormat() + " Total Text Element Content Updated: " + updatedElements + " #########################");
}
IceSte86
  • 51
  • 8
0

It's kind of horrible - but yeah there's a reason they called it "POI".

Here's my approach to "only reset text" of an existing XSLFTextShape (that must have at least some text pre-set!):

    textShape.getTextParagraphs().get(0).getTextRuns().get(0).setText(text);

    for (int i = 1; i < textShape.getTextParagraphs().get(0).getTextRuns().size(); i++) {
        textShape.getTextParagraphs().get(0).getTextRuns().get(i).setText("");
    }
    for (int i = 1; i < textShape.getTextParagraphs().size(); i++) {
        textShape.getTextParagraphs().get(i).getTextRuns().stream().filter(tr -> !tr.getRawText().equals("\n")).forEach(tr -> tr.setText(""));
    }

It will replace all existing text(paragraphs/runs) with "empty" text, but linebreaks can't be replaced for some reason. So this might leave you with some trailing lines - as they usually(!) are transparent this won't really hurt a lot.

.clearText / removing paragraphs either destoyed the formatting for me, or didn't work. Trying to reset the style (fontColor, fontFamily, fontSize, isBold, isItalit, ...) didn't result in satisfying results :(

icyerasor
  • 4,973
  • 1
  • 43
  • 52