// Copyright 2000-2003, FreeHEP
package org.freehep.graphicsio;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.image.renderable.*;
import java.io.*;
import java.text.*;
import java.util.*;
import org.freehep.graphics2d.GenericTagHandler;
import org.freehep.graphics2d.TagString;
import org.freehep.util.UserProperties;
import org.freehep.util.images.ImageUtilities;
/**
* This class provides an abstract VectorGraphicsIO class for specific output drivers.
*
* @author Charles Loomis
* @author Mark Donszelmann
* @version $Id: AbstractVectorGraphicsIO.java,v 1.14 2003/12/01 16:54:17 duns Exp $
*/
public abstract class AbstractVectorGraphicsIO
extends VectorGraphicsIO {
private static final String rootKey = AbstractVectorGraphicsIO.class.getName();
public static final String EMIT_WARNINGS = rootKey+".EMIT_WARNINGS";
public static final String EMIT_ERRORS = rootKey+".EMIT_ERRORS";
/*================================================================================
* Table of Contents:
* ------------------
* 1. Constructors & Factory Methods
* 2. Document Settings
* 3. Header, Trailer, Multipage & Comments
* 3.1 Header & Trailer
* 3.2 MultipageDocument methods
* 4. Create & Dispose
* 5. Drawing Methods
* 5.1. shapes (draw/fill)
* 5.1.1. lines, rectangles, round rectangles
* 5.1.2. polylines, polygons
* 5.1.3. ovals, arcs
* 5.1.4. shapes
* 5.2. Images
* 5.3. Strings
* 6. Transformations
* 7. Clipping
* 8. Graphics State / Settings
* 8.1. stroke/linewidth
* 8.2. paint/color
* 8.3. font
* 8.4. rendering hints
* 9. Auxiliary
* 10. Private/Utility Methods
*================================================================================*/
private Dimension size;
private Component component;
private boolean doRestoreOnDispose;
private Rectangle deviceClip;
private Area userClip;
private AffineTransform currentTransform;
private Composite currentComposite;
private Stroke currentStroke;
private RenderingHints hints;
/*================================================================================
* 1. Constructors & Factory Methods
*================================================================================*/
/**
* Constructs a Graphics context with the following graphics state:
*
* - Paint: black
*
- Font: Dailog, Plain, 12pt
*
- Stroke: Linewidth 1.0; No Dashing; Miter Join Style; Miter Limit 10; Square Endcaps;
*
- Transform: Identity
*
- Composite: AlphaComposite.SRC_OVER
*
- Clip: Rectangle(0, 0, size.width, size.height)
*
*
* @param size rectangle specifying the bounds of the image
* @param doRestoreOnDispose true if writeGraphicsRestore() should be called when
* this graphics context is disposed of.
*/
protected AbstractVectorGraphicsIO(Dimension size, boolean doRestoreOnDispose) {
super();
this.size = size;
this.component = null;
this.doRestoreOnDispose = doRestoreOnDispose;
deviceClip = (size != null ? new Rectangle(0, 0, size.width, size.height) : null);
userClip = null;
currentTransform = new AffineTransform();
currentComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
currentStroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, null, 0.0f);
super.setColor(Color.BLACK);
super.setBackground(Color.BLACK);
super.setFont(new Font("Dialog", Font.PLAIN, 12));
// Initialize the rendering hints.
hints = new RenderingHints(null);
}
/**
* Constructs a Graphics context with the following graphics state:
*
* - Paint: The color of the component.
*
- Font: The font of the component.
*
- Stroke: Linewidth 1.0; No Dashing; Miter Join Style; Miter Limit 10; Square Endcaps;
*
- Transform: The getDefaultTransform for the GraphicsConfiguration of the component.
*
- Composite: AlphaComposite.SRC_OVER
*
- Clip: The size of the component, Rectangle(0, 0, size.width, size.height)
*
*
* @param component to be used to initialize the values of the graphics state
* @param doRestoreOnDispose true if writeGraphicsRestore() should be called when
* this graphics context is disposed of.
*/
protected AbstractVectorGraphicsIO(Component component, boolean doRestoreOnDispose) {
super();
this.size = component.getSize();
this.component = component;
this.doRestoreOnDispose = doRestoreOnDispose;
deviceClip = (size != null ? new Rectangle(0, 0, size.width, size.height) : null);
userClip = null;
GraphicsConfiguration gc = component.getGraphicsConfiguration();
currentTransform = (gc != null) ? gc.getDefaultTransform() : new AffineTransform();
currentComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
currentStroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, null, 0.0f);
super.setFont(component.getFont());
super.setBackground(component.getBackground());
super.setColor(component.getForeground());
// Initialize the rendering hints.
hints = new RenderingHints(null);
}
/**
* Constructs a subgraphics context.
*
* @param graphics context to clone from
* @param doRestoreOnDispose true if writeGraphicsRestore() should be called when
* this graphics context is disposed of.
*/
protected AbstractVectorGraphicsIO(AbstractVectorGraphicsIO graphics, boolean doRestoreOnDispose) {
super(graphics);
this.doRestoreOnDispose = doRestoreOnDispose;
size = new Dimension(graphics.size);
component = graphics.component;
deviceClip = new Rectangle(graphics.deviceClip);
userClip = (graphics.userClip != null) ? new Area(graphics.userClip) : null;
currentTransform = new AffineTransform(graphics.currentTransform);
currentComposite = graphics.currentComposite;
currentStroke = graphics.currentStroke;
hints = graphics.hints;
}
/*================================================================================
| 2. Document Settings
*================================================================================*/
public Dimension getSize() {
return size;
}
public Component getComponent() {
return component;
}
/*================================================================================
| 3. Header, Trailer, Multipage & Comments
*================================================================================*/
/* 3.1 Header & Trailer */
public void startExport() {
try {
writeHeader();
// delegate this to openPage if it is a MultiPage document
if (!(this instanceof MultiPageDocument)) {
writeGraphicsState();
writeBackground();
}
} catch (IOException e) {
handleException(e);
}
}
public void endExport() {
try {
dispose();
writeTrailer();
closeStream();
} catch (IOException e) {
handleException(e);
}
}
/**
* Called to write the header part of the output.
*/
public abstract void writeHeader() throws IOException;
/**
* Called to write the initial graphics state.
*/
public void writeGraphicsState() throws IOException {
Color color = super.getColor();
super.setColor(null);
setColor(color);
Shape clip = userClip;
setClip(clip);
AffineTransform transform = currentTransform;
currentTransform = new AffineTransform();
setTransform(transform);
Font font = getFont();
super.setFont(null);
setFont(font);
Composite composite = currentComposite;
currentComposite = null;
setComposite(composite);
Stroke stroke = currentStroke;
currentStroke = null;
setStroke(stroke);
}
public abstract void writeBackground() throws IOException;
/**
* Called to write the trailing part of the output.
*/
public abstract void writeTrailer() throws IOException;
/**
* Called to close the stream you are writing to.
*/
public abstract void closeStream() throws IOException;
public void printComment(String comment) {
try {
writeComment(comment);
} catch (IOException e) {
handleException(e);
}
}
/**
* Called to Write out a comment.
*
* @param comment to be written
*/
public abstract void writeComment(String comment) throws IOException;
/* 3.2 MultipageDocument methods */
protected void resetClip(Rectangle clip) {
deviceClip = clip;
userClip = null;
}
/*================================================================================
* 4. Create & Dispose
*================================================================================*/
/**
* Disposes of the graphics context. If on creation restoreOnDispose was true,
* writeGraphicsRestore() will be called.
*/
public void dispose() {
try {
// Swing sometimes calls dispose several times for a given
// graphics object. Ensure that the grestore is only written
// once if this happens.
if (doRestoreOnDispose) {
writeGraphicsRestore();
doRestoreOnDispose = false;
}
} catch (IOException e) {
handleException(e);
}
}
/**
* Writes out the save of a graphics context for a later restore.
* Some implementations keep track of this by hand if the output
* format does not support it.
*/
protected abstract void writeGraphicsSave() throws IOException;
/**
* Writes out the restore of a graphics context.
* Some implementations keep track of this by hand if the output
* format does not support it.
*/
protected abstract void writeGraphicsRestore() throws IOException;
/*================================================================================
| 5. Drawing Methods
*================================================================================*/
/* 5.3. Images */
public boolean drawImage(Image image, int x, int y, ImageObserver observer) {
int imageWidth = image.getWidth(observer);
int imageHeight = image.getHeight(observer);
return drawImage(image, x, y, x+imageWidth, y+imageHeight,
0, 0, imageWidth, imageHeight, null, observer);
}
public boolean drawImage(Image image, int x, int y, int width, int height,
ImageObserver observer) {
int imageWidth = image.getWidth(observer);
int imageHeight = image.getHeight(observer);
return drawImage(image, x, y, x+width, y+height,
0, 0, imageWidth, imageHeight, null, observer);
}
public boolean drawImage(Image image, int x, int y, int width, int height,
Color bgColor, ImageObserver observer) {
int imageWidth = image.getWidth(observer);
int imageHeight = image.getHeight(observer);
return drawImage(image, x, y, x+width, y+height,
0, 0, imageWidth, imageHeight, bgColor, observer);
}
public boolean drawImage(Image image, int x, int y, Color bgColor,
ImageObserver observer) {
int imageWidth = image.getWidth(observer);
int imageHeight = image.getHeight(observer);
return drawImage(image, x, y, x+imageWidth, y+imageHeight,
0, 0, imageWidth, imageHeight, bgColor, observer);
}
public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
int sx1, int sy1, int sx2, int sy2,
ImageObserver observer) {
return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, observer);
}
public boolean drawImage(Image image, AffineTransform xform, ImageObserver observer) {
drawRenderedImage(ImageUtilities.createRenderedImage(image, observer, null), xform);
return true;
}
public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
drawImage(op.filter(img, null), x, y, null);
}
// NOTE: not tested yet!!!
public void drawRenderableImage(RenderableImage image, AffineTransform xform) {
drawRenderedImage(image.createRendering(new RenderContext(new AffineTransform(), getRenderingHints())),
xform);
}
/**
* Draw and resizes (transparent) image. Calls writeImage(...).
*
* @param image image to be drawn
* @param dx1, dy1, dx2, dy2 destination image bounds
* @param sx1, sy1, sx2, sy2 source image bounds
* @param bgColor background color
* @param observer for updates if image still incomplete
* @return true if successful
*/
public boolean drawImage(Image image,
int dx1, int dy1, int dx2, int dy2,
int sx1, int sy1, int sx2, int sy2,
Color bgColor, ImageObserver observer) {
try {
int srcX = Math.min(sx1, sx2);
int srcY = Math.min(sy1, sy2);
int srcWidth = Math.abs(sx2 - sx1);
int srcHeight = Math.abs(sy2 - sy1);
int width = Math.abs(dx2 - dx1);
int height = Math.abs(dy2 - dy1);
if ((srcX != 0) || (srcY != 0) ||
(srcWidth != image.getWidth(observer)) ||
(srcHeight != image.getHeight(observer))) {
// crop the source image
ImageFilter crop = new CropImageFilter(srcX, srcY, srcWidth, srcHeight);
image = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), crop));
MediaTracker mediaTracker = new MediaTracker(new Panel());
mediaTracker.addImage(image, 0);
try {
mediaTracker.waitForAll();
} catch (InterruptedException e) {
handleException(e);
}
}
boolean flipHorizontal = (dx2 source flipped XOR dest flipped
double tx = (flipHorizontal) ? (double)dx2 : (double)dx1;
double ty = (flipVertical) ? (double)dy2 : (double)dy1;
double sx = (double)width/srcWidth;
sx = flipHorizontal ? -1*sx : sx;
double sy = (double)height/srcHeight;
sy = flipVertical ? -1*sy : sy;
writeImage(ImageUtilities.createRenderedImage(image, observer, bgColor),
new AffineTransform(sx, 0, 0, sy, tx, ty),
bgColor);
return true;
} catch (IOException e) {
return false;
}
}
/*
// first use the original orientation
int clippingWidth = Math.abs(sx2 - sx1);
int clippingHeight = Math.abs(sy2 - sy1);
int sulX = Math.min(sx1, sx2);
int sulY = Math.min(sy1, sy2);
Image background = null;
if (bgColor != null) {
// Draw the image on the background color
// maybe we could crop it and fill the transparent pixels in one go
// by means of a filter.
background = new BufferedImage(clippingWidth, clippingHeight,
BufferedImage.TYPE_INT_ARGB);
Graphics bgGfx = background.getGraphics();
bgGfx.drawImage(image, 0, 0, clippingWidth, clippingWidth,
sulX, sulY, sulX+clippingWidth, sulY+clippingHeight,
getPrintColor(bgColor), observer);
} else {
// crop the source image
ImageFilter crop = new CropImageFilter(sulX, sulY, clippingWidth, clippingHeight);
background = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), crop));
MediaTracker mediaTracker = new MediaTracker(new Panel());
mediaTracker.addImage(background, 0);
try {
mediaTracker.waitForAll();
} catch (InterruptedException e) {
handleException(e);
}
}
// now flip the image if necessary
boolean flipHorizontal = (dx2 source flipped XOR dest flipped
int destWidth = Math.abs(dx2-dx1);
int destHeight = Math.abs(dy2-dy1);
try {
return writeImage(background,
flipHorizontal ? dx2 : dx1,
flipVertical ? dy2 : dy1,
flipHorizontal ? -destWidth : destWidth,
flipVertical ? -destHeight : destHeight,
(bgColor == null),
observer);
} catch (IOException e) {
return false;
}
}
*/
/**
* Draws a rendered image using a transform.
*
* @param image to be drawn
* @param transform transform to be used on the image
*/
public void drawRenderedImage(RenderedImage image, AffineTransform xform) {
try {
writeImage(image, xform, null);
} catch (Exception e) {
handleException(e);
}
}
protected abstract void writeImage(RenderedImage image, AffineTransform xform, Color bkg) throws IOException;
/**
* Clears rectangle by painting it with the backgroundColor.
*
* @param x, y, width, height rectangle to be cleared.
*/
public void clearRect(double x, double y, double width, double height) {
Paint temp = getPaint();
setPaint(getBackground());
fillRect(x, y, width, height);
setPaint(temp);
}
public void drawString(String string, double x, double y) {
try {
writeString(string, x, y);
} catch (IOException e) {
handleException(e);
}
}
// FIXME, maybe can move up more
public void drawString(String str, double x, double y, int horizontal, int vertical,
boolean framed, Color frameColor, double frameWidth,
boolean banner, Color bannerColor) {
try {
LineMetrics metrics = getFont().getLineMetrics(str, getFontRenderContext());
double width = getFont().getStringBounds(str, getFontRenderContext()).getWidth();
double height = metrics.getHeight();
double descent = metrics.getDescent();
Rectangle2D textSize = new Rectangle2D.Double(0, descent-height, width, height);
double adjustment = (getFont().getSize2D()*2)/10;
Point2D textUL = drawFrameAndBanner(x, y, textSize, adjustment,
framed, frameColor, frameWidth,
banner, bannerColor,
horizontal, vertical);
drawString(str, textUL.getX(), textUL.getY());
} catch (IOException e) {
handleException(e);
}
}
// FIXME, maybe can move up more
public void drawString(TagString str, double x, double y, int horizontal, int vertical,
boolean framed, Color frameColor, double frameWidth,
boolean banner, Color bannerColor) {
try {
GenericTagHandler tagHandler = new GenericTagHandler(this);
Rectangle2D r = tagHandler.stringSize(str);
double adjustment = (getFont().getSize2D()*2)/10;
Point2D textUL = drawFrameAndBanner(x, y, r, adjustment,
framed, frameColor, frameWidth,
banner, bannerColor,
horizontal, vertical);
tagHandler.print(str, textUL.getX(), textUL.getY());
} catch (IOException e) {
handleException(e);
}
}
protected abstract void writeString(String string, double x, double y) throws IOException;
/*================================================================================
| 6. Transformations
*================================================================================*/
/**
* Get the current transform.
*
* @return current transform
*/
public AffineTransform getTransform() {
return new AffineTransform(currentTransform);
}
/**
* Set the current transform. Since most output formats do not
* implement this functionality, the inverse transform of the
* currentTransform is calculated and multiplied by the
* transform to be set. The result is then forwarded by a call
* to writeTransform(Transform).
*
* @param transform to be set
*/
public void setTransform(AffineTransform transform) {
try {
AffineTransform difference = currentTransform.createInverse();
difference.concatenate(transform);
writeTransform(difference);
} catch (NoninvertibleTransformException e) {
writeWarning("VectorGraphics2D.setTransform(AffineTransform): current transformation matrix not invertible");
} catch (IOException e) {
handleException(e);
}
currentTransform = transform;
}
/**
* Transforms the current transform. Calls writeTransform(Transform)
*
* @param transform to be applied
*/
public void transform(AffineTransform transform) {
currentTransform.concatenate(transform);
try {
writeTransform(transform);
} catch (IOException e) {
handleException(e);
}
}
/**
* Translates the current transform. Calls writeTransform(Transform)
*
* @param x, y amount by which to translate
*/
public void translate(double x, double y) {
currentTransform.translate(x, y);
try {
writeTransform(new AffineTransform(1, 0, 0, 1, x, y));
} catch (IOException e) {
handleException(e);
}
}
/**
* Rotate the current transform over the Z-axis. Calls writeTransform(Transform).
* Rotating with a positive angle theta rotates points on the positive x axis
* toward the positive y axis.
*
* @param theta radians over which to rotate
*/
public void rotate(double theta) {
currentTransform.rotate(theta);
try {
writeTransform(new AffineTransform(Math.cos(theta), Math.sin(theta),
-Math.sin(theta), Math.cos(theta),
0, 0));
} catch (IOException e) {
handleException(e);
}
}
/**
* Scales the current transform. Calls writeTransform(Transform).
*
* @param sx, sy amount used for scaling
*/
public void scale(double sx, double sy) {
currentTransform.scale(sx, sy);
try {
writeTransform(new AffineTransform(sx, 0,
0, sy,
0, 0));
} catch (IOException e) {
handleException(e);
}
}
/**
* Shears the current transform. Calls writeTransform(Transform).
*
* @param shx, shy amount for shearing
*/
public void shear(double shx, double shy) {
currentTransform.shear(shx, shy);
try {
writeTransform(new AffineTransform(1, shy,
shx, 1,
0, 0));
} catch (IOException e) {
handleException(e);
}
}
/**
* Writes out the transform as it needs to be concatenated to the
* internal transform of the output format. If there is no implementation
* of an internal transform, then this method needs to do nothing, BUT
* all coordinates need to be transformed by the currentTransform before
* being written out.
*
* @param transform to be written
*/
protected abstract void writeTransform(AffineTransform transform) throws IOException;
/*================================================================================
| 7. Clipping
*================================================================================*/
/**
* Gets the current clip in form of a Shape (Rectangle).
*
* @return current clip
*/
public Shape getClip() {
return (userClip != null) ? new Area(untransformShape(userClip)) : null;
}
/**
* Gets the current clip in form of a Rectangle.
*
* @return current clip
*/
public Rectangle getClipBounds() {
Shape clip = getClip();
return (clip != null) ? getClip().getBounds() : null;
}
/**
* Gets the current clip in form of a Rectangle.
*
* @return current clip
*/
public Rectangle getClipBounds(Rectangle r) {
Rectangle bounds = getClipBounds();
if (bounds != null) r.setBounds(bounds);
return r;
}
/**
* Clips rectangle. Calls clip(Rectangle).
*
* @param x, y, width, height rectangle for clipping
*/
public void clipRect(int x, int y, int width, int height) {
clip(new Rectangle(x, y, width, height));
}
/**
* Clips rectangle. Calls clip(Rectangle2D).
*
* @param x, y, width, height rectangle for clipping
*/
public void clipRect(double x, double y, double width, double height) {
clip(new Rectangle2D.Double(x, y, width, height));
}
/**
* Clips rectangle. Calls clip(Rectangle).
*
* @param x, y, width, height rectangle for clipping
*/
public void setClip(int x, int y, int width, int height) {
setClip(new Rectangle(x, y, width, height));
}
/**
* Clips rectangle. Calls clip(Rectangle2D).
*
* @param x, y, width, height rectangle for clipping
*/
public void setClip(double x, double y, double width, double height) {
setClip(new Rectangle2D.Double(x, y, width, height));
}
/**
* Clips shape. Sets the userClip to shape and calls clip(Shape).
*
* @param shape used for clipping
*/
public void setClip(Shape shape) {
userClip = (shape != null) ? new Area(transformShape(shape)) : null;
try {
writeSetClip(shape);
} catch (IOException e) {
handleException(e);
}
}
/**
* Called to set a clip, no intersection made.
* Calls clip(shape).
*/
protected void writeSetClip(Shape shape) throws IOException {
clip(shape);
}
/**
* Clips using given shape. Dispatches to writeClip(Rectangle),
* writeClip(Rectangle2D) or writeClip(Shape).
*
* @param shape used for clipping
*/
public void clip(Shape s) {
Shape ts = transformShape(s);
if (userClip != null) {
if (ts != null) {
userClip.intersect(new Area(ts));
} else {
userClip = null;
}
} else {
userClip = (ts != null) ? new Area(ts) : null;
}
try {
if (s instanceof Rectangle) {
writeClip((Rectangle)s);
} else if (s instanceof Rectangle2D) {
writeClip((Rectangle2D)s);
} else {
writeClip(s);
}
} catch (IOException e) {
handleException(e);
}
}
/**
* Write out Rectangle clip. Calls writeClip(Rectangle2D).
*
* @param rectangle to be used for clipping
*/
protected void writeClip(Rectangle rectangle) throws IOException {
writeClip((Rectangle2D)rectangle);
}
/**
* Write out Rectangle2D clip.
*
* @param rectangle to be used for clipping
*/
protected abstract void writeClip(Rectangle2D rectangle) throws IOException;
/**
* Write out Shape clip.
*
* @param shape to be used for clipping
*/
protected abstract void writeClip(Shape shape) throws IOException;
/*================================================================================
| 8. Graphics State
*================================================================================*/
/* 8.1. stroke/linewidth */
/**
* Get the current stroke.
*
* @return current stroke
*/
public Stroke getStroke() {
return currentStroke;
}
/**
* Sets the current stroke.
* Calls writeStroke if stroke is unequal to the current stroke.
*
* @param stroke to be set
*/
public void setStroke(Stroke stroke) {
if (stroke.equals(currentStroke)) {
return;
}
try {
writeStroke(stroke);
} catch (IOException e) {
handleException(e);
}
currentStroke = stroke;
}
/**
* Writes the current stroke. If stroke is an instance of BasicStroke it will
* call writeWidth, writeCap, writeJoin, writeMiterLimit and writeDash,
* if any were different than the current stroke.
*/
protected void writeStroke(Stroke stroke) throws IOException {
if (stroke instanceof BasicStroke) {
BasicStroke ns = (BasicStroke) stroke;
// get the current values for comparison if available,
// otherwise set them to -1="undefined"
int currentCap = -1, currentJoin = -1;
float currentWidth = -1, currentLimit = -1, currentDashPhase = -1;
float[] currentDashArray = null;
if ((currentStroke != null) && (currentStroke instanceof BasicStroke)) {
BasicStroke cs = (BasicStroke) currentStroke;
currentCap = cs.getEndCap();
currentJoin = cs.getLineJoin();
currentWidth = cs.getLineWidth();
currentLimit = cs.getMiterLimit();
currentDashArray = cs.getDashArray();
currentDashPhase = cs.getDashPhase();
}
// Check the linewidth.
float width = ns.getLineWidth();
if (currentWidth != width) {
writeWidth(width);
}
// Check the line caps.
int cap = ns.getEndCap();
if (currentCap != cap) {
writeCap(cap);
}
// Check the line joins.
int join = ns.getLineJoin();
if (currentJoin != join) {
writeJoin(join);
}
// Check the miter limit and validity of value
float limit = ns.getMiterLimit();
if ((currentLimit != limit) && (limit >= 1.0f)) {
writeMiterLimit(limit);
}
// Get the dashing parameters.
float phase = ns.getDashPhase();
float[] newDashArray = ns.getDashArray();
// Check to see if there are differences in the phase or
// length of the array.
boolean different = false;
if (currentDashPhase!=phase) different = true;
if ((newDashArray==null && currentDashArray!=null) ||
(newDashArray!=null && currentDashArray==null))
different = true;
if (newDashArray!=null && currentDashArray!=null &&
newDashArray.length!=currentDashArray.length)
different = true;
// Check the individual dashing entries if necessary.
if (!different && newDashArray!=null && currentDashArray!=null) {
for (int i=0; i 0) {
path.moveTo((float)xPoints[0], (float)yPoints[0]);
for (int i = 1; i < nPoints; i++) {
path.lineTo((float)xPoints[i], (float)yPoints[i]);
}
if (close) path.closePath();
}
return path;
}
private Shape transformShape(AffineTransform at, Shape s) {
if (s == null) return null;
return at.createTransformedShape(s);
}
private Shape transformShape(Shape s) {
return transformShape(getTransform(), s);
}
private Shape untransformShape(Shape s) {
if (s == null) return null;
try {
return transformShape(getTransform().createInverse(), s);
} catch (NoninvertibleTransformException e) {
return null;
}
}
private Point2D drawFrameAndBanner(double x, double y, Rectangle2D textSize, double adjustment,
boolean framed, Color frameColor, double frameWidth,
boolean banner, Color bannerColor,
int horizontal, int vertical) throws IOException {
double descent = textSize.getY() + textSize.getHeight();
x = getXalignment(x, textSize.getWidth(), horizontal);
y = getYalignment(y, textSize.getHeight(), descent, vertical);
double rx = x - adjustment;
double ry = y - textSize.getHeight() + descent - adjustment;
double rw = textSize.getWidth() + 2*adjustment;
double rh = textSize.getHeight() + 2*adjustment;
if (banner) {
Paint paint = getPaint();
setColor(bannerColor);
fillRect(rx, ry, rw, rh);
setPaint(paint);
}
if (framed) {
Paint paint = getPaint();
Stroke stroke = getStroke();
setColor(frameColor);
setLineWidth(frameWidth);
drawRect(rx, ry, rw, rh);
setPaint(paint);
setStroke(stroke);
}
return new Point2D.Double(x, y);
}
}