You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2015/02/21 11:56:06 UTC

svn commit: r1661322 [5/14] - in /poi/branches/common_sl: ./ src/examples/src/org/apache/poi/hslf/examples/ src/examples/src/org/apache/poi/xslf/usermodel/ src/java/org/apache/poi/common/usermodel/ src/java/org/apache/poi/hssf/usermodel/ src/java/org/a...

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawPaint.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,448 @@
+/* ====================================================================
+   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.*;
+import java.awt.MultipleGradientPaint.ColorSpaceType;
+import java.awt.MultipleGradientPaint.CycleMethod;
+import java.awt.Shape;
+import java.awt.geom.*;
+import java.awt.image.*;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.sl.usermodel.*;
+import org.apache.poi.sl.usermodel.GradientPaint;
+import org.apache.poi.sl.usermodel.TexturePaint;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+
+public class DrawPaint {
+    public final static Color NO_PAINT = new Color(0xFF, 0xFF, 0xFF, 0);
+    private final static POILogger LOG = POILogFactory.getLogger(DrawPaint.class);
+
+    protected PlaceableShape shape;
+    
+    public DrawPaint(PlaceableShape shape) {
+        this.shape = shape;
+    }
+    
+    public Paint getPaint(Graphics2D graphics, PaintStyle paint) {
+        if (paint instanceof SolidPaint) {
+            return getSolidPaint((SolidPaint)paint, graphics);
+        } else if (paint instanceof GradientPaint) {
+            return getGradientPaint((GradientPaint)paint, graphics);
+        } else if (paint instanceof TexturePaint) {
+            return getTexturePaint((TexturePaint)paint, graphics);
+        }
+        return null;
+    }
+    
+    protected Paint getSolidPaint(SolidPaint fill, Graphics2D graphics) {
+        return applyColorTransform(fill.getSolidColor());
+    }
+
+    protected Paint getGradientPaint(GradientPaint fill, Graphics2D graphics) {
+        switch (fill.getGradientType()) {
+        case linear:
+            return createLinearGradientPaint(fill, graphics);
+        case circular:
+            return createRadialGradientPaint(fill, graphics);
+        case shape:
+            return createPathGradientPaint(fill, graphics);
+        default:
+            throw new UnsupportedOperationException("gradient fill of type "+fill+" not supported.");
+        }
+    }
+
+    protected Paint getTexturePaint(TexturePaint fill, Graphics2D graphics) {
+        InputStream is = fill.getImageData();
+        if (is == null) return NO_PAINT;
+        assert(graphics != null);
+        
+        ImageRenderer renderer = (ImageRenderer)graphics.getRenderingHint(Drawable.IMAGE_RENDERER);
+        if (renderer == null) renderer = new ImageRenderer();
+
+        try {
+            renderer.loadImage(fill.getImageData(), fill.getContentType());
+        } catch (IOException e) {
+            LOG.log(POILogger.ERROR, "Can't load image data - using transparent color", e);
+            return NO_PAINT;
+        }
+
+        int alpha = fill.getAlpha();
+        if (alpha != -1) {
+            renderer.setAlpha(fill.getAlpha()/100000.f);
+        }
+        
+        Dimension dim = renderer.getDimension();
+        Rectangle2D textAnchor = new Rectangle2D.Double(0, 0, dim.getWidth(), dim.getHeight());
+        Paint paint = new java.awt.TexturePaint(renderer.getImage(), textAnchor);
+
+        return paint;
+    }
+    
+    /**
+     * Convert color transformations in {@link ColorStyle} to a {@link Color} instance
+     */
+    public static Color applyColorTransform(ColorStyle color){
+        Color result = color.getColor();
+
+        if (result == null || color.getAlpha() == 100) return NO_PAINT;
+        
+        result = applyAlpha(result, color);
+        result = applyLuminanace(result, color);
+        result = applyShade(result, color);
+        result = applyTint(result, color);
+
+        return result;
+    }
+
+    protected static Color applyAlpha(Color c, ColorStyle fc) {
+        int alpha = c.getAlpha();
+        return (alpha == 0 || alpha == -1) ? c : new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); 
+    }
+    
+    /**
+     * Apply lumMod / lumOff adjustments
+     *
+     * @param c the color to modify
+     * @param lumMod luminance modulation in the range [0..100000]
+     * @param lumOff luminance offset in the range [0..100000]
+     * @return  modified color
+     */
+    protected static Color applyLuminanace(Color c, ColorStyle fc) {
+        int lumMod = fc.getLumMod();
+        if (lumMod == -1) lumMod = 100000;
+
+        int lumOff = fc.getLumOff();
+        if (lumOff == -1) lumOff = 0;
+        
+        if (lumMod == 100000 && lumOff == 0) return c;
+
+        int r = c.getRed();
+        int g = c.getGreen();
+        int b = c.getBlue();
+        
+        float red,green,blue;
+        
+        Color color;
+        if (lumOff > 0) {
+            float flumOff = lumOff / 100000.f;
+            red = (255.f - r) * (1.f - flumOff) + r;
+            green = (255.f - g) * flumOff + g;
+            blue = (255.f - b) * flumOff + b;
+        } else {
+            float flumMod = lumMod / 100000.f;
+            red = r * lumMod;
+            green = g * lumMod;
+            blue = b * lumMod;
+        }
+        return new Color(Math.round(red), Math.round(green), Math.round(blue), c.getAlpha());
+    }
+    
+    /**
+     * This algorithm returns result different from PowerPoint.
+     * TODO: revisit and improve
+     */
+    protected static Color applyShade(Color c, ColorStyle fc) {
+        int shade = fc.getShade();
+        if (shade == -1) return c;
+        
+        float fshade = shade / 100000.f;
+
+        float red = c.getRed() * fshade;
+        float green = c.getGreen() * fshade;
+        float blue = c.getGreen() * fshade;
+        
+        return new Color(Math.round(red), Math.round(green), Math.round(blue), c.getAlpha());
+    }
+
+    /**
+     * This algorithm returns result different from PowerPoint.
+     * TODO: revisit and improve
+     */
+    protected static Color applyTint(Color c, ColorStyle fc) {
+        int tint = fc.getTint();
+        if (tint == -1) return c;
+        
+        float ftint = tint / 100000.f;
+
+        float red = ftint * c.getRed() + (1.f - ftint) * 255.f;
+        float green = ftint * c.getGreen() + (1.f - ftint) * 255.f;
+        float blue = ftint * c.getBlue() + (1.f - ftint) * 255.f;
+
+        return new Color(Math.round(red), Math.round(green), Math.round(blue), c.getAlpha());
+    }
+    
+
+    protected Paint createLinearGradientPaint(GradientPaint fill, Graphics2D graphics) {
+        double angle = fill.getGradientAngle();
+        Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
+
+        AffineTransform at = AffineTransform.getRotateInstance(
+            Math.toRadians(angle),
+            anchor.getX() + anchor.getWidth() / 2,
+            anchor.getY() + anchor.getHeight() / 2);
+
+        double diagonal = Math.sqrt(anchor.getHeight() * anchor.getHeight() + anchor.getWidth() * anchor.getWidth());
+        Point2D p1 = new Point2D.Double(anchor.getX() + anchor.getWidth() / 2 - diagonal / 2,
+                anchor.getY() + anchor.getHeight() / 2);
+        p1 = at.transform(p1, null);
+
+        Point2D p2 = new Point2D.Double(anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight() / 2);
+        p2 = at.transform(p2, null);
+
+        snapToAnchor(p1, anchor);
+        snapToAnchor(p2, anchor);
+
+        float[] fractions = fill.getGradientFractions();
+        Color[] colors = new Color[fractions.length];
+        
+        int i = 0;
+        for (ColorStyle fc : fill.getGradientColors()) {
+            colors[i++] = applyColorTransform(fc);
+        }
+
+        AffineTransform grAt  = new AffineTransform();
+        if(fill.isRotatedWithShape()) {
+            double rotation = shape.getRotation();
+            if (rotation != 0.) {
+                double centerX = anchor.getX() + anchor.getWidth() / 2;
+                double centerY = anchor.getY() + anchor.getHeight() / 2;
+
+                grAt.translate(centerX, centerY);
+                grAt.rotate(Math.toRadians(-rotation));
+                grAt.translate(-centerX, -centerY);
+            }
+        }
+
+        return new LinearGradientPaint
+            (p1, p2, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, grAt);
+    }
+
+    protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) {
+        Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
+
+        Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2,
+                anchor.getY() + anchor.getHeight()/2);
+
+        float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
+
+        float[] fractions = fill.getGradientFractions();
+        Color[] colors = new Color[fractions.length];
+
+        int i=0;
+        for (ColorStyle fc : fill.getGradientColors()) {
+            colors[i++] = applyColorTransform(fc);
+        }
+
+        return new RadialGradientPaint(pCenter, radius, fractions, colors);
+    }
+
+    protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) {
+        // currently we ignore an eventually center setting
+        
+        float[] fractions = fill.getGradientFractions();
+        Color[] colors = new Color[fractions.length];
+
+        int i=0;
+        for (ColorStyle fc : fill.getGradientColors()) {
+            colors[i++] = applyColorTransform(fc);
+        }
+
+        return new PathGradientPaint(colors, fractions);
+    }
+    
+    protected void snapToAnchor(Point2D p, Rectangle2D anchor) {
+        if (p.getX() < anchor.getX()) {
+            p.setLocation(anchor.getX(), p.getY());
+        } else if (p.getX() > (anchor.getX() + anchor.getWidth())) {
+            p.setLocation(anchor.getX() + anchor.getWidth(), p.getY());
+        }
+
+        if (p.getY() < anchor.getY()) {
+            p.setLocation(p.getX(), anchor.getY());
+        } else if (p.getY() > (anchor.getY() + anchor.getHeight())) {
+            p.setLocation(p.getX(), anchor.getY() + anchor.getHeight());
+        }
+    }
+
+    public static class PathGradientPaint implements Paint {
+
+        // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html
+        protected final Color colors[];
+        protected final float fractions[];
+        protected final int capStyle;
+        protected final int joinStyle;
+        protected final int transparency;
+
+        
+        public PathGradientPaint(Color colors[], float fractions[]) {
+            this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
+        }
+        
+        public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) {
+            this.colors = colors;
+            this.fractions = fractions;
+            this.capStyle = capStyle;
+            this.joinStyle = joinStyle;
+
+            // determine transparency
+            boolean opaque = true;
+            for (int i = 0; i < colors.length; i++){
+                opaque = opaque && (colors[i].getAlpha() == 0xff);
+            }
+            this.transparency = opaque ? OPAQUE : TRANSLUCENT;
+        }
+        
+        public PaintContext createContext(ColorModel cm,
+            Rectangle deviceBounds,
+            Rectangle2D userBounds,
+            AffineTransform transform,
+            RenderingHints hints) {
+            return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints);
+        }
+        
+        public int getTransparency() {
+            return transparency;
+        }
+
+        class PathGradientContext implements PaintContext {
+            protected final Rectangle deviceBounds;
+            protected final Rectangle2D userBounds;
+            protected final AffineTransform xform;
+            protected final RenderingHints hints;
+
+            /**
+             * for POI: the shape will be only known when the subclasses determines the concrete implementation 
+             * in the draw/-content method, so we need to postpone the setting/creation as long as possible
+             **/
+            protected final Shape shape;
+            protected final PaintContext pCtx;
+            protected final int gradientSteps;
+            WritableRaster raster;
+
+            public PathGradientContext(
+                  ColorModel cm
+                , Rectangle deviceBounds
+                , Rectangle2D userBounds
+                , AffineTransform xform
+                , RenderingHints hints
+            ) {
+                shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
+                if (shape == null) {
+                    throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint PathGradientPaint.GRADIANT_SHAPE.");
+                }
+
+                this.deviceBounds = deviceBounds;
+                this.userBounds = userBounds;
+                this.xform = xform;
+                this.hints = hints;
+
+                gradientSteps = getGradientSteps(shape);
+
+                Point2D start = new Point2D.Double(0, 0);
+                Point2D end = new Point2D.Double(gradientSteps, 0);
+                LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform());
+                
+                Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1);
+                pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints);
+            }
+
+            public void dispose() {}
+
+            public ColorModel getColorModel() {
+                return pCtx.getColorModel();
+            }
+
+            public Raster getRaster(int xOffset, int yOffset, int w, int h) {
+                ColorModel cm = getColorModel();
+                if (raster == null) createRaster();
+
+                // TODO: eventually use caching here
+                WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h);
+                Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h);
+                if (!childRect.intersects(deviceBounds)) {
+                    // usually doesn't happen ...
+                    return childRaster;
+                }
+                
+                Rectangle2D destRect = new Rectangle2D.Double();
+                Rectangle2D.intersect(childRect, deviceBounds, destRect);
+                int dx = (int)(destRect.getX()-deviceBounds.getX());
+                int dy = (int)(destRect.getY()-deviceBounds.getY());
+                int dw = (int)destRect.getWidth();
+                int dh = (int)destRect.getHeight();
+                Object data = raster.getDataElements(dx, dy, dw, dh, null);
+                dx = (int)(destRect.getX()-childRect.getX());
+                dy = (int)(destRect.getY()-childRect.getY());
+                childRaster.setDataElements(dx, dy, dw, dh, data);
+                
+                return childRaster;
+            }
+
+            protected int getGradientSteps(Shape shape) {
+                Rectangle rect = shape.getBounds();
+                int lower = 1;
+                int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
+                while (lower < upper-1) {
+                    int mid = lower + (upper - lower) / 2;
+                    BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle);
+                    Area area = new Area(bs.createStrokedShape(shape));
+                    if (area.isSingular()) {
+                        upper = mid;
+                    } else {
+                        lower = mid;
+                    }
+                }
+                return upper;
+            }
+            
+            
+            
+            protected void createRaster() {
+                ColorModel cm = getColorModel();
+                raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight());
+                BufferedImage img = new BufferedImage(cm, raster, false, null);
+                Graphics2D graphics = img.createGraphics();
+                graphics.setRenderingHints(hints);
+                graphics.translate(-deviceBounds.getX(), -deviceBounds.getY());
+                graphics.transform(xform);
+
+                Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1);
+                int rgb[] = new int[cm.getNumComponents()];
+
+                for (int i = gradientSteps-1; i>=0; i--) {
+                    img2.getPixel(i, 0, rgb);
+                    Color c = new Color(rgb[0],rgb[1],rgb[2]);
+                    if (rgb.length == 4) {
+                        // it doesn't work to use just a color with transparency ...
+                        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f));                           
+                    }
+                    graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle));
+                    graphics.setColor(c);
+                    graphics.draw(shape);
+                }
+                
+                graphics.dispose();
+            }
+        }
+    }
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,109 @@
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.poi.sl.usermodel.PlaceableShape;
+import org.apache.poi.sl.usermodel.Shape;
+
+
+public class DrawShape<T extends Shape> implements Drawable {
+
+    protected final T shape;
+    
+    public DrawShape(T shape) {
+        this.shape = shape;
+    }
+    
+    /**
+     * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
+     *
+     * @param graphics the graphics whos transform matrix will be modified
+     */
+    public void applyTransform(Graphics2D graphics) {
+        Rectangle2D anchor = shape.getAnchor();
+        AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+        if(tx != null) {
+            anchor = tx.createTransformedShape(anchor).getBounds2D();
+        }
+
+        // rotation
+        double rotation = shape.getRotation();
+        if (rotation != 0.) {
+            // PowerPoint rotates shapes relative to the geometric center
+            double centerX = anchor.getCenterX();
+            double centerY = anchor.getCenterY();
+
+            // normalize rotation
+            rotation = (360.+(rotation%360.))%360.;
+            int quadrant = (((int)rotation+45)/90)%4;
+            double scaleX = 1.0, scaleY = 1.0;
+
+            // scale to bounding box (bug #53176)
+            if (quadrant == 1 || quadrant == 3) {
+                // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation 
+                // (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple 
+                // of 90 degrees and then resize the bounding box to its original bbox. After that we can 
+                // rotate the shape to the exact rotation amount.
+                // It's strange that you'll need to rotate the shape back and forth again, but you can
+                // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
+                // be already (differently) scaled, so you can paint the shape in its default orientation
+                // and later on, turn it around again to compare it with its original size ...
+                AffineTransform txg = new AffineTransform(); // graphics coordinate space
+                AffineTransform txs = new AffineTransform(tx); // shape coordinate space
+                txg.translate(centerX, centerY);
+                txg.rotate(Math.toRadians(quadrant*90));
+                txg.translate(-centerX, -centerY);
+                txs.translate(centerX, centerY);
+                txs.rotate(Math.toRadians(-quadrant*90));
+                txs.translate(-centerX, -centerY);
+                txg.concatenate(txs);
+                Rectangle2D anchor2 = txg.createTransformedShape(shape.getAnchor()).getBounds2D();
+                scaleX = anchor.getWidth() == 0. ? 1.0 : anchor.getWidth() / anchor2.getWidth();
+                scaleY = anchor.getHeight() == 0. ? 1.0 : anchor.getHeight() / anchor2.getHeight();
+            }
+
+            // transformation is applied reversed ...
+            graphics.translate(centerX, centerY);
+            graphics.rotate(Math.toRadians(rotation-quadrant*90.));
+            graphics.scale(scaleX, scaleY);
+            graphics.rotate(Math.toRadians(quadrant*90));
+            graphics.translate(-centerX, -centerY);
+        }
+
+        //flip horizontal
+        if (shape.getFlipHorizontal()) {
+            graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
+            graphics.scale(-1, 1);
+            graphics.translate(-anchor.getX(), -anchor.getY());
+        }
+
+        //flip vertical
+        if (shape.getFlipVertical()) {
+            graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
+            graphics.scale(1, -1);
+            graphics.translate(-anchor.getX(), -anchor.getY());
+        }
+    }
+
+
+    public void draw(Graphics2D graphics) {
+    }
+
+    public void drawContent(Graphics2D context) {
+    }
+    
+    public static Rectangle2D getAnchor(Graphics2D graphics, PlaceableShape shape) {
+        Rectangle2D anchor = shape.getAnchor();
+        if(graphics == null)  {
+            return anchor;
+        }
+
+        AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
+        if(tx != null) {
+            anchor = tx.createTransformedShape(anchor).getBounds2D();
+        }
+        return anchor;
+    }    
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSheet.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSheet.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSheet.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSheet.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,72 @@
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import org.apache.poi.sl.usermodel.MasterSheet;
+import org.apache.poi.sl.usermodel.Shape;
+import org.apache.poi.sl.usermodel.Sheet;
+
+
+public class DrawSheet implements Drawable {
+
+    protected final Sheet sheet;
+    
+    public DrawSheet(Sheet sheet) {
+        this.sheet = sheet;
+    }
+    
+    public void applyTransform(Graphics2D context) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    public void draw(Graphics2D graphics) {
+        DrawFactory drawFact = DrawFactory.getInstance(graphics);
+        MasterSheet master = sheet.getMasterSheet();
+        
+        if(sheet.getFollowMasterGraphics() && master != null) {
+            Drawable drawer = drawFact.getDrawable(master);
+            drawer.draw(graphics);
+        }
+        
+        graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, new AffineTransform());
+
+        for (Shape shape : sheet.getShapes()) {
+            if(!canDraw(shape)) continue;
+            
+            // remember the initial transform and restore it after we are done with drawing
+            AffineTransform at = graphics.getTransform();
+
+            // concrete implementations can make sense of this hint,
+            // for example PSGraphics2D or PDFGraphics2D would call gsave() / grestore
+            graphics.setRenderingHint(Drawable.GSAVE, true);
+
+            // apply rotation and flipping
+            Drawable drawer = drawFact.getDrawable(shape);
+            drawer.applyTransform(graphics);
+            // draw stuff
+            drawer.draw(graphics);
+
+            // restore the coordinate system
+            graphics.setTransform(at);
+
+            graphics.setRenderingHint(Drawable.GRESTORE, true);
+        }
+    }
+
+    public void drawContent(Graphics2D context) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    /**
+     * Checks if this <code>sheet</code> displays the specified shape.
+     *
+     * Subclasses can override it and skip certain shapes from drawings,
+     * for instance, slide masters and layouts don't display placeholders
+     */
+    protected boolean canDraw(Shape shape){
+        return true;
+    }
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSimpleShape.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSimpleShape.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSimpleShape.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawSimpleShape.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,391 @@
+package org.apache.poi.sl.draw;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.List;
+
+import javax.xml.bind.*;
+import javax.xml.stream.*;
+import javax.xml.stream.EventFilter;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+import org.apache.poi.sl.draw.binding.CTCustomGeometry2D;
+import org.apache.poi.sl.draw.geom.*;
+import org.apache.poi.sl.usermodel.*;
+import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
+import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;
+import org.apache.poi.util.Units;
+
+
+public class DrawSimpleShape<T extends SimpleShape> extends DrawShape<T> {
+
+    public DrawSimpleShape(T shape) {
+        super(shape);
+    }
+
+    @Override
+    public void draw(Graphics2D graphics) {
+//        RenderableShape rShape = new RenderableShape(this);
+//        rShape.render(graphics);
+
+        DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(shape);
+        Paint fill = drawPaint.getPaint(graphics, shape.getFillStyle().getPaint());
+        Paint line = drawPaint.getPaint(graphics, shape.getStrokeStyle().getPaint());
+        BasicStroke stroke = getStroke(); // the stroke applies both to the shadow and the shape
+        graphics.setStroke(stroke);
+
+        Collection<Outline> elems = computeOutlines(graphics);
+        
+        // first paint the shadow
+        drawShadow(graphics, elems, fill, line);
+        
+        // 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);
+                }                
+            }
+        }
+        
+        // then draw any content within this shape (text, image, etc.)
+        drawContent(graphics);
+
+        // then stroke the shape outline
+        if(line != null) {
+            graphics.setPaint(line);
+            for(Outline o : elems){
+                if(o.getPath().isStroked()){
+                    java.awt.Shape s = o.getOutline();
+                    graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+                    graphics.draw(s);
+                }
+            }
+        }
+        
+        // draw line decorations
+        drawDecoration(graphics, line, stroke);
+    }
+
+    protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {
+        if(line == null) return;
+        graphics.setPaint(line);
+        
+        List<Outline> lst = new ArrayList<Outline>();
+        LineDecoration deco = shape.getLineDecoration();
+        Outline head = getHeadDecoration(graphics, deco, stroke);
+        if (head != null) lst.add(head);
+        Outline tail = getTailDecoration(graphics, deco, stroke);
+        if (tail != null) lst.add(tail);
+        
+        
+        for(Outline o : lst){
+            java.awt.Shape s = o.getOutline();
+            Path p = o.getPath();
+            graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+            
+            if(p.isFilled()) graphics.fill(s);
+            if(p.isStroked()) graphics.draw(s);
+        }
+    }
+
+    protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
+        DecorationSize tailLength = deco.getTailLength();
+        DecorationSize tailWidth = deco.getTailWidth();
+    
+        double lineWidth = Math.max(2.5, stroke.getLineWidth());
+    
+        Rectangle2D anchor = getAnchor(graphics, shape);
+        double x2 = anchor.getX() + anchor.getWidth(),
+                y2 = anchor.getY() + anchor.getHeight();
+    
+        double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
+    
+        AffineTransform at = new AffineTransform();
+        java.awt.Shape shape = null;
+        Path p = null;
+        Rectangle2D bounds;
+        double scaleY = Math.pow(2, tailWidth.ordinal());
+        double scaleX = Math.pow(2, tailLength.ordinal());
+        switch (deco.getTailShape()) {
+            case OVAL:
+                p = new Path();
+                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:
+                p = new Path();
+                GeneralPath arrow = new GeneralPath();
+                arrow.moveTo((float) (-lineWidth * 3), (float) (-lineWidth * 2));
+                arrow.lineTo(0, 0);
+                arrow.lineTo((float) (-lineWidth * 3), (float) (lineWidth * 2));
+                shape = arrow;
+                at.translate(x2, y2);
+                at.rotate(alpha);
+                break;
+            case TRIANGLE:
+                p = new Path();
+                scaleY = tailWidth.ordinal() + 1;
+                scaleX = tailLength.ordinal() + 1;
+                GeneralPath triangle = new GeneralPath();
+                triangle.moveTo((float) (-lineWidth * scaleX), (float) (-lineWidth * scaleY / 2));
+                triangle.lineTo(0, 0);
+                triangle.lineTo((float) (-lineWidth * scaleX), (float) (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 == null ? null : new Outline(shape, p);
+    }
+    
+    Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
+        DecorationSize headLength = deco.getHeadLength();
+        DecorationSize headWidth = deco.getHeadWidth();
+    
+        double lineWidth = Math.max(2.5, stroke.getLineWidth());
+    
+        Rectangle2D anchor = getAnchor(graphics, shape);
+        double x1 = anchor.getX(),
+                y1 = anchor.getY();
+    
+        double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
+    
+        AffineTransform at = new AffineTransform();
+        java.awt.Shape shape = null;
+        Path p = null;
+        Rectangle2D bounds;
+        double scaleY = 1;
+        double scaleX = 1;
+        switch (deco.getHeadShape()) {
+            case OVAL:
+                p = new Path();
+                shape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
+                bounds = shape.getBounds2D();
+                at.translate(x1 - bounds.getWidth() / 2, y1 - bounds.getHeight() / 2);
+                at.rotate(alpha, bounds.getX() + bounds.getWidth() / 2, bounds.getY() + bounds.getHeight() / 2);
+                break;
+            case STEALTH:
+            case ARROW:
+                p = new Path(false, true);
+                GeneralPath arrow = new GeneralPath();
+                arrow.moveTo((float) (lineWidth * 3 * scaleX), (float) (-lineWidth * scaleY * 2));
+                arrow.lineTo(0, 0);
+                arrow.lineTo((float) (lineWidth * 3 * scaleX), (float) (lineWidth * scaleY * 2));
+                shape = arrow;
+                at.translate(x1, y1);
+                at.rotate(alpha);
+                break;
+            case TRIANGLE:
+                p = new Path();
+                scaleY = headWidth.ordinal() + 1;
+                scaleX = headLength.ordinal() + 1;
+                GeneralPath triangle = new GeneralPath();
+                triangle.moveTo((float) (lineWidth * scaleX), (float) (-lineWidth * scaleY / 2));
+                triangle.lineTo(0, 0);
+                triangle.lineTo((float) (lineWidth * scaleX), (float) (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 == null ? null : new Outline(shape, p);
+    }
+    
+    public BasicStroke getStroke() {
+        StrokeStyle strokeStyle = shape.getStrokeStyle();
+        
+        float lineWidth = (float) strokeStyle.getLineWidth();
+        if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt
+
+        LineDash lineDash = strokeStyle.getLineDash();
+        int dashPatI[] = lineDash.pattern;
+        float[] dashPatF = new float[dashPatI.length];
+        final float dash_phase = 0;
+        for (int i=0; i<dashPatI.length; i++) {
+            dashPatF[i] = dashPatI[i]*lineWidth;
+        }
+
+        int lineCap;
+        switch (strokeStyle.getLineCap()) {
+            case ROUND:
+                lineCap = BasicStroke.CAP_ROUND;
+                break;
+            case SQUARE:
+                lineCap = BasicStroke.CAP_SQUARE;
+                break;
+            default:
+            case FLAT:
+                lineCap = BasicStroke.CAP_BUTT;
+                break;
+        }
+
+        int lineJoin = BasicStroke.JOIN_ROUND;
+
+        return new BasicStroke(lineWidth, lineCap, lineJoin, Math.max(1, lineWidth), dashPatF, dash_phase);
+    }
+
+    protected void drawShadow(
+            Graphics2D graphics
+          , Collection<Outline> outlines
+          , Paint fill
+          , Paint line
+    ) {
+          Shadow shadow = shape.getShadow();
+          if (shadow == null || (fill == null && line == null)) return;
+
+          double shapeRotation = shape.getRotation();
+          if(shape.getFlipVertical()) {
+              shapeRotation += 180;
+          }
+          double angle = shadow.getAngle() - shapeRotation;
+          double dist = shadow.getDistance();
+          double dx = dist * Math.cos(Math.toRadians(angle));
+          double dy = dist * Math.sin(Math.toRadians(angle));
+          
+          graphics.translate(dx, dy);
+          
+          for(Outline o : outlines){
+              java.awt.Shape s = o.getOutline();
+              Path p = o.getPath();
+              graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
+              
+              if(fill != null && p.isFilled()){
+                  graphics.setPaint(fill);
+                  graphics.fill(s);
+              } else if (line != null && p.isStroked()) {
+                  graphics.setPaint(line);
+                  graphics.draw(s);
+              }
+          }
+
+          graphics.translate(-dx, -dy);
+      }
+      
+    protected static CustomGeometry getCustomGeometry(String name) {
+        return getCustomGeometry(name, null);
+    }
+    
+    protected static CustomGeometry getCustomGeometry(String name, Graphics2D graphics) {
+        @SuppressWarnings("unchecked")
+        Map<String, CustomGeometry> presets = (graphics == null)
+            ? null
+            : (Map<String, CustomGeometry>)graphics.getRenderingHint(Drawable.PRESET_GEOMETRY_CACHE);
+        
+        if (presets == null) {
+            presets = new HashMap<String,CustomGeometry>();
+            if (graphics != null) {
+                graphics.setRenderingHint(Drawable.PRESET_GEOMETRY_CACHE, presets);
+            }
+            
+            String packageName = "org.apache.poi.sl.draw.binding";
+            InputStream presetIS = Drawable.class.getResourceAsStream("presetShapeDefinitions.xml");
+            Reader xml = new InputStreamReader( presetIS, Charset.forName("UTF-8") );
+    
+            // StAX:
+            EventFilter startElementFilter = new EventFilter() {
+                @Override
+                public boolean accept(XMLEvent event) {
+                    return event.isStartElement();
+                }
+            };
+            
+            try {
+                XMLInputFactory staxFactory = XMLInputFactory.newInstance();
+                XMLEventReader staxReader = staxFactory.createXMLEventReader(xml);
+                XMLEventReader staxFiltRd = staxFactory.createFilteredReader(staxReader, startElementFilter);
+                // Ignore StartElement:
+                staxFiltRd.nextEvent();
+                // JAXB:
+                JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
+                Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+        
+                while (staxFiltRd.peek() != null) {
+                    StartElement evRoot = (StartElement)staxFiltRd.peek();
+                    String cusName = evRoot.getName().getLocalPart();
+                    // XMLEvent ev = staxReader.nextEvent();
+                    JAXBElement<org.apache.poi.sl.draw.binding.CTCustomGeometry2D> el = unmarshaller.unmarshal(staxReader, CTCustomGeometry2D.class);
+                    CTCustomGeometry2D cusGeom = el.getValue();
+                    
+                    presets.put(cusName, new CustomGeometry(cusGeom));
+                }
+            } catch (Exception e) {
+                throw new RuntimeException("Unable to load preset geometries.", e);
+            }
+        }
+        
+        return presets.get(name);
+    }
+    
+    protected Collection<Outline> computeOutlines(Graphics2D graphics) {
+
+        List<Outline> lst = new ArrayList<Outline>();
+        CustomGeometry geom = shape.getGeometry();
+        if(geom == null) {
+            return lst;
+        }
+
+        Rectangle2D anchor = getAnchor(graphics, shape);
+        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();
+
+            // 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, shape);
+
+            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);
+
+            lst.add(new Outline(canvasShape, p));
+        }
+
+        return lst;
+    }
+
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextBox.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextBox.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextBox.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextBox.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,9 @@
+package org.apache.poi.sl.draw;
+
+import org.apache.poi.sl.usermodel.*;
+
+public class DrawTextBox<T extends TextBox> extends DrawAutoShape<T> {
+    public DrawTextBox(T shape) {
+        super(shape);
+    }
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextFragment.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextFragment.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextFragment.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextFragment.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,94 @@
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.font.TextLayout;
+import java.text.*;
+
+import org.apache.poi.xslf.usermodel.XSLFRenderingHint;
+
+public class DrawTextFragment implements Drawable  {
+    final TextLayout layout;
+    final AttributedString str;
+    double x, y;
+    
+    public DrawTextFragment(TextLayout layout, AttributedString str) {
+        this.layout = layout;
+        this.str = str;
+    }
+
+    public void setPosition(double x, double y) {
+        // TODO: replace it, by applyTransform????
+        this.x = x;
+        this.y = y;
+    }
+
+    public void draw(Graphics2D graphics){
+        if(str == null) {
+            return;
+        }
+
+        double yBaseline = y + layout.getAscent();
+
+        Integer textMode = (Integer)graphics.getRenderingHint(XSLFRenderingHint.TEXT_RENDERING_MODE);
+        if(textMode != null && textMode == XSLFRenderingHint.TEXT_AS_SHAPES){
+            layout.draw(graphics, (float)x, (float)yBaseline);
+        } else {
+            graphics.drawString(str.getIterator(), (float)x, (float)yBaseline );
+        }
+    }
+
+    public void applyTransform(Graphics2D graphics) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    public void drawContent(Graphics2D graphics) {
+        // TODO Auto-generated method stub
+        
+    }
+    
+    public TextLayout getLayout() {
+        return layout;
+    }
+
+    public AttributedString getAttributedString() {
+        return str;
+    }
+    
+    /**
+     * @return full height of this text run which is sum of ascent, descent and leading
+     */
+    public float getHeight(){
+        double h = Math.ceil(layout.getAscent()) + Math.ceil(layout.getDescent()) + layout.getLeading();
+        return (float)h;
+    }
+
+    /**
+     *
+     * @return width if this text run
+     */
+    public float getWidth(){
+        return layout.getAdvance();
+    }
+
+    /**
+     *
+     * @return the string to be painted
+     */
+    public String getString(){
+        if (str == null) return "";
+
+        AttributedCharacterIterator it = str.getIterator();
+         StringBuilder buf = new StringBuilder();
+         for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
+             buf.append(c);
+         }
+        return buf.toString();
+    }
+
+    @Override
+    public String toString(){
+        return "[" + getClass().getSimpleName() + "] " + getString();
+    }
+    
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,381 @@
+package org.apache.poi.sl.draw;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.font.*;
+import java.awt.geom.Rectangle2D;
+import java.text.*;
+import java.text.AttributedCharacterIterator.Attribute;
+import java.util.*;
+
+import org.apache.poi.sl.usermodel.*;
+import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;
+import org.apache.poi.sl.usermodel.TextRun.TextCap;
+import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
+
+public class DrawTextParagraph implements Drawable {
+    protected TextParagraph paragraph;
+    double x, y;
+    protected Insets2D insets = new Insets2D(0,0,0,0);
+    protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>();
+    protected String rawText;
+    protected DrawTextFragment bullet;
+
+    /**
+     * the highest line in this paragraph. Used for line spacing.
+     */
+    protected double maxLineHeight;
+
+    public DrawTextParagraph(TextParagraph paragraph) {
+        this.paragraph = paragraph;
+    }
+
+    public Insets2D getInsets() {
+        return insets;
+    }
+
+    public void setInsets(Insets2D insets) {
+        this.insets.set(insets.top, insets.left, insets.bottom, insets.right);
+    }
+
+    public void setPosition(double x, double y) {
+        // TODO: replace it, by applyTransform????
+        this.x = x;
+        this.y = y;
+    }
+
+    public double getY() {
+        return y;
+    }
+    
+    public void draw(Graphics2D graphics){
+        if (lines.isEmpty()) return;
+        
+        double leftInset = insets.left;
+        double rightInset = insets.right;
+        double penY = y;
+
+        double leftMargin = paragraph.getLeftMargin();
+        boolean firstLine = true;
+        double indent = paragraph.getIndent();
+
+        //The vertical line spacing
+        double spacing = paragraph.getLineSpacing();
+        for(DrawTextFragment line : lines){
+            double penX = x + leftMargin;
+
+            if(firstLine) {
+                if (!isEmptyParagraph()) {
+                    bullet = getBullet(graphics, line.getAttributedString().getIterator());
+                }
+                
+                if(bullet != null){
+                    if (indent < 0) {
+                        // a negative value means "Hanging" indentation and
+                        // indicates the position of the actual bullet character.
+                        // (the bullet is shifted to right relative to the text)
+                        bullet.setPosition(penX + indent, penY);
+                    } else if(indent > 0){
+                        // a positive value means the "First Line" indentation:
+                        // the first line is indented and other lines start at the bullet ofset
+                        bullet.setPosition(penX, penY);
+                        penX += indent;
+                    } else {
+                        // a zero indent means that the bullet and text have the same offset
+                        bullet.setPosition(penX, penY);
+
+                        // don't let text overlay the bullet and advance by the bullet width
+                        penX += bullet.getLayout().getAdvance() + 1;
+                    }
+
+                    bullet.draw(graphics);
+                } else {
+                    penX += indent;
+                }
+            }
+
+            Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
+
+            switch (paragraph.getTextAlign()) {
+                case CENTER:
+                    penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;
+                    break;
+                case RIGHT:
+                    penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset);
+                    break;
+                default:
+                    break;
+            }
+
+            line.setPosition(penX, penY);
+            line.draw(graphics);
+
+            if(spacing > 0) {
+                // If linespacing >= 0, then linespacing is a percentage of normal line height.
+                penY += spacing*0.01* line.getHeight();
+            } else {
+                // positive value means absolute spacing in points
+                penY += -spacing;
+            }
+
+            firstLine = false;
+        }
+
+        y = penY - y;
+    }
+    
+    public float getFirstLineHeight() {
+        return (lines.isEmpty()) ? 0 : lines.get(0).getHeight();
+    }
+
+    public float getLastLineHeight() {
+        return (lines.isEmpty()) ? 0 : lines.get(lines.size()-1).getHeight();
+    }
+
+    public boolean isEmptyParagraph() {
+        return (lines.isEmpty() || rawText.trim().isEmpty());
+    }
+    
+    public void applyTransform(Graphics2D graphics) {
+    }
+
+    public void drawContent(Graphics2D graphics) {
+    }
+
+    /**
+     * break text into lines, each representing a line of text that fits in the wrapping width
+     *
+     * @param graphics
+     */
+    protected void breakText(Graphics2D graphics){
+        lines.clear();
+
+        DrawFactory fact = DrawFactory.getInstance(graphics);
+        StringBuilder text = new StringBuilder();
+        AttributedString at = getAttributedString(graphics, text);
+        boolean emptyParagraph = ("".equals(text.toString().trim()));
+
+        AttributedCharacterIterator it = at.getIterator();
+        LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
+        for (;;) {
+            int startIndex = measurer.getPosition();
+
+            double wrappingWidth = getWrappingWidth(lines.size() == 0, graphics) + 1; // add a pixel to compensate rounding errors
+            // shape width can be smaller that the sum of insets (this was proved by a test file)
+            if(wrappingWidth < 0) wrappingWidth = 1;
+
+            int nextBreak = text.indexOf("\n", startIndex + 1);
+            if(nextBreak == -1) nextBreak = it.getEndIndex();
+
+            TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true);
+            if (layout == null) {
+                 // layout can be null if the entire word at the current position
+                 // does not fit within the wrapping width. Try with requireNextWord=false.
+                 layout = measurer.nextLayout((float)wrappingWidth, nextBreak, false);
+            }
+
+            if(layout == null) {
+                // exit if can't break any more
+                break;
+            }
+
+            int endIndex = measurer.getPosition();
+            // skip over new line breaks (we paint 'clear' text runs not starting or ending with \n)
+            if(endIndex < it.getEndIndex() && text.charAt(endIndex) == '\n'){
+                measurer.setPosition(endIndex + 1);
+            }
+
+            TextAlign hAlign = paragraph.getTextAlign();
+            if(hAlign == TextAlign.JUSTIFY || hAlign == TextAlign.JUSTIFY_LOW) {
+                layout = layout.getJustifiedLayout((float)wrappingWidth);
+            }
+
+            AttributedString str = (emptyParagraph)
+                ? null // we will not paint empty paragraphs
+                : new AttributedString(it, startIndex, endIndex);
+            DrawTextFragment line = fact.getTextFragment(layout, str);
+            lines.add(line);
+
+            maxLineHeight = Math.max(maxLineHeight, line.getHeight());
+
+            if(endIndex == it.getEndIndex()) break;
+        }
+
+        rawText = text.toString();
+    }
+
+    protected DrawTextFragment getBullet(Graphics2D graphics, AttributedCharacterIterator firstLineAttr) {
+        BulletStyle bulletStyle = paragraph.getBulletStyle();
+        if (bulletStyle == null) return null;
+
+        String buCharacter = bulletStyle.getBulletCharacter();
+        if (buCharacter == null) return null;
+
+        String buFont = bulletStyle.getBulletFont();
+        if (buFont == null) buFont = paragraph.getDefaultFontFamily();
+        assert(buFont != null);
+
+        Color buColor = bulletStyle.getBulletFontColor();
+        if (buColor == null) buColor = (Color)firstLineAttr.getAttribute(TextAttribute.FOREGROUND);
+
+        float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);
+        float buSz = (float)bulletStyle.getBulletFontSize();
+        if(buSz > 0) fontSize *= buSz* 0.01;
+        else fontSize = -buSz;
+
+        
+        AttributedString str = new AttributedString(buCharacter);
+        str.addAttribute(TextAttribute.FOREGROUND, buColor);
+        str.addAttribute(TextAttribute.FAMILY, buFont);
+        str.addAttribute(TextAttribute.SIZE, fontSize);
+
+        TextLayout layout = new TextLayout(str.getIterator(), graphics.getFontRenderContext());
+        DrawFactory fact = DrawFactory.getInstance(graphics);
+        return fact.getTextFragment(layout, str);
+    }
+
+    protected String getRenderableText(TextRun tr) {
+        StringBuilder buf = new StringBuilder();
+        TextCap cap = tr.getTextCap();
+        for (char c : tr.getText().toCharArray()) {
+            if(c == '\t') {
+                // TODO: finish support for tabs
+                buf.append("  ");
+                continue;
+            }
+
+            switch (cap) {
+                case ALL: c = Character.toUpperCase(c); break;
+                case SMALL: c = Character.toLowerCase(c); break;
+                case NONE: break;
+            }
+
+            buf.append(c);
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Returns wrapping width to break lines in this paragraph
+     *
+     * @param firstLine whether the first line is breaking
+     *
+     * @return  wrapping width in points
+     */
+    protected double getWrappingWidth(boolean firstLine, Graphics2D graphics){
+        // internal margins for the text box
+
+        double leftInset = insets.left;
+        double rightInset = insets.right;
+
+        Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
+
+        double leftMargin = paragraph.getLeftMargin();
+        double indent = paragraph.getIndent();
+
+        double width;
+        TextShape ts = paragraph.getParentShape();
+        if (!ts.getWordWrap()) {
+            // if wordWrap == false then we return the advance to the right border of the sheet
+            width = ts.getSheet().getSlideShow().getPageSize().getWidth() - anchor.getX();
+        } else {
+            width = anchor.getWidth() -  leftInset - rightInset - leftMargin;
+            if (firstLine) {
+                if (bullet != null){
+                    if (indent > 0) width -= indent;
+                } else {
+                    if (indent > 0) width -= indent; // first line indentation
+                    else if (indent < 0) { // hanging indentation: the first line start at the left margin
+                        width += leftMargin;
+                    }
+                }
+            }
+        }
+
+        return width;
+    }
+
+    private static class AttributedStringData {
+        Attribute attribute;
+        Object value;
+        int beginIndex, endIndex;
+        AttributedStringData(Attribute attribute, Object value, int beginIndex, int endIndex) {
+            this.attribute = attribute;
+            this.value = value;
+            this.beginIndex = beginIndex;
+            this.endIndex = endIndex;
+        }
+    }
+
+    protected AttributedString getAttributedString(Graphics2D graphics, StringBuilder text){
+        List<AttributedStringData> attList = new ArrayList<AttributedStringData>();
+        if (text == null) text = new StringBuilder();
+
+        DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);
+
+        for (TextRun run : paragraph){
+            String runText = getRenderableText(run);
+            // skip empty runs
+            if (runText.isEmpty()) continue;
+
+            int beginIndex = text.length();
+            text.append(runText);
+            int endIndex = text.length();
+
+            attList.add(new AttributedStringData(TextAttribute.FOREGROUND, run.getFontColor(), beginIndex, endIndex));
+
+            // user can pass an custom object to convert fonts
+            String fontFamily = run.getFontFamily();
+            @SuppressWarnings("unchecked")
+            Map<String,String> fontMap = (Map<String,String>)graphics.getRenderingHint(Drawable.FONT_MAP);
+            if (fontMap != null && fontMap.containsKey(fontFamily)) {
+                fontFamily = fontMap.get(fontFamily);
+            }
+            if(fontHandler != null) {
+                fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily());
+            }
+            attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex));
+
+            float fontSz = (float)run.getFontSize();
+            attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, beginIndex, endIndex));
+
+            if(run.isBold()) {
+                attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex));
+            }
+            if(run.isItalic()) {
+                attList.add(new AttributedStringData(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, beginIndex, endIndex));
+            }
+            if(run.isUnderline()) {
+                attList.add(new AttributedStringData(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, beginIndex, endIndex));
+                attList.add(new AttributedStringData(TextAttribute.INPUT_METHOD_UNDERLINE, TextAttribute.UNDERLINE_LOW_TWO_PIXEL, beginIndex, endIndex));
+            }
+            if(run.isStrikethrough()) {
+                attList.add(new AttributedStringData(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, beginIndex, endIndex));
+            }
+            if(run.isSubscript()) {
+                attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, beginIndex, endIndex));
+            }
+            if(run.isSuperscript()) {
+                attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));
+            }
+        }
+
+        // ensure that the paragraph contains at least one character
+        // We need this trick to correctly measure text
+        if (text.length() == 0) {
+            float fontSz = (float)paragraph.getDefaultFontSize();
+            text.append(" ");
+            attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, 0, 1));
+        }
+
+        AttributedString string = new AttributedString(text.toString());
+        for (AttributedStringData asd : attList) {
+            string.addAttribute(asd.attribute, asd.value, asd.beginIndex, asd.endIndex);
+        }
+
+        return string;
+    }
+
+
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,140 @@
+package org.apache.poi.sl.draw;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.util.Iterator;
+
+import org.apache.poi.sl.usermodel.*;
+
+public class DrawTextShape<T extends TextShape> extends DrawSimpleShape<T> {
+
+    public DrawTextShape(T shape) {
+        super(shape);
+    }
+
+    @Override
+    public void drawContent(Graphics2D graphics) {
+        Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
+        Insets2D insets = shape.getInsets();
+        double x = anchor.getX() + insets.left;
+        double y = anchor.getY();
+
+        // remember the initial transform
+        AffineTransform tx = graphics.getTransform();
+
+        // Transform of text in flipped shapes is special.
+        // At this point the flip and rotation transform is already applied
+        // (see XSLFShape#applyTransform ), but we need to restore it to avoid painting "upside down".
+        // See Bugzilla 54210.
+
+        if(shape.getFlipVertical()){
+            graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
+            graphics.scale(1, -1);
+            graphics.translate(-anchor.getX(), -anchor.getY());
+
+            // text in vertically flipped shapes is rotated by 180 degrees
+            double centerX = anchor.getX() + anchor.getWidth()/2;
+            double centerY = anchor.getY() + anchor.getHeight()/2;
+            graphics.translate(centerX, centerY);
+            graphics.rotate(Math.toRadians(180));
+            graphics.translate(-centerX, -centerY);
+        }
+
+        // Horizontal flipping applies only to shape outline and not to the text in the shape.
+        // Applying flip second time restores the original not-flipped transform
+        if(shape.getFlipHorizontal()){
+            graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
+            graphics.scale(-1, 1);
+            graphics.translate(-anchor.getX() , -anchor.getY());
+        }
+
+
+        // first dry-run to calculate the total height of the text
+        double textHeight = shape.getTextHeight();
+
+        switch (shape.getVerticalAlignment()){
+            case TOP:
+                y += insets.top;
+                break;
+            case BOTTOM:
+                y += anchor.getHeight() - textHeight - insets.bottom;
+                break;
+            default:
+            case MIDDLE:
+                double delta = anchor.getHeight() - textHeight - insets.top - insets.bottom;
+                y += insets.top + delta/2;
+                break;
+        }
+
+        drawParagraphs(graphics, x, y);
+
+        // restore the transform
+        graphics.setTransform(tx);
+    }
+
+    /**
+     * paint the paragraphs starting from top left (x,y)
+     *
+     * @return  the vertical advance, i.e. the cumulative space occupied by the text
+     */
+    public double drawParagraphs(Graphics2D graphics, double x, double y) {
+        DrawFactory fact = DrawFactory.getInstance(graphics);
+        Insets2D shapePadding = shape.getInsets();
+
+        double y0 = y;
+        Iterator<TextParagraph> paragraphs = shape.iterator();
+        
+        boolean isFirstLine = true;
+        while (paragraphs.hasNext()){
+            TextParagraph p = paragraphs.next();
+            DrawTextParagraph dp = fact.getDrawable(p);
+            dp.setInsets(shapePadding);
+            dp.breakText(graphics);
+
+            if (!isFirstLine) {
+                // the amount of vertical white space before the paragraph
+                double spaceBefore = p.getSpaceBefore();
+                if(spaceBefore > 0) {
+                    // positive value means percentage spacing of the height of the first line, e.g.
+                    // the higher the first line, the bigger the space before the paragraph
+                    y += spaceBefore*0.01*dp.getFirstLineHeight();
+                } else {
+                    // negative value means the absolute spacing in points
+                    y += -spaceBefore;
+                }
+                isFirstLine = false;
+            }
+            
+            dp.setPosition(x, y);
+            dp.draw(graphics);
+            y += dp.getY();
+
+            if (paragraphs.hasNext()) {
+                double spaceAfter = p.getSpaceAfter();
+                if(spaceAfter > 0) {
+                    // positive value means percentage spacing of the height of the last line, e.g.
+                    // the higher the last line, the bigger the space after the paragraph
+                    y += spaceAfter*0.01*dp.getLastLineHeight();
+                } else {
+                    // negative value means the absolute spacing in points
+                    y += -spaceAfter;
+                }
+            }
+        }
+        return y - y0;
+    }
+
+    /**
+     * Compute the cumulative height occupied by the text
+     */
+    protected double getTextHeight(){
+        // dry-run in a 1x1 image and return the vertical advance
+        BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
+        Graphics2D graphics = img.createGraphics();
+        return drawParagraphs(graphics, 0, 0);
+    }
+
+    
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,123 @@
+/* ====================================================================
+   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 java.awt.RenderingHints;
+
+import org.apache.poi.util.Internal;
+
+
+public interface Drawable {
+    class DrawableHint extends RenderingHints.Key {
+        protected DrawableHint(int id) {
+            super(id);
+        }
+        
+        public boolean isCompatibleValue(Object val) {
+            return true;
+        }
+    }
+    
+    /**
+     * {@link DrawFactory} which will be used to draw objects into this graphics context
+     */
+    DrawableHint DRAW_FACTORY = new DrawableHint(1);
+
+    /**
+     * Key will be internally used to store affine transformation temporarily within group shapes
+     */
+    @Internal
+    DrawableHint GROUP_TRANSFORM = new DrawableHint(2);
+
+    /**
+     * Use a custom image renderer of an instance of {@link ImageRenderer}
+     */
+    DrawableHint IMAGE_RENDERER = new DrawableHint(3);
+
+    /**
+     *  how to render text:
+     *
+     *  {@link #TEXT_AS_CHARACTERS} (default) means to draw via
+     *   {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}.
+     *   This mode draws text as characters. Use it if the target graphics writes the actual
+     *   character codes instead of glyph outlines (PDFGraphics2D, SVGGraphics2D, etc.)
+     *
+     *   {@link #TEXT_AS_SHAPES} means to render via
+     *   {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}.
+     *   This mode draws glyphs as shapes and provides some advanced capabilities such as
+     *   justification and font substitution. Use it if the target graphics is an image.
+     *
+     */
+    DrawableHint TEXT_RENDERING_MODE = new DrawableHint(4);
+
+    /**
+     * PathGradientPaint needs the shape to be set.
+     * It will be achieved through setting it in the rendering hints
+     */
+    DrawableHint GRADIENT_SHAPE = new DrawableHint(5);
+
+
+    /**
+     * Internal key for caching the preset geometries
+     */
+    DrawableHint PRESET_GEOMETRY_CACHE = new DrawableHint(6);
+    
+    /**
+     * draw text via {@link java.awt.Graphics2D#drawString(java.text.AttributedCharacterIterator, float, float)}
+     */
+    int TEXT_AS_CHARACTERS = 1;
+
+    /**
+     * draw text via {@link java.awt.font.TextLayout#draw(java.awt.Graphics2D, float, float)}
+     */
+    int TEXT_AS_SHAPES = 2;
+
+    /**
+     * Use this object to resolve unknown / missing fonts when rendering slides
+     */
+    DrawableHint FONT_HANDLER = new DrawableHint(7);
+    DrawableHint FONT_FALLBACK = new DrawableHint(8);
+    DrawableHint FONT_MAP = new DrawableHint(9);
+    
+    DrawableHint GSAVE = new DrawableHint(10);
+    DrawableHint GRESTORE = new DrawableHint(11);
+    
+    
+    
+    /**
+     * Apply 2-D transforms before drawing this shape. This includes rotation and flipping.
+     *
+     * @param graphics the graphics whos transform matrix will be modified
+     */
+    void applyTransform(Graphics2D graphics);
+    
+    /**
+     * Draw this shape into the supplied canvas
+     *
+     * @param graphics the graphics to draw into
+     */
+    void draw(Graphics2D graphics);
+    
+    /**
+     * draw any content within this shape (image, text, etc.).
+     *
+     * @param graphics the graphics to draw into
+     */
+    void drawContent(Graphics2D graphics);    
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,155 @@
+/*
+ *  ====================================================================
+ *    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.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.RescaleOp;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.imageio.ImageIO;
+
+/**
+ * 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>
+ * public class MyImageRendener extends ImageRendener {
+ *
+ *     public boolean drawImage(Graphics2D graphics,Rectangle2D anchor,Insets clip) {
+ *         // draw image
+ *     }
+ *
+ *     public void loadImage(InputStream data, String contentType) throws IOException {
+ *         if ("image/wmf".equals(contentType)) {
+ *             // use Apache Batik to handle WMF
+ *         } else {
+ *             super.loadImage(data,contentType);
+ *         }
+ *     }
+ * }
+ * </code>
+ * </pre>
+ *
+ * and then pass this class to your instance of java.awt.Graphics2D:
+ *
+ * <pre>
+ * <code>
+ * graphics.setRenderingHint(Drawable.IMAGE_RENDERER, new MyImageRendener());
+ * </code>
+ * </pre>
+ */
+public class ImageRenderer {
+    protected BufferedImage img;
+
+    /**
+     * Load and buffer the image
+     *
+     * @param data the raw image stream
+     * @param contentType the content type
+     */
+    public void loadImage(InputStream data, String contentType) throws IOException {
+        img = ImageIO.read(data);
+    }
+
+    /**
+     * @return the buffered image
+     */
+    public BufferedImage getImage() {
+        return img;
+    }
+
+    /**
+     * @return the dimension of the buffered image
+     */
+    public 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)
+     */
+    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;
+    }
+
+
+    /**
+     * Render picture data into the supplied graphics
+     *
+     * @return true if the picture data was successfully rendered
+     */
+    public boolean drawImage(
+        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;
+    }
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjPoint2D.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,126 @@
+/* ====================================================================
+   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.binding;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+import com.sun.xml.internal.bind.Locatable;
+import com.sun.xml.internal.bind.annotation.XmlLocation;
+import org.xml.sax.Locator;
+
+
+/**
+ * <p>Java class for CT_AdjPoint2D complex type.
+ * 
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * 
+ * <pre>
+ * &lt;complexType name="CT_AdjPoint2D">
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;attribute name="x" use="required" type="{http://schemas.openxmlformats.org/drawingml/2006/main}ST_AdjCoordinate" />
+ *       &lt;attribute name="y" use="required" type="{http://schemas.openxmlformats.org/drawingml/2006/main}ST_AdjCoordinate" />
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ * 
+ * 
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "CT_AdjPoint2D", namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")
+public class CTAdjPoint2D
+    implements Locatable
+{
+
+    @XmlAttribute(name = "x", required = true)
+    protected String x;
+    @XmlAttribute(name = "y", required = true)
+    protected String y;
+    @XmlLocation
+    @XmlTransient
+    protected Locator locator;
+
+    /**
+     * Gets the value of the x property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getX() {
+        return x;
+    }
+
+    /**
+     * Sets the value of the x property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setX(String value) {
+        this.x = value;
+    }
+
+    public boolean isSetX() {
+        return (this.x!= null);
+    }
+
+    /**
+     * Gets the value of the y property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getY() {
+        return y;
+    }
+
+    /**
+     * Sets the value of the y property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setY(String value) {
+        this.y = value;
+    }
+
+    public boolean isSetY() {
+        return (this.y!= null);
+    }
+
+    public Locator sourceLocation() {
+        return locator;
+    }
+
+    public void setSourceLocation(Locator newLocator) {
+        locator = newLocator;
+    }
+
+}

Added: poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java
URL: http://svn.apache.org/viewvc/poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java?rev=1661322&view=auto
==============================================================================
--- poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java (added)
+++ poi/branches/common_sl/src/scratchpad/src/org/apache/poi/sl/draw/binding/CTAdjustHandleList.java Sat Feb 21 10:56:03 2015
@@ -0,0 +1,116 @@
+/* ====================================================================
+   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.binding;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElements;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+import com.sun.xml.internal.bind.Locatable;
+import com.sun.xml.internal.bind.annotation.XmlLocation;
+import org.xml.sax.Locator;
+
+
+/**
+ * <p>Java class for CT_AdjustHandleList complex type.
+ * 
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * 
+ * <pre>
+ * &lt;complexType name="CT_AdjustHandleList">
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;choice maxOccurs="unbounded" minOccurs="0">
+ *         &lt;element name="ahXY" type="{http://schemas.openxmlformats.org/drawingml/2006/main}CT_XYAdjustHandle"/>
+ *         &lt;element name="ahPolar" type="{http://schemas.openxmlformats.org/drawingml/2006/main}CT_PolarAdjustHandle"/>
+ *       &lt;/choice>
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ * 
+ * 
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "CT_AdjustHandleList", namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", propOrder = {
+    "ahXYOrAhPolar"
+})
+public class CTAdjustHandleList
+    implements Locatable
+{
+
+    @XmlElements({
+        @XmlElement(name = "ahXY", namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", type = CTXYAdjustHandle.class),
+        @XmlElement(name = "ahPolar", namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", type = CTPolarAdjustHandle.class)
+    })
+    protected List<Object> ahXYOrAhPolar;
+    @XmlLocation
+    @XmlTransient
+    protected Locator locator;
+
+    /**
+     * Gets the value of the ahXYOrAhPolar property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the ahXYOrAhPolar property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getAhXYOrAhPolar().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link CTXYAdjustHandle }
+     * {@link CTPolarAdjustHandle }
+     * 
+     * 
+     */
+    public List<Object> getAhXYOrAhPolar() {
+        if (ahXYOrAhPolar == null) {
+            ahXYOrAhPolar = new ArrayList<Object>();
+        }
+        return this.ahXYOrAhPolar;
+    }
+
+    public boolean isSetAhXYOrAhPolar() {
+        return ((this.ahXYOrAhPolar!= null)&&(!this.ahXYOrAhPolar.isEmpty()));
+    }
+
+    public void unsetAhXYOrAhPolar() {
+        this.ahXYOrAhPolar = null;
+    }
+
+    public Locator sourceLocation() {
+        return locator;
+    }
+
+    public void setSourceLocation(Locator newLocator) {
+        locator = newLocator;
+    }
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org