org.apache.xmlgraphics.java2d.GraphicContext 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: GraphicContext.java 1610846 2014-07-15 20:44:18Z vhennebert $ */
package org.apache.xmlgraphics.java2d;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.NoninvertibleTransformException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
// CSOFF: OperatorWrap
// CSOFF: ParameterName
// CSOFF: WhitespaceAround
/**
* Handles the attributes in a graphic context:
* + Composite
* + Font
* + Paint
* + Stroke
* + Clip
* + RenderingHints
* + AffineTransform
*
* @version $Id: GraphicContext.java 1610846 2014-07-15 20:44:18Z vhennebert $
*
* Originally authored by Vincent Hardy and Christophe Jolif.
*/
public class GraphicContext implements Cloneable {
/**
* Default Transform to be used for creating FontRenderContext.
*/
protected AffineTransform defaultTransform = new AffineTransform();
/**
* Current AffineTransform. This is the concatenation
* of the original AffineTransform (i.e., last setTransform
* invocation) and the following transform invocations,
* as captured by originalTransform and the transformStack.
*/
protected AffineTransform transform = new AffineTransform();
/**
* Transform stack
*/
protected List transformStack = new ArrayList();
/**
* Defines whether the transform stack is valid or not.
* This is for use by the class clients. The client should
* validate the stack every time it starts using it. The
* stack becomes invalid when a new transform is set.
* @see #invalidateTransformStack()
* @see #isTransformStackValid
* @see #setTransform
*/
protected boolean transformStackValid = true;
/**
* Current Paint
*/
protected Paint paint = Color.black;
/**
* Current Stroke
*/
protected Stroke stroke = new BasicStroke();
/**
* Current Composite
*/
protected Composite composite = AlphaComposite.SrcOver;
/**
* Current clip
*/
protected Shape clip;
/**
* Current set of RenderingHints
*/
protected RenderingHints hints = new RenderingHints(null);
/**
* Current Font
*/
protected Font font = new Font("sanserif", Font.PLAIN, 12);
/**
* Current background color.
*/
protected Color background = new Color(0, 0, 0, 0);
/**
* Current foreground color
*/
protected Color foreground = Color.black;
/**
* Default constructor
*/
public GraphicContext() {
// to workaround a JDK bug
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);
}
/**
* @param defaultDeviceTransform Default affine transform applied to map the user space to the
* user space.
*/
public GraphicContext(AffineTransform defaultDeviceTransform) {
this();
defaultTransform = new AffineTransform(defaultDeviceTransform);
transform = new AffineTransform(defaultTransform);
if (!defaultTransform.isIdentity()) {
transformStack.add(TransformStackElement.createGeneralTransformElement(defaultTransform));
}
}
/**
* Copy constructor.
* @param template the instance to make a copy of
*/
protected GraphicContext(GraphicContext template) {
this(template.defaultTransform);
//
// Now, copy each GC element in turn
//
// Default transform
/* Set in constructor */
// Transform
this.transform = new AffineTransform(template.transform);
// Transform stack
this.transformStack = new ArrayList(template.transformStack.size());
for (int i = 0; i < template.transformStack.size(); i++) {
TransformStackElement stackElement
= (TransformStackElement)template.transformStack.get(i);
this.transformStack.add(stackElement.clone());
}
// Transform stack validity
this.transformStackValid = template.transformStackValid;
// Paint (immutable by requirement)
this.paint = template.paint;
// Stroke (immutable by requirement)
this.stroke = template.stroke;
// Composite (immutable by requirement)
this.composite = template.composite;
// Clip
if (template.clip != null) {
this.clip = new GeneralPath(template.clip);
} else {
this.clip = null;
}
// RenderingHints
this.hints = (RenderingHints)template.hints.clone();
// Font (immutable)
this.font = template.font;
// Background, Foreground (immutable)
this.background = template.background;
this.foreground = template.foreground;
}
/**
* @return a deep copy of this context
*/
public Object clone() {
return new GraphicContext(this);
}
/**
* Gets this graphics context's current color.
* @return this graphics context's current color.
* @see java.awt.Color
* @see java.awt.Graphics#setColor
*/
public Color getColor() {
return foreground;
}
/**
* Sets this graphics context's current color to the specified
* color. All subsequent graphics operations using this graphics
* context use this specified color.
* @param c the new rendering color.
* @see java.awt.Color
* @see java.awt.Graphics#getColor
*/
public void setColor(Color c) {
if (c == null) {
return;
}
if (paint != c) {
setPaint(c);
}
}
/**
* Gets the current font.
* @return this graphics context's current font.
* @see java.awt.Font
* @see java.awt.Graphics#setFont
*/
public Font getFont() {
return font;
}
/**
* Sets this graphics context's font to the specified font.
* All subsequent text operations using this graphics context
* use this font.
* @param font the font.
* @see java.awt.Graphics#getFont
*/
public void setFont(Font font) {
if (font != null) {
this.font = font;
}
}
/**
* Returns the bounding rectangle of the current clipping area.
* This method refers to the user clip, which is independent of the
* clipping associated with device bounds and window visibility.
* If no clip has previously been set, or if the clip has been
* cleared using setClip(null)
, this method returns
* null
.
* The coordinates in the rectangle are relative to the coordinate
* system origin of this graphics context.
* @return the bounding rectangle of the current clipping area,
* or null
if no clip is set.
* @see java.awt.Graphics#getClip
* @see java.awt.Graphics#clipRect
* @see java.awt.Graphics#setClip(int, int, int, int)
* @see java.awt.Graphics#setClip(Shape)
* @since JDK1.1
*/
public Rectangle getClipBounds() {
Shape c = getClip();
if (c == null) {
return null;
} else {
return c.getBounds();
}
}
/**
* Intersects the current clip with the specified rectangle.
* The resulting clipping area is the intersection of the current
* clipping area and the specified rectangle. If there is no
* current clipping area, either because the clip has never been
* set, or the clip has been cleared using setClip(null)
,
* the specified rectangle becomes the new clip.
* This method sets the user clip, which is independent of the
* clipping associated with device bounds and window visibility.
* This method can only be used to make the current clip smaller.
* To set the current clip larger, use any of the setClip methods.
* Rendering operations have no effect outside of the clipping area.
* @param x the x coordinate of the rectangle to intersect the clip with
* @param y the y coordinate of the rectangle to intersect the clip with
* @param width the width of the rectangle to intersect the clip with
* @param height the height of the rectangle to intersect the clip with
* @see #setClip(int, int, int, int)
* @see #setClip(Shape)
*/
public void clipRect(int x, int y, int width, int height) {
clip(new Rectangle(x, y, width, height));
}
/**
* Sets the current clip to the rectangle specified by the given
* coordinates. This method sets the user clip, which is
* independent of the clipping associated with device bounds
* and window visibility.
* Rendering operations have no effect outside of the clipping area.
* @param x the x coordinate of the new clip rectangle.
* @param y the y coordinate of the new clip rectangle.
* @param width the width of the new clip rectangle.
* @param height the height of the new clip rectangle.
* @see java.awt.Graphics#clipRect
* @see java.awt.Graphics#setClip(Shape)
* @since JDK1.1
*/
public void setClip(int x, int y, int width, int height) {
setClip(new Rectangle(x, y, width, height));
}
/**
* Gets the current clipping area.
* This method returns the user clip, which is independent of the
* clipping associated with device bounds and window visibility.
* If no clip has previously been set, or if the clip has been
* cleared using setClip(null)
, this method returns
* null
.
* @return a Shape
object representing the
* current clipping area, or null
if
* no clip is set.
* @see java.awt.Graphics#getClipBounds()
* @see java.awt.Graphics#clipRect
* @see java.awt.Graphics#setClip(int, int, int, int)
* @see java.awt.Graphics#setClip(Shape)
* @since JDK1.1
*/
public Shape getClip() {
try {
return transform.createInverse().createTransformedShape(clip);
} catch (NoninvertibleTransformException e) {
return null;
}
}
/**
* Sets the current clipping area to an arbitrary clip shape.
* Not all objects that implement the Shape
* interface can be used to set the clip. The only
* Shape
objects that are guaranteed to be
* supported are Shape
objects that are
* obtained via the getClip
method and via
* Rectangle
objects. This method sets the
* user clip, which is independent of the clipping associated
* with device bounds and window visibility.
* @param clip the Shape
to use to set the clip
* @see java.awt.Graphics#getClip()
* @see java.awt.Graphics#clipRect
* @see java.awt.Graphics#setClip(int, int, int, int)
* @since JDK1.1
*/
public void setClip(Shape clip) {
if (clip != null) {
this.clip = transform.createTransformedShape(clip);
} else {
this.clip = null;
}
}
/**
* Sets the Composite
for the Graphics2D
context.
* The Composite
is used in all drawing methods such as
* drawImage
, drawString
, draw
,
* and fill
. It specifies how new pixels are to be combined
* with the existing pixels on the graphics device during the rendering
* process.
* If this Graphics2D
context is drawing to a
* Component
on the display screen and the
* Composite
is a custom object rather than an
* instance of the AlphaComposite
class, and if
* there is a security manager, its checkPermission
* method is called with an AWTPermission("readDisplayPixels")
* permission.
*
* @param comp the Composite
object to be used for rendering
* @throws SecurityException
* if a custom Composite
object is being
* used to render to the screen and a security manager
* is set and its checkPermission
method
* does not allow the operation.
* @see java.awt.Graphics#setXORMode
* @see java.awt.Graphics#setPaintMode
* @see java.awt.AlphaComposite
*/
public void setComposite(Composite comp) {
this.composite = comp;
}
/**
* Sets the Paint
attribute for the
* Graphics2D
context. Calling this method
* with a null
Paint
object does
* not have any effect on the current Paint
attribute
* of this Graphics2D
.
* @param paint the Paint
object to be used to generate
* color during the rendering process, or null
* @see java.awt.Graphics#setColor
* @see java.awt.GradientPaint
* @see java.awt.TexturePaint
*/
public void setPaint(Paint paint) {
if (paint == null) {
return;
}
this.paint = paint;
if (paint instanceof Color) {
foreground = (Color)paint;
} else {
// use default; otherwise the previous Color will be used
foreground = Color.black;
}
}
/**
* Sets the Stroke
for the Graphics2D
context.
* @param s the Stroke
object to be used to stroke a
* Shape
during the rendering process
* @see BasicStroke
*/
public void setStroke(Stroke s) {
stroke = s;
}
/**
* Sets the value of a single preference for the rendering algorithms.
* Hint categories include controls for rendering quality and overall
* time/quality trade-off in the rendering process. Refer to the
* RenderingHints
class for definitions of some common
* keys and values.
* @param hintKey the key of the hint to be set.
* @param hintValue the value indicating preferences for the specified
* hint category.
* @see RenderingHints
*/
public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
hints.put(hintKey, hintValue);
}
/**
* Returns the value of a single preference for the rendering algorithms.
* Hint categories include controls for rendering quality and overall
* time/quality trade-off in the rendering process. Refer to the
* RenderingHints
class for definitions of some common
* keys and values.
* @param hintKey the key corresponding to the hint to get.
* @return an object representing the value for the specified hint key.
* Some of the keys and their associated values are defined in the
* RenderingHints
class.
* @see RenderingHints
*/
public Object getRenderingHint(RenderingHints.Key hintKey) {
return hints.get(hintKey);
}
/**
* Replaces the values of all preferences for the rendering
* algorithms with the specified hints
.
* The existing values for all rendering hints are discarded and
* the new set of known hints and values are initialized from the
* specified {@link Map} object.
* Hint categories include controls for rendering quality and
* overall time/quality trade-off in the rendering process.
* Refer to the RenderingHints
class for definitions of
* some common keys and values.
* @param hints the rendering hints to be set
* @see RenderingHints
*/
public void setRenderingHints(Map hints) {
this.hints = new RenderingHints(hints);
}
/**
* Sets the values of an arbitrary number of preferences for the
* rendering algorithms.
* Only values for the rendering hints that are present in the
* specified Map
object are modified.
* All other preferences not present in the specified
* object are left unmodified.
* Hint categories include controls for rendering quality and
* overall time/quality trade-off in the rendering process.
* Refer to the RenderingHints
class for definitions of
* some common keys and values.
* @param hints the rendering hints to be set
* @see RenderingHints
*/
public void addRenderingHints(Map hints) {
this.hints.putAll(hints);
}
/**
* Gets the preferences for the rendering algorithms. Hint categories
* include controls for rendering quality and overall time/quality
* trade-off in the rendering process.
* Returns all of the hint key/value pairs that were ever specified in
* one operation. Refer to the
* RenderingHints
class for definitions of some common
* keys and values.
* @return a reference to an instance of RenderingHints
* that contains the current preferences.
* @see RenderingHints
*/
public RenderingHints getRenderingHints() {
return hints;
}
/**
* Translates the origin of the graphics context to the point
* (x, y) in the current coordinate system.
* Modifies this graphics context so that its new origin corresponds
* to the point (x, y) in this graphics context's
* original coordinate system. All coordinates used in subsequent
* rendering operations on this graphics context will be relative
* to this new origin.
* @param x the x coordinate.
* @param y the y coordinate.
*/
public void translate(int x, int y) {
if (x != 0 || y != 0) {
transform.translate(x, y);
transformStack.add(TransformStackElement.createTranslateElement(x, y));
}
}
/**
* Concatenates the current
* Graphics2D
Transform
* with a translation transform.
* Subsequent rendering is translated by the specified
* distance relative to the previous position.
* This is equivalent to calling transform(T), where T is an
* AffineTransform
represented by the following matrix:
*
* [ 1 0 tx ]
* [ 0 1 ty ]
* [ 0 0 1 ]
*
* @param tx the distance to translate along the x-axis
* @param ty the distance to translate along the y-axis
*/
public void translate(double tx, double ty) {
transform.translate(tx, ty);
transformStack.add(TransformStackElement.createTranslateElement(tx, ty));
}
/**
* Concatenates the current Graphics2D
* Transform
with a rotation transform.
* Subsequent rendering is rotated by the specified radians relative
* to the previous origin.
* This is equivalent to calling transform(R)
, where R is an
* AffineTransform
represented by the following matrix:
*
* [ cos(theta) -sin(theta) 0 ]
* [ sin(theta) cos(theta) 0 ]
* [ 0 0 1 ]
*
* Rotating with a positive angle theta rotates points on the positive
* x axis toward the positive y axis.
* @param theta the angle of rotation in radians
*/
public void rotate(double theta) {
transform.rotate(theta);
transformStack.add(TransformStackElement.createRotateElement(theta));
}
/**
* Concatenates the current Graphics2D
* Transform
with a translated rotation
* transform. Subsequent rendering is transformed by a transform
* which is constructed by translating to the specified location,
* rotating by the specified radians, and translating back by the same
* amount as the original translation. This is equivalent to the
* following sequence of calls:
*
* translate(x, y);
* rotate(theta);
* translate(-x, -y);
*
* Rotating with a positive angle theta rotates points on the positive
* x axis toward the positive y axis.
* @param theta the angle of rotation in radians
* @param x x coordinate of the origin of the rotation
* @param y y coordinate of the origin of the rotation
*/
public void rotate(double theta, double x, double y) {
transform.rotate(theta, x, y);
transformStack.add(TransformStackElement.createTranslateElement(x, y));
transformStack.add(TransformStackElement.createRotateElement(theta));
transformStack.add(TransformStackElement.createTranslateElement(-x, -y));
}
/**
* Concatenates the current Graphics2D
* Transform
with a scaling transformation
* Subsequent rendering is resized according to the specified scaling
* factors relative to the previous scaling.
* This is equivalent to calling transform(S)
, where S is an
* AffineTransform
represented by the following matrix:
*
* [ sx 0 0 ]
* [ 0 sy 0 ]
* [ 0 0 1 ]
*
* @param sx the amount by which X coordinates in subsequent
* rendering operations are multiplied relative to previous
* rendering operations.
* @param sy the amount by which Y coordinates in subsequent
* rendering operations are multiplied relative to previous
* rendering operations.
*/
public void scale(double sx, double sy) {
transform.scale(sx, sy);
transformStack.add(TransformStackElement.createScaleElement(sx, sy));
}
/**
* Concatenates the current Graphics2D
* Transform
with a shearing transform.
* Subsequent renderings are sheared by the specified
* multiplier relative to the previous position.
* This is equivalent to calling transform(SH)
, where SH
* is an AffineTransform
represented by the following
* matrix:
*
* [ 1 shx 0 ]
* [ shy 1 0 ]
* [ 0 0 1 ]
*
* @param shx the multiplier by which coordinates are shifted in
* the positive X axis direction as a function of their Y coordinate
* @param shy the multiplier by which coordinates are shifted in
* the positive Y axis direction as a function of their X coordinate
*/
public void shear(double shx, double shy) {
transform.shear(shx, shy);
transformStack.add(TransformStackElement.createShearElement(shx, shy));
}
/**
* Composes an AffineTransform
object with the
* Transform
in this Graphics2D
according
* to the rule last-specified-first-applied. If the current
* Transform
is Cx, the result of composition
* with Tx is a new Transform
Cx'. Cx' becomes the
* current Transform
for this Graphics2D
.
* Transforming a point p by the updated Transform
Cx' is
* equivalent to first transforming p by Tx and then transforming
* the result by the original Transform
Cx. In other
* words, Cx'(p) = Cx(Tx(p)). A copy of the Tx is made, if necessary,
* so further modifications to Tx do not affect rendering.
* @param tx the AffineTransform
object to be composed with
* the current Transform
* @see #setTransform
* @see AffineTransform
*/
public void transform(AffineTransform tx) {
transform.concatenate(tx);
transformStack.add(TransformStackElement.createGeneralTransformElement(tx));
}
/**
* Sets the Transform
in the Graphics2D
* context.
* @param tx the AffineTransform
object to be used in the
* rendering process
* @see #transform
* @see AffineTransform
*/
public void setTransform(AffineTransform tx) {
transform = new AffineTransform(tx);
invalidateTransformStack();
if (!tx.isIdentity()) {
transformStack.add(TransformStackElement.createGeneralTransformElement(tx));
}
}
/**
* Marks the GraphicContext's isNewTransformStack to false
* as a memento that the current transform stack was read and
* has not been reset. Only the setTransform method can
* override this memento.
*/
public void validateTransformStack() {
transformStackValid = true;
}
/**
* Checks the status of the transform stack.
* @return true if the transform stack is valid
*/
public boolean isTransformStackValid() {
return transformStackValid;
}
/**
* @return array containing the successive transforms that
* were concatenated with the original one.
*/
public TransformStackElement[] getTransformStack() {
TransformStackElement[] stack = new TransformStackElement[transformStack.size()];
transformStack.toArray(stack);
return stack;
}
/**
* Marks the GraphicContext's isNewTransformStack to true
* as a memento that the current transform stack was reset
* since it was last read. Only validateTransformStack
* can override this memento
*/
protected void invalidateTransformStack() {
transformStack.clear();
transformStackValid = false;
}
/**
* Returns a copy of the current Transform
in the
* Graphics2D
context.
* @return the current AffineTransform
in the
* Graphics2D
context.
* @see #transform
* @see #setTransform
*/
public AffineTransform getTransform() {
return new AffineTransform(transform);
}
/**
* Returns the current Paint
of the
* Graphics2D
context.
* @return the current Graphics2D
Paint
,
* which defines a color or pattern.
* @see #setPaint
* @see java.awt.Graphics#setColor
*/
public Paint getPaint() {
return paint;
}
/**
* Returns the current Composite
in the
* Graphics2D
context.
* @return the current Graphics2D
Composite
,
* which defines a compositing style.
* @see #setComposite
*/
public Composite getComposite() {
return composite;
}
/**
* Sets the background color for the Graphics2D
context.
* The background color is used for clearing a region.
* When a Graphics2D
is constructed for a
* Component
, the background color is
* inherited from the Component
. Setting the background color
* in the Graphics2D
context only affects the subsequent
* clearRect
calls and not the background color of the
* Component
. To change the background
* of the Component
, use appropriate methods of
* the Component
.
* @param color the background color that isused in
* subsequent calls to clearRect
* @see #getBackground
* @see java.awt.Graphics#clearRect
*/
public void setBackground(Color color) {
if (color == null) {
return;
}
background = color;
}
/**
* Returns the background color used for clearing a region.
* @return the current Graphics2D
Color
,
* which defines the background color.
* @see #setBackground
*/
public Color getBackground() {
return background;
}
/**
* Returns the current Stroke
in the
* Graphics2D
context.
* @return the current Graphics2D
Stroke
,
* which defines the line style.
* @see #setStroke
*/
public Stroke getStroke() {
return stroke;
}
/**
* Intersects the current Clip
with the interior of the
* specified Shape
and sets the Clip
to the
* resulting intersection. The specified Shape
is
* transformed with the current Graphics2D
* Transform
before being intersected with the current
* Clip
. This method is used to make the current
* Clip
smaller.
* To make the Clip
larger, use setClip
.
* The user clip modified by this method is independent of the
* clipping associated with device bounds and visibility. If no clip has
* previously been set, or if the clip has been cleared using
* {@link java.awt.Graphics#setClip(Shape) setClip} with a
* null
argument, the specified Shape
becomes
* the new user clip.
* @param s the Shape
to be intersected with the current
* Clip
. If s
is null
,
* this method clears the current Clip
.
*/
public void clip(Shape s) {
if (s != null) {
s = transform.createTransformedShape(s);
}
if (clip != null) {
Area newClip = new Area(clip);
newClip.intersect(new Area(s));
clip = new GeneralPath(newClip);
} else {
clip = s;
}
}
/**
* Get the rendering context of the Font
within this
* Graphics2D
context.
* The {@link FontRenderContext}
* encapsulates application hints such as anti-aliasing and
* fractional metrics, as well as target device specific information
* such as dots-per-inch. This information should be provided by the
* application when using objects that perform typographical
* formatting, such as Font
and
* TextLayout
. This information should also be provided
* by applications that perform their own layout and need accurate
* measurements of various characteristics of glyphs such as advance
* and line height when various rendering hints have been applied to
* the text rendering.
*
* @return a reference to an instance of FontRenderContext.
* @see java.awt.font.FontRenderContext
* @see java.awt.Font#createGlyphVector(FontRenderContext,char[])
* @see java.awt.font.TextLayout
* @since JDK1.2
*/
public FontRenderContext getFontRenderContext() {
//
// Find if antialiasing should be used.
//
Object antialiasingHint = hints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
boolean isAntialiased = true;
if (antialiasingHint != RenderingHints.VALUE_TEXT_ANTIALIAS_ON
&& antialiasingHint != RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT) {
// If antialias was not turned off, then use the general rendering
// hint.
if (antialiasingHint != RenderingHints.VALUE_TEXT_ANTIALIAS_OFF) {
antialiasingHint = hints.get(RenderingHints.KEY_ANTIALIASING);
// Test general hint
if (antialiasingHint != RenderingHints.VALUE_ANTIALIAS_ON
&& antialiasingHint != RenderingHints.VALUE_ANTIALIAS_DEFAULT) {
// Antialiasing was not requested. However, if it was not turned
// off explicitly, use it.
if (antialiasingHint == RenderingHints.VALUE_ANTIALIAS_OFF) {
isAntialiased = false;
}
}
} else {
isAntialiased = false;
}
}
//
// Find out whether fractional metrics should be used.
//
boolean useFractionalMetrics = true;
if (hints.get(RenderingHints.KEY_FRACTIONALMETRICS)
== RenderingHints.VALUE_FRACTIONALMETRICS_OFF) {
useFractionalMetrics = false;
}
FontRenderContext frc = new FontRenderContext(defaultTransform,
isAntialiased,
useFractionalMetrics);
return frc;
}
}