Formatting
[jalview.git] / src / org / jibble / epsgraphics / EpsGraphics2D.java
index 7955440..45eeb5b 100755 (executable)
@@ -1,30 +1,30 @@
 /*\r
-Copyright Paul James Mutton, 2001-2004, http://www.jibble.org/\r
+ Copyright Paul James Mutton, 2001-2004, http://www.jibble.org/\r
 \r
-This file is part of EpsGraphics2D.\r
+ This file is part of EpsGraphics2D.\r
 \r
-This software is dual-licensed, allowing you to choose between the GNU\r
-General Public License (GPL) and the www.jibble.org Commercial License.\r
-Since the GPL may be too restrictive for use in a proprietary application,\r
-a commercial license is also provided. Full license information can be\r
-found at http://www.jibble.org/licenses/\r
+ This software is dual-licensed, allowing you to choose between the GNU\r
+ General Public License (GPL) and the www.jibble.org Commercial License.\r
+ Since the GPL may be too restrictive for use in a proprietary application,\r
+ a commercial license is also provided. Full license information can be\r
+ found at http://www.jibble.org/licenses/\r
 \r
-$Author$\r
-$Id$\r
+ $Author$\r
+ $Id$\r
 \r
-*/\r
+ */\r
 \r
 package org.jibble.epsgraphics;\r
 \r
-import java.awt.*;\r
-import java.awt.image.*;\r
-import java.awt.image.renderable.*;\r
-import java.awt.geom.*;\r
 import java.io.*;\r
 import java.text.*;\r
-import java.awt.font.*;\r
 import java.util.*;\r
 \r
+import java.awt.*;\r
+import java.awt.font.*;\r
+import java.awt.geom.*;\r
+import java.awt.image.*;\r
+import java.awt.image.renderable.*;\r
 \r
 /**\r
  * EpsGraphics2D is suitable for creating high quality EPS graphics for\r
@@ -70,1275 +70,1376 @@ import java.util.*;
  *           <a href="http://www.jibble.org/">http://www.jibble.org/</a>\r
  *\r
  */\r
-public class EpsGraphics2D extends java.awt.Graphics2D {\r
-\r
-\r
-    public static final String VERSION = "0.8.8";\r
-\r
-\r
-    /**\r
-     * Constructs a new EPS document that is initially empty and can be\r
-     * drawn on like a Graphics2D object.  The EPS document is stored in\r
-     * memory.\r
-     */\r
-    public EpsGraphics2D() {\r
-        this("Untitled");\r
-    }\r
-\r
-\r
-    /**\r
-     * Constructs a new EPS document that is initially empty and can be\r
-     * drawn on like a Graphics2D object.  The EPS document is stored in\r
-     * memory.\r
-     */\r
-    public EpsGraphics2D(String title) {\r
-        _document = new EpsDocument(title);\r
-        _backgroundColor = Color.white;\r
-        _clip = null;\r
-        _transform = new AffineTransform();\r
-        _clipTransform = new AffineTransform();\r
-        _accurateTextMode = true;\r
-        setColor(Color.black);\r
-        setPaint(Color.black);\r
-        setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));\r
-        setFont(Font.decode(null));\r
-        setStroke(new BasicStroke());\r
-    }\r
-\r
-\r
-    /**\r
-     * Constructs a new EPS document that is initially empty and can be\r
-     * drawn on like a Graphics2D object. The EPS document is written to\r
-     * the file as it goes, which reduces memory usage. The bounding box of\r
-     * the document is fixed and specified at construction time by\r
-     * minX,minY,maxX,maxY. The file is flushed and closed when the close()\r
-     * method is called.\r
-     */\r
-    public EpsGraphics2D(String title, File file, int minX, int minY, int maxX, int maxY) throws IOException {\r
-        this(title, new FileOutputStream(file), minX, minY, maxX, maxY);\r
-    }\r
-\r
-\r
-    /**\r
-     * Constructs a new EPS document that is initially empty and can be\r
-     * drawn on like a Graphics2D object. The EPS document is written to\r
-     * the output stream as it goes, which reduces memory usage. The\r
-     * bounding box of the document is fixed and specified at construction\r
-     * time by minX,minY,maxX,maxY. The output stream is flushed and closed\r
-     * when the close() method is called.\r
-     */\r
-    public EpsGraphics2D(String title, OutputStream outputStream, int minX, int minY, int maxX, int maxY) throws IOException {\r
-        this(title);\r
-        _document = new EpsDocument(title, outputStream, minX, minY, maxX, maxY);\r
-    }\r
-\r
-\r
-    /**\r
-     * Constructs a new EpsGraphics2D instance that is a copy of the\r
-     * supplied argument and points at the same EpsDocument.\r
-     */\r
-    protected EpsGraphics2D(EpsGraphics2D g) {\r
-        _document = g._document;\r
-        _backgroundColor = g._backgroundColor;\r
-        _clip = g._clip;\r
-        _clipTransform = (AffineTransform) g._clipTransform.clone();\r
-        _transform = (AffineTransform) g._transform.clone();\r
-        _color = g._color;\r
-        _paint = g._paint;\r
-        _composite = g._composite;\r
-        _font = g._font;\r
-        _stroke = g._stroke;\r
-        _accurateTextMode = g._accurateTextMode;\r
-    }\r
-\r
-\r
-    /**\r
-     * This method is called to indicate that a particular method is not\r
-     * supported yet.  The stack trace is printed to the standard output.\r
-     */\r
-    private void methodNotSupported() {\r
-        EpsException e = new EpsException("Method not currently supported by EpsGraphics2D version " + VERSION);\r
-        e.printStackTrace(System.err);\r
-    }\r
-\r
-\r
-    /////////////// Specialist methods ///////////////////////\r
-\r
-\r
-    /**\r
-     * Sets whether to use accurate text mode when rendering text in EPS.\r
-     * This is enabled (true) by default. When accurate text mode is used,\r
-     * all text will be rendered in EPS to appear exactly the same as it\r
-     * would do when drawn with a Graphics2D context. With accurate text\r
-     * mode enabled, it is not necessary for the EPS viewer to have the\r
-     * required font installed.\r
-     * <p>\r
-     * Turning off accurate text mode will require the EPS viewer to have\r
-     * the necessary fonts installed. If you are using a lot of text, you\r
-     * will find that this significantly reduces the file size of your EPS\r
-     * documents.  AffineTransforms can only affect the starting point of\r
-     * text using this simpler text mode - all text will be horizontal.\r
-     */\r
-    public void setAccurateTextMode(boolean b) {\r
-        _accurateTextMode = b;\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns whether accurate text mode is being used.\r
-     */\r
-    public boolean getAccurateTextMode() {\r
-        return _accurateTextMode;\r
-    }\r
-\r
-\r
-    /**\r
-     * Flushes the buffered contents of this EPS document to the underlying\r
-     * OutputStream it is being written to.\r
-     */\r
-    public void flush() throws IOException {\r
-        _document.flush();\r
-    }\r
-\r
-\r
-    /**\r
-     * Closes the EPS file being output to the underlying OutputStream.\r
-     * The OutputStream is automatically flushed before being closed.\r
-     * If you forget to do this, the file may be incomplete.\r
-     */\r
-    public void close() throws IOException {\r
-        flush();\r
-        _document.close();\r
-    }\r
-\r
-\r
-    /**\r
-     * Appends a line to the EpsDocument.\r
-     */\r
-    private void append(String line) {\r
-        _document.append(this, line);\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the point after it has been transformed by the transformation.\r
-     */\r
-    private Point2D transform(float x, float y) {\r
-        Point2D result = new Point2D.Float(x, y);\r
-        result = _transform.transform(result, result);\r
-        result.setLocation(result.getX(), -result.getY());\r
-        return result;\r
-    }\r
-\r
-\r
-    /**\r
-     * Appends the commands required to draw a shape on the EPS document.\r
-     */\r
-    private void draw(Shape s, String action) {\r
-\r
-        if (s != null) {\r
-\r
-            //Rectangle2D userBounds = s.getBounds2D();\r
-            if (!_transform.isIdentity()) {\r
-                s = _transform.createTransformedShape(s);\r
-            }\r
-\r
-            // Update the bounds.\r
-            if (!action.equals("clip")) {\r
-                Rectangle2D shapeBounds = s.getBounds2D();\r
-                Rectangle2D visibleBounds = shapeBounds;\r
-                if (_clip != null) {\r
-                    Rectangle2D clipBounds = _clip.getBounds2D();\r
-                    visibleBounds = shapeBounds.createIntersection(clipBounds);\r
-                }\r
-                float lineRadius = _stroke.getLineWidth() / 2;\r
-                float minX = (float) visibleBounds.getMinX() - lineRadius;\r
-                float minY = (float) visibleBounds.getMinY() - lineRadius;\r
-                float maxX = (float) visibleBounds.getMaxX() + lineRadius;\r
-                float maxY = (float) visibleBounds.getMaxY() + lineRadius;\r
-                _document.updateBounds(minX, -minY);\r
-                _document.updateBounds(maxX, -maxY);\r
-            }\r
-\r
-            append("newpath");\r
-            int type = 0;\r
-            float[] coords = new float[6];\r
-            PathIterator it = s.getPathIterator(null);\r
-            float x0 = 0;\r
-            float y0 = 0;\r
-            int count = 0;\r
-            while (!it.isDone()) {\r
-                type = it.currentSegment(coords);\r
-                float x1 = coords[0];\r
-                float y1 = -coords[1];\r
-                float x2 = coords[2];\r
-                float y2 = -coords[3];\r
-                float x3 = coords[4];\r
-                float y3 = -coords[5];\r
-\r
-                if (type == PathIterator.SEG_CLOSE) {\r
-                    append("closepath");\r
-                    count++;\r
-                }\r
-                else if (type == PathIterator.SEG_CUBICTO) {\r
-                    append(x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3 + " curveto");\r
-                    count++;\r
-                    x0 = x3;\r
-                    y0 = y3;\r
-                }\r
-                else if (type == PathIterator.SEG_LINETO) {\r
-                    append(x1 + " " + y1 + " lineto");\r
-                    count++;\r
-                    x0 = x1;\r
-                    y0 = y1;\r
-                }\r
-                else if (type == PathIterator.SEG_MOVETO) {\r
-                    append(x1 + " " + y1 + " moveto");\r
-                    count++;\r
-                    x0 = x1;\r
-                    y0 = y1;\r
-                }\r
-                else if (type == PathIterator.SEG_QUADTO) {\r
-                    // Convert the quad curve into a cubic.\r
-                    float _x1 = x0 + 2 / 3f * (x1 - x0);\r
-                    float _y1 = y0 + 2 / 3f * (y1 - y0);\r
-                    float _x2 = x1 + 1 / 3f * (x2 - x1);\r
-                    float _y2 = y1 + 1 / 3f * (y2 - y1);\r
-                    float _x3 = x2;\r
-                    float _y3 = y2;\r
-                    append(_x1 + " " + _y1 + " " + _x2 + " " + _y2 + " " + _x3 + " " + _y3 + " curveto");\r
-                    count++;\r
-                    x0 = _x3;\r
-                    y0 = _y3;\r
-                }\r
-                else if (type == PathIterator.WIND_EVEN_ODD) {\r
-                    // Ignore.\r
-                }\r
-                else if (type == PathIterator.WIND_NON_ZERO) {\r
-                    // Ignore.\r
-                }\r
-                it.next();\r
-            }\r
-            append(action);\r
-            append("newpath");\r
+public class EpsGraphics2D\r
+    extends java.awt.Graphics2D\r
+{\r
+\r
+  public static final String VERSION = "0.8.8";\r
+\r
+  /**\r
+   * Constructs a new EPS document that is initially empty and can be\r
+   * drawn on like a Graphics2D object.  The EPS document is stored in\r
+   * memory.\r
+   */\r
+  public EpsGraphics2D()\r
+  {\r
+    this("Untitled");\r
+  }\r
+\r
+  /**\r
+   * Constructs a new EPS document that is initially empty and can be\r
+   * drawn on like a Graphics2D object.  The EPS document is stored in\r
+   * memory.\r
+   */\r
+  public EpsGraphics2D(String title)\r
+  {\r
+    _document = new EpsDocument(title);\r
+    _backgroundColor = Color.white;\r
+    _clip = null;\r
+    _transform = new AffineTransform();\r
+    _clipTransform = new AffineTransform();\r
+    _accurateTextMode = true;\r
+    setColor(Color.black);\r
+    setPaint(Color.black);\r
+    setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));\r
+    setFont(Font.decode(null));\r
+    setStroke(new BasicStroke());\r
+  }\r
+\r
+  /**\r
+   * Constructs a new EPS document that is initially empty and can be\r
+   * drawn on like a Graphics2D object. The EPS document is written to\r
+   * the file as it goes, which reduces memory usage. The bounding box of\r
+   * the document is fixed and specified at construction time by\r
+   * minX,minY,maxX,maxY. The file is flushed and closed when the close()\r
+   * method is called.\r
+   */\r
+  public EpsGraphics2D(String title, File file, int minX, int minY, int maxX,\r
+                       int maxY)\r
+      throws IOException\r
+  {\r
+    this(title, new FileOutputStream(file), minX, minY, maxX, maxY);\r
+  }\r
+\r
+  /**\r
+   * Constructs a new EPS document that is initially empty and can be\r
+   * drawn on like a Graphics2D object. The EPS document is written to\r
+   * the output stream as it goes, which reduces memory usage. The\r
+   * bounding box of the document is fixed and specified at construction\r
+   * time by minX,minY,maxX,maxY. The output stream is flushed and closed\r
+   * when the close() method is called.\r
+   */\r
+  public EpsGraphics2D(String title, OutputStream outputStream, int minX,\r
+                       int minY, int maxX, int maxY)\r
+      throws IOException\r
+  {\r
+    this(title);\r
+    _document = new EpsDocument(title, outputStream, minX, minY, maxX, maxY);\r
+  }\r
+\r
+  /**\r
+   * Constructs a new EpsGraphics2D instance that is a copy of the\r
+   * supplied argument and points at the same EpsDocument.\r
+   */\r
+  protected EpsGraphics2D(EpsGraphics2D g)\r
+  {\r
+    _document = g._document;\r
+    _backgroundColor = g._backgroundColor;\r
+    _clip = g._clip;\r
+    _clipTransform = (AffineTransform) g._clipTransform.clone();\r
+    _transform = (AffineTransform) g._transform.clone();\r
+    _color = g._color;\r
+    _paint = g._paint;\r
+    _composite = g._composite;\r
+    _font = g._font;\r
+    _stroke = g._stroke;\r
+    _accurateTextMode = g._accurateTextMode;\r
+  }\r
+\r
+  /**\r
+   * This method is called to indicate that a particular method is not\r
+   * supported yet.  The stack trace is printed to the standard output.\r
+   */\r
+  private void methodNotSupported()\r
+  {\r
+    EpsException e = new EpsException(\r
+        "Method not currently supported by EpsGraphics2D version " + VERSION);\r
+    e.printStackTrace(System.err);\r
+  }\r
+\r
+  /////////////// Specialist methods ///////////////////////\r
+\r
+\r
+  /**\r
+   * Sets whether to use accurate text mode when rendering text in EPS.\r
+   * This is enabled (true) by default. When accurate text mode is used,\r
+   * all text will be rendered in EPS to appear exactly the same as it\r
+   * would do when drawn with a Graphics2D context. With accurate text\r
+   * mode enabled, it is not necessary for the EPS viewer to have the\r
+   * required font installed.\r
+   * <p>\r
+   * Turning off accurate text mode will require the EPS viewer to have\r
+   * the necessary fonts installed. If you are using a lot of text, you\r
+   * will find that this significantly reduces the file size of your EPS\r
+   * documents.  AffineTransforms can only affect the starting point of\r
+   * text using this simpler text mode - all text will be horizontal.\r
+   */\r
+  public void setAccurateTextMode(boolean b)\r
+  {\r
+    _accurateTextMode = b;\r
+  }\r
+\r
+  /**\r
+   * Returns whether accurate text mode is being used.\r
+   */\r
+  public boolean getAccurateTextMode()\r
+  {\r
+    return _accurateTextMode;\r
+  }\r
+\r
+  /**\r
+   * Flushes the buffered contents of this EPS document to the underlying\r
+   * OutputStream it is being written to.\r
+   */\r
+  public void flush()\r
+      throws IOException\r
+  {\r
+    _document.flush();\r
+  }\r
+\r
+  /**\r
+   * Closes the EPS file being output to the underlying OutputStream.\r
+   * The OutputStream is automatically flushed before being closed.\r
+   * If you forget to do this, the file may be incomplete.\r
+   */\r
+  public void close()\r
+      throws IOException\r
+  {\r
+    flush();\r
+    _document.close();\r
+  }\r
+\r
+  /**\r
+   * Appends a line to the EpsDocument.\r
+   */\r
+  private void append(String line)\r
+  {\r
+    _document.append(this, line);\r
+  }\r
+\r
+  /**\r
+   * Returns the point after it has been transformed by the transformation.\r
+   */\r
+  private Point2D transform(float x, float y)\r
+  {\r
+    Point2D result = new Point2D.Float(x, y);\r
+    result = _transform.transform(result, result);\r
+    result.setLocation(result.getX(), -result.getY());\r
+    return result;\r
+  }\r
+\r
+  /**\r
+   * Appends the commands required to draw a shape on the EPS document.\r
+   */\r
+  private void draw(Shape s, String action)\r
+  {\r
+\r
+    if (s != null)\r
+    {\r
+\r
+      //Rectangle2D userBounds = s.getBounds2D();\r
+      if (!_transform.isIdentity())\r
+      {\r
+        s = _transform.createTransformedShape(s);\r
+      }\r
+\r
+      // Update the bounds.\r
+      if (!action.equals("clip"))\r
+      {\r
+        Rectangle2D shapeBounds = s.getBounds2D();\r
+        Rectangle2D visibleBounds = shapeBounds;\r
+        if (_clip != null)\r
+        {\r
+          Rectangle2D clipBounds = _clip.getBounds2D();\r
+          visibleBounds = shapeBounds.createIntersection(clipBounds);\r
         }\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns a hex string that always contains two characters.\r
-     */\r
-    private String toHexString(int n) {\r
-        String result = Integer.toString(n, 16);\r
-        while (result.length() < 2) {\r
-            result = "0" + result;\r
+        float lineRadius = _stroke.getLineWidth() / 2;\r
+        float minX = (float) visibleBounds.getMinX() - lineRadius;\r
+        float minY = (float) visibleBounds.getMinY() - lineRadius;\r
+        float maxX = (float) visibleBounds.getMaxX() + lineRadius;\r
+        float maxY = (float) visibleBounds.getMaxY() + lineRadius;\r
+        _document.updateBounds(minX, -minY);\r
+        _document.updateBounds(maxX, -maxY);\r
+      }\r
+\r
+      append("newpath");\r
+      int type = 0;\r
+      float[] coords = new float[6];\r
+      PathIterator it = s.getPathIterator(null);\r
+      float x0 = 0;\r
+      float y0 = 0;\r
+      int count = 0;\r
+      while (!it.isDone())\r
+      {\r
+        type = it.currentSegment(coords);\r
+        float x1 = coords[0];\r
+        float y1 = -coords[1];\r
+        float x2 = coords[2];\r
+        float y2 = -coords[3];\r
+        float x3 = coords[4];\r
+        float y3 = -coords[5];\r
+\r
+        if (type == PathIterator.SEG_CLOSE)\r
+        {\r
+          append("closepath");\r
+          count++;\r
         }\r
-        return result;\r
-    }\r
-\r
-\r
-    /////////////// Graphics2D methods ///////////////////////\r
-\r
-\r
-    /**\r
-     * Draws a 3D rectangle outline.  If it is raised, light appears to come\r
-     * from the top left.\r
-     */\r
-    public void draw3DRect(int x, int y, int width, int height, boolean raised) {\r
-        Color originalColor = getColor();\r
-        Stroke originalStroke = getStroke();\r
-\r
-        setStroke(new BasicStroke(1.0f));\r
-\r
-        if (raised) {\r
-            setColor(originalColor.brighter());\r
+        else if (type == PathIterator.SEG_CUBICTO)\r
+        {\r
+          append(x1 + " " + y1 + " " + x2 + " " + y2 + " " + x3 + " " + y3 +\r
+                 " curveto");\r
+          count++;\r
+          x0 = x3;\r
+          y0 = y3;\r
         }\r
-        else {\r
-            setColor(originalColor.darker());\r
+        else if (type == PathIterator.SEG_LINETO)\r
+        {\r
+          append(x1 + " " + y1 + " lineto");\r
+          count++;\r
+          x0 = x1;\r
+          y0 = y1;\r
         }\r
-\r
-        drawLine(x, y, x + width, y);\r
-        drawLine(x, y, x, y + height);\r
-\r
-        if (raised) {\r
-            setColor(originalColor.darker());\r
+        else if (type == PathIterator.SEG_MOVETO)\r
+        {\r
+          append(x1 + " " + y1 + " moveto");\r
+          count++;\r
+          x0 = x1;\r
+          y0 = y1;\r
         }\r
-        else {\r
-            setColor(originalColor.brighter());\r
+        else if (type == PathIterator.SEG_QUADTO)\r
+        {\r
+          // Convert the quad curve into a cubic.\r
+          float _x1 = x0 + 2 / 3f * (x1 - x0);\r
+          float _y1 = y0 + 2 / 3f * (y1 - y0);\r
+          float _x2 = x1 + 1 / 3f * (x2 - x1);\r
+          float _y2 = y1 + 1 / 3f * (y2 - y1);\r
+          float _x3 = x2;\r
+          float _y3 = y2;\r
+          append(_x1 + " " + _y1 + " " + _x2 + " " + _y2 + " " + _x3 + " " +\r
+                 _y3 + " curveto");\r
+          count++;\r
+          x0 = _x3;\r
+          y0 = _y3;\r
         }\r
-\r
-        drawLine(x + width, y + height, x, y + height);\r
-        drawLine(x + width, y + height, x + width, y);\r
-\r
-        setColor(originalColor);\r
-        setStroke(originalStroke);\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills a 3D rectangle.  If raised, it has bright fill and light appears\r
-     * to come from the top left.\r
-     */\r
-    public void fill3DRect(int x, int y, int width, int height, boolean raised) {\r
-        Color originalColor = getColor();\r
-\r
-        if (raised) {\r
-            setColor(originalColor.brighter());\r
+        else if (type == PathIterator.WIND_EVEN_ODD)\r
+        {\r
+          // Ignore.\r
         }\r
-        else {\r
-            setColor(originalColor.darker());\r
+        else if (type == PathIterator.WIND_NON_ZERO)\r
+        {\r
+          // Ignore.\r
         }\r
-        draw(new Rectangle(x, y, width, height), "fill");\r
-        setColor(originalColor);\r
-        draw3DRect(x, y, width, height, raised);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a Shape on the EPS document.\r
-     */\r
-    public void draw(Shape s) {\r
-        draw(s, "stroke");\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an Image on the EPS document.\r
-     */\r
-    public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {\r
-        AffineTransform at = getTransform();\r
-        transform(xform);\r
-        boolean st = drawImage(img, 0, 0, obs);\r
-        setTransform(at);\r
-        return st;\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a BufferedImage on the EPS document.\r
-     */\r
-    public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {\r
-        BufferedImage img1 = op.filter(img, null);\r
-        drawImage(img1, new AffineTransform(1f, 0f, 0f, 1f, x, y), null);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a RenderedImage on the EPS document.\r
-     */\r
-    public void drawRenderedImage(RenderedImage img, AffineTransform xform) {\r
-        Hashtable properties = new Hashtable();\r
-        String[] names = img.getPropertyNames();\r
-        for (int i = 0; i < names.length; i++) {\r
-            properties.put(names[i], img.getProperty(names[i]));\r
+        it.next();\r
+      }\r
+      append(action);\r
+      append("newpath");\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Returns a hex string that always contains two characters.\r
+   */\r
+  private String toHexString(int n)\r
+  {\r
+    String result = Integer.toString(n, 16);\r
+    while (result.length() < 2)\r
+    {\r
+      result = "0" + result;\r
+    }\r
+    return result;\r
+  }\r
+\r
+  /////////////// Graphics2D methods ///////////////////////\r
+\r
+\r
+  /**\r
+   * Draws a 3D rectangle outline.  If it is raised, light appears to come\r
+   * from the top left.\r
+   */\r
+  public void draw3DRect(int x, int y, int width, int height, boolean raised)\r
+  {\r
+    Color originalColor = getColor();\r
+    Stroke originalStroke = getStroke();\r
+\r
+    setStroke(new BasicStroke(1.0f));\r
+\r
+    if (raised)\r
+    {\r
+      setColor(originalColor.brighter());\r
+    }\r
+    else\r
+    {\r
+      setColor(originalColor.darker());\r
+    }\r
+\r
+    drawLine(x, y, x + width, y);\r
+    drawLine(x, y, x, y + height);\r
+\r
+    if (raised)\r
+    {\r
+      setColor(originalColor.darker());\r
+    }\r
+    else\r
+    {\r
+      setColor(originalColor.brighter());\r
+    }\r
+\r
+    drawLine(x + width, y + height, x, y + height);\r
+    drawLine(x + width, y + height, x + width, y);\r
+\r
+    setColor(originalColor);\r
+    setStroke(originalStroke);\r
+  }\r
+\r
+  /**\r
+   * Fills a 3D rectangle.  If raised, it has bright fill and light appears\r
+   * to come from the top left.\r
+   */\r
+  public void fill3DRect(int x, int y, int width, int height, boolean raised)\r
+  {\r
+    Color originalColor = getColor();\r
+\r
+    if (raised)\r
+    {\r
+      setColor(originalColor.brighter());\r
+    }\r
+    else\r
+    {\r
+      setColor(originalColor.darker());\r
+    }\r
+    draw(new Rectangle(x, y, width, height), "fill");\r
+    setColor(originalColor);\r
+    draw3DRect(x, y, width, height, raised);\r
+  }\r
+\r
+  /**\r
+   * Draws a Shape on the EPS document.\r
+   */\r
+  public void draw(Shape s)\r
+  {\r
+    draw(s, "stroke");\r
+  }\r
+\r
+  /**\r
+   * Draws an Image on the EPS document.\r
+   */\r
+  public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)\r
+  {\r
+    AffineTransform at = getTransform();\r
+    transform(xform);\r
+    boolean st = drawImage(img, 0, 0, obs);\r
+    setTransform(at);\r
+    return st;\r
+  }\r
+\r
+  /**\r
+   * Draws a BufferedImage on the EPS document.\r
+   */\r
+  public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)\r
+  {\r
+    BufferedImage img1 = op.filter(img, null);\r
+    drawImage(img1, new AffineTransform(1f, 0f, 0f, 1f, x, y), null);\r
+  }\r
+\r
+  /**\r
+   * Draws a RenderedImage on the EPS document.\r
+   */\r
+  public void drawRenderedImage(RenderedImage img, AffineTransform xform)\r
+  {\r
+    Hashtable properties = new Hashtable();\r
+    String[] names = img.getPropertyNames();\r
+    for (int i = 0; i < names.length; i++)\r
+    {\r
+      properties.put(names[i], img.getProperty(names[i]));\r
+    }\r
+\r
+    ColorModel cm = img.getColorModel();\r
+    WritableRaster wr = img.copyData(null);\r
+    BufferedImage img1 = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(),\r
+                                           properties);\r
+    AffineTransform at = AffineTransform.getTranslateInstance(img.getMinX(),\r
+        img.getMinY());\r
+    at.preConcatenate(xform);\r
+    drawImage(img1, at, null);\r
+  }\r
+\r
+  /**\r
+   * Draws a RenderableImage by invoking its createDefaultRendering method.\r
+   */\r
+  public void drawRenderableImage(RenderableImage img, AffineTransform xform)\r
+  {\r
+    drawRenderedImage(img.createDefaultRendering(), xform);\r
+  }\r
+\r
+  /**\r
+   * Draws a string at (x,y)\r
+   */\r
+  public void drawString(String str, int x, int y)\r
+  {\r
+    drawString(str, (float) x, (float) y);\r
+  }\r
+\r
+  /**\r
+   * Draws a string at (x,y)\r
+   */\r
+  public void drawString(String s, float x, float y)\r
+  {\r
+    if (s != null && s.length() > 0)\r
+    {\r
+      AttributedString as = new AttributedString(s);\r
+      as.addAttribute(TextAttribute.FONT, getFont());\r
+      drawString(as.getIterator(), x, y);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Draws the characters of an AttributedCharacterIterator, starting from\r
+   * (x,y).\r
+   */\r
+  public void drawString(AttributedCharacterIterator iterator, int x, int y)\r
+  {\r
+    drawString(iterator, (float) x, (float) y);\r
+  }\r
+\r
+  /**\r
+   * Draws the characters of an AttributedCharacterIterator, starting from\r
+   * (x,y).\r
+   */\r
+  public void drawString(AttributedCharacterIterator iterator, float x, float y)\r
+  {\r
+    if (getAccurateTextMode())\r
+    {\r
+      TextLayout layout = new TextLayout(iterator, getFontRenderContext());\r
+      Shape shape = layout.getOutline(AffineTransform.getTranslateInstance(x, y));\r
+      draw(shape, "fill");\r
+    }\r
+    else\r
+    {\r
+      append("newpath");\r
+      Point2D location = transform(x, y);\r
+      append(location.getX() + " " + location.getY() + " moveto");\r
+      StringBuffer buffer = new StringBuffer();\r
+      for (char ch = iterator.first(); ch != CharacterIterator.DONE;\r
+           ch = iterator.next())\r
+      {\r
+        if (ch == '(' || ch == ')')\r
+        {\r
+          buffer.append('\\');\r
         }\r
-\r
-        ColorModel cm = img.getColorModel();\r
-        WritableRaster wr = img.copyData(null);\r
-        BufferedImage img1 = new BufferedImage(cm, wr, cm.isAlphaPremultiplied(), properties);\r
-        AffineTransform at = AffineTransform.getTranslateInstance(img.getMinX(), img.getMinY());\r
-        at.preConcatenate(xform);\r
-        drawImage(img1, at, null);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a RenderableImage by invoking its createDefaultRendering method.\r
-     */\r
-    public void drawRenderableImage(RenderableImage img, AffineTransform xform) {\r
-        drawRenderedImage(img.createDefaultRendering(), xform);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a string at (x,y)\r
-     */\r
-    public void drawString(String str, int x, int y) {\r
-        drawString(str, (float) x, (float) y);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a string at (x,y)\r
-     */\r
-    public void drawString(String s, float x, float y) {\r
-        if (s != null && s.length() > 0) {\r
-            AttributedString as = new AttributedString(s);\r
-            as.addAttribute(TextAttribute.FONT, getFont());\r
-            drawString(as.getIterator(), x, y);\r
+        buffer.append(ch);\r
+      }\r
+      append("(" + buffer.toString() + ") show");\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Draws a GlyphVector at (x,y)\r
+   */\r
+  public void drawGlyphVector(GlyphVector g, float x, float y)\r
+  {\r
+    Shape shape = g.getOutline(x, y);\r
+    draw(shape, "fill");\r
+  }\r
+\r
+  /**\r
+   * Fills a Shape on the EPS document.\r
+   */\r
+  public void fill(Shape s)\r
+  {\r
+    draw(s, "fill");\r
+  }\r
+\r
+  /**\r
+   * Checks whether or not the specified Shape intersects the specified\r
+   * Rectangle, which is in device space.\r
+   */\r
+  public boolean hit(Rectangle rect, Shape s, boolean onStroke)\r
+  {\r
+    return s.intersects(rect);\r
+  }\r
+\r
+  /**\r
+   * Returns the device configuration associated with this EpsGraphics2D\r
+   * object.\r
+   */\r
+  public GraphicsConfiguration getDeviceConfiguration()\r
+  {\r
+    GraphicsConfiguration gc = null;\r
+    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
+    GraphicsDevice[] gds = ge.getScreenDevices();\r
+    for (int i = 0; i < gds.length; i++)\r
+    {\r
+      GraphicsDevice gd = gds[i];\r
+      GraphicsConfiguration[] gcs = gd.getConfigurations();\r
+      if (gcs.length > 0)\r
+      {\r
+        return gcs[0];\r
+      }\r
+    }\r
+    return gc;\r
+  }\r
+\r
+  /**\r
+   * Sets the Composite to be used by this EpsGraphics2D.  EpsGraphics2D\r
+   * does not make use of these.\r
+   */\r
+  public void setComposite(Composite comp)\r
+  {\r
+    _composite = comp;\r
+  }\r
+\r
+  /**\r
+   * Sets the Paint attribute for the EpsGraphics2D object.  Only Paint\r
+   * objects of type Color are respected by EpsGraphics2D.\r
+   */\r
+  public void setPaint(Paint paint)\r
+  {\r
+    _paint = paint;\r
+    if (paint instanceof Color)\r
+    {\r
+      setColor( (Color) paint);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Sets the stroke.  Only accepts BasicStroke objects (or subclasses of\r
+   * BasicStroke).\r
+   */\r
+  public void setStroke(Stroke s)\r
+  {\r
+    if (s instanceof BasicStroke)\r
+    {\r
+      _stroke = (BasicStroke) s;\r
+\r
+      append(_stroke.getLineWidth() + " setlinewidth");\r
+      float miterLimit = _stroke.getMiterLimit();\r
+      if (miterLimit < 1.0f)\r
+      {\r
+        miterLimit = 1;\r
+      }\r
+      append(miterLimit + " setmiterlimit");\r
+      append(_stroke.getLineJoin() + " setlinejoin");\r
+      append(_stroke.getEndCap() + " setlinecap");\r
+\r
+      StringBuffer dashes = new StringBuffer();\r
+      dashes.append("[ ");\r
+      float[] dashArray = _stroke.getDashArray();\r
+      if (dashArray != null)\r
+      {\r
+        for (int i = 0; i < dashArray.length; i++)\r
+        {\r
+          dashes.append( (dashArray[i]) + " ");\r
         }\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws the characters of an AttributedCharacterIterator, starting from\r
-     * (x,y).\r
-     */\r
-    public void drawString(AttributedCharacterIterator iterator, int x, int y) {\r
-        drawString(iterator, (float) x, (float) y);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws the characters of an AttributedCharacterIterator, starting from\r
-     * (x,y).\r
-     */\r
-    public void drawString(AttributedCharacterIterator iterator, float x, float y) {\r
-        if (getAccurateTextMode()) {\r
-            TextLayout layout = new TextLayout(iterator, getFontRenderContext());\r
-            Shape shape = layout.getOutline(AffineTransform.getTranslateInstance(x, y));\r
-            draw(shape, "fill");\r
-        }\r
-        else {\r
-            append("newpath");\r
-            Point2D location = transform(x, y);\r
-            append(location.getX() + " " + location.getY() + " moveto");\r
-            StringBuffer buffer = new StringBuffer();\r
-            for (char ch = iterator.first(); ch != CharacterIterator.DONE; ch = iterator.next()) {\r
-                if (ch == '(' || ch == ')') {\r
-                    buffer.append('\\');\r
-                }\r
-                buffer.append(ch);\r
-            }\r
-            append("(" + buffer.toString() + ") show");\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a GlyphVector at (x,y)\r
-     */\r
-    public void drawGlyphVector(GlyphVector g, float x, float y) {\r
-        Shape shape = g.getOutline(x, y);\r
-        draw(shape, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills a Shape on the EPS document.\r
-     */\r
-    public void fill(Shape s) {\r
-        draw(s, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Checks whether or not the specified Shape intersects the specified\r
-     * Rectangle, which is in device space.\r
-     */\r
-    public boolean hit(Rectangle rect, Shape s, boolean onStroke) {\r
-        return s.intersects(rect);\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the device configuration associated with this EpsGraphics2D\r
-     * object.\r
-     */\r
-    public GraphicsConfiguration getDeviceConfiguration() {\r
-        GraphicsConfiguration gc = null;\r
-        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
-        GraphicsDevice[] gds = ge.getScreenDevices();\r
-        for (int i = 0; i < gds.length; i++) {\r
-            GraphicsDevice gd = gds[i];\r
-            GraphicsConfiguration[] gcs = gd.getConfigurations();\r
-            if (gcs.length > 0) {\r
-                return gcs[0];\r
-            }\r
-        }\r
-        return gc;\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the Composite to be used by this EpsGraphics2D.  EpsGraphics2D\r
-     * does not make use of these.\r
-     */\r
-    public void setComposite(Composite comp) {\r
-        _composite = comp;\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the Paint attribute for the EpsGraphics2D object.  Only Paint\r
-     * objects of type Color are respected by EpsGraphics2D.\r
-     */\r
-    public void setPaint(Paint paint) {\r
-        _paint = paint;\r
-        if (paint instanceof Color) {\r
-            setColor((Color) paint);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the stroke.  Only accepts BasicStroke objects (or subclasses of\r
-     * BasicStroke).\r
-     */\r
-    public void setStroke(Stroke s) {\r
-        if (s instanceof BasicStroke) {\r
-            _stroke = (BasicStroke) s;\r
-\r
-            append(_stroke.getLineWidth() + " setlinewidth");\r
-            float miterLimit = _stroke.getMiterLimit();\r
-            if (miterLimit < 1.0f) {\r
-                miterLimit = 1;\r
-            }\r
-            append(miterLimit + " setmiterlimit");\r
-            append(_stroke.getLineJoin() + " setlinejoin");\r
-            append(_stroke.getEndCap() + " setlinecap");\r
-\r
-            StringBuffer dashes = new StringBuffer();\r
-            dashes.append("[ ");\r
-            float[] dashArray = _stroke.getDashArray();\r
-            if (dashArray != null) {\r
-                for (int i = 0; i < dashArray.length; i++) {\r
-                    dashes.append((dashArray[i]) + " ");\r
-                }\r
-            }\r
-            dashes.append("]");\r
-            append(dashes.toString() + " 0 setdash");\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets a rendering hint. These are not used by EpsGraphics2D.\r
-     */\r
-    public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {\r
-        // Do nothing.\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the value of a single preference for the rendering\r
-     * algorithms.  Rendering hints are not used by EpsGraphics2D.\r
-     */\r
-    public Object getRenderingHint(RenderingHints.Key hintKey) {\r
-        return null;\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the rendering hints.  These are ignored by EpsGraphics2D.\r
-     */\r
-    public void setRenderingHints(Map hints) {\r
-        // Do nothing.\r
-    }\r
-\r
-\r
-    /**\r
-     * Adds rendering hints.  These are ignored by EpsGraphics2D.\r
-     */\r
-    public void addRenderingHints(Map hints) {\r
-        // Do nothing.\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the preferences for the rendering algorithms.\r
-     */\r
-    public RenderingHints getRenderingHints() {\r
-        return new RenderingHints(null);\r
-    }\r
-\r
-\r
-    /**\r
-     * Translates the origin of the EpsGraphics2D context to the point (x,y)\r
-     * in the current coordinate system.\r
-     */\r
-    public void translate(int x, int y) {\r
-        translate((double) x, (double) y);\r
-    }\r
-\r
-\r
-    /**\r
-     * Concatenates the current EpsGraphics2D Transformation with a\r
-     * translation transform.\r
-     */\r
-    public void translate(double tx, double ty) {\r
-        transform(AffineTransform.getTranslateInstance(tx, ty));\r
-    }\r
-\r
-\r
-    /**\r
-     * Concatenates the current EpsGraphics2D Transform with a rotation\r
-     * transform.\r
-     */\r
-    public void rotate(double theta) {\r
-        rotate(theta, 0, 0);\r
-    }\r
-\r
-\r
-    /**\r
-     * Concatenates the current EpsGraphics2D Transform with a translated\r
-     * rotation transform.\r
-     */\r
-    public void rotate(double theta, double x, double y) {\r
-        transform(AffineTransform.getRotateInstance(theta, x, y));\r
-    }\r
-\r
-\r
-    /**\r
-     * Concatenates the current EpsGraphics2D Transform with a scaling\r
-     * transformation.\r
-     */\r
-    public void scale(double sx, double sy) {\r
-        transform(AffineTransform.getScaleInstance(sx, sy));\r
-    }\r
-\r
-\r
-    /**\r
-     * Concatenates the current EpsGraphics2D Transform with a shearing\r
-     * transform.\r
-     */\r
-    public void shear(double shx, double shy) {\r
-        transform(AffineTransform.getShearInstance(shx, shy));\r
-    }\r
-\r
-\r
-    /**\r
-     * Composes an AffineTransform object with the Transform in this\r
-     * EpsGraphics2D according to the rule last-specified-first-applied.\r
-     */\r
-    public void transform(AffineTransform Tx) {\r
-        _transform.concatenate(Tx);\r
-        setTransform(getTransform());\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the AffineTransform to be used by this EpsGraphics2D.\r
-     */\r
-    public void setTransform(AffineTransform Tx) {\r
-        if (Tx == null) {\r
-            _transform = new AffineTransform();\r
-        }\r
-        else {\r
-            _transform = new AffineTransform(Tx);\r
-        }\r
-        // Need to update the stroke and font so they know the scale changed\r
-        setStroke(getStroke());\r
-        setFont(getFont());\r
-    }\r
-\r
-\r
-    /**\r
-     * Gets the AffineTransform used by this EpsGraphics2D.\r
-     */\r
-    public AffineTransform getTransform() {\r
-        return new AffineTransform(_transform);\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the current Paint of the EpsGraphics2D object.\r
-     */\r
-    public Paint getPaint() {\r
-        return _paint;\r
-    }\r
-\r
-\r
-    /**\r
-     * returns the current Composite of the EpsGraphics2D object.\r
-     */\r
-    public Composite getComposite() {\r
-        return _composite;\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the background color to be used by the clearRect method.\r
-     */\r
-    public void setBackground(Color color) {\r
-        if (color == null) {\r
-            color = Color.black;\r
-        }\r
-        _backgroundColor = color;\r
-    }\r
-\r
-\r
-    /**\r
-     * Gets the background color that is used by the clearRect method.\r
-     */\r
-    public Color getBackground() {\r
-        return _backgroundColor;\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the Stroke currently used.  Guaranteed to be an instance of\r
-     * BasicStroke.\r
-     */\r
-    public Stroke getStroke() {\r
-        return _stroke;\r
-    }\r
-\r
-\r
-    /**\r
-     * Intersects the current clip with the interior of the specified Shape\r
-     * and sets the clip to the resulting intersection.\r
-     */\r
-    public void clip(Shape s) {\r
-        if (_clip == null) {\r
-            setClip(s);\r
-        }\r
-        else {\r
-            Area area = new Area(_clip);\r
-            area.intersect(new Area(s));\r
-            setClip(area);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the FontRenderContext.\r
-     */\r
-    public FontRenderContext getFontRenderContext() {\r
-        return _fontRenderContext;\r
-    }\r
-\r
-\r
-    /////////////// Graphics methods ///////////////////////\r
-\r
-\r
-    /**\r
-     * Returns a new Graphics object that is identical to this EpsGraphics2D.\r
-     */\r
-    public Graphics create() {\r
-        return new EpsGraphics2D(this);\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns an EpsGraphics2D object based on this\r
-     * Graphics object, but with a new translation and clip\r
-     * area.\r
-     */\r
-    public Graphics create(int x, int y, int width, int height) {\r
-        Graphics g = create();\r
-        g.translate(x, y);\r
-        g.clipRect(0, 0, width, height);\r
-        return g;\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the current Color.  This will be a default value (black)\r
-     * until it is changed using the setColor method.\r
-     */\r
-    public Color getColor() {\r
-        return _color;\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the Color to be used when drawing all future shapes, text, etc.\r
-     */\r
-    public void setColor(Color c) {\r
-        if (c == null) {\r
-            c = Color.black;\r
-        }\r
-        _color = c;\r
-        append((c.getRed() / 255f) + " " + (c.getGreen() / 255f) + " " + (c.getBlue() / 255f) + " setrgbcolor");\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the paint mode of this EpsGraphics2D object to overwrite the\r
-     * destination EpsDocument with the current color.\r
-     */\r
-    public void setPaintMode() {\r
-        // Do nothing - paint mode is the only method supported anyway.\r
-    }\r
-\r
-\r
-    /**\r
-     * <b><i><font color="red">Not implemented</font></i></b> - performs no action.\r
-     */\r
-    public void setXORMode(Color c1) {\r
-        methodNotSupported();\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the Font currently being used.\r
-     */\r
-    public Font getFont() {\r
-        return _font;\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the Font to be used in future text.\r
-     */\r
-    public void setFont(Font font) {\r
-        if (font == null) {\r
-            font = Font.decode(null);\r
-        }\r
-        _font = font;\r
-        append("/" + _font.getPSName() + " findfont " + ((int) _font.getSize()) + " scalefont setfont");\r
-    }\r
-\r
-\r
-    /**\r
-     * Gets the font metrics of the current font.\r
-     */\r
-    public FontMetrics getFontMetrics() {\r
-        return getFontMetrics(getFont());\r
-    }\r
-\r
-\r
-    /**\r
-     * Gets the font metrics for the specified font.\r
-     */\r
-    public FontMetrics getFontMetrics(Font f) {\r
-        BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);\r
-        Graphics g = image.getGraphics();\r
-        return g.getFontMetrics(f);\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the bounding rectangle of the current clipping area.\r
-     */\r
-    public Rectangle getClipBounds() {\r
-        if (_clip == null) {\r
-            return null;\r
-        }\r
-        Rectangle rect = getClip().getBounds();\r
-        return rect;\r
-    }\r
-\r
-\r
-    /**\r
-     * Intersects the current clip with the specified rectangle.\r
-     */\r
-    public void clipRect(int x, int y, int width, int height) {\r
-        clip(new Rectangle(x, y, width, height));\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the current clip to the rectangle specified by the given\r
-     * coordinates.\r
-     */\r
-    public void setClip(int x, int y, int width, int height) {\r
-        setClip(new Rectangle(x, y, width, height));\r
-    }\r
-\r
-\r
-    /**\r
-     * Gets the current clipping area.\r
-     */\r
-    public Shape getClip() {\r
-        if (_clip == null) {\r
-            return null;\r
-        }\r
-        else {\r
-            try {\r
-                AffineTransform t = _transform.createInverse();\r
-                t.concatenate(_clipTransform);\r
-                return t.createTransformedShape(_clip);\r
-            }\r
-            catch (Exception e) {\r
-                throw new EpsException("Unable to get inverse of matrix: " + _transform);\r
-            }\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Sets the current clipping area to an arbitrary clip shape.\r
-     */\r
-    public void setClip(Shape clip) {\r
-        if (clip != null) {\r
-            if (_document.isClipSet()) {\r
-                append("grestore");\r
-                append("gsave");\r
-            }\r
-            else {\r
-                _document.setClipSet(true);\r
-                append("gsave");\r
-            }\r
-            draw(clip, "clip");\r
-            _clip = clip;\r
-            _clipTransform = (AffineTransform) _transform.clone();\r
-        }\r
-        else {\r
-            if (_document.isClipSet()) {\r
-                append("grestore");\r
-                _document.setClipSet(false);\r
-            }\r
-            _clip = null;\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * <b><i><font color="red">Not implemented</font></i></b> - performs no action.\r
-     */\r
-    public void copyArea(int x, int y, int width, int height, int dx, int dy) {\r
-        methodNotSupported();\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a straight line from (x1,y1) to (x2,y2).\r
-     */\r
-    public void drawLine(int x1, int y1, int x2, int y2) {\r
-        Shape shape = new Line2D.Float(x1, y1, x2, y2);\r
-        draw(shape);\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills a rectangle with top-left corner placed at (x,y).\r
-     */\r
-    public void fillRect(int x, int y, int width, int height) {\r
-        Shape shape = new Rectangle(x, y, width, height);\r
-        draw(shape, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a rectangle with top-left corner placed at (x,y).\r
-     */\r
-    public void drawRect(int x, int y, int width, int height) {\r
-        Shape shape = new Rectangle(x, y, width, height);\r
-        draw(shape);\r
-    }\r
-\r
-\r
-    /**\r
-     * Clears a rectangle with top-left corner placed at (x,y) using the\r
-     * current background color.\r
-     */\r
-    public void clearRect(int x, int y, int width, int height) {\r
-        Color originalColor = getColor();\r
-\r
-        setColor(getBackground());\r
-        Shape shape = new Rectangle(x, y, width, height);\r
-        draw(shape, "fill");\r
-\r
-        setColor(originalColor);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a rounded rectangle.\r
-     */\r
-    public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {\r
-        Shape shape = new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight);\r
-        draw(shape);\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills a rounded rectangle.\r
-     */\r
-    public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {\r
-        Shape shape = new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight);\r
-        draw(shape, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an oval.\r
-     */\r
-    public void drawOval(int x, int y, int width, int height) {\r
-        Shape shape = new Ellipse2D.Float(x, y, width, height);\r
-        draw(shape);\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills an oval.\r
-     */\r
-    public void fillOval(int x, int y, int width, int height) {\r
-        Shape shape = new Ellipse2D.Float(x, y, width, height);\r
-        draw(shape, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an arc.\r
-     */\r
-    public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {\r
-        Shape shape = new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN);\r
-        draw(shape);\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills an arc.\r
-     */\r
-    public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {\r
-        Shape shape = new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.PIE);\r
-        draw(shape, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a polyline.\r
-     */\r
-    public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {\r
-        if (nPoints > 0) {\r
-            GeneralPath path = new GeneralPath();\r
-            path.moveTo(xPoints[0], yPoints[0]);\r
-            for (int i = 1; i < nPoints; i++) {\r
-                path.lineTo(xPoints[i], yPoints[i]);\r
-            }\r
-            draw(path);\r
-        }\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a polygon made with the specified points.\r
-     */\r
-    public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {\r
-        Shape shape = new Polygon(xPoints, yPoints, nPoints);\r
-        draw(shape);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws a polygon.\r
-     */\r
-    public void drawPolygon(Polygon p) {\r
-        draw(p);\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills a polygon made with the specified points.\r
-     */\r
-    public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {\r
-        Shape shape = new Polygon(xPoints, yPoints, nPoints);\r
-        draw(shape, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Fills a polygon.\r
-     */\r
-    public void fillPolygon(Polygon p) {\r
-        draw(p, "fill");\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws the specified characters, starting from (x,y)\r
-     */\r
-    public void drawChars(char[] data, int offset, int length, int x, int y) {\r
-        String string = new String(data, offset, length);\r
-        drawString(string, x, y);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws the specified bytes, starting from (x,y)\r
-     */\r
-    public void drawBytes(byte[] data, int offset, int length, int x, int y) {\r
-        String string = new String(data, offset, length);\r
-        drawString(string, x, y);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an image.\r
-     */\r
-    public boolean drawImage(Image img, int x, int y, ImageObserver observer) {\r
-        return drawImage(img, x, y, Color.white, observer);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an image.\r
-     */\r
-    public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {\r
-        return drawImage(img, x, y, width, height, Color.white, observer);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an image.\r
-     */\r
-    public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {\r
-        int width = img.getWidth(null);\r
-        int height = img.getHeight(null);\r
-        return drawImage(img, x, y, width, height, bgcolor, observer);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an image.\r
-     */\r
-    public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {\r
-        return drawImage(img, x, y, x + width, y + height, 0, 0, width, height, bgcolor, observer);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an image.\r
-     */\r
-    public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {\r
-        return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, Color.white, observer);\r
-    }\r
-\r
-\r
-    /**\r
-     * Draws an image.\r
-     */\r
-    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) {\r
-        if (dx1 >= dx2) {\r
-            throw new IllegalArgumentException("dx1 >= dx2");\r
-        }\r
-        if (sx1 >= sx2) {\r
-            throw new IllegalArgumentException("sx1 >= sx2");\r
-        }\r
-        if (dy1 >= dy2) {\r
-            throw new IllegalArgumentException("dy1 >= dy2");\r
-        }\r
-        if (sy1 >= sy2) {\r
-            throw new IllegalArgumentException("sy1 >= sy2");\r
-        }\r
-\r
+      }\r
+      dashes.append("]");\r
+      append(dashes.toString() + " 0 setdash");\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Sets a rendering hint. These are not used by EpsGraphics2D.\r
+   */\r
+  public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)\r
+  {\r
+    // Do nothing.\r
+  }\r
+\r
+  /**\r
+   * Returns the value of a single preference for the rendering\r
+   * algorithms.  Rendering hints are not used by EpsGraphics2D.\r
+   */\r
+  public Object getRenderingHint(RenderingHints.Key hintKey)\r
+  {\r
+    return null;\r
+  }\r
+\r
+  /**\r
+   * Sets the rendering hints.  These are ignored by EpsGraphics2D.\r
+   */\r
+  public void setRenderingHints(Map hints)\r
+  {\r
+    // Do nothing.\r
+  }\r
+\r
+  /**\r
+   * Adds rendering hints.  These are ignored by EpsGraphics2D.\r
+   */\r
+  public void addRenderingHints(Map hints)\r
+  {\r
+    // Do nothing.\r
+  }\r
+\r
+  /**\r
+   * Returns the preferences for the rendering algorithms.\r
+   */\r
+  public RenderingHints getRenderingHints()\r
+  {\r
+    return new RenderingHints(null);\r
+  }\r
+\r
+  /**\r
+   * Translates the origin of the EpsGraphics2D context to the point (x,y)\r
+   * in the current coordinate system.\r
+   */\r
+  public void translate(int x, int y)\r
+  {\r
+    translate( (double) x, (double) y);\r
+  }\r
+\r
+  /**\r
+   * Concatenates the current EpsGraphics2D Transformation with a\r
+   * translation transform.\r
+   */\r
+  public void translate(double tx, double ty)\r
+  {\r
+    transform(AffineTransform.getTranslateInstance(tx, ty));\r
+  }\r
+\r
+  /**\r
+   * Concatenates the current EpsGraphics2D Transform with a rotation\r
+   * transform.\r
+   */\r
+  public void rotate(double theta)\r
+  {\r
+    rotate(theta, 0, 0);\r
+  }\r
+\r
+  /**\r
+   * Concatenates the current EpsGraphics2D Transform with a translated\r
+   * rotation transform.\r
+   */\r
+  public void rotate(double theta, double x, double y)\r
+  {\r
+    transform(AffineTransform.getRotateInstance(theta, x, y));\r
+  }\r
+\r
+  /**\r
+   * Concatenates the current EpsGraphics2D Transform with a scaling\r
+   * transformation.\r
+   */\r
+  public void scale(double sx, double sy)\r
+  {\r
+    transform(AffineTransform.getScaleInstance(sx, sy));\r
+  }\r
+\r
+  /**\r
+   * Concatenates the current EpsGraphics2D Transform with a shearing\r
+   * transform.\r
+   */\r
+  public void shear(double shx, double shy)\r
+  {\r
+    transform(AffineTransform.getShearInstance(shx, shy));\r
+  }\r
+\r
+  /**\r
+   * Composes an AffineTransform object with the Transform in this\r
+   * EpsGraphics2D according to the rule last-specified-first-applied.\r
+   */\r
+  public void transform(AffineTransform Tx)\r
+  {\r
+    _transform.concatenate(Tx);\r
+    setTransform(getTransform());\r
+  }\r
+\r
+  /**\r
+   * Sets the AffineTransform to be used by this EpsGraphics2D.\r
+   */\r
+  public void setTransform(AffineTransform Tx)\r
+  {\r
+    if (Tx == null)\r
+    {\r
+      _transform = new AffineTransform();\r
+    }\r
+    else\r
+    {\r
+      _transform = new AffineTransform(Tx);\r
+    }\r
+    // Need to update the stroke and font so they know the scale changed\r
+    setStroke(getStroke());\r
+    setFont(getFont());\r
+  }\r
+\r
+  /**\r
+   * Gets the AffineTransform used by this EpsGraphics2D.\r
+   */\r
+  public AffineTransform getTransform()\r
+  {\r
+    return new AffineTransform(_transform);\r
+  }\r
+\r
+  /**\r
+   * Returns the current Paint of the EpsGraphics2D object.\r
+   */\r
+  public Paint getPaint()\r
+  {\r
+    return _paint;\r
+  }\r
+\r
+  /**\r
+   * returns the current Composite of the EpsGraphics2D object.\r
+   */\r
+  public Composite getComposite()\r
+  {\r
+    return _composite;\r
+  }\r
+\r
+  /**\r
+   * Sets the background color to be used by the clearRect method.\r
+   */\r
+  public void setBackground(Color color)\r
+  {\r
+    if (color == null)\r
+    {\r
+      color = Color.black;\r
+    }\r
+    _backgroundColor = color;\r
+  }\r
+\r
+  /**\r
+   * Gets the background color that is used by the clearRect method.\r
+   */\r
+  public Color getBackground()\r
+  {\r
+    return _backgroundColor;\r
+  }\r
+\r
+  /**\r
+   * Returns the Stroke currently used.  Guaranteed to be an instance of\r
+   * BasicStroke.\r
+   */\r
+  public Stroke getStroke()\r
+  {\r
+    return _stroke;\r
+  }\r
+\r
+  /**\r
+   * Intersects the current clip with the interior of the specified Shape\r
+   * and sets the clip to the resulting intersection.\r
+   */\r
+  public void clip(Shape s)\r
+  {\r
+    if (_clip == null)\r
+    {\r
+      setClip(s);\r
+    }\r
+    else\r
+    {\r
+      Area area = new Area(_clip);\r
+      area.intersect(new Area(s));\r
+      setClip(area);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Returns the FontRenderContext.\r
+   */\r
+  public FontRenderContext getFontRenderContext()\r
+  {\r
+    return _fontRenderContext;\r
+  }\r
+\r
+  /////////////// Graphics methods ///////////////////////\r
+\r
+\r
+  /**\r
+   * Returns a new Graphics object that is identical to this EpsGraphics2D.\r
+   */\r
+  public Graphics create()\r
+  {\r
+    return new EpsGraphics2D(this);\r
+  }\r
+\r
+  /**\r
+   * Returns an EpsGraphics2D object based on this\r
+   * Graphics object, but with a new translation and clip\r
+   * area.\r
+   */\r
+  public Graphics create(int x, int y, int width, int height)\r
+  {\r
+    Graphics g = create();\r
+    g.translate(x, y);\r
+    g.clipRect(0, 0, width, height);\r
+    return g;\r
+  }\r
+\r
+  /**\r
+   * Returns the current Color.  This will be a default value (black)\r
+   * until it is changed using the setColor method.\r
+   */\r
+  public Color getColor()\r
+  {\r
+    return _color;\r
+  }\r
+\r
+  /**\r
+   * Sets the Color to be used when drawing all future shapes, text, etc.\r
+   */\r
+  public void setColor(Color c)\r
+  {\r
+    if (c == null)\r
+    {\r
+      c = Color.black;\r
+    }\r
+    _color = c;\r
+    append( (c.getRed() / 255f) + " " + (c.getGreen() / 255f) + " " +\r
+           (c.getBlue() / 255f) + " setrgbcolor");\r
+  }\r
+\r
+  /**\r
+   * Sets the paint mode of this EpsGraphics2D object to overwrite the\r
+   * destination EpsDocument with the current color.\r
+   */\r
+  public void setPaintMode()\r
+  {\r
+    // Do nothing - paint mode is the only method supported anyway.\r
+  }\r
+\r
+  /**\r
+   * <b><i><font color="red">Not implemented</font></i></b> - performs no action.\r
+   */\r
+  public void setXORMode(Color c1)\r
+  {\r
+    methodNotSupported();\r
+  }\r
+\r
+  /**\r
+   * Returns the Font currently being used.\r
+   */\r
+  public Font getFont()\r
+  {\r
+    return _font;\r
+  }\r
+\r
+  /**\r
+   * Sets the Font to be used in future text.\r
+   */\r
+  public void setFont(Font font)\r
+  {\r
+    if (font == null)\r
+    {\r
+      font = Font.decode(null);\r
+    }\r
+    _font = font;\r
+    append("/" + _font.getPSName() + " findfont " + ( (int) _font.getSize()) +\r
+           " scalefont setfont");\r
+  }\r
+\r
+  /**\r
+   * Gets the font metrics of the current font.\r
+   */\r
+  public FontMetrics getFontMetrics()\r
+  {\r
+    return getFontMetrics(getFont());\r
+  }\r
+\r
+  /**\r
+   * Gets the font metrics for the specified font.\r
+   */\r
+  public FontMetrics getFontMetrics(Font f)\r
+  {\r
+    BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);\r
+    Graphics g = image.getGraphics();\r
+    return g.getFontMetrics(f);\r
+  }\r
+\r
+  /**\r
+   * Returns the bounding rectangle of the current clipping area.\r
+   */\r
+  public Rectangle getClipBounds()\r
+  {\r
+    if (_clip == null)\r
+    {\r
+      return null;\r
+    }\r
+    Rectangle rect = getClip().getBounds();\r
+    return rect;\r
+  }\r
+\r
+  /**\r
+   * Intersects the current clip with the specified rectangle.\r
+   */\r
+  public void clipRect(int x, int y, int width, int height)\r
+  {\r
+    clip(new Rectangle(x, y, width, height));\r
+  }\r
+\r
+  /**\r
+   * Sets the current clip to the rectangle specified by the given\r
+   * coordinates.\r
+   */\r
+  public void setClip(int x, int y, int width, int height)\r
+  {\r
+    setClip(new Rectangle(x, y, width, height));\r
+  }\r
+\r
+  /**\r
+   * Gets the current clipping area.\r
+   */\r
+  public Shape getClip()\r
+  {\r
+    if (_clip == null)\r
+    {\r
+      return null;\r
+    }\r
+    else\r
+    {\r
+      try\r
+      {\r
+        AffineTransform t = _transform.createInverse();\r
+        t.concatenate(_clipTransform);\r
+        return t.createTransformedShape(_clip);\r
+      }\r
+      catch (Exception e)\r
+      {\r
+        throw new EpsException("Unable to get inverse of matrix: " + _transform);\r
+      }\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Sets the current clipping area to an arbitrary clip shape.\r
+   */\r
+  public void setClip(Shape clip)\r
+  {\r
+    if (clip != null)\r
+    {\r
+      if (_document.isClipSet())\r
+      {\r
+        append("grestore");\r
         append("gsave");\r
-\r
-        int width = sx2 - sx1;\r
-        int height = sy2 - sy1;\r
-        int destWidth = dx2 - dx1;\r
-        int destHeight = dy2 - dy1;\r
-\r
-        int[] pixels = new int[width * height];\r
-        PixelGrabber pg = new PixelGrabber(img, sx1, sy1, sx2 - sx1, sy2 - sy1, pixels, 0, width);\r
-        try {\r
-            pg.grabPixels();\r
-        }\r
-        catch(InterruptedException e) {\r
-            return false;\r
-        }\r
-\r
-        AffineTransform matrix = new AffineTransform(_transform);\r
-        matrix.translate(dx1, dy1);\r
-        matrix.scale(destWidth / (double) width, destHeight / (double) height);\r
-        double[] m = new double[6];\r
-        try {\r
-            matrix = matrix.createInverse();\r
-        }\r
-        catch (Exception e) {\r
-            throw new EpsException("Unable to get inverse of matrix: " + matrix);\r
-        }\r
-        matrix.scale(1, -1);\r
-        matrix.getMatrix(m);\r
-        append(width + " " + height + " 8 [" + m[0] + " " + m[1] + " " + m[2] + " " +  m[3] + " " + m[4] + " " + m[5] + "]");\r
-        // Fill the background to update the bounding box.\r
-        Color oldColor = getColor();\r
-        setColor(getBackground());\r
-        fillRect(dx1, dy1, destWidth, destHeight);\r
-        setColor(oldColor);\r
-        append("{currentfile 3 " + width + " mul string readhexstring pop} bind");\r
-        append("false 3 colorimage");\r
-        StringBuffer line = new StringBuffer();\r
-        for (int y = 0; y < height; y++) {\r
-            for (int x = 0; x < width; x++) {\r
-                Color color = new Color(pixels[x + width * y]);\r
-                line.append(toHexString(color.getRed()) + toHexString(color.getGreen()) + toHexString(color.getBlue()));\r
-                if (line.length() > 64) {\r
-                    append(line.toString());\r
-                    line = new StringBuffer();\r
-                }\r
-            }\r
-        }\r
-        if (line.length() > 0) {\r
-            append(line.toString());\r
-        }\r
-\r
+      }\r
+      else\r
+      {\r
+        _document.setClipSet(true);\r
+        append("gsave");\r
+      }\r
+      draw(clip, "clip");\r
+      _clip = clip;\r
+      _clipTransform = (AffineTransform) _transform.clone();\r
+    }\r
+    else\r
+    {\r
+      if (_document.isClipSet())\r
+      {\r
         append("grestore");\r
-\r
-        return true;\r
-    }\r
-\r
-\r
-    /**\r
-     * Disposes of all resources used by this EpsGraphics2D object.\r
-     * If this is the only remaining EpsGraphics2D instance pointing at\r
-     * a EpsDocument object, then the EpsDocument object shall become\r
-     * eligible for garbage collection.\r
-     */\r
-    public void dispose() {\r
-        _document = null;\r
-    }\r
-\r
-\r
-    /**\r
-     * Finalizes the object.\r
-     */\r
-    public void finalize() {\r
-        super.finalize();\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the entire contents of the EPS document, complete with\r
-     * headers and bounding box.  The returned String is suitable for\r
-     * being written directly to disk as an EPS file.\r
-     */\r
-    public String toString() {\r
-        StringWriter writer = new StringWriter();\r
-        try {\r
-            _document.write(writer);\r
-            _document.flush();\r
-            _document.close();\r
-        }\r
-        catch (IOException e) {\r
-            throw new EpsException(e.toString());\r
-        }\r
-        return writer.toString();\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns true if the specified rectangular area might intersect the\r
-     * current clipping area.\r
-     */\r
-    public boolean hitClip(int x, int y, int width, int height) {\r
-        if (_clip == null) {\r
-            return true;\r
-        }\r
-        Rectangle rect = new Rectangle(x, y, width, height);\r
-        return hit(rect, _clip, true);\r
-    }\r
-\r
-\r
-    /**\r
-     * Returns the bounding rectangle of the current clipping area.\r
-     */\r
-    public Rectangle getClipBounds(Rectangle r) {\r
-        if (_clip == null) {\r
-            return r;\r
+        _document.setClipSet(false);\r
+      }\r
+      _clip = null;\r
+    }\r
+  }\r
+\r
+  /**\r
+   * <b><i><font color="red">Not implemented</font></i></b> - performs no action.\r
+   */\r
+  public void copyArea(int x, int y, int width, int height, int dx, int dy)\r
+  {\r
+    methodNotSupported();\r
+  }\r
+\r
+  /**\r
+   * Draws a straight line from (x1,y1) to (x2,y2).\r
+   */\r
+  public void drawLine(int x1, int y1, int x2, int y2)\r
+  {\r
+    Shape shape = new Line2D.Float(x1, y1, x2, y2);\r
+    draw(shape);\r
+  }\r
+\r
+  /**\r
+   * Fills a rectangle with top-left corner placed at (x,y).\r
+   */\r
+  public void fillRect(int x, int y, int width, int height)\r
+  {\r
+    Shape shape = new Rectangle(x, y, width, height);\r
+    draw(shape, "fill");\r
+  }\r
+\r
+  /**\r
+   * Draws a rectangle with top-left corner placed at (x,y).\r
+   */\r
+  public void drawRect(int x, int y, int width, int height)\r
+  {\r
+    Shape shape = new Rectangle(x, y, width, height);\r
+    draw(shape);\r
+  }\r
+\r
+  /**\r
+   * Clears a rectangle with top-left corner placed at (x,y) using the\r
+   * current background color.\r
+   */\r
+  public void clearRect(int x, int y, int width, int height)\r
+  {\r
+    Color originalColor = getColor();\r
+\r
+    setColor(getBackground());\r
+    Shape shape = new Rectangle(x, y, width, height);\r
+    draw(shape, "fill");\r
+\r
+    setColor(originalColor);\r
+  }\r
+\r
+  /**\r
+   * Draws a rounded rectangle.\r
+   */\r
+  public void drawRoundRect(int x, int y, int width, int height, int arcWidth,\r
+                            int arcHeight)\r
+  {\r
+    Shape shape = new RoundRectangle2D.Float(x, y, width, height, arcWidth,\r
+                                             arcHeight);\r
+    draw(shape);\r
+  }\r
+\r
+  /**\r
+   * Fills a rounded rectangle.\r
+   */\r
+  public void fillRoundRect(int x, int y, int width, int height, int arcWidth,\r
+                            int arcHeight)\r
+  {\r
+    Shape shape = new RoundRectangle2D.Float(x, y, width, height, arcWidth,\r
+                                             arcHeight);\r
+    draw(shape, "fill");\r
+  }\r
+\r
+  /**\r
+   * Draws an oval.\r
+   */\r
+  public void drawOval(int x, int y, int width, int height)\r
+  {\r
+    Shape shape = new Ellipse2D.Float(x, y, width, height);\r
+    draw(shape);\r
+  }\r
+\r
+  /**\r
+   * Fills an oval.\r
+   */\r
+  public void fillOval(int x, int y, int width, int height)\r
+  {\r
+    Shape shape = new Ellipse2D.Float(x, y, width, height);\r
+    draw(shape, "fill");\r
+  }\r
+\r
+  /**\r
+   * Draws an arc.\r
+   */\r
+  public void drawArc(int x, int y, int width, int height, int startAngle,\r
+                      int arcAngle)\r
+  {\r
+    Shape shape = new Arc2D.Float(x, y, width, height, startAngle, arcAngle,\r
+                                  Arc2D.OPEN);\r
+    draw(shape);\r
+  }\r
+\r
+  /**\r
+   * Fills an arc.\r
+   */\r
+  public void fillArc(int x, int y, int width, int height, int startAngle,\r
+                      int arcAngle)\r
+  {\r
+    Shape shape = new Arc2D.Float(x, y, width, height, startAngle, arcAngle,\r
+                                  Arc2D.PIE);\r
+    draw(shape, "fill");\r
+  }\r
+\r
+  /**\r
+   * Draws a polyline.\r
+   */\r
+  public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)\r
+  {\r
+    if (nPoints > 0)\r
+    {\r
+      GeneralPath path = new GeneralPath();\r
+      path.moveTo(xPoints[0], yPoints[0]);\r
+      for (int i = 1; i < nPoints; i++)\r
+      {\r
+        path.lineTo(xPoints[i], yPoints[i]);\r
+      }\r
+      draw(path);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Draws a polygon made with the specified points.\r
+   */\r
+  public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)\r
+  {\r
+    Shape shape = new Polygon(xPoints, yPoints, nPoints);\r
+    draw(shape);\r
+  }\r
+\r
+  /**\r
+   * Draws a polygon.\r
+   */\r
+  public void drawPolygon(Polygon p)\r
+  {\r
+    draw(p);\r
+  }\r
+\r
+  /**\r
+   * Fills a polygon made with the specified points.\r
+   */\r
+  public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)\r
+  {\r
+    Shape shape = new Polygon(xPoints, yPoints, nPoints);\r
+    draw(shape, "fill");\r
+  }\r
+\r
+  /**\r
+   * Fills a polygon.\r
+   */\r
+  public void fillPolygon(Polygon p)\r
+  {\r
+    draw(p, "fill");\r
+  }\r
+\r
+  /**\r
+   * Draws the specified characters, starting from (x,y)\r
+   */\r
+  public void drawChars(char[] data, int offset, int length, int x, int y)\r
+  {\r
+    String string = new String(data, offset, length);\r
+    drawString(string, x, y);\r
+  }\r
+\r
+  /**\r
+   * Draws the specified bytes, starting from (x,y)\r
+   */\r
+  public void drawBytes(byte[] data, int offset, int length, int x, int y)\r
+  {\r
+    String string = new String(data, offset, length);\r
+    drawString(string, x, y);\r
+  }\r
+\r
+  /**\r
+   * Draws an image.\r
+   */\r
+  public boolean drawImage(Image img, int x, int y, ImageObserver observer)\r
+  {\r
+    return drawImage(img, x, y, Color.white, observer);\r
+  }\r
+\r
+  /**\r
+   * Draws an image.\r
+   */\r
+  public boolean drawImage(Image img, int x, int y, int width, int height,\r
+                           ImageObserver observer)\r
+  {\r
+    return drawImage(img, x, y, width, height, Color.white, observer);\r
+  }\r
+\r
+  /**\r
+   * Draws an image.\r
+   */\r
+  public boolean drawImage(Image img, int x, int y, Color bgcolor,\r
+                           ImageObserver observer)\r
+  {\r
+    int width = img.getWidth(null);\r
+    int height = img.getHeight(null);\r
+    return drawImage(img, x, y, width, height, bgcolor, observer);\r
+  }\r
+\r
+  /**\r
+   * Draws an image.\r
+   */\r
+  public boolean drawImage(Image img, int x, int y, int width, int height,\r
+                           Color bgcolor, ImageObserver observer)\r
+  {\r
+    return drawImage(img, x, y, x + width, y + height, 0, 0, width, height,\r
+                     bgcolor, observer);\r
+  }\r
+\r
+  /**\r
+   * Draws an image.\r
+   */\r
+  public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,\r
+                           int sx1, int sy1, int sx2, int sy2,\r
+                           ImageObserver observer)\r
+  {\r
+    return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, Color.white,\r
+                     observer);\r
+  }\r
+\r
+  /**\r
+   * Draws an image.\r
+   */\r
+  public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,\r
+                           int sx1, int sy1, int sx2, int sy2, Color bgcolor,\r
+                           ImageObserver observer)\r
+  {\r
+    if (dx1 >= dx2)\r
+    {\r
+      throw new IllegalArgumentException("dx1 >= dx2");\r
+    }\r
+    if (sx1 >= sx2)\r
+    {\r
+      throw new IllegalArgumentException("sx1 >= sx2");\r
+    }\r
+    if (dy1 >= dy2)\r
+    {\r
+      throw new IllegalArgumentException("dy1 >= dy2");\r
+    }\r
+    if (sy1 >= sy2)\r
+    {\r
+      throw new IllegalArgumentException("sy1 >= sy2");\r
+    }\r
+\r
+    append("gsave");\r
+\r
+    int width = sx2 - sx1;\r
+    int height = sy2 - sy1;\r
+    int destWidth = dx2 - dx1;\r
+    int destHeight = dy2 - dy1;\r
+\r
+    int[] pixels = new int[width * height];\r
+    PixelGrabber pg = new PixelGrabber(img, sx1, sy1, sx2 - sx1, sy2 - sy1,\r
+                                       pixels, 0, width);\r
+    try\r
+    {\r
+      pg.grabPixels();\r
+    }\r
+    catch (InterruptedException e)\r
+    {\r
+      return false;\r
+    }\r
+\r
+    AffineTransform matrix = new AffineTransform(_transform);\r
+    matrix.translate(dx1, dy1);\r
+    matrix.scale(destWidth / (double) width, destHeight / (double) height);\r
+    double[] m = new double[6];\r
+    try\r
+    {\r
+      matrix = matrix.createInverse();\r
+    }\r
+    catch (Exception e)\r
+    {\r
+      throw new EpsException("Unable to get inverse of matrix: " + matrix);\r
+    }\r
+    matrix.scale(1, -1);\r
+    matrix.getMatrix(m);\r
+    append(width + " " + height + " 8 [" + m[0] + " " + m[1] + " " + m[2] + " " +\r
+           m[3] + " " + m[4] + " " + m[5] + "]");\r
+    // Fill the background to update the bounding box.\r
+    Color oldColor = getColor();\r
+    setColor(getBackground());\r
+    fillRect(dx1, dy1, destWidth, destHeight);\r
+    setColor(oldColor);\r
+    append("{currentfile 3 " + width + " mul string readhexstring pop} bind");\r
+    append("false 3 colorimage");\r
+    StringBuffer line = new StringBuffer();\r
+    for (int y = 0; y < height; y++)\r
+    {\r
+      for (int x = 0; x < width; x++)\r
+      {\r
+        Color color = new Color(pixels[x + width * y]);\r
+        line.append(toHexString(color.getRed()) + toHexString(color.getGreen()) +\r
+                    toHexString(color.getBlue()));\r
+        if (line.length() > 64)\r
+        {\r
+          append(line.toString());\r
+          line = new StringBuffer();\r
         }\r
-        Rectangle rect = getClipBounds();\r
-        r.setLocation((int) rect.getX(), (int) rect.getY());\r
-        r.setSize((int) rect.getWidth(), (int) rect.getHeight());\r
-        return r;\r
-    }\r
-\r
-\r
-    private Color _color;\r
-    private Color _backgroundColor;\r
-    private Paint _paint;\r
-    private Composite _composite;\r
-    private BasicStroke _stroke;\r
-    private Font _font;\r
-    private Shape _clip;\r
-    private AffineTransform _clipTransform;\r
-    private AffineTransform _transform;\r
-    private boolean _accurateTextMode;\r
-\r
-    private EpsDocument _document;\r
-\r
-    private static FontRenderContext _fontRenderContext = new FontRenderContext(null, false, true);\r
+      }\r
+    }\r
+    if (line.length() > 0)\r
+    {\r
+      append(line.toString());\r
+    }\r
+\r
+    append("grestore");\r
+\r
+    return true;\r
+  }\r
+\r
+  /**\r
+   * Disposes of all resources used by this EpsGraphics2D object.\r
+   * If this is the only remaining EpsGraphics2D instance pointing at\r
+   * a EpsDocument object, then the EpsDocument object shall become\r
+   * eligible for garbage collection.\r
+   */\r
+  public void dispose()\r
+  {\r
+    _document = null;\r
+  }\r
+\r
+  /**\r
+   * Finalizes the object.\r
+   */\r
+  public void finalize()\r
+  {\r
+    super.finalize();\r
+  }\r
+\r
+  /**\r
+   * Returns the entire contents of the EPS document, complete with\r
+   * headers and bounding box.  The returned String is suitable for\r
+   * being written directly to disk as an EPS file.\r
+   */\r
+  public String toString()\r
+  {\r
+    StringWriter writer = new StringWriter();\r
+    try\r
+    {\r
+      _document.write(writer);\r
+      _document.flush();\r
+      _document.close();\r
+    }\r
+    catch (IOException e)\r
+    {\r
+      throw new EpsException(e.toString());\r
+    }\r
+    return writer.toString();\r
+  }\r
+\r
+  /**\r
+   * Returns true if the specified rectangular area might intersect the\r
+   * current clipping area.\r
+   */\r
+  public boolean hitClip(int x, int y, int width, int height)\r
+  {\r
+    if (_clip == null)\r
+    {\r
+      return true;\r
+    }\r
+    Rectangle rect = new Rectangle(x, y, width, height);\r
+    return hit(rect, _clip, true);\r
+  }\r
+\r
+  /**\r
+   * Returns the bounding rectangle of the current clipping area.\r
+   */\r
+  public Rectangle getClipBounds(Rectangle r)\r
+  {\r
+    if (_clip == null)\r
+    {\r
+      return r;\r
+    }\r
+    Rectangle rect = getClipBounds();\r
+    r.setLocation( (int) rect.getX(), (int) rect.getY());\r
+    r.setSize( (int) rect.getWidth(), (int) rect.getHeight());\r
+    return r;\r
+  }\r
+\r
+  private Color _color;\r
+  private Color _backgroundColor;\r
+  private Paint _paint;\r
+  private Composite _composite;\r
+  private BasicStroke _stroke;\r
+  private Font _font;\r
+  private Shape _clip;\r
+  private AffineTransform _clipTransform;\r
+  private AffineTransform _transform;\r
+  private boolean _accurateTextMode;\r
+\r
+  private EpsDocument _document;\r
+\r
+  private static FontRenderContext _fontRenderContext = new FontRenderContext(null, false, true);\r
 }
\ No newline at end of file