Products Technologies Demo Docs Blog Support Company

Converting Paragraphs to Styles by Comparing Formatting Attributes

This example shows how to identify paragraphs that are commonly formatted, and then convert them to style sheets. The example checks for character and paragraph formatting and compares them using Reflection.

Converting Paragraphs to Styles by Comparing Formatting Attributes

A smart way to maintain the same look and feel throughout a document is to use stylesheets in professional documents. TX Text Control provides a rich set of features for using styles in your documents.

Another use case is when you receive a document that does not have any styles, but does have formatted text. In these scenarios, it may be helpful to convert the existing paragraphs that have common formatting to styles.

The Sample Application

Consider the document shown in the following screenshot. The paragraphs are formatted. However, as shown, no styles are applied.

Styles

The sample contains the StyleManager class, which parses the paragraphs for common paragraph and character formatting. If a new format is detected, a new style will be created and applied to the document. If formatting is detected that already exists as a style, that style is applied.

The following screenshot shows the same document with style sheets created for each of the unique paragraph styles.

Styles

These styles can then be applied to other paragraphs in the document to maintain a consistent layout throughout the document.

StyleManager Class

For your reference, the complete code of the StyleManager class is shown below. We will discuss its specific functions in more detail later on. If you want to use this code for ASP.NET (Core), you can easily replace TextControl with ServerTextControl in the code.

using System;
using System.Diagnostics;
using System.Linq;
using TXTextControl;

// **************************************************************
// *                                                            *
// *  This example demonstrates how to extract styles from      *
// *  paragraphs and how to apply them to other paragraphs.     *
// *                                                            *
// **************************************************************

public class StyleManager
{
        // The TextControl instance to work with
        private TextControl _textControl;
        // A list of properties to compare
        private string[] _positiveListParagraphFormat = { "Alignment", "ForeColor", "BottomDistance", "TopDistance", "LeftIndent", "RightIndent" };
        private string[] _positiveListCharacterFormat = { "Bold", "Italic", "FontName", "FontSize", "ForeColor", "Strikeout", "Underline" };

        // A counter to create unique style names
        private int styleNameCounter;

        public string[] PositiveListParagraphFormat { get => _positiveListParagraphFormat; set => _positiveListParagraphFormat = value; }
        public string[] PositiveListCharacterFormat { get => _positiveListCharacterFormat; set => _positiveListCharacterFormat = value; }

        // The constructor requires a TextControl instance
        public StyleManager(TextControl control)
        {
                _textControl = control;
                styleNameCounter = 1;
        }

        // This method applies the styles to all paragraphs
        public void ApplyStyles()
        {
                foreach (Paragraph paragraph in _textControl.Paragraphs)
                {
                        if (IsCommonFormatting(paragraph))
                        {
                                var style = FindOrCreateStyle(paragraph);
                                paragraph.FormattingStyle = style.Name;
                        }
                }
        }

        // This method creates a new style or returns an existing one
        private ParagraphStyle FindOrCreateStyle(Paragraph paragraph)
        {
                var existingStyle = FindStyle(paragraph);
                if (existingStyle == null)
                {
                        existingStyle = CreateStyle(paragraph);
                }
                return existingStyle;
        }

        // This method searches for an existing style
        private ParagraphStyle FindStyle(Paragraph paragraph)
        {
                foreach (ParagraphStyle paragraphStyle in _textControl.ParagraphStyles)
                {
                        // compare paragraph format
                        if (IsParagraphFormatEqual(paragraph.Format, paragraphStyle.ParagraphFormat))
                        {
                                // compare character format
                                if (IsCharacterFormatEqual(paragraph, paragraphStyle))
                                {
                                        return paragraphStyle;
                                }
                        }
                }

                return null;
        }

        // This method creates a new style
        private ParagraphStyle CreateStyle(Paragraph paragraph)
        {
                ParagraphStyle paragraphStyle = new ParagraphStyle("Style" + styleNameCounter, "[Normal]");

                CopyCharacterFormat(paragraph, paragraphStyle);
                CopyParagraphFormat(paragraph.Format, paragraphStyle.ParagraphFormat);

                // add the style to the collection
                _textControl.ParagraphStyles.Add(paragraphStyle);
                styleNameCounter++; // increase the counter
                return paragraphStyle; // return the new style
        }

        // This method compares the character format of a paragraph with a style using Reflection
        private bool IsCharacterFormatEqual(Paragraph paragraph, ParagraphStyle paragraphStyle)
        {
                paragraph.Select();
                var selection = _textControl.Selection;

                foreach (var property in typeof(Selection).GetProperties())
                {
                        if (!_positiveListCharacterFormat.Contains(property.Name))
                        {
                                continue;
                        }

                        object value1 = property.GetValue(selection);
                        var paragraphStyleProperty = typeof(ParagraphStyle).GetProperty(property.Name)?.GetValue(paragraphStyle);

                        // compare the values
                        if (!object.Equals(value1, paragraphStyleProperty))
                        {
                                return false;
                        }
                }

                return true;
        }

        // This method compares the paragraph format of a paragraph with a style using Reflection
        private bool IsParagraphFormatEqual(ParagraphFormat format1, ParagraphFormat format2)
        {
                foreach (var property in typeof(ParagraphFormat).GetProperties())
                {
                        if (_positiveListParagraphFormat.Contains(property.Name))
                        {
                                object value1 = property.GetValue(format1);
                                object value2 = property.GetValue(format2);

                                if (!object.Equals(value1, value2))
                                {
                                        return false;
                                }
                        }
                }

                return true;
        }

        // This method copies the character format of a paragraph to a style using Reflection
        private void CopyCharacterFormat(Paragraph paragraph, ParagraphStyle style)
        {
                paragraph.Select();
                var selection = _textControl.Selection;
                var styleType = style.GetType();

                foreach (var property in styleType.GetProperties())
                {
                        var selectionProperty = typeof(Selection).GetProperty(property.Name);

                        if (selectionProperty != null && selectionProperty.PropertyType == property.PropertyType)
                        {
                                property.SetValue(style, selectionProperty.GetValue(selection));
                        }
                }
        }

        // This method copies the paragraph format of a paragraph to a style using Reflection
        private void CopyParagraphFormat(ParagraphFormat source, ParagraphFormat destination)
        {
                foreach (var property in typeof(ParagraphFormat).GetProperties())
                {
                        object value = property.GetValue(source);
                        if (value != null && _positiveListParagraphFormat.Contains(property.Name))
                        {
                                property.SetValue(destination, value);
                        }
                }
        }

        // This method checks if the paragraph has common formatting
        private bool IsCommonFormatting(Paragraph paragraph)
        {
                paragraph.Select();
                return _textControl.Selection.IsCommonValueSelected(Selection.Attribute.All);
        }

}

The use is very simple.

StyleManager styleManager = new StyleManager(textControl1);
styleManager.ApplyStyles();

If you only want to check for certain formatting properties, you can define them beforehand.

StyleManager styleManager = new StyleManager(textControl1);
styleManager.PositiveListCharacterFormat = new string[] { "Bold", "Italic", "FontName" };
styleManager.ApplyStyles();

Code in Detail

Basically, the algorithm loops through all the paragraphs and checks if the character formatting of the paragraphs is the same.

private bool IsCommonFormatting(Paragraph paragraph)
{
  paragraph.Select();
  return _textControl.Selection.IsCommonValueSelected(Selection.Attribute.All);
}

It then checks to see if it should create a new style or if the style already exists. The interesting methods are IsParagraphFormatEqual and IsCharacterFormatEqual, which compare the formatting properties of the current paragraph and the styles.

private bool IsCharacterFormatEqual(Paragraph paragraph, ParagraphStyle paragraphStyle)
{
  paragraph.Select();
  var selection = _textControl.Selection;

  foreach (var property in typeof(Selection).GetProperties())
  {
    if (!_positiveListCharacterFormat.Contains(property.Name))
    {
      continue;
    }

    object value1 = property.GetValue(selection);
    var paragraphStyleProperty = typeof(ParagraphStyle).GetProperty(property.Name)?.GetValue(paragraphStyle);

    // compare the values
    if (!object.Equals(value1, paragraphStyleProperty))
    {
      return false;
    }
  }

  return true;
}
private bool IsParagraphFormatEqual(ParagraphFormat format1, ParagraphFormat format2)
{
  foreach (var property in typeof(ParagraphFormat).GetProperties())
  {
    if (_positiveListParagraphFormat.Contains(property.Name))
    {
      object value1 = property.GetValue(format1);
      object value2 = property.GetValue(format2);

      if (!object.Equals(value1, value2))
      {
        return false;
      }
    }
  }

  return true;
}

The formatting properties are copied to the newly created style using the CopyCharacterFormat and CopyParagraphFormat methods. As an example, the code of the CopyParagraphFormat method is shown here:

private void CopyParagraphFormat(ParagraphFormat source, ParagraphFormat destination)
{
  foreach (var property in typeof(ParagraphFormat).GetProperties())
  {
    object value = property.GetValue(source);
    if (value != null && _positiveListParagraphFormat.Contains(property.Name))
    {
      property.SetValue(destination, value);
    }
  }
}

To avoid setting each property name individually, all methods that compare formatting properties or apply properties use Reflection.

Sample Application

The sample application is available for download from our GitHub repository.

Stay in the loop!

Subscribe to the newsletter to receive the latest updates.

GitHub

Download and Fork This Sample on GitHub

We proudly host our sample code on github.com/TextControl.

Please fork and contribute.

Download ZIP

Open on GitHub

Open in Visual Studio

Requirements for this sample

  • TX Text Control .NET for Windows Forms 32.0
  • Visual Studio 2022

Related Posts

ASP.NETWindows FormsWPF

Sneak Peek 32.0: Modifying the Normal Stylesheet

Stylesheets in TX Text Control provide a powerful tool for maintaining consistency and ensuring that documents look and feel professional. In version 32.0 it will be possible to manipulate the…


ASP.NETWindows FormsWPF

TX Text Control 33.0 SP3 is Now Available: What's New in the Latest Version

TX Text Control 33.0 Service Pack 3 is now available, offering important updates and bug fixes for all platforms. If you use TX Text Control in your document processing applications, this service…


ASP.NETWindows FormsWPF

TX Text Control 33.0 SP2 is Now Available: What's New in the Latest Version

TX Text Control 33.0 Service Pack 2 is now available, offering important updates and bug fixes for all platforms. If you use TX Text Control in your document processing applications, this service…


ASP.NETWindows FormsWPF

Document Lifecycle Optimization: Leveraging TX Text Control's Internal Format

Maintaining the integrity and functionality of documents throughout their lifecycle is paramount. TX Text Control provides a robust ecosystem that focuses on preserving documents in their internal…


ActiveXASP.NETWindows Forms

Expert Implementation Services for Legacy System Modernization

We are happy to officially announce our partnership with Quality Bytes, a specialized integration company with extensive experience in modernizing legacy systems with TX Text Control technologies.