LaTeX 筆記 NFSS 那點事兒

2021-07-14 20:15:20 字數 3828 閱讀 3018

零、我就不寫成《latex 筆記(一):nfss 那點事兒》了,省得你們指望還有二……

本文比較靠近 texnique 這個主題。想理解本文的內容,至少也得讀一下 texbook 或者 tex by topic 之類的。

一、nfss 框架是 latex2e 的核心內容,佔了 latex2e 原始碼的 30% 左右的篇幅。如果你不理解它的重要性,還有乙個資料:在完全安裝的 tex live 發行版中,各類字型巨集包編寫的字型定義檔案 (*.fd) 有 3000+。有了 nfss,大家才能快樂地使用 \bfseries \itshape 等命令切換字型,隨心所欲不踰矩了。

那麼沒有 nfss 的情況下怎麼切換字型呢?如果你像第 0 條裡面說的,讀過了兩本書之一,那麼你就知道是這麼來的:

% 以 txfonts 巨集包的字型 txr 為例

\font\txrm=txr at 12pt

\txrm hello, \tex\ and its fonts!

需要定義乙個控制序列(命令)代表實際的字型,然後在切換字型的時候用一下。plain tex 已經定義了一些,如 \bf \it \tt 之類。問題在於,\bf 是直立的粗體,\it 是普通字重的義大利斜體,如果你想要粗斜體,這個字型跟它倆一毛錢關係都沒有,必須你自己重新定義個諸如 \bfit 之類的字型命令才行。就連不同字型大小的字型,都需要重新定義

那麼話說回來,其實 \bfseries \itshape 和 \mdseries \itshape 、\bfseries \upshape 等命令給出的字型也全都不一樣啊,這怎麼搞的?往下看。

二、上面讓看的兩篇文件,介紹了 nfss 重要的屬性——座標。乙個字型有 encoding / family / series / shape 和字型大小這五個座標,比如預設字型,10pt 的 computer modern roman (cmr10),在 nfss 的座標是 ot1/cmr/m/n/10。這裡就不鋪開講了,需要你真的看完那兩篇文件。

哦,你似乎在哪兒見到過這麼一串寫法?對了,在 overfull \hbox 等資訊裡滿都是這種寫法。好了,是時候告訴你真相了:在呼叫這個字型前,實際上就有這麼個全域性的定義:

\global\font\ot1/cmr/m/n/10=cmr10

不過呢直接這樣寫是錯的,因為控制序列的名稱必須是字母類(catcode=11 的字元)。正確的寫法相當於這樣:

\global\expandafter\font\csname ot1/cmr/m/n/10\endcsname=cmr10

也就是說,真實的字型命令,是由這些座標拼起來構建的。不同的座標會構造出來不同的字型命令,你每切換一次座標,很可能就要生成乙個新的字型命令。

三、真的就是把字型座標拼起來這麼簡單?當然不止,latex 需要額外的資訊找到上面**裡等號後面要賦值的字型名。把座標與字型名稱建立對映的東西,就是每個字型巨集包必不可少的 .fd 檔案。這個放到後面穿插著說。

四、現在要提到 nfss 裡的乙個比較核心的命令——\selectfont。在 \bfseries \itshape 或者 \small \large 等的定義裡都有它。你光用 \fontseries 或者 \fontsize 改座標還起不到改字型的作用,它只是把座標資訊存到了一些內部巨集,如 \f@encoding \f@series 等。\selectfont 才是改變字型的實際操作。

那麼 \selectfont 主要幹了些啥呢? 

\declarerobustcommand\selectfont

%\pickup@font

\font@name

\size@update

\enc@update

}

定義裡,前兩行先不細看,它的作用是處理行距資訊。行距有兩種改法:\linespread 和 \renewcommand ,後者會導致 \baselinestretch 和用內部命令快取的行距擴充套件係數 \f@linespread 不一致(前者則沒有這個問題),所以在這兒要處理一下。

第 3-4 行就是定義出來所謂的 \ot1/cmr/m/n/10 了,這裡定義了乙個全域性的 \font@name,後面很多地方用得著,另外乙個重要的內部巨集是 \curr@fontshape,它儲存著前四個座標。

第 6 行則是使用這個 \ot1/cmr/m/n/10 改變字型了;第 7 行起到的作用是更新 \baselineskip 等一系列工作;第 8 行是改了 encoding 之後的一些處理。

重點看第 5 行:

\def\pickup@font

意思不難,如果 \ot1/cmr/m/n/10 已經定義過,拿來現成用就好,否則需要定義新字型了。接下來看:

\def\define@newfont

group 之內,第一行影響到向終端輸出的資訊;第 2-4 行是對 \font@name 內容的乙個拆包,把它裡頭的字型座標存進 \f@encoding \f@series 等。

後面的乙個關鍵巨集是 \try@load@fontshape:

\def\try@load@fontshape

%\global\expandafter\let

\csname\f@encoding+\f@family\endcsname\@empty

\nfss@catcodes

\let\nfss@catcodes\relax

\edef\reserved@a}}

%\reserved@a\relax}%

\fi}

我們可以看到,這個巨集起到的作用就是載入 .fd 字型定義檔案了。在此解釋兩個巨集:

那麼好了,往下走,如果 .fd 檔案載入了,各條 \declarefontfamily 也過了一遍,仍然找不到對應的字型怎麼辦?這就表明,字型包裡可能沒有定義你要的座標,這時就要進行回退(fallback),這個工作由 \wrong@fontshape 完成。每個 encoding 的定義檔案如 t1enc.def 裡,都會用 \declarefontsubstitution 命令給出用來回退的三個座標(ot1 的是在 latex2e 核心裡給的)。

\wrong@fontshape 的**就不繼續貼了,舉個例子,如果我弄了乙個錯誤的坐標出來, \ot1/cmr/***/***,latex 載入了 ot1cmr.fd 發現沒有這個座標,它就回退到 \ot1/cmr/***/n,還找不到,就回退到 \ot1/cmr/m/n。如果進行了回退,你會看到 "font shape *** undefined, use *** instead" 的提示。

如果找到了前四個座標的資訊,或者回退到了合適的前四個座標,接下來就要通過字型大小這個座標去找字型(tfm)了。這個功能由 \extract@font 完成,它呼叫了 \get@external@font 去解析 \declarefontshape 命令裡各種複雜的字型大小和 tfm 對映關係的定義式,後者內部用了 \try@size@range 完成實際的解析,用 \try@size@substitution 完成字型大小的 fallback。

五、囉囉嗦嗦地寫完了,不知道有多少人有足夠的耐心看到這兒,以及為了看懂它去看更多的書。latex 對字型的這套解決方案也許不是最優的,不過恐怕是無法替代的,不然那烏泱烏泱的字型巨集包都得改。

嗯我不是說了有 30% 的**是 nfss 嘛?事實上今天討論到的這部分連 5% 都佔不到。其餘的大頭是被 encoding 和數學字型佔了。encoding 的部分,要解決的是不同字型編碼下的一些命令和重音的實現,比如 ot1 下的 \"o 是兩個字元拼起來的,而 t1 則可以找到單個的字元。數學字型就不在這裡說了,要說起來肯定要另開一帖。

\endinput

游標那點事兒

兩種迴圈跳出方法 1 稍顯複雜點 create procedure dbo.usp cralltables client id varchar 256 asdeclare table name varchar 50 set nocount on declare t name cur cursor l...

imu那點事兒

一.對於bosch晶元的總結 offset 是指sensor的零偏。datasheet 裡邊描述的是在不同的情況下offset 的spec.offa,int 表示sensor 出廠時最初的offset spec,是component level offa,board 表示sensor 在貼到pcb ...

Spring MVC那點事兒

1 spring mvc的啟動原理?spring mvc是基於ioc容器的,因此需要先建立ioc容器,才能建立對應的spring mvc執行環境。ioc容器是通過contextloaderlistener建立的,這個類通過servletcontext建立。在springmvc中,最核心的思想其實就是...