|
|
@@ -422,12 +422,18 @@ def parse_word_to_blocks(docx_path: Path) -> list[dict]:
|
|
422
|
422
|
elif elem_type == "table":
|
|
423
|
423
|
# 表格块
|
|
424
|
424
|
table = elem
|
|
425
|
|
- table_content = _extract_table(table)
|
|
|
425
|
+ table_content = _extract_table(table, doc)
|
|
426
|
426
|
parent_id = parent_stack[-1]['id'] if parent_stack else None
|
|
427
|
427
|
|
|
428
|
428
|
# 计算表格元数据
|
|
429
|
429
|
rows = table_content.get('rows', [])
|
|
430
|
|
- cols = len(rows[0]['cells']) if rows else 0
|
|
|
430
|
+ # 遍历所有行,找出最大的列数(考虑 colspan)
|
|
|
431
|
+ max_cols = 0
|
|
|
432
|
+ for row_data in rows:
|
|
|
433
|
+ row_cols = sum(cell.get('colspan', 1) for cell in row_data.get('cells', []))
|
|
|
434
|
+ max_cols = max(max_cols, row_cols)
|
|
|
435
|
+
|
|
|
436
|
+ cols = max_cols if max_cols > 0 else 0
|
|
431
|
437
|
|
|
432
|
438
|
index = type_counters['table'] * 100 # 稀疏 index
|
|
433
|
439
|
type_counters['table'] += 1
|
|
|
@@ -708,8 +714,13 @@ def _extract_rich_text(para) -> str | list:
|
|
708
|
714
|
return segments if segments else text
|
|
709
|
715
|
|
|
710
|
716
|
|
|
711
|
|
-def _extract_table(table) -> dict:
|
|
712
|
|
- """提取表格内容(包含合并单元格和尺寸信息)- 完整修复版"""
|
|
|
717
|
+def _extract_table(table, doc=None) -> dict:
|
|
|
718
|
+ """提取表格内容(包含合并单元格和尺寸信息)- 完整修复版
|
|
|
719
|
+
|
|
|
720
|
+ Args:
|
|
|
721
|
+ table: python-docx 表格对象
|
|
|
722
|
+ doc: python-docx 文档对象(用于获取样式名称)
|
|
|
723
|
+ """
|
|
713
|
724
|
rows_data = []
|
|
714
|
725
|
|
|
715
|
726
|
# 提取表格列宽(从 tblGrid)
|
|
|
@@ -723,6 +734,12 @@ def _extract_table(table) -> dict:
|
|
723
|
734
|
# twips 转 pt (1 pt = 20 twips)
|
|
724
|
735
|
col_widths.append(int(width) / 20)
|
|
725
|
736
|
|
|
|
737
|
+ # 创建 XML 元素到 python-docx 单元格对象的映射
|
|
|
738
|
+ cell_map = {}
|
|
|
739
|
+ for row in table.rows:
|
|
|
740
|
+ for cell in row.cells:
|
|
|
741
|
+ cell_map[id(cell._element)] = cell
|
|
|
742
|
+
|
|
726
|
743
|
# 第一遍:从 XML 直接读取,建立列索引到行合并信息的映射
|
|
727
|
744
|
# col_index -> [{start_row, end_row}, ...] # 可能有多个合并区间
|
|
728
|
745
|
vmerge_map = {}
|
|
|
@@ -929,16 +946,33 @@ def _extract_table(table) -> dict:
|
|
929
|
946
|
cell_style = {}
|
|
930
|
947
|
cell_word_style = None
|
|
931
|
948
|
|
|
932
|
|
- if paras:
|
|
|
949
|
+ # 尝试使用 python-docx API 获取样式
|
|
|
950
|
+ cell_obj = cell_map.get(id(tc))
|
|
|
951
|
+ if cell_obj and cell_obj.paragraphs:
|
|
|
952
|
+ first_para = cell_obj.paragraphs[0]
|
|
|
953
|
+ if first_para.style:
|
|
|
954
|
+ cell_word_style = first_para.style.name
|
|
|
955
|
+
|
|
|
956
|
+ # 如果没有通过 API 获取到,尝试从 XML 获取
|
|
|
957
|
+ if not cell_word_style and paras:
|
|
933
|
958
|
first_p = paras[0]
|
|
934
|
959
|
pPr = first_p.find(qn('w:pPr'))
|
|
935
|
960
|
|
|
936
|
961
|
if pPr is not None:
|
|
937
|
|
- # 段落样式名称
|
|
|
962
|
+ # 段落样式名称 - 从 XML 获取样式 ID
|
|
938
|
963
|
pStyle = pPr.find(qn('w:pStyle'))
|
|
939
|
964
|
if pStyle is not None:
|
|
940
|
|
- cell_word_style = pStyle.get(qn('w:val'))
|
|
941
|
|
-
|
|
|
965
|
+ style_id = pStyle.get(qn('w:val'))
|
|
|
966
|
+ # 尝试从已知的样式映射中查找
|
|
|
967
|
+ # 注意:这里只能使用样式 ID 作为备选
|
|
|
968
|
+ cell_word_style = style_id
|
|
|
969
|
+
|
|
|
970
|
+ # 提取其他样式属性
|
|
|
971
|
+ if paras:
|
|
|
972
|
+ first_p = paras[0]
|
|
|
973
|
+ pPr = first_p.find(qn('w:pPr'))
|
|
|
974
|
+
|
|
|
975
|
+ if pPr is not None:
|
|
942
|
976
|
# 对齐方式
|
|
943
|
977
|
jc = pPr.find(qn('w:jc'))
|
|
944
|
978
|
if jc is not None:
|