ByteScope

WOFF 字型檔案格式

Web Open Font Format——把 TTF/OTF 壓縮起來給網頁用的外殼。

MIME 類型
font/woff
副檔名
.woff
分類
字型

Magic number(檔案簽章)

0x00000000774F4646wOFF'wOFF' tag

關於 WOFF 字型格式

WOFF 不是它自己的字型格式——它是個容器。它拿一份完整的 TrueType 或 OpenType 字型,把每一張表分別用 zlib 壓縮,前面加上 44 個位元組的標頭和一份新的表目錄,而還原出來的字型和放進去的那份逐位元組等價。推動它的有兩件事:體積省下來,以及 WOFF 一眼就看得出是網頁字型而不是可安裝的桌面檔案——光這一點就足以說服字體公司願意替 @font-face 授權。它還帶著一個選用的壓縮 XML 中繼資料區塊,設計用途正是放那些授權資訊。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
04signature77 4F 46 46,ASCII 的 wOFF,w 是小寫。這種大小寫混用是刻意的,也值得記住,因為在 hex 檢視器裡用不分大小寫的方式搜尋 WOFF,也會命中中繼資料區塊裡的文字和任何內嵌的版權字串。
44flavor裡面那份字型的 sfntVersion:TrueType 外框是 00 01 00 00,CFF 是 4F 54 54 4F。.ttf 或 .otf 在位移 0 帶的就是這個,只是往後移了 4 個位元組;它告訴你外殼拆掉之後手上會是什麼。
84lengthWOFF 檔案的總位元組數。它必須等於實際檔案長度,所以光看前 12 個位元組就能判斷下載有沒有被截斷,完全不用解壓縮、也不用懂字型——這種免費的檢查,格式裡少之又少。
122numTables表目錄有幾筆。每筆剛好 20 個位元組,所以這個數字同時也告訴你壓縮資料從哪裡開始。
142reserved必須是零。瀏覽器在把網頁字型交給點陣化引擎之前都會先過一遍消毒器,而「沒人會讀的欄位裡出現非零值」正是那種讓檔案在網頁上被直接拒收、桌面工具卻開得很開心的原因之一。
164totalSfntSize還原後字型的大小,包含它的目錄與表與表之間的填補。把它和位移 8 的 length 對照,不用解壓縮就得到壓縮比;一般文字字體大概略優於二比一。
204majorVersion and minorVersion位移 20 和 22 兩個 16 位元數字。它們標的是字型的版本、不是 WOFF 格式的版本,通常是從來源字型的 head 表複製過來或與之對齊;你多半會看到 1.0 和 1.1。
2412metaOffset, metaLength, metaOrigLength位移 24、28、32 三個 32 位元欄位,描述一段選用的 zlib 壓縮 XML 中繼資料區塊——授權、廠商與描述本來就是要放在那裡的。沒有的時候三個都是 0,而實務上那才是常態。
368privOffset and privLength第二個選用區塊,位在位移 36 和 40,保留給字型作者放任何他想放的東西,沒有任何規範。通常也是 0,而非零的話,在追查字型來歷時很值得看一眼。
44variableTable directory每張表 20 個位元組,緊接在標頭後面開始:標籤、在 WOFF 檔案裡的位移、壓縮後長度、原始長度,以及原始未壓縮表的檢查碼。所以第一張表的壓縮資料從 44 加上 20 乘 numTables 的地方開始,這個算術在 hex 檢視器前面心算就做得完。
variablevariableCompressed table data每張表各一條獨立的 zlib 資料流,依目錄順序排列。當壓縮某張表反而不會變小時,格式允許原封不動存放,你可以在目錄裡看出來——壓縮後長度等於原始長度的那些項目,幾十個位元組的小表很常見。

怎麼跟長得像的格式分開

容易搞混的怎麼分
WOFF2 字型位移 0 的 77 4F 46 46 對上 77 4F 46 32,而之後的一切也都不一樣:WOFF2 的標頭是 48 個位元組而不是 44,因為它在位移 20 多了一個 totalCompressedSize 欄位;它的目錄記錄是長度不定的,不是固定 20 個位元組;它的主體是整個字型一條 Brotli 資料流,而不是每張表一條 zlib。
TrueType 字型WOFF 裡面那份字型就是 TrueType 或 OpenType 字型,內容沒有任何改動,只是辨識它的那 4 個位元組從位移 0 搬到了位移 4。只檢查位移 0 的讀取端看到 wOFF、報一個未知字型;連位移 4 也檢查的讀取端,就能告訴你解壓縮之後會拿到哪一種。
GZIP兩者都帶著 zlib 壓縮的內容,也都不能拿通用解壓縮工具對著位移 0 就開始解。gzip 檔案是 1F 8B,只有一條資料流、而且從很前面就開始;WOFF 裝的是好幾條資料流,起點只有目錄知道,而且每一條前面都沒有自己的標頭。把 WOFF 改名成 .gz 再跑 gunzip 會立刻失敗,而那個失敗完全說明不了字型本身好不好。

常見的壞法

  • length 欄位和檔案對不上。位移 8 那 4 個位元組寫的是整個檔案大小,所以下載被截斷或被補過東西,在解壓縮任何內容之前就能在 hex 檢視器裡證明。這是第一個該跑的檢查,而且光靠它就解決了相當高比例的網頁字型載入失敗。
  • 字型網址底下其實是一頁錯誤頁。一個 404、一次登入轉址或一則流量限制通知被存成 .woff,用文字編輯器打開就是 HTML,而瀏覽器主控台報的是「無法解碼下載的字型」外加一個消毒器解析錯誤。那句訊息完全沒有暗示這個檔案其實是一個網頁,所以特別浪費時間。
  • 被瀏覽器的消毒器拒收。瀏覽器比桌面字型工具嚴格得多:保留欄位非零、欄位超出允許範圍、結構互相重疊,都會讓一個在你自己機器上裝得乾乾淨淨的檔案在網頁上被拒。字型在本機正常、在頁面上不行,通常就是這個,不是伺服器的問題。
  • 單一張表壞掉。因為每張表都有自己的 zlib 資料流,一個位元組被翻掉就只殺掉其中一張,其他完好無缺。於是出現一種怪異的失敗:字型載入了、度量正確、字形也畫得出來,卻掉了字距微調或垂直度量——就看是哪一張表解不開。
  • 文字模式傳輸。會轉換換行的部署步驟會改寫壓縮資料流裡的 0D 0A,而那些資料不是文字、撐不過這一關。標頭和目錄毫髮無傷地通過,所以檔案看起來一直都像字型,直到 inflate 失敗的那一刻。

這個格式現在還在用嗎

從大約 2012 年到 WOFF2 追上來之前,WOFF 就是那個網頁字型格式,而它現在的工作是相容性而不是交付。近十年發行的每個瀏覽器都偏好 WOFF2,同樣內容小上大約三分之一,所以現代的 @font-face 規則會把 WOFF2 排第一、WOFF 只當備援——而且越來越常連備援都不寫了,因為需要備援的那些瀏覽器早就從分析資料裡消失了。WOFF 現在還會冒出來的地方,是幾年前做好之後就沒重建過的封裝軟體、說明文件包與佈景主題,以及那些比 Brotli 更早的工具鏈。它真正的成就從來不是技術上的。把字型包在一個明顯不能安裝的東西裡,再帶上一個能讓授權隨檔案一起走的中繼資料區塊,這件事化解了字體公司當年完全不放行網頁使用的爭議,而此後每一種網頁字型都靠著那次和解過日子。

在這個站上打開 WOFF 字型檔

這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。

相關格式

容易和 WOFF 字型搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
WOFF2 字型77 4F 46 32.woff2
TrueType 字型00 01 00 00.ttf
OpenType 字型4F 54 54 4F.otf

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 WOFF 字型?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。