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()