分享一个word文档专业排版工具,附源码

正 文 开 始
相逢即是缘,关注⭐星标不错过~,每天分享实操教程和技巧。
📄 完整代码 (含自定义字体与WPS适配)
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from docx import Document
from docx.shared import Pt, Cm
from docx.oxml.ns import qn
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
import os
import re
import sys
# --- 1. 环境检测与 .doc 转换模块 (仅Windows有效) ---
if sys.platform.startswith('win'):
try:
import win32com.client
CONVERT_ENABLED = True
except ImportError:
CONVERT_ENABLED = False
print("提示: 如需处理 .doc 文件,请安装 pywin32: pip install pywin32")
else:
CONVERT_ENABLED = False
defconvert_doc_to_docx(doc_path):
"""将旧版 .doc 转换为 .docx"""
ifnot CONVERT_ENABLED:
returnFalse, "非Windows环境,无法转换 .doc 文件"
word = None
try:
# 优先尝试 WPS
word = win32com.client.Dispatch("Kwps.Application")
except:
try:
# 备选尝试 Office
word = win32com.client.Dispatch("Word.Application")
except:
returnFalse, "未检测到WPS或Office软件"
try:
doc = word.Documents.Open(doc_path)
docx_path = doc_path + "x"
# WPS和Office通用的保存格式代码 (wdFormatXMLDocument)
doc.SaveAs(docx_path, 12)
doc.Close()
word.Quit()
returnTrue, docx_path
except Exception as e:
if word: word.Quit()
returnFalse, f"转换失败: {str(e)}"
# --- 2. 核心配置 (初始化默认值) ---
CONFIG = {
"margin": (2.54, 2.54, 3.17, 3.17),
"western_font": "Times New Roman",
"line_height": Pt(28.95),
"line_rule": WD_LINE_SPACING.EXACTLY,
"fonts": {
"title": "方正小标宋_GBK",
"heading_1": "黑体",
"heading_2": "楷体_GB2312",
"heading_3": "仿宋_GB2312",
"body": "仿宋_GB2312"
},
"font_sizes": {
"title": 22,
"heading_1": 16,
"heading_2": 16,
"heading_3": 16,
"body": 16
}
}
defset_font(run, chinese_font, western_font):
"""强制设置字体 (解决WPS中字体不生效的问题)"""
run.font.name = western_font
r = run._element.rPr
if r isnotNone:
# 移除旧的东亚字体设置
rFonts = r.find(qn('w:eastAsia'))
if rFonts isnotNone:
r.remove(rFonts)
# 添加新的东亚字体
new_rFonts = r.makeelement(qn('w:eastAsia'), {})
new_rFonts.set(qn('w:val'), chinese_font)
r.append(new_rFonts)
defprocess_document(input_path, output_path):
"""核心处理逻辑"""
# --- 步骤1: 处理文件格式 ---
file_ext = os.path.splitext(input_path)[1].lower()
temp_docx_path = None
if file_ext == ".doc":
print("转换中: .doc -> .docx")
success, result = convert_doc_to_docx(input_path)
ifnot success:
returnFalse, result
temp_docx_path = result
process_path = temp_docx_path
elif file_ext == ".docx":
process_path = input_path
else:
returnFalse, "不支持的文件格式"
try:
doc = Document(process_path)
# 设置页边距
for section in doc.sections:
section.top_margin = Cm(CONFIG["margin"][0])
section.bottom_margin = Cm(CONFIG["margin"][1])
section.left_margin = Cm(CONFIG["margin"][2])
section.right_margin = Cm(CONFIG["margin"][3])
# --- 步骤2: 遍历段落并应用自定义样式 ---
for para in doc.paragraphs:
ifnot para.text.strip():
continue
text = para.text.strip()
# 标题1: 匹配 "一、" 格式
if re.match(r'^[一二三四五六七八九十]、', text):
pf = para.paragraph_format
pf.first_line_indent = Pt(0) # 取消缩进
pf.alignment = WD_ALIGN_PARAGRAPH.LEFT
pf.line_spacing_rule = CONFIG["line_rule"]
pf.line_spacing = CONFIG["line_height"]
for run in para.runs:
set_font(run, CONFIG["fonts"]["heading_1"], CONFIG["western_font"])
run.font.size = Pt(CONFIG["font_sizes"]["heading_1"])
run.font.bold = False
# 标题2: 匹配 "(一)" 格式
elif re.match(r'^[((][\u4e00-\u9fa5]+[))]', text):
pf = para.paragraph_format
pf.first_line_indent = Pt(0)
pf.alignment = WD_ALIGN_PARAGRAPH.LEFT
pf.line_spacing_rule = CONFIG["line_rule"]
pf.line_spacing = CONFIG["line_height"]
for run in para.runs:
set_font(run, CONFIG["fonts"]["heading_2"], CONFIG["western_font"])
run.font.size = Pt(CONFIG["font_sizes"]["heading_2"])
run.font.bold = False
# 标题3: 匹配 "1." 或 "1、" 格式
elif re.match(r'^\d+[\.、]', text):
pf = para.paragraph_format
pf.first_line_indent = Pt(0)
pf.alignment = WD_ALIGN_PARAGRAPH.LEFT
pf.line_spacing_rule = CONFIG["line_rule"]
pf.line_spacing = CONFIG["line_height"]
for run in para.runs:
set_font(run, CONFIG["fonts"]["heading_3"], CONFIG["western_font"])
run.font.size = Pt(CONFIG["font_sizes"]["heading_3"])
run.font.bold = True
# 正文: 默认样式
else:
pf = para.paragraph_format
pf.first_line_indent = Cm(0.74) # 首行缩进2字符
pf.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
pf.line_spacing_rule = CONFIG["line_rule"]
pf.line_spacing = CONFIG["line_height"]
for run in para.runs:
set_font(run, CONFIG["fonts"]["body"], CONFIG["western_font"])
run.font.size = Pt(CONFIG["font_sizes"]["body"])
run.font.bold = False
# --- 步骤3: 处理文档大标题 (第1段) ---
iflen(doc.paragraphs) > 0:
first_para = doc.paragraphs[0]
if first_para.text.strip():
pf = first_para.paragraph_format
pf.alignment = WD_ALIGN_PARAGRAPH.CENTER
pf.space_after = Pt(12)
for run in first_para.runs:
set_font(run, CONFIG["fonts"]["title"], CONFIG["western_font"])
run.font.size = Pt(CONFIG["font_sizes"]["title"])
run.font.bold = False
doc.save(output_path)
# 清理临时文件
if temp_docx_path and os.path.exists(temp_docx_path):
os.remove(temp_docx_path)
returnTrue, f"成功! 保存至: {output_path}"
except Exception as e:
if temp_docx_path and os.path.exists(temp_docx_path):
os.remove(temp_docx_path)
returnFalse, f"处理异常: {str(e)}"
# --- 3. 美观的GUI界面 (含自定义配置) ---
classModernTypesettingApp:
def__init__(self, root):
self.root = root
self.root.title("🖋️ WPS 智能排版大师 (含自定义)")
self.root.geometry("750x600")
self.root.resizable(True, True)
self.setup_styles()
self.create_widgets()
defsetup_styles(self):
style = ttk.Style()
style.configure("TLabel", font=("微软雅黑", 10))
style.configure("TButton", font=("微软雅黑", 9))
self.root.configure(bg='#f5f5f5')
defcreate_widgets(self):
notebook = ttk.Notebook(self.root)
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# --- Tab 1: 主操作界面 ---
tab_main = ttk.Frame(notebook)
notebook.add(tab_main, text="📄 文档处理")
main_frame = ttk.LabelFrame(tab_main, text=" 任务配置 ", padding=20)
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# 输入输出
tk.Label(main_frame, text="输入文件:", font=("微软雅黑", 10)).grid(row=0, column=0, sticky=tk.W, pady=10)
self.input_entry = tk.Entry(main_frame, width=40, font=("Consolas", 10), relief="solid")
self.input_entry.grid(row=0, column=1, padx=10, pady=10, sticky=tk.EW)
tk.Button(main_frame, text="📂 选择文件", command=self.browse_input, bg="#007ACC", fg="white").grid(row=0, column=2, padx=10)
tk.Label(main_frame, text="输出目录:", font=("微软雅黑", 10)).grid(row=1, column=0, sticky=tk.W, pady=10)
self.output_entry = tk.Entry(main_frame, width=40, font=("Consolas", 10), relief="solid")
self.output_entry.grid(row=1, column=1, padx=10, pady=10, sticky=tk.EW)
tk.Button(main_frame, text="📁 选择文件夹", command=self.browse_output, bg="#28A745", fg="white").grid(row=1, column=2, padx=10)
# 控制按钮
btn_frame = tk.Frame(main_frame)
btn_frame.grid(row=2, column=0, columnspan=3, pady=30)
self.start_btn = tk.Button(btn_frame, text="🚀 开始排版",
command=self.start_process,
bg="#DC3545", fg="white", width=15, height=2, font=("微软雅黑", 10, "bold"))
self.start_btn.pack(side=tk.LEFT, padx=20)
tk.Button(btn_frame, text="❌ 退出", command=self.root.quit,
width=15, height=2).pack(side=tk.LEFT, padx=20)
# 状态栏
self.status_var = tk.StringVar()
self.status_var.set("就绪: 支持 .doc 和 .docx 格式")
status_bar = tk.Label(self.root, textvariable=self.status_var, relief="sunken",
bg="#e9ecef", fg="gray", font=("Consolas", 9))
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# --- Tab 2: 字体配置界面 ---
tab_config = ttk.Frame(notebook)
notebook.add(tab_config, text="🎨 字体配置")
config_frame = ttk.LabelFrame(tab_config, text=" 自定义样式设置 ", padding=20)
config_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# 字体选择行
tk.Label(config_frame, text="字体名称", font=("微软雅黑", 10, "bold")).grid(row=0, column=1, padx=40)
tk.Label(config_frame, text="字号(磅)", font=("微软雅黑", 10, "bold")).grid(row=0, column=2, padx=20)
# 标题配置
tk.Label(config_frame, text="文档大标题:", fg="blue").grid(row=1, column=0, sticky=tk.W, pady=10)
self.title_font = ttk.Combobox(config_frame, values=[
'方正小标宋_GBK', '黑体', '宋体', '微软雅黑'
], width=15)
self.title_font.set(CONFIG["fonts"]["title"])
self.title_font.grid(row=1, column=1)
self.title_size = ttk.Combobox(config_frame, values=[16, 18, 20, 22, 24, 26, 28], width=8)
self.title_size.set(CONFIG["font_sizes"]["title"])
self.title_size.grid(row=1, column=2)
# 一级标题
tk.Label(config_frame, text="一级标题(一、):", fg="darkgreen").grid(row=2, column=0, sticky=tk.W, pady=10)
self.l1_font = ttk.Combobox(config_frame, values=[
'黑体', '方正小标宋_GBK', '楷体_GB2312'
], width=15)
self.l1_font.set(CONFIG["fonts"]["heading_1"])
self.l1_font.grid(row=2, column=1)
self.l1_size = ttk.Combobox(config_frame, values=[14, 15, 16, 17, 18], width=8)
self.l1_size.set(CONFIG["font_sizes"]["heading_1"])
self.l1_size.grid(row=2, column=2)
# 二级标题
tk.Label(config_frame, text="二级标题((一)):", fg="purple").grid(row=3, column=0, sticky=tk.W, pady=10)
self.l2_font = ttk.Combobox(config_frame, values=[
'楷体_GB2312', '仿宋_GB2312', '黑体'
], width=15)
self.l2_font.set(CONFIG["fonts"]["heading_2"])
self.l2_font.grid(row=3, column=1)
self.l2_size = ttk.Combobox(config_frame, values=[14, 15, 16, 17, 18], width=8)
self.l2_size.set(CONFIG["font_sizes"]["heading_2"])
self.l2_size.grid(row=3, column=2)
# 正文配置
tk.Label(config_frame, text="正文/三级标题:", fg="gray").grid(row=4, column=0, sticky=tk.W, pady=10)
self.body_font = ttk.Combobox(config_frame, values=[
'仿宋_GB2312', '宋体', '黑体'
], width=15)
self.body_font.set(CONFIG["fonts"]["body"])
self.body_font.grid(row=4, column=1)
self.body_size = ttk.Combobox(config_frame, values=[14, 15, 16, 17, 18], width=8)
self.body_size.set(CONFIG["font_sizes"]["body"])
self.body_size.grid(row=4, column=2)
# 操作按钮
opt_frame = tk.Frame(config_frame)
opt_frame.grid(row=5, column=0, columnspan=3, pady=30)
tk.Button(opt_frame, text="💾 保存配置", command=self.save_config,
bg="#17A2B8", fg="white", width=15).pack(side=tk.LEFT, padx=20)
tk.Button(opt_frame, text="🔙 恢复默认", command=self.reset_config,
bg="#6C757D", fg="white", width=15).pack(side=tk.LEFT, padx=20)
defbrowse_input(self):
file_path = filedialog.askopenfilename(
title="选择Word文档",
filetypes=[("Word文档", "*.docx *.doc"), ("All Files", "*.*")]
)
if file_path:
self.input_entry.delete(0, tk.END)
self.input_entry.insert(0, file_path)
defbrowse_output(self):
dir_path = filedialog.askdirectory()
if dir_path:
self.output_entry.delete(0, tk.END)
self.output_entry.insert(0, dir_path)
defsave_config(self):
"""保存用户自定义的字体设置"""
try:
CONFIG["fonts"]["title"] = self.title_font.get()
CONFIG["fonts"]["heading_1"] = self.l1_font.get()
CONFIG["fonts"]["heading_2"] = self.l2_font.get()
CONFIG["fonts"]["body"] = self.body_font.get()
CONFIG["font_sizes"]["title"] = int(self.title_size.get())
CONFIG["font_sizes"]["heading_1"] = int(self.l1_size.get())
CONFIG["font_sizes"]["heading_2"] = int(self.l2_size.get())
CONFIG["font_sizes"]["body"] = int(self.body_size.get())
self.status_var.set("✅ 配置已更新,下次处理生效")
messagebox.showinfo("成功", "字体配置已保存!")
except Exception as e:
messagebox.showerror("错误", f"配置保存失败: {e}")
defreset_config(self):
"""恢复默认配置"""
# 这里直接写死默认值
self.title_font.set('方正小标宋_GBK')
self.l1_font.set('黑体')
self.l2_font.set('楷体_GB2312')
self.body_font.set('仿宋_GB2312')
self.title_size.set(22)
self.l1_size.set(16)
self.l2_size.set(16)
self.body_size.set(16)
self.save_config()
self.status_var.set("⚙️ 已恢复默认设置")
defstart_process(self):
input_file = self.input_entry.get()
output_dir = self.output_entry.get()
ifnot input_file ornot output_dir:
messagebox.showwarning("警告", "请输入文件路径和输出目录!")
return
ifnot os.path.exists(input_file):
messagebox.showerror("错误", "输入文件不存在!")
return
# 生成输出路径
filename = os.path.basename(input_file)
name, ext = os.path.splitext(filename)
output_file = os.path.join(output_dir, f"{name}_已排版{ext}")
self.status_var.set("🔄 正在处理中 (可能需要几秒)...")
self.root.update_idletasks()
success, msg = process_document(input_file, output_file)
if success:
self.status_var.set("✅ 处理完成!")
messagebox.showinfo("成功", f"任务完成!\n\n{msg}")
else:
self.status_var.set("❌ 处理失败")
messagebox.showerror("错误", f"处理失败:\n{msg}")
if __name__ == "__main__":
root = tk.Tk()
app = ModernTypesettingApp(root)
root.mainloop()
💡 使用前准备
-
1. 安装依赖:
如果你有.doc文件需要处理,请务必在终端运行:pip install pywin32(如果没有
.doc文件,只处理.docx,则不需要安装) -
2. 字体库:
代码中使用了仿宋_GB2312和楷体_GB2312。如果你的电脑(特别是非Windows系统)没有这些字体,WPS 可能会显示异常。如果报错,可以在配置界面手动选择系统中已有的字体(如“仿宋”、“楷体”)。
🛠️ 功能说明
-
• Tab 1 (文档处理):选择你的 .doc或.docx文件,点击开始。 -
• Tab 2 (字体配置):你可以在这里修改“文档大标题”、“一级标题”、“二级标题”和“正文”的字体及大小。修改后点击“保存配置”,下次处理文档时就会生效。
夜雨聆风