Reputation: 1546
I have a Java app using Apache POI which generates a PPT (PowerPoint Presentation) with shapes and texts on these shapes. I would like these texts to automatically fit inside of the corresponding shape. There is definitely an option for this in PowerPoint, which works fine and called 'Shrink text on overflow'
With Apache POI I can set this option from the code. Example below:
private void drawRectWithText(XSLFSlide slide, int x, int y, int width, int height, String text) {
XSLFAutoShape shape1 = slide.createAutoShape();
Rectangle rect1 = new Rectangle(x, y, width, upperRectHeight);
shape1.setAnchor(rect1.getBounds2D());
shape1.setShapeType(ShapeType.RECT);
shape1.setText(text);
shape1.setTextDirection(TextDirection.HORIZONTAL);
shape1.setVerticalAlignment(VerticalAlignment.MIDDLE);
XSLFTextParagraph xslfTextParagraph = shape1.getTextParagraphs().get(0);
xslfTextParagraph.setTextAlign(TextParagraph.TextAlign.CENTER);
xslfTextParagraph.setFontAlign(FontAlign.AUTO);
XSLFTextShape xslfTextShape = xslfTextParagraph.getParentShape();
xslfTextShape.setTextAutofit(TextAutofit.NORMAL); // <-- This sets the 'Shrink text on overflow'
xslfTextShape.setAnchor(rect1.getBounds2D());
}
This indeed sets the 'Shrink text on overflow' option properly, but nothing happens, the text does not shrink.
When I open the generated PPT, the text has the original size, and overflows, but I can see that the proper option is set. However, if I set anything on this shape or if I reset the slide, the text suddenly shrinks. So the option is set properly it just does not take affect until I refresh the shape.
Is there a way in Apache POI to somehow force refresh a shape? Or is there any other way to achieve the 'Shrink text on overflow' behaviour?
I'm using Apache POI 5.0.
Upvotes: 1
Views: 1216
Reputation: 61945
In Office Open XML drawing, the Normal AutoFit needs a fontScale
attribute which specifies the percentage of the original font size to which each run in the text body is scaled. If this attribute is omitted, then a value of 100% is implied. Apache POI does not have a method to set font scale nor will it auto calculate the font scale. So it is omitted. Only PowerPoint's GUI will auto calculate the font scale when necessary.
To present a workaround, one could use XSLFTextShape.resizeToFitText to calculate the size of a rectangle which would be needed to fit all the text in. If that size is bigger than the given size, then calculate the needed font scale to fit the text in the lower sized rectangle. However, XSLFTextShape.resizeToFitText
will resize the shape, so the wanted size needs to be set again after that.
Example:
import java.io.FileOutputStream;
import org.apache.poi.xslf.usermodel.*;
import org.apache.poi.sl.usermodel.*;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
public class CreatePPTXTextShape {
static void drawRectWithText(XSLFSlide slide, int x, int y, int width, int height, String text) {
XSLFAutoShape shape = slide.createAutoShape();
Rectangle rect = new Rectangle(x, y, width, height);
shape.setAnchor(rect.getBounds2D());
shape.setShapeType(ShapeType.RECT);
shape.setText(text);
shape.setTextDirection(TextShape.TextDirection.HORIZONTAL);
shape.setVerticalAlignment(VerticalAlignment.MIDDLE);
shape.setTextAutofit(TextShape.TextAutofit.NORMAL); // <-- This sets the 'Shrink text to overflow'
// calculate the needed font scaling
Rectangle2D rect2D = shape.resizeToFitText();
double upscaledHeight = rect2D.getHeight(); // needed height to fit the text in
double hScale = 1d;
if (upscaledHeight > height) hScale = height / upscaledHeight;
System.out.println(hScale);
// set font scale
shape.getTextBody().getXmlObject().getBodyPr().getNormAutofit().setFontScale(hScale * 100000);
// shape.resizeToFitText() had resized the shape, so set wanted size again
shape.setAnchor(rect.getBounds2D());
XSLFTextParagraph xslfTextParagraph = shape.getTextParagraphs().get(0);
xslfTextParagraph.setTextAlign(TextParagraph.TextAlign.CENTER);
}
public static void main(String[] args) throws Exception {
SlideShow slideShow = new XMLSlideShow();
XSLFSlide slide = (XSLFSlide)slideShow.createSlide();
drawRectWithText(slide, 100, 100, 50, 50, "This is a test text.");
drawRectWithText(slide, 100, 200, 100, 50, "This is a longer test text.");
drawRectWithText(slide, 300, 100, 50, 100, "This is a much longer test text.");
FileOutputStream out = new FileOutputStream("./CreatePPTXTextShape.pptx");
slideShow.write(out);
out.close();
slideShow.close();
}
}
Upvotes: 3