From 4616112aee78e1b674edbc0ac55164cb8605c45c Mon Sep 17 00:00:00 2001 From: dfinke Date: Thu, 23 Nov 2017 12:30:35 -0500 Subject: [PATCH 01/43] Commented out last line --- ConvertExcelToImageFile.ps1 | 54 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/ConvertExcelToImageFile.ps1 b/ConvertExcelToImageFile.ps1 index be1ad54..6381759 100644 --- a/ConvertExcelToImageFile.ps1 +++ b/ConvertExcelToImageFile.ps1 @@ -1,15 +1,15 @@ Function Convert-XlRangeToImage { <# - .Synopsis + .Synopsis Gets the specified part of an Excel file and exports it as an image - .Description + .Description Excel allows charts to be exported directly to a file, but can't do this with the rest of a sheet. To work round this this function * Opens a copy of Excel and loads a file * Selects a worksheet and then a range of cells in that worksheet * Copies the select to the clipboard - * Saves the clipboard contents as an image file (it will save as .JPG unless the file name ends .BMP or .PNG) - * Copies a single cell to the clipboard (to prevent the "you have put a lot in the clipboard" message appearing) - * Closes Excel + * Saves the clipboard contents as an image file (it will save as .JPG unless the file name ends .BMP or .PNG) + * Copies a single cell to the clipboard (to prevent the "you have put a lot in the clipboard" message appearing) + * Closes Excel #> Param ( #Path to the Excel file @@ -20,41 +20,41 @@ Param ( #Range of cells within the sheet, e.g "A1:Z99" [parameter(Mandatory=$true)] $range, - #A bmp, png or jpg file where the result will be saved - $destination = "$pwd\temp.png", - #If specified opens the image in the default viewer. + #A bmp, png or jpg file where the result will be saved + $destination = "$pwd\temp.png", + #If specified opens the image in the default viewer. [switch]$show ) $extension = $destination -replace '^.*\.(\w+)$' ,'$1' if ($extension -in @('JPEG','BMP','PNG')) { $Format = [system.Drawing.Imaging.ImageFormat]$extension - } #if we don't recognise the extension OR if it is JPG with an E, use JPEG format - else { $Format = [system.Drawing.Imaging.ImageFormat]::Jpeg} - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Starting Excel" + } #if we don't recognise the extension OR if it is JPG with an E, use JPEG format + else { $Format = [system.Drawing.Imaging.ImageFormat]::Jpeg} + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Starting Excel" $xlApp = New-Object -ComObject "Excel.Application" - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Opening Workbook and copying data" + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Opening Workbook and copying data" $xlWbk = $xlApp.Workbooks.Open($Path) - $xlWbk.Worksheets($workSheetname).Select() + $xlWbk.Worksheets($workSheetname).Select() $xlWbk.ActiveSheet.Range($range).Select() | Out-Null $xlApp.Selection.Copy() | Out-Null - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Saving copied data" - # Get-Clipboard came in with PS5. Older versions can use [System.Windows.Clipboard] but it is ugly. - $image = Get-Clipboard -Format Image - $image.Save($destination, $Format) - Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Closing Excel" - $xlWbk.ActiveSheet.Range("a1").Select() | Out-Null - $xlApp.Selection.Copy() | Out-Null + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Saving copied data" + # Get-Clipboard came in with PS5. Older versions can use [System.Windows.Clipboard] but it is ugly. + $image = Get-Clipboard -Format Image + $image.Save($destination, $Format) + Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Status "Closing Excel" + $xlWbk.ActiveSheet.Range("a1").Select() | Out-Null + $xlApp.Selection.Copy() | Out-Null $xlApp.Quit() Write-Progress -Activity "Exporting $range of $workSheetname in $Path" -Completed if ($show) {Start-Process -FilePath $destination} - else {Get-Item -Path $destination} + else {Get-Item -Path $destination} } <# -del demo*.xlsx +del demo*.xlsx $workSheetname = 'Processes' $Path = "$pwd\demo.xlsx" -$myData = Get-Process | Select-Object -Property Name,WS,CPU,Description,company,startTime +$myData = Get-Process | Select-Object -Property Name,WS,CPU,Description,company,startTime $excelPackage = $myData | Export-Excel -KillExcel -Path $Path -WorkSheetname $workSheetname -ClearSheet -AutoSize -AutoFilter -BoldTopRow -FreezeTopRow -PassThru $workSheet = $excelPackage.Workbook.Worksheets[$workSheetname] @@ -63,12 +63,12 @@ Set-Format -WorkSheet $workSheet -Range "b:b" -NumberFormat Set-Format -WorkSheet $workSheet -Range "C:C" -NumberFormat "#,##0.00" -AutoFit Set-Format -WorkSheet $workSheet -Range "F:F" -NumberFormat "dd MMMM HH:mm:ss" -AutoFit Add-ConditionalFormatting -WorkSheet $workSheet -Range "c2:c1000" -DataBarColor Blue -Add-ConditionalFormatting -WorkSheet $workSheet -Range "b2:B1000" -RuleType GreaterThan -ConditionValue '104857600' -ForeGroundColor "Red" -Bold +Add-ConditionalFormatting -WorkSheet $workSheet -Range "b2:B1000" -RuleType GreaterThan -ConditionValue '104857600' -ForeGroundColor "Red" -Bold -Export-Excel -ExcelPackage $excelPackage -WorkSheetname $workSheetname +Export-Excel -ExcelPackage $excelPackage -WorkSheetname $workSheetname -Convert-XlRangeToImage -Path $Path -workSheetname $workSheetname -range $range -destination "$pwd\temp.png" -show +Convert-XlRangeToImage -Path $Path -workSheetname $workSheetname -range $range -destination "$pwd\temp.png" -show #> -Convert-XlRangeToImage -Path $Path -workSheetname $workSheetname -range $range -destination "$pwd\temp.png" -show \ No newline at end of file +#Convert-XlRangeToImage -Path $Path -workSheetname $workSheetname -range $range -destination "$pwd\temp.png" -show \ No newline at end of file From df3702a09c7ae0c684d3ba4dd6ce8e7e8b85ee1e Mon Sep 17 00:00:00 2001 From: dfinke Date: Thu, 23 Nov 2017 12:30:51 -0500 Subject: [PATCH 02/43] Updated readme --- GetExcelTable.ps1 | 104 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 14 +++++++ 2 files changed, 118 insertions(+) create mode 100644 GetExcelTable.ps1 diff --git a/GetExcelTable.ps1 b/GetExcelTable.ps1 new file mode 100644 index 0000000..e1aacc0 --- /dev/null +++ b/GetExcelTable.ps1 @@ -0,0 +1,104 @@ +Function Get-ExcelTableName { + Param ( + $Path, + $WorksheetName + ) + + $Path = (Resolve-Path $Path).ProviderPath + $Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite' + + $Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream + + if ($WorksheetName) { + $Worksheet = $Excel.Workbook.Worksheets[$WorkSheetName] + } else { + $Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1 + } + + foreach($TableName in $Worksheet.Tables.Name) { + [PSCustomObject][Ordered]@{ + WorksheetName=$Worksheet.Name + TableName=$TableName + } + } + + $Stream.Close() + $Stream.Dispose() + $Excel.Dispose() + $Excel = $null +} + +Function Get-ExcelTable { + Param ( + $Path, + $TableName, + $WorksheetName + ) + + $Path = (Resolve-Path $Path).ProviderPath + $Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite' + + $Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream + + if ($WorksheetName) { + $Worksheet = $Excel.Workbook.Worksheets[$WorkSheetName] + } else { + $Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1 + } + + if($TableName) { + $Table = $Worksheet.Tables[$TableName] + } else { + $Table = $Worksheet.Tables | Select-Object -First 1 + } + + $rowCount = $Table.Address.Rows + $colCount = $Table.Address.Columns + + $digits = "0123456789".ToCharArray() + + $start, $end=$Table.Address.Address.Split(':') + + $pos=$start.IndexOfAny($digits) + [int]$startCol=ConvertFrom-ExcelColumnName $start.Substring(0,$pos) + [int]$startRow=$start.Substring($pos) + + $propertyNames = for($col=$startCol; $col -lt ($startCol+$colCount); $col+= 1) { + $Worksheet.Cells[$startRow, $col].value + } + + $startRow++ + for($row=$startRow; $row -lt ($startRow+$rowCount); $row += 1) { + $nr=[ordered]@{} + $c=0 + for($col=$startCol; $col -lt ($startCol+$colCount); $col+= 1) { + $nr.($propertyNames[$c]) = $Worksheet.Cells[$row, $col].value + $c++ + } + [pscustomobject]$nr + } + + $Stream.Close() + $Stream.Dispose() + $Excel.Dispose() + $Excel = $null +} + +function ConvertFrom-ExcelColumnName { + param($columnName) + + $sum=0 + $columnName.ToCharArray() | + ForEach { + $sum*=26 + $sum+=[char]$_.tostring().toupper()-[char]'A'+1 + } + $sum +} + +cls + +ipmo .\ImportExcel.psd1 -Force + +#Get-ExcelTableName .\testTable.xlsx | Get-ExcelTable .\testTable.xlsx +Get-ExcelTable .\testTable.xlsx Table3 \ No newline at end of file diff --git a/README.md b/README.md index 03e0c1c..9860f7a 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,20 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi ``` # What's new + +#### 11/23/2017 +More great additions and thanks to [James O'Neill](https://twitter.com/jamesoneill) + +* Added `Convert-XlRangeToImage` Gets the specified part of an Excel file and exports it as an image +* Fixed a typo in the message at line 373. +* Now catch an attempt to both clear the sheet and append to it. +* Fixed some issues when appending to sheets where the header isn't in row 1 or the data doesn't start in column 1. +* Added support for more settings when creating a pivot chart. +* Corrected a typo PivotTableName was PivtoTableName in definition of New-PivotTableDefinition +* Add-ConditionalFormat and Set-Format added to the parameters so each has the choice of working more like the other. +* Added Set-Row and Set-Column - fill a formula down or across. +* Added Send-SQLDataToExcel. Insert a rowset and then call Export-Excel for ranges, charts, pivots etc + #### 10/30/2017 Huge thanks to [James O'Neill](https://twitter.com/jamesoneill). PowerShell aficionado. He always brings a flare when working with PowerShell. This is no exception. From 182e4313b38641026d4aa822ada6521c359c9333 Mon Sep 17 00:00:00 2001 From: dfinke Date: Thu, 23 Nov 2017 12:31:10 -0500 Subject: [PATCH 03/43] bump version --- ImportExcel.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 88e9c1f..9799980 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.4' +ModuleVersion = '4.0.5' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' From 360c497beef871861235fa8c297976303479e844 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 10:07:23 -0500 Subject: [PATCH 04/43] Added new ps1 files --- InstallModule.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InstallModule.ps1 b/InstallModule.ps1 index e4431ff..82304ff 100644 --- a/InstallModule.ps1 +++ b/InstallModule.ps1 @@ -49,7 +49,10 @@ Begin { 'Open-ExcelPackage.ps1', 'Pivot.ps1', 'Plot.ps1', + 'Send-SQLDataToExcel.ps1', 'Set-CellStyle.ps1', + 'Set-Column.ps1', + 'Set-Row.ps1', 'SetFormat.ps1', 'TrackingUtils.ps1', 'Update-FirstObjectProperties.ps1' From 1df63e32069a239651360261b31b283ecb9f91c9 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 10:07:39 -0500 Subject: [PATCH 05/43] added xlrange to image example --- Examples/XlRangeToImage/XlRangeToImage.ps1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Examples/XlRangeToImage/XlRangeToImage.ps1 diff --git a/Examples/XlRangeToImage/XlRangeToImage.ps1 b/Examples/XlRangeToImage/XlRangeToImage.ps1 new file mode 100644 index 0000000..40a3b51 --- /dev/null +++ b/Examples/XlRangeToImage/XlRangeToImage.ps1 @@ -0,0 +1,19 @@ +ipmo .\ImportExcel.psd1 -Force + +. .\ConvertExcelToImageFile.ps1 + +$xlFileName = "C:\Temp\testPNG.xlsx" + +rm C:\Temp\testPNG.xlsx -ErrorAction Ignore + +@" +Region,Item,Cost +North,Pear,1 +South,Apple,2 +East,Grapes,3 +West,Berry,4 +"@ | ConvertFrom-Csv | + Export-Excel $xlFileName ` + -ConditionalText (New-ConditionalText Apple), (New-ConditionalText Berry -ConditionalTextColor White -BackgroundColor Purple) + +Convert-XlRangeToImage -Path $xlFileName -workSheetname sheet1 -range A1:C5 -Show \ No newline at end of file From 0f9bf07d30dfc8d0193e72e017d278944e00c384 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 10:16:13 -0500 Subject: [PATCH 06/43] Bumped version. Added url for how to videos --- ImportExcel.psd1 | 4 ++-- RemoveWorksheet.ps1 | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 RemoveWorksheet.ps1 diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 9799980..3c26ce4 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.5' +ModuleVersion = '4.0.6' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' @@ -19,7 +19,7 @@ CompanyName = 'Doug Finke' Copyright = 'c 2015 All rights reserved.' # Description of the functionality provided by this module -Description = 'PowerShell module to import/export Excel spreadsheets, without Excel' + Description = 'PowerShell module to import/export Excel spreadsheets, without Excel. Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq' # Minimum version of the Windows PowerShell engine required by this module # PowerShellVersion = '' diff --git a/RemoveWorksheet.ps1 b/RemoveWorksheet.ps1 new file mode 100644 index 0000000..0b2c06b --- /dev/null +++ b/RemoveWorksheet.ps1 @@ -0,0 +1,35 @@ +Function Remove-WorkSheet { + Param ( + $Path, + $WorksheetName + ) + + $Path = (Resolve-Path $Path).ProviderPath + + $Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage $Path + + $workSheet = $Excel.Workbook.Worksheets[$WorkSheetName] + + if($workSheet) { + if($Excel.Workbook.Worksheets.Count -gt 1) { + $Excel.Workbook.Worksheets.Delete($workSheet) + } else { + throw "Cannot delete $WorksheetName. A workbook must contain at least one visible worksheet" + } + + } else { + throw "$WorksheetName not found" + } + + $Excel.Save() + $Excel.Dispose() +} + +cls + +ipmo .\ImportExcel.psd1 -Force + +$names = Get-ExcelSheetInfo C:\Temp\testDelete.xlsx +$names | % { Remove-WorkSheet C:\Temp\testDelete.xlsx $_.Name} + +##Remove-WorkSheet C:\Temp\testDelete.xlsx sheet6 \ No newline at end of file From 21d9c56854a7bcaeb84b853076fa6f61c7d6b869 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 14:13:30 -0500 Subject: [PATCH 07/43] updated to image example --- Examples/XlRangeToImage/XlRangeToImage.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Examples/XlRangeToImage/XlRangeToImage.ps1 b/Examples/XlRangeToImage/XlRangeToImage.ps1 index 40a3b51..c7afbca 100644 --- a/Examples/XlRangeToImage/XlRangeToImage.ps1 +++ b/Examples/XlRangeToImage/XlRangeToImage.ps1 @@ -6,14 +6,18 @@ $xlFileName = "C:\Temp\testPNG.xlsx" rm C:\Temp\testPNG.xlsx -ErrorAction Ignore -@" +$range = @" Region,Item,Cost North,Pear,1 South,Apple,2 East,Grapes,3 West,Berry,4 +North,Pear,1 +South,Apple,2 +East,Grapes,3 +West,Berry,4 "@ | ConvertFrom-Csv | - Export-Excel $xlFileName ` + Export-Excel $xlFileName -ReturnRange ` -ConditionalText (New-ConditionalText Apple), (New-ConditionalText Berry -ConditionalTextColor White -BackgroundColor Purple) -Convert-XlRangeToImage -Path $xlFileName -workSheetname sheet1 -range A1:C5 -Show \ No newline at end of file +Convert-XlRangeToImage -Path $xlFileName -workSheetname sheet1 -range $range -Show From 28ddd7de1360aab827bb497efc01242b1ddf4638 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 14:13:51 -0500 Subject: [PATCH 08/43] Added ReturnRange param --- Export-Excel.ps1 | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 0090255..14d8d97 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -397,7 +397,8 @@ [ScriptBlock]$CellStyleSB, [Parameter(ParameterSetName = 'Now')] # [Parameter(ParameterSetName = 'TableNow')] - [Switch]$Now + [Switch]$Now, + [Switch]$ReturnRange ) Begin { @@ -526,7 +527,7 @@ } Try { $script:Header = $null - if ($append -and $clearSheet) {throw "You can't use -Append AND -ClearSheet."} + if ($append -and $clearSheet) {throw "You can't use -Append AND -ClearSheet."} if ($KillExcel) { Stop-ExcelProcess } @@ -553,7 +554,7 @@ $pkg = New-Object OfficeOpenXml.ExcelPackage $Path } - + [OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber foreach ($format in $ConditionalFormat ) { $target = "Add$($format.Formatter)" @@ -566,15 +567,15 @@ #if there is a title or anything else above the header row, specifying StartRow will skip it. if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1","$StartRow"} #$script:Header = $ws.Cells[$headerrange].Value - #using a slightly odd syntax otherwise header ends up as a 2D array - $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } + #using a slightly odd syntax otherwise header ends up as a 2D array + $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } $row = $ws.Dimension.Rows Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + "Start row $row") } elseif($Title) { #Can only add a title if not appending $Row = $StartRow Add-Title - $Row ++ ; $startRow ++ + $Row ++ ; $startRow ++ } else { $Row = $StartRow @@ -657,9 +658,9 @@ $headerRange = $ws.Dimension.Address -replace "\d+$","1" #if there is a title or anything else above the header row, specifying StartRow will skip it. if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1","$StartRow"} - #using a slightly odd syntax otherwise header ends up as a 2D array - $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } - } + #using a slightly odd syntax otherwise header ends up as a 2D array + $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } + } $totalRows = $ws.Dimension.End.Row $totalColumns = $ws.Dimension.Columns foreach ($c in 0..($totalColumns - 1)) { @@ -962,6 +963,10 @@ $pkg } else { + if($ReturnRange) { + $ws.Dimension.Address + } + $pkg.Save() $pkg.Dispose() From 0862fcdc8ce238f9e5f949eaf0c390d4d41b3e13 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 14:14:07 -0500 Subject: [PATCH 09/43] Changed description --- ImportExcel.psd1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 3c26ce4..28fc9e5 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.6' +ModuleVersion = '4.0.7' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' @@ -19,7 +19,10 @@ CompanyName = 'Doug Finke' Copyright = 'c 2015 All rights reserved.' # Description of the functionality provided by this module - Description = 'PowerShell module to import/export Excel spreadsheets, without Excel. Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq' + Description = @' +PowerShell module to import/export Excel spreadsheets, without Excel. +Check out the How To Videos https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq +'@ # Minimum version of the Windows PowerShell engine required by this module # PowerShellVersion = '' From a4169a42f1b41921cb476a8bcddd6140ab501a82 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 24 Nov 2017 14:26:11 -0500 Subject: [PATCH 10/43] Added YouTube link --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 9860f7a..1f32baa 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This PowerShell Module allows you to read and write Excel files without installi ![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/testimonial.png) +# How to Vidoes +* [PowerShell Excel Module - ImportExcel](https://www.youtube.com/watch?v=U3Ne_yX4tYo&list=PL5uoqS92stXioZw-u-ze_NtvSo0k0K0kq) + Installation - #### [PowerShell V5](https://www.microsoft.com/en-us/download/details.aspx?id=50395) and Later From 9b578538813795f45865985d7f5852eb8172e26a Mon Sep 17 00:00:00 2001 From: dfinke Date: Wed, 29 Nov 2017 18:19:51 -0500 Subject: [PATCH 11/43] fixed --- ImportExcel.psd1 | 2 +- ImportExcel.psm1 | 1 + InstallModule.ps1 | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 28fc9e5..cb623e8 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.7' +ModuleVersion = '4.0.8' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index 250a659..69f8eb2 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -31,6 +31,7 @@ . $PSScriptRoot\SetFormat.ps1 . $PSScriptRoot\TrackingUtils.ps1 . $PSScriptRoot\Update-FirstObjectProperties.ps1 +. $PSScriptRoot\ConvertExcelToImageFile.ps1 New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" diff --git a/InstallModule.ps1 b/InstallModule.ps1 index 82304ff..6877963 100644 --- a/InstallModule.ps1 +++ b/InstallModule.ps1 @@ -27,6 +27,7 @@ Begin { 'ColorCompletion.ps1', 'ConvertFromExcelData.ps1', 'ConvertFromExcelToSQLInsert.ps1', + 'ConvertExcelToImageFile.ps1', 'ConvertToExcelXlsx.ps1', 'Copy-ExcelWorkSheet.ps1', 'Export-Charts.ps1', From dc4b66fffee9f1d13b22f1b82fd1dc562893bed9 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 2 Dec 2017 12:38:24 -0500 Subject: [PATCH 12/43] Two things, checks for $Chart.DataLabel and if the directory for the xlsx path does not exist, it creates it --- Export-Excel.ps1 | 174 +++++++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 81 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 14d8d97..e2b0bbe 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -321,13 +321,13 @@ [CmdletBinding(DefaultParameterSetName = 'Default')] Param( - [Parameter(ParameterSetName="Default",Position=0)] - [Parameter(ParameterSetName="Table" ,Position=0)] + [Parameter(ParameterSetName = "Default", Position = 0)] + [Parameter(ParameterSetName = "Table" , Position = 0)] [String]$Path, - [Parameter(Mandatory=$true,ParameterSetName="PackageDefault")] - [Parameter(Mandatory=$true,ParameterSetName="PackageTable")] + [Parameter(Mandatory = $true, ParameterSetName = "PackageDefault")] + [Parameter(Mandatory = $true, ParameterSetName = "PackageTable")] [OfficeOpenXml.ExcelPackage]$ExcelPackage, - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] $TargetData, [String]$Password, [String]$WorkSheetname = 'Sheet1', @@ -376,8 +376,8 @@ $true } })] - [Parameter(ParameterSetName = 'Table' ,Mandatory = $true)] - [Parameter(ParameterSetName = 'PackageTable' ,Mandatory = $true)] + [Parameter(ParameterSetName = 'Table' , Mandatory = $true)] + [Parameter(ParameterSetName = 'PackageTable' , Mandatory = $true)] [String]$TableName, [Parameter(ParameterSetName = 'Table')] [Parameter(ParameterSetName = 'PackageTable')] @@ -493,7 +493,7 @@ if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) { $ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor) } - elseif ($TitleBackgroundColor) { + elseif ($TitleBackgroundColor) { Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'." } } @@ -542,37 +542,43 @@ } if ($ExcelPackage) { - $pkg = $ExcelPackage - $Path = $pkg.File + $pkg = $ExcelPackage + $Path = $pkg.File } - Else { + Else { $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) - if (Test-Path $Path) { + $targetPath = Split-Path $Path + if (!(Test-Path $targetPath)) { + Write-Debug "Base path $($targetPath) does not exist, creating" + $null = mkdir $targetPath -ErrorAction Ignore + } + elseif (Test-Path $Path) { Write-Debug "Path '$Path' already exists" } $pkg = New-Object OfficeOpenXml.ExcelPackage $Path } - [OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber + [OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber foreach ($format in $ConditionalFormat ) { $target = "Add$($format.Formatter)" $rule = ($ws.ConditionalFormatting).PSObject.Methods[$target].Invoke($format.Range, $format.IconType) $rule.Reverse = $format.Reverse } - if ($append) { - $headerRange = $ws.Dimension.Address -replace "\d+$","1" + if ($append) { + $headerRange = $ws.Dimension.Address -replace "\d+$", "1" #if there is a title or anything else above the header row, specifying StartRow will skip it. - if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1","$StartRow"} + if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"} #$script:Header = $ws.Cells[$headerrange].Value #using a slightly odd syntax otherwise header ends up as a 2D array $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } - $row = $ws.Dimension.Rows + $row = $ws.Dimension.Rows Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + "Start row $row") } - elseif($Title) { #Can only add a title if not appending + elseif ($Title) { + #Can only add a title if not appending $Row = $StartRow Add-Title $Row ++ ; $startRow ++ @@ -587,7 +593,8 @@ $pattern = 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort' } Catch { - if ($AlreadyExists) { #Is this set anywhere ? + if ($AlreadyExists) { + #Is this set anywhere ? throw "Failed exporting worksheet '$WorkSheetname' to '$Path': The worksheet '$WorkSheetname' already exists." } else { @@ -597,76 +604,76 @@ } Process { - if ($TargetData) { - Try { - if ($firstTimeThru) { - $firstTimeThru = $false - $isDataTypeValueType = $TargetData.GetType().name -match $pattern - Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" - } - - if ($isDataTypeValueType) { - $ColumnIndex = $StartColumn - - Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData - - $ColumnIndex += 1 - $Row += 1 - } - else { - #region Add headers - if (-not $script:Header) { - $ColumnIndex = $StartColumn - $script:Header = $TargetData.PSObject.Properties.Name | Where-Object {$_ -notin $ExcludeProperty} - - if ($NoHeader) { - # Don't push the headers to the spread sheet - $Row -= 1 - } - else { - foreach ($Name in $script:Header) { - $ws.Cells[$Row, $ColumnIndex].Value = $Name - Write-Verbose "Cell '$Row`:$ColumnIndex' add header '$Name'" - $ColumnIndex += 1 - } - } + if ($TargetData) { + Try { + if ($firstTimeThru) { + $firstTimeThru = $false + $isDataTypeValueType = $TargetData.GetType().name -match $pattern + Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" } - #endregion - $Row += 1 - $ColumnIndex = $StartColumn + if ($isDataTypeValueType) { + $ColumnIndex = $StartColumn - foreach ($Name in $script:Header) { - #region Add non header values - Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData.$Name + Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData $ColumnIndex += 1 + $Row += 1 + } + else { + #region Add headers + if (-not $script:Header) { + $ColumnIndex = $StartColumn + $script:Header = $TargetData.PSObject.Properties.Name | Where-Object {$_ -notin $ExcludeProperty} + + if ($NoHeader) { + # Don't push the headers to the spread sheet + $Row -= 1 + } + else { + foreach ($Name in $script:Header) { + $ws.Cells[$Row, $ColumnIndex].Value = $Name + Write-Verbose "Cell '$Row`:$ColumnIndex' add header '$Name'" + $ColumnIndex += 1 + } + } + } #endregion + + $Row += 1 + $ColumnIndex = $StartColumn + + foreach ($Name in $script:Header) { + #region Add non header values + Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData.$Name + + $ColumnIndex += 1 + #endregion + } } } + Catch { + throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_" + } } - Catch { - throw "Failed exporting worksheet '$WorkSheetname' to '$Path': $_" - } - } } End { Try { if ($AutoNameRange) { if (-not $script:header) { - $headerRange = $ws.Dimension.Address -replace "\d+$","1" - #if there is a title or anything else above the header row, specifying StartRow will skip it. - if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1","$StartRow"} - #using a slightly odd syntax otherwise header ends up as a 2D array - $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } + $headerRange = $ws.Dimension.Address -replace "\d+$", "1" + #if there is a title or anything else above the header row, specifying StartRow will skip it. + if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"} + #using a slightly odd syntax otherwise header ends up as a 2D array + $ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } } $totalRows = $ws.Dimension.End.Row $totalColumns = $ws.Dimension.Columns foreach ($c in 0..($totalColumns - 1)) { $targetRangeName = "$($script:Header[$c])" $targetColumn = $c + $StartColumn - $theCell = $ws.Cells[($startrow+1), $targetColumn, $totalRows , $targetColumn ] + $theCell = $ws.Cells[($startrow + 1), $targetColumn, $totalRows , $targetColumn ] $ws.Names.Add($targetRangeName, $theCell) | Out-Null if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress($targetRangeName)) { @@ -676,7 +683,7 @@ } if ($Title) { - $startAddress = $ws.Dimension.Start.address -replace "$($ws.Dimension.Start.row)`$", "$($ws.Dimension.Start.row + 1)" + $startAddress = $ws.Dimension.Start.address -replace "$($ws.Dimension.Start.row)`$", "$($ws.Dimension.Start.row + 1)" } else { $startAddress = $ws.Dimension.Start.Address @@ -709,7 +716,7 @@ $targetName = $item.Key $pivotTableName = $targetName #+ 'PivotTable' #Make sure the Pivot table sheet doesn't already exist - try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {} + try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {} $wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber $pivotTableDataName = $targetName + 'PivotTableData' @@ -769,19 +776,22 @@ $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) $chart.SetPosition(0, 0, 4, 0) #Changed position to top row, next to a chart which doesn't pivot on columns $chart.SetSize(600, 400) - $chart.DataLabel.ShowCategory = [boolean]$item.value.ShowCategory - $chart.DataLabel.ShowPercent = [boolean]$item.value.ShowPercent - if ([boolean]$item.value.NoLegend) {$chart.Legend.Remove()} - if ($item.value.ChartTitle) {$chart.Title.Text = $item.value.chartTitle} + if ($chart.DataLabel) { + $chart.DataLabel.ShowCategory = [boolean]$item.value.ShowCategory + $chart.DataLabel.ShowPercent = [boolean]$item.value.ShowPercent + } + if ([boolean]$item.value.NoLegend) {$chart.Legend.Remove()} + if ($item.value.ChartTitle) {$chart.Title.Text = $item.value.chartTitle} } } } } - if ($IncludePivotTable -or $IncludePivotChart) { #changed so -includePivotChart Implies -includePivotTable. + if ($IncludePivotTable -or $IncludePivotChart) { + #changed so -includePivotChart Implies -includePivotTable. $pivotTableName = $WorkSheetname + 'PivotTable' #Make sure the Pivot table sheet doesn't already exist - try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {} + try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {} $wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber $wsPivot.View.TabSelected = $true @@ -822,9 +832,11 @@ if ($IncludePivotChart) { $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) - $chart.DataLabel.ShowCategory = $ShowCategory - $chart.DataLabel.ShowPercent = $ShowPercent - $chart.SetPosition(0,26,2,26) # if Pivot table is rows+data only it will be 2 columns wide if has pivot columns we don't know how wide it will be + if ($chart.DataLabel) { + $chart.DataLabel.ShowCategory = $ShowCategory + $chart.DataLabel.ShowPercent = $ShowPercent + } + $chart.SetPosition(0, 26, 2, 26) # if Pivot table is rows+data only it will be 2 columns wide if has pivot columns we don't know how wide it will be if ($NoLegend) { $chart.Legend.Remove() } @@ -963,7 +975,7 @@ $pkg } else { - if($ReturnRange) { + if ($ReturnRange) { $ws.Dimension.Address } @@ -1001,5 +1013,5 @@ function New-PivotTableDefinition { $parameters = @{} + $PSBoundParameters $parameters.Remove('PivotTableName') - @{$PivotTableName=$parameters} + @{$PivotTableName = $parameters} } \ No newline at end of file From c598bbc2a58c9ccf2cee33ec3960addf9d7513ef Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 2 Dec 2017 18:06:40 -0500 Subject: [PATCH 13/43] updated --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1f32baa..fb1a242 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +#### 12/2017 +* Fixed when using certain a `ChartType` for the Pivot Table Chart, would throw an error +* Fixed - when you specify a file, and the directory does not exit, it now creates it + #### 11/23/2017 More great additions and thanks to [James O'Neill](https://twitter.com/jamesoneill) From 458a08dab04cfc5a89700e5fa9eb607c8d4aeb25 Mon Sep 17 00:00:00 2001 From: dfinke Date: Thu, 21 Dec 2017 09:33:14 -0500 Subject: [PATCH 14/43] Add example to set the background color of a cloumn --- .../SetColumnBackgroundColor.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 diff --git a/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 b/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 new file mode 100644 index 0000000..67d3738 --- /dev/null +++ b/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 @@ -0,0 +1,9 @@ + +$p = ps | select Company, Handles | Export-Excel c:\temp\testBackgroundColor.xlsx -ClearSheet -KillExcel -PassThru + +$ws = $p.Workbook.WorkSheets[1] +$totalRows = $ws.Dimension.Rows + +Set-Format -Address $ws.Cells["B2:B$($totalRows)"] -BackgroundColor LightBlue + +Export-Excel -ExcelPackage $p -show \ No newline at end of file From e8a3d3f3507e4c35038fdc2afeb7e424860dac9f Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 2 Jan 2018 11:32:04 -0500 Subject: [PATCH 15/43] Added -Force to New-Alias --- ImportExcel.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index 69f8eb2..2d7cbbe 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -33,7 +33,7 @@ . $PSScriptRoot\Update-FirstObjectProperties.ps1 . $PSScriptRoot\ConvertExcelToImageFile.ps1 -New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" +New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force if ($PSVersionTable.PSVersion.Major -ge 5) { . $PSScriptRoot\Plot.ps1 From 5e4220bd09277d2f33c9d1e2e9342b6e03a0f8bb Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 2 Jan 2018 16:08:00 -0500 Subject: [PATCH 16/43] Supports excluding Row Grand Totals --- Export-Excel.ps1 | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index e2b0bbe..f35ca21 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -398,7 +398,8 @@ [Parameter(ParameterSetName = 'Now')] # [Parameter(ParameterSetName = 'TableNow')] [Switch]$Now, - [Switch]$ReturnRange + [Switch]$ReturnRange, + [Switch]$NoTotalsInPivot ) Begin { @@ -784,6 +785,10 @@ if ($item.value.ChartTitle) {$chart.Title.Text = $item.value.chartTitle} } } + + if($item.Value.NoTotalsInPivot) { + $pivotTable.RowGrandTotals = $false + } } } @@ -829,6 +834,10 @@ $pivotTable.DataOnRows = $false } } + + if($NoTotalsInPivot) { + $pivotTable.RowGrandTotals = $false + } if ($IncludePivotChart) { $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) @@ -1007,7 +1016,8 @@ function New-PivotTableDefinition { [Switch]$NoLegend, [Switch]$ShowCategory, [Switch]$ShowPercent, - [String]$ChartTitle + [String]$ChartTitle, + [Switch]$NoTotalsInPivot ) $parameters = @{} + $PSBoundParameters From 7de56c803c0a5653b2ca0fefc074734b3730c1e8 Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 2 Jan 2018 16:08:38 -0500 Subject: [PATCH 17/43] bump version --- ImportExcel.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index cb623e8..ac256a9 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.8' +ModuleVersion = '4.0.9' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' From 5969bba1692c969359b5718c8d80f2093e479ddc Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 2 Jan 2018 16:37:44 -0500 Subject: [PATCH 18/43] updated and answers issue #273 --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fb1a242..6e953d5 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,17 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new -#### 12/2017 +#### 1/1/2018 +* Added switch `[Switch]$NoTotalsInPivot`. Allows hiding of the row totals in the pivot table. +Thanks you to [jameseholt](https://github.com/jameseholt) for the request. + +```powershell + get-process | where Company | select Company, Handles, WorkingSet | + export-excel C:\temp\testColumnGrand.xlsx ` + -Show -ClearSheet -KillExcel ` + -IncludePivotTable -PivotRows Company -PivotData @{"Handles"="average"} -NoTotalsInPivot +``` + * Fixed when using certain a `ChartType` for the Pivot Table Chart, would throw an error * Fixed - when you specify a file, and the directory does not exit, it now creates it From 2f70cd88e8b82fe105f3c0caba6de91bb36906ae Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 9 Jan 2018 19:39:13 -0500 Subject: [PATCH 19/43] Allow xlsm files to be read --- ImportExcel.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index 2d7cbbe..777f5d1 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -238,7 +238,7 @@ Function Import-Excel { Param ( [Alias('FullName')] [Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)] - [ValidateScript({(Test-Path -Path $_ -PathType Leaf) -and ($_ -match '.xls$|.xlsx$')})] + [ValidateScript( {(Test-Path -Path $_ -PathType Leaf) -and ($_ -match '.xls$|.xlsx$|.xlsm$')})] [String]$Path, [Alias('Sheet')] [Parameter(Position=1)] From a5b9ddc257386924a2faa27e40fb50e79d304ad9 Mon Sep 17 00:00:00 2001 From: dfinke Date: Fri, 12 Jan 2018 19:32:19 -0500 Subject: [PATCH 20/43] Fix 276 and 262 --- Set-Column.ps1 | 64 ++++++++++++------------- Set-Row.ps1 | 82 +++++++++++++++---------------- SetFormat.ps1 | 12 ++--- formatting.ps1 | 128 ++++++++++++++++++++++++------------------------- 4 files changed, 143 insertions(+), 143 deletions(-) diff --git a/Set-Column.ps1 b/Set-Column.ps1 index f195cf8..32da5bb 100644 --- a/Set-Column.ps1 +++ b/Set-Column.ps1 @@ -4,20 +4,20 @@ Adds a column to the existing data area in an Excel sheet, fills values and sets formatting .DESCRIPTION Set-Column takes a value which is either string containing a value or formula or a scriptblock - which evaluates to a string, and optionally a column number and fills that value down the column. - A column name can be specified and the new column can be made a named range. - The column can be formatted. + which evaluates to a string, and optionally a column number and fills that value down the column. + A column name can be specified and the new column can be made a named range. + The column can be formatted. .Example C:> Set-Column -Worksheet $ws -Heading "WinsToFastLaps" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange Here $WS already contains a worksheet which contains counts of races won and fastest laps recorded by racing drivers (in columns C and E) - Set-Column specifies that Column 7 should have a heading of "WinsToFastLaps" and the data cells should contain =E2/C2 , =E3/C3 - the data celss should become a named range, which will also be "WinsToFastLaps" the column width will be set automatically - + Set-Column specifies that Column 7 should have a heading of "WinsToFastLaps" and the data cells should contain =E2/C2 , =E3/C3 + the data celss should become a named range, which will also be "WinsToFastLaps" the column width will be set automatically + #> [cmdletbinding()] Param ( [Parameter(ParameterSetName="Package",Mandatory=$true)] - [OfficeOpenXml.ExcelPackage]$ExcelPackage, + [OfficeOpenXml.ExcelPackage]$ExcelPackage, #Sheet to update [Parameter(ParameterSetName="Package")] $Worksheetname = "Sheet1", @@ -27,7 +27,7 @@ #Column to fill down - first column is 1. 0 will be interpreted as first unused column $Column = 0 , [Int]$StartRow , - #value, formula or script block for to fill in. Script block can use $row, $column [number], $ColumnName [letter(s)], $startRow, $startColumn, $endRow, $endColumn + #value, formula or script block for to fill in. Script block can use $row, $column [number], $ColumnName [letter(s)], $startRow, $startColumn, $endRow, $endColumn [parameter(Mandatory=$true)] $Value , #Optional column heading @@ -80,41 +80,41 @@ [Switch]$AutoNameRange, [switch]$PassThru ) - #if we were passed a package object and a worksheet name , get the worksheet. - if ($ExcelPackage) {$Worksheet = $ExcelPackage.Workbook.Worksheets[$Worksheetname] } - - #In a script block to build a formula, we may want any of corners or the columnname, + #if we were passed a package object and a worksheet name , get the worksheet. + if ($ExcelPackage) {$Worksheet = $ExcelPackage.Workbook.Worksheets[$Worksheetname] } + + #In a script block to build a formula, we may want any of corners or the columnname, #if column and startrow aren't specified, assume first unused column, and first row if (-not $StartRow) {$startRow = $Worksheet.Dimension.Start.Row } - $StartColumn = $Worksheet.Dimension.Start.Column + $StartColumn = $Worksheet.Dimension.Start.Column $endColumn = $Worksheet.Dimension.End.Column - $endRow = $Worksheet.Dimension.End.Row - if ($Column -lt 2 ) {$Column = $endColumn + 1 } + $endRow = $Worksheet.Dimension.End.Row + if ($Column -lt 2 ) {$Column = $endColumn + 1 } $ColumnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1","" - Write-Verbose -Message "Updating Column $ColumnName" + Write-Verbose -Message "Updating Column $ColumnName" #If there is a heading, insert it and use it as the name for a range (if we're creating one) if ($Heading) { - $Worksheet.Cells[$StartRow, $Column].Value = $heading - $startRow ++ + $Worksheet.Cells[$StartRow, $Column].Value = $heading + $startRow ++ if ($AutoNameRange) { $Worksheet.Names.Add( $heading, ($Worksheet.Cells[$startrow, $Column, $endRow, $Column]) ) | Out-Null } - } + } #Fill in the data if ($value) { foreach ($row in ($StartRow.. $endRow)) { - if ($Value -is [scriptblock]) { #re-create the script block otherwise variables from this function are out of scope. - $cellData = & ([scriptblock]::create( $Value )) - Write-Verbose -Message $cellData + if ($Value -is [scriptblock]) { #re-create the script block otherwise variables from this function are out of scope. + $cellData = & ([scriptblock]::create( $Value )) + Write-Verbose -Message $cellData } - else { $cellData = $Value} + else { $cellData = $Value} if ($cellData -match "^=") { $Worksheet.Cells[$Row, $Column].Formula = $cellData } - else { $Worksheet.Cells[$Row, $Column].Value = $cellData } - if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } - }} - #region Apply formatting + else { $Worksheet.Cells[$Row, $Column].Value = $cellData } + if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } + }} + #region Apply formatting if ($Underline) { $Worksheet.Column( $Column).Style.Font.UnderLine = $true $Worksheet.Column( $Column).Style.Font.UnderLineType = $UnderLineType - } + } if ($Bold) { $Worksheet.Column( $Column).Style.Font.Bold = $true } if ($Italic) { $Worksheet.Column( $Column).Style.Font.Italic = $true } if ($StrikeThru) { $Worksheet.Column( $Column).Style.Font.Strike = $true } @@ -123,9 +123,9 @@ if ($TextRotation) { $Worksheet.Column( $Column).Style.TextRotation = $TextRotation } if ($WrapText) { $Worksheet.Column( $Column).Style.WrapText = $true } if ($HorizontalAlignment) { $Worksheet.Column( $Column).Style.HorizontalAlignment = $HorizontalAlignment} - if ($VerticalAlignment) { $Worksheet.Column( $Column).Style.VerticalAlignment = $VerticalAlignment } + if ($VerticalAlignment) { $Worksheet.Column( $Column).Style.VerticalAlignment = $VerticalAlignment } if ($FontColor) { $Worksheet.Column( $Column).Style.Font.Color.SetColor( $FontColor ) } - if ($BorderRound) { $Worksheet.Column( $Column).Style.Border.BorderAround( $BorderAround ) } + if ($BorderAround) { $Worksheet.Column( $Column).Style.Border.BorderAround( $BorderAround ) } if ($BackgroundColor) { $Worksheet.Column( $Column).Style.Fill.PatternType = $BackgroundPattern $Worksheet.Column( $Column).Style.Fill.BackgroundColor.SetColor($BackgroundColor ) @@ -133,7 +133,7 @@ } if ($Autosize) { $Worksheet.Column( $Column).AutoFit() } elseif ($Width) { $Worksheet.Column( $Column).Width = $Width } - #endregion + #endregion #return the new data if -passthru was specified. - if ($passThru) { $Worksheet.Column( $Column)} + if ($passThru) { $Worksheet.Column( $Column)} } \ No newline at end of file diff --git a/Set-Row.ps1 b/Set-Row.ps1 index f1e43be..e66850e 100644 --- a/Set-Row.ps1 +++ b/Set-Row.ps1 @@ -1,40 +1,40 @@ Function Set-Row { <# -.Synopsis +.Synopsis Fills values into a row in a Excel spreadsheet -.Description - Set-Row accepts either a Worksheet object or an Excel package object returned by Export-Excel and the name of a sheet, - and inserts the chosen contents into a row of the sheet. - The contents can be a constant "42" , a formula or a script block which is converted into a constant or formula. - The first cell of the row can optional be given a heading. -.Example - Set-row -Worksheet $ws -Heading Total -Value {"=sum($columnName`2:$columnName$endrow)" } - - $Ws contains a worksheet object, and no Row number is specified so Set-Row will select the next row after the end of the data in the sheet - The first cell will contain "Total", and each other cell will contain - =Sum(xx2:xx99) - where xx is the column name, and 99 is the last row of data. - Note the use of `2 to Prevent 2 becoming part of the variable "ColumnName" - The script block can use $row, $column, $ColumnName, $startRow/Column $endRow/Column +.Description + Set-Row accepts either a Worksheet object or an Excel package object returned by Export-Excel and the name of a sheet, + and inserts the chosen contents into a row of the sheet. + The contents can be a constant "42" , a formula or a script block which is converted into a constant or formula. + The first cell of the row can optional be given a heading. +.Example + Set-row -Worksheet $ws -Heading Total -Value {"=sum($columnName`2:$columnName$endrow)" } + + $Ws contains a worksheet object, and no Row number is specified so Set-Row will select the next row after the end of the data in the sheet + The first cell will contain "Total", and each other cell will contain + =Sum(xx2:xx99) - where xx is the column name, and 99 is the last row of data. + Note the use of `2 to Prevent 2 becoming part of the variable "ColumnName" + The script block can use $row, $column, $ColumnName, $startRow/Column $endRow/Column + - #> [cmdletbinding()] Param ( #An Excel package object - e.g. from Export-Excel -passthru - requires a sheet name [Parameter(ParameterSetName="Package",Mandatory=$true)] - [OfficeOpenXml.ExcelPackage]$ExcelPackage, - #the name to update in the package + [OfficeOpenXml.ExcelPackage]$ExcelPackage, + #the name to update in the package [Parameter(ParameterSetName="Package")] $Worksheetname = "Sheet1", - #A worksheet object + #A worksheet object [Parameter(ParameterSetName="sheet",Mandatory=$true)] [OfficeOpenXml.Excelworksheet] $Worksheet, #Row to fill right - first row is 1. 0 will be interpreted as first unused row $Row = 0 , - #Position in the row to start from + #Position in the row to start from [Int]$StartColumn, - #value, formula or script block for to fill in. Script block can use $row, $column [number], $ColumnName [letter(s)], $startRow, $startColumn, $endRow, $endColumn + #value, formula or script block for to fill in. Script block can use $row, $column [number], $ColumnName [letter(s)], $startRow, $startColumn, $endRow, $endColumn [parameter(Mandatory=$true)] $Value, #Optional Row heading @@ -78,43 +78,43 @@ #Degrees to rotate text. Up to +90 for anti-clockwise ("upwards"), or to -90 for clockwise. [ValidateRange(-90, 90)] [int]$TextRotation , - #Set cells to a fixed hieght + #Set cells to a fixed hieght [float]$Height, [switch]$PassThru ) - #if we were passed a package object and a worksheet name , get the worksheet. - if ($ExcelPackage) {$Worksheet = $ExcelPackage.Workbook.worksheets[$Worksheetname] } + #if we were passed a package object and a worksheet name , get the worksheet. + if ($ExcelPackage) {$Worksheet = $ExcelPackage.Workbook.worksheets[$Worksheetname] } - #In a script block to build a formula, we may want any of corners or the columnname, + #In a script block to build a formula, we may want any of corners or the columnname, #if row and start column aren't specified assume first unused row, and first column if (-not $StartColumn) {$StartColumn = $Worksheet.Dimension.Start.Column } $startRow = $Worksheet.Dimension.Start.Row + 1 $endColumn = $Worksheet.Dimension.End.Column - $endRow = $Worksheet.Dimension.End.Row + $endRow = $Worksheet.Dimension.End.Row if ($Row -lt 2 ) {$Row = $endRow + 1 } - - Write-Verbose -Message "Updating Row $Row" - #Add a row label + + Write-Verbose -Message "Updating Row $Row" + #Add a row label if ($Heading) { - $Worksheet.Cells[$Row, $StartColumn].Value = $Heading - $StartColumn ++ + $Worksheet.Cells[$Row, $StartColumn].Value = $Heading + $StartColumn ++ } #Fill in the data if ($value) {foreach ($column in ($StartColumn..$EndColumn)) { #We might want the column name in a script block $ColumnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1","" if ($Value -is [scriptblock] ) { - #re-create the script block otherwise variables from this function are out of scope. - $cellData = & ([scriptblock]::create( $Value )) - Write-Verbose -Message $cellData + #re-create the script block otherwise variables from this function are out of scope. + $cellData = & ([scriptblock]::create( $Value )) + Write-Verbose -Message $cellData } - else{$cellData = $Value} + else{$cellData = $Value} if ($cellData -match "^=") { $Worksheet.Cells[$Row, $column].Formula = $cellData } - else { $Worksheet.Cells[$Row, $Column].Value = $cellData } - if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } + else { $Worksheet.Cells[$Row, $Column].Value = $cellData } + if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } }} - #region Apply formatting + #region Apply formatting if ($Underline) { $worksheet.row( $Row ).Style.Font.UnderLine = $true $worksheet.row( $Row ).Style.Font.UnderLineType = $UnderLineType @@ -130,13 +130,13 @@ if ($VerticalAlignment) { $worksheet.row( $Row ).Style.VerticalAlignment = $VerticalAlignment } if ($Height) { $worksheet.row( $Row ).Height = $Height } if ($FontColor) { $worksheet.row( $Row ).Style.Font.Color.SetColor( $FontColor ) } - if ($BorderRound) { $worksheet.row( $Row ).Style.Border.BorderAround( $BorderAround ) } + if ($BorderAround) { $worksheet.row( $Row ).Style.Border.BorderAround( $BorderAround ) } if ($BackgroundColor) { $worksheet.row( $Row ).Style.Fill.PatternType = $BackgroundPattern $worksheet.row( $Row ).Style.Fill.BackgroundColor.SetColor($BackgroundColor ) if ($PatternColor) { $worksheet.row( $Row ).Style.Fill.PatternColor.SetColor( $PatternColor ) } - } - #endregion + } + #endregion #return the new data if -passthru was specified. - if ($passThru) {$Worksheet.Row($Row)} + if ($passThru) {$Worksheet.Row($Row)} } \ No newline at end of file diff --git a/SetFormat.ps1 b/SetFormat.ps1 index dfe96ff..ec1cd39 100644 --- a/SetFormat.ps1 +++ b/SetFormat.ps1 @@ -72,22 +72,22 @@ [switch]$Hidden ) begin { - #Allow Set-Format to take Worksheet and range parameters (like Add Contitional formatting) - convert them to an address - if ($WorkSheet -and $Range) {$Address = $WorkSheet.Cells[$Range] } + #Allow Set-Format to take Worksheet and range parameters (like Add Contitional formatting) - convert them to an address + if ($WorkSheet -and $Range) {$Address = $WorkSheet.Cells[$Range] } } process { if ($Address -is [Array]) { - [void]$PSBoundParameters.Remove("Address") + [void]$PSBoundParameters.Remove("Address") $Address | Set-Format @PSBoundParameters - } + } else { if ($ResetFont) { $Address.Style.Font.Color.SetColor("Black") $Address.Style.Font.Bold = $false $Address.Style.Font.Italic = $false $Address.Style.Font.UnderLine = $false - $Address.Style.Font.Strike = $falsee + $Address.Style.Font.Strike = $false } if ($Underline) { $Address.Style.Font.UnderLine = $true @@ -98,7 +98,7 @@ if ($StrikeThru) {$Address.Style.Font.Strike = $true } if ($FontShift) {$Address.Style.Font.VerticalAlign = $FontShift } if ($FontColor) {$Address.Style.Font.Color.SetColor( $FontColor ) } - if ($BorderRound) {$Address.Style.Border.BorderAround( $BorderAround ) } + if ($BorderAround) {$Address.Style.Border.BorderAround( $BorderAround ) } if ($NumberFormat) {$Address.Style.Numberformat.Format = $NumberFormat } if ($TextRotation) {$Address.Style.TextRotation = $TextRotation } if ($WrapText) {$Address.Style.WrapText = $true } diff --git a/formatting.ps1 b/formatting.ps1 index 2ec8b58..efc703e 100644 --- a/formatting.ps1 +++ b/formatting.ps1 @@ -1,58 +1,58 @@ Function Add-ConditionalFormatting { <# -.Synopsis - Adds contitional formatting to worksheet -.Example - $excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru +.Synopsis + Adds contitional formatting to worksheet +.Example + $excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru - Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b":b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003" - Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled" + Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b":b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003" + Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled" $excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern $excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true - $excel.Save() ; $excel.Dispose() + $excel.Save() ; $excel.Dispose() - Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel - The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show + Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel + The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show Fixed formats are then applied to dates in columns D..G and the top row is formatted - Finally the workbook is saved and the Excel closed. + Finally the workbook is saved and the Excel closed. #> Param ( - #The worksheet where the format is to be applied + #The worksheet where the format is to be applied [OfficeOpenXml.ExcelWorksheet]$WorkSheet , - #The area of the worksheet where the format is to be applied + #The area of the worksheet where the format is to be applied [OfficeOpenXml.ExcelAddress]$Range , - #One of the standard named rules - Top / Bottom / Less than / Greater than / Contains etc + #One of the standard named rules - Top / Bottom / Less than / Greater than / Contains etc [Parameter(Mandatory=$true,ParameterSetName="NamedRule",Position=3)] [OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType]$RuleType , - #Text colour for matching objects - [Alias("ForeGroundColour")] + #Text colour for matching objects + [Alias("ForeGroundColour")] [System.Drawing.Color]$ForeGroundColor, - #colour for databar type charts - [Parameter(Mandatory=$true,ParameterSetName="DataBar")] - [Alias("DataBarColour")] + #colour for databar type charts + [Parameter(Mandatory=$true,ParameterSetName="DataBar")] + [Alias("DataBarColour")] [System.Drawing.Color]$DataBarColor, #One of the three-icon set types (e.g. Traffic Lights) - [Parameter(Mandatory=$true,ParameterSetName="ThreeIconSet")] + [Parameter(Mandatory=$true,ParameterSetName="ThreeIconSet")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting3IconsSetType]$ThreeIconsSet, #A four-icon set name - [Parameter(Mandatory=$true,ParameterSetName="FourIconSet")] + [Parameter(Mandatory=$true,ParameterSetName="FourIconSet")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting4IconsSetType]$FourIconsSet, #A five-icon set name - [Parameter(Mandatory=$true,ParameterSetName="FiveIconSet")] + [Parameter(Mandatory=$true,ParameterSetName="FiveIconSet")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting5IconsSetType]$FiveIconsSet, - #A value for the condition (e.g. "2000" if the test is 'lessthan 2000') + #A value for the condition (e.g. "2000" if the test is 'lessthan 2000') [string]$ConditionValue, - #A second value for the conditions like between x and Y + #A second value for the conditions like between x and Y [string]$ConditionValue2, #Background colour for matching items [System.Drawing.Color]$BackgroundColor, #Background pattern for matching items - [OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid, + [OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid, #Secondary colour when a background pattern requires it [System.Drawing.Color]$PatternColor, #Sets the numeric format for matching items - $NumberFormat, + $NumberFormat, #Put matching items in bold face [switch]$Bold, #Put matching items in italic @@ -61,27 +61,27 @@ [switch]$Underline, #Strikethrough text of matching items [switch]$StrikeThru - ) + ) - If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)} + If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)} elseif ($FourIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) } elseif ($FiveIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $IconType) } elseif ($DataBarColor) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Range , $DataBarColor) } else { $rule = ($WorkSheet.ConditionalFormatting)."Add$RuleType"($Range)} - - if ($ConditionValue -and $RuleType -match "Top|Botom") {$rule.Rank = $ConditionValue } + + if ($ConditionValue -and $RuleType -match "Top|Botom") {$rule.Rank = $ConditionValue } if ($ConditionValue -and $RuleType -match "StdDev") {$rule.StdDev = $ConditionValue } if ($ConditionValue -and $RuleType -match "Than|Equal|Expression") {$rule.Formula = $ConditionValue } if ($ConditionValue -and $RuleType -match "Text|With") {$rule.Text = $ConditionValue } if ($ConditionValue -and $ConditionValue2 -and $RuleType -match "Between") {$rule.Formula = $ConditionValue $rule.Formula2 = $ConditionValue2} - + if ($NumberFormat) {$rule.Style.NumberFormat.Format = $NumberFormat } - if ($Underline) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::Single } - if ($Bold) {$rule.Style.Font.Bold = $true} + if ($Underline) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::Single } + if ($Bold) {$rule.Style.Font.Bold = $true} if ($Italic) {$rule.Style.Font.Italic = $true} - if ($StrikeThru) {$rule.Style.Font.Strike = $true} + if ($StrikeThru) {$rule.Style.Font.Strike = $true} if ($ForeGroundColor) {$rule.Style.Font.Color.color = $ForeGroundColor } if ($BackgroundColor) {$rule.Style.Fill.BackgroundColor.color = $BackgroundColor } if ($BackgroundPattern) {$rule.Style.Fill.PatternType = $BackgroundPattern } @@ -90,14 +90,14 @@ Function Set-Format { <# -.SYNOPSIS - Applies Number, font, alignment and colour formatting to a range of Excel Cells +.SYNOPSIS + Applies Number, font, alignment and colour formatting to a range of Excel Cells .EXAMPLE $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NumberFormat "#,###" Selects column 3 from a sheet object (within a workbook object, which is a child of the ExcelPackage object) and passes it to Set-Format which formats as an integer with comma seperated groups .EXAMPLE - Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" - Instead of piping the address in this version specifies a block of cells and applies similar formatting + Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" + Instead of piping the address in this version specifies a block of cells and applies similar formatting #> Param ( @@ -105,62 +105,62 @@ Function Set-Format { [Parameter(ValueFromPipeline=$true)] [object[]]$Address , #Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc - [Alias("NFormat")] - $NumberFormat, + [Alias("NFormat")] + $NumberFormat, #Style of border to draw around the range [OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround, - #Colour for the text - if none specified it will be left as it it is + #Colour for the text - if none specified it will be left as it it is [System.Drawing.Color]$FontColor, #Clear Bold, Italic, StrikeThrough and Underline and set colour to black - [switch]$ResetFont, + [switch]$ResetFont, #Make text bold [switch]$Bold, #Make text italic [switch]$Italic, - #Underline the text using the underline style in -underline type + #Underline the text using the underline style in -underline type [switch]$Underline, - #Should Underline use single or double, normal or accounting mode : default is single normal + #Should Underline use single or double, normal or accounting mode : default is single normal [OfficeOpenXml.Style.ExcelUnderLineType]$UnderLineType = [OfficeOpenXml.Style.ExcelUnderLineType]::Single, #StrikeThrough text [switch]$StrikeThru, #Subscript or superscript [OfficeOpenXml.Style.ExcelVerticalAlignmentFont]$FontShift, #Font to use - Excel defaults to Calibri - [String]$FontName, + [String]$FontName, #Point size for the text [float]$FontSize, - #Change background colour + #Change background colour [System.Drawing.Color]$BackgroundColor, #Background pattern - solid by default [OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern =[OfficeOpenXml.Style.ExcelFillStyle]::Solid , - #Secondary colour for background pattern - [Alias("PatternColour")] + #Secondary colour for background pattern + [Alias("PatternColour")] [System.Drawing.Color]$PatternColor, #Turn on text wrapping - [switch]$WrapText, - #Position cell contents to left, right or centre ... + [switch]$WrapText, + #Position cell contents to left, right or centre ... [OfficeOpenXml.Style.ExcelHorizontalAlignment]$HorizontalAlignment, - #Position cell contents to top bottom or centre + #Position cell contents to top bottom or centre [OfficeOpenXml.Style.ExcelVerticalAlignment]$VerticalAlignment, #Degrees to rotate text. Up to +90 for anti-clockwise ("upwards"), or to -90 for clockwise. [ValidateRange(-90,90)] [int]$TextRotation , #Autofit cells to width (columns or ranges only) - [switch]$AutoFit, + [switch]$AutoFit, #Set cells to a fixed width (columns or ranges only), ignored if Autofit is specified [float]$Width, #Set cells to a fixed hieght (rows or ranges only) - [float]$Height, + [float]$Height, #Hide a row or column (not a range) - [switch]$Hidden + [switch]$Hidden ) process { - Foreach ($range in $Address) { + Foreach ($range in $Address) { if ($ResetFont) {$Range.Style.Font.Color.SetColor("Black") $Range.Style.Font.Bold = $false $Range.Style.Font.Italic = $false $Range.Style.Font.UnderLine = $false - $Range.Style.Font.Strike = $falsee + $Range.Style.Font.Strike = $false } if ($Underline) {$Range.Style.Font.UnderLine = $true $Range.Style.Font.UnderLineType =$UnderLineType @@ -170,7 +170,7 @@ Function Set-Format { if ($StrikeThru) {$Range.Style.Font.Strike = $true } if ($FontShift) {$Range.Style.Font.VerticalAlign = $FontShift } if ($FontColor) {$Range.Style.Font.Color.SetColor( $FontColor ) } - if ($BorderRound) {$Range.Style.Border.BorderAround( $BorderAround ) } + if ($BorderAround) {$Range.Style.Border.BorderAround( $BorderAround ) } if ($NumberFormat) {$Range.Style.Numberformat.Format= $NumberFormat } if ($TextRotation) {$Range.Style.TextRotation = $TextRotation } if ($WrapText) {$Range.Style.WrapText = $true } @@ -179,7 +179,7 @@ Function Set-Format { if ($BackgroundColor) { $Range.Style.Fill.PatternType = $BackgroundPattern - $Range.Style.Fill.BackgroundColor.SetColor($BackgroundColor) + $Range.Style.Fill.BackgroundColor.SetColor($BackgroundColor) if ($PatternColor) { $range.Style.Fill.PatternColor.SetColor( $PatternColor) } @@ -191,13 +191,13 @@ Function Set-Format { ($range.Start.Row)..($range.Start.Row + $range.Rows) | ForEach-Object {$ws.Row($_).Height = $Height } } - else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Range.GetType().name)) } + else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Range.GetType().name)) } } if ($AutoFit) { if ($Range -is [OfficeOpenXml.ExcelColumn]) {$Range.AutoFit() } - elseif ($Range -is [OfficeOpenXml.ExcelRange] ) {$Range.AutoFitColumns() } - else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Range.GetType().name)) } - + elseif ($Range -is [OfficeOpenXml.ExcelRange] ) {$Range.AutoFitColumns() } + else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Range.GetType().name)) } + } elseif ($Width) { if ($Range -is [OfficeOpenXml.ExcelColumn]) {$Range.Width = $Width} @@ -205,7 +205,7 @@ Function Set-Format { ($range.Start.Column)..($range.Start.Column+ $range.Columns) | ForEach-Object {$ws.Column($_).Width = $Width} } - else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Range.GetType().name)) } + else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Range.GetType().name)) } } if ($Hidden) { if ($Range -is [OfficeOpenXml.ExcelRow] -or @@ -216,12 +216,12 @@ Function Set-Format { } } -#Argument completer for colours. If we have PS 5 or Tab expansion++ then we'll register it. Otherwise it does nothing. +#Argument completer for colours. If we have PS 5 or Tab expansion++ then we'll register it. Otherwise it does nothing. Function ColorCompletion{ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) [System.Drawing.KnownColor].GetFields() | Where-Object {$_.IsStatic -and $_.name -like "$wordToComplete*" } | Sort-Object name | ForEach-Object {New-CompletionResult $_.name $_.name - } + } } if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) { From 80224da067a58cbf6a6c10c791955ef390a4fc34 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sun, 14 Jan 2018 09:35:09 -0500 Subject: [PATCH 21/43] updated --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 6e953d5..403b925 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,12 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +#### +* Added -Force to New-Alias +* Add example to set the background color of a column +* Supports excluding Row Grand Totals for PivotTables +* Allow xlsm files to be read +* Fix `Set-Column.ps1`, `Set-Row.ps1`, `SetFormat.ps1`, `formatting.ps1` **$falsee** and **$BorderRound** #### 1/1/2018 * Added switch `[Switch]$NoTotalsInPivot`. Allows hiding of the row totals in the pivot table. Thanks you to [jameseholt](https://github.com/jameseholt) for the request. From 848177c3581cba00e0271818b2b24c8c6f69c57c Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 10 Feb 2018 20:14:02 -0500 Subject: [PATCH 22/43] fixed spelling #285 --- Export-Excel.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index f35ca21..9de6d19 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -8,7 +8,7 @@ .PARAMETER Path Path to a new or existing .XLSX file .PARAMETER ExcelPackage - An object representing an Excel Package - usually this is returned by specifying -Passthru alllowing multiple commands to work on the same Workbook without saving and reloading each time. + An object representing an Excel Package - usually this is returned by specifying -Passthru allowing multiple commands to work on the same Workbook without saving and reloading each time. .PARAMETER WorkSheetName The name of a sheet within the workbook - "Sheet1" by default .PARAMETER ClearSheet @@ -834,10 +834,10 @@ $pivotTable.DataOnRows = $false } } - + if($NoTotalsInPivot) { $pivotTable.RowGrandTotals = $false - } + } if ($IncludePivotChart) { $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) From 34457d05dae2be1c81bf4e0ccab70f78eb6a3225 Mon Sep 17 00:00:00 2001 From: dfinke Date: Wed, 14 Mar 2018 20:11:36 -0400 Subject: [PATCH 23/43] Fixes from James merged --- ImportExcel.psd1 | 2 +- README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index ac256a9..8b02949 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.9' +ModuleVersion = '4.0.10' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' diff --git a/README.md b/README.md index 403b925..0641dab 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +#### 3/14/2018 +- Thank you to [James O'Neill](https://twitter.com/jamesoneill), fixed bugs with ChangeDatabase parameter which would prevent it working + #### * Added -Force to New-Alias * Add example to set the background color of a column From 4408a0461983229e455f01867691eb391e1a4497 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:23:28 -0400 Subject: [PATCH 24/43] added Pivot Table Filter pic --- images/PivotTableFilter.png | Bin 0 -> 28863 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/PivotTableFilter.png diff --git a/images/PivotTableFilter.png b/images/PivotTableFilter.png new file mode 100644 index 0000000000000000000000000000000000000000..af012a7f37b742e72001f9ac7b36b7d5a26483e3 GIT binary patch literal 28863 zcmYg%WmFtXv^6rwKp-%<1%hh`?lQQ$ySux~5CXy7-Q8US1cJMJf@^Rmd~@%8-&?QO zn(6Kz-Cd`u&Z)D{-t|pMK@tsx5CsMX22EN@Oa%r8whDTEhztk)=f{@hfSzDoRU}1V zY9@(~pf>EU^QzS-?t-Fki=ip$JVp;m^9Q>B%yQh07~d~QW|PT99XB_py<0vdii zpbUlshI&||x@}tK++~gInx$oVv$;-Y5nz4ArA{1yxWVZ=yYoC1iVC7Q-5p2N+bWs@ zuH5rd$pCQHAw3boK72|LjBw{0b-?UeFzAbLRADW7+$MSw8Nh_tA1C<6BMirwf_`W~pt@KQ-_yX<|w_mZDN>>FK?r zLXLntEGa4y>)k?w(T01>SE3bAhHMu>Kwtbm2&zCj8nIqbGlW7zLzN(Tv_XK#h=`l# z4VXBpxZDr)U?&wYP(<)ODFxWc=C}%pGYD{feVq>gE5mFA6-@u}gcnXhK|vWdyGm6b z2`k|16qbglo{a75{bN=3`xG*6W?NfZFcy4PAtNp-d!UPp%XjVP!AZ@F8R8x#M$Ahd z#=64T*YzMF(nD%4F3sMz_|NTd@md9m$VE;)8 z`|0(4*oXz&5kcv}%X8}Dm zI}f5sxm-@$>I)^J>o$F`RQrq?JHc$-U$ z(ichoq$l0AV>ZGbk0u84lhyX30s79RT^rO@|9U@(Y~4zbu;2F}OiWBBDkJDCQw$Q!_ms{OQrOSt~Yv)(vi)cz38qa#bik`#%;EKN{Y27ZavOS8j~brh3c33UQaTTtRV@Ze3pU~|B^_06#&`FVNF*(T5$oeN3y!10zq)C8Cu0-sM%Qy{QQ@PAbU z?K*{$Yk0T)xe3f9`5AQnIcZx{z7sT0Q1{52w;8CjDurPe2n%HOMlra2J#+o~$~ojQ z(hz#*dWOfOYqj`C2&-j7iu#I5UxS7ZSXPrafkQ%q*7Bwc{18!<45^g>%L}sZ{DJQl zr7Hsny~L3b2~#B}C+pLI)o6%~XZ*v0O&LA98B^wM+0kU^I9^vmmPJsaS8(pdZ=kbp zp)_v0iekpC91%RcO`s>%~dDP=31J%8sCH_;5rij2EMzv)EK4 zrt`AB+(3NOUhn5g0fSE`)nB8ud;}Zd^uhAEhoUemFovplvKTlKb%?&6kt^HEu1js_ z%Z;X;kvj#CYq@4h!mSMqldw?V=2;vB6&b%yyuA!8KkBY~o^Jl?u|GOZmu)qarMy_x zZ2i|?Bq+=HP+eVIg8qt#i1fchMngkj=8(OZ$mB8wB3Bx-SF^CaQD7!7pMb-(@Wn35;HvPf49iIEZiH%HjnFm@t`WWW>p{DGx} zKcI~>Qc}X;wiRjxbQ0vX31>2JbK~22CKkYKp(_F7^3KPO>4IhpRmy{dgZ1C4#*hmB zNkn3R+*3Mm^sa2YD;xtH!ekgqv`I^~h9N)v1at3kD6mob{=k-X7)02&;nj}KTk^@ec&<}z}^&j{?eGP1If&?6!Onh6Mp;kkWjY;0tM z9;~dhv$J(tb~ZLP&~;x$MFl#-etv#-c6M%VZfa`ShfW*4fp7K?nc=O!3|+&cyof-= zZqIj2Zd?Utm4;+udD7gGu$B&X4YUG5%eKx@!5} zP2YdXoc0XKgz*wjpOUhP?FW=oMA>pC`=k7 zXpa$Jxi&(q?CgH*T+%1Y^u7=FlgR@J8N-;X*ki142C+|wFPU^s_yz;mP^6^O++Ob& zfgqJxR@8jJ$2fGaSip>S!m4+kRBSMyc!8auhpww~jTBT-&g%E)f=*q*jP_eoO8n}T z_3M)Q+zH(~svP?As`%|0`_M&yR`SLeDc@8Tn!bl5_KR!|P8;5LWZHTlbQuh|qqw^v zU_mO*yQVvAszAXnC(aOr~cV|sfAC$IgSV!|hr5OQ@KIuC#?GHS|%XM8^oeCRtH zG;+0=`^<+0oR=H4!?s^u+SbBIXsU%$^(Bi7c_>{7T?cBbSV&BNc`#Qr z)PF@!kp9Ly0iWztLhALueS6|FWjeZQ22#8N~jg z`JkR5=anHYAKccDVgcU9u_2$;jIaf{77z#7;dwm3TntPgU9^FKLxiuSBaI6gc1b|C zOG>0t$2P|0qCYebfrH!Y3*!09N2RSxj-y`KB3gOeEN z>HPEQGA7ow%?6IJyefI-gHPx3>zQL<&L4lutiE4#&7k=&q^!^0JqH8qn2}6@hb%Wj z43k4C!|84YGT#^fMmqjbi$rd=5^d0iToH zin1VE*gHagyXicj20B-v_N`hl`Qu8teop0v$J3Pf7PrM^jpny`2^o7^H2)c4BZ@t` zT24lM2Osv#K7@uuwHc1}ggPdM)|yRsv$fyVM6V_&l+i_ZlvB3R0snJ=Fwj4ml_dU+XS(5&6+@_5}ZW>`|p7C7*{a54aY^a4`C*!ST> z@m>)Y{t5bAx?PS|zQxz`&m6i}A3;&=?G~%s=BcukM=$euKTpTU_YG;chY4NbVkcT! z^?W&bO6%y4JEm^Zi9CAAr?II@PjRcSy6*X#Z$=C{|5*4K#^i&fqasfN9+oor+OfsW zjZg2i3nh8YNs2n9B2|5n?&fsx} z$iB%~TZXbjdn~d(xoSZ0M$+vuuX>0`epHO?#b*0Y-=7Yep0OM!oPe;N__4ihsyN|= zyl>K9pa_|F=z#;g%@y24J^a({DNO1!*W=7_-`o9JEIbk#;l(xnn*37a=n*|<_ruHT zN@A7g1`~!lR%r4rH?wNy-r7LJ2vA`_hUvOQ=`~5Ck_^8OH%yvN7bKGb+ z#l@CO_sv3y`Vz_*Wh&VK(?lnF_d=0!xO{_!JPj@}@t-{Q&Iau^_EJ`DI%pjO6SbUh zzGG+Qtd-R_jFV-$8g=%91M39@0o}NBY;ObE-Y)|lEw*`WD z-jAMh>i!)%G4Kb27JcUrH_S&GM~NENgp8H)6Y{7{_ph@4`S2*@6~G z#ulOKT(xrb%`_Ivm8Z3JLe}3~+{u#~D8O817m+p&0ACez3`pwK@RBAe>nYp2A$;Qm zjYoBKn22nd!xj;K>L1#PVXP-H^Cc*>lWRM#ciOyqoswyE9Zj5-Nn{;Cprj)44V>gO z+_H6~AWt;x9SNT>R%bm1epy9@dcJ|Dd>z^7g3sXvS}WFUWkSE9S5=njF9PKnyYSwl zmUV|sYesso!D1uTwI(xn=s;vgEWq3>2ZZNmbJ?U!L^?+R_eC4ymkC=(?rVA-E7%xF zRjLw)<1&2n(;4*@D-v6(XzNGdgiCj$6^$uM^AZ5n^j@pAhgwStE}D;w z?^{=mc^sWOS<|MLj$8K)Q=a8mmI1AARyZ@}wQ$WUbafxR7-uGME#e_3Dx_h{A+nl-$YHi7E= z=@V=6$;`;}v8tAh(YZP?NLi-uehfaV)ATefnVgFtfdYBs%6q>vnrLE9yWK#qB=ThG@)aghg=9npYh&#*+pnp~mrQ!RAK61u3i7QexD`&el`V~TJ2lq`e<2^C|N z_9U$WQPtLRJ#B8YLln%7V((I3_FywFYg$3FMa_EBg8SzUhPWT(chxvINEW4)YSbdl z-ANGBK{9n~1&P7&6geBA6L8*n&W@HINuK_ zF;G;Y$1c)QM$oR}gX4{h`##qbsVM=gFLKsqg(5{(F;9f#S5O8ikl*qbsm}N|)xF*5 zus%xD&d(7{&PapGV5`HA8kLe<2cfJ0m@oXEDhCuJ{q@L5ko)x`*Zn16Iz|!)j$eP1 zv2U3&tD#4^LdhK;`!;$eyNmi^8m95VEl4%X-$h_o*vj1mmiTQ2a;zd@^Igedn8BP$TQXJ@z>_oZ<7S1gC1b*L=WQ1$I;=XaB*?Af&szLPj}*Sa&e+zH!K?= zwJMMy1|h-YSq*lBu%kuU>RMu(ts?*TJ>tAOOL)y;HqML#Y7>&Sf@w#^)vH}C`6N^B zlwi@l=9ro8UbN~2&jxm#-QJDyrte?M#raZ=blu?WLL5xMBv1$@N8O!h6e+U_&VzgJ zV(?tsO33E$qpY5ckvk<#UMbGqzB`AsB0c&cYuDD&?^Ptdsl+mj56{f&Yf%}k%NAp; zEmIB&A|X?i(H9A-P;n|xR_GZ|?Q?T1Ced`2kp7_s2mk}*WWV{I<5&8b8Z{eTNAh?l zqvpeLWuUC$aYgrEDt%Upav1qA5v*1z^WE!=7)122o&ca%DUZbcX3M$tes40_aoe3x z<&ol{nf2lImVEmt_K${qj(z|EIugX?8@x9wX~&kb+2vAnWpo zF#J{W6+%J^Ej%3I^Nn|@5Pk>0+e@Sg8LD4W&F>c0)SB7W7@OxwYL=if&ygFn3f!NL zF)aKVm3HhT)zRQ+^FjO$ik{A2g{9F$IzA(xOS-36K^p+bI zHr?X6+Vb_p$`oy9XnHJ}RQkk5A`L zY)?6LuuqdtwqE=ntiSwizsvI)`v!@EBj+sbNc`9sq$Pn}Hvi;E7%C^hX=kNt zZG5;otqtUCPHV!!HqpDMS_B&?L&HW^$=yiM+ zwU1paSK8JLs1yQoB{q&(F%|x{@l7Yxua@k+vo&Hmq_z5j?SN=_s)u9%Szi>KipnvP zTrKI_-=kaI3JjTxMiW|8e=0}i+vtw(K^_&|=kOVu>*x9Zwv0D3UGY<246D(uRW_W= z#T)>IcRjX$Mu|Yh?Xyv)W+uG2oDl_M0L3?I*byn>FN3Q2ts&*^>?|TPlS37#yg+KJ z(xoLgxVS3Sxsww$WU2>k7V5~wO*l6*Y^I^g?oW66ZhT=jnN|MZ4hKrX~5|ZnEx)jmb*h^eZ zHbQ*RmkCX*@~4EpCHh8_oNRAUt`0Tzl8oxsI^QihMJ{bk*=V2_J#C4W)&lz%B^q$s zkW}q=tLVjrZQS|}HN^cs$!7fgf}a%Sw>&mfizQ+Qq(b1y&BT&ow_ zuk8yaMeMwz#UHISzj6>mL_ z%sUK3I?QZ0w*We6rBo8X1@A?ZTq$KHUInG7(#kOx5HsnmkOyh1xCW-RNnF&hgvfo= zOXNZdpB82&MQ`JtP+*e~p)NI564f~;o2O3{b1A)Bh}{inzM7@L;q-HmnUlrQdBmkn z4W3SE!|t#SKnUlKF5(oQkdWf-Fs#V*-q*l*fW&e9|^7O`Ot?_{Ki)(H75fTXe*^Y z-sS9&%F9Wt+M(~dYFWM3QjSTjOH^POBV*LO%df{)Hv`5;)|nrELgsKIu%gY6F4V!X zV#tpNeHrpvA~-b;*7<3sA;5hqIB+aStSTzPL|qv#O%c;sV3@E`!@x}t7LESf*3!ox zA!IbVbC3#?d(rgw*5U1me{0@kq1c3Ugrc06d9z@0T~D0=6?TkG_! ztDWhVu=!z&$*zeK#CQkPirt73D6g&$JCxW`B3u1w~MOi=Atf%X^z0Z2EBD#`u!$*~x(&Z$aovNJm@q*Lx=YqmX3u!1GmP0Lc^0^!&Qc`X)!s*gkW!Uem^_%LnbnpwcFaJ`=hS zrtxf#?X)IWdF@CQCtpeVvH z2LsRuxx4>re*dZADVQp5%>TC>(FY?hHyG`52}K*U8Q;bi`xV>0! z_N#b&S1XC>#S)+4xpI)VW>H(c)-pkSKga#2t*WGX(MK&Vp$%;XbBTlziLocxvH{1+ zFf9b5V3JYy=#V7IH}E8Z$5tT>gUHHFyL0a38awpSIH&OEGoYTqsu~(Otdi)-+9hhFyQGr7^3Y26t3s1PHZn{O*^PeGRw8y?n0qfC5y*ba2Wq z1KD>2^7mr&(@0_EGQ44z!1O)mo!sx4v0NZ8*f~s zz2#4iZixtS+z$ayd3@t|$PR$@&4vl{g=2Rb#q2#N@4Un5quUd;TO01i8%>xbBhBd; zC@G!O6rbVZ@@qFgrc|V2nLM~2c&96aF=bU&{;A{GTF7KGJIkji#?XId&^Y6D4?x|Y zqUvCuZfx29ERNG}RDP)L*sAbh#j-5)_NBP-;8RxPuuag&clRZf2w_*cqhp(}!N!AC z-Ao1ihsXnX5yhlD51-c{&$$y#HcTCG-CWj+s|QUYMo|s5MdDtT`E#L5J96@C&(STj z8+&(;M2Hjj`YRIp*|orT3)djrOhd@rNgxgsks(M>B0mlI5q#cG=2#rC^w}6M`-Spg zCxLVgj1VTRV}GtbDe8SW53AqzxVD&V!oR$}-$FBV7wlFpsA%tASYI8#HfB*7Lj=9s zVzsWhoh5mm)aQ3Py!k775NqH?lJoOZjQ_i;_U}L6rbp)ZvQlD>4zBAYTL8MXEF!7# zq0uX~uS|iDcaHF-+EY1}mTbjSxqnx;eu-Fxew_%Mx_Lqb%`4OzYHpDwE}cNs47SR^ z^p)WY0F-5z&Ix9^UpG$}QU}liVI+g`0YbJ8r~fRdm&V+rxdcpybG6J!LFh*Vj)*cC z^YV3A`<_cv%ja?Mqg+;UYExkEfYtv8cP0rpd4Er<`PbxfBzka3!e|wL`>!*D36K+e-QG`_LaK=eoB_PY!+O?90w0!?LqGGP3? zC!R}|@Wz60;(a*aICX{wj&483DVUIfW0a)B*Evpnu@c`Nt&`HDqaT~fRZH#L@inz5 z_Umu0GptgK%&Q~fV%M6tOG!1lX|c%k==$S7n($D4w|+|d^=T?SAxt4r?kC?CgUI1K zx}1F4@qk9h=81V0wvzP8G=BG|U2{ALiq}IzR<62!a2#xmOM~BeW0`x* z@raK1o%HX~cO&mmk~tVbbuqLjvZ;S#DosEvT?!Kx{D=rLtM@MIFDlzUn93FG@_e@V zE}xXQYa+swx^Z{s)CgdpO&GP&k&`oX;!G{b#aEXxl0|iG=og)xlbX$%pRWst*cA|y zJjuobkQ0`%{&>lKH8SHUS3Uq|tBJ6gQ9q)P^V9ON;p_sICG5pnc4 zKVwEqH|@05eA6nHwo6LiKnt5V8C#RQWVZNFODZC17}I>{XP9{EUi_YTdij(oYD47@ zu?%)1tT)E|t~kot^1WiAb+%I>-5T7h3SGI{H!vs)Dijgs4L0^nQ?PUake7E+nFu1e zg`qtSFx3E2Dr|AE(N)GokeZJ~sKjNAIH$dA$jb(*BnUMZUfS^K;WQ}p!#}$R*Uk#) zikMNnt78ymCRw~t|574)TPLhO>&;QC@Fz{ce9KmaB2-&($HoS~VLvRKfsPe~m+x|X z!C}I$DteF?DCm_uEOMB|OWuucDBs?0f7pw=^&O*_sdNbmltf3g`Y>q)GXj;?#7!FQ z4oS)0G`?$r4;%Q1aiIg`6ySRYPgn$S5S3m}+&Ix_Lvn74ooJT6$RVTnTqOROpNW*o z+gpa{|7-u}zKd)bGh|C*s8nL8*p2wND){%h3L#Xya}~fFXT;`aTBlWYd`wT)$C`j~ zqYGyEV+$1taGcR!P-=L9tzGJ2-h!!A#c@)xXh&Ggy~Qp%A%C0l;yF|ujuwTX+8P?_;^Ja)ad9Zh@b>m@Z*T8tzn%L(yqSrK$$z97l))VP zb{hhL7(im|%OLLUD=GZlz`|A(8KP za%qi3xmu7R%B#XX%&ug<(xImh_V#`A{ik-|k%8M?MxJsIQXeyC92vhMtT0zzch%Mr zRKpacksMHZ08{%DBpS-Y%CK{Wy-PB(%>#6z(1M2#ekeiYYQi3{9HKOzgq~WGXnql7YeSQ70@}VBI2nv99u^<@ zxQW7~BC$}!uQ}S5yv{u**~F}dKUii-zk=+5|7&6*nItU(+O=y2hw<+u=j+xF-30m) z1+zSiqNpOk7Jy7$We`-WrSOhQs+4WnFIb5j_nVoG;C|ZPdn`@E@amNCs2WZrs%5Cz z|I=quaU=VS*i^-PxQPHlCteUK(;vQoqhHf1!9Ok#S^lpf!Vt!usQBGp*RfG`zFNI@ z7pLmSv`Zp|pD8RqE{3F#{*`i+y@~uM4^V-YJ+!q~W1d*4J|^J5Mt$U(2|3_ovzpN!ZSTxG5;9VOsQ6 zu8sI&c#+6-C)?sABqaYCVCz^_Op~I-Fc-KWfaC=gD)K;Qdo%1m9O^p~DU5FRf-QE4 zGH{hWbbs~X2$GC6G*4xoA$$Bzv1%g5^YUx%-<+56_29<4A9AkJENd~%iywBc&Hkz) z{lj0ebxA_pa5c#iq^KYsnglFKdC4?G;~Wd~Xn?48%Qc&oVp!XAh`uigsT%cg^}b-6 zX5BE3X}SN1i*H4_ZAJT1XA+xm!1v0{2^J>!#dRkC4IB|UATw>78&!t=jbhsQ%xHxG z3p7NMW#{Vy8^lK=>O>V>84pgqvouC<2`4|!Nk9yik8+O2YU5QZ#0Dvym0@4k2i;WP zS5w>wJvl4q{98N)wBg8o<1O-9o2hF5pk+&1wRbnJ zpZ`t{;d(v&l;>R7Z#lg)!g0@aZp`NHsbs~+q*|942~ALI&Ys}IR9hZnTj-D56BSKF zj6#NoDN?*o9z$A9e-HP^Z^B{zVK~2fP!3}*B!@X}nQa|7Qo|G9h3&Z(`B9ioY)Vfv zxOZrFn8GwCX--udXLV^f^QC{ti7x9G<=1m~t7T+RP^|AQ4% zGYrwH)O3X1s>2C*gJ@!=HSbTu)sS`v^_oGM0tVTwIc2N9b77V{8rO8y7;kQ=a9l^- ziTAahurG#fYbPjjk$NUMCA8 zvBYj=$c*ieC#gLw^1pm-A;=7MH)|g24tCt)wX3IbUAVO4t3A~>K2?Fcp1rMd&UCI(wB~N$zEb~{;E$j=3n2LRz=y9P z5#5wU4)n=#{hW}9)FKU$Y@I#x`z$}ym(f|m#1|b~B2{TMOJ4y3D|bKmQUlNr!y zP!O8?Gevvzka?C6AvOVm0g9GV=HV|33+vuirT9{H}s`h5APw!H&-__4=Y!<6$bDXHCpWW`S^7}DdBsccIIG~(*g^kuUZ#08Ck z87m%kvNLW6{|+W11qgs*R-&i3-S*qa=_+$WgH7Y~NDE>>SF)+CYv5WeEMp?ACibn{ zHKN>h6p;HeM<4@;M-Z6&Q2(KoONUGx!^LDJ;+5*-JYrG4pg@VumZ?rVoOK-iD4Uez zhglgyOkkc8HAfq9Nx$YRp8>-oVuZHaLea00i68LYV;eibgDi+6~=9QY~KFSsQ zl8W>O&!k0Gb=L{6QX zSKP=dn7SQ)ftRvy+=EoD%;p&m6X>+Hsvjf1%W5w=9N~?s$t2&5wBMu~XxS=89ZfRc z(*g_12LJ~d4pQtwI!_Vk9W4+dOBHI8%5XCZcO0`nuRyGIH?$^n%CXD1eV<>U=da+HYT!BiSv&QW^DdgnnZSrX&0%-VD7aVC!Nd z_G8;W{~R#VK!o^j_-cwL%VajC&UV%6Fq*5TY!OzuUH1OnL?y_~YKuU!iGMky?PbL1 zX~xK>?MDut4<{qUeLh_f*%G6R+ipa%l6A85vITNpF=tJ&Zl$=lYn2O&!Ai27@;lL7 ztFeaOJesRPQnrHv+J9azgp1%lQrc$Nj_$sOT3)G#i+SDCFP^HYGa{8&R;>6^dS`^y z>=%Og6Tit^?BR3+EYImGL?;)hqIczfOR0c&(3SFRw^l7!a< zqr|O63b#LezB&UV5c8F^MGw~7(%6>JOcz!;HaPzaozZ~y-yK(Yo34_6Z=$^)DZZi+ zTwHqwzU;KVy$sG@W1@G9u9t8u%RmH=8a+#=u4J;isH|uI_REIe=0y@cRQNe|0j!hw zdviJLNE{R?53R}f{!l`L^vAJ&o@X1V(Cp#v+qia)bt$S6P_VPe5twl{c_*Fl@iwCE zU`4fsGV>Wvi2NgRqW`Lp@ld)A)XMaa>f~t42al+&rE=~A%je~CDeUvthX$Rq2c-Q6 zT%1l7QYY)G`ww^steDnJ*V8pEATt=CM9L>une_AM%!ACm?bLRrGYFp{#Vt zsW!%cr|?%!kQu8syNkNFv4VnygoT6hXz{{;_m&S16}7hNDa@@6UAdFyofG#BbD>@CD^3h4~bNIw62AQj`i`N`oT&VpLoWvNU!w zAHI7otp>4-T)3rQk+p}u42f?*!2edAUb&k5;FqSaCAUzXI=fZH+$9kwjgH}Wnu~Y| zIQK0+V8KACrhs{pEIS%UQ?_Np{^{6F1n6qd#?R$jH`nCf&-H*AWZ>?t4)7~X7^}9V zM{!LTc#r~_Dy+`KzhHn#CLYCjAJhM?_*0uSP?3z3CzJElM!p?c85b)HT?825@^20s zVhj21PW*{foU+og=Qf+i^Li!?1uVG)P@QNkt?tqyk zrnb1)0PlQyLnv;bHp0495LS#*pX9r*ml7&__ozpwOWo%2E_u6N4k3}ID-cWr z&Z@>6TBq@Sw&j%9lQg9}^b7w^>H{S|-TR^xc`%V#s5?pJKs!mRAMS7%gr)zMqDY1KzMRQ1keTVOKA`16j2> zz;+fYY{Hup6LBWV?&|<4xv*#HkE+J;xcm84|7jm5_^|yjk-Jb04~1y61n(%9LDV5K zLs;Qvgd}!^`R1O>s|;w%mN|xt4Winz?=6@%QymVX(GMH1`X*Kr>mOjk$J1JA{5LnF z9(`vm&ZROtxlHpNhqAH{@sD_nd;^Dw&#(v^n=f6#eyZjYNc4ZC3=dPAv56mr_g^-+ z&!SDU&L*3@pIPzF<#QLFEJCC47W?@oHo36O;3vj|$G!RLQVg`5h%egjdI(VOO4fpt z$lh2fs|{!${tf@wAcMsNK_rLwhaZL!2r(8cvDm@`Nv^Zi2O z2f}~0tnh|>^LQHeJha4RM^rK(^5QpON`6s*Al_npWyW^{QL^}K5>mjBf!loWcCU?8 z3PTG*<){;zFq$!%g61%ls*5WsDpX`caqDzdk^+yQP|oumFE9n(-f@C%`>+K}-J> z6E0vu^M)LDuyJHo<~K4xg{_>HHyJw>7ZFx!rt(6V*&-tqM}bH<$5TNrMW_QpA24hN ztk|EU6U`6g5Cs7|b0YJWRBra>F)Mrqt4n9TLw5(g3H*ayrJ3Wm$4RtM@8?G*%lC7% z))dB(?RP?1TouxfSIi{KGY^Cy|*hye#X|ZQBbLAi;`-@gkBnTZJOyKO%J5EDJ zRnCt%F4}S!)>5*-)u$qDU*i&c+4ObnlDuoYZ3{xR$@eI$3`1d)-fcaZ^|kjpFWGYS1s(w@ zNBv2EBzvs}?(wx2@?g+!hk26aw9d9x}e5%x`HJ?*)A?xYd{-}0iM z;?ONpVZwGfh|Dyq>96db8@ZKYuA(5N-^Hm5r3Tx~2#F3oAG3SZ>rBGpryA~L3z&*w#rGa=RdXYSUz(_-y)d#vyDj8S z`Ksy6syX*4op+GOyCG8p)~b`)yCJgXBrN+pqvza)2@k9lx-hnJ``VYjdDuA6Q7G>s z-3-$`Xz!isB5aSh)sw~J?f?6W{fKH`>7lN{GQlVLZ!?H^70FaIm38ifpF;O{f+tTM z_X`3S#^F&{bG9kmKt*xN|MFGMm%*KAP!%2M5M!*!2RL~#s)Z#SLAPtdF!+u`YIeBd z7Y)t@M0Aw%OeTAOv4FV6G`O_HeuO*GFpO-pl!k4blTz`1XvK@YR2tSg!*%SVYWF|- z;+mmw0D_CpvS8({fu)P^2B6H`54W%-144g7 zkW+JUn74u(FNemRMBmGT=YO15nCk#1Z~Eo#M%+3?{XmNT_|iRp^Q@X3j~|%I07M*+ z?@m{HaT&gj(qWTDQ#V6eUdEPfCH6g%Z6vb(AjxhLwH|N0p?S2%HfhM_rx6Wb)>a|v zjc+A_z}i)BR{v2CMx{~(h*>q@Bp88+=o&RbrOrBirJW#s#hM_U4&TmAE=!DFlgoyX zJzR_)5!7UTjIOwv^x506FIvX!?-E~X{QxK2ZK=X$gl<61sY_7}arm>MxmKsq(iU~; zXJL0u9Lvu+2+1`oA=v?KyAs&9(smPP7+qUO@7O#~e`7)Qz{92eUeFB?f@9Jlk}Hlk zmv@3M#F`@*$UH;18|?Ib$2Y8FIKVqR9peQ6byWpRFv)c$rS zR@=ZVr5Lh@vVuJWX*N>l>BlnZqT8OMMh8}Kb@flIrSpF6;IHaLKnQ1^=YEM~(jlvX z9tvM-jmU9P?FTb?-Avbyk~+1@YN@W|&RI4DX294(;xS2yrDRM0Kym&-H-{_2{sH*OO zur5E3Kg-Qa3mdL`8Sb9v0vKbv6<;OP3neILWR*NkfB5)ToX!09^_u3y5$s83S%9jo zGg?lu1><#?O(GLjmy02Y=<)7%YAyFW8FDo`Xn((}IN0g!EzT9E&T$j8XFc#^z6UQ; zJ*Yb@hyMI0C@Y5M1GI@@yFWkuWx21;%+5;5L?g9rd^W&Mr(ktauB!$xFQ`#Oo3w1p zGg9P>eW9tFG#sgS8{tbW7Ms8N?z@7ON?qmEPiBkZ?2vqdCW2C`tP`=-Bs9ThAcs+o z0X#X3KWzs4l^392vm@PjS5H%gtjRbHO%hM7?LANJJWWOBxK@{-fGZM5Qys^7v#IQb z)t0~e@s$=$I3mCS=-!PO?rVZ3=B$?In{|Ib)hMiD)2J@Uo)atTx>d2Nm;d@blIeLs zI%z9RSPb@@{c10->)v9uCTQ0P_$J10wMTcXHp<&%nQ5ri;JFoC{mG$nsV4B|f#47+ z1je9RlPf~3;9h!~!@NN8*Db_HjLP4K&Gx0?#{#ofGcx&jxz1V6epuoN#?i*k(HtkG zF3G3%i>B{BqU&|?)|x;xl=SU>LE>R1L}=CMksTkp(S~mvQ)>4%&xXQ%-ty$>-mvaO zJh)z*D_TZL_(0P9vLcyK2XkeR#=3tb!iW%3r&T)ji<+B8Q7BB&%sP&+;`y@QHdH1$ z?aa4nSD~FfKWGZ`@`K*!onpK*PgbJj4JuP}@-})_vu?v)&Z$1QiHwn=US!l(40$=C z`IB~O12iNe=uL8NC7fO)i7S&;H8nJQmM8y~5F?8jG5K25_}eCEj1cspgo(E%Ij@H5 z0irw?K7TFtOPTV`mZveBWs$E5#7`fcK62lhw;&8_D?PRNM&W${Z?> ztDdT58x?cAj91!t__#&t1&2)i=WTBE-G8<$Jc{8l-cK1J8-DZnTO*bU%k{q z$(DP=bdUVriC+f3pemn~=L^T}4>;gCFR3)ywz`cNb*&>xg#A+hWZ$xKb6OerfiE&8WROY)@x5}&cI)XFe>{~Y*{tM2gJpZsCp5AWXAHr3 z%p{e}zFAta{*(W_RZ17w@YvtqswhfcP}%GrhPlU5{$`e{yG$4#XZpD^FCU<+Ypy=I z6`}2$3emLY7B52UY>e5#4%fLlv=|!R`(~Td7=%J^Dy!K+Gs!JGx?4V?_AN-S@sHMI z`a7yz@=~s{$Q_h}bFt^53DEq8V|q7>^Xbz|`rC>ZKI)@Q4S;`jJ&Xd<)HpH)}G z%Nj*cEgXl=2E+HldH)8Sn*{n93y_z_bJ@w_@8WY=$-hjyf7{8rz1Lk48=ZXh?)-q| z=f4x+G{K`g8%{f@_{U7nWlk1Dv8+bbmh!421=dT+pfqK8b1+E%V&L)vuwcWu7Q)aj zBSzKX&t&@JstB!?hZsN?pxZT1wZJU@N&Hx}#v;v8VKa+Xv+@LPs#tD4<2tIcJTU?; zMg&?M&5eV?W8hx%uTsuQBCZ*SnSO-Gl9=rMZC@-&6Q{LL4PRzeAcq(PuUfb-SJl!b zVUd60`YwxWp~5}lYK5xmROcVd#DWHDn(D$EtwI|^XlxUelu0!tJ}P?MtM5-RD8(e# zKQ8DlyB%~FL&?pv zrF1k#6zDOpgZwI_`CjU9C)3w*XMs}ig(`6xKL&8GL9@U$ekWN4wF9zn2>AMLbQNp# zQz6hqX3#9Zfxp`(nR&4v^$Z;zsIgO-OdqYKasQW0wW8e*9{7R5TSOEid3RwWicZ*| zW;yf#h;PyTg=)y`8u+^-!fv$2|45^T4i(;p;TQu!H7U4y(=}3~Ld`WCas%QqFo>Ox z{PW>wum?kiE;TU?v|=lOxM55!DaR1wXg zX@7H>bm^u_UImZlJCZV7e!T8{2@IPUO1yd zG}UGQ?1v69Blk_y@qA)|I-gUE!4tvtOy6w4qi>Ezc7Svnu0#i>1h04F@}t(@zo}ms zjDHlqAAP^haNC{4w?*N9mdx5~`&h#@s`wRyxuAF=&s3)&O|lcnviKH*|1-^}sFvT*!@v-IZ!*AI<1o=l>zZWcyp>@J z0kc+sS-Y8GZO>uv(dc(F*~=yo)!+L_uJgf(&9MEu>1s&v#WzgkqDq$k*VI>sMfHW< z3XF6}iO8o1?lxKvKnV`rUkgToiqkHxc`n|I`UTsX$%_f#_o}Q=2IG* z$eOs*-SK;kGXAFtfv*cc3=V_ZQP9h=pTkwQSUj{h{ODg}yyB+3lWbZWDfGSL(rb*V z_9d6K_C9$hc}@q}G*rxNQsZU*AB)4=D@z3px$sYmt~rXE3UHWxW#>)VJvAPHx<#GB|@R*d81pbqGY!5FetAqKTy@|xXh0}(w zK+Zw_btC8X>@I;IAG)6>DmwKITurR*af*2Fv5cjR3~RecftinlV?x^$j=E~)ZNxMi~g z??l4UD*p((w1tI$><0xYY4B?GxGBu5z?_qp3m5)&IuSP>`3W$C3y$g2>Iy?e6YwXlO`GOe`!c{Qdj)$;nA&CCdu>y-0Rjf z-)twsbki#C@bbTJTK}ea358|8)gOI_XRkBJx9Q6_RQ+mk5!!o!hYW!K%-8|EgE9yH zH*VQ<8Rsa=u-E2cd*%ny^R+gj7Cs-8f$?l>2Y}f4`1sgZUyRIQT1B1M81XpHNtsXR zD|FLRxRamWGL&mFL4Hb6;n>cGh*A76i|n$Z=S4X8s*d%u`i-YxBLgtL%2#VWV!CcQe+zQ=We6Y8TJ*gTZfMBl90=0!h+^`t15BH8Gmw z|Mub1ag5_k1DD|qaAFgHtu0sg>$s|A>SRh;5nm$Ri^P;GB2joa*DwIZugV&I5rdR+ zElY7HERbhrT-ju^!o;a+U>X<1!*J7R-GByymhgPtGska_KKN3mAKGTw=g2Qy(kFbn z9Fn;dw9h(+iYsb$;1h;wHWp5?0rI9HcqlI{m00^2lM` z-|V(knQ>1RVO7`l3j@OExR6ze$^&LXTXiizZ(cuUwU`am>q2~F^HP+meG67ry;tym z$0=}qwL|CCasLbBox!Fn=NHWiKGO=xslu;Pd8x(Yt#tuht->agB6voMngp0>E@)|) zNFp*vd}SJtq}Z?(BZ0Y9p`Uz}WlWtOXT<{7sCnRLAD78zeGeMm^lyYK2gwI6!;QaD zT}aTg2s&c;?4j45+O*{;=2YS$-t@G`V_$z)n#?Ya&f`{;g(81_Xg3=KJ^pJKiY`?8 z$fhu|XVMI{3wc-M17XVtVc~j$aKuo>x4SSXy;pAnRSSwt1ca&IF8FfH;^JI0lGma@U;> zt@F7&e$Eo|*L%}-SIOPoncp;nR-%A(Bf4Fy|#*D-kTyGlAX(g zSijMROD3_{ZA{~9U;k;#U!0KfU%qf4wxo64_fbD#)>2~vIbJV7N6ratT6BuTETk?| zxJ-u3Qm+J2|7W9|8j&0n-R9Soj1{0@=aygica0BNMD(eipxa;pV{|FOnZ*lx^(1$X zR_)zIF|=?rilp2p1yRoTtAE=2@K)8U9e1KrUaO;V8I#ui1Uq5U3QuEO=j`&O_4#4J zSCUtd)m#{-tK=D4X!uVE`*(BG68yCfdH7=;{I)&hGKI7IHcYJf3+BQ<*M_}QHpgbx z!{z-v3rsw0nTQ`hhZj#(H5!&ZEDFjmeZuA*ubvqrQhis0z@8X;wqwgwE2H|`o2fNi zddY~?#zSY`5DU*R>@cb%+{#}`^~NMx0UMaz{n&%Td)D@>P_ zx71pre%OlPT{-mQI)z)Y48+p6ysSSMCvLJu&d8vrEJItMM4I`0ClC78Tfh$-G8aFuQ-k z40bdYN(G*Q$S<&QgpN%11^z54e+vB5Mh0V;P`SQ|W?qnIjwCJBnP$HNg^Lo|nXGo( z6aEVjZBPFt3$ikakT%iPUGVjSTLA21BMy$sSE^6bU)UcAExfhd82oQIb#wnlawDq2K-*ORJTD_N!ZdvJSK;#a2BPwP`k-@oUn zT=m}q{Mukb6+ReR)eU&q@}DIsWE{bjOo)%)r&(rZU|{(8v43c2Xk=t$U|?Wya1iFi z)ZZ@!)7@}l6BFN`Z4Y6>)Lj%Ox1KpblNFN4^}#_Hb~|zG^Wh>0dG9dMw_1L;?b*yh z@O>LhnHE1gRmSb#qyG)|`TEsSHoUv94*?1XvxLxTafS1#X{N;q2~;TfRy4gx_Bu=g zL1;jSf9Xy%@WNTJimx=qwDNpKub&XkIT%(WxPii=FyXQfw!*MsU#-6N6Xu0UthirN zIC8A6%`?vNoTDc9J{- z*V8=)dQ?6nlqu#GgaL>HbAPj!#51ScU?l?A*G4p5(E+5pB(NsOi6lN90 zeoglGd)8RQZ65h#XlY*Wg3=5*^)2-L@Gp*;KA-ToJLVm!AM@~Q?vwX4J`l8 z7I3}!pzxFxw#o|q<@7wCpx4vwlXn{Lt|v`oX7))ITx#K+c(M-g4V&1YSiEd(S!nN6rcOnkzK)eg3_gXX_g4T(%r?pt+;C zpDmuiyw_1N%Mg2+A>Z=imY9b(T9~TM$!F%pRcFFOLC>JDrWeuQSYYhZ61`odMEj~} zlPIvjs2$FMC*-Vc(`LEL7`>tBg29uC4klI84F8BBP@_MaI+TYj+Ezd9 zzTSJk$b%U5D;Ao6HEJ?d>t*utKCe=Wgcp{dT}5|H#gm724CV5BS<3#EvV|bm+txH@ z7ol9IbB-_5iiERJx-k7ycYIUAtv(ULgQkhq$Nq-y-`^~tsZ^_p(DbeHYqg^A)4T0p z@irnWa<<1N<=I{`Ef(XBAQ@_kPc1%<3oQoH6oG_Co4VZB<~;r?0EYDDOfz?AZ>Mba zPUC$+`?CgnbfO&a)02+!c7E85UBEhjQcORfoJEEhWp;{9f%#$Q9?pP9E|JA~o`WyK zmx92wNkz!67P{9AXHcL3IK`!jAU>G`kwl2)puxqhEHs&-{!ZcWJ^?;e?!)<2sE8jQ zJS~kX!KEs%lj?6ETq_$Sq<@O}(AkNEl(0Y=A!f2cgXqRLoa})iRe81(&4ZSjLZIGx zt1-N6&w$Sa6ZklWUr`|^`x#-WM%4eX{ah+au|xbV(+>dYr!$#_*0l>U@bK`wPKnsv zXTpqMU8=EO&CbbsOWH8JEQ%wUPt&%nyHj~|FopN#+8ubvtU;z~i(d>Th2FXHn_Tou zwhF$+89Yz>M$5#PDkn*Mkl#&K1)bssh9z-WAIxWY0iTw1fKaz++4iz2jh)Y*Q#;Mk z$^?yfW4|{ag9+GPh`8f;-j2MDlD-XBf2~EPl?N|-oK&^eZqyoYSB0&=uJn2#q zj!)lPHM=SB@OF%iUTKqRaT#=O9byfc$vsS!#h(*ov`g;1a95xNM>k|l>|~-P8mR2dN)n7Impf_*jj?oAP=!&m~^5WJpyTgAy+%m z-M*tHD&SYdTULj@S$wFcXh{Rx_u-=sgcco96!_R-?o}aAgR`eLh>-C^VpQgG{_xV> zpIjeo5iqug+Nh4>x8~kde_R*q+L=r}N(uCLjR(4}jVcvb|D`aQ$N~LtC8!SPp(W;T zOY3BZYcs}hbHPVgR^b&%rzj_LjDk&m$-a$AIFqP5qE6?%!8hnl## z>c-{r&Chwm%rSVA@mI9{up)0}~Sy%msmsjg5k01Lkd&m6g@e z(E)R2684E87xI35-~j`{5lDLqzRBp`12FzvxU1MiSydI0zYugQ3P!`8Tw={4&eYM% zB6VbxBt=Jrx%I)^SuL2#KTw~pQ0P@rWS_N3&HGqg zZlsr#Ncl2zsA?p$Rjt8Yu7lNZ&X)Ikc&#o?MLpCG<&0im2RMGl##Sc9-S~cMGcxMy zM3v|5Cw{t^BA{6Q4)&D)_=Xq$FGgdC_(c1su3v5Nb{#}wdgtd{|Mjr2eL_(E+pkY; zIFV&>c+mcA&5wWyZX(X|aEj83B{EY&^bha4C7*L?UcA|q)tn}JHzeOZ*z-Nc+2Y?& z;zJrg|5R4Jo6M|~<0yP&3Tj$yZ8O$kl5PbOE~mC*hir6_z-=snQX0%8OEaFlF|at2 zP8^Eku)kcJT(o1iaaN2A%Uz}Sm*nGlI$5vuZTno$fL>iRl zJK1}}Bxu16{vBNesM$DtyJ*mTG-o@1(?B83SCn>;pjr{)}pHyT|T;+L1 zg$9SiPWT(t`uvOJ(7djWSLlAUTP&B-$!@eFYrhvEk~n&vdhh-V-{}> zP>}?D4~7kzkre&ovIB08ePhma;2R*~(Nj}Q<##yU&q+itMvbB(OXOU;Hw!i(TExne z6zY28RGte;=5bppBXLF$1wu~Ff72b=)S$5|Yd8OW(sO7iRfxPE9x#uV%;#dIaPx4i zJv?jX)LHthYIKWRuxBtEoLy${n$`81A(ZZ1wKTonbg4*Rw=KFPbcaK+qGFWa%&PwI zW2!be{2mn|?8RvOYHnxQzkIO!kH{!w~xmul3w$AvyY@Tv+=Mkn3SG}HH}<|AFKcP!N!dXWK>KNn>pd$8?F zbQ%}_rb5mtzXoyO=-8y)Ol_5^v5NWW`P`%dI5YVe1{-UQI@d-{kBu3NS{i5d9-7|M zeI>8&a&H?ccU`F!RUQb{O0gtl(5yF`nhm?6Y}0UK49<#KD_8n8Y7@PfKn7G0$ml3S zUlqE{U?*AOvJtgg+~0S*)B75B#5JrY@?`*5jTjBm+A#wb&_UUo$!g4>y+4~sC>%?r z4NE>IOnA^A8ty59N3F@rmR=Atih}f@A95nRL@soykCAA&4~!B^JcwJ_f|F|q1Wz2J z9YR%dR6DiIB8OqvD1xS-c^leB5aSfqPGM7{{d)iQ*hIXpEQ+dD@(uI-AuEn!^G)QT zBHCCUyRgem+ZPa>1161G&YoTKaPL9qZ^P;C(D2O!AlMDR5pBFMu>sOdLcq)yY^C0* zo$lt|*rT+Ix1zm(fmCfa6i`x(Kp*7jnnzQcv99HDd6u;S_?DPHxGllxO-_&Dv*b;JH2Ou13 z|3Lj+If08I4Jhi-J(5y?1SC<;bFpMDNKbKHQ^UoL@lm%vbq1k6ULD>u8=T-XhmVj5 zShK1V$PVYr|8(KcA`mf;32$7SA65stolg=?5vIFqWs#?$yuhCSh$%f^=YU7j3Cg&!I zRYO9%6f^jIj<&FcRzTicon8ENlkJ1<>lI2>X0=W|>j&nh*m+NH0hM&~X%`o0*5`RX7q2xFr)0{ZdQYdCN4$>R$4da?~aNgOm zgJg}KbdkD*sVZ-zXj$l(nVA_G8DW(G1H6fm+-U&~G++&`zexgvfIFU=Gfv9Pe(jCM)o!99|V{6OPrjz4_2e zio3EXoe2VXdx&&p$-wk?wiNMkad6@=Q2v!H0X{yQG65dmE43X?zYbmPHcyo2wmB0z zx_KHXfHB@xSX%n9!81olky~%j!dD7;suKgjS;Hj^9S$C`Y~j9Y?s~SVN+G604DKjIgDC&O(9J8vnthy;Q$ZBc%(zj%(0R$uQ2_^aWT^7fx^# ze!rA?{ijO%o}YCKn6Lc3;A_q|l^Wrk@~QZ$>~ifg<{b$wm9!>tng1#h*f10J@AGsk z9bTuk%#Hvgxn<7}Rln&DOvA8O@MtHHtnRT$*w9%VPYjVF<_y6dPw?_NG9A=?Bq^Mx zE!*k!VDxHr4s+h4;vtTj*x)d&?dzs@VV|0g|JimcDIFmskf4;s?uoyMEPwj}8Y$dP z>93Q%(Ub`gMQdmgfh|})i(r^~m`eLZfvY4j${E_RP1+fxs)Xk~8+2lnXtTi5y^P#4 zPgTZMlPQ5Ya%L|*6hWS-u^3w{^mfs7ruj;?H4tm^?1rMKTsg-~sc0YnQ>_eqcss5X z{&9LK`)A7vQBkzh+B&UTljA!&V3|jL8jM8GCU6)X>39@o$;!|FC8}rJefdtIrUE)G zOBOjOJ%qQ+fzfWUlmehIzFa2m)W;UCC< zdVe=Zgp?g(|E5g2iru;_jl~=s*%2VA$eWrAu=B5KuTC+s6!Nk=jLiCih+g@_2|l>a z@tspJ>Ge@%YuUm=Vf-l=T}HaUEeQs*7tZt!{LvxP;vU`ercPsi^9O%114TQxd7*US z-k>%na^TAfZTa%`n>4?dViO>t?*C__AZ7T#*nL^9sQhCI@-;i8UU!LcU%|zF`P1wY zks%HHtcxWmEJ4QAJXdftdTdIxSh$<^7g?e}=jjL2C#hoX&(J#{f~9AU>F{Fi5Iw

h-*{V8HhA~oSqLPV-IFZp+PZ!TSO?(_hj%l2zrwB1?eiO($oiC=erk3Z`Qvo zg1%^5aI8ys}yD6&IUu*l-b@=OI zVWzm;U|?GfPM_}KahUzww<0gmLt+z_t=yVe<=Ko_uL%rII8q$?*8$nCa zdet`BC0P{6r+GsguPrKs8XDc!aSDRL8%)UA3Ie*k7U$BX&OKlMC!CDx+P&6K(#(SK zU1GQLhbG5Y??)05mKtf^Ry0daM7r7ST_~qS51mJhBw5r!%aCiny6S0vi*L{w7S{0B z#!Y+Bi8Zq*M1%<0cl?vW(IKlnpUcqHIeRCZQgGpy(!&CDtVw{5yF6|=xGm=VsjfI$ zG<4!enppF+8t?L-)tSiWBA!)>Thc%-b#w9^uEiRZHN^;;14oW|G!-Y)NItwg;igATH)R%PY2lEog3{d(V>Vr#w9rLzw#XfbWI8-{0B&q|R z@)ZKgDv19PbK?n;h@LM^cZv0o3?pnXGR;KvCX>i>P>0%l)JApLw%>Eb*e{y8cvUrdZ>S3p&s zCoi&fZZy4`GEactUEsab2H-p7v#_giBumQaNn6!wKUJuo5vOfG2_{P=t5RYSY z_S#M4`~fzz{LI5P)7%^-@h}m3Sz`)v&3$_j({~pFh>myW^y@5yVOARdx0l9kJy3r& zJ&R)%Q;EM!<9LpOh}bqujy^e}%WAH2*|dG&@mAPhIOvWPdFz}0q_O%Q3_{1;jh&V6 z$S8Xqlk9%n5z{^&-QH03{%zAY#`y&W+|2CJsi~zJ##CHfb#6&fFoWe^L5MnN4M3<^;L@qoH7<)| z&YCm7e!wwgxP=WdO=8c?cPd(r2IA88pBHlbk*y7)V=zR|n-8=n#uB}{;EO|dFb~2K@(HTVqSsRgXxwxBi-HlHkyRS z_c?`xhRdLA`aby-EdiH9UU$|~nSl454|BmA%BA zX5XHUOmWxuD)UMJRy7&sgO)387f;CLxXH)=SzVTvAzA0U#3|*vsqI2D1~P+ot1fZ2 zwptrEeb#)Bn}~#9@{b4Irdc#kaL|cpymfea-Sqasp2b-ox$`dgI!x(y(W`U-NN_{z zvuCIO$dV>~kE?A$3I5IbBgdL>Y6IK2$3~^j2p8$+HN$gA#*<*jJ?HnVk8XPc zcH;oz0)*zBr>hm_%~owyI-M@i4@bg1G!jWKen@b4vA&$G!GU*3HiFZrs z@!Mvv4ntiYiHkaEFOspIIJR!hvez0?Z_&upIVvF@wwR8}V7I5f>w3>yJ@ zxvM<(ENi0y=Y5(a{6~f-k-wJFJh&KsZmcyp$2={`5Ea&782KSxX@?KHIyiRy$qOg; z^vt=0n12K(LWt)|e^fa)ht|iefaHI*#OUJ2xx}Nh-o|v*p1I%$r#RZr^%`Ny#xrPD zZPoL2q|m?yA&e3FLEgtY4*z1L zk^9{xkzR%4ee@RY6+yKsqV57}<6p8mhl6<|kAU+=8SDfd`=NiJ15(AR5KlocU486( z+e`^fPn%mExkpYT#o~Wxf#PZj7@W6|49Vfen(M8u?#aNVJ}euM(?w^{W_Z`*@tBup zr)gDh@8<+y;=}e_RLWJl;L+|lXv?RM8H*xk4lbY`>voKLlS{L~d(dj{2IynJW-P6N zQq0D@h{bWc`7pJ&I3R|d%XQVCj7ow^pRDEi!7c@Wp5 z!A0Pl4Y6SH&m9Z8t#^Jffso5T){YAyAqR}FP!;0jrlnsAJ|ZnKo-)&T&1sGxmFXBwVun5?{|GQToac&~FA_x|b93*s+|9LjHVBsbr4qs;lUqWTQ9q;fnF(SX91A0&|91?A}# zsLqch8vw`kl9u*NsLca!OwXxsW7JfH6Q1_2p|b*;en0eo?-qiR&^4B$00R-mPdE2AWn=gj zF-D|`CrB|sCNCM)l0gN9ncQHD^_GRZm;qlmwG!+=oat7ok0oIz zH;30g4_2#T<~5BgA1hE6xP8W-5)8L;)z zNBf3H{d%4O%0)og@BM7^rKV+Vu?bq1#uoeTO~t1M<1Ea)N90d3G7moF}o%6S-xq`=1$ ztm^8#HQUPr$r9l0u5a9!?8kMo0J%qY=V0dzLxtH|aZn;U2N<(HDid6`8|jmBaUG&- z%8c=6wZZdg449lYfNejC6%M^B=x~APM!85^!@;%Jn_}fGF?ya$|1fiL7$6x+Fdy}B8-=|P=~7wVw3j$1GQLQmXv zfI5x$U?4nQ`XIPSOc)jdo}kxsk8bUnP8ZEXz5q3(?-Mata$L99rpHn>>-`Eq!>8bQ zZi$PUC|#}nEM*~R`SDexdKsW7BHrg|MMqU2NA`y3Uh?U(prg&~y{dEskKwHbP={L^ z1-)gT{k-6bsA^4Lnmb0fmE~!07?|>4k*P*WK3xSQ^MF z$&?1->fKW7lXYwNc;F_c^}4@L>YD_E)39HnvAT5Rz=rhna^Pkrb;G{%uS{*4czXlq zzr-!==VpGwIsFZ$oa4@Fx&%LcG1K8{8d&{RdE#xU`}4a?HU~#a#Kdu1^wr+iS|H93!aZkye5pY`?2`frp6ati4mEymaB;ek zOQtc$2N6_G2q?vkNhUN;5d%U<4OFI*j9im;kE%32Ni}ru4*uBn?LG*_z?G2a=dLSY#xNHcd(UNjoBMw`h!4BWJ`d+0ZzBH1$!&d0 z{NOlucWBLNfJ8X?lFLOjLROLrWj%?*eUIE?Hv8g!??8$8tfovdqP(kUTh_00NBwY7H9X3w3j$y`=1lh z9PTfmR^9Wbo59etdN*2wPqp-sAB(kZ$gY4sk4}My$qpZiY_1Za>%kvz`^Nsg74VMS z%WS0e1JL;~QOZ9G9@}z9X3F>qa9#R59pAeZ{HbA7*A*9cn-@&Uc)Wo8KHZeOxVKBa zOxKNW(54=3-ehrFi}=zT@p#C*sYW87P3H(gdO^)bU_RH4HN~28Dz;MdONC%?;YnrW z$yO>Vsw6`ufxdnhY>u-FC$t}(E4WN8lNBYKs_r7dz!x!VXcAwU8Bl6xqu+ zQkyiovoG9@44wM-o~b1H{KzKgIVx>w!ECaS$hJbekKdp)Skl~4kt!C35}bZ6BOo!0 ze_vQ}w<*$uYlwpl<2>Ai+)>zsP?-{B->>PkdFGXts1*_Gf~~WvikoO=;ar^8z{kFFy%`9RELl^6U_H+RADR3I+xSCMG62 zI)HRtjX6E7+0$ciFa=k2IeF_QE;t1@9>ntdL0_-)Ps(Qe>u!THi;rS!v1DRSPEJ36 z65YU#aSIxMK9a33SH4*hG-+WD*&DBD%nVwF4J^Sk!v(jwo<=jrqEHW3@U!rA`fvQ< ziqhfAyu}U?2IAC|xxcx*ylnSH4HEs|>xlWdRp5dX-kbTstmW1(A!>I4UMnylkrOZ4 zyTIesS&qt8iyQVj=d Date: Sat, 31 Mar 2018 08:25:17 -0400 Subject: [PATCH 25/43] bump version --- ImportExcel.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 8b02949..4e90000 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.10' +ModuleVersion = '4.0.11' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' From 593c586a24bb06688415fdbf49e4881b817d7b19 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:25:41 -0400 Subject: [PATCH 26/43] Added -PivotFilter param and code --- Export-Excel.ps1 | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 9de6d19..b8d3b63 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -342,6 +342,7 @@ [String[]]$PivotRows, [String[]]$PivotColumns, $PivotData, + [String[]]$PivotFilter, [Switch]$PivotDataToColumn, [Hashtable]$PivotTableDefinition, [Switch]$IncludePivotChart, @@ -711,6 +712,9 @@ $tbl = $ws.Tables.Add($targetRange, $TableName) $tbl.TableStyle = $TableStyle } + + $PivotTableStartCell = "A1" + if($PivotFilter) {$PivotTableStartCell = "A3"} if ($PivotTableDefinition) { foreach ($item in $PivotTableDefinition.GetEnumerator()) { @@ -722,7 +726,7 @@ $pivotTableDataName = $targetName + 'PivotTableData' if (!$item.Value.SourceWorkSheet) { - $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells['A1'], $ws.Cells[$dataRange], $pivotTableDataName) + $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName) } else { $workSheet = Find-WorkSheet $item.Value.SourceWorkSheet @@ -731,7 +735,7 @@ $targetStartAddress = $workSheet.Dimension.Start.Address $targetDataRange = "{0}:{1}" -f $targetStartAddress, $workSheet.Dimension.End.Address - $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells['A1'], $workSheet.Cells[$targetDataRange], $pivotTableDataName) + $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $workSheet.Cells[$targetDataRange], $pivotTableDataName) } } @@ -803,7 +807,7 @@ $pivotTableDataName = $WorkSheetname + 'PivotTableData' - $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells['A1'], $ws.Cells[$dataRange], $pivotTableDataName) + $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName) if ($PivotRows) { foreach ($Row in $PivotRows) { @@ -843,7 +847,7 @@ $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) if ($chart.DataLabel) { $chart.DataLabel.ShowCategory = $ShowCategory - $chart.DataLabel.ShowPercent = $ShowPercent + $chart.DataLabel.ShowPercent = $ShowPercent } $chart.SetPosition(0, 26, 2, 26) # if Pivot table is rows+data only it will be 2 columns wide if has pivot columns we don't know how wide it will be if ($NoLegend) { @@ -853,6 +857,14 @@ } } + if($pivotTable -and $PivotFilter) { + + foreach($pFilter in $PivotFilter) { + $null = $pivotTable.PageFields.Add($pivotTable.Fields[$pFilter]) + } + } + + if ($Password) { $ws.Protection.SetPassword($Password) } From 9e01d7fc0b17822006765dba5f529aec43ecf133 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:26:00 -0400 Subject: [PATCH 27/43] Added Example for using PivotFilter --- .../PivotTableFilters/testPivotFilter.ps1 | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Examples/PivotTableFilters/testPivotFilter.ps1 diff --git a/Examples/PivotTableFilters/testPivotFilter.ps1 b/Examples/PivotTableFilters/testPivotFilter.ps1 new file mode 100644 index 0000000..8217c4e --- /dev/null +++ b/Examples/PivotTableFilters/testPivotFilter.ps1 @@ -0,0 +1,23 @@ +Import-Module ..\..\ImportExcel.psd1 -Force + +$xlFile=".\testPivot.xlsx" +Remove-Item $xlFile -ErrorAction Ignore + + +$data =@" +Region,Area,Product,Units,Cost +North,A1,Apple,100,.5 +South,A2,Pear,120,1.5 +East,A3,Grape,140,2.5 +West,A4,Banana,160,3.5 +North,A1,Pear,120,1.5 +North,A1,Grape,140,2.5 +"@ | ConvertFrom-Csv + +$data | + Export-Excel $xlFile -Show ` + -AutoSize -AutoFilter ` + -IncludePivotTable ` + -PivotRows Product ` + -PivotData @{"Units"="sum"} ` + -PivotFilter Region, Area ` From 97275a99de110ce6beced6812750ad605a1669cf Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:26:05 -0400 Subject: [PATCH 28/43] Updating readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0641dab..ba6b7b6 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +#### 3/31/2018 +- Added `-PivotFilter` parameter, allows you to drill down into a subset of the overall dataset. + + #### 3/14/2018 - Thank you to [James O'Neill](https://twitter.com/jamesoneill), fixed bugs with ChangeDatabase parameter which would prevent it working From f33afef2f010962e9ea8cef7cbd92c890acbaa4a Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:32:48 -0400 Subject: [PATCH 29/43] updated readme --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ba6b7b6..9a68584 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,22 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new #### 3/31/2018 -- Added `-PivotFilter` parameter, allows you to drill down into a subset of the overall dataset. + +- Added `-PivotFilter` parameter, allows you to set up a filter so you can drill down into a subset of the overall dataset. + +```powershell +$data =@" +Region,Area,Product,Units,Cost +North,A1,Apple,100,.5 +South,A2,Pear,120,1.5 +East,A3,Grape,140,2.5 +West,A4,Banana,160,3.5 +North,A1,Pear,120,1.5 +North,A1,Grape,140,2.5 +"@ +``` + +![](https://github.com/dfinke/ImportExcel/blob/master/images/PivotTableFilter.png?raw=true) #### 3/14/2018 From 81fc0742f0edbb7e2cd4f28e5da87808fd67ee83 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:42:43 -0400 Subject: [PATCH 30/43] Added several new params --- SetFormat.ps1 | 54 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/SetFormat.ps1 b/SetFormat.ps1 index ec1cd39..80d043b 100644 --- a/SetFormat.ps1 +++ b/SetFormat.ps1 @@ -25,8 +25,17 @@ $NumberFormat, #Style of border to draw around the range [OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround, + [System.Drawing.Color]$BorderColor=[System.Drawing.Color]::Black, + [OfficeOpenXml.Style.ExcelBorderStyle]$BorderBottom, + [OfficeOpenXml.Style.ExcelBorderStyle]$BorderTop, + [OfficeOpenXml.Style.ExcelBorderStyle]$BorderLeft, + [OfficeOpenXml.Style.ExcelBorderStyle]$BorderRight, #Colour for the text - if none specified it will be left as it it is [System.Drawing.Color]$FontColor, + #Value for the cell + $Value, + #Formula for the cell + $Formula, #Clear Bold, Italic, StrikeThrough and Underline and set colour to black [switch]$ResetFont, #Make text bold @@ -98,7 +107,31 @@ if ($StrikeThru) {$Address.Style.Font.Strike = $true } if ($FontShift) {$Address.Style.Font.VerticalAlign = $FontShift } if ($FontColor) {$Address.Style.Font.Color.SetColor( $FontColor ) } - if ($BorderAround) {$Address.Style.Border.BorderAround( $BorderAround ) } + + if ($BorderAround) { + $Address.Style.Border.BorderAround($BorderAround, $BorderColor) + } + + if ($BorderBottom) { + $Address.Style.Border.Bottom.Style=$BorderBottom + $Address.Style.Border.Bottom.Color.SetColor($BorderColor) + } + + if ($BorderTop) { + $Address.Style.Border.Top.Style=$BorderTop + $Address.Style.Border.Top.Color.SetColor($BorderColor) + } + + if ($BorderLeft) { + $Address.Style.Border.Left.Style=$BorderLeft + $Address.Style.Border.Left.Color.SetColor($BorderColor) + } + + if ($BorderRight) { + $Address.Style.Border.Right.Style=$BorderRight + $Address.Style.Border.Right.Color.SetColor($BorderColor) + } + if ($NumberFormat) {$Address.Style.Numberformat.Format = $NumberFormat } if ($TextRotation) {$Address.Style.TextRotation = $TextRotation } if ($WrapText) {$Address.Style.WrapText = $true } @@ -123,15 +156,20 @@ } if ($Autosize) { if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.AutoFit() } - elseif ($Address -is [OfficeOpenXml.ExcelRange] ) {$Address.AutoFitColumns() } + elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { + $Address.AutoFitColumns() + } else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Address.GetType().name)) } } elseif ($Width) { if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.Width = $Width} elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { - ($Address.Start.Column)..($Address.Start.Column + $Address.Columns) | - ForEach-Object {$ws.Column($_).Width = $Width} + ($Address.Start.Column)..($Address.Start.Column + $Address.Columns - 1) | + ForEach-Object { + #$ws.Column($_).Width = $Width + $Address.Worksheet.Column($_).Width = $Width + } } else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Address.GetType().name)) } } @@ -140,6 +178,14 @@ $Address -is [OfficeOpenXml.ExcelColumn] ) {$Address.Hidden = $True} else {Write-Warning -Message ("Can hide a row or a column but not a {0} object" -f ($Address.GetType().name)) } } + + if ($Value) { + $Address.Value = $Value + } + + if ($Formula) { + $Address.Formula = $Formula + } } } } \ No newline at end of file From 7dad54f6e967c53725fdf5804fb896c31db13afc Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:43:29 -0400 Subject: [PATCH 31/43] added custom report example --- Examples/CustomReporting/CustomReport.ps1 | 73 +++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Examples/CustomReporting/CustomReport.ps1 diff --git a/Examples/CustomReporting/CustomReport.ps1 b/Examples/CustomReporting/CustomReport.ps1 new file mode 100644 index 0000000..a4e2682 --- /dev/null +++ b/Examples/CustomReporting/CustomReport.ps1 @@ -0,0 +1,73 @@ +Import-Module ..\..\ImportExcel.psd1 -Force + +$f = ".\dashboard.xlsx" +Remove-Item $f -ErrorAction Ignore + +$data = @" +From,To,RDollars,RPercent,MDollars,MPercent,Revenue,Margin +Atlanta,New York,3602000,.0809,955000,.09,245,65 +New York,Washington,4674000,.105,336000,.03,222,16 +Chicago,New York,4674000,.0804,1536000,.14,550,43 +New York,Philadelphia,12180000,.1427,-716000,-.07,321,-25 +New York,San Francisco,3221000,.0629,1088000,.04,436,21 +New York,Phoneix,2782000,.0723,467000,.10,674,33 +"@ | ConvertFrom-Csv + +$data | Export-Excel $f -AutoSize + +$excel = Open-ExcelPackage $f + +$sheet1 = $excel.Workbook.Worksheets["sheet1"] + +$sheet1.View.ShowGridLines = $false +$sheet1.View.ShowHeaders = $false + +Set-Format -Address $sheet1.Cells["C:C"] -NumberFormat "$#,##0" -WrapText -HorizontalAlignment Center +Set-Format -Address $sheet1.Cells["D:D"] -NumberFormat "#.#0%" -WrapText -HorizontalAlignment Center + +Set-Format -Address $sheet1.Cells["E:E"] -NumberFormat "$#,##0" -WrapText -HorizontalAlignment Center +Set-Format -Address $sheet1.Cells["F:F"] -NumberFormat "#.#0%" -WrapText -HorizontalAlignment Center + +Set-Format -Address $sheet1.Cells["G:H"] -WrapText -HorizontalAlignment Center + +## Insert Rows/Columns +$sheet1.InsertRow(1, 1) + +foreach ($col in Write-Output 2 4 6 8 10 12 14) { + $sheet1.InsertColumn($col, 1) + $sheet1.Column($col).width = .75 +} + +Set-Format -Address $sheet1.Cells["E:E"] -Width 12 +Set-Format -Address $sheet1.Cells["I:I"] -Width 12 + +$BorderBottom = "Thick" +$BorderColor = "LightBlue" + +Set-Format -Address $sheet1.Cells["A2"] -BorderBottom $BorderBottom -BorderColor $BorderColor +Set-Format -Address $sheet1.Cells["A2"] -BorderTop Thick -BorderColor green + +Set-Format -Address $sheet1.Cells["C2"] -BorderBottom $BorderBottom -BorderColor $BorderColor +Set-Format -Address $sheet1.Cells["E2:G2"] -BorderBottom $BorderBottom -BorderColor $BorderColor +Set-Format -Address $sheet1.Cells["I2:K2"] -BorderBottom $BorderBottom -BorderColor $BorderColor +Set-Format -Address $sheet1.Cells["M2:O2"] -BorderBottom $BorderBottom -BorderColor $BorderColor + +Set-Format -Address $sheet1.Cells["A2:C8"] -FontColor GrayText + +$HorizontalAlignment = "Center" +Set-Format -Address $sheet1.Cells["F1"] -HorizontalAlignment $HorizontalAlignment -Bold -Value Revenue +Set-Format -Address $sheet1.Cells["J1"] -HorizontalAlignment $HorizontalAlignment -Bold -Value Margin +Set-Format -Address $sheet1.Cells["N1"] -HorizontalAlignment $HorizontalAlignment -Bold -Value Passenger + +Set-Format -Address $sheet1.Cells["E2"] -Value '($)' +Set-Format -Address $sheet1.Cells["G2"] -Value '%' +Set-Format -Address $sheet1.Cells["I2"] -Value '($)' +Set-Format -Address $sheet1.Cells["K2"] -Value '%' + +Set-Format -Address $sheet1.Cells["C10"] -HorizontalAlignment Right -Bold -Value "Grand Total Calculation" +Set-Format -Address $sheet1.Cells["E10"] -Formula "=Sum(E3:E8)" -Bold +Set-Format -Address $sheet1.Cells["I10"] -Formula "=Sum(I3:I8)" -Bold +Set-Format -Address $sheet1.Cells["M10"] -Formula "=Sum(M3:M8)" -Bold +Set-Format -Address $sheet1.Cells["O10"] -Formula "=Sum(O3:O8)" -Bold + +Close-ExcelPackage $excel -Show \ No newline at end of file From 066ab8f3487769897b07d2b5bec7759b1e606876 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:46:00 -0400 Subject: [PATCH 32/43] update border format --- Examples/CustomReporting/CustomReport.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Examples/CustomReporting/CustomReport.ps1 b/Examples/CustomReporting/CustomReport.ps1 index a4e2682..16d4671 100644 --- a/Examples/CustomReporting/CustomReport.ps1 +++ b/Examples/CustomReporting/CustomReport.ps1 @@ -45,7 +45,6 @@ $BorderBottom = "Thick" $BorderColor = "LightBlue" Set-Format -Address $sheet1.Cells["A2"] -BorderBottom $BorderBottom -BorderColor $BorderColor -Set-Format -Address $sheet1.Cells["A2"] -BorderTop Thick -BorderColor green Set-Format -Address $sheet1.Cells["C2"] -BorderBottom $BorderBottom -BorderColor $BorderColor Set-Format -Address $sheet1.Cells["E2:G2"] -BorderBottom $BorderBottom -BorderColor $BorderColor From c6dc928e111881ac71786b04deaa8a274c53d467 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:46:14 -0400 Subject: [PATCH 33/43] add new pic --- README.md | 15 +++++++++++++++ images/CustomReport.png | Bin 0 -> 65657 bytes 2 files changed, 15 insertions(+) create mode 100644 images/CustomReport.png diff --git a/README.md b/README.md index 9a68584..4774899 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,21 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new #### 3/31/2018 +- Updated `Set-Format` + * Added parameters to set borders for cells, including top, bottm, left and right + * Added parameters to set `value` and `formula` + +```powershell +$data = @" +From,To,RDollars,RPercent,MDollars,MPercent,Revenue,Margin +Atlanta,New York,3602000,.0809,955000,.09,245,65 +New York,Washington,4674000,.105,336000,.03,222,16 +Chicago,New York,4674000,.0804,1536000,.14,550,43 +New York,Philadelphia,12180000,.1427,-716000,-.07,321,-25 +New York,San Francisco,3221000,.0629,1088000,.04,436,21 +New York,Phoneix,2782000,.0723,467000,.10,674,33 +"@ +``` - Added `-PivotFilter` parameter, allows you to set up a filter so you can drill down into a subset of the overall dataset. diff --git a/images/CustomReport.png b/images/CustomReport.png new file mode 100644 index 0000000000000000000000000000000000000000..675f0fb408186e7da3bc6a9aa4e150ae6b59077d GIT binary patch literal 65657 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 BM3Vpj literal 0 HcmV?d00001 From 695c986b7899a17c6106a5f0f9b17123ca618a56 Mon Sep 17 00:00:00 2001 From: dfinke Date: Sat, 31 Mar 2018 08:47:52 -0400 Subject: [PATCH 34/43] Add image --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 4774899..0f07b30 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,9 @@ New York,Phoneix,2782000,.0723,467000,.10,674,33 "@ ``` +![](https://github.com/dfinke/ImportExcel/blob/master/images/CustomReport.png?raw=true) + + - Added `-PivotFilter` parameter, allows you to set up a filter so you can drill down into a subset of the overall dataset. ```powershell From b5177de50def2b9efe9b09c8f2675db5785bc087 Mon Sep 17 00:00:00 2001 From: Doug Finke Date: Sat, 7 Apr 2018 19:34:47 -0400 Subject: [PATCH 35/43] updated --- Examples/PivotTableFilters/testPivotFilter.ps1 | 3 +-- Examples/TryMultiplePivotTables.ps1 | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Examples/PivotTableFilters/testPivotFilter.ps1 b/Examples/PivotTableFilters/testPivotFilter.ps1 index 8217c4e..b8ff7e3 100644 --- a/Examples/PivotTableFilters/testPivotFilter.ps1 +++ b/Examples/PivotTableFilters/testPivotFilter.ps1 @@ -19,5 +19,4 @@ $data | -AutoSize -AutoFilter ` -IncludePivotTable ` -PivotRows Product ` - -PivotData @{"Units"="sum"} ` - -PivotFilter Region, Area ` + -PivotData @{"Units"="sum"} -PivotFilter Region, Area diff --git a/Examples/TryMultiplePivotTables.ps1 b/Examples/TryMultiplePivotTables.ps1 index 7b0382b..f53338f 100644 --- a/Examples/TryMultiplePivotTables.ps1 +++ b/Examples/TryMultiplePivotTables.ps1 @@ -1,6 +1,6 @@ # To ship, is to choose -ipmo .\ImportExcel.psd1 -Force +#ipmo .\ImportExcel.psd1 -Force $pt=[ordered]@{} From 243ba0bb3c036672fc7e3797db00f5173bbc1e20 Mon Sep 17 00:00:00 2001 From: JustinGrote Date: Mon, 9 Apr 2018 14:35:28 -0700 Subject: [PATCH 36/43] Add ReZip Parameter for MSOnline compatability --- Export-Excel.ps1 | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index b8d3b63..5748505 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -400,7 +400,8 @@ # [Parameter(ParameterSetName = 'TableNow')] [Switch]$Now, [Switch]$ReturnRange, - [Switch]$NoTotalsInPivot + [Switch]$NoTotalsInPivot, + [Switch]$ReZip ) Begin { @@ -1000,7 +1001,26 @@ $ws.Dimension.Address } + + $pkg.Save() + + if ($ReZip) { + write-verbose "Re-Zipping $($pkg.file) using .NET ZIP library" + $zipAssembly = "System.IO.Compression.Filesystem" + try { + Add-Type -assembly $zipAssembly -ErrorAction stop + } catch { + write-error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+" + continue + } + + $TempZipPath = Join-Path -path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName()) + [io.compression.zipfile]::ExtractToDirectory($pkg.File,$TempZipPath) | Out-Null + Remove-Item $pkg.File -Force + [io.compression.zipfile]::CreateFromDirectory($TempZipPath,$pkg.File) | Out-Null + } + $pkg.Dispose() if ($Show) { From 13652bc4ede7e8a1a3019de03a1fb21275fbc82d Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 10 Apr 2018 19:01:30 -0400 Subject: [PATCH 37/43] bumped version --- ImportExcel.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 4e90000..3075844 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.11' +ModuleVersion = '4.0.12' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' From 85a78dad7eaf879806158751310cf149eabbc5b7 Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 10 Apr 2018 19:02:51 -0400 Subject: [PATCH 38/43] Updated readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0f07b30..a54ef2c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,12 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +#### 4/10/2018 +-New parameter `-ReZip` +Thanks to [Justin Grote](https://github.com/JustinGrote) for finding and fixing the error that Excel files created do not import to PowerBI online. Plus, thank you to [CrashM](https://github.com/CrashM) for confirming the fix. + +Super helpful! + #### 3/31/2018 - Updated `Set-Format` * Added parameters to set borders for cells, including top, bottm, left and right From 5dd73789a39916801ffca4adaca695824148d5f2 Mon Sep 17 00:00:00 2001 From: dfinke Date: Tue, 10 Apr 2018 19:04:55 -0400 Subject: [PATCH 39/43] Updated --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a54ef2c..726c7d5 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new #### 4/10/2018 --New parameter `-ReZip` +-New parameter `-ReZip`. It ReZips the xlsx so it can be imported to PowerBI + Thanks to [Justin Grote](https://github.com/JustinGrote) for finding and fixing the error that Excel files created do not import to PowerBI online. Plus, thank you to [CrashM](https://github.com/CrashM) for confirming the fix. Super helpful! From 3d76bec6c43ae6e7ee67bbea708086c699d1841e Mon Sep 17 00:00:00 2001 From: Pavlo Yalandin Date: Wed, 11 Apr 2018 11:52:32 +0300 Subject: [PATCH 40/43] Updated Install.ps1 to install all needed scripts as part of module for PowerShell v4 --- Install.ps1 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Install.ps1 b/Install.ps1 index a827fce..66f7089 100644 --- a/Install.ps1 +++ b/Install.ps1 @@ -20,14 +20,19 @@ Begin { Write-Verbose "$ModuleName module installation started" $Files = @( + 'AddConditionalFormatting.ps1', 'Charting.ps1', + 'ColorCompletion.ps1', 'ConvertFromExcelData.ps1', 'ConvertFromExcelToSQLInsert.ps1', + 'ConvertExcelToImageFile.ps1', 'ConvertToExcelXlsx.ps1', 'Copy-ExcelWorkSheet.ps1', 'EPPlus.dll', + 'Export-charts.ps1', 'Export-Excel.ps1', 'Export-ExcelSheet.ps1', + 'formatting.ps1', 'Get-ExcelColumnName.ps1', 'Get-ExcelSheetInfo.ps1', 'Get-ExcelWorkbookInfo.ps1', @@ -43,9 +48,14 @@ Begin { 'New-ConditionalText.ps1', 'New-ExcelChart.ps1', 'New-PSItem.ps1', + 'Open-ExcelPackage.ps1', 'Pivot.ps1', - 'Plot.ps1', + 'plot.ps1', + 'Send-SqlDataToExcel.ps1', 'Set-CellStyle.ps1', + 'Set-Column.ps1', + 'Set-Row.ps1', + 'SetFormat.ps1', 'TrackingUtils.ps1', 'Update-FirstObjectProperties.ps1' ) From 33d86cb3c96b242ae65f14236601dd8bc2c83b8d Mon Sep 17 00:00:00 2001 From: Ili Metuky Date: Tue, 13 Mar 2018 15:13:28 +0200 Subject: [PATCH 41/43] Fixed the bug "Unable to find type [PSPlot]" Also added a test for it in ImportExcel.Tests.ps1 The previous fix was broke again by commit: https://github.com/dfinke/ImportExcel/commit/bd40cfe829d6128ac39296805ed5c41ff424bc0f#diff-f50d26003c0baf98f417a44fc1cac92b --- ImportExcel.Tests.ps1 | 11 +++++++++++ ImportExcel.psm1 | 1 - 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ImportExcel.Tests.ps1 b/ImportExcel.Tests.ps1 index 91d8042..faacee7 100644 --- a/ImportExcel.Tests.ps1 +++ b/ImportExcel.Tests.ps1 @@ -2070,4 +2070,15 @@ Context 'special cases' { } } } +} + +Context 'General Tests' { + in $TestDrive { + Describe 'Get Help' { + it 'New-Plot' { + #Get-Help : Unable to find type [PSPlot]. + {Help New-Plot} | Should -Not -Throw + } + } + } } \ No newline at end of file diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index 777f5d1..d88e8ae 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -39,7 +39,6 @@ if ($PSVersionTable.PSVersion.Major -ge 5) { . $PSScriptRoot\Plot.ps1 Function New-Plot { - [OutputType([PSPlot])] Param() [PSPlot]::new() From feb58689523c3bb02b75792c90bda90fc9bc8d97 Mon Sep 17 00:00:00 2001 From: dfinke Date: Mon, 16 Apr 2018 15:23:01 -0400 Subject: [PATCH 42/43] bumped version --- ImportExcel.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImportExcel.psd1 b/ImportExcel.psd1 index 3075844..807d835 100644 --- a/ImportExcel.psd1 +++ b/ImportExcel.psd1 @@ -4,7 +4,7 @@ RootModule = 'ImportExcel.psm1' # Version number of this module. -ModuleVersion = '4.0.12' +ModuleVersion = '4.0.13' # ID used to uniquely identify this module GUID = '60dd4136-feff-401a-ba27-a84458c57ede' From 6f921e1a3d0cdae5c328f0f543e0bf2a78ae7699 Mon Sep 17 00:00:00 2001 From: dfinke Date: Mon, 16 Apr 2018 15:23:09 -0400 Subject: [PATCH 43/43] updated --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 726c7d5..340d773 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi # What's new +#### 4/10/2018 +Thanks to the community yet again, [ili101](https://github.com/ili101) for fixes and features +- Removed `[PSPlot]` as OutputType. Fixes it throwing an error + #### 4/10/2018 -New parameter `-ReZip`. It ReZips the xlsx so it can be imported to PowerBI