Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloning sheet doesn't clone Drawing part #1787

Closed
SzymonSel opened this issue Sep 9, 2024 · 26 comments
Closed

Cloning sheet doesn't clone Drawing part #1787

SzymonSel opened this issue Sep 9, 2024 · 26 comments
Assignees

Comments

@SzymonSel
Copy link

SzymonSel commented Sep 9, 2024

Describe the bug
I have a template xlsx file, with template sheet, which in turn are cloned and their content is replaced ie. Cell values and images.
After opening the file, the cell values are unique for each cloned Sheet except for the images which are the same, namely the last replaced image.

Screenshots
image

To Reproduce
I have this code which clones a given Sheet:

    public static async void CloneSheet(SpreadsheetDocument spreadSheet, string sheetName, string newSheetName) {
          // Get the source sheet
          var sheets = spreadSheet.WorkbookPart.Workbook.Sheets;
          var sourceSheet = sheets.Elements<Sheet>().FirstOrDefault(s => s.Name == sheetName);
          if (sourceSheet == null) {
              throw new ArgumentException($"Sheet with name {sheetName} does not exist.");
          }

          // Get the source worksheet part
          var sourceSheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(sourceSheet.Id);

          // Create a new worksheet part
          var newSheetPart = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
          newSheetPart.Worksheet = (Worksheet)sourceSheetPart.Worksheet.Clone();

          // Clone the relationships
          foreach (var rel in sourceSheetPart.Parts) {
              newSheetPart.AddPart(rel.OpenXmlPart, rel.RelationshipId);
          }

          // Clone DrawingsPart and its related ImageParts
          if (sourceSheetPart.DrawingsPart != null) {
              var sourceDrawingsPart = sourceSheetPart.DrawingsPart;
              DrawingsPart newDrawingsPart;
              
              if (newSheetPart.DrawingsPart == null) {
                  newDrawingsPart = newSheetPart.AddNewPart<DrawingsPart>();
              } else {
                  newDrawingsPart = newSheetPart.DrawingsPart;
              }

              newDrawingsPart.WorksheetDrawing = (WorksheetDrawing)sourceDrawingsPart.WorksheetDrawing.Clone();

              var imagePartsToClone = sourceDrawingsPart.ImageParts.ToList();

              foreach (var imagePart in imagePartsToClone) {
                  var newImagePart = newDrawingsPart.AddImagePart(imagePart.ContentType);


                  using (var stream = imagePart.GetStream()) {
                          newImagePart.FeedData(stream);
                  }

                  // Update the BlipFill.Blip.Embed.Value to reference the new image part
                  foreach (var blip in newDrawingsPart.WorksheetDrawing.Descendants<DocumentFormat.OpenXml.Drawing.Blip>()) {
                      if (blip.Embed.Value == sourceDrawingsPart.GetIdOfPart(imagePart)) {
                          var newId = newDrawingsPart.GetIdOfPart(newImagePart);
                          blip.Embed.Value = newId;
                      }
                  }
              }
          }

          // Create a new sheet and add it to the workbook
          var newSheetId = spreadSheet.WorkbookPart.GetIdOfPart(newSheetPart);
          var newSheet = new Sheet {
              Id = newSheetId,
              SheetId = sheets.Elements<Sheet>().Max(s => s.SheetId.Value) + 1,
              Name = newSheetName // todo: Ensure the new sheet name is unique
          };

          sheets.Append(newSheet);

          // Save the workbook
          spreadSheet.WorkbookPart.Workbook.Save();
      }

Observed behavior
What I discovered is that the Clone() method on the sourceDrawingsPart.WorksheetDrawing doesn't work and in effect, make all the cloned sheets share the DrawingsPart.

Expected behavior
How can achieve DrawingsPart cloning to work?

Desktop (please complete the following information):

  • OS: MacOS, Windows
  • .NET Target: .NET Core, .NET Framework
  • DocumentFormat.OpenXml Version: 3.1.0, 3.0.2
@SzymonSel
Copy link
Author

@twsouthwick is there an ETA on this issue?

@twsouthwick
Copy link
Member

@SzymonSel sorry for the delay. I've found some issues in this area, but can't repro your setup as I need to know how you created the initial spreadsheet. Can you provide a fully self-contained repro?

@SzymonSel
Copy link
Author

SzymonSel commented Nov 20, 2024

I've created a sample workbook in Excel, containing a single sheet with a sample image and some cell content. Please find below some sample code execution.
Template file: Book1.xlsx

Sample code:

private async Task<FileViewModel> TemplateStripped() {
    var template_path = _mediaFileStore.Combine("Templates", "Book1.xlsx");
    var templateSheet = "Sheet1";

    using (var ms = new MemoryStream()) {
        using (var oldMemoryStream = await _mediaFileStore.GetFileStreamAsync(template_path)) {
            await oldMemoryStream.CopyToAsync(ms);
        }

        ms.Position = 0;

        using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(ms, true)) {
            for (int i = 0; i < 4; i++) {
                var newParkSheetName = $"New sheet {i}";
                SpreadsheetService.CloneSheet(spreadSheet, templateSheet, newParkSheetName);
            }

            SpreadsheetService.RemoveSheet(spreadSheet, templateSheet);
            
            spreadSheet.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
            spreadSheet.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
            spreadSheet.WorkbookPart.Workbook.Save();

            SpreadsheetService.ResetView(spreadSheet);
        }

        ms.Position = 0;

        var fileViewModel = new FileViewModel {
            FileName = $"Book1_generated.xlsx",
            ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            FileStream = ms.ToArray()
        };

        return fileViewModel;
    }
}

The resulting, generated file structure is as such:
Image

And the generated file itself: Book1_generated.xlsx

As you can surely notice, the Drawing1.xml isn't cloned, but rather shared between the sheets - now replace an image, replaces it in all the sheets - They're the same picture! ("The Office" reference intended ;))

@twsouthwick
Copy link
Member

Can you share the methods such as SpreadsheetService.CloneSheet? These are not in the sdk

@twsouthwick
Copy link
Member

nevermind - I see it

@twsouthwick
Copy link
Member

twsouthwick commented Nov 20, 2024

Ok - still missing some methods:

  • RemoveSheet
  • ResetView

In the future, something like this would be ideal:

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using System;
using System.IO;
using System.Linq;

var templateSheet = "Sheet1";

using (var ms = File.Open("result.xlsx", FileMode.Create,FileAccess.ReadWrite))
{
    using (var oldMemoryStream = File.OpenRead("Book1.xlsx"))
    {
        await oldMemoryStream.CopyToAsync(ms);
    }

    ms.Position = 0;

    using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(ms, true))
    {
        for (int i = 0; i < 4; i++)
        {
            var newParkSheetName = $"New sheet {i}";
            CloneSheet(spreadSheet, templateSheet, newParkSheetName);
        }

        RemoveSheet(spreadSheet, templateSheet);

        spreadSheet.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
        spreadSheet.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
        spreadSheet.WorkbookPart.Workbook.Save();

        ResetView(spreadSheet);
    }
}

static async void CloneSheet(SpreadsheetDocument spreadSheet, string sheetName, string newSheetName)
{
    // Get the source sheet
    var sheets = spreadSheet.WorkbookPart.Workbook.Sheets;
    var sourceSheet = sheets.Elements<Sheet>().FirstOrDefault(s => s.Name == sheetName);
    if (sourceSheet == null)
    {
        throw new ArgumentException($"Sheet with name {sheetName} does not exist.");
    }

    // Get the source worksheet part
    var sourceSheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(sourceSheet.Id);

    // Create a new worksheet part
    var newSheetPart = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();
    newSheetPart.Worksheet = (Worksheet)sourceSheetPart.Worksheet.Clone();

    // Clone the relationships
    foreach (var rel in sourceSheetPart.Parts)
    {
        newSheetPart.AddPart(rel.OpenXmlPart, rel.RelationshipId);
    }

    // Clone DrawingsPart and its related ImageParts
    if (sourceSheetPart.DrawingsPart != null)
    {
        var sourceDrawingsPart = sourceSheetPart.DrawingsPart;
        DrawingsPart newDrawingsPart;

        if (newSheetPart.DrawingsPart == null)
        {
            newDrawingsPart = newSheetPart.AddNewPart<DrawingsPart>();
        }
        else
        {
            newDrawingsPart = newSheetPart.DrawingsPart;
        }

        newDrawingsPart.WorksheetDrawing = (WorksheetDrawing)sourceDrawingsPart.WorksheetDrawing.Clone();

        var imagePartsToClone = sourceDrawingsPart.ImageParts.ToList();

        foreach (var imagePart in imagePartsToClone)
        {
            var newImagePart = newDrawingsPart.AddImagePart(imagePart.ContentType);


            using (var stream = imagePart.GetStream())
            {
                newImagePart.FeedData(stream);
            }

            // Update the BlipFill.Blip.Embed.Value to reference the new image part
            foreach (var blip in newDrawingsPart.WorksheetDrawing.Descendants<DocumentFormat.OpenXml.Drawing.Blip>())
            {
                if (blip.Embed.Value == sourceDrawingsPart.GetIdOfPart(imagePart))
                {
                    var newId = newDrawingsPart.GetIdOfPart(newImagePart);
                    blip.Embed.Value = newId;
                }
            }
        }
    }

    // Create a new sheet and add it to the workbook
    var newSheetId = spreadSheet.WorkbookPart.GetIdOfPart(newSheetPart);
    var newSheet = new Sheet
    {
        Id = newSheetId,
        SheetId = sheets.Elements<Sheet>().Max(s => s.SheetId.Value) + 1,
        Name = newSheetName // todo: Ensure the new sheet name is unique
    };

    sheets.Append(newSheet);

    // Save the workbook
    spreadSheet.WorkbookPart.Workbook.Save();
}

Even better would be unit tests like #1814 that linked a repo that has an easily cloneable set of tests.

@SzymonSel
Copy link
Author

SzymonSel commented Nov 20, 2024

Ok, got it! I will do better next time. Thanks for the guidndance and sorry for the inconvenience. Below are the missing methods.

public static void ResetView(SpreadsheetDocument spreadSheet, string sheetName = "") {
    WorkbookPart workbookPart = spreadSheet.WorkbookPart;
    Sheet firstSheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault();
    if (firstSheet != null) {
        sheetName = firstSheet.Name;
    }

    Sheet sheet = spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().First(s => s.Name == sheetName);
    string relationshipId = sheet.Id.Value;
    WorksheetPart worksheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(relationshipId);

    Row firstRow = worksheetPart.Worksheet.Descendants<Row>().FirstOrDefault();
    Cell firstCell = firstRow?.Descendants<Cell>().FirstOrDefault();
    string firstCellReference = firstCell?.CellReference?.Value;

    SheetViews sheetViews = worksheetPart.Worksheet.GetFirstChild<SheetViews>();
    if (sheetViews == null) {
        sheetViews = new SheetViews();
        worksheetPart.Worksheet.InsertAfter(sheetViews, worksheetPart.Worksheet.Elements<SheetData>().FirstOrDefault());
    }

    SheetView sheetView = sheetViews.Elements<SheetView>().FirstOrDefault();
    if (sheetView == null) {
        sheetView = new SheetView() { WorkbookViewId = 0 };
        sheetViews.Append(sheetView);
    } else {
        sheetView.RemoveAllChildren<Selection>();
    }

    sheetView.RemoveAllChildren<Selection>();

    Selection selection = new Selection() { ActiveCell = "A1", SequenceOfReferences = new ListValue<StringValue>() { InnerText = "A1" } };
    sheetView.Append(selection);

    worksheetPart.Worksheet.Save();
}

public static void RemoveSheet(SpreadsheetDocument spreadSheet, string sheetName) {
    var theSheet = spreadSheet.WorkbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sheetName);
    if (theSheet == null) {
        return;
    }

    var worksheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(theSheet.Id);
    spreadSheet.WorkbookPart.DeletePart(worksheetPart);
    theSheet.Remove();

    if (spreadSheet.WorkbookPart.CalculationChainPart != null) {
        spreadSheet.WorkbookPart.DeletePart(spreadSheet.WorkbookPart.CalculationChainPart);
    }

    spreadSheet.WorkbookPart.Workbook.Save();
    spreadSheet.Save();
}

@SzymonSel
Copy link
Author

Even better would be unit tests like #1814 that linked a repo that has an easily cloneable set of tests.

The unit test are a pretty neat idea! I'll have I go at it next time though, since I'm not very fluent with the intricacies of the SDK and I personally struggeld to programaticaly test/debug the root of the problem. It was rather a game of Minesweeper by a 3 year old :)

@twsouthwick
Copy link
Member

haha no problem. whatever you can give us is a help - at least something that can be run outside of your environment is a huge help (I can easily copy/paste and run it).

Taking a look now....

@twsouthwick
Copy link
Member

I think this is user error and not an issue in the SDK. If I remove the following part:

-    // Clone the relationships
-    foreach (var rel in sourceSheetPart.Parts)
-    {
-        newSheetPart.AddPart(rel.OpenXmlPart, rel.RelationshipId);
-    }

then it does appear to clone it. Am I missing something?

@SzymonSel
Copy link
Author

SzymonSel commented Nov 21, 2024

Not quite, this produces a malformed document:
Image

Although, as I can see, the drawing parts have indeed been cloned:
Image

But nevertheless this time, the ImageParts are missing completly.

The foreach loop is an attempt to mimic a deep clone, since it became clear, that a deep clone is not having place.

@twsouthwick
Copy link
Member

This is sounding more like a user error rather than an SDK issue.

@mikeebowen @tomjebo can you take a look at this?

@twsouthwick twsouthwick assigned tomjebo and mikeebowen and unassigned twsouthwick Nov 22, 2024
@SzymonSel
Copy link
Author

Shouldn't Clone() work recursively? I would imagine not of the code between the lines newSheetPart.Worksheet = (Worksheet)sourceSheetPart.Worksheet.Clone(); and // Create a new sheet and add it to the workbook shouldn't really be needed.

@SzymonSel
Copy link
Author

Would you need some further examples?

@SzymonSel
Copy link
Author

Hi again, any update on the issue would be highly appreciated.

@mkaszewiak
Copy link
Collaborator

mkaszewiak commented Jan 14, 2025

Hi @SzymonSel

I looked at your code and Taylor is correct you need to remove the foreach loop

   /*    foreach (var rel in sourceSheetPart.Parts)
       {
           Console.WriteLine(rel.RelationshipId);

           newSheetPart.AddPart(rel.OpenXmlPart, rel.RelationshipId);
       }*/

The issue is when you clone the drawing part, clone doesn't update the xml so the drawing element still has the id of the reloationship of the removed drawing part. To go around this issue you need to get relationship id of the new drawing part from the sheet part. As in the example below:

    var ralId2 = newSheetPart.GetIdOfPart(newSheetPart.DrawingsPart);
    var drawing = newSheetPart.Worksheet.Descendants<Drawing>().FirstOrDefault();

    if (drawing != null)
    {
        drawing.Id = ralId2;
    }
    else 
    {
        newSheetPart.Worksheet.AddChild(new Drawing()
        { 
        Id = ralId2,
        });
    }

As as side note there is no need to call Save() method within the using statement.

Below is full working sample

using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Drawing.Diagrams;

var templateSheet = "Sheet1";
var filePath = @"C:\source\TestDocuments\Book1.xlsx";

using (var ms = File.Open(@"C:\source\TestDocuments\result2.xlsx", FileMode.Create, FileAccess.ReadWrite))
{
    using (var oldMemoryStream = File.OpenRead(filePath))
    {
        await oldMemoryStream.CopyToAsync(ms);
    }

    ms.Position = 0;

    using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(ms, true))
    {
        for (int i = 0; i < 4; i++)
        {
            var newParkSheetName = $"Sheet {i}";
            CloneSheet(spreadSheet, templateSheet, newParkSheetName);
        }

        RemoveSheet(spreadSheet, templateSheet);

        spreadSheet.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
        spreadSheet.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
        
        //Save is unesscessary withing using statement 
        //spreadSheet.WorkbookPart.Workbook.Save();

        ResetView(spreadSheet);
    }
}

static async void CloneSheet(SpreadsheetDocument spreadSheet, string sheetName, string newSheetName)
{
    // Get the source sheet
    var sheets = spreadSheet.WorkbookPart.Workbook.Sheets;
    var sourceSheet = sheets.Elements<Sheet>().FirstOrDefault(s => s.Name == sheetName);

    /*    if (sourceSheet == null)
        {
            throw new ArgumentException($"Sheet with name {sheetName} does not exist.");
        }*/

    // Get the source worksheet part
    var sourceSheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(sourceSheet.Id);

    // Create a new worksheet part
    var newSheetPart = spreadSheet.WorkbookPart.AddNewPart<WorksheetPart>();


    newSheetPart.Worksheet = (Worksheet)sourceSheetPart.Worksheet.Clone();



    // Clone the relationships
    /*    foreach (var rel in sourceSheetPart.Parts)
        {
            Console.WriteLine(rel.RelationshipId);

            newSheetPart.AddPart(rel.OpenXmlPart, rel.RelationshipId);
        }*/
    DrawingsPart newDrawingsPart;
    // Clone DrawingsPart and its related ImageParts
    if (sourceSheetPart.DrawingsPart != null)
    {
        var sourceDrawingsPart = sourceSheetPart.DrawingsPart;


        if (newSheetPart.DrawingsPart == null)
        {
            newDrawingsPart = newSheetPart.AddNewPart<DrawingsPart>();
        }
        else
        {
            newDrawingsPart = newSheetPart.DrawingsPart;
        }

        newDrawingsPart.WorksheetDrawing = (WorksheetDrawing)sourceDrawingsPart.WorksheetDrawing.Clone();

        var imagePartsToClone = sourceDrawingsPart.ImageParts.ToList();

        foreach (var imagePart in imagePartsToClone)
        {
            var newImagePart = newDrawingsPart.AddImagePart(imagePart.ContentType);


            using (var stream = imagePart.GetStream())
            {
                newImagePart.FeedData(stream);
            }

            // Update the BlipFill.Blip.Embed.Value to reference the new image part
            foreach (var blip in newDrawingsPart.WorksheetDrawing.Descendants<DocumentFormat.OpenXml.Drawing.Blip>())
            {
                if (blip.Embed.Value == sourceDrawingsPart.GetIdOfPart(imagePart))
                {
                    var newId = newDrawingsPart.GetIdOfPart(newImagePart);
                    blip.Embed.Value = newId;
                }
            }
        }
    }

    var ralId2 = newSheetPart.GetIdOfPart(newSheetPart.DrawingsPart);
    var drawing = newSheetPart.Worksheet.Descendants<Drawing>().FirstOrDefault();

    if (drawing != null)
    {
        drawing.Id = ralId2;
    }
    else
    {
        newSheetPart.Worksheet.AddChild(new Drawing()
        {
            Id = ralId2,
        });
    }
    // Create a new sheet and add it to the workbook
    var newSheetId = spreadSheet.WorkbookPart.GetIdOfPart(newSheetPart);
    var newSheet = new Sheet
    {
        Id = newSheetId,
        SheetId = sheets.Elements<Sheet>().Max(s => s.SheetId.Value) + 1,
        Name = newSheetName // todo: Ensure the new sheet name is unique

    };

    sheets.Append(newSheet);

}
static void ResetView(SpreadsheetDocument spreadSheet, string sheetName = "")
{
    WorkbookPart workbookPart = spreadSheet.WorkbookPart;
    Sheet firstSheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault();
    if (firstSheet != null)
    {
        sheetName = firstSheet.Name;
    }

    Sheet sheet = spreadSheet.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().First(s => s.Name == sheetName);
    string relationshipId = sheet.Id.Value;
    WorksheetPart worksheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(relationshipId);

    Row firstRow = worksheetPart.Worksheet.Descendants<Row>().FirstOrDefault();
    Cell firstCell = firstRow?.Descendants<Cell>().FirstOrDefault();
    string firstCellReference = firstCell?.CellReference?.Value;

    SheetViews sheetViews = worksheetPart.Worksheet.GetFirstChild<SheetViews>();
    if (sheetViews == null)
    {
        sheetViews = new SheetViews();
        worksheetPart.Worksheet.InsertAfter(sheetViews, worksheetPart.Worksheet.Elements<SheetData>().FirstOrDefault());
    }

    SheetView sheetView = sheetViews.Elements<SheetView>().FirstOrDefault();
    if (sheetView == null)
    {
        sheetView = new SheetView() { WorkbookViewId = 0 };
        sheetViews.Append(sheetView);
    }
    else
    {
        sheetView.RemoveAllChildren<Selection>();
    }

    sheetView.RemoveAllChildren<Selection>();

    Selection selection = new Selection() { ActiveCell = "A1", SequenceOfReferences = new ListValue<StringValue>() { InnerText = "A1" } };
    sheetView.Append(selection);

    //Save is unnecessary withing using statement 
    // worksheetPart.Worksheet.Save();
}

static void RemoveSheet(SpreadsheetDocument spreadSheet, string sheetName)
{
    var theSheet = spreadSheet.WorkbookPart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sheetName);
    if (theSheet == null)
    {
        return;
    }

    var worksheetPart = (WorksheetPart)spreadSheet.WorkbookPart.GetPartById(theSheet.Id);
    spreadSheet.WorkbookPart.DeletePart(worksheetPart);
    theSheet.Remove();

    if (spreadSheet.WorkbookPart.CalculationChainPart != null)
    {
        spreadSheet.WorkbookPart.DeletePart(spreadSheet.WorkbookPart.CalculationChainPart);
    }

    //Save is unnecessary withing using statement 
    //spreadSheet.WorkbookPart.Workbook.Save();
   // spreadSheet.Save();
}

@tomjebo tomjebo removed their assignment Jan 16, 2025
@SzymonSel
Copy link
Author

Thank you @mkaszewiak for your detailed reply. I've copied your solution line by line, but sadly I still can 't get it working, getting the same error as before, namely a malformed document.

Repair Result to Comparison.xml
Errors were detected in file ’/Downloads/Comparison.xlsx’

Excel completed file level validation and repair. Some parts of this workbook may have been repaired or discarded.


Repaired Part: /xl/worksheets/sheet4.xml part. 
Repaired Part: /xl/worksheets/sheet5.xml part. 
Repaired Part: /xl/worksheets/sheet6.xml part. 
Repaired Part: /xl/worksheets/sheet7.xml part. 
Repaired Part: /xl/worksheets/sheet8.xml part. 
Repaired Part: /xl/worksheets/sheet9.xml part. 


Removed Feature: Picture information from /xl/worksheets/sheet4.xml part
Removed Feature: Picture information from /xl/worksheets/sheet5.xml part
Removed Feature: Picture information from /xl/worksheets/sheet6.xml part
Removed Feature: Picture information from /xl/worksheets/sheet7.xml part
Removed Feature: Picture information from /xl/worksheets/sheet8.xml part
Removed Feature: Picture information from /xl/worksheets/sheet9.xml part

In your example, you are using File.Open instead of the original new MemoryStream. I'm running a web app on Azure, so File.Open is not an option for me. Could this be the problem?

@mkaszewiak
Copy link
Collaborator

HI @SzymonSel thank you for your update sorry to hear that you still facing the issue. To help me troubleshoot this issue further,
could you please provide me with with the following details:

  • confirm that you are using latest SDK which is 3.2 ?
  • What is your Operating System
  • Which .NET framework version you are using.

Thank you

@SzymonSel
Copy link
Author

I've upgraded to 3.2.0 and still having the issue.
I'm on MacOS Sequoia 15.1.1 locally and Linux Web App on Azure with both running .NET framework 8.0

@mkaszewiak
Copy link
Collaborator

Hi @SzymonSel thank you for the update, could you please share the output file so I can investigate it as mine result file is working fine. Thanks

@SzymonSel
Copy link
Author

I did some more investigating in the mean time. A simple example does in fact work now, but for a more complex document things start to get off.
Pls find the template file and resulting generated file here on google drive
Note the template sheet name is: Transport {BuildingName}

@mikeebowen
Copy link
Collaborator

Hi @SzymonSel , when you say "start to get off", how is it off? Are parts or attributes missing or incorrect? Does the validator find any errors?

@mikeebowen
Copy link
Collaborator

Hi @SzymonSel , If you run the validator on Comparison_generated.xlsx there are 6 instances of The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist., so the issue is still that you are cloning the xml, but not updating the relationship id in the xml. Below is the full list of errors:

[
  {
    "Id": "Sem_InvalidRelationshipId",
    "Description": "The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist.",
    "Namespaces": {},
    "XPath": "/xdr:wsDr[1]/xdr:twoCellAnchor[5]/xdr:sp[1]/xdr:nvSpPr[1]/xdr:cNvPr[1]/a:hlinkClick[1]",
    "PartUri": "/xl/drawings/drawing4.xml",
    "ErrorType": "Semantic"
  },
  {
    "Id": "Sem_InvalidRelationshipId",
    "Description": "The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist.",
    "Namespaces": {},
    "XPath": "/xdr:wsDr[1]/xdr:twoCellAnchor[5]/xdr:sp[1]/xdr:nvSpPr[1]/xdr:cNvPr[1]/a:hlinkClick[1]",
    "PartUri": "/xl/drawings/drawing5.xml",
    "ErrorType": "Semantic"
  },
  {
    "Id": "Sem_InvalidRelationshipId",
    "Description": "The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist.",
    "Namespaces": {},
    "XPath": "/xdr:wsDr[1]/xdr:twoCellAnchor[5]/xdr:sp[1]/xdr:nvSpPr[1]/xdr:cNvPr[1]/a:hlinkClick[1]",
    "PartUri": "/xl/drawings/drawing6.xml",
    "ErrorType": "Semantic"
  },
  {
    "Id": "Sem_InvalidRelationshipId",
    "Description": "The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist.",
    "Namespaces": {},
    "XPath": "/xdr:wsDr[1]/xdr:twoCellAnchor[5]/xdr:sp[1]/xdr:nvSpPr[1]/xdr:cNvPr[1]/a:hlinkClick[1]",
    "PartUri": "/xl/drawings/drawing7.xml",
    "ErrorType": "Semantic"
  },
  {
    "Id": "Sem_InvalidRelationshipId",
    "Description": "The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist.",
    "Namespaces": {},
    "XPath": "/xdr:wsDr[1]/xdr:twoCellAnchor[5]/xdr:sp[1]/xdr:nvSpPr[1]/xdr:cNvPr[1]/a:hlinkClick[1]",
    "PartUri": "/xl/drawings/drawing8.xml",
    "ErrorType": "Semantic"
  },
  {
    "Id": "Sem_InvalidRelationshipId",
    "Description": "The relationship 'rId2' referenced by attribute 'http://schemas.openxmlformats.org/officeDocument/2006/relationships:id' does not exist.",
    "Namespaces": {},
    "XPath": "/xdr:wsDr[1]/xdr:twoCellAnchor[5]/xdr:sp[1]/xdr:nvSpPr[1]/xdr:cNvPr[1]/a:hlinkClick[1]",
    "PartUri": "/xl/drawings/drawing9.xml",
    "ErrorType": "Semantic"
  }
]

@SzymonSel
Copy link
Author

Hi @mikeebowen ,

I'm not familiar with the Validator tool, and but I only did some visual inspection and noticed the above mentioned issues. By "off" I meant, that Excel can't open the file for reasons you discovered.

The generated file, you are referencing is a product of the code example provided by @mkaszewiak . Therin is a part responsible for updating the relation ids.

@mikeebowen
Copy link
Collaborator

Hi @SzymonSel , the Clone method creates an exact copy of the xml, so you will need to find the elements that have a relationship to rId2 and update it to the new relationship ID to resolve the issue.

@AlfredHellstern
Copy link
Collaborator

We consider this issue resolved, so closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants