wkhtmltopdf 跨平台处理中文、日文等非英文字符文件名
2025-03-01 22:33:35
wkhtmltopdf 处理跨平台非英文字符文件名问题
在使用 wkhtmltopdf 进行 PDF 生成时,如果传入的参数包含非英文字符(比如中文、日文)文件名,可能会遇到一些问题。我最近就碰到了:在 Ubuntu 服务器上,传入名为 "Japan日本.xml" 的文件,输出的文件却变成了 "Japan.xml",非英文字符部分丢失了。但在我的 Windows 客户端上就没问题。虽然服务器已经安装了必要的字体,并且 locale 设置也支持 UTF-8,问题还是存在。
问题原因分析
这类问题通常由以下几个方面引起:
- 操作系统和文件系统的差异: Windows 和 Linux 在处理文件名编码上可能存在差异。Windows 默认可能使用 UTF-16 或本地编码,而 Linux 通常使用 UTF-8。
- wkhtmltopdf 的内部处理: wkhtmltopdf 在接收和处理文件名参数时,可能没有正确处理不同编码。
- Shell 环境: Shell 在传递参数给 wkhtmltopdf 时,可能进行了编码转换或字符过滤,导致非英文字符丢失。
- PHP 的
escapeshellarg
和shell_exec
函数:escapeshellarg
函数可能无法完美处理某些特殊字符或编码。shell_exec
在执行系统命令时,也依赖于系统的 locale 设置。 - 命令行参数空格 : 有时会因为疏忽忘记在参数与其对应值之间加入空格,这会导致命令运行不起来。
解决方案
针对上述可能的原因,可以尝试以下解决方案:
1. 统一编码为 UTF-8
确保所有环节都使用 UTF-8 编码,这是最通用的做法。
-
PHP 文件: 确保 PHP 文件本身以 UTF-8 无 BOM 格式保存。
-
HTML/XML 输入文件: 确保输入文件(比如你的 "Japan日本.xml")也是 UTF-8 编码。
-
服务器 Locale 设置:
# 查看当前 locale 设置 locale # 设置 locale (如果需要) sudo locale-gen zh_CN.UTF-8 sudo update-locale LANG=zh_CN.UTF-8 sudo dpkg-reconfigure locales # 如果上面的方法无效,可以试试这个 # 之后需要重启服务或重新登录让设置生效。
将
zh_CN.UTF-8
替换为你需要的语言环境,比如日语ja_JP.UTF-8
。 -
检查php.ini设置。 确认
default_charset = "UTF-8"
。 -
数据库:如果内容有来自数据库,要保证数据库使用UTF-8。
2. 使用 escapeshellarg
的替代方案
escapeshellarg
有时不能很好地处理多字节字符。可以尝试直接拼接字符串,但要非常小心防止 shell 注入攻击。更推荐下面的方法:
2.1. 使用 Symfony Process 组件 (推荐)
Symfony Process 组件提供了更安全、更强大的方式来执行外部命令,能更好地处理各种情况。
<?php
require_once 'vendor/autoload.php'; // 如果使用 Composer
use Symfony\Component\Process\Process;
$WKHTMLTOPDF = $wkhtmltopdf_path;
$inputFilePath = '/usr/local/apache2/htdocs/Japan日本.xml';
$outputFilePath = '/path/to/output.pdf';
$process = new Process([
$WKHTMLTOPDF,
'--dump-outline',
$inputFilePath, //Symfony Process 组件可以自动进行正确的参数转义,因此无需再对文件名使用 escapeshellarg 函数。
$inputFilePath, //这里将输入文件的路径 $inputFilePath 直接传递给了 Process 组件
$outputFilePath
]);
$process->run();
if (!$process->isSuccessful()) {
echo 'Error: ' . $process->getErrorOutput();
} else {
echo $process->getOutput();
}
- 安装:
composer require symfony/process
- 原理: Process 组件会自动处理参数的转义和引号,避免了
escapeshellarg
的潜在问题。
3. 显式指定 wkhtmltopdf 的编码
尝试使用 --input-encoding
和 --output-encoding
选项明确指定输入和输出编码。
<?php
$WKHTMLTOPDF = $wkhtmltopdf_path;
$_options = [
'--input-encoding', 'UTF-8',
'--output-encoding', 'UTF-8',
'--dump-outline ' . escapeshellarg('/usr/local/apache2/htdocs/Japan日本.xml'),
$input_file_path,
$output_file_path
];
$options_string = implode(' ', $_options);
$output = shell_exec('"' . $WKHTMLTOPDF . '" ' . $options_string);
?>
不过这个例子仍然使用了escapeshellarg, 如果结合Symfony Process, 可靠性更高:
<?php
require_once 'vendor/autoload.php';
use Symfony\Component\Process\Process;
$WKHTMLTOPDF = $wkhtmltopdf_path;
$inputFilePath = '/usr/local/apache2/htdocs/Japan日本.xml';
$outputFilePath = '/path/to/output.pdf';
$process = new Process([
$WKHTMLTOPDF,
'--input-encoding', 'UTF-8',
'--output-encoding', 'UTF-8',
'--dump-outline',
$inputFilePath,
$inputFilePath,
$outputFilePath
]);
$process->run();
if (!$process->isSuccessful()) {
echo 'Error: ' . $process->getErrorOutput();
}
4. 检查字体安装
虽然你已经确认安装了字体,但可以再次确认 wkhtmltopdf 能否找到它们。有时字体路径配置不正确,会导致 wkhtmltopdf 使用默认字体,而默认字体可能不支持非英文字符。
-
使用
fc-list
命令列出已安装字体:fc-list
查看输出中是否包含你需要的支持中文、日文的字体。
-
如果字体未正确安装,重新安装或配置字体:
可以尝试在html里面显式使用
font-family
确保使用了支持日语的字体。例如:<style> body { font-family: 'MS Mincho', 'MS Gothic', sans-serif; /* 或其他日语字体 */ } </style>
5. 命令行参数空格问题处理方法
养成良好的习惯:参数与值之间永远有空格!
// 不推荐
$options_string = '--output'.$output_file_path;
// 推荐
$options_string = '--output ' . $output_file_path;
或者,在创建包含选项的数组时就包含这些空格:
$_options = [
'--dump-outline ', // 注意这里最后的空格
'/usr/local/apache2/htdocs/Japan日本.xml',
$input_file_path,
$output_file_path
];
再次推荐使用 Symfony Process组件, 因为Process会自动帮你添加空格:
$process = new Process([
$WKHTMLTOPDF,
'--dump-outline', // 这里无需添加额外的空格
$inputFilePath,
$inputFilePath,
$outputFilePath
]);
6. (进阶) Docker 化
如果上述方法仍然无法解决,或者为了获得更一致的运行环境,可以考虑将 wkhtmltopdf 和你的 PHP 应用一起打包到 Docker 容器中。
-
优势: Docker 可以确保在任何环境中(开发、测试、生产)都使用相同的依赖和配置,避免因环境差异导致的问题。
-
Dockerfile 示例:
FROM php:7.4-apache # 或者其他你需要的 PHP 版本 # 安装 wkhtmltopdf RUN apt-get update && apt-get install -y --no-install-recommends \ wkhtmltopdf \ && rm -rf /var/lib/apt/lists/* # 安装中文字体 (示例,根据需要修改) RUN apt-get update && apt-get install -y --no-install-recommends \ fonts-wqy-zenhei \ && rm -rf /var/lib/apt/lists/* # 安装日语字体(示例) RUN apt-get update && apt-get install -y --no-install-recommends \ fonts-takao \ && rm -rf /var/lib/apt/lists/* # 设置 locale (示例) ENV LANG ja_JP.UTF-8 ENV LANGUAGE ja_JP:ja ENV LC_ALL ja_JP.UTF-8 # 其他配置 (如复制 PHP 代码、安装 Composer 依赖等) # ...
构建Docker镜像,运行容器,将你的应用运行起来.
通过这些方案的逐一尝试和组合,应该能够解决 wkhtmltopdf 在处理非英文字符文件名时遇到的问题,并提高命令执行的可靠性。使用Symfony Process, 避免使用escapeshellarg
加上对shell_exec
更好的错误控制可以大幅度增加脚本的稳定性. Docker 可以消除跨平台的坑。