javaapache-poidocx

Change image layout or wrap in DOCX with Apache POI


I paste image into docx programmatically. But in result the layout does not suit me. Faced a lack of documentation. I need to change image wrap (layout). For example now I have this:

enter image description here

But want this:

enter image description here

UPD1: What I do: iterate through the paragraphs, then through the runs and find certain run with special bookmark. In this run I add picture:

XWPFPicture pic =  run.addPicture(
        new ByteArrayInputStream(picSource),
        Document.PICTURE_TYPE_PNG,
        "pic",
        Units.toEMU(100),
        Units.toEMU(30));

UPD2: Investigated something interesting inside this class:

org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTAnchor

method setWrapTight(CTWrapTight var1). May be is it. By still don't know how to apply it to my code.

UPD3: Finally I came to this (currentRun - run with our picture):

    CTWrapTight ctWrapTight = currentRun.getCTR().getDrawingList().get(0).addNewAnchor().addNewWrapTight();
 CTWrapPath ctWrapPath = ctWrapTight.addNewWrapPolygon();

 CTPoint2D ctStart = ctWrapPath.addNewStart();
 ctStart.setX(0L);
 ctStart.setY(0L);

 CTPoint2D ctLineTo1 = ctWrapPath.addNewLineTo();
 CTPoint2D ctLineTo2 = ctWrapPath.addNewLineTo();
 CTPoint2D ctLineTo3 = ctWrapPath.addNewLineTo();

 ctLineTo1.setX(21384L);
 ctLineTo1.setY(20520L);

 ctLineTo2.setX(21384L);
 ctLineTo2.setY(0L);

 ctLineTo3.setX(0L);
 ctLineTo3.setY(0L);

ctWrapTight.setWrapText(STWrapText.BOTH_SIDES);

But it's break down document when I try to open it:

We're sorry. We can't open document because we found a problem with its contents.

Dependency are:

<dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.17</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>3.17</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>ooxml-schemas</artifactId>
        <version>1.3</version>
    </dependency>

Solution

  • While you already found the right classes to use - CTAnchor and successors - there is also the need considering the XML Schema definition of anchor. And this states there are many more duty elements required than only the ones which are defining wrapping. So using class by class of org.openxmlformats.schemas.drawingml.x2006 you are probably coding page wise code. My preferred solution for such problems is providing XML having all needed elements updated by some variables. This XML then can be parsed to get the needed object.

    Example:

    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 WordInsertPictures {
    
     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=\"0\">"
      +"<wp:start x=\"0\" y=\"0\"/>"
      +"<wp:lineTo x=\"0\" y=\"21600\"/>" //Square polygon 21600 x 21600 leads to wrap points in fully width x height
      +"<wp:lineTo x=\"21600\" y=\"21600\"/>"// Why? I don't know. Try & error ;-).
      +"<wp:lineTo x=\"21600\" y=\"0\"/>"
      +"<wp:lineTo x=\"0\" y=\"0\"/>"
      +"</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();
    
      run.setText("The picture in line: ");
    
      InputStream in = new FileInputStream("samplePict.jpeg");
      run.addPicture(in, Document.PICTURE_TYPE_JPEG, "samplePict.jpeg", Units.toEMU(100), Units.toEMU(30));
      in.close();  
    
      run.setText(" text after the picture.");
    
      paragraph = document.createParagraph();
    
      run = paragraph.createRun();
      in = new FileInputStream("samplePict.jpeg");
      run.addPicture(in, Document.PICTURE_TYPE_JPEG, "samplePict.jpeg", Units.toEMU(100), Units.toEMU(30));
      in.close();  
      CTDrawing drawing = run.getCTR().getDrawingArray(0);
      CTGraphicalObject graphicalobject = drawing.getInlineArray(0).getGraphic();
      CTAnchor anchor = getAnchorWithGraphic(graphicalobject, "samplePict.jpeg", 
                                             Units.toEMU(100), Units.toEMU(30), 
                                             Units.toEMU(30), Units.toEMU(0));
      drawing.setAnchorArray(new CTAnchor[]{anchor});
      drawing.removeInline(0);
    
      run = paragraph.createRun();
      run.setText("The previous picture is anchored wrapTight. The previous picture is anchored wrapTight. The previous picture is anchored wrapTight. The previous picture is anchored wrapTight. The previous picture is anchored wrapTight. The previous picture is anchored wrapTight.");
    
      document.write(new FileOutputStream("WordInsertPictures.docx"));
      document.close();
     }
    }