PowerShell: Checking a Font for Specific Glyphs

I had the requirement to check which of the fonts installed on my machine supported Japanese numerals. The easiest way to actually verify this was by doing it through a Powershell script.

The method below only works on Windows machines since it relies on the PresentationCore assembly.

param (
    [switch] $Verbose
)

Add-Type -AssemblyName PresentationCore

# List of Japanese numerals to check
$japaneseNumerals = @(
    [char] 0x4E00, # 一
    [char] 0x4E8C, # 二
    [char] 0x4E09, # 三
    [char] 0x56DB, # 四
    [char] 0x4E94, # 五
    [char] 0x516D, # 六
    [char] 0x4E03, # 七
    [char] 0x516B, # 八
    [char] 0x4E5D, # 九
    [char] 0x5341  # 十
)

# Function to check if a font contains a specific glyph
function FontSupportsCharacter {
    param (
        # The font name to check
        [string] $fontName,
        # The character to check
        [char] $character
    )

    try {
        # Create a GlyphTypeface object for the font
        $typefaces = (New-Object System.Windows.Media.FontFamily($fontName)).GetTypefaces()
        foreach ($typeface in $typefaces) {
            $glyphTypeface = $null
            $typeface.TryGetGlyphTypeface([ref]$glyphTypeface) | Out-Null
            if ($glyphTypeface -and $glyphTypeface.CharacterToGlyphMap.ContainsKey([int][char]$character)) {
                return $true
            }
        }
    } catch {
        # Handle errors, e.g., if the font cannot be loaded
        Write-Verbose -Message "Error loading font: ${fontName}"
    }

    return $false
}

if ($Verbose) {
    $VerbosePreference = "Continue"
}

# Get all installed fonts
$installedFonts = (New-Object System.Drawing.Text.InstalledFontCollection).Families

# Loop through each installed font
foreach ($font in $installedFonts) {
    $glyphFound = $true
    foreach ($numeral in $japaneseNumerals) {
        $exists = FontSupportsCharacter -fontName $font.Name -character $numeral
        if (-not $exists) {
            $glyphFound = $false
            Write-Verbose -Message "Font `"$($font.Name)`" does NOT contain glyph for `"${numeral}`" (U+$([System.String]::Format("{0:X4}", [int][char]$numeral)))."
            break
        }
    }

    if ($glyphFound) {
        Write-Output "Font `"$($font.Name)`" contains all Japanese numerals."
    }
}

Personally, I would expect the operating system to be able to filter fonts, but unfortunately it falls to the user to gain these insights.

Content-based file search with Powershell and FileLocator

I love Powershell. Unfortunately, as soon as we cross into the realm of trying to grep for a specific string in gigabytes worth of large files, Powershell becomes a bit of a slowpoke.

Thankfully I also use the incredible FileLocator Pro, a highly optimized tool for searching file contents – no matter the size. The search is blazingly fast – and you can easily utilize FileLocator’s magic within Powershell!

For the sake of clarity: I will be using Powershell 7.1.3 for the following example.

# Add the required assembly
Add-Type -Path "C:\Program Files\Mythicsoft\FileLocator Pro\Mythicsoft.Search.Core.dll"

# Prepare the base search engine and criteria
$searchEngine                      = New-Object Mythicsoft.Search.Core.SearchEngine
$searchCriteria                    = New-Object Mythicsoft.Search.Core.SearchFileSystemCriteria

$searchCriteria.FileName           = "*.log"
$searchCriteria.FileNameExprType   = [Mythicsoft.Search.Core.ExpressionType]::Boolean

$searchCriteria.LookIn             = "C:\Temp\LogData"
$searchCriteria.LookInExprType     = [Mythicsoft.Search.Core.ExpressionType]::Boolean

$searchCriteria.SearchSubDirectory = $true

$searchCriteria.ContainingText     = ".*The device cannot perform the requested procedure.*"
$searchCriteria.ContentsExprType   = [Mythicsoft.Search.Core.ExpressionType]::RegExp

# Actually perform the search, $false executes it on the same thread as the Powershell session (as in: it's blocking)
$searchEngine.Start($searchCriteria, $false)

foreach($result in $searchEngine.SearchResultItems)
{
   # SeachResultItems are on a per-file basis.
   foreach($line in $result.FoundLines)
   {
      "Match in $($result.FileName) on line $($line.LineNumber): $($line.Value)"
   }
}

Wowzers, that’s pretty easy! In fact, a lot easier (and quicker, to boot!) than playing around with Get-Contents, StreamReaders and the like.

One thing of note here: Between running this on a loop for every file in a directory, it is actually quicker to process an entire tree of folders/files. The larger the dataset, the larger the gains through invoking FileLocator.

And yeah, you can use FileLocator on the command line through flpsearch.exe – however the results are not as easily digestable as the IEnumerables you get through the assembly.