import os
import sys
import time
import subprocess
from pathlib import Path
from docx2pdf import convert
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm
from datetime import timedelta
# 全局统计变量
global_start_time = None
total_conversion_time = 0
file_conversion_times = []
class WordToPDFConverter:
"""使用docx2pdf或LibreOffice进行高质量转换"""
def __init__(self, word_path, pdf_path=None):
self.word_path = os.path.abspath(word_path)
if pdf_path is None:
pdf_path = os.path.splitext(word_path)[0] + '.pdf'
self.pdf_path = os.path.abspath(pdf_path)
self.start_time = None
self.end_time = None
def convert_using_docx2pdf(self):
"""使用docx2pdf库(依赖Word或LibreOffice)"""
try:
self.start_time = time.time()
# docx2pdf内部会处理转换,不需要额外初始化
convert(self.word_path, self.pdf_path)
self.end_time = time.time()
return True
except Exception as e:
print(f"docx2pdf转换失败: {e}")
return False
def convert_using_libreoffice_fast(self):
"""使用LibreOffice(跨平台)- 快速版本"""
try:
self.start_time = time.time()
# 构建命令行参数(优化版本)
if sys.platform == 'win32':
# Windows系统尝试多个可能的LibreOffice路径
lo_paths = [
r"C:\Program Files\LibreOffice\program\soffice.exe",
r"C:\Program Files (x86)\LibreOffice\program\soffice.exe",
r"C:\Program Files\LibreOffice 7\program\soffice.exe",
r"C:\Program Files\LibreOffice 24\program\soffice.exe",
r"C:\Program Files\LibreOffice 8\program\soffice.exe"
]
# 查找可用的LibreOffice路径
soffice_path = "soffice.exe"
for lo_path in lo_paths:
if os.path.exists(lo_path):
soffice_path = lo_path
break
cmd = [
soffice_path,
'--headless', # 无界面模式
'--nologo', # 不显示logo
'--nodefault', # 不加载默认模板
'--nofirststartwizard', # 跳过首次启动向导
'--nolockcheck', # 不检查文件锁定
'--invisible', # 完全不可见模式
'--norestore', # 不恢复会话
'--convert-to', 'pdf',
'--outdir', os.path.dirname(self.pdf_path),
self.word_path
]
else:
# Linux/Mac系统
cmd = [
'soffice',
'--headless',
'--nologo',
'--nodefault',
'--nofirststartwizard',
'--nolockcheck',
'--invisible',
'--norestore',
'--convert-to', 'pdf',
'--outdir', os.path.dirname(self.pdf_path),
self.word_path
]
# 使用subprocess快速执行
result = subprocess.run(
cmd,
capture_output=True,
text=True,
shell=True,
timeout=30 # 设置超时防止卡住
)
if result.returncode == 0:
# 重命名输出文件
base_name = os.path.splitext(os.path.basename(self.word_path))[0]
temp_pdf = os.path.join(os.path.dirname(self.pdf_path), f"{base_name}.pdf")
if os.path.exists(temp_pdf) and temp_pdf != self.pdf_path:
os.rename(temp_pdf, self.pdf_path)
self.end_time = time.time()
return True
else:
print(f"LibreOffice转换失败: {result.stderr}")
return False
except subprocess.TimeoutExpired:
print(f"LibreOffice转换超时: {self.word_path}")
return False
except Exception as e:
print(f"LibreOffice转换失败: {e}")
return False
def convert(self, method='auto', fast_mode=True):
"""
执行转换
Args:
method: 'auto', 'docx2pdf', 'libreoffice'
fast_mode: 是否启用快速模式
"""
if not os.path.exists(self.word_path):
raise FileNotFoundError(f"Word文件不存在: {self.word_path}")
# 确保输出目录存在
os.makedirs(os.path.dirname(self.pdf_path) or '.', exist_ok=True)
# 检查文件扩展名
if not self.word_path.lower().endswith(('.docx', '.doc')):
print(f"不支持的文件格式: {self.word_path}")
return False
# 记录开始时间
self.start_time = time.time()
# 根据选择的方法进行转换
if method == 'docx2pdf':
result = self.convert_using_docx2pdf()
elif method == 'libreoffice':
result = self.convert_using_libreoffice_fast()
elif method == 'auto':
# 自动选择:先尝试docx2pdf,失败则使用LibreOffice
try:
result = self.convert_using_docx2pdf()
if not result:
result = self.convert_using_libreoffice_fast()
except:
result = self.convert_using_libreoffice_fast()
else:
raise ValueError(f"不支持的转换方法: {method}")
# 记录结束时间
self.end_time = time.time()
# 记录转换时间
if self.start_time and self.end_time:
conversion_time = self.end_time - self.start_time
file_conversion_times.append((self.word_path, conversion_time))
return result
def batch_convert_folder_fast(input_folder, output_folder=None, method='auto',
max_workers=4, batch_size=5):
"""
快速批量转换指定文件夹中的所有Word文件为PDF
Args:
input_folder: 包含Word文件的文件夹路径
output_folder: 输出PDF的文件夹路径(可选)
method: 转换方法 'auto', 'docx2pdf', 'libreoffice'
max_workers: 最大并发线程数
batch_size: 每批处理的文件数
"""
global global_start_time, total_conversion_time
# 记录全局开始时间
global_start_time = time.time()
# 确保输入文件夹存在
input_path = Path(input_folder)
if not input_path.exists():
print(f"错误:文件夹不存在 - {input_folder}")
return
# 设置输出文件夹
if output_folder is None:
output_path = input_path / "pdf_output_fast"
else:
output_path = Path(output_folder)
# 创建输出文件夹
output_path.mkdir(parents=True, exist_ok=True)
# 查找所有Word文件
word_files = []
for ext in ['.docx', '.doc']:
word_files.extend(input_path.glob(f"*{ext}"))
# 按文件大小排序,小文件优先处理
word_files.sort(key=lambda x: x.stat().st_size if x.exists() else 0)
if not word_files:
print(f"在 '{input_folder}' 中没有找到Word文件!")
print("支持的文件格式: .docx, .doc")
return
# 显示转换方法信息
method_info = {
'docx2pdf': 'docx2pdf(需要Microsoft Word或LibreOffice)',
'libreoffice': 'LibreOffice命令行',
'auto': '自动选择(推荐)'
}
print("=" * 60)
print("🚀 高速Word转PDF批量转换工具")
print("=" * 60)
print(f"📁 输入文件夹: {input_folder}")
print(f"📂 输出文件夹: {output_folder if output_folder else output_path}")
print(f"📄 找到 {len(word_files)} 个Word文件")
print(f"🔄 转换方法: {method} - {method_info.get(method, method)}")
print(f"⚡ 并发线程: {max_workers}")
print(f"📦 批处理大小: {batch_size}")
print("-" * 60)
# 分批次处理
batches = []
for i in range(0, len(word_files), batch_size):
batches.append(word_files[i:i + batch_size])
print(f"📊 分成了 {len(batches)} 个批次进行转换")
# 统计信息
success_count = 0
failed_files = []
batch_times = []
# 处理每个批次
for batch_num, batch in enumerate(batches, 1):
batch_start = time.time()
print(f"\n📦 批次 {batch_num}/{len(batches)}: {len(batch)} 个文件")
batch_success = 0
batch_failed = []
if max_workers > 1 and len(batch) > 1:
# 多线程处理批次
with ThreadPoolExecutor(max_workers=min(max_workers, len(batch))) as executor:
# 创建任务
futures = []
for word_file in batch:
pdf_file = output_path / f"{word_file.stem}.pdf"
future = executor.submit(convert_single_file_fast,
str(word_file), str(pdf_file), method)
futures.append((word_file, future))
# 等待结果
for word_file, future in tqdm(futures, desc=f"批次{batch_num}", unit="文件"):
try:
success, conversion_time = future.result(timeout=60)
if success:
batch_success += 1
else:
batch_failed.append(word_file.name)
except Exception as e:
batch_failed.append(word_file.name)
print(f"文件 {word_file.name} 处理异常: {e}")
else:
# 单线程处理
for word_file in tqdm(batch, desc=f"批次{batch_num}", unit="文件"):
try:
pdf_file = output_path / f"{word_file.stem}.pdf"
converter = WordToPDFConverter(str(word_file), str(pdf_file))
if converter.convert(method=method, fast_mode=True):
batch_success += 1
else:
batch_failed.append(word_file.name)
except Exception as e:
batch_failed.append(word_file.name)
print(f"文件 {word_file.name} 转换出错: {e}")
batch_end = time.time()
batch_time = batch_end - batch_start
batch_times.append(batch_time)
success_count += batch_success
failed_files.extend(batch_failed)
print(f" ✅ 成功: {batch_success}/{len(batch)}")
print(f" ⏱️ 耗时: {batch_time:.2f} 秒")
print(f" 📈 速度: {len(batch)/batch_time:.2f} 文件/秒" if batch_time > 0 else "")
# 计算总耗时
total_end_time = time.time()
total_conversion_time = total_end_time - global_start_time
# 输出结果
print("\n" + "=" * 60)
print("🎉 批量转换完成!")
print("=" * 60)
print(f"📊 统计信息:")
print(f" 总文件数: {len(word_files)}")
print(f" 成功: {success_count}")
print(f" 失败: {len(failed_files)}")
print(f" 总用时: {total_conversion_time:.2f} 秒")
print(f" 平均速度: {len(word_files)/total_conversion_time:.2f} 文件/秒")
print(f" 输出位置: {output_path}")
# 显示时间分析
if file_conversion_times:
print(f"\n⏱️ 时间分析:")
print(f" 最快文件: {min(t for _, t in file_conversion_times):.2f} 秒")
print(f" 最慢文件: {max(t for _, t in file_conversion_times):.2f} 秒")
print(f" 平均时间: {sum(t for _, t in file_conversion_times)/len(file_conversion_times):.2f} 秒/文件")
# 显示最慢的几个文件
slowest_files = sorted(file_conversion_times, key=lambda x: x[1], reverse=True)[:3]
if slowest_files:
print(f" 🐌 最慢的3个文件:")
for file_path, t in slowest_files:
filename = os.path.basename(file_path)
print(f" {filename}: {t:.2f} 秒")
if failed_files:
print(f"\n📝 失败的文件 ({len(failed_files)}个):")
for i, file in enumerate(failed_files[:10], 1): # 只显示前10个
print(f" {i:2d}. {file}")
if len(failed_files) > 10:
print(f" ... 还有 {len(failed_files)-10} 个文件")
# 生成详细报告文件
report_path = output_path / "conversion_report_detailed.txt"
with open(report_path, 'w', encoding='utf-8') as f:
f.write("=" * 60 + "\n")
f.write("Word转PDF批量转换详细报告\n")
f.write("=" * 60 + "\n\n")
f.write(f"转换时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"输入文件夹: {input_folder}\n")
f.write(f"输出文件夹: {output_path}\n")
f.write(f"转换方法: {method}\n")
f.write(f"并发线程: {max_workers}\n")
f.write(f"批处理大小: {batch_size}\n\n")
f.write(f"总文件数: {len(word_files)}\n")
f.write(f"成功转换: {success_count}\n")
f.write(f"转换失败: {len(failed_files)}\n")
f.write(f"总用时: {total_conversion_time:.2f} 秒\n")
f.write(f"平均速度: {len(word_files)/total_conversion_time:.2f} 文件/秒\n\n")
# 批次时间统计
f.write("批次处理时间:\n")
for i, batch_time in enumerate(batch_times, 1):
f.write(f" 批次 {i}: {batch_time:.2f} 秒\n")
f.write(f"\n平均批次时间: {sum(batch_times)/len(batch_times):.2f} 秒\n")
if file_conversion_times:
f.write("\n文件转换时间统计:\n")
f.write("-" * 40 + "\n")
total_file_time = sum(t for _, t in file_conversion_times)
for file_path, conv_time in file_conversion_times:
filename = os.path.basename(file_path)
f.write(f"{filename:<40} {conv_time:6.2f} 秒\n")
f.write("-" * 40 + "\n")
f.write(f"{'总计':<40} {total_file_time:6.2f} 秒\n")
f.write(f"{'平均':<40} {total_file_time/len(file_conversion_times):6.2f} 秒\n")
if failed_files:
f.write(f"\n失败文件列表 ({len(failed_files)}个):\n")
f.write("-" * 40 + "\n")
for file in failed_files:
f.write(f"- {file}\n")
print(f"\n📋 详细报告已保存到: {report_path}")
# 显示性能总结
print("\n" + "=" * 60)
print("📈 性能总结:")
print(f" 总处理时间: {timedelta(seconds=int(total_conversion_time))}")
print(f" 处理效率: {len(word_files)/total_conversion_time:.2f} 文件/秒")
if file_conversion_times:
print(f" 系统开销: {total_conversion_time - sum(t for _, t in file_conversion_times):.2f} 秒")
print("=" * 60)
def convert_single_file_fast(word_path, pdf_path, method='auto'):
"""快速转换单个文件(用于多线程)"""
try:
converter = WordToPDFConverter(word_path, pdf_path)
success = converter.convert(method=method, fast_mode=True)
# 返回成功状态和转换时间
conversion_time = converter.end_time - converter.start_time if converter.end_time and converter.start_time else 0
return success, conversion_time
except Exception as e:
print(f"文件 {os.path.basename(word_path)} 转换出错: {e}")
return False, 0
def check_docx2pdf_available():
"""检查docx2pdf是否可用"""
try:
from docx2pdf import convert
return True
except ImportError:
return False
def check_libreoffice_available():
"""检查LibreOffice是否可用"""
try:
if sys.platform == 'win32':
lo_paths = [
r"C:\Program Files\LibreOffice\program\soffice.exe",
r"C:\Program Files (x86)\LibreOffice\program\soffice.exe",
r"C:\Program Files\LibreOffice 7\program\soffice.exe"
]
for lo_path in lo_paths:
if os.path.exists(lo_path):
return True
else:
# Linux/Mac系统
result = subprocess.run(['which', 'soffice'], capture_output=True)
return result.returncode == 0
except:
pass
return False
def show_conversion_method_menu():
"""显示转换方法选择菜单"""
print("\n" + "=" * 60)
print("📋 请选择转换方法:")
print("=" * 60)
# 检查各方法是否可用
docx2pdf_available = check_docx2pdf_available()
libreoffice_available = check_libreoffice_available()
print(f"1. docx2pdf方法")
print(f" {'✅ 可用' if docx2pdf_available else '❌ 不可用'}")
print(f" - 基于Microsoft Word或LibreOffice")
print(f" - 转换质量高,支持复杂格式")
print(f" - 需要安装Microsoft Word或LibreOffice")
print(f"\n2. LibreOffice命令行方法")
print(f" {'✅ 可用' if libreoffice_available else '❌ 不可用'}")
print(f" - 直接使用LibreOffice命令行")
print(f" - 开源免费,跨平台")
print(f" - 需要安装LibreOffice")
print(f"\n3. 自动选择(推荐)")
print(f" {'✅ 可用' if docx2pdf_available or libreoffice_available else '❌ 不可用'}")
print(f" - 自动选择可用的最佳方法")
print(f" - 先尝试docx2pdf,失败则使用LibreOffice")
print("\n" + "-" * 60)
# 根据可用情况显示选项
available_options = []
if docx2pdf_available:
available_options.append(("docx2pdf", "1"))
if libreoffice_available:
available_options.append(("libreoffice", "2"))
if docx2pdf_available or libreoffice_available:
available_options.append(("auto", "3"))
while True:
choice = input("\n请选择转换方法 (输入数字或名称,回车使用自动选择): ").strip()
if not choice:
return 'auto'
# 检查数字选择
if choice == '1' and docx2pdf_available:
return 'docx2pdf'
elif choice == '2' and libreoffice_available:
return 'libreoffice'
elif choice == '3':
return 'auto'
# 检查名称选择
choice_lower = choice.lower()
if choice_lower == 'docx2pdf' and docx2pdf_available:
return 'docx2pdf'
elif choice_lower == 'libreoffice' and libreoffice_available:
return 'libreoffice'
elif choice_lower == 'auto':
return 'auto'
print("❌ 无效选择或该方法不可用,请重新选择")
def interactive_batch_convert():
"""交互式批量转换"""
print("🚀 Word转PDF批量转换工具")
print("=" * 60)
# 获取输入文件夹
while True:
input_folder = input("请输入Word文件所在文件夹路径: ").strip()
if not input_folder:
input_folder = os.getcwd()
print(f"使用当前文件夹: {input_folder}")
break
input_path = Path(input_folder)
if input_path.exists():
# 检查文件夹中是否有Word文件
word_files = list(input_path.glob("*.docx")) + list(input_path.glob("*.doc"))
if word_files:
print(f"✅ 找到 {len(word_files)} 个Word文件")
break
else:
print(f"⚠️ 文件夹中没有找到Word文件,支持的文件格式: .docx, .doc")
print("请检查路径是否正确,或按回车使用当前文件夹")
else:
print("❌ 文件夹不存在,请重新输入")
# 获取输出文件夹
default_output = str(Path(input_folder) / "pdf_output")
output_folder = input(f"PDF输出文件夹 (回车使用 '{default_output}'): ").strip()
if not output_folder:
output_folder = default_output
# 选择转换方法
conversion_method = show_conversion_method_menu()
# 配置参数选择
print("\n⚙️ 配置转换参数:")
print("-" * 40)
# 选择线程数
cpu_count = os.cpu_count() or 4
while True:
threads_input = input(f"并发线程数 (1-{cpu_count*2},回车使用4): ").strip()
if not threads_input:
max_workers = 4
break
try:
max_workers = int(threads_input)
if 1 <= max_workers <= cpu_count * 2:
break
else:
print(f"请输入1到{cpu_count*2}之间的数字")
except ValueError:
print("请输入有效的数字")
# 选择批次大小
while True:
batch_input = input("每批处理的文件数 (1-50,回车使用10): ").strip()
if not batch_input:
batch_size = 10
break
try:
batch_size = int(batch_input)
if 1 <= batch_size <= 50:
break
else:
print("请输入1到50之间的数字")
except ValueError:
print("请输入有效的数字")
# 显示配置摘要
print("\n" + "=" * 60)
print("📋 配置摘要:")
print("=" * 60)
print(f"输入文件夹: {input_folder}")
print(f"输出文件夹: {output_folder}")
print(f"转换方法: {conversion_method}")
print(f"并发线程: {max_workers}")
print(f"批次大小: {batch_size}")
print(f"开始时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
# 确认开始转换
confirm = input("\n是否开始转换?(y/n,回车开始): ").strip().lower()
if confirm == 'n':
print("转换已取消")
return
print("\n🚀 开始转换...")
print("-" * 60)
# 开始转换
batch_convert_folder_fast(
input_folder=input_folder,
output_folder=output_folder,
method=conversion_method,
max_workers=max_workers,
batch_size=batch_size
)
# ==================== 简单批量转换函数(优化版) ====================
def simple_batch_convert_fast():
"""
简单的批量转换函数 - 直接调用即可(优化版)
"""
# 设置您的文件夹路径(修改这里)
input_folder = r"E:\导师任务\lj\111\4" # Word文件所在文件夹
output_folder = r"E:\PyCharm Projects\pdf2" # 输出文件夹(可选,不填则自动创建)
# 用户选择转换方法
print("\n" + "=" * 60)
print("🔧 请选择转换方法:")
print("=" * 60)
print("1. docx2pdf方法(推荐,需要Microsoft Word或LibreOffice)")
print("2. LibreOffice命令行方法(开源免费)")
print("3. 自动选择(推荐,自动选择最佳方法)")
print("=" * 60)
while True:
choice = input("\n请选择转换方法 (1/2/3,回车使用自动选择): ").strip()
if not choice or choice == '3':
method = 'auto'
break
elif choice == '1':
method = 'docx2pdf'
break
elif choice == '2':
method = 'libreoffice'
break
else:
print("❌ 无效选择,请输入1、2或3")
# 开始高速批量转换
print(f"\n🚀 开始高速转换: {input_folder}")
print(f"📂 输出到: {output_folder}")
print(f"🔄 使用: {method} 方法")
print(f"⏱️ 开始时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print("-" * 60)
batch_convert_folder_fast(
input_folder=input_folder,
output_folder=output_folder,
method=method, # 用户选择的转换方法
max_workers=4, # 并发线程数(根据CPU核心数调整)
batch_size=10 # 每批处理的文件数
)
# ==================== 命令行支持 ====================
def main():
"""
主函数,支持命令行参数
使用方式: python word转pdf.py [输入文件夹] [输出文件夹] [转换方法]
示例: python word转pdf.py "E:\文档" "E:\输出" "docx2pdf"
"""
if len(sys.argv) > 1:
# 使用命令行参数
input_folder = sys.argv[1]
output_folder = sys.argv[2] if len(sys.argv) > 2 else None
method = sys.argv[3] if len(sys.argv) > 3 else 'auto'
# 验证方法参数
if method not in ['auto', 'docx2pdf', 'libreoffice']:
print(f"❌ 无效的转换方法: {method}")
print("可用的方法: auto, docx2pdf, libreoffice")
method = 'auto'
print(f"使用命令行参数:")
print(f"输入文件夹: {input_folder}")
print(f"输出文件夹: {output_folder if output_folder else '自动创建'}")
print(f"转换方法: {method}")
print("=" * 60)
batch_convert_folder_fast(
input_folder=input_folder,
output_folder=output_folder,
method=method,
max_workers=4,
batch_size=10
)
else:
# 交互式使用
interactive_batch_convert()
# ==================== 快速测试函数 ====================
def quick_test():
"""快速测试转换功能"""
print("🔧 快速测试Word转PDF功能...")
# 创建测试文件夹
test_folder = Path("test_docs")
test_folder.mkdir(exist_ok=True)
# 检查当前文件夹中的Word文件
input_folder = os.getcwd()
word_files = list(Path(input_folder).glob("*.docx")) + list(Path(input_folder).glob("*.doc"))
if word_files:
print(f"找到 {len(word_files)} 个Word文件")
print("请选择要测试的转换方法:")
# 检查可用方法
docx2pdf_available = check_docx2pdf_available()
libreoffice_available = check_libreoffice_available()
if not docx2pdf_available and not libreoffice_available:
print("❌ 没有可用的转换方法,请安装Microsoft Word或LibreOffice")
return
print("1. 测试docx2pdf方法" + (" (可用)" if docx2pdf_available else " (不可用)"))
print("2. 测试LibreOffice方法" + (" (可用)" if libreoffice_available else " (不可用)"))
while True:
choice = input("\n请选择测试方法 (1/2): ").strip()
if choice == '1' and docx2pdf_available:
method = 'docx2pdf'
break
elif choice == '2' and libreoffice_available:
method = 'libreoffice'
break
else:
print("❌ 无效选择或该方法不可用")
# 测试第一个文件
test_file = word_files[0]
output_file = test_folder / f"{test_file.stem}_test_{method}.pdf"
print(f"\n测试文件: {test_file.name}")
print(f"测试方法: {method}")
print(f"输出文件: {output_file}")
print("-" * 40)
converter = WordToPDFConverter(str(test_file), str(output_file))
if converter.convert(method):
print(f"✅ 测试成功!文件已转换到: {output_file}")
print(f"⏱️ 转换用时: {converter.end_time - converter.start_time:.2f} 秒")
else:
print("❌ 测试失败,请检查安装和配置")
else:
print("⚠️ 当前文件夹中没有找到Word文件")
print("请将Word文件放在当前文件夹中,或指定文件夹路径")
# ==================== 主程序入口 ====================
if __name__ == "__main__":
print("🚀 Word转PDF批量转换工具 v2.0")
print("=" * 60)
print("功能:")
print("1. 批量转换文件夹中的所有Word文件")
print("2. 支持docx2pdf和LibreOffice两种方法")
print("3. 自动选择最佳转换方法")
print("4. 显示详细的时间统计和性能分析")
print("=" * 60)
# 显示菜单
print("\n请选择操作模式:")
print("1. 交互式批量转换(推荐)")
print("2. 简单批量转换(预设路径)")
print("3. 快速测试转换功能")
print("4. 命令行模式")
print("=" * 60)
while True:
mode = input("\n请选择操作模式 (1/2/3/4,回车使用交互式): ").strip()
if not mode or mode == '1':
interactive_batch_convert()
break
elif mode == '2':
simple_batch_convert_fast()
break
elif mode == '3':
quick_test()
break
elif mode == '4':
print("\n命令行模式:")
print("用法: python word转pdf.py [输入文件夹] [输出文件夹] [转换方法]")
print("示例: python word转pdf.py \"E:\\文档\" \"E:\\输出\" \"docx2pdf\"")
print("转换方法: auto, docx2pdf, libreoffice")
break
else:
print("❌ 无效选择,请输入1、2、3或4")