本文来自依云's Blog,转载请注明。
GUI 程序我现在依然倾向于 GTK,因为虽然 Qt 拥有良好的跨平台性,但可能是太注重跨平台性了,在 Linux 平台上反而有一些水土不服的问题。
字体太多,支持太少
你可能觉得,系统上字体太少,所以经常会遇到不常见的字符无法显示的情况。然而对于 Qt 来说,字体越多,反而越容易遇到个别字符不能显示的情况。
这是我的 /etc/fonts/conf.d/66-qt.conf
中的一段。因为顺序的原因,我只能放到 /etc 下。除了针对 sans-serif 配置外,我也有同样的配置应用于 serif 和 monospace。
<fontconfig> <!-- Adjust font order for Qt applications --> <alias> <family>sans-serif</family> <prefer> <!-- 格拉哥里字母:Ⰽⱁⱀⱄⱅⰰⱀⱅⰹⱀ Ⰹⱍⰹⰳⱁⰲ --> <family>Noto Sans Glagolitic</family> <!-- 爪哇文:꧁ ꧂ --> <family>Noto Sans Javanese</family> <!-- 西夏文:𗷲𗒅 --> <family>Noto Serif Tangut</family> <!-- 埃及象形文字:𓁹 --> <family>Noto Sans Egyptian Hieroglyphs</family> <!-- 苏美尔楔形文字:𒆠𒂗𒂠 --> <family>Noto Sans Cuneiform</family> <!-- 中日韩统一表意文字扩展 C:𫚥 --> <family>HanaMinB</family> <!-- 拉让文:ꥃ --> <family>Noto Sans Rejang</family> <!-- 越南傣文:ꪀꪑ --> <family>Noto Sans Tai Viet</family> <!-- 切罗基文:ꮳꮧꮢ ᨣ --> <family>Noto Sans Cherokee</family> <!-- 老傣仂文:ᨣ --> <family>Noto Sans Tai Tham</family> <!-- 安纳托利亚象形文字:𔘓 --> <family>Noto Sans Anatolian Hieroglyphs</family> <!-- 马姆穆文补充:𖤍 --> <family>Noto Sans Bamum</family> <!-- 图标字体(PUA): --> <family>OperatorMonoSSmLig Nerd Font</family> <!-- 巴塔克文:ᯤ --> <family>Noto Sans Batak</family> <!-- 古北欧文:ᛋᛖᚱᚣᚨᛚᚳᚨᚾᛞᛚᛖ --> <family>Noto Sans Runic</family> </prefer> </alias> </fontconfig>
这个配置的意思是,把这些字体的优先级提高一些。当使用 fontconfig 的程序要显示字符的时候,它会指定一个模式,匹配到一个字体列表。渲染文字的时候,就可以遍历这个列表,直到找到可以显示这个字符的字体,所以一般来说,只要系统上装了对应字符的字体,它就能显示出来。
但是 Qt 额外地需要这个配置,因为 Qt 只会检查列表中的前255项。而世界上的不同文字那么多,所以想要能够显示它们,就得有一堆字体。比如 noto-fonts 这个包里就有614个字体文件,远超 Qt 支持的数量。总有些奇奇怪怪的文字被网友用来当颜文字,或者挂在名字上彰显个性。不这么调整一下,Qt 遇到了就只能「吃豆腐」了。
空心豆腐
当一个字符显示不出来的时候,那么怎么显示好呢?一般会显示成某种方框。Pango 和火狐会将该字符的 Unicode 码点以十六进制的形式显示在方框里边,这样虽然不知道这个字符长什么样子,但至少知道它是哪个字符,也知道多块豆腐是不是同一字符,在不能复制字符本身的时候很有用。比如当它出现在求助者的截图里的时候,比如当它出现在不能复制的地方的时候。
然而 Qt 不这样做。管你什么字符,Qt 统一显示为空心方框。从视觉上完全无法知晓它到底是什么字符,要是复制不到的话,就别想弄明白你缺什么字体了。
PS: Matrix 客户端 fluffychat 的 Web 版,使用的是 Fluffy 图形界面库,即使在 Web 版,文字渲染依然完全是自己做的。不管浏览器的设置不管系统的设置,豆腐块是带叉号的方框,还不能选中,十分讨厌。
非 BMP 字符
所有使用 UTF-16 的平台(Java、JavaScript、Windows、Qt),外加 MySQL 容易遇到的一个问题:非 BMP 字符(也就是那些 U+FFFF 之后的字符)会被当作是两个字符处理。随着 emoji 的流行,大家应该都修了不少。然而,Qt 在展示非 BMP 字符的时候,你可以选中半个字符。如果不小心漏掉半个的话,复制出来的半个字符就会变成问号(还好不是 GBK 时代那样弄乱后续所有字符)。
font features
一些字体可以通过 fontconfig 设置 fontfeatures 属性来启用(或者禁用)一些特性,比如连字,带斜杠的 0,小型大写字母,居中的中文标点,等等。Pango 很早就支持了,火狐最近也支持了,但 Qt 那边依旧没啥动静。(感谢 Coelacanthus 的评论。)
Mar 08, 2022 05:17:22 PM
还有一个水土不服的问题是,Qt 并不完全听从 fontconfig 的设置,他会忽略 fontconfig 提供的字体 feature 设置,然后用一个 hardcode 的列表取而代之
https://github.com/qt/qtbase/blob/a1fb3971f222afa01583e41f4d8f0e037d2c7892/src/gui/text/qtextengine.cpp#L1687-L1705
这导致我必须专门为其编译一个字体而不是使用 OpenType feature 切换字形等功能
而这个功能,Pango 早在九年前就已经实现,前段时间我参考原有代码给火狐写了一个,然后转过头来想给 Qt 也写一个的时候,看到这个 hardcode 的列表真的是哭笑不得
https://bugreports.qt.io/browse/QTBUG-78645
Mar 09, 2022 11:40:41 AM
哦对,我是记得好像漏掉了一个群友最近告诉我的问题。我自己不用这个所以搞忘了。
Mar 11, 2022 06:57:56 PM
GTK 感觉走一条相反的道路。
GTK4 移除了好多抹除跨平台差异的 API,如果一件事情无法用平台通用的方法实现,他们就放弃它,这对开发者来说很难受。
Mar 12, 2022 10:28:37 AM
呃,能具体说说吗?
「如果一件事情无法用平台通用的方法实现,他们就放弃它」——这听着很 Qt / Java 啊。
Mar 12, 2022 11:09:29 PM
比如说这个: https://stackoverflow.com/questions/62614703/how-to-make-window-centered-in-gtk4
Mar 14, 2022 02:06:27 PM
这就很 Java 了 QAQ
flameshot 不支持选窗口截图也是这种理由唉。
Aug 20, 2022 08:37:14 PM
最近又发现了另一个问题,Qt 在加载可变字体时默认的 Regular 字重似乎是按字体最细渲染的
Aug 21, 2022 10:23:57 AM
啊咧,我好像没遇到过。有复现的例子吗?
Jul 29, 2024 03:58:35 PM
Qt 似乎在字体匹配的时候把匹配到的第一个字体重新匹配一遍()导致如果把一个等宽的中文字体放到sans的第一个之后英文字体也变成等宽的()