Why do I have to call GraphicsEnvorinment.registerFont() even if I my Font were created from file?

前端 未结 3 989
夕颜
夕颜 2021-01-06 13:02

I\'m developing a web application which use JFreeChart to render chart. However, when server dose not have any Chinese font installed, JFreeChart dose not display Chinese ch

相关标签:
3条回答
  • 2021-01-06 13:08

    Why do I have to register font into JVM even if I create my font from file?

    How else is the JVM going to know your font exists?

    Your font has to be registered with the JVM so that Java knows how to draw your font in the graphics environment that JFreeChart uses to render charts.

    How do I make it always use the font I set in order to display character properly in all environment ?

    You need to check that the registerFont() method returns true. If it returns false, your font is not available.

    It looks like you're loading the font correctly. Maybe the file path of your font isn't correct on your server. You might want to try

    getClass().getResource(fontPath);
    
    0 讨论(0)
  • 2021-01-06 13:16

    When you create a Font directly from the TTF, Java obviously knows where to get a copy of the font file itself from within that single Font object. So why does the font also need to be registered in order to use it? The answer is that it does not always have to be registered, or at least not so long as the entire chain of control uses the original Font object directly.

    What really happens when Java tries to render the font?

    The nuance is in how JFreeChart asks to render the text. Rendering of text is performed in the TextUtilities#drawRotatedString method from jcommon. On JDK7, this method will, by default:

    • create an AttributedString based on the "attributes" of the font that you have passed in,
    • call Graphics2D#drawString on the attributed string, which then
    • creates a new TextLayout object.

    TextLayout is the class that selects the actual Font object to be provided to Graphics2D. TextLayout is designed to support the rendering of multilingual text using various fonts (even if the same single source String needs to be rendered in multiple fonts) by using automatic font selection to find an appropriate font for each piece of the string.

    The "attributes" mentioned above are simple facts about the font (like font family name, size, etc) that are derived from the Font that you provide. In the event that the font you've provided is unable to render all of the characters in the input string, the attributes are used to select similar fonts to use for those runs of text that need to use a different font.

    When JFreeChart invokes TextLayout, it always functions like this:

    • extract the attributes from the Font object you supplied,
    • call the static Font#getFont to get a font that matches the supplied attributes (see TextLayout#singleFont), and
    • uses the returned (possibly different) Font object to draw the text.

    If you have not statically registered your font somewhere (like with GraphicsEnvironment#registerFont), then all that the static Font#getFont method will have to go on is the attribute string containing the font family name. It won't know where to access the Font object that contains the reference to the TTF, let alone any of the data needed to actually render the font.

    OK, but I thought you said that I didn't need to register the font?

    If you don't want to register the font, then the trick is in making sure that your text is rendered only with the Font object that you provide. It so happens that there is another constructor for TextLayout that accepts a Font object directly, rather than a set of attributes that are used to look up a font.

    Helpfully, JFreeChart even provides a way to force it to use this constructor instead. In TextUtilities#drawRotatedString, a special configuration parameter can be used to force JFreeChart to construct the TextLayout object itself using the exact Font object you provide.

    To do this, you can either set up a jcommon.properties file like this:

    • create a resource file named jcommon.properties (which should end up in the root level of your classpath/JAR), and
    • add the following line to it:

    org.jfree.text.UseDrawRotatedStringWorkaround=true

    or else simply make a call to the static function:

    TextUtilities.setUseDrawRotatedStringWorkaround(true)
    

    This will ask JFreeChart to render the text using your font directly, and...voila! It works even without having registered the font. This was tested in the context of the question above, which is that of using JFreeChart to render the text directly to a raster image. Your mileage might vary if you are trying to render to a display device (which I did not try).

    Is it wise not to register the font?

    I can't say for sure. One of my applications runs inside an OSGi container and I was wary of creating PermGen classloader leaks by statically registering a Font that can never be unregistered. Using the Font object directly avoids that problem, which is why I wanted to go this route. I suppose it's always possible that a specific Java platform might have trouble if you did this, but this works fine in my tests on at least Windows, Linux, and OS X hosts with Oracle JDK 7.

    0 讨论(0)
  • 2021-01-06 13:18

    I know this is an old question, but I was searching for the answer myself and having seen the responses above, I still didn't really understand the purpose of registering a font. Having researched the issue, this is what I found:

    You don't have to register your font with the graphics environment, but doing so comes with the advantage of being able to use the registered font in 'new Font()' constructors.

    You can use the following code to get a list of all the fonts which are currently available (i.e installed and ready to use in your application):

    String fonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    

    Assuming you're on Windows, and one of the installed fonts is Arial, you can use this font in your application like this:

    JButton yesButton = new JButton ("Yes");
    yesButton.setFont(new Font("Arial", Font.PLAIN,30));
    

    Now Suppose you want to load in and use your own custom font from a file:

    Font robotoFont = Font.createFont(Font.TRUETYPE_FONT,getClass().getResourceAsStream("/res/fonts/Roboto/Roboto-Light.ttf"));
    

    If you wanted to set that as the font for a JButton, you might write this code:

    JButton yesButton = new JButton("Yes");
    yesButton.setFont(robotoFont.deriveFont(Font.PLAIN, 30f));
    

    But if you tried to write some code like:

    JButton yesButton = new JButton("Yes");
    yesButton.setFont(new Font ("Roboto Light", Font.PLAIN,30));
    

    The JButton would just be given a default font, because the graphics environment is not aware of any font named "Roboto Light". The solution to this is to register your font with the graphics environment :

     GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
     genv.registerFont(robotoFont);
    

    You will then be able to use this font in 'new Font()' constructors like this:

    JButton yesButton = new JButton("Yes");
    bestButton.setFont(new Font ("Roboto Light", Font.PLAIN,30));
    
    0 讨论(0)
提交回复
热议问题