SVG 格式
Scalable Vector Graphics
XML 结构
SVG 和本站其他格式都不一样,因为它不是二进制,而是纯粹的 XML 文本。 你可以直接用文本编辑器打开一个 SVG 读起来。这让清除元数据在概念上更简单, 但也意味着信息能藏身的地方更多了。
元数据可以以专门的元素出现,也可以是任意元素上的属性、XML 注释或处理指令。 矢量编辑器尤其喜欢往 SVG 文件里塞自己的数据。
SVG 的基本结构
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100" viewBox="0 0 100 100">
<!-- 元数据元素 -->
<title>My Drawing</title>
<desc>Created by John Doe</desc>
<metadata>
<rdf:RDF xmlns:rdf="...">
<!-- RDF/Dublin Core 元数据 -->
</rdf:RDF>
</metadata>
<!-- 矢量内容 -->
<circle cx="50" cy="50" r="40" fill="red"/>
</svg>几个关键元素
<svg>:根元素,带有 viewBox 和尺寸xmlns:命名空间声明<defs>:可复用的定义(渐变、图案)<g>:分组元素
元数据元素
SVG 规范定义了好几种挂载元数据的方式,不同工具用法也不同。 有些一目了然(毕竟真的有一个 <metadata> 元素),有些则藏得更深。
<metadata>
结构化元数据的容器,通常是 RDF/Dublin Core, 可能包含创作者姓名、创建日期、权利声明等等。
<metadata>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<rdf:Description>
<dc:creator>John Doe</dc:creator>
<dc:date>2024-01-15</dc:date>
</rdf:Description>
</rdf:RDF>
</metadata><title>
给整个 SVG 或单个元素起的可读标题。常用于无障碍访问, 但也可能暴露这份文件是干什么用的。
<desc>
描述元素,可能写着关于这张图、创作者或用途的详细信息。
XML 注释
<!-- comment --> 里可能藏着作者备注、 创建日期、软件信息或调试信息。
编辑器命名空间
这里就是 SVG 变乱的地方。每个矢量编辑器都会加上自己的带命名空间的属性, 用来存放 SVG 本身不支持的功能。打开一个 Inkscape 文件,你会看到满屏的 inkscape: 和 sodipodi: 属性。 它们本身算不上隐私风险,但足以标出你用的是什么软件。
inkscape:* / sodipodi:*
Inkscape 会加上带命名空间的属性,记录图层名称、参考线位置、 文档设置和编辑历史。
<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
inkscape:version="1.2"
sodipodi:docname="my-drawing.svg">
<sodipodi:namedview inkscape:current-layer="layer1"/>
</svg>Adobe Illustrator
Illustrator 会嵌入大量元数据,包括版本、画板设置, 有时甚至是完整的 AI 格式数据。
xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/" xmlns:x="http://ns.adobe.com/Extensibility/1.0/"
Sketch / Figma
现代设计工具可能会在自定义属性里嵌入图层名称、组件 ID 和导出设置。
处理指令
SVG 文件开头的 XML 处理指令(<?...?>)里可能带有软件标识。
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/css" href="style.css"?> <!-- Created with Inkscape (http://www.inkscape.org/) -->
保留与清除
- •
<?xml ...?>声明:保留(必需) - •
<?xml-stylesheet ...?>:保留(有实际功能) - • 其他处理指令:清除
基于 DOM 的处理
既然 SVG 是合法的 XML,我们就可以把它解析成 DOM 树, 再像做手术一样精准摘掉不想要的部分。这比用正则表达式硬匹配准确得多, 也不会误伤有效内容。
解析 XML
把 SVG 当作 XML 文档解析,保留原有结构
删除元数据元素
删掉 <metadata>、<title>、<desc> 元素
剥掉编辑器属性
清除 inkscape:*、sodipodi:* 以及其他编辑器命名空间
删除注释
把文档里所有 XML 注释都去掉
清理命名空间
从根元素上移除用不到的命名空间声明
序列化
把清理干净的 XML 重新写成 SVG 文件
处理小结
会保留
- • 所有矢量图形(路径、形状、文字)
- • 样式和 CSS
- • 渐变和图案
- • 滤镜和特效
- • 嵌入的图片(通过引用)
- • viewBox 和尺寸
- • 标准的 SVG 命名空间
会清除
- • <metadata> 元素
- • <title> 元素
- • <desc> 元素
- • XML 注释
- • inkscape:* 属性
- • sodipodi:* 属性
- • Adobe/Sketch 命名空间
- • 用不到的命名空间声明
嵌入的位图
有个容易被忽视的坑:SVG 文件可以用 base64 data URI 的方式嵌入位图。 这些嵌进去的 JPEG 或 PNG,里面可能还藏着自己的 EXIF 元数据。
目前的局限
PicScrub 目前会清除 SVG 这一层的元数据,但不会解码和处理嵌入的位图:
<image href="data:image/jpeg;base64,/9j/4AAQ..." />
PicScrub 目前不会处理 SVG 文件里嵌入的位图。 如果想做到彻底,请把嵌入的图片提取出来单独处理。