Blog Post

ITOps Talk Blog
3 MIN READ

Adding optional font packages to Windows containers

ViniciusApolinario's avatar
Jun 29, 2022

Customer feedback is the main driver for the Windows container team when planning new features and improvements to the platform. Since we launched containers in Windows Server 2016, customers have told us how a slim container image impacts the overall performance. With that in mind, we removed as much of the base container images as we could, including components such as fonts – which in most cases is not relevant.

Since then, we also heard your feedback about scenarios that do need these fonts back for applications to properly work. Today we’ll cover how you can add fonts back to Windows containers on the Server Core base container image in a way that is supported for both Windows Server 2019 and 2022.

 

Building the Container

When we removed the fonts from the Server Core base container image, we also removed the feature that installs new fonts. As you build your container image, you’ll have to incorporate the steps below to add the necessary feature back to Windows containers.

First, you’ll need an up-to-date Windows Server 2019 or 2022 host or VM as a container host. Putting removed features back requires up-to-date media. There are a few ways to acquire up-to-date install media, but the simplest is just to take a Windows Server 2019 or 2022 VM and let Windows Update bring it up to date.

Next, you need to prepare your container host.  You can use the instructions in our documentation page. You’ll also need to share the %windir%\WinSxS directory. For this example, we created a local user with a randomly-generated password (represented as <password>):

 

For Command Prompt:

 

net user ShareUser <password> /ADD
net share WinSxS=%windir%\WinSxS /grant:ShareUser,READ

 

For PowerShell:

 

net user ShareUser ‘<password>’ /ADD
net share WinSxS=${env:windir}\WinSxS /grant:ShareUser,READ

 

 

Next, create a file called InstallFonts.cmd and add the following content to it:

 

REM Connect to the WinSxS share on the container host
for /f "tokens=3 delims=: " %%g in ('netsh interface ip show address ^| findstr /c:"Default Gateway"') do set GATEWAY=%%g
net use o: \\%GATEWAY%\WinSxS /user:ShareUser %SHARE_PW%
if errorlevel 1 goto :eof
 
dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-MinConsoleFonts /Source:O:\ /LimitAccess
dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-Support /Source:O:\ /LimitAccess
dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-BitmapFonts /Source:O:\ /LimitAccess
dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-TrueType /Source:O:\ /LimitAccess
dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-UAPFonts /Source:O:\ /LimitAccess

 

 

Now you can add the context to your dockerfile. Here’s an example:

 

FROM mcr.microsoft.com/windows/servercore:ltsc2022
ARG SHARE_PW=
WORKDIR /install
COPY InstallFonts.cmd .
RUN InstallFonts.cmd

 

 

With a dockerfile in place, you can build and tag your container image using:

 

docker build -t <newname:tag> --build-arg SHARE_PW=<password> .

 

 

Yes, you will end up with the SHARE_PW in the build trace, but if you set it up as a randomly-generated string for each build, you’re not leaking a real secret. Furthermore, once the build is complete you can clean up the share and user with:

 

net share WinSxS /delete
net user ShareUser /delete

 

 

Running the workload

Due to a limitation in how Server Core containers handle fonts, you do need to specifically tell Windows about the newly available fonts in the container.  A simple PowerShell script will do the job.  This must be run after the container is started and prior to running your workload.  We suggest calling this LoadFonts.ps1.

 

$fontCSharpCode = @'
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace FontResource
{
    public class AddRemoveFonts
    {
        [DllImport("gdi32.dll")]
        static extern int AddFontResource(string lpFilename);
        public static int AddFont(string fontFilePath) {
            try 
            {
                return AddFontResource(fontFilePath);
            }
            catch
            {
                return 0;
            }
        }
    }
}
'@
 
Add-Type $fontCSharpCode
 
foreach($font in $(gci C:\Windows\Fonts))
{
 
        Write-Output "Loading $($font.FullName)"
        [FontResource.AddRemoveFonts]::AddFont($font.FullName) | Out-Null
 
}

 

 

Microsoft hopes to remove this limitation in a future version of Windows, but the script is required for current releases of Windows Server 2019 and 2022. Once you have built the container as described above and run this script inside the container, all of the fonts usually present on Windows Server Core will be available to your containerized workload.

 

We cannot overstate how valuable your feedback is to us. Please continue sending your suggestions via our GitHub page!

Updated Jun 28, 2022
Version 1.0
  • jgcrespo that's correct. My apologies. That's correct. If you notice on the InstallFonts.cmd, we map the O drive that is then used on the dism command.

  • hvc202 we have looked into it, and it looks like those two Japanese files are in a FoD: Language.Fonts.Jpan~~~und-JPAN~0.0.1.0

    The command should be:

    dism /online /add-capability /capabilityname:"Language.Fonts.Jpan~~~und-JPAN~0.0.1.0"

     

    We have not tried that command itself, so if you find an issue running this, please open an issue here: Issues · microsoft/Windows-Containers (github.com)

  • Hi jgcrespo. You missed part of the command from the blog which indicates the source of the DISM command is on-line. Please look at the command line: 

     

    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-MinConsoleFonts /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-Support /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-BitmapFonts /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-TrueType /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-UAPFonts /Source:O:\ /LimitAccess
  • jgcrespo's avatar
    jgcrespo
    Copper Contributor

    Hello,
    Using image mcr.microsoft.com/windows/servercore:ltsc2022 or mcr.microsoft.com/windows/servercore:ltsc2019, I cannot enable any of the font features except the first one ServerCoreFonts-NonCritical-Fonts-MinConsoleFonts. I get the following error:

    C:\>dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-BitmapFonts /LimitAccess
    
    Deployment Image Servicing and Management tool
    Version: 10.0.17763.3406
    
    Image Version: 10.0.17763.3406
    
    Enabling feature(s)
    [==========================100.0%==========================]
    
    Error: 0x800f081f
    
    The source files could not be found.
    Use the "Source" option to specify the location of the files that are required to restore the feature. For more information on specifying a source location,
     see http://go.microsoft.com/fwlink/?LinkId=243077.
    
    The DISM log file can be found at C:\Windows\Logs\DISM\dism.log

     Any ideas on what's wrong?

  • hvc202's avatar
    hvc202
    Copper Contributor

    I have built container with InstallFonts.cmd but some fonts were not copied from host to container.

    Example:

    The following japanese fonts were not on the container.

    *********************

    -a---- 2021/05/08 17:14 8990160 msgothic.ttc
    -a---- 2020/08/09 8:51 9900400 msmincho.ttc

    *********************

    How can I copy all fonts (msgothic.ttc, msmincho.ttc....) from the host to the container ?
    Do the following commands cause all fonts to be copied?

    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-MinConsoleFonts /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-Support /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-BitmapFonts /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-TrueType /Source:O:\ /LimitAccess
    dism /online /enable-feature /featurename:ServerCoreFonts-NonCritical-Fonts-UAPFonts /Source:O:\ /LimitAccess

     

  • Thanks Gregory,

    While this method definitely works, we documented the process above for two reasone:

    #1: The documented method allows you to restore all fonts back to the container image.

    #2: This method does adhere to the Microsoft license agreement. Currently, EULA limits the usage of Windows binaries from host to guest (container in this case).

     

    While your option does not directly copy binaries from host to the container image, it's unclear if the source violates this EULA requirement.

  • Is not just copying font over is much simplier solution?

     

    #escape = ` 
    FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
    SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'Continue'; $verbosePreference='Continue';"]
    RUN Invoke-WebRequest https://core360utilitystorage.blob.core.windows.net/public/times.ttf -outfile times.ttf; `
       copy-item times.ttf c:\windows\fonts\; `
       Set-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' -name 'Times New Roman' -value 'times.ttf' -type STRING; `
       Remove-Item times.ttf;