#56004 - Support for WMF rendering

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1724897 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-01-16 00:11:01 +00:00
parent 0ba83f7683
commit 50d98918d9
17 changed files with 468 additions and 141 deletions

View File

@ -186,6 +186,10 @@ public final class EscherProperties {
public static final short LINESTYLE__HITLINETEST = 509; public static final short LINESTYLE__HITLINETEST = 509;
public static final short LINESTYLE__LINEFILLSHAPE = 510; public static final short LINESTYLE__LINEFILLSHAPE = 510;
public static final short LINESTYLE__NOLINEDRAWDASH = 511; public static final short LINESTYLE__NOLINEDRAWDASH = 511;
public static final short LINESTYLE__NOLINEDRAWDASH_LEFT = 0x057F;
public static final short LINESTYLE__NOLINEDRAWDASH_TOP = 0x05BF;
public static final short LINESTYLE__NOLINEDRAWDASH_BOTTOM = 0x063F;
public static final short LINESTYLE__NOLINEDRAWDASH_RIGHT = 0x05FF;
public static final short SHADOWSTYLE__TYPE = 512; public static final short SHADOWSTYLE__TYPE = 512;
public static final short SHADOWSTYLE__COLOR = 513; public static final short SHADOWSTYLE__COLOR = 513;
public static final short SHADOWSTYLE__HIGHLIGHT = 514; public static final short SHADOWSTYLE__HIGHLIGHT = 514;
@ -488,6 +492,10 @@ public final class EscherProperties {
addProp(m, LINESTYLE__HITLINETEST, "linestyle.hitlinetest"); addProp(m, LINESTYLE__HITLINETEST, "linestyle.hitlinetest");
addProp(m, LINESTYLE__LINEFILLSHAPE, "linestyle.linefillshape"); addProp(m, LINESTYLE__LINEFILLSHAPE, "linestyle.linefillshape");
addProp(m, LINESTYLE__NOLINEDRAWDASH, "linestyle.nolinedrawdash", EscherPropertyMetaData.TYPE_BOOLEAN); addProp(m, LINESTYLE__NOLINEDRAWDASH, "linestyle.nolinedrawdash", EscherPropertyMetaData.TYPE_BOOLEAN);
addProp(m, LINESTYLE__NOLINEDRAWDASH_LEFT, "linestyle.nolinedrawdash.left", EscherPropertyMetaData.TYPE_BOOLEAN);
addProp(m, LINESTYLE__NOLINEDRAWDASH_TOP, "linestyle.nolinedrawdash.top", EscherPropertyMetaData.TYPE_BOOLEAN);
addProp(m, LINESTYLE__NOLINEDRAWDASH_BOTTOM, "linestyle.nolinedrawdash.bottom", EscherPropertyMetaData.TYPE_BOOLEAN);
addProp(m, LINESTYLE__NOLINEDRAWDASH_RIGHT, "linestyle.nolinedrawdash.right", EscherPropertyMetaData.TYPE_BOOLEAN);
addProp(m, SHADOWSTYLE__TYPE, "shadowstyle.type"); addProp(m, SHADOWSTYLE__TYPE, "shadowstyle.type");
addProp(m, SHADOWSTYLE__COLOR, "shadowstyle.color", EscherPropertyMetaData.TYPE_RGB); addProp(m, SHADOWSTYLE__COLOR, "shadowstyle.color", EscherPropertyMetaData.TYPE_RGB);
addProp(m, SHADOWSTYLE__HIGHLIGHT, "shadowstyle.highlight"); addProp(m, SHADOWSTYLE__HIGHLIGHT, "shadowstyle.highlight");

View File

@ -0,0 +1,142 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.sl.draw;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
* For now this class renders only images supported by the javax.imageio.ImageIO framework.
**/
public class BitmapImageRenderer implements ImageRenderer {
private final static POILogger LOG = POILogFactory.getLogger(ImageRenderer.class);
protected BufferedImage img;
@Override
public void loadImage(InputStream data, String contentType) throws IOException {
img = convertBufferedImage(ImageIO.read(data), contentType);
}
@Override
public void loadImage(byte data[], String contentType) throws IOException {
img = convertBufferedImage(ImageIO.read(new ByteArrayInputStream(data)), contentType);
}
/**
* Add alpha channel to buffered image
*/
private static BufferedImage convertBufferedImage(BufferedImage img, String contentType) {
if (img == null) {
LOG.log(POILogger.WARN, "Content-type: "+contentType+" is not support. Image ignored.");
return null;
}
BufferedImage bi = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
return bi;
}
/**
* @return the buffered image
*/
public BufferedImage getImage() {
return img;
}
@Override
public Dimension getDimension() {
return (img == null)
? new Dimension(0,0)
: new Dimension(img.getWidth(),img.getHeight());
}
@Override
public void setAlpha(double alpha) {
if (img == null) return;
Dimension dim = getDimension();
BufferedImage newImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImg.createGraphics();
RescaleOp op = new RescaleOp(new float[]{1.0f, 1.0f, 1.0f, (float)alpha}, new float[]{0,0,0,0}, null);
g.drawImage(img, op, 0, 0);
g.dispose();
img = newImg;
}
@Override
public boolean drawImage(
Graphics2D graphics,
Rectangle2D anchor) {
return drawImage(graphics, anchor, null);
}
@Override
public boolean drawImage(
Graphics2D graphics,
Rectangle2D anchor,
Insets clip) {
if (img == null) return false;
boolean isClipped = true;
if (clip == null) {
isClipped = false;
clip = new Insets(0,0,0,0);
}
int iw = img.getWidth();
int ih = img.getHeight();
double cw = (100000-clip.left-clip.right) / 100000.0;
double ch = (100000-clip.top-clip.bottom) / 100000.0;
double sx = anchor.getWidth()/(iw*cw);
double sy = anchor.getHeight()/(ih*ch);
double tx = anchor.getX()-(iw*sx*clip.left/100000.0);
double ty = anchor.getY()-(ih*sy*clip.top/100000.0);
AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
Shape clipOld = graphics.getClip();
if (isClipped) graphics.clip(anchor.getBounds2D());
graphics.drawRenderedImage(img, at);
graphics.setClip(clipOld);
return true;
}
}

View File

@ -26,6 +26,7 @@ import java.text.AttributedString;
import org.apache.poi.sl.usermodel.Background; import org.apache.poi.sl.usermodel.Background;
import org.apache.poi.sl.usermodel.ConnectorShape; import org.apache.poi.sl.usermodel.ConnectorShape;
import org.apache.poi.sl.usermodel.FreeformShape; import org.apache.poi.sl.usermodel.FreeformShape;
import org.apache.poi.sl.usermodel.GraphicalFrame;
import org.apache.poi.sl.usermodel.GroupShape; import org.apache.poi.sl.usermodel.GroupShape;
import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.MasterSheet;
import org.apache.poi.sl.usermodel.PictureShape; import org.apache.poi.sl.usermodel.PictureShape;
@ -87,6 +88,8 @@ public class DrawFactory {
return getDrawable((GroupShape<?,?>)shape); return getDrawable((GroupShape<?,?>)shape);
} else if (shape instanceof PictureShape) { } else if (shape instanceof PictureShape) {
return getDrawable((PictureShape<?,?>)shape); return getDrawable((PictureShape<?,?>)shape);
} else if (shape instanceof GraphicalFrame) {
return getDrawable((GraphicalFrame<?,?>)shape);
} else if (shape instanceof Background) { } else if (shape instanceof Background) {
return getDrawable((Background<?,?>)shape); return getDrawable((Background<?,?>)shape);
} else if (shape instanceof ConnectorShape) { } else if (shape instanceof ConnectorShape) {
@ -144,6 +147,10 @@ public class DrawFactory {
return new DrawPictureShape(shape); return new DrawPictureShape(shape);
} }
public DrawGraphicalFrame getDrawable(GraphicalFrame<?,?> shape) {
return new DrawGraphicalFrame(shape);
}
public DrawTextParagraph getDrawable(TextParagraph<?,?,?> paragraph) { public DrawTextParagraph getDrawable(TextParagraph<?,?,?> paragraph) {
return new DrawTextParagraph(paragraph); return new DrawTextParagraph(paragraph);
} }

View File

@ -0,0 +1,40 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.sl.draw;
import java.awt.Graphics2D;
import org.apache.poi.sl.usermodel.GraphicalFrame;
import org.apache.poi.sl.usermodel.PictureShape;
public class DrawGraphicalFrame extends DrawShape {
public DrawGraphicalFrame(GraphicalFrame<?,?> shape) {
super(shape);
}
public void draw(Graphics2D context) {
PictureShape<?,?> ps = ((GraphicalFrame<?,?>)getShape()).getFallbackPicture();
if (ps == null) {
return;
}
DrawPictureShape dps = DrawFactory.getInstance(context).getDrawable(ps);
dps.draw(context);
}
}

View File

@ -130,8 +130,7 @@ public class DrawPaint {
if (is == null) return null; if (is == null) return null;
assert(graphics != null); assert(graphics != null);
ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER); ImageRenderer renderer = DrawPictureShape.getImageRenderer(graphics, fill.getContentType());
if (renderer == null) renderer = new ImageRenderer();
try { try {
renderer.loadImage(is, fill.getContentType()); renderer.loadImage(is, fill.getContentType());

View File

@ -24,11 +24,17 @@ import java.awt.geom.Rectangle2D;
import java.io.IOException; import java.io.IOException;
import org.apache.poi.sl.usermodel.PictureData; import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.sl.usermodel.PictureShape; import org.apache.poi.sl.usermodel.PictureShape;
import org.apache.poi.sl.usermodel.RectAlign; import org.apache.poi.sl.usermodel.RectAlign;
public class DrawPictureShape extends DrawSimpleShape { public class DrawPictureShape extends DrawSimpleShape {
private static final POILogger LOG = POILogFactory.getLogger(DrawPictureShape.class);
private static final String WMF_IMAGE_RENDERER = "org.apache.poi.hwmf.draw.HwmfSLImageRenderer";
public DrawPictureShape(PictureShape<?,?> shape) { public DrawPictureShape(PictureShape<?,?> shape) {
super(shape); super(shape);
} }
@ -38,14 +44,11 @@ public class DrawPictureShape extends DrawSimpleShape {
PictureData data = getShape().getPictureData(); PictureData data = getShape().getPictureData();
if(data == null) return; if(data == null) return;
ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
if (renderer == null) renderer = new ImageRenderer();
Rectangle2D anchor = getAnchor(graphics, getShape()); Rectangle2D anchor = getAnchor(graphics, getShape());
Insets insets = getShape().getClipping(); Insets insets = getShape().getClipping();
try { try {
ImageRenderer renderer = getImageRenderer(graphics, data.getContentType());
renderer.loadImage(data.getData(), data.getContentType()); renderer.loadImage(data.getData(), data.getContentType());
renderer.drawImage(graphics, anchor, insets); renderer.drawImage(graphics, anchor, insets);
} catch (IOException e) { } catch (IOException e) {
@ -54,6 +57,34 @@ public class DrawPictureShape extends DrawSimpleShape {
} }
} }
/**
* Returns an ImageRenderer for the PictureData
*
* @param graphics
* @return
*/
public static ImageRenderer getImageRenderer(Graphics2D graphics, String contentType) {
ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
if (renderer != null) {
return renderer;
}
if (PictureType.WMF.contentType.equals(contentType)) {
try {
@SuppressWarnings("unchecked")
Class<? extends ImageRenderer> irc = (Class<? extends ImageRenderer>)
Thread.currentThread().getContextClassLoader().loadClass(WMF_IMAGE_RENDERER);
return irc.newInstance();
} catch (Exception e) {
// WMF image renderer is not on the classpath, continuing with BitmapRenderer
// although this doesn't make much sense ...
LOG.log(POILogger.ERROR, "WMF image renderer is not on the classpath - include poi-scratchpad jar!", e);
}
}
return new BitmapImageRenderer();
}
@Override @Override
protected PictureShape<?,?> getShape() { protected PictureShape<?,?> getShape() {
return (PictureShape<?,?>)shape; return (PictureShape<?,?>)shape;

View File

@ -98,6 +98,7 @@ public class DrawSimpleShape extends DrawShape {
// then stroke the shape outline // then stroke the shape outline
if(line != null) { if(line != null) {
graphics.setPaint(line); graphics.setPaint(line);
graphics.setStroke(stroke);
for(Outline o : elems){ for(Outline o : elems){
if(o.getPath().isStroked()){ if(o.getPath().isStroked()){
java.awt.Shape s = o.getOutline(); java.awt.Shape s = o.getOutline();

View File

@ -31,13 +31,12 @@ import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* For now this class renders only images supported by the javax.imageio.ImageIO * Classes can implement this interfaces to support other formats, for
* framework. Subclasses can override this class to support other formats, for
* example, use Apache Batik to render WMF, PICT can be rendered using Apple QuickTime API for Java: * example, use Apache Batik to render WMF, PICT can be rendered using Apple QuickTime API for Java:
* *
* <pre> * <pre>
* <code> * <code>
* public class MyImageRendener extends ImageRendener { * public class MyImageRendener implements ImageRendener {
* InputStream data; * InputStream data;
* *
* public boolean drawImage(Graphics2D graphics,Rectangle2D anchor,Insets clip) { * public boolean drawImage(Graphics2D graphics,Rectangle2D anchor,Insets clip) {
@ -79,127 +78,49 @@ import org.apache.poi.util.POILogger;
* </code> * </code>
* </pre> * </pre>
*/ */
public class ImageRenderer { public interface ImageRenderer {
private final static POILogger LOG = POILogFactory.getLogger(ImageRenderer.class);
protected BufferedImage img;
/** /**
* Load and buffer the image * Load and buffer the image
* *
* @param data the raw image stream * @param data the raw image stream
* @param contentType the content type * @param contentType the content type
*/ */
public void loadImage(InputStream data, String contentType) throws IOException { void loadImage(InputStream data, String contentType) throws IOException;
img = convertBufferedImage(ImageIO.read(data), contentType);
}
/** /**
* Load and buffer the image * Load and buffer the image
* *
* @param data the raw image stream * @param data the raw image bytes
* @param contentType the content type * @param contentType the content type
*/ */
public void loadImage(byte data[], String contentType) throws IOException { void loadImage(byte data[], String contentType) throws IOException;
img = convertBufferedImage(ImageIO.read(new ByteArrayInputStream(data)), contentType);
}
/**
* Add alpha channel to buffered image
*/
private static BufferedImage convertBufferedImage(BufferedImage img, String contentType) {
if (img == null) {
LOG.log(POILogger.WARN, "Content-type: "+contentType+" is not support. Image ignored.");
return null;
}
BufferedImage bi = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, null);
g.dispose();
return bi;
}
/**
* @return the buffered image
*/
public BufferedImage getImage() {
return img;
}
/** /**
* @return the dimension of the buffered image * @return the dimension of the buffered image
*/ */
public Dimension getDimension() { Dimension getDimension();
return (img == null)
? new Dimension(0,0)
: new Dimension(img.getWidth(),img.getHeight());
}
/** /**
* @param alpha the alpha [0..1] to be added to the image (possibly already containing an alpha channel) * @param alpha the alpha [0..1] to be added to the image (possibly already containing an alpha channel)
*/ */
public void setAlpha(double alpha) { void setAlpha(double alpha);
if (img == null) return;
Dimension dim = getDimension();
BufferedImage newImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImg.createGraphics();
RescaleOp op = new RescaleOp(new float[]{1.0f, 1.0f, 1.0f, (float)alpha}, new float[]{0,0,0,0}, null);
g.drawImage(img, op, 0, 0);
g.dispose();
img = newImg;
}
/**
* @return the image as buffered image
*/
BufferedImage getImage();
/**
* Render picture data into the supplied graphics
*
* @return true if the picture data was successfully rendered
*/
boolean drawImage(Graphics2D graphics, Rectangle2D anchor);
/** /**
* Render picture data into the supplied graphics * Render picture data into the supplied graphics
* *
* @return true if the picture data was successfully rendered * @return true if the picture data was successfully rendered
*/ */
public boolean drawImage( boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip);
Graphics2D graphics, }
Rectangle2D anchor) {
return drawImage(graphics, anchor, null);
}
/**
* Render picture data into the supplied graphics
*
* @return true if the picture data was successfully rendered
*/
public boolean drawImage(
Graphics2D graphics,
Rectangle2D anchor,
Insets clip) {
if (img == null) return false;
boolean isClipped = true;
if (clip == null) {
isClipped = false;
clip = new Insets(0,0,0,0);
}
int iw = img.getWidth();
int ih = img.getHeight();
double cw = (100000-clip.left-clip.right) / 100000.0;
double ch = (100000-clip.top-clip.bottom) / 100000.0;
double sx = anchor.getWidth()/(iw*cw);
double sy = anchor.getHeight()/(ih*ch);
double tx = anchor.getX()-(iw*sx*clip.left/100000.0);
double ty = anchor.getY()-(ih*sy*clip.top/100000.0);
AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
Shape clipOld = graphics.getClip();
if (isClipped) graphics.clip(anchor.getBounds2D());
graphics.drawRenderedImage(img, at);
graphics.setClip(clipOld);
return true;
}
}

View File

@ -0,0 +1,29 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.sl.usermodel;
public interface GraphicalFrame<
S extends Shape<S,P>,
P extends TextParagraph<S,P,?>
> extends Shape<S,P>, PlaceableShape<S,P> {
/**
* @return a fallback representation as picture shape
*/
PictureShape<S,P> getFallbackPicture();
}

View File

@ -27,25 +27,26 @@ import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.sl.draw.DrawNotImplemented; import org.apache.poi.sl.usermodel.GraphicalFrame;
import org.apache.poi.sl.usermodel.PlaceableShape;
import org.apache.poi.sl.usermodel.ShapeType; import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.util.Beta; import org.apache.poi.util.Beta;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.Units; import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame;
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape;
/**
* @author Yegor Kozlov
*/
@Beta @Beta
@DrawNotImplemented public class XSLFGraphicFrame extends XSLFShape implements GraphicalFrame<XSLFShape, XSLFTextParagraph> {
public class XSLFGraphicFrame extends XSLFShape implements PlaceableShape<XSLFShape, XSLFTextParagraph> { private static final POILogger LOG = POILogFactory.getLogger(XSLFGraphicFrame.class);
/*package*/ XSLFGraphicFrame(CTGraphicalObjectFrame shape, XSLFSheet sheet){ /*package*/ XSLFGraphicFrame(CTGraphicalObjectFrame shape, XSLFSheet sheet){
super(shape,sheet); super(shape,sheet);
} }
@ -104,7 +105,7 @@ public class XSLFGraphicFrame extends XSLFShape implements PlaceableShape<XSLFSh
public void setRotation(double theta){ public void setRotation(double theta){
throw new IllegalArgumentException("Operation not supported"); throw new IllegalArgumentException("Operation not supported");
} }
/** /**
* Rotation angle in degrees * Rotation angle in degrees
* <p> * <p>
@ -125,7 +126,7 @@ public class XSLFGraphicFrame extends XSLFShape implements PlaceableShape<XSLFSh
public void setFlipVertical(boolean flip){ public void setFlipVertical(boolean flip){
throw new IllegalArgumentException("Operation not supported"); throw new IllegalArgumentException("Operation not supported");
} }
/** /**
* Whether the shape is horizontally flipped * Whether the shape is horizontally flipped
* *
@ -189,4 +190,30 @@ public class XSLFGraphicFrame extends XSLFShape implements PlaceableShape<XSLFSh
} }
} }
@Override
public XSLFPictureShape getFallbackPicture() {
String xquery =
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main'; "
+ "declare namespace mc='http://schemas.openxmlformats.org/markup-compatibility/2006' "
+ ".//mc:Fallback/*/p:pic"
;
XmlObject xo = selectProperty(XmlObject.class, xquery);
if (xo == null) {
return null;
}
CTGroupShape gs;
try {
gs = CTGroupShape.Factory.parse(xo.newDomNode());
} catch (XmlException e) {
LOG.log(POILogger.WARN, "Can't parse fallback picture stream of graphical frame", e);
return null;
}
if (gs.sizeOfPicArray() == 0) {
return null;
}
return new XSLFPictureShape(gs.getPicArray(0), getSheet());
}
} }

View File

@ -257,7 +257,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
public Placeholder getPlaceholder() { public Placeholder getPlaceholder() {
CTPlaceholder ph = getCTPlaceholder(); CTPlaceholder ph = getCTPlaceholder();
if (ph == null || !ph.isSetType()) { if (ph == null || !(ph.isSetType() || ph.isSetIdx())) {
return null; return null;
} }
return Placeholder.lookupOoxml(ph.getType().intValue()); return Placeholder.lookupOoxml(ph.getType().intValue());

View File

@ -37,6 +37,7 @@ public class TestPPTX2PNG {
POIDataSamples samples = POIDataSamples.getSlideShowInstance(); POIDataSamples samples = POIDataSamples.getSlideShowInstance();
String[] testFiles = {"alterman_security.ppt","alterman_security.pptx","KEY02.pptx","themes.pptx","backgrounds.pptx","layouts.pptx", "sample.pptx", "shapes.pptx",}; String[] testFiles = {"alterman_security.ppt","alterman_security.pptx","KEY02.pptx","themes.pptx","backgrounds.pptx","layouts.pptx", "sample.pptx", "shapes.pptx",};
// String[] testFiles = {"41246-2.ppt","45543.ppt","53446.ppt","ParagraphStylesShorterThanCharStyles.ppt"};
String[] args = { String[] args = {
"-format", "null", // png,gif,jpg or null for test "-format", "null", // png,gif,jpg or null for test
"-slide", "-1", // -1 for all "-slide", "-1", // -1 for all

View File

@ -100,7 +100,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
opt.setRecordId(EscherOptRecord.RECORD_ID); opt.setRecordId(EscherOptRecord.RECORD_ID);
_escherContainer.addChildRecord(opt); _escherContainer.addChildRecord(opt);
EscherRecord anchor; EscherRecord anchor;
if(isChild) { if(isChild) {
anchor = new EscherChildAnchorRecord(); anchor = new EscherChildAnchorRecord();
} else { } else {
@ -155,7 +155,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
} }
/** /**
* @return color of the line. If color is not set returns <code>java.awt.Color.black</code> * @return color of the line. If color is not set returns {@code null}
*/ */
public Color getLineColor(){ public Color getLineColor(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
@ -164,7 +164,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
if(p != null && (p.getPropertyValue() & 0x8) == 0) return null; if(p != null && (p.getPropertyValue() & 0x8) == 0) return null;
Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1); Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1);
return clr == null ? Color.black : clr; return clr == null ? null : clr;
} }
/** /**
@ -187,7 +187,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDCAPSTYLE, pen == LineCap.FLAT ? -1 : pen.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDCAPSTYLE, pen == LineCap.FLAT ? -1 : pen.nativeId);
} }
/** /**
* Gets line dashing. * Gets line dashing.
* *
@ -417,7 +417,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
}; };
} }
public DecorationShape getLineHeadDecoration(){ public DecorationShape getLineHeadDecoration(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD); EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD);
@ -428,7 +428,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD, decoShape == null ? -1 : decoShape.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
} }
public DecorationSize getLineHeadWidth(){ public DecorationSize getLineHeadWidth(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH); EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH);
@ -439,7 +439,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
} }
public DecorationSize getLineHeadLength(){ public DecorationSize getLineHeadLength(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH); EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH);
@ -450,7 +450,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
} }
public DecorationShape getLineTailDecoration(){ public DecorationShape getLineTailDecoration(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD); EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD);
@ -461,7 +461,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD, decoShape == null ? -1 : decoShape.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
} }
public DecorationSize getLineTailWidth(){ public DecorationSize getLineTailWidth(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH); EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH);
@ -472,7 +472,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
} }
public DecorationSize getLineTailLength(){ public DecorationSize getLineTailLength(){
AbstractEscherOptRecord opt = getEscherOptRecord(); AbstractEscherOptRecord opt = getEscherOptRecord();
EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH); EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH);
@ -484,8 +484,8 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId); setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
} }
public LineDecoration getLineDecoration() { public LineDecoration getLineDecoration() {
return new LineDecoration() { return new LineDecoration() {
@ -514,7 +514,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
} }
}; };
} }
@Override @Override
public Placeholder getPlaceholder() { public Placeholder getPlaceholder() {
List<? extends Record> clRecords = getClientRecords(); List<? extends Record> clRecords = getClientRecords();
@ -530,10 +530,10 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
return Placeholder.lookupNative(rtp.getPlaceholderId()); return Placeholder.lookupNative(rtp.getPlaceholderId());
} }
} }
return null; return null;
} }
@Override @Override
public void setPlaceholder(Placeholder placeholder) { public void setPlaceholder(Placeholder placeholder) {
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID);
@ -547,7 +547,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
// Placeholders can't be grouped // Placeholders can't be grouped
setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, (placeholder == null ? -1 : 262144)); setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, (placeholder == null ? -1 : 262144));
HSLFEscherClientDataRecord clientData = getClientData(false); HSLFEscherClientDataRecord clientData = getClientData(false);
if (placeholder == null) { if (placeholder == null) {
if (clientData != null) { if (clientData != null) {
@ -560,7 +560,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
} }
return; return;
} }
if (clientData == null) { if (clientData == null) {
clientData = getClientData(true); clientData = getClientData(true);
} }
@ -596,11 +596,11 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
} else { } else {
phId = (byte)placeholder.nativeSlideId; phId = (byte)placeholder.nativeSlideId;
} }
if (phId == -2) { if (phId == -2) {
throw new HSLFException("Placeholder "+placeholder.name()+" not supported for this sheet type ("+sheet.getClass()+")"); throw new HSLFException("Placeholder "+placeholder.name()+" not supported for this sheet type ("+sheet.getClass()+")");
} }
switch (placeholder) { switch (placeholder) {
case HEADER: case HEADER:
case FOOTER: case FOOTER:
@ -637,7 +637,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H
setLineColor(null); setLineColor(null);
return; return;
} }
// TODO: handle PaintStyle // TODO: handle PaintStyle
for (Object st : styles) { for (Object st : styles) {
if (st instanceof Number) { if (st instanceof Number) {

View File

@ -270,7 +270,6 @@ public class HwmfGraphics {
* This methods gathers and sets the corresponding graphics transformations. * This methods gathers and sets the corresponding graphics transformations.
*/ */
public void updateWindowMapMode() { public void updateWindowMapMode() {
GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration();
Rectangle2D win = prop.getWindow(); Rectangle2D win = prop.getWindow();
HwmfMapMode mapMode = prop.getMapMode(); HwmfMapMode mapMode = prop.getMapMode();
graphicsCtx.setTransform(initialAT); graphicsCtx.setTransform(initialAT);
@ -292,12 +291,14 @@ public class HwmfGraphics {
case MM_HIMETRIC: case MM_HIMETRIC:
case MM_LOENGLISH: case MM_LOENGLISH:
case MM_HIENGLISH: case MM_HIENGLISH:
case MM_TWIPS: case MM_TWIPS: {
// TODO: to be validated ... // TODO: to be validated ...
GraphicsConfiguration gc = graphicsCtx.getDeviceConfiguration();
graphicsCtx.transform(gc.getNormalizingTransform()); graphicsCtx.transform(gc.getNormalizingTransform());
graphicsCtx.scale(1./mapMode.scale, -1./mapMode.scale); graphicsCtx.scale(1./mapMode.scale, -1./mapMode.scale);
graphicsCtx.translate(-win.getX(), -win.getY()); graphicsCtx.translate(-win.getX(), -win.getY());
break; break;
}
case MM_TEXT: case MM_TEXT:
// TODO: to be validated ... // TODO: to be validated ...
break; break;

View File

@ -0,0 +1,115 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hwmf.draw;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.hwmf.usermodel.HwmfPicture;
import org.apache.poi.sl.draw.ImageRenderer;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.util.Units;
public class HwmfSLImageRenderer implements ImageRenderer {
HwmfPicture image = null;
double alpha = 0;
@Override
public void loadImage(InputStream data, String contentType) throws IOException {
if (!PictureData.PictureType.WMF.contentType.equals(contentType)) {
throw new IOException("Invalid picture type");
}
image = new HwmfPicture(data);
}
@Override
public void loadImage(byte[] data, String contentType) throws IOException {
if (!PictureData.PictureType.WMF.contentType.equals(contentType)) {
throw new IOException("Invalid picture type");
}
image = new HwmfPicture(new ByteArrayInputStream(data));
}
@Override
public Dimension getDimension() {
int width = 0, height = 0;
if (image != null) {
Dimension dim = image.getSize();
width = Units.pointsToPixel(dim.getWidth());
// keep aspect ratio for height
height = Units.pointsToPixel(dim.getHeight());
}
return new Dimension(width, height);
}
@Override
public void setAlpha(double alpha) {
this.alpha = alpha;
}
@Override
public BufferedImage getImage() {
if (image == null) {
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
}
Dimension dim = getDimension();
BufferedImage bufImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bufImg.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
image.draw(g);
g.dispose();
if (alpha != 0) {
BufferedImage newImg = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
g = newImg.createGraphics();
RescaleOp op = new RescaleOp(new float[]{1.0f, 1.0f, 1.0f, (float)alpha}, new float[]{0,0,0,0}, null);
g.drawImage(bufImg, op, 0, 0);
g.dispose();
bufImg = newImg;
}
return bufImg;
}
@Override
public boolean drawImage(Graphics2D graphics, Rectangle2D anchor) {
return drawImage(graphics, anchor, null);
}
@Override
public boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip) {
if (image == null) {
return false;
} else {
image.draw(graphics, anchor);
return true;
}
}
}

View File

@ -339,7 +339,7 @@ public class HwmfMisc {
* The META_DIBCREATEPATTERNBRUSH record creates a Brush Object with a * The META_DIBCREATEPATTERNBRUSH record creates a Brush Object with a
* pattern specified by a DeviceIndependentBitmap (DIB) Object * pattern specified by a DeviceIndependentBitmap (DIB) Object
*/ */
public static class WmfDibCreatePatternBrush implements HwmfRecord, HwmfImageRecord { public static class WmfDibCreatePatternBrush implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
private HwmfBrushStyle style; private HwmfBrushStyle style;
@ -388,6 +388,11 @@ public class HwmfMisc {
@Override @Override
public void draw(HwmfGraphics ctx) { public void draw(HwmfGraphics ctx) {
ctx.addObjectTableEntry(this);
}
@Override
public void applyObject(HwmfGraphics ctx) {
HwmfDrawProperties prop = ctx.getProperties(); HwmfDrawProperties prop = ctx.getProperties();
prop.setBrushStyle(style); prop.setBrushStyle(style);
prop.setBrushBitmap(getImage()); prop.setBrushBitmap(getImage());

View File

@ -102,11 +102,11 @@ public class HwmfPicture {
try { try {
Rectangle2D wmfBounds = getBounds(); Rectangle2D wmfBounds = getBounds();
// scale output bounds to image bounds // scale output bounds to image bounds
ctx.translate(graphicsBounds.getX(), graphicsBounds.getY());
ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight()); ctx.scale(graphicsBounds.getWidth()/wmfBounds.getWidth(), graphicsBounds.getHeight()/wmfBounds.getHeight());
ctx.translate(-wmfBounds.getX(), -wmfBounds.getY());
HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds); HwmfGraphics g = new HwmfGraphics(ctx, wmfBounds);
for (HwmfRecord r : records) { for (HwmfRecord r : records) {
r.draw(g); r.draw(g);
} }
} finally { } finally {