Hive 项目 PDF 高级处理完全指南:pypdfium2、pdf-lib、pdfjs-dist 与命令行工具实战

Hive 项目 PDF 高级处理完全指南:pypdfium2、pdf-lib、pdfjs-dist 与命令行工具实战

【免费下载链接】hive Multi-Agent Harness for Production AI 【免费下载链接】hive 项目地址: https://gitcode.com/gh_mirrors/hive48/hive

导读

本文是 Hive 项目 内置 Agent Skill hive.pdf(SKILL.md)的进阶配套参考,以仓库中的 REFERENCE.md 为主体展开。它面向需要在 Agent 工作流中处理复杂 PDF 任务的开发者,覆盖 Python 侧的高性能渲染库 pypdfium2、JavaScript 侧的 pdf-lib 与 pdfjs-dist、poppler-utils 与 qpdf 的命令行高级用法,以及批量处理、性能优化与故障排查的完整方案。读完本文,你将掌握从"快速文本抽取"到"复杂报表生成、合并拆分、加密解密、图像提取、扫描件 OCR"的全栈 PDF 处理能力,并了解这些能力如何与 Hive 框架中的 pdf_read 工具及大型 PDF 的 agentic 处理路径相互配合。


1. 定位:REFERENCE.md 在 Hive Skill 体系中的角色

在 Hive 的框架中,PDF 处理能力以 Agent Skill 的形式内置于框架,由 DefaultSkillManager 注册为 hive.pdf,其目录下包含两个文档:

  • SKILL.md:主技能文档,覆盖 pypdf、pdfplumber、reportlab 等基础操作,以及 OCR、水印、加密等常见任务;
  • REFERENCE.md:进阶参考文档(本文主体),覆盖主文档未展开的高级特性、更多三方库与复杂工作流。

从源码结构看,hive.pdf 被设计为"按需加载"的参考型 Skill——其正文不会常驻系统提示词,而是等 Agent 依据描述触发渐进式披露后才注入(见 defaults.py 中关于 pdf 是 reference skill 的注释)。因此 REFERENCE.md 实际承担着"专家级备查手册"的职责:当基础技能不足以应对高性能渲染、浏览器端渲染、复杂页面操作或加密 PDF 时,Agent 与开发者可以翻到这里获取完整的进阶方案。

在实际运行时,Hive 的 pdf_read 工具(pdf_read_tool.py)已内置对 pdfplumber 的调用,并对扫描页输出 needs_vision 标记;上传路径上,小 PDF 会内联为多模态 file 块,超过 10 MB 阈值(LARGE_PDF_THRESHOLD_BYTES = 10 * 1024 * 1024,见 routes_execution.py)的大 PDF 则进入"仅路径"的 agentic 模式,提示 Agent 用 pdf_read(file_path=..., pages='1-N') 分段加载。本文的进阶工具恰好可以补足这些内置能力之外的场景:渲染、创建、合并、加密与故障恢复。


2. pypdfium2:高性能 PDF 渲染与图像生成

2.1 为什么选择 pypdfium2

pypdfium2 是 Chromium 的 PDF 引擎 PDFium 的 Python 绑定,许可证为 Apache/BSD(宽松可商用)。它最突出的能力是快速渲染与图像生成,常被用作 PyMuPDF(fitz)的替代方案,适合需要把 PDF 页面转成位图供视觉模型、OCR 或缩略图流水线使用的场景。其底层直接复用 Chromium 的排版渲染管线,因此渲染质量与 Chromium 内置 PDF 阅读器一致。

2.2 渲染 PDF 页面为图像

基础用法非常直接:打开 PdfDocument 后按索引取页,调用 page.render() 得到位图,再经 bitmap.to_pil() 转换为 PIL Image 保存:

import pypdfium2 as pdfium
from PIL import Image

# Load PDF
pdf = pdfium.PdfDocument("document.pdf")

# Render page to image
page = pdf[0]  # First page
bitmap = page.render(
    scale=2.0,  # Higher resolution
    rotation=0  # No rotation
)

# Convert to PIL Image
img = bitmap.to_pil()
img.save("page_1.png", "PNG")

# Process multiple pages
for i, page in enumerate(pdf):
    bitmap = page.render(scale=1.5)
    img = bitmap.to_pil()
    img.save(f"page_{i+1}.jpg", "JPEG", quality=90)

关键参数说明:

  • scale:渲染倍率,1.0 为页面原始尺寸(72 DPI 基准),2.0/3.0 常用于需要放大细节、表格识别或喂给视觉模型的高清场景。倍率越高内存与耗时越大,预览用 1.0~1.5、最终输出用 2.0~3.0 是比较均衡的实践(REFERENCE.md 的性能建议也明确"low resolution for previews, high resolution for final output")。
  • rotation:旋转角度(0/90/180/270),用于修正扫描件方向。
  • 输出格式由保存时的扩展名决定,JPEG 可附加 quality 参数控制压缩质量。

2.3 用 pypdfium2 抽取文本

渲染之外,pypdfium2 也提供基础的文本抽取 API page.get_text(),适合快速获取整页文本长度或做概览:

import pypdfium2 as pdfium

pdf = pdfium.PdfDocument("document.pdf")
for i, page in enumerate(pdf):
    text = page.get_text()
    print(f"Page {i+1} text length: {len(text)} chars")

需要说明的是,pypdfium2 的文本抽取属于"够用"级别;对于带坐标的结构化文本或复杂表格,REFERENCE.md 与 Hive 内置的 pdf_read 工具都更推荐 pdfplumber(其底层基于 pdfminer,表格与版式还原更好,见 pdf_read_tool.py 的说明)。


3. JavaScript 库:pdf-lib 与 pdfjs-dist

3.1 pdf-lib:创建与修改 PDF(MIT 许可证)

pdf-lib 是一个可在任意 JavaScript 环境(Node、浏览器、Deno 等)中创建和修改 PDF 的库,纯 JS 实现、无原生依赖,适合在 Agent 的 Node 工具链或前端中直接使用。

加载并操作已有 PDF:读取字节流 → PDFDocument.load → 查询页数、新增页面、绘制文本 → save() 落盘:

import { PDFDocument } from 'pdf-lib';
import fs from 'fs';

async function manipulatePDF() {
    // Load existing PDF
    const existingPdfBytes = fs.readFileSync('input.pdf');
    const pdfDoc = await PDFDocument.load(existingPdfBytes);

    // Get page count
    const pageCount = pdfDoc.getPageCount();
    console.log(`Document has ${pageCount} pages`);

    // Add new page
    const newPage = pdfDoc.addPage([600, 400]);
    newPage.drawText('Added by pdf-lib', {
        x: 100,
        y: 300,
        size: 16
    });

    // Save modified PDF
    const pdfBytes = await pdfDoc.save();
    fs.writeFileSync('modified.pdf', pdfBytes);
}

从零创建复杂 PDF:通过 embedFont 嵌入标准字体(StandardFonts.Helvetica 等),用 drawText 控制样式,用 drawRectangle 绘制色块,再以坐标循环拼出表格效果,可快速生成发票、报表等结构简单但样式多样的文档:

import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
import fs from 'fs';

async function createPDF() {
    const pdfDoc = await PDFDocument.create();

    // Add fonts
    const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
    const helveticaBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);

    // Add page
    const page = pdfDoc.addPage([595, 842]); // A4 size
    const { width, height } = page.getSize();

    // Add text with styling
    page.drawText('Invoice #12345', {
        x: 50,
        y: height - 50,
        size: 18,
        font: helveticaBold,
        color: rgb(0.2, 0.2, 0.8)
    });

    // Add rectangle (header background)
    page.drawRectangle({
        x: 40,
        y: height - 100,
        width: width - 80,
        height: 30,
        color: rgb(0.9, 0.9, 0.9)
    });

    // Add table-like content
    const items = [
        ['Item', 'Qty', 'Price', 'Total'],
        ['Widget', '2', '$50', '$100'],
        ['Gadget', '1', '$75', '$75']
    ];

    let yPos = height - 150;
    items.forEach(row => {
        let xPos = 50;
        row.forEach(cell => {
            page.drawText(cell, {
                x: xPos,
                y: yPos,
                size: 12,
                font: helveticaFont
            });
            xPos += 120;
        });
        yPos -= 25;
    });

    const pdfBytes = await pdfDoc.save();
    fs.writeFileSync('created.pdf', pdfBytes);
}

A4 页面尺寸为 595×842 点(pt);所有坐标以页面左下角为原点,这与 PDF 规范一致,也是新手最易踩坑之处。

高级合并与拆分:copyPages 支持把源文档的任意页面(含指定索引数组)复制进新文档,配合 getPageIndices() 可实现整本合并或选择性抽取:

import { PDFDocument } from 'pdf-lib';
import fs from 'fs';

async function mergePDFs() {
    // Create new document
    const mergedPdf = await PDFDocument.create();

    // Load source PDFs
    const pdf1Bytes = fs.readFileSync('doc1.pdf');
    const pdf2Bytes = fs.readFileSync('doc2.pdf');

    const pdf1 = await PDFDocument.load(pdf1Bytes);
    const pdf2 = await PDFDocument.load(pdf2Bytes);

    // Copy pages from first PDF
    const pdf1Pages = await mergedPdf.copyPages(pdf1, pdf1.getPageIndices());
    pdf1Pages.forEach(page => mergedPdf.addPage(page));

    // Copy specific pages from second PDF (pages 0, 2, 4)
    const pdf2Pages = await mergedPdf.copyPages(pdf2, [0, 2, 4]);
    pdf2Pages.forEach(page => mergedPdf.addPage(page));

    const mergedPdfBytes = await mergedPdf.save();
    fs.writeFileSync('merged.pdf', mergedPdfBytes);
}

此外,REFERENCE.md 的性能建议指出:pdf-lib 在表单(Form)结构的保留上优于多数替代方案,处理带表单的 PDF 时优先考虑它,并在处理前先校验表单字段。

3.2 pdfjs-dist:浏览器端渲染与解析(Apache 许可证)

pdfjs-dist 即 Mozilla 的 PDF.js,是浏览器端渲染 PDF 的事实标准库。其核心是多线程架构——渲染任务交给 Web Worker,避免阻塞主线程。

基础加载与渲染:关键一步是配置 workerSrc(指向 pdf.worker.js),然后 getDocument 异步加载,getPage + getViewport({ scale }) 得到视口尺寸,最后在 canvas 上执行渲染:

import * as pdfjsLib from 'pdfjs-dist';

// Configure worker (important for performance)
pdfjsLib.GlobalWorkerOptions.workerSrc = './pdf.worker.js';

async function renderPDF() {
    // Load PDF
    const loadingTask = pdfjsLib.getDocument('document.pdf');
    const pdf = await loadingTask.promise;

    console.log(`Loaded PDF with ${pdf.numPages} pages`);

    // Get first page
    const page = await pdf.getPage(1);
    const viewport = page.getViewport({ scale: 1.5 });

    // Render to canvas
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;

    const renderContext = {
        canvasContext: context,
        viewport: viewport
    };

    await page.render(renderContext).promise;
    document.body.appendChild(canvas);
}

注意 getPage(1) 的索引从 1 开始,与 pypdfium2 的 pdf[0](从 0 开始)不同。

带坐标的文本抽取:getTextContent() 返回的 items 中,每个文本项带有 transform 矩阵(transform[4]、transform[5] 即 x、y 平移量)与宽高信息,可据此重建带坐标的文本层,用于高亮、检索、点击定位等高级交互:

import * as pdfjsLib from 'pdfjs-dist';

async function extractText() {
    const loadingTask = pdfjsLib.getDocument('document.pdf');
    const pdf = await loadingTask.promise;

    let fullText = '';

    // Extract text from all pages
    for (let i = 1; i <= pdf.numPages; i++) {
        const page = await pdf.getPage(i);
        const textContent = await page.getTextContent();

        const pageText = textContent.items
            .map(item => item.str)
            .join(' ');

        fullText += `\n--- Page ${i} ---\n${pageText}`;

        // Get text with coordinates for advanced processing
        const textWithCoords = textContent.items.map(item => ({
            text: item.str,
            x: item.transform[4],
            y: item.transform[5],
            width: item.width,
            height: item.height
        }));
    }

    console.log(fullText);
    return fullText;
}

注释与表单抽取:page.getAnnotations() 返回注释对象数组,subtype 标识类型(Link、Text、Widget 等),contents 为注释内容,rect 为坐标矩形,可用于实现批注清单、表单字段盘点等能力:

import * as pdfjsLib from 'pdfjs-dist';

async function extractAnnotations() {
    const loadingTask = pdfjsLib.getDocument('annotated.pdf');
    const pdf = await loadingTask.promise;

    for (let i = 1; i <= pdf.numPages; i++) {
        const page = await pdf.getPage(i);
        const annotations = await page.getAnnotations();

        annotations.forEach(annotation => {
            console.log(`Annotation type: ${annotation.subtype}`);
            console.log(`Content: ${annotation.contents}`);
            console.log(`Coordinates: ${JSON.stringify(annotation.rect)}`);
        });
    }
}

4. 命令行工具的高级操作

4.1 poppler-utils:文本、图像与渲染三件套

poppler-utils 是 Linux 桌面与 CI 环境中最常用的 PDF 工具集,Hive 桌面运行时的 pdf_read 失败兜底方案(见 pdf_read_tool.py)也明确依赖其中的 pdftotext、pdfinfo、pdfimages、pdfgrep。

带包围盒坐标的文本抽取——结构化数据提取的关键:

# Extract text with bounding box coordinates (essential for structured data)
pdftotext -bbox-layout document.pdf output.xml

# The XML output contains precise coordinates for each text element

-bbox-layout 输出的 XML 为每个文本元素标注精确坐标,是解析双栏论文、发票、简历等版式化文档的基础;对纯文本需求,-layout(保留版式)与 -f/-l(页码范围)则更快。

高级图像转换:

# Convert to PNG images with specific resolution
pdftoppm -png -r 300 document.pdf output_prefix

# Convert specific page range with high resolution
pdftoppm -png -r 600 -f 1 -l 3 document.pdf high_res_pages

# Convert to JPEG with quality setting
pdftoppm -jpeg -jpegopt quality=85 -r 200 document.pdf jpeg_output

-r 指定 DPI(300 为印刷级,600 为超高清扫描),-f/-l 限定页范围,-jpegopt 控制 JPEG 质量。

提取内嵌图像:

# Extract all embedded images with metadata
pdfimages -j -p document.pdf page_images

# List image info without extracting
pdfimages -list document.pdf

# Extract images in their original format
pdfimages -all document.pdf images/img
  • -j:按 JPEG 原始格式写出(避免重编码损失);
  • -p:文件名携带页码信息;
  • -list:仅打印图像清单(位置、尺寸、色彩空间),用于先盘点再决定是否提取;
  • -all:按原始格式提取所有图像。

4.2 qpdf:页面操作、优化修复与加密

qpdf 以结构级操作见长,是 PDF 修复与页面操纵的瑞士军刀(Apache 许可证)。

复杂页面操作:

# Split PDF into groups of pages
qpdf --split-pages=3 input.pdf output_group_%02d.pdf

# Extract specific pages with complex ranges
qpdf input.pdf --pages input.pdf 1,3-5,8,10-end -- extracted.pdf

# Merge specific pages from multiple PDFs
qpdf --empty --pages doc1.pdf 1-3 doc2.pdf 5-7 doc3.pdf 2,4 -- combined.pdf

--pages 支持逗号、连字符、end 关键字的混合范围语法,甚至可在一次命令中跨多个源文件选页合并,%02d 为拆分输出文件名提供补零序号。

优化与修复:

# Optimize PDF for web (linearize for streaming)
qpdf --linearize input.pdf optimized.pdf

# Remove unused objects and compress
qpdf --optimize-level=all input.pdf compressed.pdf

# Attempt to repair corrupted PDF structure
qpdf --check input.pdf
qpdf --fix-qdf damaged.pdf repaired.pdf

# Show detailed PDF structure for debugging
qpdf --show-all-pages input.pdf > structure.txt
  • --linearize:生成线性化(Web 优化)PDF,支持边下载边显示;
  • --optimize-level=all:清理无用对象并压缩,显著减小体积;
  • --check:只读体检,报告结构问题;
  • --fix-qdf:修复损坏结构(通常配合 --qdf 先规范化再修复)。

高级加密:

# Add password protection with specific permissions
qpdf --encrypt user_pass owner_pass 256 --print=none --modify=none -- input.pdf encrypted.pdf

# Check encryption status
qpdf --show-encryption encrypted.pdf

# Remove password protection (requires password)
qpdf --password=secret123 --decrypt encrypted.pdf decrypted.pdf

--encrypt 第一个参数为用户密码(打开密码),第二个为所有者密码(权限密码),256 指定 AES-256 加密强度,--print=none --modify=none 限制打印与修改权限。-- 用于分隔选项与输入文件。


5. Python 高级技巧:pdfplumber 与 reportlab

5.1 pdfplumber:坐标级文本与复杂表格

pdfplumber 基于 pdfminer.six,提供字符级(chars)坐标访问。REFERENCE.md 演示了三种进阶能力:

带坐标的文本抽取:逐字符输出 text 及左下角坐标 x0/y0;within_bbox((left, top, right, bottom)) 可按包围盒截取指定区域文本,适合处理固定版式的表单或合同条款区域:

import pdfplumber

with pdfplumber.open("document.pdf") as pdf:
    page = pdf.pages[0]

    # Extract all text with coordinates
    chars = page.chars
    for char in chars[:10]:  # First 10 characters
        print(f"Char: '{char['text']}' at x:{char['x0']:.1f} y:{char['y0']:.1f}")

    # Extract text by bounding box (left, top, right, bottom)
    bbox_text = page.within_bbox((100, 100, 400, 200)).extract_text()

自定义表格提取设置:对于无边框、不规则或压线表格,默认策略可能失效。通过 table_settings 可指定垂直/水平线检测策略(lines/text/explicit)、吸附容差与交叉点容差:

import pdfplumber
import pandas as pd

with pdfplumber.open("complex_table.pdf") as pdf:
    page = pdf.pages[0]

    # Extract tables with custom settings for complex layouts
    table_settings = {
        "vertical_strategy": "lines",
        "horizontal_strategy": "lines",
        "snap_tolerance": 3,
        "intersection_tolerance": 15
    }
    tables = page.extract_tables(table_settings)

    # Visual debugging for table extraction
    img = page.to_image(resolution=150)
    img.save("debug_layout.png")

snap_tolerance 控制相近线条被吸附为同一条线的距离阈值,intersection_tolerance 控制交点判定容差;调整这两个参数通常是解决"表格断裂/错位"的第一步。page.to_image() 可输出版式调试图,用于肉眼核对表格识别结果——这与 Hive 中扫描页需要视觉兜底的思路一致(needs_vision 标记,见 pdf_read_tool.py)。

5.2 reportlab:带表格的专业报表生成

reportlab 的 Platypus 框架(SimpleDocTemplate + Flowable 对象)适合生成多页结构化报表。其 Table 配合 TableStyle 可实现表头着色、居中对齐、加粗、底纹与网格线等专业排版:

from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import colors

# Sample data
data = [
    ['Product', 'Q1', 'Q2', 'Q3', 'Q4'],
    ['Widgets', '120', '135', '142', '158'],
    ['Gadgets', '85', '92', '98', '105']
]

# Create PDF with table
doc = SimpleDocTemplate("report.pdf")
elements = []

# Add title
styles = getSampleStyleSheet()
title = Paragraph("Quarterly Sales Report", styles['Title'])
elements.append(title)

# Add table with advanced styling
table = Table(data)
table.setStyle(TableStyle([
    ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
    ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
    ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
    ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
    ('FONTSIZE', (0, 0), (-1, 0), 14),
    ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
    ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
    ('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
elements.append(table)

doc.build(elements)

TableStyle 命令采用 (操作, 起始单元格, 结束单元格, 参数...) 四元组形式,(0, 0), (-1, -1) 表示从左上角到右下角的全表范围。


6. 复杂工作流实战

6.1 从 PDF 中提取图片/图形

  • 方法一:pdfimages(最快)——直接抽取内嵌位图,无重编码,速度远超渲染方案:
# Extract all images with original quality
pdfimages -all document.pdf images/img
  • 方法二:pypdfium2 + 图像处理——当 PDF 中的"图"是矢量绘制或由文本组成、无内嵌位图时,只能渲染页面再做检测。REFERENCE.md 给出了思路骨架:高倍率渲染 → 转 numpy 数组 → 以非白区域构建掩码 → 找轮廓取包围盒:
import pypdfium2 as pdfium
from PIL import Image
import numpy as np

def extract_figures(pdf_path, output_dir):
    pdf = pdfium.PdfDocument(pdf_path)

    for page_num, page in enumerate(pdf):
        # Render high-resolution page
        bitmap = page.render(scale=3.0)
        img = bitmap.to_pil()

        # Convert to numpy for processing
        img_array = np.array(img)

        # Simple figure detection (non-white regions)
        mask = np.any(img_array != [255, 255, 255], axis=2)

        # Find contours and extract bounding boxes
        # (This is simplified - real implementation would need more sophisticated detection)

        # Save detected figures
        # ... implementation depends on specific needs

注意 REFERENCE.md 明确提示:真实场景需更精细的检测(如结合版面分析区分图表与文本块),上述代码是起点而非成品。

6.2 批量 PDF 处理(含错误处理)

使用 pypdf 的 PdfReader/PdfWriter,配合 glob 遍历与逐文件 try/except,实现单文件失败不中断全批的健壮流水线——合并或批量抽文本两个操作共用一套框架:

import os
import glob
from pypdf import PdfReader, PdfWriter
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def batch_process_pdfs(input_dir, operation='merge'):
    pdf_files = glob.glob(os.path.join(input_dir, "*.pdf"))

    if operation == 'merge':
        writer = PdfWriter()
        for pdf_file in pdf_files:
            try:
                reader = PdfReader(pdf_file)
                for page in reader.pages:
                    writer.add_page(page)
                logger.info(f"Processed: {pdf_file}")
            except Exception as e:
                logger.error(f"Failed to process {pdf_file}: {e}")
                continue

        with open("batch_merged.pdf", "wb") as output:
            writer.write(output)

    elif operation == 'extract_text':
        for pdf_file in pdf_files:
            try:
                reader = PdfReader(pdf_file)
                text = ""
                for page in reader.pages:
                    text += page.extract_text()

                output_file = pdf_file.replace('.pdf', '.txt')
                with open(output_file, 'w', encoding='utf-8') as f:
                    f.write(text)
                logger.info(f"Extracted text from: {pdf_file}")

            except Exception as e:
                logger.error(f"Failed to extract text from {pdf_file}: {e}")
                continue

6.3 高级裁剪(Mediabox)

通过直接修改页面的 mediabox 四边坐标(单位:点),可裁剪掉页眉页脚或广告区域:

from pypdf import PdfWriter, PdfReader

reader = PdfReader("input.pdf")
writer = PdfWriter()

# Crop page (left, bottom, right, top in points)
page = reader.pages[0]
page.mediabox.left = 50
page.mediabox.bottom = 50
page.mediabox.right = 550
page.mediabox.top = 750

writer.add_page(page)
with open("cropped.pdf", "wb") as output:
    writer.write(output)

7. 性能优化要点

REFERENCE.md 针对不同场景给出了明确的选型建议,整理如下:

场景推荐方案原因
大文件处理流式/分块,避免整本载入内存;qpdf --split-pages 拆分;pypdfium2 逐页处理大文件整载易 OOM,拆分后可并行
纯文本抽取pdftotext -bbox-layout 最快;结构化数据与表格用 pdfplumber;避免对超大文档用 pypdf.extract_text()文本模式快于渲染;pypdf 逐页抽取超慢
图像提取pdfimages 远快于页面渲染;预览用低分辨率、最终输出用高分辨率直接取内嵌位图免渲染
表单填充pdf-lib 保留表单结构优于多数替代结构保留好,处理前先校验字段
内存管理分块处理,如每次 10 页写入一个 chunk 文件控制峰值内存与上下文占用

其中"分块处理大 PDF"给出了完整可运行示例:按 chunk_size 把大文件切成若干小块逐个写出,后续可对每块单独抽文本或喂给 Agent(这正好与 Hive 中大 PDF 的 agentic 路径——pdf_read(file_path=..., pages='1-N') 分段加载——形成呼应,见 routes_execution.py):

# Process PDFs in chunks
def process_large_pdf(pdf_path, chunk_size=10):
    reader = PdfReader(pdf_path)
    total_pages = len(reader.pages)

    for start_idx in range(0, total_pages, chunk_size):
        end_idx = min(start_idx + chunk_size, total_pages)
        writer = PdfWriter()

        for i in range(start_idx, end_idx):
            writer.add_page(reader.pages[i])

        # Process chunk
        with open(f"chunk_{start_idx//chunk_size}.pdf", "wb") as output:
            writer.write(output)

8. 常见问题排查

8.1 加密 PDF

用 pypdf 的 is_encrypted 探测 + decrypt(password) 解锁;注意解密失败时抛异常,需捕获处理。这与 Hive pdf_read 工具对加密 PDF 的兜底建议一致(qpdf --decrypt --password=<pw> 后重试,见 pdf_read_tool.py):

# Handle password-protected PDFs
from pypdf import PdfReader

try:
    reader = PdfReader("encrypted.pdf")
    if reader.is_encrypted:
        reader.decrypt("password")
except Exception as e:
    print(f"Failed to decrypt: {e}")

8.2 损坏 PDF

先用 qpdf --check 体检定位问题,再尝试 --replace-input 原地修复(比输出新文件更省磁盘操作):

# Use qpdf to repair
qpdf --check corrupted.pdf
qpdf --replace-input corrupted.pdf

8.3 文本抽取异常(扫描件兜底 OCR)

当页面无文本层(扫描件)时,pypdf/pdfplumber 抽取结果为空或极短。REFERENCE.md 给出 pytesseract + pdf2image 的 OCR 兜底方案;Hive 中 pdf_read 工具则通过 _VISION_TEXT_THRESHOLD = 200 字符阈值将低文本页标记为 needs_vision,由视觉模型或 attach_file 直接读图(见 pdf_read_tool.py):

# Fallback to OCR for scanned PDFs
import pytesseract
from pdf2image import convert_from_path

def extract_text_with_ocr(pdf_path):
    images = convert_from_path(pdf_path)
    text = ""
    for i, image in enumerate(images):
        text += pytesseract.image_to_string(image)
    return text

9. 与 Hive 运行时集成的落地建议

结合仓库源码,以上进阶能力在 Hive 中有几条明确的落地路径:

  1. 文本抽取优先走内置 pdf_read:工具已封装 pdfplumber 的文本/表格抽取、页范围过滤、多文件批处理与解密支持,并对扫描页输出 needs_vision 信号,无需重复造轮子;其完整参数行为(pages、max_pages、include_metadata 等)有 test_pdf_read_tool.py 覆盖验证(如 max_pages 上下限钳制、pages="1-20" 超限截断并返回 truncation 元数据)。
  2. 大 PDF 走 agentic 路径:超过 10 MB 的 PDF 不上传字节,而是以路径形式提示 Agent 用 pdf_read(file_path=..., pages='1-N') 分段加载(初始建议页数按 min(50, page_count) 计算),避免巨型文档撑爆上下文——routes_execution.py 的注释形象地称之为"calculus-textbook 场景"。
  3. 渲染、生成、加密、修复等场景按需选用本文工具:渲染用 pypdfium2,浏览器端展示用 pdfjs-dist,生成报表用 reportlab/pdf-lib,结构级操作(拆分/合并/加密/修复)用 qpdf,位图提取用 pdfimages,扫描件 OCR 用 pdf2image + pytesseract。

附:各库许可证速查

REFERENCE.md 结尾汇总了全部涉及库的许可证,便于合规选用:

  • pypdf:BSD License
  • pdfplumber:MIT License
  • pypdfium2:Apache/BSD License
  • reportlab:BSD License
  • poppler-utils:GPL-2 License
  • qpdf:Apache License
  • pdf-lib:MIT License
  • pdfjs-dist:Apache License

其中 GPL-2 的 poppler-utils 与其余宽松许可库需分开评估分发场景;若在商业闭源产品中分发二进制,优先选择 MIT/Apache/BSD 的纯库方案(pdf-lib、pdfjs-dist、pypdfium2 等)。

【免费下载链接】hive Multi-Agent Harness for Production AI 【免费下载链接】hive 项目地址: https://gitcode.com/gh_mirrors/hive48/hive

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值