org.apache.xmlgraphics.java2d.ps.PSGraphics2D Maven / Gradle / Ivy
Show all versions of xmlgraphics-commons Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id: PSGraphics2D.java 1809627 2017-09-25 13:42:08Z ssteiner $ */
package org.apache.xmlgraphics.java2d.ps;
//Java
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
import org.apache.xmlgraphics.java2d.GraphicContext;
import org.apache.xmlgraphics.java2d.GraphicsConfigurationWithoutTransparency;
import org.apache.xmlgraphics.java2d.StrokingTextHandler;
import org.apache.xmlgraphics.java2d.TextHandler;
import org.apache.xmlgraphics.ps.PSGenerator;
import org.apache.xmlgraphics.ps.PSImageUtils;
/**
* This is a concrete implementation of AbstractGraphics2D (and
* therefore of Graphics2D) which is able to generate PostScript
* code.
*
* @version $Id: PSGraphics2D.java 1809627 2017-09-25 13:42:08Z ssteiner $
* @see org.apache.xmlgraphics.java2d.AbstractGraphics2D
*
* Originally authored by Keiron Liddle.
*/
public class PSGraphics2D extends AbstractGraphics2D {
private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
private static final boolean DEBUG = false;
protected List pathHashCache = new ArrayList();
protected boolean startCache;
/**
* The G2D instance that represents the root instance
* (used in context with create()/dispose()). Null if this instance is the root instance.
*/
protected PSGraphics2D rootG2D;
/** the PostScript generator being created */
protected PSGenerator gen;
/** Disable or enable clipping */
protected boolean clippingDisabled;
/** Fallback text handler */
protected TextHandler fallbackTextHandler = new StrokingTextHandler();
/** Custom text handler */
protected TextHandler customTextHandler;
/**
* the current colour for use in svg
*/
protected Color currentColour = new Color(0, 0, 0);
/**
* Create a new Graphics2D that generates PostScript code.
* @param textAsShapes True if text should be rendered as graphics
* @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean)
*/
public PSGraphics2D(boolean textAsShapes) {
super(textAsShapes);
}
/**
* Create a new Graphics2D that generates PostScript code.
* @param textAsShapes True if text should be rendered as graphics
* @param gen PostScript generator to use for output
* @see org.apache.xmlgraphics.java2d.AbstractGraphics2D#AbstractGraphics2D(boolean)
*/
public PSGraphics2D(boolean textAsShapes, PSGenerator gen) {
this(textAsShapes);
setPSGenerator(gen);
}
/**
* Constructor for creating copies
* @param g parent PostScript Graphics2D
*/
public PSGraphics2D(PSGraphics2D g) {
super(g);
this.rootG2D = (g.rootG2D != null ? g.rootG2D : g);
setPSGenerator(g.gen);
this.clippingDisabled = g.clippingDisabled;
//this.fallbackTextHandler is not copied
//TODO The customTextHandler should probably not be passed over just like that
//fallbackTextHandler, for example, has to be recreated to point to the sub-Graphics2D
//to get the text positioning right. This might require changes in the TextHandler interface
this.customTextHandler = g.customTextHandler;
this.currentColour = g.currentColour;
}
/**
* Sets the PostScript generator
* @param gen the PostScript generator
*/
public void setPSGenerator(PSGenerator gen) {
this.gen = gen;
}
/** @return the PostScript generator used by this instance. */
public PSGenerator getPSGenerator() {
return this.gen;
}
/**
* Sets the GraphicContext
* @param c GraphicContext to use
*/
public void setGraphicContext(GraphicContext c) {
gc = c;
//setPrivateHints();
}
/** @return the fallback TextHandler implementation */
public TextHandler getFallbackTextHandler() {
return this.fallbackTextHandler;
}
/** @return the custom TextHandler implementation */
public TextHandler getCustomTextHandler() {
return this.customTextHandler;
}
/**
* Sets a custom TextHandler implementation that is responsible for painting text. The default
* TextHandler paints all text as shapes. A custom implementation can implement text painting
* using text painting operators.
* @param handler the custom TextHandler implementation
*/
public void setCustomTextHandler(TextHandler handler) {
this.customTextHandler = handler;
}
/* TODO Add me back at the right place!!!
private void setPrivateHints() {
setRenderingHint(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING,
RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON);
}*/
/**
* Disable clipping on each draw command.
*
* @param b set to true to disable all clipping.
*/
public void disableClipping(boolean b) {
this.clippingDisabled = b;
}
/**
* Creates a new Graphics
object that is
* a copy of this Graphics
object.
* @return a new graphics context that is a copy of
* this graphics context.
*/
public Graphics create() {
preparePainting();
return new PSGraphics2D(this);
}
/**
* Central handler for IOExceptions for this class.
* @param ioe IOException to handle
*/
public void handleIOException(IOException ioe) {
//TODO Surely, there's a better way to do this.
ioe.printStackTrace();
}
/**
* This method is used by AbstractPSDocumentGraphics2D to prepare a new page if
* necessary.
*/
public void preparePainting() {
//nop, used by AbstractPSDocumentGraphics2D
if (rootG2D != null) {
rootG2D.preparePainting();
}
}
/**
* Draws as much of the specified image as is currently available.
* The image is drawn with its top-left corner at
* (x, y) in this graphics context's coordinate
* space. Transparent pixels in the image do not affect whatever
* pixels are already there.
*
* This method returns immediately in all cases, even if the
* complete image has not yet been loaded, and it has not been dithered
* and converted for the current output device.
*
* If the image has not yet been completely loaded, then
* drawImage
returns false
. As more of
* the image becomes available, the process that draws the image notifies
* the specified image observer.
* @param img the specified image to be drawn.
* @param x the x coordinate.
* @param y the y coordinate.
* @param observer object to be notified as more of
* the image is converted.
* @return True if the image has been fully drawn/loaded
* @see java.awt.Image
* @see java.awt.image.ImageObserver
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
*/
public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
return drawImage(img, x, y, observer, null);
}
public boolean drawImage(Image img, int x, int y, ImageObserver observer, Color mask) {
preparePainting();
if (DEBUG) {
System.out.println("drawImage: " + x + ", " + y + " " + img.getClass().getName());
}
final int width = img.getWidth(observer);
final int height = img.getHeight(observer);
if (width == -1 || height == -1) {
return false;
}
Dimension size = new Dimension(width, height);
BufferedImage buf = buildBufferedImage(size);
java.awt.Graphics2D g = buf.createGraphics();
g.setComposite(AlphaComposite.SrcOver);
g.setBackground(new Color(1, 1, 1, 0));
g.fillRect(0, 0, width, height);
g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight()));
if (!g.drawImage(img, 0, 0, observer)) {
return false;
}
g.dispose();
try {
AffineTransform at = getTransform();
gen.saveGraphicsState();
gen.concatMatrix(at);
Shape imclip = getClip();
writeClip(imclip);
PSImageUtils.renderBitmapImage(buf, x, y, width, height, gen, mask);
gen.restoreGraphicsState();
} catch (IOException ioe) {
handleIOException(ioe);
}
return true;
}
/**
* Creates a buffered image.
* @param size dimensions of the image to be created
* @return the buffered image
*/
public BufferedImage buildBufferedImage(Dimension size) {
return new BufferedImage(size.width, size.height,
BufferedImage.TYPE_INT_ARGB);
}
/**
* Draws as much of the specified image as has already been scaled
* to fit inside the specified rectangle.
*
* The image is drawn inside the specified rectangle of this
* graphics context's coordinate space, and is scaled if
* necessary. Transparent pixels do not affect whatever pixels
* are already there.
*
* This method returns immediately in all cases, even if the
* entire image has not yet been scaled, dithered, and converted
* for the current output device.
* If the current output representation is not yet complete, then
* drawImage
returns false
. As more of
* the image becomes available, the process that draws the image notifies
* the image observer by calling its imageUpdate
method.
*
* A scaled version of an image will not necessarily be
* available immediately just because an unscaled version of the
* image has been constructed for this output device. Each size of
* the image may be cached separately and generated from the original
* data in a separate image production sequence.
* @param img the specified image to be drawn.
* @param x the x coordinate.
* @param y the y coordinate.
* @param width the width of the rectangle.
* @param height the height of the rectangle.
* @param observer object to be notified as more of
* the image is converted.
* @return True if the image has been fully loaded/drawn
* @see java.awt.Image
* @see java.awt.image.ImageObserver
* @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
*/
public boolean drawImage(Image img, int x, int y, int width, int height,
ImageObserver observer) {
preparePainting();
System.err.println("NYI: drawImage");
return true;
}
/**
* Disposes of this graphics context and releases
* any system resources that it is using.
* A Graphics
object cannot be used after
* dispose
has been called.
*
* When a Java program runs, a large number of Graphics
* objects can be created within a short time frame.
* Although the finalization process of the garbage collector
* also disposes of the same system resources, it is preferable
* to manually free the associated resources by calling this
* method rather than to rely on a finalization process which
* may not run to completion for a long period of time.
*
* Graphics objects which are provided as arguments to the
* paint
and update
methods
* of components are automatically released by the system when
* those methods return. For efficiency, programmers should
* call dispose
when finished using
* a Graphics
object only if it was created
* directly from a component or another Graphics
object.
* @see java.awt.Graphics#finalize
* @see java.awt.Component#paint
* @see java.awt.Component#update
* @see java.awt.Component#getGraphics
* @see java.awt.Graphics#create
*/
public void dispose() {
this.gen = null;
this.fallbackTextHandler = null;
this.customTextHandler = null;
this.currentColour = null;
}
/**
* Processes a Shape generating the necessary painting operations.
* @param s Shape to process
* @return the winding rule of the path defining the shape
* @throws IOException In case of an I/O problem.
*/
public int processShape(Shape s, boolean cached) throws IOException {
if (s instanceof Rectangle2D) {
// Special optimization in case of Rectangle Shape
Rectangle2D r = (Rectangle2D) s;
gen.defineRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
return PathIterator.WIND_NON_ZERO;
} else {
PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
if (cached) {
processPathIteratorCached(s);
} else {
processPathIterator(iter);
}
return iter.getWindingRule();
}
}
protected String processPathIteratorToString(PathIterator iter) throws IOException {
StringBuilder cmd = new StringBuilder();
double[] vals = new double[6];
while (!iter.isDone()) {
int type = iter.currentSegment(vals);
switch (type) {
case PathIterator.SEG_CUBICTO:
cmd.append(gen.formatDouble(vals[0])).append(" ").append(gen.formatDouble(vals[1])).append(" ")
.append(gen.formatDouble(vals[2])).append(" ").append(gen.formatDouble(vals[3])).append(" ")
.append(gen.formatDouble(vals[4])).append(" ").append(gen.formatDouble(vals[5])).append(" ")
.append(gen.mapCommand("curveto")).append("\n");
break;
case PathIterator.SEG_LINETO:
cmd.append(gen.formatDouble(vals[0])).append(" ").append(gen.formatDouble(vals[1])).append(" ")
.append(gen.mapCommand("lineto")).append("\n");
break;
case PathIterator.SEG_MOVETO:
cmd.append(gen.formatDouble(vals[0])).append(" ").append(gen.formatDouble(vals[1])).append(" ")
.append(gen.mapCommand("moveto")).append("\n");
break;
case PathIterator.SEG_QUADTO:
cmd.append(gen.formatDouble(vals[0])).append(" ").append(gen.formatDouble(vals[1])).append(" ")
.append(gen.formatDouble(vals[2])).append(" ").append(gen.formatDouble(vals[3])).append(" QT")
.append("\n");
break;
case PathIterator.SEG_CLOSE:
cmd.append(gen.mapCommand("closepath")).append("\n");
break;
default:
break;
}
iter.next();
}
return cmd.toString().trim();
}
protected void processPathIteratorCached(Shape s) throws IOException {
String cmd = processPathIteratorToString(s.getPathIterator(IDENTITY_TRANSFORM));
int hash = cmd.hashCode();
if (!startCache) {
if (pathHashCache.contains(hash)) {
startCache = true;
pathHashCache.clear();
} else {
gen.writeln(cmd);
pathHashCache.add(hash);
}
}
if (startCache) {
if (!pathHashCache.contains(hash)) {
gen.writeln("/f" + hash + "{" + cmd + "}def");
pathHashCache.add(hash);
}
gen.writeln("f" + hash);
}
}
/**
* Processes a path iterator generating the nexessary painting operations.
* @param iter PathIterator to process
* @throws IOException In case of an I/O problem.
*/
public void processPathIterator(PathIterator iter) throws IOException {
gen.writeln(processPathIteratorToString(iter));
}
/**
* Strokes the outline of a Shape
using the settings of the
* current Graphics2D
context. The rendering attributes
* applied include the Clip
, Transform
,
* Paint
, Composite
and
* Stroke
attributes.
* @param s the Shape
to be rendered
* @see #setStroke
* @see #setPaint
* @see java.awt.Graphics#setColor
* @see #transform
* @see #setTransform
* @see #clip
* @see #setClip
* @see #setComposite
*/
public void draw(Shape s) {
preparePainting();
try {
gen.saveGraphicsState();
AffineTransform trans = getTransform();
boolean newTransform = !trans.isIdentity();
if (newTransform) {
gen.concatMatrix(trans);
}
Shape imclip = getClip();
if (shouldBeClipped(imclip, s)) {
writeClip(imclip);
}
establishColor(getColor());
applyPaint(getPaint(), false);
applyStroke(getStroke());
gen.writeln(gen.mapCommand("newpath"));
processShape(s, false);
doDrawing(false, true, false);
gen.restoreGraphicsState();
} catch (IOException ioe) {
handleIOException(ioe);
}
}
/**
* Determines if a shape interacts with a clipping region.
* @param clip Shape defining the clipping region
* @param s Shape to be drawn
* @return true if code for a clipping region needs to be generated.
*/
public boolean shouldBeClipped(Shape clip, Shape s) {
if (clip == null || s == null) {
return false;
}
if (s instanceof Line2D) {
//Line shapes don't work with intersections so always clip
return true;
}
Area as = new Area(s);
Area imclip = new Area(clip);
imclip.intersect(as);
return !imclip.equals(as);
}
/**
* Establishes a clipping region
* @param s Shape defining the clipping region
*/
public void writeClip(Shape s) {
if (s == null) {
return;
}
if (!this.clippingDisabled) {
preparePainting();
try {
gen.writeln(gen.mapCommand("newpath"));
processShape(s, false);
// clip area
gen.writeln(gen.mapCommand("clip"));
} catch (IOException ioe) {
handleIOException(ioe);
}
}
}
/**
* Applies a new Paint object.
* @param paint Paint object to use
* @param fill True if to be applied for filling
*/
protected void applyPaint(Paint paint, boolean fill) {
preparePainting();
if (paint instanceof GradientPaint) {
System.err.println("NYI: Gradient paint");
} else if (paint instanceof TexturePaint) {
if (fill) {
try {
// create pattern with texture and use it for filling of a graphics object
PSTilingPattern psTilingPattern = new PSTilingPattern("Pattern1",
(TexturePaint)paint, 0, 0, 3, null);
gen.write(psTilingPattern.toString(gen.isAcrobatDownsample()));
gen.writeln("/Pattern " + gen.mapCommand("setcolorspace"));
gen.writeln(psTilingPattern.getName() + " " + gen.mapCommand("setcolor"));
} catch (IOException ioe) {
handleIOException(ioe);
}
}
}
}
/**
* Applies a new Stroke object.
* @param stroke Stroke object to use
*/
protected void applyStroke(Stroke stroke) {
preparePainting();
try {
applyStroke(stroke, gen);
} catch (IOException ioe) {
handleIOException(ioe);
}
}
/**
* Applies a new Stroke object.
* @param stroke the Stroke instance
* @param gen the PS generator
* @throws IOException if an I/O error occurs
*/
public static void applyStroke(Stroke stroke, final PSGenerator gen)
throws IOException {
if (stroke instanceof BasicStroke) {
BasicStroke basicStroke = (BasicStroke)stroke;
float[] da = basicStroke.getDashArray();
if (da != null) {
StringBuffer sb = new StringBuffer("[");
for (int count = 0; count < da.length; count++) {
sb.append(gen.formatDouble(da[count]));
if (count < da.length - 1) {
sb.append(" ");
}
}
sb.append("] ");
float offset = basicStroke.getDashPhase();
sb.append(gen.formatDouble(offset));
gen.useDash(sb.toString());
} else {
gen.useDash(null);
}
int ec = basicStroke.getEndCap();
switch (ec) {
case BasicStroke.CAP_BUTT:
gen.useLineCap(0);
break;
case BasicStroke.CAP_ROUND:
gen.useLineCap(1);
break;
case BasicStroke.CAP_SQUARE:
gen.useLineCap(2);
break;
default: System.err.println("Unsupported line cap: " + ec);
}
int lj = basicStroke.getLineJoin();
switch (lj) {
case BasicStroke.JOIN_MITER:
gen.useLineJoin(0);
float ml = basicStroke.getMiterLimit();
gen.useMiterLimit(ml >= -1 ? ml : 1);
break;
case BasicStroke.JOIN_ROUND:
gen.useLineJoin(1);
break;
case BasicStroke.JOIN_BEVEL:
gen.useLineJoin(2);
break;
default: System.err.println("Unsupported line join: " + lj);
}
float lw = basicStroke.getLineWidth();
gen.useLineWidth(lw);
} else {
System.err.println("Stroke not supported: " + stroke.toString());
}
}
/**
* Renders a {@link RenderedImage},
* applying a transform from image
* space into user space before drawing.
* The transformation from user space into device space is done with
* the current Transform
in the Graphics2D
.
* The specified transformation is applied to the image before the
* transform attribute in the Graphics2D
context is applied.
* The rendering attributes applied include the Clip
,
* Transform
, and Composite
attributes. Note
* that no rendering is done if the specified transform is
* noninvertible.
* @param img the image to be rendered
* @param xform the transformation from image space into user space
* @see #transform
* @see #setTransform
* @see #setComposite
* @see #clip
* @see #setClip
*/
public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
preparePainting();
try {
AffineTransform at = getTransform();
gen.saveGraphicsState();
gen.concatMatrix(at);
gen.concatMatrix(xform);
Shape imclip = getClip();
writeClip(imclip);
PSImageUtils.renderBitmapImage(img,
0, 0, img.getWidth(), img.getHeight(), gen, null);
gen.restoreGraphicsState();
} catch (IOException ioe) {
handleIOException(ioe);
}
}
/**
* Renders a
* {@link RenderableImage},
* applying a transform from image space into user space before drawing.
* The transformation from user space into device space is done with
* the current Transform
in the Graphics2D
.
* The specified transformation is applied to the image before the
* transform attribute in the Graphics2D
context is applied.
* The rendering attributes applied include the Clip
,
* Transform
, and Composite
attributes. Note
* that no rendering is done if the specified transform is
* noninvertible.
*
* Rendering hints set on the Graphics2D
object might
* be used in rendering the RenderableImage
.
* If explicit control is required over specific hints recognized by a
* specific RenderableImage
, or if knowledge of which hints
* are used is required, then a RenderedImage
should be
* obtained directly from the RenderableImage
* and rendered using
* {@link #drawRenderedImage(RenderedImage, AffineTransform) drawRenderedImage}.
* @param img the image to be rendered
* @param xform the transformation from image space into user space
* @see #transform
* @see #setTransform
* @see #setComposite
* @see #clip
* @see #setClip
* @see #drawRenderedImage
*/
public void drawRenderableImage(RenderableImage img,
AffineTransform xform) {
preparePainting();
System.err.println("NYI: drawRenderableImage");
}
/**
* Establishes the given color in the PostScript interpreter.
* @param c the color to set
* @throws IOException In case of an I/O problem
*/
public void establishColor(Color c) throws IOException {
gen.useColor(c);
}
/**
* Renders the text specified by the specified String
,
* using the current Font
and Paint
attributes
* in the Graphics2D
context.
* The baseline of the first character is at position
* (x, y) in the User Space.
* The rendering attributes applied include the Clip
,
* Transform
, Paint
, Font
and
* Composite
attributes. For characters in script systems
* such as Hebrew and Arabic, the glyphs can be rendered from right to
* left, in which case the coordinate supplied is the location of the
* leftmost character on the baseline.
* @param s the String
to be rendered
* @param x the x-coordinate where the String
* should be rendered
* @param y the y-coordinate where the String
* should be rendered
* @see #setPaint
* @see java.awt.Graphics#setColor
* @see java.awt.Graphics#setFont
* @see #setTransform
* @see #setComposite
* @see #setClip
*/
public void drawString(String s, float x, float y) {
try {
if (customTextHandler != null && !textAsShapes) {
customTextHandler.drawString(this, s, x, y);
} else {
fallbackTextHandler.drawString(this, s, x, y);
}
} catch (IOException ioe) {
handleIOException(ioe);
}
}
/**
* Fills the interior of a Shape
using the settings of the
* Graphics2D
context. The rendering attributes applied
* include the Clip
, Transform
,
* Paint
, and Composite
.
* @param s the Shape
to be filled
* @see #setPaint
* @see java.awt.Graphics#setColor
* @see #transform
* @see #setTransform
* @see #setComposite
* @see #clip
* @see #setClip
*/
public void fill(Shape s) {
preparePainting();
try {
gen.saveGraphicsState();
AffineTransform trans = getTransform();
boolean newTransform = !trans.isIdentity();
if (newTransform) {
gen.concatMatrix(trans);
}
Shape imclip = getClip();
if (shouldBeClipped(imclip, s)) {
writeClip(imclip);
}
establishColor(getColor());
applyPaint(getPaint(), true);
gen.writeln(gen.mapCommand("newpath"));
int windingRule = processShape(s, true);
doDrawing(true, false,
windingRule == PathIterator.WIND_EVEN_ODD);
gen.restoreGraphicsState();
} catch (IOException ioe) {
handleIOException(ioe);
}
}
/**
* Commits a painting operation.
* @param fill filling
* @param stroke stroking
* @param nonzero true if the non-zero winding rule should be used when filling
* @exception IOException In case of an I/O problem
*/
protected void doDrawing(boolean fill, boolean stroke, boolean nonzero)
throws IOException {
preparePainting();
if (fill) {
if (stroke) {
if (!nonzero) {
gen.writeln(gen.mapCommand("gsave") + " "
+ gen.mapCommand("fill") + " "
+ gen.mapCommand("grestore") + " "
+ gen.mapCommand("stroke"));
} else {
gen.writeln(gen.mapCommand("gsave") + " "
+ gen.mapCommand("eofill") + " "
+ gen.mapCommand("grestore") + " "
+ gen.mapCommand("stroke"));
}
} else {
if (!nonzero) {
gen.writeln(gen.mapCommand("fill"));
} else {
gen.writeln(gen.mapCommand("eofill"));
}
}
} else {
// if(stroke)
gen.writeln(gen.mapCommand("stroke"));
}
}
/**
* Returns the device configuration associated with this
* Graphics2D
.
* @return the device configuration
*/
public GraphicsConfiguration getDeviceConfiguration() {
return new GraphicsConfigurationWithoutTransparency();
}
/**
* Used to create proper font metrics
*/
private Graphics2D fmg;
{
BufferedImage bi = new BufferedImage(1, 1,
BufferedImage.TYPE_INT_ARGB);
fmg = bi.createGraphics();
}
/**
* Gets the font metrics for the specified font.
* @return the font metrics for the specified font.
* @param f the specified font
* @see java.awt.Graphics#getFont
* @see java.awt.FontMetrics
* @see java.awt.Graphics#getFontMetrics()
*/
public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
return fmg.getFontMetrics(f);
}
/**
* Sets the paint mode of this graphics context to alternate between
* this graphics context's current color and the new specified color.
* This specifies that logical pixel operations are performed in the
* XOR mode, which alternates pixels between the current color and
* a specified XOR color.
*
* When drawing operations are performed, pixels which are the
* current color are changed to the specified color, and vice versa.
*
* Pixels that are of colors other than those two colors are changed
* in an unpredictable but reversible manner; if the same figure is
* drawn twice, then all pixels are restored to their original values.
* @param c1 the XOR alternation color
*/
public void setXORMode(Color c1) {
System.err.println("NYI: setXORMode");
}
/**
* Copies an area of the component by a distance specified by
* dx
and dy
. From the point specified
* by x
and y
, this method
* copies downwards and to the right. To copy an area of the
* component to the left or upwards, specify a negative value for
* dx
or dy
.
* If a portion of the source rectangle lies outside the bounds
* of the component, or is obscured by another window or component,
* copyArea
will be unable to copy the associated
* pixels. The area that is omitted can be refreshed by calling
* the component's paint
method.
* @param x the x coordinate of the source rectangle.
* @param y the y coordinate of the source rectangle.
* @param width the width of the source rectangle.
* @param height the height of the source rectangle.
* @param dx the horizontal distance to copy the pixels.
* @param dy the vertical distance to copy the pixels.
*/
public void copyArea(int x, int y, int width, int height, int dx,
int dy) {
System.err.println("NYI: copyArea");
}
/* --- for debugging
public void transform(AffineTransform tx) {
System.out.println("transform(" + toArray(tx) + ")");
super.transform(zx);
}
public void scale(double sx, double sy) {
System.out.println("scale(" + sx + ", " + sy + ")");
super.scale(sx, sy);
}
public void translate(double tx, double ty) {
System.out.println("translate(double " + tx + ", " + ty + ")");
super.translate(tx, ty);
}
public void translate(int tx, int ty) {
System.out.println("translate(int " + tx + ", " + ty + ")");
super.translate(tx, ty);
}
*/
}