记录word插入文字/图片,生成新word并转为pdf

发布于:2025-09-15 ⋅ 阅读:(15) ⋅ 点赞:(0)
安装 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']
        ]);
    }