diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index f214e05e1..84c1156af 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers 30311 - More work on Conditional Formatting refactored all junits' usage of HSSF.testdata.path to one place 44739 - Small fixes for conditional formatting (regions with max row/col index) diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 7bdc284da..bb7394dd3 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 43670, 44501 - Fix how HDGF deals with trailing data in the list of chunk headers 30311 - More work on Conditional Formatting refactored all junits' usage of HSSF.testdata.path to one place 44739 - Small fixes for conditional formatting (regions with max row/col index) diff --git a/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeader.java b/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeader.java index 0d17669c2..88c91c74f 100644 --- a/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeader.java +++ b/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeader.java @@ -47,10 +47,32 @@ public abstract class ChunkHeader { ch.unknown3 = (short)LittleEndian.getUnsignedByte(data, offset + 18); return ch; - } else if(documentVersion == 5) { - throw new RuntimeException("TODO"); + } else if(documentVersion == 5 || documentVersion == 4) { + ChunkHeaderV4V5 ch = new ChunkHeaderV4V5(); + + ch.type = (int)LittleEndian.getShort(data, offset + 0); + ch.id = (int)LittleEndian.getShort(data, offset + 2); + ch.unknown2 = (short)LittleEndian.getUnsignedByte(data, offset + 4); + ch.unknown3 = (short)LittleEndian.getUnsignedByte(data, offset + 5); + ch.unknown1 = (short)LittleEndian.getShort(data, offset + 6); + ch.length = (int)LittleEndian.getUInt(data, offset + 8); + + return ch; } else { - throw new IllegalArgumentException("Visio files with versions below 5 are not supported, yours was " + documentVersion); + throw new IllegalArgumentException("Visio files with versions below 4 are not supported, yours was " + documentVersion); + } + } + + /** + * Returns the size of a chunk header for the given document version. + */ + public static int getHeaderSize(int documentVersion) { + if(documentVersion > 6) { + return ChunkHeaderV11.getHeaderSize(); + } else if(documentVersion == 6) { + return ChunkHeaderV6.getHeaderSize(); + } else { + return ChunkHeaderV4V5.getHeaderSize(); } } diff --git a/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV4V5.java b/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV4V5.java new file mode 100644 index 000000000..335c9c543 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV4V5.java @@ -0,0 +1,56 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.hdgf.chunks; + +/** + * A chunk header from v4 or v5 + */ +public class ChunkHeaderV4V5 extends ChunkHeader { + protected short unknown2; + protected short unknown3; + + public short getUnknown2() { + return unknown2; + } + public short getUnknown3() { + return unknown3; + } + + protected static int getHeaderSize() { + return 12; + } + + public int getSizeInBytes() { + return getHeaderSize(); + } + + /** + * Does the chunk have a trailer? + */ + public boolean hasTrailer() { + // V4 and V5 never has trailers + return false; + } + + /** + * Does the chunk have a separator? + */ + public boolean hasSeparator() { + // V4 and V5 never has separators + return false; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV6.java b/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV6.java index e8061563a..4d13b85bd 100644 --- a/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV6.java +++ b/src/scratchpad/src/org/apache/poi/hdgf/chunks/ChunkHeaderV6.java @@ -30,9 +30,13 @@ public class ChunkHeaderV6 extends ChunkHeader { return unknown3; } - public int getSizeInBytes() { + protected static int getHeaderSize() { + // Looks like it ought to be 19... return 19; } + public int getSizeInBytes() { + return getHeaderSize(); + } /** * Does the chunk have a trailer? diff --git a/src/scratchpad/src/org/apache/poi/hdgf/extractor/VisioTextExtractor.java b/src/scratchpad/src/org/apache/poi/hdgf/extractor/VisioTextExtractor.java index a7857e46f..034714c7b 100644 --- a/src/scratchpad/src/org/apache/poi/hdgf/extractor/VisioTextExtractor.java +++ b/src/scratchpad/src/org/apache/poi/hdgf/extractor/VisioTextExtractor.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import org.apache.poi.POITextExtractor; import org.apache.poi.hdgf.HDGFDiagram; +import org.apache.poi.hdgf.chunks.Chunk; import org.apache.poi.hdgf.chunks.Chunk.Command; import org.apache.poi.hdgf.streams.ChunkStream; import org.apache.poi.hdgf.streams.PointerContainingStream; @@ -71,11 +72,13 @@ public class VisioTextExtractor extends POITextExtractor { if(stream instanceof ChunkStream) { ChunkStream cs = (ChunkStream)stream; for(int i=0; i 0) { // First command - Command cmd = cs.getChunks()[i].getCommands()[0]; + Command cmd = chunk.getCommands()[0]; if(cmd != null && cmd.getValue() != null) { text.add( cmd.getValue().toString() ); } diff --git a/src/scratchpad/src/org/apache/poi/hdgf/streams/ChunkStream.java b/src/scratchpad/src/org/apache/poi/hdgf/streams/ChunkStream.java index a59fe43ff..65b69d95a 100644 --- a/src/scratchpad/src/org/apache/poi/hdgf/streams/ChunkStream.java +++ b/src/scratchpad/src/org/apache/poi/hdgf/streams/ChunkStream.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import org.apache.poi.hdgf.chunks.Chunk; import org.apache.poi.hdgf.chunks.ChunkFactory; +import org.apache.poi.hdgf.chunks.ChunkHeader; import org.apache.poi.hdgf.pointers.Pointer; public class ChunkStream extends Stream { @@ -51,10 +52,17 @@ public class ChunkStream extends Stream { int pos = 0; byte[] contents = getStore().getContents(); while(pos < contents.length) { - Chunk chunk = chunkFactory.createChunk(contents, pos); - chunksA.add(chunk); - - pos += chunk.getOnDiskSize(); + // Ensure we have enough data to create a chunk from + int headerSize = ChunkHeader.getHeaderSize(chunkFactory.getVersion()); + if(pos+headerSize <= contents.length) { + Chunk chunk = chunkFactory.createChunk(contents, pos); + chunksA.add(chunk); + + pos += chunk.getOnDiskSize(); + } else { + System.err.println("Needed " + headerSize + " bytes to create the next chunk header, but only found " + (contents.length-pos) + " bytes, ignoring rest of data"); + pos = contents.length; + } } chunks = (Chunk[])chunksA.toArray(new Chunk[chunksA.size()]); diff --git a/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt b/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt index 23e1e94ca..20d2398e3 100644 Binary files a/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt and b/src/scratchpad/src/org/apache/poi/hslf/data/empty.ppt differ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java index bc01d9970..4aad44d75 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java @@ -21,25 +21,28 @@ import java.awt.*; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; +import java.awt.font.TextLayout; import java.awt.image.*; import java.awt.image.renderable.RenderableImage; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; +import java.awt.geom.*; import java.text.AttributedCharacterIterator; import java.util.Map; import java.util.ArrayList; - -import org.apache.poi.ddf.EscherProperties; import org.apache.poi.hslf.usermodel.RichTextRun; import org.apache.poi.hslf.exceptions.HSLFException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Translates Graphics2D calls into PowerPoint. * * @author Yegor Kozlov */ -public class PPGraphics2D extends Graphics2D { - //The group to write the graphics calls into. +public class PPGraphics2D extends Graphics2D implements Cloneable { + + private static final Log log = LogFactory.getLog(PPGraphics2D.class); + + //The ppt object to write into. private ShapeGroup group; private AffineTransform transform; @@ -47,9 +50,14 @@ public class PPGraphics2D extends Graphics2D { private Paint paint; private Font font; private Color foreground; - private Color background = Color.white; - private Shape clip; - int count = 0; + private Color background; + private RenderingHints hints; + + /** + * the maximum distance that the line segments used to approximate the curved segments + */ + public static final float FLATNESS = 0.1f; + /** * Construct Java Graphics object which translates graphic calls in ppt drawing layer. * @@ -57,7 +65,14 @@ public class PPGraphics2D extends Graphics2D { */ public PPGraphics2D(ShapeGroup group){ this.group = group; + transform = new AffineTransform(); + stroke = new BasicStroke(); + paint = Color.black; + font = new Font("Arial", Font.PLAIN, 12); + background = Color.black; + foreground = Color.white; + hints = new RenderingHints(null); } /** @@ -67,58 +82,144 @@ public class PPGraphics2D extends Graphics2D { return group; } + /** + * Gets the current font. + * @return this graphics context's current font. + * @see java.awt.Font + * @see java.awt.Graphics#setFont(Font) + */ public Font getFont(){ return font; } + /** + * Sets this graphics context's font to the specified font. + * All subsequent text operations using this graphics context + * use this font. + * @param font the font. + * @see java.awt.Graphics#getFont + * @see java.awt.Graphics#drawString(java.lang.String, int, int) + * @see java.awt.Graphics#drawBytes(byte[], int, int, int, int) + * @see java.awt.Graphics#drawChars(char[], int, int, int, int) + */ public void setFont(Font font){ this.font = font; } - public Color getColor(){ + /** + * Gets this graphics context's current color. + * @return this graphics context's current color. + * @see java.awt.Color + * @see java.awt.Graphics#setColor + */ + public Color getColor(){ return foreground; } - public void setColor(Color color) { - this.foreground = color; + /** + * Sets this graphics context's current color to the specified + * color. All subsequent graphics operations using this graphics + * context use this specified color. + * @param c the new rendering color. + * @see java.awt.Color + * @see java.awt.Graphics#getColor + */ + public void setColor(Color c) { + setPaint(c); } + /** + * Returns the current Stroke in the + * Graphics2D context. + * @return the current Graphics2D Stroke, + * which defines the line style. + * @see #setStroke + */ public Stroke getStroke(){ return stroke; } + /** + * Sets the Stroke for the Graphics2D context. + * @param s the Stroke object to be used to stroke a + * Shape during the rendering process + */ public void setStroke(Stroke s){ this.stroke = s; } + /** + * Returns the current Paint of the + * Graphics2D context. + * @return the current Graphics2D Paint, + * which defines a color or pattern. + * @see #setPaint + * @see java.awt.Graphics#setColor + */ public Paint getPaint(){ return paint; } - public void setPaint(Paint paint){ + /** + * Sets the Paint attribute for the + * Graphics2D context. Calling this method + * with a null Paint object does + * not have any effect on the current Paint attribute + * of this Graphics2D. + * @param paint the Paint object to be used to generate + * color during the rendering process, or null + * @see java.awt.Graphics#setColor + */ + public void setPaint(Paint paint){ + if(paint == null) return; + this.paint = paint; - if (paint instanceof Color) setColor((Color)paint); + if (paint instanceof Color) foreground = (Color)paint; } + /** + * Returns a copy of the current Transform in the + * Graphics2D context. + * @return the current AffineTransform in the + * Graphics2D context. + * @see #transform + * @see #setTransform + */ public AffineTransform getTransform(){ - return (AffineTransform)transform.clone(); + return new AffineTransform(transform); } - public void setTransform(AffineTransform trans) { - transform = (AffineTransform)trans.clone(); + /** + * Sets the Transform in the Graphics2D + * context. + * @param Tx the AffineTransform object to be used in the + * rendering process + * @see #transform + * @see AffineTransform + */ + public void setTransform(AffineTransform Tx) { + transform = new AffineTransform(Tx); } + /** + * Strokes the outline of a Shape using the settings of the + * current Graphics2D context. The rendering attributes + * applied include the Clip, Transform, + * Paint, Composite and + * Stroke attributes. + * @param shape the Shape to be rendered + * @see #setStroke + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #transform + * @see #setTransform + * @see #clip + * @see #setClip + * @see #setComposite + */ public void draw(Shape shape){ - if(clip != null) { - java.awt.Rectangle bounds = getTransform().createTransformedShape(shape).getBounds(); - if (bounds.width == 0) bounds.width = 1; - if (bounds.height == 0) bounds.height = 1; - if (!clip.getBounds().contains(bounds)) { - return; - } - } - PathIterator it = shape.getPathIterator(transform); + PathIterator it = shape.getPathIterator(transform, FLATNESS); double[] prev = null; double[] coords = new double[6]; double[] first = new double[6]; @@ -127,17 +228,12 @@ public class PPGraphics2D extends Graphics2D { int type = it.currentSegment(coords); if (prev != null ){ Line line = new Line(group); - if (stroke instanceof BasicStroke){ - BasicStroke bs = (BasicStroke)stroke; - line.setLineWidth(bs.getLineWidth()); - float[] dash = bs.getDashArray(); - if (dash != null) line.setLineDashing(Line.PEN_DASH); - } - if(getColor() != null) line.setLineColor(getColor()); + applyPaint(line); + applyStroke(line); if (type == PathIterator.SEG_LINETO) { - line.setAnchor(new java.awt.Rectangle((int)prev[0], (int)prev[1], (int)(coords[0] - prev[0]), (int)(coords[1] - prev[1]))); + line.setAnchor(new Rectangle2D.Double(prev[0], prev[1], (coords[0] - prev[0]), (coords[1] - prev[1]))); } else if (type == PathIterator.SEG_CLOSE){ - line.setAnchor(new java.awt.Rectangle((int)coords[0], (int)coords[1], (int)(first[0] - coords[0]), (int)(first[1] - coords[1]))); + line.setAnchor(new Rectangle2D.Double(coords[0], coords[1], (first[0] - coords[0]), (first[1] - coords[1]))); } group.addShape(line); } @@ -147,368 +243,1589 @@ public class PPGraphics2D extends Graphics2D { } - public void drawString(String string, float x, float y){ - TextBox txt = new TextBox(group); - txt.getTextRun().supplySlideShow(group.getSheet().getSlideShow()); - txt.getTextRun().setSheet(group.getSheet()); - txt.setText(string); + /** + * Renders the text specified by the specified String, + * using the current text attribute state in the Graphics2D context. + * The baseline of the first character is at position + * (xy) in the User Space. + * The rendering attributes applied include the Clip, + * Transform, Paint, Font and + * Composite attributes. For characters in script systems + * such as Hebrew and Arabic, the glyphs can be rendered from right to + * left, in which case the coordinate supplied is the location of the + * leftmost character on the baseline. + * @param s the String to be rendered + * @param x the x coordinate of the location where the + * String should be rendered + * @param y the y coordinate of the location where the + * String should be rendered + * @throws NullPointerException if str is + * null + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see java.awt.Graphics#setFont + * @see #setTransform + * @see #setComposite + * @see #setClip + */ + public void drawString(String s, float x, float y) { + TextBox txt = new TextBox(group); + txt.getTextRun().supplySlideShow(group.getSheet().getSlideShow()); + txt.getTextRun().setSheet(group.getSheet()); + txt.setText(s); - RichTextRun rt = txt.getTextRun().getRichTextRuns()[0]; - rt.setFontSize(font.getSize()); - rt.setFontName(font.getFamily()); + RichTextRun rt = txt.getTextRun().getRichTextRuns()[0]; + rt.setFontSize(font.getSize()); + rt.setFontName(font.getFamily()); - if(getColor() != null) rt.setFontColor(getColor()); + if (getColor() != null) rt.setFontColor(getColor()); if (font.isBold()) rt.setBold(true); if (font.isItalic()) rt.setItalic(true); - txt.setMarginBottom(0); - txt.setMarginTop(0); - txt.setMarginLeft(0); - txt.setMarginRight(0); - txt.setWordWrap(TextBox.WrapNone); + txt.setMarginBottom(0); + txt.setMarginTop(0); + txt.setMarginLeft(0); + txt.setMarginRight(0); + txt.setWordWrap(TextBox.WrapNone); + txt.setHorizontalAlignment(TextBox.AlignLeft); + txt.setVerticalAlignment(TextBox.AnchorMiddle); - if (!"".equals(string)) txt.resizeToFitText(); - int height = (int)txt.getAnchor().getHeight(); - /* - In powerpoint anchor of a shape is its top left corner. - Java graphics sets string coordinates by the baseline of the first character - so we need to shift down by the height of the textbox + TextLayout layout = new TextLayout(s, font, getFontRenderContext()); + float ascent = layout.getAscent(); + + float width = (float) Math.floor(layout.getAdvance()); + /** + * Even if top and bottom margins are set to 0 PowerPoint + * always sets extra space between the text and its bounding box. + * + * Approximation height = ascent*2 works good enough in most cases */ - txt.moveTo((int)x, (int)(y - height)); + float height = ascent * 2; - if(clip != null) { - if (!clip.getBounds().contains(txt.getAnchor())) { - ;//return; - } - } - group.addShape(txt); + /* + In powerpoint anchor of a shape is its top left corner. + Java graphics sets string coordinates by the baseline of the first character + so we need to shift up by the height of the textbox + */ + y -= height / 2 + ascent / 2; + + /* + In powerpoint anchor of a shape is its top left corner. + Java graphics sets string coordinates by the baseline of the first character + so we need to shift down by the height of the textbox + */ + txt.setAnchor(new Rectangle2D.Float(x, y, width, height)); + + group.addShape(txt); } + /** + * Fills the interior of a Shape using the settings of the + * Graphics2D context. The rendering attributes applied + * include the Clip, Transform, + * Paint, and Composite. + * @param shape the Shape to be filled + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip + */ public void fill(Shape shape){ - if(clip != null) { - java.awt.Rectangle bounds = getTransform().createTransformedShape(shape).getBounds(); - if (bounds.width == 0) bounds.width = 1; - if (bounds.height == 0) bounds.height = 1; - if (!clip.getBounds().contains(bounds)) { - return; - } - } - PathIterator it = shape.getPathIterator(transform); + PathIterator it = shape.getPathIterator(transform, FLATNESS); ArrayList pnt = new ArrayList(); double[] coords = new double[6]; while(!it.isDone()){ int type = it.currentSegment(coords); if (type != PathIterator.SEG_CLOSE) { - pnt.add(new Point((int)coords[0], (int)coords[1])); + pnt.add(new Point2D.Double(coords[0], coords[1])); } it.next(); } - int[] xPoints= new int[pnt.size()]; - int[] yPoints= new int[pnt.size()]; - for (int i = 0; i < pnt.size(); i++) { - Point p = (Point)pnt.get(i); - xPoints[i] = p.x; - yPoints[i] = p.y; + if(pnt.size() > 0){ + Point2D[] points = (Point2D[])pnt.toArray(new Point2D[pnt.size()]); + Polygon p = new Polygon(group); + p.setPoints(points); + applyPaint(p); + + p.setLineColor(null); //Fills must be "No Line" + + Rectangle2D bounds = transform.createTransformedShape(shape).getBounds2D(); + p.setAnchor(bounds); + group.addShape(p); } - - AutoShape r = new AutoShape(ShapeTypes.Rectangle); - if (paint instanceof Color){ - Color color = (Color)paint; - r.setFillColor(color); - } - if(getColor() != null) r.setLineColor(getColor()); - if (stroke instanceof BasicStroke){ - BasicStroke bs = (BasicStroke)stroke; - r.setLineWidth(bs.getLineWidth()); - float[] dash = bs.getDashArray(); - if (dash != null) r.setLineDashing(Line.PEN_DASH); - } - - java.awt.Rectangle bounds = transform.createTransformedShape(shape).getBounds(); - r.setAnchor(bounds); - group.addShape(r); } - public void translate(int x, int y) { - AffineTransform at = new AffineTransform(); - at.translate(x, y); - transform.concatenate(at); + /** + * Translates the origin of the graphics context to the point + * (xy) in the current coordinate system. + * Modifies this graphics context so that its new origin corresponds + * to the point (xy) in this graphics context's + * original coordinate system. All coordinates used in subsequent + * rendering operations on this graphics context will be relative + * to this new origin. + * @param x the x coordinate. + * @param y the y coordinate. + */ + public void translate(int x, int y){ + transform.translate(x, y); } - public void clip(Shape shape) { - this.clip = transform.createTransformedShape(shape); - //update size of the escher group which holds the drawing - group.setAnchor(clip.getBounds()); + /** + * Intersects the current Clip with the interior of the + * specified Shape and sets the Clip to the + * resulting intersection. The specified Shape is + * transformed with the current Graphics2D + * Transform before being intersected with the current + * Clip. This method is used to make the current + * Clip smaller. + * To make the Clip larger, use setClip. + * The user clip modified by this method is independent of the + * clipping associated with device bounds and visibility. If no clip has + * previously been set, or if the clip has been cleared using + * {@link java.awt.Graphics#setClip(Shape) setClip} with a + * null argument, the specified Shape becomes + * the new user clip. + * @param s the Shape to be intersected with the current + * Clip. If s is null, + * this method clears the current Clip. + */ + public void clip(Shape s){ + log.warn("Not implemented"); } - public Shape getClip() { - return clip; + /** + * Gets the current clipping area. + * This method returns the user clip, which is independent of the + * clipping associated with device bounds and window visibility. + * If no clip has previously been set, or if the clip has been + * cleared using setClip(null), this method returns + * null. + * @return a Shape object representing the + * current clipping area, or null if + * no clip is set. + * @see java.awt.Graphics#getClipBounds() + * @see java.awt.Graphics#clipRect + * @see java.awt.Graphics#setClip(int, int, int, int) + * @see java.awt.Graphics#setClip(Shape) + * @since JDK1.1 + */ + public Shape getClip(){ + log.warn("Not implemented"); + return null; } - public void scale(double sx, double sy) { - AffineTransform at = new AffineTransform(); - at.scale(sx, sy); - transform.concatenate(at); - } - //=============================================== - public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - throw new HSLFException("Not implemented"); + /** + * Concatenates the current Graphics2D + * Transform with a scaling transformation + * Subsequent rendering is resized according to the specified scaling + * factors relative to the previous scaling. + * This is equivalent to calling transform(S), where S is an + * AffineTransform represented by the following matrix: + *
+     *          [   sx   0    0   ]
+     *          [   0    sy   0   ]
+     *          [   0    0    1   ]
+     * 
+ * @param sx the amount by which X coordinates in subsequent + * rendering operations are multiplied relative to previous + * rendering operations. + * @param sy the amount by which Y coordinates in subsequent + * rendering operations are multiplied relative to previous + * rendering operations. + */ + public void scale(double sx, double sy){ + transform.scale(sx, sy); } - public void drawString(String str, int x, int y) { - throw new HSLFException("Not implemented"); + /** + * Draws an outlined round-cornered rectangle using this graphics + * context's current color. The left and right edges of the rectangle + * are at x and x + width, + * respectively. The top and bottom edges of the rectangle are at + * y and y + height. + * @param x the x coordinate of the rectangle to be drawn. + * @param y the y coordinate of the rectangle to be drawn. + * @param width the width of the rectangle to be drawn. + * @param height the height of the rectangle to be drawn. + * @param arcWidth the horizontal diameter of the arc + * at the four corners. + * @param arcHeight the vertical diameter of the arc + * at the four corners. + * @see java.awt.Graphics#fillRoundRect + */ + public void drawRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight){ + AutoShape shape = new AutoShape(ShapeTypes.RoundRectangle, group); + shape.setFillColor(null); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); + } + + /** + * Draws the text given by the specified string, using this + * graphics context's current font and color. The baseline of the + * first character is at position (xy) in this + * graphics context's coordinate system. + * @param str the string to be drawn. + * @param x the x coordinate. + * @param y the y coordinate. + * @see java.awt.Graphics#drawBytes + * @see java.awt.Graphics#drawChars + */ + public void drawString(String str, int x, int y){ + drawString(str, (float)x, (float)y); } - public void fillOval(int x, int y, int width, int height) { - throw new HSLFException("Not implemented"); + /** + * Fills an oval bounded by the specified rectangle with the + * current color. + * @param x the x coordinate of the upper left corner + * of the oval to be filled. + * @param y the y coordinate of the upper left corner + * of the oval to be filled. + * @param width the width of the oval to be filled. + * @param height the height of the oval to be filled. + * @see java.awt.Graphics#drawOval + */ + public void fillOval(int x, int y, int width, int height){ + AutoShape shape = new AutoShape(ShapeTypes.Ellipse, group); + applyPaint(shape); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); } - public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { - throw new HSLFException("Not implemented"); + /** + * Fills the specified rounded corner rectangle with the current color. + * The left and right edges of the rectangle + * are at x and x + width - 1, + * respectively. The top and bottom edges of the rectangle are at + * y and y + height - 1. + * @param x the x coordinate of the rectangle to be filled. + * @param y the y coordinate of the rectangle to be filled. + * @param width the width of the rectangle to be filled. + * @param height the height of the rectangle to be filled. + * @param arcWidth the horizontal diameter + * of the arc at the four corners. + * @param arcHeight the vertical diameter + * of the arc at the four corners. + * @see java.awt.Graphics#drawRoundRect + */ + public void fillRoundRect(int x, int y, int width, int height, + int arcWidth, int arcHeight){ + AutoShape shape = new AutoShape(ShapeTypes.RoundRectangle, group); + applyPaint(shape); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); } + /** + * Fills a circular or elliptical arc covering the specified rectangle. + *

+ * The resulting arc begins at startAngle and extends + * for arcAngle degrees. + * Angles are interpreted such that 0 degrees + * is at the 3 o'clock position. + * A positive value indicates a counter-clockwise rotation + * while a negative value indicates a clockwise rotation. + *

+ * The center of the arc is the center of the rectangle whose origin + * is (xy) and whose size is specified by the + * width and height arguments. + *

+ * The resulting arc covers an area + * width + 1 pixels wide + * by height + 1 pixels tall. + *

+ * The angles are specified relative to the non-square extents of + * the bounding rectangle such that 45 degrees always falls on the + * line from the center of the ellipse to the upper right corner of + * the bounding rectangle. As a result, if the bounding rectangle is + * noticeably longer in one axis than the other, the angles to the + * start and end of the arc segment will be skewed farther along the + * longer axis of the bounds. + * @param x the x coordinate of the + * upper-left corner of the arc to be filled. + * @param y the y coordinate of the + * upper-left corner of the arc to be filled. + * @param width the width of the arc to be filled. + * @param height the height of the arc to be filled. + * @param startAngle the beginning angle. + * @param arcAngle the angular extent of the arc, + * relative to the start angle. + * @see java.awt.Graphics#drawArc + */ public void fillArc(int x, int y, int width, int height, - int startAngle, int arcAngle) { - throw new HSLFException("Not implemented"); - } - - public void setPaintMode() { - throw new HSLFException("Not implemented"); + int startAngle, int arcAngle){ + AutoShape shape = new AutoShape(ShapeTypes.Arc, group); + applyPaint(shape); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); } + /** + * Draws the outline of a circular or elliptical arc + * covering the specified rectangle. + *

+ * The resulting arc begins at startAngle and extends + * for arcAngle degrees, using the current color. + * Angles are interpreted such that 0 degrees + * is at the 3 o'clock position. + * A positive value indicates a counter-clockwise rotation + * while a negative value indicates a clockwise rotation. + *

+ * The center of the arc is the center of the rectangle whose origin + * is (xy) and whose size is specified by the + * width and height arguments. + *

+ * The resulting arc covers an area + * width + 1 pixels wide + * by height + 1 pixels tall. + *

+ * The angles are specified relative to the non-square extents of + * the bounding rectangle such that 45 degrees always falls on the + * line from the center of the ellipse to the upper right corner of + * the bounding rectangle. As a result, if the bounding rectangle is + * noticeably longer in one axis than the other, the angles to the + * start and end of the arc segment will be skewed farther along the + * longer axis of the bounds. + * @param x the x coordinate of the + * upper-left corner of the arc to be drawn. + * @param y the y coordinate of the + * upper-left corner of the arc to be drawn. + * @param width the width of the arc to be drawn. + * @param height the height of the arc to be drawn. + * @param startAngle the beginning angle. + * @param arcAngle the angular extent of the arc, + * relative to the start angle. + * @see java.awt.Graphics#fillArc + */ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { - throw new HSLFException("Not implemented"); + AutoShape shape = new AutoShape(ShapeTypes.Arc, group); + shape.setFillColor(null); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); } - public void drawPolyline(int xPoints[], int yPoints[], - int nPoints) { - throw new HSLFException("Not implemented"); - } + /** + * Draws a sequence of connected lines defined by + * arrays of x and y coordinates. + * Each pair of (xy) coordinates defines a point. + * The figure is not closed if the first point + * differs from the last point. + * @param xPoints an array of x points + * @param yPoints an array of y points + * @param nPoints the total number of points + * @see java.awt.Graphics#drawPolygon(int[], int[], int) + * @since JDK1.1 + */ + public void drawPolyline(int[] xPoints, int[] yPoints, + int nPoints){ + if(nPoints > 0){ + GeneralPath path = new GeneralPath(); + path.moveTo(xPoints[0], yPoints[0]); + for(int i=1; ix, y, + * width, and height arguments. + *

+ * The oval covers an area that is + * width + 1 pixels wide + * and height + 1 pixels tall. + * @param x the x coordinate of the upper left + * corner of the oval to be drawn. + * @param y the y coordinate of the upper left + * corner of the oval to be drawn. + * @param width the width of the oval to be drawn. + * @param height the height of the oval to be drawn. + * @see java.awt.Graphics#fillOval + */ + public void drawOval(int x, int y, int width, int height){ + AutoShape shape = new AutoShape(ShapeTypes.Ellipse, group); + shape.setFillColor(null); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); } - + /** + * Draws as much of the specified image as is currently available. + * The image is drawn with its top-left corner at + * (xy) in this graphics context's coordinate + * space. Transparent pixels are drawn in the specified + * background color. + *

+ * This operation is equivalent to filling a rectangle of the + * width and height of the specified image with the given color and then + * drawing the image on top of it, but possibly more efficient. + *

+ * This method returns immediately in all cases, even if the + * complete image has not yet been loaded, and it has not been dithered + * and converted for the current output device. + *

+ * If the image has not yet been completely loaded, then + * drawImage returns false. As more of + * the image becomes available, the process that draws the image notifies + * the specified image observer. + * @param img the specified image to be drawn. + * @param x the x coordinate. + * @param y the y coordinate. + * @param bgcolor the background color to paint under the + * non-opaque portions of the image. + * @param observer object to be notified as more of + * the image is converted. + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + */ public boolean drawImage(Image img, int x, int y, Color bgcolor, - ImageObserver observer) { - throw new HSLFException("Not implemented"); + ImageObserver observer){ + log.warn("Not implemented"); + + return false; } + /** + * Draws as much of the specified image as has already been scaled + * to fit inside the specified rectangle. + *

+ * The image is drawn inside the specified rectangle of this + * graphics context's coordinate space, and is scaled if + * necessary. Transparent pixels are drawn in the specified + * background color. + * This operation is equivalent to filling a rectangle of the + * width and height of the specified image with the given color and then + * drawing the image on top of it, but possibly more efficient. + *

+ * This method returns immediately in all cases, even if the + * entire image has not yet been scaled, dithered, and converted + * for the current output device. + * If the current output representation is not yet complete then + * drawImage returns false. As more of + * the image becomes available, the process that draws the image notifies + * the specified image observer. + *

+ * A scaled version of an image will not necessarily be + * available immediately just because an unscaled version of the + * image has been constructed for this output device. Each size of + * the image may be cached separately and generated from the original + * data in a separate image production sequence. + * @param img the specified image to be drawn. + * @param x the x coordinate. + * @param y the y coordinate. + * @param width the width of the rectangle. + * @param height the height of the rectangle. + * @param bgcolor the background color to paint under the + * non-opaque portions of the image. + * @param observer object to be notified as more of + * the image is converted. + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + */ public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, - ImageObserver observer) { - throw new HSLFException("Not implemented"); + ImageObserver observer){ + log.warn("Not implemented"); + + return false; } + /** + * Draws as much of the specified area of the specified image as is + * currently available, scaling it on the fly to fit inside the + * specified area of the destination drawable surface. Transparent pixels + * do not affect whatever pixels are already there. + *

+ * This method returns immediately in all cases, even if the + * image area to be drawn has not yet been scaled, dithered, and converted + * for the current output device. + * If the current output representation is not yet complete then + * drawImage returns false. As more of + * the image becomes available, the process that draws the image notifies + * the specified image observer. + *

+ * This method always uses the unscaled version of the image + * to render the scaled rectangle and performs the required + * scaling on the fly. It does not use a cached, scaled version + * of the image for this operation. Scaling of the image from source + * to destination is performed such that the first coordinate + * of the source rectangle is mapped to the first coordinate of + * the destination rectangle, and the second source coordinate is + * mapped to the second destination coordinate. The subimage is + * scaled and flipped as needed to preserve those mappings. + * @param img the specified image to be drawn + * @param dx1 the x coordinate of the first corner of the + * destination rectangle. + * @param dy1 the y coordinate of the first corner of the + * destination rectangle. + * @param dx2 the x coordinate of the second corner of the + * destination rectangle. + * @param dy2 the y coordinate of the second corner of the + * destination rectangle. + * @param sx1 the x coordinate of the first corner of the + * source rectangle. + * @param sy1 the y coordinate of the first corner of the + * source rectangle. + * @param sx2 the x coordinate of the second corner of the + * source rectangle. + * @param sy2 the y coordinate of the second corner of the + * source rectangle. + * @param observer object to be notified as more of the image is + * scaled and converted. + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + * @since JDK1.1 + */ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, - ImageObserver observer) { - throw new HSLFException("Not implemented"); + ImageObserver observer){ + log.warn("Not implemented"); + return false; } + /** + * Draws as much of the specified area of the specified image as is + * currently available, scaling it on the fly to fit inside the + * specified area of the destination drawable surface. + *

+ * Transparent pixels are drawn in the specified background color. + * This operation is equivalent to filling a rectangle of the + * width and height of the specified image with the given color and then + * drawing the image on top of it, but possibly more efficient. + *

+ * This method returns immediately in all cases, even if the + * image area to be drawn has not yet been scaled, dithered, and converted + * for the current output device. + * If the current output representation is not yet complete then + * drawImage returns false. As more of + * the image becomes available, the process that draws the image notifies + * the specified image observer. + *

+ * This method always uses the unscaled version of the image + * to render the scaled rectangle and performs the required + * scaling on the fly. It does not use a cached, scaled version + * of the image for this operation. Scaling of the image from source + * to destination is performed such that the first coordinate + * of the source rectangle is mapped to the first coordinate of + * the destination rectangle, and the second source coordinate is + * mapped to the second destination coordinate. The subimage is + * scaled and flipped as needed to preserve those mappings. + * @param img the specified image to be drawn + * @param dx1 the x coordinate of the first corner of the + * destination rectangle. + * @param dy1 the y coordinate of the first corner of the + * destination rectangle. + * @param dx2 the x coordinate of the second corner of the + * destination rectangle. + * @param dy2 the y coordinate of the second corner of the + * destination rectangle. + * @param sx1 the x coordinate of the first corner of the + * source rectangle. + * @param sy1 the y coordinate of the first corner of the + * source rectangle. + * @param sx2 the x coordinate of the second corner of the + * source rectangle. + * @param sy2 the y coordinate of the second corner of the + * source rectangle. + * @param bgcolor the background color to paint under the + * non-opaque portions of the image. + * @param observer object to be notified as more of the image is + * scaled and converted. + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + * @since JDK1.1 + */ 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) { - throw new HSLFException("Not implemented"); + ImageObserver observer){ + log.warn("Not implemented"); + return false; } + /** + * Draws as much of the specified image as is currently available. + * The image is drawn with its top-left corner at + * (xy) in this graphics context's coordinate + * space. Transparent pixels in the image do not affect whatever + * pixels are already there. + *

+ * This method returns immediately in all cases, even if the + * complete image has not yet been loaded, and it has not been dithered + * and converted for the current output device. + *

+ * If the image has completely loaded and its pixels are + * no longer being changed, then + * drawImage returns true. + * Otherwise, drawImage returns false + * and as more of + * the image becomes available + * or it is time to draw another frame of animation, + * the process that loads the image notifies + * the specified image observer. + * @param img the specified image to be drawn. This method does + * nothing if img is null. + * @param x the x coordinate. + * @param y the y coordinate. + * @param observer object to be notified as more of + * the image is converted. + * @return false if the image pixels are still changing; + * true otherwise. + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { - throw new HSLFException("Not implemented"); - } - - public boolean drawImage(Image img, int x, int y, - int width, int height, - ImageObserver observer) { - throw new HSLFException("Not implemented"); + log.warn("Not implemented"); + return false; } + /** + * Disposes of this graphics context and releases + * any system resources that it is using. + * A Graphics object cannot be used after + * disposehas been called. + *

+ * When a Java program runs, a large number of Graphics + * objects can be created within a short time frame. + * Although the finalization process of the garbage collector + * also disposes of the same system resources, it is preferable + * to manually free the associated resources by calling this + * method rather than to rely on a finalization process which + * may not run to completion for a long period of time. + *

+ * Graphics objects which are provided as arguments to the + * paint and update methods + * of components are automatically released by the system when + * those methods return. For efficiency, programmers should + * call dispose when finished using + * a Graphics object only if it was created + * directly from a component or another Graphics object. + * @see java.awt.Graphics#finalize + * @see java.awt.Component#paint + * @see java.awt.Component#update + * @see java.awt.Component#getGraphics + * @see java.awt.Graphics#create + */ public void dispose() { - throw new HSLFException("Not implemented"); + ; } - public void drawLine(int x1, int y1, int x2, int y2) { - Line line = new Line(); - line.setAnchor(new java.awt.Rectangle(x1, y1, x2-x1, y2-y1)); - if (stroke instanceof BasicStroke){ - BasicStroke bs = (BasicStroke)stroke; - line.setLineWidth(bs.getLineWidth()); - } - if(getColor() != null) line.setLineColor(getColor()); - group.addShape(line); + /** + * Draws a line, using the current color, between the points + * (x1, y1) and (x2, y2) + * in this graphics context's coordinate system. + * @param x1 the first point's x coordinate. + * @param y1 the first point's y coordinate. + * @param x2 the second point's x coordinate. + * @param y2 the second point's y coordinate. + */ + public void drawLine(int x1, int y1, int x2, int y2){ + Line2D line = new Line2D.Float(x1, y1, x2, y2); + draw(line); } - public void fillPolygon(int xPoints[], int yPoints[], - int nPoints) { - throw new HSLFException("Not implemented"); + /** + * 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){ + java.awt.Polygon polygon = new java.awt.Polygon(xPoints, yPoints, nPoints); + fill(polygon); } - public FontMetrics getFontMetrics(Font f) { - throw new HSLFException("Not implemented"); + /** + * Fills the specified rectangle. + * The left and right edges of the rectangle are at + * x and x + width - 1. + * The top and bottom edges are at + * y and y + height - 1. + * The resulting rectangle covers an area + * width pixels wide by + * height pixels tall. + * The rectangle is filled using the graphics context's current color. + * @param x the x coordinate + * of the rectangle to be filled. + * @param y the y coordinate + * of the rectangle to be filled. + * @param width the width of the rectangle to be filled. + * @param height the height of the rectangle to be filled. + * @see java.awt.Graphics#clearRect + * @see java.awt.Graphics#drawRect + */ + public void fillRect(int x, int y, int width, int height){ + AutoShape shape = new AutoShape(ShapeTypes.Rectangle, group); + applyPaint(shape); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); } - public void fillRect(int x, int y, int width, int height) { - throw new HSLFException("Not implemented"); + /** + * Draws the outline of the specified rectangle. + * The left and right edges of the rectangle are at + * x and x + width. + * The top and bottom edges are at + * y and y + height. + * The rectangle is drawn using the graphics context's current color. + * @param x the x coordinate + * of the rectangle to be drawn. + * @param y the y coordinate + * of the rectangle to be drawn. + * @param width the width of the rectangle to be drawn. + * @param height the height of the rectangle to be drawn. + * @see java.awt.Graphics#fillRect + * @see java.awt.Graphics#clearRect + */ + public void drawRect(int x, int y, int width, int height) { + AutoShape shape = new AutoShape(ShapeTypes.Rectangle, group); + shape.setFillColor(null); + applyStroke(shape); + shape.setAnchor(new Rectangle2D.Double(x, y, width, height)); + group.addShape(shape); + } - public void drawPolygon(int xPoints[], int yPoints[], - int nPoints) { - throw new HSLFException("Not implemented"); + /** + * Draws a closed polygon defined by + * arrays of x and y coordinates. + * Each pair of (xy) coordinates defines a point. + *

+ * 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. + * @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#fillPolygon(int[],int[],int) + * @see java.awt.Graphics#drawPolyline + */ + public void drawPolygon(int[] xPoints, int[] yPoints, + int nPoints){ + java.awt.Polygon polygon = new java.awt.Polygon(xPoints, yPoints, nPoints); + draw(polygon); } - public void clipRect(int x, int y, int width, int height) { - throw new HSLFException("Not implemented"); + /** + * Intersects the current clip with the specified rectangle. + * The resulting clipping area is the intersection of the current + * clipping area and the specified rectangle. If there is no + * current clipping area, either because the clip has never been + * set, or the clip has been cleared using setClip(null), + * the specified rectangle becomes the new clip. + * This method sets the user clip, which is independent of the + * clipping associated with device bounds and window visibility. + * This method can only be used to make the current clip smaller. + * To set the current clip larger, use any of the setClip methods. + * Rendering operations have no effect outside of the clipping area. + * @param x the x coordinate of the rectangle to intersect the clip with + * @param y the y coordinate of the rectangle to intersect the clip with + * @param width the width of the rectangle to intersect the clip with + * @param height the height of the rectangle to intersect the clip with + * @see #setClip(int, int, int, int) + * @see #setClip(Shape) + */ + public void clipRect(int x, int y, int width, int height){ + clip(new Rectangle(x, y, width, height)); } + /** + * Sets the current clipping area to an arbitrary clip shape. + * Not all objects that implement the Shape + * interface can be used to set the clip. The only + * Shape objects that are guaranteed to be + * supported are Shape objects that are + * obtained via the getClip method and via + * Rectangle objects. This method sets the + * user clip, which is independent of the clipping associated + * with device bounds and window visibility. + * @param clip the Shape to use to set the clip + * @see java.awt.Graphics#getClip() + * @see java.awt.Graphics#clipRect + * @see java.awt.Graphics#setClip(int, int, int, int) + * @since JDK1.1 + */ public void setClip(Shape clip) { - throw new HSLFException("Not implemented"); + log.warn("Not implemented"); } - public java.awt.Rectangle getClipBounds() { - throw new HSLFException("Not implemented"); + /** + * Returns the bounding rectangle of the current clipping area. + * This method refers to the user clip, which is independent of the + * clipping associated with device bounds and window visibility. + * If no clip has previously been set, or if the clip has been + * cleared using setClip(null), this method returns + * null. + * The coordinates in the rectangle are relative to the coordinate + * system origin of this graphics context. + * @return the bounding rectangle of the current clipping area, + * or null if no clip is set. + * @see java.awt.Graphics#getClip + * @see java.awt.Graphics#clipRect + * @see java.awt.Graphics#setClip(int, int, int, int) + * @see java.awt.Graphics#setClip(Shape) + * @since JDK1.1 + */ + public Rectangle getClipBounds(){ + Shape c = getClip(); + if(c==null) + return null; + else + return c.getBounds(); } - public void drawString(AttributedCharacterIterator iterator, int x, int y) { - throw new HSLFException("Not implemented"); + /** + * Draws the text given by the specified iterator, using this + * graphics context's current color. The iterator has to specify a font + * for each character. The baseline of the + * first character is at position (xy) in this + * graphics context's coordinate system. + * @param iterator the iterator whose text is to be drawn + * @param x the x coordinate. + * @param y the y coordinate. + * @see java.awt.Graphics#drawBytes + * @see java.awt.Graphics#drawChars + */ + public void drawString(AttributedCharacterIterator iterator, + int x, int y){ + drawString(iterator, (float)x, (float)y); } + /** + * Clears the specified rectangle by filling it with the background + * color of the current drawing surface. This operation does not + * use the current paint mode. + *

+ * Beginning with Java 1.1, the background color + * of offscreen images may be system dependent. Applications should + * use setColor followed by fillRect to + * ensure that an offscreen image is cleared to a specific color. + * @param x the x coordinate of the rectangle to clear. + * @param y the y coordinate of the rectangle to clear. + * @param width the width of the rectangle to clear. + * @param height the height of the rectangle to clear. + * @see java.awt.Graphics#fillRect(int, int, int, int) + * @see java.awt.Graphics#drawRect + * @see java.awt.Graphics#setColor(java.awt.Color) + * @see java.awt.Graphics#setPaintMode + * @see java.awt.Graphics#setXORMode(java.awt.Color) + */ public void clearRect(int x, int y, int width, int height) { - throw new HSLFException("Not implemented"); + Paint paint = getPaint(); + setColor(getBackground()); + fillRect(x, y, width, height); + setPaint(paint); } public void copyArea(int x, int y, int width, int height, int dx, int dy) { - throw new HSLFException("Not implemented"); + ; } - public void setClip(int x, int y, int width, int height) { - throw new HSLFException("Not implemented"); + /** + * Sets the current clip to the rectangle specified by the given + * coordinates. This method sets the user clip, which is + * independent of the clipping associated with device bounds + * and window visibility. + * Rendering operations have no effect outside of the clipping area. + * @param x the x coordinate of the new clip rectangle. + * @param y the y coordinate of the new clip rectangle. + * @param width the width of the new clip rectangle. + * @param height the height of the new clip rectangle. + * @see java.awt.Graphics#clipRect + * @see java.awt.Graphics#setClip(Shape) + * @since JDK1.1 + */ + public void setClip(int x, int y, int width, int height){ + setClip(new Rectangle(x, y, width, height)); } - public void rotate(double d) { - throw new HSLFException("Not implemented"); - + /** + * Concatenates the current Graphics2D + * Transform with a rotation transform. + * Subsequent rendering is rotated by the specified radians relative + * to the previous origin. + * This is equivalent to calling transform(R), where R is an + * AffineTransform represented by the following matrix: + *

+     *          [   cos(theta)    -sin(theta)    0   ]
+     *          [   sin(theta)     cos(theta)    0   ]
+     *          [       0              0         1   ]
+     * 
+ * Rotating with a positive angle theta rotates points on the positive + * x axis toward the positive y axis. + * @param theta the angle of rotation in radians + */ + public void rotate(double theta){ + transform.rotate(theta); } - public void rotate(double d, double d1, double d2) { - throw new HSLFException("Not implemented"); + /** + * Concatenates the current Graphics2D + * Transform with a translated rotation + * transform. Subsequent rendering is transformed by a transform + * which is constructed by translating to the specified location, + * rotating by the specified radians, and translating back by the same + * amount as the original translation. This is equivalent to the + * following sequence of calls: + *
+     *          translate(x, y);
+     *          rotate(theta);
+     *          translate(-x, -y);
+     * 
+ * Rotating with a positive angle theta rotates points on the positive + * x axis toward the positive y axis. + * @param theta the angle of rotation in radians + * @param x x coordinate of the origin of the rotation + * @param y y coordinate of the origin of the rotation + */ + public void rotate(double theta, double x, double y){ + transform.rotate(theta, x, y); } - public void shear(double d, double d1) { - throw new HSLFException("Not implemented"); + /** + * Concatenates the current Graphics2D + * Transform with a shearing transform. + * Subsequent renderings are sheared by the specified + * multiplier relative to the previous position. + * This is equivalent to calling transform(SH), where SH + * is an AffineTransform represented by the following + * matrix: + *
+     *          [   1   shx   0   ]
+     *          [  shy   1    0   ]
+     *          [   0    0    1   ]
+     * 
+ * @param shx the multiplier by which coordinates are shifted in + * the positive X axis direction as a function of their Y coordinate + * @param shy the multiplier by which coordinates are shifted in + * the positive Y axis direction as a function of their X coordinate + */ + public void shear(double shx, double shy){ + transform.shear(shx, shy); } + /** + * Get the rendering context of the Font within this + * Graphics2D context. + * The {@link FontRenderContext} + * encapsulates application hints such as anti-aliasing and + * fractional metrics, as well as target device specific information + * such as dots-per-inch. This information should be provided by the + * application when using objects that perform typographical + * formatting, such as Font and + * TextLayout. This information should also be provided + * by applications that perform their own layout and need accurate + * measurements of various characteristics of glyphs such as advance + * and line height when various rendering hints have been applied to + * the text rendering. + * + * @return a reference to an instance of FontRenderContext. + * @see java.awt.font.FontRenderContext + * @see java.awt.Font#createGlyphVector(FontRenderContext,char[]) + * @see java.awt.font.TextLayout + * @since JDK1.2 + */ public FontRenderContext getFontRenderContext() { - return new FontRenderContext(transform, true, true); + boolean isAntiAliased = RenderingHints.VALUE_TEXT_ANTIALIAS_ON.equals( + getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING)); + boolean usesFractionalMetrics = RenderingHints.VALUE_FRACTIONALMETRICS_ON.equals( + getRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS)); + + + return new FontRenderContext(new AffineTransform(), isAntiAliased, usesFractionalMetrics); } - public void transform(AffineTransform affinetransform) { - throw new HSLFException("Not implemented"); + /** + * Composes an AffineTransform object with the + * Transform in this Graphics2D according + * to the rule last-specified-first-applied. If the current + * Transform is Cx, the result of composition + * with Tx is a new Transform Cx'. Cx' becomes the + * current Transform for this Graphics2D. + * Transforming a point p by the updated Transform Cx' is + * equivalent to first transforming p by Tx and then transforming + * the result by the original Transform Cx. In other + * words, Cx'(p) = Cx(Tx(p)). A copy of the Tx is made, if necessary, + * so further modifications to Tx do not affect rendering. + * @param Tx the AffineTransform object to be composed with + * the current Transform + * @see #setTransform + * @see AffineTransform + */ + public void transform(AffineTransform Tx) { + transform.concatenate(Tx); } - public void drawImage(BufferedImage bufferedimage, BufferedImageOp op, int x, int y) { - throw new HSLFException("Not implemented"); + /** + * Renders a BufferedImage that is + * filtered with a + * {@link BufferedImageOp}. + * The rendering attributes applied include the Clip, + * Transform + * and Composite attributes. This is equivalent to: + *
+     * img1 = op.filter(img, null);
+     * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
+     * 
+ * @param img the BufferedImage to be rendered + * @param op the filter to be applied to the image before rendering + * @param x the x coordinate in user space where the image is rendered + * @param y the y coordinate in user space where the image is rendered + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip(Shape) + */ + public void drawImage(BufferedImage img, + BufferedImageOp op, + int x, + int y){ + img = op.filter(img, null); + drawImage(img, x, y, null); } - public void setBackground(Color c) { - throw new HSLFException("Not implemented"); + /** + * Sets the background color for the Graphics2D context. + * The background color is used for clearing a region. + * When a Graphics2D is constructed for a + * Component, the background color is + * inherited from the Component. Setting the background color + * in the Graphics2D context only affects the subsequent + * clearRect calls and not the background color of the + * Component. To change the background + * of the Component, use appropriate methods of + * the Component. + * @param color the background color that isused in + * subsequent calls to clearRect + * @see #getBackground + * @see java.awt.Graphics#clearRect + */ + public void setBackground(Color color) { + if(color == null) + return; + + background = color; } - public void drawRenderedImage(RenderedImage renderedimage, AffineTransform affinetransform) { - throw new HSLFException("Not implemented"); + /** + * Returns the background color used for clearing a region. + * @return the current Graphics2D Color, + * which defines the background color. + * @see #setBackground + */ + public Color getBackground(){ + return background; } - public Color getBackground() { - throw new HSLFException("Not implemented"); + /** + * Sets the Composite for the Graphics2D context. + * The Composite is used in all drawing methods such as + * drawImage, drawString, draw, + * and fill. It specifies how new pixels are to be combined + * with the existing pixels on the graphics device during the rendering + * process. + *

If this Graphics2D context is drawing to a + * Component on the display screen and the + * Composite is a custom object rather than an + * instance of the AlphaComposite class, and if + * there is a security manager, its checkPermission + * method is called with an AWTPermission("readDisplayPixels") + * permission. + * + * @param comp the Composite object to be used for rendering + * @throws SecurityException + * if a custom Composite object is being + * used to render to the screen and a security manager + * is set and its checkPermission method + * does not allow the operation. + * @see java.awt.Graphics#setXORMode + * @see java.awt.Graphics#setPaintMode + * @see java.awt.AlphaComposite + */ + public void setComposite(Composite comp){ + log.warn("Not implemented"); } - public void setComposite(Composite composite) { - throw new HSLFException("Not implemented"); - + /** + * Returns the current Composite in the + * Graphics2D context. + * @return the current Graphics2D Composite, + * which defines a compositing style. + * @see #setComposite + */ + public Composite getComposite(){ + log.warn("Not implemented"); + return null; } - public Composite getComposite() { - throw new HSLFException("Not implemented"); + /** + * Returns the value of a single preference for the rendering algorithms. + * Hint categories include controls for rendering quality and overall + * time/quality trade-off in the rendering process. Refer to the + * RenderingHints class for definitions of some common + * keys and values. + * @param hintKey the key corresponding to the hint to get. + * @return an object representing the value for the specified hint key. + * Some of the keys and their associated values are defined in the + * RenderingHints class. + * @see RenderingHints + */ + public Object getRenderingHint(RenderingHints.Key hintKey){ + return hints.get(hintKey); } - public Object getRenderingHint(java.awt.RenderingHints.Key key) { - throw new HSLFException("Not implemented"); - } - - public boolean drawImage(Image image, AffineTransform affinetransform, ImageObserver imageobserver) { - throw new HSLFException("Not implemented"); - } - - public void setRenderingHint(java.awt.RenderingHints.Key key, Object obj) { - throw new HSLFException("Not implemented"); + /** + * Sets the value of a single preference for the rendering algorithms. + * Hint categories include controls for rendering quality and overall + * time/quality trade-off in the rendering process. Refer to the + * RenderingHints class for definitions of some common + * keys and values. + * @param hintKey the key of the hint to be set. + * @param hintValue the value indicating preferences for the specified + * hint category. + * @see RenderingHints + */ + public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue){ + hints.put(hintKey, hintValue); } + /** + * Renders the text of the specified + * {@link GlyphVector} using + * the Graphics2D context's rendering attributes. + * The rendering attributes applied include the Clip, + * Transform, Paint, and + * Composite attributes. The GlyphVector + * specifies individual glyphs from a {@link Font}. + * The GlyphVector can also contain the glyph positions. + * This is the fastest way to render a set of characters to the + * screen. + * + * @param g the GlyphVector to be rendered + * @param x the x position in user space where the glyphs should be + * rendered + * @param y the y position in user space where the glyphs should be + * rendered + * + * @see java.awt.Font#createGlyphVector(FontRenderContext, char[]) + * @see java.awt.font.GlyphVector + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #setTransform + * @see #setComposite + * @see #setClip(Shape) + */ public void drawGlyphVector(GlyphVector g, float x, float y) { - throw new HSLFException("Not implemented"); - + Shape glyphOutline = g.getOutline(x, y); + fill(glyphOutline); } + /** + * Returns the device configuration associated with this + * Graphics2D. + * @return the device configuration + */ public GraphicsConfiguration getDeviceConfiguration() { - throw new HSLFException("Not implemented"); + return GraphicsEnvironment.getLocalGraphicsEnvironment(). + getDefaultScreenDevice().getDefaultConfiguration(); } - public void addRenderingHints(Map map) { - throw new HSLFException("Not implemented"); + /** + * Sets the values of an arbitrary number of preferences for the + * rendering algorithms. + * Only values for the rendering hints that are present in the + * specified Map object are modified. + * All other preferences not present in the specified + * object are left unmodified. + * Hint categories include controls for rendering quality and + * overall time/quality trade-off in the rendering process. + * Refer to the RenderingHints class for definitions of + * some common keys and values. + * @param hints the rendering hints to be set + * @see RenderingHints + */ + public void addRenderingHints(Map hints){ + this.hints.putAll(hints); } - public void translate(double d, double d1) { - - throw new HSLFException("Not implemented"); + /** + * Concatenates the current + * Graphics2D Transform + * with a translation transform. + * Subsequent rendering is translated by the specified + * distance relative to the previous position. + * This is equivalent to calling transform(T), where T is an + * AffineTransform represented by the following matrix: + *

+     *          [   1    0    tx  ]
+     *          [   0    1    ty  ]
+     *          [   0    0    1   ]
+     * 
+ * @param tx the distance to translate along the x-axis + * @param ty the distance to translate along the y-axis + */ + public void translate(double tx, double ty){ + transform.translate(tx, ty); } - public void drawString(AttributedCharacterIterator attributedcharacteriterator, float x, float y) { - throw new HSLFException("Not implemented"); + /** + * Renders the text of the specified iterator, using the + * Graphics2D context's current Paint. The + * iterator must specify a font + * for each character. The baseline of the + * first character is at position (xy) in the + * User Space. + * The rendering attributes applied include the Clip, + * Transform, Paint, and + * Composite attributes. + * For characters in script systems such as Hebrew and Arabic, + * the glyphs can be rendered from right to left, in which case the + * coordinate supplied is the location of the leftmost character + * on the baseline. + * @param iterator the iterator whose text is to be rendered + * @param x the x coordinate where the iterator's text is to be + * rendered + * @param y the y coordinate where the iterator's text is to be + * rendered + * @see #setPaint + * @see java.awt.Graphics#setColor + * @see #setTransform + * @see #setComposite + * @see #setClip + */ + public void drawString(AttributedCharacterIterator iterator, float x, float y) { + log.warn("Not implemented"); } - public boolean hit(java.awt.Rectangle rectangle, Shape shape, boolean flag) { - throw new HSLFException("Not implemented"); + /** + * Checks whether or not the specified Shape intersects + * the specified {@link Rectangle}, which is in device + * space. If onStroke is false, this method checks + * whether or not the interior of the specified Shape + * intersects the specified Rectangle. If + * onStroke is true, this method checks + * whether or not the Stroke of the specified + * Shape outline intersects the specified + * Rectangle. + * The rendering attributes taken into account include the + * Clip, Transform, and Stroke + * attributes. + * @param rect the area in device space to check for a hit + * @param s the Shape to check for a hit + * @param onStroke flag used to choose between testing the + * stroked or the filled shape. If the flag is true, the + * Stroke oultine is tested. If the flag is + * false, the filled Shape is tested. + * @return true if there is a hit; false + * otherwise. + * @see #setStroke + * @see #fill(Shape) + * @see #draw(Shape) + * @see #transform + * @see #setTransform + * @see #clip + * @see #setClip(Shape) + */ + public boolean hit(Rectangle rect, + Shape s, + boolean onStroke){ + if (onStroke) { + s = getStroke().createStrokedShape(s); + } + + s = getTransform().createTransformedShape(s); + + return s.intersects(rect); } - public RenderingHints getRenderingHints() { - throw new HSLFException("Not implemented"); + /** + * Gets the preferences for the rendering algorithms. Hint categories + * include controls for rendering quality and overall time/quality + * trade-off in the rendering process. + * Returns all of the hint key/value pairs that were ever specified in + * one operation. Refer to the + * RenderingHints class for definitions of some common + * keys and values. + * @return a reference to an instance of RenderingHints + * that contains the current preferences. + * @see RenderingHints + */ + public RenderingHints getRenderingHints(){ + return hints; } - public void setRenderingHints(Map map) { - throw new HSLFException("Not implemented"); - + /** + * Replaces the values of all preferences for the rendering + * algorithms with the specified hints. + * The existing values for all rendering hints are discarded and + * the new set of known hints and values are initialized from the + * specified {@link Map} object. + * Hint categories include controls for rendering quality and + * overall time/quality trade-off in the rendering process. + * Refer to the RenderingHints class for definitions of + * some common keys and values. + * @param hints the rendering hints to be set + * @see RenderingHints + */ + public void setRenderingHints(Map hints){ + this.hints = new RenderingHints(hints); } - public void drawRenderableImage(RenderableImage renderableimage, AffineTransform affinetransform) { - throw new HSLFException("Not implemented"); + /** + * Renders an image, applying a transform from image space into user space + * before drawing. + * The transformation from user space into device space is done with + * the current Transform in the Graphics2D. + * The specified transformation is applied to the image before the + * transform attribute in the Graphics2D context is applied. + * The rendering attributes applied include the Clip, + * Transform, and Composite attributes. + * Note that no rendering is done if the specified transform is + * noninvertible. + * @param img the Image to be rendered + * @param xform the transformation from image space into user space + * @param obs the {@link ImageObserver} + * to be notified as more of the Image + * is converted + * @return true if the Image is + * fully loaded and completely rendered; + * false if the Image is still being loaded. + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip(Shape) + */ + public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { + log.warn("Not implemented"); + return false; + } + + /** + * Draws as much of the specified image as has already been scaled + * to fit inside the specified rectangle. + *

+ * The image is drawn inside the specified rectangle of this + * graphics context's coordinate space, and is scaled if + * necessary. Transparent pixels do not affect whatever pixels + * are already there. + *

+ * This method returns immediately in all cases, even if the + * entire image has not yet been scaled, dithered, and converted + * for the current output device. + * If the current output representation is not yet complete, then + * drawImage returns false. As more of + * the image becomes available, the process that loads the image notifies + * the image observer by calling its imageUpdate method. + *

+ * A scaled version of an image will not necessarily be + * available immediately just because an unscaled version of the + * image has been constructed for this output device. Each size of + * the image may be cached separately and generated from the original + * data in a separate image production sequence. + * @param img the specified image to be drawn. This method does + * nothing if img is null. + * @param x the x coordinate. + * @param y the y coordinate. + * @param width the width of the rectangle. + * @param height the height of the rectangle. + * @param observer object to be notified as more of + * the image is converted. + * @return false if the image pixels are still changing; + * true otherwise. + * @see java.awt.Image + * @see java.awt.image.ImageObserver + * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int) + */ + public boolean drawImage(Image img, int x, int y, + int width, int height, + ImageObserver observer) { + log.warn("Not implemented"); + return false; + } + + /** + * Creates a new Graphics object that is + * a copy of this Graphics object. + * @return a new graphics context that is a copy of + * this graphics context. + */ + public Graphics create() { + try { + return (Graphics)clone(); + } catch (CloneNotSupportedException e){ + throw new HSLFException(e); + } + } + + /** + * Gets the font metrics for the specified font. + * @return the font metrics for the specified font. + * @param f the specified font + * @see java.awt.Graphics#getFont + * @see java.awt.FontMetrics + * @see java.awt.Graphics#getFontMetrics() + */ + public FontMetrics getFontMetrics(Font f) { + return Toolkit.getDefaultToolkit().getFontMetrics(f); + } + + /** + * Sets the paint mode of this graphics context to alternate between + * this graphics context's current color and the new specified color. + * This specifies that logical pixel operations are performed in the + * XOR mode, which alternates pixels between the current color and + * a specified XOR color. + *

+ * When drawing operations are performed, pixels which are the + * current color are changed to the specified color, and vice versa. + *

+ * Pixels that are of colors other than those two colors are changed + * in an unpredictable but reversible manner; if the same figure is + * drawn twice, then all pixels are restored to their original values. + * @param c1 the XOR alternation color + */ + public void setXORMode(Color c1) { + log.warn("Not implemented"); + } + + /** + * Sets the paint mode of this graphics context to overwrite the + * destination with this graphics context's current color. + * This sets the logical pixel operation function to the paint or + * overwrite mode. All subsequent rendering operations will + * overwrite the destination with the current color. + */ + public void setPaintMode() { + log.warn("Not implemented"); + } + + /** + * Renders a + * {@link RenderableImage}, + * applying a transform from image space into user space before drawing. + * The transformation from user space into device space is done with + * the current Transform in the Graphics2D. + * The specified transformation is applied to the image before the + * transform attribute in the Graphics2D context is applied. + * The rendering attributes applied include the Clip, + * Transform, and Composite attributes. Note + * that no rendering is done if the specified transform is + * noninvertible. + *

+ * Rendering hints set on the Graphics2D object might + * be used in rendering the RenderableImage. + * If explicit control is required over specific hints recognized by a + * specific RenderableImage, or if knowledge of which hints + * are used is required, then a RenderedImage should be + * obtained directly from the RenderableImage + * and rendered using + *{@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}. + * @param img the image to be rendered. This method does + * nothing if img is null. + * @param xform the transformation from image space into user space + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip + * @see #drawRenderedImage + */ + public void drawRenderedImage(RenderedImage img, AffineTransform xform) { + log.warn("Not implemented"); + } + + /** + * Renders a {@link RenderedImage}, + * applying a transform from image + * space into user space before drawing. + * The transformation from user space into device space is done with + * the current Transform in the Graphics2D. + * The specified transformation is applied to the image before the + * transform attribute in the Graphics2D context is applied. + * The rendering attributes applied include the Clip, + * Transform, and Composite attributes. Note + * that no rendering is done if the specified transform is + * noninvertible. + * @param img the image to be rendered. This method does + * nothing if img is null. + * @param xform the transformation from image space into user space + * @see #transform + * @see #setTransform + * @see #setComposite + * @see #clip + * @see #setClip + */ + public void drawRenderableImage(RenderableImage img, AffineTransform xform) { + log.warn("Not implemented"); + } + + protected void applyStroke(SimpleShape shape) { + if (stroke instanceof BasicStroke){ + BasicStroke bs = (BasicStroke)stroke; + shape.setLineWidth(bs.getLineWidth()); + float[] dash = bs.getDashArray(); + if (dash != null) { + //TODO: implement more dashing styles + shape.setLineDashing(Line.PEN_DASH); + } + } + } + + protected void applyPaint(SimpleShape shape) { + if (paint instanceof Color) { + shape.getFill().setForegroundColor((Color)paint); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Polygon.java b/src/scratchpad/src/org/apache/poi/hslf/model/Polygon.java new file mode 100755 index 000000000..ea4afe01b --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Polygon.java @@ -0,0 +1,160 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.hslf.model; + +import org.apache.poi.ddf.*; +import org.apache.poi.util.LittleEndian; + +import java.awt.geom.Point2D; + +/** + * A simple closed polygon shape + * + * @author Yegor Kozlov + */ +public class Polygon extends AutoShape { + /** + * Create a Polygon object and initialize it from the supplied Record container. + * + * @param escherRecord EscherSpContainer container which holds information about this shape + * @param parent the parent of the shape + */ + protected Polygon(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + + } + + /** + * Create a new Polygon. This constructor is used when a new shape is created. + * + * @param parent the parent of this Shape. For example, if this text box is a cell + * in a table then the parent is Table. + */ + public Polygon(Shape parent){ + super(null, parent); + _escherContainer = createSpContainer(ShapeTypes.NotPrimitive, parent instanceof ShapeGroup); + } + + /** + * Create a new Polygon. This constructor is used when a new shape is created. + * + */ + public Polygon(){ + this(null); + } + + /** + * Set the polygon vertices + * + * @param xPoints + * @param yPoints + */ + public void setPoints(float[] xPoints, float[] yPoints) + { + float right = findBiggest(xPoints); + float bottom = findBiggest(yPoints); + float left = findSmallest(xPoints); + float top = findSmallest(yPoints); + + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__RIGHT, (int)((right - left)*POINT_DPI/MASTER_DPI))); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__BOTTOM, (int)((bottom - top)*POINT_DPI/MASTER_DPI))); + + for (int i = 0; i < xPoints.length; i++) { + xPoints[i] += -left; + yPoints[i] += -top; + } + + int numpoints = xPoints.length; + + EscherArrayProperty verticesProp = new EscherArrayProperty(EscherProperties.GEOMETRY__VERTICES, false, new byte[0] ); + verticesProp.setNumberOfElementsInArray(numpoints+1); + verticesProp.setNumberOfElementsInMemory(numpoints+1); + verticesProp.setSizeOfElements(0xFFF0); + for (int i = 0; i < numpoints; i++) + { + byte[] data = new byte[4]; + LittleEndian.putShort(data, 0, (short)(xPoints[i]*POINT_DPI/MASTER_DPI)); + LittleEndian.putShort(data, 2, (short)(yPoints[i]*POINT_DPI/MASTER_DPI)); + verticesProp.setElement(i, data); + } + byte[] data = new byte[4]; + LittleEndian.putShort(data, 0, (short)(xPoints[0]*POINT_DPI/MASTER_DPI)); + LittleEndian.putShort(data, 2, (short)(yPoints[0]*POINT_DPI/MASTER_DPI)); + verticesProp.setElement(numpoints, data); + opt.addEscherProperty(verticesProp); + + EscherArrayProperty segmentsProp = new EscherArrayProperty(EscherProperties.GEOMETRY__SEGMENTINFO, false, null ); + segmentsProp.setSizeOfElements(0x0002); + segmentsProp.setNumberOfElementsInArray(numpoints * 2 + 4); + segmentsProp.setNumberOfElementsInMemory(numpoints * 2 + 4); + segmentsProp.setElement(0, new byte[] { (byte)0x00, (byte)0x40 } ); + segmentsProp.setElement(1, new byte[] { (byte)0x00, (byte)0xAC } ); + for (int i = 0; i < numpoints; i++) + { + segmentsProp.setElement(2 + i * 2, new byte[] { (byte)0x01, (byte)0x00 } ); + segmentsProp.setElement(3 + i * 2, new byte[] { (byte)0x00, (byte)0xAC } ); + } + segmentsProp.setElement(segmentsProp.getNumberOfElementsInArray() - 2, new byte[] { (byte)0x01, (byte)0x60 } ); + segmentsProp.setElement(segmentsProp.getNumberOfElementsInArray() - 1, new byte[] { (byte)0x00, (byte)0x80 } ); + opt.addEscherProperty(segmentsProp); + + opt.sortProperties(); + } + + /** + * Set the polygon vertices + * + * @param points the polygon vertices + */ + public void setPoints(Point2D[] points) + { + float[] xpoints = new float[points.length]; + float[] ypoints = new float[points.length]; + for (int i = 0; i < points.length; i++) { + xpoints[i] = (float)points[i].getX(); + ypoints[i] = (float)points[i].getY(); + + } + + setPoints(xpoints, ypoints); + } + + private float findBiggest( float[] values ) + { + float result = Float.MIN_VALUE; + for ( int i = 0; i < values.length; i++ ) + { + if (values[i] > result) + result = values[i]; + } + return result; + } + + private float findSmallest( float[] values ) + { + float result = Float.MAX_VALUE; + for ( int i = 0; i < values.length; i++ ) + { + if (values[i] < result) + result = values[i]; + } + return result; + } + + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java index 5cff81a8d..2e23c5926 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java @@ -24,6 +24,7 @@ import org.apache.poi.util.POILogFactory; import java.util.Iterator; import java.awt.*; +import java.awt.geom.Rectangle2D; /** *

@@ -143,24 +144,39 @@ public abstract class Shape { * @return the anchor of this shape */ public java.awt.Rectangle getAnchor(){ + Rectangle2D anchor2d = getAnchor2D(); + return anchor2d.getBounds(); + } + + /** + * Returns the anchor (the bounding box rectangle) of this shape. + * All coordinates are expressed in points (72 dpi). + * + * @return the anchor of this shape + */ + public Rectangle2D getAnchor2D(){ EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); int flags = spRecord.getFlags(); - java.awt.Rectangle anchor=null; + Rectangle2D anchor=null; if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); - anchor.x = rec.getDx1()*POINT_DPI/MASTER_DPI; - anchor.y = rec.getDy1()*POINT_DPI/MASTER_DPI; - anchor.width = rec.getDx2()*POINT_DPI/MASTER_DPI - anchor.x; - anchor.height = rec.getDy2()*POINT_DPI/MASTER_DPI - anchor.y; + anchor = new Rectangle2D.Float( + (float)rec.getDx1()*POINT_DPI/MASTER_DPI, + (float)rec.getDy1()*POINT_DPI/MASTER_DPI, + (float)(rec.getDx2()-rec.getDx1())*POINT_DPI/MASTER_DPI, + (float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI + ); } else { EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); - anchor.y = rec.getFlag()*POINT_DPI/MASTER_DPI; - anchor.x = rec.getCol1()*POINT_DPI/MASTER_DPI; - anchor.width = (rec.getDx1() - rec.getCol1())*POINT_DPI/MASTER_DPI; - anchor.height = (rec.getRow1() - rec.getFlag())*POINT_DPI/MASTER_DPI; + anchor = new Rectangle2D.Float( + (float)rec.getCol1()*POINT_DPI/MASTER_DPI, + (float)rec.getFlag()*POINT_DPI/MASTER_DPI, + (float)(rec.getDx1()-rec.getCol1())*POINT_DPI/MASTER_DPI, + (float)(rec.getRow1()-rec.getFlag())*POINT_DPI/MASTER_DPI + ); } return anchor; } @@ -171,22 +187,22 @@ public abstract class Shape { * * @param anchor new anchor */ - public void setAnchor(java.awt.Rectangle anchor){ + public void setAnchor(Rectangle2D anchor){ EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); int flags = spRecord.getFlags(); if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); - rec.setDx1(anchor.x*MASTER_DPI/POINT_DPI); - rec.setDy1(anchor.y*MASTER_DPI/POINT_DPI); - rec.setDx2((anchor.width + anchor.x)*MASTER_DPI/POINT_DPI); - rec.setDy2((anchor.height + anchor.y)*MASTER_DPI/POINT_DPI); + rec.setDx1((int)(anchor.getX()*MASTER_DPI/POINT_DPI)); + rec.setDy1((int)(anchor.getY()*MASTER_DPI/POINT_DPI)); + rec.setDx2((int)((anchor.getWidth() + anchor.getX())*MASTER_DPI/POINT_DPI)); + rec.setDy2((int)((anchor.getHeight() + anchor.getY())*MASTER_DPI/POINT_DPI)); } else { EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); - rec.setFlag((short)(anchor.y*MASTER_DPI/POINT_DPI)); - rec.setCol1((short)(anchor.x*MASTER_DPI/POINT_DPI)); - rec.setDx1((short)((anchor.width + anchor.x)*MASTER_DPI/POINT_DPI)); - rec.setRow1((short)((anchor.height + anchor.y)*MASTER_DPI/POINT_DPI)); + rec.setFlag((short)(anchor.getY()*MASTER_DPI/POINT_DPI)); + rec.setCol1((short)(anchor.getX()*MASTER_DPI/POINT_DPI)); + rec.setDx1((short)(((anchor.getWidth() + anchor.getX())*MASTER_DPI/POINT_DPI))); + rec.setRow1((short)(((anchor.getHeight() + anchor.getY())*MASTER_DPI/POINT_DPI))); } } @@ -197,9 +213,9 @@ public abstract class Shape { * @param x the x coordinate of the top left corner of the shape * @param y the y coordinate of the top left corner of the shape */ - public void moveTo(int x, int y){ - java.awt.Rectangle anchor = getAnchor(); - anchor.setLocation(x, y); + public void moveTo(float x, float y){ + Rectangle2D anchor = getAnchor2D(); + anchor.setRect(x, y, anchor.getWidth(), anchor.getHeight()); setAnchor(anchor); } @@ -254,6 +270,28 @@ public abstract class Shape { } } + /** + * Set an simple escher property for this shape. + * + * @param propId The id of the property. One of the constants defined in EscherOptRecord. + * @param value value of the property. If value = -1 then the property is removed. + */ + public void setEscherProperty(short propId, int value){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, propId, value); + } + + /** + * Get the value of a simple escher property for this shape. + * + * @param propId The id of the property. One of the constants defined in EscherOptRecord. + */ + public int getEscherProperty(short propId){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, propId); + return prop == null ? 0 : prop.getPropertyNumber(); + } + /** * @return The shape container and it's children that can represent this * shape. diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java index aeb34763e..dbcc8069c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -22,6 +22,7 @@ import org.apache.poi.util.POILogger; import java.util.ArrayList; import java.util.List; +import java.awt.geom.Rectangle2D; /** * Represents a group of shapes. @@ -186,16 +187,16 @@ public class ShapeGroup extends Shape{ * * @return the anchor of this shape group */ - public java.awt.Rectangle getAnchor(){ + public Rectangle2D getAnchor2D(){ EscherContainerRecord groupInfoContainer = (EscherContainerRecord)_escherContainer.getChild(0); EscherSpgrRecord spgr = (EscherSpgrRecord)getEscherChild(groupInfoContainer, EscherSpgrRecord.RECORD_ID); - java.awt.Rectangle anchor=null; + Rectangle2D anchor = new Rectangle2D.Float( + (float)spgr.getRectX1()*POINT_DPI/MASTER_DPI, + (float)spgr.getRectY1()*POINT_DPI/MASTER_DPI, + (float)(spgr.getRectX2() - spgr.getRectX1())*POINT_DPI/MASTER_DPI, + (float)(spgr.getRectY2() - spgr.getRectY1())*POINT_DPI/MASTER_DPI + ); - anchor = new java.awt.Rectangle(); - anchor.x = spgr.getRectX1()*POINT_DPI/MASTER_DPI; - anchor.y = spgr.getRectY1()*POINT_DPI/MASTER_DPI; - anchor.width = (spgr.getRectX2() - spgr.getRectX1())*POINT_DPI/MASTER_DPI; - anchor.height = (spgr.getRectY2() - spgr.getRectY1())*POINT_DPI/MASTER_DPI; return anchor; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java index 107491600..58fc33305 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java @@ -105,9 +105,13 @@ public class SimpleShape extends Shape { */ public void setLineColor(Color color){ EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); - int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); - setEscherProperty(opt, EscherProperties.LINESTYLE__COLOR, rgb); - setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, color == null ? 0x180010 : 0x180018); + if (color == null) { + setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80000); + } else { + int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); + setEscherProperty(opt, EscherProperties.LINESTYLE__COLOR, rgb); + setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, color == null ? 0x180010 : 0x180018); + } } /** @@ -212,9 +216,13 @@ public class SimpleShape extends Shape { */ public void setFillColor(Color color){ EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); - int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); - setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb); - setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, color == null ? 0x150010 : 0x150011); + if(color == null) { + setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150000); + } else { + int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); + setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, rgb); + setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150011); + } } } diff --git a/src/scratchpad/testcases/org/apache/poi/hdgf/data/44594-2.vsd b/src/scratchpad/testcases/org/apache/poi/hdgf/data/44594-2.vsd new file mode 100644 index 000000000..a597a0d93 Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hdgf/data/44594-2.vsd differ diff --git a/src/scratchpad/testcases/org/apache/poi/hdgf/data/44594.vsd b/src/scratchpad/testcases/org/apache/poi/hdgf/data/44594.vsd new file mode 100644 index 000000000..abed78b4d Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hdgf/data/44594.vsd differ diff --git a/src/scratchpad/testcases/org/apache/poi/hdgf/extractor/TestVisioExtractor.java b/src/scratchpad/testcases/org/apache/poi/hdgf/extractor/TestVisioExtractor.java index a6541e9b0..8f890db3a 100644 --- a/src/scratchpad/testcases/org/apache/poi/hdgf/extractor/TestVisioExtractor.java +++ b/src/scratchpad/testcases/org/apache/poi/hdgf/extractor/TestVisioExtractor.java @@ -17,25 +17,21 @@ package org.apache.poi.hdgf.extractor; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileInputStream; import java.io.PrintStream; import junit.framework.TestCase; import org.apache.poi.hdgf.HDGFDiagram; -import org.apache.poi.hdgf.chunks.Chunk; -import org.apache.poi.hdgf.chunks.ChunkFactory; -import org.apache.poi.hdgf.pointers.Pointer; -import org.apache.poi.hdgf.pointers.PointerFactory; -import org.apache.poi.hssf.record.formula.eval.StringOperationEval; -import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; public class TestVisioExtractor extends TestCase { - private String filename; + private String dirname; + private String defFilename; protected void setUp() throws Exception { - String dirname = System.getProperty("HDGF.testdata.path"); - filename = dirname + "/Test_Visio-Some_Random_Text.vsd"; + dirname = System.getProperty("HDGF.testdata.path"); + defFilename = dirname + "/Test_Visio-Some_Random_Text.vsd"; } /** @@ -44,14 +40,14 @@ public class TestVisioExtractor extends TestCase { public void testCreation() throws Exception { VisioTextExtractor extractor; - extractor = new VisioTextExtractor(new FileInputStream(filename)); + extractor = new VisioTextExtractor(new FileInputStream(defFilename)); assertNotNull(extractor); assertNotNull(extractor.getAllText()); assertEquals(3, extractor.getAllText().length); extractor = new VisioTextExtractor( new POIFSFileSystem( - new FileInputStream(filename) + new FileInputStream(defFilename) ) ); assertNotNull(extractor); @@ -61,7 +57,7 @@ public class TestVisioExtractor extends TestCase { extractor = new VisioTextExtractor( new HDGFDiagram( new POIFSFileSystem( - new FileInputStream(filename) + new FileInputStream(defFilename) ) ) ); @@ -72,7 +68,7 @@ public class TestVisioExtractor extends TestCase { public void testExtraction() throws Exception { VisioTextExtractor extractor = - new VisioTextExtractor(new FileInputStream(filename)); + new VisioTextExtractor(new FileInputStream(defFilename)); // Check the array fetch String[] text = extractor.getAllText(); @@ -88,13 +84,30 @@ public class TestVisioExtractor extends TestCase { assertEquals("Test View\nI am a test view\nSome random text, on a page\n", textS); } + public void testProblemFiles() throws Exception { + File a = new File(dirname, "44594.vsd"); + VisioTextExtractor.main(new String[] {a.toString()}); + + File b = new File(dirname, "44594-2.vsd"); + VisioTextExtractor.main(new String[] {b.toString()}); + + File c = new File(dirname, "ShortChunk1.vsd"); + VisioTextExtractor.main(new String[] {c.toString()}); + + File d = new File(dirname, "ShortChunk2.vsd"); + VisioTextExtractor.main(new String[] {d.toString()}); + + File e = new File(dirname, "ShortChunk3.vsd"); + VisioTextExtractor.main(new String[] {e.toString()}); + } + public void testMain() throws Exception { PrintStream oldOut = System.out; ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream capture = new PrintStream(baos); System.setOut(capture); - VisioTextExtractor.main(new String[] {filename}); + VisioTextExtractor.main(new String[] {defFilename}); // Put things back System.setOut(oldOut); diff --git a/src/scratchpad/testcases/org/apache/poi/hdgf/streams/TestStreamBugs.java b/src/scratchpad/testcases/org/apache/poi/hdgf/streams/TestStreamBugs.java new file mode 100644 index 000000000..9d4cac168 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hdgf/streams/TestStreamBugs.java @@ -0,0 +1,102 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.hdgf.streams; + +import java.io.FileInputStream; + +import org.apache.poi.hdgf.HDGFDiagram; +import org.apache.poi.hdgf.chunks.ChunkFactory; +import org.apache.poi.hdgf.pointers.Pointer; +import org.apache.poi.hdgf.pointers.PointerFactory; +import org.apache.poi.poifs.filesystem.DocumentEntry; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +/** + * Tests for bugs with streams + */ +public class TestStreamBugs extends StreamTest { + private byte[] contents; + private ChunkFactory chunkFactory; + private PointerFactory ptrFactory; + private POIFSFileSystem filesystem; + + protected void setUp() throws Exception { + String dirname = System.getProperty("HDGF.testdata.path"); + String filename = dirname + "/44594.vsd"; + ptrFactory = new PointerFactory(11); + chunkFactory = new ChunkFactory(11); + + FileInputStream fin = new FileInputStream(filename); + filesystem = new POIFSFileSystem(fin); + + DocumentEntry docProps = + (DocumentEntry)filesystem.getRoot().getEntry("VisioDocument"); + + // Grab the document stream + contents = new byte[docProps.getSize()]; + filesystem.createDocumentInputStream("VisioDocument").read(contents); + } + + public void testGetTrailer() throws Exception { + Pointer trailerPointer = ptrFactory.createPointer(contents, 0x24); + Stream.createStream(trailerPointer, contents, chunkFactory, ptrFactory); + } + + public void TOIMPLEMENTtestGetCertainChunks() throws Exception { + int offsetA = 3708; + int offsetB = 3744; + } + + public void testGetChildren() throws Exception { + Pointer trailerPointer = ptrFactory.createPointer(contents, 0x24); + TrailerStream trailer = (TrailerStream) + Stream.createStream(trailerPointer, contents, chunkFactory, ptrFactory); + + // Get without recursing + Pointer[] ptrs = trailer.getChildPointers(); + for(int i=0; i