173 lines
6.7 KiB
Python
173 lines
6.7 KiB
Python
import win32com.client as win32
|
||
import pythoncom
|
||
import logging
|
||
from pathlib import Path
|
||
|
||
# 配置日志
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||
handlers=[
|
||
logging.FileHandler('docx_merge_win32com.log'),
|
||
logging.StreamHandler()
|
||
]
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Word常量定义(如果无法从constants模块获取)
|
||
class WordConstants:
|
||
wdCollapseEnd = 0
|
||
wdSectionBreakNextPage = 2
|
||
wdHeaderFooterPrimary = 1
|
||
wdHeaderFooterFirstPage = 2
|
||
wdHeaderFooterEvenPages = 3
|
||
wdBorderBottom = -3 # 下边框常量
|
||
wdLineStyleSingle = 1 # 单线样式
|
||
|
||
class WordDocumentMerger:
|
||
def __init__(self):
|
||
# 初始化COM对象
|
||
pythoncom.CoInitialize()
|
||
self.word_app = win32.DispatchEx('Word.Application')
|
||
self.word_app.Visible = False
|
||
self.word_app.DisplayAlerts = False
|
||
self.constants = WordConstants()
|
||
|
||
def merge_documents(self, doc1_path, doc2_path, output_path):
|
||
"""合并两个Word文档"""
|
||
doc1 = doc2 = None
|
||
try:
|
||
logger.info(f"开始合并文档: {doc1_path} 和 {doc2_path}")
|
||
|
||
# 打开文档
|
||
doc1 = self.word_app.Documents.Open(str(doc1_path))
|
||
logger.info(f"已打开第一个文档: {doc1_path}")
|
||
doc2 = self.word_app.Documents.Open(str(doc2_path))
|
||
logger.info(f"已打开第二个文档: {doc2_path}")
|
||
|
||
# 插入第二个文档的内容
|
||
self._insert_document_content(doc1, doc2)
|
||
|
||
# 特别处理页眉格式
|
||
self._fix_header_borders(doc1)
|
||
|
||
# 保存合并后的文档
|
||
output_path = self._ensure_unique_filename(output_path)
|
||
doc1.SaveAs(str(output_path))
|
||
logger.info(f"已保存合并文档: {output_path}")
|
||
return output_path
|
||
except Exception as e:
|
||
logger.error(f"文档合并失败: {str(e)}", exc_info=True)
|
||
return None
|
||
finally:
|
||
self._cleanup(doc1, doc2)
|
||
|
||
def _insert_section_break(self, doc):
|
||
"""在文档末尾插入分节符"""
|
||
try:
|
||
end_range = doc.Content
|
||
end_range.Collapse(Direction=self.constants.wdCollapseEnd)
|
||
end_range.InsertBreak(Type=self.constants.wdSectionBreakNextPage)
|
||
logger.debug("已插入分节符")
|
||
except Exception as e:
|
||
logger.error(f"插入分节符失败: {str(e)}")
|
||
raise
|
||
|
||
def _insert_document_content(self, target_doc, source_doc):
|
||
"""插入文档内容"""
|
||
try:
|
||
source_range = source_doc.Content
|
||
target_range = target_doc.Content
|
||
target_range.Collapse(Direction=self.constants.wdCollapseEnd)
|
||
source_range.Copy()
|
||
target_range.Paste()
|
||
logger.info("已插入第二个文档内容")
|
||
|
||
# 合并页眉页脚
|
||
self._merge_headers_footers(target_doc, source_doc)
|
||
except Exception as e:
|
||
logger.error(f"插入文档内容失败: {str(e)}")
|
||
raise
|
||
|
||
def _merge_headers_footers(self, target_doc, source_doc):
|
||
"""合并页眉页脚"""
|
||
try:
|
||
last_section = target_doc.Sections(target_doc.Sections.Count)
|
||
src_section = source_doc.Sections(1)
|
||
|
||
# 合并页眉
|
||
for header_type in [self.constants.wdHeaderFooterPrimary,
|
||
self.constants.wdHeaderFooterFirstPage,
|
||
self.constants.wdHeaderFooterEvenPages]:
|
||
if src_section.Headers(header_type).Exists:
|
||
src_section.Headers(header_type).Range.Copy()
|
||
last_section.Headers(header_type).Range.Paste()
|
||
|
||
# 合并页脚
|
||
for footer_type in [self.constants.wdHeaderFooterPrimary,
|
||
self.constants.wdHeaderFooterFirstPage,
|
||
self.constants.wdHeaderFooterEvenPages]:
|
||
if src_section.Footers(footer_type).Exists:
|
||
src_section.Footers(footer_type).Range.Copy()
|
||
last_section.Footers(footer_type).Range.Paste()
|
||
|
||
logger.info("已合并页眉页脚")
|
||
except Exception as e:
|
||
logger.error(f"合并页眉页脚失败: {str(e)}")
|
||
raise
|
||
|
||
def _fix_header_borders(self, doc):
|
||
"""确保所有页眉都有底部边框线"""
|
||
try:
|
||
for section in doc.Sections:
|
||
for header_type in [self.constants.wdHeaderFooterPrimary,
|
||
self.constants.wdHeaderFooterFirstPage]:
|
||
if section.Headers(header_type).Exists:
|
||
header = section.Headers(header_type).Range
|
||
if header.Paragraphs.Count > 0:
|
||
border = header.Paragraphs(1).Borders(
|
||
self.constants.wdBorderBottom)
|
||
border.LineStyle = self.constants.wdLineStyleSingle
|
||
border.LineWidth = 4
|
||
logger.info("已修复所有页眉边框线")
|
||
except Exception as e:
|
||
logger.error(f"修复页眉边框失败: {str(e)}")
|
||
|
||
def _ensure_unique_filename(self, path):
|
||
"""确保文件名唯一"""
|
||
path = Path(path)
|
||
if not path.exists():
|
||
return path
|
||
|
||
counter = 1
|
||
while True:
|
||
new_path = path.parent / f"{path.stem}_{counter}{path.suffix}"
|
||
if not new_path.exists():
|
||
return new_path
|
||
counter += 1
|
||
|
||
def _cleanup(self, *docs):
|
||
"""清理资源"""
|
||
try:
|
||
for doc in docs:
|
||
if doc and hasattr(doc, 'Close'):
|
||
doc.Close(SaveChanges=False)
|
||
if hasattr(self, 'word_app') and self.word_app:
|
||
self.word_app.Quit()
|
||
pythoncom.CoUninitialize()
|
||
logger.info("已清理资源")
|
||
except Exception as e:
|
||
logger.error(f"清理资源失败: {str(e)}")
|
||
|
||
# if __name__ == "__main__":
|
||
# merger = WordDocumentMerger()
|
||
|
||
# # 修改为您的实际文件路径
|
||
# doc1_path = Path(r"C:\Users\VogelimKafig\Desktop\金风模板\jingfeng_fengmian1.docx")
|
||
# doc2_path = Path(r"C:\Users\VogelimKafig\Desktop\金风模板\quexian.docx")
|
||
# output_path = Path(r"C:\Users\VogelimKafig\Desktop\金风模板\merged_document.docx")
|
||
|
||
# if merger.merge_documents(doc1_path, doc2_path, output_path):
|
||
# print(f"合并成功! 输出文件: {output_path}")
|
||
# else:
|
||
# print("合并失败,请查看日志文件。") |