diff --git a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java new file mode 100644 index 000000000..44b8e4d83 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java @@ -0,0 +1,773 @@ +package org.apache.poi.hssf.usermodel; + +import java.awt.*; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.awt.font.GlyphVector; +import java.awt.font.FontRenderContext; +import java.util.Map; +import java.text.AttributedCharacterIterator; + +public class DummyGraphics2d + extends Graphics2D +{ + BufferedImage img; + private Graphics2D g2D; + + public DummyGraphics2d() + { + img = new BufferedImage(1000, 1000, 2); + g2D = (Graphics2D)img.getGraphics(); + } + + public void addRenderingHints(Map hints) + { + System.out.println( "addRenderingHinds(Map):" ); + System.out.println( " hints = " + hints ); + g2D.addRenderingHints( hints ); + } + + public void clip(Shape s) + { + System.out.println( "clip(Shape):" ); + System.out.println( " s = " + s ); + g2D.clip( s ); + } + + public void draw(Shape s) + { + System.out.println( "draw(Shape):" ); + System.out.println( "s = " + s ); + g2D.draw( s ); + } + + public void drawGlyphVector(GlyphVector g, float x, float y) + { + System.out.println( "drawGlyphVector(GlyphVector, float, float):" ); + System.out.println( "g = " + g ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawGlyphVector( g, x, y ); + } + + public void drawImage(BufferedImage img, + BufferedImageOp op, + int x, + int y) + { + System.out.println( "drawImage(BufferedImage, BufferedImageOp, x, y):" ); + System.out.println( "img = " + img ); + System.out.println( "op = " + op ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawImage( img, op, x, y ); + } + + public boolean drawImage(Image img, + AffineTransform xform, + ImageObserver obs) + { + System.out.println( "drawImage(Image,AfflineTransform,ImageObserver):" ); + System.out.println( "img = " + img ); + System.out.println( "xform = " + xform ); + System.out.println( "obs = " + obs ); + return g2D.drawImage( img, xform, obs ); + } + + public void drawRenderableImage(RenderableImage img, + AffineTransform xform) + { + System.out.println( "drawRenderableImage(RenderableImage, AfflineTransform):" ); + System.out.println( "img = " + img ); + System.out.println( "xform = " + xform ); + g2D.drawRenderableImage( img, xform ); + } + + public void drawRenderedImage(RenderedImage img, + AffineTransform xform) + { + System.out.println( "drawRenderedImage(RenderedImage, AffineTransform):" ); + System.out.println( "img = " + img ); + System.out.println( "xform = " + xform ); + g2D.drawRenderedImage( img, xform ); + } + + public void drawString(AttributedCharacterIterator iterator, + float x, float y) + { + System.out.println( "drawString(AttributedCharacterIterator):" ); + System.out.println( "iterator = " + iterator ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawString( iterator, x, y ); + } + +// public void drawString(AttributedCharacterIterator iterator, +// int x, int y) +// { +// g2D.drawString( iterator, x, y ); +// } + + public void drawString(String s, float x, float y) + { + System.out.println( "drawString(s,x,y):" ); + System.out.println( "s = " + s ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawString( s, x, y ); + } + +// public void drawString(String str, int x, int y) +// { +// g2D.drawString( str, x, y ); +// } + + public void fill(Shape s) + { + System.out.println( "fill(Shape):" ); + System.out.println( "s = " + s ); + g2D.fill( s ); + } + +// public void fill3DRect(int x, int y, int width, int height, +// boolean raised) { +// g2D.fill3DRect( x, y, width, height, raised ); +// } + + public Color getBackground() + { + System.out.println( "getBackground():" ); + return g2D.getBackground(); + } + + public Composite getComposite() + { + System.out.println( "getComposite():" ); + return g2D.getComposite(); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + System.out.println( "getDeviceConfiguration():" ); + return g2D.getDeviceConfiguration(); + } + + public FontRenderContext getFontRenderContext() + { + System.out.println( "getFontRenderContext():" ); + return g2D.getFontRenderContext(); + } + + public Paint getPaint() + { + System.out.println( "getPaint():" ); + return g2D.getPaint(); + } + + public Object getRenderingHint(RenderingHints.Key hintKey) + { + System.out.println( "getRenderingHint(RenderingHints.Key):" ); + System.out.println( "hintKey = " + hintKey ); + return g2D.getRenderingHint( hintKey ); + } + + public RenderingHints getRenderingHints() + { + System.out.println( "getRenderingHints():" ); + return g2D.getRenderingHints(); + } + + public Stroke getStroke() + { + System.out.println( "getStroke():" ); + return g2D.getStroke(); + } + + public AffineTransform getTransform() + { + System.out.println( "getTransform():" ); + return g2D.getTransform(); + } + + public boolean hit(Rectangle rect, + Shape s, + boolean onStroke) + { + System.out.println( "hit(Rectangle, Shape, onStroke):" ); + System.out.println( "rect = " + rect ); + System.out.println( "s = " + s ); + System.out.println( "onStroke = " + onStroke ); + return g2D.hit( rect, s, onStroke ); + } + + public void rotate(double theta) + { + System.out.println( "rotate(theta):" ); + System.out.println( "theta = " + theta ); + g2D.rotate( theta ); + } + + public void rotate(double theta, double x, double y) + { + System.out.println( "rotate(double,double,double):" ); + System.out.println( "theta = " + theta ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.rotate( theta, x, y ); + } + + public void scale(double sx, double sy) + { + System.out.println( "scale(double,double):" ); + System.out.println( "sx = " + sx ); + System.out.println( "sy" ); + g2D.scale( sx, sy ); + } + + public void setBackground(Color color) + { + System.out.println( "setBackground(Color):" ); + System.out.println( "color = " + color ); + g2D.setBackground( color ); + } + + public void setComposite(Composite comp) + { + System.out.println( "setComposite(Composite):" ); + System.out.println( "comp = " + comp ); + g2D.setComposite( comp ); + } + + public void setPaint( Paint paint ) + { + System.out.println( "setPain(Paint):" ); + System.out.println( "paint = " + paint ); + g2D.setPaint( paint ); + } + + public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) + { + System.out.println( "setRenderingHint(RenderingHints.Key, Object):" ); + System.out.println( "hintKey = " + hintKey ); + System.out.println( "hintValue = " + hintValue ); + g2D.setRenderingHint( hintKey, hintValue ); + } + + public void setRenderingHints(Map hints) + { + System.out.println( "setRenderingHints(Map):" ); + System.out.println( "hints = " + hints ); + g2D.setRenderingHints( hints ); + } + + public void setStroke(Stroke s) + { + System.out.println( "setStroke(Stoke):" ); + System.out.println( "s = " + s ); + g2D.setStroke( s ); + } + + public void setTransform(AffineTransform Tx) + { + System.out.println( "setTransform():" ); + System.out.println( "Tx = " + Tx ); + g2D.setTransform( Tx ); + } + + public void shear(double shx, double shy) + { + System.out.println( "shear(shx, dhy):" ); + System.out.println( "shx = " + shx ); + System.out.println( "shy = " + shy ); + g2D.shear( shx, shy ); + } + + public void transform(AffineTransform Tx) + { + System.out.println( "transform(AffineTransform):" ); + System.out.println( "Tx = " + Tx ); + g2D.transform( Tx ); + } + + public void translate(double tx, double ty) + { + System.out.println( "translate(double, double):" ); + System.out.println( "tx = " + tx ); + System.out.println( "ty = " + ty ); + g2D.translate( tx, ty ); + } + +// public void translate(int x, int y) +// { +// g2D.translate( x, y ); +// } + + public void clearRect(int x, int y, int width, int height) + { + System.out.println( "clearRect(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.clearRect( x, y, width, height ); + } + + public void clipRect(int x, int y, int width, int height) + { + System.out.println( "clipRect(int, int, int, int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.clipRect( x, y, width, height ); + } + + public void copyArea(int x, int y, int width, int height, + int dx, int dy) + { + System.out.println( "copyArea(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.copyArea( x, y, width, height, dx, dy ); + } + + public Graphics create() + { + System.out.println( "create():" ); + return g2D.create(); + } + + public Graphics create(int x, int y, int width, int height) { + System.out.println( "create(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + return g2D.create( x, y, width, height ); + } + + public void dispose() + { + System.out.println( "dispose():" ); + g2D.dispose(); + } + + public void draw3DRect(int x, int y, int width, int height, + boolean raised) { + System.out.println( "draw3DRect(int,int,int,int,boolean):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "raised = " + raised ); + g2D.draw3DRect( x, y, width, height, raised ); + } + + public void drawArc(int x, int y, int width, int height, + int startAngle, int arcAngle) + { + System.out.println( "drawArc(int,int,int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "startAngle = " + startAngle ); + System.out.println( "arcAngle = " + arcAngle ); + g2D.drawArc( x, y, width, height, startAngle, arcAngle ); + } + + public void drawBytes(byte data[], int offset, int length, int x, int y) { + System.out.println( "drawBytes(byte[],int,int,int,int):" ); + System.out.println( "data = " + data ); + System.out.println( "offset = " + offset ); + System.out.println( "length = " + length ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawBytes( data, offset, length, x, y ); + } + + public void drawChars(char data[], int offset, int length, int x, int y) { + System.out.println( "drawChars(data,int,int,int,int):" ); + System.out.println( "data = " + data ); + System.out.println( "offset = " + offset ); + System.out.println( "length = " + length ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawChars( data, offset, length, x, y ); + } + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) + { + System.out.println( "drawImage(Image,int,int,int,int,int,int,int,int,ImageObserver):" ); + System.out.println( "img = " + img ); + System.out.println( "dx1 = " + dx1 ); + System.out.println( "dy1 = " + dy1 ); + System.out.println( "dx2 = " + dx2 ); + System.out.println( "dy2 = " + dy2 ); + System.out.println( "sx1 = " + sx1 ); + System.out.println( "sy1 = " + sy1 ); + System.out.println( "sx2 = " + sx2 ); + System.out.println( "sy2 = " + sy2 ); + System.out.println( "observer = " + observer ); + return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer ); + } + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, + ImageObserver observer) + { + System.out.println( "drawImage(Image,int,int,int,int,int,int,int,int,Color,ImageObserver):" ); + System.out.println( "img = " + img ); + System.out.println( "dx1 = " + dx1 ); + System.out.println( "dy1 = " + dy1 ); + System.out.println( "dx2 = " + dx2 ); + System.out.println( "dy2 = " + dy2 ); + System.out.println( "sx1 = " + sx1 ); + System.out.println( "sy1 = " + sy1 ); + System.out.println( "sx2 = " + sx2 ); + System.out.println( "sy2 = " + sy2 ); + System.out.println( "bgcolor = " + bgcolor ); + System.out.println( "observer = " + observer ); + return g2D.drawImage( img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer ); + } + + public boolean drawImage(Image img, int x, int y, + Color bgcolor, + ImageObserver observer) + { + System.out.println( "drawImage(Image,int,int,Color,ImageObserver):" ); + System.out.println( "img = " + img ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "bgcolor = " + bgcolor ); + System.out.println( "observer = " + observer ); + return g2D.drawImage( img, x, y, bgcolor, observer ); + } + + public boolean drawImage(Image img, int x, int y, + ImageObserver observer) + { + System.out.println( "drawImage(Image,int,int,observer):" ); + System.out.println( "img = " + img ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "observer = " + observer ); + return g2D.drawImage( img, x, y, observer ); + } + + public boolean drawImage(Image img, int x, int y, + int width, int height, + Color bgcolor, + ImageObserver observer) + { + System.out.println( "drawImage(Image,int,int,int,int,Color,ImageObserver):" ); + System.out.println( "img = " + img ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "bgcolor = " + bgcolor ); + System.out.println( "observer = " + observer ); + return g2D.drawImage( img, x, y, width, height, bgcolor, observer ); + } + + public boolean drawImage(Image img, int x, int y, + int width, int height, + ImageObserver observer) + { + System.out.println( "drawImage(Image,int,int,width,height,observer):" ); + System.out.println( "img = " + img ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "observer = " + observer ); + return g2D.drawImage( img, x, y, width, height, observer ); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + System.out.println( "drawLine(int,int,int,int):" ); + System.out.println( "x1 = " + x1 ); + System.out.println( "y1 = " + y1 ); + System.out.println( "x2 = " + x2 ); + System.out.println( "y2 = " + y2 ); + g2D.drawLine( x1, y1, x2, y2 ); + } + + public void drawOval(int x, int y, int width, int height) + { + System.out.println( "drawOval(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.drawOval( x, y, width, height ); + } + + public void drawPolygon(Polygon p) { + System.out.println( "drawPolygon(Polygon):" ); + System.out.println( "p = " + p ); + g2D.drawPolygon( p ); + } + + public void drawPolygon(int xPoints[], int yPoints[], + int nPoints) + { + System.out.println( "drawPolygon(int[],int[],int):" ); + System.out.println( "xPoints = " + xPoints ); + System.out.println( "yPoints = " + yPoints ); + System.out.println( "nPoints = " + nPoints ); + g2D.drawPolygon( xPoints, yPoints, nPoints ); + } + + public void drawPolyline(int xPoints[], int yPoints[], + int nPoints) + { + System.out.println( "drawPolyline(int[],int[],int):" ); + System.out.println( "xPoints = " + xPoints ); + System.out.println( "yPoints = " + yPoints ); + System.out.println( "nPoints = " + nPoints ); + g2D.drawPolyline( xPoints, yPoints, nPoints ); + } + + public void drawRect(int x, int y, int width, int height) { + System.out.println( "drawRect(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.drawRect( x, y, width, height ); + } + + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + System.out.println( "drawRoundRect(int,int,int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "arcWidth = " + arcWidth ); + System.out.println( "arcHeight = " + arcHeight ); + g2D.drawRoundRect( x, y, width, height, arcWidth, arcHeight ); + } + + public void drawString(AttributedCharacterIterator iterator, + int x, int y) + { + System.out.println( "drawString(AttributedCharacterIterator,int,int):" ); + System.out.println( "iterator = " + iterator ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawString( iterator, x, y ); + } + + public void drawString(String str, int x, int y) + { + System.out.println( "drawString(str,int,int):" ); + System.out.println( "str = " + str ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.drawString( str, x, y ); + } + + public void fill3DRect(int x, int y, int width, int height, + boolean raised) { + System.out.println( "fill3DRect(int,int,int,int,boolean):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "raised = " + raised ); + g2D.fill3DRect( x, y, width, height, raised ); + } + + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) + { + System.out.println( "fillArc(int,int,int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + System.out.println( "startAngle = " + startAngle ); + System.out.println( "arcAngle = " + arcAngle ); + g2D.fillArc( x, y, width, height, startAngle, arcAngle ); + } + + public void fillOval(int x, int y, int width, int height) + { + System.out.println( "fillOval(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.fillOval( x, y, width, height ); + } + + public void fillPolygon(Polygon p) { + System.out.println( "fillPolygon(Polygon):" ); + System.out.println( "p = " + p ); + g2D.fillPolygon( p ); + } + + public void fillPolygon(int xPoints[], int yPoints[], + int nPoints) + { + System.out.println( "fillPolygon(int[],int[],int):" ); + System.out.println( "xPoints = " + xPoints ); + System.out.println( "yPoints = " + yPoints ); + System.out.println( "nPoints = " + nPoints ); + g2D.fillPolygon( xPoints, yPoints, nPoints ); + } + + public void fillRect(int x, int y, int width, int height) + { + System.out.println( "fillRect(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.fillRect( x, y, width, height ); + } + + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + System.out.println( "fillRoundRect(int,int,int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.fillRoundRect( x, y, width, height, arcWidth, arcHeight ); + } + + public void finalize() { + System.out.println( "finalize():" ); + g2D.finalize(); + } + + public Shape getClip() + { + System.out.println( "getClip():" ); + return g2D.getClip(); + } + + public Rectangle getClipBounds() + { + System.out.println( "getClipBounds():" ); + return g2D.getClipBounds(); + } + + public Rectangle getClipBounds(Rectangle r) { + System.out.println( "getClipBounds(Rectangle):" ); + System.out.println( "r = " + r ); + return g2D.getClipBounds( r ); + } + + public Rectangle getClipRect() { + System.out.println( "getClipRect():" ); + return g2D.getClipRect(); + } + + public Color getColor() + { + System.out.println( "getColor():" ); + return g2D.getColor(); + } + + public Font getFont() + { + System.out.println( "getFont():" ); + return g2D.getFont(); + } + + public FontMetrics getFontMetrics() { + System.out.println( "getFontMetrics():" ); + return g2D.getFontMetrics(); + } + + public FontMetrics getFontMetrics(Font f) + { + System.out.println( "getFontMetrics():" ); + return g2D.getFontMetrics( f ); + } + + public boolean hitClip(int x, int y, int width, int height) { + System.out.println( "hitClip(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + return g2D.hitClip( x, y, width, height ); + } + + public void setClip(Shape clip) + { + System.out.println( "setClip(Shape):" ); + System.out.println( "clip = " + clip ); + g2D.setClip( clip ); + } + + public void setClip(int x, int y, int width, int height) + { + System.out.println( "setClip(int,int,int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + System.out.println( "width = " + width ); + System.out.println( "height = " + height ); + g2D.setClip( x, y, width, height ); + } + + public void setColor(Color c) + { + System.out.println( "setColor():" ); + System.out.println( "c = " + c ); + g2D.setColor( c ); + } + + public void setFont(Font font) + { + System.out.println( "setFont(Font):" ); + System.out.println( "font = " + font ); + g2D.setFont( font ); + } + + public void setPaintMode() + { + System.out.println( "setPaintMode():" ); + g2D.setPaintMode(); + } + + public void setXORMode(Color c1) + { + System.out.println( "setXORMode(Color):" ); + System.out.println( "c1 = " + c1 ); + g2D.setXORMode( c1 ); + } + + public String toString() { + System.out.println( "toString():" ); + return g2D.toString(); + } + + public void translate(int x, int y) + { + System.out.println( "translate(int,int):" ); + System.out.println( "x = " + x ); + System.out.println( "y = " + y ); + g2D.translate( x, y ); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java new file mode 100644 index 000000000..fa8065a44 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics.java @@ -0,0 +1,450 @@ +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.poi.hssf.util.HSSFColor; + +import java.awt.*; +import java.awt.image.ImageObserver; +import java.text.AttributedCharacterIterator; + +/** + * Translates Graphics calls into escher calls. The translation is lossy so + * many features are not supported and some just aren't implemented yet. If + * in doubt test the specific calls you wish to make. Graphics calls are + * always performed into an EscherGroup so one will need to be created. + *

+ * Important: + *

+ * One important concept worth considering is that of font size. One of the + * difficulties in converting Graphics calls into escher drawing calls is that + * Excel does not have the concept of absolute pixel positions. It measures + * it's cell widths in 'characters' and the cell heights in points. + * Unfortunately it's not defined exactly what a type of character it's + * measuring. Presumably this is due to the fact that the Excel will be + * using different fonts on different platforms or even within the same + * platform. + *

+ * Because of this constraint we've had to calculate the + * verticalPointsPerPixel. This the amount the font should be scaled by when + * you issue commands such as drawString(). A good way to calculate this + * is to use the follow formula: + *

+ *

+ *      multipler = groupHeightInPoints / heightOfGroup
+ * 
+ *

+ * The height of the group is calculated fairly simply by calculating the + * difference between the y coordinates of the bounding box of the shape. The + * height of the group can be calculated by using a convenience called + * HSSFClientAnchor.getAnchorHeightInPoints(). + *

+ * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class EscherGraphics + extends Graphics +{ + private HSSFShapeGroup escherGroup; + private HSSFWorkbook workbook; + private float verticalPointsPerPixel = 1.0f; + private float verticalPixelsPerPoint; + private Color foreground; + private Color background = Color.white; + private Font font; + private static POILogger logger = POILogFactory.getLogger(EscherGraphics.class); + + /** + * Construct an escher graphics object. + * + * @param escherGroup The escher group to write the graphics calls into. + * @param workbook The workbook we are using. + * @param forecolor The foreground color to use as default. + * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.). + */ + public EscherGraphics(HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor, float verticalPointsPerPixel ) + { + this.escherGroup = escherGroup; + this.workbook = workbook; + this.verticalPointsPerPixel = verticalPointsPerPixel; + this.verticalPixelsPerPoint = 1 / verticalPointsPerPixel; + this.font = new Font("Arial", 0, 10); + this.foreground = forecolor; +// background = backcolor; + } + + /** + * Constructs an escher graphics object. + * + * @param escherGroup The escher group to write the graphics calls into. + * @param workbook The workbook we are using. + * @param foreground The foreground color to use as default. + * @param verticalPointsPerPixel The font multiplier. (See class description for information on how this works.). + * @param font The font to use. + */ + EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color foreground, Font font, float verticalPointsPerPixel ) + { + this.escherGroup = escherGroup; + this.workbook = workbook; + this.foreground = foreground; +// this.background = background; + this.font = font; + this.verticalPointsPerPixel = verticalPointsPerPixel; + this.verticalPixelsPerPoint = 1 / verticalPointsPerPixel; + } + + /** + * Constructs an escher graphics object. + * + * @param escherGroup The escher group to write the graphics calls into. + * @param workbook The workbook we are using. + * @param forecolor The default foreground color. + */ +// public EscherGraphics( HSSFShapeGroup escherGroup, HSSFWorkbook workbook, Color forecolor) +// { +// this(escherGroup, workbook, forecolor, 1.0f); +// } + + + public void clearRect(int x, int y, int width, int height) + { + Color color = foreground; + setColor(background); + fillRect(x,y,width,height); + setColor(color); + } + + public void clipRect(int x, int y, int width, int height) + { + logger.log(POILogger.WARN,"clipRect not supported"); + } + + public void copyArea(int x, int y, int width, int height, int dx, int dy) + { + logger.log(POILogger.WARN,"copyArea not supported"); + } + + public Graphics create() + { + EscherGraphics g = new EscherGraphics(escherGroup, workbook, + foreground, font, verticalPointsPerPixel ); + return g; + } + + public void dispose() + { + } + + public void drawArc(int x, int y, int width, int height, + int startAngle, int arcAngle) + { + logger.log(POILogger.WARN,"drawArc not supported"); + } + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + Color bgcolor, + ImageObserver observer) + { + logger.log(POILogger.WARN,"drawImage not supported"); + + return true; + } + + public boolean drawImage(Image img, + int dx1, int dy1, int dx2, int dy2, + int sx1, int sy1, int sx2, int sy2, + ImageObserver observer) + { + logger.log(POILogger.WARN,"drawImage not supported"); + return true; + } + + public boolean drawImage(Image image, int i, int j, int k, int l, Color color, ImageObserver imageobserver) + { + return drawImage(image, i, j, i + k, j + l, 0, 0, image.getWidth(imageobserver), image.getHeight(imageobserver), color, imageobserver); + } + + public boolean drawImage(Image image, int i, int j, int k, int l, ImageObserver imageobserver) + { + return drawImage(image, i, j, i + k, j + l, 0, 0, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver); + } + + public boolean drawImage(Image image, int i, int j, Color color, ImageObserver imageobserver) + { + return drawImage(image, i, j, image.getWidth(imageobserver), image.getHeight(imageobserver), color, imageobserver); + } + + public boolean drawImage(Image image, int i, int j, ImageObserver imageobserver) + { + return drawImage(image, i, j, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor(x1, y1, x2, y2) ); + shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE); + shape.setLineWidth(0); + shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + } + + public void drawOval(int x, int y, int width, int height) + { + HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor(x,y,x+width,y+height) ); + shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL); + shape.setLineWidth(0); + shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + shape.setNoFill(true); + } + + public void drawPolygon(int xPoints[], int yPoints[], + int nPoints) + { + int right = findBiggest(xPoints); + int bottom = findBiggest(yPoints); + int left = findSmallest(xPoints); + int top = findSmallest(yPoints); + HSSFPolygon shape = escherGroup.createPolygon(new HSSFChildAnchor(left,top,right,bottom) ); + shape.setPolygonDrawArea(right - left, bottom - top); + shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints, -top)); + shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + shape.setLineWidth(0); + shape.setNoFill(true); + } + + private int[] addToAll( int[] values, int amount ) + { + int[] result = new int[values.length]; + for ( int i = 0; i < values.length; i++ ) + result[i] = values[i] + amount; + return result; + } + + public void drawPolyline(int xPoints[], int yPoints[], + int nPoints) + { + logger.log(POILogger.WARN,"drawPolyline not supported"); + } + + public void drawRect(int x, int y, int width, int height) + { + logger.log(POILogger.WARN,"drawRect not supported"); + } + + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + logger.log(POILogger.WARN,"drawRoundRect not supported"); + } + + public void drawString(String str, int x, int y) + { + if (str == null || str.equals("")) + return; + + Font excelFont = font; + if ( font.getName().equals( "SansSerif" ) ) + { + excelFont = new Font( "Arial", font.getStyle(), (int) ( font.getSize() / verticalPixelsPerPoint ) ); + } + else + { + excelFont = new Font( font.getName(), font.getStyle(), (int) ( font.getSize() / verticalPixelsPerPoint )); + } + FontDetails d = StaticFontMetrics.getFontDetails( excelFont ); + int width = (int) ( (d.getStringWidth( str ) * 2.5) + 12 ); + int height = (int) ( ( font.getSize() * 2.0 * verticalPixelsPerPoint ) + 6 ); + y -= ( font.getSize() * verticalPixelsPerPoint ); // we want to draw the shape from the top-left + HSSFTextbox textbox = escherGroup.createTextbox( new HSSFChildAnchor( x, y, x + width, y + height ) ); + textbox.setNoFill( true ); + textbox.setLineStyle( HSSFShape.LINESTYLE_NONE ); + HSSFRichTextString s = new HSSFRichTextString( str ); + HSSFFont hssfFont = matchFont( excelFont ); + s.applyFont( hssfFont ); + textbox.setString( s ); + } + + private HSSFFont matchFont( Font font ) + { + HSSFColor hssfColor = workbook.getCustomPalette() + .findColor((byte)foreground.getRed(), (byte)foreground.getGreen(), (byte)foreground.getBlue()); + if (hssfColor == null) + hssfColor = workbook.getCustomPalette().findSimilarColor((byte)foreground.getRed(), (byte)foreground.getGreen(), (byte)foreground.getBlue()); + boolean bold = (font.getStyle() & Font.BOLD) != 0; + boolean italic = (font.getStyle() & Font.ITALIC) != 0; + HSSFFont hssfFont = workbook.findFont(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0, + hssfColor.getIndex(), + (short)(font.getSize() * 20), + font.getName(), + italic, + false, + (short)0, + (byte)0); + if (hssfFont == null) + { + hssfFont = workbook.createFont(); + hssfFont.setBoldweight(bold ? HSSFFont.BOLDWEIGHT_BOLD : 0); + hssfFont.setColor(hssfColor.getIndex()); + hssfFont.setFontHeight((short)(font.getSize() * 20)); + hssfFont.setFontName(font.getName()); + hssfFont.setItalic(italic); + hssfFont.setStrikeout(false); + hssfFont.setTypeOffset((short) 0); + hssfFont.setUnderline((byte) 0); + } + + return hssfFont; + } + + + public void drawString(AttributedCharacterIterator iterator, + int x, int y) + { + logger.log(POILogger.WARN,"drawString not supported"); + } + + public void fillArc(int x, int y, int width, int height, + int startAngle, int arcAngle) + { + logger.log(POILogger.WARN,"fillArc not supported"); + } + + public void fillOval(int x, int y, int width, int height) + { + HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor( x, y, x + width, y + height ) ); + shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL); + shape.setLineStyle(HSSFShape.LINESTYLE_NONE); + shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + } + + public void fillPolygon(int xPoints[], int yPoints[], + int nPoints) + { + int right = findBiggest(xPoints); + int bottom = findBiggest(yPoints); + int left = findSmallest(xPoints); + int top = findSmallest(yPoints); + HSSFPolygon shape = escherGroup.createPolygon(new HSSFChildAnchor(left,top,right,bottom) ); + shape.setPolygonDrawArea(right - left, bottom - top); + shape.setPoints(addToAll(xPoints, -left), addToAll(yPoints, -top)); + shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + } + + private int findBiggest( int[] values ) + { + int result = Integer.MIN_VALUE; + for ( int i = 0; i < values.length; i++ ) + { + if (values[i] > result) + result = values[i]; + } + return result; + } + + private int findSmallest( int[] values ) + { + int result = Integer.MAX_VALUE; + for ( int i = 0; i < values.length; i++ ) + { + if (values[i] < result) + result = values[i]; + } + return result; + } + + public void fillRect(int x, int y, int width, int height) + { + HSSFSimpleShape shape = escherGroup.createShape(new HSSFChildAnchor( x, y, x + width, y + height ) ); + shape.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE); + shape.setLineStyle(HSSFShape.LINESTYLE_NONE); + shape.setFillColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + shape.setLineStyleColor(foreground.getRed(), foreground.getGreen(), foreground.getBlue()); + } + + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + logger.log(POILogger.WARN,"fillRoundRect not supported"); + } + + public Shape getClip() + { + return getClipBounds(); + } + + public Rectangle getClipBounds() + { + return null; + } + + public Rectangle getClipRect() + { + return getClipBounds(); + } + + public Color getColor() + { + return foreground; + } + + public Font getFont() + { + return font; + } + + public FontMetrics getFontMetrics(Font f) + { + return Toolkit.getDefaultToolkit().getFontMetrics(f); + } + + public void setClip(int x, int y, int width, int height) + { + setClip(((Shape) (new Rectangle(x,y,width,height)))); + } + + public void setClip(Shape shape) + { + // ignore... not implemented + } + + public void setColor(Color color) + { + foreground = color; + } + + public void setFont(Font f) + { + font = f; + } + + public void setPaintMode() + { + logger.log(POILogger.WARN,"setPaintMode not supported"); + } + + public void setXORMode(Color color) + { + logger.log(POILogger.WARN,"setXORMode not supported"); + } + + public void translate(int x, int y) + { + logger.log(POILogger.WARN,"translate not supported"); + } + + public Color getBackground() + { + return background; + } + + public void setBackground( Color background ) + { + this.background = background; + } + + +} + diff --git a/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java new file mode 100644 index 000000000..f3298d79e --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/EscherGraphics2d.java @@ -0,0 +1,567 @@ +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.GeneralPath; +import java.awt.image.BufferedImage; +import java.awt.image.BufferedImageOp; +import java.awt.image.ImageObserver; +import java.awt.image.RenderedImage; +import java.awt.image.renderable.RenderableImage; +import java.text.AttributedCharacterIterator; +import java.util.Map; + +/** + * Translates Graphics2d calls into escher calls. The translation is lossy so + * many features are not supported and some just aren't implemented yet. If + * in doubt test the specific calls you wish to make. Graphics calls are + * always drawn into an EscherGroup so one will need to be created. + *

+ * Important: + *

+ * One important concept worth considering is that of font size. One of the + * difficulties in converting Graphics calls into escher drawing calls is that + * Excel does not have the concept of absolute pixel positions. It measures + * it's cell widths in 'characters' and the cell heights in points. + * Unfortunately it's not defined exactly what a type of character it's + * measuring. Presumably this is due to the fact that the Excel will be + * using different fonts on different platforms or even within the same + * platform. + *

+ * Because of this constraint you have to calculate the verticalPointsPerPixel. + * This the amount the font should be scaled by when + * you issue commands such as drawString(). A good way to calculate this + * is to use the follow formula: + *

+ *

+ *      multipler = groupHeightInPoints / heightOfGroup
+ * 
+ *

+ * The height of the group is calculated fairly simply by calculating the + * difference between the y coordinates of the bounding box of the shape. The + * height of the group can be calculated by using a convenience called + * HSSFClientAnchor.getAnchorHeightInPoints(). + *

+ * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class EscherGraphics2d extends Graphics2D +{ + private EscherGraphics escherGraphics; + private BufferedImage img; + private AffineTransform trans; + private Stroke stroke; + private Paint paint; + private Shape deviceclip; + private POILogger logger = POILogFactory.getLogger(getClass()); + + /** + * Constructs one escher graphics object from an escher graphics object. + * + * @param escherGraphics the original EscherGraphics2d object to copy + */ + public EscherGraphics2d(EscherGraphics escherGraphics) + { + this.escherGraphics = escherGraphics; + setImg( new BufferedImage(1, 1, 2) ); + setColor(Color.black); + } + + public void addRenderingHints(Map map) + { + getG2D().addRenderingHints(map); + } + + public void clearRect(int i, int j, int k, int l) + { + Paint paint1 = getPaint(); + setColor(getBackground()); + fillRect(i, j, k, l); + setPaint(paint1); + } + + public void clip(Shape shape) + { + if(getDeviceclip() != null) + { + Area area = new Area(getClip()); + if(shape != null) + area.intersect(new Area(shape)); + shape = area; + } + setClip(shape); + } + + public void clipRect(int x, int y, int width, int height) + { + clip(new Rectangle(x,y,width,height)); + } + + public void copyArea(int x, int y, int width, int height, + int dx, int dy) + { + getG2D().copyArea(x,y,width,height,dx,dy); + } + + public Graphics create() + { + EscherGraphics2d g2d = new EscherGraphics2d(escherGraphics); + return g2d; + } + + public void dispose() + { + getEscherGraphics().dispose(); + getG2D().dispose(); + getImg().flush(); + } + + public void draw(Shape shape) + { + logger.log(POILogger.WARN,"copyArea not supported"); + } + + public void drawArc(int x, int y, int width, int height, + int startAngle, int arcAngle) + { + draw(new java.awt.geom.Arc2D.Float(x, y, width, height, startAngle, arcAngle, 0)); + } + + public void drawGlyphVector(GlyphVector g, float x, float y) + { + fill(g.getOutline(x, y)); + } + + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, + int sx2, int sy2, Color bgColor, ImageObserver imageobserver) + { + logger.log(POILogger.WARN,"drawImage() not supported"); + return true; + } + + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, + int sx2, int sy2, ImageObserver imageobserver) + { + logger.log(POILogger.WARN,"drawImage() not supported"); + return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, imageobserver); + } + public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, Color bgColor, ImageObserver imageobserver) + { + logger.log(POILogger.WARN,"drawImage() not supported"); + return true; + } + + public boolean drawImage(Image img, int x, int y, + int width, int height, + ImageObserver observer) + { + return drawImage(img, x,y,width,height, null, observer); + } + + public boolean drawImage(Image image, int x, int y, Color bgColor, ImageObserver imageobserver) + { + return drawImage(image, x, y, image.getWidth(imageobserver), image.getHeight(imageobserver), bgColor, imageobserver); + } + + public boolean drawImage(Image image, int x, int y, ImageObserver imageobserver) + { + return drawImage(image, x, y, image.getWidth(imageobserver), image.getHeight(imageobserver), imageobserver); + } + + public boolean drawImage(Image image, AffineTransform affinetransform, ImageObserver imageobserver) + { + AffineTransform affinetransform1 = (AffineTransform)getTrans().clone(); + getTrans().concatenate(affinetransform); + drawImage(image, 0, 0, imageobserver); + setTrans( affinetransform1 ); + return true; + } + + public void drawImage(BufferedImage bufferedimage, BufferedImageOp op, int x, int y) + { + BufferedImage img = op.filter(bufferedimage, null); + drawImage(((Image) (img)), new AffineTransform(1.0F, 0.0F, 0.0F, 1.0F, x, y), null); + } + + public void drawLine(int x1, int y1, int x2, int y2) + { + getEscherGraphics().drawLine(x1,y1,x2,y2); +// draw(new GeneralPath(new java.awt.geom.Line2D.Float(x1, y1, x2, y2))); + } + + public void drawOval(int x, int y, int width, int height) + { + getEscherGraphics().drawOval(x,y,width,height); +// draw(new java.awt.geom.Ellipse2D.Float(x, y, width, height)); + } + + public void drawPolygon(int xPoints[], int yPoints[], + int nPoints) + { + getEscherGraphics().drawPolygon(xPoints, yPoints, nPoints); + } + + public void drawPolyline(int xPoints[], int yPoints[], int nPoints) + { + if(nPoints > 0) + { + GeneralPath generalpath = new GeneralPath(); + generalpath.moveTo(xPoints[0], yPoints[0]); + for(int j = 1; j < nPoints; j++) + generalpath.lineTo(xPoints[j], yPoints[j]); + + draw(generalpath); + } + } + + public void drawRect(int x, int y, int width, int height) + { + escherGraphics.drawRect(x,y,width,height); + } + + public void drawRenderableImage(RenderableImage renderableimage, AffineTransform affinetransform) + { + drawRenderedImage(renderableimage.createDefaultRendering(), affinetransform); + } + + public void drawRenderedImage(RenderedImage renderedimage, AffineTransform affinetransform) + { + BufferedImage bufferedimage = new BufferedImage(renderedimage.getColorModel(), renderedimage.getData().createCompatibleWritableRaster(), false, null); + bufferedimage.setData(renderedimage.getData()); + drawImage(bufferedimage, affinetransform, null); + } + + public void drawRoundRect(int i, int j, int k, int l, int i1, int j1) + { + draw(new java.awt.geom.RoundRectangle2D.Float(i, j, k, l, i1, j1)); + } + + public void drawString(String string, float x, float y) + { + getEscherGraphics().drawString(string, (int)x, (int)y); + } + + public void drawString(String string, int x, int y) + { + getEscherGraphics().drawString(string, x, y); + } + + public void drawString(AttributedCharacterIterator attributedcharacteriterator, float x, float y) + { + TextLayout textlayout = new TextLayout(attributedcharacteriterator, getFontRenderContext()); + Paint paint1 = getPaint(); + setColor(getColor()); + fill(textlayout.getOutline(AffineTransform.getTranslateInstance(x, y))); + setPaint(paint1); + } + + public void drawString(AttributedCharacterIterator attributedcharacteriterator, int x, int y) + { + drawString(attributedcharacteriterator, x, y); + } + + public void fill(Shape shape) + { + logger.log(POILogger.WARN,"fill(Shape) not supported"); + } + + public void fillArc(int i, int j, int k, int l, int i1, int j1) + { + fill(new java.awt.geom.Arc2D.Float(i, j, k, l, i1, j1, 2)); + } + + public void fillOval(int x, int y, int width, int height) + { + escherGraphics.fillOval(x,y,width,height); + } + + /** + * Fills a closed polygon defined by + * arrays of x and y coordinates. + *

+ * This method draws the polygon defined by nPoint line + * segments, where the first nPoint - 1 + * line segments are line segments from + * (xPoints[i - 1], yPoints[i - 1]) + * to (xPoints[i], yPoints[i]), for + * 1 ≤ i ≤ nPoints. + * The figure is automatically closed by drawing a line connecting + * the final point to the first point, if those points are different. + *

+ * The area inside the polygon is defined using an + * even-odd fill rule, also known as the alternating rule. + * @param xPoints a an array of x coordinates. + * @param yPoints a an array of y coordinates. + * @param nPoints a the total number of points. + * @see java.awt.Graphics#drawPolygon(int[], int[], int) + */ + public void fillPolygon(int xPoints[], int yPoints[], int nPoints) + { + escherGraphics.fillPolygon(xPoints, yPoints, nPoints); + } + + public void fillRect(int x, int y, int width, int height) + { + getEscherGraphics().fillRect(x,y,width,height); + } + + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight) + { + fill(new java.awt.geom.RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight)); + } + + public Color getBackground() + { + return getEscherGraphics().getBackground(); + } + + public Shape getClip() + { + try + { + return getTrans().createInverse().createTransformedShape(getDeviceclip()); + } + catch(Exception _ex) + { + return null; + } + } + + public Rectangle getClipBounds() + { + if(getDeviceclip() != null) + return getClip().getBounds(); + else + return null; + } + + public Color getColor() + { + return escherGraphics.getColor(); + } + + public Composite getComposite() + { + return getG2D().getComposite(); + } + + public GraphicsConfiguration getDeviceConfiguration() + { + return getG2D().getDeviceConfiguration(); + } + + public Font getFont() + { + return getEscherGraphics().getFont(); + } + + public FontMetrics getFontMetrics(Font font) + { + return getEscherGraphics().getFontMetrics(font); + } + + public FontRenderContext getFontRenderContext() + { + getG2D().setTransform(getTrans()); + return getG2D().getFontRenderContext(); + } + + public Paint getPaint() + { + return paint; + } + + public Object getRenderingHint(java.awt.RenderingHints.Key key) + { + return getG2D().getRenderingHint(key); + } + + public RenderingHints getRenderingHints() + { + return getG2D().getRenderingHints(); + } + + public Stroke getStroke() + { + return stroke; + } + + public AffineTransform getTransform() + { + return (AffineTransform)getTrans().clone(); + } + + public boolean hit(Rectangle rectangle, Shape shape, boolean flag) + { + getG2D().setTransform(getTrans()); + getG2D().setStroke(getStroke()); + getG2D().setClip(getClip()); + return getG2D().hit(rectangle, shape, flag); + } + + public void rotate(double d) + { + getTrans().rotate(d); + } + + public void rotate(double d, double d1, double d2) + { + getTrans().rotate(d, d1, d2); + } + + public void scale(double d, double d1) + { + getTrans().scale(d, d1); + } + + public void setBackground(Color c) + { + getEscherGraphics().setBackground(c); + } + + public void setClip(int i, int j, int k, int l) + { + setClip(((Shape) (new Rectangle(i, j, k, l)))); + } + + public void setClip(Shape shape) + { + setDeviceclip( getTrans().createTransformedShape(shape) ); + } + + public void setColor(Color c) + { + escherGraphics.setColor(c); + } + + public void setComposite(Composite composite) + { + getG2D().setComposite(composite); + } + + public void setFont(Font font) + { + getEscherGraphics().setFont(font); + } + + public void setPaint(Paint paint1) + { + if(paint1 != null) + { + paint = paint1; + if(paint1 instanceof Color) + setColor( (Color)paint1 ); + } + } + + public void setPaintMode() + { + getEscherGraphics().setPaintMode(); + } + + public void setRenderingHint(java.awt.RenderingHints.Key key, Object obj) + { + getG2D().setRenderingHint(key, obj); + } + + public void setRenderingHints(Map map) + { + getG2D().setRenderingHints(map); + } + + public void setStroke(Stroke s) + { + stroke = s; + } + + public void setTransform(AffineTransform affinetransform) + { + setTrans( (AffineTransform)affinetransform.clone() ); + } + + public void setXORMode(Color color1) + { + getEscherGraphics().setXORMode(color1); + } + + public void shear(double d, double d1) + { + getTrans().shear(d, d1); + } + + public void transform(AffineTransform affinetransform) + { + getTrans().concatenate(affinetransform); + } + +// Image transformImage(Image image, Rectangle rectangle, Rectangle rectangle1, ImageObserver imageobserver, Color color1) +// { +// logger.log(POILogger.WARN,"transformImage() not supported"); +// return null; +// } +// +// Image transformImage(Image image, int ai[], Rectangle rectangle, ImageObserver imageobserver, Color color1) +// { +// logger.log(POILogger.WARN,"transformImage() not supported"); +// return null; +// } + + public void translate(double d, double d1) + { + getTrans().translate(d, d1); + } + + public void translate(int i, int j) + { + getTrans().translate(i, j); + } + + private EscherGraphics getEscherGraphics() + { + return escherGraphics; + } + + private BufferedImage getImg() + { + return img; + } + + private void setImg( BufferedImage img ) + { + this.img = img; + } + + private Graphics2D getG2D() + { + return (Graphics2D) img.getGraphics(); + } + + private AffineTransform getTrans() + { + return trans; + } + + private void setTrans( AffineTransform trans ) + { + this.trans = trans; + } + + private Shape getDeviceclip() + { + return deviceclip; + } + + private void setDeviceclip( Shape deviceclip ) + { + this.deviceclip = deviceclip; + } + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/FontDetails.java b/src/java/org/apache/poi/hssf/usermodel/FontDetails.java new file mode 100644 index 000000000..96b0d87a8 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/FontDetails.java @@ -0,0 +1,143 @@ +package org.apache.poi.hssf.usermodel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * Stores width and height details about a font. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class FontDetails +{ + private String fontName; + private int height; + private Map charWidths = new HashMap(); + + /** + * Construct the font details with the given name and height. + * + * @param fontName The font name. + * @param height The height of the font. + */ + public FontDetails( String fontName, int height ) + { + this.fontName = fontName; + this.height = height; + } + + public String getFontName() + { + return fontName; + } + + public int getHeight() + { + return height; + } + + public void addChar( char c, int width ) + { + charWidths.put(new Character(c), new Integer(width)); + } + + /** + * Retrieves the width of the specified character. If the metrics for + * a particular character are not available it defaults to returning the + * width for the 'W' character. + */ + public int getCharWidth( char c ) + { + Integer widthInteger = (Integer)(charWidths.get(new Character(c))); + if (widthInteger == null && c != 'W') + return getCharWidth('W'); + else + return widthInteger.intValue(); + } + + public void addChars( char[] characters, int[] widths ) + { + for ( int i = 0; i < characters.length; i++ ) + { + charWidths.put( new Character(characters[i]), new Integer(widths[i])); + } + } + + /** + * Create an instance of FontDetails by loading them from the + * provided property object. + * @param fontName the font name + * @param fontMetricsProps the property object holding the details of this + * particular font. + * @return a new FontDetails instance. + */ + public static FontDetails create( String fontName, Properties fontMetricsProps ) + { + String heightStr = fontMetricsProps.getProperty( "font." + fontName + ".height"); + String widthsStr = fontMetricsProps.getProperty( "font." + fontName + ".widths"); + String charactersStr = fontMetricsProps.getProperty( "font." + fontName + ".characters"); + int height = Integer.parseInt(heightStr); + FontDetails d = new FontDetails(fontName, height); + String[] charactersStrArray = split(charactersStr, ",", -1); + String[] widthsStrArray = split(widthsStr, ",", -1); + if (charactersStrArray.length != widthsStrArray.length) + throw new RuntimeException("Number of characters does not number of widths for font " + fontName); + for ( int i = 0; i < widthsStrArray.length; i++ ) + { + if (charactersStrArray[i].length() != 0) + d.addChar(charactersStrArray[i].charAt(0), Integer.parseInt(widthsStrArray[i])); + } + return d; + } + + /** + * Gets the width of all characters in a string. + * + * @param str The string to measure. + * @return The width of the string for a 10 point font. + */ + public int getStringWidth(String str) + { + int width = 0; + for (int i = 0; i < str.length(); i++) + { + width += getCharWidth(str.charAt(i)); + } + return width; + } + + /** + * Split the given string into an array of strings using the given + * delimiter. + */ + private static String[] split(String text, String separator, int max) + { + StringTokenizer tok = new StringTokenizer(text, separator); + int listSize = tok.countTokens(); + if(max != -1 && listSize > max) + listSize = max; + String list[] = new String[listSize]; + for(int i = 0; tok.hasMoreTokens(); i++) + { + if(max != -1 && i == listSize - 1) + { + StringBuffer buf = new StringBuffer((text.length() * (listSize - i)) / listSize); + while(tok.hasMoreTokens()) + { + buf.append(tok.nextToken()); + if(tok.hasMoreTokens()) + buf.append(separator); + } + list[i] = buf.toString().trim(); + break; + } + list[i] = tok.nextToken().trim(); + } + + return list; + } + + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java new file mode 100644 index 000000000..b9db3c78e --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFAnchor.java @@ -0,0 +1,40 @@ +package org.apache.poi.hssf.usermodel; + + +/** + * An anchor is what specifics the position of a shape within a client object + * or within another containing shape. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public abstract class HSSFAnchor +{ + int dx1; + int dy1; + int dx2; + int dy2; + + public HSSFAnchor() + { + } + + public HSSFAnchor( int dx1, int dy1, int dx2, int dy2 ) + { + this.dx1 = dx1; + this.dy1 = dy1; + this.dx2 = dx2; + this.dy2 = dy2; + } + + public int getDx1(){ return dx1; } + public void setDx1( int dx1 ){ this.dx1 = dx1; } + public int getDy1(){ return dy1; } + public void setDy1( int dy1 ){ this.dy1 = dy1; } + public int getDy2(){ return dy2; } + public void setDy2( int dy2 ){ this.dy2 = dy2; } + public int getDx2(){ return dx2; } + public void setDx2( int dx2 ){ this.dx2 = dx2; } + + public abstract boolean isHorizontallyFlipped(); + public abstract boolean isVerticallyFlipped(); +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java new file mode 100644 index 000000000..fd69f48cd --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFChildAnchor.java @@ -0,0 +1,37 @@ +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.EscherClientAnchorRecord; +import org.apache.poi.ddf.EscherChildAnchorRecord; + +public class HSSFChildAnchor + extends HSSFAnchor +{ + public HSSFChildAnchor() + { + } + + public HSSFChildAnchor( int dx1, int dy1, int dx2, int dy2 ) + { + super( dx1, dy1, dx2, dy2 ); + } + + public void setAnchor(int dx1, int dy1, int dx2, int dy2) + { + this.dx1 = dx1; + this.dy1 = dy1; + this.dx2 = dx2; + this.dy2 = dy2; + } + + public boolean isHorizontallyFlipped() + { + return dx1 > dx2; + } + + public boolean isVerticallyFlipped() + { + return dy1 > dy2; + } + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java new file mode 100644 index 000000000..cbbaf167f --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFClientAnchor.java @@ -0,0 +1,207 @@ +package org.apache.poi.hssf.usermodel; + +import org.apache.poi.ddf.EscherClientAnchorRecord; +import org.apache.poi.ddf.EscherRecord; + + +/** + * A client anchor is attached to an excel worksheet. It anchors against a + * top-left and buttom-right cell. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class HSSFClientAnchor + extends HSSFAnchor +{ + short col1; + int row1; + short col2; + int row2; + + /** + * Creates a new client anchor and defaults all the anchor positions to 0. + */ + public HSSFClientAnchor() + { + } + + /** + * Creates a new client anchor and sets the top-left and bottom-right + * coordinates of the anchor. + * + * @param dx1 the x coordinate within the first cell. + * @param dy1 the y coordinate within the first cell. + * @param dx2 the x coordinate within the second cell. + * @param dy2 the y coordinate within the second cell. + * @param col1 the column (0 based) of the first cell. + * @param row1 the row (0 based) of the first cell. + * @param col2 the column (0 based) of the second cell. + * @param row2 the row (0 based) of the second cell. + */ + public HSSFClientAnchor( int dx1, int dy1, int dx2, int dy2, short col1, int row1, short col2, int row2 ) + { + super( dx1, dy1, dx2, dy2 ); + + checkRange(dx1, 0, 1023, "dx1"); + checkRange(dx2, 0, 1023, "dx2"); + checkRange(dy1, 0, 255, "dy1"); + checkRange(dy2, 0, 255, "dy2"); + checkRange(col1, 0, 255, "col1"); + checkRange(col2, 0, 255, "col2"); + checkRange(row1, 0, 255 * 256, "row1"); + checkRange(row2, 0, 255 * 256, "row2"); + + this.col1 = col1; + this.row1 = row1; + this.col2 = col2; + this.row2 = row2; + } + + /** + * Calculates the height of a client anchor in points. + * + * @param sheet the sheet the anchor will be attached to + * @return the shape height. + */ + public float getAnchorHeightInPoints(HSSFSheet sheet ) + { + int y1 = Math.min( getDy1(), getDy2() ); + int y2 = Math.max( getDy1(), getDy2() ); + int row1 = Math.min( getRow1(), getRow2() ); + int row2 = Math.max( getRow1(), getRow2() ); + + float points = 0; + if (row1 == row2) + { + points = ((y2 - y1) / 256.0f) * getRowHeightInPoints(sheet, row2); + } + else + { + points += ((256.0f - y1) / 256.0f) * getRowHeightInPoints(sheet, row1); + for (int i = row1 + 1; i < row2; i++) + { + points += getRowHeightInPoints(sheet, i); + } + points += (y2 / 256.0f) * getRowHeightInPoints(sheet, row2); + } + + return points; + } + + private float getRowHeightInPoints(HSSFSheet sheet, int rowNum) + { + HSSFRow row = sheet.getRow(rowNum); + if (row == null) + return sheet.getDefaultRowHeightInPoints(); + else + return row.getHeightInPoints(); + } + + public short getCol1() + { + return col1; + } + + public void setCol1( short col1 ) + { + checkRange(col1, 0, 255, "col1"); + this.col1 = col1; + } + + public short getCol2() + { + return col2; + } + + public void setCol2( short col2 ) + { + checkRange(col2, 0, 255, "col2"); + this.col2 = col2; + } + + public int getRow1() + { + return row1; + } + + public void setRow1( int row1 ) + { + checkRange(row1, 0, 256 * 256, "row1"); + this.row1 = row1; + } + + public int getRow2() + { + return row2; + } + + public void setRow2( int row2 ) + { + checkRange(row2, 0, 256 * 256, "row2"); + this.row2 = row2; + } + + /** + * Dets the top-left and bottom-right + * coordinates of the anchor. + * + * @param x1 the x coordinate within the first cell. + * @param y1 the y coordinate within the first cell. + * @param x2 the x coordinate within the second cell. + * @param y2 the y coordinate within the second cell. + * @param col1 the column (0 based) of the first cell. + * @param row1 the row (0 based) of the first cell. + * @param col2 the column (0 based) of the second cell. + * @param row2 the row (0 based) of the second cell. + */ + public void setAnchor( short col1, int row1, int x1, int y1, short col2, int row2, int x2, int y2 ) + { + checkRange(dx1, 0, 1023, "dx1"); + checkRange(dx2, 0, 1023, "dx2"); + checkRange(dy1, 0, 255, "dy1"); + checkRange(dy2, 0, 255, "dy2"); + checkRange(col1, 0, 255, "col1"); + checkRange(col2, 0, 255, "col2"); + checkRange(row1, 0, 255 * 256, "row1"); + checkRange(row2, 0, 255 * 256, "row2"); + + this.col1 = col1; + this.row1 = row1; + this.dx1 = x1; + this.dy1 = y1; + this.col2 = col2; + this.row2 = row2; + this.dx2 = x2; + this.dy2 = y2; + } + + /** + * @return true if the anchor goes from right to left. + */ + public boolean isHorizontallyFlipped() + { + if (col1 == col2) + return dx1 > dx2; + else + return col1 > col2; + } + + /** + * @return true if the anchor goes from bottom to top. + */ + public boolean isVerticallyFlipped() + { + if (row1 == row2) + return dy1 > dy2; + else + return row1 > row2; + } + + private void checkRange( int value, int minRange, int maxRange, String varName ) + { + if (value < minRange || value > maxRange) + throw new IllegalArgumentException(varName + " must be between " + minRange + " and " + maxRange); + } + + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java index 61b8c60de..7485b0bb2 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFFont.java @@ -165,7 +165,7 @@ public class HSSFFont /** * set the name for the font (i.e. Arial) - * @param String representing the name of the font to use + * @param name String representing the name of the font to use * @see #FONT_ARIAL */ @@ -359,7 +359,7 @@ public class HSSFFont /** * set type of text underlining to use - * @param underlining type + * @param underline type * @see #U_NONE * @see #U_SINGLE * @see #U_DOUBLE @@ -386,4 +386,13 @@ public class HSSFFont { return font.getUnderline(); } + + public String toString() + { + return "org.apache.poi.hssf.usermodel.HSSFFont{" + + font + + "}"; + } + + } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java index 863533d45..d4554fd75 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPalette.java @@ -110,7 +110,35 @@ public class HSSFPalette } return null; } - + + /** + * Finds the closest matching color in the custom palette. The + * method for finding the distance between the colors is fairly + * primative. + * + * @param red The red component of the color to match. + * @param green The green component of the color to match. + * @param blue The blue component of the color to match. + * @return The closest color or null if there are no custom + * colors currently defined. + */ + public HSSFColor findSimilarColor(byte red, byte green, byte blue) + { + HSSFColor result = null; + int minColorDistance = Integer.MAX_VALUE; + byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX); + for (short i = (short) PaletteRecord.FIRST_COLOR_INDEX; b != null; + b = palette.getColor(++i)) + { + int colorDistance = red - b[0] + green - b[1] + blue - b[2]; + if (colorDistance < minColorDistance) + { + result = getColor(i); + } + } + return result; + } + /** * Sets the color at the given offset * @@ -123,7 +151,32 @@ public class HSSFPalette { palette.setColor(index, red, green, blue); } - + + /** + * Adds a new color into an empty color slot. + * @param red The red component + * @param green The green component + * @param blue The blue component + * + * @return The new custom color. + * + * @throws RuntimeException if there are more more free color indexes. + */ + public HSSFColor addColor( byte red, byte green, byte blue ) + { + byte[] b = palette.getColor(PaletteRecord.FIRST_COLOR_INDEX); + short i; + for (i = (short) PaletteRecord.FIRST_COLOR_INDEX; i < PaletteRecord.STANDARD_PALETTE_SIZE + PaletteRecord.FIRST_COLOR_INDEX; b = palette.getColor(++i)) + { + if (b == null) + { + setColorAtIndex( i, red, green, blue ); + return getColor(i); + } + } + throw new RuntimeException("Could not find free color index"); + } + private static class CustomColor extends HSSFColor { private short byteOffset; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java new file mode 100644 index 000000000..d0a376c0e --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java @@ -0,0 +1,159 @@ +package org.apache.poi.hssf.usermodel; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * The patriarch is the toplevel container for shapes in a sheet. It does + * little other than act as a container for other shapes and groups. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class HSSFPatriarch + implements HSSFShapeContainer +{ + List shapes = new ArrayList(); + HSSFSheet sheet; + int x1 = 0; + int y1 = 0 ; + int x2 = 1023; + int y2 = 255; + + /** + * Creates the patriarch. + * + * @param sheet the sheet this patriarch is stored in. + */ + HSSFPatriarch(HSSFSheet sheet) + { + this.sheet = sheet; + } + + /** + * Creates a new group record stored under this patriarch. + * + * @param anchor the client anchor describes how this group is attached + * to the sheet. + * @return the newly created group. + */ + public HSSFShapeGroup createGroup(HSSFClientAnchor anchor) + { + HSSFShapeGroup group = new HSSFShapeGroup(null, anchor); + group.anchor = anchor; + shapes.add(group); + return group; + } + + /** + * Creates a simple shape. This includes such shapes as lines, rectangles, + * and ovals. + * + * @param anchor the client anchor describes how this group is attached + * to the sheet. + * @return the newly created shape. + */ + public HSSFSimpleShape createSimpleShape(HSSFClientAnchor anchor) + { + HSSFSimpleShape shape = new HSSFSimpleShape(null, anchor); + shape.anchor = anchor; + shapes.add(shape); + return shape; + } + + /** + * Creates a polygon + * + * @param anchor the client anchor describes how this group is attached + * to the sheet. + * @return the newly created shape. + */ + public HSSFPolygon createPolygon(HSSFClientAnchor anchor) + { + HSSFPolygon shape = new HSSFPolygon(null, anchor); + shape.anchor = anchor; + shapes.add(shape); + return shape; + } + + /** + * Constructs a textbox under the patriarch. + * + * @param anchor the client anchor describes how this group is attached + * to the sheet. + * @return the newly created textbox. + */ + public HSSFTextbox createTextbox(HSSFClientAnchor anchor) + { + HSSFTextbox shape = new HSSFTextbox(null, anchor); + shape.anchor = anchor; + shapes.add(shape); + return shape; + } + + /** + * Returns a list of all shapes contained by the patriarch. + */ + public List getChildren() + { + return shapes; + } + + /** + * Total count of all children and their children's children. + */ + public int countOfAllChildren() + { + int count = shapes.size(); + for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); ) + { + HSSFShape shape = (HSSFShape) iterator.next(); + count += shape.countOfAllChildren(); + } + return count; + } + /** + * Sets the coordinate space of this group. All children are contrained + * to these coordinates. + */ + public void setCoordinates( int x1, int y1, int x2, int y2 ) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * The top left x coordinate of this group. + */ + public int getX1() + { + return x1; + } + + /** + * The top left y coordinate of this group. + */ + public int getY1() + { + return y1; + } + + /** + * The bottom right x coordinate of this group. + */ + public int getX2() + { + return x2; + } + + /** + * The bottom right y coordinate of this group. + */ + public int getY2() + { + return y2; + } + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java new file mode 100644 index 000000000..67f453265 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPolygon.java @@ -0,0 +1,66 @@ +package org.apache.poi.hssf.usermodel; + +/** + * @author Glen Stampoultzis (glens at superlinksoftware.com) + */ +public class HSSFPolygon + extends HSSFShape +{ + int[] xPoints; + int[] yPoints; + int drawAreaWidth = 100; + int drawAreaHeight = 100; + + HSSFPolygon( HSSFShape parent, HSSFAnchor anchor ) + { + super( parent, anchor ); + } + + public int[] getXPoints() + { + return xPoints; + } + + public int[] getYPoints() + { + return yPoints; + } + + public void setPoints(int[] xPoints, int[] yPoints) + { + this.xPoints = cloneArray(xPoints); + this.yPoints = cloneArray(yPoints); + } + + private int[] cloneArray( int[] a ) + { + int[] result = new int[a.length]; + for ( int i = 0; i < a.length; i++ ) + result[i] = a[i]; + + return result; + } + + /** + * Defines the width and height of the points in the polygon + * @param width + * @param height + */ + public void setPolygonDrawArea( int width, int height ) + { + this.drawAreaWidth = width; + this.drawAreaHeight = height; + } + + public int getDrawAreaWidth() + { + return drawAreaWidth; + } + + public int getDrawAreaHeight() + { + return drawAreaHeight; + } + + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java new file mode 100644 index 000000000..59edf61d6 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFRichTextString.java @@ -0,0 +1,179 @@ +package org.apache.poi.hssf.usermodel; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Rich text unicode string. These strings can have fonts applied to + * arbitary parts of the string. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class HSSFRichTextString + implements Comparable +{ + /** Place holder for indicating that NO_FONT has been applied here */ + public static final short NO_FONT = -1; + + String string; + SortedMap formattingRuns = new TreeMap(); + + public HSSFRichTextString() + { + this(""); + } + + public HSSFRichTextString( String string ) + { + this.string = string; + this.formattingRuns.put(new Integer(0), new Short(NO_FONT)); + } + + /** + * Applies a font to the specified characters of a string. + * + * @param startIndex The start index to apply the font to (inclusive) + * @param endIndex The end index to apply the font to (exclusive) + * @param fontIndex The font to use. + */ + public void applyFont(int startIndex, int endIndex, short fontIndex) + { + if (startIndex > endIndex) + throw new IllegalArgumentException("Start index must be less than end index."); + if (startIndex < 0 || endIndex > length()) + throw new IllegalArgumentException("Start and end index not in range."); + if (startIndex == endIndex) + return; + + Integer from = new Integer(startIndex); + Integer to = new Integer(endIndex); + short fontAtIndex = NO_FONT; + if (endIndex != length()) + fontAtIndex = getFontAtIndex(endIndex); + formattingRuns.subMap(from, to).clear(); + formattingRuns.put(from, new Short(fontIndex)); + if (endIndex != length()) + { + if (fontIndex != fontAtIndex) + formattingRuns.put(to, new Short(fontAtIndex)); + } + } + + /** + * Applies a font to the specified characters of a string. + * + * @param startIndex The start index to apply the font to (inclusive) + * @param endIndex The end index to apply to font to (exclusive) + * @param font The index of the font to use. + */ + public void applyFont(int startIndex, int endIndex, HSSFFont font) + { + applyFont(startIndex, endIndex, font.getIndex()); + } + + /** + * Sets the font of the entire string. + * @param font The font to use. + */ + public void applyFont(HSSFFont font) + { + applyFont(0, string.length(), font); + } + + /** + * Returns the plain string representation. + */ + public String getString() + { + return string; + } + + /** + * @return the number of characters in the font. + */ + public int length() + { + return string.length(); + } + + /** + * Returns the font in use at a particular index. + * + * @param index The index. + * @return The font that's currently being applied at that + * index or null if no font is being applied or the + * index is out of range. + */ + public short getFontAtIndex( int index ) + { + if (index < 0 || index >= string.length()) + throw new ArrayIndexOutOfBoundsException("Font index " + index + " out of bounds of string"); + Integer key = new Integer(index + 1); + SortedMap head = formattingRuns.headMap(key); + if (head.isEmpty()) + throw new IllegalStateException("Should not reach here. No font found."); + else + return ((Short) head.get(head.lastKey())).shortValue(); + } + + /** + * @return The number of formatting runs used. There will always be at + * least one of font NO_FONT. + * + * @see #NO_FONT + */ + public int numFormattingRuns() + { + return formattingRuns.size(); + } + + /** + * The index within the string to which the specified formatting run applies. + * @param index the index of the formatting run + * @return the index within the string. + */ + public int getIndexOfFormattingRun(int index) + { + Map.Entry[] runs = (Map.Entry[]) formattingRuns.entrySet().toArray(new Map.Entry[formattingRuns.size()] ); + return ((Integer)runs[index].getKey()).intValue(); + } + + /** + * Gets the font used in a particular formatting run. + * + * @param index the index of the formatting run + * @return the font number used. + */ + public short getFontOfFormattingRun(int index) + { + Map.Entry[] runs = (Map.Entry[]) formattingRuns.entrySet().toArray(new Map.Entry[formattingRuns.size()] ); + return ((Short)(runs[index].getValue())).shortValue(); + } + + /** + * Compares one rich text string to another. + */ + public int compareTo( Object o ) + { + return 0; // todo + } + + /** + * @return the plain text representation of this string. + */ + public String toString() + { + return string; + } + + /** + * Applies the specified font to the entire string. + * + * @param fontIndex the font to apply. + */ + public void applyFont( short fontIndex ) + { + applyFont(0, string.length(), fontIndex); + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java new file mode 100644 index 000000000..61620f235 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShape.java @@ -0,0 +1,195 @@ +package org.apache.poi.hssf.usermodel; + +/** + * An abstract shape. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public abstract class HSSFShape +{ + public static final int LINEWIDTH_ONE_PT = 12700; + public static final int LINEWIDTH_DEFAULT = 9525; + + public static final int LINESTYLE_SOLID = 0; // Solid (continuous) pen + public static final int LINESTYLE_DASHSYS = 1; // PS_DASH system dash style + public static final int LINESTYLE_DOTSYS = 2; // PS_DOT system dash style + public static final int LINESTYLE_DASHDOTSYS = 3; // PS_DASHDOT system dash style + public static final int LINESTYLE_DASHDOTDOTSYS = 4; // PS_DASHDOTDOT system dash style + public static final int LINESTYLE_DOTGEL = 5; // square dot style + public static final int LINESTYLE_DASHGEL = 6; // dash style + public static final int LINESTYLE_LONGDASHGEL = 7; // long dash style + public static final int LINESTYLE_DASHDOTGEL = 8; // dash short dash + public static final int LINESTYLE_LONGDASHDOTGEL = 9; // long dash short dash + public static final int LINESTYLE_LONGDASHDOTDOTGEL = 10; // long dash short dash short dash + public static final int LINESTYLE_NONE = -1; + + HSSFShape parent; + HSSFAnchor anchor; + int lineStyleColor = 0x08000040; + int fillColor = 0x08000009; + int lineWidth = LINEWIDTH_DEFAULT; // 12700 = 1pt + int lineStyle = LINESTYLE_SOLID; + boolean noFill = false; + + /** + * Create a new shape with the specified parent and anchor. + */ + HSSFShape( HSSFShape parent, HSSFAnchor anchor ) + { + this.parent = parent; + this.anchor = anchor; + } + + /** + * Gets the parent shape. + */ + public HSSFShape getParent() + { + return parent; + } + + /** + * @return the anchor that is used by this shape. + */ + public HSSFAnchor getAnchor() + { + return anchor; + } + + /** + * Sets a particular anchor. A top-level shape must have an anchor of + * HSSFClientAnchor. A child anchor must have an anchor of HSSFChildAnchor + * + * @param anchor the anchor to use. + * @throws IllegalArgumentException when the wrong anchor is used for + * this particular shape. + * + * @see HSSFChildAnchor + * @see HSSFClientAnchor + */ + public void setAnchor( HSSFAnchor anchor ) + { + if ( parent == null ) + { + if ( anchor instanceof HSSFChildAnchor ) + throw new IllegalArgumentException( "Must use client anchors for shapes directly attached to sheet." ); + } + else + { + if ( anchor instanceof HSSFClientAnchor ) + throw new IllegalArgumentException( "Must use child anchors for shapes attached to groups." ); + } + + this.anchor = anchor; + } + + /** + * The color applied to the lines of this shape. + */ + public int getLineStyleColor() + { + return lineStyleColor; + } + + /** + * The color applied to the lines of this shape. + */ + public void setLineStyleColor( int lineStyleColor ) + { + this.lineStyleColor = lineStyleColor; + } + + /** + * The color applied to the lines of this shape. + */ + public void setLineStyleColor( int red, int green, int blue ) + { + this.lineStyleColor = ((blue) << 16) | ((green) << 8) | red; + } + + /** + * The color used to fill this shape. + */ + public int getFillColor() + { + return fillColor; + } + + /** + * The color used to fill this shape. + */ + public void setFillColor( int fillColor ) + { + this.fillColor = fillColor; + } + + /** + * The color used to fill this shape. + */ + public void setFillColor( int red, int green, int blue ) + { + this.fillColor = ((blue) << 16) | ((green) << 8) | red; + } + + /** + * @return returns with width of the line in EMUs. 12700 = 1 pt. + */ + public int getLineWidth() + { + return lineWidth; + } + + /** + * Sets the width of the line. 12700 = 1 pt. + * + * @param lineWidth width in EMU's. 12700EMU's = 1 pt + * + * @see HSSFShape#LINEWIDTH_ONE_PT + */ + public void setLineWidth( int lineWidth ) + { + this.lineWidth = lineWidth; + } + + /** + * @return One of the constants in LINESTYLE_* + */ + public int getLineStyle() + { + return lineStyle; + } + + /** + * Sets the line style. + * + * @param lineStyle One of the constants in LINESTYLE_* + */ + public void setLineStyle( int lineStyle ) + { + this.lineStyle = lineStyle; + } + + /** + * @return true if this shape is not filled with a color. + */ + public boolean isNoFill() + { + return noFill; + } + + /** + * Sets whether this shape is filled or transparent. + */ + public void setNoFill( boolean noFill ) + { + this.noFill = noFill; + } + + /** + * Count of all children and their childrens children. + */ + public int countOfAllChildren() + { + return 1; + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java new file mode 100644 index 000000000..f641fe9c0 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeContainer.java @@ -0,0 +1,17 @@ +package org.apache.poi.hssf.usermodel; + +import java.util.List; + +/** + * An interface that indicates whether a class can contain children. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public interface HSSFShapeContainer +{ + /** + * @return Any children contained by this shape. + */ + List getChildren(); + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java new file mode 100644 index 000000000..c33c8c6d3 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFShapeGroup.java @@ -0,0 +1,148 @@ +package org.apache.poi.hssf.usermodel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Iterator; + +/** + * A shape group may contain other shapes. It was no actual form on the + * sheet. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class HSSFShapeGroup + extends HSSFShape + implements HSSFShapeContainer +{ + List shapes = new ArrayList(); + int x1 = 0; + int y1 = 0 ; + int x2 = 1023; + int y2 = 255; + + + public HSSFShapeGroup( HSSFShape parent, HSSFAnchor anchor ) + { + super( parent, anchor ); + } + + /** + * Create another group under this group. + * @param anchor the position of the new group. + * @return the group + */ + public HSSFShapeGroup createGroup(HSSFChildAnchor anchor) + { + HSSFShapeGroup group = new HSSFShapeGroup(this, anchor); + group.anchor = anchor; + shapes.add(group); + return group; + } + + /** + * Create a new simple shape under this group. + * @param anchor the position of the shape. + * @return the shape + */ + public HSSFSimpleShape createShape(HSSFChildAnchor anchor) + { + HSSFSimpleShape shape = new HSSFSimpleShape(this, anchor); + shape.anchor = anchor; + shapes.add(shape); + return shape; + } + + /** + * Create a new textbox under this group. + * @param anchor the position of the shape. + * @return the textbox + */ + public HSSFTextbox createTextbox(HSSFChildAnchor anchor) + { + HSSFTextbox shape = new HSSFTextbox(this, anchor); + shape.anchor = anchor; + shapes.add(shape); + return shape; + } + + /** + * Creates a polygon + * + * @param anchor the client anchor describes how this group is attached + * to the sheet. + * @return the newly created shape. + */ + public HSSFPolygon createPolygon(HSSFChildAnchor anchor) + { + HSSFPolygon shape = new HSSFPolygon(this, anchor); + shape.anchor = anchor; + shapes.add(shape); + return shape; + } + + /** + * Return all children contained by this shape. + */ + public List getChildren() + { + return shapes; + } + + /** + * Sets the coordinate space of this group. All children are contrained + * to these coordinates. + */ + public void setCoordinates( int x1, int y1, int x2, int y2 ) + { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + } + + /** + * The top left x coordinate of this group. + */ + public int getX1() + { + return x1; + } + + /** + * The top left y coordinate of this group. + */ + public int getY1() + { + return y1; + } + + /** + * The bottom right x coordinate of this group. + */ + public int getX2() + { + return x2; + } + + /** + * The bottom right y coordinate of this group. + */ + public int getY2() + { + return y2; + } + + /** + * Count of all children and their childrens children. + */ + public int countOfAllChildren() + { + int count = shapes.size(); + for ( Iterator iterator = shapes.iterator(); iterator.hasNext(); ) + { + HSSFShape shape = (HSSFShape) iterator.next(); + count += shape.countOfAllChildren(); + } + return count; + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index f8c314b80..da8c606cf 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -59,26 +59,20 @@ */ package org.apache.poi.hssf.usermodel; +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.hssf.model.Sheet; +import org.apache.poi.hssf.model.Workbook; +import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.util.Region; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.TreeMap; -import org.apache.poi.hssf.model.Sheet; -import org.apache.poi.hssf.model.Workbook; -import org.apache.poi.hssf.record.CellValueRecordInterface; -import org.apache.poi.hssf.record.HCenterRecord; -import org.apache.poi.hssf.record.PageBreakRecord; -import org.apache.poi.hssf.record.Record; -import org.apache.poi.hssf.record.RowRecord; -import org.apache.poi.hssf.record.SCLRecord; -import org.apache.poi.hssf.record.VCenterRecord; -import org.apache.poi.hssf.record.WSBoolRecord; -import org.apache.poi.hssf.record.WindowTwoRecord; -import org.apache.poi.hssf.util.Region; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; - /** * High level representation of a worksheet. * @author Andrew C. Oliver (acoliver at apache dot org) @@ -874,13 +868,13 @@ public class HSSFSheet { getSheet().setMargin( margin, size ); } - + /** * Answer whether protection is enabled or disabled * @return true => protection enabled; false => protection disabled */ public boolean getProtect() { - return getSheet().getProtect().getProtect(); + return getSheet().getProtect().getProtect(); } /** @@ -888,7 +882,7 @@ public class HSSFSheet * @param protect true => protection enabled; false => protection disabled */ public void setProtect(boolean protect) { - getSheet().getProtect().setProtect(protect); + getSheet().getProtect().setProtect(protect); } /** @@ -926,34 +920,34 @@ public class HSSFSheet //move merged regions completely if they fall within the new region boundaries when they are shifted for (int i = 0; i < this.getNumMergedRegions(); i++) { Region merged = this.getMergedRegionAt(i); - + boolean inStart = (merged.getRowFrom() >= startRow || merged.getRowTo() >= startRow); boolean inEnd = (merged.getRowTo() <= endRow || merged.getRowFrom() <= endRow); - + //dont check if it's not within the shifted area if (! (inStart && inEnd)) continue; - - //only shift if the region outside the shifted rows is not merged too + + //only shift if the region outside the shifted rows is not merged too if (!merged.contains(startRow-1, (short)0) && !merged.contains(endRow+1, (short)0)){ - merged.setRowFrom(merged.getRowFrom()+n); + merged.setRowFrom(merged.getRowFrom()+n); merged.setRowTo(merged.getRowTo()+n); //have to remove/add it back shiftedRegions.add(merged); this.removeMergedRegion(i); i = i -1; // we have to back up now since we removed one - + } - + } - + //readd so it doesn't get shifted again Iterator iterator = shiftedRegions.iterator(); while (iterator.hasNext()) { Region region = (Region)iterator.next(); - + this.addMergedRegion(region); } - + } /** @@ -978,7 +972,7 @@ public class HSSFSheet * Shifts rows between startRow and endRow n number of rows. * If you use a negative number, it will shift rows up. * Code ensures that rows don't wrap around - * + * *

* Additionally shifts merged regions that are completely defined in these * rows (ie. merged 2 cells on a row to be shifted). @@ -1006,19 +1000,19 @@ public class HSSFSheet inc = -1; } - shiftMerged(startRow, endRow, n, true); - sheet.shiftRowBreaks(startRow, endRow, n); + shiftMerged(startRow, endRow, n, true); + sheet.shiftRowBreaks(startRow, endRow, n); for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc ) { HSSFRow row = getRow( rowNum ); - HSSFRow row2Replace = getRow( rowNum + n ); + HSSFRow row2Replace = getRow( rowNum + n ); if ( row2Replace == null ) row2Replace = createRow( rowNum + n ); - + HSSFCell cell; - + // Removes the cells before over writting them. @@ -1033,10 +1027,10 @@ public class HSSFSheet if (copyRowHeight) { row2Replace.setHeight(row.getHeight()); } - + if (resetOriginalRowHeight) { row.setHeight((short)0xff); - } + } } for ( short col = row.getFirstCellNum(); col <= row.getLastCellNum(); col++ ) { @@ -1255,4 +1249,46 @@ public class HSSFSheet if (column > 255) throw new IllegalArgumentException("Maximum column number is 255"); if (column < 0) throw new IllegalArgumentException("Minimum column number is 0"); } + + /** + * Aggregates the drawing records and dumps the escher record hierarchy + * to the standard output. + */ + public void dumpDrawingRecords() + { + sheet.aggregateDrawingRecords(book.getDrawingManager()); + + EscherAggregate r = (EscherAggregate) getSheet().findFirstRecordBySid(EscherAggregate.sid); + List escherRecords = r.getEscherRecords(); + for ( Iterator iterator = escherRecords.iterator(); iterator.hasNext(); ) + { + EscherRecord escherRecord = (EscherRecord) iterator.next(); + PrintWriter w = new PrintWriter(System.out); + escherRecord.display(w, 0); + w.close(); + } + } + + /** + * Creates the toplevel drawing patriarch. This will have the effect of + * removing any existing drawings on this sheet. + * + * @return The new patriarch. + */ + public HSSFPatriarch createDrawingPatriarch() + { + // Create the drawing group if it doesn't already exist. + book.createDrawingGroup(); + + sheet.aggregateDrawingRecords(book.getDrawingManager()); + EscherAggregate agg = (EscherAggregate) sheet.findFirstRecordBySid(EscherAggregate.sid); + HSSFPatriarch patriarch = new HSSFPatriarch(this); + agg.clear(); // Initially the behaviour will be to clear out any existing shapes in the sheet when + // creating a new patriarch. + agg.setPatriarch(patriarch); + return patriarch; + } + + + } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java new file mode 100644 index 000000000..c90fe6bb8 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSimpleShape.java @@ -0,0 +1,64 @@ +package org.apache.poi.hssf.usermodel; + +/** + * Represents a simple shape such as a line, rectangle or oval. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class HSSFSimpleShape + extends HSSFShape +{ + // The commented out ones haven't been tested yet or aren't supported + // by HSSFSimpleShape. + + public final static short OBJECT_TYPE_LINE = 1; + public final static short OBJECT_TYPE_RECTANGLE = 2; + public final static short OBJECT_TYPE_OVAL = 3; +// public final static short OBJECT_TYPE_ARC = 4; +// public final static short OBJECT_TYPE_CHART = 5; +// public final static short OBJECT_TYPE_TEXT = 6; +// public final static short OBJECT_TYPE_BUTTON = 7; +// public final static short OBJECT_TYPE_PICTURE = 8; +// public final static short OBJECT_TYPE_POLYGON = 9; +// public final static short OBJECT_TYPE_CHECKBOX = 11; +// public final static short OBJECT_TYPE_OPTION_BUTTON = 12; +// public final static short OBJECT_TYPE_EDIT_BOX = 13; +// public final static short OBJECT_TYPE_LABEL = 14; +// public final static short OBJECT_TYPE_DIALOG_BOX = 15; +// public final static short OBJECT_TYPE_SPINNER = 16; +// public final static short OBJECT_TYPE_SCROLL_BAR = 17; +// public final static short OBJECT_TYPE_LIST_BOX = 18; +// public final static short OBJECT_TYPE_GROUP_BOX = 19; +// public final static short OBJECT_TYPE_COMBO_BOX = 20; +// public final static short OBJECT_TYPE_COMMENT = 25; +// public final static short OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30; + + int shapeType = OBJECT_TYPE_LINE; + + HSSFSimpleShape( HSSFShape parent, HSSFAnchor anchor ) + { + super( parent, anchor ); + } + + /** + * Gets the shape type. + * @return One of the OBJECT_TYPE_* constants. + * + * @see #OBJECT_TYPE_LINE + * @see #OBJECT_TYPE_OVAL + * @see #OBJECT_TYPE_RECTANGLE + */ + public int getShapeType() { return shapeType; } + + /** + * Sets the shape types. + * + * @param shapeType One of the OBJECT_TYPE_* constants. + * + * @see #OBJECT_TYPE_LINE + * @see #OBJECT_TYPE_OVAL + * @see #OBJECT_TYPE_RECTANGLE + */ + public void setShapeType( int shapeType ){ this.shapeType = shapeType; } + +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java new file mode 100644 index 000000000..423e256fa --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFTextbox.java @@ -0,0 +1,107 @@ +package org.apache.poi.hssf.usermodel; + +/** + * A textbox is a shape that may hold a rich text string. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +public class HSSFTextbox + extends HSSFSimpleShape +{ + public final static short OBJECT_TYPE_TEXT = 6; + + int marginLeft, marginRight, marginTop, marginBottom; + + HSSFRichTextString string = new HSSFRichTextString(""); + + /** + * Construct a new textbox with the given parent and anchor. + * @param parent + * @param anchor One of HSSFClientAnchor or HSSFChildAnchor + */ + public HSSFTextbox( HSSFShape parent, HSSFAnchor anchor ) + { + super( parent, anchor ); + setShapeType(OBJECT_TYPE_TEXT); + } + + /** + * @return the rich text string for this textbox. + */ + public HSSFRichTextString getString() + { + return string; + } + + /** + * @param string Sets the rich text string used by this object. + */ + public void setString( HSSFRichTextString string ) + { + this.string = string; + } + + /** + * @return Returns the left margin within the textbox. + */ + public int getMarginLeft() + { + return marginLeft; + } + + /** + * Sets the left margin within the textbox. + */ + public void setMarginLeft( int marginLeft ) + { + this.marginLeft = marginLeft; + } + + /** + * @return returns the right margin within the textbox. + */ + public int getMarginRight() + { + return marginRight; + } + + /** + * Sets the right margin within the textbox. + */ + public void setMarginRight( int marginRight ) + { + this.marginRight = marginRight; + } + + /** + * @return returns the top margin within the textbox. + */ + public int getMarginTop() + { + return marginTop; + } + + /** + * Sets the top margin within the textbox. + */ + public void setMarginTop( int marginTop ) + { + this.marginTop = marginTop; + } + + /** + * Gets the bottom margin within the textbox. + */ + public int getMarginBottom() + { + return marginBottom; + } + + /** + * Sets the bottom margin within the textbox. + */ + public void setMarginBottom( int marginBottom ) + { + this.marginBottom = marginBottom; + } +} diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index c1bfbd318..4ea6e683c 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -71,14 +71,7 @@ import java.util.Stack; import org.apache.poi.hssf.eventmodel.EventRecordFactory; import org.apache.poi.hssf.model.Sheet; import org.apache.poi.hssf.model.Workbook; -import org.apache.poi.hssf.record.BackupRecord; -import org.apache.poi.hssf.record.ExtendedFormatRecord; -import org.apache.poi.hssf.record.FontRecord; -import org.apache.poi.hssf.record.NameRecord; -import org.apache.poi.hssf.record.RecordFactory; -import org.apache.poi.hssf.record.SSTRecord; -import org.apache.poi.hssf.record.UnknownRecord; -import org.apache.poi.hssf.record.WindowTwoRecord; +import org.apache.poi.hssf.record.*; import org.apache.poi.hssf.record.formula.Area3DPtg; import org.apache.poi.hssf.record.formula.MemFuncPtg; import org.apache.poi.hssf.record.formula.UnionPtg; @@ -90,6 +83,8 @@ import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.poi.util.HexDump; +import org.apache.poi.ddf.*; /** * High level representation of a workbook. This is the first object most users @@ -129,16 +124,16 @@ public class HSSFWorkbook */ private ArrayList sheets; - + /** * this holds the HSSFName objects attached to this workbook */ private ArrayList names; - + /** * holds whether or not to preserve other nodes in the POIFS. Used - * for macros and embedded objects. + * for macros and embedded objects. */ private boolean preserveNodes; @@ -155,7 +150,7 @@ public class HSSFWorkbook * someplace else. */ private HSSFDataFormat formatter; - + private static POILogger log = POILogFactory.getLogger(HSSFWorkbook.class); /** @@ -179,7 +174,7 @@ public class HSSFWorkbook * low level models. If you're reading in a workbook...start here. * * @param fs the POI filesystem that contains the Workbook stream. - * @param preserveNodes whether to preseve other nodes, such as + * @param preserveNodes whether to preseve other nodes, such as * macros. This takes more memory, so only say yes if you * need to. * @see org.apache.poi.poifs.filesystem.POIFSFileSystem @@ -190,20 +185,20 @@ public class HSSFWorkbook throws IOException { this.preserveNodes = preserveNodes; - + if (preserveNodes) { - this.poifs = fs; + this.poifs = fs; } sheets = new ArrayList(INITIAL_CAPACITY); names = new ArrayList(INITIAL_CAPACITY); - + InputStream stream = fs.createDocumentInputStream("Workbook"); - + EventRecordFactory factory = new EventRecordFactory(); - - - + + + List records = RecordFactory.createRecords(stream); workbook = Workbook.createWorkbook(records); @@ -224,7 +219,7 @@ public class HSSFWorkbook // workbook.setSheetName(sheets.size() -1, "Sheet"+sheets.size()); } - + for (int i = 0 ; i < workbook.getNumNames() ; ++i){ HSSFName name = new HSSFName(workbook, workbook.getNameRecord(i)); names.add(name); @@ -240,7 +235,7 @@ public class HSSFWorkbook * inputstream. * * @param s the POI filesystem that contains the Workbook stream. - * @param preserveNodes whether to preseve other nodes, such as + * @param preserveNodes whether to preseve other nodes, such as * macros. This takes more memory, so only say yes if you * need to. * @see org.apache.poi.poifs.filesystem.POIFSFileSystem @@ -264,22 +259,22 @@ public class HSSFWorkbook // none currently } - + /** * sets the order of appearance for a given sheet. * * @param sheetname the name of the sheet to reorder * @param pos the position that we want to insert the sheet into (0 based) */ - + public void setSheetOrder(String sheetname, int pos ) { workbook.setSheetOrder(sheetname, pos); } - + public final static byte ENCODING_COMPRESSED_UNICODE = 0; public final static byte ENCODING_UTF_16 = 1; - - + + /** * set the sheet name. * Will throw IllegalArgumentException if the name is greater than 31 chars @@ -299,17 +294,17 @@ public class HSSFWorkbook { throw new RuntimeException("Sheet out of bounds"); } - + switch ( encoding ) { case ENCODING_COMPRESSED_UNICODE: case ENCODING_UTF_16: break; - + default: // TODO java.io.UnsupportedEncodingException throw new RuntimeException( "Unsupported encoding" ); } - + workbook.setSheetName( sheet, name, encoding ); } @@ -337,14 +332,14 @@ public class HSSFWorkbook /** Returns the index of the sheet by his name * @param name the sheet name * @return index of the sheet (0 based) - */ + */ public int getSheetIndex(String name) { int retval = workbook.getSheetIndex(name); - + return retval; } - + /** * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns * the high level representation. Use this to create new sheets. @@ -561,7 +556,7 @@ public class HSSFWorkbook //does a lot of the house keeping for builtin records, like setting lengths to zero etc isNewRecord = true; } - + short definitionTextLength = settingRowAndColumn ? (short)0x001a : (short)0x000b; nameRecord.setDefinitionTextLength(definitionTextLength); @@ -663,6 +658,40 @@ public class HSSFWorkbook return retval; } + /** + * Finds a font that matches the one with the supplied attributes + */ + public HSSFFont findFont(short boldWeight, short color, short fontHeight, + String name, boolean italic, boolean strikeout, + short typeOffset, byte underline) + { +// System.out.println( boldWeight + ", " + color + ", " + fontHeight + ", " + name + ", " + italic + ", " + strikeout + ", " + typeOffset + ", " + underline ); + for (short i = 0; i < workbook.getNumberOfFontRecords(); i++) + { + if (i == 4) + continue; + + FontRecord font = workbook.getFontRecordAt(i); + HSSFFont hssfFont = new HSSFFont(i, font); +// System.out.println( hssfFont.getBoldweight() + ", " + hssfFont.getColor() + ", " + hssfFont.getFontHeight() + ", " + hssfFont.getFontName() + ", " + hssfFont.getItalic() + ", " + hssfFont.getStrikeout() + ", " + hssfFont.getTypeOffset() + ", " + hssfFont.getUnderline() ); + if (hssfFont.getBoldweight() == boldWeight + && hssfFont.getColor() == color + && hssfFont.getFontHeight() == fontHeight + && hssfFont.getFontName().equals(name) + && hssfFont.getItalic() == italic + && hssfFont.getStrikeout() == strikeout + && hssfFont.getTypeOffset() == typeOffset + && hssfFont.getUnderline() == underline) + { +// System.out.println( "Found font" ); + return hssfFont; + } + } + +// System.out.println( "No font found" ); + return null; + } + /** * get the number of fonts in the font table * @return number of fonts @@ -741,10 +770,10 @@ public class HSSFWorkbook { byte[] bytes = getBytes(); POIFSFileSystem fs = new POIFSFileSystem(); - + fs.createDocument(new ByteArrayInputStream(bytes), "Workbook"); - if (preserveNodes) { + if (preserveNodes) { List excepts = new ArrayList(1); excepts.add("Workbook"); copyNodes(this.poifs,fs,excepts); @@ -768,6 +797,12 @@ public class HSSFWorkbook public byte[] getBytes() { log.log(DEBUG, "HSSFWorkbook.getBytes()"); + + // before getting the workbook size we must tell the sheets that + // serialization is about to occur. + for (int k = 0; k < sheets.size(); k++) + ((HSSFSheet) sheets.get(k)).getSheet().preSerialize(); + int wbsize = workbook.getSize(); // log.debug("REMOVEME: old sizing method "+workbook.serialize().length); @@ -777,10 +812,10 @@ public class HSSFWorkbook for (int k = 0; k < sheets.size(); k++) { workbook.setSheetBof(k, totalsize); - - // sheetbytes.add((( HSSFSheet ) sheets.get(k)).getSheet().getSize()); totalsize += ((HSSFSheet) sheets.get(k)).getSheet().getSize(); } + + /* if (totalsize < 4096) { totalsize = 4096; @@ -818,58 +853,58 @@ public class HSSFWorkbook { return workbook; } - + /** gets the total number of named ranges in the workboko * @return number of named ranges - */ + */ public int getNumberOfNames(){ int result = names.size(); return result; } - + /** gets the Named range * @param index position of the named range * @return named range high level - */ + */ public HSSFName getNameAt(int index){ HSSFName result = (HSSFName) names.get(index); - + return result; } - + /** gets the named range name * @param index the named range index (0 based) * @return named range name - */ + */ public String getNameName(int index){ String result = getNameAt(index).getNameName(); - + return result; } - + /** * Sets the printarea for the sheet provided *

* i.e. Reference = $A$1:$B$2 * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) - * @param reference Valid name Reference for the Print Area + * @param reference Valid name Reference for the Print Area */ public void setPrintArea(int sheetIndex, String reference) { NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); - + if (name == null) name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); //adding one here because 0 indicates a global named region; doesnt make sense for print areas - + short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex); - name.setExternSheetNumber(externSheetIndex); + name.setExternSheetNumber(externSheetIndex); name.setAreaReference(reference); - - + + } - + /** * For the Convenience of Java Programmers maintaining pointers. * @see setPrintArea(int, String) @@ -881,57 +916,57 @@ public class HSSFWorkbook */ public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) { - - //using absolute references because they dont get copied and pasted anyway + + //using absolute references because they dont get copied and pasted anyway CellReference cell = new CellReference(startRow, startColumn, true, true); String reference = cell.toString(); - + cell = new CellReference(endRow, endColumn, true, true); reference = reference+":"+cell.toString(); - - setPrintArea(sheetIndex, reference); + + setPrintArea(sheetIndex, reference); } - - + + /** * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified. - * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) + * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) * @return String Null if no print area has been defined - */ + */ public String getPrintArea(int sheetIndex) { - NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); + NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); if (name == null) return null; //adding one here because 0 indicates a global named region; doesnt make sense for print areas - + return name.getAreaReference(workbook); - } - + } + /** * Delete the printarea for the sheet specified * @param sheetIndex Zero-based sheet index (0 = First Sheet) */ public void removePrintArea(int sheetIndex) { - getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); + getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); } - + /** creates a new named range and add it to the model * @return named range high level - */ + */ public HSSFName createName(){ NameRecord nameRecord = workbook.createName(); - + HSSFName newName = new HSSFName(workbook, nameRecord); - + names.add(newName); - - return newName; + + return newName; } - + /** gets the named range index by his name * @param name named range name - * @return named range index - */ + * @return named range index + */ public int getNameIndex(String name) { int retval = -1; @@ -952,10 +987,10 @@ public class HSSFWorkbook /** remove the named range by his index * @param index named range index (0 based) - */ + */ public void removeName(int index){ names.remove(index); - workbook.removeName(index); + workbook.removeName(index); } /** @@ -969,29 +1004,29 @@ public class HSSFWorkbook formatter = new HSSFDataFormat(workbook); return formatter; } - + /** remove the named range by his name * @param name named range name - */ + */ public void removeName(String name){ int index = getNameIndex(name); - - removeName(index); - + + removeName(index); + } public HSSFPalette getCustomPalette() { return new HSSFPalette(workbook.getCustomPalette()); } - + /** * Copies nodes from one POIFS to the other minus the excepts * @param source is the source POIFS to copy from - * @param target is the target POIFS to copy to - * @param excepts is a list of Strings specifying what nodes NOT to copy + * @param target is the target POIFS to copy to + * @param excepts is a list of Strings specifying what nodes NOT to copy */ - private void copyNodes(POIFSFileSystem source, POIFSFileSystem target, + private void copyNodes(POIFSFileSystem source, POIFSFileSystem target, List excepts) throws IOException { //System.err.println("CopyNodes called"); @@ -999,13 +1034,13 @@ public class HSSFWorkbook DirectoryEntry newRoot = target.getRoot(); Iterator entries = root.getEntries(); - + while (entries.hasNext()) { Entry entry = (Entry)entries.next(); if (!isInList(entry.getName(), excepts)) { copyNodeRecursively(entry,newRoot); } - } + } } private boolean isInList(String entry, List list) { @@ -1017,18 +1052,18 @@ public class HSSFWorkbook return false; } - private void copyNodeRecursively(Entry entry, DirectoryEntry target) + private void copyNodeRecursively(Entry entry, DirectoryEntry target) throws IOException { //System.err.println("copyNodeRecursively called with "+entry.getName()+ // ","+target.getName()); - DirectoryEntry newTarget = null; + DirectoryEntry newTarget = null; if (entry.isDirectoryEntry()) { newTarget = target.createDirectory(entry.getName()); Iterator entries = ((DirectoryEntry)entry).getEntries(); while (entries.hasNext()) { copyNodeRecursively((Entry)entries.next(),newTarget); - } + } } else { DocumentEntry dentry = (DocumentEntry)entry; DocumentInputStream dstream = new DocumentInputStream(dentry); @@ -1037,6 +1072,7 @@ public class HSSFWorkbook } } + /** Test only. Do not use */ public void insertChartRecord() { int loc = workbook.findFirstRecordLocBySid(SSTRecord.sid); diff --git a/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java b/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java new file mode 100644 index 000000000..1c7b330cc --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/StaticFontMetrics.java @@ -0,0 +1,81 @@ +package org.apache.poi.hssf.usermodel; + +import java.util.*; +import java.awt.*; +import java.io.*; + +/** + * Allows the user to lookup the font metrics for a particular font without + * actually having the font on the system. The font details are loaded + * as a resource from the POI jar file (or classpath) and should be contained + * in path "/font_metrics.properties". The font widths are for a 10 point + * version of the font. Use a multiplier for other sizes. + * + * @author Glen Stampoultzis (glens at apache.org) + */ +class StaticFontMetrics +{ + private static Properties fontMetricsProps; + private static Map fontDetailsMap = new HashMap(); + + /** + * Retrieves the fake font details for a given font. + * @param font the font to lookup. + * @return the fake font. + */ + public static FontDetails getFontDetails(Font font) + { + if (fontMetricsProps == null) + { + InputStream metricsIn = null; + try + { + fontMetricsProps = new Properties(); + if (System.getProperty("font.metrics.filename") != null) + { + String filename = System.getProperty("font.metrics.filename"); + File file = new File(filename); + if (!file.exists()) + throw new FileNotFoundException("font_metrics.properties not found at path " + file.getAbsolutePath()); + metricsIn = new FileInputStream(file); + } + else + { + metricsIn = FontDetails.class.getResourceAsStream("/font_metrics.properties"); + if (metricsIn == null) + throw new FileNotFoundException("font_metrics.properties not found in classpath"); + } + fontMetricsProps.load(metricsIn); + } + catch ( IOException e ) + { + throw new RuntimeException("Could not load font metrics: " + e.getMessage()); + } + finally + { + if (metricsIn != null) + { + try + { + metricsIn.close(); + } + catch ( IOException ignore ) { } + } + } + } + + String fontName = font.getName(); + + if (fontDetailsMap.get(fontName) == null) + { + FontDetails fontDetails = FontDetails.create(fontName, fontMetricsProps); + fontDetailsMap.put( fontName, fontDetails ); + return fontDetails; + } + else + { + return (FontDetails) fontDetailsMap.get(fontName); + } + + } +}