Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
c52a7e1
feat: Get-OpenXML ( Fixes #2 )
Oct 14, 2025
d974cbe
feat: OpenXML EZOut Build ( Fixes #4 )
Oct 14, 2025
56826c5
feat: OpenXML Workflow ( Fixes #12 )
Oct 14, 2025
67a4592
feat: OpenXML.Excel.File.get_Worksheets ( Fixes #5 )
Oct 14, 2025
ffbb683
feat: OpenXML.Excel.File.get_Worksheets ( Fixes #5 )
Oct 14, 2025
aa1cfec
feat: OpenXML Module Scaffolding ( Fixes #1 )
Oct 14, 2025
c670fae
feat: OpenXML.Excel.Worksheet.get_Cell ( Fixes #6 )
Oct 14, 2025
624bdac
feat: OpenXML.Excel.Worksheet.get_Cell ( Fixes #6 )
Oct 14, 2025
1979ef5
feat: OpenXML.PowerPoint.File.get_Slides ( Fixes #8 )
Oct 14, 2025
752eb0a
feat: OpenXML.PowerPoint.File.get_Slides ( Fixes #8 )
Oct 14, 2025
5cfa47f
feat: OpenXML.PowerPoint.Slide.get_Text ( Fixes #10 )
Oct 14, 2025
77cb309
feat: OpenXML.PowerPoint.Slide.get_Text ( Fixes #10 )
Oct 14, 2025
32c468e
feat: OpenXML.PowerPoint.File.get_Text ( Fixes #9 )
Oct 14, 2025
269660f
feat: OpenXML.PowerPoint.File.get_Text ( Fixes #9 )
Oct 14, 2025
8d5775e
feat: OpenXML sample documents ( Fixes #3 )
Oct 14, 2025
0caee8c
feat: OpenXML Default Display ( Fixes #7 )
Oct 14, 2025
d10216f
feat: OpenXML Default Display ( Fixes #7 )
Oct 14, 2025
070c9c7
feat: OpenXML.Word.File.get_Text ( Fixes #9 )
Oct 14, 2025
00e3cee
feat: OpenXML.Word.File.get_Text ( Fixes #9 )
Oct 14, 2025
3e4237b
feat: OpenXML.File.get_DocumentProperty ( Fixes #13 )
Oct 14, 2025
b604b86
feat: OpenXML.File.get_DocumentProperty ( Fixes #13 )
Oct 14, 2025
b11fadb
feat: Adding loader check to PSM1 ( Fixes #1 )
StartAutomating Oct 14, 2025
2750557
feat: Get-OpenXML simplification ( Fixes #2 )
StartAutomating Oct 15, 2025
0fac9b9
feat: `OpenXML.get_Parts` ( Fixes #17 )
StartAutomating Oct 15, 2025
84a9bab
feat: `OpenXML.get_Parts` ( Fixes #17 )
Oct 15, 2025
db172a3
feat: `Copy-OpenXML` ( Fixes #18 )
StartAutomating Oct 15, 2025
2319f8d
feat: `Export-OpenXML` ( Fixes #15 )
StartAutomating Oct 15, 2025
b708a4a
feat: `Import-OpenXML` ( Fixes #14 )
StartAutomating Oct 15, 2025
ea4760b
feat: `Close-OpenXML` ( Fixes #16 )
StartAutomating Oct 15, 2025
e7ff3e0
feat: `Set-OpenXML` ( Fixes #19 )
StartAutomating Oct 15, 2025
49101b4
feat: OpenXML module exports ( Fixes #1 )
StartAutomating Oct 15, 2025
5f4b80e
docs: CODE_OF_CONDUCT ( Fixes #20 )
StartAutomating Oct 15, 2025
0d1e849
docs: CONTRIBUTING ( Fixes #21 )
StartAutomating Oct 15, 2025
7c29864
feat: OpenXML sample documents ( Fixes #3 )
Oct 15, 2025
bed2041
docs: `OpenXML.get_Created` ( Fixes #23 )
StartAutomating Oct 15, 2025
27b1581
docs: `OpenXML.get_Created` ( Fixes #23 )
Oct 15, 2025
6c30fc8
docs: `OpenXML.get_Modified` ( Fixes #24 )
StartAutomating Oct 15, 2025
a5a0c11
docs: `OpenXML.get_Modified` ( Fixes #24 )
Oct 15, 2025
15c00cb
feat: OpenXML sample documents ( Fixes #3 )
Oct 15, 2025
770759a
feat: `OpenXML.Excel.File.get_SharedString` ( Fixes #25 )
StartAutomating Oct 15, 2025
fc838d3
feat: `OpenXML.Excel.File.get_SharedString` ( Fixes #25 )
Oct 15, 2025
d7f98fe
feat: `OpenXML.Excel.File.get_Worksheets` ( Fixes #5 )
StartAutomating Oct 15, 2025
3dae7c1
feat: `OpenXML.Excel.File.get_Worksheets` ( Fixes #5 )
Oct 15, 2025
37281b8
feat: `OpenXML.Excel.File.get_Cell` ( Fixes #6 )
StartAutomating Oct 15, 2025
ea80003
feat: `OpenXML.Excel.File.get_Cell` ( Fixes #6 )
Oct 15, 2025
e71919d
feat: Updating manifest ( Fixes #1 )
StartAutomating Oct 15, 2025
a2283a3
feat: `OpenXML.Excel.File.get_Worksheets` ( Fixes #5 )
StartAutomating Oct 15, 2025
eecd6bc
feat: `OpenXML.Excel.File.get_Worksheets` ( Fixes #5 )
Oct 15, 2025
4604413
feat: `OpenXML.Excel.File.get_Cell` ( Fixes #6 )
StartAutomating Oct 15, 2025
8ce8f10
feat: `OpenXML.Excel.File.get_Cell` ( Fixes #6 )
Oct 15, 2025
b14b256
feat: `OpenXML.Excel.File.get_Formula` ( Fixes #26 )
StartAutomating Oct 15, 2025
381125d
feat: `OpenXML.Excel.File.get_Formula` ( Fixes #26 )
StartAutomating Oct 15, 2025
1ea7349
feat: `OpenXML.Excel.File.get_Formula` ( Fixes #26 )
Oct 15, 2025
3499232
docs: Adding README
StartAutomating Oct 15, 2025
17378d5
feat: `OpenXML.get_DocumentProperty` ( Fixes #13 )
StartAutomating Oct 15, 2025
065a301
feat: `OpenXML.get_DocumentProperty` ( Fixes #13 )
Oct 15, 2025
8b2d50e
docs: Adding SECURITY ( Fixes #22 )
StartAutomating Oct 15, 2025
6150ef1
docs: Adding SECURITY ( Fixes #22 )
StartAutomating Oct 15, 2025
a635a42
test: OpenXML Tests ( Fixes #27 )
StartAutomating Oct 15, 2025
7c5ef4e
feat: `Start-OpenXML` ( Fixes #28 )
StartAutomating Oct 16, 2025
d5a7f0f
test: OpenXML Tests ( Fixes #27 )
StartAutomating Oct 16, 2025
ebb4eae
feat: `Stop-OpenXML` ( Fixes #29 )
StartAutomating Oct 16, 2025
1c2e757
feat: `Set-OpenXML` ( Fixes #19 )
StartAutomating Oct 16, 2025
d1bb9a8
feat: Improving aliasing
StartAutomating Oct 16, 2025
7f0fd56
feat: Improving aliasing
Oct 16, 2025
16ecdfd
release: Updating CHANGELOG and Release Notes ( Fixes #1 )
StartAutomating Oct 16, 2025
aedbbb7
release: Updating CHANGELOG and Release Notes ( Fixes #1 )
StartAutomating Oct 16, 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
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: [StartAutomating]
498 changes: 498 additions & 0 deletions .github/workflows/BuildOpenXML.yml

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions Build/OpenXML.ezout.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#requires -Module EZOut
# Install-Module EZOut or https://github.com/StartAutomating/EZOut
$myFile = $MyInvocation.MyCommand.ScriptBlock.File
$myRoot = $myFile | Split-Path | Split-Path
$myModuleName = $myFile | Split-Path | Split-Path | Split-Path -Leaf
Push-Location $myRoot
$formatting = @(
# Add your own Write-FormatView here,
# or put them in a Formatting or Views directory
foreach ($potentialDirectory in 'Formatting','Views','Types') {
Join-Path $myRoot $potentialDirectory |
Get-ChildItem -ea ignore |
Import-FormatView -FilePath {$_.Fullname}
}
)

$destinationRoot = $myRoot

if ($formatting) {
$myFormatFilePath = Join-Path $destinationRoot "$myModuleName.format.ps1xml"
# You can also output to multiple paths by passing a hashtable to -OutputPath.
$formatting | Out-FormatData -Module $MyModuleName -OutputPath $myFormatFilePath
}

$types = @(
# Add your own Write-TypeView statements here
# or declare them in the 'Types' directory
Join-Path $myRoot Types |
Get-Item -ea ignore |
Import-TypeView

)

if ($types) {
$myTypesFilePath = Join-Path $destinationRoot "$myModuleName.types.ps1xml"
# You can also output to multiple paths by passing a hashtable to -OutputPath.
$types | Out-TypeData -OutputPath $myTypesFilePath
}
Pop-Location
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## OpenXML 0.1

* Initial Build of OpenXML Module (#1)
* Commands:
* `Get-OpenXML` (#2)
* `Import-OpenXML` (#14)
* `Export-OpenXML` (#15)
* `Close-OpenXML` (#16)
* `Copy-OpenXML` (#18)
* `Set-OpenXML` (#19)
* `Start-OpenXML` (#28)
* `Stop-OpenXML` (#29)
* Initial Extended Types
* `OpenXML`
* `OpenXML.get_Parts` (#17)
* `OpenXML.get_Created` (#23)
* `OpenXML.get_Modified` (#24)
* `OpenXML.File`
* `OpenXML.File.get_DocumentProperty` (#13)
* `OpenXML.File` default display (#7)
* `OpenXML.Excel.File`
* `OpenXML.Excel.File.get_Worksheets` (#5)
* `OpenXML.Excel.File.get_SharedString` (#25)
* `OpenXML.Excel.Worksheet`
* `OpenXML.Excel.Worksheet.get_Cell` (#6)
* `OpenXML.Excel.Worksheet.get_Formula` (#26)
* `OpenXML.PowerPoint.File`
* `OpenXML.PowerPoint.File.get_Slides` (#8)
* `OpenXML.PowerPoint.File.get_Text` (#9)
* `OpenXML.PowerPoint.Slide`
* `OpenXML.PowerPoint.get_Text` (#10)
* `OpenXML.Word.File`
* `OpenXML.Word.File.get_Text` (#11)
* Sample Documents (#3)
* Initial Tests (#27)
* Build workflow
* Building types with [EZOut](https://github.com/StartAutomating/EZOut) (#4)
* Building GitHub Workflow with [PSDevOps](https://github.com/StartAutomating/PSDevOps) (#12)
* Core Documentation
* README (#1)
* CODE_OF_CONDUCT (#20)
* CONTRIBUTING (#21)
* SECURITY (#22)
9 changes: 9 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Code of Conduct

We have a simple subjective code of conduct:

1. Be Respectful
2. Be Helpful
3. Do No Harm

Failure to follow the code of conduct may result in blocks or banishment.
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Contibuting

Contributing code is very welcome! So is contributing [an issue](https://github.com/PowerShellWeb/OpenXML/issues) or starting a [discussion](https://github.com/PowerShellWeb/OpenXML/discussion).

All projects are easier with community help, and this project is no different.

## Contributing Examples

Examples are more than welcome! To contribute an example, please open an issue describing your example and create a pull request.

## Contributing Code

If you would like to contribute code, please describe what you intend to do in an issue or two first.




32 changes: 32 additions & 0 deletions Commands/Close-OpenXML.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
function Close-OpenXML {
<#
.SYNOPSIS
Closes OpenXML files
.DESCRIPTION
Closes OpenXML files and streams
#>
param(
[Parameter(ValueFromPipeline)]
[PSObject]
$InputObject
)

process {
if (-not $InputObject) { return }
if ($InputObject -isnot [IO.Packaging.Package]) { return }
if ($InputObject.MemoryStream) {
try {
$InputObject.MemoryStream.Close()
} catch {
$PSCmdlet.WriteError($_)
}
}

try {
$InputObject.Close()
} catch {
$PSCmdlet.WriteError($_)
}
}

}
108 changes: 108 additions & 0 deletions Commands/Copy-OpenXML.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
function Copy-OpenXML
{
<#
.SYNOPSIS
Copies OpenXML
.DESCRIPTION
Copies content from one OpenXML file to another
#>
param(
# The destination path
[Parameter(ValueFromPipelineByPropertyName)]
[Alias('Destination')]
[string]
$DestinationPath,

# The input object
[Parameter(ValueFromPipeline)]
[PSObject]
$InputObject,

# If set, will update existing packages.
[switch]
$Force
)

process {
# If the input was not a package
if ($inputObject -isnot [IO.Packaging.Package]) {
$loadedPackage = # see if it is a file we can load
if ($InputObject -is [IO.FileInfo]) {
Get-OpenXML $InputObject.FullName
} elseif ($inputFile = Get-Item -ErrorAction Ignore -Path "$InputObject") {
Get-OpenXML $inputFile
}

# If it was not, return.
if ($loadedPackage -isnot [IO.Packaging.Package]) { return }
$InputObject = $loadedPackage
}

# Get the absolute path of the destination, without creating the file,
$unresolvedDestination = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DestinationPath)

# then see if the file exists.
$fileExists = Test-Path $unresolvedDestination
# If it does and we are not using the -Force
if ($fileExists -and -not $force) {
# write an error
Write-Error "$unresolvedDestionation already exists, use -Force to update" -Category ResourceExists
return
}
# If it did not exist, create it with New-Item -Force
elseif (-not $fileExists)
{
# this will create intermediate paths.
$newFile = New-Item -ItemType File -Path $unresolvedDestination -Force
if (-not $newFile) { return }
}

# Try to open or create our package for read and write.
$destinationPackage = [IO.Packaging.Package]::Open($unresolvedDestination, 'OpenOrCreate', 'ReadWrite')

# If we could not, we are done.
if (-not $destinationPackage) { return }

# Get the input parts and relationships
$inputPackageParts = $InputObject.GetParts()
$inputPackageRelationships = $InputObject.GetRelationships()

# For each part in the input
foreach ($inputPart in $inputPackageParts) {
# Create or open a part in the destination
$destinationPart =
if (-not $destinationPackage.PartExists($inputPart.Uri)) {
$destinationPackage.CreatePart($inputPart.Uri, $inputPart.ContentType)
} else {
$destinationPackage.GetPart($inputPart.Uri)
}

# and copy the streams.
$inputStream = $inputPart.GetStream()
$destinationStream = $destinationPart.GetStream()
$inputStream.CopyTo($destinationStream)
$inputStream.Close()
$destinationStream.Close()
}

# Then, create any relationships that do not exist.
foreach ($inputRelationship in $inputPackageRelationships) {
if ($inputRelationship) {
if (-not $destinationPackage.RelationshipExists($inputRelationship.id)) {
$null = $destinationPackage.CreateRelationship(
$inputRelationship.targetUri,
$inputRelationship.targetMode,
$inputRelationship.relationshipType,
$inputRelationship.id
)
}
}
}

# We can now close our package, writing the file.
$destinationPackage.Close()

# We want to open it right back up again as we output the updated file.
Get-OpenXML -FilePath $unresolvedDestination
}
}
38 changes: 38 additions & 0 deletions Commands/Export-OpenXML.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
function Export-OpenXML {
<#
.SYNOPSIS
Exports OpenXML
.DESCRIPTION
Exports loaded OpenXML to a file.
#>
[Alias('Save-OpenXML')]
param(
# The file path to save the turtle graphics pattern.
[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
[Alias('Path')]
[string]
$FilePath,

# The input object.
# This must be a package loaded with this module.
[Parameter(ValueFromPipeline)]
[PSObject]
$InputObject,

# If set, will force the export even if a file already exists.
[switch]
$Force
)

process {
# If there is no input return
if (-not $InputObject) { return }
# If the input is not a package, pass it thru
if ($InputObject -isnot [IO.Packaging.Package]) {
return $InputObject
}

Copy-OpenXML -DestinationPath $FilePath -InputObject $inputObject -force:$Force
}
}

101 changes: 101 additions & 0 deletions Commands/Get-OpenXML.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
function Get-OpenXML
{
<#
.SYNOPSIS
Gets Open Office XML files (Excel, PowerPoint, and Word)
.DESCRIPTION
Gets Open Office XML files (Excel, PowerPoint, and Word) as a structured object.

The object contains the file path, parts, and relationships of the OpenXML document.

This cmdlet can be used to read the contents of .docx, .pptx, .xps, .xlsx files
(or any files that are readable with [`IO.Packaging.Package`](https://learn.microsoft.com/en-us/dotnet/api/system.io.packaging.package?wt.mc_id=MVP_321542))
.EXAMPLE
# Get an OpenXML document
Get-OpenXML -FilePath './Sample.docx'
#>
[CmdletBinding()]
[Alias('OpenXML')]
param(
# The path to the OpenXML file to read
[Parameter(ValueFromPipelineByPropertyName=$true)]
[Alias('Fullname')]
[string]
$FilePath
)

begin {

filter openXMLFromFile {
$filePath = $_
# Get the file info and read the file as a byte stream.
$fileInfo = $FilePath -as [IO.FileInfo]
# By reading the file with Get-Content -AsByteStream, we avoid locking the file
# (or the file being locked by another process)
$packageBytes = Get-Content -Path $FilePath -AsByteStream -Raw

# If there were no bytes, return
if (-not $packageBytes) { return }

# Create a memory stream from the byte array
$memoryStream = [IO.MemoryStream]::new($packageBytes)
# and open the package from the memory stream
$filePackage = [IO.Packaging.Package]::Open($memoryStream, "Open", "ReadWrite")
# If that did not work, return.
if (-not $filePackage) { return }

$filePackage.pstypenames.insert(0,'OpenXML')
$filePackage.pstypenames.insert(0,'OpenXML.File')
$packageContent = $filePackage.Parts
$openXMLObject = $filePackage |
Add-Member NoteProperty FilePath $filePath -Force -PassThru |
Add-Member NoteProperty MemoryStream $memoryStream -Force -PassThru

# Now we can get more specific about what type of OpenXML file this is.
# By looking for certain key parts, we can determine if this is a PowerPoint, Excel, or Word file.
# For example, if the package contains a part with `/ppt/` in the URI,
if ($filePackage.Parts.Keys -match '/ppt/') {
# it is an `OpenXML.PowerPoint.File`
$openXmlObject.pstypenames.insert(0, 'OpenXML.PowerPoint.File')
}

# If the package contains a part with `/xl/` in the URI,
if ($filePackage.Parts.Keys -match '/xl/') {
# it is an `OpenXML.Excel.File`
$openXmlObject.pstypenames.insert(0, 'OpenXML.Excel.File')
}

# If the package contains a part with `/word/` in the URI, it is a Word file.
if ($filePackage.Parts.Keys -match '/word/') {
# it is an `OpenXML.Word.File`
$openXmlObject.pstypenames.insert(0, 'OpenXML.Word.File')
}

# If the package contains a part with `/Documents/` in the URI,
if ($filePackage.Parts.Keys -match '/Documents/') {
# it is an `OpenXML.XPS.File`
$openXmlObject.pstypenames.insert(0, 'OpenXML.XPS.File')
}

# Now we output our openXML object
$OpenXMLObject
}
}

process {
if ($filePath) {
# Try to resolve the file path
$resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
# If we could not resolve the path, exit
if (-not $resolvedPath ) { return }

$resolvedPath | openXMLFromFile
} else {
$memoryStream = [IO.MemoryStream]::new()
$EmptyPackage = [io.packaging.package]::Open($memoryStream ,'Create')
$EmptyPackage | Add-Member NoteProperty -Name MemoryStream -Value $memoryStream -Force
$EmptyPackage.pstypenames.insert(0, 'OpenXML')
$EmptyPackage
}
}
}
Loading
Loading