优化docxv3.0
This commit is contained in:
@@ -30,22 +30,23 @@ def is_image_run(run: Run) -> bool:
|
|||||||
return '<w:drawing' in xml or '<w:pict' in xml
|
return '<w:drawing' in xml or '<w:pict' in xml
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== MODIFICATION START ====================
|
||||||
|
# 对 is_formatting_only_run 函数进行了修改
|
||||||
|
# 旧的实现无法识别仅包含颜色等 rPr 属性的空 Run,导致其与后续文本 Run 错误合并。
|
||||||
|
# 新的实现通过一个更简单的标准来判断:只要一个 Run 的文本内容在去除空白后为空,
|
||||||
|
# 它就被认为是纯格式化的,从而解决了交叉引用文本消失的问题。
|
||||||
|
# ==========================================================
|
||||||
def is_formatting_only_run(run: Run) -> bool:
|
def is_formatting_only_run(run: Run) -> bool:
|
||||||
"""
|
"""
|
||||||
检查一个 Run 是否主要用于格式化,例如一个空的粗体/斜体/下划线 Run。
|
检查一个 Run 是否仅用于格式化,不包含应被翻译的实质性文本。
|
||||||
|
这包括:
|
||||||
|
- 完全没有文本的 Run (即使它带有颜色等格式)。
|
||||||
|
- 只包含空格、制表符等空白字符的 Run。
|
||||||
"""
|
"""
|
||||||
text = run.text
|
return not run.text.strip()
|
||||||
if not text.strip():
|
|
||||||
# Handles empty runs with formatting
|
|
||||||
if run.underline or run.bold or run.italic or run.font.strike or run.font.subscript or run.font.superscript:
|
# ===================== MODIFICATION END =====================
|
||||||
return True
|
|
||||||
# Handles runs that are just whitespace but have formatting that might be visually significant
|
|
||||||
if text and run.underline:
|
|
||||||
return True
|
|
||||||
# A simple tab run is also considered formatting-only for our purpose
|
|
||||||
if text == '\t':
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------- 配置类 ----------------
|
# ---------------- 配置类 ----------------
|
||||||
@@ -85,8 +86,8 @@ class DocxTranslator(AiTranslator):
|
|||||||
RECURSIVE_CONTAINER_TAGS = {
|
RECURSIVE_CONTAINER_TAGS = {
|
||||||
qn('w:smartTag'), qn('w:sdtContent'), qn('w:hyperlink'),
|
qn('w:smartTag'), qn('w:sdtContent'), qn('w:hyperlink'),
|
||||||
}
|
}
|
||||||
# [v5.0] 定义不应翻译其结果的域指令, [v5.3] 增加了 'REF'
|
# [v5.0] 定义不应翻译其结果的域指令
|
||||||
SKIPPABLE_FIELD_INSTRUCTIONS = {'PAGEREF', 'SEQ', 'PAGE', 'NUMPAGES', 'DATE', 'TIME', 'SECTION', 'REF'}
|
SKIPPABLE_FIELD_INSTRUCTIONS = {'PAGEREF', 'SEQ', 'PAGE', 'NUMPAGES', 'DATE', 'TIME', 'SECTION'}
|
||||||
|
|
||||||
def __init__(self, config: DocxTranslatorConfig):
|
def __init__(self, config: DocxTranslatorConfig):
|
||||||
super().__init__(config=config)
|
super().__init__(config=config)
|
||||||
@@ -263,7 +264,9 @@ class DocxTranslator(AiTranslator):
|
|||||||
|
|
||||||
first_real_run_index = -1
|
first_real_run_index = -1
|
||||||
for i, run in enumerate(runs):
|
for i, run in enumerate(runs):
|
||||||
|
# 确保run仍然在文档树中
|
||||||
if run.element.getparent() is not None:
|
if run.element.getparent() is not None:
|
||||||
|
# 找到第一个可以写入文本的run
|
||||||
run.text = final_text
|
run.text = final_text
|
||||||
first_real_run_index = i
|
first_real_run_index = i
|
||||||
break
|
break
|
||||||
@@ -272,6 +275,7 @@ class DocxTranslator(AiTranslator):
|
|||||||
self.logger.warning(f"无法应用翻译 '{final_text}',因为找不到有效的run。")
|
self.logger.warning(f"无法应用翻译 '{final_text}',因为找不到有效的run。")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# 从第一个有效的run之后开始,删除所有多余的run
|
||||||
for i in range(first_real_run_index + 1, len(runs)):
|
for i in range(first_real_run_index + 1, len(runs)):
|
||||||
run = runs[i]
|
run = runs[i]
|
||||||
parent_element = run.element.getparent()
|
parent_element = run.element.getparent()
|
||||||
@@ -279,6 +283,7 @@ class DocxTranslator(AiTranslator):
|
|||||||
try:
|
try:
|
||||||
parent_element.remove(run.element)
|
parent_element.remove(run.element)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
# 如果元素已经被其他操作移除,这里会抛出ValueError,可以安全地忽略
|
||||||
self.logger.debug(f"尝试删除一个不存在的run元素。这通常是安全的。")
|
self.logger.debug(f"尝试删除一个不存在的run元素。这通常是安全的。")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user