分节与页眉页脚处理完成

This commit is contained in:
Sidney233
2026-01-12 17:08:37 +08:00
parent a644a8a074
commit e042384953
4 changed files with 3071 additions and 16 deletions
+75 -14
View File
@@ -8,6 +8,7 @@ import logging
from PIL import Image
from loguru import logger
from docx import Document
from docx.document import Document as DocxDocument
from docx.oxml.xmlchemy import BaseOxmlElement
from docx.text.paragraph import Paragraph
from docx.text.hyperlink import Hyperlink
@@ -72,7 +73,8 @@ class DocxConverter:
self.docx_obj = None
self.max_levels: int = 10 # 文档层次结构的最大层级数
self.parents: dict = {} # 父级节点映射
self.blocks = []
self.pages = []
self.cur_page = []
self.pre_num_id: int = -1 # 上一个处理元素的 numId
self.pre_ilevel: int = -1 # 上一个处理元素的缩进等级, 用于判断列表层级
self.list_block_stack: list = [] # 列表块堆栈
@@ -81,13 +83,14 @@ class DocxConverter:
) # 列表计数器 (numId, ilvl) -> count
self.equation_bookends: str = "<eq>{EQ}</eq>" # 公式标记格式
def convert(
self,
file_stream: BinaryIO,
):
self.docx_obj = Document(file_stream)
self.pages.append(self.cur_page)
self._walk_linear(self.docx_obj.element.body)
self._add_header_footer(self.docx_obj)
def _walk_linear(
self,
@@ -165,7 +168,7 @@ class DocxConverter:
"table_type": "",
"table_nest_level": "",
}
self.blocks.append(table_block)
self.cur_page.append(table_block)
def _handle_text_elements(
self,
@@ -179,10 +182,17 @@ class DocxConverter:
doc: DoclingDocument 对象
Returns:
list[RefItem]: 元素引用列表
"""
#TODO: 使用element.find(".//w:sectPr", namespaces=DocxConverter._BLIP_NAMESPACES) 找到分节位置,第一个分节符和最后一个是单独的p标签,而中间的是在最后一个body元素内的,需要分开判断
"""
is_section_end = False
if element.find(".//w:sectPr", namespaces=DocxConverter._BLIP_NAMESPACES):
# 如果没有text内容
if element.text == "":
self.cur_page = []
self.pages.append(self.cur_page)
else:
# 标记本节结束,处理完文本之后再分节
is_section_end = True
elem_ref = []
paragraph = Paragraph(element, self.docx_obj)
paragraph_elements = self._get_paragraph_elements(paragraph)
@@ -251,7 +261,7 @@ class DocxConverter:
}
],
}
self.blocks.append(title_block)
self.cur_page.append(title_block)
elem_ref.append(id(title_block))
elif "Heading" in p_style_id:
@@ -279,7 +289,7 @@ class DocxConverter:
}
],
}
self.blocks.append(h_block)
self.cur_page.append(h_block)
elem_ref.append(id(h_block))
elif len(equations) > 0:
@@ -306,7 +316,7 @@ class DocxConverter:
}
],
}
self.blocks.append(eq_block)
self.cur_page.append(eq_block)
elem_ref.append(id(eq_block))
else:
# 行内公式
@@ -320,7 +330,7 @@ class DocxConverter:
}
],
}
self.blocks.append(inline_eq_block)
self.cur_page.append(inline_eq_block)
elem_ref.append(id(inline_eq_block))
text_tmp = text
for eq in equations:
@@ -401,7 +411,7 @@ class DocxConverter:
}
],
}
self.blocks.append(text_block)
self.cur_page.append(text_block)
elem_ref.append(id(text_block))
else:
# 文本样式名称不仅有默认值,还可能有用户自定义值
@@ -424,9 +434,13 @@ class DocxConverter:
}
],
}
self.blocks.append(text_block)
self.cur_page.append(text_block)
elem_ref.append(id(text_block))
if is_section_end:
self.cur_page = []
self.pages.append(self.cur_page)
return elem_ref
def _handle_pictures(self, drawing_blip: Any):
@@ -492,7 +506,7 @@ class DocxConverter:
}
],
}
self.blocks.append(image_block)
self.cur_page.append(image_block)
elem_ref.append(id(image_block))
return elem_ref
@@ -858,7 +872,7 @@ class DocxConverter:
"list_type": "text_list",
"ilevel": ilevel,
}
self.blocks.append(list_block)
self.cur_page.append(list_block)
# 入栈, 记录当前的列表块
self.list_block_stack.append(list_block)
# 记录当前引用
@@ -1135,3 +1149,50 @@ class DocxConverter:
self.list_counters[key] = 0
self.list_counters[key] += 1
return self.list_counters[key]
def _add_header_footer(self, docx_obj: DocxDocument) -> None:
"""
处理页眉和页脚,按照分节顺序添加到 pages 列表中,
分为整个文档是否启用奇偶页不同和每一节是否启用首页不同两种情况,
暂不考虑包含图片和表格
"""
is_odd_even_different = docx_obj.settings.odd_and_even_pages_header_footer
for sec_idx, section in enumerate(docx_obj.sections):
hdrs = [section.header]
if is_odd_even_different:
hdrs.append(section.even_page_header)
if section.different_first_page_header_footer:
hdrs.append(section.first_page_header)
for hdr in hdrs:
par = [
txt for txt in (par.text.strip() for par in hdr.paragraphs) if txt
]
try:
self.pages[sec_idx].append(
{
"type": BlockType.HEADER,
"content": "".join(par),
}
)
except IndexError:
logger.error("Section index out of range when adding header.")
ftrs = [section.footer]
if is_odd_even_different:
ftrs.append(section.even_page_footer)
if section.different_first_page_header_footer:
ftrs.append(section.first_page_footer)
for ftr in ftrs:
par = [
txt for txt in (par.text.strip() for par in ftr.paragraphs) if txt
]
try:
self.pages[sec_idx].append(
{
"type": BlockType.FOOTER,
"content": "".join(par),
}
)
except IndexError:
logger.error("Section index out of range when adding header.")
+1 -1
View File
@@ -11,7 +11,7 @@ def convert_path(file_path: str):
def convert_binary(file_binary: BinaryIO):
converter = DocxConverter()
converter.convert(file_binary)
return converter.blocks
return converter.pages
if __name__ == "__main__":
# print(convert_path("test.docx"))
Binary file not shown.
File diff suppressed because one or more lines are too long