Can find the cell Node by Bookmark while containing the form field in it

Dear Sir

We have encountered another issue that is incompatible with the old version of the code , and we are using the version is 22.11

When I navigate to a cell in the table through Bookmark “C_d2109dc1_eb9a_42e1_a820_865dfea4da8b” and insert a FormFieldCheckBox into it, I open this document at next time. Similarly, when I use Bookmark to find this cell, I will find that it finds the last cell in the previous row. bellow is the document for demo

demo.7z (26.3 KB)

The code for inserting FormFieldCheckBox is as follows

DocumentBuilder db = new DocumentBuilder(firstRun.Document as Document);
db.MoveTo(firstRun);
db.InsertCheckBox("", selected, 0);

the code for finding the cell node base on Bookmark .

public static Aspose.Words.Tables.Cell FindCellByBookmark(DocumentBase doc, Bookmark bookmark)
{
    if (bookmark == null)
    {
        return null;
    }

    //Get the previous node
    Node node = bookmark.BookmarkStart.PreviousPreOrder(doc);

    //Get the cell
    while (node != null && node.NodeType != NodeType.Cell)
    {
        node = node.PreviousPreOrder(doc);
    }

    if (node != null && node.NodeType == NodeType.Cell)
    {
        return node as Aspose.Words.Tables.Cell;
    }

    return null;
}

@wengyeung In older version of Aspose.Words the BookmarkStart and BookmarkEnd nodes was allowed only in the inline level. But this restriction violates MS Word specification, where there are allowed also on body, row and cells level. So in old version of Aspose.Words while reading the document, if the bookmark was encountered on row, or cell level, it was moved inside the cell. In current version Aspose.Words preserves position of the bookmark in the same position, as it is in the document. Please see the document structure of your document:

As you can see the mentioned C_d2109dc1_eb9a_42e1_a820_865dfea4da8b is on the row level, not inside the cell, so your code selects the last cell of the previous row. After inserting the checkbox, the document structure looks like this:

Thank you very much for your reply. My question is, I used the original document, found the cell I wanted through Bookmark, and successfully inserted the Checkbox. Why couldn’t I find this cell when I opened the document again? We used the same version of Aspose.word twice, and the following document is our original document ,and you can see that location of Bookmark ‘’ is right at the cell .

original.7z (26.4 KB)

image.png (29.2 KB)

Due to the production accident caused by this issue, the version release was rolled back, and we are unable to provide a reasonable explanation to the customer. We hope to resolve this issue as soon as possible.

@wengyeung Unfortunately, I cannot reproduce the problem on my side. Here is a simple code I have used for testing:

string bookmakrName = "C_d2109dc1_eb9a_42e1_a820_865dfea4da8b";

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

// Get cell
Cell cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
Console.WriteLine(cell.Range.FormFields.Count == 1);
// Insert form field into the cell
builder.MoveTo(cell.LastChild);
builder.InsertCheckBox("", true, 0);

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

// Open the saved document and get the same cell.
doc = new Document(@"C:\Temp\out.doc");
cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
Console.WriteLine(cell.Range.FormFields.Count == 1);

Dear Sir
We can finally reproduce the problem because we encounter this problem when constructing Document objects using file streams on the internet.

var stream = new byte[];
doc = new Document(new MemoryStream(stream));

@wengyeung Thank you for additional information. I have tested with streams and still everything works as expected:

string bookmakrName = "C_d2109dc1_eb9a_42e1_a820_865dfea4da8b";

MemoryStream input = new MemoryStream(File.ReadAllBytes(@"C:\Temp\demo.doc"));

// Load document from stream
Document doc = new Document(input);
DocumentBuilder builder = new DocumentBuilder(doc);

// Get cell
Cell cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
Console.WriteLine(cell.Range.FormFields.Count == 1);
// Insert form field into the cell
builder.MoveTo(cell.LastChild);
builder.InsertCheckBox("", true, 0);

// Save output to stream.
MemoryStream output = new MemoryStream();
doc.Save(output, SaveFormat.Doc);
// Reset stream position to zero.
output.Position= 0;

// Open the saved document and get the same cell.
doc = new Document(output);
cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
Console.WriteLine(cell.Range.FormFields.Count == 1);

Dear Sir
There is one more procedure to clear the content at cell than the code for demo at my code ,please reference the bellow code that highlighted .
It causes the problem when we create the Instance of DocumentBuilder base on the document of property under the run object which cleared at last procedure .

string bookmakrName = "C_d2109dc1_eb9a_42e1_a820_865dfea4da8b";

MemoryStream input = new MemoryStream(File.ReadAllBytes(@"C:\Temp\demo.doc"));

// Load document from stream
Document doc = new Document(input);

// Get cell
Cell cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
///clear the content at first paragraph .
var para = cell.FirstParagraph;
if (para != null && para.Runs.Count > 0)
{
    para.Runs[0].Text = "";
}
DocumentBuilder builder = new DocumentBuilder(para.Runs[0].Document as Document);
///end of clearing 

Console.WriteLine(cell.Range.FormFields.Count == 1);
// Insert form field into the cell
builder.MoveTo(cell.LastChild);
builder.InsertCheckBox("", true, 0);

// Save output to stream.
MemoryStream output = new MemoryStream();
doc.Save(output, SaveFormat.Doc);
// Reset stream position to zero.
output.Position = 0;

// Open the saved document and get the same cell.
doc = new Document(output);
cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
Console.WriteLine(cell.Range.FormFields.Count == 1);

@wengyeung The problem is that there is no runs in the first paragraph of the cell:

So the following line throws:

DocumentBuilder builder = new DocumentBuilder(para.Runs[0].Document as Document);

You can fix this by changing code like this:

DocumentBuilder builder = new DocumentBuilder(para.Document as Document);

Dear Sir,

The problem still exists , But it will works as bellow that replace the non-empty content in the run object by a space char ,so we guess it’s a bug for the Aspose.word component .

var para = cell.FirstParagraph;
if (para != null && para.Runs.Count > 0)
{
    para.Runs[0].Text = " ";
}

@wengyeung Unfortunately, i cannot reproduce the problem. Also As i mentioned there is no Run nodes in the first paragraph in the referenced cell. Please see the screenshot provided in my previous answer. So the condition if (para != null && para.Runs.Count > 0) never pass on my side.

Could you please create a simple console application that will allow us to reproduce the problem?

Dear Alexey
bellow code can reproduce the problem after executes the method “InsertCheckbox”,you can find the bookmark location had changed from cell as the capture .

original.7z (58.9 KB)

public void InsertCheckbox()
{
    string bookmakrName = "C_d2109dc1_eb9a_42e1_a820_865dfea4da8b";
    this.SetCellCheckbox(bookmakrName, "true ", "d:\\original.doc", "d:\\1.doc");

    //this.SetCellCheckbox(bookmakrName, "false" ,"d:\\1.doc", "d:\\2.doc");

    //this.SetCellCheckbox(bookmakrName,"true" ,"d:\\2.doc", "d:\\3.doc");
}
public void SetCellCheckbox(string bookmakrName, string val, string fileFullPath, string saveFileFullpath)
{
    Document doc = new Document(fileFullPath);
    DocumentBuilder builder = new DocumentBuilder(doc);

    // Get cell
    Cell cell = FindCellByBookmark(doc, doc.Range.Bookmarks[bookmakrName]);
    var para = cell.FirstParagraph;
    if (para != null && para.Runs.Count > 0)
    {
        para.Runs[0].Text = "";
    }
    DocumentBuilder db = new DocumentBuilder(cell.Document as Document);
    db.MoveTo(cell.LastChild);
    db.InsertCheckBox("", val.ToLower().Equals("true"), 0);
    // Save output.
    doc.Save(saveFileFullpath);
}

private Cell FindCellByBookmark(DocumentBase doc, Aspose.Words.Bookmark bookmark)
{
    if (bookmark == null)
    {
        return null;
    }

    //Get the previous node
    Node node = bookmark.BookmarkStart.PreviousPreOrder(doc);

    //Get the cell
    while (node != null && node.NodeType != NodeType.Cell)
    {
        node = node.PreviousPreOrder(doc);
    }

    if (node != null && node.NodeType == NodeType.Cell)
    {
        return node as Aspose.Words.Tables.Cell;
    }

    return null;
}

@wengyeung Thank you for additional information.
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): WORDSNET-25253

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.