|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
/* |
|
* |
|
* (C) Copyright IBM Corp. 1998, All Rights Reserved |
|
*/ |
|
|
|
package sun.font; |
|
|
|
import java.awt.BasicStroke; |
|
import java.awt.Graphics2D; |
|
import java.awt.Shape; |
|
import java.awt.Stroke; |
|
|
|
import java.awt.geom.GeneralPath; |
|
import java.awt.geom.Line2D; |
|
|
|
import java.awt.font.TextAttribute; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
/** |
|
* This class provides drawing and bounds-measurement of |
|
* underlines. Additionally, it has a factory method for |
|
* obtaining underlines from values of underline attributes. |
|
*/ |
|
|
|
abstract class Underline { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
abstract void drawUnderline(Graphics2D g2d, |
|
float thickness, |
|
float x1, |
|
float x2, |
|
float y); |
|
|
|
|
|
|
|
*/ |
|
abstract float getLowerDrawLimit(float thickness); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
abstract Shape getUnderlineShape(float thickness, |
|
float x1, |
|
float x2, |
|
float y); |
|
|
|
// Implementation of underline for standard and Input Method underlines. |
|
// These classes are private. |
|
|
|
// IM Underlines ignore thickness param, and instead use |
|
|
|
private static final float DEFAULT_THICKNESS = 1.0f; |
|
|
|
// StandardUnderline's constructor takes a boolean param indicating |
|
// whether to override the default thickness. These values clarify |
|
|
|
private static final boolean USE_THICKNESS = true; |
|
private static final boolean IGNORE_THICKNESS = false; |
|
|
|
// Implementation of standard underline and all input method underlines |
|
|
|
private static final class StandardUnderline extends Underline { |
|
|
|
|
|
private float shift; |
|
|
|
// the actual line thickness is this value times |
|
|
|
private float thicknessMultiplier; |
|
|
|
// if non-null, underline is drawn with a BasicStroke |
|
|
|
private float[] dashPattern; |
|
|
|
// if false, all underlines are DEFAULT_THICKNESS thick |
|
|
|
private boolean useThickness; |
|
|
|
|
|
private BasicStroke cachedStroke; |
|
|
|
StandardUnderline(float shift, |
|
float thicknessMultiplier, |
|
float[] dashPattern, |
|
boolean useThickness) { |
|
|
|
this.shift = shift; |
|
this.thicknessMultiplier = thicknessMultiplier; |
|
this.dashPattern = dashPattern; |
|
this.useThickness = useThickness; |
|
this.cachedStroke = null; |
|
} |
|
|
|
private BasicStroke createStroke(float lineThickness) { |
|
|
|
if (dashPattern == null) { |
|
return new BasicStroke(lineThickness, |
|
BasicStroke.CAP_BUTT, |
|
BasicStroke.JOIN_MITER); |
|
} |
|
else { |
|
return new BasicStroke(lineThickness, |
|
BasicStroke.CAP_BUTT, |
|
BasicStroke.JOIN_MITER, |
|
10.0f, |
|
dashPattern, |
|
0); |
|
} |
|
} |
|
|
|
private float getLineThickness(float thickness) { |
|
|
|
if (useThickness) { |
|
return thickness * thicknessMultiplier; |
|
} |
|
else { |
|
return DEFAULT_THICKNESS * thicknessMultiplier; |
|
} |
|
} |
|
|
|
private Stroke getStroke(float thickness) { |
|
|
|
float lineThickness = getLineThickness(thickness); |
|
BasicStroke stroke = cachedStroke; |
|
if (stroke == null || |
|
stroke.getLineWidth() != lineThickness) { |
|
|
|
stroke = createStroke(lineThickness); |
|
cachedStroke = stroke; |
|
} |
|
|
|
return stroke; |
|
} |
|
|
|
void drawUnderline(Graphics2D g2d, |
|
float thickness, |
|
float x1, |
|
float x2, |
|
float y) { |
|
|
|
|
|
Stroke saveStroke = g2d.getStroke(); |
|
g2d.setStroke(getStroke(thickness)); |
|
g2d.draw(new Line2D.Float(x1, y + shift, x2, y + shift)); |
|
g2d.setStroke(saveStroke); |
|
} |
|
|
|
float getLowerDrawLimit(float thickness) { |
|
|
|
return shift + getLineThickness(thickness); |
|
} |
|
|
|
Shape getUnderlineShape(float thickness, |
|
float x1, |
|
float x2, |
|
float y) { |
|
|
|
Stroke ulStroke = getStroke(thickness); |
|
Line2D line = new Line2D.Float(x1, y + shift, x2, y + shift); |
|
return ulStroke.createStrokedShape(line); |
|
} |
|
} |
|
|
|
|
|
private static class IMGrayUnderline extends Underline { |
|
|
|
private BasicStroke stroke; |
|
|
|
IMGrayUnderline() { |
|
stroke = new BasicStroke(DEFAULT_THICKNESS, |
|
BasicStroke.CAP_BUTT, |
|
BasicStroke.JOIN_MITER, |
|
10.0f, |
|
new float[] {1, 1}, |
|
0); |
|
} |
|
|
|
void drawUnderline(Graphics2D g2d, |
|
float thickness, |
|
float x1, |
|
float x2, |
|
float y) { |
|
|
|
Stroke saveStroke = g2d.getStroke(); |
|
g2d.setStroke(stroke); |
|
|
|
Line2D.Float drawLine = new Line2D.Float(x1, y, x2, y); |
|
g2d.draw(drawLine); |
|
|
|
drawLine.y1 += DEFAULT_THICKNESS; |
|
drawLine.y2 += DEFAULT_THICKNESS; |
|
drawLine.x1 += DEFAULT_THICKNESS; |
|
|
|
g2d.draw(drawLine); |
|
|
|
g2d.setStroke(saveStroke); |
|
} |
|
|
|
float getLowerDrawLimit(float thickness) { |
|
|
|
return DEFAULT_THICKNESS * 2; |
|
} |
|
|
|
Shape getUnderlineShape(float thickness, |
|
float x1, |
|
float x2, |
|
float y) { |
|
|
|
GeneralPath gp = new GeneralPath(); |
|
|
|
Line2D.Float line = new Line2D.Float(x1, y, x2, y); |
|
gp.append(stroke.createStrokedShape(line), false); |
|
|
|
line.y1 += DEFAULT_THICKNESS; |
|
line.y2 += DEFAULT_THICKNESS; |
|
line.x1 += DEFAULT_THICKNESS; |
|
|
|
gp.append(stroke.createStrokedShape(line), false); |
|
|
|
return gp; |
|
} |
|
} |
|
|
|
// Keep a map of underlines, one for each type |
|
// of underline. The Underline objects are Flyweights |
|
// (shared across multiple clients), so they should be immutable. |
|
// If this implementation changes then clone underline |
|
|
|
private static final ConcurrentHashMap<Object, Underline> |
|
UNDERLINES = new ConcurrentHashMap<Object, Underline>(6); |
|
private static final Underline[] UNDERLINE_LIST; |
|
|
|
static { |
|
Underline[] uls = new Underline[6]; |
|
|
|
uls[0] = new StandardUnderline(0, 1, null, USE_THICKNESS); |
|
UNDERLINES.put(TextAttribute.UNDERLINE_ON, uls[0]); |
|
|
|
uls[1] = new StandardUnderline(1, 1, null, IGNORE_THICKNESS); |
|
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_ONE_PIXEL, uls[1]); |
|
|
|
uls[2] = new StandardUnderline(1, 2, null, IGNORE_THICKNESS); |
|
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_TWO_PIXEL, uls[2]); |
|
|
|
uls[3] = new StandardUnderline(1, 1, new float[] { 1, 1 }, IGNORE_THICKNESS); |
|
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_DOTTED, uls[3]); |
|
|
|
uls[4] = new IMGrayUnderline(); |
|
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_GRAY, uls[4]); |
|
|
|
uls[5] = new StandardUnderline(1, 1, new float[] { 4, 4 }, IGNORE_THICKNESS); |
|
UNDERLINES.put(TextAttribute.UNDERLINE_LOW_DASHED, uls[5]); |
|
|
|
UNDERLINE_LIST = uls; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static Underline getUnderline(Object value) { |
|
|
|
if (value == null) { |
|
return null; |
|
} |
|
|
|
return (Underline) UNDERLINES.get(value); |
|
} |
|
|
|
static Underline getUnderline(int index) { |
|
return index < 0 ? null : UNDERLINE_LIST[index]; |
|
} |
|
} |