テキストの高度な書式設定

Windows Presentation Foundation (WPF) には、アプリケーションにテキストを含めるための信頼性の高い一連の APIs が用意されています。 TextBlock などのレイアウトおよびuser interface (UI) APIs は、テキスト表示において最も共通して一般的に使用される要素を提供します。 GlyphRunDrawingFormattedText などの描画 APIs では、書式設定されたテキストを描画に含めることができます。 最も高度なレベルでは、WPF により、テキスト ストアの管理、テキスト ランの書式設定の管理、埋め込みオブジェクトの管理などの、テキスト表示のあらゆる面の制御を行う拡張性のあるテキスト書式設定エンジンが提供されます。

ここでは、WPF におけるテキスト書式設定の概要を説明します。 特に、クライアントの実装と WPF のテキスト書式設定エンジンの使用を重点的に説明します。

メモメモ

このドキュメント内のすべてのコード例は、テキストの高度な書式設定のサンプルにあります。

このトピックは、次のセクションで構成されています。

  • 必要条件
  • テキストの高度な書式設定
  • テキスト フォーマッタの使用
  • クライアント テキスト ストアの実装
  • テキスト ランの提供
  • 書式設定プロパティの指定
  • 関連トピック

必要条件

ここでは、テキスト表示に使用する高いレベルの APIs に精通していることを前提として説明を進めます。 ほとんどのユーザーの場合、ここで説明する APIs の高度なテキスト書式設定は必要ありません。 別のテキスト APIs の概要については、「WPF のドキュメント」を参照してください。

テキストの高度な書式設定

WPF のテキスト レイアウトおよび UI コントロールは、アプリケーションに書式設定されたテキストを簡単に含めることができる書式設定プロパティを提供します。 これらのコントロールは、テキストの書体、サイズ、色などのテキスト表示を処理するためのさまざまなプロパティを公開します。 通常の場合は、これらのコントロールでアプリケーション内のテキスト表示の大半を処理できます。 ただし、一部の高度な処理の場合、テキスト表示に加えてテキスト保存のコントロールも必要となります。 WPF では拡張可能なテキスト書式設定エンジンを使用できます。

WPF の高度なテキスト書式設定機能は、テキスト書式設定エンジン、テキスト ストア、テキスト ラン、および書式設定プロパティで構成されています。 テキスト書式設定エンジンである TextFormatter により、表示用のテキスト行が作成されます。 これは、行書式設定プロセスを開始し、テキスト フォーマッタの FormatLine を呼び出すことにより実行されます。 テキスト フォーマッタは、テキスト ストアの GetTextRun メソッドを呼び出すことにより、そのストアからテキスト ランを取得します。 次に、テキスト フォーマッタにより TextRun オブジェクトから TextLine オブジェクトが作成され、検査または表示用にアプリケーションに渡されます。

テキスト フォーマッタの使用

TextFormatter は WPF のテキスト書式設定エンジンであり、テキスト行の書式指定および改行のサービスを提供します。 テキスト フォーマッタは異なるテキスト文字形式および段落スタイルを処理でき、国際的なテキスト レイアウトがサポートされています。

従来のテキスト API とは異なり、TextFormatter は一連のコールバック メソッドを使用して、テキスト レイアウト クライアントと対話します。 このテキスト フォーマッタを使用するには、クライアントが TextSource クラスの実装にこれらのメソッドを提供している必要があります。 クライアント アプリケーションと TextFormatter の間のテキスト レイアウトの対話を次の図に示します。

アプリケーションと TextFormatter の対話

テキスト レイアウト クライアントと TextFormatter のダイアグラム

テキスト フォーマッタは、TextSource の実装であるテキスト ストアから書式設定されたテキスト行を取得するために使用されます。 これは、最初に Create メソッドを使用してテキスト フォーマッタのインスタンスを作成することにより実行されます。 このメソッドによりテキスト フォーマッタのインスタンスが作成され、行の最大高と最大幅が設定されます。 テキスト フォーマッタのインスタンスが作成されると直ちに、FormatLine メソッドを呼び出すことにより、行作成プロセスが開始されます。 TextFormatter によりテキストのソースが呼び戻され、行を形成する一続きのテキストの、テキストと書式設定パラメーターが取得されます。

次の例では、テキスト ストアを書式設定するプロセスを示します。 TextFormatter オブジェクトを使用してテキスト ストアからテキスト行が取得され、DrawingContext に描画するテキスト行が書式設定されます。

         ' Create a DrawingGroup object for storing formatted text.
         textDest = New DrawingGroup()
         Dim dc As DrawingContext = textDest.Open()

         ' Update the text store.
         _textStore.Text = textToFormat.Text
         _textStore.FontRendering = _currentRendering

         ' Create a TextFormatter object.
         Dim formatter As TextFormatter = TextFormatter.Create()

         ' Format each line of text from the text store and draw it.
         Do While textStorePosition < _textStore.Text.Length
            ' Create a textline from the text store using the TextFormatter object.
            Using myTextLine As TextLine = formatter.FormatLine(_textStore, textStorePosition, 96*6, New GenericTextParagraphProperties(_currentRendering), Nothing)
                ' Draw the formatted text into the drawing context.
                myTextLine.Draw(dc, linePosition, InvertAxes.None)

                ' Update the index position in the text store.
                textStorePosition += myTextLine.Length

                ' Update the line position coordinate for the displayed line.
                linePosition.Y += myTextLine.Height
            End Using
         Loop

         ' Persist the drawn text content.
         dc.Close()

         ' Display the formatted text in the DrawingGroup object.
         myDrawingBrush.Drawing = textDest
// Create a DrawingGroup object for storing formatted text.
textDest = new DrawingGroup();
DrawingContext dc = textDest.Open();

// Update the text store.
_textStore.Text = textToFormat.Text;
_textStore.FontRendering = _currentRendering;

// Create a TextFormatter object.
TextFormatter formatter = TextFormatter.Create();

// Format each line of text from the text store and draw it.
while (textStorePosition < _textStore.Text.Length)
{
   // Create a textline from the text store using the TextFormatter object.
   using (TextLine myTextLine = formatter.FormatLine(
       _textStore,
       textStorePosition,
       96*6,
       new GenericTextParagraphProperties(_currentRendering),
       null))
   {
       // Draw the formatted text into the drawing context.
       myTextLine.Draw(dc, linePosition, InvertAxes.None);

       // Update the index position in the text store.
       textStorePosition += myTextLine.Length;

       // Update the line position coordinate for the displayed line.
       linePosition.Y += myTextLine.Height;
   }
}

// Persist the drawn text content.
dc.Close();

// Display the formatted text in the DrawingGroup object.
myDrawingBrush.Drawing = textDest;

クライアント テキスト ストアの実装

テキスト書式設定エンジンを拡張する場合は、テキスト ストアをすべての面で実装および管理する必要があります。 これは単純な作業ではありません。 テキスト ストアの役割は、テキスト ランのプロパティ、段落プロパティ、埋め込みオブジェクト、およびその他の類似したコンテンツを追跡することです。 また、テキスト ストアは、TextLine オブジェクトを作成するときにテキスト フォーマッタが使用する、個々の TextRun オブジェクトをテキスト フォーマッタに提供します。

テキスト ストアの仮想化を処理する場合は、テキスト ストアが TextSource から派生している必要があります。 TextSource では、テキスト フォーマッタがテキスト ストアからテキスト ランを取得するために使用するメソッドが定義されています。 GetTextRun は、行の書式設定に使用されるテキスト ランを取得するためにテキスト フォーマッタが使用するメソッドです。 次のいずれかの条件が発生するまで、GetTextRun はテキスト フォーマッタによって繰り返し呼び出されます。

  • TextEndOfLine またはサブクラスが返された場合。

  • テキスト ランの累積幅が、テキスト フォーマッタを作成する呼び出し、またはテキスト フォーマッタの FormatLine メソッドのいずれかに指定されている行の最大幅を超えた場合。

  • "CF"、"LF"、または "CRLF" などの Unicode 改行シーケンスが返された場合。

テキスト ランの提供

テキスト フォーマッタとテキスト ストアの間の対話は、テキスト書式設定プロセスにおける中核となります。 TextSource を実装することにより、テキスト フォーマッタに TextRun オブジェクトおよびテキスト ランの書式設定に使用されるプロパティが提供されます。 この対話は、テキスト フォーマッタによって呼び出される GetTextRun メソッドによって処理されます。

定義済みの TextRun オブジェクトの一部を次の表に示します。

TextRun 型

使用法

TextCharacters

テキスト フォーマッタに文字グリフの表示を戻すために使用される、特殊なテキスト ランです。

TextEmbeddedObject

ボタン、テキスト内の画像などの、測定、ヒット テスト、描画がすべて実行されるコンテンツを提供するために使用される、特殊なテキスト ランです。

TextEndOfLine

行の終わりを示すために使用される、特殊なテキスト ランです。

TextEndOfParagraph

段落の終わりを示すために使用される、特殊なテキスト ランです。

TextEndOfSegment

前の TextModifier 実行の影響を受けるスコープを終了する場合のように、セグメントの終わりを示すために使用される特殊なテキスト ランです。

TextHidden

隠し文字の範囲を示すために使用される特殊なテキスト ランです。

TextModifier

スコープ内のテキスト ランのプロパティを変更するために使用される、特殊なテキスト ランです。 この範囲は、次に一致する TextEndOfSegment のテキスト ラン、または次の TextEndOfParagraph まで拡張されます。

定義済みの TextRun オブジェクトは、すべてサブクラス化できます。 これにより、カスタム データを含むテキスト ランをテキスト ソースからテキスト フォーマッタに提供できます。

GetTextRun メソッドの例を次に示します。 このテキスト ストアは、次のテキスト フォーマッタに TextRun オブジェクトを返して処理を行います。

      ' Used by the TextFormatter object to retrieve a run of text from the text source.
      Public Overrides Function GetTextRun(ByVal textSourceCharacterIndex As Integer) As TextRun
         ' Make sure text source index is in bounds.
         If textSourceCharacterIndex < 0 Then
            Throw New ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.")
         End If
         If textSourceCharacterIndex >= _text.Length Then
            Return New TextEndOfParagraph(1)
         End If

         ' Create TextCharacters using the current font rendering properties.
         If textSourceCharacterIndex < _text.Length Then
            Return New TextCharacters(_text, textSourceCharacterIndex, _text.Length - textSourceCharacterIndex, New GenericTextRunProperties(_currentRendering))
         End If

         ' Return an end-of-paragraph if no more text source.
         Return New TextEndOfParagraph(1)
      End Function
// Used by the TextFormatter object to retrieve a run of text from the text source.
public override TextRun GetTextRun(int textSourceCharacterIndex)
{
   // Make sure text source index is in bounds.
   if (textSourceCharacterIndex < 0)
      throw new ArgumentOutOfRangeException("textSourceCharacterIndex", "Value must be greater than 0.");
   if (textSourceCharacterIndex >= _text.Length)
   {
      return new TextEndOfParagraph(1);
   }

   // Create TextCharacters using the current font rendering properties.
   if (textSourceCharacterIndex < _text.Length)
   {
      return new TextCharacters(
         _text,
         textSourceCharacterIndex,
         _text.Length - textSourceCharacterIndex,
         new GenericTextRunProperties(_currentRendering));
   }

   // Return an end-of-paragraph if no more text source.
   return new TextEndOfParagraph(1);
}
メモメモ

この例では、テキスト ストアによりすべてのテキストに対して同じテキスト プロパティが提供されます。高度なテキスト ストアの場合、個々の文字に異なるプロパティを持たせるために、それぞれのテキスト ストア の範囲管理を実装する必要があります。

書式設定プロパティの指定

TextRun オブジェクトは、テキスト ストアによって提供されるプロパティを使用して書式設定されます。 このようなプロパティには、TextParagraphPropertiesTextRunProperties の 2 つの型があります。 TextParagraphProperties は、TextAlignmentFlowDirection などの、段落の包括的なプロパティを処理します。 TextRunProperties は、前景ブラシ、Typeface、およびフォント サイズなどの、段落内のそれぞれのテキスト ランで異なるプロパティです。 カスタム段落プロパティ型およびカスタム テキスト ラン プロパティ型を実装するには、アプリケーションでそれぞれ TextParagraphProperties および TextRunProperties から派生するクラスを作成する必要があります。

参照

概念

WPF のタイポグラフィ

WPF のドキュメント