ByteScope

SVG 檔案格式

Scalable Vector Graphics,以 XML 為基礎的向量影像格式。

MIME 類型
image/svg+xml
副檔名
.svg
分類
圖片

Magic number(檔案簽章)

含有 `<svg` 元素的純文字 XML(前面通常會有 `<?xml`)。

關於 SVG 格式

SVG 用 XML 把影像描述成向量圖形,所以放大到任何尺寸都不會模糊,還能用 CSS 設定樣式與製作動畫。因為它是純文字,沒有二進位的 magic number — 靠開頭附近的 XML 宣告或 `<svg` 元素來辨識。

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

位移長度欄位意義
03UTF-8 byte-order markEF BB BF,可有可無,而且是 XML 允許出現在宣告之前的唯一東西。少數比較舊的剖析器和某些伺服器設定至今仍會拒收它,所以它一再成為「明明合法的 SVG 卻畫不出來」的原因。
02UTF-16 byte-order mark小端序是 FF FE,大端序是 FE FF。跟 UTF-8 那個標記不同,這個不是可有可無的:XML 規定 UTF-16 內容必須帶 BOM。UTF-16 的 SVG 很罕見,而且會弄壞每一個假設「一個字元一個位元組」的工具。
05XML declaration opener3C 3F 78 6D 6C,也就是開啟宣告的那幾個字元。XML 規定它只要出現,就必須是整份文件最前面的東西 — 所以位移 0,或是在 UTF-8 BOM 後面的位移 3。對 UTF-8 內容來說它是選用的,這也是為什麼沒有偵測器能硬性要求它存在。
05DOCTYPE opener3C 21 44 4F 43。Illustrator 和 Inkscape 產出的舊檔案,開頭是一個指名 SVG 1.1 DTD 的 DOCTYPE。只有在既沒有 BOM 也沒有宣告時它才落在位移 0,否則就緊接在宣告後面。SVG 2 已經拿掉 DTD,所以新檔案不會帶。
04Root element start tag3C 73 76 67。只有在檔案沒有 BOM、沒有宣告、沒有 DOCTYPE、開頭也沒有註解時,它才會落在位移 0 — XML 允許這四樣東西全部排在它前面,所以這個位移是真的不固定。偵測器的做法是在前幾百個位元組裡找這個字串,而不是去測某個位置。

怎麼跟長得像的格式分開

容易搞混的怎麼分
GZIP.svgz 是壓過 gzip 的 SVG,所以它開頭是 1F 8B 08,裡面完全沒有看得懂的標記。伺服器會配上 gzip 的 Content-Encoding 標頭送出,瀏覽器則透明地解壓縮;在 hex 編輯器裡它就是一個 gzip member,如果寫入端有填檔名,那個名字會以 .svg 結尾。
XMLSVG 就是 XML,所以任何通用的 XML 測試都會通過,而且什麼也沒告訴你。唯一能分開兩者的是根元素的名字:第一個不是宣告、DOCTYPE、註解或處理指令的標籤,必須是 svg 元素。巢狀在別的根元素底下的 svg 元素,不構成一份 SVG 文件。
HTMLHTML5 允許行內的 svg 元素,所以一個 HTML 頁面裡可能出現辨識 SVG 用的那串位元組。看根元素到底是什麼:開頭是 DOCTYPE html、或是 html 起始標籤,就代表這是一份剛好裝了一張圖的 HTML,而且必須以 text/html 而不是 image/svg+xml 提供。
PNG在位元組層級不可能混淆 — 89 50 4E 47 對上一堆看得懂的文字 — 但實務上天天在混淆,因為好幾套設計工具的「匯出成 SVG」吐出來的 SVG,整份內容就是一個裝著 base64 PNG 的 image 元素。這個檔案確實是 SVG,裡面那張圖卻仍然是點陣圖,放大照樣糊。

常見的壞法

  • 多出來的一個 & 符號。XML 採用嚴苛的錯誤處理,所以 href 屬性裡的網址只要有一個沒跳脫的 &,整份文件就不是良構的,瀏覽器會什麼都不畫,而不是把前面 95% 的圖畫出來。它每次都必須寫成 &amp;。
  • 命名空間不見了。把根元素上那個指向 http://www.w3.org/2000/svg 的 xmlns 屬性拿掉,檔案仍然是合法的 XML、也照樣在文字編輯器裡打得開,但在每個瀏覽器裡都是一片空白。手改過的 SVG 突然不能用,最常見的原因就是這個。
  • 編碼對不上。宣告說是 UTF-8,檔案卻是用 Latin-1 或 Shift-JIS 存的。text 元素會變成亂碼,或是剖析器在第一段不合法的序列就中止 — 而值得檢查的位元組在 text 元素裡面,不在標頭那邊。
  • 截斷。寫到一半的 svg 結束標籤是致命的良構性錯誤,所以不像被截斷的 PNG,這裡沒有半張圖可看。判斷方式在檔案結尾:完好的 SVG 以一個 svg 結束標籤收尾,通常後面還有一個換行。
  • BOM 出現在不該出現的地方。擺在一個宣告了別種編碼的宣告前面就是衝突;而在被行內嵌進 HTML 頁面的 SVG 裡,那三個位元組會變成看得見的空白,把周圍的版面推歪。

這個格式現在還在用嗎

SVG 是網路唯一達成共識的向量影像格式,二十五年過去仍然沒有對手。圖示、logo、圖表、示意圖和插畫全部用 SVG 出貨,因為同一個檔案在任何像素密度下都是銳利的,還能從 CSS 改顏色;2012 年前後用來解同一個問題的圖示字型,現在基本上已經消失了。SVG 不適合的場合是照片類內容 — 把一張照片描邊向量化,做出來的檔案比原本那張 JPEG 更大也更慢畫 — 以及動輒上萬條路徑的美術稿,那時每個元素的 DOM 成本會蓋過檔案變小的好處。它同時也是一個活生生的攻擊面,因為 SVG 可以夾帶 script 和外部引用,所以從使用者那裡收進來的檔案,一定要先淨化過再送出去。

在這個站上打開 SVG 檔

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

相關格式

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

格式開頭位元組副檔名
PNG89 50 4E 47 0D 0A.png
XML無固定簽章.xml
HTML無固定簽章.html

常見問題

檔案的 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 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

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