2012-08-26 10 views
5

Używając Java, czy istnieje jakiś wbudowany sposób renderowania tekstu, który ogranicza się do prostokąta na obiekcie graphics2D?Jak renderować zawijany tekst na obrazie w java

Wiem, że mogę używać Graphics2D.drawString, ale rysuje tylko linię tekstu.

wiem też, że mogę używać

FontMetrics fm= graphics.getFontMetrics(font); 
Rectangle2D rect=fm.getStringBounds("Some Text",graphics); 

, aby uzyskać informacje o granicach sznurku przy wytapianiu użyciu niektórych Font font na jakimś Graphics2D graphics obiektu.

Mogę więc zacząć pętlę, łamiąc ciąg i tak dalej, aby wymusić dopasowanie do jakiegoś prostokąta.

Ale byłoby znacznie wolą nie trzeba pisać te ...

Czy istnieją jakieś gotowe funkcja, która zrobi to za mnie?

+0

ja osobiście jak na przykład świadczy Andrzeja, jak jest to stosunkowo szybko osiągnąć. Jednak w przeszłości stosowałem to podejście w przeszłości http://java.sun.com/developer/onlineTraining/Media/2DText/style.html#multiple – MadProgrammer

+0

Jakieś zalety i wady w przypadku dwóch alternatyw? – epeleg

+0

Powiedziałbym, że rozwiązanie Andrzeja jest łatwiejsze, IMHO – MadProgrammer

Odpowiedz

4

To tu może być to, czego szukasz:

StringUtils.java:

import java.awt.FontMetrics; 
import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Iterator; 
import java.util.List; 

/** 
* Globally available utility classes, mostly for string manipulation. 
* 
* @author Jim Menard, <a href="mailto:[email protected]">[email protected]</a> 
*/ 
public class StringUtils { 
    /** 
    * Returns an array of strings, one for each line in the string after it has 
    * been wrapped to fit lines of <var>maxWidth</var>. Lines end with any of 
    * cr, lf, or cr lf. A line ending at the end of the string will not output a 
    * further, empty string. 
    * <p> 
    * This code assumes <var>str</var> is not <code>null</code>. 
    * 
    * @param str 
    *   the string to split 
    * @param fm 
    *   needed for string width calculations 
    * @param maxWidth 
    *   the max line width, in points 
    * @return a non-empty list of strings 
    */ 
    public static List wrap(String str, FontMetrics fm, int maxWidth) { 
    List lines = splitIntoLines(str); 
    if (lines.size() == 0) 
     return lines; 

    ArrayList strings = new ArrayList(); 
    for (Iterator iter = lines.iterator(); iter.hasNext();) 
     wrapLineInto((String) iter.next(), strings, fm, maxWidth); 
    return strings; 
    } 

    /** 
    * Given a line of text and font metrics information, wrap the line and add 
    * the new line(s) to <var>list</var>. 
    * 
    * @param line 
    *   a line of text 
    * @param list 
    *   an output list of strings 
    * @param fm 
    *   font metrics 
    * @param maxWidth 
    *   maximum width of the line(s) 
    */ 
    public static void wrapLineInto(String line, List list, FontMetrics fm, int maxWidth) { 
    int len = line.length(); 
    int width; 
    while (len > 0 && (width = fm.stringWidth(line)) > maxWidth) { 
     // Guess where to split the line. Look for the next space before 
     // or after the guess. 
     int guess = len * maxWidth/width; 
     String before = line.substring(0, guess).trim(); 

     width = fm.stringWidth(before); 
     int pos; 
     if (width > maxWidth) // Too long 
     pos = findBreakBefore(line, guess); 
     else { // Too short or possibly just right 
     pos = findBreakAfter(line, guess); 
     if (pos != -1) { // Make sure this doesn't make us too long 
      before = line.substring(0, pos).trim(); 
      if (fm.stringWidth(before) > maxWidth) 
      pos = findBreakBefore(line, guess); 
     } 
     } 
     if (pos == -1) 
     pos = guess; // Split in the middle of the word 

     list.add(line.substring(0, pos).trim()); 
     line = line.substring(pos).trim(); 
     len = line.length(); 
    } 
    if (len > 0) 
     list.add(line); 
    } 

    /** 
    * Returns the index of the first whitespace character or '-' in <var>line</var> 
    * that is at or before <var>start</var>. Returns -1 if no such character is 
    * found. 
    * 
    * @param line 
    *   a string 
    * @param start 
    *   where to star looking 
    */ 
    public static int findBreakBefore(String line, int start) { 
    for (int i = start; i >= 0; --i) { 
     char c = line.charAt(i); 
     if (Character.isWhitespace(c) || c == '-') 
     return i; 
    } 
    return -1; 
    } 

    /** 
    * Returns the index of the first whitespace character or '-' in <var>line</var> 
    * that is at or after <var>start</var>. Returns -1 if no such character is 
    * found. 
    * 
    * @param line 
    *   a string 
    * @param start 
    *   where to star looking 
    */ 
    public static int findBreakAfter(String line, int start) { 
    int len = line.length(); 
    for (int i = start; i < len; ++i) { 
     char c = line.charAt(i); 
     if (Character.isWhitespace(c) || c == '-') 
     return i; 
    } 
    return -1; 
    } 
    /** 
    * Returns an array of strings, one for each line in the string. Lines end 
    * with any of cr, lf, or cr lf. A line ending at the end of the string will 
    * not output a further, empty string. 
    * <p> 
    * This code assumes <var>str</var> is not <code>null</code>. 
    * 
    * @param str 
    *   the string to split 
    * @return a non-empty list of strings 
    */ 
    public static List splitIntoLines(String str) { 
    ArrayList strings = new ArrayList(); 

    int len = str.length(); 
    if (len == 0) { 
     strings.add(""); 
     return strings; 
    } 

    int lineStart = 0; 

    for (int i = 0; i < len; ++i) { 
     char c = str.charAt(i); 
     if (c == '\r') { 
     int newlineLength = 1; 
     if ((i + 1) < len && str.charAt(i + 1) == '\n') 
      newlineLength = 2; 
     strings.add(str.substring(lineStart, i)); 
     lineStart = i + newlineLength; 
     if (newlineLength == 2) // skip \n next time through loop 
      ++i; 
     } else if (c == '\n') { 
     strings.add(str.substring(lineStart, i)); 
     lineStart = i + 1; 
     } 
    } 
    if (lineStart < len) 
     strings.add(str.substring(lineStart)); 

    return strings; 
    } 

} 

chcesz umieścić to w swojej własnej klasie, a następnie po prostu za to, co miał:

FontMetrics fm= graphics.getFontMetrics(font); 
Rectangle2D rect=fm.getStringBounds("Some Text",graphics); 

połączenia wrap(String str, FontMetrics fm, int maxWidth) który powróci do List z String S, które wykonane zostały zawinięte KKon rdingly do swojej maxWidth który będzie szerokość Rectangle2D tekst zostanie oddany do:

String text="Some Text"; 
FontMetrics fm= graphics.getFontMetrics(font); 
Rectangle2D rect=fm.getStringBounds(text,graphics); 
List<String> textList=StringUtils.wrap(text, fm, int maxWidth); 

referencyjny:

3

Zobacz źródło LabelRenderTest.java w this answer. Używa HTML/CSS, z ustawioną szerokością ciała za pomocą CSS, dzięki czemu automatycznie owija wiersz.

+0

+ 1 fajna technika muszę powiedzieć –

+0

@David Others nie zgodziłoby się z tobą, powołując się na to, że za pomocą formatowania HTML tracimy niektóre zalety layoutów (coś wspólnego z linią .. drop AFAIR) i obsługa J2SE dla HTML jest ograniczona do 3.2, a obsługa CSS jest stosunkowo niejednolita i nieudokumentowana. OTOH, może to być szybki i niezręczny sposób na automatyczne zapakowanie tekstu. –

5

pisałem niewielką funkcję, która może pomóc. 447 to szerokość dostępna, którą możesz uzyskać z wymaganej szerokości, aby renderować tekst.

private void drawTextUgly(String text, FontMetrics textMetrics, Graphics2D g2) 
{ 
    // Ugly code to wrap text 
    int lineHeight = textMetrics.getHeight(); 
    String textToDraw = text; 
    String[] arr = textToDraw.split(" "); 
    int nIndex = 0; 
    int startX = 319; 
    int startY = 113; 
    while (nIndex < arr.length) 
    { 
     String line = arr[nIndex++]; 
     while ((nIndex < arr.length) && (textMetrics.stringWidth(line + " " + arr[nIndex]) < 447)) 
     { 
      line = line + " " + arr[nIndex]; 
      nIndex++; 
     } 
     GraphicsUtility.drawString(g2, line, startX, startY); 
     startY = startY + lineHeight; 
    } 
} 
1
private List<String> wrap(String txt, FontMetrics fm, int maxWidth){ 
    StringTokenizer st = new StringTokenizer(txt) ; 

    List<String> list = new ArrayList<String>(); 
    String line = ""; 
    String lineBeforeAppend = ""; 
    while (st.hasMoreTokens()){ 
     String seg = st.nextToken(); 
     lineBeforeAppend = line; 
     line += seg + " "; 
     int width = fm.stringWidth(line); 
     if(width < maxWidth){ 
      continue; 
     }else { //new Line. 
      list.add(lineBeforeAppend); 
      line = seg + " "; 
     } 
    } 
    //the remaining part. 
    if(line.length() > 0){ 
     list.add(line); 
    } 
    return list; 
}