PHP拆分重组pdf,php拆分pdf指定页数,并合并成新pdf

在ThinkPHP5中实现PDF拆分与合并功能,主要使用FPDI库来处理PDF页面操作。以下是完整的实现流程和代码示例。

环境准备与依赖安装

首先通过Composer安装必要的依赖包

cpp 复制代码
composer require setasign/fpdi
composer require setasign/fpdf

核心功能实现

以下是完整的PDF处理类,包含拆分指定页面和合并多个PDF的功能:

cpp 复制代码
<?php
namespace app\common\util;

use setasign\Fpdi\Tcpdf\Fpdi;

class PdfHandler
{
    /**
     * 拆分PDF指定页面并合并为新PDF
     * @param array $pdfFiles PDF文件路径数组
     * @param array $pageRules 页面规则 ['file1.pdf' => '1,3,5', 'file2.pdf' => '2-4']
     * @param string $outputName 输出文件名
     * @param string $outputMode 输出模式 D-下载 I-预览 F-保存
     * @return mixed
     */
    public function splitAndMergePdf($pdfFiles, $pageRules, $outputName = 'merged.pdf', $outputMode = 'D')
    {
        try {
            $pdf = new Fpdi();
            
            foreach ($pdfFiles as $index => $pdfFile) {
                $fileKey = basename($pdfFile);
                
                if (!isset($pageRules[$fileKey])) {
                    continue;
                }
                
                $pageCount = $pdf->setSourceFile($pdfFile);
                $selectedPages = $this->parsePageRules($pageRules[$fileKey], $pageCount);
                
                foreach ($selectedPages as $pageNo) {
                    $templateId = $pdf->importPage($pageNo);
                    $size = $pdf->getTemplateSize($templateId);
                    
                    $pdf->AddPage($size['orientation'], $size);
                    $pdf->useTemplate($templateId);
                }
            }
            
            $pdf->Output($outputName, $outputMode);
            $pdf->closeParsers();
            
            return true;
        } catch (\Exception $e) {
            throw new \Exception("PDF处理失败: " . $e->getMessage());
        }
    }
    
    /**
     * 解析页面规则
     * @param string $rule 页面规则 '1,3,5' 或 '2-4' 或 'all'
     * @param int $totalPages 总页数
     * @return array
     */
    private function parsePageRules($rule, $totalPages)
    {
        $pages = [];
        
        if ($rule === 'all') {
            for ($i = 1; $i <= $totalPages; $i++) {
                $pages[] = $i;
            }
            return $pages;
        }
        
        if (strpos($rule, '-') !== false) {
            list($start, $end) = explode('-', $rule);
            $start = max(1, intval(trim($start)));
            $end = min($totalPages, intval(trim($end)));
            
            for ($i = $start; $i <= $end; $i++) {
                $pages[] = $i;
            }
            return $pages;
        }
        
        if (strpos($rule, ',') !== false) {
            $pageArray = explode(',', $rule);
            foreach ($pageArray as $page) {
                $page = intval(trim($page));
                if ($page >= 1 && $page <= $totalPages) {
                    $pages[] = $page;
                }
            }
            return $pages;
        }
        
        $page = intval(trim($rule));
        if ($page >= 1 && $page <= $totalPages) {
            $pages[] = $page;
        }
        
        return $pages;
    }
    
    /**
     * 简单合并多个PDF(全部页面)
     * @param array $pdfFiles PDF文件路径数组
     * @param string $outputName 输出文件名
     * @param string $outputMode 输出模式
     * @return mixed
     */
    public function simpleMergePdf($pdfFiles, $outputName = 'merged.pdf', $outputMode = 'D')
    {
        try {
            $pdf = new Fpdi();
            
            foreach ($pdfFiles as $pdfFile) {
                $pageCount = $pdf->setSourceFile($pdfFile);
                
                for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
                    $templateId = $pdf->importPage($pageNo);
                    $size = $pdf->getTemplateSize($templateId);
                    
                    $pdf->AddPage($size['orientation'], $size);
                    $pdf->useTemplate($templateId);
                }
            }
            
            $pdf->Output($outputName, $outputMode);
            $pdf->closeParsers();
            
            return true;
        } catch (\Exception $e) {
            throw new \Exception("PDF合并失败: " . $e->getMessage());
        }
    }
}

控制器调用示例

cpp 复制代码
<?php
namespace app\index\controller;

use think\Controller;
use think\Request;
use app\common\util\PdfHandler;

class Pdf extends Controller
{
    /**
     * 拆分合并PDF页面
     */
    public function splitMerge()
    {
        if (Request::instance()->isPost()) {
            try {
                $pdfHandler = new PdfHandler();
                
                // PDF文件路径
                $pdfFiles = [
                    ROOT_PATH . 'public/uploads/employee_agreement.pdf',
                    ROOT_PATH . 'public/uploads/work_summary.pdf',
                    ROOT_PATH . 'public/uploads/rental_agreement.pdf'
                ];
                
                // 页面规则:指定每个PDF要提取的页面
                $pageRules = [
                    'employee_agreement.pdf' => '1,3',      // 第1页和第3页
                    'work_summary.pdf' => '2-4',             // 第2页到第4页
                    'rental_agreement.pdf' => 'all'         // 所有页面
                ];
                
                $result = $pdfHandler->splitAndMergePdf(
                    $pdfFiles, 
                    $pageRules, 
                    'custom_merged.pdf', 
                    'D'
                );
                
                if ($result) {
                    return json(['code' => 1, 'msg' => 'PDF处理成功']);
                }
                
            } catch (\Exception $e) {
                return json(['code' => 0, 'msg' => $e->getMessage()]);
            }
        }
        
        return $this->fetch();
    }
    
    /**
     * 简单合并多个PDF
     */
    public function simpleMerge()
    {
        try {
            $pdfHandler = new PdfHandler();
            
            $pdfFiles = [
                ROOT_PATH . 'public/uploads/doc1.pdf',
                ROOT_PATH . 'public/uploads/doc2.pdf',
                ROOT_PATH . 'public/uploads/doc3.pdf'
            ];
            
            $result = $pdfHandler->simpleMergePdf(
                $pdfFiles,
                'simple_merged.pdf',
                'D'
            );
            
            if ($result) {
                return json(['code' => 1, 'msg' => 'PDF合并成功']);
            }
            
        } catch (\Exception $e) {
            return json(['code' => 0, 'msg' => $e->getMessage()]);
        }
    }
}

前端表单页面

cpp 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PDF拆分合并工具</title>
    <style>
        .container { max-width: 800px; margin: 50px auto; padding: 20px; }
        .form-group { margin-bottom: 20px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input, select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
        .btn { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
        .btn:hover { background: #0056b3; }
        .file-item { background: #f8f9fa; padding: 15px; margin-bottom: 10px; border-radius: 4px; }
    </style>
</head>
<body>
    <div class="container">
        <h2>PDF拆分重组工具</h2>
        
        <form id="pdfForm" action="{:url('index/pdf/splitMerge')}" method="post">
            <div class="form-group">
                <label>选择PDF文件:</label>
                <input type="file" id="pdfFiles" multiple accept=".pdf">
                
                <div id="fileList"></div>
            </div>
            
            <div class="form-group">
                <label>输出文件名:</label>
                <input type="text" name="output_name" value="custom_merged.pdf" required>
            </div>
            
            <button type="submit" class="btn">生成新PDF</button>
        </form>
    </div>

    <script>
        document.getElementById('pdfFiles').addEventListener('change', function(e) {
            const fileList = document.getElementById('fileList');
            fileList.innerHTML = '';
            
            Array.from(e.target.files).forEach((file, index) => {
                const fileItem = document.createElement('div');
                fileItem.className = 'file-item';
                fileItem.innerHTML = `
                    <strong>${file.name}</strong>
                    <div style="margin-top: 10px;">
                        <label>页面选择:</label>
                        <input type="text" name="page_rules[${file.name}]" 
                           placeholder="例如: 1,3,5 或 2-4 或 all">
                    </div>
                `;
                fileList.appendChild(fileItem);
            });
        });
    </script>
</body>
</html>

功能特点说明

  1. 灵活的页面选择‌:支持单个页面、页面范围、多个指定页面以及全部页面的选择方式‌
  2. 多种输出模式‌:支持下载(D)、浏览器预览(I)、服务器保存(F)等输出方式
  3. 异常处理完善‌:包含完整的错误捕获和处理机制
  4. 内存管理优化‌:使用closeParsers()方法及时释放资源
  5. 自动尺寸适配‌ :根据原PDF页面尺寸自动设置新页面大小
    该方案适用于文档管理系统、在线PDF处理服务等场景,能够高效地完成PDF页面的拆分和重组任务。
相关推荐
NewsMash4 小时前
益品康丰集团:以科技重塑康养未来,让健康触手可及
1024程序员节
xixixi777774 小时前
怎么区分主动攻击和被动攻击啊,为什么跨站脚本是被动攻击?
xss·1024程序员节·跨站脚本·主动攻击·被动攻击
lazily-c4 小时前
Web后端开发总结
java·学习·web·1024程序员节
咕噜企业签名分发-淼淼4 小时前
苹果个人开发者如何实现应用下载安装
1024程序员节
cungudafa4 小时前
xcode只显示堆栈不定位断点处
1024程序员节
liu****4 小时前
4.基础开发工具(一)
linux·开发语言·1024程序员节
深鱼~5 小时前
NAS云同步总断连?CloudSync+cpolar无缝对接
1024程序员节
麦克马5 小时前
Git 版本回退 reset --mixed 命令
1024程序员节
电脑小白技术5 小时前
笔记本电脑待机、睡眠与休眠模式的技术差异解析
1024程序员节·笔记本电脑待机·笔记本电脑休眠