全能文档处理工具箱 (Doc Toolkit)
功能总览
| 分类 | 功能 | 支持格式 |
|---|---|---|
| 文档合并 | 多文件合并为一个 | DOCX、XLSX、PPTX、PDF |
| 文档拆分 | 按页/按Sheet/按节拆分 | DOCX、XLSX、PPTX、PDF |
| 格式转换 | 任意格式互转 | DOCX、PDF、XLSX、PPTX、MD、HTML、TXT、CSV、图片 |
| 图片压缩 | 批量压缩图片文件大小 | PNG、JPG、JPEG、WEBP |
| 智能抠图 | AI 去除图片背景 | PNG、JPG、JPEG |
| PDF 工具 | 水印、加密、旋转、裁剪、提取图片 | |
| 图片提取 | 从文档中提取嵌入图片 | DOCX、PPTX、PDF、XLSX |
一、文档合并
1.1 Word 文档合并(DOCX)
将多个 Word 文档按顺序合并为一个文件。
from docx import Document
from docx.oxml.ns import qn
import copy, os
def merge_docx(files, output_path):
"""合并多个 DOCX 文件,保留格式"""
merged = Document(files[0])
for f in files[1:]:
doc = Document(f)
# 每个文档之间添加分页符
merged.add_page_break()
for element in doc.element.body:
merged.element.body.append(copy.deepcopy(element))
merged.save(output_path)
return output_path
# 使用示例
files = ["doc1.docx", "doc2.docx", "doc3.docx"]
merge_docx(files, "merged.docx")
1.2 Excel 文件合并(XLSX)
将多个 Excel 文件合并到一个工作簿的不同 Sheet,或按行合并到同一个 Sheet。
import pandas as pd
import os
# 方式一:每个文件作为一个 Sheet 合并到一个工作簿
def merge_xlsx_as_sheets(files, output_path):
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
for f in files:
name = os.path.splitext(os.path.basename(f))[0]
# Sheet 名最长31字符,去除非法字符
safe_name = name[:31].replace('/', '_').replace('\\', '_').replace('*', '_').replace('?', '_').replace('[', '_').replace(']', '_')
all_sheets = pd.read_excel(f, sheet_name=None)
for sheet_name, df in all_sheets.items():
unique_name = f"{safe_name}_{sheet_name}"[:31]
df.to_excel(writer, sheet_name=unique_name, index=False)
# 方式二:相同结构的表按行合并(纵向追加)
def merge_xlsx_concat(files, output_path, header_file=None):
dfs = []
for f in files:
df = pd.read_excel(f)
dfs.append(df)
result = pd.concat(dfs, ignore_index=True)
result.to_excel(output_path, index=False)
return len(result)
# 使用示例
files = ["data1.xlsx", "data2.xlsx", "data3.xlsx"]
merge_xlsx_as_sheets(files, "merged_all_sheets.xlsx")
merge_xlsx_concat(files, "merged_concat.xlsx")
1.3 PPT 文件合并(PPTX)
将多个 PPT 文件合并为一个演示文稿。
from pptx import Presentation
from pptx.util import Inches, Pt
import copy
def merge_pptx(files, output_path):
"""合并多个 PPTX 文件,每个文件的幻灯片追加到末尾"""
merged = Presentation(files[0])
for f in files[1:]:
prs = Presentation(f)
# 记录源文件的幻灯片宽高
src_width = prs.slide_width
src_height = prs.slide_height
for slide in prs.slides:
# 创建新的 layout(使用默认布局)
layout = merged.slide_layouts[6] # 空白布局
new_slide = merged.slides.add_slide(layout)
# 复制所有元素
for shape in slide.shapes:
el = copy.deepcopy(shape.element)
new_slide.shapes._spTree.append(el)
merged.save(output_path)
return output_path
# 使用示例
files = ["slides1.pptx", "slides2.pptx"]
merge_pptx(files, "merged.pptx")
1.4 PDF 文件合并
from pypdf import PdfReader, PdfWriter
def merge_pdf(files, output_path):
"""合并多个 PDF 文件"""
writer = PdfWriter()
for f in files:
reader = PdfReader(f)
for page in reader.pages:
writer.add_page(page)
with open(output_path, "wb") as fh:
writer.write(fh)
return output_path
# 使用示例
files = ["report1.pdf", "report2.pdf", "report3.pdf"]
merge_pdf(files, "merged.pdf")
二、文档拆分
2.1 Word 文档拆分(DOCX)
按分页符拆分,或按指定页数拆分。
from docx import Document
from docx.oxml.ns import qn
from lxml import etree
import copy
def split_docx(input_path, output_dir):
"""按分页符拆分 DOCX 为多个文件"""
doc = Document(input_path)
sections = []
current_elements = []
for element in doc.element.body:
# 检测分页符 (w:br type="page" 或分节符)
is_break = False
if element.tag == qn('w:p'):
for br in element.iter(qn('w:br')):
if br.get(qn('w:type')) == 'page':
is_break = True
break
if is_break:
sections.append(current_elements)
current_elements = []
else:
current_elements.append(element)
if current_elements:
sections.append(current_elements)
output_files = []
for i, elems in enumerate(sections):
new_doc = Document()
new_doc.element.body.clear()
for el in elems:
new_doc.element.body.append(copy.deepcopy(el))
out = f"{output_dir}/split_{i+1}.docx"
new_doc.save(out)
output_files.append(out)
return output_files
# 按固定页数拆分
def split_docx_by_pages(input_path, output_dir, pages_per_file=5):
"""按每N页拆分 DOCX(按段落数量近似分页)"""
doc = Document(input_path)
total_paras = len(doc.paragraphs)
paras_per_file = total_paras // pages_per_file
output_files = []
for i in range(0, total_paras, paras_per_file):
new_doc = Document()
for para in doc.paragraphs[i:i+paras_per_file]:
new_doc.add_paragraph(para.text, para.style.style_id if para.style else None)
out = f"{output_dir}/split_{i//paras_per_file+1}.docx"
new_doc.save(out)
output_files.append(out)
return output_files
2.2 Excel 文件拆分(XLSX)
按 Sheet 拆分为独立文件,或按某列值拆分。
import pandas as pd
import os
def split_xlsx_by_sheet(input_path, output_dir):
"""将每个 Sheet 拆分为独立的 XLSX 文件"""
all_sheets = pd.read_excel(input_path, sheet_name=None)
output_files = []
for name, df in all_sheets.items():
out = os.path.join(output_dir, f"{name}.xlsx")
df.to_excel(out, index=False)
output_files.append(out)
return output_files
def split_xlsx_by_column(input_path, output_dir, column_name):
"""按某列的值拆分为多个文件"""
df = pd.read_excel(input_path)
output_files = []
for val, group in df.groupby(column_name):
safe_val = str(val).replace('/', '_').replace('\\', '_')[:50]
out = os.path.join(output_dir, f"split_{safe_val}.xlsx")
group.to_excel(out, index=False)
output_files.append(out)
return output_files
2.3 PPT 文件拆分(PPTX)
按幻灯片范围拆分。
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
import copy, os
def split_pptx(input_path, output_dir, slides_per_file=None, ranges=None):
"""
拆分 PPTX 文件
slides_per_file: 每个文件包含的幻灯片数
ranges: 自定义范围列表,如 [(0,5), (5,10)] 表示前5页和后5页
"""
prs = Presentation(input_path)
total = len(prs.slides)
if ranges is None and slides_per_file:
ranges = [(i, min(i+slides_per_file, total))
for i in range(0, total, slides_per_file)]
output_files = []
for start, end in ranges:
new_prs = Presentation()
new_prs.slide_width = prs.slide_width
new_prs.slide_height = prs.slide_height
for idx in range(start, end):
slide = prs.slides[idx]
layout = new_prs.slide_layouts[6] # 空白布局
new_slide = new_prs.slides.add_slide(layout)
for shape in slide.shapes:
el = copy.deepcopy(shape.element)
new_slide.shapes._spTree.append(el)
out = os.path.join(output_dir, f"split_{start+1}-{end}.pptx")
new_prs.save(out)
output_files.append(out)
return output_files
2.4 PDF 文件拆分
from pypdf import PdfReader, PdfWriter
import os
def split_pdf(input_path, output_dir, pages_per_file=None, page_ranges=None):
"""
拆分 PDF 文件
pages_per_file: 每个文件的页数
page_ranges: 自定义页码列表,如 [(1,3), (4,6)](1-indexed)
"""
reader = PdfReader(input_path)
total = len(reader.pages)
output_files = []
if page_ranges is None and pages_per_file:
page_ranges = [(i+1, min(i+pages_per_file, total))
for i in range(0, total, pages_per_file)]
for start, end in page_ranges:
writer = PdfWriter()
for i in range(start-1, end): # 转为 0-indexed
writer.add_page(reader.pages[i])
out = os.path.join(output_dir, f"split_{start}-{end}.pdf")
with open(out, "wb") as f:
writer.write(f)
output_files.append(out)
return output_files
# 特殊:每页单独一个文件
def split_pdf_each_page(input_path, output_dir):
return split_pdf(input_path, output_dir, pages_per_file=1)
三、格式转换
3.1 转换矩阵
| 源格式 → 目标格式 | DOCX | XLSX | PPTX | TXT | MD | HTML | CSV | PNG/JPG | |
|---|---|---|---|---|---|---|---|---|---|
| - | ✅ | ✅(表格) | ❌ | ✅ | ✅ | ✅ | ✅(表格) | ✅ | |
| DOCX | ✅ | - | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
| XLSX | ✅ | ❌ | - | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ |
| PPTX | ✅ | ❌ | ❌ | - | ✅ | ✅ | ❌ | ❌ | ✅ |
| MD | ✅ | ✅ | ❌ | ❌ | ✅ | - | ✅ | ❌ | ❌ |
| HTML | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | - | ❌ | ❌ |
| TXT | ✅ | ✅ | ❌ | ❌ | - | ✅ | ✅ | ❌ | ❌ |
| CSV | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | - | ❌ |
| 图片 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | - |
3.2 DOCX 转换
from docx import Document
from docx.shared import Pt
import subprocess, os
# DOCX → PDF(使用 LibreOffice,需安装)
def docx_to_pdf(input_path, output_path):
"""通过 LibreOffice 将 DOCX 转为 PDF"""
cmd = [
'libreoffice', '--headless', '--convert-to', 'pdf',
'--outdir', os.path.dirname(output_path), input_path
]
subprocess.run(cmd, check=True, capture_output=True)
return output_path
# DOCX → TXT
def docx_to_txt(input_path, output_path):
doc = Document(input_path)
text = "\n".join(p.text for p in doc.paragraphs)
with open(output_path, "w", encoding="utf-8") as f:
f.write(text)
return output_path
# DOCX → Markdown
def docx_to_md(input_path, output_path):
doc = Document(input_path)
lines = []
for p in doc.paragraphs:
text = p.text
if p.style and 'Heading' in (p.style.name or ''):
level = ''.join(c for c in p.style.name if c.isdigit()) or '1'
lines.append(f"{'#' * int(level)} {text}")
else:
lines.append(text)
md = "\n\n".join(lines)
with open(output_path, "w", encoding="utf-8") as f:
f.write(md)
return output_path
# DOCX → HTML
def docx_to_html(input_path, output_path):
"""Mammoth 提取 DOCX 内容为 HTML"""
try:
import mammoth
except ImportError:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "mammoth", "-q"])
import mammoth
with open(input_path, "rb") as f:
result = mammoth.convert_to_html(f)
with open(output_path, "w", encoding="utf-8") as f:
f.write(result.value)
return output_path
3.3 PDF 转换
from pypdf import PdfReader
import pdfplumber
import os
# PDF → TXT
def pdf_to_txt(input_path, output_path):
reader = PdfReader(input_path)
text = "\n\n".join(page.extract_text() or "" for page in reader.pages)
with open(output_path, "w", encoding="utf-8") as f:
f.write(text)
return output_path
# PDF → Markdown
def pdf_to_md(input_path, output_path):
"""提取 PDF 内容转为 Markdown"""
reader = PdfReader(input_path)
md_lines = []
for i, page in enumerate(reader.pages):
text = page.extract_text() or ""
md_lines.append(f"<!-- 第 {i+1} 页 -->\n{text}")
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n\n---\n\n".join(md_lines))
return output_path
# PDF → 图片(每页渲染为 PNG)
def pdf_to_images(input_path, output_dir, scale=2.0):
"""使用 pypdfium2 将 PDF 渲染为图片"""
try:
import pypdfium2 as pdfium
except ImportError:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "pypdfium2", "-q"])
import pypdfium2 as pdfium
pdf = pdfium.PdfDocument(input_path)
output_files = []
for i, page in enumerate(pdf):
bitmap = page.render(scale=scale)
img = bitmap.to_pil()
out = os.path.join(output_dir, f"page_{i+1}.png")
img.save(out)
output_files.append(out)
return output_files
# PDF → DOCX(提取文本到 Word)
def pdf_to_docx(input_path, output_path):
from docx import Document
reader = PdfReader(input_path)
doc = Document()
for page in reader.pages:
text = page.extract_text() or ""
if text:
doc.add_paragraph(text)
doc.add_page_break()
doc.save(output_path)
return output_path
3.4 XLSX 转换
import pandas as pd
import os
# XLSX → CSV
def xlsx_to_csv(input_path, output_dir):
all_sheets = pd.read_excel(input_path, sheet_name=None)
output_files = []
for name, df in all_sheets.items():
out = os.path.join(output_dir, f"{name}.csv")
df.to_csv(out, index=False, encoding="utf-8-sig")
output_files.append(out)
return output_files
# XLSX → PDF
def xlsx_to_pdf(input_path, output_path):
"""Excel 表格转为 PDF 报告"""
import subprocess
cmd = [
'libreoffice', '--headless', '--convert-to', 'pdf',
'--outdir', os.path.dirname(output_path), input_path
]
subprocess.run(cmd, check=True, capture_output=True)
return output_path
# XLSX → HTML
def xlsx_to_html(input_path, output_path):
"""将每个 Sheet 转为 HTML 表格"""
all_sheets = pd.read_excel(input_path, sheet_name=None)
html_parts = []
for name, df in all_sheets.items():
html_parts.append(f"<h2>{name}</h2>")
html_parts.append(df.to_html(index=False, border=1))
html = "<html><head><meta charset='utf-8'></head><body>" + "\n".join(html_parts) + "</body></html>"
with open(output_path, "w", encoding="utf-8") as f:
f.write(html)
return output_path
3.5 PPTX 转换
from pptx import Presentation
import os
# PPTX → PDF(通过 LibreOffice)
def pptx_to_pdf(input_path, output_path):
import subprocess
cmd = [
'libreoffice', '--headless', '--convert-to', 'pdf',
'--outdir', os.path.dirname(output_path), input_path
]
subprocess.run(cmd, check=True, capture_output=True)
return output_path
# PPTX → TXT(提取所有文本)
def pptx_to_txt(input_path, output_path):
prs = Presentation(input_path)
texts = []
for i, slide in enumerate(prs.slides):
texts.append(f"=== 第 {i+1} 页 ===")
for shape in slide.shapes:
if shape.has_text_frame:
texts.append(shape.text_frame.text)
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n\n".join(texts))
return output_path
# PPTX → 图片(每页渲染为 PNG)
def pptx_to_images(input_path, output_dir, dpi=150):
"""使用 LibreOffice 将每页幻灯片导出为图片"""
import subprocess
cmd = [
'libreoffice', '--headless', '--convert-to', 'png',
'--outdir', output_dir, input_path
]
subprocess.run(cmd, check=True, capture_output=True)
# LibreOffice 导出文件名格式为 原文件名.png
output_files = [os.path.join(output_dir, f) for f in os.listdir(output_dir) if f.endswith('.png')]
return output_files
3.6 图片 → PDF
from PIL import Image
import os
def images_to_pdf(image_files, output_path):
"""将多张图片合并为一个 PDF 文件"""
images = []
for f in sorted(image_files):
img = Image.open(f)
if img.mode == 'RGBA':
img = img.convert('RGB')
images.append(img)
if images:
first = images[0]
rest = images[1:]
first.save(output_path, "PDF", save_all=True, append_images=rest)
return output_path
3.7 批量格式转换
import os, glob
def batch_convert(input_dir, output_dir, source_ext, target_ext, convert_func):
"""批量转换目录下所有指定格式的文件"""
files = sorted(glob.glob(os.path.join(input_dir, f"*{source_ext}")))
results = []
os.makedirs(output_dir, exist_ok=True)
for f in files:
name = os.path.splitext(os.path.basename(f))[0]
out = os.path.join(output_dir, f"{name}{target_ext}")
try:
convert_func(f, out)
results.append({"file": f, "output": out, "status": "success"})
except Exception as e:
results.append({"file": f, "output": out, "status": "error", "error": str(e)})
return results
四、图片压缩
支持 PNG、JPG、JPEG、WEBP 格式的批量压缩,可控制压缩质量与最大尺寸。
from PIL import Image
import os, glob
def compress_images(input_dir, output_dir, quality=85, max_width=None, max_height=None, max_size_kb=None):
"""
批量压缩图片
参数:
input_dir: 输入目录
output_dir: 输出目录
quality: JPEG 质量 1-100(默认 85)
max_width: 最大宽度(像素),None 表示不限制
max_height: 最大高度(像素),None 表示不限制
max_size_kb: 目标文件大小上限(KB),None 表示不限制
"""
os.makedirs(output_dir, exist_ok=True)
supported = ["*.png", "*.jpg", "*.jpeg", "*.webp"]
results = []
for pattern in supported:
for filepath in sorted(glob.glob(os.path.join(input_dir, pattern))):
try:
img = Image.open(filepath)
fname = os.path.basename(filepath)
out = os.path.join(output_dir, fname)
# 缩放
w, h = img.size
if max_width and w > max_width:
ratio = max_width / w
img = img.resize((max_width, int(h * ratio)), Image.LANCZOS)
if max_height and img.height > max_height:
ratio = max_height / img.height
img = img.resize((int(img.width * ratio), max_height), Image.LANCZOS)
# 转换格式(PNG → JPG 可大幅减小体积)
ext = os.path.splitext(fname)[1].lower()
if ext == '.png' and img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
# 保存并压缩
save_kwargs = {"quality": quality, "optimize": True}
if ext == '.png':
img.save(out, "PNG", optimize=True)
elif ext == '.webp':
img.save(out, "WEBP", quality=quality)
else:
img.save(out, "JPEG", **save_kwargs)
# 如果指定了最大文件大小,逐步降低质量重压
if max_size_kb:
q = quality
while os.path.getsize(out) > max_size_kb * 1024 and q > 10:
q -= 10
img.save(out, "JPEG", quality=q, optimize=True)
orig_size = os.path.getsize(filepath)
new_size = os.path.getsize(out)
ratio = (1 - new_size / orig_size) * 100
results.append({
"file": fname,
"original_kb": round(orig_size / 1024, 1),
"compressed_kb": round(new_size / 1024, 1),
"reduced": f"{ratio:.1f}%"
})
except Exception as e:
results.append({"file": os.path.basename(filepath), "error": str(e)})
return results
# 使用示例:压缩到 200KB 以内,宽度不超过 1920px
# results = compress_images("images/", "compressed/", quality=85, max_width=1920, max_size_kb=200)
PNG 无损压缩(推荐)
def png_lossless_compress(input_dir, output_dir):
"""使用 Pillow 的 optimize 模式对 PNG 进行无损压缩"""
try:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow", "-q"])
except: pass
from PIL import Image
import glob, os
os.makedirs(output_dir, exist_ok=True)
results = []
for f in glob.glob(os.path.join(input_dir, "*.png")):
img = Image.open(f)
out = os.path.join(output_dir, os.path.basename(f))
img.save(out, "PNG", optimize=True)
orig = os.path.getsize(f) / 1024
new = os.path.getsize(out) / 1024
results.append({"file": os.path.basename(f), "original_kb": round(orig,1), "compressed_kb": round(new,1), "saved": f"{(1-new/orig)*100:.1f}%"})
return results
五、智能抠图(去除背景)
使用 rembg 库实现 AI 自动抠图,支持批量处理。
def remove_background(input_path, output_path):
"""
AI 智能去除图片背景
输入:任意图片(PNG/JPG/JPEG/WEBP)
输出:透明背景 PNG
"""
try:
from rembg import remove
except ImportError:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "rembg[gpu]", "-q"])
from rembg import remove
from PIL import Image
input_img = Image.open(input_path)
output_img = remove(input_img)
# 确保输出为 RGBA(保留透明通道)
if output_img.mode != 'RGBA':
output_img = output_img.convert('RGBA')
output_img.save(output_path, "PNG")
return output_path
def batch_remove_background(input_dir, output_dir):
"""批量去除目录下所有图片的背景"""
import glob, os
try:
from rembg import remove
except ImportError:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "rembg[gpu]", "-q"])
from rembg import remove
from PIL import Image
os.makedirs(output_dir, exist_ok=True)
results = []
for f in sorted(glob.glob(os.path.join(input_dir, "*"))):
ext = os.path.splitext(f)[1].lower()
if ext not in [".png", ".jpg", ".jpeg", ".webp"]:
continue
try:
img = Image.open(f)
result = remove(img)
if result.mode != 'RGBA':
result = result.convert('RGBA')
name = os.path.splitext(os.path.basename(f))[0] + ".png"
out = os.path.join(output_dir, name)
result.save(out, "PNG")
results.append({"input": os.path.basename(f), "output": name, "status": "success"})
except Exception as e:
results.append({"input": os.path.basename(f), "status": "error", "error": str(e)})
return results
# 使用示例
# remove_background("photo.jpg", "photo_nobg.png")
# batch_remove_background("product_photos/", "output_nobg/")
替代方案:纯色背景替换
def replace_background_white(input_path, output_path):
"""将图片背景替换为白色"""
try:
from rembg import remove
except ImportError:
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "rembg[gpu]", "-q"])
from rembg import remove
from PIL import Image
img = Image.open(input_path)
result = remove(img)
# 创建白色背景
background = Image.new("RGBA", result.size, (255, 255, 255, 255))
composite = Image.alpha_composite(background, result)
composite = composite.convert("RGB")
composite.save(output_path, "JPEG", quality=95)
return output_path
六、PDF 高级工具
6.1 添加水印
from pypdf import PdfReader, PdfWriter, PdfMerger
from pypdf.generic import StringObject, NameObject, ArrayObject, NumberObject
import io
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from PIL import Image as PILImage
def add_text_watermark(input_path, output_path, text="机密文件", opacity=0.3, font_size=50):
"""给 PDF 添加文字水印"""
reader = PdfReader(input_path)
writer = PdfWriter()
# 创建水印 PDF
packet = io.BytesIO()
c = canvas.Canvas(packet, pagesize=A4)
c.setFont("Helvetica-Bold", font_size)
c.setFillColorRGB(0.5, 0.5, 0.5, opacity)
c.saveState()
c.translate(A4[0]/2, A4[1]/2)
c.rotate(45)
c.drawCentredString(0, 0, text)
c.restoreState()
c.save()
packet.seek(0)
watermark = PdfReader(packet)
for page in reader.pages:
page.merge_page(watermark.pages[0])
writer.add_page(page)
with open(output_path, "wb") as f:
writer.write(f)
return output_path
def add_image_watermark(input_path, output_path, watermark_image_path, opacity=0.2):
"""给 PDF 添加图片水印"""
reader = PdfReader(input_path)
writer = PdfWriter()
# 创建水印页面
packet = io.BytesIO()
c = canvas.Canvas(packet, pagesize=A4)
c.setFillAlpha(opacity)
c.drawImage(watermark_image_path, 50, 50, width=A4[0]-100, height=A4[1]-100, mask='auto')
c.save()
packet.seek(0)
watermark = PdfReader(packet)
for page in reader.pages:
page.merge_page(watermark.pages[0])
writer.add_page(page)
with open(output_path, "wb") as f:
writer.write(f)
return output_path
6.2 加密 / 解密
from pypdf import PdfReader, PdfWriter
def encrypt_pdf(input_path, output_path, user_password="", owner_password=""):
"""PDF 加密"""
reader = PdfReader(input_path)
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
writer.encrypt(user_password=user_password, owner_password=owner_password)
with open(output_path, "wb") as f:
writer.write(f)
return output_path
def decrypt_pdf(input_path, output_path, password=""):
"""PDF 解密"""
reader = PdfReader(input_path)
if reader.is_encrypted:
reader.decrypt(password)
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
with open(output_path, "wb") as f:
writer.write(f)
return output_path
6.3 旋转页面
from pypdf import PdfReader, PdfWriter
def rotate_pdf(input_path, output_path, rotation=90):
"""旋转 PDF 所有页面(90/180/270 度)"""
reader = PdfReader(input_path)
writer = PdfWriter()
for page in reader.pages:
page.rotate(rotation)
writer.add_page(page)
with open(output_path, "wb") as f:
writer.write(f)
return output_path
6.4 裁剪页面
def crop_pdf(input_path, output_dir, left=0, bottom=0, right=None, top=None):
"""裁剪 PDF 页面(值为磅,1 inch = 72 磅)"""
from pypdf import PdfReader, PdfWriter
reader = PdfReader(input_path)
output_files = []
for i, page in enumerate(reader.pages):
w = float(page.mediabox.width)
h = float(page.mediabox.height)
page.mediabox.left = left
page.mediabox.bottom = bottom
page.mediabox.right = right or w
page.mediabox.top = top or h
writer = PdfWriter()
writer.add_page(page)
out = os.path.join(output_dir, f"cropped_{i+1}.pdf")
with open(out, "wb") as f:
writer.write(f)
output_files.append(out)
return output_files
6.5 提取 PDF 中的图片
from pypdf import PdfReader
from PIL import Image
import io, os
def extract_pdf_images(input_path, output_dir):
"""提取 PDF 中嵌入的所有图片"""
reader = PdfReader(input_path)
output_files = []
count = 0
for page in reader.pages:
for img in page.images:
try:
image = Image.open(io.BytesIO(img.data))
ext = img.name.lower().split('.')[-1] if '.' in img.name else 'png'
if ext not in ['png', 'jpg', 'jpeg', 'bmp', 'tiff']:
ext = 'png'
out = os.path.join(output_dir, f"img_{count+1}.{ext}")
image.save(out)
output_files.append(out)
count += 1
except Exception as e:
count += 1
return output_files
七、从文档中提取图片
7.1 从 DOCX 提取图片
import zipfile, os
def extract_docx_images(input_path, output_dir):
"""从 DOCX 文件中提取所有嵌入图片"""
os.makedirs(output_dir, exist_ok=True)
output_files = []
with zipfile.ZipFile(input_path, 'r') as z:
for name in z.namelist():
if name.startswith('word/media/'):
data = z.read(name)
fname = os.path.basename(name)
out = os.path.join(output_dir, fname)
with open(out, 'wb') as f:
f.write(data)
output_files.append(out)
return output_files
7.2 从 PPTX 提取图片
import zipfile, os
def extract_pptx_images(input_path, output_dir):
"""从 PPTX 文件中提取所有嵌入图片"""
os.makedirs(output_dir, exist_ok=True)
output_files = []
with zipfile.ZipFile(input_path, 'r') as z:
for name in z.namelist():
if name.startswith('ppt/media/'):
data = z.read(name)
fname = os.path.basename(name)
out = os.path.join(output_dir, fname)
with open(out, 'wb') as f:
f.write(data)
output_files.append(out)
return output_files
八、综合工具函数
8.1 自动识别文件格式
def detect_file_type(filepath):
"""根据文件扩展名识别文档类型"""
ext = os.path.splitext(filepath)[1].lower()
type_map = {
".docx": "word", ".doc": "word",
".xlsx": "excel", ".xls": "excel", ".csv": "excel",
".pptx": "ppt", ".ppt": "ppt",
".pdf": "pdf",
".png": "image", ".jpg": "image", ".jpeg": "image", ".webp": "image",
".md": "markdown", ".html": "html", ".htm": "html",
".txt": "text",
}
return type_map.get(ext, "unknown")
8.2 批量处理指定目录
def batch_process(input_dir, output_dir, operation, pattern="*"):
"""
批量处理目录下文件
operation: 处理函数,接收 (input_path, output_path) 参数
pattern: 文件匹配模式,如 "*.docx"、"*.pdf"
"""
import glob, os
os.makedirs(output_dir, exist_ok=True)
files = sorted(glob.glob(os.path.join(input_dir, pattern)))
results = []
for f in files:
name = os.path.splitext(os.path.basename(f))[0]
out = os.path.join(output_dir, f"{name}_processed{os.path.splitext(f)[1]}")
try:
operation(f, out)
results.append({"file": os.path.basename(f), "output": out, "status": "success"})
except Exception as e:
results.append({"file": os.path.basename(f), "error": str(e), "status": "failed"})
return results
8.3 生成处理报告
def generate_report(results, report_path):
"""将处理结果生成 HTML 报告"""
html = """<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>文档处理报告</title>
<style>
body{font-family:'Microsoft YaHei',sans-serif;max-width:900px;margin:30px auto;padding:0 20px}
table{width:100%;border-collapse:collapse;margin-top:20px}
th{background:#4472C4;color:#fff;padding:10px;text-align:left}
td{padding:8px 10px;border-bottom:1px solid #ddd}
.success{color:#28a745}.failed{color:#dc3545}
h1{color:#333;border-bottom:2px solid #4472C4;padding-bottom:10px}
</style></head>
<body><h1>文档处理报告</h1><p>处理时间:""" + __import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S') + """</p>
<table><tr><th>文件名</th><th>状态</th><th>输出</th><th>备注</th></tr>"""
for r in results:
status = r.get('status', '')
status_cls = 'success' if status == 'success' else 'failed'
html += f"""<tr>
<td>{r.get('file', '')}</td>
<td class="{status_cls}">{status}</td>
<td>{os.path.basename(r.get('output', ''))}</td>
<td>{r.get('error', '') or r.get('saved', '') or ''}</td></tr>"""
html += "</table></body></html>"
with open(report_path, "w", encoding="utf-8") as f:
f.write(html)
return report_path
九、依赖安装
根据使用的功能,可能需要安装以下 Python 包:
# 基础依赖(通常已预装)
# pip install python-docx openpyxl python-pptx pypdf pdfplumber Pillow pandas
# 高级依赖(按需安装)
# pip install mammoth # DOCX → HTML 转换
# pip install pypdfium2 # PDF 高质量渲染
# pip install rembg[gpu] # AI 抠图(GPU 加速)
# pip install reportlab # PDF 创建与水印
# pip install pypdf # PDF 合并拆分加密
十、注意事项
- LibreOffice 转换:DOCX/PPTX/XLSX → PDF 依赖 LibreOffice,Windows 环境需先安装
- 大文件处理:超过 100MB 的文件建议分块处理,避免内存溢出
- 中文支持:reportlab 生成 PDF 时需注册中文字体,否则中文会显示为空白
- PPT 合并:不同尺寸的 PPT 合并后可能存在布局偏移
- 抠图质量:rembg 使用 U2-Net 模型,对复杂背景(如网格、透明物体)效果有限
- 图片压缩:PNG 无损压缩效果有限,建议对照片类图片转为 JPG 格式
- PDF 加密:忘记密码后无法恢复,请妥善保管密码
- 输出目录:所有输出文件统一写入工作区的 output 子目录