Trying to add and removing some space before the colon and after colon

@alexey.noskov I am trying to add and removing some space before the colon and after colon
the before and after value should be dynamic. if the colon after we are not find any space we need to add one space.

Please find the below input and expected output.
Expected_output.docx (13.0 KB)
input101 (1).docx (13.2 KB)

Please find below code i have used

public static void AddDynamicSpacing(Document doc)
{
    string before_value = "0";
    string after_value = "1";

    string label = ":";

    FindReplaceOptions options = new FindReplaceOptions()
    {
        ReplacingCallback = new ReplaceNoSpaceColonHandler()
    };
    options.UseSubstitutions = true;
    options.IgnoreDeleted = true;
    options.IgnoreFields = true;
    List<string> before_valueStringSpaces = new List<string>();
    for (var i = 0; i < Convert.ToInt32(before_value); i++)
    {
        before_valueStringSpaces.Add(" ");
    }
    var before_valuespaces = string.Join("", before_valueStringSpaces);

    List<string> after_valueStringSpaces = new List<string>();
    for (var i = 0; i < Convert.ToInt32(after_value); i++)
    {
        after_valueStringSpaces.Add(" ");
    }
    var after_valueSpaces = string.Join("", after_valueStringSpaces);

    var pattern = "";

    if (label == "!" || label == "?")
    {
        label = "\\" + label;
    }

    if (after_value == "0")
    {
        pattern = string.Format(@"({0})\s+", label);
        doc.Range.Replace(new Regex(pattern), label + after_valueSpaces, options);
    }
    else
    {
        var range1 = Convert.ToInt32(after_value) - 1;
        var range2 = Convert.ToInt32(after_value) + 1;

        pattern = string.Format(@"({0})\saaa|({0})\sbbb", label);
        pattern = pattern.Replace("aaa", "{0," + range1.ToString() + "}");
        pattern = pattern.Replace("bbb", "{" + range2.ToString() + ",}");
        doc.Range.Replace(new Regex(pattern), label + after_valueSpaces, options);
    }

    if (before_value == "0")
    {
        pattern = string.Format(@"\s+({0})", label);
        doc.Range.Replace(new Regex(pattern), before_valuespaces + label, options);
    }
    else
    {
        var range1 = Convert.ToInt32(before_value) - 1;
        var range2 = Convert.ToInt32(before_value) + 1;

        pattern = string.Format(@"\saaa({0})", label);
        pattern = pattern.Replace("aaa", "{0," + range1.ToString() + "}");
        pattern = pattern.Replace("bbb", "{" + range2.ToString() + ",}");
        doc.Range.Replace(new Regex(pattern), before_valuespaces + label, options);
    }
}

Any Update on this?

@Manasahr Please check this forum thread, which is very similar to your post.

@eduardo.canal in that code is working fine but it’s overriding the right data also, for example, if a sentence has a colon after 1 space. I am also passing after value =1 on that time correct data should not be overriding.

I need to compare space after and space before with passing values of after and before values if both are equal don’t do anything. if both are not equal then perform the above activity

@Manasahr
I noticed that all the data has been overwritten, and that is intentional. Please note that the proposed solution takes a pessimistic approach, where the data is normalized by removing the spaces before and after the colons, and then the required amount of whitespaces is inserted.

An alternative approach would be to count the whitespaces before and after the colons. However, this could be more expensive than the current solution because some whitespaces could be in a different node than the node that contains the colon. If you really require a solution that use this approach, I will prepare a code example for it.

Please give me example code .

1 Like

@Manasahr please check the following code:

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

FindReplaceOptions opt = new FindReplaceOptions()
{
    ReplacingCallback = new EnsureWhiteSpacesHandler(0, 1)
};

doc.Range.Replace(":", string.Empty, opt);

doc.Save("C:\\Temp\\output.docx");
public class EnsureWhiteSpacesHandler : IReplacingCallback
{
    private readonly int _spaceBefore;
    private readonly int _spaceAfter;

    public EnsureWhiteSpacesHandler(int spaceBefore, int spaceAfter)
    {
        _spaceBefore = spaceBefore;
        _spaceAfter = spaceAfter;
    }

    ReplaceAction IReplacingCallback.Replacing(ReplacingArgs args)
    {
        var offset = args.MatchOffset;
        var listBefore = new List<SpacesPerRun>();
        var beforeSpaces = CountSpacesBefore(((Run)args.MatchNode).Text.Substring(0, offset), ((Run)args.MatchNode), listBefore);

        var listAfter = new List<SpacesPerRun>();
        var afterSpaces = CountSpacesAfter(((Run)args.MatchNode).Text.Substring(offset + 1), ((Run)args.MatchNode), listAfter);

        if (_spaceBefore > beforeSpaces)
        {
            var missingSpaces = _spaceBefore - beforeSpaces;
            var spacesBefore = "";
            for (var i = 0; i < missingSpaces; i++)
            {
                spacesBefore += " ";
            }

            
            ((Run)args.MatchNode).Text = ((Run)args.MatchNode).Text.Substring(0, offset) + spacesBefore + ((Run)args.MatchNode).Text.Substring(offset);
            offset += missingSpaces;
        }
        else if(_spaceBefore < beforeSpaces)
        {
            var extraSpaces = beforeSpaces - _spaceBefore;
            var indx = 0;
            while(indx < listBefore.Count && extraSpaces > 0)
                {
                var item = listBefore[indx];

                if(item.Spaces > 0)
                {
                    // First element is the node that contains the : we cannot remove it
                    if (indx == 0)
                    {
                        if(extraSpaces > item.Spaces)
                        {
                            item.Run.Text = item.Run.Text.Substring(item.Spaces - 1);
                            offset -= item.Spaces;
                        }
                        else
                        {
                            item.Run.Text = item.Run.Text.Substring(extraSpaces - 1);
                            offset -= extraSpaces;
                        }
                    }
                    else if (indx == listBefore.Count - 1)
                    {
                        item.Run.Text = item.Run.Text.Substring(0, item.Run.Text.Length - extraSpaces);
                    }
                    else
                    {
                        if (extraSpaces >= item.Spaces)
                        {
                            item.Run.Remove();
                        }
                        else
                        {
                            item.Run.Text = item.Run.Text.Substring(0, item.Run.Text.Length - extraSpaces);
                        }
                    }
                }

                indx++;
                extraSpaces -= item.Spaces;
            }
        }

        if (_spaceAfter > afterSpaces)
        {
            var missingSpaces = _spaceAfter - afterSpaces;
            var spacesAfter = "";
            for (var i = 0; i < missingSpaces; i++)
            {
                spacesAfter += " ";
            }

            ((Run)args.MatchNode).Text = ((Run)args.MatchNode).Text.Substring(0, offset + 1) + spacesAfter + ((Run)args.MatchNode).Text.Substring(offset+1);
        }
        else if (_spaceAfter < afterSpaces)
        {
            var extraSpaces = afterSpaces - _spaceAfter;
            var indx = 0;
            while (indx < listAfter.Count && extraSpaces > 0)
            {
                var item = listAfter[indx];

                if (item.Spaces > 0)
                {
                    // First element is the node that contains the : we cannot remove it
                    if (indx == 0)
                    {
                        item.Run.Text = extraSpaces > item.Spaces ? item.Run.Text.Substring(0, item.Run.Text.Length - item.Spaces) : item.Run.Text.Substring(0, item.Run.Text.Length - extraSpaces);
                    }
                    else if (indx == listAfter.Count - 1)
                    {
                        item.Run.Text = item.Run.Text.Substring(extraSpaces);
                    }
                    else
                    {
                        if (extraSpaces >= item.Spaces)
                        {
                            item.Run.Remove();
                        }
                        else
                        {
                            item.Run.Text = item.Run.Text.Substring(extraSpaces);
                        }
                    }
                }

                indx++;
                extraSpaces -= item.Spaces;
            }
        }

        return ReplaceAction.Skip;
    }

    private int CountSpacesBefore(string text, Run node, List<SpacesPerRun> list)
    {
        int counter = 0;
        var arr = text.ToList();
        var indx = arr.Count - 1;
        if (indx>=0)
        {
            var letter = arr[indx];
                
            while (letter == ' ')
            {
                counter++;
                indx--;

                letter = indx >= 0 ? arr[indx] : 'a';
            }
        }

        list.Add(new SpacesPerRun { Run = node, Spaces = counter });

        if (indx < 0)
        {
            var prevNode = node.PreviousSibling;
            if(prevNode != null && prevNode.NodeType == NodeType.Run)
            {
                Run prevRun = (Run)prevNode;
                counter += CountSpacesBefore(prevRun.Text, prevRun, list);
            }
        }

        return counter;
    }

    private int CountSpacesAfter(string text, Run node, List<SpacesPerRun> list)
    {
        int counter = 0;
        var arr = text.ToList();
        var indx = 0;
        if (arr.Any())
        {
            var letter = arr[indx];

            while (letter == ' ')
            {
                counter++;
                indx++;

                letter = indx < arr.Count ? arr[indx] : 'a';
            }
        }

        list.Add(new SpacesPerRun { Run = node, Spaces = counter });

        if (indx >= arr.Count)
        {
            var nextNode = node.NextSibling;
            if (nextNode != null && nextNode.NodeType == NodeType.Run)
            {
                Run nextRun = (Run)nextNode;
                counter += CountSpacesAfter(nextRun.Text, nextRun, list);
            }
        }

        return counter;
    }

    internal class SpacesPerRun
    {
        public Run Run { get; set; }
        public int Spaces { get; set; }
    }
}

input.docx (12.9 KB)
output.docx (10.0 KB)

@Manasahr Alternatively, you can use the following regular expression (regex) to achieve your desired outcome. In my opinion, this approach is clearer than the one mentioned above:

const int _amountSpaceAfter = 10;
const int _amountSpaceBefore = 5;
Document doc = new Document("C:\\Temp\\input.docx");

FindReplaceOptions opt = new FindReplaceOptions();
opt.UseSubstitutions = true;

var spacesAfter = "";
for (var i = 0; i < _amountSpaceAfter; i++)
{
    spacesAfter += " ";
}

var spacesBefore = "";
for (var i = 0; i < _amountSpaceBefore; i++)
{
    spacesBefore += " ";
}

Regex regex = new Regex("(\\s+)?([:])(\\s+)?");
doc.Range.Replace(regex, spacesBefore + "$2"+ spacesAfter, opt);

doc.Save("C:\\Temp\\output.docx");

Additionally, you can consider using a mixed solution where you count the number of whitespaces before and after, and return ReplaceAction.Replace; if the number of spaces does not meet the required criteria.

@eduardo.canal Thanks for your quick response, using above code I am not greeting what I expected output kindly help me.
Please see my input and expected output world document help me accordingly.
Expected_output.docx (13.0 KB)
input101 (1).docx (13.2 KB)

@Manasahr please note that you must set the desired amount of spaces before and after the colon:

const int _amountSpaceAfter = 1;
const int _amountSpaceBefore = 0;

output.docx (10.2 KB)

@eduardo.canal _amountSpaceAfter and _amountSpaceBefore should be dynamic if i set _amountSpaceBefore =10 and _amountSpaceAfter =10 on that time _amountSpaceBefore is not working as I expected working. please see the output i got in this case.
image.png (14.1 KB)

@Manasahr
Sorry for the confusion. I was testing the latest solution provided (the one that uses Regex). I have already fixed the bug in the previous solution.

if (_spaceBefore > beforeSpaces)
{
    var missingSpaces = _spaceBefore - beforeSpaces;
    var spacesBefore = "";
    for (var i = 0; i < missingSpaces; i++)
    {
        spacesBefore += " ";
    }

    ((Run)args.MatchNode).Text = ((Run)args.MatchNode).Text.Substring(0, offset) + spacesBefore + ((Run)args.MatchNode).Text.Substring(offset);
    offset += missingSpaces;
}