Arvind
Arvind

Reputation: 46

How to create a WedgeRectCallout shape in Word using apache poi?

I need to create a WedgeRectCallout shape in a Word document using Apache POI. I am not able to find any reference to this in Apache POI Using the link you can see the WedgeRectCallout image

Also, the link shows how to add a textbox in a Word document, but not a shape.

In C#, I can do the same using Spire.Doc library and writing below code:

ShapeObject Shape1 = para1.AppendShape(30, 50, ShapeType.WedgeRectCallout);

Upvotes: 0

Views: 1298

Answers (1)

Axel Richter
Axel Richter

Reputation: 61945

The newest Word versions are using wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" for shapes. But this it not supported by neither the low level ooxml-schemasnor apache poi. It would only be possible by manipulating the XML directly on very low level base.

But Word also uses Vector Markup Language (VML) as a fallback. And this could be done as ooxml-schemas-1.4 provides com.microsoft.schemas.vml. This also will be the most compatible way.

You can read about Vector Markup Language (VML). The simplest callout shape will be the following XML

<w:pict>
 <v:shape coordsize="21600,21600" adj="7200,21600" path="m 1,1 l 1,14400, 7200,14400, @0,@1, 14400,14400, 21600,14400, 21600,1 x e" style="position:absolute;margin-left:1in;margin-top:-150px;width:100px;height:150px;z-index:251659264;visibility:visible;rotation:0;" strokecolor="#0000FF" fillcolor="yellow">
  <v:formulas>
   <v:f eqn="val #0"/>
   <v:f eqn="val #1"/>
  </v:formulas>
  <v:handles><v:h position="#0,#1"/></v:handles>
  <v:path textboxrect="1,1,21600,14400"/>
  <v:textbox>
   <w:txbxContent><w:p><w:r><w:t>The callout</w:t><w:br/></w:r><w:r><w:t>text...</w:t><w:br/></w:r></w:p>
   </w:txbxContent>
  </v:textbox>
 </v:shape>
</w:pict>

As you see, the outline of the shape is determined by a path. So creating other shapes will also be possible if one has understand how the path works.

Example code for creating callout shapes:

import java.io.FileOutputStream;

import org.apache.poi.xwpf.usermodel.*;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPicture;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTxbxContent;

import com.microsoft.schemas.vml.CTGroup;
import com.microsoft.schemas.vml.CTShape;
import com.microsoft.schemas.vml.CTFormulas;
import com.microsoft.schemas.vml.CTTextbox;

import org.w3c.dom.Node;

public class CreateWordShapes {

 public static void appendCalloutShape(XWPFRun run, String left, String top, String width, String height, 
                                       String strokecolor, String fillcolor, String calloutext, boolean hashandles) throws Exception {
  CTGroup ctGroup = CTGroup.Factory.newInstance();

  CTShape ctShape = ctGroup.addNewShape();
  ctShape.setCoordsize("21600,21600");
  if (hashandles) { //is not Libreoffice Writer compatible
   ctShape.setAdj("" + 21600*1/3 + ",21600");
   CTFormulas cTFormulas = ctShape.addNewFormulas();
   cTFormulas.addNewF().setEqn("val #0");
   cTFormulas.addNewF().setEqn("val #1");
   ctShape.setPath2("m 1,1 l 1," + 21600*2/3 + ", " + 21600*1/3 + "," + 21600*2/3 + ", @0,@1, " + 21600*2/3 + "," + 21600*2/3 + ", 21600," + 21600*2/3 + ", 21600,1 x e");
   ctShape.addNewHandles().addNewH().setPosition("#0,#1");
  } else { // is Libreoffice Writer compatible
   ctShape.setPath2("m 1,1 l 1," + 21600*2/3 + ", " + 21600*1/3 + "," + 21600*2/3 + ", " + 21600*1/3 + ",21600, " + 21600*2/3 + "," + 21600*2/3 + ", 21600," + 21600*2/3 + ", 21600,1 x e");
  }

  ctShape.addNewPath().setTextboxrect("1,1,21600," + 21600*2/3);

  ctShape.setStyle("position:absolute;margin-left:" + left + ";margin-top:" + top + ";width:" + width + ";height:" + height + ";z-index:251659264;visibility:visible;rotation:0;");
  ctShape.setStrokecolor(strokecolor);
  ctShape.setFillcolor(fillcolor);

  CTTextbox cTTextbox = ctShape.addNewTextbox();

  CTTxbxContent ctTxbxContent = cTTextbox.addNewTxbxContent();
  XWPFParagraph textboxparagraph = new XWPFParagraph(ctTxbxContent.addNewP(), run.getDocument());
  XWPFRun textboxrun = null;
  String[] lines = calloutext.split("\n");
  for (int i = 0; i < lines.length; i++) {
   textboxrun = textboxparagraph.createRun();
   textboxrun.setText(lines[i]);
   textboxrun.addBreak();
  }

  Node ctGroupNode = ctGroup.getDomNode(); 
  CTPicture ctPicture = CTPicture.Factory.parse(ctGroupNode);
  CTR cTR = run.getCTR();
  cTR.addNewPict();
  cTR.setPictArray(0, ctPicture);
 }

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

  XWPFDocument document = new XWPFDocument();

  XWPFParagraph paragraph = document.createParagraph();
  XWPFRun run =  paragraph.createRun();  
  run.setText("Callout shape over text: Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor Lorem ipsum semit dolor.");

  appendCalloutShape(run, "200pt", "0", "1in", "1in", "black", "#00FF00", "The callout\ntext...", false);

  paragraph = document.createParagraph();
  paragraph = document.createParagraph();
  paragraph = document.createParagraph();
  paragraph = document.createParagraph();
  run =  paragraph.createRun();  
  run.setText("Callout shape:");

  appendCalloutShape(run, "1in", "-150px", "100px", "150px", "#0000FF", "yellow", "The callout\ntext...", true);


  FileOutputStream out = new FileOutputStream("test.docx");
  document.write(out);
  out.close();
  document.close();
 }
}

Result:

enter image description here

Note: This needs apache poi 4.0.1 and ooxml-schemas-1.4.jar to be in the class path.

Upvotes: 1

Related Questions