安装 LibreOffice
sudo yum install libreoffice-headless -y
查看是否成功
libreoffice --version
手动运行转化:
libreoffice --headless --convert-to pdf --outdir '/www/wwwroot/K-HUISHOUYOU/public/output' '/www/wwwroot/K-HUISHOUYOU/public/output/doc_20250914151052_10852.docx' 2>&1
安装完整的 LibreOffice,缺少 libreoffice-writer
(处理 .docx
的模块)
sudo dnf install libreoffice-writer -y
安装中文字体
查系统已安装完整的中文语言包
locale -a | grep zh
查字体是否被安装
fc-list :lang=zh | grep -i noto
安装字体:kkgithub.com 镜像
sudo wget -O /usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf \
https://kkgithub.com/notofonts/noto-cjk/raw/main/Sans/OTF/SimplifiedChinese/NotoSansCJKsc-Regular.otf
设置权限:
sudo chmod 644 /usr/share/fonts/noto-cjk/*.otf
更新字体缓存(这个重要)
sudo fc-cache -fv
public function generatePdf()
{
$data = input();
if($data['sign'] == 'ys'){
$templateFile = 'output/moban/hsyys.docx';
}elseif($data['sign'] == 'ws'){
$templateFile = 'output/moban/hsyws.docx';
}
if (!file_exists($templateFile)) {
return json(['success' => false, 'msg' => '模板文件不存在,请检查 template/hsyys.docx']);
}
try {
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($templateFile);
} catch (\Exception $e) {
return json(['success' => false, 'msg' => '加载模板失败: ' . $e->getMessage()]);
}
// 2. 替换占位符
$fields = ['serial', 'merit', 'company', 'creditcode', 'venue', 'contact'];
foreach ($fields as $field) {
// if (!isset($data[$field]) || $data[$field] === '') {
// return json(['success' => false, 'msg' => "缺少必要字段: $field"]);
// }
// 安全处理:转义 HTML,防止特殊字符破坏文档
$value = htmlspecialchars(strip_tags($data[$field]), ENT_QUOTES, 'UTF-8');
$templateProcessor->setValue($field, $value);
}
// ✅ 新增:插入图片(支持远程 URL 或本地路径)
$imageUrl = $data['imgurl'] ?? '';
if (!empty($imageUrl)) {
$imagePath = $imageUrl;
if (preg_match('/^https?:\/\//', $imageUrl)) {
$tempImage = sys_get_temp_dir() . '/temp_img_' . md5($imageUrl) . '.png';
$imageContent = file_get_contents($imageUrl);
if ($imageContent === false) {
return json(['success' => false, 'msg' => '无法下载图片']);
}
if (file_put_contents($tempImage, $imageContent) === false) {
return json(['success' => false, 'msg' => '无法保存临时图片']);
}
$imagePath = $tempImage;
}
if (!file_exists($imagePath) || !is_readable($imagePath)) {
return json(['success' => false, 'msg' => '图片不可读: ' . $imagePath]);
}
try {
$templateProcessor->setImageValue('image', [
'path' => $imagePath,
'width' => 200,
'height' => 100,
'ratio' => false, // 可选
]);
} catch (\Exception $e) {
@unlink($tempImage ?? '');
return json(['success' => false, 'msg' => '插入图片失败: ' . $e->getMessage()]);
}
if (isset($tempImage)) {
@unlink($tempImage);
}
}
$outputDir = 'output';
$pdfDir = 'output/pdf';
if (!is_dir($outputDir)) {
if (!mkdir($outputDir, 0777, true)) {
return json(['success' => false, 'msg' => '无法创建 output 目录']);
}
}
if (!is_dir($pdfDir)) {
if (!mkdir($pdfDir, 0777, true)) {
return json(['success' => false, 'msg' => '无法创建 output/pdf 目录']);
}
}
// 4. 生成临时文件(使用绝对路径)
$tempWordFile = $outputDir . '/doc_' . date('YmdHis') . '_' . rand(10000, 99999) . '.docx';
$tempPdfFile = $pdfDir . '/contract_' . date('YmdHis') . '_' . rand(10000, 99999) . '.pdf';
try {
$templateProcessor->saveAs($tempWordFile);
} catch (\Exception $e) {
return json(['success' => false, 'msg' => '保存 Word 失败: ' . $e->getMessage()]);
}
// 验证 Word 文件是否生成成功且非空
if (!file_exists($tempWordFile)) {
return json(['success' => false, 'msg' => 'Word 文件未生成,路径错误']);
}
if (filesize($tempWordFile) < 1024) {
@unlink($tempWordFile);
return json(['success' => false, 'msg' => 'Word 文件大小异常,可能模板占位符错误']);
}
// 使用绝对路径(关键!)
$absoluteWord = realpath($tempWordFile);
$absoluteOutDir = realpath($outputDir);
if (!$absoluteWord || !$absoluteOutDir) {
return json(['success' => false, 'msg' => '路径解析失败']);
}
// 5. 调用 LibreOffice 转 PDF
$command = "libreoffice --headless --convert-to pdf --outdir " .
escapeshellarg($absoluteOutDir) . " " .
escapeshellarg($absoluteWord) . " 2>&1";
exec($command, $output, $returnCode);
// 删除源 Word 文件
@unlink($tempWordFile);
if ($returnCode !== 0) {
return json([
'success' => false,
'msg' => 'LibreOffice 执行失败',
'command' => $command,
'output' => implode("\n", $output)
]);
}
// 6. 检查 PDF 是否生成(LibreOffice 生成在同目录)
$expectedPdf = str_replace('.docx', '.pdf', $tempWordFile);
if (!file_exists($expectedPdf)) {
return json([
'success' => false,
'msg' => 'PDF 文件未生成,请检查 LibreOffice 是否正常安装',
'expected' => $expectedPdf,
'output' => $output,
'command' => $command
]);
}
// 移动并重命名为目标 PDF
if (!rename($expectedPdf, $tempPdfFile)) {
return json(['success' => false, 'msg' => '无法移动 PDF 文件,权限不足']);
}
// 7. 返回成功
// 带域名的 PDF URL
$pdfUrlAll = 'https://' . $_SERVER['HTTP_HOST'] . '/'. $tempPdfFile;
$pdfUrl = '/' . $tempPdfFile;
return json([
'success' => true,
'msg' => 'PDF 生成成功',
'url' => $pdfUrlAll,
'file' => $pdfUrl,
'sign' => $data['sign']
]);
}