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页面的拆分和重组任务。
相关推荐
学传打活20 小时前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng1689 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心11 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552717 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552717 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝19 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552719 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552719 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u01030552722 天前
Java实用类应用精讲
人工智能·1024程序员节
u01030552722 天前
Java I/O核心操作实战
人工智能·1024程序员节