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.

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.

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 TX Text Control .NET Server for ASP.NET
TXTextControl Namespace
ServerTextControl Class
The ServerTextControl class implements a component that provide high-level text processing features for server-based applications.
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);
}
}
view raw test.cs hosted with ❤ by GitHub

The use is very simple.

StyleManager styleManager = new StyleManager(textControl1);
styleManager.ApplyStyles();
view raw test.cs hosted with ❤ by GitHub

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();
view raw test.cs hosted with ❤ by GitHub

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);
}
view raw test.cs hosted with ❤ by GitHub

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;
}
view raw test.cs hosted with ❤ by GitHub
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;
}
view raw test.cs hosted with ❤ by GitHub

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);
}
}
}
view raw test.cs hosted with ❤ by GitHub

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.