richard-scryber / scryber.core

Scryber.Core is a dotnet 6 html to pdf engine written entirely in C# for creating beautiful flowing documents from html templates including css styles, object data binding and svg drawing.
Other
195 stars 31 forks source link

The layout of component 'text1' failed. The initialization of the fonts failed. Could not load the system fonts #77

Open pedrommuller opened 2 years ago

pedrommuller commented 2 years ago

I'm getting this issue on a mac OS every time I want to generate a pdf I understand that might be an issue with some dependencies I already have installed mono-libgdiplus

any guidance for this?

Brandejs commented 1 year ago

Hi im run into the same issue on Mac with the M2 chip

https://scrybercore.readthedocs.io/en/latest/libgdiplus.html?highlight=mac

I did following

brew install mono-libgdiplus
sudo ln -s /opt/homebrew/Cellar/mono-libgdiplus/6.1_1/lib/libgdiplus.dylib /usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.3  

and stuck on this. doc.SaveAsPDF(targetFilename, FileMode.OpenOrCreate);

Scryber.PDFLayoutException: The layout of component 'text1' failed. The initialization of the fonts failed. Could not load the system fonts

Scryber.PDFLayoutException
The layout of component 'text1' failed. The initialization of the fonts failed. Could not load the system fonts
   at Scryber.Layout.LayoutEngineBase.DoLayoutTextComponent(IPDFTextComponent text, Style style)
   at Scryber.Layout.LayoutEngineBase.DoLayoutAChild(IPDFComponent comp, Style full)
   at Scryber.Layout.LayoutEngineBase.DoLayoutAChild(Component comp)
   at Scryber.Layout.LayoutEngineBase.DoLayoutChildren(ComponentList children)
   at Scryber.Layout.LayoutEngineBase.DoLayoutChildren()
   at Scryber.Layout.LayoutEnginePanel.DoLayoutBlockComponent(PDFPositionOptions position, PDFColumnOptions columnOptions)
   at Scryber.Layout.LayoutEnginePanel.DoLayoutComponent()
   at Scryber.Layout.LayoutEngineHeading.DoLayoutComponent()
   at Scryber.Layout.LayoutEngineBase.Layout(PDFLayoutContext context, Style fullstyle)
   at Scryber.Layout.LayoutEngineBase.DoLayoutViewPortComponent(IPDFViewPortComponent viewPort, Style style)
   at Scryber.Layout.LayoutEngineBase.DoLayoutAChild(IPDFComponent comp, Style full)
   at Scryber.Layout.LayoutEngineBase.DoLayoutAChild(Component comp)
   at Scryber.Layout.LayoutEngineBase.DoLayoutChildren(ComponentList children)
   at Scryber.Layout.LayoutEngineBase.DoLayoutChildren()
   at Scryber.Layout.LayoutEnginePage.LayoutPageContent()
   at Scryber.Layout.LayoutEnginePage.DoLayoutComponent()
   at Scryber.Layout.LayoutEngineSection.DoLayoutComponent()
   at Scryber.Layout.LayoutEngineBase.Layout(PDFLayoutContext context, Style fullstyle)
   at Scryber.Layout.LayoutEngineDocument.LayoutPageWithStyle(PageBase pg, Style full)
   at Scryber.Layout.LayoutEngineDocument.LayoutPage(PageBase pg)
   at Scryber.Layout.LayoutEngineDocument.LayoutAllPages()
   at Scryber.Layout.LayoutEngineDocument.DoLayoutComponent()
   at Scryber.Layout.LayoutEngineBase.Layout(PDFLayoutContext context, Style fullstyle)
   at Scryber.Components.Document.RenderToPDF(PDFRenderContext context, PDFWriter writer)
   at Scryber.Components.Document.RenderToPDF(Stream tostream)
   at Scryber.Components.Document.RenderTo(Stream tostream, OutputFormat format)
   at Scryber.Components.Document.SaveAs(Stream stream, Boolean bind, OutputFormat format)
   at Scryber.Components.Document.SaveAsPDF(String path, FileMode mode, Boolean bind)
   at Scryber.Components.Document.SaveAsPDF(String path, FileMode mode)
  ...
   at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_1.<<InvokeTestMethodAsync>b__1>d.MoveNext() in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\Runners\TestInvoker.cs:line 264
--- End of stack trace from previous location ---
   at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in C:\Dev\xunit\xunit\src\xunit.execution\Sdk\Frameworks\ExecutionTimer.cs:line 48
   at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in C:\Dev\xunit\xunit\src\xunit.core\Sdk\ExceptionAggregator.cs:line 90

System.Configuration.ConfigurationErrorsException
The initialization of the fonts failed. Could not load the system fonts
   at Scryber.Drawing.PDFFontFactory.AssertInitialized()
   at Scryber.Drawing.PDFFontFactory.GetFontDefinition(String family, FontStyle style, Boolean throwNotFound)
   at Scryber.Components.Document.GetFontResource(PDFFont font, Boolean create, Boolean throwOnNotFound)
   at Scryber.Layout.LayoutEngineText.Layout(PDFLayoutContext context, Style fullstyle)
   at Scryber.Layout.LayoutEngineBase.DoLayoutTextComponent(IPDFTextComponent text, Style style)

System.Exception
Could not load the system fonts
   at Scryber.Drawing.PDFFontFactory.LoadSystemFonts()
   at Scryber.Drawing.PDFFontFactory.AssertInitialized()

System.Collections.Generic.KeyNotFoundException
The given key 'OS/2' was not present in the dictionary.
   at System.Collections.ObjectModel.KeyedCollection`2.get_Item(TKey key)
   at Scryber.OpenType.TTFTableFactory.ReadDirectory(String tname, TTFDirectoryList list, BigEndianReader reader)
   at Scryber.OpenType.TTFRef.DoLoadRef(BigEndianReader reader, String fullpath)
   at Scryber.OpenType.TTFRef.LoadCollectionRefs(BigEndianReader reader, String fullPath)
   at Scryber.OpenType.TTFRef.LoadCollectionRefs(Stream stream, String fullPath)
   at Scryber.OpenType.TTFRef.LoadCollectionRefs(FileInfo fi)
   at Scryber.OpenType.TTFRef.LoadRefs(DirectoryInfo dir)
   at Scryber.Drawing.PDFFontFactory.LoadSystemFonts()

Did you find any solution?

tristaniels commented 6 months ago

I believe this happens when two threads attempt to call the SafeAsPDF() method at the same time. To fix I wrap the call in a lock statement.

public class MyService
{
    private static readonly object LockObject = new();

    public byte[] CreatePdf()
    {
        var doc = // Code to setup the document.
        var output = // Code to setup the output.

        lock (LockObject)
        {
           doc.SaveAsPDF(output);
        }

        // Code to return.
    }
}
richard-scryber commented 6 months ago

Thanks Tristan, Jan - The initialisation of the fonts is wrapped in a thread lock, so should be ok. However it will re-throw any error caused during initialisation.

It is possible to remove the use of system fonts using the configuration options if they are not accessible to the running process. Depending on your set-up something along these lines should exclude the look up of system fonts.

From the application configuration file...

{
    "Scryber": {
         "Fonts" : {
             "UseSystemFonts": "False",
             "Register" : [
                {
                     "Family": "Segoe UI",
                     "File": "Mocks/Fonts/segoeui.ttf"
                } ]

Or via code (before anything is invoked)

var service = Scryber.ServiceProvider.GetService<IScryberConfigurationService>();
service.FontOptions.UseSystemFonts = false;

Let me know if this works for you. It would be good to know.

Richard