When the Down Arrow is not an Upside-Down Up Arrow (2022)

When the Down Arrow is not an Upside-Down Up Arrow (2022)

当向下箭头不是向上箭头的倒影时 (2022)

When the Down Arrow is not an Upside-Down Up Arrow May 25, 2022 First posts are hard, so I’ll start small. Here’s two arrows: ⇧ ⇩ If you’re on reading this on a relatively recent smartphone, chances are you see two reflected, but otherwise identical arrows. One points up and the other points down. 当向下箭头不是向上箭头的倒影时,2022年5月25日。第一篇文章总是很难写,所以我从简单的开始。这里有两个箭头:⇧ ⇩。如果你是在较新的智能手机上阅读这篇文章,你很可能会看到两个互为镜像但外观一致的箭头。一个指向向上,另一个指向向下。

On MacOS (Big Sur 11.6), however, the up arrow is much squatter than the one pointing down. This is a photo of how it renders on my MacBook Pro: Alex, those are two different arrows. Well yes, in the sense that one is up and the other is down, but they should be otherwise identical. I input two Unicode characters, “Upwards White Arrow” and “Downwards White Arrow”, and I expected (reasonably, I think) that the down arrow would have the same proportions as the up arrow, only pointing down. So what’s going on here? 然而,在 MacOS (Big Sur 11.6) 上,向上箭头比向下箭头要扁得多。这是一张它在我 MacBook Pro 上渲染效果的照片:Alex,那是两个不同的箭头。嗯,是的,从一个向上一个向下的意义上来说确实不同,但除此之外它们理应是完全一样的。我输入了两个 Unicode 字符,“向上白色箭头”和“向下白色箭头”,我(我认为合理地)预期向下箭头应该与向上箭头具有相同的比例,只是方向朝下。那么这里发生了什么呢?

Let’s start with the basics. Unicode, if you’ve never never had to think about it before, is the international standard for representing text in computer software. When rendering text, your computer reads a series of Unicode “code points” and then turns those into the glyphs specified by your font. 让我们从基础开始。如果你以前从未考虑过 Unicode,它是计算机软件中表示文本的国际标准。在渲染文本时,你的计算机读取一系列 Unicode“码位”(code points),然后将它们转换为字体指定的字形。

In Latin script, every character you type has a corresponding hexadecimal number—a code point—that gets saved in the computer’s memory when you type it. Because each character in the English alphabet is a single code point, representing English in Unicode is straightfoward. The uppercase letter “C” is code point U+0043, lowercase “r” is U+0072, and lowercase “o” is U+006F. Other code points include the semicolon (U+003B), the lowercase letter “w” (U+0077), and the percent symbol (U+0025). 在拉丁字母中,你输入的每个字符都有一个对应的十六进制数字——即码位——当你输入它时,它会被保存在计算机内存中。由于英文字母表中的每个字符都是一个单一的码位,因此用 Unicode 表示英语非常简单。大写字母“C”的码位是 U+0043,小写“r”是 U+0072,小写“o”是 U+006F。其他码位包括分号 (U+003B)、小写字母“w” (U+0077) 和百分号 (U+0025)。

To display the text, the computer will read each code point and display whatever that sequence of code points should represent, in the chosen font. When I write “Crow”, what I’m really writing is: (U+0043)(U+0072)(U+006F)(U+0077) (The code points have letters because they’re written in hexadecimal; not important for our purposes.) That’s why you can easily change the font on a webpage or a document—your computer has all this text saved as code points, it only has to render them differently. 为了显示文本,计算机将读取每个码位,并以所选字体显示该码位序列所代表的内容。当我写“Crow”时,我实际写的是:(U+0043)(U+0072)(U+006F)(U+0077)(码位包含字母是因为它们是用十六进制书写的;这对我们来说不重要。)这就是为什么你可以轻松更改网页或文档上的字体——你的计算机将所有这些文本保存为码位,它只需要以不同的方式渲染它们即可。

Other languages, where multiple code points might combine to create a single character, are far more complex, but operate under essentially the same principle: Unicode provides the code points, the computer translates that into text using a font. 其他语言(其中多个码位可能组合成一个字符)要复杂得多,但其运作原理基本相同:Unicode 提供码位,计算机使用字体将其转换为文本。

Back to my messed-up arrows. The ⇧ is the “Upwards White Arrow” code point (U+21E7) and the ⇩ is “Downwards White Arrow” (U+21E9). If you’re looking closely at those hexadecimals, you’ll see they’re two numbers apart. In between them (U+21E8) is, you guessed it: ⇨, the “Rightwards White Arrow.” 回到我那乱七八糟的箭头。⇧ 是“向上白色箭头”码位 (U+21E7),而 ⇩ 是“向下白色箭头” (U+21E9)。如果你仔细观察这些十六进制数,你会发现它们相差两个数字。在它们之间 (U+21E8) 的正是你猜到的:⇨,“向右白色箭头”。

Well maybe Unicode specifices that these arrows should look different for some reason, and I’m using them wrong. As with all open standards, you can simply go look up the definition. The Unicode consortium has a webpage where you can search the whole standard by code point! Which is how I ended up with a PDF of Unicode characters 2190-21FF (8592-8703 in decimal), a subset of the standard appropriately titled Arrows: 也许 Unicode 出于某种原因规定这些箭头应该看起来不同,而我用错了。和所有开放标准一样,你可以直接去查阅定义。Unicode 联盟有一个网页,你可以通过码位搜索整个标准!这就是我最终得到一份 Unicode 字符 2190-21FF(十进制 8592-8703)PDF 的原因,这是标准中一个恰如其分地命名为“箭头”的子集:

That’s a lot of arrows! But wait… compututer, enhance! Those are the arrows I want! They all look the same! If the Unicode standard suggests they should be the same, why don’t my arrows do that? 那真是好多箭头!但是等等……计算机,放大!那些就是我想要的箭头!它们看起来都一样!如果 Unicode 标准建议它们应该相同,为什么我的箭头不是这样呢?

One StackOverflow answer for a different set of arrows posits that the Lucida Grande font (MacOS default) might render the arrows differently. This could have been the case for those arrows, but it would also be a weird thing for a font to do. All these arrows live right next to each other on the standard; there aren’t a lot of good reasons to render one of a set differently. StackOverflow 上针对另一组箭头的一个回答认为,Lucida Grande 字体(MacOS 默认字体)可能会以不同的方式渲染箭头。对于那些箭头来说情况可能是这样,但对于一种字体来说,这样做是很奇怪的。所有这些箭头在标准中都紧挨在一起;没有太多充分的理由将其中一个渲染得与众不同。

The answer is that the Lucida Grande font does not render the other arrows at all, it only renders the Upwards White Arrow. The other arrows are rendered in an entirely different font, a fallback font called STIXGeneral. You can see this by inspecting the following line in your browser’s Dev Tools: ⇧⇩ 答案是 Lucida Grande 字体根本不渲染其他箭头,它只渲染“向上白色箭头”。其他箭头是由一种完全不同的字体渲染的,这是一种名为 STIXGeneral 的回退字体。你可以通过在浏览器的开发者工具中检查以下行来看到这一点:⇧⇩

Depending on your browser (I’m using Firefox) and OS, you might see something like this: PT Sans is the font used on this website, but it doesn’t have either of the arrows, so Firefox looks to my system font, Lucida Grande, and renders the Upwards White Arrow using it. Then, seeing that neither of those two fonts supports the Downwards White Arrow, it switches to a more comprehensive fallback font called STIXGeneral to display the character. 根据你的浏览器(我使用的是 Firefox)和操作系统,你可能会看到类似这样的内容:PT Sans 是本网站使用的字体,但它没有这两个箭头中的任何一个,所以 Firefox 会查看我的系统字体 Lucida Grande,并使用它渲染“向上白色箭头”。然后,由于发现这两个字体都不支持“向下白色箭头”,它会切换到一种更全面的回退字体 STIXGeneral 来显示该字符。

Why render just one of the arrows? According to Unicode CJK & Unihan group chair and Apple Font Developer Dr. Ken Lunde, some fonts implemented just the Upwards White Arrow because it is present in many Traditional Chinese fonts via a different, non-Unicode encoding called Big5. Lucida Grande presumably supported Big5 encoding, and the Upwards White Arrow glpyh was later mapped to its unicode representation, once the “Arrows” set came out. 为什么要只渲染其中一个箭头?据 Unicode CJK & Unihan 小组主席兼苹果字体开发人员 Ken Lunde 博士称,一些字体只实现了“向上白色箭头”,因为它通过一种名为 Big5 的不同非 Unicode 编码存在于许多繁体中文字体中。Lucida Grande 大概支持 Big5 编码,而当“箭头”集发布后,“向上白色箭头”的字形后来被映射到了它的 Unicode 表示上。

The creators of that font never actually specifically looked at the “Arrows” set and said “we’ll support this, but only the up arrow;” they simply re-used the characters that they had ready to go, to support what they could. 该字体的创建者从未专门查看过“箭头”集并说“我们将支持这个,但只支持向上箭头”;他们只是重用了他们现有的字符,以支持他们所能支持的内容。

There is a platform-independent solution though, one that renders properly no matter what fonts are installed, as long as they have the Upwards White Arrow. I used this little trick to mimic the Reddit upvote arrow recently. Try inspecting the element below: ⇧⇧ Have fun! 不过,有一个与平台无关的解决方案,无论安装了什么字体,只要它们有“向上白色箭头”,它就能正确渲染。我最近用了这个小技巧来模仿 Reddit 的点赞箭头。试着检查下面的元素:⇧⇧ 玩得开心!

Enormous thanks to Twitter users @ken_lunde, @fake_unicode, and @litherum for finding my tweet and tagging various experts to help me explore the issue. If you read this post and would like to be credited by name and bio, let me know. 非常感谢 Twitter 用户 @ken_lunde、@fake_unicode 和 @litherum 找到我的推文并标记了各路专家来帮助我探索这个问题。如果你读了这篇文章并希望在文中署名和介绍,请告诉我。

Update 1: Some commentors have pointed out that it’s possible the Upwards White Arrow glyph originally represented the shift key, instead of CJK characters. If you were involved in the creation of the Lucida Grande font and know where it came from, contact me! 更新 1:一些评论者指出,“向上白色箭头”字形最初可能代表 Shift 键,而不是中日韩字符。如果你参与了 Lucida Grande 字体的创建并知道它的来源,请联系我!

Further reading The Rust Programming Language has a very good explanation of how they model the complexities of UTF-8, and graphemes in particular: Storing UTF-8 Encode 延伸阅读:Rust 编程语言对他们如何建模 UTF-8 的复杂性(特别是字位)有很好的解释:存储 UTF-8 编码。