跨多个平台的文本显示实现



我已经在互联网上搜索了好几个星期,试图弄清楚文本(比如你现在正在阅读的内容)是如何在屏幕上显示的。

结果少得惊人。

我遇到了光栅化、位图、矢量图形等概念。我不明白的是,底层实现是如何以我们人类所理解的方式在所有系统(windows、linux等)中如此一致地工作的。是否在某个地方定义了规范?实现代码是开源的,公众可以查看吗?

我现在的理解是:

  • 使用外部绘图程序创建字体,每次一个字符
  • 将这些字符添加到特定语言库可以理解的字体文件中
  • 这些字符然后根据GPU的需要从文件中读取,并以由父代码定义的线性方式显示在屏幕上

此外,如果在字体文件中定义了字符,如'F,C。。。,Z',如何支持矢量图形(依赖于一组坐标点)?如果没有坐标点,光栅化似乎是更改大小的唯一选项。

这大约是我的假设/研究范围。

如果你熟悉这个话题,并且能提供一个对我和其他读者有用的详细答案,请自行回答。我觉得很有意思的是,我们认为有多少代码是理所当然的,而这些代码在引擎盖下却非常复杂。

以下提供了一个概述(省略了许多血腥的细节):

在互联网上显示文本的两个关键组成部分是(i)字符编码和(ii)字体。

字符编码是一种方案;A";,被分配了作为字节序列的表示。过去已经设计了许多不同的字符编码方案。如今,在互联网上几乎无处不在的是Unicode。Unicode将每个字符分配给代码点,这是一个整数值;例如,Unicode将拉丁大写字母A分配给代码点65,或以十六进制表示的41。按照惯例,Unicode码点是指使用四到六个十六进制数字;U+";作为前缀。因此,拉丁大写字母A被分配给U+0041。

字体提供用于显示文本的图形数据。多年来创建了各种字体格式。如今,互联网上普遍使用的是遵循OpenType规范的字体(这是1991年左右创建的TrueType字体格式的扩展)。

在屏幕上看到的字形。OpenType字体包含字形的数据,还包含一个将Unicode代码点映射到相应字形的表。更准确地说,字符到字形映射(或"cmap")表将Unicode代码点映射到字形ID。代码点由Unicode定义;字形ID是字体内部实现的详细信息,用于在其他表中查找字形描述和相关数据。

OpenType字体中的Glyph可以定义为位图,也可以(更常见)定义为矢量轮廓(贝塞尔曲线)。字形描述有一个假定的坐标网格。然后,矢量轮廓被定义为贝塞尔曲线控制点的坐标对的有序列表。当显示文本时,基于所请求的文本大小(例如,10点)和显示器上的像素大小,将矢量轮廓缩放到显示网格上。光栅化器读取字体中的控制点数据,根据显示网格的需要进行缩放,并生成一个位图,该位图放置在屏幕上的适当位置。

关于显示光栅化位图的一个额外细节是:大多数操作系统或应用程序都会使用某种过滤,使字形看起来更平滑、更清晰。例如,灰度抗锯齿过滤器会将字形边缘的显示像素设置为灰度级,而不是纯黑或纯白,以使缩放轮廓与物理像素边界不完全对齐时(大多数情况下)边缘看起来更平滑。

我提到";在适当的位置";。字体具有整个字体和每个字形的度量(定位)信息。

字体范围的指标将包括文本行的推荐行到行距离,以及基线在每行中的位置。这些度量以字体的字形设计网格为单位表示;基线对应于网格内的y=0。要开始一行,将(0,0)设计网格位置与页面布局中基线与文本容器边缘相交的位置对齐,并定位第一个字形。

字体还具有字形度量。字形度量之一是每个给定字形的前进宽度。因此,当应用程序绘制一行文本时,它会有一个"开始";笔位置";如上所述。然后,它相应地将第一个字形放在线上,并将笔的位置前移第一个字形的前进宽度。然后,它使用新的笔位置放置第二个字形,然后再次前进。等等,因为字形是沿着线放置的。

(自然地)在布置文本行时会有更多的复杂性。我上面描述的内容对于在基本文本编辑器中显示的英文文本来说已经足够了。更一般地,文本行的显示可以包括用某些替代字形替换默认字形;例如,当显示阿拉伯语文本以使字符以草书形式连接时,就需要这样做。OpenType字体包含一个";字形替换";(或"GSUB")表,该表提供了字形替换操作的详细信息。此外,由于各种原因,可以调整字形的位置;例如将变音符号字形正确地定位在字母上。OpenType字体包含一个";字形定位";("GPOS")表,提供位置调整数据。如今,操作系统平台和浏览器支持所有这些功能,因此可以使用OpenType字体显示许多不同语言的Unicode编码文本。


字形缩放的附录:

在字体中,每个em设置一个具有一定数量单位的网格。这是由字体设计者设置的。例如,设计者可能会指定每em 1000个单位,或每em 2048个单位。字体中的字形和所有度量值——字形前进宽度、默认的线对线区分等——都以字体设计网格单位设置。

em与作者设置的内容有何关联?在文字处理应用程序中,通常以点数为单位设置文本大小。在印刷界,点是一个定义良好的长度单位,大约但不完全是1/72英寸。在数字印刷术中,点的定义正好是1/72英寸。现在,在文字处理器中,当您将文本大小设置为,例如,12点时,这实际上意味着每个em12点。

因此,例如,假设一个字体是使用每个em 1000个设计单位设计的。并且假设一个特定的字形正好有1 em宽(例如,一个em短划线);就设计网格单元而言,它将正好有1000个单元宽。现在,假设文本大小设置为36个点。这意味着每em 36个点并且36个点=1/2〃;,因此字形将精确地打印1/2〃;宽的

当文本被光栅化时,它是为具有特定像素密度的特定目标设备完成的。桌面显示器可以具有96dpi的像素(或点)密度;打印机可能具有1200dpi的像素密度。这些是相对于英寸的,但从英寸你可以得到点,对于给定的文本大小,你可以得到ems。根据设备和文本大小,每个em最终会有一定数量的像素。因此,光栅化器采用以每个em的字体设计单位定义的字形轮廓,并根据每个em的给定像素数对其进行放大或缩小

例如,假设一个字体是使用每em 1000个单位设计的,而打印机是1000 dpi。如果文本被设置为72点;每个em,字体设计单位将与打印机点完全匹配。如果文本设置为12点,则光栅化器将按比例缩小,以便每个打印机点有6个字体设计单位。

此时,字形轮廓中的细节可能无法与设备网格中的整个单元对齐。光栅化器需要决定哪些像素/点得到墨水,哪些没有。字体可以包括";提示";影响光栅化器行为。这些提示可能会确保某些字体细节保持对齐,或者这些提示可能是基于当前pixels-per-em将Bezier控制点移动一定量的指令。

有关更多详细信息,请参阅苹果TrueType参考手册中的数字化字体设计和字体引擎,该手册将详细介绍。