309 lines
10 KiB
Java
309 lines
10 KiB
Java
/*
|
|
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
/*
|
|
*
|
|
* (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 {
|
|
|
|
/**
|
|
* Draws the underline into g2d. The thickness should be obtained
|
|
* from a LineMetrics object. Note that some underlines ignore the
|
|
* thickness parameter.
|
|
* The underline is drawn from (x1, y) to (x2, y).
|
|
*/
|
|
abstract void drawUnderline(Graphics2D g2d,
|
|
float thickness,
|
|
float x1,
|
|
float x2,
|
|
float y);
|
|
|
|
/**
|
|
* Returns the bottom of the bounding rectangle for this underline.
|
|
*/
|
|
abstract float getLowerDrawLimit(float thickness);
|
|
|
|
/**
|
|
* Returns a Shape representing the underline. The thickness should be obtained
|
|
* from a LineMetrics object. Note that some underlines ignore the
|
|
* thickness parameter.
|
|
*/
|
|
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
|
|
// DEFAULT_THICKNESS
|
|
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
|
|
// the semantics of the parameter.
|
|
private static final boolean USE_THICKNESS = true;
|
|
private static final boolean IGNORE_THICKNESS = false;
|
|
|
|
// Implementation of standard underline and all input method underlines
|
|
// except UNDERLINE_LOW_GRAY.
|
|
private static final class StandardUnderline extends Underline {
|
|
|
|
// the amount by which to move the underline
|
|
private float shift;
|
|
|
|
// the actual line thickness is this value times
|
|
// the requested thickness
|
|
private float thicknessMultiplier;
|
|
|
|
// if non-null, underline is drawn with a BasicStroke
|
|
// with this dash pattern
|
|
private float[] dashPattern;
|
|
|
|
// if false, all underlines are DEFAULT_THICKNESS thick
|
|
// if true, use thickness param
|
|
private boolean useThickness;
|
|
|
|
// cached BasicStroke
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Implementation of UNDERLINE_LOW_GRAY.
|
|
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
|
|
// instances in getUnderline before returning them.
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Return the Underline for the given value of
|
|
* TextAttribute.INPUT_METHOD_UNDERLINE or
|
|
* TextAttribute.UNDERLINE.
|
|
* If value is not an input method underline value or
|
|
* TextAttribute.UNDERLINE_ON, null is returned.
|
|
*/
|
|
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];
|
|
}
|
|
}
|