JFreeChart - wrong chart font - java

I have a problem with XYLineChart. I don't know how to set the chart font to look like Swing components. When I use this:
chart.setTitle(new TextTitle("Tahoma title, style plain, size 11", new Font("Tahoma", Font.PLAIN, 11)));
It is still wrong :(
EDIT: When I create the chart in a new frame, the font in the title is good. How do I set all the labels, axis titles, and other texts to the same font size, without bold?
SOLVED :)
public static void changeStyle(JFreeChart chart) {
final StandardChartTheme chartTheme = (StandardChartTheme)StandardChartTheme.createJFreeTheme();
final Font font = new Font("Tahoma", Font.PLAIN, 11);
final Color color = new Color(0, 0, 0);
chartTheme.setExtraLargeFont(font);
chartTheme.setLargeFont(font);
chartTheme.setRegularFont(font);
chartTheme.setSmallFont(font);
chartTheme.setAxisLabelPaint(color);
chartTheme.setLegendItemPaint(color);
chartTheme.setItemLabelPaint(color);
chartTheme.apply(chart);
}

If you want to change the existing title's font, do something like this:
chart.getTitle().setFont(new Font("Tahoma", Font.PLAIN, 11));
Addendum:
How do I change all items' fonts, e.g. labels, axis, etc.?
StandardChartTheme offers this capability by operating on individual chart components.

I don't know 2 things about XYLineCharts, but it looks like you might be setting the font on the wrong thing. Try setting it on different components.
Not much, but might get you going.
Good luck ;)
Damo

Related

Calibri Font when in <html> text moves to the bottom part of the component

There is not a lot to explain. Just see the MCVE/image below:
public class FontExample extends JFrame {
private static final Font FONT = new Font("Calibri", Font.PLAIN, 14);
public FontExample() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
JLabel withoutHtml = new JLabel("hello stackoverflow");
withoutHtml.setFont(FONT);
withoutHtml.setBorder(BorderFactory.createLineBorder(Color.red));
add(withoutHtml);
JLabel withHtml = new JLabel("<html><body style='vertical-align:top;'>hello stackoverflow");
withHtml.setBorder(BorderFactory.createLineBorder(Color.green));
withHtml.setFont(FONT);
add(withHtml);
setLocationByPlatform(true);
pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
//Make sure Calibri font is installed
if (!"Calibri".equals(FONT.getFamily())) {
System.err.println("Font calibri is not installed.");
System.exit(1);
}
new FontExample().setVisible(true);
});
}
}
The green one is with the <html> tag. Is there a way to fix it? And by fix, I mean to make it like the left one, without this stupid space?
It does not seem to happen with any other font (I tested 2-3 more). I am on Java 8 with Windows 7 and Windows 10.
I tried to add padding at bottom:
JLabel withHtml = new JLabel("<html><body style='padding-bottom:5px'>hello stackoverflow");
and as expected what I get is this:
which a) will screw the alignment of other components in the same container (bad for UI purposes) and b) I will have to hard code a lot of values since 5 since to be the proper for font size 14. But for other font size, it needs another value.
#Andrew Thomson in comments said to use the HTML format for all JLabels. But then, if they are next to another text-based component like a JTextField, I get this:
which obviously, is bad too.
UPDATE
Also, I tried to download Calibri font (among with variations like "Calibri Light", etc) somewhere from the web and install it as described in this question. I do not know if that "Overrides" the existing one, but I had the same result.
A line of text consists of 3 parts:
The ascent
The descent
The leading
To see more clearly, I used Calibri with size 50. The label without HTML is:
In HTML mode, things are different. The HTML renderer puts the leading first (for some reason):
This gives the unpleasant result you have observed.
Now you will ask "But why do I see that effect only with Calibri?" In fact the effect exists with all fonts, but it's usually much smaller, so you don't notice it.
Here is a program that outputs the metrics for some common Windows fonts:
import java.awt.*;
import javax.swing.JLabel;
public class FontInfo
{
static void info(String family, int size)
{
Font font = new Font(family, Font.PLAIN, size);
if(!font.getFamily().equals(family))
throw new RuntimeException("Font not available: "+family);
FontMetrics fm = new JLabel().getFontMetrics(font);
System.out.printf("%-16s %2d %2d %2d\n", family, fm.getAscent(), fm.getDescent(), fm.getLeading());
}
public static void main(String[] args)
{
String[] fonts = {"Arial", "Calibri", "Courier New", "Segoe UI", "Tahoma", "Times New Roman", "Verdana"};
System.out.printf("%-16s %s\n", "", " A D L");
for(String f : fonts)
info(f, 50);
}
}
For size 50, the results are:
A D L
Arial 46 11 2
Calibri 38 13 11
Courier New 42 15 0
Segoe UI 54 13 0
Tahoma 50 11 0
Times New Roman 45 11 2
Verdana 51 11 0
As you can see, the leading for Calibri is huge compared to the other fonts.
For size 14, the results are:
A D L
Arial 13 3 1
Calibri 11 4 3
Courier New 12 5 0
Segoe UI 16 4 0
Tahoma 14 3 0
Times New Roman 13 3 1
Verdana 15 3 0
The leading for Calibri is still 3 pixels. Other fonts have 0 or 1, which means the effect for them is invisible or very small.
It doesn't seem possible to change the behavior of the HTML renderer. However, if the goal is to align the baselines of adjacent components, then it is possible. The FlowLayout you have used has an alignOnBaseline property. If you enable it, it does align the components correctly:
UPDATE 1
Here's a JFixedLabel class that gives the same result, whether it contains HTML or plain text. It translates the Graphics by the leading value when in HTML mode:
import java.awt.Graphics;
import javax.swing.JLabel;
import javax.swing.plaf.basic.BasicHTML;
public class JFixedLabel extends JLabel
{
public JFixedLabel(String text)
{
super(text);
}
#Override
protected void paintComponent(Graphics g)
{
int dy;
if(getClientProperty(BasicHTML.propertyKey)!=null)
dy = getFontMetrics(getFont()).getLeading();
else
dy = 0;
g.translate(0, -dy);
super.paintComponent(g);
g.translate(0, dy);
}
}
Result:
UPDATE 2
The previous solution had an issue with icons, so here's a new one that handles both text and icons. Here we don't extend JLabel, instead we define a new UI class:
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.metal.MetalLabelUI;
public class FixedLabelUI extends MetalLabelUI
{
#Override
protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text, Icon icon,
Rectangle viewR, Rectangle iconR, Rectangle textR)
{
String res = super.layoutCL(label, fontMetrics, text, icon, viewR, iconR, textR);
if(label.getClientProperty(BasicHTML.propertyKey)!=null)
textR.y -= fontMetrics.getLeading();
return res;
}
}
To assign the UI to a label, do like this:
JLabel label = new JLabel();
label.setUI(new FixedLabelUI());
Olivier's answer suggests to use flowLayout.setAlignOnBaseline(true); but it will not work in another Layoutmanagers, e.g GridLayout. However, it helped me a lot to find the exact solution I was looking for. Even if it is a messy/hacky one.
Here it is:
If you System.out.println(label.getFontMetrics(label.getFont())), you will see that the actual class of the FontMetrics is FontDesignMetrics. Luckily for us, the getters for the values ascent, descent and leading rely on the fields without some crazy calculations. Luckily for us vol.2, These font metrics are the same (equals) for the same font. That means, we have a single FontDesignMetrics instance of for each Font style-size combination (and obviously its family).
With other words:
private static final Font FONT = new Font("Calibri", Font.PLAIN, 50);
JLabel withoutHtml = new JLabel("hello stackoverflow");
withoutHtml.setFont(FONT);
add(withoutHtml);
JLabel withHtml = new JLabel("<html>hello stackoverflow");
withHtml.setFont(FONT);
FontMetrics withHtmlFontMetrics = withHtml.getFontMetrics(withHtml.getFont());
FontMetrics withoutHtmlFontMetrics = withoutHtml.getFontMetrics(withoutHtml.getFont());
boolean equals = withHtmlFontMetrics.equals(withoutHtmlFontMetrics);
System.out.println(equals);
It prints true even if the getFontMetrics was called in different labels. If you withHtml.setFont(FONT.deriveFont(Font.BOLD)); you will see that it prints false. Because the font is different, we have different font metrics instance.
The fix
(Disclaimer: Desperate times call for desperate measures)
As I already mentioned, it's some sort of hacky and it relies on reflection. With reflection we can manipulate these 3 values. Something like:
FontMetrics fontMetrics = label.getFontMetrics(label.getFont());
Field descentField = fontMetrics.getClass().getDeclaredField("descent");
descentField.setAccessible(true);
descentField.set(fontMetrics, 0);
But you are going to either hard code values for each font size/style, or you can do what I did.
What I did is to copy these values from other font's FontMetrics. It looks that in case of Calibri font, Tahoma is the one.
First, create the method that change the values in the fields, taken from Tahoma font metrics:
private static void copyTahomaFontMetricsTo(JComponent component) {
try {
FontMetrics calibriMetrics = component.getFontMetrics(component.getFont());
// Create a dummy JLabel with tahoma font, to obtain tahoma font metrics
JLabel dummyTahomaLabel = new JLabel();
dummyTahomaLabel.setFont(new Font("Tahoma", component.getFont().getStyle(), component.getFont().getSize()));
FontMetrics tahomaMetrics = dummyTahomaLabel.getFontMetrics(dummyTahomaLabel.getFont());
Field descentField = calibriMetrics.getClass().getDeclaredField("descent");
descentField.setAccessible(true);
descentField.set(calibriMetrics, tahomaMetrics.getDescent());
Field ascentField = calibriMetrics.getClass().getDeclaredField("ascent");
ascentField.setAccessible(true);
ascentField.set(calibriMetrics, tahomaMetrics.getAscent());
Field leadingField = calibriMetrics.getClass().getDeclaredField("leading");
leadingField.setAccessible(true);
leadingField.set(calibriMetrics, tahomaMetrics.getLeading());
} catch (Exception e) {
e.printStackTrace();
}
}
Now, call it by: copyTahomaFontMetricsTo(withHtml); without caring if its the withHtml label or the withoutHtml, since they both have the same font.
The result (font size in frame title):
Even with other text-based components next to it:
As you see, it is works! Plus the layout alignment is not screwed.
It looks perfect, but it's not.
Again, as mentioned earlier, for each font (combination of family, size and style), there is one instance of FontMetrics. Changing one of these label's font to Font.BOLD will stop us from getting perfect alignment. Probably a one (or two) pixels miss. Plus we will have to copyTahomaFontMetricsTo for the Bold as well:
copyTahomaFontMetricsTo(withoutBoldFont);
copyTahomaFontMetricsTo(withBoldFont);
and the result (again font size on frame's title):
Look closer:
There is one pixel difference. But I guess I will take it since this is way (way) better than Swing's/Windows default Calibri-HTML behavior:
The complete example:
public class FontExample extends JFrame {
private static final Font FONT = new Font("Calibri", Font.PLAIN, 20);
public FontExample() {
super("Font: " + FONT.getSize());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
JLabel withoutHtml = new JLabel("hello stackoverflow");
withoutHtml.setBorder(BorderFactory.createLineBorder(Color.GREEN));
withoutHtml.setFont(FONT.deriveFont(Font.BOLD));
add(withoutHtml);
JLabel withHtml = new JLabel("<html>hello stackoverflow");
withHtml.setBorder(BorderFactory.createLineBorder(Color.RED));
withHtml.setFont(FONT);
copyTahomaFontMetricsTo(withoutHtml);
copyTahomaFontMetricsTo(withHtml);
add(withHtml);
setLocationByPlatform(true);
pack();
}
private static void copyTahomaFontMetricsTo(JLabel label) {
try {
FontMetrics calibriMetrics = label.getFontMetrics(label.getFont());
// Create a dummy JLabel with tahoma font, to obtain tahoma font metrics
JLabel dummyTahomaLabel = new JLabel();
dummyTahomaLabel.setFont(new Font("Tahoma", label.getFont().getStyle(), label.getFont().getSize()));
FontMetrics tahomaMetrics = dummyTahomaLabel.getFontMetrics(dummyTahomaLabel.getFont());
Field descentField = calibriMetrics.getClass().getDeclaredField("descent");
descentField.setAccessible(true);
descentField.set(calibriMetrics, tahomaMetrics.getDescent());
Field ascentField = calibriMetrics.getClass().getDeclaredField("ascent");
ascentField.setAccessible(true);
ascentField.set(calibriMetrics, tahomaMetrics.getAscent());
Field leadingField = calibriMetrics.getClass().getDeclaredField("leading");
leadingField.setAccessible(true);
leadingField.set(calibriMetrics, tahomaMetrics.getLeading());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new FontExample().setVisible(true);
});
}
}
Two ways you can probably handle this, add
html {
margin:0;
}
or add padding to both bits of text. :)
Of course you can try
<html style="margin:0;">
<body style='vertical-align:text-bottom;' worked for me, but if I'm misunderstanding your question, you can find other values at https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align

Change preview font in table?

In my program I implemented a table, I recently found out how to change the font size of the table so this is what I did:
It sets the font of the contents in the table to 20, however before I confirm an input with enter or select another cell, the preview is still the standard font and too small
table.setFont(new Font("Tahoma", Font.BOLD, 20));
The preview should also be the same size, that's what I wanna do.
What you need to do is modify the Default Cell Editor like this:
Object dce = jTable1.getDefaultEditor(Object.class);
if(dce instanceof DefaultCellEditor) {
Font font = new Font(jTable1.getFont().getName(), jTable1.getFont().getStyle(), 20); // 20 is your desired font size
((DefaultCellEditor) dce).getComponent().setFont(font);
}
This concept was retrieved from this SO Answer by #Redwine.

JFreeChart Vertical Label for IntervalMarker

I would like to rotate the label for the IntervalMarker:
IntervalMarker im = new IntervalMarker(...);
im.setLabel("LABEL");
// im.setLabelOffsetType(LengthAdjustmentType.EXPAND);
// im.setLabelOffset(new RectangleInsets(10.0, 10.0, 10.0, 10.0));
// im.setLabelFont(new Font("SansSerif", Font.ITALIC, 11));
im.setLabelAnchor(RectangleAnchor.TOP_LEFT);
im.setLabelTextAnchor(TextAnchor.TOP_LEFT);
im.setPaint(new Color(208, 194, 214));
It didn't allow me to upload a picture, here is the link:
http://i54.tinypic.com/5z40fs.png
I would like to have "LABEL" vertical, for better looks.
Thank you
I've found it easier to add Text Annotations to vertical markers for more control over their labels. Here's an example:
// vertical line marker and label
Marker updateMarker = new ValueMarker(dayOf, Color.black, dashedStroke, null, null, 1.0f);
XYTextAnnotation updateLabel = new XYTextAnnotation("Update", dayOf - labelOffset, labelHeight);
updateLabel.setFont(new Font("Sans Serif", Font.BOLD, 10));
updateLabel.setRotationAnchor(TextAnchor.BASELINE_CENTER);
updateLabel.setTextAnchor(TextAnchor.BASELINE_CENTER);
updateLabel.setRotationAngle(-3.14 / 2);
updateLabel.setPaint(Color.black);
plot.addDomainMarker(updateMarker, Layer.BACKGROUND);
plot.addAnnotation(updateLabel);
This rotation makes the label appear on the left side of the vertical marker line reading bottom-to-top. I use variables "labelOffset" and "labelHeight" to determine the exact position of the label in relation to the vertical line, but these could be set statically as well.
Rendering the marker's label is handled by the draw{Domain|Range}Marker() method of the plot's renderer using drawAlignedString(). You'd have to use drawRotatedString(), instead.
I couldn't find how to do this directly, but you can always put a rotated text Annotation in an appropriate place.

Java: Fonts and Pixels

I'm making a game and in the menu I want to display the text in the center of the screen.
Is there a way in Java to get/calculate the width of a piece of text in a specified font with specified size and style.
Martijn
The FontMetrics.stringWidth method does just that -- it will return the width in pixels for a given String.
One can obtain the FontMetrics from a Graphics object by the getFontMetrics method.
For example:
g.setFont(new Font("Serif", Font.BOLD, 24));
int width = g.getFontMetrics().stringWidth("Hello World!");
System.out.println(width);
The result was:
135
In the class Font you have methods such like getLineMetrics or getStringBounds that may help you.
Just use a JLabel that is center aligned and the proper layout manager and you don't have to worry about this.
JLabel label = new JLabel("Text");
frame.add(label , SwingConstants.CENTER);

Are there any built-in methods in Java to increase Font size?

Are there any built-in methods in Java to increase Font size?
The Font class allows you to specify font size.
So, to create a font you do something like this:
Font f = new Font("serif", Font.PLAIN, fontSize);
The fontSize parameter will determine the size of your Font.
You can't actually change the size of an existing Font object. The best way to achieve a similar effect is to use the deriveFont(size) method to create a new almost identical Font that is a different size.
Font biggerFont = existingFont.deriveFont(bigNumber);
You can derive a new Font with a different size by using the following:
Font original = // some font
Font bigger = original.deriveFont(newSize);
Where newSize is a float, not an int. This is well documented in the JavaDoc for Font as other people have pointed out
Assuming that you want to change the font size on a specific JLabel, you can do:
label.setFont(label.getFont().deriveFont(newSize));
Make sure that newSize is a float not an int.
I interpreted this question as "How can I increase font size for Swing across the board." I'm not aware of any built-in way to do this, but you could do it yourself by modifying the values in the UIManager class on startup before you create any Swing components.
I do this by having a parameter passed into my app that I use as a multiplier. If I pass in 150 it'll multiply all existing fonts by 150%. The code is as follows
public static void initializeFontSize() {
String fontSizeParam = System.getProperty("myapp.fontSize");
if (fontSizeParam != null) {
float multiplier = Integer.parseInt(fontSizeParam) / 100.0f;
UIDefaults defaults = UIManager.getDefaults();
int i = 0;
for (Enumeration e = defaults.keys(); e.hasMoreElements(); i++) {
Object key = e.nextElement();
Object value = defaults.get(key);
if (value instanceof Font) {
Font font = (Font) value;
int newSize = Math.round(font.getSize() * multiplier);
if (value instanceof FontUIResource) {
defaults.put(key, new FontUIResource(font.getName(), font.getStyle(), newSize));
} else {
defaults.put(key, new Font(font.getName(), font.getStyle(), newSize));
}
}
}
}
}
you can set the property swing.plaf.metal.controlFont when running you application:
java -Dswing.plaf.metal.controlFont=Dialog-50 YourMainClass
in this example, you set the default font to be "Dialog" with size 50.
The question is way too vague to give a good answer. But I think you want to systematically increase font size in your application.
The font face, style and size in a Java Swing application is controlled via the LookAndFeel mechanism. You need to change the font in the look-and-feel if you want the change to apply to all Swing components of a given type.
Have a look at the UIManager example.
Here's how to change the font globally for some UI components:
UIManager.put("Label.font", new FontUIResource(new Font("Dialog", Font.PLAIN, 10)));
UIManager.put("Button.font", new FontUIResource(new Font("Dialog", Font.BOLD, 10)));
UIManager.put("TextField.font", new FontUIResource(new Font("Dialog", Font.PLAIN, 10)));

Categories