Java code for wrapping text lines to a max line width
Solution 1
Apache commons has WordUtils and wrap function in it:
http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/text/WordUtils.html
P.S. Looks like this is deprecated and you need to use
instead.
Solution 2
Use the word-wrap library (available on Maven Central).
Here's one way to use it:
String text = "hi there how are you going?";
String wrapped =
WordWrap.from(text)
.maxWidth(10)
.insertHyphens(true) // true is the default
.wrap();
Output is:
hi there
how are
you going?
The library conserves leading spaces on lines which is one complaint about the behaviour of the Apache commons-lang offering. You can also specify the stringWidth
function to get pixel-accurate results when rendering the text.
The library has decent unit test coverage (something to bear in mind when you consider copy and paste of code chunks from the web!).
The Maven dependency is:
<dependency>
<groupId>com.github.davidmoten</groupId>
<artifactId>word-wrap</artifactId>
<version>0.1.9</version>
</dependency>
Be sure to check for a later version.
Solution 3
Here's my take
private static final String LINEBREAK = "\n"; // or "\r\n";
public static String wrap(String string, int lineLength) {
StringBuilder b = new StringBuilder();
for (String line : string.split(Pattern.quote(LINEBREAK))) {
b.append(wrapLine(line, lineLength));
}
return b.toString();
}
private static String wrapLine(String line, int lineLength) {
if (line.length() == 0) return LINEBREAK;
if (line.length() <= lineLength) return line + LINEBREAK;
String[] words = line.split(" ");
StringBuilder allLines = new StringBuilder();
StringBuilder trimmedLine = new StringBuilder();
for (String word : words) {
if (trimmedLine.length() + 1 + word.length() <= lineLength) {
trimmedLine.append(word).append(" ");
} else {
allLines.append(trimmedLine).append(LINEBREAK);
trimmedLine = new StringBuilder();
trimmedLine.append(word).append(" ");
}
}
if (trimmedLine.length() > 0) {
allLines.append(trimmedLine);
}
allLines.append(linebreak);
return allLines.toString();
}
(This solution strips two spaces to one space (so same fault that @jett has with Apache commons WordUtils)).
Solution 4
I use the following. This preserves all horizontal white space, permits multi-character linefeed and wordbreak sequences, and allows hanging or otherwise-marked multi-character indents for broken words. The Javadoc is intended to sufficiently describe usage.
/**Wraps a source String into a series of lines having a maximum specified length. The source is
* wrapped at: spaces, horizontal tabs, system newLine characters, or a specified newLine character
* sequence. Existing newLine character sequences in the source string, whether they be the system
* newLine or the specified newLine, are honored. Existing whitespace (spaces and horizontal tabs)
* is preserved.
* <p>
* When <tt>wrapLongWords</tt> is true, words having a length greater than the specified
* <tt>lineLength</tt> will be broken, the specified <tt>longWordBreak</tt> terminator appended,
* and a new line initiated with the text of the specified <tt>longWordLinePrefix</tt> string. The
* position of the break will be unceremoniously chosen such that <tt>ineLength</tt> is honored.
* One use of <tt>longWordLinePrefix</tt> is to effect "hanging indents" by specifying a series of
* spaces for this parameter. This parameter can contain the lineFeed character(s). Although
* <tt>longWordLinePrefix</tt> can contain the horizontal tab character, the results are not
* guaranteed because no attempt is made to determine the quantity of character positions occupied by a
* horizontal tab.</p>
* <p>
* Example usage:
* <pre>
* wrap( " A very long word is Abracadabra in my book", 11, "\n", true, "-", " ");</pre>
* returns (note the effect of the single-character lineFeed):
* <pre>
* A very
* long word
* is Abraca-
* dabra in
* my book</pre>
* Whereas, the following:
* <pre>
* wrap( " A very long word is Abracadabra in my book", 11, null, true, null, " ");</pre>
* returns (due to the 2-character system linefeed):
* <pre>
* A very
* long
* word is A
* bracada
* bra in
* my book</pre></p>
*
* @param src the String to be word wrapped, may be null
* @param lineLength the maximum line length, including the length of <tt>newLineStr</tt> and, when
* applicable, <tt>longWordLinePrefix</tt>. If the value is insufficient to accommodate
* these two parameters + 1 character, it will be increased accordingly.
* @param newLineStr the string to insert for a new line, or <code>null</code> to use the value
* reported as the system line separator by the JVM
* @param wrapLongWords when <tt>false</tt>, words longer than <tt>wrapLength</t> will not be broken
* @param longWordBreak string with which to precede <tt>newLineStr</tt> on each line of a broken word,
* excepting the last line, or <tt>null</tt> if this feature is not to be used
* @param longWordLinePrefix string with which to prefix each line of a broken word, subsequent
* to the first line, or <tt>null</tt> if no prefix is to be used
* @return a line with newlines inserted, or <code>null</code> if <tt>src</tt> is null
*/
public static String wrap( String src, int lineLength, String newLineStr, boolean wrapLongWords,
String longWordBreak, String longWordLinePrefix ) {
// Trivial case
if ( src == null ) return null;
if ( newLineStr == null )
newLineStr = System.getProperty( "line.separator" );
if ( longWordBreak == null )
longWordBreak = "";
if ( longWordLinePrefix == null )
longWordLinePrefix = "";
// Adjust maximum line length to accommodate the newLine string
lineLength -= newLineStr.length();
if ( lineLength < 1 )
lineLength = 1;
// Guard for long word break or prefix that would create an infinite loop
if ( wrapLongWords && lineLength - longWordBreak.length() - longWordLinePrefix.length() < 1 )
lineLength += longWordBreak.length() + longWordLinePrefix.length();
int
remaining = lineLength,
breakLength = longWordBreak.length();
Matcher m = Pattern.compile( ".+?[ \\t]|.+?(?:" + newLineStr + ")|.+?$" ).matcher( src );
StringBuilder cache = new StringBuilder();
while ( m.find() ) {
String word = m.group();
// Breakup long word
while ( wrapLongWords && word.length() > lineLength ) {
cache
.append( word.substring( 0, remaining - breakLength ) )
.append( longWordBreak )
.append( newLineStr );
word = longWordLinePrefix + word.substring( remaining - breakLength );
remaining = lineLength;
} // if
// Linefeed if word exceeds remaining space
if ( word.length() > remaining ) {
cache
.append( newLineStr )
.append( word );
remaining = lineLength;
} // if
// Word fits in remaining space
else
cache.append( word );
remaining -= word.length();
} // while
return cache.toString();
} // wrap()
Related videos on Youtube
George Armhold
I'm a consultant, currently focusing on the following topics: Go! (Golang) Ruby, Ruby on Rails Docker and containers more generally search (ElasticSearch, Apache Solr & Bleve) I've worked on lots of other stuff in the past, including: Java Android and iOS apps Please visit my website for details.
Updated on July 09, 2022Comments
-
George Armhold almost 2 years
Before I re-invent the wheel (poorly), I'd like to know if there is a some existing Java code for wrapping text lines to a given maximum width. Ideally it would:
- respect existing linebreaks
- break up lines that exceed a maximum length on word boundaries
- break up words whose length exceeds the maximum line width by inserting hyphens
Edit: there are no "pixels" here, only java.lang.String. "maximum width" refers to the number of characters on a line.
-
aioobe over 13 yearsAre you after a pixel-perfect algorithm, or would an approximation do?
-
Mike Clark over 10 yearsMy brain threw an IndexOutOfBoundsException while reading this code snippet. :(
-
jett almost 10 yearsIt just had one caveat for me. It strips off any leading spaces on the string.
-
JeffThompson over 4 yearsWhy the final
allLines.append(linebreak);
right before thereturn
? Seems to add an extra linebreak where you don't need one.