Environment
- Aspose.Slides.NET6.CrossPlatform 26.6.0
- .NET 10, Ubuntu 24.04 (x64/arm64), Docker base
mcr.microsoft.com/dotnet/sdk:10.0 fontconfig,libfontconfig1,libgdiplus,libharfbuzz0binstalled- Valid Aspose license applied (the issue does NOT reproduce in evaluation mode — in eval mode the font is never loaded by any path, which masks the difference)
Summary
A TrueType font (Alibaba PuHuiTi 3.0, a free CJK font) is silently substituted when it is placed in the standard Linux system font folder /usr/share/fonts, even though fc-match "Alibaba PuHuiTi 3.0" resolves it correctly and other fonts in the same folder (Arial, Microsoft YaHei) load fine.
The same font file loads correctly only when it is placed in a non-default folder and passed to FontsLoader.LoadExternalFonts. Passing the default folder /usr/share/fonts itself to LoadExternalFonts does not help.
Reproduction matrix (single image, same license, only the font-loading call differs)
Font (all in the same folder) (1) default (2) LoadExternalFonts (3) LoadExternalFonts
scanning ["/usr/share/fonts"] ["/opt/extra-fonts"]
(a default folder) (a non-default folder)
------------------------------- ------------- ----------------------- -----------------------
Arial OK OK OK
Microsoft YaHei OK OK OK
Alibaba PuHuiTi 3.0 substituted substituted OK <-- the bug
------------------------------- ------------- ----------------------- -----------------------
/opt/extra-fonts contains a byte-identical copy of the same files as /usr/share/fonts.
The attached minimal repro ships only AlibabaPuHuiTi-3-55-Regular.ttf; the Arial /
Microsoft YaHei rows (controls showing other fonts in the same folder are unaffected)
were confirmed in a separate run.
Steps to reproduce
- Put
AlibabaPuHuiTi-3-55-Regular.ttfin/usr/share/fontsand runfc-cache -f.
(Confirm fontconfig sees it:fc-match "Alibaba PuHuiTi 3.0"→ points to the file.) - Put a byte-identical copy in a non-default folder, e.g.
/opt/extra-fonts. - Run the attached program (sets the license from
ASPOSE_LICENSE):
// scenario ①: no LoadExternalFonts -> "Alibaba PuHuiTi 3.0" is substituted
// scenario ②: FontsLoader.LoadExternalFonts(new[]{ "/usr/share/fonts" }) -> still substituted
// scenario ③: FontsLoader.LoadExternalFonts(new[]{ "/opt/extra-fonts" }) -> resolved
using var pres = new Presentation();
var slide = pres.Slides[0];
var shape = slide.Shapes.AddAutoShape(ShapeType.Rectangle, 10, 10, 600, 40);
shape.TextFrame.Text = "Alibaba PuHuiTi 3.0: 测试 Ag 一二三";
var pf = shape.TextFrame.Paragraphs[0].Portions[0].PortionFormat;
pf.LatinFont = new FontData("Alibaba PuHuiTi 3.0");
pf.EastAsianFont = new FontData("Alibaba PuHuiTi 3.0");
foreach (var s in pres.FontsManager.GetSubstitutions())
Console.WriteLine($"{s.OriginalFontName} -> {s.SubstitutedFontName}");
Observed output:
- ①
Alibaba PuHuiTi 3.0 -> Arial - ②
Alibaba PuHuiTi 3.0 -> Arial - ③ (no substitutions — resolved)
Expected
A font that loads correctly via LoadExternalFonts from a non-default folder should also load when it is present in the default system folder /usr/share/fonts (and/or when that folder is passed to LoadExternalFonts). Aspose appears to skip/ignore fonts in folders it already treats as default, even though it cannot resolve them by default scanning.
Questions
- Is this expected behavior? Why does the identical file load from
/opt/extra-fontsbut not from/usr/share/fonts? - Why does
LoadExternalFonts(["/usr/share/fonts"])have no effect when/usr/share/fontsis a default folder? - Is the per-font failure related to this font’s OpenType name table (it exposes a typographic family
Alibaba PuHuiTi 3.0plus a legacy familyAlibaba PuHuiTi 3.0 55 Regularand Chinese localized names)? Arial / Microsoft YaHei in the same folder are unaffected.
Notes
- Free font download (Alibaba PuHuiTi 3.0): https://www.alibabafonts.com/
- A self-contained Docker repro is attached (Dockerfile + Program.cs). The license is read at runtime from a mounted file (
ASPOSE_LICENSE), so no license is embedded in the sample.
Archive.zip (3.2 KB)