Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
794484d
add IsValidChild method for WordprocessingDocument
mikeebowen Apr 10, 2025
9f65d67
add IsValidDocument method for SpreadsheetDocument
mikeebowen Apr 14, 2025
b67a9bf
Add IsMinimumDocument method for Presentation
mikeebowen Apr 23, 2025
6b9d2aa
update tests and xml comments
mikeebowen Apr 23, 2025
61192e4
Merge remote-tracking branch 'upstream/main' into issue-816
mikeebowen Apr 23, 2025
0d6de91
Merge remote-tracking branch 'upstream/main' into issue-816
mikeebowen Apr 28, 2025
e9cca0a
Merge branch 'main' into issue-816
mikeebowen Apr 29, 2025
84edcb0
Merge branch 'issue-816' of github.com:mikeebowen/Open-XML-SDK into i…
mikeebowen Apr 29, 2025
48afb54
only test null argument for < .Net 8+
mikeebowen Apr 29, 2025
f83d1d0
move Wordprocessing minimum document validation to virtual method
mikeebowen Apr 29, 2025
8dc68ef
Change minimum Word doc API to OpenSettings options
mikeebowen May 1, 2025
1a0d73a
add VerifyMinimumPackage option to PresentationDocument
mikeebowen May 5, 2025
8ba0e4d
fix linting issues
mikeebowen May 5, 2025
55e3eef
remove trailing whitespace
mikeebowen May 6, 2025
69a5900
remove unnecessary $ from string
mikeebowen May 6, 2025
9bc42e2
remove unnecessary usings
mikeebowen May 6, 2025
3a0c367
use feature for minimum document
twsouthwick May 12, 2025
fca841a
a bit more
twsouthwick May 12, 2025
b46015b
Merge pull request #3 from twsouthwick/use-feature
mikeebowen May 12, 2025
a4b61a7
move VerfiyMinimumPackage to feature
mikeebowen May 12, 2025
8e1d50d
remove ThrowIfNotMinimumPackage from OpenXmlPackage
mikeebowen May 12, 2025
8d1d6ea
fix merge conflict
mikeebowen May 14, 2025
04ef42b
verify wordprocessing document
mikeebowen May 15, 2025
a236566
add minimum document validation for PowerPoint and Excel
mikeebowen May 15, 2025
af392cc
add tests
mikeebowen May 15, 2025
b6d9b45
Merge branch 'issue-816' of github.com:mikeebowen/Open-XML-SDK into i…
mikeebowen May 15, 2025
7d4c2b4
revert to match main
mikeebowen May 15, 2025
8c4d993
add validation error if part is empty
mikeebowen May 15, 2025
12699db
update how to check for empty parts
mikeebowen May 16, 2025
8772882
fix tests
mikeebowen May 16, 2025
a1847d7
custom file property part test passes
mikeebowen Jun 12, 2025
92dcc74
fix P002_Pptx_DeleteAdd_CoreExtendedProperties test
mikeebowen Jun 12, 2025
1df4b0b
fix X006_Xlsx_DeleteAdd_CoreExtendedProperties and W051_AddNewPart_To…
mikeebowen Jun 13, 2025
1cdf8d2
Merge remote-tracking branch 'upstream/main' into issue-816
mikeebowen Jun 13, 2025
d7ee41b
add blank line
mikeebowen Jun 13, 2025
2097f05
remove file from earlier version of API
mikeebowen Jun 16, 2025
31aec5a
add IsEmptyPart method to ensure stream is disposed
mikeebowen Jun 16, 2025
2d5dd09
Update src/DocumentFormat.OpenXml.Framework/Packaging/OpenXmlPart.cs
mikeebowen Jun 30, 2025
e911447
Merge remote-tracking branch 'upstream/main' into issue-816
mikeebowen Jun 30, 2025
4861f13
move file extension check to IsEmptyPartMethod
mikeebowen Jun 30, 2025
25e5c67
removed unnecessary using
mikeebowen Jun 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions src/DocumentFormat.OpenXml/Packaging/PresentationDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -267,6 +270,120 @@ public static PresentationDocument Open(Package package, OpenSettings openSettin
.Build()
.Open(package);

/// <summary>
/// Validates whether the specified file is a minimum valid PresentationDocument.
/// </summary>
/// <param name="path">The path to the PresentationDocument file.</param>
/// <param name="documentType">
/// The expected type of the PresentationDocument. Defaults to <see cref="PresentationDocumentType.Presentation"/>.
/// Supported types are:
/// <list type="bullet">
/// <item><see cref="PresentationDocumentType.Presentation"/> (.pptx)</item>
/// <item><see cref="PresentationDocumentType.Template"/> (.potx)</item>
/// <item><see cref="PresentationDocumentType.MacroEnabledPresentation"/> (.pptm)</item>
/// <item><see cref="PresentationDocumentType.MacroEnabledTemplate"/> (.potm)</item>
/// </list>
/// </param>
/// <returns>
/// <c>true</c> if the file is a minimum valid PresentationDocument; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when the <paramref name="documentType"/> is invalid or unsupported.
/// </exception>
/// <remarks>
/// A minimum valid PresentationDocument must meet the following criteria:
/// <list type="bullet">
/// <item>The file must exist and have a valid extension matching the <paramref name="documentType"/>.</item>
/// <item>The file must contain a valid <see cref="NotesSize"/> element in the presentation part.</item>
/// </list>
/// Unsupported document types include:
/// <list type="bullet">
/// <item><see cref="PresentationDocumentType.AddIn"/> (.ppam)</item>
/// <item><see cref="PresentationDocumentType.Slideshow"/> (.ppsx)</item>
/// <item><see cref="PresentationDocumentType.MacroEnabledSlideshow"/> (.ppsm)</item>
/// </list>
/// </remarks>
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;
}
}

/// <summary>
/// Changes the document type.
/// </summary>
Expand Down
106 changes: 106 additions & 0 deletions src/DocumentFormat.OpenXml/Packaging/SpreadsheetDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -267,6 +268,111 @@ public static SpreadsheetDocument Open(System.IO.Stream stream, bool isEditable)
public static SpreadsheetDocument Open(System.IO.Packaging.Package package)
=> Open(package, new OpenSettings());

/// <summary>
/// Validates whether the specified file is a minimum valid SpreadsheetDocument.
/// </summary>
/// <param name="path">The path to the SpreadsheetDocument file.</param>
/// <param name="documentType">
/// The expected type of the SpreadsheetDocument. Defaults to <see cref="SpreadsheetDocumentType.Workbook"/>.
/// Supported types are:
/// <list type="bullet">
/// <item><see cref="SpreadsheetDocumentType.Workbook"/> (.xlsx)</item>
/// <item><see cref="SpreadsheetDocumentType.Template"/> (.xltx)</item>
/// <item><see cref="SpreadsheetDocumentType.MacroEnabledWorkbook"/> (.xlsm)</item>
/// <item><see cref="SpreadsheetDocumentType.MacroEnabledTemplate"/> (.xltm)</item>
/// </list>
/// </param>
/// <returns>
/// <c>true</c> if the file is a minimum valid SpreadsheetDocument; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when the <paramref name="documentType"/> is invalid or unsupported.
/// </exception>
/// <remarks>
/// A minimum valid SpreadsheetDocument must meet the following criteria:
/// <list type="bullet">
/// <item>The file must exist and have a valid extension matching the <paramref name="documentType"/>.</item>
/// <item>The file must contain at least one <see cref="Sheet"/> element in the workbook part.</item>
/// <item>The file must contain at least one <see cref="SheetData"/> element in the worksheet part.</item>
/// </list>
/// Unsupported document types include <see cref="SpreadsheetDocumentType.AddIn"/> (.xlam).
/// </remarks>
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))
{
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<Sheet>();
SheetData? sheetData = spreadsheetDocument?.WorkbookPart?.WorksheetParts?.FirstOrDefaultAndMaxOne()?.Worksheet?.GetFirstChild<SheetData>();

return sheet is not null && sheetData is not null;
}
}
catch
{
return false;
}
}

/// <summary>
/// Changes the document type.
/// </summary>
Expand Down
90 changes: 90 additions & 0 deletions src/DocumentFormat.OpenXml/Packaging/WordprocessingDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,96 @@ public static WordprocessingDocument Open(Package package, OpenSettings openSett
public static WordprocessingDocument Open(Package package)
=> Open(package, new OpenSettings());

/// <summary>
/// Validates whether the specified file is a minimum valid WordprocessingDocument.
/// </summary>
/// <param name="path">The path to the WordprocessingDocument file.</param>
/// <param name="documentType">
/// The expected type of the WordprocessingDocument. Defaults to <see cref="WordprocessingDocumentType.Document"/>.
/// Supported types are:
/// <list type="bullet">
/// <item><see cref="WordprocessingDocumentType.Document"/> (.docx)</item>
/// <item><see cref="WordprocessingDocumentType.Template"/> (.dotx)</item>
/// <item><see cref="WordprocessingDocumentType.MacroEnabledDocument"/> (.docm)</item>
/// <item><see cref="WordprocessingDocumentType.MacroEnabledTemplate"/> (.dotm)</item>
/// </list>
/// </param>
/// <returns>
/// <c>true</c> if the file is a minimum valid WordprocessingDocument; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// A minimum valid WordprocessingDocument must meet the following criteria:
/// <list type="bullet">
/// <item>The file must exist and have a valid extension matching the <paramref name="documentType"/>.</item>
/// <item>The file must contain a <see cref="Body"/> element in the main document part.</item>
/// </list>
/// </remarks>
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))
{
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;
}
}

/// <summary>
/// Changes the document type.
/// </summary>
Expand Down
Loading
Loading