From 7e8416d67c8dceacd060865d4c1c27d65f401f95 Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 30 Nov 2021 15:49:07 -0500 Subject: [PATCH 1/2] Bump version and update change log --- ImportExcel.psd1 | 2 +- changelog.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 59e1612b..7d7a2be3 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -6,7 +6,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. - ModuleVersion = '7.4.0' + ModuleVersion = '7.4.1' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' diff --git a/changelog.md b/changelog.md index e504edaa..06b93aed 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,10 @@ +# v7.4.1 + +- Implements: https://github.com/dfinke/ImportExcel/issues/1111 +- Refactored ReZip into separate function +- Deletes temp folder after rezipping +- Added -ReZip to `Close-ExcelPackage` + # v7.4.0 - Thank you to [Max Goczall](https://github.com/muschebubusche) for this contribution! From aa1b0427671c97ae4c135efd9363ad6b0e7c7e98 Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 30 Nov 2021 15:50:11 -0500 Subject: [PATCH 2/2] Add -ReZip to Close-ExcelPackage like in Export-Excel #1111 --- Private/Invoke-ExcelReZipFile.ps1 | 25 ++ Public/Close-ExcelPackage.ps1 | 31 +- Public/Export-Excel.ps1 | 501 +++++++++++++++--------------- 3 files changed, 290 insertions(+), 267 deletions(-) create mode 100644 Private/Invoke-ExcelReZipFile.ps1 diff --git a/Private/Invoke-ExcelReZipFile.ps1 b/Private/Invoke-ExcelReZipFile.ps1 new file mode 100644 index 00000000..0c949689 --- /dev/null +++ b/Private/Invoke-ExcelReZipFile.ps1 @@ -0,0 +1,25 @@ +function Invoke-ExcelReZipFile { + <# + #> + param( + [Parameter(Mandatory)] + [OfficeOpenXml.ExcelPackage]$ExcelPackage + ) + + Write-Verbose -Message "Re-Zipping $($ExcelPackage.file) using .NET ZIP library" + try { + Add-Type -AssemblyName 'System.IO.Compression.Filesystem' -ErrorAction stop + } + catch { + Write-Error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+" + continue + } + try { + $TempZipPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName()) + $null = [io.compression.zipfile]::ExtractToDirectory($ExcelPackage.File, $TempZipPath) + Remove-Item $ExcelPackage.File -Force + $null = [io.compression.zipfile]::CreateFromDirectory($TempZipPath, $ExcelPackage.File) + Remove-Item $TempZipPath -Recurse -Force + } + catch { throw "Error resizipping $path : $_" } +} \ No newline at end of file diff --git a/Public/Close-ExcelPackage.ps1 b/Public/Close-ExcelPackage.ps1 index c2d9ad4d..ee64bc05 100644 --- a/Public/Close-ExcelPackage.ps1 +++ b/Public/Close-ExcelPackage.ps1 @@ -1,33 +1,38 @@ function Close-ExcelPackage { [CmdLetBinding()] - [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] param ( - [parameter(Mandatory=$true, ValueFromPipeline=$true)] + [parameter(Mandatory = $true, ValueFromPipeline = $true)] [OfficeOpenXml.ExcelPackage]$ExcelPackage, - [switch]$Show, + [Switch]$Show, [Switch]$NoSave, $SaveAs, [ValidateNotNullOrEmpty()] [String]$Password, - [switch]$Calculate + [Switch]$Calculate, + [Switch]$ReZip ) - if ( $NoSave) {$ExcelPackage.Dispose()} + + if ( $NoSave) { $ExcelPackage.Dispose() } else { if ($Calculate) { - try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) } - catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook."} + try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) } + catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook." } } if ($SaveAs) { $SaveAs = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs) - if ($Password) {$ExcelPackage.SaveAs( $SaveAs, $Password ) } - else {$ExcelPackage.SaveAs( $SaveAs)} + if ($Password) { $ExcelPackage.SaveAs( $SaveAs, $Password ) } + else { $ExcelPackage.SaveAs( $SaveAs) } } - else { - if ($Password) {$ExcelPackage.Save($Password) } - else {$ExcelPackage.Save() } + else { + if ($Password) { $ExcelPackage.Save($Password) } + else { $ExcelPackage.Save() } $SaveAs = $ExcelPackage.File.FullName } + if ($ReZip) { + Invoke-ExcelReZipFile -ExcelPackage $ExcelPackage + } $ExcelPackage.Dispose() - if ($Show) {Start-Process -FilePath $SaveAs } + if ($Show) { Start-Process -FilePath $SaveAs } } } diff --git a/Public/Export-Excel.ps1 b/Public/Export-Excel.ps1 index 40e41f2c..f11d1e5d 100644 --- a/Public/Export-Excel.ps1 +++ b/Public/Export-Excel.ps1 @@ -22,7 +22,7 @@ [Switch]$TitleBold, [Int]$TitleSize = 22, $TitleBackgroundColor, - [parameter(DontShow=$true)] + [parameter(DontShow = $true)] [Switch]$IncludePivotTable, [String]$PivotTableName, [String[]]$PivotRows, @@ -48,14 +48,14 @@ [Switch]$BoldTopRow, [Switch]$NoHeader, [ValidateScript( { - if (-not $_) { throw 'RangeName is null or empty.' } - elseif ($_[0] -notmatch '[a-z]') { throw 'RangeName starts with an invalid character.' } + if (-not $_) { throw 'RangeName is null or empty.' } + elseif ($_[0] -notmatch '[a-z]') { throw 'RangeName starts with an invalid character.' } else { $true } - })] + })] [String]$RangeName, [Alias('Table')] $TableName, - [OfficeOpenXml.Table.TableStyles]$TableStyle = [OfficeOpenXml.Table.TableStyles]::Medium6, + [OfficeOpenXml.Table.TableStyles]$TableStyle = [OfficeOpenXml.Table.TableStyles]::Medium6, [Switch]$Barchart, [Switch]$PieChart, [Switch]$LineChart , @@ -88,7 +88,7 @@ [Switch]$Now, [Switch]$ReturnRange, #By default PivotTables have Totals for each Row (on the right) and for each column at the bottom. This allows just one or neither to be selected. - [ValidateSet("Both","Columns","Rows","None")] + [ValidateSet("Both", "Columns", "Rows", "None")] [String]$PivotTotals = "Both", #Included for compatibility - equivalent to -PivotTotals "None" [Switch]$NoTotalsInPivot, @@ -98,13 +98,13 @@ begin { $numberRegex = [Regex]'\d' $isDataTypeValueType = $false - if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" } + if ($NoClobber) { Write-Warning -Message "-NoClobber parameter is no longer used" } #Open the file, get the worksheet, and decide where in the sheet we are writing, and if there is a number format to apply. - try { + try { $script:Header = $null - if ($Append -and $ClearSheet) {throw "You can't use -Append AND -ClearSheet." ; return} + if ($Append -and $ClearSheet) { throw "You can't use -Append AND -ClearSheet." ; return } #To force -Now not to format as a table, allow $false in -TableName to be "No table" - $TableName = if ($null -eq $TableName -or ($TableName -is [bool] -and $false -eq $TableName)) { $null } else {[String]$TableName} + $TableName = if ($null -eq $TableName -or ($TableName -is [bool] -and $false -eq $TableName)) { $null } else { [String]$TableName } if ($Now -or (-not $Path -and -not $ExcelPackage) ) { if (-not $PSBoundParameters.ContainsKey("Path")) { $Path = [System.IO.Path]::GetTempFileName() -replace '\.tmp', '.xlsx' } if (-not $PSBoundParameters.ContainsKey("Show")) { $Show = $true } @@ -120,59 +120,59 @@ $pkg = $ExcelPackage $Path = $pkg.File } - Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel -Password:$Password} + Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel -Password:$Password } } - catch {throw "Could not open Excel Package $path"} - try { - $params = @{WorksheetName=$WorksheetName} - foreach ($p in @("ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter", "Activate")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}} + catch { throw "Could not open Excel Package $path" } + try { + $params = @{WorksheetName = $WorksheetName } + foreach ($p in @("ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter", "Activate")) { if ($PSBoundParameters[$p]) { $params[$p] = $PSBoundParameters[$p] } } $ws = $pkg | Add-Worksheet @params if ($ws.Name -ne $WorksheetName) { Write-Warning -Message "The Worksheet name has been changed from $WorksheetName to $($ws.Name), this may cause errors later." $WorksheetName = $ws.Name } } - catch {throw "Could not get worksheet $WorksheetName"} - try { + catch { throw "Could not get worksheet $WorksheetName" } + try { if ($Append -and $ws.Dimension) { #if there is a title or anything else above the header row, append needs to be combined wih a suitable startrow parameter $headerRange = $ws.Dimension.Address -replace "\d+$", $StartRow #using a slightly odd syntax otherwise header ends up as a 2D array - $ws.Cells[$headerRange].Value | ForEach-Object -Begin {$Script:header = @()} -Process {$Script:header += $_ } + $ws.Cells[$headerRange].Value | ForEach-Object -Begin { $Script:header = @() } -Process { $Script:header += $_ } $NoHeader = $true #if we did not get AutoNameRange, but headers have ranges of the same name make autoNameRange True, otherwise make it false if (-not $AutoNameRange) { - $AutoNameRange = $true ; foreach ($h in $header) {if ($ws.names.name -notcontains $h) {$AutoNameRange = $false} } + $AutoNameRange = $true ; foreach ($h in $header) { if ($ws.names.name -notcontains $h) { $AutoNameRange = $false } } } #if we did not get a Rangename but there is a Range which covers the active part of the sheet, set Rangename to that. - if (-not $RangeName -and $ws.names.where({$_.name[0] -match '[a-z]'})) { + if (-not $RangeName -and $ws.names.where({ $_.name[0] -match '[a-z]' })) { $theRange = $ws.names.where({ - ($_.Name[0] -match '[a-z]' ) -and - ($_.Start.Row -eq $StartRow) -and - ($_.Start.Column -eq $StartColumn) -and - ($_.End.Row -eq $ws.Dimension.End.Row) -and - ($_.End.Column -eq $ws.Dimension.End.column) } , 'First', 1) - if ($theRange) {$rangename = $theRange.name} + ($_.Name[0] -match '[a-z]' ) -and + ($_.Start.Row -eq $StartRow) -and + ($_.Start.Column -eq $StartColumn) -and + ($_.End.Row -eq $ws.Dimension.End.Row) -and + ($_.End.Column -eq $ws.Dimension.End.column) } , 'First', 1) + if ($theRange) { $rangename = $theRange.name } } #if we did not get a table name but there is a table which covers the active part of the sheet, set table name to that, and don't do anything with autofilter - $existingTable = $ws.Tables.Where({$_.address.address -eq $ws.dimension.address},'First', 1) + $existingTable = $ws.Tables.Where({ $_.address.address -eq $ws.dimension.address }, 'First', 1) if ($null -eq $TableName -and $existingTable) { - $TableName = $existingTable.Name - $TableStyle = $existingTable.StyleName -replace "^TableStyle","" + $TableName = $existingTable.Name + $TableStyle = $existingTable.StyleName -replace "^TableStyle", "" $AutoFilter = $false } #if we did not get $autofilter but a filter range is set and it covers the right area, set autofilter to true elseif (-not $AutoFilter -and $ws.Names['_xlnm._FilterDatabase']) { - if ( ($ws.Names['_xlnm._FilterDatabase'].Start.Row -eq $StartRow) -and + if ( ($ws.Names['_xlnm._FilterDatabase'].Start.Row -eq $StartRow) -and ($ws.Names['_xlnm._FilterDatabase'].Start.Column -eq $StartColumn) -and - ($ws.Names['_xlnm._FilterDatabase'].End.Row -eq $ws.Dimension.End.Row) -and - ($ws.Names['_xlnm._FilterDatabase'].End.Column -eq $ws.Dimension.End.Column) ) {$AutoFilter = $true} + ($ws.Names['_xlnm._FilterDatabase'].End.Row -eq $ws.Dimension.End.Row) -and + ($ws.Names['_xlnm._FilterDatabase'].End.Column -eq $ws.Dimension.End.Column) ) { $AutoFilter = $true } } $row = $ws.Dimension.End.Row Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + " Start row is $row") - if ($Title) {Write-Warning -Message "-Title Parameter is ignored when appending."} + if ($Title) { Write-Warning -Message "-Title Parameter is ignored when appending." } } elseif ($Title) { #Can only add a title if not appending! @@ -180,41 +180,41 @@ $ws.Cells[$row, $StartColumn].Value = $Title $ws.Cells[$row, $StartColumn].Style.Font.Size = $TitleSize - if ($PSBoundParameters.ContainsKey("TitleBold")) { + if ($PSBoundParameters.ContainsKey("TitleBold")) { #Set title to Bold face font if -TitleBold was specified. #Otherwise the default will be unbolded. $ws.Cells[$row, $StartColumn].Style.Font.Bold = [boolean]$TitleBold } if ($TitleBackgroundColor ) { - if ($TitleBackgroundColor -is [string]) {$TitleBackgroundColor = [System.Drawing.Color]::$TitleBackgroundColor } + if ($TitleBackgroundColor -is [string]) { $TitleBackgroundColor = [System.Drawing.Color]::$TitleBackgroundColor } $ws.Cells[$row, $StartColumn].Style.Fill.PatternType = $TitleFillPattern $ws.Cells[$row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor) } $row ++ ; $startRow ++ } - else { $row = $StartRow } + else { $row = $StartRow } $ColumnIndex = $StartColumn $Numberformat = Expand-NumberFormat -NumberFormat $Numberformat if ((-not $ws.Dimension) -and ($Numberformat -ne $ws.Cells.Style.Numberformat.Format)) { - $ws.Cells.Style.Numberformat.Format = $Numberformat - $setNumformat = $false + $ws.Cells.Style.Numberformat.Format = $Numberformat + $setNumformat = $false } - else { $setNumformat = ($Numberformat -ne $ws.Cells.Style.Numberformat.Format) } + else { $setNumformat = ($Numberformat -ne $ws.Cells.Style.Numberformat.Format) } } - catch {throw "Failed preparing to export to worksheet '$WorksheetName' to '$Path': $_"} + catch { throw "Failed preparing to export to worksheet '$WorksheetName' to '$Path': $_" } #region Special case -inputobject passed a dataTable object <# If inputObject was passed via the pipeline it won't be visible until the process block, we will only see it here if it was passed as a parameter if it is a data table don't do foreach on it (slow) - put the whole table in and set dates on date columns, set things up for the end block, and skip the process block #> - if ($InputObject -is [System.Data.DataTable]) { + if ($InputObject -is [System.Data.DataTable]) { if ($Append -and $ws.dimension) { $row ++ - $null = $ws.Cells[$row,$StartColumn].LoadFromDataTable($InputObject, $false ) - if ($TableName -or $PSBoundParameters.ContainsKey('TableStyle')) { + $null = $ws.Cells[$row, $StartColumn].LoadFromDataTable($InputObject, $false ) + if ($TableName -or $PSBoundParameters.ContainsKey('TableStyle')) { Add-ExcelTable -Range $ws.Cells[$ws.Dimension] -TableName $TableName -TableStyle $TableStyle } } - else { + else { #Change TableName if $TableName is non-empty; don't leave caller with a renamed table! $orginalTableName = $InputObject.TableName if ($PSBoundParameters.ContainsKey("TableName")) { @@ -226,157 +226,160 @@ } #Insert as a table, if Tablestyle didn't arrive as a default, or $TableName non-null - even if empty if ($null -ne $TableName -or $PSBoundParameters.ContainsKey("TableStyle")) { - $null = $ws.Cells[$row,$StartColumn].LoadFromDataTable($InputObject, (-not $noHeader),$TableStyle ) + $null = $ws.Cells[$row, $StartColumn].LoadFromDataTable($InputObject, (-not $noHeader), $TableStyle ) # Workaround for EPPlus not marking the empty row on an empty table as dummy row. if ($InputObject.Rows.Count -eq 0) { ($ws.Tables | Select-Object -Last 1).TableXml.table.SetAttribute('insertRow', 1) } } else { - $null = $ws.Cells[$row,$StartColumn].LoadFromDataTable($InputObject, (-not $noHeader) ) + $null = $ws.Cells[$row, $StartColumn].LoadFromDataTable($InputObject, (-not $noHeader) ) } $InputObject.TableName = $orginalTableName } - foreach ($c in $InputObject.Columns.where({$_.datatype -eq [datetime]})) { + foreach ($c in $InputObject.Columns.where({ $_.datatype -eq [datetime] })) { Set-ExcelColumn -Worksheet $ws -Column ($c.Ordinal + $StartColumn) -NumberFormat 'Date-Time' } - foreach ($c in $InputObject.Columns.where({$_.datatype -eq [timespan]})) { + foreach ($c in $InputObject.Columns.where({ $_.datatype -eq [timespan] })) { Set-ExcelColumn -Worksheet $ws -Column ($c.Ordinal + $StartColumn) -NumberFormat '[h]:mm:ss' } - $ColumnIndex += $InputObject.Columns.Count - 1 - if ($noHeader) {$row += $InputObject.Rows.Count -1 } - else {$row += $InputObject.Rows.Count } + $ColumnIndex += $InputObject.Columns.Count - 1 + if ($noHeader) { $row += $InputObject.Rows.Count - 1 } + else { $row += $InputObject.Rows.Count } $null = $PSBoundParameters.Remove('InputObject') $firstTimeThru = $false } #endregion - else {$firstTimeThru = $true} + else { $firstTimeThru = $true } } - process { if ($PSBoundParameters.ContainsKey("InputObject")) { - try { - if ($null -eq $InputObject) {$row += 1} - foreach ($TargetData in $InputObject) { - if ($firstTimeThru) { - $firstTimeThru = $false - $isDataTypeValueType = ($null -eq $TargetData) -or ($TargetData.GetType().name -match 'string|timespan|datetime|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort|URI|ExcelHyperLink') - if ($isDataTypeValueType ) { - $script:Header = @(".") # dummy value to make sure we go through the "for each name in $header" - if (-not $Append) {$row -= 1} # By default row will be 1, it is incremented before inserting values (so it ends pointing at final row.); si first data row is 2 - move back up 1 if there is no header . - } - if ($null -ne $TargetData) {Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" } - } - #region Add headers - if we are appending, or we have been through here once already we will have the headers - if (-not $script:Header) { - if ($DisplayPropertySet -and $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames) { - $script:Header = $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Where( {$_ -notin $ExcludeProperty}) - } - else { - if ($NoAliasOrScriptPropeties) {$propType = "Property"} else {$propType = "*"} - $script:Header = $TargetData.PSObject.Properties.where( {$_.MemberType -like $propType}).Name - } - foreach ($exclusion in $ExcludeProperty) {$script:Header = $script:Header -notlike $exclusion} - if ($NoHeader) { - # Don't push the headers to the spreadsheet - $row -= 1 + process { + if ($PSBoundParameters.ContainsKey("InputObject")) { + try { + if ($null -eq $InputObject) { $row += 1 } + foreach ($TargetData in $InputObject) { + if ($firstTimeThru) { + $firstTimeThru = $false + $isDataTypeValueType = ($null -eq $TargetData) -or ($TargetData.GetType().name -match 'string|timespan|datetime|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort|URI|ExcelHyperLink') + if ($isDataTypeValueType ) { + $script:Header = @(".") # dummy value to make sure we go through the "for each name in $header" + if (-not $Append) { $row -= 1 } # By default row will be 1, it is incremented before inserting values (so it ends pointing at final row.); si first data row is 2 - move back up 1 if there is no header . + } + if ($null -ne $TargetData) { Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" } } - else { - $ColumnIndex = $StartColumn - foreach ($Name in $script:Header) { - $ws.Cells[$row, $ColumnIndex].Value = $Name - Write-Verbose "Cell '$row`:$ColumnIndex' add header '$Name'" - $ColumnIndex += 1 + #region Add headers - if we are appending, or we have been through here once already we will have the headers + if (-not $script:Header) { + if ($DisplayPropertySet -and $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames) { + $script:Header = $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Where( { $_ -notin $ExcludeProperty }) + } + else { + if ($NoAliasOrScriptPropeties) { $propType = "Property" } else { $propType = "*" } + $script:Header = $TargetData.PSObject.Properties.where( { $_.MemberType -like $propType }).Name + } + foreach ($exclusion in $ExcludeProperty) { $script:Header = $script:Header -notlike $exclusion } + if ($NoHeader) { + # Don't push the headers to the spreadsheet + $row -= 1 + } + else { + $ColumnIndex = $StartColumn + foreach ($Name in $script:Header) { + $ws.Cells[$row, $ColumnIndex].Value = $Name + Write-Verbose "Cell '$row`:$ColumnIndex' add header '$Name'" + $ColumnIndex += 1 + } } } - } - #endregion - #region Add non header values - $row += 1 - $ColumnIndex = $StartColumn - <# + #endregion + #region Add non header values + $row += 1 + $ColumnIndex = $StartColumn + <# For each item in the header OR for the Data item if this is a simple Type or data table : If it is a date insert with one of Excel's built in formats - recognized as "Date and time to be localized" if it is a timespan insert with a built in format for elapsed hours, minutes and seconds if its any other numeric insert as is , setting format if need be. Preserve URI, Insert a data table, convert non string objects to string. For strings, check for fomula, URI or Number, before inserting as a string (ignore nulls) #> - foreach ($Name in $script:Header) { - if ($isDataTypeValueType) {$v = $TargetData} - else {$v = $TargetData.$Name} - try { - if ($v -is [DateTime]) { - $ws.Cells[$row, $ColumnIndex].Value = $v - $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized. - } - elseif ($v -is [TimeSpan]) { - $ws.Cells[$row, $ColumnIndex].Value = $v - $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = '[h]:mm:ss' - } - elseif ($v -is [System.ValueType]) { - $ws.Cells[$row, $ColumnIndex].Value = $v - if ($setNumformat) {$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat } - } - elseif ($v -is [uri] ) { - $ws.Cells[$row, $ColumnIndex].HyperLink = $v - $ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue) - $ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true - } - elseif ($v -isnot [String] ) { #Other objects or null. - if ($null -ne $v) { $ws.Cells[$row, $ColumnIndex].Value = $v.toString()} - } - elseif ($v[0] -eq '=') { - $ws.Cells[$row, $ColumnIndex].Formula = ($v -replace '^=','') - if ($setNumformat) {$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat } - } - elseif ( [System.Uri]::IsWellFormedUriString($v , [System.UriKind]::Absolute) ) { - if ($v -match "^xl://internal/") { - $referenceAddress = $v -replace "^xl://internal/" , "" - $display = $referenceAddress -replace "!A1$" , "" - $h = New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList $referenceAddress , $display - $ws.Cells[$row, $ColumnIndex].HyperLink = $h + foreach ($Name in $script:Header) { + if ($isDataTypeValueType) { $v = $TargetData } + else { $v = $TargetData.$Name } + try { + if ($v -is [DateTime]) { + $ws.Cells[$row, $ColumnIndex].Value = $v + $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized. } - else {$ws.Cells[$row, $ColumnIndex].HyperLink = $v } #$ws.Cells[$row, $ColumnIndex].Value = $v.AbsoluteUri - $ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue) - $ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true - } - else { - $number = $null - if ( $numberRegex.IsMatch($v) -and # if it contains digit(s) - this syntax is quicker than -match for many items and cuts out slow checks for non numbers - $NoNumberConversion -ne '*' -and # and NoNumberConversion isn't specified - $NoNumberConversion -notcontains $Name -and - [Double]::TryParse($v, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number) - ) { - $ws.Cells[$row, $ColumnIndex].Value = $number - if ($setNumformat) {$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat } + elseif ($v -is [TimeSpan]) { + $ws.Cells[$row, $ColumnIndex].Value = $v + $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = '[h]:mm:ss' + } + elseif ($v -is [System.ValueType]) { + $ws.Cells[$row, $ColumnIndex].Value = $v + if ($setNumformat) { $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat } + } + elseif ($v -is [uri] ) { + $ws.Cells[$row, $ColumnIndex].HyperLink = $v + $ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue) + $ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true + } + elseif ($v -isnot [String] ) { + #Other objects or null. + if ($null -ne $v) { $ws.Cells[$row, $ColumnIndex].Value = $v.toString() } + } + elseif ($v[0] -eq '=') { + $ws.Cells[$row, $ColumnIndex].Formula = ($v -replace '^=', '') + if ($setNumformat) { $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat } + } + elseif ( [System.Uri]::IsWellFormedUriString($v , [System.UriKind]::Absolute) ) { + if ($v -match "^xl://internal/") { + $referenceAddress = $v -replace "^xl://internal/" , "" + $display = $referenceAddress -replace "!A1$" , "" + $h = New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList $referenceAddress , $display + $ws.Cells[$row, $ColumnIndex].HyperLink = $h + } + else { $ws.Cells[$row, $ColumnIndex].HyperLink = $v } #$ws.Cells[$row, $ColumnIndex].Value = $v.AbsoluteUri + $ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue) + $ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true } else { - $ws.Cells[$row, $ColumnIndex].Value = $v + $number = $null + if ( $numberRegex.IsMatch($v) -and # if it contains digit(s) - this syntax is quicker than -match for many items and cuts out slow checks for non numbers + $NoNumberConversion -ne '*' -and # and NoNumberConversion isn't specified + $NoNumberConversion -notcontains $Name -and + [Double]::TryParse($v, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number) + ) { + $ws.Cells[$row, $ColumnIndex].Value = $number + if ($setNumformat) { $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat } + } + else { + $ws.Cells[$row, $ColumnIndex].Value = $v + } } } + catch { Write-Warning -Message "Could not insert the '$Name' property at Row $row, Column $ColumnIndex" } + $ColumnIndex += 1 } - catch {Write-Warning -Message "Could not insert the '$Name' property at Row $row, Column $ColumnIndex"} - $ColumnIndex += 1 + $ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false + #endregion } - $ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false - #endregion } + catch { throw "Failed exporting data to worksheet '$WorksheetName' to '$Path': $_" } } - catch {throw "Failed exporting data to worksheet '$WorksheetName' to '$Path': $_" } - }} + } end { if ($firstTimeThru -and $ws.Dimension) { - $LastRow = $ws.Dimension.End.Row - $LastCol = $ws.Dimension.End.Column - $endAddress = $ws.Dimension.End.Address + $LastRow = $ws.Dimension.End.Row + $LastCol = $ws.Dimension.End.Column + $endAddress = $ws.Dimension.End.Address } else { - $LastRow = $row - $LastCol = $ColumnIndex - $endAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($LastRow , $LastCol) + $LastRow = $row + $LastCol = $ColumnIndex + $endAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($LastRow , $LastCol) } - $startAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($StartRow, $StartColumn) - $dataRange = "{0}:{1}" -f $startAddress, $endAddress + $startAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($StartRow, $StartColumn) + $dataRange = "{0}:{1}" -f $startAddress, $endAddress Write-Debug "Data Range '$dataRange'" if ($AutoNameRange) { @@ -385,13 +388,14 @@ # if there aren't any headers, use the the first row of data to name the ranges: this is the last point that headers will be used. $headerRange = $ws.Dimension.Address -replace "\d+$", $StartRow #using a slightly odd syntax otherwise header ends up as a 2D array - $ws.Cells[$headerRange].Value | ForEach-Object -Begin {$Script:header = @()} -Process {$Script:header += $_ } - if ($PSBoundParameters.ContainsKey($TargetData)) { #if Export was called with data that writes no header start the range at $startRow ($startRow is data) - $targetRow = $StartRow + $ws.Cells[$headerRange].Value | ForEach-Object -Begin { $Script:header = @() } -Process { $Script:header += $_ } + if ($PSBoundParameters.ContainsKey($TargetData)) { + #if Export was called with data that writes no header start the range at $startRow ($startRow is data) + $targetRow = $StartRow } else { $targetRow = $StartRow + 1 } #if Export was called without data to add names (assume $startRow is a header) or... } # ... called with data that writes a header, then start the range at $startRow + 1 - else { $targetRow = $StartRow + 1 } + else { $targetRow = $StartRow + 1 } #Dimension.start.row always seems to be one so we work out the target row #, but start.column is the first populated one and .Columns is the count of populated ones. @@ -400,7 +404,8 @@ foreach ($c in 0..($LastCol - $StartColumn)) { $targetRangeName = @($script:Header)[$c] #Let Add-ExcelName fix (and warn about) bad names Add-ExcelName -RangeName $targetRangeName -Range $ws.Cells[$targetRow, ($StartColumn + $c ), $LastRow, ($StartColumn + $c )] - try {#this test can throw with some names, surpress any error + try { + #this test can throw with some names, surpress any error if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress(($targetRangeName -replace '\W' , '_' ))) { Write-Warning -Message "AutoNameRange: Property name '$targetRangeName' is also a valid Excel address and may cause issues. Consider renaming the property." } @@ -410,13 +415,13 @@ } } } - catch {Write-Warning -Message "Failed adding named ranges to worksheet '$WorksheetName': $_" } + catch { Write-Warning -Message "Failed adding named ranges to worksheet '$WorksheetName': $_" } } #Empty string is not allowed as a name for ranges or tables. - if ($RangeName) { Add-ExcelName -Range $ws.Cells[$dataRange] -RangeName $RangeName} + if ($RangeName) { Add-ExcelName -Range $ws.Cells[$dataRange] -RangeName $RangeName } #Allow table to be inserted by specifying Name, or Style or both; only process autoFilter if there is no table (they clash). - if ($null -ne $TableName -or $PSBoundParameters.ContainsKey('TableStyle')) { + if ($null -ne $TableName -or $PSBoundParameters.ContainsKey('TableStyle')) { #Already inserted Excel table if input was a DataTable if ($InputObject -isnot [System.Data.DataTable]) { Add-ExcelTable -Range $ws.Cells[$dataRange] -TableName $TableName -TableStyle $TableStyle @@ -427,19 +432,19 @@ $ws.Cells[$dataRange].AutoFilter = $true Write-Verbose -Message "Enabled autofilter. " } - catch {Write-Warning -Message "Failed adding autofilter to worksheet '$WorksheetName': $_"} + catch { Write-Warning -Message "Failed adding autofilter to worksheet '$WorksheetName': $_" } } if ($PivotTableDefinition) { foreach ($item in $PivotTableDefinition.GetEnumerator()) { $params = $item.value - if ($Activate) {$params.Activate = $true } + if ($Activate) { $params.Activate = $true } if ($params.keys -notcontains 'SourceRange' -and - ($params.Keys -notcontains 'SourceWorksheet' -or $params.SourceWorksheet -eq $WorksheetName)) {$params.SourceRange = $dataRange} - if ($params.Keys -notcontains 'SourceWorksheet') {$params.SourceWorksheet = $ws } - if ($params.Keys -notcontains 'NoTotalsInPivot' -and $NoTotalsInPivot ) {$params.PivotTotals = 'None'} - if ($params.Keys -notcontains 'PivotTotals' -and $PivotTotals ) {$params.PivotTotals = $PivotTotals} - if ($params.Keys -notcontains 'PivotDataToColumn' -and $PivotDataToColumn) {$params.PivotDataToColumn = $true} + ($params.Keys -notcontains 'SourceWorksheet' -or $params.SourceWorksheet -eq $WorksheetName)) { $params.SourceRange = $dataRange } + if ($params.Keys -notcontains 'SourceWorksheet') { $params.SourceWorksheet = $ws } + if ($params.Keys -notcontains 'NoTotalsInPivot' -and $NoTotalsInPivot ) { $params.PivotTotals = 'None' } + if ($params.Keys -notcontains 'PivotTotals' -and $PivotTotals ) { $params.PivotTotals = $PivotTotals } + if ($params.Keys -notcontains 'PivotDataToColumn' -and $PivotDataToColumn) { $params.PivotDataToColumn = $true } Add-PivotTable -ExcelPackage $pkg -PivotTableName $item.key @Params } @@ -453,23 +458,23 @@ $PivotTableName += 'Pivot' } - if ($PivotTableName) {$params.PivotTableName = $PivotTableName} - else {$params.PivotTableName = $WorksheetName + 'PivotTable'} - if ($Activate) {$params.Activate = $true } - if ($PivotFilter) {$params.PivotFilter = $PivotFilter} - if ($PivotRows) {$params.PivotRows = $PivotRows} - if ($PivotColumns) {$Params.PivotColumns = $PivotColumns} - if ($PivotData) {$Params.PivotData = $PivotData} - if ($NoTotalsInPivot) {$params.PivotTotals = "None" } - Elseif ($PivotTotals) {$params.PivotTotals = $PivotTotals} - if ($PivotDataToColumn) {$params.PivotDataToColumn = $true} + if ($PivotTableName) { $params.PivotTableName = $PivotTableName } + else { $params.PivotTableName = $WorksheetName + 'PivotTable' } + if ($Activate) { $params.Activate = $true } + if ($PivotFilter) { $params.PivotFilter = $PivotFilter } + if ($PivotRows) { $params.PivotRows = $PivotRows } + if ($PivotColumns) { $Params.PivotColumns = $PivotColumns } + if ($PivotData) { $Params.PivotData = $PivotData } + if ($NoTotalsInPivot) { $params.PivotTotals = "None" } + Elseif ($PivotTotals) { $params.PivotTotals = $PivotTotals } + if ($PivotDataToColumn) { $params.PivotDataToColumn = $true } if ($IncludePivotChart -or $PSBoundParameters.ContainsKey('PivotChartType')) { - $params.IncludePivotChart = $true - $Params.ChartType = $PivotChartType - if ($ShowCategory) {$params.ShowCategory = $true} - if ($ShowPercent) {$params.ShowPercent = $true} - if ($NoLegend) {$params.NoLegend = $true} + $params.IncludePivotChart = $true + $Params.ChartType = $PivotChartType + if ($ShowCategory) { $params.ShowCategory = $true } + if ($ShowPercent) { $params.ShowPercent = $true } + if ($NoLegend) { $params.NoLegend = $true } } Add-PivotTable -ExcelPackage $pkg -SourceWorksheet $ws @params } @@ -513,9 +518,10 @@ } } } - catch {Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorksheetName': $_"} + catch { Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorksheetName': $_" } - if ($PSBoundParameters.ContainsKey("BoldTopRow")) { #it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true + if ($PSBoundParameters.ContainsKey("BoldTopRow")) { + #it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true try { if ($Title) { $range = $ws.Dimension.Address -replace '\d+', ($StartRow + 1) @@ -526,41 +532,41 @@ $ws.Cells[$range].Style.Font.Bold = [boolean]$BoldTopRow Write-Verbose -Message "Set $range font style to bold." } - catch {Write-Warning -Message "Failed setting the top row to bold in worksheet '$WorksheetName': $_"} + catch { Write-Warning -Message "Failed setting the top row to bold in worksheet '$WorksheetName': $_" } } if ($AutoSize -and -not $env:NoAutoSize) { try { #Don't fit the all the columns in the sheet; if we are adding cells beside things with hidden columns, that unhides them if ($MaxAutoSizeRows -and $MaxAutoSizeRows -lt $LastRow ) { - $AutosizeRange = [OfficeOpenXml.ExcelAddress]::GetAddress($startRow,$StartColumn, $MaxAutoSizeRows , $LastCol) + $AutosizeRange = [OfficeOpenXml.ExcelAddress]::GetAddress($startRow, $StartColumn, $MaxAutoSizeRows , $LastCol) $ws.Cells[$AutosizeRange].AutoFitColumns() } - else {$ws.Cells[$dataRange].AutoFitColumns() } + else { $ws.Cells[$dataRange].AutoFitColumns() } Write-Verbose -Message "Auto-sized columns" } - catch { Write-Warning -Message "Failed autosizing columns of worksheet '$WorksheetName': $_"} + catch { Write-Warning -Message "Failed autosizing columns of worksheet '$WorksheetName': $_" } } - elseif ($AutoSize) {Write-Warning -Message "Auto-fitting columns is not available with this OS configuration." } + elseif ($AutoSize) { Write-Warning -Message "Auto-fitting columns is not available with this OS configuration." } foreach ($Sheet in $HideSheet) { try { - $pkg.Workbook.Worksheets.Where({$_.Name -like $sheet}) | ForEach-Object { + $pkg.Workbook.Worksheets.Where({ $_.Name -like $sheet }) | ForEach-Object { $_.Hidden = 'Hidden' Write-verbose -Message "Sheet '$($_.Name)' Hidden." } } - catch {Write-Warning -Message "Failed hiding worksheet '$sheet': $_"} + catch { Write-Warning -Message "Failed hiding worksheet '$sheet': $_" } } foreach ($Sheet in $UnHideSheet) { try { - $pkg.Workbook.Worksheets.Where({$_.Name -like $sheet}) | ForEach-Object { + $pkg.Workbook.Worksheets.Where({ $_.Name -like $sheet }) | ForEach-Object { $_.Hidden = 'Visible' Write-verbose -Message "Sheet '$($_.Name)' shown" } } - catch {Write-Warning -Message "Failed showing worksheet '$sheet': $_"} + catch { Write-Warning -Message "Failed showing worksheet '$sheet': $_" } } - if (-not $pkg.Workbook.Worksheets.Where({$_.Hidden -eq 'visible'})) { + if (-not $pkg.Workbook.Worksheets.Where({ $_.Hidden -eq 'visible' })) { Write-Verbose -Message "No Sheets were left visible, making $WorksheetName visible" $ws.Hidden = 'Visible' } @@ -568,46 +574,46 @@ foreach ($chartDef in $ExcelChartDefinition) { if ($chartDef -is [System.Management.Automation.PSCustomObject]) { $params = @{} - $chartDef.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} + $chartDef.PSObject.Properties | ForEach-Object { if ( $null -ne $_.value) { $params[$_.name] = $_.value } } Add-ExcelChart -Worksheet $ws @params } - elseif ($chartDef -is [hashtable] -or $chartDef -is[System.Collections.Specialized.OrderedDictionary]) { + elseif ($chartDef -is [hashtable] -or $chartDef -is [System.Collections.Specialized.OrderedDictionary]) { Add-ExcelChart -Worksheet $ws @chartDef } } if ($Calculate) { - try { [OfficeOpenXml.CalculationExtension]::Calculate($ws) } - catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook. $_"} + try { [OfficeOpenXml.CalculationExtension]::Calculate($ws) } + catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook. $_" } } if ($Barchart -or $PieChart -or $LineChart -or $ColumnChart) { - if ($NoHeader) {$FirstDataRow = $startRow} - else {$FirstDataRow = $startRow + 1 } + if ($NoHeader) { $FirstDataRow = $startRow } + else { $FirstDataRow = $startRow + 1 } $range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $startColumn, $FirstDataRow, $lastCol ) - $xCol = $ws.cells[$range] | Where-Object {$_.value -is [string] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 + $xCol = $ws.cells[$range] | Where-Object { $_.value -is [string] } | ForEach-Object { $_.start.column } | Sort-Object | Select-Object -first 1 if (-not $xcol) { - $xcol = $StartColumn - $range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, ($startColumn +1), $FirstDataRow, $lastCol ) + $xcol = $StartColumn + $range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, ($startColumn + 1), $FirstDataRow, $lastCol ) } - $yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] -or $_.Formula } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 - if (-not ($xCol -and $ycol)) { Write-Warning -Message "Can't identify a string column and a number column to use as chart labels and data. "} + $yCol = $ws.cells[$range] | Where-Object { $_.value -is [valueType] -or $_.Formula } | ForEach-Object { $_.start.column } | Sort-Object | Select-Object -first 1 + if (-not ($xCol -and $ycol)) { Write-Warning -Message "Can't identify a string column and a number column to use as chart labels and data. " } else { $params = @{ - XRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $xcol , $lastrow, $xcol) - YRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $ycol , $lastrow, $ycol) - Title = '' - Column = ($lastCol +1) - Width = 800 - } - if ($ShowPercent) {$params["ShowPercent"] = $true} - if ($ShowCategory) {$params["ShowCategory"] = $true} - if ($NoLegend) {$params["NoLegend"] = $true} - if (-not $NoHeader) {$params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value} - if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } - elseif ($Barchart) {$Params["chartType"] = "BarStacked" } - elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } - elseif ($LineChart) {$Params["chartType"] = "Line" } + XRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $xcol , $lastrow, $xcol) + YRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $ycol , $lastrow, $ycol) + Title = '' + Column = ($lastCol + 1) + Width = 800 + } + if ($ShowPercent) { $params["ShowPercent"] = $true } + if ($ShowCategory) { $params["ShowCategory"] = $true } + if ($NoLegend) { $params["NoLegend"] = $true } + if (-not $NoHeader) { $params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value } + if ($ColumnChart) { $Params["chartType"] = "ColumnStacked" } + elseif ($Barchart) { $Params["chartType"] = "BarStacked" } + elseif ($PieChart) { $Params["chartType"] = "PieExploded3D" } + elseif ($LineChart) { $Params["chartType"] = "Line" } Add-ExcelChart -Worksheet $ws @params } @@ -615,35 +621,36 @@ # It now doesn't matter if the conditional formating rules are passed in $conditionalText or $conditional format. # Just one with an alias for compatiblity it will break things for people who are using both at once - foreach ($c in (@() + $ConditionalText + $ConditionalFormat) ) { + foreach ($c in (@() + $ConditionalText + $ConditionalFormat) ) { try { #we can take an object with a .ConditionalType property made by New-ConditionalText or with a .Formatter Property made by New-ConditionalFormattingIconSet or a hash table if ($c.ConditionalType) { - $cfParams = @{RuleType = $c.ConditionalType; ConditionValue = $c.Text ; - BackgroundColor = $c.BackgroundColor; BackgroundPattern = $c.PatternType ; - ForeGroundColor = $c.ConditionalTextColor} - if ($c.Range) {$cfParams.Range = $c.Range} - else {$cfParams.Range = $ws.Dimension.Address } + $cfParams = @{RuleType = $c.ConditionalType; ConditionValue = $c.Text ; + BackgroundColor = $c.BackgroundColor; BackgroundPattern = $c.PatternType ; + ForeGroundColor = $c.ConditionalTextColor + } + if ($c.Range) { $cfParams.Range = $c.Range } + else { $cfParams.Range = $ws.Dimension.Address } Add-ConditionalFormatting -Worksheet $ws @cfParams Write-Verbose -Message "Added conditional formatting to range $($c.range)" } - elseif ($c.formatter) { + elseif ($c.formatter) { switch ($c.formatter) { - "ThreeIconSet" {Add-ConditionalFormatting -Worksheet $ws -ThreeIconsSet $c.IconType -range $c.range -reverse:$c.reverse } - "FourIconSet" {Add-ConditionalFormatting -Worksheet $ws -FourIconsSet $c.IconType -range $c.range -reverse:$c.reverse } - "FiveIconSet" {Add-ConditionalFormatting -Worksheet $ws -FiveIconsSet $c.IconType -range $c.range -reverse:$c.reverse } + "ThreeIconSet" { Add-ConditionalFormatting -Worksheet $ws -ThreeIconsSet $c.IconType -range $c.range -reverse:$c.reverse } + "FourIconSet" { Add-ConditionalFormatting -Worksheet $ws -FourIconsSet $c.IconType -range $c.range -reverse:$c.reverse } + "FiveIconSet" { Add-ConditionalFormatting -Worksheet $ws -FiveIconsSet $c.IconType -range $c.range -reverse:$c.reverse } } Write-Verbose -Message "Added conditional formatting to range $($c.range)" } - elseif ($c -is [hashtable] -or $c -is[System.Collections.Specialized.OrderedDictionary]) { - if (-not $c.Range -or $c.Address) {$c.Address = $ws.Dimension.Address } + elseif ($c -is [hashtable] -or $c -is [System.Collections.Specialized.OrderedDictionary]) { + if (-not $c.Range -or $c.Address) { $c.Address = $ws.Dimension.Address } Add-ConditionalFormatting -Worksheet $ws @c } } - catch {throw "Error applying conditional formatting to worksheet $_"} + catch { throw "Error applying conditional formatting to worksheet $_" } } foreach ($s in $Style) { - if (-not $s.Range) {$s["Range"] = $ws.Dimension.Address } + if (-not $s.Range) { $s["Range"] = $ws.Dimension.Address } Set-ExcelRange -Worksheet $ws @s } if ($CellStyleSB) { @@ -652,7 +659,7 @@ $LastColumn = $ws.Dimension.Address -replace "^.*:(\w*)\d+$" , '$1' & $CellStyleSB $ws $TotalRows $LastColumn } - catch {Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorksheetName': $_"} + catch { Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorksheetName': $_" } } #Can only add password, may want to support -password $Null removing password. @@ -661,32 +668,18 @@ $ws.Protection.SetPassword($Password) Write-Verbose -Message 'Set password on workbook' } - catch {throw "Failed setting password for worksheet '$WorksheetName': $_"} + catch { throw "Failed setting password for worksheet '$WorksheetName': $_" } } - if ($PassThru) { $pkg } + if ($PassThru) { $pkg } else { - if ($ReturnRange) {$dataRange } + if ($ReturnRange) { $dataRange } - if ($Password) { $pkg.Save($Password) } - else { $pkg.Save() } + if ($Password) { $pkg.Save($Password) } + else { $pkg.Save() } Write-Verbose -Message "Saved workbook $($pkg.File)" if ($ReZip) { - Write-Verbose -Message "Re-Zipping $($pkg.file) using .NET ZIP library" - try { - Add-Type -AssemblyName 'System.IO.Compression.Filesystem' -ErrorAction stop - } - catch { - Write-Error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+" - continue - } - try { - $TempZipPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName()) - $null = [io.compression.zipfile]::ExtractToDirectory($pkg.File, $TempZipPath) - Remove-Item $pkg.File -Force - $null = [io.compression.zipfile]::CreateFromDirectory($TempZipPath, $pkg.File) - } - catch {throw "Error resizipping $path : $_"} + Invoke-ExcelReZipFile -ExcelPackage $pkg } $pkg.Dispose()