Don't merge box-drawing characters for precise positioning
Box-drawing and block element characters (U+2500-U+259F, U+25A0-U+25FF) are now rendered as individual tspans with their own x positions to prevent visual misalignment caused by font rendering variations.
This commit is contained in:
@@ -255,6 +255,17 @@ class _Span(TypedDict):
|
|||||||
has_bg: bool
|
has_bg: bool
|
||||||
|
|
||||||
|
|
||||||
|
def _is_box_drawing(char: str) -> bool:
|
||||||
|
"""Check if character is a box-drawing or block element that needs precise positioning."""
|
||||||
|
if not char:
|
||||||
|
return False
|
||||||
|
code = ord(char[0])
|
||||||
|
# Box Drawing: U+2500-U+257F
|
||||||
|
# Block Elements: U+2580-U+259F
|
||||||
|
# Geometric Shapes (some): U+25A0-U+25FF
|
||||||
|
return 0x2500 <= code <= 0x259F or 0x25A0 <= code <= 0x25FF
|
||||||
|
|
||||||
|
|
||||||
def _build_row_spans(
|
def _build_row_spans(
|
||||||
row_data: list[CharData],
|
row_data: list[CharData],
|
||||||
default_fg: str,
|
default_fg: str,
|
||||||
@@ -286,9 +297,15 @@ def _build_row_spans(
|
|||||||
|
|
||||||
has_bg = bg != default_bg
|
has_bg = bg != default_bg
|
||||||
|
|
||||||
|
# Don't merge box-drawing characters - they need precise x positioning
|
||||||
|
is_box = _is_box_drawing(char_data)
|
||||||
|
prev_is_box = current_span is not None and _is_box_drawing(current_span["text"][-1:])
|
||||||
|
|
||||||
# Check if we can extend current span
|
# Check if we can extend current span
|
||||||
if (
|
if (
|
||||||
current_span is not None
|
current_span is not None
|
||||||
|
and not is_box
|
||||||
|
and not prev_is_box
|
||||||
and current_span["fg"] == fg
|
and current_span["fg"] == fg
|
||||||
and current_span["bg"] == bg
|
and current_span["bg"] == bg
|
||||||
and current_span["bold"] == char["bold"]
|
and current_span["bold"] == char["bold"]
|
||||||
|
|||||||
@@ -716,14 +716,17 @@ class TestEdgeCases:
|
|||||||
assert "A中B🎉C" in svg
|
assert "A中B🎉C" in svg
|
||||||
|
|
||||||
def test_special_unicode_blocks(self) -> None:
|
def test_special_unicode_blocks(self) -> None:
|
||||||
"""Unicode box drawing characters render."""
|
"""Unicode box drawing characters render (separately for precise positioning)."""
|
||||||
buffer = [[
|
buffer = [[
|
||||||
self._char("┌"),
|
self._char("┌"),
|
||||||
self._char("─"),
|
self._char("─"),
|
||||||
self._char("┐"),
|
self._char("┐"),
|
||||||
]]
|
]]
|
||||||
svg = render_terminal_svg(buffer, width=3, height=1)
|
svg = render_terminal_svg(buffer, width=3, height=1)
|
||||||
assert "┌─┐" in svg
|
# Box drawing chars are rendered separately for precise x positioning
|
||||||
|
assert "┌" in svg
|
||||||
|
assert "─" in svg
|
||||||
|
assert "┐" in svg
|
||||||
|
|
||||||
def test_ansi_bright_colors(self) -> None:
|
def test_ansi_bright_colors(self) -> None:
|
||||||
"""All bright ANSI colors render."""
|
"""All bright ANSI colors render."""
|
||||||
|
|||||||
Reference in New Issue
Block a user