編者按:本文作者李松峰,資深技術圖書譯者,翻譯出版過40餘部技術及互動設計專著,現任360奇舞團Web前端開發資深專家,360前端技術委員會委員、W3C AC代表。
本文是閱讀CSS標準文檔的筆記,也是對CSS字體相關知識的一個梳理。
Cascading Style Sheets, level 1CSS 1定義的字體屬性包括:font-family、font-style、font-variant、font-weight、font-size和font。其算法要求UA(User Agent,用戶代理)對給定元素中的字符逐個查找匹配的CSS屬性,首先嘗試匹配font-style,比如italic和oblique。然後匹配font-variant,normal匹配非small-caps(小型大寫)字體,而small-caps則匹配 (1)標識為small-caps的字體 (2)合成小型大寫字母的字體 (3)將小寫字母替換為大寫字母的字體(小型大寫字母可以通過將normal字體的大寫字母縮小來實現)。接著匹配font-weight和font-size,這兩個屬性都是必須匹配成功的。
font-family是一個按照優先級排列的字體或通用字體名字的列表,每個名字以逗號分隔,表示多選一。
body { font-family: gill, helvetica, sans-serif; }
有兩種值:字體名和通用字體名。在上面的例子中,gill和helvetica就是明確指定的字體名,而sans-serif則是通用字體名。CSS 1定義了5種通用字體:
serif:襯線
sans-serif:非襯線
cursive:手寫
fantasy:裝飾
monospace:等寬
通常,除了指定特定的字體名,最後都應該指定一個通用字體名。另外,如果字體名包含空格,必須用引號括起來。
font-style有3個值:normal、italic和oblique。noraml就是正常字體,而italic是斜體,oblique有可能是將正常字體傾斜後得到的。對於italic而言,如果沒有italic字體,則使用oblique。
通常字體名字中包含Oblique、Slanted或Incline的,UA通常將其歸類為oblique;而字體名字中包含Italic、Cursive或Kursiv的,UA則將其歸類為italic。
font-variant主要用於指定normal和small-caps。真正的small-caps字體的字母比大寫字母小一些,而且比例也稍有差異。但是,如果不是真正的small-caps字體,也可以將normal字體中的大寫字母縮小後代替。實在不行,也可以直接用大寫字母。
CSS 1只支持small-caps一種變體,不支持老式數字、小型大寫數字、緊縮或疏鬆字母等其他變體。
font-weight選擇字重,有關鍵字值normal、bold、bolder、lighter和數值100~900,表示筆畫越來越粗。normal對應400,bold對應700。bolder和lighter則相對於從父元素繼承的字重加粗或變細。現實中的字體(字體數據)都會有一個或幾個屬性的值用於描述字體的"重量"。但不同字體使用的屬性值並不統一,比如包含以下關鍵字的值似乎都可以認為是粗體:Regular、Roman、Book、Medium、Semi-、DemiBold、Bold或Black,具體要看其normal字形有多重。正因為如此,CSS 1才定義了一系列數值。OpenType使用9個數值區分字重,可以直接對應。但並非所有字體都有那麼多字重,CSS 1為此也定義了如何向前或向後"尋值",比如500如果沒有,則用400。
font-size定義了絕對值、相對值、長度值和百分比值。絕對值包括xx-small、x-small、small、medium、large、x-large和xx-large等關鍵字,它們在UA裡以1.5倍的比例差異保存著絕對大小。相對值有larger和smaller關鍵字,取決於父元素文本大小的絕對值。不允許負值。
最後,font是以上所有屬性的簡寫屬性,可以同時設置font-style、font-variant、font-weight、font-size、line-height和font-family。這個屬性借鑑了傳統排版中的同時設置多個屬性的簡寫法。
Cascading Style Sheets Level 2 Revision 1CSS 1奠定了字體屬性的格局。CSS 2.1僅對CSS 1進行了補充和擴展。
首先,CSS 1隻規定font-family最後要使用一種通用字體作為後備,並沒有詳細說明這些字體的特點和舉例。CSS 2.1則詳細解讀了serif、sans-serif、cursive、fantasy和monospace。
其次,對所有屬性統一增加了inherit關鍵字。而且,font-variant仍然只支持small-caps一種變體形式。
最後,CSS 2.1比較重要的一個增補,是為font簡寫屬性增加了6種系統字體關鍵字:caption、icon、menu、message-box、small-caption和status-bar。比如,通過font: menu;可以讓網頁中的某個元素繼承系統菜單中使用的字體及屬性。假設系統菜單使用了12像素、字重600的Times,則font: menu就相當於:
font: 600 12px Times;
因為系統字體的樣式只能整體繼承,不能單獨獲取,所以只能通過font簡寫屬性來繼承系統字體的樣式。另外,由於font簡寫屬性會將所有沒有明確給出的值重置為相應的初始值,所以上面的聲明又相當於:
font-family: Times;
font-style: normal;
font-variant: normal;
font-weight: 600;
font-size: 12px;
line-height: normal;
CSS Fonts Module Level 3我們知道,從CSS3開始,CSS規範被拆成眾多模塊單獨升級,新的需求也會作為一個新模塊來立項並標準化。在CSS 2.1之後,CSS中關於字體的內容就獨立為CSS3 Fonts模塊。同時為了支持可下載的自定義字體,又創建了CSS3 Web Fonts模塊。
但最終,CSS3 Fonts和CSS3 Web Fonts合併為CSS Fonts Module Level 3(以下簡稱"ML3")。後來,ML3中涉及字體加載的內容,又獨立為CSS Font Loading Module Level 3。
ML3進一步細化了CSS字體屬性的內涵和外延。比如,對於font-family字體列表中出現的字體名。ML3就進一步說明:
字體名只指定一種字體的名字,而不能指定該種字體中某一個字體。比如,Futura是一種字體,它又包含Futura Medium、Futura Medium Italic、Futura Condensed Medium和Futura Condensed ExtraBold字體。
ML3還增加了font-stretch屬性,用於從字體中選擇正常、緊縮或疏鬆的字形,有以下關鍵字值:
ultra-condensed
extra-condensed
condensed
semi-condensed
normal
semi-expanded
expanded
extra-expanded
ultra-expanded
另外,對於之前font-style屬性所謂的"偽斜體"關鍵字oblique,也可以通過font-synthesis屬性設置是否啟用。因為對某些文字,比如西裡爾文字,真正的斜體與人工合成的斜體差別很大。比如,下面的規則禁止UA合成阿拉伯文斜體:
*:lang(ar) { font-synthesis: none; }
ML3還增加了font-size-adjust屬性,用於在使用備用字體時,依然能保持原本所要使用字體的縱橫比(小寫字母與大寫字母的相對高度),從而保證可讀性。
@font-face如前所述,ML3最大的變化就是增加了Font Resource一節。這一節定義了@font-face規則,@font-face規則用於定義一個(種)新的字體,其屬性由包含在花括號內的規則描述符聲明決定。其中,font-family和src是必需的描述符。(注意,在普通CSS聲明裡,font-family和src是屬性,而在@font-face規則裡,因為是用於定義新字體,所以font-family和src就成了描述符,即用於描述新字體的屬性。)
font-family描述符定義字體名字,會覆蓋底層字體文件中的名字,而且如果名字跟系統字體衝突,還會覆蓋系統字體。
src描述符定義字體的來源,其值為按優先次序排列的來源列表,來源本地使用local()引入,來源外部使用url()引入。使用url()引入外部字體時,可以用format()添加字體格式的提示。ML3定義的有效字體格式字符串有:
格式字符串字體格式常見擴展名"woff"WOFF 1.0 (Web Open Font Format).woff"woff2"WOFF 2.0 (Web Open Font Format).woff2"truetype"TrueType.ttf"opentype"OpenType.ttf, .otf"embedded-opentype"Embedded OpenType.eot"svg"SVG Font.svg, .svgz此外,由於OpenType是TrueType超集和擴展,字符串"truetype"和"opentype"表示的意思是一樣的。
@font-face {
font-family: bodytext;
src: url(ideal-sans-serif.woff2) format("woff2"),
url(good-sans-serif.woff) format("woff"),
url(basic-sans-serif.ttf) format("opentype");
}
在使用local()加載本地字體時,因為UA要匹配字體文件中包含的全名,而同一字體在不同平臺下的全名可能不一樣,因此需要指定所有可能的名字,以便跨平臺使用。
@font-face {
font-family: MyGentium;
src: local(Gentium Bold),
local(Gentium-Bold),
url(GentiumBold.woff);
font-weight: bold;
}
local()函數很有用,比如上面的例子其實是為不同平臺下的同一款字體創建了一個統一的別名。再比如下面的例子,用於將一種大字體中永遠都不會被引用到的字體抽取出來:
@font-face {
font-family: Hoefler Text Ornaments;
src: local(HoeflerText-Ornaments);
}
另外,ML3規定UA只能優先使用字體文件中的英文全名來匹配字體。即便用戶作業系統將地區設置為比如德國、芬蘭,而字體文件中也有德語、芬蘭語的字體全名,也要匹配英文全名,這是為了全平臺統一起見。因此下面h2最終會使用默認的襯線字體:
@font-face {
font-family: SectionHeader;
src: local("Arial Lihavoitu");
font-weight: bold;
}
h2 { font-family: SectionHeader, serif; }
最後,如果存在多個src描述符,後聲明的會覆蓋先聲明的:
@font-face {
font-family: MainText;
src: url(gentium.eot);
src: local("Gentium"), url(gentium.woff);
}
font-style、font-weight和font-stretch這三個描述符的值跟對應屬性的值一樣,只是不允許使用相對關鍵字bolder和lighter。
綜上所述,@font-face規則的引入為CSS字體的使用提供了很大便利。事實上,只有結合馬上要出場的unicode-range描述符,@font-face規則才能發揮出最大的潛力。
unicode-rangeunicode-range描述符用於指定當前定義字體支持的Unicode碼點(code point),是一個逗號分隔的Unicode範圍值。這相當於定義了一個字符集,UA可以根據這個字符集來決定針對某個文字是否需要下載新字體。
Unicode範圍值支持單個碼點(如U+416)、碼點區間(如U+400-4ff)和通配範圍(如U+4??)。
那麼Unicdoe範圍或者說字符範圍(字符集)有什麼用呢?可以用多個@font-face規則和不同的Unicode範圍共同定義一個複合型字體。換句話說,可以在一個自定義字體中包含來自不同語言文字的多個字體的不同字形,也可以定義一個只包含某種字體常用或罕用字符的新字體。如果同一個新字體的多個@font-face規則中的Unicode範圍有重合,那麼最後定義的規則先匹配。
下面的規則會基於日文字體MSMincho和英文字體Gentium定義一個新字體JapaneseWithGentium,其中拉丁字母的字形來自Gentium字體,其他字形來自MSMincho。
@font-face {
font-family: JapaneseWithGentium;
src: local(MSMincho);
}
@font-face {
font-family: JapaneseWithGentium;
src: url(../fonts/Gentium.woff);
unicode-range: U+0-2FF;
}
利用@font-face的層疊規則,還可以實現對字體的"按需下載"。比如,在下面的例子,如果只需要拉丁字符的字形,UA只會下載DroidSans.woff。
@font-face {
font-family: DroidSans;
src: url(DroidSansFallback.woff);
}
@font-face {
font-family: DroidSans;
src: url(DroidSansJapanese.woff);
unicode-range: U+3000-9FFF, U+ff??;
}
@font-face {
font-family: DroidSans;
src: url(DroidSans.woff);
unicode-range: U+000-5FF, U+1e00-1fff, U+2000-2300;
}
ML3規定,@font-face指定的字體資源採用懶加載策略,即如果不用到,就不下載。樣式表裡可以定義很多@font-face,但UA必須只下載那些樣式規則中引用到的字體。有一個例外就是當自定義字體作為後備字體時,UA可以提前下載,比如:
@font-face {
font-family: GeometricModern;
src: url(font.woff);
}
p {
font-family: GeometricModern, sans-serif;
}
h2 {
font-family: Futura, GeometricModern, sans-serif;
}
一般情況下,頁面都會先於字體加載完。此時使用自定義字體的文本應該如何顯示呢?ML3規定UA可以按照自定義字體不可用的情形來渲染字體,或者用後備字體將文本渲染為透明的。但在自定義字體下載失敗後,UA必須顯示文本。因此,ML3也要求樣式表作者指定與自定義字體大小類似的後備字體。
關於字體的獲取,ML3規定必須使用啟用CORS的手段,使用Anonymous模式,將referrer設置為樣式表的URL,將origin設置為包含樣式表的文檔的URL。
關於字體匹配,ML3是有史以來最詳細的。它增加了font-stretch的匹配,羅列出了font-style所有可能的匹配情形。small-caps完全從字體匹配流程中剝離,由字體特性來處理。要求必須使用Unicode變體選擇符。簇序列作為一個單位匹配。
字體特性如前所述,font-variant屬性一直以來只有一個small-caps變體。而實踐中使用的字體遠不止只有小型大寫字母一種變化形式。為此,ML3擴展了font-variant屬性,使其成為了控制所有樣式相關字體特性的一個簡寫屬性。
換句話說,font-variant有了font-variant-ligatures、font-variant-postion、font-variant-caps、font-variant-numeric、font-variant-east-asian等具體屬性。
字體特性可以通過font-variant或font-feature-settings來啟用。具體細節因為涉及英文字體的很多古老傳統,有興趣的讀者可以自行學習。
尾聲CSS Font Module Level 3在2018年9月成為W3C推薦標準,隨即又開始了CSS Fonts Module Level 4,包含了一些CSS字體相關的試驗性特性。另外,從CSS Font Module Level 3中獨立出去的CSS Font Loading Module Level 3已經得到主流瀏覽器較新版本的支持。
相關連結Cascading Style Sheets, level 1(https://www.w3.org/TR/REC-CSS1/)
Cascading Style Sheets Level 2 Revision 1 (CSS 2.1)(https://www.w3.org/TR/2011/REC-CSS2-20110607/)
CSS Font Module Level 3(https://drafts.csswg.org/css-fonts-3/)
CSS Fonts Module Level 4(https://drafts.csswg.org/css-fonts-4/)
CSS Font Loading Module Level 3(https://drafts.csswg.org/css-font-loading-3/)
Character Model for the World Wide Web 1.0: Fundamentals:(https://www.w3.org/TR/2005/REC-charmod-20050215/)
關於奇舞周刊《奇舞周刊》是360公司專業前端團隊「奇舞團」運營的前端技術社區。關注公眾號後,直接發送連結到後臺即可給我們投稿。