|
@@ -0,0 +1,1849 @@
|
|
|
+from docx import Document
|
|
|
+from docx.shared import Inches, Pt, RGBColor, Cm
|
|
|
+from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
|
|
|
+from docx.enum.section import WD_SECTION_START, WD_ORIENTATION
|
|
|
+from docx.enum.table import WD_ALIGN_VERTICAL
|
|
|
+from docx.oxml.ns import qn
|
|
|
+from docx.table import _Cell
|
|
|
+from docx.oxml import OxmlElement
|
|
|
+from docx.oxml.shared import qn
|
|
|
+import os
|
|
|
+import openpyxl
|
|
|
+
|
|
|
+# 全局文档对象
|
|
|
+_document = None
|
|
|
+
|
|
|
+def get_document():
|
|
|
+ """获取当前文档对象,如果不存在则创建新文档"""
|
|
|
+ global _document
|
|
|
+ if _document is None:
|
|
|
+ _document = Document()
|
|
|
+ return _document
|
|
|
+
|
|
|
+# 基本文档操作
|
|
|
+def create_new_document():
|
|
|
+ """创建一个新的Word文档"""
|
|
|
+ global _document
|
|
|
+ _document = Document()
|
|
|
+ return "已创建新文档"
|
|
|
+
|
|
|
+def save_document(file_path):
|
|
|
+ """保存Word文档"""
|
|
|
+ doc = get_document()
|
|
|
+ doc.save(file_path)
|
|
|
+ return f"文档已保存为 {file_path}"
|
|
|
+
|
|
|
+def open_document(file_path):
|
|
|
+ """打开现有的Word文档"""
|
|
|
+ global _document
|
|
|
+ if not os.path.exists(file_path):
|
|
|
+ return f"错误:文件 {file_path} 不存在"
|
|
|
+ _document = Document(file_path)
|
|
|
+ return f"已打开文档 {file_path}"
|
|
|
+
|
|
|
+# 内容添加函数
|
|
|
+def add_paragraph_to_docx(text):
|
|
|
+ """向Word文档添加段落"""
|
|
|
+ doc = get_document()
|
|
|
+ doc.add_paragraph(text)
|
|
|
+ return "段落已添加"
|
|
|
+
|
|
|
+# 设置全局字体
|
|
|
+def set_global_font(font_name=None, font_size=None, font_color=None):
|
|
|
+ """
|
|
|
+ 设置文档的全局字体
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ font_name: 字体名称
|
|
|
+ font_size: 字体大小(磅)
|
|
|
+ font_color: 字体颜色(RGB格式,如"FF0000"表示红色)
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ # 遍历文档中的所有段落
|
|
|
+ for paragraph in doc.paragraphs:
|
|
|
+ for run in paragraph.runs:
|
|
|
+ if font_name:
|
|
|
+ run.font.name = font_name
|
|
|
+ if font_size is not None:
|
|
|
+ run.font.size = Pt(font_size)
|
|
|
+ if font_color:
|
|
|
+ run.font.color.rgb = RGBColor.from_string(font_color)
|
|
|
+
|
|
|
+ # 遍历文档中的所有表格
|
|
|
+ for table in doc.tables:
|
|
|
+ for row in table.rows:
|
|
|
+ for cell in row.cells:
|
|
|
+ for paragraph in cell.paragraphs:
|
|
|
+ for run in paragraph.runs:
|
|
|
+ if font_name:
|
|
|
+ run.font.name = font_name
|
|
|
+ if font_size is not None:
|
|
|
+ run.font.size = Pt(font_size)
|
|
|
+ if font_color:
|
|
|
+ run.font.color.rgb = RGBColor.from_string(font_color)
|
|
|
+
|
|
|
+ return "已设置全局字体"
|
|
|
+ except Exception as e:
|
|
|
+ return f"设置全局字体时出错: {str(e)}"
|
|
|
+
|
|
|
+def add_heading(text, level=1):
|
|
|
+ """添加标题"""
|
|
|
+ doc = get_document()
|
|
|
+ doc.add_heading(text, level=level)
|
|
|
+ return f"已添加{level}级标题: {text}"
|
|
|
+
|
|
|
+def add_table(rows, cols, data=None):
|
|
|
+ """添加表格"""
|
|
|
+ doc = get_document()
|
|
|
+ table = doc.add_table(rows=rows, cols=cols)
|
|
|
+
|
|
|
+ # 如果提供了数据,填充表格
|
|
|
+ if data:
|
|
|
+ for i in range(min(len(data), rows)):
|
|
|
+ row_data = data[i]
|
|
|
+ for j in range(min(len(row_data), cols)):
|
|
|
+ table.cell(i, j).text = str(row_data[j])
|
|
|
+
|
|
|
+ return f"已添加{rows}行{cols}列的表格"
|
|
|
+
|
|
|
+def add_picture(image_path, width=None, height=None):
|
|
|
+ """添加图片"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if not os.path.exists(image_path):
|
|
|
+ return f"错误:图片文件 {image_path} 不存在"
|
|
|
+
|
|
|
+ if width and height:
|
|
|
+ doc.add_picture(image_path, width=Cm(width), height=Cm(height))
|
|
|
+ elif width:
|
|
|
+ doc.add_picture(image_path, width=Cm(width))
|
|
|
+ elif height:
|
|
|
+ doc.add_picture(image_path, height=Cm(height))
|
|
|
+ else:
|
|
|
+ doc.add_picture(image_path)
|
|
|
+
|
|
|
+ return f"已添加图片: {image_path}"
|
|
|
+
|
|
|
+def add_page_break():
|
|
|
+ """添加分页符"""
|
|
|
+ doc = get_document()
|
|
|
+ doc.add_page_break()
|
|
|
+ return "已添加分页符"
|
|
|
+
|
|
|
+# 样式和格式设置
|
|
|
+def set_paragraph_style(paragraph_index, style_name):
|
|
|
+ """设置段落样式"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if paragraph_index >= len(doc.paragraphs):
|
|
|
+ return f"错误:段落索引 {paragraph_index} 超出范围"
|
|
|
+
|
|
|
+ try:
|
|
|
+ doc.paragraphs[paragraph_index].style = style_name
|
|
|
+ return f"已将段落 {paragraph_index} 的样式设置为 {style_name}"
|
|
|
+ except Exception as e:
|
|
|
+ return f"设置样式失败: {str(e)}"
|
|
|
+
|
|
|
+def set_font(paragraph_index, run_index, font_name=None, font_size=None,
|
|
|
+ bold=None, italic=None, underline=None, color=None):
|
|
|
+ """设置文本字体"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if paragraph_index >= len(doc.paragraphs):
|
|
|
+ return f"错误:段落索引 {paragraph_index} 超出范围"
|
|
|
+
|
|
|
+ paragraph = doc.paragraphs[paragraph_index]
|
|
|
+
|
|
|
+ if run_index >= len(paragraph.runs):
|
|
|
+ return f"错误:文本运行索引 {run_index} 超出范围"
|
|
|
+
|
|
|
+ run = paragraph.runs[run_index]
|
|
|
+
|
|
|
+ if font_name:
|
|
|
+ run.font.name = font_name
|
|
|
+ # 设置中文字体
|
|
|
+ run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)
|
|
|
+
|
|
|
+ if font_size is not None:
|
|
|
+ run.font.size = Pt(font_size)
|
|
|
+
|
|
|
+ if bold is not None:
|
|
|
+ run.font.bold = bold
|
|
|
+
|
|
|
+ if italic is not None:
|
|
|
+ run.font.italic = italic
|
|
|
+
|
|
|
+ if underline is not None:
|
|
|
+ run.font.underline = underline
|
|
|
+
|
|
|
+ if color:
|
|
|
+ # 将十六进制颜色转换为RGB
|
|
|
+ if color.startswith('#'):
|
|
|
+ color = color[1:]
|
|
|
+ r = int(color[0:2], 16)
|
|
|
+ g = int(color[2:4], 16)
|
|
|
+ b = int(color[4:6], 16)
|
|
|
+ run.font.color.rgb = RGBColor(r, g, b)
|
|
|
+
|
|
|
+ return "已设置字体属性"
|
|
|
+
|
|
|
+def set_paragraph_alignment(paragraph_index, alignment):
|
|
|
+ """设置段落对齐方式"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if paragraph_index >= len(doc.paragraphs):
|
|
|
+ return f"错误:段落索引 {paragraph_index} 超出范围"
|
|
|
+
|
|
|
+ paragraph = doc.paragraphs[paragraph_index]
|
|
|
+
|
|
|
+ alignment_map = {
|
|
|
+ "LEFT": WD_ALIGN_PARAGRAPH.LEFT,
|
|
|
+ "CENTER": WD_ALIGN_PARAGRAPH.CENTER,
|
|
|
+ "RIGHT": WD_ALIGN_PARAGRAPH.RIGHT,
|
|
|
+ "JUSTIFY": WD_ALIGN_PARAGRAPH.JUSTIFY
|
|
|
+ }
|
|
|
+
|
|
|
+ if alignment not in alignment_map:
|
|
|
+ return f"错误:不支持的对齐方式 {alignment}"
|
|
|
+
|
|
|
+ paragraph.alignment = alignment_map[alignment]
|
|
|
+ return f"已将段落 {paragraph_index} 的对齐方式设置为 {alignment}"
|
|
|
+
|
|
|
+def set_paragraph_spacing(paragraph_index, before=None, after=None, line_spacing=None):
|
|
|
+ """设置段落间距"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if paragraph_index >= len(doc.paragraphs):
|
|
|
+ return f"错误:段落索引 {paragraph_index} 超出范围"
|
|
|
+
|
|
|
+ paragraph = doc.paragraphs[paragraph_index]
|
|
|
+
|
|
|
+ if before is not None:
|
|
|
+ paragraph.paragraph_format.space_before = Pt(before)
|
|
|
+
|
|
|
+ if after is not None:
|
|
|
+ paragraph.paragraph_format.space_after = Pt(after)
|
|
|
+
|
|
|
+ if line_spacing is not None:
|
|
|
+ paragraph.paragraph_format.line_spacing = line_spacing
|
|
|
+ paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
|
|
|
+
|
|
|
+ return f"已设置段落 {paragraph_index} 的间距"
|
|
|
+
|
|
|
+# 节和页面设置
|
|
|
+def add_section(start_type="NEW_PAGE"):
|
|
|
+ """添加新的节"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ start_type_map = {
|
|
|
+ "NEW_PAGE": WD_SECTION_START.NEW_PAGE,
|
|
|
+ "EVEN_PAGE": WD_SECTION_START.EVEN_PAGE,
|
|
|
+ "ODD_PAGE": WD_SECTION_START.ODD_PAGE,
|
|
|
+ "CONTINUOUS": WD_SECTION_START.CONTINUOUS
|
|
|
+ }
|
|
|
+
|
|
|
+ if start_type not in start_type_map:
|
|
|
+ return f"错误:不支持的节开始类型 {start_type}"
|
|
|
+
|
|
|
+ doc.add_section(start_type_map[start_type])
|
|
|
+ return f"已添加新节,开始类型为 {start_type}"
|
|
|
+
|
|
|
+def set_section_orientation(section_index, orientation):
|
|
|
+ """设置节的页面方向"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if section_index >= len(doc.sections):
|
|
|
+ return f"错误:节索引 {section_index} 超出范围"
|
|
|
+
|
|
|
+ section = doc.sections[section_index]
|
|
|
+
|
|
|
+ orientation_map = {
|
|
|
+ "PORTRAIT": WD_ORIENTATION.PORTRAIT,
|
|
|
+ "LANDSCAPE": WD_ORIENTATION.LANDSCAPE
|
|
|
+ }
|
|
|
+
|
|
|
+ if orientation not in orientation_map:
|
|
|
+ return f"错误:不支持的页面方向 {orientation}"
|
|
|
+
|
|
|
+ section.orientation = orientation_map[orientation]
|
|
|
+ return f"已将节 {section_index} 的页面方向设置为 {orientation}"
|
|
|
+
|
|
|
+def set_section_page_size(section_index, width, height):
|
|
|
+ """设置节的页面大小"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if section_index >= len(doc.sections):
|
|
|
+ return f"错误:节索引 {section_index} 超出范围"
|
|
|
+
|
|
|
+ section = doc.sections[section_index]
|
|
|
+
|
|
|
+ section.page_width = Cm(width)
|
|
|
+ section.page_height = Cm(height)
|
|
|
+
|
|
|
+ return f"已将节 {section_index} 的页面大小设置为 {width}cm x {height}cm"
|
|
|
+
|
|
|
+def set_section_margins(section_index, top=None, right=None, bottom=None, left=None):
|
|
|
+ """设置节的页边距"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if section_index >= len(doc.sections):
|
|
|
+ return f"错误:节索引 {section_index} 超出范围"
|
|
|
+
|
|
|
+ section = doc.sections[section_index]
|
|
|
+
|
|
|
+ if top is not None:
|
|
|
+ section.top_margin = Cm(top)
|
|
|
+
|
|
|
+ if right is not None:
|
|
|
+ section.right_margin = Cm(right)
|
|
|
+
|
|
|
+ if bottom is not None:
|
|
|
+ section.bottom_margin = Cm(bottom)
|
|
|
+
|
|
|
+ if left is not None:
|
|
|
+ section.left_margin = Cm(left)
|
|
|
+
|
|
|
+ return f"已设置节 {section_index} 的页边距"
|
|
|
+
|
|
|
+# 页眉页脚
|
|
|
+def add_header(section_index, text):
|
|
|
+ """添加页眉"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if section_index >= len(doc.sections):
|
|
|
+ return f"错误:节索引 {section_index} 超出范围"
|
|
|
+
|
|
|
+ section = doc.sections[section_index]
|
|
|
+ header = section.header
|
|
|
+ header.paragraphs[0].text = text
|
|
|
+
|
|
|
+ return f"已为节 {section_index} 添加页眉: {text}"
|
|
|
+
|
|
|
+def add_footer(section_index, text):
|
|
|
+ """添加页脚"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if section_index >= len(doc.sections):
|
|
|
+ return f"错误:节索引 {section_index} 超出范围"
|
|
|
+
|
|
|
+ section = doc.sections[section_index]
|
|
|
+ footer = section.footer
|
|
|
+ footer.paragraphs[0].text = text
|
|
|
+
|
|
|
+ return f"已为节 {section_index} 添加页脚: {text}"
|
|
|
+
|
|
|
+def add_page_number(section_index, position="FOOTER", alignment="CENTER", format=None):
|
|
|
+ """添加页码"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if section_index >= len(doc.sections):
|
|
|
+ return f"错误:节索引 {section_index} 超出范围"
|
|
|
+
|
|
|
+ section = doc.sections[section_index]
|
|
|
+
|
|
|
+ # 获取页眉或页脚
|
|
|
+ if position == "HEADER":
|
|
|
+ container = section.header
|
|
|
+ else: # FOOTER
|
|
|
+ container = section.footer
|
|
|
+
|
|
|
+ # 清除现有内容
|
|
|
+ for paragraph in container.paragraphs:
|
|
|
+ paragraph.clear()
|
|
|
+
|
|
|
+ # 添加新段落
|
|
|
+ paragraph = container.paragraphs[0]
|
|
|
+
|
|
|
+ # 设置对齐方式
|
|
|
+ alignment_map = {
|
|
|
+ "LEFT": WD_ALIGN_PARAGRAPH.LEFT,
|
|
|
+ "CENTER": WD_ALIGN_PARAGRAPH.CENTER,
|
|
|
+ "RIGHT": WD_ALIGN_PARAGRAPH.RIGHT
|
|
|
+ }
|
|
|
+
|
|
|
+ if alignment in alignment_map:
|
|
|
+ paragraph.alignment = alignment_map[alignment]
|
|
|
+
|
|
|
+ # 添加页码字段
|
|
|
+ run = paragraph.add_run()
|
|
|
+
|
|
|
+ # 这里使用一个简单的方法来模拟页码
|
|
|
+ # 实际上,python-docx对页码字段的支持有限
|
|
|
+ # 这里只是添加一个占位符文本
|
|
|
+ if format:
|
|
|
+ run.text = format.format("X") # 使用X作为页码占位符
|
|
|
+ else:
|
|
|
+ run.text = "Page X"
|
|
|
+
|
|
|
+ return f"已为节 {section_index} 添加页码"
|
|
|
+
|
|
|
+# 表格操作
|
|
|
+def merge_table_cells(table_index, start_row, start_col, end_row, end_col):
|
|
|
+ """合并表格单元格"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 检查行列索引是否有效
|
|
|
+ if start_row < 0 or start_col < 0 or end_row >= len(table.rows) or end_col >= len(table.columns):
|
|
|
+ return "错误:行列索引超出范围"
|
|
|
+
|
|
|
+ # 获取要合并的单元格
|
|
|
+ start_cell = table.cell(start_row, start_col)
|
|
|
+ end_cell = table.cell(end_row, end_col)
|
|
|
+
|
|
|
+ # 合并单元格
|
|
|
+ start_cell.merge(end_cell)
|
|
|
+
|
|
|
+ return f"已合并表格 {table_index} 的单元格 ({start_row},{start_col}) 到 ({end_row},{end_col})"
|
|
|
+
|
|
|
+def set_table_cell_text(table_index, row, col, text):
|
|
|
+ """设置表格单元格文本"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 检查行列索引是否有效
|
|
|
+ if row < 0 or col < 0 or row >= len(table.rows) or col >= len(table.columns):
|
|
|
+ return "错误:行列索引超出范围"
|
|
|
+
|
|
|
+ # 设置单元格文本
|
|
|
+ table.cell(row, col).text = text
|
|
|
+
|
|
|
+ return f"已设置表格 {table_index} 单元格 ({row},{col}) 的文本"
|
|
|
+
|
|
|
+def set_table_style(table_index, style_name):
|
|
|
+ """设置表格样式"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ try:
|
|
|
+ table.style = style_name
|
|
|
+ return f"已将表格 {table_index} 的样式设置为 {style_name}"
|
|
|
+ except Exception as e:
|
|
|
+ return f"设置表格样式失败: {str(e)}"
|
|
|
+
|
|
|
+def set_table_cell_background(table_index, row, col, color):
|
|
|
+ """设置表格单元格背景色"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 检查行列索引是否有效
|
|
|
+ if row < 0 or col < 0 or row >= len(table.rows) or col >= len(table.columns):
|
|
|
+ return "错误:行列索引超出范围"
|
|
|
+
|
|
|
+ # 获取单元格
|
|
|
+ cell = table.cell(row, col)
|
|
|
+
|
|
|
+ # 设置背景色
|
|
|
+ # 注意:这需要使用低级API
|
|
|
+ try:
|
|
|
+ shading_elm = cell._element.get_or_add_tcPr().get_or_add_shd()
|
|
|
+ shading_elm.set(qn('w:fill'), color)
|
|
|
+ return f"已设置表格 {table_index} 单元格 ({row},{col}) 的背景色为 {color}"
|
|
|
+ except Exception as e:
|
|
|
+ return f"设置单元格背景色失败: {str(e)}"
|
|
|
+
|
|
|
+def set_table_cell_vertical_alignment(table_index, row, col, alignment):
|
|
|
+ """设置表格单元格垂直对齐方式"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 检查行列索引是否有效
|
|
|
+ if row < 0 or col < 0 or row >= len(table.rows) or col >= len(table.columns):
|
|
|
+ return "错误:行列索引超出范围"
|
|
|
+
|
|
|
+ # 获取单元格
|
|
|
+ cell = table.cell(row, col)
|
|
|
+
|
|
|
+ # 设置垂直对齐方式
|
|
|
+ alignment_map = {
|
|
|
+ "TOP": WD_ALIGN_VERTICAL.TOP,
|
|
|
+ "CENTER": WD_ALIGN_VERTICAL.CENTER,
|
|
|
+ "BOTTOM": WD_ALIGN_VERTICAL.BOTTOM
|
|
|
+ }
|
|
|
+
|
|
|
+ if alignment not in alignment_map:
|
|
|
+ return f"错误:不支持的垂直对齐方式 {alignment}"
|
|
|
+
|
|
|
+ cell.vertical_alignment = alignment_map[alignment]
|
|
|
+
|
|
|
+ return f"已设置表格 {table_index} 单元格 ({row},{col}) 的垂直对齐方式为 {alignment}"
|
|
|
+
|
|
|
+def set_table_width(table_index, width):
|
|
|
+ """设置表格宽度"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 设置表格宽度
|
|
|
+ # 注意:python-docx对表格宽度的支持有限
|
|
|
+ # 这里使用一个近似的方法
|
|
|
+ table.autofit = False
|
|
|
+ table.width = Cm(width)
|
|
|
+
|
|
|
+ return f"已将表格 {table_index} 的宽度设置为 {width}cm"
|
|
|
+
|
|
|
+def set_table_column_width(table_index, col, width):
|
|
|
+ """设置表格列宽"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 检查列索引是否有效
|
|
|
+ if col < 0 or col >= len(table.columns):
|
|
|
+ return "错误:列索引超出范围"
|
|
|
+
|
|
|
+ # 设置列宽
|
|
|
+ for cell in table.columns[col].cells:
|
|
|
+ cell.width = Cm(width)
|
|
|
+
|
|
|
+ return f"已将表格 {table_index} 列 {col} 的宽度设置为 {width}cm"
|
|
|
+
|
|
|
+# 高级功能
|
|
|
+def add_hyperlink(paragraph_index, text, url):
|
|
|
+ """添加超链接"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if paragraph_index >= len(doc.paragraphs):
|
|
|
+ return f"错误:段落索引 {paragraph_index} 超出范围"
|
|
|
+
|
|
|
+ paragraph = doc.paragraphs[paragraph_index]
|
|
|
+
|
|
|
+ # 添加超链接
|
|
|
+ # 注意:python-docx对超链接的支持有限
|
|
|
+ # 这里使用一个简单的方法
|
|
|
+ run = paragraph.add_run(text)
|
|
|
+ run.font.underline = True
|
|
|
+ run.font.color.rgb = RGBColor(0, 0, 255) # 蓝色
|
|
|
+
|
|
|
+ # 实际上,这并不是真正的超链接,只是模拟了超链接的外观
|
|
|
+ # 要添加真正的超链接,需要使用更低级的API
|
|
|
+
|
|
|
+ return f"已为段落 {paragraph_index} 添加超链接: {text} -> {url}"
|
|
|
+
|
|
|
+def add_bookmark(paragraph_index, bookmark_name, text):
|
|
|
+ """添加书签"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if paragraph_index >= len(doc.paragraphs):
|
|
|
+ return f"错误:段落索引 {paragraph_index} 超出范围"
|
|
|
+
|
|
|
+ paragraph = doc.paragraphs[paragraph_index]
|
|
|
+
|
|
|
+ # 添加书签
|
|
|
+ # 注意:python-docx对书签的支持有限
|
|
|
+ # 这里只是添加文本,而不是真正的书签
|
|
|
+ paragraph.add_run(text)
|
|
|
+
|
|
|
+ return f"已为段落 {paragraph_index} 添加书签: {bookmark_name} -> {text}"
|
|
|
+
|
|
|
+def add_table_of_contents(title, levels=3):
|
|
|
+ """添加目录"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ # 添加目录标题
|
|
|
+ doc.add_heading(title, level=1)
|
|
|
+
|
|
|
+ # 添加目录占位符段落
|
|
|
+ paragraph = doc.add_paragraph()
|
|
|
+ run = paragraph.add_run("目录将在Word中自动生成")
|
|
|
+
|
|
|
+ return f"已添加目录占位符,标题为 {title},包含 {levels} 级标题"
|
|
|
+
|
|
|
+# 添加设置表格边框的函数
|
|
|
+def set_table_borders(table_index, border_style="single", border_width=1, border_color="000000",
|
|
|
+ apply_to="all", first_row=False, first_column=False, last_row=False, last_column=False):
|
|
|
+ """设置表格边框样式"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 边框样式映射
|
|
|
+ style_map = {
|
|
|
+ "single": "single",
|
|
|
+ "thick": "thick",
|
|
|
+ "double": "double",
|
|
|
+ "dotted": "dotted",
|
|
|
+ "dashed": "dashed",
|
|
|
+ "none": "nil"
|
|
|
+ }
|
|
|
+
|
|
|
+ if border_style not in style_map:
|
|
|
+ return f"错误:不支持的边框样式 {border_style}"
|
|
|
+
|
|
|
+ border_style = style_map[border_style]
|
|
|
+
|
|
|
+ # 应用边框的位置映射
|
|
|
+ positions = []
|
|
|
+ if apply_to == "all":
|
|
|
+ positions = ["top", "bottom", "left", "right", "insideH", "insideV"]
|
|
|
+ elif apply_to == "outside":
|
|
|
+ positions = ["top", "bottom", "left", "right"]
|
|
|
+ elif apply_to == "inside":
|
|
|
+ positions = ["insideH", "insideV"]
|
|
|
+ elif apply_to in ["top", "bottom", "left", "right"]:
|
|
|
+ positions = [apply_to]
|
|
|
+ else:
|
|
|
+ return f"错误:不支持的边框应用位置 {apply_to}"
|
|
|
+
|
|
|
+ # 为表格设置边框
|
|
|
+ tbl = table._tbl
|
|
|
+
|
|
|
+ # 获取表格属性
|
|
|
+ tblPr = tbl.xpath('./w:tblPr')
|
|
|
+ if not tblPr:
|
|
|
+ tblPr = OxmlElement('w:tblPr')
|
|
|
+ tbl.insert(0, tblPr)
|
|
|
+ else:
|
|
|
+ tblPr = tblPr[0]
|
|
|
+
|
|
|
+ # 创建边框元素
|
|
|
+ tblBorders = OxmlElement('w:tblBorders')
|
|
|
+
|
|
|
+ # 添加各个位置的边框
|
|
|
+ for position in positions:
|
|
|
+ border = OxmlElement(f'w:{position}')
|
|
|
+ border.set(qn('w:val'), border_style)
|
|
|
+ border.set(qn('w:sz'), str(border_width * 8)) # 8 = 1pt
|
|
|
+ border.set(qn('w:space'), '0')
|
|
|
+ border.set(qn('w:color'), border_color)
|
|
|
+ tblBorders.append(border)
|
|
|
+
|
|
|
+ # 移除现有边框并添加新边框
|
|
|
+ existing_borders = tblPr.xpath('./w:tblBorders')
|
|
|
+ if existing_borders:
|
|
|
+ tblPr.remove(existing_borders[0])
|
|
|
+ tblPr.append(tblBorders)
|
|
|
+
|
|
|
+ # 处理特殊行列边框
|
|
|
+ if first_row or first_column or last_row or last_column:
|
|
|
+ # 创建表格样式属性
|
|
|
+ tblLook = OxmlElement('w:tblLook')
|
|
|
+ tblLook.set(qn('w:val'), '0000')
|
|
|
+ tblLook.set(qn('w:firstRow'), '1' if first_row else '0')
|
|
|
+ tblLook.set(qn('w:lastRow'), '1' if last_row else '0')
|
|
|
+ tblLook.set(qn('w:firstColumn'), '1' if first_column else '0')
|
|
|
+ tblLook.set(qn('w:lastColumn'), '1' if last_column else '0')
|
|
|
+ tblLook.set(qn('w:noHBand'), '0')
|
|
|
+ tblLook.set(qn('w:noVBand'), '0')
|
|
|
+
|
|
|
+ # 移除现有样式并添加新样式
|
|
|
+ existing_tblLook = tblPr.xpath('./w:tblLook')
|
|
|
+ if existing_tblLook:
|
|
|
+ tblPr.remove(existing_tblLook[0])
|
|
|
+ tblPr.append(tblLook)
|
|
|
+
|
|
|
+ return f"已设置表格 {table_index} 的边框样式"
|
|
|
+
|
|
|
+# 修改 set_cell_borders 函数
|
|
|
+def set_cell_borders(table_index, row, col, border_style="single", border_width=1, border_color="000000",
|
|
|
+ apply_to="all"):
|
|
|
+ """设置单元格边框样式"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 检查行列索引是否有效
|
|
|
+ if row < 0 or col < 0 or row >= len(table.rows) or col >= len(table.columns):
|
|
|
+ return "错误:行列索引超出范围"
|
|
|
+
|
|
|
+ # 获取单元格
|
|
|
+ cell = table.cell(row, col)
|
|
|
+
|
|
|
+ # 边框样式映射
|
|
|
+ style_map = {
|
|
|
+ "single": "single",
|
|
|
+ "thick": "thick",
|
|
|
+ "double": "double",
|
|
|
+ "dotted": "dotted",
|
|
|
+ "dashed": "dashed",
|
|
|
+ "none": "nil"
|
|
|
+ }
|
|
|
+
|
|
|
+ if border_style not in style_map:
|
|
|
+ return f"错误:不支持的边框样式 {border_style}"
|
|
|
+
|
|
|
+ border_style = style_map[border_style]
|
|
|
+
|
|
|
+ # 应用边框的位置
|
|
|
+ positions = []
|
|
|
+ if apply_to == "all":
|
|
|
+ positions = ["top", "bottom", "left", "right"]
|
|
|
+ elif apply_to in ["top", "bottom", "left", "right"]:
|
|
|
+ positions = [apply_to]
|
|
|
+ else:
|
|
|
+ return f"错误:不支持的边框应用位置 {apply_to}"
|
|
|
+
|
|
|
+ # 为单元格设置边框
|
|
|
+ tc = cell._tc
|
|
|
+
|
|
|
+ # 获取单元格属性
|
|
|
+ tcPr = tc.xpath('./w:tcPr')
|
|
|
+ if not tcPr:
|
|
|
+ tcPr = OxmlElement('w:tcPr')
|
|
|
+ tc.insert(0, tcPr)
|
|
|
+ else:
|
|
|
+ tcPr = tcPr[0]
|
|
|
+
|
|
|
+ # 创建边框元素
|
|
|
+ tcBorders = OxmlElement('w:tcBorders')
|
|
|
+
|
|
|
+ # 添加各个位置的边框
|
|
|
+ for position in positions:
|
|
|
+ border = OxmlElement(f'w:{position}')
|
|
|
+ border.set(qn('w:val'), border_style)
|
|
|
+ border.set(qn('w:sz'), str(border_width * 8)) # 8 = 1pt
|
|
|
+ border.set(qn('w:space'), '0')
|
|
|
+ border.set(qn('w:color'), border_color)
|
|
|
+ tcBorders.append(border)
|
|
|
+
|
|
|
+ # 移除现有边框并添加新边框
|
|
|
+ existing_borders = tcPr.xpath('./w:tcBorders')
|
|
|
+ if existing_borders:
|
|
|
+ tcPr.remove(existing_borders[0])
|
|
|
+ tcPr.append(tcBorders)
|
|
|
+
|
|
|
+ return f"已设置表格 {table_index} 单元格 ({row},{col}) 的边框样式"
|
|
|
+
|
|
|
+# 添加一个新的函数,用于一次性为整个表格添加标准边框
|
|
|
+def add_table_standard_borders(table_index):
|
|
|
+ """为表格添加标准边框(所有单元格都有边框)"""
|
|
|
+ doc = get_document()
|
|
|
+
|
|
|
+ if table_index >= len(doc.tables):
|
|
|
+ return f"错误:表格索引 {table_index} 超出范围"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 为表格设置标准边框
|
|
|
+ for row in table.rows:
|
|
|
+ for cell in row.cells:
|
|
|
+ # 获取单元格的XML元素
|
|
|
+ tc = cell._tc
|
|
|
+
|
|
|
+ # 获取单元格属性
|
|
|
+ tcPr = tc.xpath('./w:tcPr')
|
|
|
+ if not tcPr:
|
|
|
+ tcPr = OxmlElement('w:tcPr')
|
|
|
+ tc.insert(0, tcPr)
|
|
|
+ else:
|
|
|
+ tcPr = tcPr[0]
|
|
|
+
|
|
|
+ # 创建边框元素
|
|
|
+ tcBorders = OxmlElement('w:tcBorders')
|
|
|
+
|
|
|
+ # 添加四个方向的边框
|
|
|
+ for position in ["top", "bottom", "left", "right"]:
|
|
|
+ border = OxmlElement(f'w:{position}')
|
|
|
+ border.set(qn('w:val'), 'single')
|
|
|
+ border.set(qn('w:sz'), '4') # 0.5pt
|
|
|
+ border.set(qn('w:space'), '0')
|
|
|
+ border.set(qn('w:color'), '000000')
|
|
|
+ tcBorders.append(border)
|
|
|
+
|
|
|
+ # 移除现有边框并添加新边框
|
|
|
+ existing_borders = tcPr.xpath('./w:tcBorders')
|
|
|
+ if existing_borders:
|
|
|
+ tcPr.remove(existing_borders[0])
|
|
|
+ tcPr.append(tcBorders)
|
|
|
+
|
|
|
+ return f"已为表格 {table_index} 添加标准边框"
|
|
|
+
|
|
|
+def add_border(cell):
|
|
|
+ """为单元格添加边框"""
|
|
|
+ tcPr = cell._element.tcPr
|
|
|
+ if tcPr is None:
|
|
|
+ tcPr = OxmlElement('w:tcPr')
|
|
|
+ cell._element.append(tcPr)
|
|
|
+
|
|
|
+ # 添加边框
|
|
|
+ tcBorders = OxmlElement('w:tcBorders')
|
|
|
+ for border in ['top', 'left', 'bottom', 'right']:
|
|
|
+ border_element = OxmlElement(f'w:{border}')
|
|
|
+ border_element.set(qn('w:val'), 'single')
|
|
|
+ border_element.set(qn('w:sz'), '4') # 边框宽度
|
|
|
+ border_element.set(qn('w:space'), '0')
|
|
|
+ border_element.set(qn('w:color'), '000000') # 边框颜色
|
|
|
+ tcBorders.append(border_element)
|
|
|
+
|
|
|
+ tcPr.append(tcBorders)
|
|
|
+
|
|
|
+def copy_cell_style(excel_cell, word_cell):
|
|
|
+ """复制单元格样式"""
|
|
|
+ # 获取Excel单元格的字体
|
|
|
+ if excel_cell.font:
|
|
|
+ run = word_cell.paragraphs[0].runs
|
|
|
+ if not run:
|
|
|
+ run = word_cell.paragraphs[0].add_run()
|
|
|
+ else:
|
|
|
+ run = run[0]
|
|
|
+
|
|
|
+ # 设置字体名称
|
|
|
+ if excel_cell.font.name:
|
|
|
+ run.font.name = excel_cell.font.name
|
|
|
+
|
|
|
+ # 设置字体大小
|
|
|
+ if excel_cell.font.size:
|
|
|
+ # Excel字体大小与Word字体大小的转换
|
|
|
+ run.font.size = Pt(excel_cell.font.size)
|
|
|
+
|
|
|
+ # 设置粗体
|
|
|
+ if excel_cell.font.bold:
|
|
|
+ run.font.bold = True
|
|
|
+
|
|
|
+ # 设置斜体
|
|
|
+ if excel_cell.font.italic:
|
|
|
+ run.font.italic = True
|
|
|
+
|
|
|
+ # 设置下划线
|
|
|
+ if excel_cell.font.underline:
|
|
|
+ run.font.underline = True
|
|
|
+
|
|
|
+ # 设置字体颜色
|
|
|
+ if excel_cell.font.color and excel_cell.font.color.rgb:
|
|
|
+ color = excel_cell.font.color.rgb
|
|
|
+ if isinstance(color, str) and len(color) == 8: # ARGB格式
|
|
|
+ rgb = color[2:] # 去掉Alpha通道
|
|
|
+ run.font.color.rgb = RGBColor.from_string(rgb)
|
|
|
+
|
|
|
+ # 设置单元格对齐方式
|
|
|
+ if excel_cell.alignment:
|
|
|
+ paragraph = word_cell.paragraphs[0]
|
|
|
+
|
|
|
+ # 水平对齐
|
|
|
+ if excel_cell.alignment.horizontal:
|
|
|
+ if excel_cell.alignment.horizontal == 'center':
|
|
|
+ paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
|
+ elif excel_cell.alignment.horizontal == 'right':
|
|
|
+ paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
|
|
+ elif excel_cell.alignment.horizontal == 'left':
|
|
|
+ paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
|
|
+
|
|
|
+ # 添加边框
|
|
|
+ add_border(word_cell)
|
|
|
+
|
|
|
+def copy_excel_to_word(excel_path, word_path, sheet_name=None, output_path=None):
|
|
|
+ """
|
|
|
+ 将Excel表格复制到Word文档
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ excel_path: Excel文件路径
|
|
|
+ word_path: Word文件路径,如果为None则创建新文档
|
|
|
+ sheet_name: 要复制的工作表名称,如果为None则复制第一个工作表
|
|
|
+ output_path: 输出Word文件路径,如果为None则覆盖原文件
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 加载Excel文件
|
|
|
+ if not os.path.exists(excel_path):
|
|
|
+ return {"error": f"Excel文件不存在: {excel_path}"}
|
|
|
+
|
|
|
+ wb = openpyxl.load_workbook(excel_path, data_only=True)
|
|
|
+
|
|
|
+ # 选择工作表
|
|
|
+ if sheet_name:
|
|
|
+ if sheet_name not in wb.sheetnames:
|
|
|
+ return {"error": f"工作表 '{sheet_name}' 不存在"}
|
|
|
+ ws = wb[sheet_name]
|
|
|
+ else:
|
|
|
+ ws = wb.active
|
|
|
+
|
|
|
+ # 加载或创建Word文档
|
|
|
+ if word_path and os.path.exists(word_path):
|
|
|
+ doc = Document(word_path)
|
|
|
+ else:
|
|
|
+ doc = Document()
|
|
|
+
|
|
|
+ # 获取Excel表格的行数和列数
|
|
|
+ max_row = ws.max_row
|
|
|
+ max_col = ws.max_column
|
|
|
+
|
|
|
+ if max_row == 0 or max_col == 0:
|
|
|
+ return {"error": "Excel表格为空"}
|
|
|
+
|
|
|
+ # 在Word文档中创建表格
|
|
|
+ table = doc.add_table(rows=max_row, cols=max_col)
|
|
|
+ table.style = 'Table Grid' # 应用网格样式
|
|
|
+
|
|
|
+ # 复制数据和样式
|
|
|
+ for i in range(max_row):
|
|
|
+ for j in range(max_col):
|
|
|
+ # Excel单元格索引从1开始
|
|
|
+ excel_cell = ws.cell(row=i+1, column=j+1)
|
|
|
+
|
|
|
+ # Word表格索引从0开始
|
|
|
+ word_cell = table.cell(i, j)
|
|
|
+
|
|
|
+ # 复制单元格内容
|
|
|
+ word_cell.text = str(excel_cell.value) if excel_cell.value is not None else ""
|
|
|
+
|
|
|
+ # 复制单元格样式
|
|
|
+ copy_cell_style(excel_cell, word_cell)
|
|
|
+
|
|
|
+ # 保存Word文档
|
|
|
+ save_path = output_path if output_path else (word_path if word_path else "output.docx")
|
|
|
+ doc.save(save_path)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已将Excel表格复制到Word文档",
|
|
|
+ "excel_path": excel_path,
|
|
|
+ "sheet_name": sheet_name if sheet_name else ws.title,
|
|
|
+ "output_path": save_path
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"复制Excel表格到Word文档失败: {str(e)}"}
|
|
|
+
|
|
|
+def copy_excel_range_to_word(excel_path, word_path, sheet_name, range_str, output_path=None):
|
|
|
+ """
|
|
|
+ 将Excel表格的指定区域复制到Word文档
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ excel_path: Excel文件路径
|
|
|
+ word_path: Word文件路径,如果为None则创建新文档
|
|
|
+ sheet_name: 要复制的工作表名称
|
|
|
+ range_str: 要复制的单元格区域,如'A1:C5'
|
|
|
+ output_path: 输出Word文件路径,如果为None则覆盖原文件
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ from openpyxl.utils.cell import range_boundaries
|
|
|
+
|
|
|
+ # 加载Excel文件
|
|
|
+ if not os.path.exists(excel_path):
|
|
|
+ return {"error": f"Excel文件不存在: {excel_path}"}
|
|
|
+
|
|
|
+ wb = openpyxl.load_workbook(excel_path, data_only=True)
|
|
|
+
|
|
|
+ # 选择工作表
|
|
|
+ if sheet_name not in wb.sheetnames:
|
|
|
+ return {"error": f"工作表 '{sheet_name}' 不存在"}
|
|
|
+ ws = wb[sheet_name]
|
|
|
+
|
|
|
+ # 解析区域范围
|
|
|
+ try:
|
|
|
+ min_col, min_row, max_col, max_row = range_boundaries(range_str)
|
|
|
+ except ValueError:
|
|
|
+ return {"error": f"无效的区域范围: {range_str}"}
|
|
|
+
|
|
|
+ # 计算行数和列数
|
|
|
+ rows = max_row - min_row + 1
|
|
|
+ cols = max_col - min_col + 1
|
|
|
+
|
|
|
+ if rows <= 0 or cols <= 0:
|
|
|
+ return {"error": "指定的区域无效"}
|
|
|
+
|
|
|
+ # 加载或创建Word文档
|
|
|
+ if word_path and os.path.exists(word_path):
|
|
|
+ doc = Document(word_path)
|
|
|
+ else:
|
|
|
+ doc = Document()
|
|
|
+
|
|
|
+ # 在Word文档中创建表格
|
|
|
+ table = doc.add_table(rows=rows, cols=cols)
|
|
|
+ table.style = 'Table Grid' # 应用网格样式
|
|
|
+
|
|
|
+ # 复制数据和样式
|
|
|
+ for i in range(rows):
|
|
|
+ for j in range(cols):
|
|
|
+ # Excel单元格索引
|
|
|
+ excel_row = min_row + i
|
|
|
+ excel_col = min_col + j
|
|
|
+
|
|
|
+ # 获取Excel单元格
|
|
|
+ excel_cell = ws.cell(row=excel_row, column=excel_col)
|
|
|
+
|
|
|
+ # Word表格索引从0开始
|
|
|
+ word_cell = table.cell(i, j)
|
|
|
+
|
|
|
+ # 复制单元格内容
|
|
|
+ word_cell.text = str(excel_cell.value) if excel_cell.value is not None else ""
|
|
|
+
|
|
|
+ # 复制单元格样式
|
|
|
+ copy_cell_style(excel_cell, word_cell)
|
|
|
+
|
|
|
+ # 保存Word文档
|
|
|
+ save_path = output_path if output_path else (word_path if word_path else "output.docx")
|
|
|
+ doc.save(save_path)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已将Excel表格区域复制到Word文档",
|
|
|
+ "excel_path": excel_path,
|
|
|
+ "sheet_name": sheet_name,
|
|
|
+ "range": range_str,
|
|
|
+ "output_path": save_path
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"复制Excel表格区域到Word文档失败: {str(e)}"}
|
|
|
+
|
|
|
+def add_table_row(table_index=0, position=None):
|
|
|
+ """
|
|
|
+ 在Word文档中的表格添加一行
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ position: 要插入行的位置,如果为None则添加到表格末尾
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return f"错误:文档中没有索引为 {table_index} 的表格"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ if position is None:
|
|
|
+ # 添加到表格末尾
|
|
|
+ table.add_row()
|
|
|
+ return f"已在表格 {table_index} 末尾添加一行"
|
|
|
+ else:
|
|
|
+ # 在指定位置插入行
|
|
|
+ row_count = len(table.rows)
|
|
|
+ if position < 0 or position > row_count:
|
|
|
+ return f"错误:行索引超出范围,有效范围为0-{row_count}"
|
|
|
+
|
|
|
+ # 创建新行
|
|
|
+ new_row = table.add_row()
|
|
|
+
|
|
|
+ # 如果不是添加到末尾,需要移动行
|
|
|
+ if position < row_count:
|
|
|
+ # 将新行移动到指定位置
|
|
|
+ for i in range(row_count, position, -1):
|
|
|
+ # 复制上一行的内容到当前行
|
|
|
+ for j in range(len(table.columns)):
|
|
|
+ table.cell(i, j).text = table.cell(i-1, j).text
|
|
|
+
|
|
|
+ # 清空目标位置的行
|
|
|
+ for j in range(len(table.columns)):
|
|
|
+ table.cell(position, j).text = ""
|
|
|
+
|
|
|
+ return f"已在表格 {table_index} 的位置 {position} 插入一行"
|
|
|
+ except Exception as e:
|
|
|
+ return f"添加表格行时出错: {str(e)}"
|
|
|
+
|
|
|
+def add_table_column(table_index=0, position=None):
|
|
|
+ """
|
|
|
+ 在Word文档中的表格添加一列
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ position: 要插入列的位置,如果为None则添加到表格末尾
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return f"错误:文档中没有索引为 {table_index} 的表格"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 获取当前列数
|
|
|
+ col_count = len(table.columns)
|
|
|
+
|
|
|
+ if position is None:
|
|
|
+ position = col_count
|
|
|
+ elif position < 0 or position > col_count:
|
|
|
+ return f"错误:列索引超出范围,有效范围为0-{col_count}"
|
|
|
+
|
|
|
+ # 为每一行添加一个单元格
|
|
|
+ for i, row in enumerate(table.rows):
|
|
|
+ # 获取表格行的XML元素
|
|
|
+ tr = row._tr
|
|
|
+
|
|
|
+ # 创建新的单元格元素
|
|
|
+ tc = OxmlElement('w:tc')
|
|
|
+
|
|
|
+ # 添加段落到单元格
|
|
|
+ p = OxmlElement('w:p')
|
|
|
+ tc.append(p)
|
|
|
+
|
|
|
+ # 添加单元格属性
|
|
|
+ tcPr = OxmlElement('w:tcPr')
|
|
|
+ tc.append(tcPr)
|
|
|
+
|
|
|
+ # 确定插入位置
|
|
|
+ if position == col_count:
|
|
|
+ # 添加到末尾
|
|
|
+ tr.append(tc)
|
|
|
+ else:
|
|
|
+ # 在指定位置插入
|
|
|
+ ref_tc = tr.findall('.//w:tc', namespaces=tr.nsmap)[position]
|
|
|
+ ref_tc.addprevious(tc)
|
|
|
+
|
|
|
+ return f"已在表格 {table_index} 的位置 {position} 插入一列"
|
|
|
+ except Exception as e:
|
|
|
+ return f"添加表格列时出错: {str(e)}"
|
|
|
+
|
|
|
+def delete_table_row(table_index=0, row_index=None):
|
|
|
+ """
|
|
|
+ 删除Word文档中表格的一行
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ row_index: 要删除的行索引
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return f"错误:文档中没有索引为 {table_index} 的表格"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ row_count = len(table.rows)
|
|
|
+ if row_index is None or row_index < 0 or row_index >= row_count:
|
|
|
+ return f"错误:行索引超出范围,有效范围为0-{row_count-1}"
|
|
|
+
|
|
|
+ # 获取要删除的行
|
|
|
+ tr = table.rows[row_index]._tr
|
|
|
+
|
|
|
+ # 从表格中删除行
|
|
|
+ tr.getparent().remove(tr)
|
|
|
+
|
|
|
+ return f"已删除表格 {table_index} 的第 {row_index} 行"
|
|
|
+ except Exception as e:
|
|
|
+ return f"删除表格行时出错: {str(e)}"
|
|
|
+
|
|
|
+def delete_table_column(table_index=0, column_index=None):
|
|
|
+ """
|
|
|
+ 删除Word文档中表格的一列
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ column_index: 要删除的列索引
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return f"错误:文档中没有索引为 {table_index} 的表格"
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ col_count = len(table.columns)
|
|
|
+ if column_index is None or column_index < 0 or column_index >= col_count:
|
|
|
+ return f"错误:列索引超出范围,有效范围为0-{col_count-1}"
|
|
|
+
|
|
|
+ # 遍历每一行,删除指定列的单元格
|
|
|
+ for row in table.rows:
|
|
|
+ # 获取行的XML元素
|
|
|
+ tr = row._tr
|
|
|
+
|
|
|
+ # 找到要删除的单元格
|
|
|
+ cells = tr.findall('.//w:tc', namespaces=tr.nsmap)
|
|
|
+ if column_index < len(cells):
|
|
|
+ # 删除单元格
|
|
|
+ tr.remove(cells[column_index])
|
|
|
+
|
|
|
+ return f"已删除表格 {table_index} 的第 {column_index} 列"
|
|
|
+ except Exception as e:
|
|
|
+ return f"删除表格列时出错: {str(e)}"
|
|
|
+
|
|
|
+def get_table_dimensions(table_index=0):
|
|
|
+ """
|
|
|
+ 获取表格的维度(行数和列数)
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 表格的行数和列数
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+ rows = len(table.rows)
|
|
|
+ cols = len(table.columns)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "rows": rows,
|
|
|
+ "columns": cols
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取表格维度时出错: {str(e)}"}
|
|
|
+
|
|
|
+def get_table_cell_text(table_index=0, row=0, col=0):
|
|
|
+ """
|
|
|
+ 获取表格单元格的文本内容
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ row: 行索引
|
|
|
+ col: 列索引
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 单元格的文本内容
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ if row < 0 or row >= len(table.rows) or col < 0 or col >= len(table.columns):
|
|
|
+ return {"error": f"单元格索引 ({row}, {col}) 超出范围"}
|
|
|
+
|
|
|
+ cell_text = table.cell(row, col).text
|
|
|
+
|
|
|
+ return {
|
|
|
+ "text": cell_text
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取单元格文本时出错: {str(e)}"}
|
|
|
+
|
|
|
+def get_table_row(table_index=0, row=0):
|
|
|
+ """
|
|
|
+ 获取表格中一行的所有单元格文本
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ row: 行索引
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 行中所有单元格的文本内容列表
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ if row < 0 or row >= len(table.rows):
|
|
|
+ return {"error": f"行索引 {row} 超出范围"}
|
|
|
+
|
|
|
+ row_cells = []
|
|
|
+ for cell in table.rows[row].cells:
|
|
|
+ row_cells.append(cell.text)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "cells": row_cells
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取表格行时出错: {str(e)}"}
|
|
|
+
|
|
|
+def get_table_column(table_index=0, col=0):
|
|
|
+ """
|
|
|
+ 获取表格中一列的所有单元格文本
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ col: 列索引
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 列中所有单元格的文本内容列表
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ if col < 0 or col >= len(table.columns):
|
|
|
+ return {"error": f"列索引 {col} 超出范围"}
|
|
|
+
|
|
|
+ column_cells = []
|
|
|
+ for row in table.rows:
|
|
|
+ column_cells.append(row.cells[col].text)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "cells": column_cells
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取表格列时出错: {str(e)}"}
|
|
|
+
|
|
|
+def get_table_range(table_index=0, start_row=0, start_col=0, end_row=None, end_col=None):
|
|
|
+ """
|
|
|
+ 获取表格中指定范围的单元格文本
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ start_row: 起始行索引
|
|
|
+ start_col: 起始列索引
|
|
|
+ end_row: 结束行索引,如果为None则取最后一行
|
|
|
+ end_col: 结束列索引,如果为None则取最后一列
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 指定范围内所有单元格的文本内容二维数组
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 设置默认结束位置
|
|
|
+ if end_row is None:
|
|
|
+ end_row = len(table.rows) - 1
|
|
|
+ if end_col is None:
|
|
|
+ end_col = len(table.columns) - 1
|
|
|
+
|
|
|
+ # 检查索引范围
|
|
|
+ if start_row < 0 or start_row > end_row or end_row >= len(table.rows):
|
|
|
+ return {"error": f"行索引范围 ({start_row}, {end_row}) 无效"}
|
|
|
+ if start_col < 0 or start_col > end_col or end_col >= len(table.columns):
|
|
|
+ return {"error": f"列索引范围 ({start_col}, {end_col}) 无效"}
|
|
|
+
|
|
|
+ # 获取范围内的单元格文本
|
|
|
+ range_data = []
|
|
|
+ for row in range(start_row, end_row + 1):
|
|
|
+ row_data = []
|
|
|
+ for col in range(start_col, end_col + 1):
|
|
|
+ row_data.append(table.cell(row, col).text)
|
|
|
+ range_data.append(row_data)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "data": range_data
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取表格范围时出错: {str(e)}"}
|
|
|
+
|
|
|
+def get_all_tables_info():
|
|
|
+ """
|
|
|
+ 获取文档中所有表格的基本信息
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 所有表格的行数和列数信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ tables_info = []
|
|
|
+
|
|
|
+ for i, table in enumerate(doc.tables):
|
|
|
+ tables_info.append({
|
|
|
+ "table_index": i,
|
|
|
+ "rows": len(table.rows),
|
|
|
+ "columns": len(table.columns)
|
|
|
+ })
|
|
|
+
|
|
|
+ return {
|
|
|
+ "tables_count": len(doc.tables),
|
|
|
+ "tables": tables_info
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取表格信息时出错: {str(e)}"}
|
|
|
+
|
|
|
+def set_table_data(table_index=0, data=None, start_row=0, start_col=0, clear_existing=False):
|
|
|
+ """
|
|
|
+ 批量设置表格数据
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ data: 二维数组数据
|
|
|
+ start_row: 起始行索引
|
|
|
+ start_col: 起始列索引
|
|
|
+ clear_existing: 是否清除现有内容
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ if not data or not isinstance(data, list):
|
|
|
+ return {"error": "数据必须是非空的二维数组"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+ rows = len(table.rows)
|
|
|
+ cols = len(table.columns)
|
|
|
+
|
|
|
+ # 检查数据是否超出表格范围
|
|
|
+ data_rows = len(data)
|
|
|
+ if data_rows == 0:
|
|
|
+ return {"error": "数据不能为空"}
|
|
|
+
|
|
|
+ data_cols = max(len(row) for row in data)
|
|
|
+ if start_row < 0 or start_row + data_rows > rows:
|
|
|
+ return {"error": f"数据行超出表格范围,表格有 {rows} 行,数据需要 {start_row + data_rows} 行"}
|
|
|
+
|
|
|
+ if start_col < 0 or start_col + data_cols > cols:
|
|
|
+ return {"error": f"数据列超出表格范围,表格有 {cols} 列,数据需要 {start_col + data_cols} 列"}
|
|
|
+
|
|
|
+ # 填充数据
|
|
|
+ for i, row_data in enumerate(data):
|
|
|
+ row_idx = start_row + i
|
|
|
+ for j, cell_value in enumerate(row_data):
|
|
|
+ col_idx = start_col + j
|
|
|
+ cell = table.cell(row_idx, col_idx)
|
|
|
+
|
|
|
+ # 如果需要清除现有内容
|
|
|
+ if clear_existing:
|
|
|
+ cell.text = ""
|
|
|
+
|
|
|
+ # 设置单元格文本
|
|
|
+ if cell_value is not None:
|
|
|
+ if cell.text and not clear_existing:
|
|
|
+ cell.text += "\n" + str(cell_value)
|
|
|
+ else:
|
|
|
+ cell.text = str(cell_value)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已设置表格 {table_index} 的数据,从 ({start_row}, {start_col}) 开始,共 {data_rows} 行 {data_cols} 列"
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"设置表格数据时出错: {str(e)}"}
|
|
|
+
|
|
|
+def set_table_row_data(table_index=0, row=0, data=None, start_col=0, clear_existing=False):
|
|
|
+ """
|
|
|
+ 设置表格一行的数据
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ row: 行索引
|
|
|
+ data: 一维数组数据
|
|
|
+ start_col: 起始列索引
|
|
|
+ clear_existing: 是否清除现有内容
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ if not data or not isinstance(data, list):
|
|
|
+ return {"error": "数据必须是非空的一维数组"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ if row < 0 or row >= len(table.rows):
|
|
|
+ return {"error": f"行索引 {row} 超出范围"}
|
|
|
+
|
|
|
+ cols = len(table.columns)
|
|
|
+ data_cols = len(data)
|
|
|
+
|
|
|
+ if start_col < 0 or start_col + data_cols > cols:
|
|
|
+ return {"error": f"数据列超出表格范围,表格有 {cols} 列,数据需要 {start_col + data_cols} 列"}
|
|
|
+
|
|
|
+ # 填充数据
|
|
|
+ for j, cell_value in enumerate(data):
|
|
|
+ col_idx = start_col + j
|
|
|
+ cell = table.cell(row, col_idx)
|
|
|
+
|
|
|
+ # 如果需要清除现有内容
|
|
|
+ if clear_existing:
|
|
|
+ cell.text = ""
|
|
|
+
|
|
|
+ # 设置单元格文本
|
|
|
+ if cell_value is not None:
|
|
|
+ if cell.text and not clear_existing:
|
|
|
+ cell.text += "\n" + str(cell_value)
|
|
|
+ else:
|
|
|
+ cell.text = str(cell_value)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已设置表格 {table_index} 的第 {row} 行数据,从列 {start_col} 开始,共 {data_cols} 列"
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"设置表格行数据时出错: {str(e)}"}
|
|
|
+
|
|
|
+def set_table_column_data(table_index=0, col=0, data=None, start_row=0, clear_existing=False):
|
|
|
+ """
|
|
|
+ 设置表格一列的数据
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ col: 列索引
|
|
|
+ data: 一维数组数据
|
|
|
+ start_row: 起始行索引
|
|
|
+ clear_existing: 是否清除现有内容
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ if not data or not isinstance(data, list):
|
|
|
+ return {"error": "数据必须是非空的一维数组"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ if col < 0 or col >= len(table.columns):
|
|
|
+ return {"error": f"列索引 {col} 超出范围"}
|
|
|
+
|
|
|
+ rows = len(table.rows)
|
|
|
+ data_rows = len(data)
|
|
|
+
|
|
|
+ if start_row < 0 or start_row + data_rows > rows:
|
|
|
+ return {"error": f"数据行超出表格范围,表格有 {rows} 行,数据需要 {start_row + data_rows} 行"}
|
|
|
+
|
|
|
+ # 填充数据
|
|
|
+ for i, cell_value in enumerate(data):
|
|
|
+ row_idx = start_row + i
|
|
|
+ cell = table.cell(row_idx, col)
|
|
|
+
|
|
|
+ # 如果需要清除现有内容
|
|
|
+ if clear_existing:
|
|
|
+ cell.text = ""
|
|
|
+
|
|
|
+ # 设置单元格文本
|
|
|
+ if cell_value is not None:
|
|
|
+ if cell.text and not clear_existing:
|
|
|
+ cell.text += "\n" + str(cell_value)
|
|
|
+ else:
|
|
|
+ cell.text = str(cell_value)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已设置表格 {table_index} 的第 {col} 列数据,从行 {start_row} 开始,共 {data_rows} 行"
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"设置表格列数据时出错: {str(e)}"}
|
|
|
+
|
|
|
+def clear_table_range(table_index=0, start_row=0, start_col=0, end_row=None, end_col=None):
|
|
|
+ """
|
|
|
+ 清除表格中指定范围的内容
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ start_row: 起始行索引
|
|
|
+ start_col: 起始列索引
|
|
|
+ end_row: 结束行索引,如果为None则取最后一行
|
|
|
+ end_col: 结束列索引,如果为None则取最后一列
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 设置默认结束位置
|
|
|
+ if end_row is None:
|
|
|
+ end_row = len(table.rows) - 1
|
|
|
+ if end_col is None:
|
|
|
+ end_col = len(table.columns) - 1
|
|
|
+
|
|
|
+ # 检查索引范围
|
|
|
+ if start_row < 0 or start_row > end_row or end_row >= len(table.rows):
|
|
|
+ return {"error": f"行索引范围 ({start_row}, {end_row}) 无效"}
|
|
|
+ if start_col < 0 or start_col > end_col or end_col >= len(table.columns):
|
|
|
+ return {"error": f"列索引范围 ({start_col}, {end_col}) 无效"}
|
|
|
+
|
|
|
+ # 清除范围内的单元格文本
|
|
|
+ for row in range(start_row, end_row + 1):
|
|
|
+ for col in range(start_col, end_col + 1):
|
|
|
+ table.cell(row, col).text = ""
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已清除表格 {table_index} 的区域 ({start_row}, {start_col}) 到 ({end_row}, {end_col})"
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"清除表格区域时出错: {str(e)}"}
|
|
|
+
|
|
|
+def get_table_detail_by_id(table_index=0):
|
|
|
+ """
|
|
|
+ 获取表格的详细信息,包括所有单元格的内容
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 表格的基本信息和单元格内容的多维数组
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+ rows = len(table.rows)
|
|
|
+ cols = len(table.columns)
|
|
|
+
|
|
|
+ # 获取所有单元格内容
|
|
|
+ cells_content = []
|
|
|
+ for i in range(rows):
|
|
|
+ row_content = []
|
|
|
+ for j in range(cols):
|
|
|
+ cell = table.cell(i, j)
|
|
|
+ row_content.append(cell.text)
|
|
|
+ cells_content.append(row_content)
|
|
|
+
|
|
|
+ # 构建表格详细信息
|
|
|
+ table_detail = {
|
|
|
+ "table_index": table_index,
|
|
|
+ "rows": rows,
|
|
|
+ "columns": cols,
|
|
|
+ "content": cells_content
|
|
|
+ }
|
|
|
+
|
|
|
+ return table_detail
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"获取表格详细信息时出错: {str(e)}"}
|
|
|
+
|
|
|
+def set_table_font(table_index=0, font_name=None, font_size=None, bold=None, italic=None,
|
|
|
+ color=None, start_row=0, end_row=None, start_col=0, end_col=None):
|
|
|
+ """
|
|
|
+ 设置表格中指定范围单元格的字体属性
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ font_name: 字体名称
|
|
|
+ font_size: 字体大小(磅)
|
|
|
+ bold: 是否加粗
|
|
|
+ italic: 是否斜体
|
|
|
+ color: 字体颜色(十六进制格式,如"FF0000"表示红色)
|
|
|
+ start_row: 起始行索引
|
|
|
+ end_row: 结束行索引,如果为None则取最后一行
|
|
|
+ start_col: 起始列索引
|
|
|
+ end_col: 结束列索引,如果为None则取最后一列
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 设置默认结束位置
|
|
|
+ if end_row is None:
|
|
|
+ end_row = len(table.rows) - 1
|
|
|
+ if end_col is None:
|
|
|
+ end_col = len(table.columns) - 1
|
|
|
+
|
|
|
+ # 检查索引范围
|
|
|
+ if start_row < 0 or start_row > end_row or end_row >= len(table.rows):
|
|
|
+ return {"error": f"行索引范围 ({start_row}, {end_row}) 无效"}
|
|
|
+ if start_col < 0 or start_col > end_col or end_col >= len(table.columns):
|
|
|
+ return {"error": f"列索引范围 ({start_col}, {end_col}) 无效"}
|
|
|
+
|
|
|
+ # 遍历指定范围的单元格
|
|
|
+ for row in range(start_row, end_row + 1):
|
|
|
+ for col in range(start_col, end_col + 1):
|
|
|
+ cell = table.cell(row, col)
|
|
|
+
|
|
|
+ # 遍历单元格中的所有段落
|
|
|
+ for paragraph in cell.paragraphs:
|
|
|
+ # 如果段落没有文本运行,添加一个空的运行
|
|
|
+ if not paragraph.runs:
|
|
|
+ paragraph.add_run()
|
|
|
+
|
|
|
+ # 设置每个文本运行的字体属性
|
|
|
+ for run in paragraph.runs:
|
|
|
+ if font_name:
|
|
|
+ run.font.name = font_name
|
|
|
+ # 设置中文字体
|
|
|
+ run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)
|
|
|
+
|
|
|
+ if font_size is not None:
|
|
|
+ run.font.size = Pt(font_size)
|
|
|
+
|
|
|
+ if bold is not None:
|
|
|
+ run.font.bold = bold
|
|
|
+
|
|
|
+ if italic is not None:
|
|
|
+ run.font.italic = italic
|
|
|
+
|
|
|
+ if color:
|
|
|
+ # 将十六进制颜色转换为RGB
|
|
|
+ if color.startswith('#'):
|
|
|
+ color = color[1:]
|
|
|
+ r = int(color[0:2], 16)
|
|
|
+ g = int(color[2:4], 16)
|
|
|
+ b = int(color[4:6], 16)
|
|
|
+ run.font.color.rgb = RGBColor(r, g, b)
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已设置表格 {table_index} 的字体属性,范围: ({start_row},{start_col}) 到 ({end_row},{end_col})"
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"设置表格字体时出错: {str(e)}"}
|
|
|
+
|
|
|
+def set_table_cell_alignment(table_index=0, start_row=0, end_row=None, start_col=0, end_col=None,
|
|
|
+ horizontal_alignment="CENTER", vertical_alignment="CENTER"):
|
|
|
+ """
|
|
|
+ 设置表格单元格的水平和垂直对齐方式
|
|
|
+
|
|
|
+ 参数:
|
|
|
+ table_index: 表格索引,默认为第一个表格
|
|
|
+ start_row: 起始行索引
|
|
|
+ end_row: 结束行索引,如果为None则取最后一行
|
|
|
+ start_col: 起始列索引
|
|
|
+ end_col: 结束列索引,如果为None则取最后一列
|
|
|
+ horizontal_alignment: 水平对齐方式,可选值:"LEFT", "CENTER", "RIGHT", "JUSTIFY"
|
|
|
+ vertical_alignment: 垂直对齐方式,可选值:"TOP", "CENTER", "BOTTOM"
|
|
|
+
|
|
|
+ 返回:
|
|
|
+ 成功或错误信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ doc = get_document()
|
|
|
+ if not doc.tables or table_index >= len(doc.tables):
|
|
|
+ return {"error": f"文档中没有索引为 {table_index} 的表格"}
|
|
|
+
|
|
|
+ table = doc.tables[table_index]
|
|
|
+
|
|
|
+ # 设置默认结束位置
|
|
|
+ if end_row is None:
|
|
|
+ end_row = len(table.rows) - 1
|
|
|
+ if end_col is None:
|
|
|
+ end_col = len(table.columns) - 1
|
|
|
+
|
|
|
+ # 检查索引范围
|
|
|
+ if start_row < 0 or start_row > end_row or end_row >= len(table.rows):
|
|
|
+ return {"error": f"行索引范围 ({start_row}, {end_row}) 无效"}
|
|
|
+ if start_col < 0 or start_col > end_col or end_col >= len(table.columns):
|
|
|
+ return {"error": f"列索引范围 ({start_col}, {end_col}) 无效"}
|
|
|
+
|
|
|
+ # 水平对齐方式映射
|
|
|
+ h_alignment_map = {
|
|
|
+ "LEFT": WD_ALIGN_PARAGRAPH.LEFT,
|
|
|
+ "CENTER": WD_ALIGN_PARAGRAPH.CENTER,
|
|
|
+ "RIGHT": WD_ALIGN_PARAGRAPH.RIGHT,
|
|
|
+ "JUSTIFY": WD_ALIGN_PARAGRAPH.JUSTIFY
|
|
|
+ }
|
|
|
+
|
|
|
+ # 垂直对齐方式映射
|
|
|
+ v_alignment_map = {
|
|
|
+ "TOP": WD_ALIGN_VERTICAL.TOP,
|
|
|
+ "CENTER": WD_ALIGN_VERTICAL.CENTER,
|
|
|
+ "BOTTOM": WD_ALIGN_VERTICAL.BOTTOM
|
|
|
+ }
|
|
|
+
|
|
|
+ # 检查对齐方式是否有效
|
|
|
+ if horizontal_alignment not in h_alignment_map:
|
|
|
+ return {"error": f"不支持的水平对齐方式: {horizontal_alignment}"}
|
|
|
+ if vertical_alignment not in v_alignment_map:
|
|
|
+ return {"error": f"不支持的垂直对齐方式: {vertical_alignment}"}
|
|
|
+
|
|
|
+ # 设置指定范围内单元格的对齐方式
|
|
|
+ for row in range(start_row, end_row + 1):
|
|
|
+ for col in range(start_col, end_col + 1):
|
|
|
+ cell = table.cell(row, col)
|
|
|
+
|
|
|
+ # 设置垂直对齐方式
|
|
|
+ cell.vertical_alignment = v_alignment_map[vertical_alignment]
|
|
|
+
|
|
|
+ # 设置水平对齐方式(应用于单元格中的所有段落)
|
|
|
+ for paragraph in cell.paragraphs:
|
|
|
+ paragraph.alignment = h_alignment_map[horizontal_alignment]
|
|
|
+
|
|
|
+ return {
|
|
|
+ "message": f"已设置表格 {table_index} 单元格的对齐方式,范围: ({start_row},{start_col}) 到 ({end_row},{end_col})"
|
|
|
+ }
|
|
|
+ except Exception as e:
|
|
|
+ return {"error": f"设置表格单元格对齐方式时出错: {str(e)}"}
|
|
|
+
|
|
|
+# 函数映射表,用于根据函数名调用相应的函数
|
|
|
+function_map = {
|
|
|
+ "create_new_document": create_new_document,
|
|
|
+ "set_global_font": set_global_font,
|
|
|
+ "save_document": save_document,
|
|
|
+ "open_document": open_document,
|
|
|
+ "add_paragraph_to_docx": add_paragraph_to_docx,
|
|
|
+ "add_heading": add_heading,
|
|
|
+ "add_table": add_table,
|
|
|
+ "add_picture": add_picture,
|
|
|
+ "add_page_break": add_page_break,
|
|
|
+ "set_paragraph_style": set_paragraph_style,
|
|
|
+ "set_font": set_font,
|
|
|
+ "set_paragraph_alignment": set_paragraph_alignment,
|
|
|
+ "set_paragraph_spacing": set_paragraph_spacing,
|
|
|
+ "add_section": add_section,
|
|
|
+ "set_section_orientation": set_section_orientation,
|
|
|
+ "set_section_page_size": set_section_page_size,
|
|
|
+ "set_section_margins": set_section_margins,
|
|
|
+ "add_header": add_header,
|
|
|
+ "add_footer": add_footer,
|
|
|
+ "add_page_number": add_page_number,
|
|
|
+ "merge_table_cells": merge_table_cells,
|
|
|
+ "set_table_cell_text": set_table_cell_text,
|
|
|
+ "set_table_style": set_table_style,
|
|
|
+ "set_table_cell_background": set_table_cell_background,
|
|
|
+ "set_table_cell_vertical_alignment": set_table_cell_vertical_alignment,
|
|
|
+ "set_table_width": set_table_width,
|
|
|
+ "set_table_column_width": set_table_column_width,
|
|
|
+ "add_hyperlink": add_hyperlink,
|
|
|
+ "add_bookmark": add_bookmark,
|
|
|
+ "add_table_of_contents": add_table_of_contents,
|
|
|
+ "set_table_borders": set_table_borders,
|
|
|
+ "set_cell_borders": set_cell_borders,
|
|
|
+ "add_table_standard_borders": add_table_standard_borders,
|
|
|
+ "copy_excel_to_word": copy_excel_to_word,
|
|
|
+ "copy_excel_range_to_word": copy_excel_range_to_word,
|
|
|
+ "add_table_row": add_table_row,
|
|
|
+ "add_table_column": add_table_column,
|
|
|
+ "delete_table_row": delete_table_row,
|
|
|
+ "delete_table_column": delete_table_column,
|
|
|
+ "get_table_dimensions": get_table_dimensions,
|
|
|
+ "get_table_cell_text": get_table_cell_text,
|
|
|
+ "get_table_row": get_table_row,
|
|
|
+ "get_table_column": get_table_column,
|
|
|
+ "get_table_range": get_table_range,
|
|
|
+ "get_all_tables_info": get_all_tables_info,
|
|
|
+ "set_table_data": set_table_data,
|
|
|
+ "set_table_row_data": set_table_row_data,
|
|
|
+ "set_table_column_data": set_table_column_data,
|
|
|
+ "clear_table_range": clear_table_range,
|
|
|
+ "get_table_detail_by_id": get_table_detail_by_id,
|
|
|
+ "set_table_font": set_table_font,
|
|
|
+ "set_table_cell_alignment": set_table_cell_alignment
|
|
|
+}
|
|
|
+
|
|
|
+def execute_docx_function(function_name, **kwargs):
|
|
|
+ """执行指定的函数"""
|
|
|
+ if function_name not in function_map:
|
|
|
+ return f"错误:未知的函数 {function_name}"
|
|
|
+
|
|
|
+ try:
|
|
|
+ return function_map[function_name](**kwargs)
|
|
|
+ except Exception as e:
|
|
|
+ return f"执行函数 {function_name} 时出错: {str(e)}"
|