当前位置: 首页 > 知识库问答 >
问题:

为什么一个只支持拉丁字符的Java字体声称支持亚洲字符,尽管它不支持?

颜志业
2023-03-14

在使用JFreeChart呈现图表时,我注意到当图表的类别标签包含日语字符时出现了一个布局问题。虽然文本是用正确的字形呈现的,但文本被定位在错误的位置,大概是因为字体度量错误。

该图表最初被配置为对该文本使用源Sans Pro常规字体,该字体仅支持拉丁字符集。显而易见的解决方案是捆绑一个实际的日语.ttf字体,并要求JFreeChart使用它。这工作很好,因为输出文本使用了正确的字形,并且它的布局也是正确的。

>

  • java.awt如何在第一个场景中正确呈现日语字符,当使用除了拉丁字符之外实际上不支持任何其他字符的源字体时?如果重要的话,我将在带有JDK 1.7u45的OS X 10.9上进行测试。

    有没有办法在不捆绑单独的日文字体的情况下呈现日文字符?(这是我的最终目标!)虽然捆绑解决方案可以工作,但如果可以避免的话,我不想在我的应用程序中添加6 Mb的膨胀。Java显然知道如何在没有字体的情况下呈现日文字形(至少在我的本地环境中是这样)--看起来只是度量被破坏了。我在想这是否与下面的“FrankenFont”问题有关。

    在JRE执行内部转换之后,为什么源Sans Pro字体告诉调用方(通过canDisplayUpTo())它可以显示日语字符,尽管它不能?(见下文)

    >

  • 这是一个服务器应用程序,我们呈现的文本将显示在客户端的浏览器和/或PDF导出中。图表总是光栅化到服务器上的PNGs。

    我无法控制服务器操作系统或环境,尽管使用Java标准的平台字体很好,但许多平台的字体选择很差,这在我的用例中是不可接受的,所以我需要捆绑我自己的字体(至少对于拉丁字体)。日语文本使用平台字体是可以接受的。

    应用程序可能会被要求显示日语和拉丁文的混合文本,而不需要事先了解文本类型。如果一个字符串包含混合语言,只要字形正确呈现,那么我对使用什么字体感到矛盾。

    我理解java.awt.font#TextLayout是聪明的,当尝试布局文本时,它首先询问底层字体是否能够实际呈现所提供的字符。如果不是,它可能会交换一种知道如何呈现这些字符的不同字体,但根据我对JRE类的调试,这里没有发生这种情况。TextLayout#SingleFont始终为字体返回一个非空值,并继续执行构造函数的FastInit()部分。

    一个非常奇怪的注意是,源Sans Pro font在JRE对字体执行转换后,不知怎的被迫告诉调用方它知道如何呈现日语字符。

    例如:

    // We load our font here (download from the first link above in the question)
    
    File fontFile = new File("/tmp/source-sans-pro.regular.ttf");
    Font font = Font.createFont(Font.TRUETYPE_FONT, new FileInputStream(fontFile));
    GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
    
    // Here is some Japanese text that we want to display
    String str = "クローズ";
    
    // Should say that the font cannot display any of these characters (return code = 0)
    
    System.out.println("Font " + font.getName() + " can display up to: " + font.canDisplayUpTo(str));
    
    // But after doing this magic manipulation, the font claims that it can display the
    // entire string (return code = -1)
    
    AttributedString as = new AttributedString(str, font.getAttributes());
    Map<AttributedCharacterIterator.Attribute,Object> attributes = as.getIterator().getAttributes();
    Font newFont = Font.getFont(attributes);
    
    // Eeek, -1!    
    System.out.println("Font " + newFont.getName() + " can display up to: " + newFont.canDisplayUpTo(str));
    

    它的输出是:

    Font Source Sans Pro can display up to: 0
    Font Source Sans Pro can display up to: -1
    

    注意,上面提到的三行“神操”,不是我自己干的事;我们将真正的源font对象传递给JFreeChart,但在绘制字形时它会被JRE吞噬,这正是上面的三行“魔术操作”代码所复制的。上面显示的操作与以下调用序列中发生的操作在功能上是等价的:

      null

    这里有一个更完整的示例,展示了JFreeChart呈现示例,它基于JFreeChart示例之一:https://gist.github.com/sdudley/b710fd384e495e7f1439。这个示例的输出如下所示。

  • 共有1个答案

    司寇烨伟
    2023-03-14

    我终于想通了。有许多潜在的原因,而跨平台可变性的增加又进一步阻碍了这一进程。

    出现布局问题的原因是JFreeChart无意中使用了与AWT实际用于呈现字体的字体对象不同的字体对象来计算布局的度量。(供参考,JFreeChart的计算发生在org.jfree.text#GetTextBounds中。)

    不同字体对象的原因是 ;问题中提到的隐式“魔术操作”的结果,该操作是在java.awt.Font.TextLayout#SingleFont中执行的。

    font = Font.getFont(font.getAttributes())
    

    在英语中,这要求字体管理器根据所提供字体的“属性”(名称、族、点大小等)给我们一个新的字体对象。在某些情况下,它回馈给您的字体将与您最初使用的字体不同。

    要更正度量(从而修复布局),修复方法是在设置JFreeChart对象中的字体之前,在您自己的font对象上运行上面的一行程序。

    这样做之后,布局对我来说很好,日语字符也一样。它也应该为您修复布局,尽管它可能不能为您正确显示日语字符。阅读以下关于本机字体的内容,了解原因。

    原因有很多,但至少在Mac OS X上,与问题相关的原因是字体管理器似乎更喜欢在可能的情况下返回原生字体。

    换句话说,如果您使用font.createFont从名为“Foobar”的物理TTF字体创建一个新字体,然后使用从“Foobar”物理字体派生的属性调用font.getFont()……只要OS X已经安装了Foobar字体,字体管理器就会返回一个cfont对象,而不是您期望的TrueTypeFont对象。即使通过GraphicsEnvironment.getLocalGraphicsEnvironment().RegisterFont注册字体,这一点似乎也成立。

    在我的例子中,这给调查带来了麻烦:我的Mac上已经安装了“Source Sans”字体,这意味着我得到的结果与没有安装的人不同。

    问题的关键是Mac OS Xcfont对象始终支持亚洲字符集。我不清楚允许这样做的确切机制,但我怀疑这是OS X本身的某种后备字体特性,而不是Java。无论哪种情况,cfont总是声称(并且确实能够)用正确的字形呈现亚洲字符。

    这就明确了允许原始问题发生的机制:

    • 我们从物理TTF文件创建了物理字体,该文件本身不支持日语。
    • 在我的Mac OS X字体簿中也安装了与上面相同的物理字体
    • 在计算图表的布局时,JFreeChart向物理font对象询问日语文本的度量。物理字体无法正确执行此操作,因为它不支持亚洲字符集。
    • 在实际绘制图表时,TextLayout#SingleFont中的神奇操作导致它获得一个CFont对象,并使用同名的本机字体绘制字形,而不是物理的TrueTypeFont。因此,字形是正确的,但它们的位置不正确。

    如果使用创建的TTF字体中的属性调用font.getFont(),将得到三种不同的结果之一,这取决于字体是否已注册以及是否在本机安装了相同的字体:

    • 如果安装了与TTF字体同名的本机平台字体(无论是否注册了该字体),您将为所需的字体获得支持亚洲的cfont
    • 如果您在GraphicsEnvironment中注册了TTFfont但没有同名的本机字体,则调用font.getFont()将返回一个物理的TrueTypeFont对象。这将提供您所需的字体,但无法获得亚洲字符。
    • 如果您没有注册TTFfont并且您也没有同名的本机字体,则调用font.getfont()返回支持亚洲的CFont,但它不是您请求的字体。

    事后看来,这一切都不完全令人惊讶。导致:

    在production应用程序中,我正在创建一个字体,但我忘记了最初在GraphicsEnvironment中注册它。如果您在执行上述神奇操作时还没有注册字体,font.getFont()将不知道如何检索它,您将得到一个备份字体。哎呀。

    在Windows、Mac和Linux上,这种备份字体一般看起来是Dialog,这是一种支持亚洲字符的逻辑(复合)字体。至少在Java7U72中,对于西文字母,对话框字体默认为以下字体:

    • Mac:Lucida Grande
    • Linux(CentOS):Lucida SANS
    • Windows:Arial

    这个错误对我们的亚洲用户来说其实是件好事,因为这意味着他们的字符集按照预期的逻辑字体呈现出来……尽管西方用户没有得到我们想要的字符集。

    同样的问题在Mac OS X上也适用,如果您请求“Lucida Sans”...但是如果您请求略有不同的名称“LucidaSans”(注意缺少空间),那么您将得到一个cfont对象,它支持Lucida Sans和亚洲字符,因此您可以鱼与熊掌兼得。

    在其他平台上,请求“LucidaSans”会产生标准对话框字体的副本,因为没有这样的字体,Java会返回它的默认字体。在Linux上,您在这里有些幸运,因为Dialog实际上对于西文文本默认为Lucida Sans(对于亚洲字符,它也使用了一个不错的后备字体)。

    这为我们提供了一个在所有平台上获得(几乎)相同的物理字体的路径,并且还支持亚洲字符,方法是请求具有以下名称的字体:

    • Mac OS X:“LucidaSans”(生成Lucida Sans+Asian备份字体)
    • Linux:“对话框”(产生Lucida Sans+Asian备份字体)
    • Windows:“对话框”(产生Arial+Asian备份字体)

    我仔细研究了Windows上的fonts.properties,但我找不到默认为Lucida Sans的字体序列,所以看起来我们的Windows用户需要使用Arial...但至少它在视觉上与Lucida Sans没有太大区别,Windows的字体呈现质量是合理的。

    总而言之,我们现在几乎只使用平台字体。(我敢肯定,@TrashGod现在笑得很开心!)Mac和Linux服务器都得到了Lucida Sans,Windows得到了Arial,渲染质量不错,皆大欢喜!

     类似资料:
    • Mocha支持断言库中任何抛出的s err.expected和err.actual属性AssertionError。mocha将尝试显示预期内容与断言实际看到的内容之间的差异。这是一个“字符串”差异的例子:

    • 谷歌Web字体上的一些字体支持多个“字符集”。问题是,如果我使用的web字体只提供“拉丁”字形,用户将页面翻译成不支持字形的语言时,会清楚地注意到混乱的文本。 我希望我的网页字体支持最流行的语言在世界上除了英语,例如,西班牙语,德语,法语等。 出于这个目的,我想知道,“拉丁”和“拉丁扩展”到底是针对哪种语言的。 我想答案应该是这样的:

    • 问题内容: 如何在Linux上从TrueType或嵌入式OpenType字体中提取受支持的Unicode字符列表? 我是否可以使用工具或库来处理.ttf或.eot文件并构建字体提供的代码点列表(例如U + 0123,U + 1234等)? 问题答案: 这是使用FontTools模块的方法(您可以使用来安装该模块): 该脚本将字体路径作为参数:

    • 我在Windows上使用系统字体作为。不支持CJK字符,但不是默认为这些字符(例如

    • 我们正在使用itext v5。0.5商业许可证。我们需要下一个亚洲人。jar来支持汉字。我找不到itext asian。itext v 5.0.5在线版的jar。我们无法升级到最新版本的itext,因为它需要大量测试。请给出下载itext asian的路径。5.0.5版的jar?

    • 目录 10.1. 常规字符集和校对 10.2. MySQL中的字符集和校对 10.3. 确定默认字符集和校对 10.3.1. 服务器字符集和校对 10.3.2. 数据库字符集和校对 10.3.3. 表字符集和校对 10.3.4. 列字符集和校对 10.3.5. 字符集和校对分配示例 10.3.6. 连接字符集和校对 10.3.7. 字符串文字字符集和校对 10.3.8. 在SQL语句中使用COLL