用邮件合并为 Word 文档批量配图

2019-10-27

引言

在 Word 文档中插入图片是再日常不过的操作之一。然而,如果你有过批量向 Word 中插入图片的经历,那大概不会是什么令人愉快的体验——不仅要考虑粘贴格式、文字环绕等千奇百怪的格式问题,还要应对 Word 顽疾一般的卡顿和崩溃。这些软件缺陷经常能让加图片这类本不费工夫的操作变成一种煎熬。

设想这样的工作场景:你的老板准备对三国公司进行投资,但在此之前想对它的三个子公司——魏国公司、蜀国公司和吴国公司——进行调查,看它们是否在几家主流假新闻媒体——《扭腰时报》、《花儿街日报》和蓬勃新闻——上有过负面报道。为此,你被要求在上述媒体的网站上搜索三家公司的相关报道,并将搜索结果截图,分类汇总到一份报告中。

上面的任务只涉及三家公司和三个网站,加起来也不过需要创建 3 个文档,插入 9 次图片。但如果需要调查的是 30 家公司和 30 个网站呢?逐一手工创建文档并插入图片似乎就不是明智的做法了。且不论操作繁琐和浪费时间,忙中出错的机率也会大大增加。

有没有什么更省事的方法——比如,能不能只提供图片素材和文档模板,剩下的配图工作让机器自动完成?答案是肯定的,方法就是借助 Word 内建的自动化工具——邮件合并

这个答案听起来可能让人有些摸不着头脑——发邮件和插入图片有什么关系?实际上,邮件合并是一个「名不副实」的功能。它的作用是以一个模板为基础,批量将用户提供的信息填入留空位置。之所以被称作「邮件合并」,是因为其最主要的使用场景之一就是制作群发邮件——内容相同、只有称呼等部分因收件人而异。

但邮件合并功能的潜力并不止于填写收件人;它能填写任何文本变量。另一方面,Word 文档中的图片不仅可以直接嵌入在文件中,也可以是指向外部文件(包括本地和远程)的链接。而既然链接的本质是文本,我们就可以通过插入文本的方式插入链接,进而实现插入图片。这样,邮件合并和插入图片间的桥梁就被打通了。后文就以上面设想的工作场景为例,说明如何使用邮件合并功能批量插入图片。

一、准备工作

在正式使用邮件合并前,需要先做一些准备工作。

  1. 按一定的命名规律收集资源文件。 这实际上应该是任何资料收集工作都应遵循的原则,只是对于自动化操作来说格外重要。例如,在上面的情境中,我们可以将针对魏国公司的三张截图分别命名为 1-NYT.png1-WSJ.png1-BBG.png,而将针对另外两家公司的截图各自以 2-3- 为前缀。在此过程中,还可以使用 ShareX 这类具有一定编程能力的截图工具进一步简便操作,但这不是本文重点,在此不赘。

  2. 准备存放待填充信息的 Excel 表格。 尽管邮件合并的信息输入可以直接在 Word 中进行,但对于涉及多个变量、条目较多的情况,使用独立的文件作为数据源显然更加可靠、更便于后续修改。其中,表格的第一行是变量的名称,可以自己任取。还是以上述情境为例,涉及的变量包括公司的名称(不妨称作 corpName)和三张截图的文件名(不妨称作 pathNYTpathWSJpathBBG)。之后的各行则依次填入实际信息。注意 Excel 的自动填充功能可以极大加快填写进度。例如,填写第一张截图的名称 1-NYT(扩展名 .png 是始终不变的,故无需作为变量的一部分)后,按住单元格右下角向下拖动,即可将同列单元格快速填好。

  3. 最后,将上述所有图片资源文件和 Excel 表格放在同一个项目文件夹下(不妨称作 fakeNews),就完成了全部的准备工作。

二、建立邮件合并模板

任何邮件合并操作都是以建立模板为起点的。为此,我们在项目文件夹下新建一个 Word 文档(不妨称为 fakeNews.docx),在其中填好最终所需成品中共同的部分,并将变化的部分留空。

接着,切换到「邮件」选项卡,选择「开始邮件合并」>「信件」,启动邮件合并功能。然后,点击「选择收件人」>「使用已有列表」,在弹出的对话框中选择之前准备好的 Excel 表格。

这时,在「插入合并域」的下拉菜单中,就可以看到我们在表格第一行填写的变量名称了。

我们首先填写最简单的一个变量。将光标移动到模板标题的「负面报道」字样之前,然后在「插入合并域」的下拉菜单中点选存放公司名称的变量 corpName。这时,如果你选中工具栏中的「预览结果」选项,就可以通过右边的箭头按钮实时预览填充效果。

接下来的重点就是插入图片。上面提到,Word 中的图片可以通过链接的形式插入。用 Office 的术语来说,就是插入一个指向外部图片的(field)。

什么是域?这个名字听起来很玄乎,但所谓的「域」就是一个占位符,它的作用是告诉 Word「显示的时候把我换成别的东西」。例如,上一步插入的公司名称变量,本质也是一个域。我们可以通过按下 Option-F9 (macOS) / Alt-F9 (Windows) 让它现出原形:

{ MERGEFIELD corpName }

这就是 Office 中域代码的一般形式:大写字母标示的域类型,后接参数指定要显示的内容,最外部由一对花括号包裹。在上面的域代码中,MERGEFIELD 域告诉 Word:「把我换成数据源中 corpName 变量的内容」。

回到我们的问题,图片的插入是由 INCLUDEPICTURE 域实现的。它的语法是:

{ INCLUDEPICTURE [开关] "文件名" }

其中,开关(switch) 是可写可不写的控制选项,位置也可以挪到代码最后。就 INCLUDEPICTURE 域而言,最常用的开关是 \d,其作用是避免把引用的图片文件嵌入到文档中。考虑到处于制作初期,图片资源可能还需要更新,同时出于节约空间和提高性能的考虑,建议使用这个选项。(后文会提及如何在定稿后嵌入图片的问题。)这样,如果要引用在《扭腰时报》网站上搜索魏国公司的截图,相应的域代码是:

{ INCLUDEPICTURE \d "1-NYT.png" }

延伸说明:关于域代码的输入

Word 中插入域的「官方」途径是使用「插入」选项卡下的「域」按钮。但这种方法不够快捷,也不能满足很多复杂的需求;手工输入域代码大多时候是更好的方式。不过,Word 并不会识别直接输入或从别处粘贴的域代码;要将文本形式的代码转换为实际起作用的域,需要按照如下步骤操作:

  1. 通过 Option-F9 (macOS) / Alt-F9 (Windows) 组合键将显示模式切换为显示域代码本身。
  2. 输入(或粘贴)所需域代码中不包含最外侧花括号的部分。
  3. 选中上述不带外侧括号的代码,注意一定不要选中行尾的换行符。如果不小心选多了,可以按 Shift-← 取消选择最后一个字符。
  4. 按组合键 Command-F9 (macOS) / Ctrl-F9 (Windows),Word 会自动在选中区域两侧加上花括号并将其转化为域代码。

域代码的另一项重要特性是可以相互嵌套。因此,既然邮件合并的变量本质上也是一个域,就可以一步到位地写出插入任何一家公司《扭腰时报》截图的代码:

{ INCLUDEPICTURE \d "{ MERGEFIELD pathNYT }.png" }

请注意,手工输入带有嵌套的域代码时,需要按照前段所述方法,自内而外地将各部分代码转化为实际起作用的域(如本例中,就应先处理内层的 MERGEFIELD 域,再处理外层的 INCLUDEPICTURE 域)。当然,你仍然可以通过「插入合并域」下拉菜单直接插入 pathNYT 变量,其效果是相同的。


延伸说明:关于 MERGEFORMAT 开关

如果你观察通过「插入域」对话框所插入的域的代码,会发现代码最后往往带了一个 \* MERGEFORMAT 的尾巴。这里的 MERGEFORMAT 也是一个开关,其作用是在域的内容更新时,维持之前内容的格式。

但这很多时候并不是我们预期的效果,在用域插入图片时尤其如此。例如,你先用 INCLUDEPICTURE 域引用了一张尺寸为 1920×1080 像素的截图,但后来用另一张尺寸为 1920×1200 像素的新截图覆盖了原来的文件。如果你的域代码中包含了 \* MERGEFORMAT,那么在刷新文档后就会发现新的截图被「压扁」了,原因正是 MERGEFORMAT 开关保留了原来图片的 16:9 宽高比,而不是自动适应到新图片的 16:10。因此,除非的确需要 MERGEFORMAT 的格式维持功能,否则一般无需在域代码中加上这个开关。


最后,我们依次输入并处理用于插入其余两个网站截图的域代码;完成后的效果如下图所示:

三、生成成品文档

舞台搭好以后,邮件合并功能终于可以开始它的表演了。

首先,再次按下 Option-F9 (macOS) / Alt-F9 (Windows),关闭显示域代码的模式。然后,点击「邮件」选项卡下的「完成并合并」>「编辑单个文档」,在弹出的对话框中选择「确定」。

Word 会自动新建一个文档,其中合并了针对每家公司的内容。可以看到,标题的位置已经被更换为各个公司的名称,但截图的位置仍然是空白。这并不是因为操作有误,而是因为我们插入的域还没有更新

为此,先将文件另存到图片文件所在文件夹(本例中即之前创建的 fakeNews 文件夹),然后按 Command-A (macOS) / Ctrl-A (Windows) 全选,再按 F9 更新选中部分的域。

稍等片刻,就可以看到图片纷纷显示出来了。(如果你事后检查时发现某张图片需要更换,只需直接在原始图片的基础上修改,然后回到 Word 中选中相应图片,再按一次 F9,就可以将变动体现出来。)


延伸说明:关于相对路径

出于简化演示的考虑,上述步骤将文档和图片资源平级地放在同一文件夹下,以便在域代码的路径部分直接通过文件名引用图片。但这未必是最佳实践方式;更多时候,我们倾向于将图片等外部资源进一步整理在项目文件夹的子文件夹,例如 ./res./img 等中。

这就会涉及路径格式的问题。最省事的做法当然是直接用绝对路径;考虑到图片文件最终会被嵌入到文件内部,在准备步骤中使用绝对路径并不影响最终成品的可交换性。使用时,注意将路径中的分隔符同一为域代码可识别的双反斜杠(\\),斜杠(/)或冒号(:)即可。

但绝对路径毕竟看起来有些蠢笨,用起来也不够灵活。能否在域代码中使用相对路径呢?答案是肯定的。注意到域代码 { FILENAME \p } 会返回当前文档的完整路径,因此,域代码 { FILENAME \p }\\..\\ 返回的就是当前文件夹的完整路径。于是,路径

{ FILENAME \p }\\..\\Child\\image.png

指向当前文件夹的名为 Child子文件夹下名为 image.png 的图片文件。

类似地,路径

{ FILENAME \p }\\..\\..\\scan.pdf

指向当前文件夹的上一级文件夹下名为 scan.pdf 的 PDF 文档。

据此,在上面的例子中,如果其他安排不变,只是所有图片素材存放在主文档所在文件夹下名为 res 的子文件夹中,那么插入《扭腰时报》网站截图的代码应改为:

{ INCLUDEPICTURE \d "{ FILENAME \p }\\..\\res\\{ MERGEFIELD pathNYT }.png" }

不过,这份文件现在还不能直接发给别人。如果你充分理解了上面步骤的原理,就会意识到目前所插入的图片并没有嵌入在文档中,而只是以链接的形式从文档外部调用的。因此,如果换一台电脑打开这份文件,图片就会因为那台电脑上没有相应的原件而不能显示。

因此,解决的方法就是断开链接,让图片直接存储在文档内部。首先确保文件内容无误、图片均能正常显示。然后,对于 Windows 版 Word,点击「文件」>「信息」,然后在右下角的「相关文档」 下, 单击「编辑文件链接」;对于 Word for Mac,点击「编辑」>「链接…」。在弹出的对话框中,选中所有图片链接,然后单击「断开链接」。这样,以链接形式插入的图片就被固化在文档内部了。

如果你观察这步操作前后的文件属性,会发现其占用空间有明显的增加,多出的部分正是图片资源所占用的;这也是为什么我们在之前编辑域代码时,建议暂不使用 \d 选项过早嵌入图片的原因。

这就是我们需要的成品文档。


延伸说明:让邮件合并输出多个文件

尽管上面的步骤已经达到了我们的目的,但还有一处不足:邮件合并的结果是一个整合版,而不是若干独立的文档。能不能让邮件合并为每条记录生成一个单独的文件呢?很遗憾,Word 并没有提供这样的选项。

当然,解决办法还是有的,那就是通过 Word 内建的编程工具——宏(Marco)。思路是:通过邮件合并生成的文档会用分节符(section mark)隔开为每条记录输出的部分。因此,只要通过查找文档中的分节符识别出针对各条记录的部分,然后将其内容创建为新文档并保存,就实现了拆分的目的。比较讽刺的是,微软显然是知道这个功能缺失的,甚至曾经专门编写过一篇知识库文章(原文已撤下,链接为网页快照)提供了反映上述思路的代码。可惜,这段针对 Word 97 编写的代码已经不适用于现在的版本了。

在民间自创的解决方案中,最完善的要数 Graham Mayor 制作的「Merge and Split」加载项。下载解压后,Windows 用户可以直接双击其中 exe 格式的自解压安装包,加载项会被自动安装到 Word 的启动目录。Mac 用户则需要自行将其中 dotm 格式的加载项源文件复制到 Word 的启动目录(~/Library/Group Containers/UBF8T346G9.Office/User Content/Startup/Word)。重启 Word 后,就可以看到「邮件」选项卡下多出了一个功能区,点击其中的「Merge & Split」按钮即可启动邮件合并、且将结果保存为独立的文件。如果你之前已经通过邮件合并功能生成了整合版的结果,也可以点击「Split Merged Letter」按钮将其拆分。


总结

本文的写作背景是我在工作中遇到的实际任务。在那些任务中,最终需要生成的文件从几个到十几个不等,每份文件包含的图片有二十多张。这样的工作量尽管咬咬牙也不是不能接受,但作为一个被 AppleScript/Shortcuts 这些自动化工具惯坏的人,还是不可避免地打起了自动化的歪脑筋,翻出 Office 的文档开始折腾起来,没想到最后确实天无绝人之路。

邮件合并能做的也不只是插入文本和图片。由于它可以插入任意文本,任何可以通过域代码插入的内容,都可以借助邮件合并实现批量添加。这包括了超链接(籍由 HYPERLINK)、外部文件(籍由 LINK)、引注(籍由 CITATION)等。再加上 IF= (Formula) 等可进行简单逻辑和公式运算的域,可以挖掘的自动化潜力几乎是无限制的。

不过,正如任何自动化/编程操作一样,使用邮件合并功能时,也应该对其快捷程度抱有合理的预期。因为,如果工作涉及的文件数量相对较少(例如上文示例中的情形),那么直接手动完成也要不了多久;即使工作量真的很大,考虑到因为不熟练或操作失误而耽误的调试时间,最终花费的时间总和也未必更少。

我尝试使用邮件合并完成工作任务的过程正是如此:因为之前对 Word 的自动化功能几乎一无所知,最开始的操作可谓处处受阻;上文步骤描述中的每一段,几乎都对应着我遇到的一个坑。结果,尽管最后一步到位的感受的确很爽,但最后并没有比手工完成节约太多时间。

但这并不是要否认自动化的价值。毕竟,自动化的意义不仅在于节约时间,还在于减少错误。通过将需要人工介入的步骤尽可能减少(只剩下准备文档素材和模版),邮件合并回避了复制粘贴、格式调整这些最容易出错的环节,保证了输出结果都是「一个模子里倒出来的」。如果后期发现素材或模板需要调整,要做的也无非是重新生成一遍,而不需要扎进文件堆里逐个修改替换。

更重要的是,哪怕没能节省时间,自动化也让枯燥的工作变得不那么枯燥——至少,你能感受到自己是在思考,而不是仅仅在搬砖。