A potential bug in the latest version of Aspose 25.1.0

Hi Aspose support,
We are using the paid version of Aspose Word but the library with version 24.8.0 is not responding with the expected results while searching the word document for specific text.
The issue was reproduced in the latest version 25.10 as well.

when we search the documents with specific text. The Aspose library returns no result for the first time, but when the class containing Aspose liberay references is initialized again and same methods from Aspose library searches the text and responds correctly.

This is increasing the performance overhead on outside and leading to production issues as well.

The console app has been attached to reproduce the issue:
https://www.dropbox.com/scl/fo/4woip1zjusikt4x0a5fs1/AHjjhNhFnIoVS4KpJN1e4pQ?rlkey=ufjj8g1l2yz1a34bowkb7pwy4&st=z4psvq0f&dl=1

Could you please look into the issue and solve It please?

@omnicasa As I can see in your code you are modifying the document while replacing. This might affect the replacing process. So I would suggest to search in backward direction:

doc.Range.Replace(new Regex(pattern), "", new FindReplaceOptions() { ReplacingCallback = new FindAndInsertBookmark(), Direction = FindReplaceDirection.Backward });

Alternatively, you can use the following code to wrap the matched text into bookmarks:

const string pattern = @"\{\{(.*?)\}\}";
Regex regex = new Regex(pattern);

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

// Replace placeholder with itself to make it to be represented as a single run.
FindReplaceOptions opt = new FindReplaceOptions();
opt.UseSubstitutions = true;
doc.Range.Replace(regex, "$0", opt);
List<Run> runs = doc.GetChildNodes(NodeType.Run, true).Cast<Run>()
    .Where(r => regex.IsMatch(r.Text)).ToList();

int bkIndex = 0;
foreach (Run r in runs)
{
    string bkName = $"bookmakrk_{bkIndex++}";
    r.ParentNode.InsertBefore(new BookmarkStart(doc, bkName), r);
    r.ParentNode.InsertAfter(new BookmarkEnd(doc, bkName), r);
}

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

Such approach is less complicated then the approach with IReplacingCallback.

Hi Aspose,
I have changed the implementation as per above suggestion and we can get the tags consistently BUT there is another potential bug where the Aspose library does not return the correct X, Y coordinates of the searched word for the first time.

When we wait for a second using Thread.Sleep() and call the same process again, it responds with correct coordinates.

This leads to the following problems on our side

  • It is placing the signature (object) in an inconsistent position.
  • it is increasing the performance overhead on our side and leading to production issues as well.

Sample App to reproduce issue:
https://www.dropbox.com/scl/fi/cgeoqqoso5aot593occrf/AsposeWordIssues_v2_issues.zip?rlkey=b23j9eb9ee175s0il0gvwxnz2&st=rm6mknr4&dl=1

Screenshot:

@omnicasa As I can see you are using the first suggested approach and partially the second approach. But the second approach is used improperly, since you are adding the bookmarks after creating LayoutCollector and LayoutEnumerator.

But the difference in the coordinates you highlighted on the screenshot occurs because you are setting the license in your project incorrectly. So the on the first iteration Aspose.Words works in evaluation mode and an evaluation text is added at the beginning of the document and it pushes the content down in the document.
You should set the license on application start or in a static constructor of your class to resolve the problem.

If you would like to use the second approach suggested above, you can modify your code like this:

private DocxToPDFConvertorResponse FindSignTagsWithCoordinates()
{
    DocxToPDFConvertorResponse response = new DocxToPDFConvertorResponse();
    response.Coordinates = new List<Coordinates>();

    // Replace placeholder with itself to make it to be represented as a single run.
    FindReplaceOptions opt = new FindReplaceOptions();
    opt.UseSubstitutions = true;
            
    Regex regex = new Regex(pattern);
    doc.Range.Replace(regex, "$0", opt);
    List<Run> runs = doc.GetChildNodes(NodeType.Run, true).Cast<Run>()
        .Where(r => regex.IsMatch(r.Text)).ToList();

    List<string> tmpBookmakrs = new List<string>();
    int bkIndex = 0;
    foreach (Run r in runs)
    {
        string bkName = $"bookmakrk_{bkIndex++}";
        r.ParentNode.InsertBefore(new BookmarkStart(doc, bkName), r);
        r.ParentNode.InsertAfter(new BookmarkEnd(doc, bkName), r);
        tmpBookmakrs.Add(bkName);
    }

    LayoutCollector layoutCollector = new LayoutCollector(doc);
    LayoutEnumerator layoutEnumerator = new LayoutEnumerator(doc);

    foreach (string bkName in tmpBookmakrs)
    {
        Bookmark bk = doc.Range.Bookmarks[bkName];
        // get rectangle occupied by the bookmark.
        layoutEnumerator.Current = layoutCollector.GetEntity(bk.BookmarkStart);
        RectangleF startRect = layoutEnumerator.Rectangle;
        layoutEnumerator.Current = layoutCollector.GetEntity(bk.BookmarkEnd);
        RectangleF endRect = layoutEnumerator.Rectangle;
        // Union of the rectangle will be the result.
        RectangleF resultRect = RectangleF.Union(startRect, endRect);
        int pageNo = layoutEnumerator.PageIndex;

        // Create a Coordinates object
        response.Coordinates.Add(new Coordinates
        {
            Left = resultRect.X,
            Top = resultRect.Y,
            PageNumber = pageNo,
            ParagraphText = bk.Text
        });

        Console.WriteLine($"Run Text: {bk.Text}, Coordinates: (X:{resultRect.X}, Y:{resultRect.Y}), Page {pageNo}");
    }

    return response;
}

In addition, if I understand your goal properly, you need to replace placeholders with signature images. It is not required to determine coordinates for this. You can simply replace placeholder with signature image shape.

Hi @alexey.noskov, Thank you for your support.

Both of our issues are solved.

Regarding your question about the goal. We do not need to put a rectangle. We send the coordinates to a third-party service to put the signature on the exact place. It is working now.

@omnicasa It is perfect that you managed to resolve the problems. Please feel free to ask in case of any further issues. We are always glad to help you.