Meaning and unit of `x` and `y` in Office Open XML

2019-07-07 01:35发布

问题:

Does someone knows what is the meaning and unit of x and y in Office Open XML wrapPolygon?

The documentation states:

"Specifies a coordinate on the x-axis. The origin point for this coordinate shall be specified by the parent XML element. ...The possible values for this attribute are defined by the ST_Coordinate simple type (§ 5.1.12.16)."

That sounds like it should be describing a polygon starting at x=0 and y=0 in top left point of the picture. And the unit would be EMU.

But this cannot be true because if used in the code Change image layout or wrap in DOCX with Apache POI, then, if I do something like:

...
  +"<wp:wrapTight wrapText=\"bothSides\">"
  +"<wp:wrapPolygon edited=\"0\">"
  +"<wp:start x=\"0\" y=\"0\"/>"
  +"<wp:lineTo x=\"0\" y=\""+height+"\"/>"
  +"<wp:lineTo x=\""+width+"\" y=\""+height+"\"/>"
  +"<wp:lineTo x=\""+width+"\" y=\"0\"/>"
  +"<wp:lineTo x=\"0\" y=\"0\"/>"
  +"</wp:wrapPolygon>"
  +"</wp:wrapTight>"
...

then the resulting wrap points are much far outside the picture.

Instead describing a square polygon 21600 x 21600

...
  +"<wp:wrapPolygon edited=\"0\">"
  +"<wp:start x=\"0\" y=\"0\"/>"
  +"<wp:lineTo x=\"0\" y=\"21600\"/>"
  +"<wp:lineTo x=\"21600\" y=\"21600\"/>"
  +"<wp:lineTo x=\"21600\" y=\"0\"/>"
  +"<wp:lineTo x=\"0\" y=\"0\"/>"
  +"</wp:wrapPolygon>"
...

leads to wrap points which are in fully width x height of the picture.

And this is independent of the pictures size itself. It may be a square or rectangle sized picture in all possible sizes.

So while "The origin point for this coordinate shall be specified by the parent XML element." and the polygon starts at x=0 and y=0 in top left point of the picture, the unit cannot be EMU as for width and height. And since a square polygon 21600 x 21600 leads to rectangle wrap points if the picture is a rectangle, even the meaning of the polygon itself is not clear.

Is this documented somewhere?

回答1:

Well, seems nobody answers. So I will at least providing an example for why this rule: "A square polygon 21600 x 21600 leads to wrap points in fully width x height independent of picture size." could be useful.

If the need is setting more complex wrap points, a ellipse for example, only depicting this ellipse to a square 21600 x 21600 is needed, independent of picture size. This is much more simple than calculating the wrap points dependent on the really picture size. So we can forcing Word having really wrapping text tight around the picture.

Example Code:

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

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

import org.apache.poi.util.Units;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor;

public class WordPicturesWrapTight {

 private static CTAnchor getAnchorWithGraphic(CTGraphicalObject graphicalobject, 
                                              String drawingDescr, int width, int height,
                                              int left, int top) throws Exception {

  String anchorXML = 
   "<wp:anchor xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" "
  +"simplePos=\"0\" relativeHeight=\"0\" behindDoc=\"1\" locked=\"0\" layoutInCell=\"1\" allowOverlap=\"1\">"
  +"<wp:simplePos x=\"0\" y=\"0\"/>"
  +"<wp:positionH relativeFrom=\"column\"><wp:posOffset>"+left+"</wp:posOffset></wp:positionH>"
  +"<wp:positionV relativeFrom=\"paragraph\"><wp:posOffset>"+top+"</wp:posOffset></wp:positionV>"
  +"<wp:extent cx=\""+width+"\" cy=\""+height+"\"/>"
  +"<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>"
  +"<wp:wrapTight wrapText=\"bothSides\">"
  +"<wp:wrapPolygon edited=\"1\">"; //Set edited 1, so Word will not destroying the wrap points.

  //A square polygon 21600 x 21600 leads to wrap points in fully width x height independent of picture size.
  //So if the need is setting more complex wrap points, a ellipse for example, only depicting this ellipse 
  //to a square 21600 x 21600 is needed independent of picture size.

  long[] x = new long[5];
  long[] y = new long[5];
  for (int i = 0; i < 5; i++) {
   x[i] = i * 2700L; //2700 = 21600/2/4
   y[i] = Math.round(Math.sqrt(116640000d - Math.pow(i * 2700d, 2d))); //116640000 = (21600/2)^2
  }

  anchorXML += "<wp:start x=\""+(x[0]+10800)+"\" y=\""+(10800-y[0])+"\"/>";

  for (int i = 1; i < 5; i++) {
   anchorXML += "<wp:lineTo x=\""+(x[i]+10800)+"\" y=\""+(10800-y[i])+"\"/>";
  }
  for (int i = 3; i > -1; i--) {
   anchorXML += "<wp:lineTo x=\""+(x[i]+10800)+"\" y=\""+(10800+y[i])+"\"/>";
  }
  for (int i = 1; i < 5; i++) {
   anchorXML += "<wp:lineTo x=\""+(10800-x[i])+"\" y=\""+(10800+y[i])+"\"/>";
  }
  for (int i = 3; i > -1; i--) {
   anchorXML += "<wp:lineTo x=\""+(10800-x[i])+"\" y=\""+(10800-y[i])+"\"/>";
  }

  anchorXML += "</wp:wrapPolygon>"
  +"</wp:wrapTight>"
  +"<wp:docPr id=\"1\" name=\"Drawing 0\" descr=\""+drawingDescr+"\"/><wp:cNvGraphicFramePr/>"
  +"</wp:anchor>";

  CTDrawing drawing = CTDrawing.Factory.parse(anchorXML);
  CTAnchor anchor = drawing.getAnchorArray(0);
  anchor.setGraphic(graphicalobject);
  return anchor;  
 }

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

  XWPFDocument document = new XWPFDocument();

  XWPFParagraph paragraph = document.createParagraph();
  XWPFRun run = paragraph.createRun();

  InputStream in = new FileInputStream("ellipticSample.png");
  run.addPicture(in, Document.PICTURE_TYPE_PNG, "ellipticSample.png", Units.toEMU(100), Units.toEMU(60));
  in.close();  
  CTDrawing drawing = run.getCTR().getDrawingArray(0);
  CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic();
  CTAnchor anchor = getAnchorWithGraphic(graphicalobject, "ellipticSample.png", 
                                         Units.toEMU(100), Units.toEMU(60), 
                                         Units.toEMU(100), Units.toEMU(16));
  drawing.setAnchorArray(new CTAnchor[]{anchor});
  drawing.removeInline(0);

  run = paragraph.createRun();
  run.setText("The picture is anchored wrap tight. This means text will wrap this according to a polygon described by wrap points. Seems a square polygon 21600 x 21600 leads to wrap points in fully width x height independent of picture size. So if the need is setting more complex wrap points, a ellipse for example, only depicting this ellipse to a square 21600 x 21600 is needed independent of the picture size. This is much more simple if more complex wrap points shall be set. But it's a shame, that this feature not seems documented. So can we rely on it or not?");

  document.write(new FileOutputStream("WordPicturesWrapTight.docx"));
  document.close();
 }
}

Result: