Fix: PDF依赖缺失提示框右侧边框未对齐
使用 unicodedata 计算字符串终端显示宽度,替代原来按字符数填充的方式, 正确处理中文、全角标点和 emoji 等宽字符,使提示框右侧 ║ 对齐。 Co-Authored-By: Claude <noreply@anthropic.com>
Showing
1 changed file
with
41 additions
and
1 deletions
| @@ -5,6 +5,7 @@ | @@ -5,6 +5,7 @@ | ||
| 5 | import os | 5 | import os |
| 6 | import sys | 6 | import sys |
| 7 | import platform | 7 | import platform |
| 8 | +import unicodedata | ||
| 8 | from pathlib import Path | 9 | from pathlib import Path |
| 9 | from loguru import logger | 10 | from loguru import logger |
| 10 | from ctypes import util as ctypes_util | 11 | from ctypes import util as ctypes_util |
| @@ -12,9 +13,48 @@ from ctypes import util as ctypes_util | @@ -12,9 +13,48 @@ from ctypes import util as ctypes_util | ||
| 12 | BOX_CONTENT_WIDTH = 62 | 13 | BOX_CONTENT_WIDTH = 62 |
| 13 | 14 | ||
| 14 | 15 | ||
| 16 | +def _display_width(text: str) -> int: | ||
| 17 | + """计算字符串在终端中的显示宽度(中文/emoji 等宽字符占 2 列)。""" | ||
| 18 | + width = 0 | ||
| 19 | + chars = list(text) | ||
| 20 | + n = len(chars) | ||
| 21 | + i = 0 | ||
| 22 | + while i < n: | ||
| 23 | + ch = chars[i] | ||
| 24 | + cp = ord(ch) | ||
| 25 | + | ||
| 26 | + # 零宽:组合标记 / 格式字符(含 Zero Width Joiner 等) | ||
| 27 | + if unicodedata.category(ch) in ('Mn', 'Me', 'Cf'): | ||
| 28 | + i += 1 | ||
| 29 | + continue | ||
| 30 | + | ||
| 31 | + # East Asian Fullwidth / Wide(中文汉字、全角标点等) | ||
| 32 | + if unicodedata.east_asian_width(ch) in ('F', 'W'): | ||
| 33 | + width += 2 | ||
| 34 | + i += 1 | ||
| 35 | + continue | ||
| 36 | + | ||
| 37 | + # 补充平面 emoji(U+1F000 及以上,如 📄🍎📖🐧🪟) | ||
| 38 | + if cp >= 0x1F000: | ||
| 39 | + width += 2 | ||
| 40 | + i += 1 | ||
| 41 | + continue | ||
| 42 | + | ||
| 43 | + # 文本符号 + VS16 (U+FE0F) → emoji 呈现,宽 2 列(如 ⚠️) | ||
| 44 | + if i + 1 < n and ord(chars[i + 1]) == 0xFE0F: | ||
| 45 | + width += 2 | ||
| 46 | + i += 2 # 跳过当前字符和 VS16 | ||
| 47 | + continue | ||
| 48 | + | ||
| 49 | + width += 1 | ||
| 50 | + i += 1 | ||
| 51 | + return width | ||
| 52 | + | ||
| 53 | + | ||
| 15 | def _box_line(text: str = "") -> str: | 54 | def _box_line(text: str = "") -> str: |
| 16 | """Render a single line inside the 66-char help box.""" | 55 | """Render a single line inside the 66-char help box.""" |
| 17 | - return f"║ {text:<{BOX_CONTENT_WIDTH}}║\n" | 56 | + padding = max(0, BOX_CONTENT_WIDTH - _display_width(text)) |
| 57 | + return f"║ {text}{' ' * padding}║\n" | ||
| 18 | 58 | ||
| 19 | 59 | ||
| 20 | def _get_platform_specific_instructions(): | 60 | def _get_platform_specific_instructions(): |
-
Please register or login to post a comment