又重新写了个PDF工具箱-转换office格式/合并/拆分/删除常见操作都有了

之前OCR的对于扫描类的文档倒是还有点用处,但是一旦文档中有截图,截图上带文字之类的,识别出来的东西就乱套了,最主要的时ocr识别出来的格式都乱了,而且效率还比较低,性能差点的机子,经不起几个服务就垮掉了,于是乎还是自己重新整一个,放弃ocr,因为日常的PDF扫描的连10%都占不到。

之前看到很多PDF工具,但是功能似乎都不是很全,尤其是在现在的统信UOS和银河麒麟这种国产操作系统上,应用更是少的可怜,要知道在Windows上还有adobe里提供的arcrobat这个处理PDF的大神,但是linux上就没戏了,要说没有也不行,毕竟wps还带了个PDF处理工具,就是处处要会员,要花钱。。。。。。

先来个界面图再介绍会更直观一些,非常简陋哈。

一、 项目概述

PDFTools 是一个基于 Python Flask 框架开发的 PDF 文档处理工具,提供了 PDF 转 Word、PDF 转 Excel、PDF 转 PPT、PDF 拆分、PDF 合并、PDF 页面删除等多种功能。该工具采用 Web 界面设计,操作简单直观,支持多文件处理。

二、技术架构

技术栈:

  • **后端框架**: Flask 2.0+

  • **PDF 处理库**:

  • PyPDF2: PDF 文件的基础操作(拆分、合并、页面删除)

  • pdf2docx: PDF 转 Word 功能

- pdfplumber: PDF 内容提取和表格识别 (看了别人的对比文章说这个提取的效果最好!就用上了,发现还真的不错!!)

  • PyMuPDF (fitz): 高级 PDF 内容提取和图像处理

  • **文档处理库**:

  • openpyxl: Excel 文件创建和编辑

  • python-pptx: PPT 文件创建和编辑

  • PIL (Pillow): 图像处理

  • **前端技术**: HTML5、CSS3、JavaScript (jQuery)

架构

三、功能

截图上都看的够清楚的了不用多做介绍了,说说怎么实现的然后上代码。

1、PDF 拆分模块

顾名思义就是将一个 PDF 文件拆分为多个 PDF 文件,可以按指定页码范围拆分或一次性的全部拆分所有单页。

使用 PyPDF2 库读取 PDF 文件,根据用户指定的页码范围或拆分模式,将指定页面写入新的 PDF 文件。

**关键函数**: `split_pdf()`

python 复制代码
def split_pdf(input_path, output_dir, ranges=None, split_all=False, original_name="pdf"):
    """
    拆分PDF文件
    :param input_path: 输入PDF文件路径
    :param output_dir: 输出目录
    :param ranges: 页码范围列表,格式如["1-3", "5-7", "9"]
    :param split_all: 是否拆分所有页面为单页
    :param original_name: 原始文件名(不带扩展名)
    :return: 生成的文件路径列表
    """
    output_files = []
    
    with open(input_path, 'rb') as pdf_file:
        pdf_reader = PyPDF2.PdfReader(pdf_file)
        total_pages = len(pdf_reader.pages)
        
        if split_all:
            # 拆分所有页面为单页
            for i in range(total_pages):
                pdf_writer = PyPDF2.PdfWriter()
                pdf_writer.add_page(pdf_reader.pages[i])
                
                # 保存新PDF,使用原始文件名
                output_filename = f"{original_name}_page_{i+1}.pdf"
                output_path = os.path.join(output_dir, output_filename)
                
                with open(output_path, 'wb') as output_file:
                    pdf_writer.write(output_file)
                
                output_files.append(output_path)
        elif ranges:
            # 按指定范围拆分
            for i, page_range in enumerate(ranges):
                pdf_writer = PyPDF2.PdfWriter()
                
                # 解析页码范围
                if '-' in page_range:
                    start, end = map(int, page_range.split('-'))
                    # 确保页码在有效范围内
                    start = max(1, min(start, total_pages))
                    end = max(start, min(end, total_pages))
                    pages = range(start-1, end)
                else:
                    page_num = int(page_range)
                    page_num = max(1, min(page_num, total_pages))
                    pages = [page_num-1]
                
                # 添加指定页码到新PDF
                for page_idx in pages:
                    pdf_writer.add_page(pdf_reader.pages[page_idx])
                
                # 保存新PDF,使用原始文件名
                output_filename = f"{original_name}_split_{i+1}.pdf"
                output_path = os.path.join(output_dir, output_filename)
                
                with open(output_path, 'wb') as output_file:
                    pdf_writer.write(output_file)
                
                output_files.append(output_path)
    
    return output_files

2、PDF 合并模块

将多个 PDF 文件合并为一个 PDF 文件。

使用 PyPDF2 库读取多个 PDF 文件,将所有页面按顺序添加到新的 PDF 文件中。

**关键函数**: `merge_pdfs()`

python 复制代码
def merge_pdfs(input_paths, output_path):
    """
    合并多个PDF文件
    :param input_paths: 输入PDF文件路径列表
    :param output_path: 输出PDF文件路径
    :return: 输出文件路径
    """
    pdf_writer = PyPDF2.PdfWriter()
    
    try:
        # 逐个处理PDF文件
        for input_path in input_paths:
            # 读取文件内容到内存
            with open(input_path, 'rb') as pdf_file:
                pdf_content = pdf_file.read()
            
            # 创建内存流对象
            from io import BytesIO
            pdf_stream = BytesIO(pdf_content)
            
            # 使用内存流创建PdfReader对象
            pdf_reader = PyPDF2.PdfReader(pdf_stream)
            
            # 逐个添加页面
            for page in pdf_reader.pages:
                pdf_writer.add_page(page)
        
        # 写入合并后的PDF
        with open(output_path, 'wb') as output_file:
            pdf_writer.write(output_file)
            
    except Exception as e:
        # 记录详细错误信息
        print(f"Error merging PDFs: {e}")
        print(f"Input paths: {input_paths}")
        print(f"Output path: {output_path}")
        raise
    
    return output_path

3、PDF 页面删除模块

删除 PDF 文件中的指定页面。

使用 PyPDF2 库读取 PDF 文件,根据用户指定的页码范围,将不需要删除的页面写入新的 PDF 文件。

**关键函数**: `delete_pages()`

python 复制代码
def delete_pages(pdf_path, output_path, delete_ranges):
    """
    删除PDF文件中的指定页面
    :param pdf_path: 输入PDF文件路径
    :param output_path: 输出PDF文件路径
    :param delete_ranges: 要删除的页码范围列表,格式如["1-3", "5-7", "9"]
    :return: 输出文件路径
    """
    with open(pdf_path, 'rb') as pdf_file:
        pdf_reader = PyPDF2.PdfReader(pdf_file)
        pdf_writer = PyPDF2.PdfWriter()
        
        total_pages = len(pdf_reader.pages)
        
        # 创建要保留的页码集合
        keep_pages = set(range(total_pages))
        
        # 解析要删除的页码范围
        for page_range in delete_ranges:
            if '-' in page_range:
                # 处理页码范围(如1-3)
                start, end = map(int, page_range.split('-'))
                # 确保页码在有效范围内
                start = max(1, min(start, total_pages))
                end = max(start, min(end, total_pages))
                # 从要保留的页码集合中移除这个范围内的页码
                for i in range(start-1, end):
                    if i in keep_pages:
                        keep_pages.remove(i)
            else:
                # 处理单个页码(如5)
                page_num = int(page_range)
                page_num = max(1, min(page_num, total_pages))
                # 从要保留的页码集合中移除这个页码
                if (page_num-1) in keep_pages:
                    keep_pages.remove(page_num-1)
        
        # 将保留的页码按顺序添加到新PDF
        for page_idx in sorted(keep_pages):
            pdf_writer.add_page(pdf_reader.pages[page_idx])
        
        # 保存新PDF
        with open(output_path, 'wb') as output_file:
            pdf_writer.write(output_file)
    
    return output_path

4、PDF 转 Word 模块 这个很成熟也最简单

将 PDF 文件转换为 Word 文档(.docx 格式)。

使用 pdf2docx 库实现 PDF 到 Word 的转换,保留原始文档的格式和布局。

**关键函数**: `pdf_to_word()`

python 复制代码
def pdf_to_word(pdf_path, output_path):
    """
    将PDF转换为Word文档
    :param pdf_path: 输入PDF文件路径
    :param output_path: 输出Word文件路径
    :return: 输出文件路径
    """
    cv = Converter(pdf_path)
    cv.convert(output_path)
    cv.close()
    return output_path

5、PDF 转 Excel 模块

将 PDF 文件中的表格和文本转换为 Excel 表格。

为了更好的实现转换,所以使用了 pdfplumber 库,用来精确的提取 PDF 中的表格和文本内容,然后使用 openpyxl 库创建 Excel 文件并写入内容。

**关键函数**: `pdf_to_excel()`

python 复制代码
def pdf_to_excel(pdf_path, output_path):
    """
    将PDF转换为Excel文件
    :param pdf_path: 输入PDF文件路径
    :param output_path: 输出Excel文件路径
    :return: 输出文件路径
    """
    wb = openpyxl.Workbook()
    ws = wb.active
    
    # 使用pdfplumber提取表格
    with pdfplumber.open(pdf_path) as pdf:
        row = 1
        
        for page_num, page in enumerate(pdf.pages):
            # 提取页面中的所有表格
            tables = page.extract_tables()
            
            if tables:
                for table in tables:
                    for table_row in table:
                        if any(cell and cell.strip() for cell in table_row):
                            for col, cell_value in enumerate(table_row, 1):
                                if cell_value and cell_value.strip():
                                    ws.cell(row=row, column=col, value=cell_value.strip())
                            row += 1
            else:
                # 如果没有表格,提取文本并按行写入
                text = page.extract_text()
                if text:
                    lines = text.split('\n')
                    for line in lines:
                        if line.strip():
                            ws.cell(row=row, column=1, value=line.strip())
                            row += 1
            
            # 每页之间添加空行
            row += 2
    
    wb.save(output_path)
    return output_path

6、PDF 转 PPT 模块

将 PDF 文件转换为 PowerPoint 演示文稿(.pptx 格式)。

结合使用 pdfplumber 和 PyMuPDF 库提取 PDF 中的文本、表格和图片内容,然后使用 python-pptx 库创建 PPT 文件并添加内容。

**关键函数**: `pdf_to_ppt()`

python 复制代码
def pdf_to_ppt(pdf_path, output_path):
    """
    将PDF转换为PPT文件
    :param pdf_path: 输入PDF文件路径
    :param output_path: 输出PPT文件路径
    :return: 输出文件路径
    """
    prs = Presentation()
    
    # 使用pdfplumber和PyMuPDF结合提取内容
    pdf_document = fitz.open(pdf_path)
    
    with pdfplumber.open(pdf_path) as pdf_plumber:
        for page_num in range(len(pdf_document)):
            # 获取页面
            fitz_page = pdf_document[page_num]
            plumber_page = pdf_plumber.pages[page_num]
            
            # 使用空白幻灯片布局
            slide_layout = prs.slide_layouts[6]  # 空白布局
            slide = prs.slides.add_slide(slide_layout)
            
            current_top = prs.slide_height * 0.05
            content_height = prs.slide_height * 0.05
            
            # 提取并添加文本
            text = plumber_page.extract_text()
            if text:
                # 添加文本框到幻灯片顶部
                left = prs.slide_width * 0.05
                width = prs.slide_width * 0.9
                height = prs.slide_height * 0.15  # 文本高度
                
                textbox = slide.shapes.add_textbox(left, current_top, width, height)
                text_frame = textbox.text_frame
                text_frame.text = text
                current_top += height + prs.slide_height * 0.05
                content_height += height + prs.slide_height * 0.05
            
            # 提取并添加表格
            tables = plumber_page.extract_tables()
            if tables:
                for table in tables:
                    # 计算表格大小
                    table_height = prs.slide_height * 0.2  # 表格高度
                    
                    # 确保表格不会超出幻灯片底部
                    if current_top + table_height > prs.slide_height * 0.9:
                        # 如果超出,创建新幻灯片
                        slide_layout = prs.slide_layouts[6]
                        slide = prs.slides.add_slide(slide_layout)
                        current_top = prs.slide_height * 0.05
                    
                    left = prs.slide_width * 0.1
                    width = prs.slide_width * 0.8
                    
                    # 添加表格
                    table_shape = slide.shapes.add_table(
                        rows=len(table),
                        cols=len(table[0]) if table else 1,
                        left=left,
                        top=current_top,
                        width=width,
                        height=table_height
                    )
                    
                    # 填充表格内容
                    table_object = table_shape.table
                    for row_idx, table_row in enumerate(table):
                        for col_idx, cell_value in enumerate(table_row):
                            if cell_value and cell_value.strip():
                                table_object.cell(row_idx, col_idx).text = cell_value.strip()
                    
                    current_top += table_height + prs.slide_height * 0.05
                    content_height += table_height + prs.slide_height * 0.05
            
            # 提取并添加图片
            images = fitz_page.get_images(full=True)
            
            print(f"第 {page_num+1} 页发现 {len(images)} 张图片")
            
            if images:
                for img_index, img in enumerate(images):
                    try:
                        xref = img[0]
                        base_image = pdf_document.extract_image(xref)
                        image_bytes = base_image["image"]
                        image_ext = base_image["ext"]
                        
                        print(f"  处理图片 {img_index+1}/{len(images)}: {image_ext}")
                        
                        # 使用PIL处理图片,确保格式兼容
                        image_stream = io.BytesIO(image_bytes)
                        pil_image = Image.open(image_stream)
                        
                        # 如果图片格式不是RGB,转换为RGB
                        if pil_image.mode != 'RGB':
                            pil_image = pil_image.convert('RGB')
                        
                        # 将图片保存为临时JPEG格式
                        temp_stream = io.BytesIO()
                        pil_image.save(temp_stream, format='JPEG', quality=95)
                        temp_stream.seek(0)
                        
                        # 获取原始图片大小
                        img_width, img_height = pil_image.size
                        aspect_ratio = img_width / img_height
                        
                        print(f"  原始图片大小: {img_width}x{img_height}, 宽高比: {aspect_ratio:.2f}")
                        
                        # 设置幻灯片可用区域
                        available_width = prs.slide_width * 0.9
                        available_height = prs.slide_height * 0.9
                        
                        # 计算图片在幻灯片上的最佳大小(保持宽高比)
                        scale_factor = min(available_width / img_width, available_height / img_height)
                        new_width = img_width * scale_factor
                        new_height = img_height * scale_factor
                        
                        print(f"  调整后图片大小: {int(new_width)}x{int(new_height)}")
                        
                        # 计算图片位置(居中)
                        left = (prs.slide_width - new_width) / 2
                        top = (prs.slide_height - new_height) / 2
                        
                        print(f"  图片位置: ({int(left)}, {int(top)})")
                        
                        # 确保图片大小合理
                        if new_width > 0 and new_height > 0:
                            # 添加图片到幻灯片
                            slide.shapes.add_picture(
                                temp_stream, 
                                left, 
                                top, 
                                width=new_width, 
                                height=new_height
                            )
                            print(f"  ✅ 图片添加成功")
                        else:
                            print(f"  ❌ 图片大小无效")
                    except Exception as e:
                        # 如果图片处理失败,记录详细错误
                        print(f"  ❌ 处理图片时出错: {e}")
                        import traceback
                        traceback.print_exc()
                        pass
    
    # 关闭PDF文档
    pdf_document.close()
    
    # 保存PPT文件
    prs.save(output_path)
    return output_path

另外还有多文件选择列表的和排序、删除,都是js的不多说了。

javascript 复制代码
// 删除文件
function deleteFile(index) {
    // 从数组中移除文件
    uploadedFiles.splice(index, 1);
    
    // 清空文件列表并重新显示
    fileList.innerHTML = '';
    uploadedFiles.forEach((fileInfo, idx) => {
        displayFile(fileInfo, idx);
    });
    
    // 更新执行按钮状态
    checkExecuteBtn();
}

// 排序文件(上下移动)
function moveFile(currentIndex, direction) {
    if (direction === 'up' && currentIndex > 0) {
        // 向上移动
        [uploadedFiles[currentIndex], uploadedFiles[currentIndex - 1]] = 
            [uploadedFiles[currentIndex - 1], uploadedFiles[currentIndex]];
    } else if (direction === 'down' && currentIndex < uploadedFiles.length - 1) {
        // 向下移动
        [uploadedFiles[currentIndex], uploadedFiles[currentIndex + 1]] = 
            [uploadedFiles[currentIndex + 1], uploadedFiles[currentIndex]];
    }
    
    // 重新显示文件列表
    fileList.innerHTML = '';
    uploadedFiles.forEach((fileInfo, idx) => {
        displayFile(fileInfo, idx);
    });

四、主要依赖

  • Flask>=2.0.0

  • PyPDF2>=2.0.0

  • pdf2docx>=0.5.0

  • openpyxl>=3.0.0

  • python-pptx>=0.6.0

  • PyMuPDF>=1.20.0

  • pdfplumber>=0.6.0

  • Pillow>=8.0.0

  • Wand>=0.6.0

为了兼容Windows7系统上部署使用,用的版本都比较低,python为3.8版本。

五、目录结构

也就这些了,有兴趣的看看,欢迎讨论。

相关推荐
Kagol9 小时前
深入浅出 TinyEditor 富文本编辑器系列2:快速开始
前端·typescript·开源
ASD123asfadxv9 小时前
【技术实践】基于YOLO11-Seg与DySnakeConv的14种杂草智能识别系统
python
木土雨成小小测试员9 小时前
Python测试开发之前端二
javascript·python·jquery
小二·9 小时前
Python Web 开发进阶实战:Flask-Login 用户认证与权限管理 —— 构建多用户待办事项系统
前端·python·flask
两万五千个小时9 小时前
Claude Code 中的子 Agent 派生实现:Task Tool 完全指南
人工智能·python
浩瀚之水_csdn9 小时前
python字符串解析
前端·数据库·python
liu****9 小时前
机器学习-特征降维
人工智能·python·机器学习·python基础·特征降维
程序猿阿伟10 小时前
《Python生态事件溯源与CQRS轻量化落地指南》
大数据·python·微服务
王夏奇10 小时前
python在汽车电子行业中应用2—具体包的介绍和使用
网络·python·汽车