Excel workbook generated by Open-XML-SDK are converted to XLSM by Google spread sheet.
See original GitHub issueDescription First I would like to thank you for this library.
I am posting here a wired behavior when generating an Excel file with Open XML SDK and uploading the file to Google spreadsheet. I believe this is an oddity caused by a bug in Open XML SDK. This is why I am posting here. I don’t think this is a question about using Open XML SDK. But in case I’am wrong, I have also posted to Stack overflow :
So I am using the Open XML SDK 2.14.0 to create Excel files. If I upload the XLSX file produced by Open XML SDK to Google Spreadsheet, it opens the file as an XLSX file but if I make any change in the spreadsheet then Google Spreadsheet changes the file type to XLSM. If I generate an empty workbook from MS Excel (Office 360) or Libre Office Calc I don’t have this issue : Google Spreadsheet will not change the file type if I change the data.
Information
- .NET Target: .net core 3.1
- DocumentFormat.OpenXml Version: (ie 2.14.0)
Repro
The minimalistic code bellow (found in the official documentation) reproduces the issue. It generate a foo.xlsx
file in C:\Temp
.
var filepath = @"C:\temp\foo.xlsx";
// Create a spreadsheet document by supplying the filepath.
// By default, AutoSave = true, Editable = true, and Type = xlsx.
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "mySheet" };
sheets.Append(sheet);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
Observed
If I upload the generated file to Google Spreadsheet, it shows this is an XLSX file.
But if I change anything in the workbook then Google Spreadsheet changes the file type to XLSM.
Expected
Open XML SDK generates the correct XLSX file so that Google spreadsheet does not change to XLSM when saving the first time.
Issue Analytics
- State:
- Created 2 years ago
- Comments:22 (1 by maintainers)
@Yanal-Yves thanks for confirming and yes, I added the core part right after creating the document. I think we will consider adding this (and possibly the other standard parts) to the creation process. I’ll create a PR for this and we can review and discuss but it looks like it should be straightforward.
There were some additional comments on PR #1107 about how to resolve this. We will close PR #1107 so I’m copying the substantive comments here. Thanks @ThomasBarnekow for these comments:
ThomasBarnekow:
@tomjebo, having looked at your project description, I wanted to comment on your points here (as I did not see a way to do that in the project).
Won’t add some of the basic parts that some apps like Google Sheets expect. core.xml, app.xml, etc…
This is covered by my comment above regarding the “minimal” documents. It would be a very good idea to add this to the SDK. While other libraries have filled at least some of the gaps, I believe this is so basic that it should be done by the SDK.
We don’t define the root element for CoreFilePropertiesPart and that means the programmer has to add the contents of these parts (at least core.xml) with raw XML like this: [example not shown]
This is true for all parts. In all cases, you have to set that root element explicitly after having added a new part. Using a WordprocessingDocument and a MainDocumentPart as an example, you’ll have to do the following to create a minimal document when using the strongly typed classes:
using var wordDocument = WordprocessingDocument.Create(stream, type); MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.Document = new Document( new Body( new Paragraph())); I am not 100% sure whether the w:p element is required by the standard, but Word would add it. I’ve laid out the code to mimic the layout of the corresponding XML document.
Using the Linq-to-XML way, you’d do it like this.
using var wordDocument = WordprocessingDocument.Create(stream, type); MainDocumentPart part = wordDocument.AddMainDocumentPart(); part.SetXElement( new XElement(W.document, new XAttribute(XNamespace.Xmlns + “w”, W.w), new XElement(W.body, new XElement(W.p))) It would be really cool if the SDK could create standards-compliant, minimal parts (e.g., MainDocumentPart, CoreFilePropertiesPart) when calling the AddMainDocumentPart, for example. That way, the Document and RootElement properties would not have to be marked as nullable.
We should investigate building these documents more completely like Office, even though Office doesn’t throw an error on opening them.
Yes, absolutely!
Reference: PR 1107 issuecomment-996561981
ThomasBarnekow: It would certainly be nice to be able to tell the SDK to create “more complete” or “initially compliant” documents such as minimal Word, Excel, and PowerPoint documents that fulfill certain criteria. But those should have to be created both explicitly (e.g., through settings or otherwise) and consistently. “Consistently” should also mean “for all Office document types”, i.e., Excel, Word, and PowerPoint. The latter would particularly benefit from a feature that creates a minimal PresentationDocument as the minimal markup required is much more involved than that of WordprocessingDocument and SpreadsheetDocument, for example.
Whatever magic we add should not create breaking changes. When creating documents with the SDK, no parts are added at the moment. Thus, callers would happily (try to) create another part of the same type, which will likely fail (I did not test that).
This method (or its siblings) would not be the place to inject such functionality. You’d have to duplicate this in all other Create methods. We’d need some other place, such as the constructor(s).
Reference: PR 1107 discussion_r771194825