定制字体在安卓系统中会导致某些字母组合显示错误的字符。

11

我在我的Android项目中使用自定义字体。但是当文本包含连续的字母IJ时,它会给出以下字形:

enter image description here

这似乎是字体PUA区域中位置为\uE2C5的字形。

单个的IJ字形都存在于字体中,如果我将文本设置为I J,它们就可以显示出来。

enter image description here

这不是OpenType字体(我认为Android甚至不支持自定义字体中的OpenType渲染),因此不应该发生这种情况。到底发生了什么?

以下是可重现该问题的简单项目:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView = (TextView) findViewById(R.id.textview);
        Typeface tf = Typeface.createFromAsset(this.getAssets(), "MenksoftHawang.ttf");
        textView.setTypeface(tf);

        textView.setText("IJ");
    }
}

字体可以从这里下载。将其放入项目的assets文件夹中。

3个回答

9
我无法确定你看到的内容的原因,但 "IJ" 字母组合似乎有一个特殊待遇的历史。这段话出自维基百科关于"字体连字"的页面。
然而,荷兰语的 ij 却有些模糊。根据所使用的标准,它可以被视为两个字母的组合、一个字母或一个字母本身,并且它的大写和小写形式通常在几种专业字体中作为单个字形提供(例如 Zapfino),具有独特的连字。荷兰流行的非衬线体大写 IJ 采用类似带有断开的左手笔画的 U 连字。
此外,维基百科上还有一些信息:
一些兼容字符对于符合 Unicode 标准的文本处理和显示软件来说完全可以不需要。这包括:
连字 如拉丁字母中的 "ffi",在遗留字符集中通常会被编码为一个单独的字符。Unicode 对于连字的处理方式是将其视为富文本,并且如果打开,则通过字形替换进行处理。
基于这些,我的猜测是你正在使用的字体利用了这种类型的用法来执行自己的操作。
更新 使用 setFontFeatureSettings() 可以禁用显示所示的字形,以下代码可以实现。不幸的是,这仅在 API 21+ 中可用。有一个警告,可能会有其他副作用,你需要注意。
对于低于 21 的 API,可以使用字体编辑程序(如 FontForge)并删除 U+E2C5 处的字形。我已经这样做了,它确实消除了字形。只要你认为能够识别所有可能导致字形的字母组合,这可能是最好的方法。
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String features = "\"liga\"=0";

        TextView textView = (TextView) findViewById(R.id.textview);
        Typeface tf = Typeface.createFromAsset(this.getAssets(), "MenksoftHawang" +
                ".ttf");
        textView.setTypeface(tf);
        textView.setFontFeatureSettings(features);

        textView.setText("IJ ij");
    }
}

我之前不知道这一点。无论是 ij 还是 IJ 都会生成其他字符,而 fi 则会产生一个空白格。这听起来可能就是问题所在了。你有任何想法在字体文件中应该查找哪里吗? - Suragch
@Suragch 还有一件事。看一下 TextViewsetFontFeatureSettings() 方法。它支持 CSS 字体特性设置。那里有一些代码似乎可以控制连字的开启和关闭。这看起来很有前途,但也可能是一个兔子洞。 - Cheticamp
@Suragch 请看我对上面更新的补充。我不想留下一个部分解决方案。 - Cheticamp
我会研究您提供的链接。我还会进行更多的研究,以获取完整的连字列表。不幸的是,在该位置删除字形并不是一个选项,因为它需要用于显示蒙古文本。我还将探索其他编辑字体中连字的方法。 - Suragch
听起来不错。我确信连字是问题所在。FontForge,以及其他一些字体编辑程序,可以让您删除连字,但无法删除字符本身。它也会为您提供字体中所有连字的完整列表。如果您对此解决方案感到满意,那么我将清理答案,使其更加简洁明了。 - Cheticamp
显示剩余5条评论

4

我不确定问题的确切原因,但我认为这可能也与字体有关。

但是,您可以采用微小的字母间距来解决问题,这将显示出预期的字母。

textView1.setText("IJ");

textView2.setLetterSpacing(0.04f);
textView2.setText("IJ");

结果:

在此输入图像描述


这是一个显示图片的html代码片段。

有趣!这让我非常好奇为什么会发生这种情况。以及为什么设置字母间距可以防止它发生。 - Suragch
注意:设置字母间距仅适用于API 21及以上版本。 - Suragch

3

这个问题似乎有三种解决方案:

1. 编程方式禁用连字

您可以使用setFontFeatureSettings(如此处所建议)或setLetterSpacing(如此处所建议)以编程方式禁用连字。这两种方法的一个缺点是它们仅适用于API 21及以上版本。

2. 编辑字体中的连字

您可以使用字体编辑软件修复或删除字体中的连字错误。请参见这个Q&A以了解如何在FontForge中进行操作。潜在的问题是,字体版权所有者可能不允许第三方编辑他们的字体。

3. 使用不同的字体

不同的字体通常是可用的,这种情况也是如此。在问题中有问题的TrueType字体相同的公司还有该字体的OpenType版本。OpenType版本似乎没有连字错误。然而,缺点是它的大小显着增加,这将使应用程序下载的大小更大。
注:
  • 我以前认为在Android 6.0之前没有连字渲染。然而,TrueType字体显然支持一些基本的连字,并且这些在Android 6.0之前的版本中呈现。
  • 以下来自FontForge的图像显示了问题字体中定义的所有连字。 enter image description here

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