深入理解 Linux fontconfig 第三部分

上一篇说到,我们 debug 了 FcConfigSubstitute 和 FcConfigReference,最后找到了 FcFileScanFontConfig,一番魔改后发现 <match target="scan"> 虽然应用了,但是没有影响最终 FontSet 中 “Noto Scan CJK SC” 的 charset。 我们这篇要继续 debug: FcConfigSubstitute (config, font, FcMatchScan) FcFileScanFontConfig 中的这段代码。 无踪无迹的指针 我们知道,上面的 font 是一个指针。要想前后输出有所变化,是一定要修改这个指针指向的数据的。也就是说,我们只需要去 FcConfigSubsitute里找修改指针指向数据的部分就可以了。所以我们只需要关注应用规则集时候的 case FcRuleEdit 里 case FcOpAssign 的部分就好。 通过 if (value[object]) 这个判断我们找到了 test 跟 edit 的联系,原来是用 test 去找到要修改的部分: /* different 'kind' won't be the target of edit */ if (!value[object] && kind == r->u.test->kind) value[object] = vl; 但是剩下的都是 FcConfigAdd 和 FcConfigDel 是修改 config 的,并不直接修改 Font。...

November 26, 2022

深入理解 Linux fontconfig 第二部分

这是深入理解 Linux fontconfig 系列的第二部分,第一部分请移步。 前面说到了 FcConfigSubstitute这个函数,它实际封装了: FcConfigSubstituteWithPat (FcConfig *config, FcPattern *p, FcPattern *p_pat, FcMatchKind kind) { 这不过第二个 p_pat 是 0。 整个 FcConfigSubstituteWithPat 函数比较长,就不全贴出来了。具体在这里。 它先是获取了 config: config = FcConfigReference (config); 然后针对 FcMatchPattern 这个 kind 先往 pattern p 里加入了 lang 和 prgname 元素。接着就是针对从 config 里取到的某个 kind 的 RuleSet 挨个应用到 pattern。 FcConfigReference 实际上只有给它传 0 才会做一大堆初始化,不然就会使用你自己的 config。前面说过 Chromium 的 config 就是它自己做的: FcConfig* config = GetGlobalFontConfig(); 实际上包括 fontconfig 自己在内都是直接传 0 让它做初始化的。初始化最重要的函数是: config = FcInitLoadConfigAndFonts (); 这个函数在 fcinit....

November 25, 2022

深入理解 Linux fontconfig 第一部分

这篇文章是之前两篇关于 fontconfig 文章的后续,前文见第一篇 fontconfig 几个常见的坑,第二篇 Color Emoji in openSUSE。 偶然在网上看到 Alynx Zhou 的Fontconfig 和 Noto Color Emoji 和抗锯齿里面说看过我的文,没想到我对 fontconfig 的研究在中文圈还是排在前头的 ⌣。原来大家对后续还是有期待的。 先说一下我 2020 年写完那两篇文章干什么去了。我在 Color Emoji in openSUSE 里面不是提到过几个 .rb 代码文件嘛,有些人说找不到它们,其实它们在我的 fork 里面:marguerite/fonts-config。后来我又重写了 openSUSE 的 fonts-config, 在 marguerite/fonts-config-ng。可以自动生成 Emoji Glyph 的 Blacklist,也就是 81_emoji_blacklist_glyphs.conf。 但是呢,实际上这种方法在现实中不怎么灵。 <match target="scan"> <test name="family"> <string>Noto Sans CJK SC</string> </test> <edit name="charset" mode="assign"> <minus> <name>charset</name> <charset> <int>0x2122</int> </charset> </minus> </edit> </match> 大家可以看到我在 81_emoji_blacklist_glyphs.conf 里面已经有从 Noto Sans CJK SC 字体减除 0x2122(™)这个 charset leaf 的操作了,但是在 Chromium 浏览器中测试是这样的:...

November 25, 2022

使用 google 的 fontmake 复活文泉驿项目

最早是 openSUSE 中文论坛上的一个帖子疑似文泉驿字体BUG把我带入字体的坑的,以前我只是停留在玩 fontconfig 的阶段。为了解决这个BUG,我简单的学习了一下 fontforge,发现带草字头和竹字头的字形简直是太多了,绝对不能手改,于是萌生了编程化地解决这个BUG的想法。接着无意中就把文泉驿的 ttc 转成了 ufo3 格式,并且在 README.md 中发愿要使用 googlefonts 的 fontmake 工具来编译文泉驿。 其实当初没有想那么多的,转换成 ufo3 格式并使用 fontmake 来编译文泉驿有什么好处呢?首先文泉驿作为一个中古字体(只能说比什么 UMing 之类的要新一点),它一直以来不是那么开源的。我理解的开源要有源代码,作为普通用户我很难去把 ttc 当成是源代码的(当然对于会 fontforge 的人来说 ttc 就约等于源代码),我理解的源代码就是能作为文本打开的。wenq.org 曾经是有一个在线编辑器的,用户们可以众筹在线编辑字形,FangQ 会在后台把用户编辑的字形搜集起来去做新的 ttc。随着时间,这套机制已经是废弃了的,也就可以说我们无法再获取到它的源代码了。把文泉驿转存成 ufo3 格式,它的每一个字形都是文本化的 glif 格式,相当于复现了源代码。有了源代码一切都好办了,未来我们还可以再制作在线编辑器,让文泉驿原来的模式继续下去。其次使用 fontmake 来编译文泉驿,相当于让文泉驿与所有现代字体比如 Noto Sans CJK 又站在了同一起跑线上。最近几年字体制作发展的很快,前几年我在 github 上是很少能看见字体项目的,但自从 Adobe 和 Google 在 github 上开源了他们的一些字体工具后呢,现在各种拼接字体大行其道了,比如更纱黑体这种,甚至偶尔还会给我推送一些做原创字体的项目。这些曾经都是与文泉驿无缘的,正如我所说的,大多数人不认为 ttc 是源代码,所以没法利用文泉驿。但现在好了,我可以用 fontmake 编译文泉驿,就表示说 fontbakery 我也可以用,ttfautohint 我也可以用,换句话说,如果我的 ufo3 格式通过了 fontbakery 的 adobefonts 或 googlefonts 的系列检查,且不论字形美丑,至少在字体质量上当年手搓出来的文泉驿是可以进化到 Noto Sans CJK 的水平的。 大多数工作是两年前做的,当时我写了一个 ufo3 的解析库,修复了草字头和竹字头的问题,并且使用 fontforge 再次生成了 ttf 字体。后来去打包 fontmake 的时候遇到了 skia-pathops 的编译问题,项目就停滞了,甚至草字头和竹字头的问题最终也没有反馈回发行版中。...

November 23, 2022

为 Discourse 开发一个 Onebox 插件(三)去掉 watir 依赖

我们在前两篇文章中已经基本实现了这个 engine,但是目前有一个非常恼人的依赖问题:由于 build.opensuse.org 的 package build status 是使用 javascript 加载的,而 nokogiri gem 并不支持 javascript,这就造成了我们需要使用 watir gem 去点一下网页上的 refresh 按钮,才能获取正确的 build status: browser = Watir::Browser.new(:chrome, chromeOptions: { args: ['--headless', '--window-size=1200x600', '--no-sandbox', '--disable-dev-shm-usage'] }) browser.goto(link) browser.image(id: reload_id).click 但是随之带来的依赖是非常恐怖的,我们需要一个 chrome-driver 和一个 chromium。要知道 discourse 没有装在本地的,都是装在 VPS 上面,一个 chromium 带来的空间和内存使用是十分恐怖的。于是我们通过这篇文章来教你如何干掉 watir 依赖。 我们测试用的界面是 marketo package,使用 chromium 右键查看网页源代码,我们发现 Refresh 按钮是这样的: <div accesskey='r' class='btn btn-outline-primary build-refresh float-right' onclick='updateBuildResult(&#39;&#39;)' title='Refresh Build Results'> Refresh <i class='fas fa-sync-alt' id='build-reload'></i> </div> 点击它的时候执行的是“updateBuildResult(’’)”这个 javascript 函数。对于现代网页开发而言,基本上 javascript 都写在一个文件里然后在 html 中引用的,我们翻遍了网页源代码只发现了一个引用:...

November 28, 2020

为 Discourse 开发一个 Onebox 插件(二)从零开始写 custom engine

上一篇为 Discourse 开发一个 Onebox 插件(一)理解 Onebox gem 里,我们知道了 Onebox 是什么样的结构。这篇我们先来写一个 onebox 的自定义引擎。 我们先准备一个脚手架 openbuildservice_onebox.rb module Onebox module Engine class OpenBuildServiceOnebox include Engine include LayoutSupport include HTML always_https matches_regexp(%r{^(https?://)?build.opensuse.org/\w+/show/(.)+$}) private end end end 我们已经知道了这两个 module Onebox和 module Engine 嵌套 class OpenBuildServiceOnebox 是为什么,为了让 Preview.new 的 ordered_engines能够找到我们这个以 Onebox 结尾的类,并调用它通过 matches_regexp 设置的 @@matcher来与真正的 URI 对比来确定唯一一个 engine。include Engine的作用是为了得到 ClassMethods里面定义的与 URI 做相等比较的方法。另外 OpenBuildServiceOnebox 这个 class 没有 initialize 方法是因为 module Engine里面已经统一实现了,我们可以直接用 @options和 @uri 这样的实例变量。 但是有一个问题是不是我忽略了?没有 to_html 这个最终把 URL 转成 html preview 的函数呀!...

November 28, 2020

为 Discourse 开发一个 Onebox 插件(一)理解 Onebox gem

为 Discourse 开发一个 Onebox 插件(一)理解 Onebox gem 我们 openSUSE 中文论坛用的是 discourse,有一天给用户贴了一个 OBS 的链接,突然想到是不是可以让它也能像 github 一样有一个漂亮的预览小窗口 🤓 于是说干就干:discourse-openbuildservice-onebox 插件。 以下是教程,由于涉及到目前最大的 Ruby on Rails 程序 discourse,会分成几部分来讲解。第一部分我们来试着理解一下 discourse 出品的 onebox gem。 Ruby 作为一门脚本语言,所有对象的方法都可以被重写。学名叫做 Meta Programming。这是理解 onebox gem 的基础。 我们下面来看 discourse 是怎么使用 onebox gem 的,下面是 app/models/post_analyzer.rb 的 cook 函数,这个函数负责把你输入的文字转为 html 保存在 postgresql 数据库,是最基础的函数之一: def cook(raw, opts = {}) [...] result = Oneboxer.apply(cooked) do |url| @onebox_urls << url if opts[:invalidate_oneboxes] Oneboxer.invalidate(url) InlineOneboxer.invalidate(url) end onebox = Oneboxer....

November 28, 2020

Color Emoji in openSUSE

上一篇文章里我们讲了 fontconfig 常见的几个坑,今天我们来继续讲一讲 openSUSE 的 Colored Emoji 支持。也就是如何配置 Noto Color Emoji 这个字体用于网页显示(用于终端显示是另一回事,涉及到比如 vte 的 teminal 之类的,有几个相关的 bug 涉及到比如宽度之类的;GTK/Qt 显示又是另一回事,涉及到 cairo)。 为什么是这个字体呢?我也很无奈啊…Noto 系列是 openSUSE 的默认字体,可以说除了英文 locale 别的都是 Noto 来显示的,Noto Color Emoji 跟其它 Noto 字体的 metrics 兼容。这一点就秒杀了其它 Emoji 字体。再者 Emoji 字体本身就不多,目前为止也就 45-generic.conf 里列出的那么不到十个,EmojiOne Color 因为版权问题不再开发了,真正 Linux 上能用的 Colored Emoji 也就剩下一个 Twitter Color Emoji 了。剩下的要么专有的要么没有颜色。 我们先来回忆一下之前的说法: 除了比如 Unicode Full Emoji List 这种专门用于测试 emoji 显示的 URL,大部分我们常见的网页在 css 里是不写 emoji 字体的。 据我摸索的经验,字体的匹配分为三种场景 第一种是直接去匹配这个字体,fc-match “Noto Color Emoji” 这样,也就是 css 的 font-family 里直接写了这个字体。...

November 4, 2020

fontconfig 几个常见的坑

最近 Microsoft 加入 OIN,贡献了它的 60000+ 项专利,这使得 openSUSE 的 freetype2 终于能够开启 ClearType 引擎了。之前 infinality 项目贡献了三大块,我们引入了两大块,但是其中第一大块的 ClearType 引擎没有默认开启,第三大块的非专利色彩滤镜也一直没有。现在专利的问题没有了,freetype2 终于更新到完全体了,它现在有两个引擎,第一个引擎是 Adobe 的 CFF 引擎,主要用于 Noto Sans CJK,第二个引擎就是 infinality 贡献的 ClearType 引擎了。总之都是好东西。 后端更新了,我也需要更新 fonts-config 来默认设置 rgba 和 hintstyle。openSUSE 的 fonts-config 是一系列跟 fontconfig 一样的字体配置文件,已经很老了,于是我需要 modernize 它一下。在这个过程中我几乎看了网上能够找到的全部 fontconfig 相关文章。里面大坑套小坑,有必要专门写文来澄清一下: 第一个坑:从 monospace 中清除 sans-serif 比如 Hack 字体的官方配置,还有最著名的 eev’s rant about fontconfig,都推荐了这么一种做法: <match> <test name=“family” compare=“eq”> <string>sans-serif</string> </test> <test name=“family” compare=“eq”> <string>monospace</string> </test> <edit name=“family” mode=“delete”/> </match> 意思是如果 pattern 有 sans-serif,还有 monospace,就把 sans-serif 删除。目的是让字体只有一个 generic name。起因在于 /usr/share/fontconfig/conf....

November 4, 2020

30 秒钟在 Github Pages 上搭建一个 openSUSE 风格的部落格

之前的 Ghost 管理密码丢了 :-( 昨晚花了一个晚上的时间把之前丢失的部落格文章通过 web.archives.org 找回到了 2011 年。09~10 年的部落格文章等想怀旧的时候再继续找。下一步应该研究的是怎么恢复和整合评论。 不过这个坑就不一定哪天来填啦。 考虑到我博客更新的速度和访问量,我觉得还是不要把它放在论坛服务器上了。毕竟 Ghost 是用 nodejs 的,一直跑着太占资源;搬服务器就要迁移,我这种懒人,迁完论坛就不管的, 博客要么丢文章要么丢评论。还是用 github 来做博客好了。 Github Pages 是由 Jekyll 驱动的静态博客,使用 Markdown 来写文章,用 git 管理,这几个对我来说都不是很难,毕竟我是一个 Ruby 程序猿。Github 自己的文档基本无用,因为文档跟用户之间少了点东西。 它只告诉了我 Github Page 其实就是一个静态网页生成器加 HTTP 服务器,你喂 Markdown 格式的文章给它,它在 yourname.github.io 上显示出来。这用来建立一个单页面是足够的。 但是它并没有说你想做一个博客应该怎么办。因为其实那是 Jekyll 这个静态网页生成器的事情。你需要给它写模板。 所以我参考了 Smashing Magazine 上面的 Build A Blog With Jekyll And Github Pages,找了一个现成的 Jekyll Now 模板。自己简单改了下。风格参考了 Grover Chou 的部落格。我觉得他那个 openSUSE Leap 风格的主题很好看,于是就抄过来了。配色和样式是抄的我们 openSUSE 中文论坛。点我预览。 要是有想要用我这套主题建博客的,建立过程很简单: 去 github 上面 fork 我的 marguerite....

December 31, 2016