feat: replace bookmark with image

This commit is contained in:
zzs 2025-03-28 11:54:53 +08:00
parent 88db5957d2
commit de95153976

View File

@ -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
)