使用wkhtmltopdf+Laravel生成PDF时标题特殊字符显示异常
问题:Laravel中wkhtmltopdf生产环境PDF标题丢失特殊字符
本地环境下,用wkhtmltopdf生成的PDF在浏览器标签页的标题能正常显示变音符号等特殊字符,保存时的文件名也正常;但生产环境中,浏览器标签页的PDF标题会丢失所有特殊字符(比如"können"变成"knnen"),不过保存文件名仍正常。
两端环境均使用wkhtmltopdf 0.12.6 (with patched qt),已在HTML中添加UTF-8编码meta标签,wkhtmltopdf配置也指定了encoding => 'utf-8',本地正常但生产环境问题依旧。
相关代码
Controller.php
<?php /** * @todo */ public function pdf($regionCode, $langCode, Model $model) { $locale = LocalisationServiceProvider::getLocale($regionCode, $langCode); $pdfFileName = $this->buildPdfFileName($model->id, $locale); if (Storage::disk('root')->exists('public/storage/wkhtmltopdf/' . $pdfFileName)) { return response()->file(public_path('/storage/wkhtmltopdf/' . $pdfFileName)); } $view = view('model.pdf', compact('model', 'locale')); $viewRendered = $view->render(); $pdf = new Pdf([ 'binary' => env('WKHTMLTOPDF_BINARY_PATH'), 'ignoreWarnings' => ('production' === env('APP_ENV')), 'disable-smart-shrinking', 'title' => $model->title . ' - Site.pdf', 'encoding' => 'utf-8', ]); $pdf->addPage($viewRendered); if (!$pdf->saveAs(public_path('/storage/wkhtmltopdf/' . $pdfFileName))) { $error = $pdf->getError(); // @todo log return response('An error occured', 500); } if (!$pdf->send($model->title . ' - Site.pdf', true)) { $error = $pdf->getError(); return response('An error occured', 500); } } /** * @todo */ public function buildPdfFileName($id, $locale) { return 'foobar_' . $id . '_' . $locale . '.pdf'; }
pdf.blade.php
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="UTF-8"> </head> <body> <div id="canvas" class="position-relative"> @hasSection('header') <header id="header"> @yield('header') </header> @endif @hasSection('main') <main role="main" id="main-content"> @yield('main') </main> @endif </div> </body> </html>
注:
buildPdfFileName仅用于本地存储的PDF内部文件名,与标题/保存文件名无关。
解决方案
1. 检查生产环境系统区域编码
生产环境的系统区域设置可能不是UTF-8,导致wkhtmltopdf处理字符串时丢失特殊字符。执行以下命令查看并设置:
# 查看当前区域设置 locale # 设置为UTF-8编码的区域(以英文为例,可根据实际语言调整) export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8
如果是服务器常驻进程(如PHP-FPM),需要重启进程让设置生效。
2. 显式确保标题字符串为UTF-8编码
生产环境中,字符串可能存在编码转换问题,显式转换标题为UTF-8:
$title = mb_convert_encoding($model->title . ' - Site.pdf', 'UTF-8', 'auto'); $pdf = new Pdf([ // 其他配置... 'title' => $title, 'encoding' => 'utf-8', ]); // send方法也使用转换后的标题 if (!$pdf->send($title, true)) { // ... }
3. 在HTML模板中添加标签 <a class="header-anchor" href="#_3-在html模板中添加标签" aria-hidden="true">#</a></h3>
<p>让wkhtmltopdf从HTML内容中读取标题,而非依赖配置参数,避免编码传递问题:<br>
修改<code>pdf.blade.php</code>的<head>部分:</p>
<pre class="hljs"><code class="language-html volc-pre-code"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<title>{{ $model->title }} - Site.pdf</title>
</head>
</code></pre>
<p>同时可以移除wkhtmltopdf配置中的<code>'title'</code>参数,让工具自动读取HTML的title。</p>
<h3 id="_4-手动设置响应头的content-disposition">4. 手动设置响应头的Content-Disposition <a class="header-anchor" href="#_4-手动设置响应头的content-disposition" aria-hidden="true">#</a></h3>
<p>当使用<code>response()->file</code>返回已生成的PDF时,确保响应头中的文件名和标题都正确编码:</p>
<pre class="hljs"><code class="language-php volc-pre-code">if (Storage::disk('root')->exists('public/storage/wkhtmltopdf/' . $pdfFileName)) {
$title = $model->title . ' - Site.pdf';
// 编码文件名,支持UTF-8
$encodedFileName = rawurlencode($title);
return response()->file(public_path('/storage/wkhtmltopdf/' . $pdfFileName), [
'Content-Disposition' => "inline; filename*=UTF-8''{$encodedFileName}; filename=\"{$encodedFileName}\"",
'Content-Type' => 'application/pdf',
]);
}
</code></pre>
<p>内容的提问来源于stack exchange,提问作者Alex</p>
相关产品推荐
相关产品推荐

