poi icon indicating copy to clipboard operation
poi copied to clipboard

BUG: Shape with empty paragraph & textrun generates invalid XML

Open sascha08-15 opened this issue 3 months ago • 0 comments

POI creates invalid pptx files if we call .clearText and do not create a paragraph and textrun in it. The later is the workaround; however, this IS a bug.

Test to reproduce below, just open the created file in PowerPoint (on Mac) it will complain about invalid xml.

import org.apache.poi.sl.usermodel.ShapeType;
import org.junit.jupiter.api.Test;

import java.awt.geom.Rectangle2D;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.junit.jupiter.api.Assertions.*;

public class PoiClearTextPptxTest {

    @Test
    public void createShapeClearTextAndSave_pptxShouldBeReportedInvalidByPowerPoint() throws Exception {
        // create a new presentation
        try (XMLSlideShow ppt = new XMLSlideShow()) {
            // create a slide
            XSLFSlide slide = ppt.createSlide();

            // create a rectangle auto-shape and position it
            XSLFAutoShape rect = slide.createAutoShape();
            rect.setShapeType(ShapeType.RECT);
            rect.setAnchor(new Rectangle2D.Double(100, 100, 400, 120));

            // add text to the shape
            XSLFTextParagraph p = rect.addNewTextParagraph();
            XSLFTextRun run = p.addNewTextRun();
            run.setText("This is a test text that will be cleared.");

            // sanity-check text is present
            assertTrue(rect.getText().contains("This is a test text"));

            // THIS IS THE KEY CALL: remove all text content from the shape
            rect.clearText();

            // After clearText(), POI leaves the shape without text runs/paragraphs in its model.
            // That can produce a PPTX that PowerPoint flags as "needs repair" / invalid.

            // write to a temporary file
            Path out = Files.createTempFile("poi-cleartext-repro-", ".pptx");
            try (FileOutputStream fos = new FileOutputStream(out.toFile())) {
                ppt.write(fos);
            }

            // Basic checks
            assertTrue(Files.exists(out));
            assertTrue(Files.size(out) > 0);

            System.out.println("Wrote test file to: " + out.toAbsolutePath());

            // Optional: try to re-open with POI (may succeed even if PowerPoint complains)
            try (FileInputStream fis = new FileInputStream(out.toFile());
                 XMLSlideShow reopened = new XMLSlideShow(fis)) {
                // inspect shapes for missing text bodies
                boolean foundTextShape = false;
                for (XSLFShape s : reopened.getSlides().get(0).getShapes()) {
                    if (s instanceof XSLFTextShape) {
                        foundTextShape = true;
                        XSLFTextShape ts = (XSLFTextShape) s;
                        // after clearText(), paragraphs/runs often are empty
                        System.out.println("Reopened shape text (may be empty): '" + ts.getText() + "'");
                    }
                }
                assertTrue(foundTextShape, "We expected at least one text shape on the slide");
            }

            // Note: open the printed file path in MS PowerPoint to reproduce the "invalid" report.
        }
    }
}

sascha08-15 avatar Oct 17 '25 11:10 sascha08-15