Report_Generate_Server/core/tables.py

303 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Table-related operations for Word Document Server.
"""
from docx.oxml.shared import OxmlElement, qn
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
def set_cell_border(cell, **kwargs):
"""
Set cell border properties.
Args:
cell: The cell to modify
**kwargs: Border properties (top, bottom, left, right, val, color)
"""
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
# Create border elements
for key, value in kwargs.items():
if key in ['top', 'left', 'bottom', 'right']:
tag = 'w:{}'.format(key)
element = OxmlElement(tag)
element.set(qn('w:val'), kwargs.get('val', 'single'))
element.set(qn('w:sz'), kwargs.get('sz', '4'))
element.set(qn('w:space'), kwargs.get('space', '0'))
element.set(qn('w:color'), kwargs.get('color', 'auto'))
tcBorders = tcPr.first_child_found_in("w:tcBorders")
if tcBorders is None:
tcBorders = OxmlElement('w:tcBorders')
tcPr.append(tcBorders)
tcBorders.append(element)
def apply_table_style(table, has_header_row=False, border_style=None, shading=None):
"""
Apply formatting to a table.
Args:
table: The table to format
has_header_row: If True, formats the first row as a header
border_style: Style for borders ('none', 'single', 'double', 'thick')
shading: 2D list of cell background colors (by row and column)
Returns:
True if successful, False otherwise
"""
try:
# Format header row if requested
if has_header_row and table.rows:
header_row = table.rows[0]
for cell in header_row.cells:
for paragraph in cell.paragraphs:
if paragraph.runs:
for run in paragraph.runs:
run.bold = True
# Apply border style if specified
if border_style:
val_map = {
'none': 'nil',
'single': 'single',
'double': 'double',
'thick': 'thick'
}
val = val_map.get(border_style.lower(), 'single')
# Apply to all cells
for row in table.rows:
for cell in row.cells:
set_cell_border(
cell,
top=True,
bottom=True,
left=True,
right=True,
val=val,
color="000000"
)
# Apply cell shading if specified
if shading:
for i, row_colors in enumerate(shading):
if i >= len(table.rows):
break
for j, color in enumerate(row_colors):
if j >= len(table.rows[i].cells):
break
try:
# Apply shading to cell
cell = table.rows[i].cells[j]
shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="{color}"/>')
cell._tc.get_or_add_tcPr().append(shading_elm)
except:
# Skip if color format is invalid
pass
return True
except Exception:
return False
def copy_table(source_table, target_doc, ifadjustheight=True, height = 1, if_merge = True, REPORT_ENUM = 'DT'):
"""
Copy a table from one document to another.
Args:
source_table: The table to copy
target_doc: The document to copy the table to
Returns:
The new table in the target document
"""
# Create a new table with the same dimensions
new_table = target_doc.add_table(rows=len(source_table.rows), cols=len(source_table.columns))
# 获取表格属性
tbl_pr = new_table._tbl.tblPr
# 设置表格边框
tbl_borders = OxmlElement('w:tblBorders')
for border_name in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
border = OxmlElement(f'w:{border_name}')
border.set(qn('w:val'), 'single') # 边框类型
border.set(qn('w:sz'), '4') # 边框粗细(1-968代表1磅)
border.set(qn('w:space'), '0') # 边框间距
border.set(qn('w:color'), 'auto') # 边框颜色
tbl_borders.append(border)
tbl_pr.append(tbl_borders)
# Try to apply the same style
try:
if source_table.style:
new_table.style = 'Table Grid'
except:
# Fall back to default grid style
try:
new_table.style = 'Table Grid'
except:
pass
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.table import WD_ALIGN_VERTICAL, WD_ROW_HEIGHT_RULE
from docx.shared import Pt, Inches, Cm, RGBColor
# Copy cell contents
for i, row in enumerate(source_table.rows):
for j, cell in enumerate(row.cells):
for paragraph in cell.paragraphs:
average_char_width_in_points = 6
if paragraph.text:
new_table.cell(i,j).text = paragraph.text
new_table.cell(i,j).paragraphs[0].runs[0].font.name = "Times New Roman" #设置英文字体
new_table.cell(i,j).paragraphs[0].runs[0].font.size = Pt(10.5) # 字体大小
new_table.cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋') #设置中文字体
new_table.cell(i,j).paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.CENTER
new_table.cell(i,j).vertical_alignment = WD_ALIGN_VERTICAL.CENTER
#new_table.cell(i,j).width = cell.width
if REPORT_ENUM == 'JF':
new_table.cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') #设置中文字体
new_table.cell(i,j).paragraphs[0].runs[0].font.size = Pt(8) # 字体大小
"""
待添加如何让表格自适应大小autofit目前不知为何没有作用
"""
#new_table.rows[i].height = row.height
#new_table.rows[i].height_rule = WD_ROW_HEIGHT_RULE.EXACTLY
if not ifadjustheight:
new_table.auto_fit = True
if if_merge:
try:
new_table = merge_tables(new_table)
except Exception as e:
print(f"合并表格失败:{e}")
return target_doc
from collections import deque
def merge_tables(table):
"""BFS遍历将相邻且相同单元格合并
Args:
table: 表格docx库的Table类型
Returns:
合并后的表格
"""
if not table or len(table.rows) == 0:
return table
rows = len(table.rows)
cols = len(table.columns)
# 创建访问标记矩阵
visited = [[False for _ in range(cols)] for _ in range(rows)]
# 定义四个方向的移动:上、右、下、左
directions = [(0, 0), (0, 1), (1, 0), (0, 0)]
for i in range(rows):
for j in range(cols):
if not visited[i][j]:
current_cell = table.cell(i, j)
current_text = current_cell.text.strip()
if not current_text: # 跳过空单元格
visited[i][j] = True
continue
# BFS队列
queue = deque()
queue.append((i, j))
visited[i][j] = True
# 记录需要合并的单元格
cells_to_merge = []
while queue:
x, y = queue.popleft()
cells_to_merge.append((x, y))
for dx, dy in directions:
nx, ny = x + dx, y + dy
# 检查边界和访问状态
if 0 <= nx < rows and 0 <= ny < cols and not visited[nx][ny]:
neighbor_cell = table.cell(nx, ny)
neighbor_text = neighbor_cell.text.strip()
if neighbor_text == current_text:
visited[nx][ny] = True
queue.append((nx, ny))
# 如果有需要合并的单元格
if len(cells_to_merge) > 1:
# 按行和列排序,确保左上角是第一个单元格
cells_to_merge.sort()
min_row, min_col = cells_to_merge[0]
max_row, max_col = cells_to_merge[-1]
# 清空所有待合并单元格(包括换行符)
for x, y in cells_to_merge[1:]:
cell = table.cell(x, y)
# 删除所有段落(彻底清空)
for paragraph in list(cell.paragraphs):
p = paragraph._element
p.getparent().remove(p)
# 可选:添加一个空段落防止格式问题
cell.add_paragraph()
# 执行合并
if max_row > min_row or max_col > min_col:
table.cell(min_row, min_col).merge(table.cell(max_row, max_col))
return table
def fill_tables(Y_table_list, row, col, Y_Table_num, Y):
"""根据前端返回json块填写表格list并实时跟进已填写表格数量
目前只支持固定的缺陷图的填写
Args:
Y_table_list (list): 前端返回的json块
row (int): 表格行数
col (int): 表格列数
Y_Table_num: json块中有几个表格
Xu_Hao: 是第几个json块
Y: 其他参数
Return:
Y1_TABLES: 三维,表和对应元素
table_index_to_images: 字典,表索引到图片路径列表的映射
Xu_Hao到达第几个表了
"""
table_index_to_images = {}
Y_TABLES = [[["" for _ in range(row)] for _ in range(col)] for _ in range(Y_Table_num)]
# 处理前端返回数据
for l, table_dict in enumerate(Y_table_list):
if table_dict:
Y_TABLES[l][1][0] = Y
Y_TABLES[l][1][1] = table_dict["QueXianLeiXing"]
Y_TABLES[l][1][2] = table_dict["QueXianWeiZhi"]
Y_TABLES[l][1][3] = table_dict["QueXianChiCun"]
Y_TABLES[l][3][0] = table_dict["WeiZongDengJi"]
Y_TABLES[l][3][1] = table_dict["visibility"]
Y_TABLES[l][3][2] = table_dict["urgency"]
Y_TABLES[l][3][3] = table_dict["repair_suggestion"]
# 获取图片路径
image_path = table_dict['Tupian_Dir']
if image_path:
# 确保路径是字符串形式
if isinstance(image_path, list):
table_index_to_images[l] = image_path.copy()
else:
table_index_to_images[l] = [str(image_path)]
return Y_TABLES, table_index_to_images