고급 텍스트 서식 지정

WPF(Windows Presentation Foundation)는 애플리케이션에 텍스트를 포함하기 위한 강력한 API 세트를 제공합니다. TextBlock 등의 레이아웃 및 UI(사용자 인터페이스) API는 텍스트 프레젠테이션을 위한 가장 일반적인 범용 요소를 제공합니다. GlyphRunDrawing, FormattedText 등의 그리기 API는 서식 있는 텍스트를 그리기에 포함하기 위한 수단을 제공합니다. 고급 수준에서 WPF는 확장 가능한 텍스트 서식 지정 엔진을 제공하여 텍스트 저장소 관리, 텍스트 실행 서식 관리, 포함된 개체 관리 등과 같은 텍스트 프레젠테이션의 모든 측면을 제어합니다.

이 항목에서는 WPF 텍스트 서식 지정을 소개합니다. WPF 텍스트 서식 지정 엔진의 클라이언트 구현과 사용에 중점을 둡니다.

참고

이 문서 내의 모든 코드 예제는 고급 텍스트 서식 지정 샘플에 있습니다.

사전 요구 사항

이 항목에서는 텍스트 프레젠테이션에 사용된 상위 수준 API에 익숙하다고 가정합니다. 대부분의 사용자 시나리오는 이 항목에서 설명된 고급 텍스트 서식 지정 API가 필요하지 않습니다. 다양한 텍스트 API 소개는 WPF의 문서를 참조하세요.

고급 텍스트 서식 지정

WPF의 텍스트 레이아웃 및 UI 컨트롤은 애플리케이션에 서식 있는 텍스트를 쉽게 포함할 수 있도록 하는 서식 지정 속성을 제공합니다. 이러한 컨트롤은 텍스트의 표현을 처리하는 다양한 속성을 표시하며 서체, 크기 및 색을 포함합니다. 일반적인 상황에서 이러한 컨트롤은 애플리케이션에서 대부분의 텍스트 표현을 처리할 수 있습니다. 그러나 일부 고급 시나리오는 텍스트 표현 뿐만 아니라 텍스트 스토리지 컨트롤이 필요합니다. WPF는 이 목적으로 확장 가능한 텍스트 서식 지정 엔진을 제공합니다.

WPF에 있는 고급 텍스트 서식 지정 기능은 텍스트 서식 지정 엔진, 텍스트 저장소, 텍스트 실행, 서식 지정 속성으로 구성됩니다. 텍스트 서식 지정 엔진인 TextFormatter는 프레젠테이션에 사용할 텍스트 줄을 만듭니다. 이 작업을 하려면 줄 서식 지정 프로세스를 시작하고 텍스트 포맷터의 FormatLine을 호출합니다. 텍스트 포맷터는 저장소의 GetTextRun 메서드를 호출하여 텍스트 저장소에서 텍스트 실행을 검색합니다. 그런 다음, TextRun 개체는 텍스트 포맷터를 통해 TextLine 개체로 구성되고 검사나 표시를 위해 애플리케이션에 제공됩니다.

텍스트 포맷터 사용

TextFormatter는 WPF 텍스트 서식 지정 엔진이며 텍스트 줄의 서식 지정과 줄 바꿈을 위한 서비스를 제공합니다. 텍스트 포맷터는 다양한 텍스트 문자 서식과 단락 스타일을 처리할 수 있으며 국제 텍스트 레이아웃에 대한 지원을 포함합니다.

기존 텍스트 API와 달리 TextFormatter는 콜백 메서드 세트를 통해 텍스트 레이아웃 클라이언트와 상호 작용합니다. 구현에서 이러한 메서드를 제공 하기 위해 클라이언트 필요는 TextSource 클래스입니다. 다음 다이어그램에서는 클라이언트 애플리케이션과 TextFormatter 간의 텍스트 레이아웃 상호 작용을 보여줍니다.

텍스트 레이아웃 클라이언트 및 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”와 같은 유니코드 줄 바꿈 시퀀스가 반환됩니다.

텍스트 실행 제공

텍스트 서식 지정 프로세스의 핵심은 텍스트 포맷터와 텍스트 저장소 간의 상호 작용입니다. 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으로 제공됩니다. TextParagraphPropertiesTextAlignment, FlowDirection 등의 단락 포함 속성을 처리합니다. TextRunProperties는 전경 브러시, Typeface, 글꼴 크기 등 단락 내에서 텍스트 실행마다 다를 수 있는 속성입니다. 사용자 지정 단락 및 사용자 지정 텍스트 실행 속성 형식을 구현하려면 애플리케이션은 각각 TextParagraphPropertiesTextRunProperties에서 파생되는 클래스를 만들어야 합니다.

참고 항목