---
category: swing
folder: GlyphVector
title: JLabelの文字列を折り返し
tags: [JLabel, GlyphVector, JTextArea, LineBreakMeasurer]
author: aterai
pubdate: 2004-03-01T11:56:19+09:00
description: GlyphVectorを使って、ラベルの文字列を折り返して表示します。
image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTNbQw2SHI/AAAAAAAAAaw/AApL8KKml8E/s800/GlyphVector.png
---
* 概要 [#summary]
`GlyphVector`を使って、ラベルの文字列を折り返して表示します。

#download(https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTNbQw2SHI/AAAAAAAAAaw/AApL8KKml8E/s800/GlyphVector.png)

* サンプルコード [#sourcecode]
#code(link){{
class WrappedLabel extends JLabel {
  private GlyphVector gvtext;
  private int width = -1;
  public WrappedLabel() {
    this(null);
  }

  public WrappedLabel(String str) {
    super(str);
  }

  @Override public void doLayout() {
    Insets i = getInsets();
    int w = getWidth() - i.left - i.right;
    if (w != width) {
      Font font = getFont();
      FontMetrics fm = getFontMetrics(font);
      FontRenderContext frc = fm.getFontRenderContext();
      gvtext = getWrappedGlyphVector(getText(), w, font, frc);
      width = w;
    }
    super.doLayout();
  }

  @Override protected void paintComponent(Graphics g) {
    if (gvtext == null) {
      super.paintComponent(g);
    } else {
      Insets i = getInsets();
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(Color.RED);
      g2.drawGlyphVector(gvtext, i.left, getFont().getSize() + i.top);
      g2.dispose();
    }
  }

  private GlyphVector getWrappedGlyphVector(
      String str, float width, Font font, FontRenderContext frc) {
    Point2D gmPos = new Point2D.Double(0d, 0d);
    GlyphVector gv = font.createGlyphVector(frc, str);
    float lineheight = (float) (gv.getLogicalBounds().getHeight());
    float xpos = 0f;
    float advance = 0f;
    int lineCount = 0;
    GlyphMetrics gm;
    for (int i = 0; i < gv.getNumGlyphs(); i++) {
      gm = gv.getGlyphMetrics(i);
      advance = gm.getAdvance();
      if (xpos < width && width <= xpos + advance) {
        lineCount++;
        xpos = 0f;
      }
      gmPos.setLocation(xpos, lineheight * lineCount);
      gv.setGlyphPosition(i, gmPos);
      xpos = xpos + advance;
    }
    return gv;
  }
}
}}

* 解説 [#explanation]
- 上: `JLabel`
-- デフォルトの`JLabel`で折り返しせずに右側から`...`で省略
- 中: `GlyphVector`
-- コンテナのサイズが変更されるたびに`GlyphVector`を更新して文字列の折り返しを実行
-- 欧文などで合字(リガチャ)がある場合は`GlyphVector gv = font.createGlyphVector(frc, str);`ではなく、[https://community.oracle.com/thread/1289266 GlyphVector bounds and kerning, ligatures | Oracle Forums]のように`char[] chars = text.toCharArray(); GlyphVector gv = font.layoutGlyphVector(frc, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);`とした方が良さそう
- 下: `JTextArea`
-- `JLabel`の`Font`と背景色を同じものに設定した編集不可の`JTextArea`を`setLineWrap(true);`として文字列の折り返しを実行

----
- ラベルの幅ではなく任意の場所で文字列を改行したい場合は、以下のように`JLabel`に`html`の`<br>`タグを利用したり、編集不可にした`JTextPane`、`JTextArea`などが使用可能
-- 参考: [[JTextPane、JLabelなどで複数行を表示>Swing/MultiLineLabel]]
#code{{
label.setText("<html>文字列を適当なところで<br />折り返す。");
}}

- `AttributedString`と`LineBreakMeasurer`を使って文字列の折り返しを描画する方法もある
#code{{
class WrappingLabel extends JLabel {
  public WrappingLabel(String text) {
    super(text);
  }

  @Override protected void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setPaint(getForeground());
    Insets i = getInsets();
    float x = i.left;
    float y = i.top;
    int w = getWidth() - i.left - i.right;
    AttributedString as = new AttributedString(getText());
    as.addAttribute(TextAttribute.FONT, getFont());
    AttributedCharacterIterator aci = as.getIterator();
    FontRenderContext frc = g2.getFontRenderContext();
    LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
    while (lbm.getPosition() < aci.getEndIndex()) {
      TextLayout tl = lbm.nextLayout(w);
      tl.draw(g2, x, y + tl.getAscent());
      y += tl.getDescent() + tl.getLeading() + tl.getAscent();
    }
    g2.dispose();
  }
}
}}

* 参考リンク [#reference]
- [https://docs.oracle.com/javase/tutorial/2d/text/drawmulstring.html Drawing Multiple Lines of Text (The Java™ Tutorials > 2D Graphics > Working with Text APIs)]
- [https://docs.oracle.com/javase/jp/8/docs/api/java/awt/font/LineBreakMeasurer.html LineBreakMeasurer (Java Platform SE 8)]
- [https://bugs.openjdk.java.net/browse/JDK-6479801 JDK-6479801 java.awt.font.LineBreakMeasurer code incorrect - Java Bug System]
- [https://bugs.openjdk.org/browse/JDK-6479801 JDK-6479801 java.awt.font.LineBreakMeasurer code incorrect - Java Bug System]

* コメント [#comment]
#comment
- `JTextArea#setLineWrap(true);`を使う方法を追加、スクリーンショットの更新。 -- &user(aterai); &new{2009-01-05 (月) 17:15:36};
- `LineBreakMeasurer`を使用する方法を追加。 -- &user(aterai); &new{2013-01-01 (火) 22:38:47};

#comment