/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2003-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.renderer.style;
// J2SE dependencies
import java.awt.BasicStroke;
import java.awt.Composite;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.font.GlyphVector;
import java.awt.image.BufferedImage;
import java.text.Bidi;
import javax.swing.Icon;
import org.geotools.resources.Classes;
/**
* Style used to represent labels over lines, polygons and points
*
*
* @author Andrea Aime
* @author dblasby
* @version $Id$
*/
/** DJB:
*
* This class was fundamentally wrong - it tried to convert <LinePlacement> into <PointPlacement>.
* Not only was it doing a really crappy job, but its fundamentally the wrong place to do it.
*
* The SLD spec defines a <PointPlacement> as:
* <xsd:sequence>
* <xsd:element ref="sld:AnchorPoint" minOccurs="0"/>
* <xsd:element ref="sld:Displacement" minOccurs="0"/>
* <xsd:element ref="sld:Rotation" minOccurs="0"/>
* </xsd:sequence>
*
* and <LinePlacement> as:
* <xsd:sequence>
* <xsd:element ref="sld:PerpendicularOffset "minOccurs="0"/>
* </xsd:sequence>
*
* its annotated as:
* A "PerpendicularOffset" gives the perpendicular distance away from a line to draw a label.
* which is a bit vague, but there's a little more details here:
*
* The PerpendicularOffset element of a LinePlacement gives the perpendicular distance away from a line to draw a label. ...
* The distance is in pixels and is positive to the left-hand.
*
* Left hand/right hand for perpendicularOffset is just crap - I'm assuming them mean +ive --> "up" and -ive --> "down".
* See the actual label code for how it deals with this.
*
* I've removed all the absoluteLineDisplacement stuff and replaced it with
* isPointPlacement() (true) --> render normally (PointPlacement Attributes)
* isPointPlacement() (false) --> render LinePlacement
*
* This replaces the old behavior which converted a LinePlacement -> pointplacement and set the absoluteLineDisplacement flag!
*
*
* @source $URL$
* */
public class TextStyle2D extends Style2D {
GlyphVector textGlyphVector;
Shape haloShape;
String label;
Font font;
double rotation;
/** yes = <PointPlacement> no = <LinePlacement> default = yes**/
boolean pointPlacement = true;
int perpendicularOffset =0; // only valid when using a LinePlacement
double anchorX;
double anchorY;
double displacementX;
double displacementY;
Paint haloFill;
Composite haloComposite;
float haloRadius;
Style2D graphic;
/** Holds value of property fill. */
private Paint fill;
/** Holds value of property composite. */
private Composite composite;
public TextStyle2D() {
// default constructor
}
public TextStyle2D(TextStyle2D t) {
this.anchorX = t.anchorX;
this.anchorY = t.anchorY;
this.composite = t.composite;
this.displacementX = t.displacementX;
this.displacementY = t.displacementY;
this.fill = t.fill;
this.font = t.font;
this.graphic = t.graphic;
this.haloComposite = t.haloComposite;
this.haloFill = t.haloFill;
this.haloRadius = t.haloRadius;
this.haloShape = t.haloShape;
this.label = t.label;
this.maxScale = t.maxScale;
this.minScale = t.minScale;
this.perpendicularOffset = t.perpendicularOffset;
this.pointPlacement = t.pointPlacement;
this.rotation = t.rotation;
this.textGlyphVector = t.textGlyphVector;
}
/**
*/
public double getAnchorX() {
return anchorX;
}
/**
*/
public double getAnchorY() {
return anchorY;
}
/**
*/
public Font getFont() {
return font;
}
/**
*/
public Composite getHaloComposite() {
return haloComposite;
}
/**
*/
public Paint getHaloFill() {
return haloFill;
}
/**
*/
public float getHaloRadius() {
return haloRadius;
}
/**
*/
public double getRotation() {
return rotation;
}
/**
* recompute each time
*/
public GlyphVector getTextGlyphVector(Graphics2D graphics) {
// arabic and hebrew are scripted and right to left, they do require full layout
// whilst western chars are easier to deal with. Find out which case we're dealing with,
// and create the glyph vector with the appropriate call
final char[] chars = label.toCharArray();
final int length = label.length();
if(Bidi.requiresBidi(chars, 0, length) &&
new Bidi(label, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT).isRightToLeft())
textGlyphVector = font.layoutGlyphVector(graphics.getFontRenderContext(), chars,
0, length, Font.LAYOUT_RIGHT_TO_LEFT);
else
textGlyphVector = font.createGlyphVector(graphics.getFontRenderContext(), chars);
return textGlyphVector;
}
/**
*/
public Shape getHaloShape(Graphics2D graphics)
{
GlyphVector gv = getTextGlyphVector(graphics);
haloShape = new BasicStroke(2f * haloRadius, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND).createStrokedShape(gv.getOutline());
return haloShape;
}
/**
* @param f
*/
public void setAnchorX(double f) {
anchorX = f;
}
/**
* @param f
*/
public void setAnchorY(double f) {
anchorY = f;
}
/**
* @param font
*/
public void setFont(Font font) {
this.font = font;
}
/**
* @param composite
*/
public void setHaloComposite(Composite composite) {
haloComposite = composite;
}
/**
* @param paint
*/
public void setHaloFill(Paint paint) {
haloFill = paint;
}
/**
* @param f
*/
public void setHaloRadius(float f) {
haloRadius = f;
}
/**
* @param f
*/
public void setRotation(double f) {
rotation = f;
}
/**
* @return Returns the label.
*/
public String getLabel() {
return label;
}
/**
* @param label The label to set.
*/
public void setLabel(String label) {
this.label = label;
}
/**
* @return Returns the pointPlacement (true => point placement, false => line placement)
*/
public boolean isPointPlacement() {
return pointPlacement;
}
/**
* @param pointPlacement (true => point placement, false => line placement.)
*/
public void setPointPlacement(boolean pointPlacement) {
this.pointPlacement = pointPlacement;
}
/**
* @return Returns the displacementX.
*/
public double getDisplacementX() {
return displacementX;
}
/**
* @param displacementX The displacementX to set.
*/
public void setDisplacementX(double displacementX) {
this.displacementX = displacementX;
}
/**
* @return Returns the displacementY.
*/
public double getDisplacementY() {
return displacementY;
}
/**
* @param displacementY The displacementY to set.
*/
public void setDisplacementY(double displacementY) {
this.displacementY = displacementY;
}
/**
* Getter for property fill.
*
* @return Value of property fill.
*/
public Paint getFill() {
return this.fill;
}
/**
* Setter for property fill.
*
* @param fill New value of property fill.
*/
public void setFill(Paint fill) {
this.fill = fill;
}
/**
* only valid for a isPointPlacement=false (ie. a lineplacement)
* @param displace in pixels
*/
public void setPerpendicularOffset(int displace)
{
perpendicularOffset = displace;
}
/**
* only valid for a isPointPlacement=false (ie. a lineplacement)
* @return displacement in pixels
*/
public int getPerpendicularOffset()
{
return perpendicularOffset;
}
/**
* Getter for property composite.
*
* @return Value of property composite.
*/
public Composite getComposite() {
return this.composite;
}
/**
* Setter for property composite.
*
* @param composite New value of property composite.
*/
public void setComposite(Composite composite) {
this.composite = composite;
}
/**
* Returns a string representation of this style.
*/
public String toString() {
return Classes.getShortClassName(this) + "[\"" + label + "\"]";
}
/**
* Sets the style2D to be drawn underneath this text
*/
public void setGraphic(Style2D s) {
graphic = s;
}
/**
* gets the Style2D to be drawn underneath this text
*/
public Style2D getGraphic() {
return graphic;
}
public Rectangle getGraphicDimensions() {
if (graphic instanceof MarkStyle2D) {
return ((MarkStyle2D)graphic).getTransformedShape(0f, 0f).getBounds();
} else if (graphic instanceof GraphicStyle2D) {
BufferedImage i = ((GraphicStyle2D)graphic).getImage();
return new Rectangle(i.getWidth(),i.getHeight());
} else if(graphic instanceof IconStyle2D) {
final Icon icon = ((IconStyle2D) graphic).getIcon();
return new Rectangle(icon.getIconWidth(), icon.getIconWidth());
} else {
throw new RuntimeException("Can't render graphic which is not a MarkStyle2D or a GraphicStyle2D");
}
}
}