0N/A/*
2362N/A * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A/*
0N/A *
0N/A * (C) Copyright IBM Corp. 1998, All Rights Reserved
0N/A */
0N/A
0N/Apackage sun.font;
0N/A
0N/Aimport java.awt.BasicStroke;
0N/Aimport java.awt.Graphics2D;
0N/Aimport java.awt.Shape;
0N/Aimport java.awt.Stroke;
0N/A
0N/Aimport java.awt.geom.GeneralPath;
0N/Aimport java.awt.geom.Line2D;
0N/A
0N/Aimport java.awt.font.TextAttribute;
0N/A
0N/Aimport java.util.concurrent.ConcurrentHashMap;
0N/A
0N/A/**
0N/A * This class provides drawing and bounds-measurement of
0N/A * underlines. Additionally, it has a factory method for
0N/A * obtaining underlines from values of underline attributes.
0N/A */
0N/A
0N/Aabstract class Underline {
0N/A
0N/A /**
0N/A * Draws the underline into g2d. The thickness should be obtained
0N/A * from a LineMetrics object. Note that some underlines ignore the
0N/A * thickness parameter.
0N/A * The underline is drawn from (x1, y) to (x2, y).
0N/A */
0N/A abstract void drawUnderline(Graphics2D g2d,
0N/A float thickness,
0N/A float x1,
0N/A float x2,
0N/A float y);
0N/A
0N/A /**
0N/A * Returns the bottom of the bounding rectangle for this underline.
0N/A */
0N/A abstract float getLowerDrawLimit(float thickness);
0N/A
0N/A /**
0N/A * Returns a Shape representing the underline. The thickness should be obtained
0N/A * from a LineMetrics object. Note that some underlines ignore the
0N/A * thickness parameter.
0N/A */
0N/A abstract Shape getUnderlineShape(float thickness,
0N/A float x1,
0N/A float x2,
0N/A float y);
0N/A
0N/A // Implementation of underline for standard and Input Method underlines.
0N/A // These classes are private.
0N/A
0N/A // IM Underlines ignore thickness param, and instead use
0N/A // DEFAULT_THICKNESS
0N/A private static final float DEFAULT_THICKNESS = 1.0f;
0N/A
0N/A // StandardUnderline's constructor takes a boolean param indicating
0N/A // whether to override the default thickness. These values clarify
0N/A // the semantics of the parameter.
0N/A private static final boolean USE_THICKNESS = true;
0N/A private static final boolean IGNORE_THICKNESS = false;
0N/A
0N/A // Implementation of standard underline and all input method underlines
0N/A // except UNDERLINE_LOW_GRAY.
0N/A private static final class StandardUnderline extends Underline {
0N/A
0N/A // the amount by which to move the underline
0N/A private float shift;
0N/A
0N/A // the actual line thickness is this value times
0N/A // the requested thickness
0N/A private float thicknessMultiplier;
0N/A
0N/A // if non-null, underline is drawn with a BasicStroke
0N/A // with this dash pattern
0N/A private float[] dashPattern;
0N/A
0N/A // if false, all underlines are DEFAULT_THICKNESS thick
0N/A // if true, use thickness param
0N/A private boolean useThickness;
0N/A
0N/A // cached BasicStroke
0N/A private BasicStroke cachedStroke;
0N/A
0N/A StandardUnderline(float shift,
0N/A float thicknessMultiplier,
0N/A float[] dashPattern,
0N/A boolean useThickness) {
0N/A
0N/A this.shift = shift;
0N/A this.thicknessMultiplier = thicknessMultiplier;
0N/A this.dashPattern = dashPattern;
0N/A this.useThickness = useThickness;
0N/A this.cachedStroke = null;
0N/A }
0N/A
0N/A private BasicStroke createStroke(float lineThickness) {
0N/A
0N/A if (dashPattern == null) {
761N/A return new BasicStroke(lineThickness,
761N/A BasicStroke.CAP_BUTT,
761N/A BasicStroke.JOIN_MITER);
0N/A }
0N/A else {
0N/A return new BasicStroke(lineThickness,
0N/A BasicStroke.CAP_BUTT,
0N/A BasicStroke.JOIN_MITER,
0N/A 10.0f,
0N/A dashPattern,
0N/A 0);
0N/A }
0N/A }
0N/A
0N/A private float getLineThickness(float thickness) {
0N/A
0N/A if (useThickness) {
0N/A return thickness * thicknessMultiplier;
0N/A }
0N/A else {
0N/A return DEFAULT_THICKNESS * thicknessMultiplier;
0N/A }
0N/A }
0N/A
0N/A private Stroke getStroke(float thickness) {
0N/A
0N/A float lineThickness = getLineThickness(thickness);
0N/A BasicStroke stroke = cachedStroke;
0N/A if (stroke == null ||
0N/A stroke.getLineWidth() != lineThickness) {
0N/A
0N/A stroke = createStroke(lineThickness);
0N/A cachedStroke = stroke;
0N/A }
0N/A
0N/A return stroke;
0N/A }
0N/A
0N/A void drawUnderline(Graphics2D g2d,
0N/A float thickness,
0N/A float x1,
0N/A float x2,
0N/A float y) {
0N/A
0N/A
0N/A Stroke saveStroke = g2d.getStroke();
0N/A g2d.setStroke(getStroke(thickness));
0N/A g2d.draw(new Line2D.Float(x1, y + shift, x2, y + shift));
0N/A g2d.setStroke(saveStroke);
0N/A }
0N/A
0N/A float getLowerDrawLimit(float thickness) {
0N/A
0N/A return shift + getLineThickness(thickness);
0N/A }
0N/A
0N/A Shape getUnderlineShape(float thickness,
0N/A float x1,
0N/A float x2,
0N/A float y) {
0N/A
0N/A Stroke ulStroke = getStroke(thickness);
0N/A Line2D line = new Line2D.Float(x1, y + shift, x2, y + shift);
0N/A return ulStroke.createStrokedShape(line);
0N/A }
0N/A }
0N/A
0N/A // Implementation of UNDERLINE_LOW_GRAY.
0N/A private static class IMGrayUnderline extends Underline {
0N/A
0N/A private BasicStroke stroke;
0N/A
0N/A IMGrayUnderline() {
0N/A stroke = new BasicStroke(DEFAULT_THICKNESS,
0N/A BasicStroke.CAP_BUTT,
0N/A BasicStroke.JOIN_MITER,
0N/A 10.0f,
0N/A new float[] {1, 1},
0N/A 0);
0N/A }
0N/A
0N/A void drawUnderline(Graphics2D g2d,
0N/A float thickness,
0N/A float x1,
0N/A float x2,
0N/A float y) {
0N/A
0N/A Stroke saveStroke = g2d.getStroke();
0N/A g2d.setStroke(stroke);
0N/A
0N/A Line2D.Float drawLine = new Line2D.Float(x1, y, x2, y);
0N/A g2d.draw(drawLine);
0N/A
0N/A drawLine.y1 += DEFAULT_THICKNESS;
0N/A drawLine.y2 += DEFAULT_THICKNESS;
0N/A drawLine.x1 += DEFAULT_THICKNESS;
0N/A
0N/A g2d.draw(drawLine);
0N/A
0N/A g2d.setStroke(saveStroke);
0N/A }
0N/A
0N/A float getLowerDrawLimit(float thickness) {
0N/A
0N/A return DEFAULT_THICKNESS * 2;
0N/A }
0N/A
0N/A Shape getUnderlineShape(float thickness,
0N/A float x1,
0N/A float x2,
0N/A float y) {
0N/A
0N/A GeneralPath gp = new GeneralPath();
0N/A
0N/A Line2D.Float line = new Line2D.Float(x1, y, x2, y);
0N/A gp.append(stroke.createStrokedShape(line), false);
0N/A
0N/A line.y1 += DEFAULT_THICKNESS;
0N/A line.y2 += DEFAULT_THICKNESS;
0N/A line.x1 += DEFAULT_THICKNESS;
0N/A
0N/A gp.append(stroke.createStrokedShape(line), false);
0N/A
0N/A return gp;
0N/A }
0N/A }
0N/A
0N/A // Keep a map of underlines, one for each type
0N/A // of underline. The Underline objects are Flyweights
0N/A // (shared across multiple clients), so they should be immutable.
0N/A // If this implementation changes then clone underline
0N/A // instances in getUnderline before returning them.
0N/A private static final ConcurrentHashMap<Object, Underline>
0N/A UNDERLINES = new ConcurrentHashMap<Object, Underline>(6);
0N/A private static final Underline[] UNDERLINE_LIST;
0N/A
0N/A static {
0N/A Underline[] uls = new Underline[6];
0N/A
0N/A uls[0] = new StandardUnderline(0, 1, null, USE_THICKNESS);
0N/A UNDERLINES.put(TextAttribute.UNDERLINE_ON, uls[0]);
0N/A
0N/A uls[1] = new StandardUnderline(1, 1, null, IGNORE_THICKNESS);
0N/A UNDERLINES.put(TextAttribute.UNDERLINE_LOW_ONE_PIXEL, uls[1]);
0N/A
0N/A uls[2] = new StandardUnderline(1, 2, null, IGNORE_THICKNESS);
0N/A UNDERLINES.put(TextAttribute.UNDERLINE_LOW_TWO_PIXEL, uls[2]);
0N/A
0N/A uls[3] = new StandardUnderline(1, 1, new float[] { 1, 1 }, IGNORE_THICKNESS);
0N/A UNDERLINES.put(TextAttribute.UNDERLINE_LOW_DOTTED, uls[3]);
0N/A
0N/A uls[4] = new IMGrayUnderline();
0N/A UNDERLINES.put(TextAttribute.UNDERLINE_LOW_GRAY, uls[4]);
0N/A
0N/A uls[5] = new StandardUnderline(1, 1, new float[] { 4, 4 }, IGNORE_THICKNESS);
0N/A UNDERLINES.put(TextAttribute.UNDERLINE_LOW_DASHED, uls[5]);
0N/A
0N/A UNDERLINE_LIST = uls;
0N/A }
0N/A
0N/A /**
0N/A * Return the Underline for the given value of
0N/A * TextAttribute.INPUT_METHOD_UNDERLINE or
0N/A * TextAttribute.UNDERLINE.
0N/A * If value is not an input method underline value or
0N/A * TextAttribute.UNDERLINE_ON, null is returned.
0N/A */
0N/A static Underline getUnderline(Object value) {
0N/A
0N/A if (value == null) {
0N/A return null;
0N/A }
0N/A
0N/A return (Underline) UNDERLINES.get(value);
0N/A }
0N/A
0N/A static Underline getUnderline(int index) {
0N/A return index < 0 ? null : UNDERLINE_LIST[index];
0N/A }
0N/A}