首页 > 解决方案 > 跨多个平台的文本显示实现

问题描述

我已经在互联网上搜索了数周,试图弄清楚文本(例如您现在正在阅读的内容)是如何显示在屏幕上的

结果非常稀少。

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

目前我的理解是这样的:

此外,如果在字体文件中定义了字符,例如“F、C、...、Z”,那么如何支持矢量图形(依赖于一组坐标点)?如果没有坐标点,光栅化似乎是改变尺寸的唯一选择。

就我的假设/研究而言,这差不多。

如果您熟悉该主题并且可以提供可能对我自己和其他读者有用的详细答案,请自行决定回答。我发现有多少我们认为理所当然的代码在引擎盖下非常复杂,这令人着迷。

标签: textfontsgraphics

解决方案


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

在 Internet 上显示文本的两个关键组件是 (i) 字符编码和 (ii) 字体。

字符编码是一种方案,通过该方案,诸如拉丁大写字母“A”之类的字符被分配为字节序列的表示形式。过去已经设计了许多不同的字符编码方案。今天,在 Internet 上几乎无处不在使用的是Unicode。Unicode 将每个字符分配给一个代码点,它是一个整数值;例如,Unicode 将 LATIN CAPITAL LETTER A 分配给代码点 65 或十六进制的 41。按照惯例,Unicode 代码点使用四到六个十六进制数字,以“U+”作为前缀。因此,拉丁文大写字母 A 被分配给 U+0041。

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

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

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

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

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

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

该字体还具有字形指标。字形度量之一是每个给定字形的前进宽度。因此,当应用程序绘制一行文本时,它在行的开头有一个起始“笔位置”,如上所述。然后它将第一个字形相应地放在行上,并将笔位置前进第一个字形的前进宽度的量。然后它使用新的笔位置放置第二个字形,并再次前进。以此类推,字形沿线放置。

布局文本行(自然)有更多的复杂性。我上面描述的对于在基本文本编辑器中显示的英文文本来说已经足够了。更一般地,一行文本的显示可能涉及用某些替代字形替换默认字形;这是需要的,例如,当显示阿拉伯文本以使字符显示为草书连接时。OpenType 字体包含一个“字形替换”(或“GSUB”)表,该表提供了字形替换操作的详细信息。此外,字形的位置可以根据各种原因进行调整;例如,将变音符号字形正确放置在字母上。OpenType 字体包含一个提供位置调整数据的“字形定位”('GPOS') 表。


关于字形缩放的附录:

在字体中,网格设置为每个em具有一定数量的单位。这是由字体设计者设置的。例如,设计者可能指定每个 em 1000 个单位,或每个 em 2048 个单位。字体中的字形和所有度量值(字形前进宽度、默认行间距离等)都在字体设计网格单位中设置。

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

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

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

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

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

有关更多详细信息,请参阅Apple 的 TrueType 参考手册中的Digitizing Letterform Designs and Font Engine ,其中包含很多细节。


推荐阅读