From de9515397650c8be9f979964ff391f2572f6de9b Mon Sep 17 00:00:00 2001 From: zzs Date: Fri, 28 Mar 2025 11:54:53 +0800 Subject: [PATCH] feat: replace bookmark with image --- BookmarkOP.py | 169 +++++++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 76 deletions(-) diff --git a/BookmarkOP.py b/BookmarkOP.py index f65fd8d..459cd8e 100644 --- a/BookmarkOP.py +++ b/BookmarkOP.py @@ -1,17 +1,12 @@ import json -import uno import base64 import io -from com.sun.star.text import XTextContent -from com.sun.star.text import XTextRange -from com.sun.star.text import XTextDocument -from com.sun.star.text import XTextCursor -from com.sun.star.text import XText -from com.sun.star.text import XBookmarksSupplier -from com.sun.star.text import XTextGraphicObjectsSupplier -from com.sun.star.graphic import XGraphicProvider -from com.sun.star.graphic import XGraphic from com.sun.star.beans import PropertyValue +import tempfile +import os +from com.sun.star.beans import PropertyValue +from uno import ByteSequence, systemPathToFileUrl +from com.sun.star.text.TextContentAnchorType import AS_CHARACTER, AT_PARAGRAPH def Replace(bookmark_name_value_map): """ @@ -317,8 +312,8 @@ def BatchInsertRow(data_array): if not data or len(data) == 0: return - if any(len(row) != col_count for row in data): - raise ValueError(f"数据列数不匹配,表格有 {col_count} 列") + # if any(len(row) != col_count for row in data): + # raise ValueError(f"数据列数不匹配,表格有 {col_count} 列") try: row_count = handle_table.getRows().getCount() @@ -528,14 +523,15 @@ def ReplaceTextAndInsertTableRow(json_str): data = json.loads(json_str) Replace(data["text"]) BatchInsertRow(data["table"]) + ReplaceBookmarkWithImage(data["image"]) def ReplaceTextAndInsertTableRowWithContentControl(json_str): data = json.loads(json_str) - SetDocumentToFormMode() Replace(data["text"]) + ReplaceBookmarkWithImage(data["image"]) BatchInsertRowWithContentControl(data["table"]) - ReplaceBookmarksWithControls({}) + ReplaceBookmarksWithControls({}, ExtractBookmarkNames(data)) def returnWithJSON(data): @@ -559,32 +555,7 @@ def SaveDocument(): print("err:", e) return False -def SetDocumentToFormMode(): - """ - 将文档设置为限制编辑模式,启用强制保护,仅允许填写窗体 - """ - doc = XSCRIPTCONTEXT.getDocument() - - # 获取文档的控制器 - controller = doc.getCurrentController() - - # 获取文档的保护设置 - protectable = doc.Protectable - - # 启用强制保护 - if not protectable.isProtected(): - protectable.protect("") # 空字符串表示无密码保护 - - # 设置编辑限制为仅允许填写窗体 - text_doc = doc.TextDocument - text_doc.setPropertyValue("EnableFormEdit", True) - text_doc.setPropertyValue("EnableFormFieldsOnly", True) - - # 刷新视图 - controller.refresh() - - -def ReplaceBookmarksWithControls(bookmark_name_value_map={}): +def ReplaceBookmarksWithControls(bookmark_name_value_map={}, exclude_bookmark_names=[]): # 获取文档对象 doc = XSCRIPTCONTEXT.getDocument() bookmarks = doc.getBookmarks() @@ -595,6 +566,9 @@ def ReplaceBookmarksWithControls(bookmark_name_value_map={}): # 遍历所有需要处理的书签 for name in bookmark_names: try: + # 检查是否需要排除 + if name in exclude_bookmark_names: + continue # 检查书签是否存在 if not bookmarks.hasByName(name): continue @@ -695,19 +669,20 @@ def NextEditableZone(current_index): raise RuntimeError(f"err: {str(e)}") from e def ReplaceBookmarkWithImage(data_array): - """ 替换书签为图片 """ - # 获取当前文档 + """ + 替换书签为图片,同时保留书签 + 参数: + data_array: 包含书签和图片信息的字典数组 + """ + # 获取当前文档和相关服务 doc = XSCRIPTCONTEXT.getDocument() - - # 获取书签供应商 bookmarks = doc.getBookmarks() - - # 获取服务管理器 smgr = XSCRIPTCONTEXT.getComponentContext().getServiceManager() - - # 创建图形提供者服务 graphic_provider = smgr.createInstanceWithContext("com.sun.star.graphic.GraphicProvider", XSCRIPTCONTEXT.getComponentContext()) - + + if not graphic_provider: + raise RuntimeError("无法初始化 GraphicProvider 服务") + for data in data_array: bookmark_name = data["bookmarkName"] image_data = data["imageData"] @@ -715,43 +690,86 @@ def ReplaceBookmarkWithImage(data_array): height = data["height"] file_type = data["fileType"] - if bookmark_name in bookmarks: - # 获取书签的锚点 + # 检查书签是否存在 + if bookmark_name not in bookmarks: + print(f"错误: 书签 '{bookmark_name}' 不存在") + continue + + try: + # 解码 Base64 数据 + image_bytes = base64.b64decode(image_data) + if not image_bytes: + raise ValueError("Base64 数据为空或无效") + + # 获取书签的文本范围和光标 bookmark = bookmarks[bookmark_name] text_range = bookmark.getAnchor() text_cursor = text_range.getText().createTextCursorByRange(text_range) - # 解码Base64图片数据 - image_bytes = base64.b64decode(image_data) - image_stream = io.BytesIO(image_bytes) + # 记录书签位置并删除书签内容 + text = text_range.getText() + text_cursor.setString("") # 删除书签范围内的文本 - # 创建PropertyValue对象 - url_prop = PropertyValue() - url_prop.Name = "URL" - url_prop.Value = "private:stream" + # 使用临时文件方式插入图片 + temp_file_path = None + try: + with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file_type}") as temp_file: + temp_file.write(image_bytes) + temp_file_path = temp_file.name - input_stream_prop = PropertyValue() - input_stream_prop.Name = "InputStream" - input_stream_prop.Value = image_stream + file_url = systemPathToFileUrl(temp_file_path) + url_prop = PropertyValue() + url_prop.Name = "URL" + url_prop.Value = file_url - mime_type_prop = PropertyValue() - mime_type_prop.Name = "MimeType" - mime_type_prop.Value = f"image/{file_type}" + graphic_properties = (url_prop,) + graphic = graphic_provider.queryGraphic(graphic_properties) - # 将PropertyValue对象放入序列中 - graphic_properties = (url_prop, input_stream_prop, mime_type_prop) + if not graphic: + raise RuntimeError(f"无法为书签 '{bookmark_name}' 生成图形对象") - # 查询图形 - graphic = graphic_provider.queryGraphic(graphic_properties) - # 创建图形对象 - graphic_object = doc.createInstance("com.sun.star.text.TextGraphicObject") - graphic_object.Graphic = graphic # 设置图形属性 - graphic_object.Width = width # 设置宽度 - graphic_object.Height = height # 设置高度 + # 创建图形对象 + graphic_object = doc.createInstance("com.sun.star.text.TextGraphicObject") + graphic_object.Graphic = graphic + graphic_object.Width = width + graphic_object.Height = height - # 替换书签为图片 - text_cursor.getText().insertTextContent(text_cursor, graphic_object, False) + # 设置锚定方式 + graphic_object.AnchorType = AS_CHARACTER # 锚定为字符 + + # 插入图片 + text.insertTextContent(text_cursor, graphic_object, False) + + if bookmark_name not in doc.getBookmarks(): + # 重新创建书签以覆盖图片位置 + bookmark_range = text.createTextCursorByRange(text_range) + new_bookmark = doc.createInstance("com.sun.star.text.Bookmark") + new_bookmark.Name = bookmark_name + text.insertTextContent(bookmark_range, new_bookmark, False) + + finally: + # 清理临时文件 + if temp_file_path and os.path.exists(temp_file_path): + os.unlink(temp_file_path) + + except Exception as e: + continue + +def ExtractBookmarkNames(data): + """ + 从 data["image"] 数组中提取所有 bookmarkName,组成一个字符串数组 + 参数: + data: 包含 "image" 键的字典,其值为图片对象数组 + 返回: + list: 包含所有 bookmarkName 的字符串数组 + """ + if "image" not in data or not isinstance(data["image"], list): + return [] + + # 使用列表推导式提取 bookmarkName + bookmark_names = [item["bookmarkName"] for item in data["image"] if "bookmarkName" in item] + return bookmark_names def GetBookmarksInLists(): """ @@ -793,5 +811,4 @@ g_exportedScripts = ( SaveDocument, NextEditableZone, GetBookmarksInLists, - SetDocumentToFormMode )