.net core(system.drawing.common) not able to render chinese and arabic fonts on linux docker container
Asked Answered
J

2

0

I create a docker file as below:

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
RUN apt-get update \
    && apt-get install -y --allow-unauthenticated \
        libc6-dev \
        libgdiplus \
        libx11-dev \
     && rm -rf /var/lib/apt/lists/*
RUN ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll
RUN ln -s /usr/lib/libc6-dev.so /usr/lib/libc6-dev.dll
RUN ln -s /usr/lib/libx11-dev.so /usr/lib/libx11-dev.dll

WORKDIR /app
COPY bin/Release/netcoreapp2.2/publish .
FROM base AS final
WORKDIR /app
ENTRYPOINT ["dotnet","MyWebApi.dll"]

When I run the image I get chinese and arabic texts as boxes. I am creating image(tiff) for Chinese and Arabic texts using system.drawing.common in my .Net core API. Do I need any additional font files in my image?

Jessen answered 25/6, 2020 at 15:0 Comment(4)
Yes, you need to add a font that contains the glyphs you want to render.Pianola
Can I get any reference how can I add fonts to my image?Jessen
see this guide. You can get a list of candidate fonts like this: apt-cache search fonts | grep -iE '^fonts.*(chinese|arabic)'Pianola
I am using my custom font file. I copied that file to /usr/share/fonts directory. Strange thing is my custom font file do not have support for Chinese language, but the image is rendered properly in Windows machine(somehow its rendering using system fonts..my guess) but not in linux(I copied MSYH.ttf file to /usr/share/fonts as well).Jessen
G
1

I faced the same issue today, Its happens on .net core 2,3 and also 5.

The problem is that libgdiplus by default use engine that not support RTL/Arabic, so these lines are not giving right result:

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

The solution, is to build the source code of the libray libgdiplus by yourself and choose the engine that support Arabic/RTL which is Pango.

These lines will solve the problem, by installing the source code and build it and install the library after selecting the right engine (pango engine using --with-pango parameter):

# Build libgdiplus with pango
RUN apt-get update && apt-get install  -y --allow-unauthenticated \
     libgif-dev autoconf libtool automake build-essential gettext libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev
RUN apt-get update && apt-get install -y libpango1.0-dev 

RUN apt-get install -y --allow-unauthenticated git
RUN git clone https://github.com/mono/libgdiplus.git /libgdiplus
WORKDIR /libgdiplus
RUN ./autogen.sh --with-pango --prefix=/usr
RUN make  
RUN make install

Note: This will work on .net core 3 and above, but for 2.1 its not working and showing this error message:

Requested 'pango >= 1.40.14' but version of Pango is 1.40.5

I ended up with migrate the project from version .netcore 2 to 3 and apply the above code and the Arabic/RTL is work fine.

Gaiety answered 17/4, 2021 at 23:46 Comment(0)
S
0

If your chinese like this:

enter image description here

You may set fonts for your render method ;

You can download ttf file and include in your project.

Then init fonts before use it.

/// <summary>
/// init fonts file in linux and return ttf file path
/// </summary>
/// <param name="resourcePath"></param>
/// <param name="_logger"></param>
/// <returns></returns>
public static List<string> InitFonts(string resourcePath,ILogger _logger)
{
    _logger.LogTrace("start init fonts");


    const string fontsPath = "/usr/share/fonts";
    if (!Directory.Exists(fontsPath))
    {
        _logger.LogWarning("the path of:/usr/share/fonts is not exist ignore init");
        return new List<string>();
    }

    var groupBuyingPath = Path.Combine(fontsPath, "groupbuying");

    var pfRegular = Path.Combine(groupBuyingPath, "PingFang-SC-Regular.ttf");
    var pfMedium = Path.Combine(groupBuyingPath, "PingFang-SC-Medium.ttf");
    var alibaba = Path.Combine(groupBuyingPath, "Alibaba-PuHuiTi-M.ttf");
    if (File.Exists(pfRegular))
    {
        _logger.LogTrace("the file:{path}  is exist ignore init", pfRegular);
        return new List<string>();
    }

    if (!Directory.Exists(groupBuyingPath))
    {
        Directory.CreateDirectory(groupBuyingPath);
        _logger.LogTrace("auto create fonts folder in :{path}", fontsPath);
    }

    File.Copy(Path.Combine(resourcePath, "Fonts", "PingFang-SC-Regular.ttf"), pfRegular);
    File.Copy(Path.Combine(resourcePath, "Fonts", "PingFang-SC-Medium.ttf"), pfMedium);
    File.Copy(Path.Combine(resourcePath, "Fonts","Alibaba-PuHuiTi-M.ttf"),alibaba);

    _logger.LogTrace("ttf file copy success");

    var psi = new ProcessStartInfo
    {
        FileName = "mkfontscale",
        WorkingDirectory = groupBuyingPath,
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
    };

    var proc = new Process
    {
        StartInfo = psi
    };

    proc.Start();   
    var psi1 = new ProcessStartInfo
    {
        FileName = "mkfontdir",
        WorkingDirectory = groupBuyingPath,
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
    };

    var proc1 = new Process
    {
        StartInfo = psi1
    };

    proc1.Start();   
    var psi2 = new ProcessStartInfo
    {
        FileName = "fc-cache",
        WorkingDirectory = groupBuyingPath,
        UseShellExecute = false,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
    };

    var proc2 = new Process
    {
        StartInfo = psi2
    };

    proc2.Start();

    _logger.LogTrace("run command success");

    return new List<string>() {pfRegular, pfMedium, alibaba};
}

my ttf file is in resourcePath/Fonts.

After run InitFonts method ,you can ls /usr/share/fonts/groupbuying/ in container (by docker exec -it xxx bash) and see fonts.dir fonts.scale

Or use fc-list check fonts is correct.

If you are not sure your fonts name,You can add this API to test fonts.

[HttpGet("[action]")]
public string GetFont(string fontName)
{
    var wwwrootPath = "wwwroot";
    var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, wwwrootPath);
    var rootPath = $"resource";

    var resourcePath = Path.Combine(directory, rootPath);
    InitFonts(resourcePath, _log);
    var font = new Font(fontName, 36, FontStyle.Bold, GraphicsUnit.Pixel); 
    return font.Name;
}

If the api return font name as your param,the InitFonts works well.

Maybe,you will run your images as root by use docker run --user root

Skullcap answered 17/11, 2023 at 2:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.