Loosing Bookmarks inside BuildingBlocks after Save

Hello support, I’m posting here but I should have support through the purchase of a licence (subscription ends on January 2023). However I didn’t manage to post on the “paid support page” probably because my account is not linked with the licence (purchased using another account).

My issue is I’m loosing bookmarks in building blocks after calling Aspose.Word.Document.Save(Stream).

I’m using Aspose.Word 22.10.0 (last version). Here are the steps to reproduce :

  • open a user created dotx file using
var source =
   new Document(
      sourceStream,
      new LoadOptions
      {
         PreserveIncludePictureField = true,
      });
  • Loop through Building Blocks and then loop through Bookmarks like this, all bookmarks are present at this stage
source
   .GlossaryDocument
   .OfType<BuildingBlock>()
   .Where(bb => bb.Name.StartsWith("XYZ"))
   .Select(bb => (bb.Name, Bookmarks: bb.Range.Bookmarks.Select(bm => bm.Name)));
  • Save the document using
var cloneStream = new MemoryStream();
source.Save(cloneStream, SaveFormat.Dotx);
  • Loop again through Building Blocks and Bookmarks of the “source” variable : all building blocks are presents but some have no bookmarks

Any help much appreciated :slight_smile:

@gilles.figuiere.pro Could you please attach your input and output DOTX files here for testing? We will check the issue and provide you more information.

Hi @alexey.noskov and thanks for your reply.

Please find attached the 2 files requested. These are not files used in production for confidentiality purpose but a demo files reproducing the issue : BookmarksInBuildingBlocks.zip (34.7 KB)

Here are the log of the tests. We can clearly see that :

  • “Building block 1” is missing bookmark “Bookmark 2” and “Bookmark 3”
  • “Building block 2” contains the right bookmarks
  • “Building block 3” is missing bookmark “Bookmark 3”

Test output

Source before Saving

Building block <Building Block 1> => Contains <3> bookmarks
  Bookmark name <Bookmark1>
  Bookmark name <Bookmark2>
  Bookmark name <Bookmark3>
Building block <Building Block 2> => Contains <2> bookmarks
  Bookmark name <Bookmark2>
  Bookmark name <Bookmark3>
Building block <Building Block 3> => Contains <1> bookmarks
  Bookmark name <Bookmark3>


Source after Saving

Building block <Building Block 1> => Contains <1> bookmarks
  Bookmark name <Bookmark1>
Building block <Building Block 2> => Contains <2> bookmarks
  Bookmark name <Bookmark2>
  Bookmark name <Bookmark3>
Building block <Building Block 3> => Contains <0> bookmarks


Copy

Building block <Building Block 1> => Contains <1> bookmarks
  Bookmark name <Bookmark1>
Building block <Building Block 2> => Contains <2> bookmarks
  Bookmark name <Bookmark2>
  Bookmark name <Bookmark3>
Building block <Building Block 3> => Contains <0> bookmarks

Copy location XXXXX\AppData\Local\Temp\tmpD39.tmp

Test source

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Aspose.Words;
using Aspose.Words.BuildingBlocks;
using Aspose.Words.Loading;
using Xunit;
using Xunit.Abstractions;

namespace Unit.Tests;

public class BookmarksInBuildingBlocksTest
{
    private readonly ITestOutputHelper _testOutputHelper;

    public BookmarksInBuildingBlocksTest(ITestOutputHelper testOutputHelper) =>
        _testOutputHelper = testOutputHelper;

    private static IEnumerable<(string Name, IEnumerable<string> Bookmarks)>
        GetBookmarks(Document document) =>
        document
            .GlossaryDocument
            .OfType<BuildingBlock>()
            .Where(bb => bb.Name.StartsWith("Building Block"))
            .Select(bb => (bb.Name, Bookmarks: bb.Range.Bookmarks.Select(bm => bm.Name)));

    private static IEnumerable<string> ToStrings(Document document)
    {
        foreach (var block in GetBookmarks(document).OrderBy(b => b.Name))
        {
            yield return $"Building block <{block.Name}> => Contains <{block.Bookmarks.Count()}> bookmarks";
            foreach (var bookmark in block.Bookmarks.OrderBy(e => e))
            {
                yield return $"  Bookmark name <{bookmark}>";
            }
        }
    }

    private void Display(Document document) =>
        ToStrings(document).ToList().ForEach(l => _testOutputHelper.WriteLine(l));

    [Fact]
    private void MissingBookmarks()
    {
         const string sourceFileName = @"XXXXXXXXXX\BookmarksInBuildingBlocks.dotx";

        var source = new Document(
            sourceFileName,
            new LoadOptions
            {
                PreserveIncludePictureField = true,
            });

        _testOutputHelper.WriteLine("Source before Saving\n");
        Display(source);

        _testOutputHelper.WriteLine("\n\n");

        var tempFileName = Path.GetTempFileName();
        source.Save(tempFileName, SaveFormat.Dotx);

        // Here we can see source is modified by calling Save,
        // even if we're asking to save to another file
        _testOutputHelper.WriteLine("Source after Saving\n");
        Display(source);
        
        // Lets try to open the copy to see if the bookmarks are also missing
        var copy = new Document(
            tempFileName,
            new LoadOptions
            {
                PreserveIncludePictureField = true,
            });

        _testOutputHelper.WriteLine("\n\n");

        _testOutputHelper.WriteLine("Copy\n");
        Display(copy);
        
        _testOutputHelper.WriteLine($"\nCopy location {tempFileName}");
    }
}

@gilles.figuiere.pro Thank you for additional information. I have managed to reproduce the problem on my side. I have logged it as WORDSNET-24513. We will investigate the issue further and provide you more information.
For now, I can say that the problem occurs because glossary document is validated in the same way as the main document and it is not allowed to have duplicated bookmarks names in the document. So Aspose.Words removes the duplicated bookmarks.

1 Like

Hi Alexey and thanks for your feedback (very appreciated). Where could I follow the advancement of WORDSNET-24513? In this thread? Elsewhere?

@gilles.figuiere.pro You can track status of the issue here in this thread. Also, we will be sure to notify you once the issue is resolved or we have additional information for you.

Hi @alexey.noskov, I don’t know what I’ve done with the thread which is now “private”. I guess it is because I tried to write a private message to you.

My point is : I’ll be off next week and would like to know if there’s a chance to get an answer before I’ll come back on Monday 7th October? We have high expectations regarding a behavior change of Aspose Word dealing with Bookmarks in BuildingBlock (Bookmark deduplication per BuildingBlock, not on whole Glossary). The document I have to build must have this format because it is beeing processed after build by another process that breaks if bookmarks in building blocks are missing.

Thanks for your answer.

@gilles.figuiere.pro I have made the thread public again.
The issue is still in the queue for analysis, I have asked the responsible developer to take a look at it shortly. We will be sure to let you know once we have news regarding it.

1 Like

Hi @alexey.noskov, I saw the issue status changed few days ago to “In code review” that’s encouraging :slight_smile:
Do you know what will be the process after? Publication in a new release? How much time will it take? Does Aspose has a new release frequency (e.g. every 15 days)?

Thanks for your answer.

@gilles.figuiere.pro If everything goes smoothly the fix will be included into the next 22.12 version of Aspose.Words, which will be released at the beginning of the next month. We will be sure to let you know once the fix is available.
Aspose.Words releases are published regularly every month.

1 Like

The issues you have found earlier (filed as WORDSNET-24513) have been fixed in this Aspose.Words for .NET 22.12 update also available on NuGet.

1 Like