how to add a picture to a .docx document with Apac

2020-01-27 05:23发布

问题:

I've created a simple docx-Document, using Java POI 3.7. XWPF. Then, I added a picture by using the Method XWPFDocument.addpicture(byte[] arg0, int arg1).

XWPFDocument docx = new XWPFDocument();  
XWPFParagraph par = docx.createParagraph();  
XWPFRun run = par.createRun();
run.setText("Hello, World. This is my first java generated docx-file. Have fun.");
run.setFontSize(13);

InputStream pic = new FileInputStream("logo.jpg");
byte [] picbytes = IOUtils.toByteArray(pic);
docx.addPicture(picbytes, Document.PICTURE_TYPE_JPEG);

Since file size of the docx grows, the picture was added "physically" to the document; but it is not displayed at all in MS Word. Seems like there is a reference to the picture missing in the document.

What's the way to do this? How is picture handling done with apache POI? And where are more tutorials on the web, there is nearly no documents or tutorials at all for XWPF, which explains handling of paragraphs, runs, etc.

The only thing I found about this is here: https://issues.apache.org/bugzilla/show_bug.cgi?id=49765 but it does not help at all.

Many thanks in advance.

回答1:

I know this post is very old but still I am posting the answer so that all those who are searching for this answer can use it. For inserting the picture in the word document you have to write two programs. The first one is :-

package org.word.POI;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.Document;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

/*
Romesh Soni
soni.romesh@gmail.com
*/

public class TestCustom
{

    public static void main(String []a) throws FileNotFoundException, IOException, InvalidFormatException
    {

        CustomXWPFDocument document = new CustomXWPFDocument(new FileInputStream(new File("C:\\Users\\amitabh\\Documents\\Apache POI\\Word File\\new.doc")));
        FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\amitabh\\Documents\\Apache POI\\Word File\\new.doc"));

        String blipId = document.addPictureData(new FileInputStream(new File("C:\\Users\\amitabh\\Pictures\\pics\\3.jpg")), Document.PICTURE_TYPE_JPEG);

        System.out.println(document.getNextPicNameNumber(Document.PICTURE_TYPE_JPEG));

        //System.out.println(document.getNextPicNameNumber(Document.PICTURE_TYPE_JPEG));
        document.createPicture(blipId,document.getNextPicNameNumber(Document.PICTURE_TYPE_JPEG), 500, 500);


        document.write(fos);
        fos.flush();
        fos.close();

    }

}

Now here I have used "CustomeXwPFDocument" in this code and you will not get any imports through any jar file so you have to add another .java class in your package. the code for "CustomXWPFDocument" class goes like this:-

package org.word.POI;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;

import java.io.IOException;
import java.io.InputStream;

public class CustomXWPFDocument extends XWPFDocument
{
    public CustomXWPFDocument(InputStream in) throws IOException
    {
        super(in);
    }

    public void createPicture(String blipId,int id, int width, int height)
    {
        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        //String blipId = getAllPictures().get(id).getPackageRelationship().getId();


        CTInline inline = createParagraph().createRun().getCTR().addNewDrawing().addNewInline();

        String picXml = "" +
                "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
                "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
                "         <pic:nvPicPr>" +
                "            <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" +
                "            <pic:cNvPicPr/>" +
                "         </pic:nvPicPr>" +
                "         <pic:blipFill>" +
                "            <a:blip r:embed=\"" + blipId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
                "            <a:stretch>" +
                "               <a:fillRect/>" +
                "            </a:stretch>" +
                "         </pic:blipFill>" +
                "         <pic:spPr>" +
                "            <a:xfrm>" +
                "               <a:off x=\"0\" y=\"0\"/>" +
                "               <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" +
                "            </a:xfrm>" +
                "            <a:prstGeom prst=\"rect\">" +
                "               <a:avLst/>" +
                "            </a:prstGeom>" +
                "         </pic:spPr>" +
                "      </pic:pic>" +
                "   </a:graphicData>" +
                "</a:graphic>";

        //CTGraphicalObjectData graphicData = inline.addNewGraphic().addNewGraphicData();
        XmlToken xmlToken = null;
        try
        {
            xmlToken = XmlToken.Factory.parse(picXml);
        }
        catch(XmlException xe)
        {
            xe.printStackTrace();
        }
        inline.set(xmlToken);
        //graphicData.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("Picture " + id);
        docPr.setDescr("Generated");
    }
}

Use POI 3.9 jars for this program. The best URL is:- http://www.apache.org/dyn/closer.cgi/poi/release/bin/poi-bin-3.9-20121203.zip

Now you are ready to fly. Best of luck.



回答2:

The elegant solution is coming, check out: https://issues.apache.org/bugzilla/show_bug.cgi?id=55476

You can either wait till my patch gets accepted and released or try applying the patch to the latest POI SVN checkout (http://poi.apache.org/subversion.html, read only access link will work) and building POI jars from sources.

The approach suggested by tsd.tom will work:

XWPFParagraph par = docx.createParagraph();
XWPFRun run = par.createRun();
run.addPicture(pic, XWPFDocument.PICTURE_TYPE_JPEG, "logo.JPG",300,300); 


回答3:

The bug link that you've provided actually has a working solution: https://issues.apache.org/bugzilla/show_bug.cgi?id=49765#c15

It requires some amount of hackery but I'm using it and it certainly works!



回答4:

I have used docx4j and found it to be working.I tried with POI but not able to make it work.Please find the code for using docx4j.

public static void main(String[] args) throws Exception {
    WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
    //wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello World");
    //wordMLPackage.getMainDocumentPart().addParagraphOfText("Text");
     java.io.InputStream is = new java.io.FileInputStream("path\\to\\images.jpg");
// commons-io.jar
     byte[] bytes = IOUtils.toByteArray(is);
     String filenameHint = null;
     String altText = null;
     int id1 = 0;
     int id2 = 1;
     org.docx4j.wml.P p = newImage( wordMLPackage, bytes,filenameHint, altText,id1, id2,6000  );
     // Now add our p to the document
     wordMLPackage.getMainDocumentPart().addObject(p);
     wordMLPackage.save(new java.io.File("helloworld.docx") );
     is.close();
}



public static org.docx4j.wml.P newImage( WordprocessingMLPackage wordMLPackage,
        byte[] bytes,
        String filenameHint, String altText, 
        int id1, int id2, long cx) throws Exception {

        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);

        Inline inline = imagePart.createImageInline(filenameHint, altText,id1, id2, cx,false);

    // Now add the inline in w:p/w:r/w:drawing
        org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
        org.docx4j.wml.P  p = factory.createP();
        org.docx4j.wml.R  run = factory.createR();             
        p.getContent().add(run);       
        org.docx4j.wml.Drawing drawing = factory.createDrawing();               
        run.getContent().add(drawing);               
        drawing.getAnchorOrInline().add(inline);

        return p;

}



回答5:

@Pradeep  Your code

    public static void main(String[] args) throws Exception {
    WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
    //wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello World");
    //wordMLPackage.getMainDocumentPart().addParagraphOfText("Text");
     java.io.InputStream is = new java.io.FileInputStream("path\\to\\images.jpg");
// commons-io.jar
     byte[] bytes = IOUtils.toByteArray(is);
     String filenameHint = null;
     String altText = null;
     int id1 = 0;
     int id2 = 1;
     org.docx4j.wml.P p = newImage( wordMLPackage, bytes,filenameHint, altText,id1, id2,6000  );
     // Now add our p to the document
     wordMLPackage.getMainDocumentPart().addObject(p);
     wordMLPackage.save(new java.io.File("helloworld.docx") );
     is.close();
}



public static org.docx4j.wml.P newImage( WordprocessingMLPackage wordMLPackage,
        byte[] bytes,
        String filenameHint, String altText, 
        int id1, int id2, long cx) throws Exception {

        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);

        Inline inline = imagePart.createImageInline(filenameHint, altText,id1, id2, cx,false);

    // Now add the inline in w:p/w:r/w:drawing
        org.docx4j.wml.ObjectFactory factory = new org.docx4j.wml.ObjectFactory();
        org.docx4j.wml.P  p = factory.createP();
        org.docx4j.wml.R  run = factory.createR();             
        p.getContent().add(run);       
        org.docx4j.wml.Drawing drawing = factory.createDrawing();               
        run.getContent().add(drawing);               
        drawing.getAnchorOrInline().add(inline);

        return p;

 works , but all the above codes does not work, i tried it personally

thanks, a lot.


回答6:

Because XWPFRun.addPicture does not work (it damage xdoc file) i manage to picture like this:

    private void addPicture(final String fileName, final int id, int width, int height, final XWPFRun run)
{
    FileInputStream fileInputStream = null;

    try
    {
        fileInputStream = new FileInputStream("/home/maq/ebworkspace/TeamForge_Tools/" + fileName);
        final String blipId = run.getDocument().addPictureData(fileInputStream, Document.PICTURE_TYPE_JPEG);

        final int EMU = 9525;
        width *= EMU;
        height *= EMU;
        //String blipId = getAllPictures().get(id).getPackageRelationship().getId();

        final CTInline inline = run.getCTR().addNewDrawing().addNewInline();

        final String picXml = "" + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
                + "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
                + "         <pic:nvPicPr>" + "            <pic:cNvPr id=\""
                + id
                + "\" name=\"Generated\"/>"
                + "            <pic:cNvPicPr/>"
                + "         </pic:nvPicPr>"
                + "         <pic:blipFill>"
                + "            <a:blip r:embed=\""
                + blipId
                + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
                + "            <a:stretch>"
                + "               <a:fillRect/>"
                + "            </a:stretch>"
                + "         </pic:blipFill>"
                + "         <pic:spPr>"
                + "            <a:xfrm>"
                + "               <a:off x=\"0\" y=\"0\"/>"
                + "               <a:ext cx=\""
                + width
                + "\" cy=\""
                + height
                + "\"/>"
                + "            </a:xfrm>"
                + "            <a:prstGeom prst=\"rect\">"
                + "               <a:avLst/>"
                + "            </a:prstGeom>"
                + "         </pic:spPr>"
                + "      </pic:pic>" + "   </a:graphicData>" + "</a:graphic>";

        //CTGraphicalObjectData graphicData = inline.addNewGraphic().addNewGraphicData();
        XmlToken xmlToken = null;
        xmlToken = XmlToken.Factory.parse(picXml);
        inline.set(xmlToken);
        //graphicData.set(xmlToken);

        inline.setDistT(0);
        inline.setDistB(0);
        inline.setDistL(0);
        inline.setDistR(0);

        final CTPositiveSize2D extent = inline.addNewExtent();
        extent.setCx(width);
        extent.setCy(height);

        final CTNonVisualDrawingProps docPr = inline.addNewDocPr();
        docPr.setId(id);
        docPr.setName("Picture " + id);
        docPr.setDescr("Generated");
    }
    catch (final Exception e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finally
    {
        //close streams 
        if (fileInputStream != null)
        {
            try
            {
                fileInputStream.close();
            }
            catch (final IOException ioEx)
            {
                //can be ignored
            }
        }
    }

}

And running example:

 addPicture(filePath, xdoc.getNextPicNameNumber(Document.PICTURE_TYPE_JPEG), 100, 50, xRun);