From 794484d978f0be2329ecca3f8a1360350e87f686 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 10 Apr 2025 14:55:17 -0700
Subject: [PATCH 01/33] add IsValidChild method for WordprocessingDocument
---
.../Packaging/WordprocessingDocument.cs | 67 ++++++++++
.../OpenXmlPackageTests.cs | 118 ++++++++++++++++++
2 files changed, 185 insertions(+)
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 85a1e7488..94da4f21d 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -310,6 +310,73 @@ public static WordprocessingDocument Open(Package package, OpenSettings openSett
public static WordprocessingDocument Open(Package package)
=> Open(package, new OpenSettings());
+ ///
+ /// Validates whether the specified file is a valid WordprocessingDocument of the given type.
+ ///
+ /// The path to the WordprocessingDocument file.
+ /// The expected type of the WordprocessingDocument. Defaults to .
+ /// True if the file is a valid WordprocessingDocument of the specified type; otherwise, false.
+ public static bool IsValidDocument(string path, WordprocessingDocumentType documentType = WordprocessingDocumentType.Document)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ try
+ {
+ if (!File.Exists(path))
+ {
+ return false;
+ }
+
+ string ext = new FileInfo(path).Extension.ToUpperInvariant();
+
+ switch (ext)
+ {
+ case ".DOCX":
+ if (documentType != WordprocessingDocumentType.Document)
+ {
+ return false;
+ }
+
+ break;
+ case ".DOTX":
+ if (documentType != WordprocessingDocumentType.Template)
+ {
+ return false;
+ }
+
+ break;
+ case ".DOCM":
+ if (documentType != WordprocessingDocumentType.MacroEnabledDocument)
+ {
+ return false;
+ }
+
+ break;
+ case ".DOTM":
+ if (documentType != WordprocessingDocumentType.MacroEnabledTemplate)
+ {
+ return false;
+ }
+
+ break;
+ default:
+ return false;
+ }
+
+ using (WordprocessingDocument wordprocessingDocument = Open(path, false))
+ {
+ return wordprocessingDocument?.MainDocumentPart?.Document?.Body is not null;
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
///
/// Changes the document type.
///
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
index 1f4a0b86f..0531c8f13 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
@@ -3,6 +3,7 @@
using DocumentFormat.OpenXml.Presentation;
using DocumentFormat.OpenXml.Wordprocessing;
+using Microsoft.Testing.Platform.MSBuild;
using System;
using System.Collections.Generic;
using System.IO;
@@ -330,5 +331,122 @@ public void SucceedWithMissingCalcChainPart()
Assert.NotNull(spd);
}
+
+ [Fact]
+ public void IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
+ {
+ // Act
+ bool result = WordprocessingDocument.IsValidDocument(null);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
+ {
+ // Arrange
+ string nonExistentPath = string.Concat(Path.GetTempPath(), "nonexistent.docx");
+
+ // Act
+ bool result = WordprocessingDocument.IsValidDocument(nonExistentPath);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
+ {
+ // Arrange
+ string unsupportedFilePath = string.Concat(Path.GetTempPath(), "unsupported.txt");
+ File.WriteAllText(unsupportedFilePath, "Test content");
+
+ try
+ {
+ // Act
+ bool result = WordprocessingDocument.IsValidDocument(unsupportedFilePath);
+
+ // Assert
+ Assert.False(result);
+ }
+ finally
+ {
+ File.Delete(unsupportedFilePath);
+ }
+ }
+
+ [Fact]
+ public void IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtension()
+ {
+ // Arrange
+ string filePath = string.Concat(Path.GetTempPath(), "test.docx");
+ File.WriteAllText(filePath, "Test content");
+
+ try
+ {
+ // Act
+ bool result = WordprocessingDocument.IsValidDocument(filePath, WordprocessingDocumentType.Template);
+
+ // Assert
+ Assert.False(result);
+ }
+ finally
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ [Fact]
+ public void IsValidDocument_ShouldReturnTrue_ForValidDocument()
+ {
+ // Arrange
+ string filePath = string.Concat(Path.GetTempPath(), "test.docx");
+
+ using (WordprocessingDocument document = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart();
+ document.MainDocumentPart.Document = new Document(new Body());
+ }
+
+ try
+ {
+ // Act
+ bool result = WordprocessingDocument.IsValidDocument(filePath);
+
+ // Assert
+ Assert.True(result);
+ }
+ finally
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ [Fact]
+ public void IsValidDocument_ShouldReturnFalse_WhenDocumentIsCorrupted()
+ {
+ // Arrange
+ string corruptedFilePath = string.Concat(Path.GetTempPath(), "corrupt.docx");
+ using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Create(corruptedFilePath, WordprocessingDocumentType.Document))
+ {
+ MainDocumentPart mainDocumentPart = wordprocessingDocument.AddMainDocumentPart();
+
+ mainDocumentPart.Document = new Document(new Paragraph());
+ }
+
+ try
+ {
+ // Act
+ bool result = WordprocessingDocument.IsValidDocument(corruptedFilePath);
+
+ // Assert
+ Assert.False(result);
+ }
+ finally
+ {
+ File.Delete(corruptedFilePath);
+ }
+ }
}
}
From 9f65d671fa80c051ce01d6554c13a74f68032cfa Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 14 Apr 2025 15:41:27 -0700
Subject: [PATCH 02/33] add IsValidDocument method for SpreadsheetDocument
---
.../Packaging/SpreadsheetDocument.cs | 78 ++++++++++
.../OpenXmlPackageTests.cs | 139 +++++++++++++++++-
2 files changed, 210 insertions(+), 7 deletions(-)
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index f950048c1..61ea63d5c 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -3,6 +3,7 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
+using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -267,6 +268,83 @@ public static SpreadsheetDocument Open(System.IO.Stream stream, bool isEditable)
public static SpreadsheetDocument Open(System.IO.Packaging.Package package)
=> Open(package, new OpenSettings());
+ ///
+ /// Validates whether the specified file is a valid SpreadsheetDocument of the given type.
+ ///
+ /// The path to the SpreadsheetDocument file.
+ /// The expected type of the SpreadsheetDocument. Defaults to .
+ /// True if the file is a valid SpreadsheetDocument of the specified type; otherwise, false.
+ public static bool IsValidDocument(string path, SpreadsheetDocumentType documentType = SpreadsheetDocumentType.Workbook)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ try
+ {
+ if (!File.Exists(path))
+ {
+ return false;
+ }
+
+ string ext = new FileInfo(path).Extension.ToUpperInvariant();
+
+ switch (ext)
+ {
+ case ".XLSX":
+ if (documentType != SpreadsheetDocumentType.Workbook)
+ {
+ return false;
+ }
+
+ break;
+ case ".XLTX":
+ if (documentType != SpreadsheetDocumentType.Template)
+ {
+ return false;
+ }
+
+ break;
+ case ".XLSM":
+ if (documentType != SpreadsheetDocumentType.MacroEnabledWorkbook)
+ {
+ return false;
+ }
+
+ break;
+ case ".XLTM":
+ if (documentType != SpreadsheetDocumentType.MacroEnabledTemplate)
+ {
+ return false;
+ }
+
+ break;
+ case ".XLAM":
+ if (documentType != SpreadsheetDocumentType.AddIn)
+ {
+ return false;
+ }
+
+ break;
+ default:
+ return false;
+ }
+
+ using (SpreadsheetDocument spreadsheetDocument = Open(path, false))
+ {
+ Sheet? sheet = spreadsheetDocument?.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
+ SheetData? sheetData = spreadsheetDocument?.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
+
+ return sheet is not null && sheetData is not null;
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
///
/// Changes the document type.
///
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
index 0531c8f13..90e594320 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
@@ -3,7 +3,6 @@
using DocumentFormat.OpenXml.Presentation;
using DocumentFormat.OpenXml.Wordprocessing;
-using Microsoft.Testing.Platform.MSBuild;
using System;
using System.Collections.Generic;
using System.IO;
@@ -333,7 +332,7 @@ public void SucceedWithMissingCalcChainPart()
}
[Fact]
- public void IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
+ public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
{
// Act
bool result = WordprocessingDocument.IsValidDocument(null);
@@ -343,7 +342,7 @@ public void IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
}
[Fact]
- public void IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
+ public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
{
// Arrange
string nonExistentPath = string.Concat(Path.GetTempPath(), "nonexistent.docx");
@@ -356,7 +355,7 @@ public void IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
}
[Fact]
- public void IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
+ public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
{
// Arrange
string unsupportedFilePath = string.Concat(Path.GetTempPath(), "unsupported.txt");
@@ -377,7 +376,7 @@ public void IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
}
[Fact]
- public void IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtension()
+ public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtension()
{
// Arrange
string filePath = string.Concat(Path.GetTempPath(), "test.docx");
@@ -398,7 +397,7 @@ public void IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtens
}
[Fact]
- public void IsValidDocument_ShouldReturnTrue_ForValidDocument()
+ public void WordprocessingDocument_IsValidDocument_ShouldReturnTrue_ForValidDocument()
{
// Arrange
string filePath = string.Concat(Path.GetTempPath(), "test.docx");
@@ -424,7 +423,7 @@ public void IsValidDocument_ShouldReturnTrue_ForValidDocument()
}
[Fact]
- public void IsValidDocument_ShouldReturnFalse_WhenDocumentIsCorrupted()
+ public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentIsCorrupted()
{
// Arrange
string corruptedFilePath = string.Concat(Path.GetTempPath(), "corrupt.docx");
@@ -448,5 +447,131 @@ public void IsValidDocument_ShouldReturnFalse_WhenDocumentIsCorrupted()
File.Delete(corruptedFilePath);
}
}
+
+ [Fact]
+ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
+ {
+ // Act
+ bool result = SpreadsheetDocument.IsValidDocument(null);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
+ {
+ // Arrange
+ string nonExistentPath = string.Concat(Path.GetTempPath(), "nonexistent.docx");
+
+ // Act
+ bool result = SpreadsheetDocument.IsValidDocument(nonExistentPath);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
+ {
+ // Arrange
+ string unsupportedFilePath = Path.GetTempFileName();
+ File.WriteAllText(unsupportedFilePath, "Test content");
+
+ try
+ {
+ // Act
+ bool result = SpreadsheetDocument.IsValidDocument(unsupportedFilePath);
+
+ // Assert
+ Assert.False(result);
+ }
+ finally
+ {
+ File.Delete(unsupportedFilePath);
+ }
+ }
+
+ [Fact]
+ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtension()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "test.xlsx");
+
+ using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ try
+ {
+ // Act
+ bool result = SpreadsheetDocument.IsValidDocument(filePath, SpreadsheetDocumentType.Template);
+
+ // Assert
+ Assert.False(result);
+ }
+ finally
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ [Fact]
+ public void SpreadsheetDocument_IsValidDocument_ShouldReturnTrue_ForValidDocument()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.xlsx");
+
+ using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ try
+ {
+ // Act
+ bool result = SpreadsheetDocument.IsValidDocument(filePath);
+
+ // Assert
+ Assert.True(result);
+ }
+ finally
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ [Fact]
+ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentIsInvalid()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.xlsx");
+
+ using (SpreadsheetDocument invalidSpreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = invalidSpreadsheetDocument.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ }
+
+ try
+ {
+ // Act
+ bool result = SpreadsheetDocument.IsValidDocument(filePath);
+
+ // Assert
+ Assert.False(result);
+ }
+ finally
+ {
+ File.Delete(filePath);
+ }
+ }
}
}
From b67a9bfee7e41826a6aa92d3a9b7211c62f32252 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Wed, 23 Apr 2025 10:42:01 -0700
Subject: [PATCH 03/33] Add IsMinimumDocument method for Presentation
---
.../Packaging/PresentationDocument.cs | 93 +++++++++++
.../OpenXmlPackageTests.cs | 155 ++++++++++++++++++
2 files changed, 248 insertions(+)
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index fe7995fa2..a75087bd2 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -2,11 +2,14 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Builder;
+using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Features;
+using DocumentFormat.OpenXml.Presentation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Packaging;
+using System.Linq;
using System.Reflection;
namespace DocumentFormat.OpenXml.Packaging
@@ -267,6 +270,96 @@ public static PresentationDocument Open(Package package, OpenSettings openSettin
.Build()
.Open(package);
+ ///
+ /// Validates whether the specified file is a minimum valid PresentationDocument.
+ ///
+ /// The path to the PresentationDocument file.
+ /// The expected type of the PresentationDocument. Defaults to PresentationDocumentType.Presentation.
+ /// True if the file is a minimum valid PresentationDocument; otherwise, false.
+ ///
+ /// Thrown when the is invalid or unsupported.
+ ///
+ public static bool IsMinimumDocument(string path, PresentationDocumentType documentType = PresentationDocumentType.Presentation)
+ {
+ if (documentType == PresentationDocumentType.AddIn || documentType == PresentationDocumentType.Slideshow || documentType == PresentationDocumentType.MacroEnabledSlideshow)
+ {
+ throw new ArgumentException($"Invalid value: {documentType}. Allowed values are PresentationDocumentType.Presentation, PresentationDocumentType.MacroEnabledPresentation, PresentationDocumentType.MacroEnabledTemplate, and PresentationDocumentType.Template.");
+ }
+
+ if (string.IsNullOrEmpty(path))
+ {
+ return false;
+ }
+
+ if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
+ {
+ return false;
+ }
+
+ try
+ {
+ if (!File.Exists(path))
+ {
+ return false;
+ }
+
+ string ext = new FileInfo(path).Extension.ToUpperInvariant();
+
+ switch (ext)
+ {
+ case ".PPTX":
+ if (documentType != PresentationDocumentType.Presentation)
+ {
+ return false;
+ }
+
+ break;
+ case ".POTX":
+ if (documentType != PresentationDocumentType.Template)
+ {
+ return false;
+ }
+
+ break;
+ case ".PPTM":
+ if (documentType != PresentationDocumentType.MacroEnabledPresentation)
+ {
+ return false;
+ }
+
+ break;
+ case ".POTM":
+ if (documentType != PresentationDocumentType.MacroEnabledTemplate)
+ {
+ return false;
+ }
+
+ break;
+ case ".PPSX":
+ throw new FileFormatException($"Validation for PresentationDocumentType.AddIn (.ppsx) is not supported.");
+ case ".PPSM":
+ throw new FileFormatException($"Validation for PresentationDocumentType.AddIn (.ppsm) is not supported.");
+ case ".PPAM":
+ throw new FileFormatException($"Validation for PresentationDocumentType.AddIn (.ppam) is not supported.");
+ default:
+ return false;
+ }
+
+ using (PresentationDocument presentationDocument = Open(path, false))
+ {
+ NotesSize? notesSize = presentationDocument.PresentationPart?.Presentation?.NotesSize;
+
+ return notesSize is not null && notesSize.Cx is not null && notesSize.Cx.HasValue &&
+ notesSize.Cx >= 0 && notesSize.Cx <= 27273042316900 && notesSize.Cy is not null && notesSize.Cy.HasValue &&
+ notesSize.Cy >= 0 && notesSize.Cy <= 27273042316900;
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
///
/// Changes the document type.
///
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
index 90e594320..3a87091e3 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
@@ -573,5 +573,160 @@ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentIs
File.Delete(filePath);
}
}
+
+#region PresentationDocument.IsMinimumDocument tests
+ [Fact]
+ public void IsMinimumDocument_ValidPptx_ReturnsTrue()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
+
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
+ {
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.True(result);
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_ValidPotx_ReturnsTrue()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.potx");
+
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
+ {
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Template);
+
+ // Assert
+ Assert.True(result);
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_InvalidExtension_ReturnsFalse()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.txt");
+
+ File.WriteAllText(filePath, string.Empty);
+
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.False(result);
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_NonExistentFile_ReturnsFalse()
+ {
+ // Arrange
+ string filePath = "nonexistent.pptx";
+
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
+ {
+ // Arrange
+ string filePath = "invalid|path.pptx";
+
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
+
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
+ {
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act & Assert
+ Assert.Throws(() =>
+ PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.AddIn));
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_EmptyPath_ReturnsFalse()
+ {
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(string.Empty, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_NullPath_ReturnsFalse()
+ {
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(null, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_InvalidContent_ReturnsFalse()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
+
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
+ {
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ }
+
+ // Act
+ bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
+
+ // Assert
+ Assert.False(result);
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+ #endregion
}
}
From 6b9d2aa1ffb8f2f0971a6c73cc091a600ddba043 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Wed, 23 Apr 2025 11:54:07 -0700
Subject: [PATCH 04/33] update tests and xml comments
---
.../Packaging/PresentationDocument.cs | 28 +-
.../Packaging/SpreadsheetDocument.cs | 48 ++-
.../Packaging/WordprocessingDocument.cs | 31 +-
.../OpenXmlPackageTests.cs | 329 ++++++++++--------
4 files changed, 279 insertions(+), 157 deletions(-)
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index a75087bd2..1843bcceb 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -274,11 +274,35 @@ public static PresentationDocument Open(Package package, OpenSettings openSettin
/// Validates whether the specified file is a minimum valid PresentationDocument.
///
/// The path to the PresentationDocument file.
- /// The expected type of the PresentationDocument. Defaults to PresentationDocumentType.Presentation.
- /// True if the file is a minimum valid PresentationDocument; otherwise, false.
+ ///
+ /// The expected type of the PresentationDocument. Defaults to .
+ /// Supported types are:
+ ///
+ /// - (.pptx)
+ /// - (.potx)
+ /// - (.pptm)
+ /// - (.potm)
+ ///
+ ///
+ ///
+ /// true if the file is a minimum valid PresentationDocument; otherwise, false.
+ ///
///
/// Thrown when the is invalid or unsupported.
///
+ ///
+ /// A minimum valid PresentationDocument must meet the following criteria:
+ ///
+ /// - The file must exist and have a valid extension matching the .
+ /// - The file must contain a valid element in the presentation part.
+ ///
+ /// Unsupported document types include:
+ ///
+ /// - (.ppam)
+ /// - (.ppsx)
+ /// - (.ppsm)
+ ///
+ ///
public static bool IsMinimumDocument(string path, PresentationDocumentType documentType = PresentationDocumentType.Presentation)
{
if (documentType == PresentationDocumentType.AddIn || documentType == PresentationDocumentType.Slideshow || documentType == PresentationDocumentType.MacroEnabledSlideshow)
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index 61ea63d5c..2b7ecab5f 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -269,18 +269,51 @@ public static SpreadsheetDocument Open(System.IO.Packaging.Package package)
=> Open(package, new OpenSettings());
///
- /// Validates whether the specified file is a valid SpreadsheetDocument of the given type.
+ /// Validates whether the specified file is a minimum valid SpreadsheetDocument.
///
/// The path to the SpreadsheetDocument file.
- /// The expected type of the SpreadsheetDocument. Defaults to .
- /// True if the file is a valid SpreadsheetDocument of the specified type; otherwise, false.
- public static bool IsValidDocument(string path, SpreadsheetDocumentType documentType = SpreadsheetDocumentType.Workbook)
+ ///
+ /// The expected type of the SpreadsheetDocument. Defaults to .
+ /// Supported types are:
+ ///
+ /// - (.xlsx)
+ /// - (.xltx)
+ /// - (.xlsm)
+ /// - (.xltm)
+ ///
+ ///
+ ///
+ /// true if the file is a minimum valid SpreadsheetDocument; otherwise, false.
+ ///
+ ///
+ /// Thrown when the is invalid or unsupported.
+ ///
+ ///
+ /// A minimum valid SpreadsheetDocument must meet the following criteria:
+ ///
+ /// - The file must exist and have a valid extension matching the .
+ /// - The file must contain at least one element in the workbook part.
+ /// - The file must contain at least one element in the worksheet part.
+ ///
+ /// Unsupported document types include (.xlam).
+ ///
+ public static bool IsMinimumDocument(string path, SpreadsheetDocumentType documentType = SpreadsheetDocumentType.Workbook)
{
+ if (documentType == SpreadsheetDocumentType.AddIn)
+ {
+ throw new ArgumentException($"Invalid value: {documentType}. Allowed values are SpreadsheetDocumentType.Workbook, SpreadsheetDocumentType.Template, SpreadsheetDocumentType.MacroEnabledWorkbook, and SpreadsheetDocumentType.MacroEnabledTemplate.");
+ }
+
if (string.IsNullOrEmpty(path))
{
return false;
}
+ if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
+ {
+ return false;
+ }
+
try
{
if (!File.Exists(path))
@@ -321,12 +354,7 @@ public static bool IsValidDocument(string path, SpreadsheetDocumentType document
break;
case ".XLAM":
- if (documentType != SpreadsheetDocumentType.AddIn)
- {
- return false;
- }
-
- break;
+ throw new FileFormatException($"Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.");
default:
return false;
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 94da4f21d..5ad3df091 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -311,18 +311,41 @@ public static WordprocessingDocument Open(Package package)
=> Open(package, new OpenSettings());
///
- /// Validates whether the specified file is a valid WordprocessingDocument of the given type.
+ /// Validates whether the specified file is a minimum valid WordprocessingDocument.
///
/// The path to the WordprocessingDocument file.
- /// The expected type of the WordprocessingDocument. Defaults to .
- /// True if the file is a valid WordprocessingDocument of the specified type; otherwise, false.
- public static bool IsValidDocument(string path, WordprocessingDocumentType documentType = WordprocessingDocumentType.Document)
+ ///
+ /// The expected type of the WordprocessingDocument. Defaults to .
+ /// Supported types are:
+ ///
+ /// - (.docx)
+ /// - (.dotx)
+ /// - (.docm)
+ /// - (.dotm)
+ ///
+ ///
+ ///
+ /// true if the file is a minimum valid WordprocessingDocument; otherwise, false.
+ ///
+ ///
+ /// A minimum valid WordprocessingDocument must meet the following criteria:
+ ///
+ /// - The file must exist and have a valid extension matching the .
+ /// - The file must contain a element in the main document part.
+ ///
+ ///
+ public static bool IsMinimumDocument(string path, WordprocessingDocumentType documentType = WordprocessingDocumentType.Document)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
+ if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
+ {
+ return false;
+ }
+
try
{
if (!File.Exists(path))
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
index 3a87091e3..34066e204 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
@@ -331,172 +331,235 @@ public void SucceedWithMissingCalcChainPart()
Assert.NotNull(spd);
}
+ #region WordprocessingDocument.IsMinimumDocument tests
[Fact]
- public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
+ public void WordprocessingDocument_IsMinimumDocument_ValidDocx_ReturnsTrue()
{
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.docx");
+
+ using (var wordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
+ {
+ var mainPart = wordDocument.AddMainDocumentPart();
+ mainPart.Document = new Document(new Body());
+ }
+
// Act
- bool result = WordprocessingDocument.IsValidDocument(null);
+ bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
// Assert
- Assert.False(result);
+ Assert.True(result);
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void WordprocessingDocument_IsMinimumDocument_ValidDotx_ReturnsTrue()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.dotx");
+
+ using (var wordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
+ {
+ var mainPart = wordDocument.AddMainDocumentPart();
+ mainPart.Document = new Document(new Body());
+ }
+
+ // Act
+ bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Template);
+
+ // Assert
+ Assert.True(result);
+
+ // Cleanup
+ File.Delete(filePath);
}
[Fact]
- public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
+ public void WordprocessingDocument_IsMinimumDocument_InvalidExtension_ReturnsFalse()
{
// Arrange
- string nonExistentPath = string.Concat(Path.GetTempPath(), "nonexistent.docx");
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid-ext.txt");
+
+ File.WriteAllText(filePath, string.Empty);
// Act
- bool result = WordprocessingDocument.IsValidDocument(nonExistentPath);
+ bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
// Assert
Assert.False(result);
+
+ // Cleanup
+ File.Delete(filePath);
}
[Fact]
- public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
+ public void WordprocessingDocument_IsMinimumDocument_NonExistentFile_ReturnsFalse()
{
// Arrange
- string unsupportedFilePath = string.Concat(Path.GetTempPath(), "unsupported.txt");
- File.WriteAllText(unsupportedFilePath, "Test content");
+ string filePath = Path.Combine(Path.GetTempPath(), "nonexistent.docx");
- try
- {
- // Act
- bool result = WordprocessingDocument.IsValidDocument(unsupportedFilePath);
+ // Act
+ bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
- // Assert
- Assert.False(result);
- }
- finally
- {
- File.Delete(unsupportedFilePath);
- }
+ // Assert
+ Assert.False(result);
}
[Fact]
- public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtension()
+ public void WordprocessingDocument_IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
{
// Arrange
- string filePath = string.Concat(Path.GetTempPath(), "test.docx");
- File.WriteAllText(filePath, "Test content");
+ string filePath = "invalid|path.docx";
- try
- {
- // Act
- bool result = WordprocessingDocument.IsValidDocument(filePath, WordprocessingDocumentType.Template);
+ // Act
+ bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
- // Assert
- Assert.False(result);
- }
- finally
- {
- File.Delete(filePath);
- }
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void WordprocessingDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
+ {
+ // Act
+ bool result = WordprocessingDocument.IsMinimumDocument(string.Empty, WordprocessingDocumentType.Document);
+
+ // Assert
+ Assert.False(result);
}
[Fact]
- public void WordprocessingDocument_IsValidDocument_ShouldReturnTrue_ForValidDocument()
+ public void WordprocessingDocument_IsMinimumDocument_NullPath_ReturnsFalse()
+ {
+ // Act
+ bool result = WordprocessingDocument.IsMinimumDocument(null, WordprocessingDocumentType.Document);
+
+ // Assert
+ Assert.False(result);
+ }
+
+ [Fact]
+ public void WordprocessingDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
{
// Arrange
- string filePath = string.Concat(Path.GetTempPath(), "test.docx");
+ string filePath = Path.Combine(Path.GetTempPath(), "invalid.docx");
- using (WordprocessingDocument document = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
+ using (var wordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
{
- document.AddMainDocumentPart();
- document.MainDocumentPart.Document = new Document(new Body());
+ var mainPart = wordDocument.AddMainDocumentPart();
+ mainPart.Document = new Document();
}
- try
- {
- // Act
- bool result = WordprocessingDocument.IsValidDocument(filePath);
+ // Act
+ bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
- // Assert
- Assert.True(result);
- }
- finally
- {
- File.Delete(filePath);
- }
+ // Assert
+ Assert.False(result);
+
+ // Cleanup
+ File.Delete(filePath);
}
+ #endregion
+ #region SpreadsheetDocument.IsMinimumDocument tests
[Fact]
- public void WordprocessingDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentIsCorrupted()
+ public void IsMinimumDocument_ValidXlsx_ReturnsTrue()
{
// Arrange
- string corruptedFilePath = string.Concat(Path.GetTempPath(), "corrupt.docx");
- using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Create(corruptedFilePath, WordprocessingDocumentType.Document))
- {
- MainDocumentPart mainDocumentPart = wordprocessingDocument.AddMainDocumentPart();
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.xlsx");
- mainDocumentPart.Document = new Document(new Paragraph());
+ using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
}
- try
- {
- // Act
- bool result = WordprocessingDocument.IsValidDocument(corruptedFilePath);
+ // Act
+ bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
- // Assert
- Assert.False(result);
- }
- finally
+ // Assert
+ Assert.True(result);
+
+ // Cleanup
+ File.Delete(filePath);
+ }
+
+ [Fact]
+ public void IsMinimumDocument_ValidXltx_ReturnsTrue()
+ {
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.xltx");
+
+ using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Template))
{
- File.Delete(corruptedFilePath);
+ WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
}
+
+ // Act
+ bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Template);
+
+ // Assert
+ Assert.True(result);
+
+ // Cleanup
+ File.Delete(filePath);
}
[Fact]
- public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenPathIsNull()
+ public void SpreadsheetDocument_IsMinimumDocument_InvalidExtension_ReturnsFalse()
{
+ // Arrange
+ string filePath = Path.Combine(Path.GetTempPath(), string.Concat(Path.GetTempFileName(), ".txt"));
+ File.WriteAllText(filePath, string.Empty);
+
// Act
- bool result = SpreadsheetDocument.IsValidDocument(null);
+ bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
// Assert
Assert.False(result);
+
+ // Cleanup
+ File.Delete(filePath);
}
[Fact]
- public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenFileDoesNotExist()
+ public void SpreadsheetDocument_IsMinimumDocument_NonExistentFile_ReturnsFalse()
{
// Arrange
- string nonExistentPath = string.Concat(Path.GetTempPath(), "nonexistent.docx");
+ string filePath = "nonexistent.xlsx";
// Act
- bool result = SpreadsheetDocument.IsValidDocument(nonExistentPath);
+ bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
// Assert
Assert.False(result);
}
[Fact]
- public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenFileExtensionIsUnsupported()
+ public void SpreadsheetDocument_IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
{
// Arrange
- string unsupportedFilePath = Path.GetTempFileName();
- File.WriteAllText(unsupportedFilePath, "Test content");
+ string filePath = "invalid|path.xlsx";
- try
- {
- // Act
- bool result = SpreadsheetDocument.IsValidDocument(unsupportedFilePath);
+ // Act
+ bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
- // Assert
- Assert.False(result);
- }
- finally
- {
- File.Delete(unsupportedFilePath);
- }
+ // Assert
+ Assert.False(result);
}
[Fact]
- public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentTypeDoesNotMatchExtension()
+ public void SpreadsheetDocument_IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
{
// Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "test.xlsx");
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.xlsx");
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
{
@@ -506,80 +569,64 @@ public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentTy
wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
}
- try
- {
- // Act
- bool result = SpreadsheetDocument.IsValidDocument(filePath, SpreadsheetDocumentType.Template);
+ // Act & Assert
+ Assert.Throws(() =>
+ SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.AddIn));
- // Assert
- Assert.False(result);
- }
- finally
- {
- File.Delete(filePath);
- }
+ // Cleanup
+ File.Delete(filePath);
}
[Fact]
- public void SpreadsheetDocument_IsValidDocument_ShouldReturnTrue_ForValidDocument()
+ public void SpreadsheetDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
{
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.xlsx");
+ // Act
+ bool result = SpreadsheetDocument.IsMinimumDocument(string.Empty, SpreadsheetDocumentType.Workbook);
- using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
+ // Assert
+ Assert.False(result);
+ }
- try
- {
- // Act
- bool result = SpreadsheetDocument.IsValidDocument(filePath);
+ [Fact]
+ public void SpreadsheetDocument_IsMinimumDocument_NullPath_ReturnsFalse()
+ {
+ // Act
+ bool result = SpreadsheetDocument.IsMinimumDocument(null, SpreadsheetDocumentType.Workbook);
- // Assert
- Assert.True(result);
- }
- finally
- {
- File.Delete(filePath);
- }
+ // Assert
+ Assert.False(result);
}
[Fact]
- public void SpreadsheetDocument_IsValidDocument_ShouldReturnFalse_WhenDocumentIsInvalid()
+ public void SpreadsheetDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
{
// Arrange
string filePath = Path.Combine(Path.GetTempPath(), "invalid.xlsx");
- using (SpreadsheetDocument invalidSpreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
+ using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
{
- WorkbookPart wbp = invalidSpreadsheetDocument.AddWorkbookPart();
+ WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
}
- try
- {
- // Act
- bool result = SpreadsheetDocument.IsValidDocument(filePath);
+ // Act
+ bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
- // Assert
- Assert.False(result);
- }
- finally
- {
- File.Delete(filePath);
- }
+ // Assert
+ Assert.False(result);
+
+ // Cleanup
+ File.Delete(filePath);
}
+ #endregion
-#region PresentationDocument.IsMinimumDocument tests
+ #region PresentationDocument.IsMinimumDocument tests
[Fact]
public void IsMinimumDocument_ValidPptx_ReturnsTrue()
{
// Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
+ string filePath = Path.Combine(Path.GetTempPath(), "valid.pptx");
using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
{
@@ -622,7 +669,7 @@ public void IsMinimumDocument_ValidPotx_ReturnsTrue()
}
[Fact]
- public void IsMinimumDocument_InvalidExtension_ReturnsFalse()
+ public void PresentationDocument_IsMinimumDocument_InvalidExtension_ReturnsFalse()
{
// Arrange
string filePath = Path.Combine(Path.GetTempPath(), "invalid.txt");
@@ -640,7 +687,7 @@ public void IsMinimumDocument_InvalidExtension_ReturnsFalse()
}
[Fact]
- public void IsMinimumDocument_NonExistentFile_ReturnsFalse()
+ public void PresentationDocument_IsMinimumDocument_NonExistentFile_ReturnsFalse()
{
// Arrange
string filePath = "nonexistent.pptx";
@@ -653,7 +700,7 @@ public void IsMinimumDocument_NonExistentFile_ReturnsFalse()
}
[Fact]
- public void IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
+ public void PresentationDocument_IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
{
// Arrange
string filePath = "invalid|path.pptx";
@@ -666,7 +713,7 @@ public void IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
}
[Fact]
- public void IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
+ public void PresentationDocument_IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
{
// Arrange
string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
@@ -687,7 +734,7 @@ public void IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
}
[Fact]
- public void IsMinimumDocument_EmptyPath_ReturnsFalse()
+ public void PresentationDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
{
// Act
bool result = PresentationDocument.IsMinimumDocument(string.Empty, PresentationDocumentType.Presentation);
@@ -697,7 +744,7 @@ public void IsMinimumDocument_EmptyPath_ReturnsFalse()
}
[Fact]
- public void IsMinimumDocument_NullPath_ReturnsFalse()
+ public void PresentationDocument_IsMinimumDocument_NullPath_ReturnsFalse()
{
// Act
bool result = PresentationDocument.IsMinimumDocument(null, PresentationDocumentType.Presentation);
@@ -707,7 +754,7 @@ public void IsMinimumDocument_NullPath_ReturnsFalse()
}
[Fact]
- public void IsMinimumDocument_InvalidContent_ReturnsFalse()
+ public void PresentationDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
{
// Arrange
string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
From 48afb54d9d1f959bf14f461a33ddd5fedabf9eea Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Tue, 29 Apr 2025 10:04:31 -0700
Subject: [PATCH 05/33] only test null argument for < .Net 8+
---
.../OpenXmlPackageTests.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
index 6effd5b0f..9cb02159f 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
@@ -442,6 +442,7 @@ public void WordprocessingDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
Assert.False(result);
}
+#if !NET8_0_OR_GREATER
[Fact]
public void WordprocessingDocument_IsMinimumDocument_NullPath_ReturnsFalse()
{
@@ -451,6 +452,7 @@ public void WordprocessingDocument_IsMinimumDocument_NullPath_ReturnsFalse()
// Assert
Assert.False(result);
}
+#endif
[Fact]
public void WordprocessingDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
@@ -599,6 +601,7 @@ public void SpreadsheetDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
Assert.False(result);
}
+#if !NET8_0_OR_GREATER
[Fact]
public void SpreadsheetDocument_IsMinimumDocument_NullPath_ReturnsFalse()
{
@@ -608,6 +611,7 @@ public void SpreadsheetDocument_IsMinimumDocument_NullPath_ReturnsFalse()
// Assert
Assert.False(result);
}
+#endif
[Fact]
public void SpreadsheetDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
@@ -755,6 +759,7 @@ public void PresentationDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
Assert.False(result);
}
+#if !NET8_0_OR_GREATER
[Fact]
public void PresentationDocument_IsMinimumDocument_NullPath_ReturnsFalse()
{
@@ -764,6 +769,7 @@ public void PresentationDocument_IsMinimumDocument_NullPath_ReturnsFalse()
// Assert
Assert.False(result);
}
+#endif
[Fact]
public void PresentationDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
From f83d1d0f4f2bd296a426d98376d91ed7c7c91136 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Tue, 29 Apr 2025 16:18:40 -0700
Subject: [PATCH 06/33] move Wordprocessing minimum document validation to
virtual method
---
.../Packaging/OpenSettings.cs | 12 +
.../Packaging/OpenXmlPackage.cs | 27 ++
.../PublicAPI/PublicAPI.Shipped.txt | 2 +
.../Packaging/WordprocessingDocument.cs | 135 ++++--
.../OpenXmlPackageTests.cs | 451 ------------------
5 files changed, 124 insertions(+), 503 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
index 8e637b432..791e5ed68 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
@@ -74,5 +74,17 @@ public MarkupCompatibilityProcessSettings MarkupCompatibilityProcessSettings
/// This property allows you to mitigate denial of service attacks where the attacker submits a package with an extremely large Open XML part. By limiting the size of the part, you can detect the attack and recover reliably.
///
public long MaxCharactersInPart { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to validate that the document meets the minimum requirements for a valid package.
+ ///
+ ///
+ /// true if the document should be validated for minimum requirements; otherwise, false.
+ ///
+ ///
+ /// When set to true, the document will be checked to ensure it contains the necessary parts and structure
+ /// to be considered a valid Open XML package.
+ ///
+ public bool CheckMinimumPackage { get; set; }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index c8ce6524a..2a63f0d08 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -105,6 +105,33 @@ internal void LoadAllParts()
}
}
+ ///
+ /// Determines whether the specified meets the minimum requirements for a valid package.
+ ///
+ /// The to validate.
+ ///
+ /// true if the package meets the minimum requirements; otherwise, false.
+ ///
+ ///
+ /// This method is intended to be overridden by derived classes to implement specific validation logic
+ /// for different types of Open XML packages (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
+ ///
+ protected virtual bool IsMinimumPackage(OpenXmlPackage package) => false;
+
+ ///
+ /// Determines whether the specified file path is valid for the given document type.
+ ///
+ /// The file path to validate.
+ /// The document type to validate against.
+ ///
+ /// true if the file path is valid for the specified document type; otherwise, false.
+ ///
+ ///
+ /// This method is intended to be overridden by derived classes to implement specific validation logic
+ /// for different types of Open XML documents (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
+ ///
+ protected virtual bool IsValidDocumentPath(string path, Enum type) => false;
+
#region public methods
///
diff --git a/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt b/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
index c7d7da820..7827e319d 100644
--- a/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
+++ b/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
@@ -1009,3 +1009,5 @@ DocumentFormat.OpenXml.OpenXmlPartWriterSettings.Encoding.set -> void
DocumentFormat.OpenXml.OpenXmlPartWriterSettings.OpenXmlPartWriterSettings() -> void
DocumentFormat.OpenXml.OpenXmlPartWriter.OpenXmlPartWriter(DocumentFormat.OpenXml.Packaging.OpenXmlPart! openXmlPart, DocumentFormat.OpenXml.OpenXmlPartWriterSettings! settings) -> void
DocumentFormat.OpenXml.OpenXmlPartWriter.OpenXmlPartWriter(System.IO.Stream! partStream, DocumentFormat.OpenXml.OpenXmlPartWriterSettings! settings) -> void
+DocumentFormat.OpenXml.Packaging.OpenSettings.CheckMinimumPackage.get -> bool
+DocumentFormat.OpenXml.Packaging.OpenSettings.CheckMinimumPackage.set -> void
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 6e7e5a395..5c3ad2cab 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -264,6 +264,24 @@ public static WordprocessingDocument Open(Stream stream, bool isEditable)
public static WordprocessingDocument Open(string path, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.CheckMinimumPackage)
+ {
+ bool isValidPath = package.IsValidDocumentPath(path, package.DocumentType);
+ bool isMinimumDocument = package.IsMinimumPackage(package);
+
+ if (!isValidPath)
+ {
+ throw new ArgumentException($"The provided path is invalid. {nameof(path)}");
+ }
+
+ if (!isMinimumDocument)
+ {
+ throw new FileFormatException($"The provided package does not conform to the minimum requirements for Office to open. {nameof(path)}");
+ }
+ }
+ })
.Build()
.Open(path, isEditable);
@@ -312,30 +330,39 @@ public static WordprocessingDocument Open(Package package)
=> Open(package, new OpenSettings());
///
- /// Validates whether the specified file is a minimum valid WordprocessingDocument.
+ /// Determines whether the specified meets the minimum requirements for a valid WordprocessingDocument package.
///
- /// The path to the WordprocessingDocument file.
- ///
- /// The expected type of the WordprocessingDocument. Defaults to .
- /// Supported types are:
- ///
- /// - (.docx)
- /// - (.dotx)
- /// - (.docm)
- /// - (.dotm)
- ///
- ///
+ /// The to validate.
///
- /// true if the file is a minimum valid WordprocessingDocument; otherwise, false.
+ /// true if the package meets the minimum requirements for a WordprocessingDocument; otherwise, false.
///
///
- /// A minimum valid WordprocessingDocument must meet the following criteria:
- ///
- /// - The file must exist and have a valid extension matching the .
- /// - The file must contain a element in the main document part.
- ///
+ /// This method checks whether the provided package is a valid and contains a
+ /// with a that has a element.
///
- public static bool IsMinimumDocument(string path, WordprocessingDocumentType documentType = WordprocessingDocumentType.Document)
+ protected override bool IsMinimumPackage(OpenXmlPackage package)
+ {
+ if (package is WordprocessingDocument wordprocessingDocument)
+ {
+ return wordprocessingDocument.MainDocumentPart?.Document?.Body is not null;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Determines whether the specified file path is valid for the given WordprocessingDocument type.
+ ///
+ /// The file path to validate.
+ /// The document type to validate against, such as .
+ ///
+ /// true if the file path is valid for the specified document type; otherwise, false.
+ ///
+ ///
+ /// This method checks the file path for validity, including ensuring the file exists, the path is well-formed,
+ /// and the file extension matches the expected type for the given .
+ ///
+ protected override bool IsValidDocumentPath(string path, Enum type)
{
if (string.IsNullOrEmpty(path))
{
@@ -354,46 +381,50 @@ public static bool IsMinimumDocument(string path, WordprocessingDocumentType doc
return false;
}
- string ext = new FileInfo(path).Extension.ToUpperInvariant();
-
- switch (ext)
+ if (type is WordprocessingDocumentType)
{
- case ".DOCX":
- if (documentType != WordprocessingDocumentType.Document)
- {
- return false;
- }
-
- break;
- case ".DOTX":
- if (documentType != WordprocessingDocumentType.Template)
- {
- return false;
- }
+ WordprocessingDocumentType documentType = (WordprocessingDocumentType)type;
- break;
- case ".DOCM":
- if (documentType != WordprocessingDocumentType.MacroEnabledDocument)
- {
- return false;
- }
+ string ext = new FileInfo(path).Extension.ToUpperInvariant();
- break;
- case ".DOTM":
- if (documentType != WordprocessingDocumentType.MacroEnabledTemplate)
- {
+ switch (ext)
+ {
+ case ".DOCX":
+ if (documentType != WordprocessingDocumentType.Document)
+ {
+ return false;
+ }
+
+ break;
+ case ".DOTX":
+ if (documentType != WordprocessingDocumentType.Template)
+ {
+ return false;
+ }
+
+ break;
+ case ".DOCM":
+ if (documentType != WordprocessingDocumentType.MacroEnabledDocument)
+ {
+ return false;
+ }
+
+ break;
+ case ".DOTM":
+ if (documentType != WordprocessingDocumentType.MacroEnabledTemplate)
+ {
+ return false;
+ }
+
+ break;
+ default:
return false;
- }
+ }
- break;
- default:
- return false;
+ return true;
}
- using (WordprocessingDocument wordprocessingDocument = Open(path, false))
- {
- return wordprocessingDocument?.MainDocumentPart?.Document?.Body is not null;
- }
+ return false;
}
catch
{
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
index 9cb02159f..3bb51863d 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/OpenXmlPackageTests.cs
@@ -342,456 +342,5 @@ public void SucceedWithMissingCalcChainPart()
Assert.NotNull(spd);
}
-
- #region WordprocessingDocument.IsMinimumDocument tests
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_ValidDocx_ReturnsTrue()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.docx");
-
- using (var wordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
- {
- var mainPart = wordDocument.AddMainDocumentPart();
- mainPart.Document = new Document(new Body());
- }
-
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.True(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_ValidDotx_ReturnsTrue()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.dotx");
-
- using (var wordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
- {
- var mainPart = wordDocument.AddMainDocumentPart();
- mainPart.Document = new Document(new Body());
- }
-
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Template);
-
- // Assert
- Assert.True(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_InvalidExtension_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid-ext.txt");
-
- File.WriteAllText(filePath, string.Empty);
-
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.False(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_NonExistentFile_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "nonexistent.docx");
-
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
- {
- // Arrange
- string filePath = "invalid|path.docx";
-
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
- {
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(string.Empty, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.False(result);
- }
-
-#if !NET8_0_OR_GREATER
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_NullPath_ReturnsFalse()
- {
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(null, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.False(result);
- }
-#endif
-
- [Fact]
- public void WordprocessingDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.docx");
-
- using (var wordDocument = WordprocessingDocument.Create(filePath, WordprocessingDocumentType.Document))
- {
- var mainPart = wordDocument.AddMainDocumentPart();
- mainPart.Document = new Document();
- }
-
- // Act
- bool result = WordprocessingDocument.IsMinimumDocument(filePath, WordprocessingDocumentType.Document);
-
- // Assert
- Assert.False(result);
-
- // Cleanup
- File.Delete(filePath);
- }
- #endregion
-
- #region SpreadsheetDocument.IsMinimumDocument tests
- [Fact]
- public void IsMinimumDocument_ValidXlsx_ReturnsTrue()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.xlsx");
-
- using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.True(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void IsMinimumDocument_ValidXltx_ReturnsTrue()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.xltx");
-
- using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Template))
- {
- WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Template);
-
- // Assert
- Assert.True(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_InvalidExtension_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), string.Concat(Path.GetTempFileName(), ".txt"));
- File.WriteAllText(filePath, string.Empty);
-
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.False(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_NonExistentFile_ReturnsFalse()
- {
- // Arrange
- string filePath = "nonexistent.xlsx";
-
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
- {
- // Arrange
- string filePath = "invalid|path.xlsx";
-
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.xlsx");
-
- using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act & Assert
- Assert.Throws(() =>
- SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.AddIn));
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
- {
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(string.Empty, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.False(result);
- }
-
-#if !NET8_0_OR_GREATER
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_NullPath_ReturnsFalse()
- {
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(null, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.False(result);
- }
-#endif
-
- [Fact]
- public void SpreadsheetDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.xlsx");
-
- using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = spreadsheetDocument.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- }
-
- // Act
- bool result = SpreadsheetDocument.IsMinimumDocument(filePath, SpreadsheetDocumentType.Workbook);
-
- // Assert
- Assert.False(result);
-
- // Cleanup
- File.Delete(filePath);
- }
- #endregion
-
- #region PresentationDocument.IsMinimumDocument tests
- [Fact]
- public void IsMinimumDocument_ValidPptx_ReturnsTrue()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "valid.pptx");
-
- using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
- {
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act
- bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.True(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void IsMinimumDocument_ValidPotx_ReturnsTrue()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.potx");
-
- using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
- {
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act
- bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Template);
-
- // Assert
- Assert.True(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void PresentationDocument_IsMinimumDocument_InvalidExtension_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.txt");
-
- File.WriteAllText(filePath, string.Empty);
-
- // Act
- bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.False(result);
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void PresentationDocument_IsMinimumDocument_NonExistentFile_ReturnsFalse()
- {
- // Arrange
- string filePath = "nonexistent.pptx";
-
- // Act
- bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public void PresentationDocument_IsMinimumDocument_InvalidPathCharacters_ReturnsFalse()
- {
- // Arrange
- string filePath = "invalid|path.pptx";
-
- // Act
- bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.False(result);
- }
-
- [Fact]
- public void PresentationDocument_IsMinimumDocument_UnsupportedDocumentType_ThrowsArgumentException()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
-
- using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
- {
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act & Assert
- Assert.Throws(() =>
- PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.AddIn));
-
- // Cleanup
- File.Delete(filePath);
- }
-
- [Fact]
- public void PresentationDocument_IsMinimumDocument_EmptyPath_ReturnsFalse()
- {
- // Act
- bool result = PresentationDocument.IsMinimumDocument(string.Empty, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.False(result);
- }
-
-#if !NET8_0_OR_GREATER
- [Fact]
- public void PresentationDocument_IsMinimumDocument_NullPath_ReturnsFalse()
- {
- // Act
- bool result = PresentationDocument.IsMinimumDocument(null, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.False(result);
- }
-#endif
-
- [Fact]
- public void PresentationDocument_IsMinimumDocument_InvalidContent_ReturnsFalse()
- {
- // Arrange
- string filePath = Path.Combine(Path.GetTempPath(), "invalid.pptx");
-
- using (PresentationDocument presentationDocument = PresentationDocument.Create(filePath, PresentationDocumentType.Presentation))
- {
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- }
-
- // Act
- bool result = PresentationDocument.IsMinimumDocument(filePath, PresentationDocumentType.Presentation);
-
- // Assert
- Assert.False(result);
-
- // Cleanup
- File.Delete(filePath);
- }
- #endregion
}
}
From 8dc68ef9929aed1625aa7f1b40043c3834c7a998 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 1 May 2025 14:33:58 -0700
Subject: [PATCH 07/33] Change minimum Word doc API to OpenSettings options
---
.../Packaging/OpenXmlPackage.cs | 25 +---
.../Packaging/WordprocessingDocument.cs | 130 ++++--------------
.../ofapiTest/OpenXmlPackageTest.cs | 130 ++++++++++++++++++
3 files changed, 159 insertions(+), 126 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index 2a63f0d08..3b70ed3e3 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -106,31 +106,16 @@ internal void LoadAllParts()
}
///
- /// Determines whether the specified meets the minimum requirements for a valid package.
+ /// Throws a if the current does not meet the minimum requirements for a valid package.
///
- /// The to validate.
- ///
- /// true if the package meets the minimum requirements; otherwise, false.
- ///
+ ///
+ /// Thrown when the package does not conform to the minimum requirements for Office to open.
+ ///
///
/// This method is intended to be overridden by derived classes to implement specific validation logic
/// for different types of Open XML packages (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
///
- protected virtual bool IsMinimumPackage(OpenXmlPackage package) => false;
-
- ///
- /// Determines whether the specified file path is valid for the given document type.
- ///
- /// The file path to validate.
- /// The document type to validate against.
- ///
- /// true if the file path is valid for the specified document type; otherwise, false.
- ///
- ///
- /// This method is intended to be overridden by derived classes to implement specific validation logic
- /// for different types of Open XML documents (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
- ///
- protected virtual bool IsValidDocumentPath(string path, Enum type) => false;
+ protected virtual void ThrowIfNotMinimumPackage() => throw new FileFormatException($"The provided package does not conform to the minimum requirements for Office to open.");
#region public methods
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 5c3ad2cab..392b6106e 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -268,18 +268,7 @@ public static WordprocessingDocument Open(string path, bool isEditable, OpenSett
{
if (openSettings.CheckMinimumPackage)
{
- bool isValidPath = package.IsValidDocumentPath(path, package.DocumentType);
- bool isMinimumDocument = package.IsMinimumPackage(package);
-
- if (!isValidPath)
- {
- throw new ArgumentException($"The provided path is invalid. {nameof(path)}");
- }
-
- if (!isMinimumDocument)
- {
- throw new FileFormatException($"The provided package does not conform to the minimum requirements for Office to open. {nameof(path)}");
- }
+ package.ThrowIfNotMinimumPackage();
}
})
.Build()
@@ -299,6 +288,13 @@ public static WordprocessingDocument Open(string path, bool isEditable, OpenSett
public static WordprocessingDocument Open(Stream stream, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.CheckMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(stream, isEditable);
@@ -315,6 +311,13 @@ public static WordprocessingDocument Open(Stream stream, bool isEditable, OpenSe
public static WordprocessingDocument Open(Package package, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.CheckMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(package);
@@ -330,105 +333,20 @@ public static WordprocessingDocument Open(Package package)
=> Open(package, new OpenSettings());
///
- /// Determines whether the specified meets the minimum requirements for a valid WordprocessingDocument package.
+ /// Throws a if the current does not meet the minimum requirements for a valid package.
///
- /// The to validate.
- ///
- /// true if the package meets the minimum requirements for a WordprocessingDocument; otherwise, false.
- ///
+ ///
+ /// Thrown when the is missing or its is null.
+ ///
///
- /// This method checks whether the provided package is a valid and contains a
- /// with a that has a element.
+ /// This method ensures that the contains the necessary parts and structure
+ /// to be opened with Word.
///
- protected override bool IsMinimumPackage(OpenXmlPackage package)
+ protected override void ThrowIfNotMinimumPackage()
{
- if (package is WordprocessingDocument wordprocessingDocument)
- {
- return wordprocessingDocument.MainDocumentPart?.Document?.Body is not null;
- }
-
- return false;
- }
-
- ///
- /// Determines whether the specified file path is valid for the given WordprocessingDocument type.
- ///
- /// The file path to validate.
- /// The document type to validate against, such as .
- ///
- /// true if the file path is valid for the specified document type; otherwise, false.
- ///
- ///
- /// This method checks the file path for validity, including ensuring the file exists, the path is well-formed,
- /// and the file extension matches the expected type for the given .
- ///
- protected override bool IsValidDocumentPath(string path, Enum type)
- {
- if (string.IsNullOrEmpty(path))
- {
- return false;
- }
-
- if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
- {
- return false;
- }
-
- try
- {
- if (!File.Exists(path))
- {
- return false;
- }
-
- if (type is WordprocessingDocumentType)
- {
- WordprocessingDocumentType documentType = (WordprocessingDocumentType)type;
-
- string ext = new FileInfo(path).Extension.ToUpperInvariant();
-
- switch (ext)
- {
- case ".DOCX":
- if (documentType != WordprocessingDocumentType.Document)
- {
- return false;
- }
-
- break;
- case ".DOTX":
- if (documentType != WordprocessingDocumentType.Template)
- {
- return false;
- }
-
- break;
- case ".DOCM":
- if (documentType != WordprocessingDocumentType.MacroEnabledDocument)
- {
- return false;
- }
-
- break;
- case ".DOTM":
- if (documentType != WordprocessingDocumentType.MacroEnabledTemplate)
- {
- return false;
- }
-
- break;
- default:
- return false;
- }
-
- return true;
- }
-
- return false;
- }
- catch
+ if (this.MainDocumentPart?.Document?.Body is null)
{
- return false;
+ throw new FileFormatException("The provided package does not conform to the minimum requirements for Word to open.");
}
}
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
index cbd1eeca6..872fe514f 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
@@ -3,8 +3,10 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
+using Microsoft.Testing.Platform.MSBuild;
using System;
using System.IO;
+using System.IO.Packaging;
using System.Linq;
using Xunit;
@@ -876,5 +878,133 @@ public void O15FileOpenTest()
Assert.NotNull(webExtensionPart);
}
}
+
+ [Fact]
+ public void CheckMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessing()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "valid.docx");
+
+ using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document(new Body());
+
+ }
+
+ // Act
+ Exception exception = Record.Exception(() =>
+ {
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { CheckMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void CheckMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Wordprocessing()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document(new Body());
+ }
+
+ // Act
+ Exception exception = Record.Exception(() =>
+ {
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { CheckMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+ }
+
+ [Fact]
+ public void CheckMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Wordprocessing()
+ {
+ // Arrange
+ string path = Path.Combine(Path.GetTempPath(), "valid.docx");
+
+ using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document(new Body());
+ }
+
+ // Act
+ using (Package package = Package.Open(path))
+ {
+ Exception exception = Record.Exception(() =>
+ {
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { CheckMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+
+ }
+
+ [Fact]
+ public void CheckMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "invalid.docx");
+
+ using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document();
+ }
+
+ // Act and Assert
+ Assert.Throws(() =>
+ {
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { CheckMinimumPackage = true });
+ });
+ }
+
+ [Fact]
+ public void CheckMinimumPackageTest_InValidDocumentStream_Throws_Wordprocessing()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document();
+ }
+
+ // Act and Assert
+ Assert.Throws(() =>
+ {
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { CheckMinimumPackage = true });
+ });
+ }
+ }
+
+ [Fact]
+ public void CheckMinimumPackageTest_InValidDocumentPackage_Throws_Wordprocessing()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document();
+ }
+
+ // Act and Assert
+ using (Package package = Package.Open(stream))
+ {
+ Assert.Throws(() =>
+ {
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { CheckMinimumPackage = true });
+ });
+ }
+ }
+ }
}
}
From 1a0d73a98b8e219212cf34fe0867225426abf0dd Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 5 May 2025 16:12:49 -0700
Subject: [PATCH 08/33] add VerifyMinimumPackage option to PresentationDocument
---
.../Packaging/OpenSettings.cs | 2 +-
.../PublicAPI/PublicAPI.Shipped.txt | 4 +-
.../Packaging/PresentationDocument.cs | 139 ++----
.../Packaging/SpreadsheetDocument.cs | 127 ++---
.../Packaging/WordprocessingDocument.cs | 6 +-
.../ofapiTest/OpenXmlPackageTest.cs | 437 +++++++++++++++++-
6 files changed, 509 insertions(+), 206 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
index 791e5ed68..a9e147c7b 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
@@ -85,6 +85,6 @@ public MarkupCompatibilityProcessSettings MarkupCompatibilityProcessSettings
/// When set to true, the document will be checked to ensure it contains the necessary parts and structure
/// to be considered a valid Open XML package.
///
- public bool CheckMinimumPackage { get; set; }
+ public bool VerifyMinimumPackage { get; set; }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt b/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
index 7827e319d..f1c172980 100644
--- a/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
+++ b/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
@@ -1009,5 +1009,5 @@ DocumentFormat.OpenXml.OpenXmlPartWriterSettings.Encoding.set -> void
DocumentFormat.OpenXml.OpenXmlPartWriterSettings.OpenXmlPartWriterSettings() -> void
DocumentFormat.OpenXml.OpenXmlPartWriter.OpenXmlPartWriter(DocumentFormat.OpenXml.Packaging.OpenXmlPart! openXmlPart, DocumentFormat.OpenXml.OpenXmlPartWriterSettings! settings) -> void
DocumentFormat.OpenXml.OpenXmlPartWriter.OpenXmlPartWriter(System.IO.Stream! partStream, DocumentFormat.OpenXml.OpenXmlPartWriterSettings! settings) -> void
-DocumentFormat.OpenXml.Packaging.OpenSettings.CheckMinimumPackage.get -> bool
-DocumentFormat.OpenXml.Packaging.OpenSettings.CheckMinimumPackage.set -> void
+DocumentFormat.OpenXml.Packaging.OpenSettings.VerifyMinimumPackage.get -> bool
+DocumentFormat.OpenXml.Packaging.OpenSettings.VerifyMinimumPackage.set -> void
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index 1843bcceb..9c251be5b 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -234,6 +234,13 @@ public static PresentationDocument Open(Package package)
public static PresentationDocument Open(string path, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.VerifyMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(path, isEditable);
@@ -251,6 +258,13 @@ public static PresentationDocument Open(string path, bool isEditable, OpenSettin
public static PresentationDocument Open(Stream stream, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.VerifyMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(stream, isEditable);
@@ -267,120 +281,55 @@ public static PresentationDocument Open(Stream stream, bool isEditable, OpenSett
public static PresentationDocument Open(Package package, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.VerifyMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(package);
///
- /// Validates whether the specified file is a minimum valid PresentationDocument.
- ///
- /// The path to the PresentationDocument file.
- ///
- /// The expected type of the PresentationDocument. Defaults to .
- /// Supported types are:
- ///
- /// - (.pptx)
- /// - (.potx)
- /// - (.pptm)
- /// - (.potm)
- ///
- ///
- ///
- /// true if the file is a minimum valid PresentationDocument; otherwise, false.
- ///
- ///
- /// Thrown when the is invalid or unsupported.
+ /// Validates that the current meets the minimum requirements for a valid package.
+ ///
+ ///
+ /// Thrown if the is ,
+ /// , or ,
+ /// as validation for these types is not supported.
+ ///
+ ///
+ /// Thrown if the does not contain valid NotesSize dimensions
+ /// or if the dimensions are outside the acceptable range for PowerPoint to open.
///
///
- /// A minimum valid PresentationDocument must meet the following criteria:
- ///
- /// - The file must exist and have a valid extension matching the .
- /// - The file must contain a valid element in the presentation part.
- ///
- /// Unsupported document types include:
- ///
- /// - (.ppam)
- /// - (.ppsx)
- /// - (.ppsm)
- ///
+ /// This method ensures that the document conforms to the minimum requirements for PowerPoint to open it.
///
- public static bool IsMinimumDocument(string path, PresentationDocumentType documentType = PresentationDocumentType.Presentation)
+ protected override void ThrowIfNotMinimumPackage()
{
- if (documentType == PresentationDocumentType.AddIn || documentType == PresentationDocumentType.Slideshow || documentType == PresentationDocumentType.MacroEnabledSlideshow)
+ if (this.DocumentType == PresentationDocumentType.Slideshow)
{
- throw new ArgumentException($"Invalid value: {documentType}. Allowed values are PresentationDocumentType.Presentation, PresentationDocumentType.MacroEnabledPresentation, PresentationDocumentType.MacroEnabledTemplate, and PresentationDocumentType.Template.");
+ throw new NotSupportedException("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.");
}
- if (string.IsNullOrEmpty(path))
+ if (this.DocumentType == PresentationDocumentType.MacroEnabledSlideshow)
{
- return false;
+ throw new NotSupportedException("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.");
}
- if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
+ if (this.DocumentType == PresentationDocumentType.AddIn)
{
- return false;
+ throw new NotSupportedException("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.");
}
- try
- {
- if (!File.Exists(path))
- {
- return false;
- }
-
- string ext = new FileInfo(path).Extension.ToUpperInvariant();
+ NotesSize? notesSize = this.PresentationPart?.Presentation?.NotesSize;
- switch (ext)
- {
- case ".PPTX":
- if (documentType != PresentationDocumentType.Presentation)
- {
- return false;
- }
-
- break;
- case ".POTX":
- if (documentType != PresentationDocumentType.Template)
- {
- return false;
- }
-
- break;
- case ".PPTM":
- if (documentType != PresentationDocumentType.MacroEnabledPresentation)
- {
- return false;
- }
-
- break;
- case ".POTM":
- if (documentType != PresentationDocumentType.MacroEnabledTemplate)
- {
- return false;
- }
-
- break;
- case ".PPSX":
- throw new FileFormatException($"Validation for PresentationDocumentType.AddIn (.ppsx) is not supported.");
- case ".PPSM":
- throw new FileFormatException($"Validation for PresentationDocumentType.AddIn (.ppsm) is not supported.");
- case ".PPAM":
- throw new FileFormatException($"Validation for PresentationDocumentType.AddIn (.ppam) is not supported.");
- default:
- return false;
- }
-
- using (PresentationDocument presentationDocument = Open(path, false))
- {
- NotesSize? notesSize = presentationDocument.PresentationPart?.Presentation?.NotesSize;
-
- return notesSize is not null && notesSize.Cx is not null && notesSize.Cx.HasValue &&
- notesSize.Cx >= 0 && notesSize.Cx <= 27273042316900 && notesSize.Cy is not null && notesSize.Cy.HasValue &&
- notesSize.Cy >= 0 && notesSize.Cy <= 27273042316900;
- }
- }
- catch
+ if (!(notesSize is not null && notesSize.Cx is not null && notesSize.Cx.HasValue &&
+ notesSize.Cx >= 0 && notesSize.Cx <= 27273042316900 && notesSize.Cy is not null &&
+ notesSize.Cy.HasValue && notesSize.Cy >= 0 && notesSize.Cy <= 27273042316900))
{
- return false;
+ throw new FileFormatException("The provided package does not conform to the minimum requirements for PowerPoint to open.");
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index 2b7ecab5f..d4e47f170 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -198,6 +198,13 @@ public static SpreadsheetDocument CreateFromTemplate(string path)
public static SpreadsheetDocument Open(string path, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.VerifyMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(path, isEditable);
@@ -215,6 +222,13 @@ public static SpreadsheetDocument Open(string path, bool isEditable, OpenSetting
public static SpreadsheetDocument Open(Stream stream, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.VerifyMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(stream, isEditable);
@@ -231,6 +245,13 @@ public static SpreadsheetDocument Open(Stream stream, bool isEditable, OpenSetti
public static SpreadsheetDocument Open(Package package, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
+ .Use(package =>
+ {
+ if (openSettings.VerifyMinimumPackage)
+ {
+ package.ThrowIfNotMinimumPackage();
+ }
+ })
.Build()
.Open(package);
@@ -269,107 +290,35 @@ public static SpreadsheetDocument Open(System.IO.Packaging.Package package)
=> Open(package, new OpenSettings());
///
- /// Validates whether the specified file is a minimum valid SpreadsheetDocument.
+ /// Throws a if the current
+ /// does not meet the minimum requirements for a valid package.
///
- /// The path to the SpreadsheetDocument file.
- ///
- /// The expected type of the SpreadsheetDocument. Defaults to .
- /// Supported types are:
+ ///
+ /// Thrown when the does not conform to the minimum requirements
+ /// for Excel to open. This includes:
///
- /// - (.xlsx)
- /// - (.xltx)
- /// - (.xlsm)
- /// - (.xltm)
+ /// - The document type is .
+ /// - The is missing or does not contain a valid .
+ /// - The in the first is missing.
///
- ///
- ///
- /// true if the file is a minimum valid SpreadsheetDocument; otherwise, false.
- ///
- ///
- /// Thrown when the is invalid or unsupported.
///
///
- /// A minimum valid SpreadsheetDocument must meet the following criteria:
- ///
- /// - The file must exist and have a valid extension matching the .
- /// - The file must contain at least one element in the workbook part.
- /// - The file must contain at least one element in the worksheet part.
- ///
- /// Unsupported document types include (.xlam).
+ /// This method ensures that the contains the necessary parts and structure
+ /// to be opened with Excel.
///
- public static bool IsMinimumDocument(string path, SpreadsheetDocumentType documentType = SpreadsheetDocumentType.Workbook)
+ protected override void ThrowIfNotMinimumPackage()
{
- if (documentType == SpreadsheetDocumentType.AddIn)
- {
- throw new ArgumentException($"Invalid value: {documentType}. Allowed values are SpreadsheetDocumentType.Workbook, SpreadsheetDocumentType.Template, SpreadsheetDocumentType.MacroEnabledWorkbook, and SpreadsheetDocumentType.MacroEnabledTemplate.");
- }
-
- if (string.IsNullOrEmpty(path))
- {
- return false;
- }
-
- if (path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0)
+ if (this.DocumentType == SpreadsheetDocumentType.AddIn)
{
- return false;
+ throw new NotSupportedException("Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.");
}
- try
- {
- if (!File.Exists(path))
- {
- return false;
- }
-
- string ext = new FileInfo(path).Extension.ToUpperInvariant();
-
- switch (ext)
- {
- case ".XLSX":
- if (documentType != SpreadsheetDocumentType.Workbook)
- {
- return false;
- }
-
- break;
- case ".XLTX":
- if (documentType != SpreadsheetDocumentType.Template)
- {
- return false;
- }
-
- break;
- case ".XLSM":
- if (documentType != SpreadsheetDocumentType.MacroEnabledWorkbook)
- {
- return false;
- }
-
- break;
- case ".XLTM":
- if (documentType != SpreadsheetDocumentType.MacroEnabledTemplate)
- {
- return false;
- }
-
- break;
- case ".XLAM":
- throw new FileFormatException($"Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.");
- default:
- return false;
- }
-
- using (SpreadsheetDocument spreadsheetDocument = Open(path, false))
- {
- Sheet? sheet = spreadsheetDocument?.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
- SheetData? sheetData = spreadsheetDocument?.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
+ Sheet? sheet = this.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
+ SheetData? sheetData = this.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
- return sheet is not null && sheetData is not null;
- }
- }
- catch
+ if (sheet is null || sheetData is null)
{
- return false;
+ throw new FileFormatException("The provided package does not conform to the minimum requirements for Excel to open.");
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 392b6106e..2422c2d7a 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -266,7 +266,7 @@ public static WordprocessingDocument Open(string path, bool isEditable, OpenSett
.UseSettings(openSettings)
.Use(package =>
{
- if (openSettings.CheckMinimumPackage)
+ if (openSettings.VerifyMinimumPackage)
{
package.ThrowIfNotMinimumPackage();
}
@@ -290,7 +290,7 @@ public static WordprocessingDocument Open(Stream stream, bool isEditable, OpenSe
.UseSettings(openSettings)
.Use(package =>
{
- if (openSettings.CheckMinimumPackage)
+ if (openSettings.VerifyMinimumPackage)
{
package.ThrowIfNotMinimumPackage();
}
@@ -313,7 +313,7 @@ public static WordprocessingDocument Open(Package package, OpenSettings openSett
.UseSettings(openSettings)
.Use(package =>
{
- if (openSettings.CheckMinimumPackage)
+ if (openSettings.VerifyMinimumPackage)
{
package.ThrowIfNotMinimumPackage();
}
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
index 872fe514f..9a59ff7a8 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
@@ -880,7 +880,7 @@ public void O15FileOpenTest()
}
[Fact]
- public void CheckMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessing()
+ public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessing()
{
// Arrange
string path = string.Concat(Path.GetTempPath(), "valid.docx");
@@ -894,7 +894,7 @@ public void CheckMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessin
// Act
Exception exception = Record.Exception(() =>
{
- using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { CheckMinimumPackage = true });
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
});
// Assert
@@ -902,7 +902,7 @@ public void CheckMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessin
}
[Fact]
- public void CheckMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Wordprocessing()
+ public void VerifyMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Wordprocessing()
{
// Arrange
using (Stream stream = new MemoryStream())
@@ -915,7 +915,7 @@ public void CheckMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Wordprocess
// Act
Exception exception = Record.Exception(() =>
{
- using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { CheckMinimumPackage = true });
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
});
// Assert
@@ -924,7 +924,7 @@ public void CheckMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Wordprocess
}
[Fact]
- public void CheckMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Wordprocessing()
+ public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Wordprocessing()
{
// Arrange
string path = Path.Combine(Path.GetTempPath(), "valid.docx");
@@ -939,17 +939,18 @@ public void CheckMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Wordproces
{
Exception exception = Record.Exception(() =>
{
- using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { CheckMinimumPackage = true });
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
});
// Assert
Assert.Null(exception);
}
-
}
+ private readonly string minPackageWordExMsg = "The provided package does not conform to the minimum requirements for Word to open.";
+
[Fact]
- public void CheckMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
+ public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
{
// Arrange
string path = string.Concat(Path.GetTempPath(), "invalid.docx");
@@ -960,14 +961,16 @@ public void CheckMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
}
// Act and Assert
- Assert.Throws(() =>
+ FileFormatException ex = Assert.Throws(() =>
{
- using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { CheckMinimumPackage = true });
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
});
+
+ Assert.Equal(minPackageWordExMsg, ex.Message);
}
[Fact]
- public void CheckMinimumPackageTest_InValidDocumentStream_Throws_Wordprocessing()
+ public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Wordprocessing()
{
// Arrange
using (Stream stream = new MemoryStream())
@@ -978,15 +981,17 @@ public void CheckMinimumPackageTest_InValidDocumentStream_Throws_Wordprocessing(
}
// Act and Assert
- Assert.Throws(() =>
+ FileFormatException ex = Assert.Throws(() =>
{
- using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { CheckMinimumPackage = true });
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
});
+
+ Assert.Equal(minPackageWordExMsg, ex.Message);
}
}
[Fact]
- public void CheckMinimumPackageTest_InValidDocumentPackage_Throws_Wordprocessing()
+ public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Wordprocessing()
{
// Arrange
using (Stream stream = new MemoryStream())
@@ -999,11 +1004,411 @@ public void CheckMinimumPackageTest_InValidDocumentPackage_Throws_Wordprocessing
// Act and Assert
using (Package package = Package.Open(stream))
{
- Assert.Throws(() =>
+ FileFormatException ex = Assert.Throws(() =>
{
- using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { CheckMinimumPackage = true });
+ using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
});
+
+ Assert.Equal(minPackageWordExMsg, ex.Message);
+ }
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Spreadsheet()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "valid.xlsx");
+
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ // Act
+ Exception exception = Record.Exception(() =>
+ {
+ using SpreadsheetDocument ssd = SpreadsheetDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Spreadsheet()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.MacroEnabledTemplate))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
}
+
+ // Act
+ Exception exception = Record.Exception(() =>
+ {
+ using SpreadsheetDocument ssd = SpreadsheetDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Spreadsheet()
+ {
+ // Arrange
+ string path = Path.Combine(Path.GetTempPath(), "valid.xlsx");
+
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Template))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ // Act
+ using (Package package = Package.Open(path))
+ {
+ Exception exception = Record.Exception(() =>
+ {
+ using SpreadsheetDocument ssd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+ }
+
+ private readonly string spreadsheetExMsg = "The provided package does not conform to the minimum requirements for Excel to open.";
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Spreadsheet()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "invalid.xlsx");
+
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.MacroEnabledWorkbook))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ }
+
+ // Act and Assert
+ FileFormatException ex = Assert.Throws(() =>
+ {
+ using SpreadsheetDocument ssd = SpreadsheetDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal(spreadsheetExMsg, ex.Message);
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Spreadsheet()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ // Act and Assert
+ FileFormatException ex = Assert.Throws(() =>
+ {
+ using SpreadsheetDocument ssd = SpreadsheetDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal(spreadsheetExMsg, ex.Message);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Spreadsheet()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ }
+
+ // Act and Assert
+ using (Package package = Package.Open(stream))
+ {
+ FileFormatException ex = Assert.Throws(() =>
+ {
+ using SpreadsheetDocument wpd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal(spreadsheetExMsg, ex.Message);
+ }
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_AddInDocumentPackage_Throws_Spreadsheet()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.AddIn))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ // Act and Assert
+ using (Package package = Package.Open(stream))
+ {
+ NotSupportedException ex = Assert.Throws(() =>
+ {
+ using SpreadsheetDocument wpd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal("Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.", ex.Message);
+ }
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Presentation()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "valid.pptx");
+
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+
+ // Act
+ Exception exception = Record.Exception(() =>
+ {
+ using PresentationDocument pd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Presentation()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act
+ Exception exception = Record.Exception(() =>
+ {
+ using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Presentation()
+ {
+ // Arrange
+ string path = Path.Combine(Path.GetTempPath(), "valid.xlsx");
+
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Template))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ }
+
+ // Act
+ using (Package package = Package.Open(path))
+ {
+ Exception exception = Record.Exception(() =>
+ {
+ using SpreadsheetDocument ssd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ // Assert
+ Assert.Null(exception);
+ }
+ }
+
+ private readonly string presentationExMsg = "The provided package does not conform to the minimum requirements for PowerPoint to open.";
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Presentation()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "invalid.pptx");
+
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = -2, Cy = 913607 });
+ }
+
+ // Act and Assert
+ FileFormatException ex = Assert.Throws(() =>
+ {
+ using PresentationDocument ssd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal(presentationExMsg, ex.Message);
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Presentation()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ }
+
+ // Act and Assert
+ FileFormatException ex = Assert.Throws(() =>
+ {
+ using PresentationDocument ssd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal(presentationExMsg, ex.Message);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Presentation()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ }
+
+ // Act and Assert
+ using (Package package = Package.Open(stream))
+ {
+ FileFormatException ex = Assert.Throws(() =>
+ {
+ using PresentationDocument pd = PresentationDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal(presentationExMsg, ex.Message);
+ }
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_AddInDocumentPackage_Throws_Presentation()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.AddIn))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act and Assert
+ NotSupportedException ex = Assert.Throws(() =>
+ {
+ using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.", ex.Message);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_MacroEnabledSlideshowDocumentPackage_Throws_Presentation()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.MacroEnabledSlideshow))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act and Assert
+ NotSupportedException ex = Assert.Throws(() =>
+ {
+ using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.", ex.Message);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_SlideshowDocumentPackage_Throws_Presentation()
+ {
+ // Arrange
+ using (Stream stream = new MemoryStream())
+ {
+ using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Slideshow))
+ {
+ // create presentation part
+ PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ presentationPart.Presentation = new Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ }
+
+ // Act and Assert
+ NotSupportedException ex = Assert.Throws(() =>
+ {
+ using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ });
+
+ Assert.Equal("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.", ex.Message);
}
}
}
From 8ba0e4d1929174ffd7fb02ca7957318c7bd9ccec Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 5 May 2025 16:18:06 -0700
Subject: [PATCH 09/33] fix linting issues
---
.../ofapiTest/OpenXmlPackageTest.cs | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
index 9a59ff7a8..1a0b190c1 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
@@ -23,8 +23,14 @@
namespace DocumentFormat.OpenXml.Tests
{
///
- /// Summary description for OpenXmlPackageTest
+ /// Contains unit tests for validating the behavior of Open XML packages, including WordprocessingDocument,
+ /// PresentationDocument, and SpreadsheetDocument. These tests cover scenarios such as auto-save functionality,
+ /// document type changes, part relationships, and minimum package verification.
///
+ ///
+ /// This class uses the xUnit framework for testing and includes tests for various Open XML document types.
+ /// It ensures the correctness of document operations, relationships, and compliance with Open XML standards.
+ ///
public class OpenXmlPackageTest
{
[Fact]
@@ -888,7 +894,6 @@ public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessi
using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
{
document.AddMainDocumentPart().Document = new Document(new Body());
-
}
// Act
@@ -1203,7 +1208,6 @@ public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Presentation
presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
}
-
// Act
Exception exception = Record.Exception(() =>
{
From 55e3eef16d83a67e680fdeb31f4c8437f17f9d6f Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Tue, 6 May 2025 09:02:43 -0700
Subject: [PATCH 10/33] remove trailing whitespace
---
src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index d4e47f170..3a58f7e8a 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -290,11 +290,11 @@ public static SpreadsheetDocument Open(System.IO.Packaging.Package package)
=> Open(package, new OpenSettings());
///
- /// Throws a if the current
+ /// Throws a if the current
/// does not meet the minimum requirements for a valid package.
///
///
- /// Thrown when the does not conform to the minimum requirements
+ /// Thrown when the does not conform to the minimum requirements
/// for Excel to open. This includes:
///
/// - The document type is .
From 69a5900abd3d057874ce69469c29533e4c543c46 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Tue, 6 May 2025 09:21:31 -0700
Subject: [PATCH 11/33] remove unnecessary $ from string
---
.../Packaging/OpenXmlPackage.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index 3b70ed3e3..c4deda42a 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -115,7 +115,7 @@ internal void LoadAllParts()
/// This method is intended to be overridden by derived classes to implement specific validation logic
/// for different types of Open XML packages (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
///
- protected virtual void ThrowIfNotMinimumPackage() => throw new FileFormatException($"The provided package does not conform to the minimum requirements for Office to open.");
+ protected virtual void ThrowIfNotMinimumPackage() => throw new FileFormatException("The provided package does not conform to the minimum requirements for Office to open.");
#region public methods
From 9bc42e2b761343423337de3df2b883fd7a4fa618 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Tue, 6 May 2025 09:50:29 -0700
Subject: [PATCH 12/33] remove unnecessary usings
---
src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index 9c251be5b..8574a62e3 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -2,14 +2,12 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Builder;
-using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Features;
using DocumentFormat.OpenXml.Presentation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Packaging;
-using System.Linq;
using System.Reflection;
namespace DocumentFormat.OpenXml.Packaging
From 3a0c3679492084ecf0ae99f8ad505021e81bc212 Mon Sep 17 00:00:00 2001
From: Taylor Southwick
Date: Mon, 12 May 2025 10:48:56 -0700
Subject: [PATCH 13/33] use feature for minimum document
---
.../OpenXmlPackageBuilderExtensions.cs | 10 ++-
.../Features/IMinimumDocumentFeature.cs | 9 ++
.../Packaging/PresentationDocument.cs | 87 +++++--------------
.../TypedPackageFeatureCollection.cs | 8 +-
4 files changed, 48 insertions(+), 66 deletions(-)
create mode 100644 src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs
diff --git a/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs b/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs
index 51020763d..b546129e5 100644
--- a/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs
@@ -110,7 +110,15 @@ public static IPackageBuilder Use(this IPackageBuilder UseSettings(this IPackageBuilder builder, OpenSettings settings)
where TPackage : OpenXmlPackage
- => builder.Use(package => package.OpenSettings = settings);
+ => builder.Use(package =>
+ {
+ package.OpenSettings = settings;
+
+ if (settings.VerifyMinimumPackage && package.Features.Get() is { } minimumFeature && !minimumFeature.Validate())
+ {
+ throw new FileFormatException("The provided package does not conform to the minimum requirements to open.");
+ }
+ });
internal static IPackageBuilder UseDefaultBehaviorAndLockBuilder(this IPackageBuilder builder)
where TPackage : OpenXmlPackage
diff --git a/src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs b/src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs
new file mode 100644
index 000000000..3f1b725cf
--- /dev/null
+++ b/src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs
@@ -0,0 +1,9 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace DocumentFormat.OpenXml.Features;
+
+internal interface IMinimumDocumentFeature
+{
+ bool Validate();
+}
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index 8574a62e3..5913f9570 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -232,13 +232,6 @@ public static PresentationDocument Open(Package package)
public static PresentationDocument Open(string path, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(path, isEditable);
@@ -256,13 +249,6 @@ public static PresentationDocument Open(string path, bool isEditable, OpenSettin
public static PresentationDocument Open(Stream stream, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(stream, isEditable);
@@ -279,58 +265,9 @@ public static PresentationDocument Open(Stream stream, bool isEditable, OpenSett
public static PresentationDocument Open(Package package, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(package);
- ///
- /// Validates that the current meets the minimum requirements for a valid package.
- ///
- ///
- /// Thrown if the is ,
- /// , or ,
- /// as validation for these types is not supported.
- ///
- ///
- /// Thrown if the does not contain valid NotesSize dimensions
- /// or if the dimensions are outside the acceptable range for PowerPoint to open.
- ///
- ///
- /// This method ensures that the document conforms to the minimum requirements for PowerPoint to open it.
- ///
- protected override void ThrowIfNotMinimumPackage()
- {
- if (this.DocumentType == PresentationDocumentType.Slideshow)
- {
- throw new NotSupportedException("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.");
- }
-
- if (this.DocumentType == PresentationDocumentType.MacroEnabledSlideshow)
- {
- throw new NotSupportedException("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.");
- }
-
- if (this.DocumentType == PresentationDocumentType.AddIn)
- {
- throw new NotSupportedException("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.");
- }
-
- NotesSize? notesSize = this.PresentationPart?.Presentation?.NotesSize;
-
- if (!(notesSize is not null && notesSize.Cx is not null && notesSize.Cx.HasValue &&
- notesSize.Cx >= 0 && notesSize.Cx <= 27273042316900 && notesSize.Cy is not null &&
- notesSize.Cy.HasValue && notesSize.Cy >= 0 && notesSize.Cy <= 27273042316900))
- {
- throw new FileFormatException("The provided package does not conform to the minimum requirements for PowerPoint to open.");
- }
- }
-
///
/// Changes the document type.
///
@@ -570,7 +507,8 @@ public LabelInfoPart? LabelInfoPart
private partial class PresentationDocumentFeatures : TypedPackageFeatureCollection,
IApplicationTypeFeature,
IMainPartFeature,
- IProgrammaticIdentifierFeature
+ IProgrammaticIdentifierFeature,
+ IMinimumDocumentFeature
{
public PresentationDocumentFeatures(OpenXmlPackage package)
: base(package)
@@ -608,6 +546,27 @@ public PresentationDocumentFeatures(OpenXmlPackage package)
"application/vnd.ms-powerpoint.addin.macroEnabled.main+xml" => PresentationDocumentType.AddIn,
_ => default,
};
+
+ bool IMinimumDocumentFeature.Validate()
+ {
+ if (DocumentType is PresentationDocumentType.Slideshow or PresentationDocumentType.MacroEnabledSlideshow or PresentationDocumentType.AddIn)
+ {
+ return false;
+ }
+
+ return HasValidNotes();
+ }
+
+ private bool HasValidNotes()
+ {
+ const long MaxSize = 27273042316900;
+
+ return MainPart?.Presentation?.NotesSize is
+ {
+ Cy: { HasValue: true, Value: >= 0 and <= MaxSize },
+ Cx: { HasValue: true, Value: >= 0 and <= MaxSize }
+ };
+ }
}
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs b/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs
index 768fc5f45..80fe18f32 100644
--- a/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs
@@ -62,12 +62,18 @@ private TDocumentType EnsureDocumentType()
return _documentType.Value;
}
- TDocumentType IDocumentTypeFeature.Current
+ protected TDocumentType DocumentType
{
get => EnsureDocumentType();
set => _documentType = value;
}
+ TDocumentType IDocumentTypeFeature.Current
+ {
+ get => DocumentType;
+ set => DocumentType = value;
+ }
+
protected TMainPart? MainPart => Package.GetSubPartOfType();
OpenXmlPart? IMainPartFeature.Part => MainPart;
From fca841ac871d77b8c4ee9e7043e4eaf4ef5170d4 Mon Sep 17 00:00:00 2001
From: Taylor Southwick
Date: Mon, 12 May 2025 12:31:49 -0700
Subject: [PATCH 14/33] a bit more
---
.../Packaging/PresentationDocument.cs | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index 5913f9570..904bdbcbb 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -559,12 +559,15 @@ bool IMinimumDocumentFeature.Validate()
private bool HasValidNotes()
{
- const long MaxSize = 27273042316900;
+ const long MaxNoteSize = 27273042316900;
- return MainPart?.Presentation?.NotesSize is
+ return MainPart is
{
- Cy: { HasValue: true, Value: >= 0 and <= MaxSize },
- Cx: { HasValue: true, Value: >= 0 and <= MaxSize }
+ Presentation.NotesSize:
+ {
+ Cy: { HasValue: true, Value: >= 0 and <= MaxNoteSize },
+ Cx: { HasValue: true, Value: >= 0 and <= MaxNoteSize },
+ }
};
}
}
From a4b61a73d0b1dd045717eeadf20bfd166b282609 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 12 May 2025 16:00:36 -0700
Subject: [PATCH 15/33] move VerfiyMinimumPackage to feature
---
.../Packaging/OpenXmlPackage.cs | 2 +-
.../Packaging/PresentationDocument.cs | 14 +++-
.../Packaging/SpreadsheetDocument.cs | 75 +++++--------------
.../Packaging/WordprocessingDocument.cs | 53 ++++---------
.../ofapiTest/OpenXmlPackageTest.cs | 24 +++---
5 files changed, 56 insertions(+), 112 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index c4deda42a..1ee530103 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -115,7 +115,7 @@ internal void LoadAllParts()
/// This method is intended to be overridden by derived classes to implement specific validation logic
/// for different types of Open XML packages (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
///
- protected virtual void ThrowIfNotMinimumPackage() => throw new FileFormatException("The provided package does not conform to the minimum requirements for Office to open.");
+ protected virtual void ThrowIfNotMinimumPackage() => throw new FileFormatException("The provided package does not conform to the minimum requirements to open.");
#region public methods
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index 904bdbcbb..525c250b2 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -549,9 +549,19 @@ public PresentationDocumentFeatures(OpenXmlPackage package)
bool IMinimumDocumentFeature.Validate()
{
- if (DocumentType is PresentationDocumentType.Slideshow or PresentationDocumentType.MacroEnabledSlideshow or PresentationDocumentType.AddIn)
+ if (this.DocumentType == PresentationDocumentType.Slideshow)
{
- return false;
+ throw new NotSupportedException("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.");
+ }
+
+ if (this.DocumentType == PresentationDocumentType.MacroEnabledSlideshow)
+ {
+ throw new NotSupportedException("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.");
+ }
+
+ if (this.DocumentType == PresentationDocumentType.AddIn)
+ {
+ throw new NotSupportedException("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.");
}
return HasValidNotes();
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index 3a58f7e8a..f1acfa35a 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -198,13 +198,6 @@ public static SpreadsheetDocument CreateFromTemplate(string path)
public static SpreadsheetDocument Open(string path, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(path, isEditable);
@@ -222,13 +215,6 @@ public static SpreadsheetDocument Open(string path, bool isEditable, OpenSetting
public static SpreadsheetDocument Open(Stream stream, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(stream, isEditable);
@@ -245,13 +231,6 @@ public static SpreadsheetDocument Open(Stream stream, bool isEditable, OpenSetti
public static SpreadsheetDocument Open(Package package, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(package);
@@ -289,39 +268,6 @@ public static SpreadsheetDocument Open(System.IO.Stream stream, bool isEditable)
public static SpreadsheetDocument Open(System.IO.Packaging.Package package)
=> Open(package, new OpenSettings());
- ///
- /// Throws a if the current
- /// does not meet the minimum requirements for a valid package.
- ///
- ///
- /// Thrown when the does not conform to the minimum requirements
- /// for Excel to open. This includes:
- ///
- /// - The document type is .
- /// - The is missing or does not contain a valid .
- /// - The in the first is missing.
- ///
- ///
- ///
- /// This method ensures that the contains the necessary parts and structure
- /// to be opened with Excel.
- ///
- protected override void ThrowIfNotMinimumPackage()
- {
- if (this.DocumentType == SpreadsheetDocumentType.AddIn)
- {
- throw new NotSupportedException("Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.");
- }
-
- Sheet? sheet = this.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
- SheetData? sheetData = this.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
-
- if (sheet is null || sheetData is null)
- {
- throw new FileFormatException("The provided package does not conform to the minimum requirements for Excel to open.");
- }
- }
-
///
/// Changes the document type.
///
@@ -560,7 +506,8 @@ public LabelInfoPart? LabelInfoPart
[DocumentFormat.OpenXml.Generator.OpenXmlPackage("SpreadsheetDocument")]
private partial class SpreadsheetDocumentFeatures : TypedPackageFeatureCollection,
IApplicationTypeFeature,
- IMainPartFeature
+ IMainPartFeature,
+ IMinimumDocumentFeature
{
public SpreadsheetDocumentFeatures(OpenXmlPackage package)
: base(package)
@@ -592,6 +539,24 @@ public SpreadsheetDocumentFeatures(OpenXmlPackage package)
"application/vnd.ms-excel.addin.macroEnabled.main+xml" => SpreadsheetDocumentType.AddIn,
_ => default,
};
+
+ bool IMinimumDocumentFeature.Validate()
+ {
+ if (DocumentType == SpreadsheetDocumentType.AddIn)
+ {
+ throw new NotSupportedException("Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.");
+ }
+
+ Sheet? sheet = MainPart?.Workbook?.Sheets?.GetFirstChild();
+ SheetData? sheetData = MainPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
+
+ if (sheet is not null && sheetData is not null)
+ {
+ return true;
+ }
+
+ return false;
+ }
}
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 2422c2d7a..94b9dcb59 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -264,13 +264,6 @@ public static WordprocessingDocument Open(Stream stream, bool isEditable)
public static WordprocessingDocument Open(string path, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(path, isEditable);
@@ -288,13 +281,6 @@ public static WordprocessingDocument Open(string path, bool isEditable, OpenSett
public static WordprocessingDocument Open(Stream stream, bool isEditable, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(stream, isEditable);
@@ -311,13 +297,6 @@ public static WordprocessingDocument Open(Stream stream, bool isEditable, OpenSe
public static WordprocessingDocument Open(Package package, OpenSettings openSettings)
=> CreateDefaultBuilder()
.UseSettings(openSettings)
- .Use(package =>
- {
- if (openSettings.VerifyMinimumPackage)
- {
- package.ThrowIfNotMinimumPackage();
- }
- })
.Build()
.Open(package);
@@ -332,24 +311,6 @@ public static WordprocessingDocument Open(Package package, OpenSettings openSett
public static WordprocessingDocument Open(Package package)
=> Open(package, new OpenSettings());
- ///
- /// Throws a if the current does not meet the minimum requirements for a valid package.
- ///
- ///
- /// Thrown when the is missing or its is null.
- ///
- ///
- /// This method ensures that the contains the necessary parts and structure
- /// to be opened with Word.
- ///
- protected override void ThrowIfNotMinimumPackage()
- {
- if (this.MainDocumentPart?.Document?.Body is null)
- {
- throw new FileFormatException("The provided package does not conform to the minimum requirements for Word to open.");
- }
- }
-
///
/// Changes the document type.
///
@@ -589,7 +550,8 @@ public LabelInfoPart? LabelInfoPart
private partial class WordprocessingDocumentFeatures : TypedPackageFeatureCollection,
IApplicationTypeFeature,
IMainPartFeature,
- IProgrammaticIdentifierFeature
+ IProgrammaticIdentifierFeature,
+ IMinimumDocumentFeature
{
public WordprocessingDocumentFeatures(OpenXmlPackage package)
: base(package)
@@ -621,6 +583,17 @@ public WordprocessingDocumentFeatures(OpenXmlPackage package)
"application/vnd.ms-word.template.macroEnabledTemplate.main+xml" => WordprocessingDocumentType.MacroEnabledTemplate,
_ => default,
};
+
+ bool IMinimumDocumentFeature.Validate()
+ {
+ return MainPart is
+ {
+ Document:
+ {
+ Body: { }
+ }
+ };
+ }
}
}
}
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
index 1a0b190c1..458b36ad9 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
@@ -952,7 +952,7 @@ public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Wordproce
}
}
- private readonly string minPackageWordExMsg = "The provided package does not conform to the minimum requirements for Word to open.";
+ private readonly string minPackageExMsg = "The provided package does not conform to the minimum requirements to open.";
[Fact]
public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
@@ -971,7 +971,7 @@ public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(minPackageWordExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
[Fact]
@@ -991,7 +991,7 @@ public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Wordprocessing
using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(minPackageWordExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
}
@@ -1014,7 +1014,7 @@ public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Wordprocessin
using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(minPackageWordExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
}
}
@@ -1095,8 +1095,6 @@ public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Spreadshe
}
}
- private readonly string spreadsheetExMsg = "The provided package does not conform to the minimum requirements for Excel to open.";
-
[Fact]
public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Spreadsheet()
{
@@ -1116,7 +1114,7 @@ public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Spreadsheet()
using SpreadsheetDocument ssd = SpreadsheetDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(spreadsheetExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
[Fact]
@@ -1138,7 +1136,7 @@ public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Spreadsheet()
using SpreadsheetDocument ssd = SpreadsheetDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(spreadsheetExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
}
@@ -1162,7 +1160,7 @@ public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Spreadsheet()
using SpreadsheetDocument wpd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(spreadsheetExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
}
}
@@ -1270,8 +1268,6 @@ public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Presentat
}
}
- private readonly string presentationExMsg = "The provided package does not conform to the minimum requirements for PowerPoint to open.";
-
[Fact]
public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Presentation()
{
@@ -1292,7 +1288,7 @@ public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Presentation()
using PresentationDocument ssd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(presentationExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
[Fact]
@@ -1314,7 +1310,7 @@ public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Presentation()
using PresentationDocument ssd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(presentationExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
}
@@ -1339,7 +1335,7 @@ public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Presentation(
using PresentationDocument pd = PresentationDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
});
- Assert.Equal(presentationExMsg, ex.Message);
+ Assert.Equal(minPackageExMsg, ex.Message);
}
}
}
From 8e1d50de387a1d9054f16acfb3cceff7db08a709 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 12 May 2025 16:19:37 -0700
Subject: [PATCH 16/33] remove ThrowIfNotMinimumPackage from OpenXmlPackage
---
.../Packaging/OpenXmlPackage.cs | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index 1ee530103..c8ce6524a 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -105,18 +105,6 @@ internal void LoadAllParts()
}
}
- ///
- /// Throws a if the current does not meet the minimum requirements for a valid package.
- ///
- ///
- /// Thrown when the package does not conform to the minimum requirements for Office to open.
- ///
- ///
- /// This method is intended to be overridden by derived classes to implement specific validation logic
- /// for different types of Open XML packages (e.g., Wordprocessing, Spreadsheet, or Presentation documents).
- ///
- protected virtual void ThrowIfNotMinimumPackage() => throw new FileFormatException("The provided package does not conform to the minimum requirements to open.");
-
#region public methods
///
From 8d1d6ea904cd10b438a5bc4b27270f7873358f8d Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Wed, 14 May 2025 15:24:31 -0700
Subject: [PATCH 17/33] fix merge conflict
---
.../Packaging/OpenXmlPackage.cs | 3 +++
.../Validation/DocumentValidator.cs | 7 +++++++
.../Validation/ValidationSettings.cs | 7 +++++++
.../Packaging/WordprocessingDocument.cs | 3 +++
4 files changed, 20 insertions(+)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index 1ee530103..e370c55ba 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -3,6 +3,7 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Features;
+using DocumentFormat.OpenXml.Validation;
using System;
using System.Collections.Generic;
using System.IO;
@@ -624,5 +625,7 @@ public void Save()
///
public override IFeatureCollection Features => _features ??= new PackageFeatureCollection(this);
+
+ internal virtual IEnumerable VerifyMinimumDocument() => Enumerable.Empty();
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index df3ea4e46..b50aa2d60 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -275,5 +275,12 @@ private static string GetPartUri(OpenXmlPart? part)
// Example: WordprocessingCommentsPart{/word/comments.xml}
return SR.Format("{0}{1}{2}", '{', part.Uri, '}');
}
+
+ private IEnumerable VerifyMinimumPackage(OpenXmlPackage package)
+ {
+ if (package is PresentationDocument)
+ {
+ }
+ }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs
index e4423c5f1..96939a7cd 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs
@@ -32,5 +32,12 @@ public ValidationSettings(FileFormatVersions fileFormat)
/// Default is 1000. A zero (0) value means no limitation.
///
public int MaxNumberOfErrors { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the validator should verify that the package meets the minimum requirements
+ /// for the specified file format. When set to true, the validation process will include checks to ensure
+ /// the document structure is sufficient for the target application to open the file.
+ ///
+ public bool VerifyMinimumPackage { get; set; }
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 94b9dcb59..52df68b5d 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -3,8 +3,11 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
+using DocumentFormat.OpenXml.Validation;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Packaging;
From 04ef42bc4d2344b6ce5627f5ebd9755a95648a8a Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 15 May 2025 10:05:12 -0700
Subject: [PATCH 18/33] verify wordprocessing document
---
.../Packaging/OpenXmlPackage.cs | 5 +++-
.../Validation/DocumentValidator.cs | 9 ++-----
.../ValidationResources.Designer.cs | 9 +++++++
.../Validation/ValidationResources.resx | 3 +++
.../Packaging/WordprocessingDocument.cs | 25 +++++++++++++++++++
5 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index e370c55ba..6d4f68217 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -626,6 +626,9 @@ public void Save()
///
public override IFeatureCollection Features => _features ??= new PackageFeatureCollection(this);
- internal virtual IEnumerable VerifyMinimumDocument() => Enumerable.Empty();
+ internal virtual void VerifyMinimumDocument(ValidationContext validationContext)
+ {
+ return;
+ }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index b50aa2d60..14f09f3f3 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -50,6 +50,8 @@ public List Validate(OpenXmlPackage document, ValidationSet
// integrate the package validation.
ValidatePackageStructure(document, context);
+ document.VerifyMinimumDocument(context);
+
foreach (var part in PartsToBeValidated(document))
{
// traverse from the part root element (by DOM or by Reader) in post-order
@@ -275,12 +277,5 @@ private static string GetPartUri(OpenXmlPart? part)
// Example: WordprocessingCommentsPart{/word/comments.xml}
return SR.Format("{0}{1}{2}", '{', part.Uri, '}');
}
-
- private IEnumerable VerifyMinimumPackage(OpenXmlPackage package)
- {
- if (package is PresentationDocument)
- {
- }
- }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
index 0f07d0a74..c7fcd18d8 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
@@ -438,6 +438,15 @@ internal static string Sch_MinLengthConstraintFailed {
}
}
+ ///
+ /// Looks up a localized string similar to The '{0}' part is missing the root element '{1}'.
+ ///
+ internal static string Sch_MissingRootElement {
+ get {
+ return ResourceManager.GetString("Sch_MissingRootElement", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The required attribute '{0}' is missing..
///
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
index ed8489361..7ed9b7698 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
@@ -342,4 +342,7 @@
Cell contents have invalid value '{0}' for type '{1}'.
+
+ The '{0}' part is missing the root element '{1}'
+
\ No newline at end of file
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 52df68b5d..f26b8c2b8 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -546,6 +546,31 @@ public LabelInfoPart? LabelInfoPart
get { return GetSubPartOfType(); }
}
+ internal override void VerifyMinimumDocument(ValidationContext validationContext)
+ {
+ if (this.MainDocumentPart?.Document is null)
+ {
+ validationContext.AddError(new()
+ {
+ ErrorType = ValidationErrorType.Schema,
+ Id = "Sch_MissingRootElement",
+ Part = this.MainDocumentPart,
+ Description = SR.Format(ValidationResources.Sch_MissingRootElement, typeof(MainDocumentPart), typeof(Document)),
+ });
+ }
+
+ if (this.MainDocumentPart?.Document is not null && this.MainDocumentPart.Document?.Body is null)
+ {
+ validationContext.AddError(new()
+ {
+ ErrorType = ValidationErrorType.Schema,
+ Id = "Sch_IncompleteContentExpectingComplex",
+ Part = this.MainDocumentPart,
+ Description = SR.Format(ValidationResources.Sch_IncompleteContentExpectingComplex, typeof(Document)),
+ });
+ }
+ }
+
///
public override IFeatureCollection Features => _features ??= new WordprocessingDocumentFeatures(this);
From a23656688add6740be6d53f01ab6247043bcb5f1 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 15 May 2025 10:58:42 -0700
Subject: [PATCH 19/33] add minimum document validation for PowerPoint and
Excel
---
.../ValidationResources.Designer.cs | 18 ++++++-------
.../Validation/ValidationResources.resx | 4 +--
.../Packaging/PresentationDocument.cs | 26 +++++++++++++++++++
.../Packaging/SpreadsheetDocument.cs | 21 +++++++++++++++
.../Packaging/WordprocessingDocument.cs | 21 ++++++---------
5 files changed, 66 insertions(+), 24 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
index c7fcd18d8..2a19d601b 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
@@ -348,6 +348,15 @@ internal static string Sch_IncompleteContentExpectingComplex {
}
}
+ ///
+ /// Looks up a localized string similar to The provided package does not conform to the minimum requirements for {0} to open..
+ ///
+ internal static string Sch_IncompletePackage {
+ get {
+ return ResourceManager.GetString("Sch_IncompletePackage", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The element '{0}' is a leaf element and cannot contain children..
///
@@ -438,15 +447,6 @@ internal static string Sch_MinLengthConstraintFailed {
}
}
- ///
- /// Looks up a localized string similar to The '{0}' part is missing the root element '{1}'.
- ///
- internal static string Sch_MissingRootElement {
- get {
- return ResourceManager.GetString("Sch_MissingRootElement", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to The required attribute '{0}' is missing..
///
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
index 7ed9b7698..0ab938c17 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
@@ -342,7 +342,7 @@
Cell contents have invalid value '{0}' for type '{1}'.
-
- The '{0}' part is missing the root element '{1}'
+
+ The provided package does not conform to the minimum requirements for {0} to open.
\ No newline at end of file
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index 525c250b2..e79ddbfc7 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -4,6 +4,7 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
using DocumentFormat.OpenXml.Presentation;
+using DocumentFormat.OpenXml.Validation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -500,6 +501,31 @@ public LabelInfoPart? LabelInfoPart
get { return GetSubPartOfType(); }
}
+ internal override void VerifyMinimumDocument(ValidationContext validationContext)
+ {
+ if (this.DocumentType is not PresentationDocumentType.Slideshow &&
+ this.DocumentType is not PresentationDocumentType.MacroEnabledTemplate &&
+ this.DocumentType is not PresentationDocumentType.AddIn)
+ {
+ if (this.PresentationPart is not {
+ Presentation.NotesSize:
+ {
+ Cx: { HasValue: true },
+ Cy: { HasValue: true },
+ }
+ })
+ {
+ validationContext.AddError(new()
+ {
+ ErrorType = ValidationErrorType.Schema,
+ Id = "Sch_IncompletePackage",
+ Part = this.PresentationPart,
+ Description = SR.Format(ValidationResources.Sch_IncompletePackage, "PowerPoint"),
+ });
+ }
+ }
+ }
+
///
public override IFeatureCollection Features => _features ??= new PresentationDocumentFeatures(this);
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index f1acfa35a..aa5fa4fbd 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -4,6 +4,7 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
using DocumentFormat.OpenXml.Spreadsheet;
+using DocumentFormat.OpenXml.Validation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -500,6 +501,26 @@ public LabelInfoPart? LabelInfoPart
get { return GetSubPartOfType(); }
}
+ internal override void VerifyMinimumDocument(ValidationContext validationContext)
+ {
+ if (this.DocumentType != SpreadsheetDocumentType.AddIn)
+ {
+ Sheet? sheet = this.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
+ SheetData? sheetData = this.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
+
+ if (sheet is not null && sheetData is not null)
+ {
+ validationContext.AddError(new()
+ {
+ ErrorType = ValidationErrorType.Schema,
+ Id = "Sch_IncompletePackage",
+ Part = this.WorkbookPart,
+ Description = SR.Format(ValidationResources.Sch_IncompletePackage, "Excel"),
+ });
+ }
+ }
+ }
+
///
public override IFeatureCollection Features => _features ??= new SpreadsheetDocumentFeatures(this);
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index f26b8c2b8..435ba9702 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -548,25 +548,20 @@ public LabelInfoPart? LabelInfoPart
internal override void VerifyMinimumDocument(ValidationContext validationContext)
{
- if (this.MainDocumentPart?.Document is null)
- {
- validationContext.AddError(new()
+ if (this.MainDocumentPart is not
{
- ErrorType = ValidationErrorType.Schema,
- Id = "Sch_MissingRootElement",
- Part = this.MainDocumentPart,
- Description = SR.Format(ValidationResources.Sch_MissingRootElement, typeof(MainDocumentPart), typeof(Document)),
- });
- }
-
- if (this.MainDocumentPart?.Document is not null && this.MainDocumentPart.Document?.Body is null)
+ Document:
+ {
+ Body: { }
+ }
+ })
{
validationContext.AddError(new()
{
ErrorType = ValidationErrorType.Schema,
- Id = "Sch_IncompleteContentExpectingComplex",
+ Id = "Sch_IncompletePackage",
Part = this.MainDocumentPart,
- Description = SR.Format(ValidationResources.Sch_IncompleteContentExpectingComplex, typeof(Document)),
+ Description = SR.Format(ValidationResources.Sch_IncompletePackage, "Word"),
});
}
}
From af392cc4bd3e35b27f466dc057ff12ead649bb6b Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 15 May 2025 14:00:03 -0700
Subject: [PATCH 20/33] add tests
---
.../Packaging/PresentationDocument.cs | 14 +-
.../Packaging/SpreadsheetDocument.cs | 2 +-
.../ofapiTest/OpenXmlValidatorTest.cs | 328 ++++++++++++++++++
3 files changed, 338 insertions(+), 6 deletions(-)
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index e79ddbfc7..b91fddeac 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -9,6 +9,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Packaging;
+using System.Linq;
using System.Reflection;
namespace DocumentFormat.OpenXml.Packaging
@@ -507,13 +508,16 @@ internal override void VerifyMinimumDocument(ValidationContext validationContext
this.DocumentType is not PresentationDocumentType.MacroEnabledTemplate &&
this.DocumentType is not PresentationDocumentType.AddIn)
{
- if (this.PresentationPart is not {
- Presentation.NotesSize:
+ if (this.PresentationPart is not
{
- Cx: { HasValue: true },
- Cy: { HasValue: true },
+ Presentation.NotesSize:
+ {
+ Cx: { HasValue: true },
+ Cy: { HasValue: true },
+ }
}
- })
+
+ || !(this.PresentationPart.SlideMasterParts?.Any() ?? false))
{
validationContext.AddError(new()
{
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index aa5fa4fbd..be5602de8 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -508,7 +508,7 @@ internal override void VerifyMinimumDocument(ValidationContext validationContext
Sheet? sheet = this.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
SheetData? sheetData = this.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
- if (sheet is not null && sheetData is not null)
+ if (sheet is null || sheetData is null)
{
validationContext.AddError(new()
{
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
index 4fdeb0daa..51a8fcb82 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
@@ -9,12 +9,14 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.IO.Packaging;
using System.Linq;
using System.Threading;
using System.Xml;
using Xunit;
using static DocumentFormat.OpenXml.Tests.TestAssets;
+using Path = System.IO.Path;
namespace DocumentFormat.OpenXml.Tests
{
@@ -3832,5 +3834,331 @@ public void VersionMismatchPartValidatingTest()
Assert.Throws(() => O14Validator.Validate(wordTestDocument.MainDocumentPart, TestContext.Current.CancellationToken));
}
}
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPath_NoErrors_Wordprocessing()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "valid.docx");
+
+ using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document(new Body());
+
+ OpenXmlValidator validator = new();
+
+ // Act
+ IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
+
+ // Assert
+ Assert.Empty(errors);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentPath_HasError_Wordprocessing()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "invalid.docx");
+
+ using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart().Document = new Document();
+ OpenXmlValidator validator = new();
+
+ // Act
+ IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
+
+ // Assert
+ Assert.Single(errors);
+ Assert.Equal(errors.FirstOrDefault()?.Description, "The provided package does not conform to the minimum requirements for Word to open.");
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPath_NoErrors_Spreadsheet()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "valid.xlsx");
+
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ OpenXmlValidator validator = new();
+
+ // Act
+ IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
+
+ // Assert
+ Assert.Empty(errors);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_InValidDocumentPath_HasError_Spreadsheet()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "invalid.xlsx");
+
+ using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.MacroEnabledWorkbook))
+ {
+ WorkbookPart wbp = document.AddWorkbookPart();
+ WorksheetPart wsp = wbp.AddNewPart();
+ wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ OpenXmlValidator validator = new();
+
+ // Act
+ IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
+
+ // Assert
+ Assert.Single(errors);
+ Assert.Equal("The provided package does not conform to the minimum requirements for Excel to open.", errors.FirstOrDefault()?.Description);
+ }
+ }
+
+ [Fact]
+ public void VerifyMinimumPackageTest_ValidDocumentPath_NoErrors_Presentation()
+ {
+ // Arrange
+ string path = string.Concat(Path.GetTempPath(), "valid.pptx");
+
+ using (PresentationDocument document = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
+ {
+ // create presentation part
+ PresentationPart presentationPart = document.AddPresentationPart();
+ presentationPart.Presentation = new DocumentFormat.OpenXml.Presentation.Presentation();
+ presentationPart.Presentation.AddChild(new DocumentFormat.OpenXml.Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ SlideMasterPart slideMasterPart = presentationPart.AddNewPart();
+ slideMasterPart.SlideMaster = new Presentation.SlideMaster(
+ new Presentation.CommonSlideData(
+ new Presentation.ShapeTree(
+ new DocumentFormat.OpenXml.Presentation.NonVisualGroupShapeProperties(
+ new DocumentFormat.OpenXml.Presentation.NonVisualDrawingProperties() { Id = (UInt32Value)1U, Name = string.Empty },
+ new DocumentFormat.OpenXml.Presentation.NonVisualGroupShapeDrawingProperties(),
+ new DocumentFormat.OpenXml.Presentation.ApplicationNonVisualDrawingProperties()),
+ new DocumentFormat.OpenXml.Presentation.GroupShapeProperties())),
+ new DocumentFormat.OpenXml.Presentation.ColorMap()
+ {
+ Background1 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Light1,
+ Background2 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Light2,
+ Text1 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Dark1,
+ Text2 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Dark2,
+ Accent1 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent1,
+ Accent2 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent2,
+ Accent3 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent3,
+ Accent4 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent4,
+ Accent5 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent5,
+ Accent6 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent6,
+ Hyperlink = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Hyperlink,
+ FollowedHyperlink = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.FollowedHyperlink,
+ });
+ OpenXmlValidator validator = new();
+
+ // Act
+ IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
+
+ // Assert
+ Assert.Empty(errors);
+ }
+ }
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_ValidDocumentStream_NoErrors_Presentation()
+ //{
+ // // Arrange
+ // using (Stream stream = new MemoryStream())
+ // {
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ // }
+
+ // // Act
+ // Exception exception = Record.Exception(() =>
+ // {
+ // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // // Assert
+ // Assert.Null(exception);
+ // }
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_ValidDocumentPackage_NoErrors_Presentation()
+ //{
+ // // Arrange
+ // string path = Path.Combine(Path.GetTempPath(), "valid.xlsx");
+
+ // using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Template))
+ // {
+ // WorkbookPart wbp = document.AddWorkbookPart();
+ // WorksheetPart wsp = wbp.AddNewPart();
+ // wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
+ // wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
+ // }
+
+ // // Act
+ // using (Package package = Package.Open(path))
+ // {
+ // Exception exception = Record.Exception(() =>
+ // {
+ // using SpreadsheetDocument ssd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // // Assert
+ // Assert.Null(exception);
+ // }
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_InValidDocumentPath_HasError_Presentation()
+ //{
+ // // Arrange
+ // string path = string.Concat(Path.GetTempPath(), "invalid.pptx");
+
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = -2, Cy = 913607 });
+ // }
+
+ // // Act and Assert
+ // FileFormatException ex = Assert.Throws(() =>
+ // {
+ // using PresentationDocument ssd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // Assert.Equal(minPackageExMsg, ex.Message);
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_InValidDocumentStream_HasError_Presentation()
+ //{
+ // // Arrange
+ // using (Stream stream = new MemoryStream())
+ // {
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // }
+
+ // // Act and Assert
+ // FileFormatException ex = Assert.Throws(() =>
+ // {
+ // using PresentationDocument ssd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // Assert.Equal(minPackageExMsg, ex.Message);
+ // }
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_InValidDocumentPackage_HasError_Presentation()
+ //{
+ // // Arrange
+ // using (Stream stream = new MemoryStream())
+ // {
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // }
+
+ // // Act and Assert
+ // using (Package package = Package.Open(stream))
+ // {
+ // FileFormatException ex = Assert.Throws(() =>
+ // {
+ // using PresentationDocument pd = PresentationDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // Assert.Equal(minPackageExMsg, ex.Message);
+ // }
+ // }
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_AddInDocumentPackage_HasError_Presentation()
+ //{
+ // // Arrange
+ // using (Stream stream = new MemoryStream())
+ // {
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.AddIn))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ // }
+
+ // // Act and Assert
+ // NotSupportedException ex = Assert.Throws(() =>
+ // {
+ // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // Assert.Equal("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.", ex.Message);
+ // }
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_MacroEnabledSlideshowDocumentPackage_HasError_Presentation()
+ //{
+ // // Arrange
+ // using (Stream stream = new MemoryStream())
+ // {
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.MacroEnabledSlideshow))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ // }
+
+ // // Act and Assert
+ // NotSupportedException ex = Assert.Throws(() =>
+ // {
+ // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // Assert.Equal("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.", ex.Message);
+ // }
+ //}
+
+ //[Fact]
+ //public void VerifyMinimumPackageTest_SlideshowDocumentPackage_HasError_Presentation()
+ //{
+ // // Arrange
+ // using (Stream stream = new MemoryStream())
+ // {
+ // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Slideshow))
+ // {
+ // // create presentation part
+ // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
+ // presentationPart.Presentation = new Presentation.Presentation();
+ // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
+ // }
+
+ // // Act and Assert
+ // NotSupportedException ex = Assert.Throws(() =>
+ // {
+ // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
+ // });
+
+ // Assert.Equal("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.", ex.Message);
+ // }
+ //}
}
}
From 7d4c2b400bc8bdb831171857f37a752ea4432808 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 15 May 2025 14:16:44 -0700
Subject: [PATCH 21/33] revert to match main
---
.../OpenXmlPackageBuilderExtensions.cs | 10 +-
.../Packaging/OpenSettings.cs | 12 -
.../Packaging/OpenXmlPackage.cs | 6 -
.../PublicAPI/PublicAPI.Shipped.txt | 2 -
.../Validation/DocumentValidator.cs | 2 -
.../ValidationResources.Designer.cs | 9 -
.../Validation/ValidationResources.resx | 3 -
.../Validation/ValidationSettings.cs | 7 -
.../Packaging/PresentationDocument.cs | 68 +--
.../Packaging/SpreadsheetDocument.cs | 43 +-
.../TypedPackageFeatureCollection.cs | 8 +-
.../Packaging/WordprocessingDocument.cs | 37 +-
.../ofapiTest/OpenXmlPackageTest.cs | 537 +-----------------
.../ofapiTest/OpenXmlValidatorTest.cs | 328 -----------
14 files changed, 6 insertions(+), 1066 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs b/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs
index b546129e5..51020763d 100644
--- a/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Builder/OpenXmlPackageBuilderExtensions.cs
@@ -110,15 +110,7 @@ public static IPackageBuilder Use(this IPackageBuilder UseSettings(this IPackageBuilder builder, OpenSettings settings)
where TPackage : OpenXmlPackage
- => builder.Use(package =>
- {
- package.OpenSettings = settings;
-
- if (settings.VerifyMinimumPackage && package.Features.Get() is { } minimumFeature && !minimumFeature.Validate())
- {
- throw new FileFormatException("The provided package does not conform to the minimum requirements to open.");
- }
- });
+ => builder.Use(package => package.OpenSettings = settings);
internal static IPackageBuilder UseDefaultBehaviorAndLockBuilder(this IPackageBuilder builder)
where TPackage : OpenXmlPackage
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
index a9e147c7b..8e637b432 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenSettings.cs
@@ -74,17 +74,5 @@ public MarkupCompatibilityProcessSettings MarkupCompatibilityProcessSettings
/// This property allows you to mitigate denial of service attacks where the attacker submits a package with an extremely large Open XML part. By limiting the size of the part, you can detect the attack and recover reliably.
///
public long MaxCharactersInPart { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to validate that the document meets the minimum requirements for a valid package.
- ///
- ///
- /// true if the document should be validated for minimum requirements; otherwise, false.
- ///
- ///
- /// When set to true, the document will be checked to ensure it contains the necessary parts and structure
- /// to be considered a valid Open XML package.
- ///
- public bool VerifyMinimumPackage { get; set; }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
index 34ef6fa9c..c8ce6524a 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPackage.cs
@@ -3,7 +3,6 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Features;
-using DocumentFormat.OpenXml.Validation;
using System;
using System.Collections.Generic;
using System.IO;
@@ -613,10 +612,5 @@ public void Save()
///
public override IFeatureCollection Features => _features ??= new PackageFeatureCollection(this);
-
- internal virtual void VerifyMinimumDocument(ValidationContext validationContext)
- {
- return;
- }
}
}
diff --git a/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt b/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
index f1c172980..c7d7da820 100644
--- a/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
+++ b/src/DocumentFormat.OpenXml.Framework/PublicAPI/PublicAPI.Shipped.txt
@@ -1009,5 +1009,3 @@ DocumentFormat.OpenXml.OpenXmlPartWriterSettings.Encoding.set -> void
DocumentFormat.OpenXml.OpenXmlPartWriterSettings.OpenXmlPartWriterSettings() -> void
DocumentFormat.OpenXml.OpenXmlPartWriter.OpenXmlPartWriter(DocumentFormat.OpenXml.Packaging.OpenXmlPart! openXmlPart, DocumentFormat.OpenXml.OpenXmlPartWriterSettings! settings) -> void
DocumentFormat.OpenXml.OpenXmlPartWriter.OpenXmlPartWriter(System.IO.Stream! partStream, DocumentFormat.OpenXml.OpenXmlPartWriterSettings! settings) -> void
-DocumentFormat.OpenXml.Packaging.OpenSettings.VerifyMinimumPackage.get -> bool
-DocumentFormat.OpenXml.Packaging.OpenSettings.VerifyMinimumPackage.set -> void
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index 14f09f3f3..df3ea4e46 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -50,8 +50,6 @@ public List Validate(OpenXmlPackage document, ValidationSet
// integrate the package validation.
ValidatePackageStructure(document, context);
- document.VerifyMinimumDocument(context);
-
foreach (var part in PartsToBeValidated(document))
{
// traverse from the part root element (by DOM or by Reader) in post-order
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
index 2a19d601b..0f07d0a74 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
@@ -348,15 +348,6 @@ internal static string Sch_IncompleteContentExpectingComplex {
}
}
- ///
- /// Looks up a localized string similar to The provided package does not conform to the minimum requirements for {0} to open..
- ///
- internal static string Sch_IncompletePackage {
- get {
- return ResourceManager.GetString("Sch_IncompletePackage", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to The element '{0}' is a leaf element and cannot contain children..
///
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
index 0ab938c17..ed8489361 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
@@ -342,7 +342,4 @@
Cell contents have invalid value '{0}' for type '{1}'.
-
- The provided package does not conform to the minimum requirements for {0} to open.
-
\ No newline at end of file
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs
index 96939a7cd..e4423c5f1 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationSettings.cs
@@ -32,12 +32,5 @@ public ValidationSettings(FileFormatVersions fileFormat)
/// Default is 1000. A zero (0) value means no limitation.
///
public int MaxNumberOfErrors { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the validator should verify that the package meets the minimum requirements
- /// for the specified file format. When set to true, the validation process will include checks to ensure
- /// the document structure is sufficient for the target application to open the file.
- ///
- public bool VerifyMinimumPackage { get; set; }
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
index b91fddeac..fe7995fa2 100644
--- a/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
@@ -3,13 +3,10 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
-using DocumentFormat.OpenXml.Presentation;
-using DocumentFormat.OpenXml.Validation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Packaging;
-using System.Linq;
using System.Reflection;
namespace DocumentFormat.OpenXml.Packaging
@@ -502,34 +499,6 @@ public LabelInfoPart? LabelInfoPart
get { return GetSubPartOfType(); }
}
- internal override void VerifyMinimumDocument(ValidationContext validationContext)
- {
- if (this.DocumentType is not PresentationDocumentType.Slideshow &&
- this.DocumentType is not PresentationDocumentType.MacroEnabledTemplate &&
- this.DocumentType is not PresentationDocumentType.AddIn)
- {
- if (this.PresentationPart is not
- {
- Presentation.NotesSize:
- {
- Cx: { HasValue: true },
- Cy: { HasValue: true },
- }
- }
-
- || !(this.PresentationPart.SlideMasterParts?.Any() ?? false))
- {
- validationContext.AddError(new()
- {
- ErrorType = ValidationErrorType.Schema,
- Id = "Sch_IncompletePackage",
- Part = this.PresentationPart,
- Description = SR.Format(ValidationResources.Sch_IncompletePackage, "PowerPoint"),
- });
- }
- }
- }
-
///
public override IFeatureCollection Features => _features ??= new PresentationDocumentFeatures(this);
@@ -537,8 +506,7 @@ this.DocumentType is not PresentationDocumentType.MacroEnabledTemplate &&
private partial class PresentationDocumentFeatures : TypedPackageFeatureCollection,
IApplicationTypeFeature,
IMainPartFeature,
- IProgrammaticIdentifierFeature,
- IMinimumDocumentFeature
+ IProgrammaticIdentifierFeature
{
public PresentationDocumentFeatures(OpenXmlPackage package)
: base(package)
@@ -576,40 +544,6 @@ public PresentationDocumentFeatures(OpenXmlPackage package)
"application/vnd.ms-powerpoint.addin.macroEnabled.main+xml" => PresentationDocumentType.AddIn,
_ => default,
};
-
- bool IMinimumDocumentFeature.Validate()
- {
- if (this.DocumentType == PresentationDocumentType.Slideshow)
- {
- throw new NotSupportedException("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.");
- }
-
- if (this.DocumentType == PresentationDocumentType.MacroEnabledSlideshow)
- {
- throw new NotSupportedException("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.");
- }
-
- if (this.DocumentType == PresentationDocumentType.AddIn)
- {
- throw new NotSupportedException("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.");
- }
-
- return HasValidNotes();
- }
-
- private bool HasValidNotes()
- {
- const long MaxNoteSize = 27273042316900;
-
- return MainPart is
- {
- Presentation.NotesSize:
- {
- Cy: { HasValue: true, Value: >= 0 and <= MaxNoteSize },
- Cx: { HasValue: true, Value: >= 0 and <= MaxNoteSize },
- }
- };
- }
}
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
index be5602de8..f950048c1 100644
--- a/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
@@ -3,8 +3,6 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
-using DocumentFormat.OpenXml.Spreadsheet;
-using DocumentFormat.OpenXml.Validation;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -501,34 +499,13 @@ public LabelInfoPart? LabelInfoPart
get { return GetSubPartOfType(); }
}
- internal override void VerifyMinimumDocument(ValidationContext validationContext)
- {
- if (this.DocumentType != SpreadsheetDocumentType.AddIn)
- {
- Sheet? sheet = this.WorkbookPart?.Workbook?.Sheets?.GetFirstChild();
- SheetData? sheetData = this.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
-
- if (sheet is null || sheetData is null)
- {
- validationContext.AddError(new()
- {
- ErrorType = ValidationErrorType.Schema,
- Id = "Sch_IncompletePackage",
- Part = this.WorkbookPart,
- Description = SR.Format(ValidationResources.Sch_IncompletePackage, "Excel"),
- });
- }
- }
- }
-
///
public override IFeatureCollection Features => _features ??= new SpreadsheetDocumentFeatures(this);
[DocumentFormat.OpenXml.Generator.OpenXmlPackage("SpreadsheetDocument")]
private partial class SpreadsheetDocumentFeatures : TypedPackageFeatureCollection,
IApplicationTypeFeature,
- IMainPartFeature,
- IMinimumDocumentFeature
+ IMainPartFeature
{
public SpreadsheetDocumentFeatures(OpenXmlPackage package)
: base(package)
@@ -560,24 +537,6 @@ public SpreadsheetDocumentFeatures(OpenXmlPackage package)
"application/vnd.ms-excel.addin.macroEnabled.main+xml" => SpreadsheetDocumentType.AddIn,
_ => default,
};
-
- bool IMinimumDocumentFeature.Validate()
- {
- if (DocumentType == SpreadsheetDocumentType.AddIn)
- {
- throw new NotSupportedException("Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.");
- }
-
- Sheet? sheet = MainPart?.Workbook?.Sheets?.GetFirstChild();
- SheetData? sheetData = MainPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild();
-
- if (sheet is not null && sheetData is not null)
- {
- return true;
- }
-
- return false;
- }
}
}
}
diff --git a/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs b/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs
index 80fe18f32..768fc5f45 100644
--- a/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/TypedPackageFeatureCollection.cs
@@ -62,18 +62,12 @@ private TDocumentType EnsureDocumentType()
return _documentType.Value;
}
- protected TDocumentType DocumentType
+ TDocumentType IDocumentTypeFeature.Current
{
get => EnsureDocumentType();
set => _documentType = value;
}
- TDocumentType IDocumentTypeFeature.Current
- {
- get => DocumentType;
- set => DocumentType = value;
- }
-
protected TMainPart? MainPart => Package.GetSubPartOfType();
OpenXmlPart? IMainPartFeature.Part => MainPart;
diff --git a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
index 435ba9702..b3bd6384d 100644
--- a/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
+++ b/src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
@@ -3,11 +3,8 @@
using DocumentFormat.OpenXml.Builder;
using DocumentFormat.OpenXml.Features;
-using DocumentFormat.OpenXml.Validation;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
-using System.Collections;
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Packaging;
@@ -546,26 +543,6 @@ public LabelInfoPart? LabelInfoPart
get { return GetSubPartOfType(); }
}
- internal override void VerifyMinimumDocument(ValidationContext validationContext)
- {
- if (this.MainDocumentPart is not
- {
- Document:
- {
- Body: { }
- }
- })
- {
- validationContext.AddError(new()
- {
- ErrorType = ValidationErrorType.Schema,
- Id = "Sch_IncompletePackage",
- Part = this.MainDocumentPart,
- Description = SR.Format(ValidationResources.Sch_IncompletePackage, "Word"),
- });
- }
- }
-
///
public override IFeatureCollection Features => _features ??= new WordprocessingDocumentFeatures(this);
@@ -573,8 +550,7 @@ internal override void VerifyMinimumDocument(ValidationContext validationContext
private partial class WordprocessingDocumentFeatures : TypedPackageFeatureCollection,
IApplicationTypeFeature,
IMainPartFeature,
- IProgrammaticIdentifierFeature,
- IMinimumDocumentFeature
+ IProgrammaticIdentifierFeature
{
public WordprocessingDocumentFeatures(OpenXmlPackage package)
: base(package)
@@ -606,17 +582,6 @@ public WordprocessingDocumentFeatures(OpenXmlPackage package)
"application/vnd.ms-word.template.macroEnabledTemplate.main+xml" => WordprocessingDocumentType.MacroEnabledTemplate,
_ => default,
};
-
- bool IMinimumDocumentFeature.Validate()
- {
- return MainPart is
- {
- Document:
- {
- Body: { }
- }
- };
- }
}
}
}
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
index 458b36ad9..cbd1eeca6 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlPackageTest.cs
@@ -3,10 +3,8 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
-using Microsoft.Testing.Platform.MSBuild;
using System;
using System.IO;
-using System.IO.Packaging;
using System.Linq;
using Xunit;
@@ -23,14 +21,8 @@
namespace DocumentFormat.OpenXml.Tests
{
///
- /// Contains unit tests for validating the behavior of Open XML packages, including WordprocessingDocument,
- /// PresentationDocument, and SpreadsheetDocument. These tests cover scenarios such as auto-save functionality,
- /// document type changes, part relationships, and minimum package verification.
+ /// Summary description for OpenXmlPackageTest
///
- ///
- /// This class uses the xUnit framework for testing and includes tests for various Open XML document types.
- /// It ensures the correctness of document operations, relationships, and compliance with Open XML standards.
- ///
public class OpenXmlPackageTest
{
[Fact]
@@ -884,532 +876,5 @@ public void O15FileOpenTest()
Assert.NotNull(webExtensionPart);
}
}
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Wordprocessing()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "valid.docx");
-
- using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document(new Body());
- }
-
- // Act
- Exception exception = Record.Exception(() =>
- {
- using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Wordprocessing()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document(new Body());
- }
-
- // Act
- Exception exception = Record.Exception(() =>
- {
- using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Wordprocessing()
- {
- // Arrange
- string path = Path.Combine(Path.GetTempPath(), "valid.docx");
-
- using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document(new Body());
- }
-
- // Act
- using (Package package = Package.Open(path))
- {
- Exception exception = Record.Exception(() =>
- {
- using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
- }
-
- private readonly string minPackageExMsg = "The provided package does not conform to the minimum requirements to open.";
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Wordprocessing()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "invalid.docx");
-
- using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document();
- }
-
- // Act and Assert
- FileFormatException ex = Assert.Throws(() =>
- {
- using WordprocessingDocument wpd = WordprocessingDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Wordprocessing()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document();
- }
-
- // Act and Assert
- FileFormatException ex = Assert.Throws(() =>
- {
- using WordprocessingDocument wpd = WordprocessingDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Wordprocessing()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document();
- }
-
- // Act and Assert
- using (Package package = Package.Open(stream))
- {
- FileFormatException ex = Assert.Throws(() =>
- {
- using WordprocessingDocument wpd = WordprocessingDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Spreadsheet()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "valid.xlsx");
-
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act
- Exception exception = Record.Exception(() =>
- {
- using SpreadsheetDocument ssd = SpreadsheetDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Spreadsheet()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.MacroEnabledTemplate))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act
- Exception exception = Record.Exception(() =>
- {
- using SpreadsheetDocument ssd = SpreadsheetDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Spreadsheet()
- {
- // Arrange
- string path = Path.Combine(Path.GetTempPath(), "valid.xlsx");
-
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Template))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act
- using (Package package = Package.Open(path))
- {
- Exception exception = Record.Exception(() =>
- {
- using SpreadsheetDocument ssd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Spreadsheet()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "invalid.xlsx");
-
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.MacroEnabledWorkbook))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- }
-
- // Act and Assert
- FileFormatException ex = Assert.Throws(() =>
- {
- using SpreadsheetDocument ssd = SpreadsheetDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Spreadsheet()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act and Assert
- FileFormatException ex = Assert.Throws(() =>
- {
- using SpreadsheetDocument ssd = SpreadsheetDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Spreadsheet()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- }
-
- // Act and Assert
- using (Package package = Package.Open(stream))
- {
- FileFormatException ex = Assert.Throws(() =>
- {
- using SpreadsheetDocument wpd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_AddInDocumentPackage_Throws_Spreadsheet()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.AddIn))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act and Assert
- using (Package package = Package.Open(stream))
- {
- NotSupportedException ex = Assert.Throws(() =>
- {
- using SpreadsheetDocument wpd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal("Validation for SpreadsheetDocument.AddIn (.xlam) is not supported.", ex.Message);
- }
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPath_DoesNotThrow_Presentation()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "valid.pptx");
-
- using (PresentationDocument presentationDocument = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act
- Exception exception = Record.Exception(() =>
- {
- using PresentationDocument pd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentStream_DoesNotThrow_Presentation()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act
- Exception exception = Record.Exception(() =>
- {
- using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPackage_DoesNotThrow_Presentation()
- {
- // Arrange
- string path = Path.Combine(Path.GetTempPath(), "valid.xlsx");
-
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Template))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- }
-
- // Act
- using (Package package = Package.Open(path))
- {
- Exception exception = Record.Exception(() =>
- {
- using SpreadsheetDocument ssd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- // Assert
- Assert.Null(exception);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPath_Throws_Presentation()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "invalid.pptx");
-
- using (PresentationDocument presentationDocument = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = -2, Cy = 913607 });
- }
-
- // Act and Assert
- FileFormatException ex = Assert.Throws(() =>
- {
- using PresentationDocument ssd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentStream_Throws_Presentation()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- }
-
- // Act and Assert
- FileFormatException ex = Assert.Throws(() =>
- {
- using PresentationDocument ssd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPackage_Throws_Presentation()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- }
-
- // Act and Assert
- using (Package package = Package.Open(stream))
- {
- FileFormatException ex = Assert.Throws(() =>
- {
- using PresentationDocument pd = PresentationDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal(minPackageExMsg, ex.Message);
- }
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_AddInDocumentPackage_Throws_Presentation()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.AddIn))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act and Assert
- NotSupportedException ex = Assert.Throws(() =>
- {
- using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.", ex.Message);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_MacroEnabledSlideshowDocumentPackage_Throws_Presentation()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.MacroEnabledSlideshow))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act and Assert
- NotSupportedException ex = Assert.Throws(() =>
- {
- using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.", ex.Message);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_SlideshowDocumentPackage_Throws_Presentation()
- {
- // Arrange
- using (Stream stream = new MemoryStream())
- {
- using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Slideshow))
- {
- // create presentation part
- PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- presentationPart.Presentation = new Presentation.Presentation();
- presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- }
-
- // Act and Assert
- NotSupportedException ex = Assert.Throws(() =>
- {
- using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- });
-
- Assert.Equal("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.", ex.Message);
- }
- }
}
}
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
index 51a8fcb82..4fdeb0daa 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
@@ -9,14 +9,12 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.IO.Packaging;
using System.Linq;
using System.Threading;
using System.Xml;
using Xunit;
using static DocumentFormat.OpenXml.Tests.TestAssets;
-using Path = System.IO.Path;
namespace DocumentFormat.OpenXml.Tests
{
@@ -3834,331 +3832,5 @@ public void VersionMismatchPartValidatingTest()
Assert.Throws(() => O14Validator.Validate(wordTestDocument.MainDocumentPart, TestContext.Current.CancellationToken));
}
}
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPath_NoErrors_Wordprocessing()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "valid.docx");
-
- using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document(new Body());
-
- OpenXmlValidator validator = new();
-
- // Act
- IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
-
- // Assert
- Assert.Empty(errors);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPath_HasError_Wordprocessing()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "invalid.docx");
-
- using (WordprocessingDocument document = WordprocessingDocument.Create(path, WordprocessingDocumentType.Document))
- {
- document.AddMainDocumentPart().Document = new Document();
- OpenXmlValidator validator = new();
-
- // Act
- IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
-
- // Assert
- Assert.Single(errors);
- Assert.Equal(errors.FirstOrDefault()?.Description, "The provided package does not conform to the minimum requirements for Word to open.");
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPath_NoErrors_Spreadsheet()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "valid.xlsx");
-
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- OpenXmlValidator validator = new();
-
- // Act
- IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
-
- // Assert
- Assert.Empty(errors);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_InValidDocumentPath_HasError_Spreadsheet()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "invalid.xlsx");
-
- using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.MacroEnabledWorkbook))
- {
- WorkbookPart wbp = document.AddWorkbookPart();
- WorksheetPart wsp = wbp.AddNewPart();
- wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- OpenXmlValidator validator = new();
-
- // Act
- IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
-
- // Assert
- Assert.Single(errors);
- Assert.Equal("The provided package does not conform to the minimum requirements for Excel to open.", errors.FirstOrDefault()?.Description);
- }
- }
-
- [Fact]
- public void VerifyMinimumPackageTest_ValidDocumentPath_NoErrors_Presentation()
- {
- // Arrange
- string path = string.Concat(Path.GetTempPath(), "valid.pptx");
-
- using (PresentationDocument document = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
- {
- // create presentation part
- PresentationPart presentationPart = document.AddPresentationPart();
- presentationPart.Presentation = new DocumentFormat.OpenXml.Presentation.Presentation();
- presentationPart.Presentation.AddChild(new DocumentFormat.OpenXml.Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- SlideMasterPart slideMasterPart = presentationPart.AddNewPart();
- slideMasterPart.SlideMaster = new Presentation.SlideMaster(
- new Presentation.CommonSlideData(
- new Presentation.ShapeTree(
- new DocumentFormat.OpenXml.Presentation.NonVisualGroupShapeProperties(
- new DocumentFormat.OpenXml.Presentation.NonVisualDrawingProperties() { Id = (UInt32Value)1U, Name = string.Empty },
- new DocumentFormat.OpenXml.Presentation.NonVisualGroupShapeDrawingProperties(),
- new DocumentFormat.OpenXml.Presentation.ApplicationNonVisualDrawingProperties()),
- new DocumentFormat.OpenXml.Presentation.GroupShapeProperties())),
- new DocumentFormat.OpenXml.Presentation.ColorMap()
- {
- Background1 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Light1,
- Background2 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Light2,
- Text1 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Dark1,
- Text2 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Dark2,
- Accent1 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent1,
- Accent2 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent2,
- Accent3 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent3,
- Accent4 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent4,
- Accent5 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent5,
- Accent6 = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Accent6,
- Hyperlink = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.Hyperlink,
- FollowedHyperlink = DocumentFormat.OpenXml.Drawing.ColorSchemeIndexValues.FollowedHyperlink,
- });
- OpenXmlValidator validator = new();
-
- // Act
- IEnumerable errors = validator.Validate(document, TestContext.Current.CancellationToken);
-
- // Assert
- Assert.Empty(errors);
- }
- }
-
- //[Fact]
- //public void VerifyMinimumPackageTest_ValidDocumentStream_NoErrors_Presentation()
- //{
- // // Arrange
- // using (Stream stream = new MemoryStream())
- // {
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- // }
-
- // // Act
- // Exception exception = Record.Exception(() =>
- // {
- // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // // Assert
- // Assert.Null(exception);
- // }
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_ValidDocumentPackage_NoErrors_Presentation()
- //{
- // // Arrange
- // string path = Path.Combine(Path.GetTempPath(), "valid.xlsx");
-
- // using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Template))
- // {
- // WorkbookPart wbp = document.AddWorkbookPart();
- // WorksheetPart wsp = wbp.AddNewPart();
- // wbp.Workbook = new Spreadsheet.Workbook(new Spreadsheet.Sheets(new Spreadsheet.Sheet() { Id = wbp.GetIdOfPart(wsp), SheetId = 1, Name = "Sheet1" }));
- // wsp.Worksheet = new Spreadsheet.Worksheet(new Spreadsheet.SheetData());
- // }
-
- // // Act
- // using (Package package = Package.Open(path))
- // {
- // Exception exception = Record.Exception(() =>
- // {
- // using SpreadsheetDocument ssd = SpreadsheetDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // // Assert
- // Assert.Null(exception);
- // }
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_InValidDocumentPath_HasError_Presentation()
- //{
- // // Arrange
- // string path = string.Concat(Path.GetTempPath(), "invalid.pptx");
-
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(path, PresentationDocumentType.Presentation))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = -2, Cy = 913607 });
- // }
-
- // // Act and Assert
- // FileFormatException ex = Assert.Throws(() =>
- // {
- // using PresentationDocument ssd = PresentationDocument.Open(path, false, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // Assert.Equal(minPackageExMsg, ex.Message);
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_InValidDocumentStream_HasError_Presentation()
- //{
- // // Arrange
- // using (Stream stream = new MemoryStream())
- // {
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // }
-
- // // Act and Assert
- // FileFormatException ex = Assert.Throws(() =>
- // {
- // using PresentationDocument ssd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // Assert.Equal(minPackageExMsg, ex.Message);
- // }
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_InValidDocumentPackage_HasError_Presentation()
- //{
- // // Arrange
- // using (Stream stream = new MemoryStream())
- // {
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Presentation))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // }
-
- // // Act and Assert
- // using (Package package = Package.Open(stream))
- // {
- // FileFormatException ex = Assert.Throws(() =>
- // {
- // using PresentationDocument pd = PresentationDocument.Open(package, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // Assert.Equal(minPackageExMsg, ex.Message);
- // }
- // }
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_AddInDocumentPackage_HasError_Presentation()
- //{
- // // Arrange
- // using (Stream stream = new MemoryStream())
- // {
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.AddIn))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- // }
-
- // // Act and Assert
- // NotSupportedException ex = Assert.Throws(() =>
- // {
- // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // Assert.Equal("Minimum package verification for PresentationDocumentType.AddIn (.ppam) is not supported.", ex.Message);
- // }
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_MacroEnabledSlideshowDocumentPackage_HasError_Presentation()
- //{
- // // Arrange
- // using (Stream stream = new MemoryStream())
- // {
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.MacroEnabledSlideshow))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- // }
-
- // // Act and Assert
- // NotSupportedException ex = Assert.Throws(() =>
- // {
- // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // Assert.Equal("Minimum package verification for PresentationDocumentType.MacroEnabledSlideshow (.ppsm) is not supported.", ex.Message);
- // }
- //}
-
- //[Fact]
- //public void VerifyMinimumPackageTest_SlideshowDocumentPackage_HasError_Presentation()
- //{
- // // Arrange
- // using (Stream stream = new MemoryStream())
- // {
- // using (PresentationDocument presentationDocument = PresentationDocument.Create(stream, PresentationDocumentType.Slideshow))
- // {
- // // create presentation part
- // PresentationPart presentationPart = presentationDocument.AddPresentationPart();
- // presentationPart.Presentation = new Presentation.Presentation();
- // presentationPart.Presentation.AddChild(new Presentation.NotesSize() { Cx = 913607, Cy = 913607 });
- // }
-
- // // Act and Assert
- // NotSupportedException ex = Assert.Throws(() =>
- // {
- // using PresentationDocument pd = PresentationDocument.Open(stream, false, new OpenSettings() { VerifyMinimumPackage = true });
- // });
-
- // Assert.Equal("Minimum package verification for PresentationDocumentType.Slideshow (.ppsx) is not supported.", ex.Message);
- // }
- //}
}
}
From 8c4d993bb4addff418d41f424444f96526b76bc8 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 15 May 2025 16:42:42 -0700
Subject: [PATCH 22/33] add validation error if part is empty
---
.../Validation/DocumentValidator.cs | 13 +++++++++++++
.../Validation/ValidationResources.Designer.cs | 9 +++++++++
.../Validation/ValidationResources.resx | 3 +++
.../ofapiTest/OpenXmlValidatorTest.cs | 17 +++++++++++++++++
4 files changed, 42 insertions(+)
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index df3ea4e46..1a6647d8f 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -106,6 +106,19 @@ private void ValidatePart(OpenXmlPart part, ValidationContext context)
{
Validate(context);
}
+ else
+ {
+ context.AddError(new ValidationErrorInfo
+ {
+ ErrorType = ValidationErrorType.Schema,
+ Id = "Sch_PartRootElementMissing",
+ Part = part,
+ Description = SR.Format(ValidationResources.Sch_MissingPartRootElement, part.Uri),
+ });
+
+ // The part's root element is empty, so no more errors in this part. Release the DOM to GC memory
+ part.UnloadRootElement();
+ }
if (!partRootElementLoaded && context.Errors.Count == lastErrorCount)
{
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
index 0f07d0a74..2b484e862 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.Designer.cs
@@ -438,6 +438,15 @@ internal static string Sch_MinLengthConstraintFailed {
}
}
+ ///
+ /// Looks up a localized string similar to The '{0}' part is missing its root element..
+ ///
+ internal static string Sch_MissingPartRootElement {
+ get {
+ return ResourceManager.GetString("Sch_MissingPartRootElement", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The required attribute '{0}' is missing..
///
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
index ed8489361..836e53611 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/ValidationResources.resx
@@ -342,4 +342,7 @@
Cell contents have invalid value '{0}' for type '{1}'.
+
+ The '{0}' part is missing its root element.
+
\ No newline at end of file
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
index 4fdeb0daa..0a91d4bd3 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
@@ -3832,5 +3832,22 @@ public void VersionMismatchPartValidatingTest()
Assert.Throws(() => O14Validator.Validate(wordTestDocument.MainDocumentPart, TestContext.Current.CancellationToken));
}
}
+
+ [Fact]
+ public void EmptyPartRootElementValidatingTest()
+ {
+ using (Stream stream = new MemoryStream())
+ using (WordprocessingDocument document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document))
+ {
+ document.AddMainDocumentPart();
+
+ IEnumerable errors = O14Validator.Validate(document, TestContext.Current.CancellationToken);
+ ValidationErrorInfo info = errors.FirstOrDefault();
+
+ Assert.Single(errors);
+ Assert.Equal("Sch_PartRootElementMissing", info.Id);
+ Assert.Equal("The '/word/document.xml' part is missing its root element.", info.Description);
+ }
+ }
}
}
From 12699db4234672f4f21f625c656225aceb748acc Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Fri, 16 May 2025 16:03:46 -0700
Subject: [PATCH 23/33] update how to check for empty parts
---
.../Validation/DocumentValidator.cs | 6 ++++--
.../ofapiTest/OpenXmlValidatorTest.cs | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index 1a6647d8f..67df02935 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -3,6 +3,7 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation.Schema;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -106,12 +107,13 @@ private void ValidatePart(OpenXmlPart part, ValidationContext context)
{
Validate(context);
}
- else
+ else if (part.Uri.ToString().EndsWith(".xml", System.StringComparison.InvariantCultureIgnoreCase) &&
+ part.GetStream().Length == 0)
{
context.AddError(new ValidationErrorInfo
{
ErrorType = ValidationErrorType.Schema,
- Id = "Sch_PartRootElementMissing",
+ Id = "Sch_MissingPartRootElement",
Part = part,
Description = SR.Format(ValidationResources.Sch_MissingPartRootElement, part.Uri),
});
diff --git a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
index 0a91d4bd3..4ee2f6a20 100644
--- a/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
+++ b/test/DocumentFormat.OpenXml.Tests/ofapiTest/OpenXmlValidatorTest.cs
@@ -3845,8 +3845,9 @@ public void EmptyPartRootElementValidatingTest()
ValidationErrorInfo info = errors.FirstOrDefault();
Assert.Single(errors);
- Assert.Equal("Sch_PartRootElementMissing", info.Id);
+ Assert.Equal("Sch_MissingPartRootElement", info.Id);
Assert.Equal("The '/word/document.xml' part is missing its root element.", info.Description);
+ Assert.Equal(ValidationErrorType.Schema, info.ErrorType);
}
}
}
From 87728823433d2e745387917d7739682e5ba6b512 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Fri, 16 May 2025 16:04:05 -0700
Subject: [PATCH 24/33] fix tests
---
.../DocxTests01.cs | 26 ++++++++++++++++---
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs b/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs
index f69761522..2f54afdd0 100644
--- a/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs
+++ b/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using DocumentFormat.OpenXml.CustomProperties;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation;
using System;
@@ -156,12 +157,24 @@ public void W050_DeleteAdd_CoreExtendedProperties()
doc.DeletePart(corePart);
doc.DeletePart(appPart);
- doc.AddCoreFilePropertiesPart();
- doc.AddExtendedFilePropertiesPart();
- doc.AddCustomFilePropertiesPart();
+ var cfpp = doc.AddCoreFilePropertiesPart();
+
+ string xml = "hello";
+ byte[] corePropsByteArray = System.Text.Encoding.UTF8.GetBytes(xml);
+ using (var stream1 = new MemoryStream(corePropsByteArray))
+ {
+ cfpp.FeedData(stream1);
+ }
+
+ var efpp = doc.AddExtendedFilePropertiesPart();
+ efpp.Properties = new ExtendedProperties.Properties();
+
+ var cusfpp = doc.AddCustomFilePropertiesPart();
+ cusfpp.Properties = new CustomProperties.Properties();
+
doc.AddDigitalSignatureOriginPart();
- doc.AddExtendedPart("relType", "contentType/xml", ".xml");
+ doc.AddExtendedPart("relType", "contentType/xml", ".xml");
var tnPart = doc.AddThumbnailPart(ThumbnailPartType.Jpeg);
doc.DeletePart(tnPart);
tnPart = doc.AddThumbnailPart("image/jpg");
@@ -180,6 +193,7 @@ public void W049_AddNewPart_ToPackage()
using (var doc = WordprocessingDocument.Open(stream, true))
{
var wpcp = doc.AddNewPart("application/xml", "rid1232131");
+ wpcp.CustomUI = new Office.CustomUI.CustomUI();
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
var errs = v.Validate(doc, TestContext.Current.CancellationToken);
@@ -207,6 +221,7 @@ public void W047_AddNewPart_ToPackage()
using (var doc = WordprocessingDocument.Open(stream, true))
{
var wpcp = doc.AddNewPart("rid123123");
+ wpcp.CustomUI = new Office.CustomUI.CustomUI();
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
var errs = v.Validate(doc, TestContext.Current.CancellationToken);
@@ -239,6 +254,7 @@ public void W045_AddNewPart_ToPart()
using (var doc = WordprocessingDocument.Open(stream, true))
{
var wpcp = doc.MainDocumentPart.AddNewPart("application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", "rid1232131");
+ wpcp.Comments = new W.Comments();
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
var errs = v.Validate(doc, TestContext.Current.CancellationToken);
@@ -266,6 +282,7 @@ public void W043_AddNewPart()
using (var doc = WordprocessingDocument.Open(stream, true))
{
var wpcp = doc.MainDocumentPart.AddNewPart("rid123123");
+ wpcp.Comments = new W.Comments();
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
var errs = v.Validate(doc, TestContext.Current.CancellationToken);
@@ -280,6 +297,7 @@ public void W042_AddNewPart()
using (var doc = WordprocessingDocument.Open(stream, true))
{
var wpcp = doc.MainDocumentPart.AddNewPart();
+ wpcp.Comments = new W.Comments();
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
var errs = v.Validate(doc, TestContext.Current.CancellationToken);
From a1847d7028e52a85de46295f055b40327e95473f Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 12 Jun 2025 14:30:57 -0700
Subject: [PATCH 25/33] custom file property part test passes
---
.../PptxTests01.cs | 24 ++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs b/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
index fb888cde3..395e9edca 100644
--- a/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
+++ b/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
@@ -388,7 +388,8 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
{
var corePart = doc.CoreFilePropertiesPart;
var appPart = doc.ExtendedFilePropertiesPart;
- var custFilePropsPart = doc.CustomFilePropertiesPart;
+ CustomFilePropertiesPart custFilePropsPart = doc.CustomFilePropertiesPart;
+ //custFilePropsPart.
var thumbNailPart = doc.ThumbnailPart;
doc.DeletePart(corePart);
@@ -401,7 +402,23 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
doc.AddCoreFilePropertiesPart();
doc.AddExtendedFilePropertiesPart();
- doc.AddCustomFilePropertiesPart();
+ var custFPP = doc.AddCustomFilePropertiesPart();
+ var custFPPStream = custFPP.GetStream();
+ using (var writer = new System.Xml.XmlTextWriter(custFPPStream, System.Text.Encoding.UTF8))
+ {
+ //writer.WriteRaw("Mary2010-12-21T00:00:00Z");
+ writer.WriteRaw("""
+
+
+
+ foobar
+
+
+ """);
+ writer.Flush();
+ //custFPPStream.Seek(0, SeekOrigin.Begin);
+ }
+
doc.AddDigitalSignatureOriginPart();
doc.AddExtendedPart("relType", "contentType/xml", ".xml");
@@ -410,8 +427,9 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
tnPart = doc.AddThumbnailPart("image/jpg");
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
+ var w = v.Validate(doc, TestContext.Current.CancellationToken);
- Assert.Empty(v.Validate(doc, TestContext.Current.CancellationToken));
+ Assert.Empty(w);
}
}
From 92dcc742ff52cdfc49a1efa44acce787ff132b6e Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Thu, 12 Jun 2025 15:57:30 -0700
Subject: [PATCH 26/33] fix P002_Pptx_DeleteAdd_CoreExtendedProperties test
---
.../PptxTests01.cs | 85 +++++++++++++++++--
1 file changed, 77 insertions(+), 8 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs b/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
index 395e9edca..f90629260 100644
--- a/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
+++ b/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
@@ -388,8 +388,8 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
{
var corePart = doc.CoreFilePropertiesPart;
var appPart = doc.ExtendedFilePropertiesPart;
- CustomFilePropertiesPart custFilePropsPart = doc.CustomFilePropertiesPart;
- //custFilePropsPart.
+ var custFilePropsPart = doc.CustomFilePropertiesPart;
+
var thumbNailPart = doc.ThumbnailPart;
doc.DeletePart(corePart);
@@ -400,13 +400,84 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
doc.DeletePart(thumbNailPart);
}
- doc.AddCoreFilePropertiesPart();
- doc.AddExtendedFilePropertiesPart();
+ var coreFPP = doc.AddCoreFilePropertiesPart();
+ var coreFPPStream = coreFPP.GetStream();
+ using (var writer = new System.Xml.XmlTextWriter(coreFPPStream, System.Text.Encoding.UTF8))
+ {
+ writer.WriteRaw("""
+
+
+
+ Joey Daccord
+ Joey Daccord
+ 2
+ 2025-06-12T19:21:34Z
+ 2025-06-12T21:23:11Z
+
+ """);
+ writer.Flush();
+ }
+
+ var appFPP = doc.AddExtendedFilePropertiesPart();
+ var appFPPStream = appFPP.GetStream();
+ using (var writer = new System.Xml.XmlTextWriter(appFPPStream, System.Text.Encoding.UTF8))
+ {
+ writer.WriteRaw("""
+
+
+ 12
+ 0
+ Microsoft Office PowerPoint
+ Widescreen
+ 0
+ 1
+ 0
+ 0
+ 0
+ false
+
+
+
+ Fonts Used
+
+
+ 3
+
+
+ Theme
+
+
+ 1
+
+
+ Slide Titles
+
+
+ 1
+
+
+
+
+
+ Aptos
+ Aptos Display
+ Arial
+ Office Theme
+ PowerPoint Presentation
+
+
+
+ false
+ false
+ false
+ 16.0000
+
+ """);
+ }
var custFPP = doc.AddCustomFilePropertiesPart();
var custFPPStream = custFPP.GetStream();
using (var writer = new System.Xml.XmlTextWriter(custFPPStream, System.Text.Encoding.UTF8))
{
- //writer.WriteRaw("Mary2010-12-21T00:00:00Z");
writer.WriteRaw("""
@@ -416,7 +487,6 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
""");
writer.Flush();
- //custFPPStream.Seek(0, SeekOrigin.Begin);
}
doc.AddDigitalSignatureOriginPart();
@@ -427,9 +497,8 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
tnPart = doc.AddThumbnailPart("image/jpg");
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
- var w = v.Validate(doc, TestContext.Current.CancellationToken);
- Assert.Empty(w);
+ Assert.Empty(v.Validate(doc, TestContext.Current.CancellationToken));
}
}
From 1df4b0b56ca13f8f05743faf5f12279a036891c2 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Fri, 13 Jun 2025 10:55:24 -0700
Subject: [PATCH 27/33] fix X006_Xlsx_DeleteAdd_CoreExtendedProperties and
W051_AddNewPart_ToOpenXmlPackage tests
---
.../DocxTests01.cs | 4 +-
.../XlsxTests01.cs | 75 ++++++++++++++++++-
2 files changed, 74 insertions(+), 5 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs b/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs
index 2f54afdd0..48321869f 100644
--- a/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs
+++ b/test/DocumentFormat.OpenXml.Tests/DocxTests01.cs
@@ -137,8 +137,8 @@ public void W051_AddNewPart_ToOpenXmlPackage()
using (var stream = GetStream(TestFiles.Hyperlink, true))
using (var doc = WordprocessingDocument.Open(stream, true))
{
- var pkg = (OpenXmlPackage)doc;
- var wpcp = pkg.AddNewPart("application/xml", "rid1232131");
+ var footer = doc.MainDocumentPart.AddNewPart();
+ footer.Footer = new W.Footer();
var v = new OpenXmlValidator(FileFormatVersions.Office2013);
var errs = v.Validate(doc, TestContext.Current.CancellationToken);
diff --git a/test/DocumentFormat.OpenXml.Tests/XlsxTests01.cs b/test/DocumentFormat.OpenXml.Tests/XlsxTests01.cs
index 21f91112b..53c50fa1f 100644
--- a/test/DocumentFormat.OpenXml.Tests/XlsxTests01.cs
+++ b/test/DocumentFormat.OpenXml.Tests/XlsxTests01.cs
@@ -67,9 +67,78 @@ public void X006_Xlsx_DeleteAdd_CoreExtendedProperties()
var appPart = doc.ExtendedFilePropertiesPart;
doc.DeletePart(corePart);
doc.DeletePart(appPart);
- doc.AddCoreFilePropertiesPart();
- doc.AddExtendedFilePropertiesPart();
- doc.AddCustomFilePropertiesPart();
+ var cFPP = doc.AddCoreFilePropertiesPart();
+ var cFPPStream = cFPP.GetStream();
+
+ using (var writer = new System.Xml.XmlTextWriter(cFPPStream, System.Text.Encoding.UTF8))
+ {
+ writer.WriteRaw("""
+
+
+ Shohei Ohtani
+ Shohei Ohtani
+ 2015-06-05T18:17:20Z
+ 2025-06-13T17:11:50Z
+
+ """);
+
+ writer.Flush();
+ }
+
+ var eFPP = doc.AddExtendedFilePropertiesPart();
+ var eFPPStream = eFPP.GetStream();
+
+ using (var writer = new System.Xml.XmlTextWriter(eFPPStream, System.Text.Encoding.UTF8))
+ {
+ writer.WriteRaw("""
+
+
+ Microsoft Excel
+ 0
+ false
+
+
+
+ Worksheets
+
+
+ 1
+
+
+
+
+
+ Sheet1
+
+
+
+ false
+ false
+ false
+ 16.0300
+
+ """);
+
+ writer.Flush();
+ }
+
+ var custFPP = doc.AddCustomFilePropertiesPart();
+ var custFPPStream = custFPP.GetStream();
+
+ using (var writer = new System.Xml.XmlTextWriter(custFPPStream, System.Text.Encoding.UTF8))
+ {
+ writer.WriteRaw("""
+
+
+
+ tacocat
+
+
+ """);
+
+ writer.Flush();
+ }
+
doc.AddDigitalSignatureOriginPart();
doc.AddExtendedPart("relType", "contentType/xml", ".xml");
var tnPart = doc.AddThumbnailPart(ThumbnailPartType.Jpeg);
From d7ee41babbf5ec1be5c29662078f0a2c2b94f781 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Fri, 13 Jun 2025 11:09:18 -0700
Subject: [PATCH 28/33] add blank line
---
test/DocumentFormat.OpenXml.Tests/PptxTests01.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs b/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
index f90629260..3c516554c 100644
--- a/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
+++ b/test/DocumentFormat.OpenXml.Tests/PptxTests01.cs
@@ -474,6 +474,7 @@ public void P002_Pptx_DeleteAdd_CoreExtendedProperties()
""");
}
+
var custFPP = doc.AddCustomFilePropertiesPart();
var custFPPStream = custFPP.GetStream();
using (var writer = new System.Xml.XmlTextWriter(custFPPStream, System.Text.Encoding.UTF8))
From 2097f050535ba3fc2e4ba7a6bc6d138e58e0895a Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 16 Jun 2025 08:42:53 -0700
Subject: [PATCH 29/33] remove file from earlier version of API
---
.../Features/IMinimumDocumentFeature.cs | 9 ---------
1 file changed, 9 deletions(-)
delete mode 100644 src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs
diff --git a/src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs b/src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs
deleted file mode 100644
index 3f1b725cf..000000000
--- a/src/DocumentFormat.OpenXml.Framework/Features/IMinimumDocumentFeature.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace DocumentFormat.OpenXml.Features;
-
-internal interface IMinimumDocumentFeature
-{
- bool Validate();
-}
From 31aec5a8c78c16fb2afca2f7322363162a43adad Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 16 Jun 2025 15:03:01 -0700
Subject: [PATCH 30/33] add IsEmptyPart method to ensure stream is disposed
---
.../Packaging/OpenXmlPart.cs | 12 ++++++++++++
.../Packaging/OpenXmlPartContainer.cs | 5 +++++
.../Validation/DocumentValidator.cs | 3 +--
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
index 873e15107..bf9caa1c7 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
@@ -448,6 +448,18 @@ internal virtual bool IsInVersion(FileFormatVersions version)
return true;
}
+ internal override bool IsEmptyPart()
+ {
+ bool isEmptyPart;
+
+ using (Stream stream = GetStream())
+ {
+ isEmptyPart = stream.Length == 0;
+ }
+
+ return isEmptyPart;
+ }
+
#endregion
#region internal methods
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPartContainer.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPartContainer.cs
index 442154644..bd9d8e78c 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPartContainer.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPartContainer.cs
@@ -1657,6 +1657,11 @@ internal OpenXmlPart CreateOpenXmlPart(string relationshipType)
// find all reachable parts from the package root, the dictionary also used for cycle reference defense
internal abstract void FindAllReachableParts(IDictionary reachableParts);
+ internal virtual bool IsEmptyPart()
+ {
+ return false;
+ }
+
#endregion
// Checks if the target part is in the same OpenXmlPackage as this part.
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index 67df02935..7a68888fc 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -107,8 +107,7 @@ private void ValidatePart(OpenXmlPart part, ValidationContext context)
{
Validate(context);
}
- else if (part.Uri.ToString().EndsWith(".xml", System.StringComparison.InvariantCultureIgnoreCase) &&
- part.GetStream().Length == 0)
+ else if (part.Uri.ToString().EndsWith(".xml", System.StringComparison.InvariantCultureIgnoreCase) && part.IsEmptyPart())
{
context.AddError(new ValidationErrorInfo
{
From 2d5dd09e694762884990603b86a62aae900d1560 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 30 Jun 2025 10:25:16 -0700
Subject: [PATCH 31/33] Update
src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
Co-authored-by: Taylor Southwick
---
.../Packaging/OpenXmlPart.cs | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
index bf9caa1c7..9fbc1d04a 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
@@ -450,14 +450,10 @@ internal virtual bool IsInVersion(FileFormatVersions version)
internal override bool IsEmptyPart()
{
- bool isEmptyPart;
-
using (Stream stream = GetStream())
{
- isEmptyPart = stream.Length == 0;
+ return stream.Length == 0;
}
-
- return isEmptyPart;
}
#endregion
From 4861f13f507a6743e2e401949420a1e6ef34c034 Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 30 Jun 2025 12:42:32 -0700
Subject: [PATCH 32/33] move file extension check to IsEmptyPartMethod
---
.../Packaging/OpenXmlPart.cs | 5 +++++
.../Validation/DocumentValidator.cs | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
index 9fbc1d04a..7ae133f85 100644
--- a/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
@@ -450,6 +450,11 @@ internal virtual bool IsInVersion(FileFormatVersions version)
internal override bool IsEmptyPart()
{
+ if (!Uri.ToString().EndsWith(".xml", System.StringComparison.InvariantCultureIgnoreCase))
+ {
+ return false;
+ }
+
using (Stream stream = GetStream())
{
return stream.Length == 0;
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index 7a68888fc..1da84c31e 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -107,7 +107,7 @@ private void ValidatePart(OpenXmlPart part, ValidationContext context)
{
Validate(context);
}
- else if (part.Uri.ToString().EndsWith(".xml", System.StringComparison.InvariantCultureIgnoreCase) && part.IsEmptyPart())
+ else if (part.IsEmptyPart())
{
context.AddError(new ValidationErrorInfo
{
From 25e5c67b43557dbc8b7328b1b7b65d3a4142738d Mon Sep 17 00:00:00 2001
From: Michael Bowen <10384982+mikeebowen@users.noreply.github.com>
Date: Mon, 30 Jun 2025 14:02:19 -0700
Subject: [PATCH 33/33] removed unnecessary using
---
.../Validation/DocumentValidator.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
index 1da84c31e..c66225b5b 100644
--- a/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
+++ b/src/DocumentFormat.OpenXml.Framework/Validation/DocumentValidator.cs
@@ -3,7 +3,6 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Validation.Schema;
-using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;