more progress with PPTX2PNG

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1188091 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-10-24 11:07:59 +00:00
parent 6c680035c2
commit 1aa02673c5
8 changed files with 316 additions and 88 deletions

View File

@ -33,19 +33,17 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector;
import org.openxmlformats.schemas.presentationml.x2006.main.CTConnectorNonVisual;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.geom.*;
/**
*
* Specifies a connection shape.
* Specifies a connection shape.
*
* @author Yegor Kozlov
*/
@Beta
public class XSLFConnectorShape extends XSLFSimpleShape {
/*package*/ XSLFConnectorShape(CTConnector shape, XSLFSheet sheet){
/*package*/ XSLFConnectorShape(CTConnector shape, XSLFSheet sheet) {
super(shape, sheet);
}
@ -71,19 +69,19 @@ public class XSLFConnectorShape extends XSLFSimpleShape {
/**
* Specifies the line end decoration, such as a triangle or arrowhead.
*/
public void setLineHeadDecoration(LineDecoration style){
public void setLineHeadDecoration(LineDecoration style) {
CTLineProperties ln = getSpPr().getLn();
CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();
if(style == null){
if(lnEnd.isSetType()) lnEnd.unsetType();
if (style == null) {
if (lnEnd.isSetType()) lnEnd.unsetType();
} else {
lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1));
}
}
public LineDecoration getLineHeadDecoration(){
public LineDecoration getLineHeadDecoration() {
CTLineProperties ln = getSpPr().getLn();
if(!ln.isSetHeadEnd()) return LineDecoration.NONE;
if (ln == null || !ln.isSetHeadEnd()) return LineDecoration.NONE;
STLineEndType.Enum end = ln.getHeadEnd().getType();
return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1];
@ -92,62 +90,62 @@ public class XSLFConnectorShape extends XSLFSimpleShape {
/**
* specifies decorations which can be added to the head of a line.
*/
public void setLineHeadWidth(LineEndWidth style){
public void setLineHeadWidth(LineEndWidth style) {
CTLineProperties ln = getSpPr().getLn();
CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();
if(style == null){
if(lnEnd.isSetW()) lnEnd.unsetW();
if (style == null) {
if (lnEnd.isSetW()) lnEnd.unsetW();
} else {
lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1));
}
}
public LineEndWidth getLineHeadWidth(){
public LineEndWidth getLineHeadWidth() {
CTLineProperties ln = getSpPr().getLn();
if(!ln.isSetHeadEnd()) return null;
if (ln == null || !ln.isSetHeadEnd()) return LineEndWidth.MEDIUM;
STLineEndWidth.Enum w = ln.getHeadEnd().getW();
return w == null ? null : LineEndWidth.values()[w.intValue() - 1];
return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1];
}
/**
* Specifies the line end width in relation to the line width.
*/
public void setLineHeadLength(LineEndLength style){
public void setLineHeadLength(LineEndLength style) {
CTLineProperties ln = getSpPr().getLn();
CTLineEndProperties lnEnd = ln.isSetHeadEnd() ? ln.getHeadEnd() : ln.addNewHeadEnd();
if(style == null){
if(lnEnd.isSetLen()) lnEnd.unsetLen();
if (style == null) {
if (lnEnd.isSetLen()) lnEnd.unsetLen();
} else {
lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1));
}
}
public LineEndLength getLineHeadLength(){
public LineEndLength getLineHeadLength() {
CTLineProperties ln = getSpPr().getLn();
if(!ln.isSetHeadEnd()) return null;
if (ln == null || !ln.isSetHeadEnd()) return LineEndLength.MEDIUM;
STLineEndLength.Enum len = ln.getHeadEnd().getLen();
return len == null ? null : LineEndLength.values()[len.intValue() - 1];
return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1];
}
/**
* Specifies the line end decoration, such as a triangle or arrowhead.
*/
public void setLineTailDecoration(LineDecoration style){
public void setLineTailDecoration(LineDecoration style) {
CTLineProperties ln = getSpPr().getLn();
CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();
if(style == null){
if(lnEnd.isSetType()) lnEnd.unsetType();
if (style == null) {
if (lnEnd.isSetType()) lnEnd.unsetType();
} else {
lnEnd.setType(STLineEndType.Enum.forInt(style.ordinal() + 1));
}
}
public LineDecoration getLineTailDecoration(){
public LineDecoration getLineTailDecoration() {
CTLineProperties ln = getSpPr().getLn();
if(!ln.isSetTailEnd()) return LineDecoration.NONE;
if (ln == null || !ln.isSetTailEnd()) return LineDecoration.NONE;
STLineEndType.Enum end = ln.getTailEnd().getType();
return end == null ? LineDecoration.NONE : LineDecoration.values()[end.intValue() - 1];
@ -156,76 +154,193 @@ public class XSLFConnectorShape extends XSLFSimpleShape {
/**
* specifies decorations which can be added to the tail of a line.
*/
public void setLineTailWidth(LineEndWidth style){
public void setLineTailWidth(LineEndWidth style) {
CTLineProperties ln = getSpPr().getLn();
CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();
if(style == null){
if(lnEnd.isSetW()) lnEnd.unsetW();
if (style == null) {
if (lnEnd.isSetW()) lnEnd.unsetW();
} else {
lnEnd.setW(STLineEndWidth.Enum.forInt(style.ordinal() + 1));
}
}
public LineEndWidth getLineTailWidth(){
public LineEndWidth getLineTailWidth() {
CTLineProperties ln = getSpPr().getLn();
if(!ln.isSetTailEnd()) return null;
if (ln == null || !ln.isSetTailEnd()) return LineEndWidth.MEDIUM;
STLineEndWidth.Enum w = ln.getTailEnd().getW();
return w == null ? null : LineEndWidth.values()[w.intValue() - 1];
return w == null ? LineEndWidth.MEDIUM : LineEndWidth.values()[w.intValue() - 1];
}
/**
* Specifies the line end width in relation to the line width.
*/
public void setLineTailLength(LineEndLength style){
public void setLineTailLength(LineEndLength style) {
CTLineProperties ln = getSpPr().getLn();
CTLineEndProperties lnEnd = ln.isSetTailEnd() ? ln.getTailEnd() : ln.addNewTailEnd();
if(style == null){
if(lnEnd.isSetLen()) lnEnd.unsetLen();
if (style == null) {
if (lnEnd.isSetLen()) lnEnd.unsetLen();
} else {
lnEnd.setLen(STLineEndLength.Enum.forInt(style.ordinal() + 1));
}
}
public LineEndLength getLineTailLength(){
public LineEndLength getLineTailLength() {
CTLineProperties ln = getSpPr().getLn();
if(!ln.isSetTailEnd()) return null;
if (ln == null || !ln.isSetTailEnd()) return LineEndLength.MEDIUM;
STLineEndLength.Enum len = ln.getTailEnd().getLen();
return len == null ? null : LineEndLength.values()[len.intValue() - 1];
return len == null ? LineEndLength.MEDIUM : LineEndLength.values()[len.intValue() - 1];
}
@Override
public void draw(Graphics2D graphics){
public void draw(Graphics2D graphics) {
java.awt.Shape outline = getOutline();
// shadow
XSLFShadow shadow = getShadow();
if(shadow != null) shadow.draw(graphics);
//border
Color lineColor = getLineColor();
if (lineColor != null){
if (lineColor != null) {
if (shadow != null) shadow.draw(graphics);
graphics.setColor(lineColor);
applyStroke(graphics);
graphics.draw(outline);
Shape tailDecoration = getTailDecoration();
if (tailDecoration != null) {
graphics.draw(tailDecoration);
}
Shape headDecoration = getHeadDecoration();
if (headDecoration != null) {
graphics.draw(headDecoration);
}
}
}
@Override
protected java.awt.Shape getOutline(){
protected java.awt.Shape getOutline() {
Rectangle2D anchor = getAnchor();
double x1 = anchor.getX(),
double x1 = anchor.getX(),
y1 = anchor.getY(),
x2 = anchor.getX() + anchor.getWidth(),
y2 = anchor.getY() + anchor.getHeight();
GeneralPath line = new GeneralPath();
line.moveTo((float)x1, (float)y1);
line.lineTo((float)x2, (float)y2);
return line;
return new Line2D.Double(x1, y1, x2, y2);
}
Shape getTailDecoration() {
LineEndLength tailLength = getLineTailLength();
LineEndWidth tailWidth = getLineTailWidth();
double lineWidth = getLineWidth();
Rectangle2D anchor = getAnchor();
double x2 = anchor.getX() + anchor.getWidth(),
y2 = anchor.getY() + anchor.getHeight();
double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
AffineTransform at = new AffineTransform();
Shape shape = null;
Rectangle2D bounds;
double scaleY = Math.pow(2, tailWidth.ordinal());
double scaleX = Math.pow(2, tailLength.ordinal());
switch (getLineHeadDecoration()) {
case OVAL:
shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
bounds = shape.getBounds2D();
at.translate(x2 - bounds.getWidth() / 2, y2 - bounds.getHeight() / 2);
at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
break;
case ARROW:
GeneralPath arrow = new GeneralPath();
arrow.moveTo(-lineWidth * 3, -lineWidth * 2);
arrow.lineTo(0, 0);
arrow.lineTo(-lineWidth * 3, lineWidth * 2);
shape = arrow;
at.translate(x2, y2);
at.rotate(alpha);
break;
case TRIANGLE:
scaleY = tailWidth.ordinal() + 1;
scaleX = tailLength.ordinal() + 1;
GeneralPath triangle = new GeneralPath();
triangle.moveTo(-lineWidth * scaleX, -lineWidth * scaleY/2);
triangle.lineTo(0, 0);
triangle.lineTo(-lineWidth * scaleX, lineWidth * scaleY/2);
triangle.closePath();
shape = triangle;
at.translate(x2, y2);
at.rotate(alpha);
break;
default:
break;
}
if (shape != null) {
shape = at.createTransformedShape(shape);
}
return shape;
}
Shape getHeadDecoration() {
LineEndLength headLength = getLineHeadLength();
LineEndWidth headWidth = getLineHeadWidth();
double lineWidth = getLineWidth();
Rectangle2D anchor = getAnchor();
double x1 = anchor.getX(),
y1 = anchor.getY();
double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
AffineTransform at = new AffineTransform();
Shape shape = null;
Rectangle2D bounds;
double scaleY = 1;
double scaleX = 1;
switch (getLineHeadDecoration()) {
case OVAL:
scaleY = Math.pow(2, headWidth.ordinal());
scaleX = Math.pow(2, headLength.ordinal());
shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
break;
case STEALTH:
case ARROW:
GeneralPath arrow = new GeneralPath();
arrow.moveTo(lineWidth * 3 * scaleX, -lineWidth * scaleY * 2);
arrow.lineTo(0, 0);
arrow.lineTo(lineWidth * 3 * scaleX, lineWidth * scaleY * 2);
shape = arrow;
at.translate(x1, y1);
at.rotate(alpha);
break;
case TRIANGLE:
scaleY = headWidth.ordinal() + 1;
scaleX = headLength.ordinal() + 1;
GeneralPath triangle = new GeneralPath();
triangle.moveTo(lineWidth * scaleX, -lineWidth * scaleY/2);
triangle.lineTo(0, 0);
triangle.lineTo(lineWidth * scaleX, lineWidth * scaleY/2);
triangle.closePath();
shape = triangle;
at.translate(x1, y1);
at.rotate(alpha);
break;
default:
break;
}
if (shape != null) {
shape = at.createTransformedShape(shape);
}
return shape;
}
}

View File

@ -0,0 +1,87 @@
/*
* ====================================================================
* 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.xslf.usermodel;
import org.apache.poi.util.Beta;
import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
/**
* For now this class renders only images supported by the javax.imageio.ImageIO
* framework. Subclasses can override this class to support other formats, for
* example, Use Apache batik to render WMF:
*
* <pre>
* <code>
* @Override
* public class MyImageRendener extends XSLFImageRendener{
* public boolean drawImage(Graphics2D graphics, XSLFPictureData data, Rectangle2D anchor){
* boolean ok = super.drawImage(graphics, data, anchor);
* if(!ok){
* // see what type of image we are
* String contentType = data.getPackagePart().getContentType();
* if(contentType.equals("image/wmf")){
* // use Apache Batik to handle WMF
* // see http://xmlgraphics.apache.org/batik/
* }
*
* }
* return ok;
* }
* }
* </code>
* </pre>
*
* and then pass this class to your instance of java.awt.Graphics2D:
*
* <pre>
* <code>
* graphics.setRenderingHint(XSLFRenderingHint.IMAGE_RENDERER, new MyImageRendener());
* </code>
* </pre>
*
* @author Yegor Kozlov
*/
@Beta
public class XSLFImageRendener {
/**
* Render picture data into the supplied graphics
*
* @return true if the picture data was succesfully renderered
*/
public boolean drawImage(Graphics2D graphics, XSLFPictureData data,
Rectangle2D anchor) {
try {
BufferedImage img = ImageIO.read(new ByteArrayInputStream(data
.getData()));
graphics.drawImage(img, (int) anchor.getX(), (int) anchor.getY(),
(int) anchor.getWidth(), (int) anchor.getHeight(), null);
return true;
} catch (Exception e) {
return false;
}
}
}

View File

@ -117,30 +117,26 @@ public class XSLFPictureShape extends XSLFSimpleShape {
public void draw(Graphics2D graphics){
java.awt.Shape outline = getOutline();
// shadow
XSLFShadow shadow = getShadow();
//fill
Color fillColor = getFillColor();
if (fillColor != null) {
graphics.setColor(fillColor);
if(shadow != null) shadow.draw(graphics);
graphics.setColor(fillColor);
applyFill(graphics);
graphics.fill(outline);
}
// text
XSLFPictureData data = getPictureData();
XSLFPictureData data = getPictureData();
if(data == null) return;
BufferedImage img;
try {
img = ImageIO.read(new ByteArrayInputStream(data.getData()));
}
catch (Exception e){
return;
}
Rectangle2D anchor = getAnchor();
graphics.drawImage(img, (int)anchor.getX(), (int)anchor.getY(),
(int)anchor.getWidth(), (int)anchor.getHeight(), null);
XSLFImageRendener renderer = (XSLFImageRendener)graphics.getRenderingHint(XSLFRenderingHint.IMAGE_RENDERER);
if(renderer == null) renderer = new XSLFImageRendener();
renderer.drawImage(graphics, data, getAnchor());
//border overlays the image
Color lineColor = getLineColor();

View File

@ -38,5 +38,5 @@ public class XSLFRenderingHint extends RenderingHints.Key {
public static final XSLFRenderingHint GSAVE = new XSLFRenderingHint(1);
public static final XSLFRenderingHint GRESTORE = new XSLFRenderingHint(2);
public static final XSLFRenderingHint SKIP_PLACEHOLDERS = new XSLFRenderingHint(3);
public static final XSLFRenderingHint IMAGE_RENDERER = new XSLFRenderingHint(3);
}

View File

@ -45,9 +45,13 @@ public class XSLFShadow extends XSLFSimpleShape {
_parent = parentShape;
}
@Override
public void draw(Graphics2D graphics) {
Shape outline = _parent.getOutline();
Color parentFillColor = _parent.getFillColor();
Color parentLineColor = _parent.getLineColor();
double angle = getAngle();
double dist = getDistance();
double dx = dist * Math.cos( Math.toRadians(angle));
@ -55,13 +59,19 @@ public class XSLFShadow extends XSLFSimpleShape {
graphics.translate(dx, dy);
//fill
Color fillColor = getFillColor();
if (fillColor != null) {
graphics.setColor(fillColor);
graphics.fill(outline);
}
if(parentFillColor != null) {
graphics.fill(outline);
}
if(parentLineColor != null) {
_parent.applyStroke(graphics);
graphics.draw(outline);
}
graphics.translate(-dx, -dy);
}
@ -75,21 +85,37 @@ public class XSLFShadow extends XSLFSimpleShape {
throw new IllegalStateException("You can't set anchor of a shadow");
}
/**
* @return the offset of this shadow in points
*/
public double getDistance(){
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
return ct.isSetDist() ? Units.toPoints(ct.getDist()) : 0;
}
/**
*
* @return the direction to offset the shadow in angles
*/
public double getAngle(){
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
return ct.isSetDir() ? (double)ct.getDir() / 60000 : 0;
}
/**
*
* @return the blur radius of the shadow
* TODO: figure out how to make sense of this property when rendering shadows
*/
public double getBlur(){
CTOuterShadowEffect ct = (CTOuterShadowEffect)getXmlObject();
return ct.isSetBlurRad() ? Units.toPoints(ct.getBlurRad()) : 0;
}
/**
* @return the color of this shadow.
* Depending whether the parent shape is filled or stroked, this color is used to fill or stroke this shadow
*/
@Override
public Color getFillColor() {
XSLFTheme theme = getSheet().getTheme();
@ -100,7 +126,10 @@ public class XSLFShadow extends XSLFSimpleShape {
else if (ct.isSetPrstClr()) {
return theme.getPresetColor(ct.getPrstClr());
}
else if (ct.isSetSrgbClr()) {
return theme.getSrgbColor(ct.getSrgbClr());
}
return Color.black;
return null;
}
}

View File

@ -279,14 +279,14 @@ public abstract class XSLFSimpleShape extends XSLFShape {
public Color getLineColor() {
final XSLFTheme theme = _sheet.getTheme();
final Color noline = new Color(0,0,0,0);
PropertyFetcher<Color> fetcher = new PropertyFetcher<Color>(){
public boolean fetch(XSLFSimpleShape shape){
CTShapeProperties spPr = shape.getSpPr();
CTLineProperties ln = spPr.getLn();
if (ln != null) {
if (ln.isSetNoFill()) {
setValue(null);
setValue(noline);
return true;
}
CTSolidColorFillProperties solidLine = ln.getSolidFill();
@ -311,7 +311,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
}
}
}
return color;
return color == noline ? null : color;
}
public void setLineWidth(double width) {
@ -480,12 +480,12 @@ public abstract class XSLFSimpleShape extends XSLFShape {
*/
public Color getFillColor() {
final XSLFTheme theme = _sheet.getTheme();
final Color nofill = new Color(0,0,0,0);
PropertyFetcher<Color> fetcher = new PropertyFetcher<Color>(){
public boolean fetch(XSLFSimpleShape shape){
CTShapeProperties spPr = shape.getSpPr();
if (spPr.isSetNoFill()) {
setValue(null);
setValue(nofill); // use it as 'nofill' value
return true;
}
if (spPr.isSetSolidFill()) {
@ -508,7 +508,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
}
}
}
return color;
return color == nofill ? null : color;
}
public XSLFShadow getShadow(){
@ -608,7 +608,7 @@ public abstract class XSLFSimpleShape extends XSLFShape {
int meter = BasicStroke.JOIN_ROUND;
Stroke stroke = new BasicStroke(lineWidth, cap, meter, 0.0f, dash,
Stroke stroke = new BasicStroke(lineWidth, cap, meter, Math.max(1, lineWidth), dash,
dash_phase);
graphics.setStroke(stroke);
}

View File

@ -387,18 +387,19 @@ public abstract class XSLFTextShape extends XSLFSimpleShape {
// shadow
XSLFShadow shadow = getShadow();
if(shadow != null) shadow.draw(graphics);
//fill
Color fillColor = getFillColor();
Color lineColor = getLineColor();
if(shadow != null) {
shadow.draw(graphics);
}
if (fillColor != null) {
graphics.setColor(fillColor);
graphics.setColor(fillColor);
applyFill(graphics);
graphics.fill(outline);
}
//border
Color lineColor = getLineColor();
if (lineColor != null){
graphics.setColor(lineColor);
applyStroke(graphics);

View File

@ -69,12 +69,12 @@ public class TestXSLFConnectorShape extends TestCase {
assertEquals(STLineEndType.ARROW, shape.getSpPr().getLn().getTailEnd().getType());
// line end width
assertEquals(null, shape.getLineHeadWidth());
assertEquals(null, shape.getLineTailWidth());
assertEquals(LineEndWidth.MEDIUM, shape.getLineHeadWidth());
assertEquals(LineEndWidth.MEDIUM, shape.getLineTailWidth());
shape.setLineHeadWidth(null);
shape.setLineHeadWidth(null);
assertEquals(null, shape.getLineHeadWidth());
assertEquals(null, shape.getLineTailWidth());
assertEquals(LineEndWidth.MEDIUM, shape.getLineHeadWidth());
assertEquals(LineEndWidth.MEDIUM, shape.getLineTailWidth());
assertFalse(shape.getSpPr().getLn().getHeadEnd().isSetW());
assertFalse(shape.getSpPr().getLn().getTailEnd().isSetW());
shape.setLineHeadWidth(LineEndWidth.LARGE);
@ -91,12 +91,12 @@ public class TestXSLFConnectorShape extends TestCase {
assertEquals(STLineEndWidth.LG, shape.getSpPr().getLn().getTailEnd().getW());
// line end length
assertEquals(null, shape.getLineHeadLength());
assertEquals(null, shape.getLineTailLength());
assertEquals(LineEndLength.MEDIUM, shape.getLineHeadLength());
assertEquals(LineEndLength.MEDIUM, shape.getLineTailLength());
shape.setLineHeadLength(null);
shape.setLineTailLength(null);
assertEquals(null, shape.getLineHeadLength());
assertEquals(null, shape.getLineTailLength());
assertEquals(LineEndLength.MEDIUM, shape.getLineHeadLength());
assertEquals(LineEndLength.MEDIUM, shape.getLineTailLength());
assertFalse(shape.getSpPr().getLn().getHeadEnd().isSetLen());
assertFalse(shape.getSpPr().getLn().getTailEnd().isSetLen());
shape.setLineHeadLength(LineEndLength.LARGE);