Find Replace a Keyword with Font Properties in a Word DOCX Document using C# .NET | Apply Text Formatting to New Content

Hi Aspose Team,

-> We are facing a problem while replace words with font properties in word documents.

-> But the problem is, we do not want to apply the font properties to all the occurrence of the same word in the word document file.

-> If we are using a word called Recipient Name in a word document and it is there 2 times in the same document then we do not want to apply the font properties to the 2nd occurrence of the word.

-> We are only trying to find and replace the 1st occurrence of the word not the 2nd one.

Q:- How to achieve the above problem?

-> Could you please find the attached files for your reference where you can find the 3 files, one is Input file and another one is the Output file and another on is the Expected_Output file.
Example.zip (64.0 KB)

-> In the above Input doc file, you can fine Recipient Name word 2 times.
-> But we want to apply Bold only to the 1st Recipient Name not the 2nd one.

Problem:- It is applying the font Bold to the 2nd Recipient Name that we do not want.

-> Could you please help us on this ASAP. Our code is already there in Production. We are in a hurry situation.

Note:-

-> We are using Aspose.Words 11.5 dll for .Net

Thanks & Regards,
Santosh Kumar Panigrahi

Hi Tahir,

-> We are facing a problem while replace words with font properties in word documents.

-> But the problem is, we do not want to apply the font properties to all the occurrence of the same word in the word document file.

-> If we are using a word called Recipient Name in a word document and it is there 2 times in the same document then we do not want to apply the font properties to the 2nd occurrence of the word.

-> We are only trying to find and replace the 1st occurrence of the word not the 2nd one.

Q:- How to achieve the above problem?

-> Could you please find the attached files for your reference where you can find the 3 files , one is Input file and another one is the Output file and another on is the Expected_Output file .
Example.zip (64.0 KB)

-> In the above Input doc file, you can fine Recipient Name word 2 times.
-> But we want to apply Bold only to the 1st Recipient Name not the 2nd one.

Problem:- It is applying the font Bold to the 2nd Recipient Name that we do not want.

-> Could you please help us on this ASAP. Our code is already there in Production. We are in a hurry situation.

Note:-

-> We are using Aspose.Words 11.5 dll for .Net

Thanks & Regards,
Santosh Kumar Panigrahi

@santoshp1989,

You can meet this requirement by using the Find and Replace functionality of Aspose.Words for .NET:

Document doc = new Document("E:\\Temp\\Example\\Input.docx");

FindReplaceOptions options = new FindReplaceOptions();
options.ReplacingCallback = new FindAndReplace();
options.Direction = FindReplaceDirection.Forward;

doc.Range.Replace("Recipient Name", "", options);
doc.Save("E:\\Temp\\Example\\20.7.docx");

private class FindAndReplace : IReplacingCallback
{
    /// <summary>
    /// NOTE: This is a simplistic method that will only work well when the match
    /// starts at the beginning of a run.
    /// </summary>
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;

        // The first (and may be the only) run can contain text before the match,
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        // This array is used to store all nodes of the match for further removing.
        ArrayList runs = new ArrayList();

        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;
        while (
            (remainingLength > 0) &&
            (currentNode != null) &&
            (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            // Select the next Run node.
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }

        foreach (Run run in runs)
        {
            run.Font.Bold = true;
        }

        return ReplaceAction.Stop;
    }

    private static Run SplitRun(Run run, int position)
    {
        Run afterRun = (Run)run.Clone(true);
        afterRun.Text = run.Text.Substring(position);
        run.Text = run.Text.Substring((0), (0) + (position));
        run.ParentNode.InsertAfter(afterRun, run);
        return afterRun;
    }
}

Hi Tahir,

Thank you for your quick response.

The solution what you given is helped us but it is not working as expected.

Please find the attached files for your reference where you can find the Input file, Output file, Expected_Output file, csv file and Excel file.

CSV file:- Where we are getting the font information related each Merge Field.

Problem:- It is not working for all of the Merged Fields.

-> You can find the Merge Fields which are not working from the attached Excel document.

Please find the piece of code what we are using:-

public static void AsposeFindAndReplace()
{
    License licence = new License();
    licence.SetLicense("Aspose.Total.lic");

    string preprocessorCsvPath = @"D:\Multitask\INPUT\CSV\163_1007202095323426-94_preprocessor.csv";
    string delimiter = ";";
    DataTable dtpreprocessor = null;
    List<MatchWord> mergefieldsNew = null;

    if (File.Exists(preprocessorCsvPath))
    {
        dtpreprocessor = ConvertCSVtoDataTable(preprocessorCsvPath, delimiter);
        mergefieldsNew = CovertToPreprocessorList(dtpreprocessor);
    }

    Document doc = new Document(@"D:\Multitask\INPUT\Input.DOCX");

    foreach (MatchWord field in mergefieldsNew)
    {
        FindReplaceOptions options = new FindReplaceOptions();

        options.ApplyFont.Name = field.fontfamily;
        options.ApplyFont.Size = field.fontsize;
        options.ApplyFont.Color = System.Drawing.Color.Black;
        options.ApplyFont.Underline = Underline.None;
        options.ApplyFont.TextEffect = TextEffect.None;
        options.ApplyFont.UnderlineColor = System.Drawing.Color.Black;
        options.ApplyFont.StrikeThrough = false;
        options.ApplyFont.DoubleStrikeThrough = false;
        options.Direction = FindReplaceDirection.Forward;
        options.FindWholeWordsOnly = false;
        options.MatchCase = false;
        options.ApplyFont.Shadow = false;
        options.ApplyFont.Outline = false;
        options.ApplyFont.Emboss = false;
        options.ApplyFont.Engrave = false;
        options.ApplyFont.Hidden = false;
        options.ApplyFont.HighlightColor = System.Drawing.Color.White;
        options.ApplyFont.ComplexScript = false;
        options.ApplyFont.NoProofing = true;
        options.ApplyFont.Color = System.Drawing.Color.Black;
        options.ApplyFont.Underline = Underline.None;
        options.ApplyFont.TextEffect = TextEffect.None;
        options.ApplyFont.UnderlineColor = System.Drawing.Color.Black;
        options.ApplyFont.StrikeThrough = false;
        options.ApplyFont.DoubleStrikeThrough = false;

        // Find font style
        if (field.style == "Bold")
        {
            options.ApplyFont.Bold = true;
            options.ApplyFont.Italic = false;
        }
        else if (field.style == "Italic")
        {
            options.ApplyFont.Bold = false;
            options.ApplyFont.Italic = true;
        }
        else if (field.style == "BoldItalic")
        {
            options.ApplyFont.Bold = true;
            options.ApplyFont.Italic = true;
        }
        else
        {
            options.ApplyFont.Bold = false;
            options.ApplyFont.Italic = false;
        }

        options.ReplacingCallback = new FindAndReplace();

        doc.Range.Replace(field.MailmergeField, field.MailmergeField, options);
    }

    doc.Save(@"D:\Multitask\OUTPUT\Output.DOCX");
}

Thanks & Regards,
Santosh Kumar Panigrahi

Hi Tahir

Sorry, I have not send the attachment.

Please find the attachment.Example.zip (71.5 KB)

Thanks & Regards,
Santosh Kumar Panigrahi

@santoshp1989,

You can extract font formatting values from each Row in CSV or XLSX file and then pass them to the ReplacingCallback method. Please try the following code:

Document doc = new Document("E:\\Temp\\Example\\Input.docx");

FindReplaceOptions options = new FindReplaceOptions();

options.ReplacingCallback = new FindAndReplace("Times New Roman", 12, true);
doc.Range.Replace("Recipient Name", "", options);

options.ReplacingCallback = new FindAndReplace("Times New Roman", 12, false);
doc.Range.Replace("CODING_SRVC_DESC", "", options);

options.ReplacingCallback = new FindAndReplace("Times New Roman", 12, false);
doc.Range.Replace("RETURN_CITY", "", options);

doc.Save("E:\\Temp\\Example\\20.7.docx");

private class FindAndReplace : IReplacingCallback
{
    public string fontName;
    public double fontSize;
    public bool fontBold;

    public FindAndReplace(string fn, double fs, bool fb)
    {
        fontName = fn;
        fontSize = fs;
        fontBold = fb;
    }

    /// <summary>
    /// NOTE: This is a simplistic method that will only work well when the match
    /// starts at the beginning of a run.
    /// </summary>
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        // This is a Run node that contains either the beginning or the complete match.
        Node currentNode = e.MatchNode;

        // The first (and may be the only) run can contain text before the match,
        // in this case it is necessary to split the run.
        if (e.MatchOffset > 0)
            currentNode = SplitRun((Run)currentNode, e.MatchOffset);

        // This array is used to store all nodes of the match for further removing.
        ArrayList runs = new ArrayList();

        // Find all runs that contain parts of the match string.
        int remainingLength = e.Match.Value.Length;
        while (
            (remainingLength > 0) &&
            (currentNode != null) &&
            (currentNode.GetText().Length <= remainingLength))
        {
            runs.Add(currentNode);
            remainingLength = remainingLength - currentNode.GetText().Length;

            // Select the next Run node.
            // Have to loop because there could be other nodes such as BookmarkStart etc.
            do
            {
                currentNode = currentNode.NextSibling;
            }
            while ((currentNode != null) && (currentNode.NodeType != NodeType.Run));
        }

        // Split the last run that contains the match if there is any text left.
        if ((currentNode != null) && (remainingLength > 0))
        {
            SplitRun((Run)currentNode, remainingLength);
            runs.Add(currentNode);
        }

        foreach (Run run in runs)
        {
            run.Font.Name = fontName;
            run.Font.Size = fontSize;
            run.Font.Bold = fontBold;
        }

        return ReplaceAction.Stop;
    }

    private static Run SplitRun(Run run, int position)
    {
        Run afterRun = (Run)run.Clone(true);
        afterRun.Text = run.Text.Substring(position);
        run.Text = run.Text.Substring((0), (0) + (position));
        run.ParentNode.InsertAfter(afterRun, run);
        return afterRun;
    }
}

Hi Tahir,

Thank you for your quick response.
I am sorry but the above code also not working as Expected.

Problem:- The issue came like earlier. The first Recipient Name merge field is not getting Bold as it is Bold from csv file.

Please find the attached zip file for your reference.Example.zip (71.8 KB)

Doubt:- Why the FindAndReplace() method is having only three parameters as we are having many Font Formatting Values?

The below code what we used for your reference:-

public static void AsposeFindAndReplace()
{
    License licence = new License();
    licence.SetLicense("Aspose.Total.lic");

    string preprocessorCsvPath = @"D:\Multitask\INPUT\CSV\163_1007202095323426-94_preprocessor.csv";
    string delimiter = ";";
    DataTable dtpreprocessor = null;
    List<MatchWord> mergefieldsNew = null;

    if (File.Exists(preprocessorCsvPath))
    {
        dtpreprocessor = ConvertCSVtoDataTable(preprocessorCsvPath, delimiter);
        mergefieldsNew = CovertToPreprocessorList(dtpreprocessor);
    }

    Document doc = new Document(@"D:\Multitask\INPUT\AS.ASLAA001.C00C.AS20192AAAA0001.20200710.123654.DOCX");

    foreach (MatchWord field in mergefieldsNew)
    {
        FindReplaceOptions options = new FindReplaceOptions();

        options.ApplyFont.Name = field.fontfamily;
        options.ApplyFont.Size = field.fontsize;
        options.ApplyFont.Color = System.Drawing.Color.Black;
        options.ApplyFont.Underline = Underline.None;
        options.ApplyFont.TextEffect = TextEffect.None;
        options.ApplyFont.UnderlineColor = System.Drawing.Color.Black;
        options.ApplyFont.StrikeThrough = false;
        options.ApplyFont.DoubleStrikeThrough = false;
        options.Direction = FindReplaceDirection.Forward;
        options.FindWholeWordsOnly = false;
        options.MatchCase = false;
        options.ApplyFont.Shadow = false;
        options.ApplyFont.Outline = false;
        options.ApplyFont.Emboss = false;
        options.ApplyFont.Engrave = false;
        options.ApplyFont.Hidden = false;
        options.ApplyFont.HighlightColor = System.Drawing.Color.White;
        options.ApplyFont.ComplexScript = false;
        options.ApplyFont.NoProofing = true;
        options.ApplyFont.Color = System.Drawing.Color.Black;
        options.ApplyFont.Underline = Underline.None;
        options.ApplyFont.TextEffect = TextEffect.None;
        options.ApplyFont.UnderlineColor = System.Drawing.Color.Black;
        options.ApplyFont.StrikeThrough = false;
        options.ApplyFont.DoubleStrikeThrough = false;

        // Find font style
        if (field.style == "Bold")
        {
            options.ApplyFont.Bold = true;
            options.ApplyFont.Italic = false;
        }
        else if (field.style == "Italic")
        {
            options.ApplyFont.Bold = false;
            options.ApplyFont.Italic = true;
        }
        else if (field.style == "BoldItalic")
        {
            options.ApplyFont.Bold = true;
            options.ApplyFont.Italic = true;
        }
        else
        {
            options.ApplyFont.Bold = false;
            options.ApplyFont.Italic = false;
        }

        options.ReplacingCallback = new FindAndReplace(options.ApplyFont.Name, options.ApplyFont.Size, options.ApplyFont.Bold);

        doc.Range.Replace(field.MailmergeField, field.MailmergeField, options);
    }

    doc.Save(@"D:\Multitask\OUTPUT\AS.ASLAA001.C00C.AS20192AAAA0001.20200710.123654.DOCX");
}

Thanks & Regards,
Santosh Kumar Panigrahi

@santoshp1989,

Yes, I did pass three parameters in constructor just to give you an example as to how you can build logic on it further to meet your requirement. You can add more parameters or even use a single DataRow object and then inside the body extract Cell values and apply formatting to Run nodes.

We are also unable to run the code you shared in previous posts. We see compile time errors for “MatchWord”, “ConvertCSVtoDataTable”, “CovertToPreprocessorList” etc. Please also create a standalone simple Console application (source code without compilation errors) that helps us to reproduce your current problem on our end and attach it here for testing. Please do not include Aspose.Words DLL files in it to reduce the file size.

Hi Hafeez,

Could you please find the attached zip file where you can able to get the Source Code without compilation errors.

Problem:- Why the 1st Recipient Name Merge field is not getting Bold?

Please help us on the above issue. We are in a Critical Situation.

Thanks & Regards,
Santosh Kumar Panigrahi

Hi Hafeez,

I am very sorry, forgot to attch the zip file. Please find the below attachment for your reference.
ImagesAspose.zip (5.7 MB)

Thanks & Regards,
Santosh Kumar Panigrahi

@santoshp1989,

Please replace your “AsposeFindAndReplace” method with the following:

public static void AsposeFindAndReplace()
{
    License licence = new License();
    licence.SetLicense("Aspose.Total.lic");

    string preprocessorCsvPath = @"E:\Temp\Example\163_1007202095323426-94_preprocessor.csv";

    string delimiter = ";";
    DataTable dtpreprocessor = null;
    List<MatchWord> mergefieldsNew = null;

    if (File.Exists(preprocessorCsvPath))
    {
        dtpreprocessor = ConvertCSVtoDataTable(preprocessorCsvPath, delimiter);
        mergefieldsNew = CovertToPreprocessorList(dtpreprocessor);
    }

    Document doc = new Document(@"E:\Temp\Example\Input.DOCX");

    ArrayList list = new ArrayList();
    foreach (MatchWord field in mergefieldsNew)
    {
        FindReplaceOptions options = new FindReplaceOptions();

        options.ApplyFont.Name = field.fontfamily;
        options.ApplyFont.Size = field.fontsize;
        options.ApplyFont.Color = System.Drawing.Color.Black;
        options.ApplyFont.Underline = Underline.None;
        options.ApplyFont.TextEffect = TextEffect.None;
        options.ApplyFont.UnderlineColor = System.Drawing.Color.Black;
        options.ApplyFont.StrikeThrough = false;
        options.ApplyFont.DoubleStrikeThrough = false;
        options.Direction = FindReplaceDirection.Forward;
        options.FindWholeWordsOnly = false;
        options.MatchCase = false;
        options.ApplyFont.Shadow = false;
        options.ApplyFont.Outline = false;
        options.ApplyFont.Emboss = false;
        options.ApplyFont.Engrave = false;
        options.ApplyFont.Hidden = false;
        options.ApplyFont.HighlightColor = System.Drawing.Color.White;
        options.ApplyFont.ComplexScript = false;
        options.ApplyFont.NoProofing = true;
        options.ApplyFont.Color = System.Drawing.Color.Black;
        options.ApplyFont.Underline = Underline.None;
        options.ApplyFont.TextEffect = TextEffect.None;
        options.ApplyFont.UnderlineColor = System.Drawing.Color.Black;
        options.ApplyFont.StrikeThrough = false;
        options.ApplyFont.DoubleStrikeThrough = false;

        // Find font style
        if (field.style == "Bold")
        {
            options.ApplyFont.Bold = true;
            options.ApplyFont.Italic = false;
        }
        else if (field.style == "Italic")
        {
            options.ApplyFont.Bold = false;
            options.ApplyFont.Italic = true;
        }
        else if (field.style == "BoldItalic")
        {
            options.ApplyFont.Bold = true;
            options.ApplyFont.Italic = true;
        }
        else
        {
            options.ApplyFont.Bold = false;
            options.ApplyFont.Italic = false;
        }

        options.ReplacingCallback = new FindAndReplace();
        if (!list.Contains(field.MailmergeField))
        {
            list.Add(field.MailmergeField);
            doc.Range.Replace(field.MailmergeField, field.MailmergeField, options);
        }
    }

    doc.Save(@"E:\Temp\Example\20.7.DOCX");
} 

And also discard previous code and use the following FindAndReplace class:

public class FindAndReplace : IReplacingCallback
{
    public bool flag = true;
    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs e)
    {
        if (flag)
        {
            flag = false;
            return ReplaceAction.Replace;
        }
        else
        {
            return ReplaceAction.Stop;
        }
    }
}

Hope, this helps.

A post was split to a new topic: Capturing extracting content from a particular zone in PDF using C#

A post was split to a new topic: Rotate a Word DOCX Document from Landscape to Portrait Orientation using C# .NET