☰
PowerShell提取Word文档字体清单:解包docx与XML解析实战
2026/10/6 9:01:28 网站建设 项目流程

很多做文档管理和办公自动化的人,都碰过这种需求:手头一份写好的 Word 文档,可能是要交付给客户的方案,也可能是团队内部沉淀已久的规范手册,突然需要把里面用到的所有字体列个清单。原因五花八门,也许是审计商业字体授权,也许是为了统一排版风格,也许只是交接的时候想搞清楚这份文档为什么在别人电脑上打开就乱。不管哪种情况,手工去翻 Word 的字体设置都是灾难级的体验——一份几十页的文档,光正文就可能混了七八种字体,更别说页眉、页脚、文本框里的隐藏字体。

这篇文章要讲的,就是用 PowerShell 把 Word 文档里的字体一次性全部挖出来。核心思路不是去操作 Word 软件本身,而是直接拆开 docx 文件这个“壳”,从底层 XML 里读取字体配置。这么做的好处非常明显:速度快、不依赖 Office 环境、还能批量处理几十上百个文档。适合谁看?IT 运维、文档管理岗、排版编辑、写自动化脚本的同学,以及任何需要定期做文档体检的人。我会把原理、完整脚本、常见坑和扩展玩法都讲清楚,看完你就能直接拿去用。

1. 这个需求到底在解决什么问题

1.1 一个字体清单背后的真实场景

先把问题说透。很多人觉得“列出文档里所有字体”是一件很小的事,但它在实际工作中对应的往往是正经业务需求。

最典型的是品牌合规检查。很多公司对对外交付的文档有严格模板要求:正文用什么字体、标题用什么字体、中文西文分别用哪款,都是规定死的。但实际写文档的人五花八门,有人用模板写到一半换了字体,有人直接复制了别的文档的内容,字体就被带进来了。这种情况下,你需要的是一份“这个文档实际用了哪些字体”的清单,拿去和模板规范对比,快速揪出不合规的字体。

第二种场景是字体授权审计。商业字体和个人免费字体的使用边界很严格,法务或采购部门需要知道公司对外发出的文档里有没有混入未授权的字体。这时候手工翻文档不现实,一个目录下几百份文档,必须用脚本批量扫描。

第三类是排版交接。设计师或者排版专员交接一个项目时,要告诉接手人这份文档依赖哪些字体,否则发到别的电脑上字体全部丢失、版式稀烂。有了字体清单,交接文档里附一张表就清清楚楚。

这些场景的核心需求其实就两条:第一要快,第二要全。“全”这个字很关键,因为字体不只存在于正文段落里,页眉页脚、脚注、文本框、图形里的文字都可能带字体设置。后面你会看到,只读主文档 XML 是远远不够的。

1.2 两条技术路线:操作 Word 还是拆解文件

面对这个需求,大致有两条技术路线可以走。

第一条是自动化操作 Word 软件本身,常见做法是用 COM 对象或者 VBA 宏。写一个循环,遍历文档里每个字符区域(Range),读出它的 Font.Name、Font.NameFarEast 这些属性。这条路的优势很明显:Word 自己在解析文件,逻辑简单直观,而且对老的 .doc 二进制格式也能支持。缺点是也很大:机器上必须装了 Office 才能跑,Word 启动速度慢,脚本跑起来像放慢了半拍,而且很多公司安全策略会拦 COM 自动化,弹各种权限提示。

第二条路线是绕过 Word,直接处理文件。docx 这个格式说起来是个文档,本质上就是个压缩包,里面全是 XML 文件。字体的配置信息就藏在 XML 的特定节点里,只要把压缩包解开、把 XML 读出来、把字体名称摘出来就行。这条路的好处是纯文件操作,秒级完成,不需要装 Office,批量处理非常稳。

我最终选的是第二条路,用 PowerShell 写了一套基于 XML 解析的脚本。下面我详细讲讲为什么这条路更划算,以及具体怎么落地。

2. 方案选型:为什么解包 docx 是更聪明的做法

2.1 先弄明白 docx 文件到底是什么

很多用 Word 的人不知道,docx 文件其实是个 ZIP 压缩包。你完全可以手动验证一下:把一个 docx 文件复制一份,把扩展名改成 .zip,双击打开,能看到里面有一堆文件夹和 XML 文件。这是 Office 2007 之后采用的 OOXML(Office Open XML)格式的标准设计。

这个压缩包里,最关键的是这么几个部分:

压缩包内部路径里面装的是什么和字体的关系
word/document.xml文档正文内容正文每个文字区域(run)的字体设置
word/styles.xml段落样式和字符样式的定义全局默认字体、各种样式里指定的字体
word/theme/theme1.xml文档主题主题字体(majorFont / minorFont),也就是默认方案
word/header1.xml、footer1.xml页眉页脚内容页眉页脚里文字的字体
word/footnotes.xml、endnotes.xml脚注、尾注内容脚注里文字的字体

一个纯文本段落里,字体信息的存放逻辑大概是这样的:文档里所有文字都在<w:r>(run)节点里,每一个 run 可以带一段<w:rPr>(run properties),里面用<w:rFonts>节点指定字体。如果一个 run 没写字体,就向上继承所在段落的样式;段落样式没写,就继承文档默认样式(通常是 Normal 样式);文档样式也没写,最终落到主题字体上。所以,想拿到“最终生效”的字体,最靠谱的做法是看每个 run 实际有没有标注,再补上样式和主题里的默认值。

2.2 三种具体实现方案的对比

具体到用 PowerShell 实现,又有三个细分方案,我把它们的优缺点摆出来对比一下。

方案 A:调用 Word COM 对象

$word = New-Object -ComObject Word.Application $word.Visible = $false $doc = $word.Documents.Open("C:\test.docx") $fontSet = @{} foreach ($range in $doc.StoryRanges) { # 遍历文档的多个 story,每个 story 里再逐段读取字体 } $doc.Close() $word.Quit()

这种方案能覆盖正文、页眉、页脚、脚注等所有 StoryRanges,而且对 .doc 老格式也有效。但它的问题刚才说了:依赖 Office 安装,速度慢,容易被安全软件拦,而且 COM 对象用完还得小心翼翼地释放,否则会残留 WINWORD.EXE 进程。如果你要在一台没装 Office 的服务器上跑字体审计,这条路直接堵死。

方案 B:使用 Open XML SDK

微软提供了 Open XML SDK,可以直接读 docx 里的强类型对象,不用自己写 XML 解析代码。在 PowerShell 里用也完全可行,引入文档的 DLL 就行。但它在 PowerShell 场景下有个尴尬的地方:依赖 .NET 版本和 SDK 的加载方式,部署起来比纯脚本麻烦。如果是在 C# 项目里做文档处理,我会毫不犹豫选 SDK,但在“临时写个脚本扫描一批文档”这种场景下,为它引入外部依赖显得笨重。

方案 C:直接用 PowerShell + .NET 的 ZipFile 和 XmlDocument

这是本文要重点讲的方案。docx 既然是 zip,那就用[System.IO.Compression.ZipFile]把它打开,取出里面的 XML 条目,再用[System.Xml.XmlDocument]加载并解析。它零外部依赖,Windows 自带的 PowerShell 就能跑,脚本复制到任何一台 Windows 机器上都能用。速度上,因为完全不做文字渲染,是三种方案里最快的。

出于“能少一个依赖就少一个依赖、能快一秒就快一秒”的原则,我最终选方案 C,而且实测下来非常稳。

2.3 动手前的环境准备

在开始写脚本之前,有两个小准备工作值得做一下。

第一是确认 PowerShell 版本。Windows 10 和 Windows 11 自带的 Windows PowerShell 5.1 完全够用,本文的代码也是按 5.1 写的。如果你用的是 PowerShell 7,代码同样兼容。

第二是检查脚本执行策略。如果你打算把脚本保存成 .ps1 文件再运行,可能会遇到系统提示“无法加载,因为在此系统上禁止运行脚本”。这是 PowerShell 的默认策略在保护你,可以用下面这行命令,把当前用户的执行策略设置为允许本地脚本运行:

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

解释一下这个参数:RemoteSigned的意思是,本地创建的脚本允许运行,从网上下载的脚本如果没有数字签名就必须先人工确认。这是比较稳妥的一个折中方案,不建议直接设成Unrestricted。

另外建议:测试的时候先复制一份 docx 文件做实验,虽然本文的脚本只读取不修改文件,但养成操作前留备份的习惯,后面玩 COM 或者文件转换之类的操作时能少很多麻烦。

3. 核心代码与完整实现

3.1 最简版脚本:先把正文里的字体读出来

我先给一个最简版本的脚本,目的是把大致的流程跑通。这个版本只读取主文档word/document.xml,收集所有 run 里显式声明的字体,去重后输出。

Add-Type -AssemblyName System.IO.Compression.FileSystem param( [string]$DocxPath = "C:\test.docx" ) # 1. 以只读方式打开 docx 压缩包 $zip = [System.IO.Compression.ZipFile]::OpenRead($DocxPath) try { # 2. 定位主文档条目 $entry = $zip.GetEntry("word/document.xml") if (-not $entry) { throw "没有找到 word/document.xml,请确认这是一个有效的 docx 文件。" } # 3. 读取 XML 内容为字符串 $reader = New-Object System.IO.StreamReader($entry.Open()) $xmlContent = $reader.ReadToEnd() $reader.Close() # 4. 加载到 XmlDocument $xml = New-Object System.Xml.XmlDocument $xml.LoadXml($xmlContent) # 5. 注册命名空间。docx 的 XML 默认命名空间是固定的 $nsMgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable) $nsMgr.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main") # 6. 选取所有 rFonts 节点 $nodes = $xml.SelectNodes("//w:rPr/w:rFonts", $nsMgr) # 7. 收集所有字体属性 $fontSet = @{} foreach ($node in $nodes) { $attrs = @("w:ascii", "w:hAnsi", "w:eastAsia", "w:cs") foreach ($attr in $attrs) { $fontName = $node.GetAttribute($attr, "http://schemas.openxmlformats.org/wordprocessingml/2006/main") if ($fontName) { $fontSet[$fontName] = $true } } } # 8. 输出结果 Write-Host "文档中检测到的字体清单:" $fontSet.Keys | Sort-Object | ForEach-Object { Write-Host $_ } } finally { $zip.Dispose() }

这段脚本的流程不复杂,但有几个点值得单独拿出来说。

为什么用ZipFile.OpenRead而不是ZipFile.ExtractToDirectory?因为后者会把文件解压到磁盘,扫描完还得清理临时目录,而OpenRead是在内存里直接读取条目内容,不落盘、不污染目录,尤其批量处理几十个文件时干净利落。

为什么要在finally里Dispose?zip 句柄如果一直不释放,文件会被占用,后续想移动或者覆盖这个文件就会报错。PowerShell 脚本里写 try/finally 的另一个好处是,前面抛异常了,后面也能保证释放资源。这个习惯值得养成。

为什么GetAttribute时要带上第三个参数?XML 里的属性名带了w:前缀,它只是命名空间别名,真正的属性名是{http://schemas.openxmlformats.org/wordprocessingml/2006/main}ascii。用GetAttribute("w:ascii")这种写法在 .NET 的 XmlDocument 里是查不到东西的,必须显式指定命名空间 URI。初学者在这里踩坑最多,我后面还会详细讲。

跑完这个脚本,你会得到一串字体名。但如果是做正经审计,这个版本还远远不够,因为字体还藏在样式、页眉页脚和主题里。下面继续补全。

3.2 理解 w:rFonts 的四个属性:西文、中文、复杂文种

很多人不知道,Word 里一个字符的字体是“按字符类型区分”的,而不是全文统一用一种字体。具体到 XML 层面,<w:rFonts>节点里常见的有四个属性,它们各管一摊:

属性名管什么字符典型情况
w:asciiASCII 字符,也就是基础拉丁字母、数字、半角标点“Hello” 这种西文字符
w:hAnsiHigh ANSI 字符,扩展西文字符带重音的法语、德语字母
w:eastAsia东亚字符中文、日文、韩文
w:csComplex Script 复杂文种字符阿拉伯文、希伯来文、印地文

举个例子。在中文文档里,一个 run 的字体设置完全可能出现这种情况:w:ascii="Calibri"表示西文字母和数字用 Calibri,w:eastAsia="宋体"表示中文文字用宋体。这俩不冲突,各管各的。

所以你在收集字体时,必须把这四个属性全部读出来,合并成集合,才能得到“这份文档究竟用了哪些字体”的完整答案。如果只读w:ascii,中文文档里的中文字体全都会被漏掉;只读w:eastAsia,西文和数字又会漏掉。前面脚本里我用一个数组循环四个属性,就是这个意思。

除此之外还有一个隐藏情况:Word 的字体设置里其实有两种赋值方式,一种是直接写具体字体名(w:ascii="Arial"),另一种是用主题字体引用(w:asciiTheme="majorHAnsi"、w:eastAsiaTheme="minorEastAsia")。前者好办,直接拿名字;后者不会出现在w:rFonts的属性值里,你得继续去word/theme/theme1.xml里查主题字体方案,找到majorFont和minorFont下对应的具体字体名。如果你的审计目标是“和模板规范对比”,这层解析就必须做。

3.3 增强版:把样式、页眉页脚、脚注全部纳入审计

下面这个增强版,是我实际用的版本。它不再只看document.xml,而是把压缩包里跟文字相关的 XML 条目全都扫一遍,包括:

  • word/document.xml:正文
  • word/styles.xml:样式定义,里面藏着 Normal 默认字体等
  • word/header*.xml和word/footer*.xml:页眉页脚,用通配符匹配
  • word/footnotes.xml和word/endnotes.xml:脚注尾注
  • word/theme/theme1.xml:主题字体

对于样式和主题这两个特殊文件,不能简单复用正文的遍历逻辑,要单独处理。样式文件里的字体,大多数情况下是作为某个样式的默认配置存在,它不直接决定文字显示,但会影响“没有显式设置字体”的那部分内容。主题文件里则是<a:fontScheme>节点。

Add-Type -AssemblyName System.IO.Compression.FileSystem function Get-DocxFontList { param( [string]$DocxPath ) $zip = [System.IO.Compression.ZipFile]::OpenRead($DocxPath) $fontSet = @{} try { $nsW = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" $nsA = "http://schemas.openxmlformats.org/drawingml/2006/main" # 需要检查的 XML 条目列表 $entryNames = @( "word/document.xml", "word/styles.xml", "word/footnotes.xml", "word/endnotes.xml" ) + ($zip.Entries | Where-Object { $_.FullName -match "word/(header|footer)\d+\.xml" } | ForEach-Object { $_.FullName }) foreach ($entryName in $entryNames) { $entry = $zip.GetEntry($entryName) if (-not $entry) { continue } $reader = New-Object System.IO.StreamReader($entry.Open()) $xmlContent = $reader.ReadToEnd() $reader.Close() $xml = New-Object System.Xml.XmlDocument $xml.LoadXml($xmlContent) $nsMgr = New-Object System.Xml.XmlNamespaceManager($xml.NameTable) $nsMgr.AddNamespace("w", $nsW) # 遍历所有 rFonts 节点 $nodes = $xml.SelectNodes("//w:rPr/w:rFonts", $nsMgr) foreach ($node in $nodes) { $attrs = @("w:ascii", "w:hAnsi", "w:eastAsia", "w:cs") foreach ($attr in $attrs) { $fontName = $node.GetAttribute($attr, $nsW) if ($fontName) { $fontSet[$fontName] = $true } } } # styles.xml 里可能还有 docDefaults 下的 rPrDefault 默认字体 if ($entryName -eq "word/styles.xml") { $defaultNodes = $xml.SelectNodes("//w:docDefaults/w:rPrDefault/w:rPr/w:rFonts", $nsMgr) foreach ($node in $defaultNodes) { $attrs = @("w:ascii", "w:hAnsi", "w:eastAsia", "w:cs") foreach ($attr in $attrs) { $fontName = $node.GetAttribute($attr, $nsW) if ($fontName) { $fontSet[$fontName] = $true } } } } } # 主题字体 $themeEntry = $zip.GetEntry("word/theme/theme1.xml") if ($themeEntry) { $reader = New-Object System.IO.StreamReader($themeEntry.Open()) $themeXmlContent = $reader.ReadToEnd() $reader.Close() $themeXml = New-Object System.Xml.XmlDocument $themeXml.LoadXml($themeXmlContent) $nsMgrTheme = New-Object System.Xml.XmlNamespaceManager($themeXml.NameTable) $nsMgrTheme.AddNamespace("a", $nsA) $fontNodes = $themeXml.SelectNodes("//a:fontScheme/a:majorFont/a:latin/@typeface | //a:fontScheme/a:majorFont/a:ea/@typeface | //a:fontScheme/a:minorFont/a:latin/@typeface | //a:fontScheme/a:minorFont/a:ea/@typeface", $nsMgrTheme) foreach ($node in $fontNodes) { if ($node.Value) { $fontSet[$node.Value] = $true } } } } finally { $zip.Dispose() } return $fontSet.Keys | Sort-Object } # 使用示例 Get-DocxFontList -DocxPath "C:\test.docx"

这个版本已经可以覆盖绝大多数真实文档了。注意一个细节:ZIP条目里是否包含word/header1.xml取决于文档有没有页眉页脚,所以匹配时我用Where-Object先找出所有符合word/header*.xml或word/footer*.xml模式的条目。Word 有时候会生成header2.xml、header3.xml,所以不能只写死header1.xml和footer1.xml。

3.4 结果输出:控制台、CSV、去重统计

脚本拿到字体集合之后,怎么输出也很讲究。

最简单的自然是控制台直接打印,适合临时看一眼,但做正经审计肯定要把结果存下来。我最常用的两种方式:

输出 CSV 报告

如果你批量扫描多份文档,想知道“每份文档分别用了哪些字体”,可以给函数加一个参数,返回对象数组而不是纯字符串。输出 CSV 的示例:

$reports = @() Get-ChildItem "D:\docs" -Filter *.docx | ForEach-Object { $fonts = Get-DocxFontList -DocxPath $_.FullName foreach ($font in $fonts) { $reports += [PSCustomObject]@{ FileName = $_.Name FontName = $font } } } $reports | Export-Csv -Path "D:\font-audit.csv" -NoTypeInformation -Encoding UTF8

注意在 Windows PowerShell 5.1 里,Export-Csv的-Encoding UTF8会生成带 BOM 的文件,Excel 打开没有乱码问题,直接双击就能用。

按出现频次排序

可以统计每个字体在文档中出现的次数(即 rFonts 节点引用的次数),这样能快速判断哪些是主体字体、哪些是偶然冒出来的杂字体:

$fontCount = @{} foreach ($node in $nodes) { $fontName = $node.GetAttribute("w:ascii", $nsW) if ($fontName) { if ($fontCount.ContainsKey($fontName)) { $fontCount[$fontName]++ } else { $fontCount[$fontName] = 1 } } } $fontCount.GetEnumerator() | Sort-Object Value -Descending | Format-Table -AutoSize

这种统计方式特别适合品牌合规检查:表格里排前面的是正文主力字体,排末尾、出现次数为 1 的,大概率是某段复制进来的“漏网之鱼”。

4. 实操过程中最常见的坑与排查技巧

4.1 问题速查表

我在给不同团队做文档处理工具时,遇到的坑翻来覆去就那么几个。这里整理成表格,方便你直接对照排查。

现象根本原因解决办法
运行 .ps1 报错“禁止运行脚本”PowerShell 执行策略限制Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
脚本打开文件报“文件被占用”之前用 COM 或编辑器打开过 Word 没退干净打开任务管理器结束 WINWORD.EXE 进程,或用[System.IO.Compression.ZipFile]::OpenRead只读方式
提示找不到ZipFile类型没加载程序集在脚本开头执行Add-Type -AssemblyName System.IO.Compression.FileSystem
SelectNodes返回空结果XML 命名空间没注册,或者路径写错确认AddNamespace("w", "...")已执行,XPath 路径以//w:rPr/w:rFonts这种形式书写
GetAttribute拿到空字符串属性名带w:前缀,但没传命名空间 URIGetAttribute("w:ascii", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")
中文输出乱码PowerShell 5.1 控制台默认编码问题输出到文件时指定-Encoding UTF8,或在脚本开头设置[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
扫描结果缺少中文字体只读了ascii属性把eastAsia也加入读取范围
扫描结果缺少默认字体文档正文没有显式字体声明,字体都继承自样式和主题必须额外扫描 styles.xml 和 theme1.xml
解压时提示文件损坏文件本身不是 docx(也许改了扩展名)用[System.IO.Compression.ZipFile]::OpenRead前先检查文件头,或用 7-Zip 手动打开验证

4.2 老版本 .doc 文件怎么处理

如果你的文档是.doc老格式,本文这套脚本是不管用的。原因很简单:.doc不是 OOXML 格式,不支持解压出 XML,它是个 OLE 复合文档,字体信息藏在二进制流里,用纯 PowerShell 解析的难度和收益不成正比。

这时候最实际的办法有两个。

如果机器上装了 Office,可以用 Word COM 把它转成 docx,再用本文的脚本扫描:

$word = New-Object -ComObject Word.Application $word.Visible = $false $doc = $word.Documents.Open("C:\old.doc") $doc.SaveAs2("C:\old_converted.docx", 16) # 16 代表 wdFormatDocumentDefault,即 docx $doc.Close() $word.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

这里有个细节:用完 COM 对象要记得释放,ReleaseComObject不能省,不然任务管理器里会残留WINWORD.EXE。如果机器没装 Office,也可以用 LibreOffice 的命令行做无头转换,但这就额外引入工具了,一般只在服务器批量转换时值得折腾。

4.3 批量扫描整个目录

把这篇文章开头那个脚本和Get-ChildItem一组合,就能做整目录扫描。这个操作我做得最多的是“检查指定目录下有没有文档用了非标准字体”。

# 设置模板允许的字体白名单 $allowedFonts = @("微软雅黑", "宋体", "Calibri", "Times New Roman") Get-ChildItem "D:\ProjectDocs" -Recurse -Filter *.docx | ForEach-Object { $fonts = Get-DocxFontList -DocxPath $_.FullName $badFonts = $fonts | Where-Object { $_ -notin $allowedFonts } if ($badFonts) { Write-Host "文档 $($_.Name) 存在非模板字体:" -ForegroundColor Yellow $badFonts | ForEach-Object { Write-Host " - $_" -ForegroundColor Red } } }

这个用法非常适合在上线交付前跑一遍。实际工作中我见过太多“文档发出去,客户打开字体全变”的案例,基本都是文档里混了目标机器上没有的字体。提前脚本审计一遍,能规避很多低级事故。

4.4 一种特殊情况的处理技巧

还有一种特殊情况值得说一句:处理从 WPS 或某些在线文档工具导出的 docx 时,XML 命名空间可能不完全一样。大多数兼容 OOXML 规范的工具都会沿用那套标准命名空间,但有极少数工具生成的文档里,命名空间 URI 略有差异,或者节点结构变成了w:r/w:rPr/w:rFonts但中间还夹着其他节点。遇到SelectNodes查不到结果的情况,先别怀疑代码,打开 Visual Studio Code 或者 Notepad++,把 docx 解压了直接看 XML 原貌,比盲调强得多。这算是我踩了好几次坑之后总结出来的经验:写解析类脚本,多看原始 XML 比多想代码逻辑重要。

5. 扩展思路与一些实在的经验

5.1 实测心得:什么时候用脚本,什么时候别用

这套脚本我用下来,最舒服的场景是处理“批量、结构清晰、只需要结果”的文档。比如一次扫描 50 份合同模板、每份 30 页,脚本五秒钟跑完,输出一张 CSV,任务结束。这种情况让手工去翻,腿都能跑断。

但有一类场景我不建议强行用脚本——文档里有大量复杂排版、艺术字、嵌入的 OLE 对象、图片里的文字。这类元素的字体设置在 OOXML 里分布得很零散,有的藏在word/embeddings里,有的藏在 DrawingML 的文本框里,还有的可能以图片形式存着,字体信息永远挖不完。这种文档倒不是不能扫,但你得对结果保持清醒:脚本列出来的字只是“XML 里能查到的字”,不代表视觉上出现的每个字。真要严格审计,这种文档得打开 Word,用查找替换功能里的字体筛选逐个确认,这个手感是脚本替代不了的。

5.2 一个特别实用的扩展场景:跨文档比对

脚本最有价值的用法之一,不是单看一份文档,而是盯着一个目录的文档变化。我见过一种玩法是把字体清单脚本和文件哈希结合:每天扫描一次目录下所有 docx 的字体集合,生成当日基线。某天有同事改了模板、引入了新字体,脚本能第一时间发现差异,截图发到工作群让排版负责人确认。这种用脚本做“文档健康巡检”的思路,比临时抱佛脚的检查强很多。

如果你觉得定时任务太复杂,那至少可以把扫描结果输出成一个 Markdown 表格,每次交付文档前手动跑一遍,把字体清单粘贴到交付说明末尾。客户和技术支持看到这张表,很多关于“为什么我打开字体不一样”的工单直接就不用提了。

5.3 扩展方向:从“字体清单”到“字体替换”

最后补充一个常见的进阶需求:拿到了字体清单之后,很多人下一步会问——能不能批量把文档里所有字体替换成另一个字体?比如把所有“宋体”换成“思源宋体”。这个需求用脚本做起来比列字体要麻烦很多,因为你要修改 docx 里的 XML 并把文件重新打包回去,而且在 run 级修改字体时还要注意别破坏了原有的样式继承。我的建议是如果只是为了统一视觉,改模板和样式比全文档替换更稳妥;如果实在要替换,也别直接在原文件上改,用脚本生成一个新文件,人工预览确认之后再使用。别问我是怎么知道要这么做的——有次我在线上直接改了原文档,一次误替换,把标题字体全搞乱了,还得从备份里找回来。

字体这件事,看起来小,实际影响用户的直观感受和文档的规范性。希望这套脚本能帮你少加班,多留点时间做真正有价值的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询