Bug 61119 - Fix preset shape rendering and shading

- Fixed conversion of ooxml to awt angle
- replace subclasses and reflection calls with enums
- implemented tinting/shading of preset shapes


git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1796823 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2017-05-30 09:33:28 +00:00
parent 94aeaa7f4a
commit 2eacea81c0
43 changed files with 20477 additions and 21356 deletions

View File

@ -33,6 +33,7 @@ import java.io.InputStream;
import org.apache.poi.sl.usermodel.ColorStyle;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint;
import org.apache.poi.sl.usermodel.PaintStyle.PaintModifier;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint;
import org.apache.poi.sl.usermodel.PlaceableShape;
@ -113,8 +114,15 @@ public class DrawPaint {
}
public Paint getPaint(Graphics2D graphics, PaintStyle paint) {
return getPaint(graphics, paint, PaintModifier.NORM);
}
public Paint getPaint(Graphics2D graphics, PaintStyle paint, PaintModifier modifier) {
if (modifier == PaintModifier.NONE) {
return null;
}
if (paint instanceof SolidPaint) {
return getSolidPaint((SolidPaint)paint, graphics);
return getSolidPaint((SolidPaint)paint, graphics, modifier);
} else if (paint instanceof GradientPaint) {
return getGradientPaint((GradientPaint)paint, graphics);
} else if (paint instanceof TexturePaint) {
@ -123,8 +131,77 @@ public class DrawPaint {
return null;
}
protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) {
return applyColorTransform(fill.getSolidColor());
protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics, final PaintModifier modifier) {
final ColorStyle orig = fill.getSolidColor();
ColorStyle cs = new ColorStyle() {
@Override
public Color getColor() {
return orig.getColor();
}
@Override
public int getAlpha() {
return orig.getAlpha();
}
@Override
public int getHueOff() {
return orig.getHueOff();
}
@Override
public int getHueMod() {
return orig.getHueMod();
}
@Override
public int getSatOff() {
return orig.getSatOff();
}
@Override
public int getSatMod() {
return orig.getSatMod();
}
@Override
public int getLumOff() {
return orig.getLumOff();
}
@Override
public int getLumMod() {
return orig.getLumMod();
}
@Override
public int getShade() {
int shade = orig.getShade();
switch (modifier) {
case DARKEN:
return Math.min(100000, Math.max(0,shade)+40000);
case DARKEN_LESS:
return Math.min(100000, Math.max(0,shade)+20000);
default:
return shade;
}
}
@Override
public int getTint() {
int tint = orig.getTint();
switch (modifier) {
case LIGHTEN:
return Math.min(100000, Math.max(0,tint)+40000);
case LIGHTEN_LESS:
return Math.min(100000, Math.max(0,tint)+20000);
default:
return tint;
}
}
};
return applyColorTransform(cs);
}
protected Paint getGradientPaint(GradientPaint fill, Graphics2D graphics) {
@ -272,9 +349,9 @@ public class DrawPaint {
return;
}
double fshade = shade / 100000.d;
double shadePct = shade / 100000.;
hsl[2] *= fshade;
hsl[2] *= 1. - shadePct;
}
/**
@ -289,16 +366,16 @@ public class DrawPaint {
return;
}
double ftint = tint / 100000.f;
hsl[2] = hsl[2] * ftint + (100 - ftint*100.);
// see 18.8.19 fgColor (Foreground Color)
double tintPct = tint / 100000.;
hsl[2] = hsl[2]*(1.-tintPct) + (100.-100.*(1.-tintPct));
}
protected Paint createLinearGradientPaint(GradientPaint fill, Graphics2D graphics) {
// TODO: we need to find the two points for gradient - the problem is, which point at the outline
// do you take? My solution would be to apply the gradient rotation to the shape in reverse
// and then scan the shape for the largest possible horizontal distance
double angle = fill.getGradientAngle();
if (!fill.isRotatedWithShape()) {
angle -= shape.getRotation();

View File

@ -48,7 +48,7 @@ public class DrawShape implements Drawable {
protected static boolean isHSLF(Shape<?,?> shape) {
return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");
}
/**
* Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
*
@ -184,16 +184,16 @@ public class DrawShape implements Drawable {
}
AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
if(tx != null) {
if(tx != null && !tx.isIdentity()) {
anchor = tx.createTransformedShape(anchor).getBounds2D();
}
return anchor;
}
protected Shape<?,?> getShape() {
return shape;
}
protected static BasicStroke getStroke(StrokeStyle strokeStyle) {
float lineWidth = (float) strokeStyle.getLineWidth();
if (lineWidth == 0.0f) {

View File

@ -57,7 +57,7 @@ import org.apache.poi.util.Units;
public class DrawSimpleShape extends DrawShape {
private static final double DECO_SIZE_POW = 1.5d;
public DrawSimpleShape(SimpleShape<?,?> shape) {
@ -79,12 +79,15 @@ public class DrawSimpleShape extends DrawShape {
// then fill the shape interior
if (fill != null) {
graphics.setPaint(fill);
for (Outline o : elems) {
if (o.getPath().isFilled()){
java.awt.Shape s = o.getOutline();
graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
graphics.fill(s);
Paint fillMod = drawPaint.getPaint(graphics, getShape().getFillStyle().getPaint(), o.getPath().getFill());
if (fillMod != null) {
graphics.setPaint(fillMod);
java.awt.Shape s = o.getOutline();
graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
graphics.fill(s);
}
}
}
}
@ -105,7 +108,7 @@ public class DrawSimpleShape extends DrawShape {
}
}
// draw line decorations
// draw line decorations
drawDecoration(graphics, line, stroke);
}
@ -378,7 +381,7 @@ public class DrawSimpleShape extends DrawShape {
presets.put(cusName, new CustomGeometry(cusGeom));
}
staxFiltRd.close();
staxReader.close();
} catch (Exception e) {
@ -392,43 +395,40 @@ public class DrawSimpleShape extends DrawShape {
}
protected Collection<Outline> computeOutlines(Graphics2D graphics) {
final SimpleShape<?,?> sh = getShape();
List<Outline> lst = new ArrayList<Outline>();
CustomGeometry geom = getShape().getGeometry();
CustomGeometry geom = sh.getGeometry();
if(geom == null) {
return lst;
}
Rectangle2D anchor = getAnchor(graphics, getShape());
Rectangle2D anchor = getAnchor(graphics, sh);
for (Path p : geom) {
double w = p.getW() == -1 ? anchor.getWidth() * Units.EMU_PER_POINT : p.getW();
double h = p.getH() == -1 ? anchor.getHeight() * Units.EMU_PER_POINT : p.getH();
double w = p.getW(), h = p.getH(), scaleX = Units.toPoints(1), scaleY = scaleX;
if (w == -1) {
w = Units.toEMU(anchor.getWidth());
} else {
scaleX = anchor.getWidth() / w;
}
if (h == -1) {
h = Units.toEMU(anchor.getHeight());
} else {
scaleY = anchor.getHeight() / h;
}
// the guides in the shape definitions are all defined relative to each other,
// so we build the path starting from (0,0).
final Rectangle2D pathAnchor = new Rectangle2D.Double(0,0,w,h);
Context ctx = new Context(geom, pathAnchor, getShape());
Context ctx = new Context(geom, pathAnchor, sh);
java.awt.Shape gp = p.getPath(ctx);
// translate the result to the canvas coordinates in points
AffineTransform at = new AffineTransform();
at.translate(anchor.getX(), anchor.getY());
double scaleX, scaleY;
if (p.getW() != -1) {
scaleX = anchor.getWidth() / p.getW();
} else {
scaleX = 1.0 / Units.EMU_PER_POINT;
}
if (p.getH() != -1) {
scaleY = anchor.getHeight() / p.getH();
} else {
scaleY = 1.0 / Units.EMU_PER_POINT;
}
at.scale(scaleX, scaleY);
java.awt.Shape canvasShape = at.createTransformedShape(gp);

View File

@ -1,41 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Absolute Value Formula
*
* @author Yegor Kozlov
*/
public class AbsExpression implements Expression {
private String arg;
AbsExpression(Matcher m){
arg = m.group(1);
}
public double evaluate(Context ctx){
double val = ctx.getValue(arg);
return Math.abs(val);
}
}

View File

@ -1,45 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Add Divide Formula
*
* @author Yegor Kozlov
*/
public class AddDivideExpression implements Expression {
private String arg1, arg2, arg3;
AddDivideExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return (x + y ) / z;
}
}

View File

@ -1,45 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Add Subtract Formula
*
* @author Yegor Kozlov
*/
public class AddSubtractExpression implements Expression {
private String arg1, arg2, arg3;
AddSubtractExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return (x + y ) - z;
}
}

View File

@ -23,8 +23,6 @@ import org.apache.poi.sl.draw.binding.CTGeomGuide;
/**
* Represents a shape adjust values (see section 20.1.9.5 in the spec)
*
* @author Yegor Kozlov
*/
public class AdjustValue extends Guide {
@ -36,10 +34,6 @@ public class AdjustValue extends Guide {
public double evaluate(Context ctx){
String name = getName();
Guide adj = ctx.getAdjustValue(name);
if(adj != null) {
return adj.evaluate(ctx);
}
return super.evaluate(ctx);
return (adj != null) ? adj.evaluate(ctx) : super.evaluate(ctx);
}
}

View File

@ -1,43 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
*/
public class ArcTanExpression implements Expression {
private String arg1, arg2;
ArcTanExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
return Math.atan(y / x);
}
}

View File

@ -19,6 +19,8 @@
package org.apache.poi.sl.draw.geom;
import static org.apache.poi.sl.draw.geom.Formula.OOXML_DEGREE;
import java.awt.geom.Arc2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
@ -27,42 +29,114 @@ import org.apache.poi.sl.draw.binding.CTPath2DArcTo;
/**
* ArcTo command within a shape path in DrawingML:
* {@code &lt;arcTo wR="wr" hR="hr" stAng="stAng" swAng="swAng"/&gt;}<p>
*
* <arcTo wR="wr" hR="hr" stAng="stAng" swAng="swAng"/>
*
* Where <code>wr</code> and <code>wh</code> are the height and width radiuses
* Where {@code wr} and {@code wh} are the height and width radiuses
* of the supposed circle being used to draw the arc. This gives the circle
* a total height of (2 * hR) and a total width of (2 * wR)
*
* stAng is the <code>start</code> angle and <code></>swAng</code> is the swing angle
*
* @author Yegor Kozlov
* stAng is the {@code start} angle and {@code swAng} is the swing angle
*/
public class ArcToCommand implements PathCommand {
private String hr, wr, stAng, swAng;
ArcToCommand(CTPath2DArcTo arc){
hr = arc.getHR().toString();
wr = arc.getWR().toString();
stAng = arc.getStAng().toString();
swAng = arc.getSwAng().toString();
hr = arc.getHR();
wr = arc.getWR();
stAng = arc.getStAng();
swAng = arc.getSwAng();
}
@Override
public void execute(Path2D.Double path, Context ctx){
double rx = ctx.getValue(wr);
double ry = ctx.getValue(hr);
double start = ctx.getValue(stAng) / 60000;
double extent = ctx.getValue(swAng) / 60000;
Point2D pt = path.getCurrentPoint();
double x0 = pt.getX() - rx - rx * Math.cos(Math.toRadians(start));
double y0 = pt.getY() - ry - ry * Math.sin(Math.toRadians(start));
double ooStart = ctx.getValue(stAng) / OOXML_DEGREE;
double ooExtent = ctx.getValue(swAng) / OOXML_DEGREE;
Arc2D arc = new Arc2D.Double(
x0,
y0,
2 * rx, 2 * ry,
-start, -extent,
Arc2D.OPEN);
// skew the angles for AWT output
double awtStart = convertOoxml2AwtAngle(ooStart, rx, ry);
double awtSweep = convertOoxml2AwtAngle(ooStart+ooExtent, rx, ry)-awtStart;
// calculate the inverse angle - taken from the (reversed) preset definition
double radStart = Math.toRadians(ooStart);
double invStart = Math.atan2(rx * Math.sin(radStart), ry * Math.cos(radStart));
Point2D pt = path.getCurrentPoint();
// calculate top/left corner
double x0 = pt.getX() - rx * Math.cos(invStart) - rx;
double y0 = pt.getY() - ry * Math.sin(invStart) - ry;
Arc2D arc = new Arc2D.Double(x0, y0, 2 * rx, 2 * ry, awtStart, awtSweep, Arc2D.OPEN);
path.append(arc, true);
}
/**
* Arc2D angles are skewed, OOXML aren't ... so we need to unskew them<p>
*
* Furthermore ooxml angle starts at the X-axis and increases clock-wise,
* where as Arc2D api states
* "45 degrees always falls on the line from the center of the ellipse to
* the upper right corner of the framing rectangle"
* so we need to reverse it
*
* <pre>
* AWT: OOXML:
* |90/-270 |270/-90 (16200000)
* | |
* +/-180-----------0 +/-180-----------0
* | (10800000) |
* |270/-90 |90/-270 (5400000)
* </pre>
*
* @param ooAngle the angle in OOXML units
* @param width the half width of the bounding box
* @param height the half height of the bounding box
*
* @return the angle in degrees
*
* @see <a href="http://www.onlinemathe.de/forum/Problem-bei-Winkelberechnungen-einer-Ellipse">unskew angle</a>
**/
private double convertOoxml2AwtAngle(double ooAngle, double width, double height) {
double aspect = (height / width);
// reverse angle for awt
double awtAngle = -ooAngle;
// normalize angle, in case it's < -360 or > 360 degrees
double awtAngle2 = awtAngle%360.;
double awtAngle3 = awtAngle-awtAngle2;
// because of tangens nature, the values left [90°-270°] and right [270°-90°] of the axis are mirrored/the same
// and the result of atan2 need to be justified
switch ((int)(awtAngle2 / 90)) {
case -3:
// -270 to -360
awtAngle3 -= 360;
awtAngle2 += 360;
break;
case -2:
case -1:
// -90 to -270
awtAngle3 -= 180;
awtAngle2 += 180;
break;
default:
case 0:
// -90 to 90
break;
case 2:
case 1:
// 90 to 270
awtAngle3 += 180;
awtAngle2 -= 180;
break;
case 3:
// 270 to 360
awtAngle3 += 360;
awtAngle2 -= 360;
break;
}
// skew
awtAngle = Math.toDegrees(Math.atan2(Math.tan(Math.toRadians(awtAngle2)), aspect)) + awtAngle3;
return awtAngle;
}
}

View File

@ -0,0 +1,153 @@
/*
* ====================================================================
* 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.geom;
import java.awt.geom.Rectangle2D;
/* package */ enum BuiltInGuide implements Formula {
_3cd4, _3cd8, _5cd8, _7cd8, _b, _cd2, _cd4, _cd8, _hc, _h, _hd2, _hd3, _hd4, _hd5, _hd6, _hd8,
_l, _ls, _r, _ss, _ssd2, _ssd4, _ssd6, _ssd8, _ssd16, _ssd32, _t, _vc,
_w, _wd2, _wd3, _wd4, _wd5, _wd6, _wd8, _wd10, _wd32;
public String getName() {
return name().substring(1);
}
@Override
public double evaluate(Context ctx) {
Rectangle2D anchor = ctx.getShapeAnchor();
double height = anchor.getHeight(), width = anchor.getWidth(), ss = Math.min(width, height);
switch (this) {
case _3cd4:
// 3 circles div 4: 3 x 360 / 4 = 270
return 270 * OOXML_DEGREE;
case _3cd8:
// 3 circles div 8: 3 x 360 / 8 = 135
return 135 * OOXML_DEGREE;
case _5cd8:
// 5 circles div 8: 5 x 360 / 8 = 225
return 225 * OOXML_DEGREE;
case _7cd8:
// 7 circles div 8: 7 x 360 / 8 = 315
return 315 * OOXML_DEGREE;
case _t:
// top
return anchor.getY();
case _b:
// bottom
return anchor.getMaxY();
case _l:
// left
return anchor.getX();
case _r:
// right
return anchor.getMaxX();
case _cd2:
// circle div 2: 360 / 2 = 180
return 180 * OOXML_DEGREE;
case _cd4:
// circle div 4: 360 / 4 = 90
return 90 * OOXML_DEGREE;
case _cd8:
// circle div 8: 360 / 8 = 45
return 45 * OOXML_DEGREE;
case _hc:
// horizontal center
return anchor.getCenterX();
case _h:
// height
return height;
case _hd2:
// height div 2
return height / 2.;
case _hd3:
// height div 3
return height / 3.;
case _hd4:
// height div 4
return height / 4.;
case _hd5:
// height div 5
return height / 5.;
case _hd6:
// height div 6
return height / 6.;
case _hd8:
// height div 8
return height / 8.;
case _ls:
// long side
return Math.max(width, height);
case _ss:
// short side
return ss;
case _ssd2:
// short side div 2
return ss / 2.;
case _ssd4:
// short side div 4
return ss / 4.;
case _ssd6:
// short side div 6
return ss / 6.;
case _ssd8:
// short side div 8
return ss / 8.;
case _ssd16:
// short side div 16
return ss / 16.;
case _ssd32:
// short side div 32
return ss / 32.;
case _vc:
// vertical center
return anchor.getCenterY();
case _w:
// width
return width;
case _wd2:
// width div 2
return width / 2.;
case _wd3:
// width div 3
return width / 3.;
case _wd4:
// width div 4
return width / 4.;
case _wd5:
// width div 5
return width / 5.;
case _wd6:
// width div 6
return width / 6.;
case _wd8:
// width div 8
return width / 8.;
case _wd10:
// width div 10
return width / 10.;
case _wd32:
// width div 32
return width / 32.;
default:
return 0;
}
}
}

View File

@ -21,16 +21,12 @@ package org.apache.poi.sl.draw.geom;
import java.awt.geom.Path2D;
/**
* Date: 10/25/11
*
* @author Yegor Kozlov
*/
public class ClosePathCommand implements PathCommand {
ClosePathCommand(){
}
@Override
public void execute(Path2D.Double path, Context ctx){
path.closePath();
}

View File

@ -23,11 +23,6 @@ import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
*/
public class Context {
final Map<String, Double> _ctx = new HashMap<String, Double>();
final IAdjustableShape _props;
@ -36,8 +31,12 @@ public class Context {
public Context(CustomGeometry geom, Rectangle2D anchor, IAdjustableShape props){
_props = props;
_anchor = anchor;
for(Guide gd : geom.adjusts) evaluate(gd);
for(Guide gd : geom.guides) evaluate(gd);
for(Guide gd : geom.adjusts) {
evaluate(gd);
}
for(Guide gd : geom.guides) {
evaluate(gd);
}
}
public Rectangle2D getShapeAnchor(){
@ -53,22 +52,19 @@ public class Context {
return Double.parseDouble(key);
}
Formula builtIn = Formula.builtInFormulas.get(key);
if(builtIn != null){
return builtIn.evaluate(this);
}
if(!_ctx.containsKey(key)) {
throw new RuntimeException("undefined variable: " + key);
}
return _ctx.get(key);
Double val = _ctx.get(key);
// BuiltInGuide throws IllegalArgumentException if key is not defined
return (val != null) ? val : evaluate(BuiltInGuide.valueOf("_"+key));
}
public double evaluate(Formula fmla){
double result = fmla.evaluate(this);
String key = fmla.getName();
if(key != null) _ctx.put(key, result);
if (fmla instanceof Guide) {
String key = ((Guide)fmla).getName();
if (key != null) {
_ctx.put(key, result);
}
}
return result;
}
}

View File

@ -1,43 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
*/
public class CosExpression implements Expression {
private String arg1, arg2;
CosExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2)/ 60000;
return x * Math.cos(Math.toRadians(y));
}
}

View File

@ -1,45 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
*/
public class CosineArcTanExpression implements Expression {
private String arg1, arg2, arg3;
CosineArcTanExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return x*Math.cos(Math.atan(z / y));
}
}

View File

@ -23,23 +23,19 @@ import java.awt.geom.Path2D;
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
/**
* Date: 10/25/11
*
* @author Yegor Kozlov
*/
public class CurveToCommand implements PathCommand {
private String arg1, arg2, arg3, arg4, arg5, arg6;
CurveToCommand(CTAdjPoint2D pt1, CTAdjPoint2D pt2, CTAdjPoint2D pt3){
arg1 = pt1.getX().toString();
arg2 = pt1.getY().toString();
arg3 = pt2.getX().toString();
arg4 = pt2.getY().toString();
arg5 = pt3.getX().toString();
arg6 = pt3.getY().toString();
arg1 = pt1.getX();
arg2 = pt1.getY();
arg3 = pt2.getX();
arg4 = pt2.getY();
arg5 = pt3.getX();
arg6 = pt3.getY();
}
@Override
public void execute(Path2D.Double path, Context ctx){
double x1 = ctx.getValue(arg1);
double y1 = ctx.getValue(arg2);

View File

@ -19,19 +19,24 @@
package org.apache.poi.sl.draw.geom;
import java.util.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.sl.draw.binding.*;
import org.apache.poi.sl.draw.binding.CTCustomGeometry2D;
import org.apache.poi.sl.draw.binding.CTGeomGuide;
import org.apache.poi.sl.draw.binding.CTGeomGuideList;
import org.apache.poi.sl.draw.binding.CTGeomRect;
import org.apache.poi.sl.draw.binding.CTPath2D;
import org.apache.poi.sl.draw.binding.CTPath2DList;
/**
* Definition of a custom geometric shape
*
* @author Yegor Kozlov
*/
public class CustomGeometry implements Iterable<Path>{
List<Guide> adjusts = new ArrayList<Guide>();
List<Guide> guides = new ArrayList<Guide>();
List<Path> paths = new ArrayList<Path>();
final List<Guide> adjusts = new ArrayList<Guide>();
final List<Guide> guides = new ArrayList<Guide>();
final List<Path> paths = new ArrayList<Path>();
Path textBounds;
public CustomGeometry(CTCustomGeometry2D geom) {
@ -59,24 +64,20 @@ public class CustomGeometry implements Iterable<Path>{
CTGeomRect rect = geom.getRect();
if(rect != null) {
textBounds = new Path();
textBounds.addCommand(
new MoveToCommand(rect.getL().toString(), rect.getT().toString()));
textBounds.addCommand(
new LineToCommand(rect.getR().toString(), rect.getT().toString()));
textBounds.addCommand(
new LineToCommand(rect.getR().toString(), rect.getB().toString()));
textBounds.addCommand(
new LineToCommand(rect.getL().toString(), rect.getB().toString()));
textBounds.addCommand(
new ClosePathCommand());
textBounds.addCommand(new MoveToCommand(rect.getL(), rect.getT()));
textBounds.addCommand(new LineToCommand(rect.getR(), rect.getT()));
textBounds.addCommand(new LineToCommand(rect.getR(), rect.getB()));
textBounds.addCommand(new LineToCommand(rect.getL(), rect.getB()));
textBounds.addCommand(new ClosePathCommand());
}
}
@Override
public Iterator<Path> iterator() {
return paths.iterator();
}
public Path getTextBounds(){
return textBounds;
return textBounds;
}
}

View File

@ -1,31 +0,0 @@
/*
* ====================================================================
* 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.geom;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
*/
public interface Expression {
double evaluate(Context ctx);
}

View File

@ -1,88 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A simple regexp-based parser of shape guide formulas in DrawingML
*/
public class ExpressionParser {
private static final Map<String, ExpressionEntry> impls =
new HashMap<String, ExpressionEntry>();
private static class ExpressionEntry {
final Pattern regex;
final Constructor<? extends Expression> con;
ExpressionEntry(String regex, Class<? extends Expression> cls)
throws SecurityException, NoSuchMethodException {
this.regex = Pattern.compile(regex);
this.con = cls.getDeclaredConstructor(Matcher.class);
impls.put(op(regex), this);
}
}
static {
try {
new ExpressionEntry("\\*/ +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", MultiplyDivideExpression.class);
new ExpressionEntry("\\+- +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)( 0)?", AddSubtractExpression.class);
new ExpressionEntry("\\+/ +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", AddDivideExpression.class);
new ExpressionEntry("\\?: +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", IfElseExpression.class);
new ExpressionEntry("val +([\\-\\w]+)", LiteralValueExpression.class);
new ExpressionEntry("abs +([\\-\\w]+)", AbsExpression.class);
new ExpressionEntry("sqrt +([\\-\\w]+)", SqrtExpression.class);
new ExpressionEntry("max +([\\-\\w]+) +([\\-\\w]+)", MaxExpression.class);
new ExpressionEntry("min +([\\-\\w]+) +([\\-\\w]+)", MinExpression.class);
new ExpressionEntry("at2 +([\\-\\w]+) +([\\-\\w]+)", ArcTanExpression.class);
new ExpressionEntry("sin +([\\-\\w]+) +([\\-\\w]+)", SinExpression.class);
new ExpressionEntry("cos +([\\-\\w]+) +([\\-\\w]+)", CosExpression.class);
new ExpressionEntry("tan +([\\-\\w]+) +([\\-\\w]+)", TanExpression.class);
new ExpressionEntry("cat2 +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", CosineArcTanExpression.class);
new ExpressionEntry("sat2 +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", SinArcTanExpression.class);
new ExpressionEntry("pin +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", PinExpression.class);
new ExpressionEntry("mod +([\\-\\w]+) +([\\-\\w]+) +([\\-\\w]+)", ModExpression.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String op(String str) {
return (str == null || !str.contains(" "))
? "" : str.substring(0, str.indexOf(" ")).replace("\\", "");
}
public static Expression parse(String str) {
ExpressionEntry ee = impls.get(op(str));
Matcher m = (ee == null) ? null : ee.regex.matcher(str);
if (m == null || !m.matches()) {
throw new RuntimeException("Unsupported formula: " + str);
}
try {
return ee.con.newInstance(m);
} catch (Exception e) {
throw new RuntimeException("Unsupported formula: " + str, e);
}
}
}

View File

@ -19,367 +19,16 @@
package org.apache.poi.sl.draw.geom;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
/**
* A guide formula in DrawingML.
* This is a base class for adjust values, geometric guides and bilt-in guides
* A guide formula in DrawingML.<p>
*
* @author Yegor Kozlov
* This is a base interface for adjust values, geometric guides and built-in guides
*/
public abstract class Formula {
String getName(){
return null;
}
abstract double evaluate(Context ctx);
static Map<String, Formula> builtInFormulas = new HashMap<String, Formula>();
static {
// 3 x 360 / 4 = 270
builtInFormulas.put("3cd4", new Formula(){
@Override
double evaluate(Context ctx){
return 270 * 60000.;
}
});
// 3 x 360 / 8 = 135
builtInFormulas.put("3cd8", new Formula(){
@Override
double evaluate(Context ctx){
return 135 * 60000.;
}
});
// 5 x 360 / 8 = 225
builtInFormulas.put("5cd8", new Formula(){
@Override
double evaluate(Context ctx){
return 270 * 60000.;
}
});
// 7 x 360 / 8 = 315
builtInFormulas.put("7cd8", new Formula(){
@Override
double evaluate(Context ctx){
return 270 * 60000.;
}
});
// bottom
builtInFormulas.put("b", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getY() + anchor.getHeight();
}
});
// 360 / 2 = 180
builtInFormulas.put("cd2", new Formula(){
@Override
double evaluate(Context ctx){
return 180 * 60000.;
}
});
// 360 / 4 = 90
builtInFormulas.put("cd4", new Formula(){
@Override
double evaluate(Context ctx){
return 90 * 60000.;
}
});
// 360 / 8 = 45
builtInFormulas.put("cd8", new Formula(){
@Override
double evaluate(Context ctx){
return 45 * 60000.;
}
});
// horizontal center
builtInFormulas.put("hc", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getX() + anchor.getWidth()/2.;
}
});
// height
builtInFormulas.put("h", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight();
}
});
// height / 2
builtInFormulas.put("hd2", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight()/2.;
}
});
// height / 3
builtInFormulas.put("hd3", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight()/3.;
}
});
// height / 4
builtInFormulas.put("hd4", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight()/4.;
}
});
// height / 5
builtInFormulas.put("hd5", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight()/5.;
}
});
// height / 6
builtInFormulas.put("hd6", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight()/6.;
}
});
// height / 8
builtInFormulas.put("hd8", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getHeight()/8.;
}
});
// left
builtInFormulas.put("l", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getX();
}
});
// long side
builtInFormulas.put("ls", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return Math.max(anchor.getWidth(), anchor.getHeight());
}
});
// right
builtInFormulas.put("r", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getX() + anchor.getWidth();
}
});
// short side
builtInFormulas.put("ss", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return Math.min(anchor.getWidth(), anchor.getHeight());
}
});
// short side / 2
builtInFormulas.put("ssd2", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
double ss = Math.min(anchor.getWidth(), anchor.getHeight());
return ss / 2.;
}
});
// short side / 4
builtInFormulas.put("ssd4", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
double ss = Math.min(anchor.getWidth(), anchor.getHeight());
return ss / 4.;
}
});
// short side / 6
builtInFormulas.put("ssd6", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
double ss = Math.min(anchor.getWidth(), anchor.getHeight());
return ss / 6.;
}
});
// short side / 8
builtInFormulas.put("ssd8", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
double ss = Math.min(anchor.getWidth(), anchor.getHeight());
return ss / 8.;
}
});
// short side / 16
builtInFormulas.put("ssd16", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
double ss = Math.min(anchor.getWidth(), anchor.getHeight());
return ss / 16.;
}
});
// short side / 32
builtInFormulas.put("ssd32", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
double ss = Math.min(anchor.getWidth(), anchor.getHeight());
return ss / 32.;
}
});
// top
builtInFormulas.put("t", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getY();
}
});
// vertical center
builtInFormulas.put("vc", new Formula(){
@Override
double evaluate(Context ctx){
Rectangle2D anchor = ctx.getShapeAnchor();
return anchor.getY() + anchor.getHeight()/2.;
}
});
// width
builtInFormulas.put("w", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth();
}
});
// width / 2
builtInFormulas.put("wd2", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/2.;
}
});
// width / 3
builtInFormulas.put("wd3", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/3.;
}
});
// width / 4
builtInFormulas.put("wd4", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/4.;
}
});
// width / 5
builtInFormulas.put("wd5", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/5.;
}
});
// width / 6
builtInFormulas.put("wd6", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/6.;
}
});
// width / 8
builtInFormulas.put("wd8", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/8.;
}
});
// width / 10
builtInFormulas.put("wd10", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/10.;
}
});
// width / 32
builtInFormulas.put("wd32", new Formula(){
@Override
double evaluate(Context ctx){
return ctx.getShapeAnchor().getWidth()/32.;
}
});
}
public interface Formula {
/**
* OOXML units are 60000ths of a degree
*/
double OOXML_DEGREE = 60000;
double evaluate(Context ctx);
}

View File

@ -19,16 +19,31 @@
package org.apache.poi.sl.draw.geom;
import static java.lang.Math.abs;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import static java.lang.Math.tan;
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;
import org.apache.poi.sl.draw.binding.CTGeomGuide;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
* A simple pattern parser of shape guide formulas in DrawingML
*/
public class Guide extends Formula {
private String name, fmla;
private Expression expr;
public class Guide implements Formula {
enum Op {
muldiv,addsub,adddiv,ifelse,val,abs,sqrt,max,min,at2,sin,cos,tan,cat2,sat2,pin,mod;
}
private final String name, fmla;
private final Op op;
private final String[] operands;
public Guide(CTGeomGuide gd) {
this(gd.getName(), gd.getFmla());
@ -37,11 +52,11 @@ public class Guide extends Formula {
public Guide(String nm, String fm){
name = nm;
fmla = fm;
expr = ExpressionParser.parse(fm);
operands = fm.split("\\s+");
op = Op.valueOf(operands[0].replace("*", "mul").replace("/", "div").replace("+", "add").replace("-", "sub").replace("?:", "ifelse"));
}
String getName(){
public String getName(){
return name;
}
@ -50,9 +65,73 @@ public class Guide extends Formula {
}
@Override
public double evaluate(Context ctx){
return expr.evaluate(ctx);
public double evaluate(Context ctx) {
double x = (operands.length > 1) ? ctx.getValue(operands[1]) : 0;
double y = (operands.length > 2) ? ctx.getValue(operands[2]) : 0;
double z = (operands.length > 3) ? ctx.getValue(operands[3]) : 0;
switch (op) {
case abs:
// Absolute Value Formula
return abs(x);
case adddiv:
// Add Divide Formula
return (x + y) / z;
case addsub:
// Add Subtract Formula
return (x + y) - z;
case at2:
// ArcTan Formula: "at2 x y" = arctan( y / z ) = value of this guide
return toDegrees(atan2(y, x)) * OOXML_DEGREE;
case cos:
// Cosine Formula: "cos x y" = (x * cos( y )) = value of this guide
return x * cos(toRadians(y / OOXML_DEGREE));
case cat2:
// Cosine ArcTan Formula: "cat2 x y z" = (x * cos(arctan(z / y) )) = value of this guide
return x*cos(atan2(z, y));
case ifelse:
// If Else Formula: "?: x y z" = if (x > 0), then y = value of this guide,
// else z = value of this guide
return x > 0 ? y : z;
case val:
// Literal Value Expression
return x;
case max:
// Maximum Value Formula
return max(x, y);
case min:
// Minimum Value Formula
return min(x, y);
case mod:
// Modulo Formula: "mod x y z" = sqrt(x^2 + b^2 + c^2) = value of this guide
return sqrt(x*x + y*y + z*z);
case muldiv:
// Multiply Divide Formula
return (x * y) / z;
case pin:
// Pin To Formula: "pin x y z" = if (y < x), then x = value of this guide
// else if (y > z), then z = value of this guide
// else y = value of this guide
if(y < x) {
return x;
} else if (y > z) {
return z;
} else {
return y;
}
case sat2:
// Sine ArcTan Formula: "sat2 x y z" = (x*sin(arctan(z / y))) = value of this guide
return x*sin(atan2(z, y));
case sin:
// Sine Formula: "sin x y" = (x * sin( y )) = value of this guide
return x * sin(toRadians(y / OOXML_DEGREE));
case sqrt:
// Square Root Formula: "sqrt x" = sqrt(x) = value of this guide
return sqrt(x);
case tan:
// Tangent Formula: "tan x y" = (x * tan( y )) = value of this guide
return x * tan(toRadians(y / OOXML_DEGREE));
default:
return 0;
}
}
}

View File

@ -21,11 +21,9 @@ package org.apache.poi.sl.draw.geom;
/**
* A bridge to the consumer application.
* A bridge to the consumer application.<p>
*
* To get a shape geometry one needs to pass shape bounds and adjust values.
*
* @author Yegor Kozlov
*/
public interface IAdjustableShape {
/**

View File

@ -1,50 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* If Else Formula:
* <p>
* Arguments: 3 (fmla="?: x y z")
* Usage: "?: x y z" = if (x > 0), then y = value of this guide,
* else z = value of this guide
* </p>
*
* @author Yegor Kozlov
*/
public class IfElseExpression implements Expression {
private String arg1, arg2, arg3;
IfElseExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return x > 0 ? y : z;
}
}

View File

@ -23,17 +23,12 @@ import java.awt.geom.Path2D;
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
/**
* Date: 10/25/11
*
* @author Yegor Kozlov
*/
public class LineToCommand implements PathCommand {
private String arg1, arg2;
LineToCommand(CTAdjPoint2D pt){
arg1 = pt.getX().toString();
arg2 = pt.getY().toString();
arg1 = pt.getX();
arg2 = pt.getY();
}
LineToCommand(String s1, String s2){
@ -41,6 +36,7 @@ public class LineToCommand implements PathCommand {
arg2 = s2;
}
@Override
public void execute(Path2D.Double path, Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);

View File

@ -1,40 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Date: 10/24/11
*
* @author Yegor Kozlov
*/
public class LiteralValueExpression implements Expression {
private String arg;
LiteralValueExpression(Matcher m){
arg = m.group(1);
}
public double evaluate(Context ctx){
return ctx.getValue(arg);
}
}

View File

@ -1,43 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Maximum Value Formula
*
* @author Yegor Kozlov
*/
public class MaxExpression implements Expression {
private String arg1, arg2;
MaxExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
return Math.max(x, y);
}
}

View File

@ -1,43 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Minimum Value Formula
*
* @author Yegor Kozlov
*/
public class MinExpression implements Expression {
private String arg1, arg2;
MinExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
return Math.min(x, y);
}
}

View File

@ -1,49 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Modulo Formula:
* <p>
* Arguments: 3 (fmla="mod x y z")
* Usage: "mod x y z" = sqrt(x^2 + b^2 + c^2) = value of this guide
* </p>
*
* @author Yegor Kozlov
*/
public class ModExpression implements Expression {
private String arg1, arg2, arg3;
ModExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return Math.sqrt(x*x + y*y + z*z);
}
}

View File

@ -23,17 +23,12 @@ import java.awt.geom.Path2D;
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
/**
* Date: 10/25/11
*
* @author Yegor Kozlov
*/
public class MoveToCommand implements PathCommand {
private String arg1, arg2;
MoveToCommand(CTAdjPoint2D pt){
arg1 = pt.getX().toString();
arg2 = pt.getY().toString();
arg1 = pt.getX();
arg2 = pt.getY();
}
MoveToCommand(String s1, String s2){
@ -41,6 +36,7 @@ public class MoveToCommand implements PathCommand {
arg2 = s2;
}
@Override
public void execute(Path2D.Double path, Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);

View File

@ -1,45 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Multiply Divide Formula
*
* @author Yegor Kozlov
*/
public class MultiplyDivideExpression implements Expression {
private String arg1, arg2, arg3;
MultiplyDivideExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return (x * y ) / z;
}
}

View File

@ -21,11 +21,6 @@ package org.apache.poi.sl.draw.geom;
import java.awt.Shape;
/**
* Date: 11/6/11
*
* @author Yegor Kozlov
*/
public class Outline {
private Shape shape;
private Path path;

View File

@ -31,17 +31,17 @@ import org.apache.poi.sl.draw.binding.CTPath2DCubicBezierTo;
import org.apache.poi.sl.draw.binding.CTPath2DLineTo;
import org.apache.poi.sl.draw.binding.CTPath2DMoveTo;
import org.apache.poi.sl.draw.binding.CTPath2DQuadBezierTo;
import org.apache.poi.sl.draw.binding.STPathFillMode;
import org.apache.poi.sl.usermodel.PaintStyle.PaintModifier;
/**
* Specifies a creation path consisting of a series of moves, lines and curves
* that when combined forms a geometric shape
*
* @author Yegor Kozlov
*/
public class Path {
private final List<PathCommand> commands;
boolean _fill, _stroke;
PaintModifier _fill;
boolean _stroke;
long _w, _h;
public Path(){
@ -52,18 +52,26 @@ public class Path {
commands = new ArrayList<PathCommand>();
_w = -1;
_h = -1;
_fill = fill;
_fill = (fill) ? PaintModifier.NORM : PaintModifier.NONE;
_stroke = stroke;
}
public Path(CTPath2D spPath){
_fill = spPath.getFill() != STPathFillMode.NONE;
switch (spPath.getFill()) {
case NONE: _fill = PaintModifier.NONE; break;
case DARKEN: _fill = PaintModifier.DARKEN; break;
case DARKEN_LESS: _fill = PaintModifier.DARKEN_LESS; break;
case LIGHTEN: _fill = PaintModifier.LIGHTEN; break;
case LIGHTEN_LESS: _fill = PaintModifier.LIGHTEN_LESS; break;
default:
case NORM: _fill = PaintModifier.NORM; break;
}
_stroke = spPath.isStroke();
_w = spPath.isSetW() ? spPath.getW() : -1;
_h = spPath.isSetH() ? spPath.getH() : -1;
_w = spPath.isSetW() ? spPath.getW() : -1;
_h = spPath.isSetH() ? spPath.getH() : -1;
commands = new ArrayList<PathCommand>();
for(Object ch : spPath.getCloseOrMoveToOrLnTo()){
if(ch instanceof CTPath2DMoveTo){
CTAdjPoint2D pt = ((CTPath2DMoveTo)ch).getPt();
@ -102,8 +110,9 @@ public class Path {
*/
public Path2D.Double getPath(Context ctx) {
Path2D.Double path = new Path2D.Double();
for(PathCommand cmd : commands)
for(PathCommand cmd : commands) {
cmd.execute(path, ctx);
}
return path;
}
@ -112,9 +121,13 @@ public class Path {
}
public boolean isFilled(){
return _fill != PaintModifier.NONE;
}
public PaintModifier getFill() {
return _fill;
}
public long getW(){
return _w;
}

View File

@ -24,15 +24,12 @@ import java.awt.geom.Path2D;
/**
* A path command in DrawingML. One of:
*
* - arcTo
* - moveTo
* - lineTo
* - cubicBezTo
* - quadBezTo
* - close
*
*
* @author Yegor Kozlov
* <li>arcTo
* <li>moveTo
* <li>lineTo
* <li>cubicBezTo
* <li>quadBezTo
* <li>close
*/
public interface PathCommand {
/**

View File

@ -1,54 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Pin To Formula:
* <gd name="enAng" fmla="pin 0 adj2 21599999"/>
*
* <p>
* Usage: "pin x y z" = if (y < x), then x = value of this guide
* else if (y > z), then z = value of this guide
* else y = value of this guide
* </p>
*
* @author Yegor Kozlov
*/
public class PinExpression implements Expression {
private String arg1, arg2, arg3;
PinExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
if(y < x) return x;
else if (y > z) return z;
else return y;
}
}

View File

@ -23,21 +23,17 @@ import java.awt.geom.Path2D;
import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
/**
* Date: 10/25/11
*
* @author Yegor Kozlov
*/
public class QuadToCommand implements PathCommand {
private String arg1, arg2, arg3, arg4;
QuadToCommand(CTAdjPoint2D pt1, CTAdjPoint2D pt2){
arg1 = pt1.getX().toString();
arg2 = pt1.getY().toString();
arg3 = pt2.getX().toString();
arg4 = pt2.getY().toString();
arg1 = pt1.getX();
arg2 = pt1.getY();
arg3 = pt2.getX();
arg4 = pt2.getY();
}
@Override
public void execute(Path2D.Double path, Context ctx){
double x1 = ctx.getValue(arg1);
double y1 = ctx.getValue(arg2);

View File

@ -1,51 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Sine ArcTan Formula:
* <gd name="dy1" fmla="sat2 x y z"/>
*
* <p>
* Arguments: 3 (fmla="sat2 x y z")
* Usage: "sat2 x y z" = (x*sin(arctan(z / y))) = value of this guide
* </p>
*
* @author Yegor Kozlov
*/
public class SinArcTanExpression implements Expression {
private String arg1, arg2, arg3;
SinArcTanExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
arg3 = m.group(3);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
double z = ctx.getValue(arg3);
return x*Math.sin(Math.atan(z / y));
}
}

View File

@ -1,49 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Sine Formula:
* <gd name="z" fmla="sin x y"/>
*
* <p>
* Arguments: 2 (fmla="sin x y")
* Usage: "sin x y" = (x * sin( y )) = value of this guide
* </p>
*
* @author Yegor Kozlov
*/
public class SinExpression implements Expression {
private String arg1, arg2;
SinExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2) / 60000;
return x * Math.sin(Math.toRadians(y));
}
}

View File

@ -1,46 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Square Root Formula:
* <gd name="x" fmla="sqrt y"/>
*
* <p>
* Arguments: 1 (fmla="sqrt x")
* Usage: "sqrt x" = sqrt(x) = value of this guide
* </p>
* @author Yegor Kozlov
*/
public class SqrtExpression implements Expression {
private String arg;
SqrtExpression(Matcher m){
arg =m.group(1);
}
public double evaluate(Context ctx){
double val = ctx.getValue(arg);
return Math.sqrt(val);
}
}

View File

@ -1,50 +0,0 @@
/*
* ====================================================================
* 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.geom;
import java.util.regex.Matcher;
/**
* Tangent Formula:
*
* <gd name="z" fmla="tan x y"/>
*
* <p>
* Arguments: 2 (fmla="tan x y")
* Usage: "tan x y" = (x * tan( y )) = value of this guide
* </p>
*
* @author Yegor Kozlov
*/
public class TanExpression implements Expression {
private String arg1, arg2;
TanExpression(Matcher m){
arg1 = m.group(1);
arg2 = m.group(2);
}
public double evaluate(Context ctx){
double x = ctx.getValue(arg1);
double y = ctx.getValue(arg2);
return x * Math.tan(Math.toRadians(y / 60000));
}
}

View File

@ -17,6 +17,9 @@
package org.apache.poi.sl.usermodel;
/**
* This interface is the counterpart to {@link StrokeStyle} - it's specifies the filling of a shape
*/
public interface FillStyle {
PaintStyle getPaint();
}

View File

@ -22,12 +22,30 @@ import java.io.InputStream;
public interface PaintStyle {
/**
* The PaintStyle can be modified by secondary sources, e.g. the attributes in the preset shapes.
* These modifications need to be taken into account when the final color is determined.
*/
enum PaintModifier {
/** don't use any paint/fill */
NONE,
/** use the paint/filling as-is */
NORM,
/** lighten the paint/filling */
LIGHTEN,
/** lighten (... a bit less) the paint/filling */
LIGHTEN_LESS,
/** darken the paint/filling */
DARKEN,
/** darken (... a bit less) the paint/filling */
DARKEN_LESS;
}
public interface SolidPaint extends PaintStyle {
interface SolidPaint extends PaintStyle {
ColorStyle getSolidColor();
}
public interface GradientPaint extends PaintStyle {
interface GradientPaint extends PaintStyle {
enum GradientType { linear, circular, shape }
/**
@ -40,7 +58,7 @@ public interface PaintStyle {
GradientType getGradientType();
}
public interface TexturePaint extends PaintStyle {
interface TexturePaint extends PaintStyle {
/**
* @return the raw image stream
*/

View File

@ -17,6 +17,9 @@
package org.apache.poi.sl.usermodel;
/**
* This interface specifies the line style of a shape
*/
public interface StrokeStyle {
enum LineCap {
/** Rounded ends */

View File

@ -632,8 +632,8 @@ public class TestXSLFTextShape {
assertEquals(TextAlign.CENTER, r1.getParentParagraph().getTextAlign());
assertEquals("Calibri", r1.getFontFamily());
assertEquals(12.0, r1.getFontSize(), 0);
// TODO calculation of tint is incorrect
assertTrue(sameColor(new Color(64,64,64), r1.getFontColor()));
// TODO calculation of tint might be incorrect
assertTrue(sameColor(new Color(191,191,191), r1.getFontColor()));
XSLFTextShape dt = (XSLFTextShape)slide.getPlaceholderByType(STPlaceholderType.INT_DT);
assertEquals("Friday, October 21, 2011", dt.getText());