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