/* * 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. 1999-2003, All Rights Reserved * */ package sun.font; import java.util.Map; import java.awt.BasicStroke;import java.awt.Color;import java.awt.Graphics2D;import java.awt.Paint;import java.awt.RenderingHints;import java.awt.Shape;import java.awt.Stroke; import java.awt.font.TextAttribute; import java.awt.geom.Area;import java.awt.geom.Line2D;import java.awt.geom.Rectangle2D;import java.awt.geom.GeneralPath; import static sun.font.AttributeValues.*;import static sun.font.EAttribute.*; /** * This class handles underlining, strikethrough, and foreground and * background styles on text. Clients simply acquire instances * of this class and hand them off to ExtendedTextLabels or GraphicComponents. */public class Decoration { /** * This interface is implemented by clients that use Decoration. * Unfortunately, interface methods have to public; ideally these * would be package-private. */ public interface Label { CoreMetrics getCoreMetrics(); Rectangle2D getLogicalBounds(); void handleDraw(Graphics2D g2d, float x, float y); Rectangle2D handleGetCharVisualBounds(int index); Rectangle2D handleGetVisualBounds(); Shape handleGetOutline(float x, float y); } private Decoration() { } /** * Return a Decoration which does nothing. */ public static Decoration getPlainDecoration() { return PLAIN; } private static final int VALUES_MASK = AttributeValues.getMask(EFOREGROUND, EBACKGROUND, ESWAP_COLORS, ESTRIKETHROUGH, EUNDERLINE, EINPUT_METHOD_HIGHLIGHT, EINPUT_METHOD_UNDERLINE); public static Decoration getDecoration(AttributeValues values) { if (values == null || !values.anyDefined(VALUES_MASK)) { return PLAIN; } values = values.applyIMHighlight(); return new DecorationImpl(values.getForeground(), values.getBackground(), values.getSwapColors(), values.getStrikethrough(), Underline.getUnderline(values.getUnderline()), Underline.getUnderline(values.getInputMethodUnderline())); } /** * Return a Decoration appropriate for the the given Map. * @param attributes the Map used to determine the Decoration */ public static Decoration getDecoration(Map attributes) { if (attributes == null) { return PLAIN; } return getDecoration(AttributeValues.fromMap(attributes)); } public void drawTextAndDecorations(Label label, Graphics2D g2d, float x, float y) { label.handleDraw(g2d, x, y); } public Rectangle2D getVisualBounds(Label label) { return label.handleGetVisualBounds(); } public Rectangle2D getCharVisualBounds(Label label, int index) { return label.handleGetCharVisualBounds(index); } Shape getOutline(Label label, float x, float y) { return label.handleGetOutline(x, y); } private static final Decoration PLAIN = new Decoration(); private static final class DecorationImpl extends Decoration { private Paint fgPaint = null; private Paint bgPaint = null; private boolean swapColors = false; private boolean strikethrough = false; private Underline stdUnderline = null; // underline from TextAttribute.UNDERLINE_ON private Underline imUnderline = null; // input method underline DecorationImpl(Paint foreground, Paint background, boolean swapColors, boolean strikethrough, Underline stdUnderline, Underline imUnderline) { fgPaint = (Paint) foreground; bgPaint = (Paint) background; this.swapColors = swapColors; this.strikethrough = strikethrough; this.stdUnderline = stdUnderline; this.imUnderline = imUnderline; } private static boolean areEqual(Object lhs, Object rhs) { if (lhs == null) { return rhs == null; } else { return lhs.equals(rhs); } } public boolean equals(Object rhs) { if (rhs == this) { return true; } if (rhs == null) { return false; } DecorationImpl other = null; try { other = (DecorationImpl) rhs; } catch(ClassCastException e) { return false; } if (!(swapColors == other.swapColors && strikethrough == other.strikethrough)) { return false; } if (!areEqual(stdUnderline, other.stdUnderline)) { return false; } if (!areEqual(fgPaint, other.fgPaint)) { return false; } if (!areEqual(bgPaint, other.bgPaint)) { return false; } return areEqual(imUnderline, other.imUnderline); } public int hashCode() { int hc = 1; if (strikethrough) { hc |= 2; } if (swapColors) { hc |= 4; } if (stdUnderline != null) { hc += stdUnderline.hashCode(); } return hc; } /** * Return the bottom of the Rectangle which encloses pixels * drawn by underlines. */ private float getUnderlineMaxY(CoreMetrics cm) { float maxY = 0; if (stdUnderline != null) { float ulBottom = cm.underlineOffset; ulBottom += stdUnderline.getLowerDrawLimit(cm.underlineThickness); maxY = Math.max(maxY, ulBottom); } if (imUnderline != null) { float ulBottom = cm.underlineOffset; ulBottom += imUnderline.getLowerDrawLimit(cm.underlineThickness); maxY = Math.max(maxY, ulBottom); } return maxY; } private void drawTextAndEmbellishments(Label label, Graphics2D g2d, float x, float y) { label.handleDraw(g2d, x, y); if (!strikethrough && stdUnderline == null && imUnderline == null) { return; } float x1 = x; float x2 = x1 + (float)label.getLogicalBounds().getWidth(); CoreMetrics cm = label.getCoreMetrics(); if (strikethrough) { Stroke savedStroke = g2d.getStroke(); g2d.setStroke(new BasicStroke(cm.strikethroughThickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER)); float strikeY = y + cm.strikethroughOffset; g2d.draw(new Line2D.Float(x1, strikeY, x2, strikeY)); g2d.setStroke(savedStroke); } float ulOffset = cm.underlineOffset; float ulThickness = cm.underlineThickness; if (stdUnderline != null) { stdUnderline.drawUnderline(g2d, ulThickness, x1, x2, y + ulOffset); } if (imUnderline != null) { imUnderline.drawUnderline(g2d, ulThickness, x1, x2, y + ulOffset); } } public void drawTextAndDecorations(Label label, Graphics2D g2d, float x, float y) { if (fgPaint == null && bgPaint == null && swapColors == false) { drawTextAndEmbellishments(label, g2d, x, y); } else { Paint savedPaint = g2d.getPaint(); Paint foreground, background; if (swapColors) { background = fgPaint==null? savedPaint : fgPaint; if (bgPaint == null) { if (background instanceof Color) { Color bg = (Color)background; // 30/59/11 is standard weights, tweaked a bit int brightness = 33 * bg.getRed() + 53 * bg.getGreen() + 14 * bg.getBlue(); foreground = brightness > 18500 ? Color.BLACK : Color.WHITE; } else { foreground = Color.WHITE; } } else { foreground = bgPaint; } } else { foreground = fgPaint==null? savedPaint : fgPaint; background = bgPaint; } if (background != null) { Rectangle2D bgArea = label.getLogicalBounds(); bgArea = new Rectangle2D.Float(x + (float)bgArea.getX(), y + (float)bgArea.getY(), (float)bgArea.getWidth(), (float)bgArea.getHeight()); g2d.setPaint(background); g2d.fill(bgArea); } g2d.setPaint(foreground); drawTextAndEmbellishments(label, g2d, x, y); g2d.setPaint(savedPaint); } } public Rectangle2D getVisualBounds(Label label) { Rectangle2D visBounds = label.handleGetVisualBounds(); if (swapColors || bgPaint != null || strikethrough || stdUnderline != null || imUnderline != null) { float minX = 0; Rectangle2D lb = label.getLogicalBounds(); float minY = 0, maxY = 0; if (swapColors || bgPaint != null) { minY = (float)lb.getY(); maxY = minY + (float)lb.getHeight(); } maxY = Math.max(maxY, getUnderlineMaxY(label.getCoreMetrics())); Rectangle2D ab = new Rectangle2D.Float(minX, minY, (float)lb.getWidth(), maxY-minY); visBounds.add(ab); } return visBounds; } Shape getOutline(Label label, float x, float y) { if (!strikethrough && stdUnderline == null && imUnderline == null) { return label.handleGetOutline(x, y); } CoreMetrics cm = label.getCoreMetrics(); // NOTE: The performace of the following code may // be very poor. float ulThickness = cm.underlineThickness; float ulOffset = cm.underlineOffset; Rectangle2D lb = label.getLogicalBounds(); float x1 = x; float x2 = x1 + (float)lb.getWidth(); Area area = null; if (stdUnderline != null) { Shape ul = stdUnderline.getUnderlineShape(ulThickness, x1, x2, y+ulOffset); area = new Area(ul); } if (strikethrough) { Stroke stStroke = new BasicStroke(cm.strikethroughThickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); float shiftY = y + cm.strikethroughOffset; Line2D line = new Line2D.Float(x1, shiftY, x2, shiftY); Area slArea = new Area(stStroke.createStrokedShape(line)); if(area == null) { area = slArea; } else { area.add(slArea); } } if (imUnderline != null) { Shape ul = imUnderline.getUnderlineShape(ulThickness, x1, x2, y+ulOffset); Area ulArea = new Area(ul); if (area == null) { area = ulArea; } else { area.add(ulArea); } } // area won't be null here, since at least one underline exists. area.add(new Area(label.handleGetOutline(x, y))); return new GeneralPath(area); } public String toString() { StringBuffer buf = new StringBuffer(); buf.append(super.toString()); buf.append("["); if (fgPaint != null) buf.append("fgPaint: " + fgPaint); if (bgPaint != null) buf.append(" bgPaint: " + bgPaint); if (swapColors) buf.append(" swapColors: true"); if (strikethrough) buf.append(" strikethrough: true"); if (stdUnderline != null) buf.append(" stdUnderline: " + stdUnderline); if (imUnderline != null) buf.append(" imUnderline: " + imUnderline); buf.append("]"); return buf.toString(); } }}
/*
* 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. 1999-2003, All Rights Reserved
*
*/
package sun.font;
import java.util.Map;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.TextAttribute;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.GeneralPath;
import static sun.font.AttributeValues.*;
import static sun.font.EAttribute.*;
/**
* This class handles underlining, strikethrough, and foreground and
* background styles on text. Clients simply acquire instances
* of this class and hand them off to ExtendedTextLabels or GraphicComponents.
*/
public class Decoration {
/**
* This interface is implemented by clients that use Decoration.
* Unfortunately, interface methods have to public; ideally these
* would be package-private.
*/
public interface Label {
CoreMetrics getCoreMetrics();
Rectangle2D getLogicalBounds();
void handleDraw(Graphics2D g2d, float x, float y);
Rectangle2D handleGetCharVisualBounds(int index);
Rectangle2D handleGetVisualBounds();
Shape handleGetOutline(float x, float y);
}
private Decoration() {
}
/**
* Return a Decoration which does nothing.
*/
public static Decoration getPlainDecoration() {
return PLAIN;
}
private static final int VALUES_MASK =
AttributeValues.getMask(EFOREGROUND, EBACKGROUND, ESWAP_COLORS,
ESTRIKETHROUGH, EUNDERLINE, EINPUT_METHOD_HIGHLIGHT,
EINPUT_METHOD_UNDERLINE);
public static Decoration getDecoration(AttributeValues values) {
if (values == null || !values.anyDefined(VALUES_MASK)) {
return PLAIN;
}
values = values.applyIMHighlight();
return new DecorationImpl(values.getForeground(),
values.getBackground(),
values.getSwapColors(),
values.getStrikethrough(),
Underline.getUnderline(values.getUnderline()),
Underline.getUnderline(values.getInputMethodUnderline()));
}
/**
* Return a Decoration appropriate for the the given Map.
* @param attributes the Map used to determine the Decoration
*/
public static Decoration getDecoration(Map attributes) {
if (attributes == null) {
return PLAIN;
}
return getDecoration(AttributeValues.fromMap(attributes));
}
public void drawTextAndDecorations(Label label,
Graphics2D g2d,
float x,
float y) {
label.handleDraw(g2d, x, y);
}
public Rectangle2D getVisualBounds(Label label) {
return label.handleGetVisualBounds();
}
public Rectangle2D getCharVisualBounds(Label label, int index) {
return label.handleGetCharVisualBounds(index);
}
Shape getOutline(Label label,
float x,
float y) {
return label.handleGetOutline(x, y);
}
private static final Decoration PLAIN = new Decoration();
private static final class DecorationImpl extends Decoration {
private Paint fgPaint = null;
private Paint bgPaint = null;
private boolean swapColors = false;
private boolean strikethrough = false;
private Underline stdUnderline = null; // underline from TextAttribute.UNDERLINE_ON
private Underline imUnderline = null; // input method underline
DecorationImpl(Paint foreground,
Paint background,
boolean swapColors,
boolean strikethrough,
Underline stdUnderline,
Underline imUnderline) {
fgPaint = (Paint) foreground;
bgPaint = (Paint) background;
this.swapColors = swapColors;
this.strikethrough = strikethrough;
this.stdUnderline = stdUnderline;
this.imUnderline = imUnderline;
}
private static boolean areEqual(Object lhs, Object rhs) {
if (lhs == null) {
return rhs == null;
}
else {
return lhs.equals(rhs);
}
}
public boolean equals(Object rhs) {
if (rhs == this) {
return true;
}
if (rhs == null) {
return false;
}
DecorationImpl other = null;
try {
other = (DecorationImpl) rhs;
}
catch(ClassCastException e) {
return false;
}
if (!(swapColors == other.swapColors &&
strikethrough == other.strikethrough)) {
return false;
}
if (!areEqual(stdUnderline, other.stdUnderline)) {
return false;
}
if (!areEqual(fgPaint, other.fgPaint)) {
return false;
}
if (!areEqual(bgPaint, other.bgPaint)) {
return false;
}
return areEqual(imUnderline, other.imUnderline);
}
public int hashCode() {
int hc = 1;
if (strikethrough) {
hc |= 2;
}
if (swapColors) {
hc |= 4;
}
if (stdUnderline != null) {
hc += stdUnderline.hashCode();
}
return hc;
}
/**
* Return the bottom of the Rectangle which encloses pixels
* drawn by underlines.
*/
private float getUnderlineMaxY(CoreMetrics cm) {
float maxY = 0;
if (stdUnderline != null) {
float ulBottom = cm.underlineOffset;
ulBottom += stdUnderline.getLowerDrawLimit(cm.underlineThickness);
maxY = Math.max(maxY, ulBottom);
}
if (imUnderline != null) {
float ulBottom = cm.underlineOffset;
ulBottom += imUnderline.getLowerDrawLimit(cm.underlineThickness);
maxY = Math.max(maxY, ulBottom);
}
return maxY;
}
private void drawTextAndEmbellishments(Label label,
Graphics2D g2d,
float x,
float y) {
label.handleDraw(g2d, x, y);
if (!strikethrough && stdUnderline == null && imUnderline == null) {
return;
}
float x1 = x;
float x2 = x1 + (float)label.getLogicalBounds().getWidth();
CoreMetrics cm = label.getCoreMetrics();
if (strikethrough) {
Stroke savedStroke = g2d.getStroke();
g2d.setStroke(new BasicStroke(cm.strikethroughThickness,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER));
float strikeY = y + cm.strikethroughOffset;
g2d.draw(new Line2D.Float(x1, strikeY, x2, strikeY));
g2d.setStroke(savedStroke);
}
float ulOffset = cm.underlineOffset;
float ulThickness = cm.underlineThickness;
if (stdUnderline != null) {
stdUnderline.drawUnderline(g2d, ulThickness, x1, x2, y + ulOffset);
}
if (imUnderline != null) {
imUnderline.drawUnderline(g2d, ulThickness, x1, x2, y + ulOffset);
}
}
public void drawTextAndDecorations(Label label,
Graphics2D g2d,
float x,
float y) {
if (fgPaint == null && bgPaint == null && swapColors == false) {
drawTextAndEmbellishments(label, g2d, x, y);
}
else {
Paint savedPaint = g2d.getPaint();
Paint foreground, background;
if (swapColors) {
background = fgPaint==null? savedPaint : fgPaint;
if (bgPaint == null) {
if (background instanceof Color) {
Color bg = (Color)background;
// 30/59/11 is standard weights, tweaked a bit
int brightness = 33 * bg.getRed()
+ 53 * bg.getGreen()
+ 14 * bg.getBlue();
foreground = brightness > 18500 ? Color.BLACK : Color.WHITE;
} else {
foreground = Color.WHITE;
}
} else {
foreground = bgPaint;
}
}
else {
foreground = fgPaint==null? savedPaint : fgPaint;
background = bgPaint;
}
if (background != null) {
Rectangle2D bgArea = label.getLogicalBounds();
bgArea = new Rectangle2D.Float(x + (float)bgArea.getX(),
y + (float)bgArea.getY(),
(float)bgArea.getWidth(),
(float)bgArea.getHeight());
g2d.setPaint(background);
g2d.fill(bgArea);
}
g2d.setPaint(foreground);
drawTextAndEmbellishments(label, g2d, x, y);
g2d.setPaint(savedPaint);
}
}
public Rectangle2D getVisualBounds(Label label) {
Rectangle2D visBounds = label.handleGetVisualBounds();
if (swapColors || bgPaint != null || strikethrough
|| stdUnderline != null || imUnderline != null) {
float minX = 0;
Rectangle2D lb = label.getLogicalBounds();
float minY = 0, maxY = 0;
if (swapColors || bgPaint != null) {
minY = (float)lb.getY();
maxY = minY + (float)lb.getHeight();
}
maxY = Math.max(maxY, getUnderlineMaxY(label.getCoreMetrics()));
Rectangle2D ab = new Rectangle2D.Float(minX, minY, (float)lb.getWidth(), maxY-minY);
visBounds.add(ab);
}
return visBounds;
}
Shape getOutline(Label label,
float x,
float y) {
if (!strikethrough && stdUnderline == null && imUnderline == null) {
return label.handleGetOutline(x, y);
}
CoreMetrics cm = label.getCoreMetrics();
// NOTE: The performace of the following code may
// be very poor.
float ulThickness = cm.underlineThickness;
float ulOffset = cm.underlineOffset;
Rectangle2D lb = label.getLogicalBounds();
float x1 = x;
float x2 = x1 + (float)lb.getWidth();
Area area = null;
if (stdUnderline != null) {
Shape ul = stdUnderline.getUnderlineShape(ulThickness,
x1, x2, y+ulOffset);
area = new Area(ul);
}
if (strikethrough) {
Stroke stStroke = new BasicStroke(cm.strikethroughThickness,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER);
float shiftY = y + cm.strikethroughOffset;
Line2D line = new Line2D.Float(x1, shiftY, x2, shiftY);
Area slArea = new Area(stStroke.createStrokedShape(line));
if(area == null) {
area = slArea;
} else {
area.add(slArea);
}
}
if (imUnderline != null) {
Shape ul = imUnderline.getUnderlineShape(ulThickness,
x1, x2, y+ulOffset);
Area ulArea = new Area(ul);
if (area == null) {
area = ulArea;
}
else {
area.add(ulArea);
}
}
// area won't be null here, since at least one underline exists.
area.add(new Area(label.handleGetOutline(x, y)));
return new GeneralPath(area);
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append(super.toString());
buf.append("[");
if (fgPaint != null) buf.append("fgPaint: " + fgPaint);
if (bgPaint != null) buf.append(" bgPaint: " + bgPaint);
if (swapColors) buf.append(" swapColors: true");
if (strikethrough) buf.append(" strikethrough: true");
if (stdUnderline != null) buf.append(" stdUnderline: " + stdUnderline);
if (imUnderline != null) buf.append(" imUnderline: " + imUnderline);
buf.append("]");
return buf.toString();
}
}
}