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

Windows Presentation Foundation (WPF) では、アプリケーションにテキストを含めるための堅牢な API のセットが提供されています。 レイアウトと TextBlock のようなユーザー インターフェイス (UI) API により、テキスト表示のための最も一般的な要素が提供されます。 GlyphRunDrawingFormattedText のような描画 API は、描画に書式設定されたテキストを追加する手段です。 最も高度なレベルでは、WPF によって提供される拡張テキスト書式設定エンジンにより、テキスト保管管理、テキスト ラン書式設定管理、埋め込みオブジェクト管理など、テキスト表示のあらゆる側面が制御されます。

このトピックでは、WPF でのテキスト書式設定の概要について説明します。 WPF のテキスト書式設定エンジンのクライアントでの実装と使用について主に取り上げます。

注意

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

必須コンポーネント

このトピックは、テキスト表示に使用されるレベルの高い API に関する知識があることを前提に書かれています。 ほとんどのユーザー シナリオでは、このトピックにあるようなテキストの高度な書式設定 API は必要ありません。 他のテキスト API の概要については、「WPF のドキュメント」を参照してください。

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

WPF のテキスト レイアウトと UI コントロールによって提供される書式設定プロパティを使用すると、書式設定されたテキストをアプリケーションに簡単に追加できます。 これらのコントロールでは、テキストの表示を処理するさまざまなプロパティが公開されます。書体、大きさ、色などです。 通常の状況では、これらのコントロールはアプリケーションの大半のテキスト表示を処理できます。 しかしながら、一部の高度なシナリオでは、テキスト表示と同様にテキスト保存を制御する必要があります。 WPF では、この目的のための拡張テキスト書式設定エンジンが提供されます。

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

テキスト フォーマッタを使用する

TextFormatter は WPF のテキスト書式設定エンジンであり、テキスト行を書式設定したり、改行したりするためのサービスを提供します。 テキスト フォーマッタは、さまざまなテキスト文字書式や段落スタイルを処理し、国際的なテキスト レイアウトに対応しています。

従来のテキスト API とは異なり、TextFormatter では、一連のコールバック メソッドにより、テキスト レイアウト クライアントとの対話が行われます。 クライアントでは、TextSource クラスの実装で、これらのメソッドを提供する必要があります。 次の図は、クライアント アプリケーションと TextFormatter の間で行われるテキスト レイアウトの相互作用を示したものです。

Diagram of text layout client and TextFormatter

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

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

// 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;
' 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

クライアント テキスト ストアを実装する

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

テキスト ストアの仮想化を処理するため、テキスト ストアは 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 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);
}
' 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

注意

この例では、テキスト ストアはすべてのテキストに同じテキスト プロパティを提供します。 高度なテキスト ストアでは、場合によっては、独自のスパン管理を実装し、個々の文字に異なるプロパティを与えるようにする必要があります。

書式設定プロパティを指定する

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

関連項目