合同生成之 word 文档转 pdf

AI摘要
本文介绍使用PHPWord和Dompdf库实现动态生成PDF合同的技术方案。通过Word模板替换变量,配置中文字体避免乱码,并提供完整代码示例说明模板数据处理、表格填充及PDF转换流程。核心要点包括依赖安装、字体注册、变量替换和格式转换。

写在前面

业务场景:需要动态数据填充,生成合同 pdf 文件。

  1. 配置合同 word模板
  2. 替换模板变量 ${var}
  3. 生成 pdf合同
# 依赖包
composer require phpoffice/phpword

composer require dompdf/dompdf
composer require dompdf/php-font-lib

# 注册字体,脚本 load_font.php(获取 github -> dompdf-utils),字体:simhei.ttf
php load_font.php simhei storage/fonts/simhei.ttf

参考代码

<?php

namespace App\Service;

use PhpOffice\PhpWord\Exception\CopyFileException;
use PhpOffice\PhpWord\Exception\CreateTemporaryFileException;
use PhpOffice\PhpWord\TemplateProcessor;

class WordPdfService
{

    /**
     * 根据 word 模板生成 pdf
     *
     * @throws CopyFileException
     * @throws CreateTemporaryFileException
     */
    public function word2Pdf(string $templateFilePath): array
    {
        $templateProcessor = new TemplateProcessor($templateFilePath);

        // 替换基本变量
        $templateData = $this->prepareTemplateData();
        $this->replaceBasicVariables($templateProcessor, $templateData);
        // 处理表格数据
        $this->replaceTableData($templateProcessor);

        // 保存临时Word文件
        $tempDir = storage_path('app');
        if (!is_dir($tempDir)) {
            mkdir($tempDir, 0755, true);
        }
        $tempWordName = uniqid('contract_') . '.docx';
        $tempWordPath = $tempDir . '/' . $tempWordName;
        $templateProcessor->saveAs($tempWordPath);

        // 转换为PDF
        $pdfPath = $this->convertWordToPdf($tempWordPath, $tempDir);

        // 清理临时Word文件
        unlink($tempWordPath);

        // 返回PDF文件路径(或直接下载)
        return ['ok' => true, 'pdfPath' => $pdfPath];
    }

    /**
     * 准备模板数据
     *
     * @return array
     */
    private function prepareTemplateData(): array
    {
        return [
            'name' => '张三',
            'projectName' => '测试项目',
            'date' => now()->format('Y年m月d日'),
        ];
    }

    /**
     * 替换基本变量
     *
     * @param $templateProcessor |   模板对象
     * @param $data |   数据
     * @return void
     */
    public function replaceBasicVariables($templateProcessor, $data): void
    {
        foreach ($data as $key => $value) {
            $templateProcessor->setValue($key, $value ?? '');
        }
    }

    /**
     * 处理表格数据
     *
     * @param $templateProcessor    |   模板对象
     * @return void
     */
    public function replaceTableData($templateProcessor): void
    {
        // 获取数据
        $goodsLists = [
            ['name' => '数据1', 'number' => 10, 'price' => '200.00', 'tax_rate' => '0.03'],
            ['name' => '数据2', 'number' => 210, 'price' => '20000.21', 'tax_rate' => '0.13'],
            ['name' => '数据4', 'number' => 320, 'price' => '30000.9987', 'tax_rate' => '0.11'],
        ];

        if (count($goodsLists) > 0) {
            // 使用cloneRow方法克隆表格行
            $templateProcessor->cloneRow('idx', count($goodsLists));
            // 行数据赋值
            foreach ($goodsLists as $index => $goods) {
                $idxNum = $index + 1;
                $taxRate = $goods['tax_rate'] * 100 . '%';
                $taxRate = str_replace('.00', '', $taxRate);

                // 设置克隆行的变量
                $templateProcessor->setValue("idx#{$idxNum}", $idxNum);
                $templateProcessor->setValue("name#{$idxNum}", $goods['name']);
                $templateProcessor->setValue("number#{$idxNum}", $goods['number']);
                $templateProcessor->setValue("price#{$idxNum}", $goods['price']);
                $templateProcessor->setValue("tax_rate#{$idxNum}", $taxRate);
            }
        }
    }


    /**
     * 将 Word 转换为 PDF (注意中文乱码)
     */
    public function convertWordToPdf($wordPath, $outputDir): string
    {
        // 配置PDF渲染器路径和名称
        \PhpOffice\PhpWord\Settings::setPdfRendererPath(base_path('vendor/dompdf/dompdf'));
        \PhpOffice\PhpWord\Settings::setPdfRendererName('DomPDF');

        // 创建Dompdf选项,设置默认字体 'simhei' 必须与注册字体时使用的名称一致
        $options = new \Dompdf\Options();
        $options->set('defaultFont', 'simhei');
        $options->set('isHtml5ParserEnabled', true);
        $options->set('isPhpEnabled', true);

        // 加载Word文档并转换为HTML
        $dompdf = new \Dompdf\Dompdf($options);
        $phpWord = \PhpOffice\PhpWord\IOFactory::load($wordPath);

        // 创建一个临时HTML文件来传递内容
        $tempHtml = $outputDir . DIRECTORY_SEPARATOR . 'temp_' . uniqid() . '.html';
        $htmlWriter = new \PhpOffice\PhpWord\Writer\HTML($phpWord);
        file_put_contents($tempHtml, $htmlWriter->getContent());

        // 加载HTML内容,明确指定UTF-8编码
        $htmlContent = file_get_contents($tempHtml);
        $dompdf->loadHtml($htmlContent, 'UTF-8');

        // 设置纸张和渲染
        $dompdf->setPaper('A4', 'portrait');
        $dompdf->render();

        // 保存PDF文件
        $pdfFilename = uniqid('contract_') . '.pdf';
        $pdfPath = $outputDir . DIRECTORY_SEPARATOR . $pdfFilename;
        file_put_contents($pdfPath, $dompdf->output());

        // 清理临时HTML文件
        unlink($tempHtml);

        return $pdfPath;
    }


}

参考代码,替换对应的:模板文件 $templateFilePath,模板填充数据prepareTemplateData()replaceTableData().$goodsList

注意事项

  1. 脚本 load_font.php 注册配置 宋体微软雅黑 等字体,避免 pdf 文件中文乱码;
  2. 创建合同模板并标记变量
  3. 建议 html 模板(更好控制布局样式)生成 pdf 文件;
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!