Как отобразить обернутый текст на изображении в Java

Используя Java, есть ли какой-либо встроенный способ рендеринга текста, чтобы он ограничивался прямоугольником на объекте graphics2D?

Я знаю, что могу использовать Graphics2D.drawString, но он рисует только одну строку текста.

Я также знаю, что могу использовать

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

чтобы получить информацию о границах строки при рендеринге с использованием некоторого Font font на некотором Graphics2D graphics объекте.

Так что я мог начать зацикливаться, разрывать свою строку и так далее, чтобы заставить ее поместиться внутри какого-то прямоугольника.

Но я бы предпочел не писать их...

Есть ли готовая функция, которая сделает это за меня?


person epeleg    schedule 26.08.2012    source источник
comment
Мне лично нравится пример, показанный Эндрю, поскольку его можно достичь относительно быстро. Однако я использовал этот подход в прошлом java.sun .com/developer/onlineTraining/Media/2DText/   -  person MadProgrammer    schedule 26.08.2012
comment
Любые плюсы и минусы двух альтернатив?   -  person epeleg    schedule 26.08.2012
comment
Я бы сказал, что решение Эндрю проще, ИМХО   -  person MadProgrammer    schedule 26.08.2012


Ответы (4)


Используйте временный JTextArea для идеального переноса строк с ~ 10 строками кода:

static void drawWrappedText(Graphics g, String text, int x, int y, int w, int h) {
    JTextArea ta = new JTextArea(text);
    ta.setLineWrap(true);
    ta.setWrapStyleWord(true);
    ta.setBounds(0, 0, w, h);
    ta.setForeground(g.getColor());
    ta.setFont(g.getFont());
    Graphics g2 = g.create(x, y, w, h); // Use new graphics to leave original graphics state unchanged
    ta.paint(g2);
}
person Adam Gawne-Cain    schedule 04.02.2021
comment
Приятно видеть, что кто-то все еще хочет ответить на вопрос, который я задал 8,5 лет назад. Я ценю усилия. Я понятия не имею, почему я задал этот вопрос и почему я не принял ни одного ответа в прошлом (я стараюсь не оставлять вопросы открытыми). У меня также нет среды для проверки вашего ответа (и времени для этого) - однако, видя, что он короткий, недавний и, по крайней мере, кажется, делает то, о чем я просил так много времени назад, я приму ваш ответ и позволю сообществу проголосовать за Это... - person epeleg; 07.03.2021

Здесь может быть то, что вы ищете:

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;
  }

}

вы бы поместили это в свой собственный класс, а затем просто использовали то, что у вас было:

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

вызовите wrap(String str, FontMetrics fm, int maxWidth), который вернет List из String, которые были обернуты в соответствии с вашим maxWidth, который будет шириной Rectangle2D, в которую будет помещен текст:

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

Ссылка:

person David Kroukamp    schedule 26.08.2012

Я написал небольшую функцию, которая может помочь. 447 - это доступная ширина, которую вы можете получить от необходимой ширины для рендеринга текста.

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;
    }
}
person Nauman Khan    schedule 19.11.2012

См. источник LabelRenderTest в этом ответе. Он использует HTML/CSS, а ширина тела задается с помощью CSS и, таким образом, делает автоматический перенос строки.

person Andrew Thompson    schedule 26.08.2012
comment
Пожалуйста, примите ответ, если он помог решить проблему. - person Andrew Thompson; 18.11.2020