#10 left/center/right/full justified text for TText
[fanfix.git] / src / jexer / bits / StringJustifier.java
CommitLineData
7657ad8c
KL
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer.bits;
30
31import java.util.List;
32import java.util.LinkedList;
33
34/**
35 * StringJustifier contains methods to convert one or more long lines of
36 * strings into justified text paragraphs.
37 */
38public final class StringJustifier {
39
40 /**
41 * Left-justify a string into a list of lines.
42 *
43 * @param str the string
44 * @param n the maximum number of characters in a line
45 * @return the list of lines
46 */
47 public static List<String> left(final String str, final int n) {
48 List<String> result = new LinkedList<String>();
49
50 /*
51 * General procedure:
52 *
53 * 1. Split on '\n' into paragraphs.
54 *
55 * 2. Scan each line, noting the position of the last
56 * beginning-of-a-word.
57 *
58 * 3. Chop at the last #2 if the next beginning-of-a-word exceeds
59 * n.
60 *
61 * 4. Return the lines.
62 */
63
64 String [] rawLines = str.split("\n");
65 for (int i = 0; i < rawLines.length; i++) {
66 StringBuilder line = new StringBuilder();
67 StringBuilder word = new StringBuilder();
68 boolean inWord = false;
69 for (int j = 0; j < rawLines[i].length(); j++) {
70 char ch = rawLines[i].charAt(j);
71 if ((ch == ' ') || (ch == '\t')) {
72 if (inWord == true) {
73 // We have just transitioned from a word to
74 // whitespace. See if we have enough space to add
75 // the word to the line.
76 if (word.length() + line.length() > n) {
77 // This word will exceed the line length. Wrap
78 // at it instead.
79 result.add(line.toString());
80 line = new StringBuilder();
81 }
82 if ((word.toString().startsWith(" "))
83 && (line.length() == 0)
84 ) {
85 line.append(word.substring(1));
86 } else {
87 line.append(word);
88 }
89 word = new StringBuilder();
90 word.append(ch);
91 inWord = false;
92 } else {
93 // We are in the whitespace before another word. Do
94 // nothing.
95 }
96 } else {
97 if (inWord == true) {
98 // We are appending to a word.
99 word.append(ch);
100 } else {
101 // We have transitioned from whitespace to a word.
102 word.append(ch);
103 inWord = true;
104 }
105 }
106 } // for (int j = 0; j < rawLines[i].length(); j++)
107
108 if (word.length() + line.length() > n) {
109 // This word will exceed the line length. Wrap at it
110 // instead.
111 result.add(line.toString());
112 line = new StringBuilder();
113 }
114 if ((word.toString().startsWith(" "))
115 && (line.length() == 0)
116 ) {
117 line.append(word.substring(1));
118 } else {
119 line.append(word);
120 }
121 result.add(line.toString());
122 } // for (int i = 0; i < rawLines.length; i++) {
123
124 return result;
125 }
126
127 /**
128 * Right-justify a string into a list of lines.
129 *
130 * @param str the string
131 * @param n the maximum number of characters in a line
132 * @return the list of lines
133 */
134 public static List<String> right(final String str, final int n) {
135 List<String> result = new LinkedList<String>();
136
137 /*
138 * Same as left(), but preceed each line with spaces to make it n
139 * chars long.
140 */
141 List<String> lines = left(str, n);
142 for (String line: lines) {
143 StringBuilder sb = new StringBuilder();
144 for (int i = 0; i < n - line.length(); i++) {
145 sb.append(' ');
146 }
147 sb.append(line);
148 result.add(sb.toString());
149 }
150
151 return result;
152 }
153
154 /**
155 * Center a string into a list of lines.
156 *
157 * @param str the string
158 * @param n the maximum number of characters in a line
159 * @return the list of lines
160 */
161 public static List<String> center(final String str, final int n) {
162 List<String> result = new LinkedList<String>();
163
164 /*
165 * Same as left(), but preceed/succeed each line with spaces to make
166 * it n chars long.
167 */
168 List<String> lines = left(str, n);
169 for (String line: lines) {
170 StringBuilder sb = new StringBuilder();
171 int l = (n - line.length()) / 2;
172 int r = n - line.length() - l;
173 for (int i = 0; i < l; i++) {
174 sb.append(' ');
175 }
176 sb.append(line);
177 for (int i = 0; i < r; i++) {
178 sb.append(' ');
179 }
180 result.add(sb.toString());
181 }
182
183 return result;
184 }
185
186 /**
187 * Fully-justify a string into a list of lines.
188 *
189 * @param str the string
190 * @param n the maximum number of characters in a line
191 * @return the list of lines
192 */
193 public static List<String> full(final String str, final int n) {
194 List<String> result = new LinkedList<String>();
195
196 /*
197 * Same as left(), but insert spaces between words to make each line
198 * n chars long. The "algorithm" here is pretty dumb: it performs a
199 * split on space and then re-inserts multiples of n between words.
200 */
201 List<String> lines = left(str, n);
202 for (int lineI = 0; lineI < lines.size() - 1; lineI++) {
203 String line = lines.get(lineI);
204 String [] words = line.split(" ");
205 if (words.length > 1) {
206 int charCount = 0;
207 for (int i = 0; i < words.length; i++) {
208 charCount += words[i].length();
209 }
210 int spaceCount = n - charCount;
211 int q = spaceCount / (words.length - 1);
212 int r = spaceCount % (words.length - 1);
213 StringBuilder sb = new StringBuilder();
214 for (int i = 0; i < words.length - 1; i++) {
215 sb.append(words[i]);
216 for (int j = 0; j < q; j++) {
217 sb.append(' ');
218 }
219 if (r > 0) {
220 sb.append(' ');
221 r--;
222 }
223 }
224 for (int j = 0; j < r; j++) {
225 sb.append(' ');
226 }
227 sb.append(words[words.length - 1]);
228 result.add(sb.toString());
229 } else {
230 result.add(line);
231 }
232 }
233 if (lines.size() > 0) {
234 result.add(lines.get(lines.size() - 1));
235 }
236
237 return result;
238 }
239
240}