Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
Below is my script istalling Monserrat fonts from zip file. I can't figure how to check if a font already installed. After installation I can open folder C:\Windows\Fonts\Montserrat and I see al of them. When I am running script second time, it is not recognize existance of this folder. Where is my mistake?
$Source = "Montserrat.zip"
$FontsFolder = "FontMontserrat"
Expand-Archive $Source -DestinationPath $FontsFolder
$FONTS = 0x14
$CopyOptions = 4 + 16;
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace($FONTS)
$allFonts = dir $FontsFolder
foreach($File in $allFonts)
If((Test-Path "C:\Windows\Fonts\Montserrat") -eq $True)
echo "Font $File already installed"
echo "Installing $File"
$CopyFlag = [String]::Format("{0:x}", $CopyOptions);
$objFolder.CopyHere($File.fullname,$CopyFlag)
–
–
$Source = "Montserrat.zip"
$FontsFolder = "FontMontserrat"
Expand-Archive $Source -DestinationPath $FontsFolder -Force
$FONTS = 0x14
$CopyOptions = 4 + 16;
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace($FONTS)
$allFonts = dir $FontsFolder
foreach($font in Get-ChildItem -Path $fontsFolder -File)
$dest = "C:\Windows\Fonts\$font"
If(Test-Path -Path $dest)
echo "Font $font already installed"
echo "Installing $font"
$CopyFlag = [String]::Format("{0:x}", $CopyOptions);
$objFolder.CopyHere($font.fullname,$CopyFlag)
I am running this script by following cmd:
set batchPath=%~dp0
powershell.exe -noexit -file "%batchPath%InstMontserrat.ps1"
I don't have to run it as administrator, but user have admin permissions.
–
I will share the script that I use. There are some steps that need to occur first. Note: This script has been tested in Dev, deployed to Pilot, then Prod. in our environment.
for Montserrat font. I copied all of the .ttf files into the same directory (instead of the sub folder where most of the fonts were).
Saved the "Install-Font.ps1" into the same directory as the fonts.
To test locally:
Open PowerShell (as admin) and CD into the fonts folder.
Run the script to load the Install-Font function
PS C:\Temp\SoftwareDeploy\Fonts> .\Install-Font.ps1
PS C:\Temp\SoftwareDeploy\Fonts> Install-Font
function Install-Font {
param (
[System.IO.FileInfo]$fontFile
try {
$gt = [Windows.Media.GlyphTypeface]::new($fontFile.FullName)
$family = $gt.Win32FamilyNames['en-us']
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()
switch ($fontFile.Extension) {
".ttf" {$fontName = "$fontName (TrueType)"}
write-host "Installing font: $fontFile with font name '$fontName'"
If (!(Test-Path ("$($env:windir)\\Fonts\\" + $fontFile.Name))) {
write-host "Copying font: $fontFile"
Copy-Item -Path $fontFile.FullName -Destination ("$($env:windir)\\Fonts\\" + $fontFile.Name) -Force
write-host "Fonts have been copied into the Fonts directory"
} else {
write-host "Font already exists: $fontFile"
If (!(Get-ItemProperty -Name $fontName -Path "HKLM:\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" -ErrorAction SilentlyContinue)) {
write-host "Registering font: $fontFile"
New-ItemProperty -Name $fontName -Path "HKLM:\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" -PropertyType string -Value $fontFile.Name -Force -ErrorAction SilentlyContinue | Out-Null
Write-Host "Fonts have been registered successfully"
} else {
write-host "Font already registered: $fontFile"
} catch {
write-host "Error installing font: $fontFile. " $_.exception.message
$FontItems = Get-ChildItem -Path $PSScriptRoot | Where-Object {($_.Name -like '*.ttf')}
foreach ($FontItem in $FontItems) {
Install-Font -fontFile $FontItem.FullName
Corrections of your script based on my comment assuming Windows 10:
# well-known SID for admin group
if ('S-1-5-32-544' -notin [System.Security.Principal.WindowsIdentity]::GetCurrent().Groups) {
throw 'Script must run as admin!'
$source = 'Montserrat.zip'
$fontsFolder = 'FontMontserrat'
Expand-Archive -Path $source -DestinationPath $fontsFolder
foreach ($font in Get-ChildItem -Path $fontsFolder -File) {
$dest = "C:\Windows\Fonts\$font"
if (Test-Path -Path $dest) {
"Font $font already installed."
else {
$font | Copy-Item -Destination $dest
–
–
–
If you do not want to install the font on OS level but only make it available for programs to use until reboot you may want to use this script that:
Will fail/throw if it cannot register/unregister font.
Broadcasts WM_FONTCHANGE to inform all windows that fonts have changed
Does not require administrator privileges
Does not install fonts in Windows, only makes them available for all programs in current session until reboot
Has verbose mode for debugging
Does not work with font folders
Usage:
register-fonts.ps1 [-v] [-unregister <PATH>[,<PATH>...]] [-register <PATH>[,<PATH>...]] # Register and unregister at same time
register-fonts.ps1 [-v] -unregister <PATH>
register-fonts.ps1 [-v] -register <PATH>
register-fonts.ps1 [-v] <PATH> # Will register font path
Param (
[Parameter(Mandatory=$False)]
[String[]]$register,
[Parameter(Mandatory=$False)]
[String[]]$unregister
# Stop script if command fails https://stackoverflow.com/questions/9948517/how-to-stop-a-powershell-script-on-the-first-error
$ErrorActionPreference = "Stop"
add-type -name Session -namespace "" -member @"
[DllImport("gdi32.dll")]
public static extern bool AddFontResource(string filePath);
[DllImport("gdi32.dll")]
public static extern bool RemoveFontResource(string filePath);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool PostMessage(IntPtr hWnd, int Msg, int wParam = 0, int lParam = 0);
$broadcast = $False;
Foreach ($unregisterFontPath in $unregister) {
Write-Verbose "Unregistering font $unregisterFontPath"
# https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-removefontresourcea
$success = [Session]::RemoveFontResource($unregisterFontPath)
if (!$success) {
Throw "Cannot unregister font $unregisterFontPath"
$broadcast = $True
Foreach ($registerFontPath in $register) {
Write-Verbose "Registering font $registerFontPath"
# https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-addfontresourcea
$success = [Session]::AddFontResource($registerFontPath)
if (!$success) {
Throw "Cannot register font $registerFontPath"
$broadcast = $True
if ($broadcast) {
# HWND_BROADCAST https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea
$HWND_BROADCAST = New-Object IntPtr 0xffff
# WM_FONTCHANGE https://learn.microsoft.com/en-us/windows/win32/gdi/wm-fontchange
$WM_FONTCHANGE = 0x1D
Write-Verbose "Broadcasting font change"
# Broadcast will let other programs know that fonts were changed https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea
$success = [Session]::PostMessage($HWND_BROADCAST, $WM_FONTCHANGE)
if (!$success) {
Throw "Cannot broadcase font change"
The script was inspired by this gist https://gist.github.com/Jaykul/d53a16ce5e7d50b13530acb4f98aaabd
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.