-
Notifications
You must be signed in to change notification settings - Fork 393
Open
Labels
status/need-triageTeam needs to triage and take a first lookTeam needs to triage and take a first look
Description
🐛 Bug Report: Incorrect Table Border Alignment
📄 Description
When using the Table component to display data that contains CJK (Chinese, Japanese, Korean) characters or other full-width symbols , the table borders become misaligned and broken.
🖥️ Environment
- spring-shell version: 3.4.x
- spring-boot version: 3.5.8
- java version: 17
- os: windows 10, Ubuntu 22
🔬 Steps to Reproduce
project that replicates the problem: https://github.com/jiangood/spring-shell-cjk-issue-demo
clone and run mvnw package && java -jar target/app.jar hi
- windows cmd
- windows powershell
- linux (by XShell)
project main code :
@Command
public class TestCommand {
@Command
public Table hi() {
String[][] data = {
{"name", "addr"},
{"Alexander", "America"},
{"张三", "中国北京"},
};
TableModel model = new ArrayTableModel(data);
TableBuilder builder = new TableBuilder(model)
.addFullBorder(BorderStyle.fancy_light);
Table table = builder.build();
return table;
}
}💡 Possible Solution
Use Jline utils to calculate the width
AttributedString.fromAnsi(text).columnLength()
I found two place to fix it (spring-shell version: 3.4.x)
-
AutoSizeConstraintspublic class AutoSizeConstraints implements SizeConstraints { @Override public Extent width(String[] raw, int tableWidth, int nbColumns) { int max = 0; int min = 0; for (String line : raw) { String[] words = line.split(" "); for (String word : words) { // -- min = Math.max(min, word.length()); // ++ min = Math.max(min, AttributedString.fromAnsi(word).columnLength()); } // -- max = Math.max(max, line.length()); // ++ max = Math.max(max, AttributedString.fromAnsi(line).columnLength()); } return new Extent(min, max); } }
-
SimpleHorizontalAlignerpublic enum SimpleHorizontalAligner implements Aligner { left, center, right; @Override public String[] align(String[] text, int cellWidth, int cellHeight) { String[] result = new String[cellHeight]; for (int i = 0; i < cellHeight; i++) { String line = (i < text.length && text[i] != null) ? text[i].trim() : ""; // -- int paddingToDistribute = cellWidth - line.length(); // ++ int paddingToDistribute = cellWidth - AttributedString.fromAnsi(line).columnLength(); int padLeft; int padRight; switch (this) { case center: { int carry = paddingToDistribute % 2; paddingToDistribute = paddingToDistribute - carry; padLeft = padRight = paddingToDistribute / 2; padRight += carry; break; } case right: { padLeft = paddingToDistribute; padRight = 0; break; } case left: { padLeft = 0; padRight = paddingToDistribute; break; } default: throw new AssertionError(); } StringBuilder sb = new StringBuilder(cellWidth); for (int j = 0; j < padLeft; j++) { sb.append(' '); } sb.append(line); for (int j = 0; j < padRight; j++) { sb.append(' '); } result[i] = sb.toString(); } return result; } }
Metadata
Metadata
Assignees
Labels
status/need-triageTeam needs to triage and take a first lookTeam needs to triage and take a first look