After Cross-Reference link Applied, Numbering is not as expected

ReplaceAction IReplacingCallback.Replacing(ReplacingArgs args)
{
try
{

            // This is a Run node that contains either the beginning or the complete match.
            Node currentNode = args.MatchNode;
            Node prevNodeVal = currentNode;
            // 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 (args.MatchOffset > 0)
                currentNode = SplitRun((Aspose.Words.Run)currentNode, args.MatchOffset);

            // This array is used to store all nodes of the match for further highlighting.
            List<Run> runs = new List<Run>();

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

                prevNodeVal = currentNode;

                // 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((Aspose.Words.Run)currentNode, remainingLength);
                runs.Add((Run)currentNode);
            }

            DocumentBuilder oDocBuild = new DocumentBuilder(args.MatchNode.Document as Document);

            // Check if the replaced text contains a hyperlink
            if (currentNode != null && CrossReferenceService.IsReferencedParaListItem)
            {
                bool FieldCountPresentAlready = false;
                if (currentNode.ParentNode.Range.Fields.Count > 0)
                {
                    FieldCountPresentAlready = true;
                }


                foreach (Field field in currentNode.ParentNode.Range.Fields)
                {
                    if (field.Type == FieldType.FieldRef)
                    {
                        // Get the text from the field result
                        string resultText = field.Result;

                        // Remove the field from the document
                        field.Remove();

                        // Find the first paragraph after the parent node
                        Paragraph para = currentNode.ParentNode as Paragraph;

                        // If no paragraph found, create a new paragraph
                        if (para == null)
                        {
                            para = new Paragraph(currentNode.Document);
                            currentNode.ParentNode.ParentNode.InsertBefore(para, currentNode.ParentNode);
                        }

                        // Create a new Run node with the resultText
                        Run newRun = new Run(currentNode.Document, resultText);

                        para.InsertBefore(newRun, currentNode);
                        break;
                    }
                }
                if (FieldCountPresentAlready)
                {
                    currentNode = currentNode.PreviousSibling;


                    if (currentNode.NodeType == NodeType.BookmarkEnd || currentNode.NodeType == NodeType.BookmarkStart)
                    {
                        currentNode = currentNode.PreviousSibling;
                    }
                    if (args.MatchOffset > 0 && currentNode.NextSibling.NodeType != NodeType.BookmarkStart && currentNode.NextSibling.NodeType != NodeType.BookmarkEnd)
                    {
                        currentNode = SplitRun((Aspose.Words.Run)currentNode, args.MatchOffset);
                        runs.Clear();
                        runs.Add((Aspose.Words.Run)currentNode);
                    }
                    else
                    {
                        runs.Clear();
                        runs.Add((Aspose.Words.Run)currentNode);
                    }
                }
            }



            // Now Add field codes in the sequence

            if (currentNode != null)
            {
                oDocBuild.MoveTo(currentNode);
            }
            else
            {
                oDocBuild.MoveTo(prevNodeVal);
            }
            SelectInsertReferenceType(oDocBuild, runs, currentNode);

            foreach (Run run in runs)
            {
                if (run.ParentNode != null)
                {
                    run.Remove();
                }
            }

        }
        catch (Exception ex)
        {
            ex.SplunkException();
        }
        return ReplaceAction.Skip;
    }

    private void SelectInsertReferenceType(DocumentBuilder oDocBuild, List<Run> runs, Node currentNode)
    {
        if (CrossReferenceService.IsReferencedParaListItem)
        {
            // Insert field after text
            oDocBuild.MoveTo(runs[runs.Count - 1]);
            oDocBuild.InsertField("REF _Ref" + mStartBookmarkNumber + " \\h", currentNode?.GetText());
            // Remove text
            runs.ForEach(r => r.Remove());
        }
        else
        {
            switch (CrossReferenceService.insertReferenceType)
            {
                case "page_number":
                    FieldPageRef ReffieldPage = (FieldPageRef)oDocBuild.InsertField(FieldType.FieldPageRef, false);
                    ReffieldPage.BookmarkName = "_Ref" + mStartBookmarkNumber;
                    ReffieldPage.InsertHyperlink = true;
                    ReffieldPage.Update();
                    break;
                case "paragraph_number":
                case "paragraph_number_no_context":
                case "paragraph_number_full_context":
                case "above_or_below":
                case "paragraph_text":
                    FieldRef Reffield = (FieldRef)oDocBuild.InsertField(FieldType.FieldRef, false);
                    Reffield.InsertParagraphNumber = CrossReferenceService.insertReferenceType != "paragraph_text" && CrossReferenceService.insertReferenceType != "above_or_below";
                    if (CrossReferenceService.insertReferenceType == "paragraph_number_full_context")
                    {
                        Reffield.InsertParagraphNumberInFullContext = true;
                    }
                    else if (CrossReferenceService.insertReferenceType == "paragraph_number_no_context")
                    {
                        Reffield.InsertParagraphNumberInRelativeContext = true;
                    }
                    if (CrossReferenceService.insertReferenceType == "above_or_below")
                    {
                        Reffield.InsertRelativePosition = true;
                    }
                    Reffield.BookmarkName = "_Ref" + mStartBookmarkNumber;
                    Reffield.InsertHyperlink = true;
                    Reffield.Update();
                    break;
            }
        }
    }

I tried to add the cross-reference Hyperlink to a paragraph in the document. It’s cross-referenced and hyperlinked correctly and navigation also works as expected. But the display number format is not correct as expected. For example I tried to link 2.4.3(a) paragraph in the document, It changed to (a) instead of 2.4.3(a). How can I correct this?WordDefaultResult.png (10.8 KB)
AsposeResult.png (10.7 KB)
Precedent Option anonymised.docx (80.0 KB)

I attached the snapshot and necessary files in the same ticket.

@coderthiyagarajan1980 As I can see in the attached document 2.4.3(a) is a simple text. Do you need to replace it with a clickable hyperlink to some location in your document? Unfortunately, it is not quite clear how 2.4.3(a) text becomes (a) on the second screenshot. Could you please elaborate the scenario in more detains and provide a compliable code that will allow us to reproduce the problem?

No @alexey.noskov My idea is to select the 2.4.3(a) text in this paragraph and link with the Numbered item 2.4.3(a) by means of creating the cross-reference link. It works as expected. Only thing is displayed text changed to (a) instead of word default behavior 2.4.3(a) {Means, I did the same stuff in word using References -> CrossReferences} that time it will show the display text as 2.4.3(a).Referenced Paragraphpng.png (175.6 KB)
Linked Paragraph.png (173.3 KB)
I attached the word’s behavior with the above files.
Referenced Paragraph_Aspose.png (177.4 KB)
Linked Paragraph_Aspose.png (172.1 KB)
Referenced Paragraphpng.png (175.6 KB)
Linked Paragraph.png (173.3 KB)

@alexey.noskov I attached the Word’s default behavior and Aspose created value for your view.

Yes I need to create cross reference hyperlink to another location which is numbered item in the document @alexey.noskov

@coderthiyagarajan1980 Unfortunately, it is still not clear how to reproduce the problem. Could you please create s simple console application that will allow us to reproduce the problem? We will check the issue and provide you more information.

@alexey.noskov I attached the code module for your view.

using System.Text;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Reflection.Metadata.Ecma335;
using System.Diagnostics;
using LexisNexis.Services.Schemas.RecentActivity;
using Amazon.Runtime.Internal.Util;
using System.Reflection;
using System.Text.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Aspose.Words.Saving;
using DocumentFormat.OpenXml.Office2010.Word;
using DocParserService.Services.Utilities;
using Microsoft.AspNetCore.Identity;
using RestSharp;
using Body = Aspose.Words.Body;
using Microsoft.Extensions.Configuration;
using System.Xml.Xsl;
using System.Xml;
using Formatting = Newtonsoft.Json.Formatting;
using LexisNexis.Services;
using System.Linq;
using Tuple = System.Tuple;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using System.Xml.Linq;
using LexisNexis.Services.Telemetry;
using DocParserService.Abstractions.Models;
using DocParserService.Abstractions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using DocParserService.Services.Models;
using Aspose.Words;
using DocumentFormat.OpenXml.Packaging;
using Aspose.Words.Fields;
using Microsoft.Extensions.FileSystemGlobbing.Internal;
using Microsoft.Extensions.FileSystemGlobbing;
using Nest;
using DocParserService.Peer.Helpers;
using FieldType = Aspose.Words.Fields.FieldType;
using Run = Aspose.Words.Run;
using DocumentFormat.OpenXml.ExtendedProperties;
using System.Text.RegularExpressions;
using Aspose.Words.Replacing;
using static MassTransit.ValidationResultExtensions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using System.Collections;
using Microsoft.Extensions.FileSystemGlobbing.Internal.PathSegments;
using DocumentFormat.OpenXml.Wordprocessing;
using Field = Aspose.Words.Fields.Field;
using Document = Aspose.Words.Document;
using Paragraph = Aspose.Words.Paragraph;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Runtime.ExceptionServices;
using System;
using BookmarkStart = Aspose.Words.BookmarkStart;
using FieldChar = Aspose.Words.Fields.FieldChar;
using DocumentFormat.OpenXml;
using Style = Aspose.Words.Style;
using DocumentFormat.OpenXml.Bibliography;
using Comment = Aspose.Words.Comment;

namespace DocParserService.Peer.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    [RequestKeyEvent]
    public class CrossReferenceController
    {
        public readonly IConfiguration _config;
        public readonly ILogger<CrossReferenceController> _logger;
        public static string insertReferenceType { get; set; } = "";

        //Assume some starting bookmark number.
        public string bookmarkStartNumber { get; set; } = "100000000";
        public static string CorrectedbookmarkStartNumber { get; set; } = "100000000";

        public static bool ISReferencedParaListItem { get; set; } = false;

        public static string preText { get; set; } = "";
        /// <summary>
        /// Constructor used to read the appsettings json file.
        /// </summary>
        /// <param name="config"></param>

        public CrossReferenceController(ILogger<CrossReferenceController> logger,
                                            IConfiguration config)
        {
            this._logger = logger;
            _config = config;
        }


        [HttpPost]
        [AllowAnonymous]
        [Route("InsertCrossReference")]
        public string InsertCrossReference([FromBody] CrossReferenceRequestModel crossReferenceRequestModel)
        {
            //Get the Open xml content
            string ooxmlContent = crossReferenceRequestModel.ooxml;
            //Get the List of Paragraph Id's
            JArray jArray = crossReferenceRequestModel.paragraphIds;
            int crossReferenceParaId = Convert.ToInt32(crossReferenceRequestModel.referenceId);
            List<int> paraIds = jArray.Select(x => Convert.ToInt32(x)).ToList();
            //Get the search text
            string findText = crossReferenceRequestModel.text;

            //Get the pre text
            if (!String.IsNullOrWhiteSpace(crossReferenceRequestModel.preText))
            {
                preText = crossReferenceRequestModel.preText;
            }

            insertReferenceType = crossReferenceRequestModel.InsertReferenceType;
            //Assume the bookmark number as 


            using (System.IO.MemoryStream mStream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(ooxmlContent)))
            {
                //Applying the Aspose License
                AsposeHelper.ApplyingAsposeLicense();
                Aspose.Words.Document doc = new Aspose.Words.Document(mStream);

                while (doc.Range.Bookmarks["_ref" + bookmarkStartNumber] != null)
                {
                    bookmarkStartNumber = (Convert.ToInt64(bookmarkStartNumber) + 1).ToString();
                    CorrectedbookmarkStartNumber = bookmarkStartNumber;
                }


                //Creating the builder from the Aspose Document
                DocumentBuilder builder = new DocumentBuilder(doc);
                builder.MoveToDocumentStart();
                bool hasListFormatinginReferencePara = false;
                if (doc.Sections.Count == 1)
                {
                    ProcessSingleSection(doc, crossReferenceParaId, ref hasListFormatinginReferencePara, bookmarkStartNumber, builder);
                }
                else
                {
                    ProcessMultipleSections(doc, crossReferenceParaId, ref hasListFormatinginReferencePara, bookmarkStartNumber, builder);
                }

                builder.MoveToDocumentStart();


                AddCrossReferenceToParagraphs(paraIds, findText, doc, hasListFormatinginReferencePara);
                using (MemoryStream dstStream = new MemoryStream())
                {
                    doc.Save(dstStream, SaveFormat.Docx);
                    using (WordprocessingDocument wordprocessingDocument =
            WordprocessingDocument.Open(dstStream, true))
                    {
                        string? jsonstring = wordprocessingDocument.ToFlatOpcString();
                        preText = "";
                        bookmarkStartNumber = "";
                        insertReferenceType = "";
                        CorrectedbookmarkStartNumber = "";
                        return jsonstring;
                    }
                }
            }
        }


        void RemoveFootnoteParagraphs(NodeCollection paragraphs)
        {
            foreach (Paragraph item in paragraphs.OfType<Paragraph>().Where(x => x.ParentNode.NodeType == NodeType.Footnote))
            {
                item.Remove();
            }
        }

        int CountImageParagraphs(NodeCollection paragraphs)
        {
            return paragraphs.OfType<Paragraph>().Count(x => x.ParentNode.NodeType == NodeType.Shape);
        }

        int CountCommentsBeforeParagraph(Document doc, int targetParagraphIndex, int CurrentSection)
        {
            int commentsParaCount = 0;

            for (int i = 0; i < targetParagraphIndex; i++)
            {
                Paragraph paragraph = doc.Sections[CurrentSection].Body.Paragraphs[i];

                foreach (Comment comment in doc.GetChildNodes(NodeType.Comment, true).Cast<Comment>())
                {
                    if (comment.GetAncestor(NodeType.Paragraph) == paragraph)
                    {
                        commentsParaCount++;
                    }
                }
            }

            return commentsParaCount;
        }

        void ProcessSingleSection(Document doc, int crossReferenceParaId, ref bool hasListFormatinginReferencePara, string bookmarkStartNumber, DocumentBuilder builder)
        {
            NodeCollection paragraphs = doc.Sections[0].Body.GetChildNodes(NodeType.Paragraph, true);

            RemoveFootnoteParagraphs(paragraphs);

            int imageParaCount = CountImageParagraphs(paragraphs);
            crossReferenceParaId -= imageParaCount;

            int commentsParaCount = CountCommentsBeforeParagraph(doc, crossReferenceParaId - 1, 0);

            Paragraph targetParagraph = (Paragraph)paragraphs[crossReferenceParaId - 1 + commentsParaCount];
            hasListFormatinginReferencePara = HasNoListFormatting(targetParagraph);

            builder.MoveTo(targetParagraph.GetChildNodes(NodeType.Run, true).Count > 0
                ? targetParagraph.GetChild(NodeType.Run, 0, true)
                : targetParagraph);

            builder.StartBookmark("_Ref" + bookmarkStartNumber);
            builder.MoveTo(targetParagraph);
            builder.EndBookmark("_Ref" + bookmarkStartNumber);
        }

        void ProcessMultipleSections(Document doc, int crossReferenceParaId, ref bool hasListFormatinginReferencePara, string bookmarkStartNumber, DocumentBuilder builder)
        {
            int currentSection = 0;
            int commentsParaCount = 0;

            for (int i = 0; i < doc.Sections.Count; i++)
            {
                int sectionParaCount = 0;
                NodeCollection paragraphs = doc.Sections[i].Body.GetChildNodes(NodeType.Paragraph, true);

                RemoveFootnoteParagraphs(paragraphs);

                int imageParaCount = CountImageParagraphs(paragraphs);
                sectionParaCount = paragraphs.Count - imageParaCount;

                if (crossReferenceParaId > sectionParaCount)
                {
                    crossReferenceParaId -= sectionParaCount;
                }
                else
                {
                    currentSection = i;
                    break;
                }
            }

            commentsParaCount = CountCommentsBeforeParagraph(doc, crossReferenceParaId - 1, currentSection);

            Paragraph targetParagraph = (Paragraph)doc.Sections[currentSection].Body.GetChildNodes(NodeType.Paragraph, true)[crossReferenceParaId - 1 + commentsParaCount];
            hasListFormatinginReferencePara = HasNoListFormatting(targetParagraph);

            if (targetParagraph.ChildNodes.Count > 0 && targetParagraph.GetChildNodes(NodeType.Run, true).Count > 0)
            {
                builder.MoveTo(targetParagraph.GetChild(NodeType.Run, 0, true));
            }
            else
            {
                builder.MoveTo(targetParagraph);
            }

            builder.StartBookmark("_Ref" + bookmarkStartNumber);
            builder.MoveTo(targetParagraph);
            builder.EndBookmark("_Ref" + bookmarkStartNumber);
        }

        private static void AddCrossReferenceToParagraphs(List<int> paraIds, string findText, Document doc, bool HasListFormatinginReferencePara)
        {
            bool bracketInFindText = Regex.IsMatch(findText, @"[\{\}\(\)\[\]]");
            string changedfindText = bracketInFindText ? Regex.Escape(findText) : findText;

            FindReplaceOptions options = new(FindReplaceDirection.Backward, new CrossReferenceAddingEvaluator());

            foreach (int paraId in paraIds)
            {
                int currentSection = 0;
                int currentPara = paraId;

                for (int sectionIndex = 0; sectionIndex < doc.Sections.Count; sectionIndex++)
                {
                    NodeCollection paragraphs = doc.Sections[sectionIndex].Body.GetChildNodes(NodeType.Paragraph, true);

                    foreach (Paragraph item in paragraphs.OfType<Paragraph>().Where(x => x.ParentNode.NodeType == NodeType.Footnote))
                    {
                        item.Remove();
                    }

                    int imageParaCount = paragraphs.OfType<Paragraph>().Count(x => x.ParentNode.NodeType == NodeType.Shape);
                    int sectionParaCount = paragraphs.Count - imageParaCount;

                    if (currentPara > sectionParaCount)
                    {
                        currentPara -= sectionParaCount;
                    }
                    else
                    {
                        currentSection = sectionIndex;
                        break;
                    }
                }

                Paragraph targetParagraph = doc.Sections[currentSection].Body.GetChildNodes(NodeType.Paragraph, true).OfType<Paragraph>().ToList()[currentPara - 1];



                if (targetParagraph != null)
                {
                    if (!HasListFormatinginReferencePara)
                    {
                        targetParagraph.Range.Replace(new Regex("\\b(?<!\\.)" + changedfindText + "(?<!\\.)"), String.Empty, options);
                    }
                    else
                    {
                        ISReferencedParaListItem = true;
                        targetParagraph.Range.Replace(new Regex("\\b(?<!\\.)" + changedfindText + "(?<!\\.)"), String.Empty, options);
                    }
                }
            }
        }


        static bool HasNoListFormatting(Paragraph para)
        {
            // Check if the paragraph is part of a multilevel list
            if (para.IsListItem && para.ListLabel?.LabelString == "" && (para.ListFormat?.ListLevel?.NumberFormat == "" || para.ListFormat?.ListLevel?.NumberStyle == NumberStyle.Bullet))
            {
                return true;
            }
            return false;
        }

    }




    public class CrossReferenceAddingEvaluator : IReplacingCallback
    {
        private readonly string mStartBookmarkNumber;
        private readonly string insertReferenceType;
        private readonly string preText;

        internal CrossReferenceAddingEvaluator(FindReplaceOptions options)
        {
            mStartBookmarkNumber = CrossReferenceController.CorrectedbookmarkStartNumber;
            insertReferenceType = CrossReferenceController.insertReferenceType;
            preText = CrossReferenceController.preText;
        }
        internal CrossReferenceAddingEvaluator()
        {
            mStartBookmarkNumber = CrossReferenceController.CorrectedbookmarkStartNumber;
            insertReferenceType = CrossReferenceController.insertReferenceType;
            preText = CrossReferenceController.preText;
        }

        private static Aspose.Words.Run SplitRun(Aspose.Words.Run run, int position)
        {
            Aspose.Words.Run afterRun = (Aspose.Words.Run)run.Clone(true);
            afterRun.Text = run.Text.Substring(position);
            run.Text = run.Text.Substring(0, position);
            run.ParentNode.InsertAfter(afterRun, run);
            return afterRun;
        }
        ReplaceAction IReplacingCallback.Replacing(ReplacingArgs args)
        {
            bool needtoCheckPreText = !String.IsNullOrEmpty(preText);
            // This is a Run node that contains either the beginning or the complete match.
            Node currentNode = args.MatchNode;
            string FindText = currentNode.Range.Text;
            if (currentNode.Range.Text.Length < args.MatchOffset) return ReplaceAction.Skip;

            Node prevNodeVal = currentNode;
            // 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 (args.MatchOffset > 0)
                currentNode = SplitRun((Aspose.Words.Run)currentNode, args.MatchOffset);

            // This array is used to store all nodes of the match for further highlighting.
            List<Run> runs = new List<Run>();

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

                prevNodeVal = currentNode;

                // 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((Aspose.Words.Run)currentNode, remainingLength);
                runs.Add((Run)currentNode);
            }

            DocumentBuilder oDocBuild = new DocumentBuilder(args.MatchNode.Document as Document);


            // Check if the replaced text contains a hyperlink
            if (currentNode != null)
            {
                bool FieldCountPresentAlready = false;
                if (currentNode.ParentNode.Range.Fields.Count > 0)
                {
                    FieldCountPresentAlready = true;
                }

                foreach (Field field in currentNode.ParentNode.Range.Fields)
                {
                    if (field.Type == FieldType.FieldRef && field.Result.Contains(FindText))
                    {
                        // Get the text from the field result
                        string resultText = field.Result;

                        // Remove the field from the document
                        field.Remove();

                        // Find the first paragraph after the parent node
                        Paragraph? para = currentNode.ParentNode as Paragraph;

                        // If no paragraph found, create a new paragraph
                        if (para == null)
                        {
                            para = new Paragraph(currentNode.Document);
                            currentNode.ParentNode.ParentNode.InsertBefore(para, currentNode.ParentNode);
                        }

                        // Create a new Run node with the resultText
                        Run newRun = new Run(currentNode.Document, resultText);

                        para.InsertBefore(newRun, currentNode);
                    }
                }
                if (FieldCountPresentAlready)
                {
                    currentNode = currentNode.PreviousSibling;


                    if (currentNode.NodeType == NodeType.BookmarkEnd || currentNode.NodeType == NodeType.BookmarkStart)
                    {
                        currentNode = currentNode.PreviousSibling;
                    }
                    if (args.MatchOffset > 0 && currentNode.NextSibling.NodeType != NodeType.BookmarkStart && currentNode.NextSibling.NodeType != NodeType.BookmarkEnd)
                    {
                        currentNode = SplitRun((Aspose.Words.Run)currentNode, args.MatchOffset);
                        runs.Clear();
                        runs.Add((Aspose.Words.Run)currentNode);
                    }
                    else
                    {
                        runs.Clear();
                        runs.Add((Aspose.Words.Run)currentNode);
                    }
                }
            }

            // Now Add field codes in the sequence

            if (currentNode != null)
            {
                oDocBuild.MoveTo(currentNode);
                SelectInsertReferenceType(oDocBuild, runs, currentNode);
                foreach (Run run in runs)
                {
                    if (run.ParentNode != null)
                    {
                        run.Remove();
                    }
                }
            }
            else
            {
                oDocBuild.MoveTo(prevNodeVal);
            }
            return ReplaceAction.Skip;
        }


        private void SelectInsertReferenceType(DocumentBuilder oDocBuild, List<Run> runs, Node currentNode)
        {
            if (CrossReferenceController.ISReferencedParaListItem)
            {
                // Insert field after text
                oDocBuild.MoveTo(runs[runs.Count - 1]);
                oDocBuild.InsertField("REF _Ref" + mStartBookmarkNumber + " \\h", currentNode?.GetText());
                // Remove text
                runs.ForEach(r => r.Remove());
            }
            else
            {
                switch (insertReferenceType)
                {
                    // Clone the runs with their formatting


                    case "page_number":

                        FieldPageRef ReffieldPage = (FieldPageRef)oDocBuild.InsertField(FieldType.FieldPageRef, false);
                        ReffieldPage.BookmarkName = "_Ref" + mStartBookmarkNumber;
                        ReffieldPage.InsertHyperlink = true;
                        ReffieldPage.Update();
                        break;
                    case "paragraph_number":
                    case "paragraph_number_no_context":
                    case "paragraph_number_full_context":
                    case "above_or_below":
                    case "paragraph_text":
                        FieldRef Reffield = (FieldRef)oDocBuild.InsertField(FieldType.FieldRef, false);
                        Reffield.InsertParagraphNumber = insertReferenceType != "paragraph_text" && insertReferenceType != "above_or_below";
                        if (insertReferenceType == "paragraph_number_full_context")
                        {
                            Reffield.InsertParagraphNumberInFullContext = true;
                        }
                        else if (insertReferenceType == "paragraph_number_no_context")
                        {
                            Reffield.InsertParagraphNumberInRelativeContext = true;
                        }
                        if (insertReferenceType == "above_or_below")
                        {
                            Reffield.InsertRelativePosition = true;
                        }
                        Reffield.BookmarkName = "_Ref" + mStartBookmarkNumber;
                        Reffield.InsertHyperlink = true;
                        Reffield.Update();
                        break;
                }
            }
        }
    }
}

I already attached the document too. I tried to link text “2.4.3(a)” in paragraph 81 with 255 paragraph in the document which is a number list item of 2.4.3(a). It linked and navigates but cross reference text changed to (a) instead of 2.4.3(a)

Any update on this

@coderthiyagarajan1980 Thank you for additional information. Unfortunately, I still cannot run your code. But I have created a simple code that inserters a bookmark into "2.4.3(a)" paragraph and replaces "2.4.3(a)" text in the document with a hyperlink to this bookmark. Everything works as expected on my side:

Document doc = new Document(@"C:\Temp\in.docx");
DocumentBuilder builder = new DocumentBuilder(doc);

// Put a bookmakr into the 255s paragrph.
string bookmarkName = "test_bookmark";
NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);
builder.MoveTo(paragraphs[285]);
builder.StartBookmark(bookmarkName);
builder.EndBookmark(bookmarkName);

// Search for "2.4.3(a)" text and replae it with bookmark to the inserted bookmark.
FindReplaceOptions opt = new FindReplaceOptions();
opt.ReplacingCallback = new ReplacingCallbackInsertHyperlinkToBookmark();
doc.Range.Replace("2.4.3(a)", bookmarkName, opt);

doc.Save(@"C:\Temp\out.docx");
internal class ReplacingCallbackInsertHyperlinkToBookmark : IReplacingCallback
{
    /// <summary>
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// </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 deleting.
        List<Run> runs = new List<Run>();

        // 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((Run)currentNode);
            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((Run)currentNode);
        }

        // Create DocumentBuilder to insert a field.
        DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
        // Move builder to the first run.
        builder.MoveTo(runs[0]);
        // Insert field
        builder.InsertHyperlink(e.Match.Value, e.Replacement, true);

        // Delete matched runs
        foreach (Run run in runs)
            run.Remove();

        // Signal to the replace engine to do nothing because we have already done all what we wanted.
        return ReplaceAction.Skip;
    }

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

Thanks @alexey.noskov. Let me check.

1 Like

Hi @alexey.noskov The above code shared by you will create the hyperlinks to that place. I want the cross reference link. I attached the snapshot in word for your reference. The Cross-Reference link may be any. Either Numbered Item or Heading or Bookmark or Equation/Figure. So need to create the reference field not simply hyperlinks.CrossReference.png (93.9 KB)

in the earlier scenario, I had taken numbered item. Could you please advise on this?

Winword’s default behavior generates the cross reference link with full list level string, where as Aspose Words it’s not. Could you please tell me the way?

@coderthiyagarajan1980 unfortunately, I cannot reproduce the problem on my side. Here is the code modified to use REF fields:

Document doc = new Document(@"C:\Temp\in.docx");

// Put a bookmakr into the 255s paragrph.
string bookmarkName = "_Ref1234567890";
NodeCollection paragraphs = doc.GetChildNodes(NodeType.Paragraph, true);
Paragraph target = (Paragraph)paragraphs[285];
target.PrependChild(new BookmarkStart(doc, bookmarkName));
target.AppendChild(new BookmarkEnd(doc, bookmarkName));

// Search for "2.4.3(a)" text and replace it with REF to the bookmakr
FindReplaceOptions opt = new FindReplaceOptions();
opt.ReplacingCallback = new ReplacingCallbackInsertRefToBookmark();
doc.Range.Replace("2.4.3(a)", bookmarkName, opt);

doc.Save(@"C:\Temp\out.docx");
internal class ReplacingCallbackInsertRefToBookmark : IReplacingCallback
{
    /// <summary>
    /// This method is called by the Aspose.Words find and replace engine for each match.
    /// </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 deleting.
        List<Run> runs = new List<Run>();

        // 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((Run)currentNode);
            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((Run)currentNode);
        }

        // Create DocumentBuilder to insert a field.
        DocumentBuilder builder = new DocumentBuilder((Document)e.MatchNode.Document);
        // Move builder to the first run.
        builder.MoveTo(runs[0]);
        // Insert field
        builder.InsertField($"REF {e.Replacement} \\r \\h");

        // Delete matched runs
        foreach (Run run in runs)
            run.Remove();

        // Signal to the replace engine to do nothing because we have already done all what we wanted.
        return ReplaceAction.Skip;
    }

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