diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 1c398d0b9..76d616f47 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 49765 - Support for adding a picture to a XSSFRun Rename/Move xssf.model.Table to xssf.usermodel.XSSFTable as it now has usermodel-like features 51061 - Correct target URI for new XSSF Tables Initial support for XSSF Charts. Provides easy access to the underlying CTChart object via the Sheet Drawing, but no high level interface onto the chart contents as yet. diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index 4188cbdac..f0f314cf9 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -928,10 +928,13 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } /** - * Adds a picture to the document. + * Adds a picture to the document. Users should normally call + * {@link XWPFRun#addPicture(InputStream, int)} + * * * @param is The stream to read image from - * @param format The format of the picture. + * @param format The format of the picture, eg {@link Document#PICTURE_TYPE_JPEG} + * * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . * @throws InvalidFormatException @@ -947,10 +950,11 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { } /** - * Adds a picture to the document. + * Adds a picture to the document. Users should normally call + * {@link XWPFRun#addPicture(InputStream, int)} * * @param pictureData The bytes to read image from - * @param format The format of the picture. + * @param format The format of the picture, eg {@link Document#PICTURE_TYPE_JPEG} * * @return the index to this picture (0 based), the added picture can be obtained from {@link #getAllPictures()} . * @throws InvalidFormatException diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java index e8689a6b9..a08823c19 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFRun.java @@ -16,6 +16,8 @@ ==================================================================== */ package org.apache.poi.xwpf.usermodel; +import java.io.IOException; +import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -23,14 +25,29 @@ import java.util.List; import javax.xml.namespace.QName; import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlString; +import org.apache.xmlbeans.XmlToken; import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; -import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture; +import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; +import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; +import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; +import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualPictureProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetGeometry2D; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; +import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; +import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBr; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTEmpty; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHpsMeasure; @@ -49,6 +66,8 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.STUnderline; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalAlignRun; import org.w3c.dom.NodeList; import org.w3c.dom.Text; +import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture; +import org.openxmlformats.schemas.drawingml.x2006.picture.CTPictureNonVisual; /** * XWPFRun object defines a region of text with a common set of properties @@ -94,28 +113,31 @@ public class XWPFRun { // (They're a different CTPicture, under the drawingml namespace) pictures = new ArrayList(); for(XmlObject o : pictTextObjs) { - XmlObject[] picts = o - .selectPath("declare namespace pic='http://schemas.openxmlformats.org/drawingml/2006/picture' .//pic:pic"); - for(XmlObject pict : picts) { - if(pict instanceof XmlAnyTypeImpl) { - // Pesky XmlBeans bug - see Bugzilla #49934 - try { - pict = org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture.Factory.parse( - pict.toString() - ); - } catch(XmlException e) { - throw new POIXMLException(e); - } - } - if(pict instanceof org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture) { - XWPFPicture picture = new XWPFPicture( - (org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture)pict, p - ); - pictures.add(picture); - } + for(CTPicture pict : getCTPictures(o)) { + XWPFPicture picture = new XWPFPicture( pict, p ); + pictures.add(picture); } } } + + private List getCTPictures(XmlObject o) { + List pictures = new ArrayList(); + XmlObject[] picts = o.selectPath("declare namespace pic='"+CTPicture.type.getName().getNamespaceURI()+"' .//pic:pic"); + for(XmlObject pict : picts) { + if(pict instanceof XmlAnyTypeImpl) { + // Pesky XmlBeans bug - see Bugzilla #49934 + try { + pict = CTPicture.Factory.parse( pict.toString() ); + } catch(XmlException e) { + throw new POIXMLException(e); + } + } + if(pict instanceof CTPicture) { + pictures.add((CTPicture)pict); + } + } + return pictures; + } /** * Get the currently used CTR object @@ -561,7 +583,107 @@ public class XWPFRun { public void removeCarriageReturn() { //TODO - } + } + + /** + * Adds a picture to the run. This method handles + * attaching the picture data to the overall file. + * + * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_EMF + * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_WMF + * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_PICT + * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_JPEG + * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_PNG + * @see org.apache.poi.xwpf.usermodel.Document#PICTURE_TYPE_DIB + * + * @param pictureData The raw picture data + * @param pictureType The type of the picture, eg {@link Document#PICTURE_TYPE_JPEG} + * @throws IOException + * @throws org.apache.poi.openxml4j.exceptions.InvalidFormatException + * @throws IOException + */ + public XWPFPicture addPicture(InputStream pictureData, int pictureType, String filename, int width, int height) + throws InvalidFormatException, IOException { + XWPFDocument doc = paragraph.document; + + // Add the picture + relationship + int picNumber = doc.addPicture(pictureData, pictureType); + XWPFPictureData picData = doc.getAllPackagePictures().get(picNumber); + + // Create the drawing entry for it + try { + CTDrawing drawing = run.addNewDrawing(); + CTInline inline = drawing.addNewInline(); + + // Do the fiddly namespace bits on the inline + // (We need full control of what goes where and as what) + String xml = + "" + + "" + + "" + + "" + + ""; + inline.set(XmlToken.Factory.parse(xml)); + + // Setup the inline + inline.setDistT(0); + inline.setDistR(0); + inline.setDistB(0); + inline.setDistL(0); + + CTNonVisualDrawingProps docPr = inline.addNewDocPr(); + docPr.setId(picNumber); + docPr.setName("Picture " + picNumber); + docPr.setDescr(filename); + + CTPositiveSize2D extent = inline.addNewExtent(); + extent.setCx(width); + extent.setCy(height); + + // Grab the picture object + CTGraphicalObject graphic = inline.getGraphic(); + CTGraphicalObjectData graphicData = graphic.getGraphicData(); + CTPicture pic = getCTPictures(graphicData).get(0); + + // Set it up + CTPictureNonVisual nvPicPr = pic.addNewNvPicPr(); + + CTNonVisualDrawingProps cNvPr = nvPicPr.addNewCNvPr(); + cNvPr.setId(picNumber); + cNvPr.setName("Picture " + picNumber); + cNvPr.setDescr(filename); + + CTNonVisualPictureProperties cNvPicPr = nvPicPr.addNewCNvPicPr(); + cNvPicPr.addNewPicLocks().setNoChangeAspect(true); + + CTBlipFillProperties blipFill = pic.addNewBlipFill(); + CTBlip blip = blipFill.addNewBlip(); + blip.setEmbed( picData.getPackageRelationship().getId() ); + blipFill.addNewStretch().addNewFillRect(); + + CTShapeProperties spPr = pic.addNewSpPr(); + CTTransform2D xfrm = spPr.addNewXfrm(); + + CTPoint2D off = xfrm.addNewOff(); + off.setX(0); + off.setY(0); + + CTPositiveSize2D ext = xfrm.addNewExt(); + ext.setCx(width); + ext.setCy(height); + + CTPresetGeometry2D prstGeom = spPr.addNewPrstGeom(); + prstGeom.setPrst(STShapeType.RECT); + prstGeom.addNewAvLst(); + + // Finish up + XWPFPicture xwpfPicture = new XWPFPicture(pic, paragraph); + pictures.add(xwpfPicture); + return xwpfPicture; + } catch(XmlException e) { + throw new IllegalStateException(e); + } + } /** * Returns the embedded pictures of the run. These diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java index 8ca44c9e8..b12932c2e 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFRun.java @@ -16,6 +16,7 @@ ==================================================================== */ package org.apache.poi.xwpf.usermodel; +import java.io.ByteArrayInputStream; import java.math.BigInteger; import java.util.List; @@ -352,4 +353,18 @@ public class TestXWPFRun extends TestCase { assertEquals(1, count); } + + public void testAddPicture() throws Exception { + XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("TestDocument.docx"); + XWPFParagraph p = doc.getParagraphArray(2); + XWPFRun r = p.getRuns().get(0); + + assertEquals(0, doc.getAllPictures().size()); + assertEquals(0, r.getEmbeddedPictures().size()); + + r.addPicture(new ByteArrayInputStream(new byte[0]), Document.PICTURE_TYPE_JPEG, "test.jpg", 21, 32); + + assertEquals(1, doc.getAllPictures().size()); + assertEquals(1, r.getEmbeddedPictures().size()); + } }