installing fonts on windows server docker image inspired me to make a website
![Cute dinosaur image](/_image?href=%2F_astro%2Fdinosaur-1.wn0WM4h5.png&w=740&f=webp)
Sometimes, you run into issues that appear straightforward at first but ultimately prove to be more challenging - especially in the realm of DevOps, where you more or less often find yourself in a non-familiar environment.
Neither I manage Windows Servers nor I work in ASP.NET projects, so I struggled with the assigned task. It wasnât my first time in such a situation, but this time, I decided to share my findings and a working solution - even though, or maybe just because, it is not my main area of expertise. In case you ever Google this or ChatGPT provides you with a better answer after learning from this article, I hope your experience will be smoother than mine.
I would like to give back a little to the community. I do different things - you call such people full-stacks nowadays. I believe I might have some interesting thoughts and insights to share, thus my motivation for maintaining a website.
tldr: how to add fonts to windows server docker image
The solution was tested on windowsservercore-ltsc2019
.
First, copy your fonts into a Docker image and register them in the Windows registry:
COPY fonts/ C:\\Windows\\Fonts\\
RUN (reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial (TrueType)' /t REG_SZ /d 'arial.ttf' /f) \
-and (reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial Bold (TrueType)' /t REG_SZ /d 'arialbd.ttf' /f)
Feel free to add more fonts; just remember to append \
at the end of the line when you break your RUN
instruction into multiple lines.
/v
is a font name, it should be descriptive of the font, but make sure to keep the(TrueType)
suffix for truetype fonts and the(OpenType)
for opentype fonts./d
is a file name inC:\Windows\Fonts
.
Afterwards, in your .NET project, based on your architecture, add and invoke this static extern method:
using System.Runtime.InteropServices;
public class SingletonService
{
public SingletonService()
{
AddFontResource(@"c:\Windows\Fonts\arial.ttf");
AddFontResource(@"c:\Windows\Fonts\arialbd.ttf");
}
[DllImport("gdi32.dll")]
static extern int AddFontResource(string lpFilename);
}
A single call is sufficient. If you dynamically update font files, invoking AddFontResource
each time you modify them might be necessary, although I havenât experimented with this.
a little more context
why the c# code is needed
To be honest, I am not sure.
The added Dockerfile
lines should be enough - in fact they were enough when I was running reg add
in the running container. My proof of concept scenario was:
- shell into a running Docker container
- download the
arial.ttf
font intoC:\Windows\Fonts
- run:
reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial (TrueType)' /t REG_SZ /d 'arial.ttf' /f
- test the API
It worked, so I moved these steps into Dockerfile
- only to find it no longer functional.
The files were there, reg add
for one of the added fonts, and suddenly all of them started working. It did not even matter which font I re-added, I could even add
Describing my problem to the ChatGPT resulted with:
Your situation is quite unique and interesting.
Such words most often mean that you have a long way ahead to solve the issue.
After digging, I came to the conclusion that some service (probably GDI - graphics device interface) in Windows needs to be restarted after you install fonts. Most sources were suggesting a system restart - but this is impossible in a Docker environment.
Why the restart needs to happen after an application is already up?
This question still bothers me. From my experience, in most cases you need to prepare the environment before you run the application.
reg add
to the HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts
registry appears to trigger a restart. Another potential solution would be to move reg add
calls to the imageâs ENTRYPOINT
, though this approach wasnât guaranteed to succeed. Ultimately, C# call to gdi32.dll
seemed like a reliable solution.
chaining commands in powershell
Each RUN
in Dockerfile
will result in another image layer. This is not desirable in this case. Chaining commands with &&
is a common practice in Linux, the PowerShell version used here did not support it. While -and
can serve as an alternative, it discards the output of executed commands â a minor difference in this case, but one that can affect debugging.
When working on a solution, start with a straightforward way, like:
RUN reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial (TrueType)' /t REG_SZ /d 'arial.ttf' /f
RUN reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial Bold (TrueType)' /t REG_SZ /d 'arialbd.ttf' /f
Then, when solution is confirmed to work - proceed to make it a little bit better, first in terms of layering:
RUN (reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial (TrueType)' /t REG_SZ /d 'arial.ttf' /f) -and (reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial Bold (TrueType)' /t REG_SZ /d 'arialbd.ttf' /f)
Finally, having in mind a person that will need to add an additional font in the future - split the command into multi-lines for better readability:
RUN (reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial (TrueType)' /t REG_SZ /d 'arial.ttf' /f) \
-and (reg add 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' /v 'Arial Bold (TrueType)' /t REG_SZ /d 'arialbd.ttf' /f)
If you have a newer powershell, you might be able to use &&
already.
why do I even need fonts on server
While the need for server-side fonts might seem niche, if you are reading this article, chances are you have your own specific reason. Whether for document processing or dynamic content generation, the presented approach offers a working solution. In my case, fonts were needed to generate PDF files.