Private/FontMan.psm1

using module .\Utilities.psm1

class FontMan {
  static [string[]] $FontFolders = [FontMan]::Get_font_folders()
  static [hashtable] $FontFileTypes = @{
    ".fon" = ""
    ".fnt" = ""
    ".ttc" = " (TrueType)"
    ".ttf" = " (TrueType)"
    ".otf" = " (OpenType)"
  }
  static [string] $fontRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
  FontMan() {
    [FontMan]::init()
  }
  static [int] InstallFont([string]$filePath) {
    return [FontMan]::InstallFont([IO.FileInfo]::new("$([FontMan]::GetUnResolvedPath($filePath))"));
  }
  static [int] InstallFont([System.IO.FileInfo]$fontFile) {
    $filePath = $fontFile.FullName; if (![IO.Path]::Exists($filePath)) {
      throw [System.IO.FileNotFoundException]::new("File Not found!", "$filePath")
    }
    $v = [ProgressUtil]::data.ShowProgress
    try {
      [string]$filePath = (Resolve-Path $filePath).path
      [string]$fileDir = Split-Path $filePath
      [string]$fileName = Split-Path $filePath -Leaf
      [string]$fileExt = (Get-Item $filePath).extension
      [string]$fileBaseName = $fileName -replace ($fileExt , "")

      $shell = New-Object -com shell.application
      $myFolder = $shell.Namespace($fileDir)
      $fileobj = $myFolder.Items().Item($fileName)
      $fontName = $myFolder.GetDetailsOf($fileobj, 21)
      if ([string]::IsNullOrWhiteSpace($fontName)) {
        $fontName = [FontMan]::GetFontName($fontFile)
      }
      if ([string]::IsNullOrWhiteSpace($fontName)) {
        $fontName = $fileBaseName
      }
      if ([IO.File]::Exists([IO.Path]::Combine($env:windir, 'Fonts', $fontFile.Name))) {
        $v ? (Write-Console "Font '$fontName' already exists!" -f Wheat) : $null
      }
      $v ? (Write-Console "Copying font: $fontName" -f DarkKhaki) : $null
      Copy-Item $filePath -Destination ([FontMan]::FontFolders[0]) -Force
      if (!(Get-ItemProperty -Name $fontName -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Fonts" -ErrorAction SilentlyContinue)) {
        $v ? (Write-Console "Registering font: $fontName" -f DarkKhaki) : $null
        New-ItemProperty -Name $fontName -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Fonts" -PropertyType string -Value $fontFile.Name -Force -ErrorAction SilentlyContinue | Out-Null
      } else {
        $v ? (Write-Console "Font already registered: $fontName" -f Wheat) : $null
      }
      [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
      $retVal = [FontMan].FontRes32::AddFont([IO.Path]::Combine([FontMan]::FontFolders[0], $fileName))
      if ($retVal -eq 0) {
        $v ? (Write-Console "Font `'$($filePath)`' installation failed" -f Red) : $null
        [console]::WriteLine()
        return 1
      } else {
        $v ? (Write-Console "Font `'$($filePath)`' installed successfully" -f Green) : $null
        [console]::WriteLine()
        Set-ItemProperty -Path "$([FontMan]::fontRegistryPath)" -Name "$($fontName)$([FontMan]::FontFileTypes.item($fileExt))" -Value "$($fileName)" -type STRING
        return 0
      }
    } catch {
      $v ? (Write-Console "An error occured installing `'$($filePath)`'" -f Red) : $null
      [console]::WriteLine()
      $v ? (Write-Console "$($error[0].ToString())" -f Red) : $null
      [console]::WriteLine()
      $error.clear()
      return 1
    }
  }
  static [int] RemoveFont([string]$filePath) {
    return [FontMan]::RemoveFont([IO.FileInfo]::new("$([FontMan]::GetUnResolvedPath($filePath))"));
  }
  static [int] RemoveFont([System.IO.FileInfo]$fontFile) {
    $filePath = $fontFile.FullName; if (![IO.Path]::Exists($filePath)) {
      throw [System.IO.FileNotFoundException]::new("File Not found!", "$filePath")
    }
    $v = [ProgressUtil]::data.ShowProgress
    $fontFinalPath = [IO.Path]::Combine([FontMan]::FontFolders[0], $filePath)
    try {
      $retVal = [FontMan].FontRes32::RemoveFont($fontFinalPath)
      $fontName = [FontMan]::GetFontName($fontFile)
      if ($retVal -eq 0) {
        $v ? (Write-Console "Font `'$filePath`' removal failed" -f Red) : $null
        [console]::WriteLine()
        return 1
      } else {
        # Get Registry StringName From Value :
        [string]$keyPath = [FontMan]::fontRegistryPath;
        $fontRegistryvaluename = Invoke-Command -ScriptBlock {
          $pattern = [Regex]::Escape($fullpath)
          foreach ($property in (Get-ItemProperty $keyPath).PsObject.Properties) {
            ## Skip the property if it was one PowerShell added
            if (($property.Name -eq "PSPath") -or ($property.Name -eq "PSChildName")) {
              continue
            }
            ## Search the text of the property
            $propertyText = "$($property.Value)"
            if ($propertyText -match $pattern) {
              "$($property.Name)"
            }
          }
        }
        $v ? (Write-Console "Font: $fontRegistryvaluename" -f DarkKhaki) : $null
        if ($fontRegistryvaluename -ne "") {
          Remove-ItemProperty -Path ([FontMan]::fontRegistryPath) -Name $fontRegistryvaluename
        } else {
          $v ? (Write-Console "Font $fontName not registered!" -f Red) : $null
        }

        if ([IO.Path]::Exists($fontFinalPath)) {
          $v ? (Write-Console "Removing font: $fontFile" -f DarkKhaki) : $null
          Remove-Item $fontFinalPath -Force
        } else {
          $v ? (Write-Console "Font does not exist: $fontFile" -f Wheat) : $null
        }

        if ($null -ne $error[0]) {
          $v ? (Write-Console "An error occured removing $`'$filePath`'" -f Red) : $null
          [console]::WriteLine()
          $v ? (Write-Console "$($error[0].ToString())" -f Red) : $null
          [console]::WriteLine()
          $error.clear()
        } else {
          $v ? (Write-Console "Font `'$filePath`' removed successfully" -f Green) : $null
          [console]::WriteLine()
        }
        return 0
      }
    } catch {
      $v ? (Write-Console "An error occured removing `'$filePath`'" -f Red) : $null
      [console]::WriteLine()
      $v ? (Write-Console "$($error[0].ToString())" -f Red) : $null
      [console]::WriteLine()
      $error.clear()
      return 1
    }
  }
  static [string] GetFontName([string]$fontFile) {
    return [FontMan]::GetFontName([System.IO.FileInfo]::new("$([FontMan]::GetUnResolvedPath($fontFile))"))
  }
  static [string] GetFontName([System.IO.FileInfo]$fontFile) {
    Add-Type -AssemblyName PresentationCore
    $fp = $fontFile.FullName; $gt = New-Object Windows.Media.GlyphTypeface("$fp")
    $family = $gt.Win32FamilyNames['en-us']; $ext = $fontFile.Extension
    if ($null -eq $family) { $family = $gt.Win32FamilyNames.Values.Item(0) }
    $face = $gt.Win32FaceNames['en-us']
    if ($null -eq $face) { $face = $gt.Win32FaceNames.Values.Item(0) }
    $fontName = ("$family $face").Trim()
    $fontName = $fontName + [FontMan]::FontFileTypes.$ext
    return $fontName
  }
  static [string[]] GetInstalledFonts() {
    Add-Type -AssemblyName System.Drawing
    $familyList = @(); $installedFontCollection = New-Object System.Drawing.Text.InstalledFontCollection
    $fontFamilies = $installedFontCollection.Families
    foreach ($fontFamily in $fontFamilies) { $familyList += $fontFamily.Name }
    return $familyList
  }
  static [bool] IsFontInstalled([string]$FontName) {
    $fonts = [FontMan]::GetInstalledFonts()
    return $fonts -contains $FontName
  }
  # static [System.Drawing.Font] NewFont([string]$Name) {
  # Add-Type -AssemblyName 'System.Drawing'
  # $fontFamily = [System.Drawing.FontFamily]::New("$Name")
  # return [System.Drawing.Font]::new($fontFamily, 8, [System.Drawing.FontStyle]::Regular, [System.Drawing.GraphicsUnit]::Point)
  # }
  static [string] GetResolvedPath([string]$Path) {
    return [FontMan]::GetResolvedPath($((Get-Variable ExecutionContext).Value.SessionState), $Path)
  }
  static [string] GetResolvedPath([System.Management.Automation.SessionState]$session, [string]$Path) {
    $paths = $session.Path.GetResolvedPSPathFromPSPath($Path);
    if ($paths.Count -gt 1) {
      throw [System.IO.IOException]::new([string]::Format([cultureinfo]::InvariantCulture, "Path {0} is ambiguous", $Path))
    } elseif ($paths.Count -lt 1) {
      throw [System.IO.IOException]::new([string]::Format([cultureinfo]::InvariantCulture, "Path {0} not Found", $Path))
    }
    return $paths[0].Path
  }
  static [string] GetUnResolvedPath([string]$Path) {
    return [FontMan]::GetUnResolvedPath($((Get-Variable ExecutionContext).Value.SessionState), $Path)
  }
  static [string] GetUnResolvedPath([System.Management.Automation.SessionState]$session, [string]$Path) {
    return $session.Path.GetUnresolvedProviderPathFromPSPath($Path)
  }
  static [string[]] Get_font_folders() {
    return (CryptoBase GetHostOs).Equals("Windows") ? $(New-Object -COM "Shell.Application").NameSpace(20).Self.Path : $(((fc-list).Split("`n") | Select-Object @{l = 'Fonts'; e = { ([IO.FileInfo]$_.Split(":")[0]).Directory } }).Fonts.Parent.FullName | Sort-Object -Unique)
  }
  static hidden [void] init() {
    if ($null -eq [FontMan].FontRes32) {
      Add-Type -TypeDefinition ([System.Text.Encoding]::UTF8.GetString([convert]::FromBase64String('dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uSU87CnVzaW5nIFN5c3RlbS5UZXh0Owp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLlJ1bnRpbWUuSW50ZXJvcFNlcnZpY2VzOwpwdWJsaWMgZW51bSBXTSA6IHVpbnQgeyBGT05UQ0hBTkdFID0gMHgwMDFEIH0KcHVibGljIGNsYXNzIEZvbnRSZXMzMiB7CiAgICBwcml2YXRlIHN0YXRpYyBJbnRQdHIgSFdORF9CUk9BRENBU1QgPSBuZXcgSW50UHRyKDB4ZmZmZik7CiAgICBwcml2YXRlIHN0YXRpYyBJbnRQdHIgSFdORF9UT1AgPSBuZXcgSW50UHRyKDApOwogICAgcHJpdmF0ZSBzdGF0aWMgSW50UHRyIEhXTkRfQk9UVE9NID0gbmV3IEludFB0cigxKTsKICAgIHByaXZhdGUgc3RhdGljIEludFB0ciBIV05EX1RPUE1PU1QgPSBuZXcgSW50UHRyKC0xKTsKICAgIHByaXZhdGUgc3RhdGljIEludFB0ciBIV05EX05PVE9QTU9TVCA9IG5ldyBJbnRQdHIoLTIpOwogICAgcHJpdmF0ZSBzdGF0aWMgSW50UHRyIEhXTkRfTUVTU0FHRSA9IG5ldyBJbnRQdHIoLTMpOwogICAgW0RsbEltcG9ydCgiZ2RpMzIuZGxsIiwgU2V0TGFzdEVycm9yID0gdHJ1ZSwgQ2hhclNldCA9IENoYXJTZXQuQXV0byldCiAgICBwdWJsaWMgc3RhdGljIGV4dGVybiBpbnQgQWRkRm9udFJlc291cmNlKHN0cmluZyBscEZpbGVuYW1lKTsKICAgIFtEbGxJbXBvcnQoImdkaTMyLmRsbCIsIFNldExhc3RFcnJvciA9IHRydWUsIENoYXJTZXQgPSBDaGFyU2V0LkF1dG8pXQogICAgcHVibGljIHN0YXRpYyBleHRlcm4gaW50IFJlbW92ZUZvbnRSZXNvdXJjZShzdHJpbmcgbHBGaWxlTmFtZSk7CiAgICBbRGxsSW1wb3J0KCJ1c2VyMzIuZGxsIiwgU2V0TGFzdEVycm9yID0gdHJ1ZSwgQ2hhclNldCA9IENoYXJTZXQuQXV0byldCiAgICBwdWJsaWMgc3RhdGljIGV4dGVybiBpbnQgU2VuZE1lc3NhZ2UoSW50UHRyIGhXbmQsIFdNIHdNc2csIEludFB0ciB3UGFyYW0sIEludFB0ciBsUGFyYW0pOwogICAgW3JldHVybjogTWFyc2hhbEFzKFVubWFuYWdlZFR5cGUuQm9vbCldCiAgICBbRGxsSW1wb3J0KCJ1c2VyMzIuZGxsIiwgU2V0TGFzdEVycm9yID0gdHJ1ZSwgQ2hhclNldCA9IENoYXJTZXQuQXV0byldCiAgICBwdWJsaWMgc3RhdGljIGV4dGVybiBib29sIFBvc3RNZXNzYWdlKEludFB0ciBoV25kLCBXTSBNc2csIEludFB0ciB3UGFyYW0sIEludFB0ciBsUGFyYW0pOwogICAgcHVibGljIHN0YXRpYyBpbnQgQWRkRm9udChzdHJpbmcgZm9udEZpbGVQYXRoKSB7CiAgICAgICAgRmlsZUluZm8gZm9udEZpbGUgPSBuZXcgRmlsZUluZm8oZm9udEZpbGVQYXRoKTsKICAgICAgICBpZiAoIWZvbnRGaWxlLkV4aXN0cykgeyByZXR1cm4gMDsgfQogICAgICAgIHRyeSAgewogICAgICAgICAgICBpbnQgcmV0VmFsID0gQWRkRm9udFJlc291cmNlKGZvbnRGaWxlUGF0aCk7CiAgICAgICAgICAgIGJvb2wgcG9zdGVkID0gUG9zdE1lc3NhZ2UoSFdORF9CUk9BRENBU1QsIFdNLkZPTlRDSEFOR0UsIEludFB0ci5aZXJvLCBJbnRQdHIuWmVybyk7CiAgICAgICAgICAgIHJldHVybiByZXRWYWw7CiAgICAgICAgfSBjYXRjaCB7CiAgICAgICAgICAgIHJldHVybiAwOwogICAgICAgIH0KICAgIH0KICAgIHB1YmxpYyBzdGF0aWMgaW50IFJlbW92ZUZvbnQoc3RyaW5nIGZvbnRGaWxlTmFtZSkgewogICAgICAgIHRyeSB7CiAgICAgICAgICAgIGludCByZXRWYWwgPSBSZW1vdmVGb250UmVzb3VyY2UoZm9udEZpbGVOYW1lKTsKICAgICAgICAgICAgYm9vbCBwb3N0ZWQgPSBQb3N0TWVzc2FnZShIV05EX0JST0FEQ0FTVCwgV00uRk9OVENIQU5HRSwgSW50UHRyLlplcm8sIEludFB0ci5aZXJvKTsKICAgICAgICAgICAgcmV0dXJuIHJldFZhbDsKICAgICAgICB9IGNhdGNoIHsKICAgICAgICAgICAgcmV0dXJuIDA7CiAgICAgICAgfQogICAgfQp9')));
      [FontMan].psobject.Properties.Add([PsScriptProperty]::new('FontRes32',
          { return (New-Object FontRes32) }
        )
      )
    }
  }
}