我正在开发一个使用JFreeChart渲染图表的Web应用程序。但是,当服务器没有安装任何中文字体时,即使我设置了字体,JFreeChart也不会显示汉字。
然后我写了一个小的测试代码,发现在绘制图表之前添加这行代码可以解决问题。
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
所以我的问题是-
>
为什么即使我从File创建字体,我也必须将字体注册到JVM?这是否意味着JFreeChart不使用我直接设置的字体?
当我将程序部署到服务器时,即使我添加了这行代码,它也不会显示汉字。如何让它始终使用我设置的字体,以便在所有环境中正确显示字符?
我知道我可以在< code>$JAVA_HOME/jre/lib中创建一个< code>fallback目录,并将我的字体放入其中。但是这并不能解释为什么JFreeChart不能用我设置的字体显示。
我很确定字体加载正确,当我将程序部署到Tomcat中时,registerFont()
也会返回true。
根据JAVA 2D FAQ,现在我意识到我必须调用注册Font()
才能使我自己的字体“安装”到JVM中,并且我的字体将通过字体
构造函数提供。
从Java SE 6开始,有一个方法:graphics environment . register Font(),它使您能够将“创建的”字体提供给字体构造器,并通过字体枚举API列出。Font.createFont()和这个方法结合起来提供了一种将字体“安装”到正在运行的JRE中的方法,因此它就像操作系统中安装的字体一样可用。然而,这种字体在JRE调用中并不持久。
但是,既然我已经从createFont()
创建/派生了Font
实例,为什么我的程序仍然不需要创建其他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;
}
}
我知道这是一个老问题,但我自己也在寻找答案,看到上面的回复后,我仍然不明白注册字体的目的。研究了这个问题后,我发现:
您不必在图形环境中注册您的字体,但是这样做的好处是可以在“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));
为什么我必须在JVM中注册字体,即使我从文件创建字体?
JVM还能如何知道您的字体存在?
您的字体必须注册到 JVM,以便 Java 知道如何在 JFreeChart 用于呈现图表的图形环境中绘制字体。
我如何让它总是使用我设置的字体,以便在所有环境中正确显示字符?
您需要检查Register sterFont()
方法是否返回true。如果返回false,则表示您的字体不可用。
看起来您正确加载了字体。也许您的字体在服务器上的文件路径不正确。您可能想试试
getClass().getResource(fontPath);
当您直接从 TTF 创建字体
时,Java 显然知道从该单个 Font 对象中获取字体文件本身的副本的位置。那么,为什么字体也需要注册才能使用它呢?答案是,它并不总是必须注册,或者至少不需要,只要整个控制链直接使用原始 Font
对象。
细微的差别在于JFreeChart要求如何呈现文本。文本的呈现是在jcommon的< code > text utilities # drawRotatedString 方法中执行的。在JDK7上,默认情况下,该方法将:
At⃣tedString
Graphics2D#draString
,然后TextLayout
对象。TextLayout
是选择要提供给Graphics2D
的实际Font对象的类。TextLayout
旨在支持使用各种字体呈现多语言文本(即使相同的单个源字符串需要以多种字体呈现),通过使用自动字体选择为字符串的每一部分找到合适的字体。
上面提到的“属性”是从您提供的font
派生的有关字体的简单事实(如字体系列名称、大小等)。如果您提供的字体无法呈现输入字符串中的所有字符,则这些属性用于选择类似字体,以用于需要使用不同字体的文本运行。
当JFreeChart调用TextLayout时,它总是这样运行:
Font
对象提取属性,Font#getFont
以获取与提供的属性匹配的字体(请参见TextLayout#singleFont
),以及如果您没有在某个地方静态注册字体(例如使用GraphicsEnvironment#registerFont
),那么静态font#getFont
方法必须继续执行的就是包含字体系列名称的属性字符串。它不知道在哪里访问包含对TTF的引用的Font对象,更不用说实际渲染字体所需的任何数据了。
如果您不想注册字体,那么诀窍在于确保仅使用您提供的 Font
对象呈现文本。碰巧的是,TextLayout
还有另一个构造函数直接接受 Font
对象,而不是用于查找字体的一组属性。
有用的是,JFreeChart甚至提供了一种方法来强制它改用这个构造函数。在“文本实用程序#绘制旋转字符串”
中,可以使用一个特殊的配置参数来强制 JFreeChart 使用您提供的确切字体
对象构造文本布局
对象本身。
为此,您可以建立一个jcommon.properties文件,如下所示:
jcommon.properties
的资源文件(该文件应最终位于类路径/JAR 的根级别),以及< code > org . jfree . text . usedrawrotatedstringworkrange = true
或者简单地调用静态函数:
TextUtilities.setUseDrawRotatedStringWorkaround(true)
这将要求JFreeChart直接使用您的字体渲染文本,并且……瞧!即使没有注册字体,它也能工作。这是在上面问题的上下文中测试的,即使用JFreeChart将文本直接渲染到光栅图像。如果您尝试渲染到显示设备(我没有尝试),您的里程可能会有所不同。
我不能肯定地说。我的一个应用程序在OSGi容器内运行,我通过静态注册一个永远无法取消注册的字体来防止创建PermGen类加载器泄漏。使用 Font 对象可以直接避免这个问题,这就是我想走这条路的原因。我想如果你这样做,一个特定的Java平台总是有可能有问题,但这在我至少在Windows,Linux和OS X主机上使用Oracle JDK 7的测试中工作得很好。
关于在最新版本的GCC和Clang中编译有几个问题:实验::filessystems链接器错误 但是现在< code>filesystem已经被c 17接受,所以不再需要< code>experimental或< code>-lstdc fs标志,对吗? 错了,我甚至不能 只给了我< code >实验版本,我怎么能包括正式接受的版本呢?
问题内容: 我创建了一个用于显示工具提示的指令: 对应功能: 应用于此: 这是我观点的一部分,由拥有者的控制器处理 为什么必须调用才能将更改应用到,该更改是早先声明和初始化的? 问题答案: 因为附加到事件的回调超出了angular的范围;angular不知道该函数何时运行/结束,因此摘要循环永远不会运行。 调用或告诉angular更新绑定并触发任何手表。
我甚至不确定这段代码是否能做任何事情,即使它有效,但我不知道该怎么做才能摆脱“从内部类引用的局部变量必须是最终的或有效的最终”错误消息,该错误消息显示在以“fireballRight[i]”开头的三行上。 任何指导将不胜感激,谷歌似乎并没有帮助我。
C++20概念的一个特点是,在某些情况下,您必须编写。例如,[expr.prim.req]/3中的这个示例:
我有方法 在正文中,我编写了一些HashMap的键集 看起来还可以,但如果我想重复这个代码 我得用try/catch来包围它。像这样 我不明白为什么
问题内容: 我使用以下Dockerfile创建了一个Docker容器(已截断): 等等。 所有这些都可以,但是我的问题是软件包的安装方式/位置。 如果我仅使用rvm运行rvm,则会显示“无法找到rvm”,但是如果运行,它会起作用。(我在网上找到了“ -l -c”选项,但不知道它们的作用,也找不到令人满意的解释!) 这不是一个docker问题-这是一个bash / * nix问题-我认为存在一些关于