17.2 字体的背景知识

摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P774

        本章剩余部分会讨论使用不同字体的各种方法。在我们开始讲解代码之前,我们先介绍一下 Windows 字体内部实现的基本知识。

17.2.1  字体分类

        Windows 支持两大类字体,也就是 GDI 字体和设备字体。GDI 字体是存储在硬盘文件中的。而设备字体是输出设备自身本来就支持的。比如,通常打印机中都有多个内置的设备字体。

        GDI 字体分三类:点阵字体(raster font)、笔画字体(stroke font)和Truetype 字体

        点阵字体中每个字符都以像素形式表示,并存储在一个位图里,所以又称为位图(bitmap)字体。每种点阵字体都是针对字符的特定尺寸和特定纵横比来设计的。Windows 通过简单地复制 GDI 点阵字体中的像素行或像素列,就可以产生较大的字符。但是字符只可以整数倍放大,而且有一定的放大上限。因此,GDI 点阵字体也被称作“不可缩放”(no scalable)字体。它们不能被任意的放大和缩小,但有个优点就是高性能(显示速度极快)和高可读性(因为是手工设计的字体,所以非常清晰)

        不同的字体有不同的字样名字(typeface name)。以下的字样名字表示的是点阵字体:

System                       (用于 SYSTEM_FONT)
FixedSys                     (用于 SYSTEM_FIXED_FONT)
Terminal                     (用于 OEM_FIXED_FONT)
Courier
MS Serif
MS Sans Serif                (用于 DEFAULT_GUI_FONT)
Small Fonts

        每个点阵字体只有有限的几种尺寸(不超过 6 种)。Courier 字体是等宽字体,外形和打字机使用的字体类似。“serif” 是衬线,指的是笔画边缘的装饰部分。“sans serif”就是无衬线的意思,所以“sans serif”字体就不含衬线。Windows 早期版本中,MS Serif 和 MS Sans Serif 字体(本处 MS 是 Microsoft 缩写)又分别被称为 Tms Rmn (意思是其类似 Times Roman 字体)和 Helv (类似 Helvetica 字体)。Small Fonts 则是专为显示小字而设计的。

        在 Windows 3.1 版本之前,Windows 提供的 GDI 字体中,除了点阵字体,就只有笔画字体了。笔画字体由一系列的点和链接点的线段组成。笔画字体可以进行平滑地缩放,也就是同以字体可以被缩放到任意尺寸,适用于各种分辨率的图形输出设备。但是性能就比较差,当在字号很小时,文本不会很清晰,当字号很大时,由于笔画是单线条,所以文字会显得很单薄。笔画字体有时又称为绘图仪字体,因为它们特别适合于绘图仪,但是其他场合就不太适合。笔画字体的字样有:Modern、Roman 和 Script。

        对于 GDI 点阵字体 GDI 笔画字体,对于不同的字形,比如“粗体”,“斜体”,“下划线”和“删除线”,Windows 都不需要存储额外的字体,而可以直接“合成”(synthesize)这些字体。比如,对斜体,Windows 只要简单地把字符的上部向右方移动一点就可以了。

        还有就是 TrueType 字体,本章的剩余部分将主要讨论它。

17.2.2  TrueType 字体

        TrueType 字体中每个字符都通过字符的轮廓线(可以是直线,也可能是曲线)来定义的。Windows 可以通过改变字符轮廓线的坐标对 TrueType 字体进行缩放。

        当程序开始使用某一特定大小的 TrueType 字体时,Windows 对该字体进行一个“点阵化”(rasterize)的过程。也就是 Windows 先使用 TrueType 字体文件中含有的“提示信息”对每个字符的轮廓坐标进行缩放。在进行缩放计算时,有时会发生四舍五入的情况,这些提示信息就可以补偿四舍五入带来的误差,避免生成很难看的字符(例如,在某些字体中,大写 H 的两竖应该一样宽,但简单对字体进行缩放可能会导致其中一竖比另一竖要宽一个像素。有了那些提示信息就可以避免发生这种现象)。缩放后产生的字符轮廓被用于建立该字符的位图,这些位图则被存在内存以备将来使用。

        最早 Windows 自带了 13 种 TrueType 字体,它们的字样名称如下:

Courier New                       Times New Roman Bold Italic
Courier New Bold                  Arial
Courier New Italic                Arial Bold
Courier New Bold Italic           Arial Italic
Times New Roman                   Arial Bold Italic
Times New Roman Bold              Symbol
Times New Roman Italic 

        新的 Windows 版本中加入了更多的 TrueType 字体。特别要说的是,Lucida Sans Unicode 字体含有世界上一些非英语语言的字符。

        Courier New,Times New Roman 和 Arial 这三个字体系列和点阵字体类似。Courier New 字体是一个等宽字体,它是模拟机械打字机输出的字符。Times New Roman 完全复制了 Times 字体(Times 字体最初是为《伦敦时报》设计的字体,主要用于印刷制品)。Times New Roman 字体看上去非常清晰。Arial 是一种无衬线(sans serif)字体,它其实就是克隆了 Helvetica 字体。Symbol 字体是一个手写字符集。

17.2.3  属性和样式的区别

        在前面的 TrueType 字体列表中,粗体或斜体的 Courier、Times New Roman 和 Arial 字体都有自己单独的字样名称,是属于独立的字体。这和传统印刷术的命名方式是一致的。但是,计算机用户通常认为粗体或斜体只是字体的一种属性。在 Windows 早期,命名、枚举和选择点阵字体时,都采用了属性的方式。但是当使用 TrueType 字体时,我们更倾向于使用传统的命名方式。

        Windows 中并没有完全解决这种不一致的命名方式。简而言之,设置斜体或粗体时,即可以使用属性,也可以使用同一系列的不同的字样来达到同样的目的导致的结果就是当应用程序在枚举可用的字体时——也就是从系统中产生可用字体列表时,必须要进行栓冲处理,这就比较复杂

17.2.4  点值

        在传统的印刷术中,字样名称和字号确定了特定的字体。其中字号是以点(point)为单位的,一点的大小差不多是 1/72 英寸。在计算机排版中,一点就正好定义为 1/72 英寸。本书英文版的字号就是 10 点。这个大小可以认为指的是从上行字母(不包含字母上任何变音符号)的顶端,到下行字母的末端的距离,比如字母"bq"的完整高度。这种说法从度量上虽然不是很精确,但很容易理解。

        一个字体的字号代表的点数其实不是一个度量值,更多的是一个排版设计的概念。具体一种字体中的一个字符的大小可能和它所使用字号的点数值(下文简称为点值)并不一致。在传统印刷术中,点值是用来表示一个字体的绝对大小但是在计算机排版时,我们使用其他的方式来决定字符的实际大小

17.2.5  行间距和字间距

        第 4 章曾提到可以调用 GetTextMetrics 函数来取得设备环境中当前选中的字体信息,前面也有很多范例程序使用了这个方法。第 4 章中的图 4-3 显示了使用 TEXTMETRIC 结构来获得一个字体的垂直大小。

        TEXTMETRIC 结构中还有一个字段叫 txExternalLeading,单词 Leading(中文含义“间隔”)是出自单词 Lead(中文含义“铅”),源于以前排字工人会用铅条隔开行之间的距离。tmInternalLeading 字段表示内部间隔值,它是为变音符号预留的空间;tmExternalLeading 值为外部间隔值,表示在相邻两行之间保留的一些额外的空间。程序员可以使用该外部间隔值,也可以忽略不用它。

        当我们说一个字体的字号是 8 点或 12 点时,指的是字符不带内部间隔的高度,也就是字符的行间距减去内部间隔的高度。有时一些大写字母上的变音符号会占据了通常属于行间的空行。所以 TEXTMETRIC 结构的 tmHeight 值实际指的是行间距而不是字体的点值。字体的具体点值可由 tmHeight 减 tmInternalLeading 得到

17.2.6  逻辑英寸问题

        正如我们在第 5 章“设备的尺寸”一节中所讨论的,Windows 98 把系统字体定义为具有 12 点行间距和 10 点字号的字体。对系统字体而言,如果在“显示”属性对话框里选择了小字体,tmHeight 值就是 16 像素点,然后 tmHeight 和 tmInternalLeading 的差值为 13。如果选择了大字体,tmHeight 值就是 20 像素点,tmHeight 和 tmInternalLeading 的差值为 16。该选项还决定了显示设备的实际分辨率,也就是 DPI(dots per inch——每英寸内像素点的个数)。选择小字体时为 96 DPI,选择大字体时就为 120DPI(译注:现在的 Windows 中还支持更高 DPI 的设置,如 144 DPI,192 DPI 等)。

        调用 GetDeviceCaps 时使用 LOGPIXELSX 和 LOGPIXELSY 标志可以获得显示设备的分辨率。这样,我们可以在显示屏上定义 96 个或 120 个像素点占据的距离为一个“逻辑英寸”。如果用尺来实际量一下屏幕的大小,然后数一下像素点的个数,你可以发现一个逻辑英寸其实比一个实际的英寸要长。这是什么原因呢?

        在纸上,一英寸排 14 个 8 点的字符是很清晰的。如果你编写一个文字处理货排版程序,你肯定也希望在显示器上能显示清晰的 8 点字号的字符。但是如果考虑到显示器的实际尺寸,那可能根本没有足够的像素点来清晰地显示字符。这样即使显示器有很高的分辨率,屏幕上的实际 8 点大小的字符也没有办法被看清楚。这是因为当人们阅读印刷品时,眼睛与文字的距离通常为一英尺,但是看显示屏,距离一般是两英尺。

        逻辑英寸实际上就是通过放大的方式来解决这个问题,让即使 8 点这样的小字,也可以在显示器上清晰地显示。同时,如果每逻辑英寸含有 96  个点,那么一个每行含有 640 像素点的显示器尺寸最小可以为 6.5 英寸。在普通的 8.5 英寸宽的纸上使用标准的一英寸页边距时,纸面上文本的宽度也正好是 6.5 英寸。这样,逻辑英寸就充分利用了显示器屏幕的宽度来尽可能大地显示字符。

        在第 5 章中,我们谈到 Windows NT 的做法和这有些不同。在 Windows NT 中,从 GetDeviceCaps 函数中得到的 LOGPIXELSX(一英寸内逻辑像素点的个数)值并不等于HORZRES(像素点个数)除以 HORZSIZE 值(毫米数)再乘以 25.4 后获得的数值。同理,LOGPIXELSY、VERTRES 和 VERTSIZE 也不一致。在各种映射方式中,Windows 会使用 HORZRES、HORZSIZE、VERTRES 和 VERTSIZE 值来计算窗口和偏移范围。然而,显示文字的程序最好不要使用根据 LOGPIXELSX 和 LOGPIXELSY 得到的显示分辨率,在 Windows 98 上开发时是这样的,在 Windows NT 上也一样

        所以,在 Windows NT 下,当程序要用特定的点值显示文本时,它不应该使用 Windows 提供的映射方式。程序应该和在 Windows 98 下一样,使用一英寸内逻辑像素点的方式,定义自己的显示模式。下面是一种我称为“逻辑缇”(logical twips)的映射方式,它非常有用:

SetMapMode (hdc, MM_ANISOTROPIC);
SetWindowExtEx (hdc, 1440, 1440, NULL);
SetViewportExt (hdc, GetDeviceCaps(hdc, LOGPIXELSX),
                     GetDeviceCaps(hdc, LOGPIXELSY), NULL);
通过使用这种映射方式,我们可以用点值的 20 倍来指定字体大小,例如,12 点就是 240.注意,这与 MM_TWIPS 映射方式不同,y 值沿向屏幕下方增长,这在连续显示多行文本时会很方便。

        请牢记逻辑英寸与实际英寸键的差异只是针对显示器的。在打印机上,GDI 和实际的标尺是完全一致的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值