为什么我必须调用graphicsenvoriment.registerfont(),即使我的字体是从文件创建的?

vulvrdjw  于 2021-07-09  发布在  Java
关注(0)|答案(3)|浏览(622)

我正在开发一个使用jfreechart呈现图表的web应用程序。但是,当服务器没有安装任何中文字体时,即使我设置了字体,jfreechart也不会显示中文字符。
然后我编写了一个小的测试代码,发现在绘制图表之前添加这行代码可以解决问题。

GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);

所以我的问题是-
为什么即使从文件中创建字体,也要将字体注册到jvm中?这是否意味着jfreechart不使用我直接设置的字体?
当我将程序部署到服务器时,即使我添加了这行代码,它也不会显示中文字符。如何使它始终使用我设置的字体,以便在所有环境中正确显示字符?
我知道我可以做一个 fallback 目录位于 $JAVA_HOME/jre/lib 把我的字体放进去。但这并不能解释为什么jfreechart不能用我设置的字体显示。

更新

我很确定字体加载正确,我也是 registerFont() 当我将程序部署到tomcat中时返回true。

更新2

根据java2dfaq,现在我意识到我必须调用 registerFont() 为了使我自己的字体“安装”到jvm中,我的字体将通过 Font 建造师。
从JavaSE6开始,有一种方法:graphicsenvironment.registerfont(),它使您能够使“创建的”字体对字体构造函数可用,并通过字体枚举API列出。createfont()和这个方法结合起来,提供了一种将字体“安装”到正在运行的jre中的方法,这样它就可以像o/s安装的字体一样使用。但是,这种字体不会在jre调用中持久存在。
但是,既然我已经 Font 创建/派生的示例 createFont() ,为什么我的程序还不需要创建其他 Font ?
下面是我使用的代码,它只是以png格式输出一个图表。如果要运行代码,应该根据需要更改输出位置和字体,下面是我在代码中使用的中文字体的sourceforge链接。

import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.io.File;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;

public class Problem {

  public static void main(String[] args) throws Exception {
    setJFreeChartTheme();

    PieDataset dataset = createDataSet();
    JFreeChart chart = ChartFactory.createPieChart(
        "Chinese Testing", dataset, true, true, false);
    ChartUtilities.saveChartAsJPEG(new File("/tmp/output.png"), 
        chart, 800, 600);

    System.out.println("Done");
  }

  private static void setJFreeChartTheme() throws Exception {
    Font font = loadFont();
    //==================================================================
    GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
    //==================================================================
    StandardChartTheme theme = new StandardChartTheme("Chinese font", true);
    theme.setExtraLargeFont(font.deriveFont(Font.BOLD, 20));
    theme.setLargeFont(font.deriveFont(Font.BOLD, 16));
    theme.setRegularFont(font.deriveFont(Font.PLAIN, 14));
    theme.setSmallFont(font.deriveFont(Font.PLAIN, 12));
    ChartFactory.setChartTheme(theme);
  }

  private static Font loadFont() throws Exception {
    File file = new File("/tmp/wqy-zenhei.ttc");
    return Font.createFont(Font.TRUETYPE_FONT, file);
  }

  private static PieDataset createDataSet() {
    DefaultPieDataset dataset = new DefaultPieDataset();
    dataset.setValue("種類1", Integer.valueOf(1));
    dataset.setValue("種類2", Integer.valueOf(2));
    dataset.setValue("種類3", Integer.valueOf(3));
    return dataset;
  }
}
2ekbmq32

2ekbmq321#

我知道这是一个老问题,但我自己在寻找答案,看到了上面的回答,我仍然没有真正理解注册字体的目的。研究了这个问题后,我发现:
您不必在图形环境中注册字体,但这样做的好处是可以在“new font()”构造函数中使用注册的字体。
您可以使用以下代码获取当前可用的所有字体的列表(即已安装并可以在应用程序中使用的字体):

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

假设您在windows上,并且安装的字体之一是arial,您可以在应用程序中使用此字体,如下所示:

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

现在假设您想从文件中加载并使用自己的自定义字体:

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

如果要将其设置为jbutton的字体,可以编写以下代码:

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

但是如果你试着写一些代码,比如:

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

jbutton只会被赋予一个默认字体,因为图形环境不知道任何名为“roboto light”的字体。解决方法是在图形环境中注册字体:

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

然后,您可以在“new font()”构造函数中使用此字体,如下所示:

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

rvpgvaaj2#

为什么即使从文件中创建字体,也要将字体注册到jvm中?
否则jvm怎么知道你的字体存在?
您的字体必须在jvm中注册,以便java知道如何在jfreechart用来呈现图表的图形环境中绘制您的字体。
如何使它始终使用我设置的字体,以便在所有环境中正确显示字符?
你需要检查一下 registerFont() 方法返回true。如果返回false,则字体不可用。
看起来你加载的字体是正确的。可能您的字体在服务器上的文件路径不正确。你也许想试试

getClass().getResource(fontPath);
gzjq41n4

gzjq41n43#

当您创建 Font 直接从ttf,java显然知道从哪个字体对象中获取字体文件本身的副本。那么为什么字体也需要注册才能使用呢?答案是,它并不总是必须注册,或者至少只要整个控制链使用原始的 Font 直接反对。

当java试图呈现字体时,会发生什么?

细微差别在于jfreechart要求如何呈现文本。文本的呈现在 TextUtilities#drawRotatedString 方法。在jdk7上,默认情况下,此方法将:
创建 AttributedString 根据你输入的字体的“属性”,
呼叫 Graphics2D#drawString 在属性字符串上
创建新的 TextLayout 对象。 TextLayout 选择要提供给的实际字体对象的类 Graphics2D . TextLayout 通过使用自动字体选择为字符串的每一部分找到合适的字体,设计为支持使用各种字体呈现多语言文本(即使同一个源字符串需要以多种字体呈现)。
上面提到的“属性”是关于从 Font 你提供的。如果您提供的字体无法呈现输入字符串中的所有字符,则这些属性将用于选择相似的字体,以用于那些需要使用不同字体的文本。
当jfreechart调用textlayout时,它总是这样运行:
Font 你提供的物品,
调用静态 Font#getFont 获取与提供的属性匹配的字体(请参见 TextLayout#singleFont ),和
使用返回的(可能不同的)字体对象来绘制文本。
如果你没有在某个地方静态注册你的字体(比如 GraphicsEnvironment#registerFont ),那么所有这些 Font#getFont 方法必须是包含字体系列名称的属性字符串。它不知道在哪里访问包含对ttf的引用的font对象,更不用说实际呈现字体所需的任何数据了。

好吧,但你不是说我不需要注册字体吗?

如果您不想注册字体,那么诀窍在于确保只使用 Font 您提供的对象。碰巧有另一个构造函数 TextLayout 接受一个 Font 对象,而不是用于查找字体的一组属性。
jfreechart甚至提供了一种强制它使用这个构造函数的方法。在 TextUtilities#drawRotatedString ,可以使用一个特殊的配置参数强制jfreechart构造 TextLayout 使用精确的 Font 您提供的对象。
为此,可以设置jcommon.properties文件,如下所示:
创建名为 jcommon.properties (应该在类路径/jar的根级别结束),并且
添加以下行: org.jfree.text.UseDrawRotatedStringWorkaround=true 或者简单地调用静态函数:

TextUtilities.setUseDrawRotatedStringWorkaround(true)

这将要求jfreechart直接使用您的字体呈现文本,并且…瞧!即使没有注册字体,它也能工作。这是在上述问题的上下文中测试的,即使用jfreechart将文本直接渲染到光栅图像。如果尝试渲染到显示设备(我没有尝试),则里程数可能会有所不同。

不注册字体明智吗?

我不能肯定。我的一个应用程序运行在一个osgi容器中,我对静态注册一个永远无法注销的字体而造成permgen类加载器泄漏持谨慎态度。直接使用font对象可以避免这个问题,这就是为什么我要走这条路。我想如果您这样做的话,一个特定的java平台可能总是会有问题,但是在我的测试中,至少在windows、linux和osx主机上使用oraclejdk7时,这是很好的。

相关问题