Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No Fonts installed on this device #125

Closed
jamesgurung opened this issue Nov 1, 2020 · 28 comments
Closed

No Fonts installed on this device #125

jamesgurung opened this issue Nov 1, 2020 · 28 comments

Comments

@jamesgurung
Copy link

Everything was running fine on 1.2.6, but since updating to 1.2.8 the default font resolver can no longer find fonts when deployed to a Linux container. It runs fine on Windows, but not when deployed using the following Dockerfile. The error is "No Fonts installed on this device".

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

# Install System.Drawing native dependencies
RUN apt-get update \
    && apt-get install -y --allow-unauthenticated \
       libc6-dev \
       libgdiplus \
       libx11-dev \
    && rm -rf /var/lib/apt/lists/*

# Install Microsoft core fonts
RUN echo "deb http://deb.debian.org/debian stable main contrib non-free" > /etc/apt/sources.list \
    && echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | debconf-set-selections \
    && apt-get update \
    && apt-get install -y \
        ttf-mscorefonts-installer \
    && apt-get clean \
    && apt-get autoremove -y \
    && rm -rf /var/lib/apt/lists/*

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY MyProject.csproj MyProject/
RUN dotnet restore "MyProject/MyProject.csproj"
COPY . MyProject/
WORKDIR "/src/MyProject"
RUN dotnet build "MyProject.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "MyProject.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyProject.dll"]

I realise it's possible to implement a custom IFontResolver (although I know literally nothing about where Linux fonts are installed, so wouldn't know where to start!)

Is this something that could be fixed to work by default in this scenario?

@Marv51
Copy link
Contributor

Marv51 commented Nov 4, 2020

Yup, same here my GitHub Action tests fail on Linux with:

System.IO.FileNotFoundException: No Fonts installed on this device!
  Stack Trace:
      at PdfSharpCore.Utils.FontResolver.ResolveTypeface(String familyName, Boolean isBold, Boolean isItalic)
   at PdfSharpCore.Fonts.FontFactory.ResolveTypeface(String familyName, FontResolvingOptions fontResolvingOptions, String typefaceKey)
   at PdfSharpCore.Drawing.XGlyphTypeface.GetOrCreateFrom(String familyName, FontResolvingOptions fontResolvingOptions)
   at PdfSharpCore.Drawing.XFont.Initialize()
   at PdfSharpCore.Drawing.XFont..ctor(String familyName, Double emSize, XFontStyle style, XPdfFontOptions pdfOptions)
   at PdfSharpCore.Drawing.XFont..ctor(String familyName, Double emSize, XFontStyle style)

@pingvinen
Copy link

pingvinen commented Nov 17, 2020

I am seeing the same issue on my linux desktop machine.

Edit: and I can confirm that simply switching the version to 1.2.6 makes the code work.

@eltonina
Copy link

I created this PR #132, for my work fine; I used 'ubuntu-latest' in Azure-devops

@darzid
Copy link

darzid commented Mar 2, 2021

I've tried v1.2.11 and v1.2.12 on Cent OS, but I still get the same message: "No Fonts installed on this device!".

I've also tried to copy a .ttf file to ~/.fonts, but then I still get the same message.

Which I find strange, because the issue reported was caused by subfolders not being included.
So, copying a single .ttf file directly to ~/.fonts (which is specified in my /etc/fonts/fonts.conf file) should not even be affected by the reported issue. Any advice?

@ststeiger
Copy link
Owner

Your /etc/fonts/fonts.conf is not being read.
The font directories are hard-coded.

@darzid
Copy link

darzid commented Mar 4, 2021

Ok. In which folders does it look then?

@mishun
Copy link
Contributor

mishun commented Mar 5, 2021

If I understand correctly, it kinda reads fonts.conf, but in a very hacky way:

static string[] ResolveLinuxFontFiles()
{
var fontList = new List<string>();
var confRegex = new Regex("<dir>(?<dir>.*)</dir>", RegexOptions.Compiled);
var ttfRegex = new Regex(@"\.ttf", RegexOptions.IgnoreCase | RegexOptions.Compiled);
using (var reader = new StreamReader(File.OpenRead("/etc/fonts/fonts.conf")))
{
string line;
while ((line = reader.ReadLine()) != null)
{
var match = confRegex.Match(line);
if (!match.Success) continue;
var path = match.Groups["dir"].Value.Replace("~", Environment.GetEnvironmentVariable("HOME"));
if (!Directory.Exists(path)) continue;
foreach (var enumerateDirectory in Directory.EnumerateDirectories(path))
{
var pathFont = Path.Combine(path, enumerateDirectory);
foreach (var strDir in Directory.EnumerateDirectories(pathFont))
{
fontList.AddRange(Directory.EnumerateFiles(strDir)
.Where(x => ttfRegex.IsMatch(x)));
}
}
}
}
return fontList.ToArray();
}

@ststeiger
Copy link
Owner

ststeiger commented Mar 5, 2021

Correction: It used to be hard-coded.

Yea, unfortunately, it's hard to find a good documentation for what to do with the contents of fonts.conf.
It looks like it searches for all directories listed directly in fonts.conf between dir-tags, then enumerates all fonts in these directories that end with ".ttf" (case-insensitive).
Also, it only searches the top directory:
return InternalEnumerateFiles(path, "*", SearchOption.TopDirectoryOnly);

@ststeiger
Copy link
Owner

Fixed the top-directory issue, and replaced the .ttf regex with EndsWith (case-insensitive).
So if your fonts.conf contains

	<dir>/usr/share/fonts</dir>
	<dir>/usr/local/share/fonts</dir>
	<dir prefix="xdg">fonts</dir>
	<!-- the following element will be removed in the future -->
	<dir>~/.fonts</dir>

it should now find *.ttf fonts in ~/.fonts and all of its subdirectories.

@ststeiger
Copy link
Owner

Question is, should we only load *.ttf, or also *.woff and *.otf.

@darzid
Copy link

darzid commented Mar 5, 2021 via email

@ststeiger
Copy link
Owner

@darzid:
In addition to reading the fonts.conf directory, I've now added

/usr/share/fonts
/usr/local/share/fonts
System.Environment.GetEnvironmentVariable("HOME") + "/.fonts"

hard-coded, just in case there is no fonts.conf, or that there are no such entries.
The fonts ending in .ttf in those folders (and subfolders) should now be picked up.

@ststeiger
Copy link
Owner

ststeiger commented Mar 5, 2021

@jamesgurung, @pingvinen: Linux uses fontfonfig to configure the location of the fonts.
Hard-coded, it now searches in /usr/share/fonts, /usr/local/share/fonts and ~/.fonts, and in addition to that, in all the directories added in dir tags in /etc/fonts/font.conf (only tags added directly there, not from other conf files referenced in font.conf).
If you don't know where the fonts are located, you can always embed the fonts you need as embedded-resources in an assembly, and implement your own IFontResolver, which loads them from the embedded-resources in that assembly. Of course that requires that the font's license allows "embedding".

@jamesgurung
Copy link
Author

@ststeiger Many thanks. I no longer have an issue; it was a regression in 1.2.8 that I assume was fixed in a subsequent update because everything is working fine now. 👍

I haven't closed because it looks like others are having related issues.

@notclive
Copy link

notclive commented Mar 5, 2021

I found that hard-coded directories weren't working for me, fonts inside /usr/share/fonts weren't being picked up.
Looking at AddFontsToFontList it appears that fonts need to be at least two directories deep, putting a font inside /usr/share/fonts/a/b worked.

@darzid
Copy link

darzid commented Mar 11, 2021 via email

@ststeiger
Copy link
Owner

@jamesgurung @notclive: Merged pinvoke of libfontconfig with pull request 151 for Linux.
Fonts should be resolved fine now.
You might want to check if it works for you.

@bcourtneyri
Copy link

bcourtneyri commented Mar 24, 2021

@notclive did you have to stay with the two directory deep solution? I have copied a .ttf file to usr/share/fonts but am still getting the error. I have a .net core web app running in a docker container on an AWS Linux server. Does the Docker container need to reference the font folder (usr/share/fonts) via a mount?

@ststeiger
Copy link
Owner

@bcourtneyri: Does the container have truetype-fonts installed ?

@bcourtneyri
Copy link

@ststeiger thanks for the follow up. The container itself does not. I'm considering whether to mount a volume with .ttfs to the linux server or to package the .ttf fonts within the asp net core project. Is it possible to do some sort of font resolve with packaged .ttfs?

@ststeiger
Copy link
Owner

ststeiger commented Mar 27, 2021

Sure, you can make a dll where the ttfs are an embedded resource.
Then you need to implement a font-resolver that reads fonts from the embedded resources.
That should eliminate the possiblity of the font not being installed or found.
However, that increases your application's size by the size of all the ttf files you embed.

@bcourtneyri
Copy link

@ststeiger thanks again. I managed to it working using the sample code you provided.

@Lure5134
Copy link

Relates to #161

@diogoxluis
Copy link

I'm getting "No Fonts installed on this device!" error, running version 1.3.62 on .net7.0 linux container.

Do I need to do any extra step or should it just work?

System.IO.FileNotFoundException: No Fonts installed on this device!
at PdfSharpCore.Utils.FontResolver.ResolveTypeface(String familyName, Boolean isBold, Boolean isItalic)
at PdfSharpCore.Fonts.FontFactory.ResolveTypeface(String familyName, FontResolvingOptions fontResolvingOptions, String typefaceKey)
at PdfSharpCore.Drawing.XGlyphTypeface.GetOrCreateFrom(String familyName, FontResolvingOptions fontResolvingOptions)
at PdfSharpCore.Drawing.XFont.Initialize()
at PdfSharpCore.Drawing.XFont..ctor(String familyName, Double emSize, XFontStyle style, XPdfFontOptions pdfOptions)
at PdfSharpCore.Drawing.XFont..ctor(String familyName, Double emSize)
at PdfSharpCore.Pdf.AcroForms.PdfTextField..ctor(PdfDictionary dict)
at PdfSharpCore.Pdf.AcroForms.PdfAcroField.PdfAcroFieldCollection.CreateAcroField(PdfDictionary dict)
at PdfSharpCore.Pdf.AcroForms.PdfAcroField.PdfAcroFieldCollection.get_Item(Int32 index)
at PdfSharpCore.Pdf.PdfDocument.MakeAcroFormsReadOnly()
at DSServer.Commands.ConvertToPDF.ConvertToPDF.ExecuteAsync(MemoryStream originalFile) in /app/src/DSServer/Commands/ConvertToPDF/ConvertToPDF.cs:line 30
at REG.Documents.Business.DocumentExchangeItemMessageAttachments.Commands.Download.DownloadDocumentExchangeItemMessageAttachmentCommand.ExecutePdfForRecipientAsync(Guid attachmentId, Stream target) in /app/src/REG.Documents.Business/DocumentExchangeItemMessageAttachments/Commands/Download/DownloadDocumentExchangeItemMessageAttachmentCommand.cs:line 172
at REG.Documents.Api.Controllers.DocumentExchangeItemMessageAttachmentController.DownloadMessageAttachmentForRecipient(Guid attachmentId) in /app/src/REG.Documents.Api/Controllers/DocumentExchangeItemMessageAttachmentController.cs:line 102
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

@bcourtneyri
Copy link

@diogoxluis I packaged the fonts into my app and referenced them from there. It worked for me as I only needed a few.

@diogoxluis
Copy link

@diogoxluis I packaged the fonts into my app and referenced them from there. It worked for me as I only needed a few.

Thanks... any pointers to how I do that?
I tried the dockerfile code at the top, but didn't do the trick for me.

@bcourtneyri
Copy link

You just put the ttf files into a folder somewhere in your project and refer to them in the code.

@Rahul-Vaity
Copy link

After using 1.2.6 version I'm getting "The type initializer for 'PdfSharpCore.Utils.FontResolver' threw an exception".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests