kajoe14
kajoe14

Reputation: 13

How to add text outlines to text within Powerpoint via Apache POI:

Does anyone have an idea how we can add outlines to text (text outline) within powerpoint templates (ppxt) using Apache POI? What I have gathered so far is that the XSLFTextRun class does not have a method to get/ set the text outline for a given run element.

And as such, I could only persist the following font/ text styles:

def fontStyles(textBox: XSLFTextBox, textRun: XSLFTextRun): Unit = {
    val fontFamily = textRun.getFontFamily
    val fontColor = textRun.getFontColor
    val fontSize = textRun.getFontSize
    val fontBold = textRun.isBold
    val fontItalic = textRun.isItalic
    val textAlign = textRun.getParagraph.getTextAlign

    textBox.getTextParagraphs.foreach { p =>
      p.getTextRuns.foreach { tr =>
        tr.setFontFamily(fontFamily)
        tr.setFontColor(fontColor)
        tr.setFontSize(fontSize)
        tr.setBold(fontBold)
        tr.setItalic(fontItalic)
        tr.getParagraph.setTextAlign(textAlign)
      }
    }
  }

Is it possible to add text outline?

Any assistance/ suggestions would be highly appreciated.

Upvotes: 0

Views: 718

Answers (1)

Axel Richter
Axel Richter

Reputation: 61852

Apache poi uses underlying ooxml-schemas classes. Those are auto generated from Office Open XML standard. So they are more complete than the high level XSLF classes. Of course they are much less convenient.

So if somewhat is not implemented in high level XSLF classes, we can get the underlying CT classes and do it using those. In case of XSLFTextRun we can get the CTRegularTextRun object. Then we can look whether there are run properties already. If not, we add one. Then we look whether there is outline set already. If so, we unset it, because we want set it new. Then we set a new outline. This simply is a line having a special color. That line is represented by CTLineProperties object. So we need to have methods to create that CTLineProperties, to set CTLineProperties to the XSLFTextRun and get CTLineProperties from XSLFTextRun.

Complete example using Java code:

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

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

import java.awt.Rectangle;

public class PPTXTextRunOutline {
    
 static org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties createSolidFillLineProperties(java.awt.Color color) {
  // create new CTLineProperties
  org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties lineProperties 
   = org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties.Factory.newInstance();
  // set line solid fill color
  lineProperties.addNewSolidFill().addNewSrgbClr().setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()});
  return lineProperties;
 }
    
 static void setOutline(XSLFTextRun run, org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties lineProperties) {
  // get underlying CTRegularTextRun object
  org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun ctRegularTextRun 
   = (org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun)run.getXmlObject();
  // Are there run properties already? If not, add one.
  if (ctRegularTextRun.getRPr() == null) ctRegularTextRun.addNewRPr();
  // Is there outline set already? If so, unset it, because we are creating it new.
  if (ctRegularTextRun.getRPr().isSetLn()) ctRegularTextRun.getRPr().unsetLn();
  // set a new outline
  ctRegularTextRun.getRPr().setLn(lineProperties);  
 }
 
  static org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties getOutline(XSLFTextRun run) {
  // get underlying CTRegularTextRun object
  org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun ctRegularTextRun 
   = (org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun)run.getXmlObject();
  // Are there run properties already? If not, return null.
  if (ctRegularTextRun.getRPr() == null) return null;
  // get outline, may be null
  org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties lineProperties = ctRegularTextRun.getRPr().getLn();
  // make a copy to avoid orphaned exceptions or value disconnected exception when set to its own XML parent
  if (lineProperties != null) lineProperties = (org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties)lineProperties.copy();
  return lineProperties;
 }

 // your method fontStyles taken to Java code
 static void fontStyles(XSLFTextRun templateRun, XSLFTextShape textShape) {
  String fontFamily = templateRun.getFontFamily();
  PaintStyle fontColor = templateRun.getFontColor();
  Double fontSize = templateRun.getFontSize();
  boolean fontBold = templateRun.isBold();
  boolean fontItalic = templateRun.isItalic();
  TextParagraph.TextAlign textAlign = templateRun.getParagraph().getTextAlign();
  org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties lineProperties = getOutline(templateRun);
  for (XSLFTextParagraph paragraph : textShape.getTextParagraphs()) {
   for (XSLFTextRun run : paragraph.getTextRuns()) {
    run.setFontFamily(fontFamily);
    if(run != templateRun) run.setFontColor(fontColor); // set PaintStyle has the issue which I am avoiding by using a copy of the underlying XML
    run.setFontSize(fontSize);
    run.setBold(fontBold);
    run.setItalic(fontItalic);
    run.getParagraph().setTextAlign(textAlign);
       
    setOutline(run, lineProperties);
   }
  }   
 }

 public static void main(String[] args) throws Exception {

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

  XSLFSlide slide = slideShow.getSlides().get(0);
  
  //as in your code, get a template text run and set its font style to all other runs in text shape
  if (slide.getShapes().size() > 0) {
   XSLFShape shape = slide.getShapes().get(0); 
   if (shape instanceof XSLFTextShape) {
    XSLFTextShape textShape = (XSLFTextShape) shape;
    XSLFTextParagraph paragraph = null;
    if(textShape.getTextParagraphs().size() > 0) paragraph = textShape.getTextParagraphs().get(0);
    if (paragraph != null) {
     XSLFTextRun run = null;
     if(paragraph.getTextRuns().size() > 0) run = paragraph.getTextRuns().get(0);
     if (run != null) {
      fontStyles(run, textShape);  
     }
    }        
   }
  }

  //new text box having outlined text from scratch
  XSLFTextBox textbox = slide.createTextBox(); 
  textbox.setAnchor(new Rectangle(100, 300, 570, 80));
  XSLFTextParagraph paragraph = null;
  if(textbox.getTextParagraphs().size() > 0) paragraph = textbox.getTextParagraphs().get(0);
  if(paragraph == null) paragraph = textbox.addNewTextParagraph(); 
  XSLFTextRun run = paragraph.addNewTextRun();
  run.setText("Test text outline");
  run.setFontSize(60d);
  run.setFontColor(java.awt.Color.YELLOW);
  setOutline(run, createSolidFillLineProperties(java.awt.Color.BLUE));
  
  FileOutputStream out = new FileOutputStream("./PPTXOit.pptx");
  slideShow.write(out);
  out.close();
 }
}

Tested and works using current apache poi 5.0.0.

Upvotes: 1

Related Questions