TanXinNiao / blog

贪心鸟的博客
0 stars 0 forks source link

CSS权威指南(第4版)第五章 字体 #15

Open TanXinNiao opened 3 years ago

TanXinNiao commented 3 years ago

CSS1规范写于1996年,“Font Properties” (字体属性)一节的开头是这样说的,"设 置字体属性是样式表最常见的用途之一。”这么多年过去了,这句话仍无法推翻。

CSS2开始支持使用@font-face下载指定的自定义字体,不过直到2009年前后这个功 能才以一致的方式被广泛支持。现在,借助Typekit等在线服务,网站可以使用任何想 用的字体。一般来说,只要你有权使用一个字体,就能在设计中使用它。

然而,要注意,这并不表明你能完全控制字体。如果字体下载失败,或者字体的格式不 被用户的浏览器支持,那么文本将使用后备字体显示。这是好事,因为即使指定的字 体无法使用,依然能把内容呈现给用户。不过要注意的是,你不能完全依赖某个字体, 千万不要觉得设计成什么样就会显示成什么样。

这章不那么好理解

TanXinNiao commented 3 years ago

字体族

我们熟知的“字体”通常包含多个变体,分别针对粗体、斜体等。例如你可能熟悉 (至少听说过)的Times字体,其实它有多种变体,包括TimesRegular, TimesBold、 Timesltalic, TimesBoldltalic 等。Times 的这些变体各自都是一个字型(font face),我 们通常说的Times,其实是这些不同字型的统称。也就是说,Times其实是一个字体族(font family),而不是一个字体。大多数人都会把字体理解为单个实体。

为了覆盖所有情况,CSS定义了五种通用字体族: 衬线字体 这种字体中的字形宽度各异,而且有衬线。因为字体中不同字符的尺寸不同,所以 宽度才有差异。例如,小写字母i和小写字母m的宽度就不同。衬线是字符笔划末 尾的装饰,例如小写字母1顶部和底部的短线,以及大写字母A两条竖线底部的短线。 Times、Georgia 和 New Century Schoolbook 都是衬线字体。

无衬线字体 这种字体中的字形宽度各异,而且无衬线。Helvetica, Geneva. Verdana. Arial和 Univers都是无衬线字体。

等宽字体 等宽字体中的字形宽度一样。一般用于显示编程代码或表格数据。这种字体中的各 个字符在横向上所占的空间是一样的;因此,尽管小写字母i和小写字母m这两个 字母本身的宽度不同,但使用等宽字体时二者所占的横向空间一样大。这种字体可 以有衬线,也可以无衬线。一个字体不管有没有衬线,只要字符宽度相同,就把它 归类为等宽字体。Courier, Courier New, Consolas和Andale Mono都是等宽字体。

草书字体 这种字体尝试模仿人类笔迹或手写体。通常,这种字体在笔划末端有较大的花饰, 而且比衬线字体华丽。例如,大写字母A左边竖线的底部可能有个小卷,或者整条 线都是花饰卷。Zapf Chancery, Author和Comic Sans都是草书字体。

奇幻字体 这种字体(有时也叫“装饰”字体或“展示”字体)还真没什么统一的特征,不过 可以确定的是,无法将其划归到其他类别中。Western, Woodblock和Klingon都是 奇幻字体。

理论上,任何一款字体都可归为上述通用字体族中的一个。实际情况可能并非如此,不 过例外情况总是少的。如果浏览器遇到无法归类为衬线、无衬线、等宽或草书的字体, 便会将其放到“奇幻”篮子里。

TanXinNiao commented 3 years ago

使用通用字体族

字体族使用font-family属性声明。

font-family 取值 [ | ] 初始值 由用户代理指定 适用于 所有元素 计算值 指定的值 继承性 是 动画性 否

如果想让一个文档使用无衬线字体,但不介意具体使用哪一款,可以这样声明: body {font-family: sans-serif;} 此时,由浏览器选择一款无衬线字体(例如Helvetica),然后应用到body元素上。借 助继承,body的所有后代都将使用这个字体,除非特指度更高的选择符覆盖了这个属性。 利用这些通用字体族,创作人员可以编写十分复杂的样式表。如:

body {font-family: serif;}
hl, h2, h3, h4 {font-family: sans-serif;}
code, pre, tt, kbd {font-family: monospace;}
p.signature {font-family: cursive;)

此时,文档中的大部分内容将使用衬线字体(例如Times),不过class为signature 的段落将使用草书字体(例如Author)渲染。一级标题到四级标题将使用无衬线字体(例 如Helvetica),而code、pre、tt和kbd元素将使用等宽字体(例如Courier)

对字体熟悉的人,可能记着多款相似的字体。假如我们想让文档中的所有段落都使用 Times 显示,不过 Times New Roman. Georgia, New Century Schoolbook 和 New York 也是可以接受的选择(这些都是衬线字体)o首先,定好各字体的顺序,然后一个接一个, 以逗号分开:


'New York', serif;)```
TanXinNiao commented 3 years ago

使用引号 在上例中,你可能注意到了之前没见过的单引号。在font-family声明中,如果字体名 称中有一个或多个空格(例如“New York” ),或者字体名称中有符号(例如#或$), 建议使用引号。因此,名为Karrank%的字体就应该放在引号里

TanXinNiao commented 3 years ago

使用 @font-face

@font-face的作用是让你在设计中使用自定义的字体。这个特性首次出现在CSS2中, 不过直到21世纪头十年的后半期才实现。尽管无法保证每个终端用户都能用上你指定 的字体,但是这个特性却得到了广泛支持。

假设你想使用的字体没有广泛安装,而是个十分特别的字体,借助@font-face的魔力, 你可以定义一个专门的字体族名称,对应于服务器上的一个字体文件。用户代理将下载 那个文件,使用它渲染页面中的文本,就好像用户的设备中安装了那个字体一样。下面 举个例子

@font-face {
  font-family: "SwitzeraADF"; 
  src: url("SwitzeraADF-Regular.otf");
}

用户代理见到font-family: SwitzeraADF声明后,会加载对应的.otf文件,然后使用 它渲染文本。 @font-face是惰性加载字型的。这表明,仅当需要使用指定的字型渲染文本时,才会加 载,否则不加载。其实,浏览器不管是否需要,都会先行下载声明的全部字型,这是浏 览器的缺陷。

TanXinNiao commented 3 years ago

使用 @font-face

@font-face的作用是让你在设计中使用自定义的字体。这个特性首次出现在CSS2中, 不过直到21世纪头十年的后半期才实现。尽管无法保证每个终端用户都能用上你指定 的字体,但是这个特性却得到了广泛支持。

假设你想使用的字体没有广泛安装,而是个十分特别的字体,借助@font-face的魔力, 你可以定义一个专门的字体族名称,对应于服务器上的一个字体文件。用户代理将下载 那个文件,使用它渲染页面中的文本,就好像用户的设备中安装了那个字体一样。下面 举个例子

@font-face {
  font-family: "SwitzeraADF"; 
  src: url("SwitzeraADF-Regular.otf");
}

用户代理见到font-family: SwitzeraADF声明后,会加载对应的.otf文件,然后使用 它渲染文本。 @font-face是惰性加载字型的。这表明,仅当需要使用指定的字型渲染文本时,才会加 载,否则不加载。其实,浏览器不管是否需要,都会先行下载声明的全部字型,这是浏 览器的缺陷。

TanXinNiao commented 3 years ago

必须的描述符

定义字体的全部参数都在@font-face { }结构中编写。这些参数称为描述符(descriptor), 与属性十分相似,格式为descriptor: value;o其实,多数描述符都直接使用现有的属 性名,稍后将做详细说明。 描述符中有两个是必需的:font-family和src。

src的作用不言而喻:为定义的字型提供一个或多个源。如果有多个源,之间以逗号分 隔。字型的源可以指向任何URI,不过有个限制:字型必须与样式表同源。因此,不能 把src指向别人的网站,下载别人的字体。你要在自己的服务器中存储一份本地副本, 或者使用同时提供样式表和字体文件的字体托管服务。

你可能觉得奇怪,这里的font-family和前一节所讲的有何不同?区别是,这里的 font-family是字体族描述符,而前一节中的font-family是字体族属性。如果还不理解, 没关系,先这样,慢慢你就会懂的。

其实,@font-face做的是低层定义,是为字体相关的属性(如font-family)服务的。通 过描述符font-family: "SwitzeraADF";定义一个字体族名称之后,用户代理的字体族 名称表中便会出现"SwitzeraADF”条目,与Helvetica, Georgia, Courier等具有同等地位, 可以在font-family属性的值中引用:

@font-face (
font-family: "SwitzeraADF"; /* 描述符 */
src: url("SwitzeraADF-Regular.otf");
}
hl {font-family: SwitzeraADF, Helvetica, sans-serif;} /* 属性 */

注意,font-family描述符的值和font-family属性中出现的那个字体族名是一样的。 如果不一样,hl规则将忽略font-family属性值中列出的第一个字体族名称,解析后一 个。只要成功下载了字体文件,而且文件的格式是用户代理支持的,那个字体就会像其 他字体一样用于渲染文本

如果想告诉用户代理所用的字体是什么格式,可以使用可选的format():

@font-face {
  font-family: "SwitzeraADF**;
  src: url("SwitzeraADF-Regular.otf") format("opentype");
}

这样做的好处是,让用户代理跳过不支持的字体格式,从而减少带宽用量,提升加载速度。 此外,使用format()还能为不带有常规扩展名的字体文件指定格式,以防用户代理不 识别:

@font-face (
  font-family: "SwitzeraADF";
  sre: url("SwitzeraADF-Regular.otf") format("opentype"),
  url("SwitzeraADF-Regular.true") format("truetype");
}

值 格式 embedded-opentype EOT (Embedded OpenType) opentype OTF (OpenType) svg SVG (Scalable Vector Graphics) truetype TTF (TrueType) woff WOFF (Web Open Font Format) 除了使用url()和format()组合之外,还可以使用local()(名称表明了作用)指定已 经安装在用户设备中的字体族名称(可以是多个):

@font-face (
font-family: "SwitzeraADF";
src: local(nSwitzera-RegularM), local("SwitzeraADF-Regular "), url("SwitzeraADF-Regular.otfM) format("opentypeM), url(SwitzeraADF-Regular.true") format("truetype");
}

这里,用户代理先检査设备中是否有名为"Switzera-Regular"或"SwitzeraADF-Regular" 的字体族,如果有,使用SwitzeraADF这个名称指代本地安装的字体;如果没有,尝试 从远端下载url()中指定的字体文件。

注意,借助这个功能可以为本地安装的字体自定义名称。例如,可以像下面这样为 Helvetica (没有这个字体的话,尝试Helvetica Neue)起个简短的名称:

@font-face (
  font-family: "H";
  src: local("Helvetica*'), local(MHelvetica Neue");
}
hl, h2, h3 {font-family: H, sans-serif;}

只要用户的设备中安装有Helvetica,前三级标题便将使用Helvetica渲染。这样做看起 来有点多此一举,不过在某些情况下确实能减少样式表文件的大小

TanXinNiao commented 3 years ago

万全之策

@font-face有个棘手的问题要解决:不同时代的不同浏览器支持不同格式的字体(从表 5-1中那些可下载的字体格式可见一斑)。为了尽量涵盖较广的场景,应该使用能确保 万全的@font-face句法。这种句法最初由Paul Irish提出,后经FontSpring的人员改进, 写法如下:

@font-face (
font-family: "SwitzeraADF";
sic: uil("SwitzeiaADF-Regular.eot");
sic: url("SwitzeraADF-Regular.eot?#iefixM) format("embedded-opentype"),
url("SwitzeraADF-Regular.woffM) format(MwoffM),
url("SwitzeraADF-Regular.ttf") format("truetype"),
url(''SwitzeraADF-Regular.svg#switzera_adf_regular") format("svg");
}
TanXinNiao commented 3 years ago

I描述符 | 默认值 | 说明                    font-style | normal | 区分常规、斜体和倾斜字型 font-weight | normal | 区分不同的字重(例如加粗) font-stretch | normal | 区分不同的字符宽度(例如紧缩和加宽) font-variant | normal | 区分众多字形变体(例如小号大写字母),在很多方面与 CSS 中的 font-feature-settings 很像 font-feature-settings | nomal | 直接访问0penType的低层特性(例如启用连字) unicode-range | U+0-10FFFF | 定义指定字体中可用的字符范围

这些字体描述符是可选的,不必一定在@font-face规则中列出。CSS规定,描述符不 像属性那样可以没有默认值。如果没有某个可选的描述符,它的值将被设为默认值。因此, 如果未列出font-weight,其值默认为normal。

TanXinNiao commented 3 years ago

限制字符范围

有一个字体描述符没有对应的CSS属性(与表5-2中的其他描述符不同),即unicode- rangeo这个描述符用于指定自定义字体可以应用到哪些字符上。使用符号字体,或者想 确保只有特定语言使用指定字型时用得到这个描述符。

unicode-range: U+59O-5FF; / 希伯来语字符 / 第一个例子只指定了一个范围,从Unicode码位590~5FFo这个范围是希伯来语字符。 因此,创作人员可以指定一个希伯来语字体,限制它只用于渲染希伯来语字符,即使字 型中还包括其他码位的字形:

@font-face (
  font-family: "CMM-Ahuvah";
  src: url("cmm-ahuvah.otf" format("opentype");
  unicode-range: U+59O-5FF;
}

unicode-range: U+4EOO-9FFF, U+FFOO-FF9F, U+30??; / 日语汉字、平假名、片假名 / 第二个例子指定了多个范围,以逗号分隔,涵盖所有日语字符。里面奇怪的U+30??值 是unicode-range允许使用的特殊格式,问号是通配符,意思是“任何数字”。因此, U+30? ?等效于U+3OOO-3OFF。unicode-range的值中只允许使用问号这一个"特殊的” 字符。

@font-face是惰性加载的,因此可以使用unicode-range限制只下载页面中真正需要用 到的字型。假设一个网站中有英语、俄语,还有一些基本的算术运算符,而你并不知道 页面中会出现哪些字符。有些页面可能全是英语,有些可能混杂着俄语和算式等。此外, 假设这三种内容都用专门的字型。为了确保用户代理只下载真正需要的字型,可以像下 面这样组织@font-face规则:

@font-face {
font-family: "MyFont'1;
src: url("myfont-general.otf" format("opentype");
}
@font-face (
font-family: "MyFont";
src: url("myfont-cyrillic.otf" format("opentype");
unicode-range: U+04??, U+O5OO-O52F, U+2DEO-2DFF, U+A64O-A69F, U+1D2B-1D78;
}
@font-face (
font-family: "MyFont";
src: url("myfont-math.otfM format("opentype");
unicode-range: U+22??;  /♦等效于 U+22OO-22FF */
}

第一个规则没有指定Unicode范围,因此始终下载,除非页面中恰巧什么字符也没有 (不是不可能)。根据第二个规则,仅当页面中有指定Unicode范围内的字符时才下载 myfont-cyrillic.otfo第三个规则一样,不过是当页面中有基本的算术运算符时才下载。

TanXinNiao commented 3 years ago

组合描述符

我们可以把多个描述符组合在一起为字型设定不同的属性,这一点没那么容易想到,不 过确实可行。例如,可以指定一个字型为粗体,一个字型为斜体,再指定一个字型为加 粗的斜体。 这么做背后的原理是,未声明的描述符使用默认值。以下面三个字型设定规则为例:

@font-face (
  font-family: MSwitzeraADF";
  font-weight: normal;
  font-style: normal;
  font-stretch: normal;
  src: url("SwitzeraADF-Regular.otf") format("opentype");
)
@font-face (
  font-family: MSwitzeraADFM;
  font-weight: bold;
  font-style: normal;
  font-stretch: normal;
  src: url("SwitzeraADF-Bold.otf") format("opentype");
}
@TanXinNiao font-face (
  font-family: "SwitzeraADF";
  font-weight: normal;
  font-style: italic;
  font-stretch: normal;
  src: url("SwitzeraADF-Italic.otf") format("opentype11);
}

这里我们明确列出了所需的描述符,即便使用默认值,也没有省略。把值为normal的 描述符去掉之后,效果完全一样:

@font-face {
  font-family: "SwitzeraADF";
  src: url("SwitzeraADF-Regular.otf") format("opentype");
}
@font-face (
  font-family: "SwitzeraADF";
  font-weight: bold;
  src: url(*SwitzeraADF-Bold.otf") format("opentype'1);
}
@font-face (
  font-family: "SwitzeraADF";
  font-style: italic;
  src: url(MSwitzeraADF-Italic.otf'*) format("opentype'*);
)

可以看出,只这三个描述符就有这么多组合方式,毕竟font-weight有11种可能的 值,font-stretch有10种可能的值,不过不一定都用得到。其实,多数字体族不会像 SwitzeraADF这样提供如此多的字型(共计24种),因此没必要所有组合都写出来。然而, 你要知道可以这么做,以防某些情况下需要使用特殊的字体渲染紧缩的加粗文本,避免 用户代理自行计算

TanXinNiao commented 3 years ago

字重的工作方式

为了弄清用户代理如何确定一个字体变体的粗细(或字重),先要理解关键字100到 900o这些数字关键字对应于字体设计中的九级字重。如果一个字体族中有全部九级字重, 那么这些数字就直接对应于预定义的级别,100是最细的,900是最粗的。

其实,这些数字并不表示字重本身。CSS规范只是说,每个数字对应的权重至少和前面 的数字具有相同的字重。因此,100、200、300和400可能都对应于同样细的变体;500 和600对应于同样粗的变体:700、800和900则对应同样较粗的变体。只要后面的数字 关键字对应的粗细不比前面的数字关键字细就行。

一般,这些数字对应于常见的变体名称(先不考虑font-weight的其他值)。400对应 于normal, 700对应于bold。其他数字不与font-weight的其他关键字对应,不过可以 对应于常见的变体名称。在字体的变体中,以"Normal” "Regular” “Roman”或“Book” 等表示的可以分配给数字400,以“Medium”表示的可以分配给数字500。然而,如果 只有一个变体,而且以“Medium”表示,那么应该分配给4。。,而非500。

如果给定的字体族中字重的等级少于9个,用户代理要做更多工作。遇到这种情况,用 户代理必须填补既定方式的空缺: • 如果500未分配,与400对应的字重一样。 • 如果300未分配,将其对应于比400细的那个变体。如果没有这样一个变体,字重 与400 一样。此时,通常为"Normal”或"Medium”变体。200和100也是这样处 理的。 • 如果600未分配,将其对应于比500黑的下一个变体。如果没有这样一个变体,字 重与500 一样。700、800和900也是这样处理的。

TanXinNiao commented 3 years ago

增大字重

如果把一个元素的字重设为bolder,用户代理首先要确定从父元素继承的font-weight 值是什么,然后选择比继承的字重高一级的最小数字。如果找不到,用户代理把元素的 字重设为下一个数字值,直到900;到顶后,就把字重设为900o下述各种情况的渲染

所示。
p (font-weight: normal;)
p em {font-weight: bolder;} /* 文本为粗体,求值结果为 700 */
hl {font-weight: bold;)
hl b {font-weight: bolder;} /*如果没有更粗的字型.其值结果为800 */
div {font-weight: 100;} /* 假设有"Light"字型,说明见下文 */
div strong {font-weight: bolder;} /* 文本为常规字型,字重为 400 */

在第一个示例中,用户代理顺着字重等级向上爬,把normal变成bold。用数字表示, 就是从400变成700。在第二个示例中,hl的字重已经设为boldo如果没有更粗的字型, 用户代理会把hl中b的字重设为800,因为这是从700 (等同于bold)起的下一级。因 为800和700对应同一个字型,所以hl中常规的文本和加粗的文本在视觉上没有区别, 但字重是不同的。

在最后一个示例中,我们把段落的字重设为最细的字型,而且假设存在“Light”变体。 此外,再假设这个字体族中还有"Regular"和“Bold”字型。段落中的em将使用 normal字型,因为这是字体族中下一个较粗的字型。然而,如果字体族中只有“Regular” 和“Bold”字型呢?此时,声明的求值结果如下:

/*假设只有"Regular1*和“Bold”两个字型*/
p {font-weight: 100;}   /* 看起来跟 normal 字重一样 */
p span {font-weight: bolder;} /* 对应于 700 */
TanXinNiao commented 3 years ago

font-weight 描述符

使用font-weight描述符可以为字型指定font-weight属性支持的字重等级。例如,下 述规则为五个字型分配六个不同的字重:

@font-face (
font-family: "SwitzeraADF";
font-weight: normal;
sic: url("f/SwitzeraADF-Regular.otfn) format("opentype");
}
@font-face (
font-family: "SwitzeraADF";
font-weight: bold;
src: url(Mf/SwitzeraADF-Bold.otfn) format("opentype");
}
@font-face (
font-family: "SwitzeraADF";
font-weight: 300;
src: url("f/SwitzeraADF-Light.otf") format("opentype");
}
@font-face {
font-family: "SwitzeraADF";
font-weight: 500;
src: url(Hf/SwitzeraADF-DemiBold.otf") format("opentype");
}
@font-face {
font-family: "SwitzeraADF";
font-weight: 700;
src: url("f/SwitzeraADF-Bold.otfn) format("opentype");
)
@font-face (
font-family: "SwitzeraADF";
font-weight: 900;
src: url("f/SwitzeraADF-ExtraBold.otfw) format("opentype");
}

这样分配之后,创作人员便可使用多个字重等级。下述规则的结果如图5-11所示

hl, h2, h3, h4 {font: 225% SwitzeraADF, Helvetica, sans-serif;}
hl {font-weight: 90。;}
h2  (font-size: 180%;   font-weight:    700;}
h3  (font-size: 150%;   font-weight:    500;}
h4  {font-size: 125%;   font-weight:    300;}
TanXinNiao commented 3 years ago

字号

类似于font-weight属性的关键字bolder和lighter, font-size属性也有相对大小关 键字,分别为larger和smaller。这两个关键字的作用与相对字重类似,分别增大和减 小一号字体大小。不过,我们首先要知道字号是如何确定的。

其实,font-size属性与渲染结果之间的关系由字体设计者决定。这个关系在字体中通 过em方框(或m盒子)表示。em方框(以及字号)与字体中字符的边界没有关系, 其实它指的是在没有行距(CSS中的line-height)的情况下两条基线之间的距离 字 体中完全有可能存在高度超过基线之间距离的字符。鉴于此,设计字体时要确保所有字 符都比em方框小。事实上,多数字体就是这么做的。

因此,font-size的作用是为字体的em方框提供一个尺寸。所以,显示出来的字符不 可能完全是指定的大小。

TanXinNiao commented 3 years ago

自动调整字号

有两个因素影响字体是否清晰易辨:字号和X高度。X高度除以字号得到的结果称为高 宽比值(aspect value)。随着字号的减小,高宽比值越高,字体越清晰易辨;相反,高 宽比值较低的字体,很容易变得模糊难辨。CSS提供的font-size-adjust属性用于改 变字体族之间的高宽比值。

使用Times的文本比使用Verdana的文本更难辨别。出现这种情况的部分原因是受限于 基于像素的显示器,不过也是由于Times在较小的字号时模糊难辨。 Verdana的x高度和字符大小的比值是0.58,而Times是0.46。为了解决这个问题,我 们可以设定Verdana的高宽比值,让用户代理根据实际情况调整文本的字号。这里,要 用到下述公式: 设定的 font-size x (font-size-adjust值:字体的高宽比值) = 调整后的font-size

因此,当实际使用的是Times而不是Verdana时,调整的结果是: 10px x (0.58 十 0.46) = 12.6px

p (font: 10px Verdana, sans-serif; font-size-adjust: 0.58;}
p.cl2 (font-family: Times, serif; }

假设用户代理能找到或算出高宽比值,把font-size-adjust设为auto就能达到所需的 效果,即使不知道首选字体的高宽比值也行。例如,假设用户代理能确定Verdana的高 宽比值是0.58,那么下述规则得到的结果与图5-23 一样:

p {font: 10px Verdana, sans-serif; font-size-adjust: auto;}
p.cl2 {font-family: Times, serif; }

font-size-adjust: none;声明将禁止调整字号。这是默认的行为。

TanXinNiao commented 3 years ago

字体拉伸

font-stretch 取值 normal | ultra-condensed | extra-condensed | condensed | semi¬condensed I semi-expanded | expanded | extra-expanded | ultra-expanded

抛开名称,这个属性的作用其实非常像font-size属性的绝对大小 关键字(例如xx-large),让创作人员在一些绝对值中选择一个,调整字体的宽度。例 如,为了突出显示着重强调元素中的文本,创作人员可能会选择一个较宽的字型显示强 调文本。

仅当使用的字体族中有宽体和窄体时,这个属性才起作用,而一般的字体族并没有这样 的变体(如果有,字体族的价格往往不菲)。因此,这个属性的作用与font-size相差 很大,后者可以随时随地改变字号。仅当使用的字体族中有加宽变体时,font-stretch: expanded声明才起作用;如果没有加宽变体,什么也不会发生,即仍使用原来的字型。

下面以十分常见的Verdana字体为例。Verdana的字型只有一个宽度,相当于font- stretch: normal。像下面这样声明对显示的文本宽度没有任何影响:

body {font-family: Verdana;}
strong {font-stretch: extra-expanded;}
footer {font-stretch: extra-condensed;}

所有文本都将使用Verdana的正常宽度显示。然而,如果换成有多个宽度字型的字体族, 例如Futura情况就不同了

TanXinNiao commented 3 years ago

字距调整

有些字体定义了字符之间相对位置的数据,即字距。不同的字符组合,字距是不一样的。 例如,如这两个字符的字距可能就与贝不同。类似地,人B和AW的字距可能也不一样, 在某些字体中,W的右上端可能在A右下端的左边。字距可以使用font-kerning属性 呼出或禁止。 font-kerning 取值 auto | normal | none 初始值 auto

none的意思十分简单:让用户代理忽略字体中的字距信息。normal的意思是让用户代 理正常处理字距,即使用字体中的字距数据。auto则把决定权交给用户代理,让用户 代理选择最合适的处理方式,当然具体怎么做由所用的字体决定。例如,OpenType规 范建议(只是建议),只要字体中有字距数据就应该使用。如果字体中没有字距数据, font-kerning没有任何作用。

注意,如果既调整字距,又应用letter-spacing属性,正确的顺序是先调整字距, 然后再根据letter-spacing属性的值调整字符间距.而不是反过来。

TanXinNiao commented 3 years ago

字体变形

除了字重、字形等之外,字体还有变形。变形信息内嵌在字型中,包括旧时的各种连写、 小号大写字母、小数的表示方式、数字之间的间距、零有没有贯穿线等。如果字型中有 这些变形信息,可以通过CSS的font-variant属性调用。 font-variant CSS2.1 normal | small-caps CSS3 | none | II II II II stylistic() II historical-forms II styleset(n)II character- variant(<feature-va/ue-name>#) II swash() II ornaments() II annotatioT II [ small-caps | all­small-caps | petite-caps | all-petite-caps | unicase | titling-caps ] II II II II ordinal II slashed-zero II II II ruby ]

h1 {font-variant: small-caps;}
h1 code, p {font-variant: normal;}

你可能发现了,h1元素中的大写字母使用较大的大写字母显示,小写字母使用较小的大 写字母显示。这与text-transform: uppercase的效果非常类似,不过就这里而言,两 种方式之间的区别是大写字母的大小不同。之所以用字体属性声明small-caps,是因为 有些字体有专门的小号大写字母字型,从而必须通过字体属性选择。

Level 3新增的值

下面讨论Level 3增加的值。不置可否,CSS3的取值繁杂,不过也能讲得清楚。这些值 其实是下述属性允许的取值总和: • font-variant-ligatures • font-variant-caps • font-variant-numeric • font-variant-alternates • font-variant-east-asian 例如(选个较为简单的), 出自 font-variant-ligatures 属性, 取 common-ligatures 或 no-common - ligatures» 出自 font- variant-numeric 属性,取 diagonal-fractions 或 stacked-fractions0 其他值参见各 属性的规范。

font-variant 描述符

font-variant描述符通过一个空格分隔的列表指定字型的哪些变体可以使用,哪些不能 使用。例如,可以像下面这样启用常见的连字、小号大写字母和带贯穿线的零: font-variant: common-ligatures small-caps slashed-zero;

注意,这个描述符与前面讲过的其他描述符有个明显的区别。以font-stretch描述符 为例,你可以把特定的字型指定给特定的font-stretch属性值。而font-variant描述 符在@font-face规则中指明允许使用的变形能轻易覆盖属性中指定的字体变形。在下 述示例中,即便SwitzeraADF有diagonal-fractions或small-c即s变形,段落也不会 使用它们显示。

@font-face {
font-family: "SwitzeraADF";
font-weight: normal;
src: url("SwitzeraADF-Regular.otf") format("opentype");
font-variant: stacked-fractions titling-caps slashed-zero;
} 
p (font: small-caps lem SwitzeraADF, sans-serif;
font-variant-numeric: diagonal-fractions;}
TanXinNiao commented 3 years ago

字体特性

与font-variant类似,font-feature-settings属性从低层控制OpenType字体的哪些 特性可以使用 font-feature-settings 取值 normal | 初始值 normal

这个属性的值可以是OpenType规范定义的一个或多个特性(以逗号分隔)。例如,若 想启用常用的连字、小号大写字母和带贯穿线的零,可以这样做: font-feature-settings: "liga" on, "smcp" on, "zero" on;

值的具体格式为: [ I on | off ]? 多数特性的值是整数。和1,相当于。幵和on (反过来用也行)。不过,有些特性的值 可以是其他数,大于1时既表示启用特性,也用于定义选择的索引。如果列出了某个特性, 但是没有提供数字,假定为1 (启用)。因此,下述规则都是等效的: ``` font-feature-settings: "liga"; /* 假定为 1 */ font-feature-settings: "liga" 1; /* 明确声明为 1 */ font-feature-settings: "liga" on; /* on = 1 */ ``` 注意,所有string>值都必须放在引号里。因此,下述规则中的第一个特性有效,而第 二个将被忽略: ``` font-feature-settings: "liga", dlig; /*启用常用的连字,想启用自由连字,但是忘了引号*/ ``` 另一个限制是,OpenType要求所有特性标签都是四个ASCII字符长。比这长或短,以 及使用非ASCII字符的特性是无效的,将被忽略(通常无需担心,除非所用的字体没有 遵守命名规则,自己编造了特性名称)。 默认情况下,OpenType字体都启用了下述特性,除非创作人员通过font - feature¬settings 或font-variant明确禁用了 : calt 根据上下文替换。 ccmp 组合字符。 dig 根据上下文连字。 liga 标准连字。 loci 本地化形式。 mark 基本定位标记。 mkmk 标记定位标记。 rlig 必要的连字。 此外,特定的字体可能还会默认启用其他特性,例如启用竖排文本的vert ### font-feature-settings 描述符 注意,这个描述符只是把特性提供出来(或者禁止使用),并不在显示文本时真正启用(参 见讲解font-feature-settings属性的那一节)。 与font-variant描述符类似,font-feature-settings描述符的作用是定义@font-face 规则中所声明的字型允许使用哪些字体特性。例如,对下述规则来说,段落不会使用另 一种小数形式或小号大写字母显示,即使SwitzeraADF提供了这些特性也不行: ``` @font-face { font-family: "SwitzeraADF"; font-weight: normal; src: url(nSwitzeraADF-Regular.otf") format("opentype"); font-feature-settings: wafrcw off, "smcp" off; } p (font: lem SwitzeraADF, sans-serif; font-feature-settings: "afrc", "smcp";} ```
TanXinNiao commented 3 years ago

字体合成

有时,指定的字体族中可能缺少某种字型,例如粗体或斜体。此时,用户代理可能会尝 试使用可用的字型合成所需的字型,不过这样得到的效果不是特别好。为了解决这个问 题,CSS提供了 font-synthesis属性,让创作人员控制合成哪种字型,或者在渲染页 面时禁止合成。

font-synthesis 取值 none | weight II style 初始值 weight style 类似地,如果字体族中缺少斜体,用户代理会直接倾斜常规字型,合成斜体。这比合成 粗体的效果还差,尤其是对衬线字体而言。图5-32对比了 Georgia的合成斜体(称为“倾 斜体”)和Georgia自带的斜体。

在支持的用户代理中,font-synthesis: none声明将阻止用户代理在对应的元素上做 这种合成。例如,如果想禁止整个文档都这么做,可以使用html (font-synthesis : none;}。这么做的缺点是,尝试使用不带相应字型的字体族显示加粗或倾斜的文本时, 文本不会加粗或倾斜。不过也有好处,我们无须担心用户代理合成效果不理想的变体。

截至2017年年末,只有Firefox支持font-synthesis属性。

TanXinNiao commented 3 years ago

font

在hl规则中,前三个值 分别针对font-style, font-weight font-variant<而在第二个规则中,顺序变成了 font-weight、font-variant和font-style0这两种写法都没错,因为这三个属性可以 随意排列。此外,如果其中某个属性的值是noimaL甚至不用写出。

然而,要注意的是,只有font属性的前三个值有这种自由。后两个值的顺序要严格得多。 注意,后两个值不仅必须是font-size和font-family这种顺序,而且必须出现在font 声明中。哪怕少一个,整个规则都是无效的,很有可能会被用户代理彻底忽略。

hl, h2, h3 (font: italic small-caps 250% sans-serif;} 
h2 {font: 200%} 
h3 {font-size:150%}

注意到了吗,h2元素既不是斜体,也没有使用小号大写字母,而且没有一个元素是加粗 的?这是正确的行为。使用font属性时,省略的值都将重设为默认值。因此,前例可 以改写成这样,但是结果不变:

hl, h2, h3 {font: italic normal small-caps 250% sans-serif;}
h2 {font: normal normal normal 200% sans-serif;}
h3 {font-size: 150%;}
TanXinNiao commented 3 years ago

使用系统字体

如果想让网页与用户的操作系统融为一体,可以在font声明中使用系统字体值。这样 设定之后,元素上应用的是操作系统中控件的字号、字体族、字重、字形和变形。可用 的系统字体值如下。 caption 用于说明文字的控件,如按钮。 icon 标注图标。 menu 在菜单中使用,即下拉菜单和菜单列表。 message-box 在对话框中使用。 small-caption 用于标注小型控件。 status-bar 用在窗口的状态栏中。

例如,你可能想把按钮的字体设成与操作系统中的按钮一样,比如说: button {font: caption;} 使用这些值可以让Web应用看起来像用户操作系统的原生应用一样。

注意,系统字体或许只能作为一个整体设置,即字体族、字号、字重、字形等必须一起设置。 因此,前例中的按钮文本看起来与操作系统中按钮的文本将是一模一样的,而不管按钮 上的文本与周围的文本协不协调。不过,设定系统字体之后可以调整单个属性的值。例如, 下述规则确保按钮的字号与父元素一样: button {font: caption; font-size: lem;} 如果调用的系统字体在用户的设备中不存在,用户代理可能会尝试模拟,例如减小 caption字体的字号,得到small-caption字体。如果做不到这一点,用户代理应该使 用默认字体。如果能找到指定的系统字体,但是无法读取全部值,用户代理应该使用默 认值。例如,用户代理或许能找到status・bar字体,但是不知道是不是小号大写字母 字型。此时,用户代理将把font-variant属性的值设为normal。

TanXinNiao commented 3 years ago

字体匹配机制

可以看出,通过css能选择字体族、字重和变体。这背后蕴藏着字体匹配机制,这是一 个非常复杂的过程,而且让人捉摸不透。创作人员一定要理解这个机制,从而让用户代 理选择合适的字体显示文档。笔者把这个机制放在本章最后,是因为不是人人都需要了 解字体相关的属性背后的运作方式,有些读者或许会选择跳过这一节。如果你感兴趣, 下面就来说一说字体匹配的过程:

  1. 用户代理创建或访问字体属性数据库。这个数据库中有用户代理能访问的全部字体 的各个CSS属性。通常,设备中的所有字体都在这里,不过可能还有其他字体(例如, 用户代理可能内置了字体)。如果用户代理遇到两个一样的字体,将忽略其中一个。
  2. 用户代理把应用了字体属性的元素摘出来,构建显示元素所需的字体属性列表。首先, 用户代理根据这个列表选择使用哪个字体族显示元素。如果能找到完全匹配的字体, 用户代理就使用那个字体;否则,还要做些额外工作。
  3. 匹配字体时先看font-stretch属性。
  4. 然后再看font-style属性。任何以“italic”或"oblique"标识的字体都能匹配 italic关键字。如果没有这样的字体,匹配失败。
  5. 接下来匹配font-weight,鉴于CSS对font-weight的处理方式(参见5.3.1节), 这一匹配绝不会失败。
  6. 然后处理font-size.匹配字号时要有一定容差,不过这个容差由用户代理定义。因 此,指定的字号和实际使用的字号在一个用户代理中可能允许存在20%的容差,而 在另一个用户代理中可能只允许10%的容差。
  7. 如果第2步没有找到匹配的字体,用户代理在同一个字体族中选择替代字体。找到后, 回到第2步。
  8. 假设找到一个基本匹配的字体,但是字体没有显示元素所需的全部信息,比如字体 缺少版权符号,那么用户代理将回到第3步,搜索替代字体,然后再执行第2步。
  9. 最后,如果找不到匹配的字体,而且所有替代字体都试过了,用户代理将选择指定 字体族中的默认字体,力争正确显示元素。

此外,用户代理处理字体变形和特性的方式如下:

  1. 查看默认启用的字体特性,包括指定文本所需的特性。默认启用的特性有”calf "ccmp" "clig” “liga” "loci” “mark” “mkmk” 和 “rlig”o
  2. 如果是@font-face规则定义的字体,检查@font-face规则中font-variant描述 符对应的特性。然后检査@font-face规则中font・featiiYe-settings描述符对应的 特性。
  3. 检査由font-variant或font-feature-settings之外的属性确定的特性设置(例如, 把letter-spacing属性设为默认值之外的值时将禁用连字)。
  4. 检査font-variant属性及其子属性(例如font-variant-ligatures),以及其他可 能会调用0penType特性的属性(例如font-kerning)的值对应的特性。
  5. 检査font-feature-settings属性的值对应的特性。

整个过程很长,也很麻烦,不过从中可以窥见用户代理是如何选择字体的。例如,你可 能指定文档使用Times或其他衬线字体: body (font-family: Times, serif;} 渲染各元素时,用户代理会检査Times提供的字符与元素中的字符是否匹配。多数情况 都能找到匹配的字符,但是假设某一段中有个汉字,此时Times中就没有匹配的字符了, 用户代理要么绕过汉字,要么寻找能满足显示需求的其他字体。西文字体极有可能不含 汉字,但是假设有个字体有(就叫它AsiaTimes吧),那么用户代理就可以使用它显示 那个元素,或者只用于显示那个汉字。因此,整个段落都可能使用AsiaTimes,或者段 落中除了那个汉字之外的文本使用Times,而那个汉字使用AsiaTimes显示。