哪个系统组件负责在Java应用程序中绑定Unicode连字?

6

我在寻找另一个问题的更好规范时,遇到了这个“元问题”(在Mac OS X上的Java Swing JComponent中呈现Devanagari连字符(Unicode))。

到目前为止我还不太理解,对于显示Java Unicode文本及其连字的给定系统的哪个“组件”(用另一个更好的词来形容)负责,具体而言如下:

就我所了解的,以下组件影响该过程:

  1. 系统字符编码(例如,在Mac OS X 10.6上是UTF-8,在Windows 7上是UTF-16(根据akira在此superuser.com帖子上的评论))。
  2. Java Charset(默认情况下在Mac OS X 10.6上是MacRoman,在Windows 7上是cp1252)。
  3. 用于呈现文本的字体及其编码信息(正如Donal Fellows在我的其他问题中建议的那样: "字体包括它们使用的编码信息":。
  4. 显然,要呈现的字符是否存在于相应的Unicode代码点中。

因此,如果一串Unicode字符没有正确显示(如在我的其他问题中所见),问题最可能出现在哪里?也就是说,“组件”(有更好的词吗?)负责“绑定”连字及其组合?

非常感谢您的帮助,请告诉我您是否需要更多信息。


我猜测可能是虚拟机,但我在这方面没有证据或专业知识。 - Mr47
@Mr47:好的,谢谢,那就是第二种情况了。这个时候 https://dev59.com/P3RC5IYBdhLWcg3wP-n5 可能会派上用场。我记下来了。我稍微修改了一下我的帖子,以便更明确地指出问题的“入口”供其他人参考。 - s.d
4个回答

4
那个系统组件被称为字体渲染器或字体光栅化器。它负责根据字体中定义的字形,将一系列字符代码转换为像素。正如其他答案所述,Java可以获得和设置的各种字符编码值是无关紧要的。当JVM给字体渲染器提供一系列字符代码时,它告诉它适用的编码是什么(可能是UTF16,但这对Java程序员来说是透明的)。字体渲染器使用字体文件中指定的字体编码来匹配相应的字形。
当前版本的Windows和Mac OS X都配备了出色的字体渲染器。
第一个混淆点是JRE自带自己的字体渲染器,作为Java2D平台的一部分,Swing使用它。应该有一个选项来控制Java使用自己的渲染器还是系统渲染器。
编辑:正如McDowell在评论中指出的,在OS X上,您可以通过设置Java属性apple.awt.graphics.UseQuartz=true来启用系统渲染器。
第二个令人困惑的点是,连字在英语中是可选的。桌面出版应用程序会在看到像“shuffle”这样的单词时替换为“ffl”连字(字体中的单个字形),但大多数其他应用程序不会费心。根据您对天城文的说法(以及我刚在维基百科上读到的内容),我认为该语言中的连字是不可选的。
默认情况下,Java2D字体渲染器不会执行连字。但是,java.awt.font.TextAttribute.LIGATURES的JavaDoc表示,对于需要它们的书写系统,总是启用连字。如果这不是您的经验,则可能已经发现了Java2D字体渲染器中的错误。与此同时,请尝试使用带有字体属性映射的Font构造函数,包括TextAttribute.LIGATURES。

非常感谢您提供这个有趣的见解。您说得对,在天城体中,连字是不可选的。然而,我已经测试了LIGATURES_ON TextAttribute(如Oracle所建议的,如下所示),但不幸的是它没有改变任何东西。这意味着fontconfig文件是最可能引起问题的源头。 Map<TextAttribute, Object> m = new Hashtable<TextAttribute, Object>(); m.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON); font = font.deriveFont( map ); g2.setFont( font ); - s.d
1
@baphomet13 - 看起来你可以在OS X上使用Java系统属性来在Java2D和Quartz渲染之间切换: apple.awt.graphics.UseQuartz - McDowell
@McDowell:你最后的评论非常准确,解决了我的问题。我可以建议你将其编辑到你的答案中,这样我就可以接受它作为最佳答案了吗?另外,我在我的相关问题[https://dev59.com/q2025IYBdhLWcg3wc1tM]上设定了一个赏金,我建议你也在那里添加你的答案,这样我就可以把奖金授予你!再次感谢,你帮了我很多! - s.d
@McDowell - 尽管 bahomet13 建议这样做,但我刚刚编辑了我的答案,包括对您的评论的引用。 - gatkin

3

我不是专家,但希望这些提示能指导您走向正确的方向...

源数据的编码对字体渲染几乎没有影响。Java中的所有字符数据都是UTF-16,只要您将信息从源转换为字符/字符串时正确转码,数据的完整性就应该得到保留。

但请注意:

  • AWT系统可以使用默认系统编码进行字体映射
  • 这不太可能适用于天城文(我不知道是否有支持它的遗留编码)

AWT通过fontconfig文件来映射字体。在我的Windows系统上,这映射到Mangal字体:

allfonts.devanagari=Mangal

毫无疑问,在Mac OS上使用了不同的字体。
原生文本渲染在Java 6时期引入 - 我不知道这是否对字体支持产生影响,或者只是影响渲染速度/反锯齿等方面。
Java 6 支持的字体 文档。

谢谢您的技巧!这非常像是我正在寻找的东西,尽管我描述问题有些困难。我需要一点时间来测试它,但一定会在这里跟进。 - s.d
我刚刚询问了几个Mac用户,他们都有fontconfig.properties映射到Mangal用于“allfonts.devanagari”。老实说,我现在完全不知道如何理解为什么Mac和Windows显示之间应该有差异,因此我会感激任何进一步的提示。 - s.d
@baphomet13 - 假设Mangal字体在两个平台上完全相同(而不是同名的不同实现),那么我怀疑gatkin更接近问题所在 - 问题可能在于字体的渲染方式。 - McDowell

2
如果你严格参考视觉呈现,那么“编码”和相关主题就不再相关:呈现从字符串到视觉显示。字符串有一个定义好(且无法更改)的编码,即UTF-16。因此,所有像“我是否用正确的编码读取了这个二进制流”的问题必须首先解决。
实际文本呈现必须由图形子系统完成。对于“普通”Java,这将是AWT/Swing,而对于SWT或任何其他替代系统都是如此。
第一步(它并非严格属于“呈现”的一部分)是将某些二进制数据转换为字符串。如果代码没有明确指定某种编码,则这可能涉及平台默认编码。这是编码总体上起作用的步骤。之后,我们就进入了快乐纯Unicode领域。

非常感谢术语规范。恐怕我的解释不是很具体。但是,我是否正确地假设正确的连字显示(例如,使用七个Unicode代码点构建的 लक्ष्मी,或者德国的 ff)与字符编码(System Property "file-encoding")有关? - s.d
另外,我已经根据您的更正修改了标题和文本。 - s.d
@baphomet:不,正确的显示并不是这样的。问题是:你的 Unicode 数据中是否包含 U+FB00 拉丁文小型连字 FF,还是包含 2 个 U+0066 拉丁文小写字母 F? - Joachim Sauer
我的 String 包含七个代码点 (\u0932\u0915\u094D\u0937\u094D\u092E\u0940),应该显示梵文连字体 लक्ष्मी (/laksmi/)。我期望像这样编写的 Unicode 数据能够显示为连字体,事实上在 Windows 7 和 Ubuntu 机器上确实如此,但在 Mac OS X 上却不行。由于梵文中的连字通常是单词,因此它们没有像拉丁小型连字 FF 那样的单个代码点。 - s.d
需要注意的是,U+FB00 是一种奇怪的东西:Unicode 通常不会为连字提供单独的代码点(认为这些是渲染决策而不是文本信息)。那个字符(以及类似的字符)只存在于一些广泛使用的遗留编码中,以确保往返正确性。 - Joachim Sauer

1

好的,我现在知道为什么我引发了Joachim的回应。实际上,我并没有从文件中读取,而是定义了一个带有Unicode字符的String变量(例如,String str = "\u0932\u0915\u094D\u0937\u094D\u092E\u0940")。这些在Mac系统上正确显示,但在Windows系统上正确显示,这引发了我的问题。我将删除数字(3),以便它不会触发更多关于读取流的答案。抱歉,我认为我为了完整性而放入了它。 - s.d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接