Matplotlib 中的字體#

Matplotlib 需要字體才能與其文字引擎搭配使用,其中一些字體隨安裝一起提供。預設字體是 DejaVu Sans,涵蓋大多數歐洲文字系統。不過,使用者可以設定預設字體,並提供自己的自訂字體。請參閱 自訂文字屬性 以了解詳細資訊,並特別參閱 使用非拉丁字符的文字,以了解 DejaVu Sans 不支援的字符。

Matplotlib 也提供將文字渲染卸載到 TeX 引擎的選項 (usetex=True),請參閱 使用 LaTeX 渲染文字

PDF 和 PostScript 中的字體#

字體在運算中具有悠久的歷史(有時不相容),導致不同的平台支援不同類型的字體。實際上,Matplotlib 支援三種字體規格(除了稍後在指南中說明的 pdf「核心字體」之外)

字體類型#

Type 1 (PDF)

Type 3 (PDF/PS)

TrueType (PDF)

最古老的類型之一,由 Adobe 推出

在推出方面與 Type 1 相似

比先前的類型新,現今普遍使用,由 Apple 推出

PostScript 的受限子集,字符字串以位元組碼表示

完整的 PostScript 語言,允許嵌入任意程式碼(理論上,甚至在點陣化時渲染碎形!)

包含可執行程式碼的虛擬機器!

這些字體支援字體提示

不支援字體提示

支援提示(虛擬機器處理「提示」)

未透過 Matplotlib 子集化

透過外部模組 ttconv 子集化

透過外部模組 fontTools 子集化

注意

Adobe 在 2023 年 1 月停用了使用 Type 1 字體進行創作的支援。

Matplotlib 支援的其他字體規格

  • Type 42 字體 (PS)

  • OpenType 字體

    • OpenType 是由 Adobe 和 Microsoft 共同開發的數位字體的新標準

    • 通常包含更大的字符集!

    • Matplotlib 的支援有限

字體子集化#

PDF 和 PostScript 格式支援在檔案中嵌入字體,讓顯示程式可以正確地渲染文字,而不論檢視器電腦上安裝了哪些字體,也不需要預先點陣化文字。這可確保如果輸出放大或調整大小,文字不會變成像素化。不過,在檔案中嵌入完整的字體可能會導致輸出檔案很大,尤其是有許多字符的字體,例如支援 CJK(中文/日文/韓文)的字體。

這個問題的解決方案是子集化文件中使用的字體,並僅嵌入實際使用的字符。這可以同時取得向量文字和小的檔案大小。計算所需的字體子集並寫入新的(縮減的)字體都是複雜的問題,因此 Matplotlib 依賴 fontTools 和 ttconv 的供應商分支。

目前已子集化 Type 3、Type 42 和 TrueType 字體。Type 1 字體則未子集化。

核心字體#

除了能夠嵌入字體之外,作為PostScriptPDF 規格的一部分,有 14 種核心字體,符合規範的檢視器必須確保這些字體可用。如果您將文件限制為僅使用這些字體,則不必在文件中嵌入任何字體資訊,但仍可取得向量文字。

這對於產生真正輕量的文件特別有幫助

# trigger core fonts for PDF backend
plt.rcParams["pdf.use14corefonts"] = True
# trigger core fonts for PS backend
plt.rcParams["ps.useafm"] = True

chars = "AFM ftw!"
fig, ax = plt.subplots()
ax.text(0.5, 0.5, chars)

fig.savefig("AFM_PDF.pdf", format="pdf")
fig.savefig("AFM_PS.ps", format="ps")

SVG 中的字體#

文字可以透過 rcParams["svg.fonttype"](預設值:'path')控制的兩種方式輸出到 SVG

  • 作為 SVG 中的路徑 ('path')

  • 在元素上使用字體樣式的 SVG 中的字串 ('none')

當透過 'path' 儲存時,Matplotlib 將計算用作向量路徑的字符路徑,並將其寫入輸出。這樣做的優點是,無論安裝了哪些字體,SVG 在所有電腦上的外觀都相同。不過,事後將無法編輯文字。相反地,使用 'none' 儲存會產生較小的檔案,而且文字將直接顯示在標記中。不過,外觀可能會因 SVG 檢視器和可用的字體而異。

Agg 中的字體#

為了透過 Agg 將文字輸出為點陣格式,Matplotlib 依賴 FreeType。由於字符的確切渲染在 FreeType 版本之間會有所不同,因此我們將我們的影像比較測試釘選到特定版本。

Matplotlib 如何選取字體#

在內部,在 Matplotlib 中使用字體是一個三個步驟的過程

  1. 建立 FontProperties 物件(明確或隱含地)

  2. 根據 FontProperties 物件,使用 FontManager 上的方法來選取 Matplotlib 知道的最接近「最佳」字體(除了 SVG 的 'none' 模式)。

  3. 字體物件的 Python Proxy 由後端程式碼用來渲染文字 -- 確切的詳細資訊取決於透過 font_manager.get_font 的後端。

選取「最佳」字體的演算法是 CSS1 規格 指定的演算法的修改版本,網頁瀏覽器使用該演算法。此演算法會考量字型系列名稱(例如「Arial」、「Noto Sans CJK」、「Hack」、...)、大小、樣式和粗細。除了直接對應到字體的系列名稱之外,還有五個「通用字型系列名稱」(serif、monospace、fantasy、cursive 和 sans-serif),這些名稱會在內部對應到任何一組字體。

目前,執行步驟 2 的公開 API 是 FontManager.findfont (並且全域 FontManager 實例上的該方法在模組層級別名為 font_manager.findfont),它只會找到單一字體並傳回該字體在檔案系統上的絕對路徑。

字體回溯#

沒有任何單一字體可以涵蓋整個 Unicode 空間,因此使用者可能需要混合使用來自不同字體的字符。雖然在一個 Figure 中,不同的 Text 實例中可以使用多個字體,但之前無法在同一個 Text 實例中使用多個字體 (如同網頁瀏覽器一樣)。從 Matplotlib 3.6 開始,Agg、SVG、PDF 和 PS 後端會在單一 Text 實例中「回溯」使用多個字體。

fig, ax = plt.subplots()
ax.text(
    .5, .5, "There are 几个汉字 in between!",
    family=['DejaVu Sans', 'Noto Sans CJK JP', 'Noto Sans TC'],
    ha='center'
)

(原始碼, 2x.png, png)

字串 "There are 几个汉字 in between!" 使用 2 種字體渲染。

其內部實作方式是將 FontProperties 物件上的「字體系列」設定為字體系列的列表。一個(目前)私有 API 會提取找到的所有字體的路徑列表,然後建構一個單一的 ft2font.FT2Font 物件,該物件會知道所有字體。字串中的每個字符都會使用列表中第一個包含該字符的字體進行渲染。

這項工作的大部分是由 Aitik Gupta 在 Google Summer of Code 2021 的支持下完成的。

由 Sphinx-Gallery 產生的圖庫