What font in Swing looks the same in all OS? - java

I use Netbeans 7.0 with JDK6 under Windows 7 to design the user interface of my Java application. I apply System look and feel. But it looks the way I want in Windows but differs in MacOS and even worse, it looks different in different window managers in Linux (LXDE, GNOME, KDE, XFCE).
By different I mean the fonts look and their size. In Windows, if a label looks "v 1.23", it looks like "v ..." in other OSes because the fonts become bigger in that OS and the JLabel do not have enough place to show. This happens in several places.
I don't want to increase the label width. I want the label to look the same in that given width in all OS. By default, Netbeans uses the font Tahoma 11pt on my pc. I think it's not available in all OSes so the other OSes use different font.
Is Arial a common font?
Should I change the font of every element to Arial manually? Or any other options?

Instead, use a layout manager and a logical font family. This example with the Font below adds 24-point italic serif text to the center of a (default) BorderLayout to achieve a pleasing result on disparate platforms.
ta.setFont(new Font("Serif", Font.ITALIC, 24));
Mac OS X:
Windows 7:
Ubuntu Linux:

When it comes to window managers there is a lot more to it than just font size, for instance the width between 2 controls are handled differently in gnome & KDE. when it comes down to using the same font size across all platforms you can use Sans Serif font, this is available in all the OS'es I've worked with.
it would also be better (if you can't find serif in an OS where you want to run your app) you can download a font (free ones from GNU Free Fonts).
when it comes to setting the font sizes & the fonts, why don't you try using a theme & set the font there...
you can use the UIManager.setLookAndFeel() method to change themes.
here is a link on Look & Feel

An old question, but I've found it because I was looking for a solution.
And my solution is: use DejaVu fonts.
The Java dialog font looks different in Linux and Windows, but DejaVuSans 12 is very like the dialog font in Linux and looks the same in Windows (in Windows 8.1 at least).
My code:
...
static Font dejaVuSans;
static final String resPath = "<classpath>/resources/"; // no leading "/"
...
public static void main(String[] args) {
/* See:
* https://stackoverflow.com/questions/7434845/setting-the-default-font-of-swing-program
* https://stackoverflow.com/questions/8361947/how-to-get-getclass-getresource-from-a-static-context
*/
dejaVuSans = null;
Font f;
try {
InputStream istream = <ClassName>.class.getClassLoader().getResourceAsStream(
resPath + "DejaVuSans.ttf");
dejaVuSans = Font.createFont(Font.TRUETYPE_FONT, istream);
f = dejaVuSans.deriveFont(Font.PLAIN, 12);
} catch (Exception e) {
System.err.println(e.getMessage());
f = null;
}
if (f == null)
f = new Font("Dialog", Font.PLAIN, 12);
java.util.Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get (key);
if (value != null && value instanceof javax.swing.plaf.FontUIResource)
UIManager.put (key, f);
}
...
}
Of course, if DejaVuSans.ttf is not embedded in the jar file you can import it at runtime. See How do you import a font?.

Lucida Sans Regular is always bundled with Java but I don't think it will solve the problem that some text/labels go out of format. That depends also on the users's screen resulotion.

Related

How to Embed Font in JavaFX?

How can I embed a custom font in a JavaFX Application? I've tried making a fonts.mf file, and following the instructions to a similar question linked below. I don't want to use CSS if I don't have to. I want to focus on learning core JavaFX stuff.
Here is what I've been messing around with:
private static Label makeTitle() {
Label title = new Label("Bandit King");
Font font = new Font("OldStyle", 40);
title.setFont(font);
return title;
}
my fonts.mf file contains just this line:
OldStyle = /home/myName/Desktop/My_Java_Projects/Bandit_King/banditKing/OLDSH.TTF
This is not a duplicate of this question. Eclipse says, "CustomFontApp, cannot be resolved to a type".
I've tried making a fonts.mf file
You don't need it.
This is not a duplicate of this question. Eclipse says, "CustomFontApp, cannot be resolved to a type".
CustomFontApp is just the name of a class used in the answer you linked - your class name is obviously different, and you should've changed it.
This line:
Font font = new Font("OldStyle", 40);
will load OldStyle font only if it's installed on your system. You aren't using an installed font, but the embedded one, so that won't work.
You need to use Font.loadFont(InputStream, double) or Font.loadFont(String, double) to load your custom font from disk:
// use this to load font from your application's resource folder (`res/fonts/OLDSIH.TTF`)
Font font = Font.loadFont(getClass().getResourceAsStream("/fonts/OLDSIH.TTF"), 40);
// or this one to load font from the specified (absolute) path
// (not recommended, use the method above or, at least, change this into relative path):
Font font = Font.loadFont("file:///home/myName/Desktop/My_Java_Projects/Bandit_King/banditKing/OLDSH.TTF", 40);

Bug or feature: Swing default gui font incorrect for Win6+

just (astonishingly ;-) noticed the reason why apps look so cramped on my win6+ machines (same for Vista and Win7, both with 120dpi setting, jdk6 and jdk7): the control font looked up from the the desktop property has both the wrong font family and the wrong size:
public static void main(String[] args) {
Font guiFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty("win.defaultGUI.font");
int guiSize = guiFont.getSize();
Font iconFont = (Font) Toolkit.getDefaultToolkit().getDesktopProperty("win.icon.font");
System.out.println("gui default: " + guiFont + "\nicon default: " + iconFont);
}
output:
gui default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=13]
icon default: java.awt.Font[family=Segoe UI,name=Segoe UI,style=plain,size=15]
The latter is used in native applications for nearly all text, while Swing uses the former ...
Questions:
Could there be any reason for this, or just a bug?
Who's responsible: the Swing lookup (when reading-in the desktopProperty from relevant system resources) or the OS in not reporting it correctly?
How to force the usage of the latter?
Options for solving the last:
With full control about the LAF one might consider to set all relevant text fonts (that's what JGoodies does, factored into a FontPolicy/Set).
A dirty hack is to set the value of the defaultGUI desktop property to the correct value - it involves reflective access to the toolkit, which naturally will blow in security constrained contexts.
??
Edit
Just in case anybody is interested, here's the dirty hack:
/**
* Replaces the default gui desktop font property with the icon font
* if the former is smaller.
*
*/
public static void ensureDefaultGUIFontSize() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Font guiFont = (Font) toolkit.getDesktopProperty("win.defaultGUI.font");
Font iconFont = (Font) toolkit.getDesktopProperty("win.icon.font");
if (guiFont.getSize() < iconFont.getSize()) {
invokeDeclaredMethod("setDesktopProperty", Toolkit.class,
toolkit, "win.defaultGUI.font", iconFont);
}
}
private static void invokeDeclaredMethod(String methodName,
Class<?> clazz, Object instance, String propertyName,
Object propertyValue) {
try {
Method method = clazz.getDeclaredMethod(methodName, String.class, Object.class);
method.setAccessible(true);
method.invoke(instance, propertyName, propertyValue);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
LOG.finer("forcing desktop property failed " + e.getStackTrace());
}
}
Edit 2
Just to clarify: the hack is fully effective only for WindowsLAF. Nimbus ignores system settings completely, Metal partly: the latter's font is always Dialog, only size is taken from desktopProperties. Sounds half-way good, but isn't: the mapping is rather weird for the main fonts, f.i. the heavily used controlFont size is set to "win.ansiVar.font.height" (what fossil leftover is that?) which is 13 on my machine ...
Edit 3
Even in windows ui, the hack is ... a hack with limitations, f.i. those mentioned in #Walter's comment:
This bug is especially noticeable when you scale the Windows UI. FYI, opening a JFileChooser reverts the hack. Also JTree/JTable row height will not be automatically updated to the new font size and you'll need to scale your icons as well
I think that isn't a bug but basic property of Win7 and built_in themes, interesting size of Font, I still use smaller Fonts (default setting from OS instalation)
for example if I set / switch
1.Windows7 Basic theme
gui default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=11]
icon default: java.awt.Font[family=Segoe UI,name=Segoe UI,style=plain,size=12]
2.Windows7 Classic theme
gui default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=11]
icon default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=11]
don't touched the Font property, will be continue for from WinXP
3.WindowXP modified theme
gui default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=11]
icon default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=13]
4.Windows7 Classic theme
gui default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=11]
icon default: java.awt.Font[family=Tahoma,name=Tahoma,style=plain,size=11]

Malformed Farsi characters on AWT

When I started programming with the JDK6, I had no problem with text components, neither in AWT nor in Swing.
But for labels or titles of AWT components I do have a problem. I can't display Farsi characters on AWTs components (in Swing I type them into the source code).
Here's my sample code:
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.Properties;
public class EmptyFarsiCharsOnAWT extends JFrame{
public EmptyFarsiCharsOnAWT() {
super("مثال");
setDefaultCloseOperation(3);
setVisible(rootPaneCheckingEnabled);
}
public static void main(String[] args) throws AWTException, IOException {
JFrame jFrame = new EmptyFarsiCharsOnAWT();
MenuItem show ;
// approach 1 = HardCoding :
/*
show = new MenuItem("\u0646\u0645\u0627\u06cc\u0634");
*
*/
// approach 2 = using simple utf-8 saved text file :
/*
BufferedReader in = new BufferedReader(new FileReader("farsiLabels.txt"));
String showLabel = in.readLine();
in.close();
show = new MenuItem(showLabel);
*
*/
// approach 3 = using properties file :
FileReader in = new FileReader("farsiLabels.properties");
Properties farsiLabels = new Properties();
farsiLabels.load(in);
show = new MenuItem(farsiLabels.getProperty("tray.show"));
PopupMenu popUp = new PopupMenu();
popUp.add(show);
// creating Tray object
Image iconIamge = Toolkit.getDefaultToolkit().getImage("greenIcon.png");
TrayIcon trayIcon = new TrayIcon(iconIamge, null, popUp);
SystemTray tray = SystemTray.getSystemTray();
tray.add(trayIcon);
jFrame.setIconImage(iconIamge);
}
}
These three approaches all work when run with an IDE, but when I make a JAR containing this class (by means of NetBeans > project > clean & build), I don't see the expected characters (it shows EMPTY/BLANK SQUARES)!
Note:
It seems I can not attach anything, so the contents of the text file would be this: نمایش and the contents of properties file:
#Sun May 02 09:45:10 IRDT 2010
tray.show=نمایش
And I think I have to let you know that I posted this question a while ago on SDN and "the Java Ranch" forums and other native forums and still I'm waiting...
By the way I am using latest version of Netbeans IDE...
I will be grateful if anybody has a solution to these damn AWT components never rendering any Farsi character for me...
I suspect that this is platform related. Your example appears to work on my platform using approach 1 in either Netbeans or the command line; I didn't try the other approaches.
There might be a disparity between the IDE and the command line with regard to the default character encoding. I've noticed that NetBeans, Eclipse and many consoles can be set to something other than the platform default. Here's code to check:
System.out.println(System.getProperty("file.encoding"));
System.out.println(Charset.defaultCharset().name());
You might look at this related question, too.
Addendum: Note show string changed to match the JFrame title for comparison. The title and menu look the same from NetBeans' Run > Run Project as well as via these command lines:
$ java -cp build/classes EmptyFarsiCharsOnAWT
$ java -jar dist/test6.jar
import javax.swing.*;
import java.awt.*;
import java.io.*;
public class EmptyFarsiCharsOnAWT extends JFrame{
public EmptyFarsiCharsOnAWT() {
super("مثال");
setDefaultCloseOperation(3);
setVisible(rootPaneCheckingEnabled);
}
public static void main(String[] args) throws AWTException, IOException {
JFrame jFrame = new EmptyFarsiCharsOnAWT();
MenuItem show ;
// approach 1 = HardCoding :
show = new MenuItem("\u0645\u062b\u0627\u0644");
PopupMenu popUp = new PopupMenu();
popUp.add(show);
// creating Tray object
Image iconIamge = Toolkit.getDefaultToolkit().getImage("image.jpg");
TrayIcon trayIcon = new TrayIcon(iconIamge, null, popUp);
SystemTray tray = SystemTray.getSystemTray();
tray.add(trayIcon);
jFrame.setIconImage(iconIamge);
}
}
The most exciting part of your reply was:
"$ java -jar dist/test6.jar" !
Does it really shows the real characters (just like the frame title)?!
and not boxes or garbage ?
I'm sorry if I believe it hard, because the only problem in my developing work with Java took such long without any answer nor from searching, nor asking in forums is this!
So, what can I do? what font should I use? Unfortunately I'm not so familiar with fonts, until now I've just used global fonts in Java (Serif,SansSerif,etc.) and only modified their size or style, but after you suggest I examined several Persian ttf fonts through these codes:
File fontFile = new File("F_JADID.TTF");
Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
show.setFont(font.deriveFont(15f));
but just boxes was the result! (just using HardCoding)
I think i should mention that my envirounment is win xp and i have this problem not only in my machine, but another running xp os too. And I'm using jdk6u17.
I can be agree with you in suspecting the fonts, because encoding problem (in my experience) appears with question mark, but garbage or empty boxes related to rendering characters.
But still i have the problem, just like the first day :(
What font you use and another question i encountered is:
Why swing doesn't have any problem without specifying the font, but AWT.
Addendum: Thanks to Oscar Reyes in this page for giving this link and thanks to StackOverflow :)
They saved me! from this section i should quote:
An application using peered AWT components can only use logical font names.
and from this section should quote:
For applications using AWT peered components, Sun's JREs select fonts for Chinese, Japanese, or Korean only when running on host operating systems localized for these specific languages
Yes, you guess right! by setting the OS locale to Farsi, i got the right result.
but i still should research and see how is it possible to have the right result by not setting the right locale, from that article.
I will explain how, when i got the result, but still will listen to here. wish me luck.

How can I inherit the system's anti-alias setting for painting text to off-screen images like swing does?

When I run my swing GUI applications under Java 6, they automatically use my configured sub-pixel anti-alias settings for all fonts. The result is much improved over standard AA options.
But when I paint to an image I can find no way to initialize the graphics context to use the system's AA configuration. Trying to play around with Java's different AA hints is a lost cause because no sub-pixel method will work for all users.
Is there any way to inherit system AA settings for a given graphics context instead of having to pick one and explicitly set the hint? At the moment I have to use GASP AA to avoid the horrible results that standard AA gives with small fonts. I have tried not setting anything for text AA, and not setting any AA hints at all.
Update 2010-01-05
I think I have pinned this down; the subpixel AA hints appear to only be respected when painting directly to the AWT graphics context; when I paint to a double-buffer image it just does standard AA; but when I bypass the double-buffer image the subpixel AA is done.
Otherwise The_Fire's answer would work in JVMs which have Swing available (but not J2ME JVMs); Note that The_Fire's answer does not work using an AWT component (using new Label() instead of new JLabel() fails), presumably because the FontRenderContext cannot be extracted until the component is realized to the display.
My current code to get the graphics context for my target image currently looks like this:
try {
if((dbImage=dctRoot.createImage(wid,hgt,1))!=null) { // if createImage returns null or throws an exception the component is not yet displayable
dbGraphics=(Graphics2D)dbImage.getGraphics();
if(dctRoot.properties.getBoolean("Antialias",true)) {
try {
// set AA on overall
dbGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING ,RenderingHints.VALUE_ANTIALIAS_ON);
// set text AA to platform/impl default
dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
// try to override platform/impl AA with font-specified AA (Java 6+)
try { dbGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_GASP").get(null)); } catch(Throwable thr) {;} // yes, ignore exception
}
catch(Throwable thr) {
dctRoot.log.println("Antialiasing not supported on this JVM ("+thr+").");
dctRoot.setProperty("Antialias","False"); // turn off AA for subsequent painting
}
}
}
}
catch(Throwable thr) {
dbImage=null;
dbGraphics=null;
}
The code to create the image uses an underlying AWT component, which forms the backdrop on which I do all my painting - the component is a Panel, because I need to be able to do a setFocusCycleRoot so it plays well with other AWT components. The create image code follows:
public DctImage createImage(int wid, int hgt, float accpty) {
GraphicsConfiguration cfg=awtComponent.getGraphicsConfiguration();
Image img=null;
if(transparentImages) {
//y { img=new BufferedImage(wid,hgt,BufferedImage.TYPE_INT_ARGB); } // NB: J2ME CDC/PP 1.1 does not have the BufferedImage constructors (one day I may discover a way to create a BufferedImage via another API!!)
try { img=cfg.createCompatibleImage(wid,hgt,Transparency.TRANSLUCENT); }// NB: J2ME CDC/PP 1.1 does not have this API, but prefer to use GraphicsConfiguration over new BufferImage(...)
catch(NoClassDefFoundError thr) { transparentImages=false; createImage(wid,hgt,accpty); } // try again with transparency disabled
catch(NoSuchMethodError thr) { transparentImages=false; createImage(wid,hgt,accpty); } // try again with transparency disabled
catch(NoSuchFieldError thr) { transparentImages=false; createImage(wid,hgt,accpty); } // try again with transparency disabled
}
else {
img=cfg.createCompatibleImage(wid,hgt);
}
if(accpty>0 && SET_ACCELERATION_PRIORITY!=null) {
try { SET_ACCELERATION_PRIORITY.invoke(img,new Object[]{new Float(accpty)}); } catch(Throwable thr) {;}
}
return (img==null ? null : new DctImage(img));
}
I found there were a few factors going on here.
First, the image needs to be created from the underlying AWT component, and it must be created without transparency:
cfg.createCompatibleImage(wid,hgt);
instead of
cfg.createCompatibleImage(wid,hgt,Transparency.TRANSLUCENT);
Second, for some inexplicable reason, the primary AA setting, KEY_ANTIALIASING, must be off to use LCD subpixel AA.
Lastly, and most importantly, the desktop font rendering hints are easily retrieved using:
java.awt.Toolkit.getDesktopProperty("awt.font.desktophints")
Update 2010-01-05
Retesting in Java 6.26, it seems like the problem with needing to set general AA off to render text AA has finally been resolved (way to go Oracle... after Sun was just a few years decade too late).
Using Swing, I'm able to get the right text anti-aliasing hint like this:
JLabel label = new JLabel();
FontMetrics fm = label.getFontMetrics( label.getFont() );
Object aaHintValue = fm.getFontRenderContext().getAntiAliasingHint();
On my system this returns RenderingHits.VALUE_TEXT_ANTIALIAS_LCD_HRGB.
java.awt.Toolkit.getDesktopProperty("awt.font.desktophints") appears to be null on linux, at least without any special vm command line options, presumably because it's unable to figure out what the platform defaults are.. adding e.g "-Dawt.useSystemAAFontSettings=lcd" seems to cure it and enables subpixel rendering if you set the hints on your Graphics2D instance.
Wait, are you running this code on a Windows JVM? I thought ClearType was a Microsoft technology that Swing inherits through some native code (ie, not available on Linux or other non Microsoft platforms).
I once wrote a servlet that generated JPGs with anti aliased fonts that ran on Debian, and this was the code I used
Font font = new Font("Komix", Font.PLAIN, 8);
Graphics2D g2;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
FontRenderContext frc = g2.getFontRenderContext();
g2.setFont(font);
g2.setPaint(Color.black);
g2.drawString(sMessage, xOffset, yOffset);
Offhand I can't recall if any of this code relies on Swing (I imported javax.swing and the servlet is about 300 lines long, so I may have thought I needed it for something else), a quick check on Google looks like this is squarely in the AWT space. Hope that helps.

How can I create a Java/Swing text component that is both styled and has a custom font?

How can I create a Java/Swing text component that is both styled and has a custom font? I want to highlight a small portion of the text in red, but at the same time use a custom (embedded via Font.createFont) font. JLabel accepts HTML text, which allows me to highlight a portion of the text, but it ignores font settings when rendering HTML. Other text components such as JTextArea will use the custom font, but they won't render HTML. What's the easiest way to do both?
Here's an example of using JTextPane unsuccessfully:
JTextPane textPane = new JTextPane();
textPane.setFont(myCustomFont);
textPane.setText(text);
MutableAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setForeground(attributes, Color.RED);
textPane.getStyledDocument().setCharacterAttributes(
text.indexOf(toHighlight),
toHighlight.length(),
attributes, true
);
This successfully displays the text with the "toHighlight" portion highlighted in red, but it doesn't use myCustomFont. Note that I could set a String font with StyleConstants.setFontFamily(), but not a custom font.
OK, I see the problem better now.
After checking some Swing source code, it is clear you cannot use the DefaultStyledDocument and have it use a physical font (one you created yourself with createFont) out of the box.
However, what I think you could do is implement your own StyleContext this way:
public class MyStyleContext extends javax.swing.text.StyleContext
{
#Override public Font getFont(AttributeSet attr)
{
Font font = attr.getAttribute("MyFont");
if (font != null)
return font;
else
return super.getFont(attr);
}
}
Then you have to:
create a DefaultStyledDocument with
a new MyStyleContext()
"attach" it to the JTextPane
call attributes.addAttribute("MyFont", myCustomFont); in your snippet above
I did not try it but I think it should work or it might be a good path to investigate.
jfpoilpret's solution worked perfectly! For posterity's sake, here's a working code snippet:
JTextPane textPane = new JTextPane();
textPane.setStyledDocument(new DefaultStyledDocument(new StyleContext() {
#Override
public Font getFont(AttributeSet attr) {
return myCustomFont;
}
}));
textPane.setText(text);
MutableAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setForeground(attributes, Color.RED);
textPane.getStyledDocument().setCharacterAttributes(
text.indexOf(toHighlight),
toHighlight.length(),
attributes, true
);
Thanks, jfpoilpret!
You should try to use JEditorPane or JTextPane instead.
They allow rich style in the content, at the price of a more complex API.
Unfortunately, if you are in search of a pixel-prefect UI, they also have an additional problem: they don't support baseline-alignment (Java 6 feature).
I had the same problem when writing a program in Clojure, ie. using fonts loaded from TTF in a JEditorPane displaying HTML text. The solution here worked all right - I copy the interesting part here for future reference:
(def font1 (with-open [s (FileInputStream. "SomeFont.ttf")]
(.deriveFont (Font/createFont Font/TRUETYPE_FONT s) (float 14))))
(def font2 (Font. "SansSerif") Font/PLAIN 14)
(let [editor (JEditorPane. "text/html" "")]
(.setDocument editor
(proxy [HTMLDocument] []
(getFont [attr]
(if (= (.getAttribute attr StyleConstants/FontFamily)
"MyFont")
font1
font2)))))
This assumes that the HTML document refers to a font-family "MyFont", e.g. with a CSS snippet like
p { font-family: "MyFont" }
Note that with this you have to handle all font requests. This is because of the limitation of proxy not being able to call the member functions of the superclass. Also, if you want to handle different font sizes, you have to do that "manually", checking the StyleConstants/FontSize attribute and creating a font with deriveFont accordingly.
I hope this will help somebody :)

Categories