to generate multiline axis labels I changed the following two methods (getTextBounds, drawRotatedString) in org.jfree.text.TextUtilities as it seemed more comfortable to me than trying to change the axis class to use textblocks (although this was a lot of work, too, despite the small result). "\n" in the axis label works as a line separator.
I did not check the result of these changement with any other thing than JFreeChart, so pay attention. The text is rotated as a block, not as single lines.
For the rest, feel free to use it and mail the problems, if you encounter any.
Code: Select all
/**
* A utility method for drawing rotated text.
* <P>
* A common rotation is -Math.PI/2 which draws text 'vertically' (with the
* top of the characters on the left).
* tk:
* This method has been modified to draw multiline strings.
* (textX, textY) is the left point on the baseline of the downmost line of text
*
* @param text the text.
* @param g2 the graphics device.
* @param textX the x-coordinate for the text (before rotation).
* @param textY the y-coordinate for the text (before rotation).
* @param angle the angle of the (clockwise) rotation (in radians).
* @param rotateX the point about which the text is rotated.
* @param rotateY the point about which the text is rotated.
*/
public static void drawRotatedString(final String text, final Graphics2D g2,
final float textX, final float textY, final double angle,
final float rotateX, final float rotateY) {
if ((text == null) || (text.equals(""))) {
return;
}
float textHeight = g2.getFontMetrics().getLineMetrics(text, g2).getHeight();
String[] lineBreakText = text.split("\n");
//an offset is needed to shift the (not rotated yet) text in -y direction
//the original y value (may be transformed by derive TextBoundsAnchorOffsets)
//is the left point on the baseline of the downmost line of text!!
float changedTextY = textY;
if (lineBreakText.length>1) {
float multilineOffset = ((lineBreakText.length-1) * textHeight);
changedTextY -= multilineOffset;
}
final AffineTransform saved = g2.getTransform();
// apply the rotation...
final AffineTransform rotate = AffineTransform.getRotateInstance(
angle, rotateX, rotateY);
g2.transform(rotate);
for (int i = 0; i < lineBreakText.length; i++) {
if (useDrawRotatedStringWorkaround) {
// workaround for JDC bug ID 4312117 and others...
final TextLayout tl = new TextLayout(lineBreakText[i], g2.getFont(),
g2.getFontRenderContext());
tl.draw(g2, textX, (changedTextY + i*textHeight));
}
else {
// replaces this code...
//here the text is drawn. Drawing starts at the offset and goes from top to bottom
g2.drawString(lineBreakText[i], textX, changedTextY + i*textHeight);
}
}
g2.setTransform(saved);
}
Code: Select all
/**
* Returns the bounds for the specified text.
* Method is modified for multiline text, the separator is "\n".
*
* @param text the text (<code>null</code> permitted).
* @param g2 the graphics context (not <code>null</code>).
* @param fm the font metrics (not <code>null</code>).
*
* @return The text bounds (<code>null</code> if the <code>text</code>
* argument is <code>null</code>).
*/
public static Rectangle2D getTextBounds(final String text,
final Graphics2D g2, final FontMetrics fm) {
Rectangle2D bounds;
String[] multiLineText = text.split("\n");
double maxWidth;
if (TextUtilities.useFontMetricsGetStringBounds) {
bounds = fm.getStringBounds(text, g2);
maxWidth = 0;
for (int i = 0; i < multiLineText.length; i++) {
//the width must correspond to the length of the longest line to stay
//aligned centered as fm.getStringBounds() doesn't understand \n
//(or else the String shifts on the y-Axis as it gets longer).
if ( fm.getStringBounds(multiLineText[i], g2).getWidth() > maxWidth ) {
maxWidth = fm.stringWidth(multiLineText[i]);
}
}
// getStringBounds() can return incorrect height for some Unicode
// characters...see bug parade 6183356, let's replace it with
// something correct
LineMetrics lm = fm.getFont().getLineMetrics(text,
g2.getFontRenderContext());
bounds.setRect(bounds.getX(), bounds.getY(),
maxWidth, lm.getHeight()*(multiLineText.length));
//System.out.println("bounds: " + bounds);
}
else {
maxWidth = 0;
for (int i = 0; i < multiLineText.length; i++) {
//see above
if ( fm.stringWidth(multiLineText[i]) > maxWidth ) {
maxWidth = fm.stringWidth(multiLineText[i]);
}
}
final double height = fm.getHeight();
if (logger.isDebugEnabled()) {
logger.debug("Height = " + height);
}
//font metrics are baseline relative
bounds = new Rectangle2D.Double(0.0, -fm.getAscent(),
maxWidth, height*(multiLineText.length));
}
return bounds;
}