下面列出了java.text.BreakIterator#getLineInstance ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private static List<String> renderText(String text) {
List<String> parts = new ArrayList<>();
BreakIterator boundary = BreakIterator.getLineInstance();
boundary.setText(text);
int start = boundary.first();
for (int end = boundary.next(); end != BreakIterator.DONE; end = boundary.next()) {
String substring = text.substring(start, end).trim();
String rendered = MongolCode.INSTANCE.unicodeToMenksoft(substring);
String code = getCode(substring);
String item = rendered + "\n" + code + "\n";
parts.add(item);
start = end;
}
return parts;
}
/**
* Adds HTML line breaks and indentation to a string so it wraps for things like long tooltips.
*
* <pre>
* string part 1
* string part 2
* ...
* string part X
* </pre>
*/
public static String addHtmlBreaksAndIndents(
final String target, final int firstLineMaxLength, final int maxLength) {
final StringBuilder sb = new StringBuilder();
final BreakIterator breakIterator = BreakIterator.getLineInstance();
breakIterator.setText(target);
int start = breakIterator.first();
int end = breakIterator.next();
int lineLength = 0;
int currentMaxLength = firstLineMaxLength;
while (end != BreakIterator.DONE) {
final String word = target.substring(start, end);
lineLength = lineLength + word.length();
if (lineLength >= currentMaxLength) {
sb.append("<br /> ");
lineLength = word.length() + 5; // Add 5 for the indent
currentMaxLength = maxLength;
}
sb.append(word);
start = end;
end = breakIterator.next();
}
return sb.toString();
}
/**
* Return break iterator appropriate for the current document.
*
* For non-i18n documents a fast whitespace-based break iterator is used.
*/
private BreakIterator getBreaker() {
Document doc = getDocument();
if ((doc != null) && Boolean.TRUE.equals(
doc.getProperty(AbstractDocument.MultiByteProperty))) {
Container c = getContainer();
Locale locale = (c == null ? Locale.getDefault() : c.getLocale());
return BreakIterator.getLineInstance(locale);
} else {
return new WhitespaceBasedBreakIterator();
}
}
/**
* Return break iterator appropriate for the current document.
*
* For non-i18n documents a fast whitespace-based break iterator is used.
*/
private BreakIterator getBreaker() {
Document doc = getDocument();
if ((doc != null) && Boolean.TRUE.equals(
doc.getProperty(AbstractDocument.MultiByteProperty))) {
Container c = getContainer();
Locale locale = (c == null ? Locale.getDefault() : c.getLocale());
return BreakIterator.getLineInstance(locale);
} else {
return new WhitespaceBasedBreakIterator();
}
}
private void processNextEmbeddedLine( String line ) {
BreakIterator words = BreakIterator.getLineInstance( Locale.US );
words.setText( line );
StringBuilder nextCell = new StringBuilder();
int start = words.first();
for ( int end = words.next(); end != DONE; start = end, end = words.next() )
nextCell = processNextWord( line, nextCell, start, end );
if ( nextCell.length() > 0 )
addCell( nextCell.toString() );
}
protected void startParagraph(AttributedCharacterIterator paragraph, boolean truncateAtChar)
{
this.paragraph = paragraph;
BreakIterator breakIt = truncateAtChar ? BreakIterator.getCharacterInstance()
: BreakIterator.getLineInstance();
lineMeasurer = new LineBreakMeasurer(paragraph, breakIt, context.getFontRenderContext());
}
public BreakIteratorTest()
{
characterBreak = BreakIterator.getCharacterInstance();
wordBreak = BreakIterator.getWordInstance();
lineBreak = BreakIterator.getLineInstance();
sentenceBreak = BreakIterator.getSentenceInstance();
}
private void makeLayoutWindow(int localStart) {
int compStart = localStart;
int compLimit = fChars.length;
// If we've already gone past the layout window, format to end of paragraph
if (layoutCount > 0 && !haveLayoutWindow) {
float avgLineLength = Math.max(layoutCharCount / layoutCount, 1);
compLimit = Math.min(localStart + (int)(avgLineLength*EST_LINES), fChars.length);
}
if (localStart > 0 || compLimit < fChars.length) {
if (charIter == null) {
charIter = new CharArrayIterator(fChars);
}
else {
charIter.reset(fChars);
}
if (fLineBreak == null) {
fLineBreak = BreakIterator.getLineInstance();
}
fLineBreak.setText(charIter);
if (localStart > 0) {
if (!fLineBreak.isBoundary(localStart)) {
compStart = fLineBreak.preceding(localStart);
}
}
if (compLimit < fChars.length) {
if (!fLineBreak.isBoundary(compLimit)) {
compLimit = fLineBreak.following(compLimit);
}
}
}
ensureComponents(compStart, compLimit);
haveLayoutWindow = true;
}
/**
* Creates a reader that breaks an input text to fit in a given width.
*
* @param reader Reader of the input text
* @param gc The graphic context that defines the currently used font sizes
* @param maxLineWidth The max width (pixels) where the text has to fit in
*/
public PyLineBreakReader(Reader reader, GC gc, int maxLineWidth) {
fReader = new BufferedReader(reader);
fGC = gc;
fMaxWidth = maxLineWidth;
fOffset = 0;
fLine = null;
fLineBreakIterator = BreakIterator.getLineInstance();
fBreakWords = true;
}
/**
* Creates a new text block from the given string, breaking the
* text into lines so that the <code>maxWidth</code> value is
* respected.
*
* @param text the text.
* @param font the font.
* @param paint the paint.
* @param maxWidth the maximum width for each line.
* @param maxLines the maximum number of lines.
* @param measurer the text measurer.
*
* @return A text block.
*/
public static TextBlock createTextBlock(String text, Font font,
Paint paint, float maxWidth, int maxLines, TextMeasurer measurer) {
TextBlock result = new TextBlock();
BreakIterator iterator = BreakIterator.getLineInstance();
iterator.setText(text);
int current = 0;
int lines = 0;
int length = text.length();
while (current < length && lines < maxLines) {
int next = nextLineBreak(text, current, maxWidth, iterator,
measurer);
if (next == BreakIterator.DONE) {
result.addLine(text.substring(current), font, paint);
return result;
} else if (next == current) {
next++; // we must take one more character or we'll loop forever
}
result.addLine(text.substring(current, next), font, paint);
lines++;
current = next;
while (current < text.length()&& text.charAt(current) == '\n') {
current++;
}
}
if (current < length) {
TextLine lastLine = result.getLastLine();
TextFragment lastFragment = lastLine.getLastTextFragment();
String oldStr = lastFragment.getText();
String newStr = "...";
if (oldStr.length() > 3) {
newStr = oldStr.substring(0, oldStr.length() - 3) + "...";
}
lastLine.removeFragment(lastFragment);
TextFragment newFragment = new TextFragment(newStr,
lastFragment.getFont(), lastFragment.getPaint());
lastLine.addFragment(newFragment);
}
return result;
}
/**
* This method calculates the preferred height for
* a rectangle that has a width of <code>maxWidth</code>
* and should display the String <code>text</code> inside it
* with line breaks
* @param fm {@link FontMetrics} to use
* @param maxWidth the maximum width of the rectangle
* @param text the text we want to display
* @return the preferred height
*/
public static int calculatePreferredHeight(FontMetrics fm, int maxWidth,
String text) {
if("".equals(text)) {
return 0;
}
// utility that helps us to break the lines
final BreakIterator bi = BreakIterator.getLineInstance();
bi.setText(text);
int lineCount = 0;
final int lineHeight = fm.getHeight();
// offsets for String.substring(start, end);
int startOffset = bi.first();
int endOffset = bi.next();
// we go over each possible line break that BreakIterator suggests.
do {
if(endOffset == text.length()) {
// we are at the end. this would cause IllegalArgumentException
// so we just subtract 1
endOffset--;
}
// get the width of the current substring
// and check if we are over the maximum width
final String substring = text.substring(startOffset, endOffset);
final int stringWidth = fm.stringWidth(substring);
if(stringWidth > maxWidth) {
// calculate how many lines we have to add.
// If there is a very long string with no spaces
// it could be that we have to add more than 1 line.
int toAdd = (int) (Math.ceil((double) stringWidth / (double) maxWidth) - 1);
lineCount+= toAdd;
// we need to advance the startOffset so
// we can start to search for a new line
startOffset = bi.preceding(endOffset);
bi.next();
}
} while((endOffset = bi.next()) != BreakIterator.DONE);
// ensure that the rest of a line also gets a line
lineCount++;
return lineHeight * lineCount;
}
/**
* Reformats a string where lines that are longer than <tt>width</tt>
* <p/>
* are split apart at the earliest wordbreak or at maxLength, whichever is
* <p/>
* sooner. If the width specified is less than 5 or greater than the input
* <p/>
* Strings length the string will be returned as is.
* <p/>
* <p/>
* <p/>
* Please note that this method can be lossy - trailing spaces on wrapped
* <p/>
* lines may be trimmed.
*
* @param input
* the String to reformat.
* @param width
* the maximum length of any one line.
* @param locale
* of the string to be wrapped.
* @return a new String with reformatted as needed.
*/
public static String wordWrap(String input, int width, Locale locale) {
// protect ourselves
if (input == null) {
return "";
} else if (width < 5) {
return input;
} else if (width >= input.length()) {
return input;
}
StringBuilder buf = new StringBuilder(input);
boolean endOfLine = false;
int lineStart = 0;
for (int i = 0; i < buf.length(); i++) {
if (buf.charAt(i) == '\n') {
lineStart = i + 1;
endOfLine = true;
}
// handle splitting at width character
if (i > lineStart + width - 1) {
if (!endOfLine) {
int limit = i - lineStart - 1;
BreakIterator breaks = BreakIterator
.getLineInstance(locale);
breaks.setText(buf.substring(lineStart, i));
int end = breaks.last();
// if the last character in the search string isn't a space,
// we can't split on it (looks bad). Search for a previous
// break character
if (end == limit + 1) {
if (!Character
.isWhitespace(buf.charAt(lineStart + end))) {
end = breaks.preceding(end - 1);
}
}
// if the last character is a space, replace it with a \n
if (end != BreakIterator.DONE && end == limit + 1) {
buf.replace(lineStart + end, lineStart + end + 1, "\n");
lineStart = lineStart + end;
}
// otherwise, just insert a \n
else if (end != BreakIterator.DONE && end != 0) {
buf.insert(lineStart + end, '\n');
lineStart = lineStart + end + 1;
} else {
buf.insert(i, '\n');
lineStart = i + 1;
}
} else {
buf.insert(i, '\n');
lineStart = i + 1;
endOfLine = false;
}
}
}
return buf.toString();
}
/**
* Method converts single-line string to the word-wrapped multiline string with the given max-width
* and max-line-number values (if the string is longer than that - the rest of the string will
* be thrown out and "..." will be added to the end)
*
* @param input - the source string
* @param width - the max width of one line of text
* @param maxlines - the max number of lines that result string can contain
* @param locale - the source string locale; is used to determine the possible places to make word-wraps in the source string
* @return the string with word wrapping
*/
public static String wordWrap(String input, int width, int maxlines, Locale locale) {
if (input == null) {
return "";
}
if (width >= input.length()) {
return input;
}
StringBuilder buf = new StringBuilder(input);
boolean endOfLine = false;
int lineStart = 0;
for (int i = 0; i < buf.length(); i++) {
if (buf.charAt(i) == '\n') {
lineStart = i + 1;
endOfLine = true;
}
// handle splitting at width character
if (i > lineStart + width - 1) {
if (!endOfLine) {
int limit = i - lineStart - 1;
BreakIterator breaks = BreakIterator.getLineInstance(locale);
breaks.setText(buf.substring(lineStart, i));
int end = breaks.last();
// if the last character in the search string isn't a space,
// we can't split on it. Search for a previous break character
if (end == limit + 1) {
if (!Character.isWhitespace(buf.charAt(lineStart + end))) {
end = breaks.preceding(end - 1);
}
}
// if the last character is a space - replace it with \n
if (end != BreakIterator.DONE && end == limit + 1) {
buf.replace(lineStart + end, lineStart + end + 1, "\n");
lineStart = lineStart + end;
}
// otherwise - just insert a \n
else if (end != BreakIterator.DONE && end != 0) {
buf.insert(lineStart + end, '\n');
lineStart = lineStart + end + 1;
}
else {
buf.insert(i, '\n');
lineStart = i + 1;
}
}
else {
buf.insert(i, '\n');
lineStart = i + 1;
endOfLine = false;
}
}
}
// Throw out excess strings
String result = buf.toString();
StringBuilder builder = new StringBuilder();
String[] lines = result.split("\n");
if (lines != null && lines.length > maxlines) {
String shortedStr = "...";
if (lines[maxlines - 1].length() + shortedStr.length() <= width) {
lines[maxlines - 1] = lines[maxlines - 1] + shortedStr;
} else {
lines[maxlines - 1] = lines[maxlines -1].substring(0, width - shortedStr.length()) + shortedStr;
}
for (int i = 0; i < maxlines; i++) {
builder.append(lines[i]).append("\n");
}
result = builder.toString();
result = result.substring(0, result.length() - 1);
}
return result;
}
/**
* Reformats a string where lines that are longer than <tt>width</tt>
* <p/>
* are split apart at the earliest wordbreak or at maxLength, whichever is
* <p/>
* sooner. If the width specified is less than 5 or greater than the input
* <p/>
* Strings length the string will be returned as is.
* <p/>
* <p/>
* <p/>
* Please note that this method can be lossy - trailing spaces on wrapped
* <p/>
* lines may be trimmed.
*
* @param input
* the String to reformat.
* @param width
* the maximum length of any one line.
* @param locale
* of the string to be wrapped.
* @return a new String with reformatted as needed.
*/
public static String wordWrap(String input, int width, Locale locale) {
// protect ourselves
if (input == null) {
return "";
} else if (width < 5) {
return input;
} else if (width >= input.length()) {
return input;
}
StringBuilder buf = new StringBuilder(input);
boolean endOfLine = false;
int lineStart = 0;
for (int i = 0; i < buf.length(); i++) {
if (buf.charAt(i) == '\n') {
lineStart = i + 1;
endOfLine = true;
}
// handle splitting at width character
if (i > lineStart + width - 1) {
if (!endOfLine) {
int limit = i - lineStart - 1;
BreakIterator breaks = BreakIterator
.getLineInstance(locale);
breaks.setText(buf.substring(lineStart, i));
int end = breaks.last();
// if the last character in the search string isn't a space,
// we can't split on it (looks bad). Search for a previous
// break character
if (end == limit + 1) {
if (!Character
.isWhitespace(buf.charAt(lineStart + end))) {
end = breaks.preceding(end - 1);
}
}
// if the last character is a space, replace it with a \n
if (end != BreakIterator.DONE && end == limit + 1) {
buf.replace(lineStart + end, lineStart + end + 1, "\n");
lineStart = lineStart + end;
}
// otherwise, just insert a \n
else if (end != BreakIterator.DONE && end != 0) {
buf.insert(lineStart + end, '\n');
lineStart = lineStart + end + 1;
} else {
buf.insert(i, '\n');
lineStart = i + 1;
}
} else {
buf.insert(i, '\n');
lineStart = i + 1;
endOfLine = false;
}
}
}
return buf.toString();
}
/**
* Synchronize the strategy with its FlowView. Allows the strategy
* to update its state to account for changes in that portion of the
* model represented by the FlowView. Also allows the strategy
* to update the FlowView in response to these changes.
*/
void sync(FlowView fv) {
View lv = getLogicalView(fv);
text.setView(lv);
Container container = fv.getContainer();
FontRenderContext frc = sun.swing.SwingUtilities2.
getFontRenderContext(container);
BreakIterator iter;
Container c = fv.getContainer();
if (c != null) {
iter = BreakIterator.getLineInstance(c.getLocale());
} else {
iter = BreakIterator.getLineInstance();
}
Object shaper = null;
if (c instanceof JComponent) {
shaper = ((JComponent) c).getClientProperty(
TextAttribute.NUMERIC_SHAPING);
}
text.setShaper(shaper);
measurer = new LineBreakMeasurer(text, iter, frc);
// If the children of the FlowView's logical view are GlyphViews, they
// need to have their painters updated.
int n = lv.getViewCount();
for( int i=0; i<n; i++ ) {
View child = lv.getView(i);
if( child instanceof GlyphView ) {
int p0 = child.getStartOffset();
int p1 = child.getEndOffset();
measurer.setPosition(text.toIteratorIndex(p0));
TextLayout layout
= measurer.nextLayout( Float.MAX_VALUE,
text.toIteratorIndex(p1), false );
((GlyphView)child).setGlyphPainter(new GlyphPainter2(layout));
}
}
// Reset measurer.
measurer.setPosition(text.getBeginIndex());
}
/**
* Wraps {@code text} so it fits within {@code maxWidth} columns.
*
* <p>The first line will be indented by {@code firstLineIndent} spaces while all the other lines
* will be indented by {@code otherLinesIndent} spaces.
*/
@VisibleForTesting
@CheckReturnValue
static String wrap(String text, int maxWidth, int firstLineIndent, int otherLinesIndent) {
int newLineIdx = text.indexOf(LINE_SEPARATOR);
if (newLineIdx != -1) {
// If a line break is found in the sentence, then we wrap the text recursively for each part.
return wrap(text.substring(0, newLineIdx), maxWidth, firstLineIndent, otherLinesIndent)
+ LINE_SEPARATOR
+ wrap(
text.substring(newLineIdx + LINE_SEPARATOR.length()),
maxWidth,
firstLineIndent,
otherLinesIndent);
}
BreakIterator boundary = BreakIterator.getLineInstance(Locale.ENGLISH);
boundary.setText(text);
int start = boundary.first();
int end = boundary.next();
// The text wrapped as it will be returned.
StringBuilder wrappedText = new StringBuilder();
// The current line being built.
StringBuilder line = new StringBuilder(Strings.repeat(" ", firstLineIndent));
while (end != BreakIterator.DONE) {
String word = text.substring(start, end);
if (line.length() + word.trim().length() > maxWidth) {
wrappedText
.append(CharMatcher.whitespace().trimTrailingFrom(line.toString()))
.append(LINE_SEPARATOR);
line = new StringBuilder(Strings.repeat(" ", otherLinesIndent));
}
line.append(word);
start = end;
end = boundary.next();
}
wrappedText.append(line);
return wrappedText.toString();
}
/**
* Break the paragraph into individual lines.
*
* @param font the font used for rendering the text.
* @param fontSize the fontSize used for rendering the text.
* @param width the width of the box holding the content.
* @return the individual lines.
* @throws IOException
*/
List<Line> getLines(PDFont font, float fontSize, float width) throws IOException
{
BreakIterator iterator = BreakIterator.getLineInstance();
iterator.setText(textContent);
final float scale = fontSize / FONTSCALE;
int start = iterator.first();
int end = iterator.next();
float lineWidth = 0;
List<Line> textLines = new ArrayList<Line>();
Line textLine = new Line();
while (end != BreakIterator.DONE)
{
String word = textContent.substring(start, end);
float wordWidth = font.getStringWidth(word) * scale;
lineWidth = lineWidth + wordWidth;
// check if the last word would fit without the whitespace ending it
if (lineWidth >= width && Character.isWhitespace(word.charAt(word.length() - 1)))
{
float whitespaceWidth = font.getStringWidth(word.substring(word.length() - 1))
* scale;
lineWidth = lineWidth - whitespaceWidth;
}
if (lineWidth >= width)
{
textLine.setWidth(textLine.calculateWidth(font, fontSize));
textLines.add(textLine);
textLine = new Line();
lineWidth = font.getStringWidth(word) * scale;
}
AttributedString as = new AttributedString(word);
as.addAttribute(TextAttribute.WIDTH, wordWidth);
Word wordInstance = new Word(word);
wordInstance.setAttributes(as);
textLine.addWord(wordInstance);
start = end;
end = iterator.next();
}
textLine.setWidth(textLine.calculateWidth(font, fontSize));
textLines.add(textLine);
return textLines;
}
/**
* Synchronize the strategy with its FlowView. Allows the strategy
* to update its state to account for changes in that portion of the
* model represented by the FlowView. Also allows the strategy
* to update the FlowView in response to these changes.
*/
void sync(FlowView fv) {
View lv = getLogicalView(fv);
text.setView(lv);
Container container = fv.getContainer();
FontRenderContext frc = sun.swing.SwingUtilities2.
getFontRenderContext(container);
BreakIterator iter;
Container c = fv.getContainer();
if (c != null) {
iter = BreakIterator.getLineInstance(c.getLocale());
} else {
iter = BreakIterator.getLineInstance();
}
Object shaper = null;
if (c instanceof JComponent) {
shaper = ((JComponent) c).getClientProperty(
TextAttribute.NUMERIC_SHAPING);
}
text.setShaper(shaper);
measurer = new LineBreakMeasurer(text, iter, frc);
// If the children of the FlowView's logical view are GlyphViews, they
// need to have their painters updated.
int n = lv.getViewCount();
for( int i=0; i<n; i++ ) {
View child = lv.getView(i);
if( child instanceof GlyphView ) {
int p0 = child.getStartOffset();
int p1 = child.getEndOffset();
measurer.setPosition(text.toIteratorIndex(p0));
TextLayout layout
= measurer.nextLayout( Float.MAX_VALUE,
text.toIteratorIndex(p1), false );
((GlyphView)child).setGlyphPainter(new GlyphPainter2(layout));
}
}
// Reset measurer.
measurer.setPosition(text.getBeginIndex());
}
/**
* Constructs a <code>LineBreakMeasurer</code> for the specified text.
*
* @param text the text for which this <code>LineBreakMeasurer</code>
* produces <code>TextLayout</code> objects; the text must contain
* at least one character; if the text available through
* <code>iter</code> changes, further calls to this
* <code>LineBreakMeasurer</code> instance are undefined (except,
* in some cases, when <code>insertChar</code> or
* <code>deleteChar</code> are invoked afterward - see below)
* @param frc contains information about a graphics device which is
* needed to measure the text correctly;
* text measurements can vary slightly depending on the
* device resolution, and attributes such as antialiasing; this
* parameter does not specify a translation between the
* <code>LineBreakMeasurer</code> and user space
* @see LineBreakMeasurer#insertChar
* @see LineBreakMeasurer#deleteChar
*/
public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
this(text, BreakIterator.getLineInstance(), frc);
}
/**
* Constructs a <code>LineBreakMeasurer</code> for the specified text.
*
* @param text the text for which this <code>LineBreakMeasurer</code>
* produces <code>TextLayout</code> objects; the text must contain
* at least one character; if the text available through
* <code>iter</code> changes, further calls to this
* <code>LineBreakMeasurer</code> instance are undefined (except,
* in some cases, when <code>insertChar</code> or
* <code>deleteChar</code> are invoked afterward - see below)
* @param frc contains information about a graphics device which is
* needed to measure the text correctly;
* text measurements can vary slightly depending on the
* device resolution, and attributes such as antialiasing; this
* parameter does not specify a translation between the
* <code>LineBreakMeasurer</code> and user space
* @see LineBreakMeasurer#insertChar
* @see LineBreakMeasurer#deleteChar
*/
public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
this(text, BreakIterator.getLineInstance(), frc);
}