PySide6读取XML填QTableWidget导出python-docxtpl阿拉伯字符显示异常
问题原因
该排版异常由双向文本(BiDi)渲染逻辑差异导致:
- Qt的QTableWidget会自动检测文本主导方向,为阿拉伯语这类从右到左(RTL)的文本自动添加隐式方向控制,句号、连字符这类中性字符的位置会按照RTL规则正确渲染
- python-docxtpl底层依赖的python-docx默认不会为文本附加方向控制信息,docx渲染时会误判混合文本中中性字符的所属方向,导致句号等标点出现在文本开头。此前使用python-bidi、arabic-reshaper不生效是因为没有配合docx的渲染规则添加对应的控制标记。
解决方案
给所有包含阿拉伯字符的文本包裹Unicode双向控制字符即可解决错位问题:
- 文本开头加右向嵌入标记
\u202B(RLE) - 文本结尾加弹出方向格式化标记
\u202C(PDF)
若模板中对应段落还有对齐偏移问题,可以直接在docx模板里把占位符所在段落设置为右对齐。
代码修改
仅需修改export_single方法中读取文本的逻辑即可,修改后代码如下:
# 辅助方法:判断文本是否包含阿拉伯字符 def has_arabic(text): for char in text: if '\u0600' <= char <= '\u06FF' or '\u0750' <= char <= '\u077F' or '\u08A0' <= char <= '\u08FF': return True return False # 修改后的导出方法 def export_single(self): teacher = self.teachers_dialog.list_widget.currentItem().text() tpl = DocxTemplate('./resources/template.docx') context = { 'teacher': '', 'days': [], 'tbl_contents': [], 'hours': 0, } # 处理教师名称的双向控制 context['teacher'] = f"\u202B{teacher}\u202C" if has_arabic(teacher) else teacher m_row_headers = [self.teachers_dialog.morning_table.verticalHeaderItem( i).text() for i in range(self.teachers_dialog.morning_table.rowCount())] m_col_headers = [self.teachers_dialog.morning_table.horizontalHeaderItem(i).text( ).split(' ')[0] for i in range(self.teachers_dialog.morning_table.columnCount())] for i in range(len(m_row_headers)): context['tbl_contents'].append({'hour': '', 'sets': []}) # 处理行头文本的双向控制 processed_hour = f"\u202B{m_row_headers[i]}\u202C" if has_arabic(m_row_headers[i]) else m_row_headers[i] context['tbl_contents'][i]['hour'] = processed_hour for j in range(len(m_col_headers)): item = self.teachers_dialog.morning_table.item(i, j) if item: context['hours'] += 1 # 核心修改:为单元格文本添加双向控制标记 cell_text = item.text() processed_text = f"\u202B{cell_text}\u202C" if has_arabic(cell_text) else cell_text context['tbl_contents'][i]['sets'].append(processed_text) else: context['tbl_contents'][i]['sets'].append('') tpl.render(context) tpl.save('result.docx')
补充说明
如果调整后仍有连字符显示异常,可结合arabic-reshaper和python-bidi做预处理,替换上述文本处理逻辑即可:
from arabic_reshaper import reshape from bidi.algorithm import get_display processed_text = get_display(reshape(cell_text))
使用该预处理方式时需提前将docx模板中对应段落设置为RTL方向,避免出现二次错位。
内容的提问来源于stack exchange,提问作者Crizalid
相关产品推荐
相关产品推荐

