diff --git a/src/private/common.ps1 b/src/private/common.ps1 index 9a8419c..4ea034d 100644 --- a/src/private/common.ps1 +++ b/src/private/common.ps1 @@ -27,3 +27,41 @@ $script:OS = if ([System.Environment]::OSVersion.Platform -eq 'Win32NT') { } else { throw 'Unsupported OS' } + +$script:SupportedFonts = @( + [pscustomobject]@{ + Extension = '.ttf' + Type = 'TrueType' + Description = 'TrueType Font' + } + [pscustomobject]@{ + Extension = '.otf' + Type = 'OpenType' + Description = 'OpenType Font' + } + [pscustomobject]@{ + Extension = '.ttc' + Type = 'TrueType' + Description = 'TrueType Font Collection' + } + [pscustomobject]@{ + Extension = '.pfb' + Type = 'PostScript Type 1' + Description = 'PostScript Type 1 Font' + } + [pscustomobject]@{ + Extension = '.pfm' + Type = 'PostScript Type 1' + Description = 'PostScript Type 1 Outline Font' + } + [pscustomobject]@{ + Extension = '.woff' + Type = 'Web Open Font Format' + Description = 'Web Open Font Format' + } + [pscustomobject]@{ + Extension = '.woff2' + Type = 'Web Open Font Format 2' + Description = 'Web Open Font Format 2' + } +) diff --git a/src/public/Get-Font.ps1 b/src/public/Get-Font.ps1 index c555059..f418835 100644 --- a/src/public/Get-Font.ps1 +++ b/src/public/Get-Font.ps1 @@ -75,8 +75,7 @@ Write-Verbose "[$functionName] - [$scopeName] - Filtering based on [$nameCount] name pattern(s)" foreach ($fontFilter in $Name) { Write-Verbose "[$functionName] - [$scopeName] - [$fontFilter] - Filtering font(s)" - $filteredFonts = $installedFonts | Where-Object { $_.Name -like "*$fontFilter*" } - + $filteredFonts = $installedFonts | Where-Object { $_.BaseName -like $fontFilter } foreach ($fontItem in $filteredFonts) { $fontName = $fontItem.BaseName $fontPath = $fontItem.FullName diff --git a/src/public/Install-Font.ps1 b/src/public/Install-Font.ps1 index 2579516..6e64223 100644 --- a/src/public/Install-Font.ps1 +++ b/src/public/Install-Font.ps1 @@ -133,6 +133,14 @@ Please run the command again with elevated rights (Run as Administrator) or prov $fontFilePath = $fontFile.FullName Write-Verbose "[$functionName] - [$scopeName] - [$fontFilePath] - Processing" + # Check if font is supported + $fontExtension = $fontFile.Extension.ToLower() + $supportedFont = $script:SupportedFonts | Where-Object { $_.Extension -eq $fontExtension } + if (-not $supportedFont) { + Write-Verbose "[$functionName] - [$scopeName] - [$fontFilePath] - Font type [$fontExtension] is not supported. Skipping." + continue + } + $folderExists = Test-Path -Path $fontDestinationFolderPath -ErrorAction SilentlyContinue if (-not $folderExists) { Write-Verbose "[$functionName] - [$scopeName] - [$fontFilePath] - Creating folder [$fontDestinationFolderPath]" @@ -174,15 +182,7 @@ Please run the command again with elevated rights (Run as Administrator) or prov continue } if ($script:OS -eq 'Windows') { - $fontType = switch ($fontFile.Extension) { - '.ttf' { 'TrueType' } # TrueType Font - '.otf' { 'OpenType' } # OpenType Font - '.ttc' { 'TrueType' } # TrueType Font Collection - '.pfb' { 'PostScript Type 1' } # PostScript Type 1 Font - '.pfm' { 'PostScript Type 1' } # PostScript Type 1 Outline Font - '.woff' { 'Web Open Font Format' } # Web Open Font Format - '.woff2' { 'Web Open Font Format 2' } # Web Open Font Format 2 - } + $fontType = $script:SupportedFonts | Where-Object { $_.Extension -eq $fontExtension } | Select-Object -ExpandProperty Type $registeredFontName = "$fontName ($fontType)" Write-Verbose "[$functionName] - [$scopeName] - [$fontFilePath] - Registering font as [$registeredFontName]" $regValue = if ('AllUsers' -eq $Scope) { $fontFileName } else { $fontDestinationFilePath } diff --git a/src/public/Uninstall-Font.ps1 b/src/public/Uninstall-Font.ps1 index 2b0f0bb..b3db2b4 100644 --- a/src/public/Uninstall-Font.ps1 +++ b/src/public/Uninstall-Font.ps1 @@ -1,4 +1,4 @@ -#Requires -Modules Admin, DynamicParams +#Requires -Modules Admin function Uninstall-Font { <# @@ -31,32 +31,17 @@ function Uninstall-Font { ValueFromPipeline, ValueFromPipelineByPropertyName )] - [Scope[]] $Scope = 'CurrentUser' - ) - - DynamicParam { - $paramDictionary = New-DynamicParamDictionary - - $dynName = @{ - Name = 'Name' - Type = [string[]] - Alias = @('FontName', 'Font') - Mandatory = $true - HelpMessage = 'Name of the font to uninstall.' - ValueFromPipeline = $true - ValueFromPipelineByPropertyName = $true - ValidationErrorMessage = "The font name provided was not found in the selected scope [$Scope]." - ValidateSet = if ([string]::IsNullOrEmpty($Scope)) { - (Get-Font -Scope 'CurrentUser' -Verbose:$false).Name - } else { - (Get-Font -Scope $Scope -Verbose:$false).Name - } - DynamicParamDictionary = $paramDictionary - } - New-DynamicParam @dynName + [Scope[]] $Scope = 'CurrentUser', - return $paramDictionary - } + # Name of the font to uninstall. + [Parameter( + Mandatory, + ValueFromPipeline, + ValueFromPipelineByPropertyName + )] + [SupportsWildcards()] + [string[]] $Name + ) begin { $functionName = $MyInvocation.MyCommand.Name @@ -85,54 +70,57 @@ Please run the command again with elevated rights (Run as Administrator) or prov Write-Verbose "[$functionName] - [$scopeName] - Processing [$nameCount] font(s)" foreach ($fontName in $Name) { Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Processing" - $font = Get-Font -Name $fontName -Scope $Scope - Write-Verbose ($font | Out-String) -Verbose - $filePath = $font.Path - - $fileExists = Test-Path -Path $filePath -ErrorAction SilentlyContinue - if (-not $fileExists) { - Write-Warning "[$functionName] - [$scopeName] - [$fontName] - File [$filePath] does not exist. Skipping." - } else { - Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Removing file [$filePath]" - $retryCount = 0 - $fileRemoved = $false - do { - try { - Remove-Item -Path $filePath -Force -ErrorAction Stop - $fileRemoved = $true - } catch { - # Common error; 'file in use'. Usually VSCode or any web browser. - $retryCount++ - if (-not $fileRemoved -and $retryCount -eq $maxRetries) { - Write-Error $_ - Write-Error "Failed [$retryCount/$maxRetries] - Stopping" - break + $fonts = Get-Font -Name $fontName -Scope $Scope + Write-Verbose ($fonts | Out-String) + foreach ($font in $fonts) { + + $filePath = $font.Path + + $fileExists = Test-Path -Path $filePath -ErrorAction SilentlyContinue + if (-not $fileExists) { + Write-Warning "[$functionName] - [$scopeName] - [$fontName] - File [$filePath] does not exist. Skipping." + } else { + Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Removing file [$filePath]" + $retryCount = 0 + $fileRemoved = $false + do { + try { + Remove-Item -Path $filePath -Force -ErrorAction Stop + $fileRemoved = $true + } catch { + # Common error; 'file in use'. + $retryCount++ + if (-not $fileRemoved -and $retryCount -eq $maxRetries) { + Write-Error $_ + Write-Error "Failed [$retryCount/$maxRetries] - Stopping" + break + } + Write-Verbose $_ + Write-Verbose "Failed [$retryCount/$maxRetries] - Retrying in $retryIntervalSeconds seconds..." + #TODO: Find a way to try to unlock file here. + Start-Sleep -Seconds $retryIntervalSeconds } - Write-Verbose $_ - Write-Verbose "Failed [$retryCount/$maxRetries] - Retrying in $retryIntervalSeconds seconds..." - #TODO: Find a way to try to unlock file here. - Start-Sleep -Seconds $retryIntervalSeconds - } - } while (-not $fileRemoved -and $retryCount -lt $maxRetries) + } while (-not $fileRemoved -and $retryCount -lt $maxRetries) - if (-not $fileRemoved) { - break # Break to skip unregistering the font if the file could not be removed. + if (-not $fileRemoved) { + break # Break to skip unregistering the font if the file could not be removed. + } } - } - if ($script:OS -eq 'Windows') { - Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Searching for font in registry" - $keys = Get-ItemProperty -Path $script:FontRegPathMap[$scopeName] - $key = $keys.PSObject.Properties | Where-Object { $_.Value -eq $filePath } - if (-not $key) { - Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Font is not registered. Skipping." - } else { - $keyName = $key.Name - Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Unregistering font [$keyName]" - Remove-ItemProperty -Path $script:FontRegPathMap[$scopeName] -Name $keyName -Force -ErrorAction Stop + if ($script:OS -eq 'Windows') { + Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Searching for font in registry" + $keys = Get-ItemProperty -Path $script:FontRegPathMap[$scopeName] + $key = $keys.PSObject.Properties | Where-Object { $_.Value -eq $filePath } + if (-not $key) { + Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Font is not registered. Skipping." + } else { + $keyName = $key.Name + Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Unregistering font [$keyName]" + Remove-ItemProperty -Path $script:FontRegPathMap[$scopeName] -Name $keyName -Force -ErrorAction Stop + } } + Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Done" } - Write-Verbose "[$functionName] - [$scopeName] - [$fontName] - Done" } Write-Verbose "[$functionName] - [$scopeName] - Done" } @@ -150,3 +138,13 @@ Please run the command again with elevated rights (Run as Administrator) or prov Write-Verbose "[$functionName] - Done" } } + +Register-ArgumentCompleter -CommandName Uninstall-Font -ParameterName Name -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters + if ([string]::IsNullOrEmpty($fakeBoundParameters['Scope'])) { + Get-Font -Scope 'CurrentUser' | Where-Object { $_.Name -like "$wordToComplete*" } | Select-Object -ExpandProperty Name + } else { + Get-Font -Scope $fakeBoundParameters['Scope'] | Where-Object { $_.Name -like "$wordToComplete*" } | Select-Object -ExpandProperty Name + } +} diff --git a/tests/Fonts.Tests.ps1 b/tests/Fonts.Tests.ps1 index d1bd62b..18bd194 100644 --- a/tests/Fonts.Tests.ps1 +++ b/tests/Fonts.Tests.ps1 @@ -49,6 +49,8 @@ Describe 'Fonts' { It 'Should install a font' { $fontPath = Join-Path -Path $PSScriptRoot -ChildPath 'Fonts/CascadiaCodePL.ttf' { Install-Font -Path $fontPath -Verbose } | Should -Not -Throw + Write-Verbose "Installed font: 'CascadiaCodePL'" -Verbose + Write-Verbose (Get-Font | Out-String) -Verbose } It "Should return the installed font 'CascadiaCodePL'" { $font = Get-Font -Name 'CascadiaCodePL' @@ -69,5 +71,15 @@ Describe 'Fonts' { Write-Verbose ($font | Out-String) -Verbose $font | Should -BeNullOrEmpty } + It 'Should install and uninstall a font based on wildcard' { + $fontPath = Join-Path -Path $PSScriptRoot -ChildPath 'Fonts/CascadiaCodePL.ttf' + { Install-Font -Path $fontPath -Verbose } | Should -Not -Throw + Write-Verbose "Installed font: 'CascadiaCodePL'" -Verbose + Write-Verbose (Get-Font | Out-String) -Verbose + { Uninstall-Font -Name 'CascadiaCode*' -Verbose } | Should -Not -Throw + $font = Get-Font -Name 'CascadiaCodePL' + Write-Verbose ($font | Out-String) -Verbose + $font | Should -BeNullOrEmpty + } } }