Skip to content

Fix graph diagram CJK/Unicode character rendering - #49

Open
ColtWindy wants to merge 1 commit into
AlexanderGrooff:masterfrom
ColtWindy:fix/graph-cjk-unicode-support
Open

Fix graph diagram CJK/Unicode character rendering#49
ColtWindy wants to merge 1 commit into
AlexanderGrooff:masterfrom
ColtWindy:fix/graph-cjk-unicode-support

Conversation

@ColtWindy

Copy link
Copy Markdown

Summary

  • Fix double-encoding bug causing garbled CJK/Korean text output (e.g., "íì¤í¸" instead of "테스트")
  • Use runewidth library for proper display width calculation
  • Handle wide characters that occupy 2 terminal columns

Problem

When rendering graph diagrams with CJK characters (Korean, Japanese, Chinese), the text was corrupted:

# Bug output
┌────────────────┐
│ A["íì¤í¸"] │
└────────────────┘

# Expected output
┌──────────┐
│ A["테스트"] │
└──────────┘

Root Cause

  1. len(string) returns byte count, not character count
  2. string[x] returns a byte, not a rune
  3. string(byte) interprets the byte as a Unicode code point, causing double-encoding
// Bug: UTF-8 bytes → Latin-1 interpretation → UTF-8 re-encoding
text := "테스트"  // UTF-8: ED 85 8C EC 8A A4 ED 8A B8
for x := 0; x < len(text); x++ {
    string(text[x])  // 0xED → U+00ED ('í') → C3 AD
}

Solution

  • Use runewidth.StringWidth() for display width calculation
  • Use runewidth.RuneWidth() to properly position wide characters
  • Fill subsequent positions with empty string for wide CJK characters
// Fixed: proper rune iteration with display width
pos := 0
for _, char := range text {
    drawing[pos] = string(char)
    charWidth := runewidth.RuneWidth(char)
    for i := 1; i < charWidth; i++ {
        drawing[pos+i] = ""  // placeholder for wide char
    }
    pos += charWidth
}

Changes

File Change
cmd/draw.go Use runewidth for drawText, drawBox, drawSubgraphLabel
cmd/mapping_node.go Use runewidth.StringWidth for box width calculation
cmd/testdata/.../korean_nodes.txt Add Korean test case

Test plan

  • All existing tests pass
  • New Korean test case passes
  • Manual testing with Korean, mixed Korean+English text
  • Subgraph labels with CJK text render correctly

🤖 Generated with Claude Code

@AlexanderGrooff

Copy link
Copy Markdown
Owner

Hi there, thanks for your contributions! I'll have a proper look some time today/next week, this seems like a nice addition 👍

@AlexanderGrooff AlexanderGrooff left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there, thanks for contributing. I think this is a great addition, but it doesn't seem quite right just yet.
Your testcase already points this out, the padding between the right border and the text is missing a whitespace so the right border doesn't align nicely.

Here's what it looks like in the browser:

Image

Please have a look at the whitespacing after the label, then we can get this merged 👍

@ColtWindy

Copy link
Copy Markdown
Author

@AlexanderGrooff Hi, thanks so much for taking the time to review this — I really appreciate it!

So I dug into this alignment issue, and turns out it's a bit of a tricky one. The problem is how web browsers handle CJK (Korean/Japanese/Chinese) characters vs how terminals do.

In terminals, CJK characters take up 2 columns (East Asian Width standard), but web browsers just render them as regular single-width characters — even with monospace fonts like Roboto Mono or Menlo. So the output looks perfectly aligned in the terminal, but breaks in the browser.

I tried a CSS workaround (wrapping CJK chars in with width: 2ch), but that didn't really work either — the spacing between box-drawing characters and CJK glyphs just
doesn't line up consistently across fonts.

Given that this is mainly a CLI tool and the web interface is more of a nice-to-have, I'm not sure it's worth adding a bunch of complexity for this edge case.

That said, I completely understand if you'd prefer not to merge this as-is. I'll leave the decision up to you — and if you think it's not ready, I'm happy to close this PR.

Thanks again for your time! 👍

- Use runewidth.StringWidth() for display width calculation instead of
  len() which returns byte count
- Use runewidth.RuneWidth() to properly position wide characters
- Fill subsequent positions with empty string for wide CJK characters
  that occupy 2 terminal columns
- Add Korean language test case (korean_nodes.txt)

This fixes double-encoding bug where UTF-8 bytes were incorrectly
interpreted as Unicode code points, causing garbled output like
"íì¤í¸" instead of "테스트".

Affected functions:
- drawText(): text rendering
- drawBox(): node label rendering
- drawSubgraphLabel(): subgraph label rendering
- setColumnWidth(): box width calculation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ColtWindy
ColtWindy force-pushed the fix/graph-cjk-unicode-support branch from bddc54a to aa2bc82 Compare February 5, 2026 01:39
@xlight

xlight commented Apr 9, 2026

Copy link
Copy Markdown

Can't wait any more . I will use your fork

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants