diff --git a/mineru/model/docx/docx_converter.py b/mineru/model/docx/docx_converter.py index 22e4c3d8..e413ed62 100644 --- a/mineru/model/docx/docx_converter.py +++ b/mineru/model/docx/docx_converter.py @@ -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}" # 公式标记格式 - 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.") diff --git a/mineru/model/docx/main.py b/mineru/model/docx/main.py index dcc39f58..fd583476 100644 --- a/mineru/model/docx/main.py +++ b/mineru/model/docx/main.py @@ -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")) diff --git a/mineru/model/docx/section.docx b/mineru/model/docx/section.docx index 826c3966..e3486d18 100644 Binary files a/mineru/model/docx/section.docx and b/mineru/model/docx/section.docx differ diff --git a/mineru/model/docx/test2/word/document.xml b/mineru/model/docx/test2/word/document.xml index 7bb61b0d..2013fceb 100644 --- a/mineru/model/docx/test2/word/document.xml +++ b/mineru/model/docx/test2/word/document.xml @@ -1,2 +1,2996 @@ -公司名称公司标语街道地址省/直辖市/自治区,市/县,邮政编码电话号码: 电话传真: 传真发票发票编号 100日期: 日期前答复:收件人姓名公司名称省/市/自治区,市/县省/自治区/直辖市、市/县、邮政编码电话: 电话收货人:接收方姓名公司名称省/市/自治区,市/县省/自治区/直辖市、市/县、邮政编码电话: 电话备注或特殊说明:要立即开始,只需点击任何占位符文本(例如此文本)并开始键入以将其替换为自己的文本。销售人员订单编号申购人发货方式船上交货价条款见票付款数量说明单价总计小计销售税运费与手续费应付总额所有支票支付给公司名称如对此发票有疑问,请联系 姓名、电话、电子邮件图表 STYLEREF 1 \s 0 SEQ 图表 \* ARABIC \s 1 1 \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 公司名称 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 公司标语 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 街道地址 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 省/直辖市/自治区,市/县,邮政编码 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话号码: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话 + + + + + + + + + + + + + + + + + + + + + + + + + 传真: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 传真 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 发票 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 发票编号 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 日期: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 日期 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 前答复: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 收件人姓名 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 公司名称 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 省/市/自治区,市/县 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 省/自治区/直辖市、市/县、邮政编码 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 收货人: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 接收方姓名 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 公司名称 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 省/市/自治区,市/县 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 省/自治区/直辖市、市/县、邮政编码 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 电话 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 备注或特殊说明: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 要立即开始,只需点击任何占位符文本(例如此文本)并开始键入以将其替换为自己的文本。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 销售人员 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 订单编号 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 申购人 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 发货方式 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 船上交货价 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 条款 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 见票付款 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 数量 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 说明 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 单价 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 总计 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 小计 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 销售税 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 运费与手续费 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 应付总额 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 所有支票支付给 + + + + + + + + + + + + + + + + + + + + + + + + + + 公司名称 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 如对此发票有疑问 + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + 请联系 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 姓名 + + + + + + + + + 、 + + + + + + + + + + + + + + + + + + + + + + + 电话 + + + + + + + + + 、 + + + + + + + + + + + + + + + + + + + + + + + 电子邮件 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 图表 + + + + + + + + + + + + + + + + + + STYLEREF 1 \s + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + SEQ + + + + + + 图表 + + + + + + \* ARABIC \s 1 + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file