モジュール java.desktop
パッケージ java.awt.font

クラスLineBreakMeasurer


  • public final class LineBreakMeasurer
    extends Object
    LineBreakMeasurerクラスを使用すれば、書式付きテキストを、特定の可視有効幅に収まる行(またはセグメント)に分けることができます。 これは、固有の幅(ラッピング幅と呼ばれます)に収まるテキストの段落をクライアントに表示する場合に便利です。

    LineBreakMeasurerは、書式付きテキストに対するイテレータを使って構築されます。 イテレータの範囲はテキスト内の1つの段落です。 LineBreakMeasurerは、次のテキスト・セグメントを開始するための、テキスト内の位置を保持します。 最初は、この位置がテキストの始点です。 段落の方向は、双方向フォーマット規則に従って、全方向(左から右または右から左)に及びます。 段落から取得されたすべてのセグメントは、その段落と同じ方向になります。

    テキストのセグメントは、nextLayoutメソッドを呼び出すことで取得されます。このメソッドは、ラッピング幅に収まるテキストを表すTextLayoutを返します。 nextLayoutメソッドは、nextLayoutが返したレイアウトの終端に現在の位置を移動します。

    LineBreakMeasurerは、もっとも一般的に使用される次のような改行ポリシーを実装します。ラッピング幅に収まるすべての単語は、同じ行に配置されます。 最初の単語が収まらなければ、ラッピング幅に収まるだけの文字がその行に配置されます。 各行には少なくとも1文字が配置されます。

    LineBreakMeasurerによって返されるTextLayoutのインスタンスは、タブを幅0のスペースと同様に扱います。 位置決めのためにタブ区切りのセグメントを取得するクライアントは、テキストに対するリミット・オフセットをとるnextLayoutのオーバーロードを使うようにしてください。 リミット・オフセットは、タブ以降の最初の文字です。 このメソッドが返すTextLayoutオブジェクトは、指定されたリミット(現在の位置とリミットとの間のテキスト全体がラッピング幅に収まらない場合には、リミットの前)で終わります。

    タブ区切りのテキストをレイアウトするクライアントには、最初のセグメントを行に配置したあと、やや異なる改行ポリシーが必要です。 残りの領域に一部の単語を収めるのではなく、全体を次の行に配置します。 ポリシーのこの変更は、booleanパラメータをとるnextLayoutのオーバーロードで要求できます。 このパラメータがtrueの場合、nextLayoutは、最初の単語が指定された領域に収まらないときにnullを返します。 下記のタブ・サンプルを参照してください。

    通常、LineBreakMeasurerの作成に使用されたテキストが変更された場合は、変更を反映するために新しいLineBreakMeasurerを作成する必要があります。 (これまでのLineBreakMeasurerはそのまま正常に動作するが、テキストの変更には対応しない。) ただし、テキストの変更が1文字の挿入または削除の場合には、insertCharまたはdeleteCharを呼び出して、既存のLineBreakMeasurerを「更新」してもかまいません。 既存のLineBreakMeasurerを更新する方が、新しく作成するよりも処理時間がかかりません。 ユーザーのキー入力によってテキストを変更する場合は、これらの方法を利用するとよいでしょう。

    :

    コンポーネントに段落を描画します。

    
     public void paint(Graphics graphics) {
    
         float dx = 0f, dy = 5f;
         Graphics2D g2d = (Graphics2D)graphics;
         FontRenderContext frc = g2d.getFontRenderContext();
    
         AttributedString text = new AttributedString(".....");
         AttributedCharacterIterator paragraph = text.getIterator();
    
         LineBreakMeasurer measurer = new LineBreakMeasurer(paragraph, frc);
         measurer.setPosition(paragraph.getBeginIndex());
         float wrappingWidth = (float)getSize().width;
    
         while (measurer.getPosition() < paragraph.getEndIndex()) {
    
             TextLayout layout = measurer.nextLayout(wrappingWidth);
    
             dy += (layout.getAscent());
             float dx = layout.isLeftToRight() ?
                 0 : (wrappingWidth - layout.getAdvance());
    
             layout.draw(graphics, dx, dy);
             dy += layout.getDescent() + layout.getLeading();
         }
     }
     

    タブ付きのテキストを描画します。 わかりやすくするため、テキストの方向はすべて左から右とします。

    
     public void paint(Graphics graphics) {
    
         float leftMargin = 10, rightMargin = 310;
         float[] tabStops = { 100, 250 };
    
         // assume styledText is an AttributedCharacterIterator, and the number
         // of tabs in styledText is tabCount
    
         int[] tabLocations = new int[tabCount+1];
    
         int i = 0;
         for (char c = styledText.first(); c != styledText.DONE; c = styledText.next()) {
             if (c == '\t') {
                 tabLocations[i++] = styledText.getIndex();
             }
         }
         tabLocations[tabCount] = styledText.getEndIndex() - 1;
    
         // Now tabLocations has an entry for every tab's offset in
         // the text.  For convenience, the last entry is tabLocations
         // is the offset of the last character in the text.
    
         LineBreakMeasurer measurer = new LineBreakMeasurer(styledText);
         int currentTab = 0;
         float verticalPos = 20;
    
         while (measurer.getPosition() < styledText.getEndIndex()) {
    
             // Lay out and draw each line.  All segments on a line
             // must be computed before any drawing can occur, since
             // we must know the largest ascent on the line.
             // TextLayouts are computed and stored in a Vector;
             // their horizontal positions are stored in a parallel
             // Vector.
    
             // lineContainsText is true after first segment is drawn
             boolean lineContainsText = false;
             boolean lineComplete = false;
             float maxAscent = 0, maxDescent = 0;
             float horizontalPos = leftMargin;
             Vector layouts = new Vector(1);
             Vector penPositions = new Vector(1);
    
             while (!lineComplete) {
                 float wrappingWidth = rightMargin - horizontalPos;
                 TextLayout layout =
                         measurer.nextLayout(wrappingWidth,
                                             tabLocations[currentTab]+1,
                                             lineContainsText);
    
                 // layout can be null if lineContainsText is true
                 if (layout != null) {
                     layouts.addElement(layout);
                     penPositions.addElement(new Float(horizontalPos));
                     horizontalPos += layout.getAdvance();
                     maxAscent = Math.max(maxAscent, layout.getAscent());
                     maxDescent = Math.max(maxDescent,
                         layout.getDescent() + layout.getLeading());
                 } else {
                     lineComplete = true;
                 }
    
                 lineContainsText = true;
    
                 if (measurer.getPosition() == tabLocations[currentTab]+1) {
                     currentTab++;
                 }
    
                 if (measurer.getPosition() == styledText.getEndIndex())
                     lineComplete = true;
                 else if (horizontalPos >= tabStops[tabStops.length-1])
                     lineComplete = true;
    
                 if (!lineComplete) {
                     // move to next tab stop
                     int j;
                     for (j=0; horizontalPos >= tabStops[j]; j++) {}
                     horizontalPos = tabStops[j];
                 }
             }
    
             verticalPos += maxAscent;
    
             Enumeration layoutEnum = layouts.elements();
             Enumeration positionEnum = penPositions.elements();
    
             // now iterate through layouts and draw them
             while (layoutEnum.hasMoreElements()) {
                 TextLayout nextLayout = (TextLayout) layoutEnum.nextElement();
                 Float nextPosition = (Float) positionEnum.nextElement();
                 nextLayout.draw(graphics, nextPosition.floatValue(), verticalPos);
             }
    
             verticalPos += maxDescent;
         }
     }
     

    関連項目:
    TextLayout
    • コンストラクタの詳細

      • LineBreakMeasurer

        public LineBreakMeasurer​(AttributedCharacterIterator text,
                                 FontRenderContext frc)
        指定されたテキストに対するLineBreakMeasurerを構築します。
        パラメータ:
        text - このLineBreakMeasurerTextLayoutオブジェクトの生成対象とするテキスト。このテキストには、1つ以上の文字が含まれていなければならない。iterで得られるテキストが変更された場合、その後のこのLineBreakMeasurerのインスタンスへの呼出しの結果は保証されない(ただし、あとでinsertCharまたはdeleteCharを呼び出す場合を除く。関連項目を参照)
        frc - テキストを正確に測定するために必要なグラフィックス・デバイスに関する情報を格納する。テキスト測定は、デバイスの解像度によりわずかに異なり、アンチエイリアスなどの属性によっても異なる。このパラメータは、LineBreakMeasurerとユーザー空間の間の変換は指定しない。
        関連項目:
        insertChar(java.text.AttributedCharacterIterator, int), deleteChar(java.text.AttributedCharacterIterator, int)
      • LineBreakMeasurer

        public LineBreakMeasurer​(AttributedCharacterIterator text,
                                 BreakIterator breakIter,
                                 FontRenderContext frc)
        指定されたテキストに対するLineBreakMeasurerを構築します。
        パラメータ:
        text - このLineBreakMeasurerTextLayoutオブジェクトの生成対象とするテキスト。このテキストには、1つ以上の文字が含まれていなければならない。iterで得られるテキストが変更された場合、その後のこのLineBreakMeasurerのインスタンスへの呼出しの結果は保証されない(ただし、あとでinsertCharまたはdeleteCharを呼び出す場合を除く。関連項目を参照)
        breakIter - 改行を定義するBreakIterator
        frc - テキストを正確に測定するために必要なグラフィックス・デバイスに関する情報を格納する。テキスト測定は、デバイスの解像度によりわずかに異なり、アンチエイリアスなどの属性によっても異なる。このパラメータは、LineBreakMeasurerとユーザー空間の間の変換は指定しない。
        例外:
        IllegalArgumentException - テキストが1文字に満たない場合
        関連項目:
        insertChar(java.text.AttributedCharacterIterator, int), deleteChar(java.text.AttributedCharacterIterator, int)
    • メソッドの詳細

      • nextOffset

        public int nextOffset​(float wrappingWidth)
        次のレイアウトの最後の位置を返します。 LineBreakMeasurerの現在の位置を更新しません。
        パラメータ:
        wrappingWidth - 次のレイアウト内のテキストに許容される最大の可視有効幅
        戻り値:
        次のTextLayoutのリミットを表す、テキスト内のオフセット。
      • nextOffset

        public int nextOffset​(float wrappingWidth,
                              int offsetLimit,
                              boolean requireNextWord)
        次のレイアウトの最後の位置を返します。 LineBreakMeasurerの現在の位置を更新しません。
        パラメータ:
        wrappingWidth - 次のレイアウト内のテキストに許容される最大の可視有効幅
        offsetLimit - リミット以降のテキストがラッピング幅に収まる場合でも、次のレイアウトに含まれない最初の文字。offsetLimitは、現在の位置よりも大きくなければならない。
        requireNextWord - trueの場合、次の単語全体がwrappingWidthに収まらないときは現在の位置が返される。falseの場合、返されるオフセットは現在の位置よりも少なくとも1大きい
        戻り値:
        次のTextLayoutのリミットを表す、テキスト内のオフセット
      • nextLayout

        public TextLayout nextLayout​(float wrappingWidth)
        次のレイアウトを返し、現在の位置を更新します。
        パラメータ:
        wrappingWidth - 次のレイアウト内のテキストに許容される最大の可視有効幅
        戻り値:
        wrappingWidthに収まる次の行を表し、現在の位置から始まるTextLayout
      • nextLayout

        public TextLayout nextLayout​(float wrappingWidth,
                                     int offsetLimit,
                                     boolean requireNextWord)
        次のレイアウトを返し、現在の位置を更新します。
        パラメータ:
        wrappingWidth - 次のレイアウト内のテキストに許容される最大の可視有効幅
        offsetLimit - リミット以降のテキストがラッピング幅に収まる場合でも、次のレイアウトに含まれない最初の文字。offsetLimitは、現在の位置よりも大きくなければならない。
        requireNextWord - trueの場合、現在の位置にある単語全体がラッピング幅に収まらないときはnullが返される。 falseの場合、少なくとも現在の位置にある文字を含む、有効なレイアウトが返される
        戻り値:
        wrappingWidthに収まる次の行を表し、現在の位置から始まるTextLayout 現在の位置が、このLineBreakMeasurerの使用するテキストの終端にある場合、nullが返される
      • getPosition

        public int getPosition()
        このLineBreakMeasurerの現在の位置を返します。
        戻り値:
        このLineBreakMeasurerの現在の位置
        関連項目:
        setPosition(int)
      • setPosition

        public void setPosition​(int newPosition)
        LineBreakMeasurerの現在の位置を設定します。
        パラメータ:
        newPosition - このLineBreakMeasurerの現在の位置。この位置は、このLineBreakMeasurerを作成するために使用されるテキスト(またはinsertChardeleteCharに最後に渡されたテキスト)内にある
        関連項目:
        getPosition()
      • insertChar

        public void insertChar​(AttributedCharacterIterator newParagraph,
                               int insertPos)
        テキストに文字が1つ挿入されたあとにLineBreakMeasurerを更新して、現在の位置をその段落の先頭に設定します。
        パラメータ:
        newParagraph - 挿入後のテキスト
        insertPos - テキスト内の、文字が挿入された位置
        例外:
        IndexOutOfBoundsException - insertPosnewParagraphの開始位置より前、またはnewParagraphの終了位置と同じか、それより後ろである場合
        NullPointerException - newParagraphnullである場合
        関連項目:
        deleteChar(java.text.AttributedCharacterIterator, int)
      • deleteChar

        public void deleteChar​(AttributedCharacterIterator newParagraph,
                               int deletePos)
        テキストから文字が1つ削除されたあとにLineBreakMeasurerを更新して、現在の位置をその段落の先頭に設定します。
        パラメータ:
        newParagraph - 削除後のテキスト
        deletePos - テキスト内の、文字が削除された位置
        例外:
        IndexOutOfBoundsException - deletePosnewParagraphの開始位置より前、またはnewParagraphの終了位置より後ろである場合
        NullPointerException - newParagraphnullである場合
        関連項目:
        insertChar(java.text.AttributedCharacterIterator, int)