Find the particular heading and apply the number style to that

  1. I want to find the particular heading then I need to give the number style 2 for that. example : PURPOSE AND SCOPE
  2. if a particular heading is not present mean. we need to add the heading and give the number style 2.
  3. I want to find the particular heading and below that, I need to add the message and give the number style 3. example :PROCESS STATEMENTS
  4. if a particular heading is not present we need to add the heading and below that we need to add a message and also give the number style 3.

Please find the below Input and expected output word document.
Expected_output500.docx (18.0 KB)
Input word document500.docx (24.1 KB)

Hi @Manasahr
Thank you for your interest in Aspose products.

I hope I understood correctly what you were asking, here is some code that will work as a start up for what you need to achieve


public void MainCode()
{
    string partialPath = @"Headings\ChangingFormat"; // Location of your input/output files

    LoadLicence(); // Load your License

    var doc = DocumentGet(partialPath); // Load your document

    Logic(doc);

    doc.Save($"{partialPath}_output.docx");
}

private void Logic(Document doc)
{
    string headingToChangeStyleIdentifier = "PURPOSE AND SCOPE";
    string headingMissing = "HEADING NOT FOUND";
    bool isHeadingMissing = true;
    string headingToAddContentBelow = "PROCESS STATEMENTS";
    string headingMissingAgain = "ANOTHER HEADING NOT FOUND";
    bool isAnotherHeadingMissing = true;
    
    // To keep track of the paragraph with N/A and remove one of the double empty paragraphs
    Paragraph paragraphWithNA = null; 

    var list = doc.GetChildNodes(NodeType.Paragraph, true);

    foreach (Paragraph paragraph in list)
    {
        paragraph.JoinRunsWithSameFormatting();
        foreach (Run run in paragraph.Runs)
        {
            // Solves Problem 1
            if (run.Text.Equals(headingToChangeStyleIdentifier, StringComparison.InvariantCultureIgnoreCase))
            {
                paragraph.ListFormat.ListLevelNumber = 0; // this will break level of Heading2, if you do not want it remove this line.
                paragraph.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading2;
            }

            // Solves Problem 2
            if (run.Text.Equals(headingMissing, StringComparison.InvariantCultureIgnoreCase))
            {
                isHeadingMissing = false;
            }                    

            // Solves Problem 3
            if (run.Text.Equals(headingToAddContentBelow, StringComparison.InvariantCultureIgnoreCase))
            {
                string content = "While it may not be obvious to everyone, there are a number of reasons creating random paragraphs can be useful. A few examples of how some people use this generator are listed in the following paragraphs.";
                Paragraph newPar = new Paragraph(doc);
                newPar.ListFormat.RemoveNumbers(); // this will remove the numbers, if you want them remove this line.
                newPar.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading3;

                newPar.AppendChild(new Run(doc, content));

                paragraph.ParentNode.InsertAfter(newPar, paragraph);
            }

            // Solves Problem 4
            if (run.Text.Equals(headingMissingAgain, StringComparison.InvariantCultureIgnoreCase))
            {
                isAnotherHeadingMissing = false;
            }

            // This is to remove the empty paragraph after "N/A"
            if (run.Text.Equals("N/A", StringComparison.InvariantCultureIgnoreCase))
            {
                paragraphWithNA = paragraph;
            }
        }               
    }

    // Removes the double empty paragraph after "N/A", leaving just one
    paragraphWithNA.NextSibling.Remove();

    // Solves Problem 2
    if (isHeadingMissing)
    {
        Paragraph lastParagraph = (Paragraph)list.LastOrDefault();

        Paragraph newPar = new Paragraph(doc);                
        newPar.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading2;
        newPar.ListFormat.ListLevelNumber = 0;

        newPar.AppendChild(new Run(doc, headingMissing));

        lastParagraph.ParentNode.InsertAfter(newPar, lastParagraph);
    }

    // Solves Problem 4
    if (isAnotherHeadingMissing)
    {
        // Inserting Heading
        Paragraph lastParagraph = (Paragraph)list.LastOrDefault();

        Paragraph paraHeading = new Paragraph(doc);
        paraHeading.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading2;
        paraHeading.ListFormat.ListLevelNumber = 0;

        paraHeading.AppendChild(new Run(doc, headingMissingAgain));

        
        lastParagraph.ParentNode.InsertAfter(paraHeading, lastParagraph);

        // Inserting Content
        string content = "While it may not be obvious to everyone, there are a number of reasons creating random paragraphs can be useful. A few examples of how some people use this generator are listed in the following paragraphs.";
        Paragraph paraContent = new Paragraph(doc);
        paraContent.ListFormat.RemoveNumbers(); // this will remove the numbers, if you want them remove this line.
        paraContent.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading3;


        paraContent.AppendChild(new Run(doc, content));

        paraHeading.ParentNode.InsertAfter(paraContent, paraHeading);
    }
}

Please let me know if you would like to know something else.
Best regards.

@carlosmc your given code is not working in my case. Kindly find the input and expected output word document above. Kindly help me asap.
I need to find the "PURPOSE AND SCOPE " heading1 and give that to serial number 2.
and if “PURPOSE AND SCOPE” are not there in the document heading1 add it and give serial number 2.

Any update on this?

Hello @manasahr

I broke up my answer and remove some of the code you can figure it out yourself, just to focus in you latests questions. This is the code just to find and modify an specific heading.

public void MainCode()
{
    string partialPath = @"Headings\FindingSpecificHeading"; // Location of your input/output files

    LoadLicence(); // Load your License

    var doc = DocumentGet(partialPath); // Load your document

    Logic(doc);

    doc.Save($"{partialPath}_output.docx");
}

private void Logic(Document doc)
{
    // This is whatever heading you are looking for
    string headingToFind= "PURPOSE AND SCOPE";           

    var list = doc.GetChildNodes(NodeType.Paragraph, true);

    foreach (Paragraph paragraph in list)
    {
        paragraph.JoinRunsWithSameFormatting();
        foreach (Run run in paragraph.Runs)
        {
            // This finds the heading
            if (run.Text.Equals(headingToFind, StringComparison.InvariantCultureIgnoreCase))
            {
                paragraph.ListFormat.ListLevelNumber = 0; // this will break level of Heading2, if you do not want it remove this line.
                paragraph.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading2; // this changes the style
            }                    
        }
    }            
}

Here is the input file you provided with the respective output.
FindingSpecificHeading_input.docx (24.1 KB)
FindingSpecificHeading_output.docx (20.3 KB)

If you need to do any other particular changes to it, you can follow the documentation for ParagraphFormat.

Regarding your second question, here is another code same to use as base to add a new heading.

public void MainCode()
{
    string partialPath = @"Headings\AddingAnSpecificHeadingNotFound"; // Location of your input/output files

    LoadLicence(); // Load your License

    var doc = DocumentGet(partialPath); // Load your document

    Logic(doc);

    doc.Save($"{partialPath}_output.docx");
}

private void Logic(Document doc)
{
    // I added some extra text so there is not match
    string headingToFind = "PURPOSE AND SCOPE AND SOME EXTRA WORDS";
    bool headingIsMissing = true;

    var list = doc.GetChildNodes(NodeType.Paragraph, true);

    foreach (Paragraph paragraph in list)
    {
        paragraph.JoinRunsWithSameFormatting();
        foreach (Run run in paragraph.Runs)
        {
            // search for the heading
            if (run.Text.Equals(headingToFind, StringComparison.InvariantCultureIgnoreCase))
            {
                headingIsMissing = false; // we change the flag because we found the heading
            }
        }
    }

    // Solves Problem 2
    if (headingIsMissing)
    {
        RemoveEmptyPagesOrParagraphsAtTheEnd(doc);

        Paragraph lastParagraph = (Paragraph)list.LastOrDefault();

        Paragraph newHeading = new Paragraph(doc);
        newHeading.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading2;
        newHeading.ListFormat.ListLevelNumber = 0;

        newHeading.AppendChild(new Run(doc, headingToFind));

        lastParagraph.ParentNode.InsertAfter(newHeading, lastParagraph);
    }
}

protected void RemoveEmptyPagesOrParagraphsAtTheEnd(Document doc)
{
    // 1. Remove empty sections if persists.
    while ((doc.Sections.Count > 0)
        && (doc.LastSection.Body.GetChildNodes(NodeType.Run, true).Count == 0)
        && (doc.LastSection.Body.GetChildNodes(NodeType.Shape, true).Count == 0))
        doc.LastSection.Remove();

    // 2. Remove empty paragraphs at the end of the document.
    while ((doc.LastSection.Body.LastChild.NodeType == NodeType.Paragraph) && !doc.LastSection.Body.LastParagraph.HasChildNodes)
        doc.LastSection.Body.LastParagraph.Remove();

    // 3. Set Window/Orphan control option for the last paragraph.
    if (doc.LastSection.Body.LastChild.NodeType == NodeType.Paragraph)
        doc.LastSection.Body.LastParagraph.ParagraphFormat.WidowControl = true;

    // 4. Enable Keep with next option if the last node is table.
    if (doc.LastSection.Body.LastChild.NodeType == NodeType.Table)
    {
        Table lastTable = (Table)doc.LastSection.Body.LastChild;
        NodeCollection rowParagraphs = lastTable.LastRow.GetChildNodes(NodeType.Paragraph, true);
        foreach (Paragraph para in rowParagraphs)
            para.ParagraphFormat.KeepWithNext = true;
    }
}

Here is the input file you provided with the respective output.
AddingAnSpecificHeadingNotFound_input.docx (24.1 KB)
AddingAnSpecificHeadingNotFound_output.docx (20.3 KB)

I hope this can help you out to achive what you wanted.

@carlosmc Thank you for the response. Your finding and adding heading 1 is correct but heading 1 must be position 2 kindly find the expected output Expected_output (2).docx (24.7 KB).
But in your output, your adding heading 1 at 11 position AddingAnSpecificHeadingNotFound_output.docx (20.3 KB). Kindly help me asap.

I used below mentioned code.

public static void Logic1(Document doc)
    {
        // I added some extra text so there is not match
        string headingToFind = "PURPOSE AND SCOPE AND SOME EXTRA WORDS";
        bool headingIsMissing = true;

        var list = doc.GetChildNodes(NodeType.Paragraph, true);

        foreach (Paragraph paragraph in list)
        {
            paragraph.JoinRunsWithSameFormatting();
            foreach (Run run in paragraph.Runs)
            {
                // search for the heading
                if (run.Text.Equals(headingToFind, StringComparison.InvariantCultureIgnoreCase))
                {
                    headingIsMissing = false; // we change the flag because we found the heading
                }
            }
        }

        // Solves Problem 2
        if (headingIsMissing)
        {

            Paragraph lastParagraph = (Paragraph)list.LastOrDefault();

            Paragraph newHeading = new Paragraph(doc);
            newHeading.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading1;
            newHeading.ListFormat.ListLevelNumber = 0;

            newHeading.AppendChild(new Run(doc, headingToFind));

            lastParagraph.ParentNode.InsertAfter(newHeading, lastParagraph);
        }
    }

Hello @manasahr,

I think you are confused, because my code above has the following:

newHeading.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading2;
newHeading.ListFormat.ListLevelNumber = 0;

The code above is using ListLevelNumber to move the heading2 into heading1 position. You can remove the second line and having it as a next level heading2.

Also if you inspect the ouput from code in the document element you will see is heading2.

image.png (11.1 KB)

Hopefully this clarify everything.

@carlosmc kindly check my expected output word document. Kindly tell me if is it possible to do that.
Expected_output (2).docx (24.7 KB)

Kindly check the below screen shot.
Please note I am not adding or modifying any heading 2. I am trying to add heading 1 and place it to that S.No.2.
image.png (8.2 KB)

I understood what you need @Manasahr, I am working on it right now.

@carlosmc Thank you so much for understanding my issue.

Hello @manasahr here is the code I think you needed. You can change the position if you want in the variable 'position ’ there. Also if that position is not found, it will add it at the end of the document.

public void MainCode()
{
    string partialPath = $@"{prefixPath}\Headings\AddingHeadingInSpecificPosition"; // Location of your input/output files

    LoadLicence(); // Load your License

    var doc = DocumentGet(partialPath); // Load your document

    Logic(doc);

    doc.Save($"{partialPath}_output.docx");
}

public static void Logic(Document doc)
{
    // I added some extra text so there is not match
    string headingToFind = "PURPOSE AND SCOPE AND SOME EXTRA WORDS";
    string contentParagraph = "See I need to add  heading 1 here(I need to give number style position 2 example: 2. PURPOSE AND SCOPE AND SOME EXTRA WORDS";
    bool headingIsMissing = true;
    int position = 2; // Position to insert the heading if not found.

    var list = doc.GetChildNodes(NodeType.Paragraph, true).Cast<Paragraph>();
    var listHeadings = list.Where(p => p.IsListItem && p.ParagraphFormat.StyleIdentifier == StyleIdentifier.Heading1); // only list items with heading1 level paragraphs

    foreach (Paragraph paragraph in listHeadings)
    {
        paragraph.JoinRunsWithSameFormatting();
        foreach (Run run in paragraph.Runs)
        {
            // search for the heading
            if (run.Text.Equals(headingToFind, StringComparison.InvariantCultureIgnoreCase))
            {
                headingIsMissing = false; // we change the flag because we found the heading
            }
        }
    }

    if (headingIsMissing)
    {
        bool isPositionFound = false;
        // Now we look for the current position of heading where we need to insert it.
        int listItemPosition = 0;
        foreach (var paragraph in listHeadings)
        {
            listItemPosition++;
            if (listItemPosition == position)
            {
                isPositionFound = true;                

                Paragraph newHeading = new Paragraph(doc);
                newHeading.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading1;
                newHeading.ListFormat.ListLevelNumber = 0;

                newHeading.AppendChild(new Run(doc, headingToFind));

                paragraph.ParentNode.InsertBefore(newHeading, paragraph);

                Paragraph paraContent = new Paragraph(doc);
                paraContent.ListFormat.RemoveNumbers(); // this will remove the numbers, if you want them remove this line.
                paraContent.AppendChild(new Run(doc, contentParagraph));
                newHeading.ParentNode.InsertAfter(paraContent, newHeading);
            }
        }

        // if the position we were looking for was not found in our list, we add it at the end
        // I recommend clearing the end of the document from empty paragraphs with the code I attached in a previous post.
        if (!isPositionFound)
        {
            Paragraph lastParagraph = (Paragraph)list.LastOrDefault();

            Paragraph newHeading = new Paragraph(doc);
            newHeading.ParagraphFormat.StyleIdentifier = StyleIdentifier.Heading1;
            newHeading.ListFormat.ListLevelNumber = 0;

            newHeading.AppendChild(new Run(doc, headingToFind));

            lastParagraph.ParentNode.InsertAfter(newHeading, lastParagraph);

            Paragraph paraContent = new Paragraph(doc);
            paraContent.ListFormat.RemoveNumbers(); // this will remove the numbers, if you want them remove this line.
            paraContent.AppendChild(new Run(doc, contentParagraph));
            newHeading.ParentNode.InsertAfter(paraContent, newHeading);
        }
    }
}

I hope this solve your problem.

@carlosmc Thank you so much it’s working great. Kindly help me for below question.
if we find the particular text in heading 1 and position 2 don’t modify anything.
if we find the particular text in heading 1 and any other position, Copy heading 1 and below the content and paste it to heading 1 position 2. (Text : HEADING ARE)
Please find the below mentioned input and expected output word document.
Input123 (2).docx (24.8 KB)
Expected_output123.docx (20.0 KB)

hi @manasahr, I refactored the code and made some functions you can reuse in order to achieve the desired result.

public void MainCode()
{
    string partialPath = $@"{prefixPath}\Headings\HeadingsCopyMove"; // Location of your input/output files

    LoadLicence(); // Load your License

    var doc = DocumentGet(partialPath); // Load your document

    Logic(doc);

    doc.Save($"{partialPath}_output.docx");
}

public static void Logic(Document doc)
{
    string headingWhereToInsert = "HEADING HOW";
    string headingToMove = "HEADING ARE";

    var helper = new HeadingHelper();

    var paragraphWhereToInsert = helper.LocateByName(doc, headingWhereToInsert);

    var nodeListToMove = helper.LocateNodeListByName(doc, headingToMove);

    bool moveParagraph = true;

    helper.InsertBeforeParagraph(paragraphWhereToInsert, nodeListToMove, moveParagraph);
}

The helper class is the following:

internal class HeadingHelper
{
    /// <summary>
    /// This method return a Paragraph that matches the name
    /// </summary>
    /// <param name="doc">Aspose Word Document</param>
    /// <param name="name">name to find</param>
    /// <returns></returns>
    internal Paragraph LocateByName(Document doc, string name)
    {
        var headingList = doc.GetChildNodes(NodeType.Paragraph, true).Cast<Paragraph>().Where(p => p.ParagraphFormat.StyleName.Contains("Heading")); // all paragraphs with Headings styles

        foreach (var paragraph in headingList)
        {
            paragraph.JoinRunsWithSameFormatting();
            foreach (Run run in paragraph.Runs)
            {
                if (run.Text.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                {
                    return paragraph;
                }
            }
        }
        return null;
    }        

    /// <summary>
    /// Insert a paragraph in the position of another copying the list style
    /// </summary>
    /// <param name="targetParagraph">Where to insert</param>
    /// <param name="paragraphToInsert">What to insert</param>
    internal void InsertBeforeParagraph(Paragraph targetParagraph, Paragraph paragraphToInsert)
    {
        if (targetParagraph != null && paragraphToInsert != null)
        {
            paragraphToInsert.ParagraphFormat.StyleIdentifier = targetParagraph.ParagraphFormat.StyleIdentifier;
            paragraphToInsert.ParagraphFormat.LeftIndent = targetParagraph.ParagraphFormat.LeftIndent;
            paragraphToInsert.ListFormat.ListLevelNumber = targetParagraph.ListFormat.ListLevelNumber;

            targetParagraph.ParentNode.InsertBefore(paragraphToInsert, targetParagraph);
        }
    }

    internal List<Node> LocateNodeListByName(Document doc, string name)
    {
        var result = new List<Node>();

        StyleIdentifier? styleParagraph = null;
        var headingList = doc.GetChildNodes(NodeType.Paragraph, true).Cast<Paragraph>().Where(p => p.ParagraphFormat.StyleName.Contains("Heading")); // all paragraphs with Headings styles
        Paragraph paragraphSource = null;

        foreach (var paragraph in headingList)
        {
            paragraph.JoinRunsWithSameFormatting();
            foreach (Run run in paragraph.Runs)
            {
                // if it have not been found
                if (run.Text.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                {
                    result.Add(paragraph);
                    paragraphSource = paragraph;
                    styleParagraph = paragraph.ParagraphFormat.StyleIdentifier;
                    break; ; // do not want to do anything else after it is found
                }
            }
        }

        var nodeSibling = paragraphSource.NextSibling;
        while (nodeSibling != null)
        {
            // We locate all paragraphs until another paragraph of the same style comes that is also a list item, 
            // which means is another heading in the same level and we do not want to add them to our result
            if (nodeSibling is Paragraph &&
               ((Paragraph)nodeSibling).ParagraphFormat.StyleIdentifier == styleParagraph.Value && ((Paragraph)nodeSibling).IsListItem)
            {
                break;// We stop the loop since we do not want to get any more paragraphs
            }
            else
            {
                result.Add(nodeSibling);
                nodeSibling = nodeSibling.NextSibling;
            }

        }

        return result;
    }

    /// <summary>
    /// Insert a list of paragraphs before another paragraph
    /// </summary>
    /// <param name="targetParagraph">Where to insert</param>
    /// <param name="paragraphListToInsert">What to insert</param>
    /// <param name="moveParagraphList">If true: Delete original paragraphs after they were cloned</param>
    internal void InsertBeforeParagraph(Paragraph targetParagraph, List<Node> nodeListToInsert, bool moveParagraphList)
    {
        if (targetParagraph != null && nodeListToInsert != null && nodeListToInsert.Count > 0)
        {
            foreach (var node in nodeListToInsert)
            {
                var nodeToInsert = node.Clone(true);                    

                targetParagraph.ParentNode.InsertBefore(nodeToInsert, targetParagraph);
            }

            if (moveParagraphList)
            {
                //Removing original nodes from the document.
                foreach (var node in nodeListToInsert)
                {
                    node.Remove();
                }
            }
        }
    }
}

This is the input and output of the code.
HeadingsCopyMove_input.docx (24.8 KB)
HeadingsCopyMove_output.docx (20.4 KB)

This will help you with what you requested.