记录word插入文字/图片,生成新word并转为pdf
安装 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();$templateFile = 'output/moban/hsyys.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);}$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]);}// 移动并重命名为目标 PDFif (!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]);}