在命令行中批处理 pdf

需求:近期有一批图由于图例太小,需要重新绘制,但只有 pdf 文件

解决方案:用 PPT 画图例,再通过 PDF-XChange 将新图例覆盖上去。

问题来了:LaTeX 编译时无法显示我覆盖的图片。于是推测直接写入的图片是注释的形式,而 LaTeX 编译时会忽略这部分。

测试:将带注释的 pdf 重新打印后,LaTeX 编译得到了想要的效果。所以最后的解决方案是将 pdf 全部另存一遍就好了。需要另存的 pdf 数量约 90 个,一个个操作繁琐难当。下面主要记录这次 pdf 批处理的坑爹过程。

imagemagick

首先想到的是 imagemagick。但是直接运行命令

1
magick convert input.pdf output.pdf

但是提示 convert: PDFDelegateFailed \`系统找不到指定的文件。按照 这里 的说法,是因为我计算机中的 64 位 imagemagick 和 32 位的 gs 不兼容。但几经尝试仍无法解决,遂放弃这个方案。

mupdf

mupdf 是在查找解决方法的时候找到的新工具,处理 pdf 更加得心应手,查看 官方文档 ,最简单用法

1
mutool convert [options] -o output input [pages]

对于 pdf 另存,最简单的即以下命令(output 参数放在前面🤣)

1
mutool convert -o output.pdf input.pdf

接着借助批处理命令进行批量转换,开启了新手接触批处理语法的踩坑之路。

1
2
3
4
5
6
7
8
9
10
rem 关闭输出
@echo off
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem 使用 mutool 进行转换
mutool convert -o a%%~nib.pdf %%i
)
pause

rem 表示注释,与 :: 相同。 mutool convert -o %%i %%i 输出文件与输入文件同名,直接覆盖掉源文件,需要前缀后缀直接在 %%~ni 追加,例如 mutool convert -o a%%~nib.pdf %%i%%~ni 表示文件名(无后缀)。

至此完成任务

pdf→png→jpg→pdf

imagemagick 没用上总感觉有些可惜。终于在查看 mupdf 文档的时候发现 mupdf 不支持转成 jpg 格式。

The following single page image formats are also supported: PNG, PNM, PAM, PBM, PKM. Each page is written to a separate file.

直接试一下 pdf to png to jpg to pdf,然后再删掉 png 和 jpg

第一步 pdf 转 png: mupdf

1
2
3
4
5
6
7
8
9
10
rem 关闭输出
@echo off
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem 使用 mutool pdf 转 png
mutool convert -o %%~ni.png %%i
)
pause

使用 mutool convert -o output.png input.pdf 得到的 png 文件文件名并非 output.png,而是 output1.png,1 是对应 pdf 的页码。我的 pdf 都只有一页,在循环中,使用 %%~ni1 就可以匹配文件名。

第二步 png 转 jpg: imagemagick

1
2
3
4
5
6
7
8
9
10
11
12
rem 关闭输出
@echo off
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem 使用 mutool pdf 转 png
mutool convert -o %%~ni.png %%i
+ rem 使用 imagemagick png 转 jpg
+ magick convert %%~ni1.png %%~ni.jpg
)
pause

第三步 jpg 转 pdf,覆盖原 pdf 文件,完成后删除 png jpg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
rem 关闭输出
@echo off
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem mutool pdf 转 png
mutool convert -o %%~ni.png %%i
rem imagemagick png 转 jpg
magick convert %%~ni1.png %%~ni.jpg
+ rem imagemagick jpg 转 pdf
+ magick convert %%~ni.jpg %%i
+ rem 删除 png jpg
+ del %%~ni1.png %%~ni.jpg
)
pause
完成

伪😏 第四步 当我把 mutools 转换得到文件名储存在变量中的时候 temp=%%~ni1,在 for 里面调用 %temp%.png 出错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rem 关闭输出
@echo off
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem mutool pdf 转 png
mutool convert -o %%~ni.png %%i
+ set temp=%%~ni1
rem imagemagick png 转 jpg
- magick convert %%~ni1.png %%~ni.jpg
+ magick convert %temp%.png %%~ni.jpg
)
pause

查找资料后发现这跟变量延迟扩展有关,需要开启变量延迟 setlocal enabledelayedexpansion,并且使用 !temp! 来引用变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
rem 关闭输出
@echo off
+ rem 开启变量延迟
+ setlocal enabledelayedexpansion
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem mutool pdf 转 png
mutool convert -o %%~ni.png %%i
set temp=%%~ni1
rem imagemagick png 转 jpg
- magick convert %temp%.png %%~ni.jpg
+ rem 开启变量延迟后使用 !temp!
+ magick convert !temp!.png %%~ni.jpg
)
pause

最终结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
rem 关闭输出
@echo off
rem 开启变量延迟
setlocal enabledelayedexpansion
rem 获取当前目录下的所有 pdf 文件
for /f %%i in ('dir /b *.pdf') do (
rem 输出 pdf 文件名
@echo %%i
rem mutool pdf 转 png
mutool convert -o %%~ni.png %%i
set temp=%%~ni1
rem imagemagick png 转 jpg
rem 开启变量延迟后使用 !temp!
magick convert !temp!.png %%~ni.jpg
rem imagemagick jpg 转 pdf
magick convert %%~ni.jpg %%i
rem 删除 png jpg
del %%~ni1.png %%~ni.jpg
)
pause

总结

这次 pdf 批量处理经历,主要收获了

  1. mupdf 这个顺手的工具
  2. 明白了一点基本批处理语法
  3. 遇到了变量延迟扩展的坑
  4. 没解决 imagemagick 报错的问题 😭😭😭