import json def Replace(bookmark_name_value_map): """ 替换所有书签 Args: bookmark_name_value_map: 书签名称和新值的映射 """ doc = XSCRIPTCONTEXT.getDocument() bookmarks = doc.getBookmarks() for bookmark_name, new_value in bookmark_name_value_map.items(): if bookmarks.hasByName(bookmark_name): bookmark = bookmarks.getByName(bookmark_name) anchor = bookmark.getAnchor() anchor.setString(new_value) def ReplaceWithJSON(json_str): """ 替换所有书签 Args: json_str: 书签名称和新值的映射的JSON字符串 """ bookmark_name_value_map = json.loads(json_str) Replace(bookmark_name_value_map) def DebugCallReplace(): """ 调试调用 """ Replace({"合同编号_A": "11111", "合同名称_A": "11111"}) def DebugCallReplaceList(): """ 调试调用,替换list """ Replace({"list": "1\r2\r3\r4"}) def QueryAll(): """ 查询所有书签,表格中的书签获取对应表格index 返回结果格式: { "text": [], "table": [ { "tableIndex": 表格索引, "bookmark": [书签名称列表] } ] } """ doc = XSCRIPTCONTEXT.getDocument() bookmarks = doc.getBookmarks() bookmark_names = bookmarks.getElementNames() filtered_bookmarks = [ bk_name for bk_name in bookmark_names if not bk_name.startswith("_") and ":" not in bk_name ] result = {"text": [], "table": []} for bk_name in filtered_bookmarks: result["text"].append(bk_name) return result def QueryAllWithJSON(): return returnWithJSON(QueryAll()) def QueryBookmarkPositionInTable(tables, bookmarks): """ 查询书签在表格中的位置 {'bk1': {'tableIndex': 0, 'row': 0, 'col': 0}, 'bk2': {'tableIndex': 0, 'row': 0, 'col': 2}} """ bookmark_names = bookmarks.getElementNames() bookmark_position = {} for table_index in range(tables.getCount()): table = tables.getByIndex(table_index) col_count = table.getColumns().getCount() row_count = table.getRows().getCount() for row in range(row_count): for col in range(col_count): cell = table.getCellByPosition(col, row) cell_text_obj = cell.getText() for bk_name in bookmark_names: bookmark_obj = bookmarks.getByName(bk_name) bookmark_text_obj = bookmark_obj.getAnchor().getText() if cell_text_obj == bookmark_text_obj: bookmark_position[bk_name] = { "tableIndex": table_index, "col": col, "row": row, } return bookmark_position def InsertRowWithJSON(json_str): """ 插入行 """ data = json.loads(json_str) InsertRow(data["location_bookmark_name"], data["data"], data["start_row_index"]) def InsertRow(location_bookmark_name, data, start_row_index=-1): """ 表格插入行 Args: location_bookmark_name: 用于定位表格的书签 data: 二维数组,用于填充表格数据 start_row_index: 起始行位置,默认为-1,即在表格末尾插入 """ doc = XSCRIPTCONTEXT.getDocument() tables = doc.getTextTables() bookmarks = doc.getBookmarks() bookmark_in_table_position = QueryBookmarkPositionInTable(tables, bookmarks) if location_bookmark_name not in bookmark_in_table_position: raise ValueError(f"未找到书签 {location_bookmark_name} 对应的表格") handle_table_index = bookmark_in_table_position[location_bookmark_name][ "tableIndex" ] try: handle_table = tables.getByIndex(handle_table_index) except IndexError: raise IndexError(f"表格索引 {handle_table_index} 超出范围") col_count = handle_table.getColumns().getCount() row_count = handle_table.getRows().getCount() if not data or len(data) == 0: return if any(len(row) != col_count for row in data): raise ValueError(f"数据列数不匹配,表格有 {col_count} 列") try: row_count = handle_table.getRows().getCount() rows_to_insert = len(data) if rows_to_insert > 0: # 确定插入位置 if start_row_index != -1 and 0 <= start_row_index < row_count: insert_pos = start_row_index + 1 # 在指定行下方插入 else: insert_pos = row_count # 插入到表格末尾 # 批量插入所有新行 handle_table.getRows().insertByIndex(insert_pos, rows_to_insert) # 填充数据到新插入的行 for data_row_idx, row_data in enumerate(data): target_row = insert_pos + data_row_idx # 计算目标行索引 for col_idx, cell_value in enumerate(row_data): cell = handle_table.getCellByPosition(col_idx, target_row) cell.setString(str(cell_value)) except Exception as e: raise RuntimeError(f"err: {str(e)}") def BatchInsertRowWithContentControl(data_array): """ 批量插入行 """ doc = XSCRIPTCONTEXT.getDocument() tables = doc.getTextTables() bookmarks = doc.getBookmarks() bookmark_in_table_position = QueryBookmarkPositionInTable(tables, bookmarks) for arr_obj in data_array: location_bookmark_name = arr_obj.get("location_bookmark_name") data = arr_obj.get("data") start_row_index = arr_obj.get("start_row_index", -1) # 默认值为 -1 if location_bookmark_name not in bookmark_in_table_position: raise ValueError(f"未找到书签 {location_bookmark_name} 对应的表格") handle_table_index = bookmark_in_table_position[location_bookmark_name][ "tableIndex" ] try: handle_table = tables.getByIndex(handle_table_index) except IndexError: raise IndexError(f"表格索引 {handle_table_index} 超出范围") col_count = handle_table.getColumns().getCount() row_count = handle_table.getRows().getCount() if not data or len(data) == 0: return if any(len(row) != col_count for row in data): raise ValueError(f"数据列数不匹配,表格有 {col_count} 列") try: row_count = handle_table.getRows().getCount() rows_to_insert = len(data) if rows_to_insert > 0: # 确定插入位置 if start_row_index != -1 and 0 <= start_row_index < row_count: insert_pos = start_row_index + 1 # 在指定行下方插入 else: insert_pos = row_count # 插入到表格末尾 # 批量插入所有新行 handle_table.getRows().insertByIndex(insert_pos, rows_to_insert) # 填充数据到新插入的行 for data_row_idx, row_data in enumerate(data): target_row = insert_pos + data_row_idx # 计算目标行索引 for col_idx, cell_value in enumerate(row_data): cell = handle_table.getCellByPosition(col_idx, target_row) cell_text = cell.getText() # === 清空单元格 === cell_text.setString("") # === 创建内容控件 === content_control = doc.createInstance("com.sun.star.text.ContentControl") # === 插入控件到单元格 === cursor = cell_text.createTextCursor() cell_text.insertTextContent(cursor, content_control, False) # === 设置控件内容 === cc_text = content_control.getText() cc_cursor = cc_text.createTextCursor() cc_cursor.setString(str(cell_value)) except Exception as e: raise RuntimeError(f"插入行时发生错误: {str(e)}") def BatchInsertRow(data_array): """ 批量插入行 """ doc = XSCRIPTCONTEXT.getDocument() tables = doc.getTextTables() bookmarks = doc.getBookmarks() bookmark_in_table_position = QueryBookmarkPositionInTable(tables, bookmarks) for arr_obj in data_array: location_bookmark_name = arr_obj.get("location_bookmark_name") data = arr_obj.get("data") start_row_index = arr_obj.get("start_row_index", -1) # 默认值为 -1 if location_bookmark_name not in bookmark_in_table_position: raise ValueError(f"未找到书签 {location_bookmark_name} 对应的表格") handle_table_index = bookmark_in_table_position[location_bookmark_name][ "tableIndex" ] try: handle_table = tables.getByIndex(handle_table_index) except IndexError: raise IndexError(f"表格索引 {handle_table_index} 超出范围") col_count = handle_table.getColumns().getCount() row_count = handle_table.getRows().getCount() if not data or len(data) == 0: return if any(len(row) != col_count for row in data): raise ValueError(f"数据列数不匹配,表格有 {col_count} 列") try: row_count = handle_table.getRows().getCount() rows_to_insert = len(data) if rows_to_insert > 0: # 确定插入位置 if start_row_index != -1 and 0 <= start_row_index < row_count: insert_pos = start_row_index + 1 # 在指定行下方插入 else: insert_pos = row_count # 插入到表格末尾 # 批量插入所有新行 handle_table.getRows().insertByIndex(insert_pos, rows_to_insert) # 填充数据到新插入的行 for data_row_idx, row_data in enumerate(data): target_row = insert_pos + data_row_idx # 计算目标行索引 for col_idx, cell_value in enumerate(row_data): cell = handle_table.getCellByPosition(col_idx, target_row) cell.setString(str(cell_value)) except Exception as e: raise RuntimeError(f"插入行时发生错误: {str(e)}") def DeleteRowWithJSON(json_str): """ 删除行 """ data = json.loads(json_str) DeleteRow( data["location_bookmark_name"], data["start_row_index"], data["delete_row_count"], ) def DeleteRow(location_bookmark_name, start_row_index, delete_row_count=-1): """ 删除表格行 Args: location_bookmark_name: 用于定位表格的书签 start_row_index: 起始行位置(删除行包含本行) delete_row_count: 删除的行数 """ doc = XSCRIPTCONTEXT.getDocument() tables = doc.getTextTables() bookmarks = doc.getBookmarks() bookmark_in_table_position = QueryBookmarkPositionInTable(tables, bookmarks) if location_bookmark_name not in bookmark_in_table_position: raise ValueError(f"未找到书签 {location_bookmark_name} 对应的表格") handle_table_index = bookmark_in_table_position[location_bookmark_name][ "tableIndex" ] handle_table = tables.getByIndex(handle_table_index) row_count = handle_table.getRows().getCount() if start_row_index < 0 or start_row_index >= row_count: raise ValueError(f"起始行索引 {start_row_index} 超出范围") # 如果未指定删除行数或超出范围,则删除剩余所有行 if start_row_index + delete_row_count > row_count or delete_row_count == -1: delete_row_count = row_count - start_row_index try: handle_table.getRows().removeByIndex(start_row_index, delete_row_count) except Exception as e: raise RuntimeError(f"err: {str(e)}") def LocateBookmark(bookmark_name): """ 定位书签并选中内容 Args: bookmark_name: 要定位的书签名称 """ doc = XSCRIPTCONTEXT.getDocument() if False == doc.getBookmarks().hasByName(bookmark_name): return bookmark = doc.getBookmarks().getByName(bookmark_name) try: # 获取书签的文本范围锚点 anchor = bookmark.getAnchor() # 获取文档控制器 controller = doc.getCurrentController() # 将视图光标移动到书签位置并选中 view_cursor = controller.getViewCursor() view_cursor.gotoRange(anchor, False) # False表示不扩展选区 # 如果需要高亮显示(可选) controller.select(anchor) # 确保窗口可见(针对隐藏窗口的情况) try: window = controller.getFrame().getContainerWindow() window.setVisible(True) window.toFront() except Exception: pass except Exception as e: raise RuntimeError(f"定位书签失败: {str(e)}") from e def InsertBookmark(bookmark_name): """ 选中内容插入书签,如果没有选中任何内容,插入点书签 Args: bookmark_name: 要插入的书签名称 """ doc = XSCRIPTCONTEXT.getDocument() controller = doc.getCurrentController() # 获取当前选区 selection = controller.getSelection() # 检查是否已存在同名书签,如果存在则先删除 bookmarks = doc.getBookmarks() if bookmarks.hasByName(bookmark_name): bookmarks.getByName(bookmark_name).dispose() try: # 创建书签 if selection.getCount() > 0: # 尝试获取选区的第一个元素 range_to_bookmark = selection.getByIndex(0) # 创建书签并附加到选区 bookmark = doc.createInstance("com.sun.star.text.Bookmark") bookmark.attach(range_to_bookmark) bookmark.setName(bookmark_name) else: # 没有选中内容,创建点书签 cursor = controller.getViewCursor() text_cursor = cursor.getText().createTextCursorByRange(cursor) # 创建书签并附加到光标位置 bookmark = doc.createInstance("com.sun.star.text.Bookmark") bookmark.attach(text_cursor) bookmark.setName(bookmark_name) except Exception as e: raise RuntimeError(f"插入书签失败: {str(e)}") from e def UpdateBookmark(bookmark_name, new_value): """ 修改书签名称(通过删除旧书签 + 插入新书签实现) Args: bookmark_name: 原书签名称 new_value: 新书签名称 """ doc = XSCRIPTCONTEXT.getDocument() bookmarks = doc.getBookmarks() if not bookmarks.hasByName(bookmark_name): raise ValueError(f"书签 '{bookmark_name}' 不存在") if bookmarks.hasByName(new_value): raise ValueError(f"书签 '{new_value}' 已存在") # 获取旧书签的锚点位置 old_bookmark = bookmarks.getByName(bookmark_name) old_bookmark.setName(new_value) def DeleteBookmark(bookmark_name): """ 删除书签但保留内容(通过断开书签与锚点的关联) """ doc = XSCRIPTCONTEXT.getDocument() bookmarks = doc.getBookmarks() if not bookmarks.hasByName(bookmark_name): return bookmark = bookmarks.getByName(bookmark_name) if hasattr(bookmark, "Anchor"): try: anchor = bookmark.Anchor anchor.getText().removeTextContent(bookmark) except Exception as e: pass def CreateTable(location_bookmark_name, data): """ 创建表格 Args: location_bookmark_name: 用于定位表格的书签 data: 二维数组,用于填充表格数据 """ def ReplaceTextAndInsertTableRow(json_str): """ 替换文本和表格 """ data = json.loads(json_str) Replace(data["text"]) BatchInsertRow(data["table"]) def ReplaceTextAndInsertTableRowWithContentControl(json_str): data = json.loads(json_str) ReplaceBookmarksWithControls(data["text"]) BatchInsertRowWithContentControl(data["table"]) def returnWithJSON(data): return json.dumps(data, ensure_ascii=False) def SaveDocument(): # 获取当前文档对象 model = XSCRIPTCONTEXT.getDocument() try: # 检查文档是否支持保存(XStorable 接口) from com.sun.star.frame import XStorable xstorable = model.uno_getAdapter(XStorable) # 调用保存方法(覆盖原文件) xstorable.store() return True except Exception as e: print("err:", e) return False def DebugCallReplaceBookmarksWithControls(): ReplaceBookmarksWithControls({"合同编号_A": "11111", "合同名称_A": "22222"}) def ReplaceBookmarksWithControls(bookmark_name_value_map={}): doc = XSCRIPTCONTEXT.getDocument() bookmarks = doc.getBookmarks() for i in reversed(range(bookmarks.getCount())): bookmark = bookmarks.getByIndex(i) name = bookmark.getName() anchor = bookmark.getAnchor() if not anchor: continue original_text = anchor.getString() replace_value = bookmark_name_value_map.get(name, original_text) # === 第1步:创建内容控件 === content_control = doc.createInstance("com.sun.star.text.ContentControl") # 获取书签所在文本对象 text = anchor.getText() anchor.setString("") # # 创建游标并清空原内容 # 插入内容控件 text.insertTextContent(anchor, content_control, True) # 设置控件内容 cc_cursor = content_control.getText().createTextCursor() cc_cursor.setString(replace_value) # === 第1步:清理旧书签 === bookmark.dispose() # === 第2步:创建新书签 === # 获取控件实际范围 cc_anchor = content_control.getText().getAnchor() new_bookmark = doc.createInstance("com.sun.star.text.Bookmark") new_bookmark.setName(name) new_bookmark.attach(cc_anchor) def NextEditableZone(current_index): """ 根据索引定位可编辑的内容控件(支持循环) Args: current_index (int/str): 从 0 开始的索引,支持字符串或整数类型 """ # 强制转换为整数 ----------------------------- try: current_index = int(current_index) except (ValueError, TypeError): print(f"错误:无法将 {current_index} 转换为整数") return doc = XSCRIPTCONTEXT.getDocument() controller = doc.getCurrentController() view_cursor = controller.getViewCursor() # 收集所有内容控件的锚点 text_content = doc.getText().createEnumeration() editable_anchors = [] while text_content.hasMoreElements(): element = text_content.nextElement() # 关键修改点:检查内容控件服务 if element.supportsService("com.sun.star.text.ContentControl"): try: anchor = element.getAnchor() editable_anchors.append(anchor) except Exception: continue # 无控件时直接返回 if not editable_anchors: print("文档中未找到内容控件") return # 计算有效索引(循环逻辑) total = len(editable_anchors) effective_index = current_index % total # 自动处理越界 # 定位到目标控件 target_anchor = editable_anchors[effective_index] view_cursor.gotoRange(target_anchor.getStart(), False) # 跳转到控件起始位置 controller.select(target_anchor) # 选中整个控件范围 g_exportedScripts = ( Replace, ReplaceWithJSON, QueryAll, QueryAllWithJSON, InsertRow, InsertRowWithJSON, DeleteRow, DeleteRowWithJSON, ReplaceTextAndInsertTableRow, DebugCallReplace, DebugCallReplaceList, ReplaceBookmarksWithControls, ReplaceTextAndInsertTableRowWithContentControl, DebugCallReplaceBookmarksWithControls, SaveDocument, NextEditableZone, )