Java code for wrapping text lines to a max line width

44,831

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

https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/WordUtils.html

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()
Share:
44,831

Related videos on Youtube

George Armhold
Author by

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 &amp; 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, 2022

Comments

  • George Armhold
    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
      aioobe over 13 years
      Are you after a pixel-perfect algorithm, or would an approximation do?
  • Mike Clark
    Mike Clark over 10 years
    My brain threw an IndexOutOfBoundsException while reading this code snippet. :(
  • jett
    jett almost 10 years
    It just had one caveat for me. It strips off any leading spaces on the string.
  • JeffThompson
    JeffThompson over 4 years
    Why the final allLines.append(linebreak); right before the return? Seems to add an extra linebreak where you don't need one.