QuestPDF icon indicating copy to clipboard operation
QuestPDF copied to clipboard

QuestPDF running on a linux Azure Function produces a SkiaSharp error

Open bjarnekrottje opened this issue 2 years ago • 8 comments

Describe the bug When I try to create a report on an Linux hosted Azure Function (Consumption-based pricing), I get the following error in Application Insights (included part of stack trace):

Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: RetrieveReport
 ---> QuestPDF.Drawing.Exceptions.InitializationException: Cannot create the PDF document using the SkiaSharp-related library. This exception usually means that, on your operating system where you run the application, SkiaSharp-related requires installing additional dependencies. Such dependencies are available as additional nuget packages, for example *.NativeAssets.Linux.NoDependencies. Some operating systems may require installing multiple nugets, e.g. MacOS may need both *.NativeAssets.macOS.NoDependencies and *.NativeAssets.Linux.NoDependencies.Please refer to the SkiaSharp-related documentation for more details. Also, please consult the inner exception that has been originally thrown by the dependency library.
 ---> System.TypeInitializationException: The type initializer for 'SkiaSharp.SKAbstractManagedWStream' threw an exception.
 ---> System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibSkiaSharp: cannot open shared object file: No such file or directory
   at SkiaSharp.SkiaApi.sk_managedwstream_set_procs(SKManagedWStreamDelegates procs)
   at SkiaSharp.SKAbstractManagedWStream..cctor()
   --- End of inner exception stack trace ---
   at SkiaSharp.SKDocument.CreatePdf(Stream stream, SKDocumentPdfMetadata metadata)
   at QuestPDF.Drawing.PdfCanvas.CreatePdf(Stream stream, DocumentMetadata documentMetadata, DocumentSettings documentSettings)
   --- End of inner exception stack trace ---
   at QuestPDF.Drawing.PdfCanvas.CreatePdf(Stream stream, DocumentMetadata documentMetadata, DocumentSettings documentSettings)
   at QuestPDF.Drawing.DocumentGenerator.GeneratePdf(Stream stream, IDocument document)
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document)

To Reproduce The Azure Function is being built and deployed using a GitHub hosted agent in GitHub Actions. The agent is a linux agent (running on ubuntu-latest). The publishing of the Function in the pipeline works and doesn't give any errors. Only when calling the Azure Function. When running the Azure Function locally on my Windows machine (and so also building it on Windows) it just works as expected.

I tried searching for this error online and came across a couple of solutions, but none of them have worked so far. I have installed the following packages in the project:

<ItemGroup>
        <PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2.3" />
        <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
        <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.OpenApi" Version="1.5.1" />
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
        <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
        <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.2.0" />
        <PackageReference Include="QuestPDF" Version="2023.5.3" />
        <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" />
        <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.3" />
    </ItemGroup>

Expected behavior I expect the project to be able to be built using a hosted GitHub agent within GitHub Actions.

Additional context The pipeline being used is just a default pipeline which restores the NuGet packages, builds and publishes the project and deploys it to Azure (shortened to keep it simple and replaced naming with X to keep it generic):

jobs:
  build-backend:
    runs-on: ubuntu-latest
    name: Build Backend
    steps:
      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Setup .NET 6
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: 6.0.x

      - name: Restore Dependencies
        run: dotnet restore Backend/X.Backend/X.Backend.csproj

      - name: Build Backend
        run: dotnet publish Backend/X.Backend/X.Backend.csproj -c Release --no-restore

     - name: 'Deploy Azure Function to DEV'
        uses: Azure/functions-action@v1
        with:
          app-name: 'x-dev'
          package: 'x.backend/'
          publish-profile: ${{ secrets.X_BACKEND_DEPLOYMENT_DEV }}

Is there something I still have to configure or change for this to work? I hope you can help me out, thank you very much in advance.

bjarnekrottje avatar Jun 16 '23 14:06 bjarnekrottje

Would you please try this one: https://github.com/mono/SkiaSharp/issues/1341#issuecomment-694168658

This one could also be helpful: https://github.com/mono/SkiaSharp/issues/1341#issuecomment-1172250647

It is purely SkiaSharp issue, so I hope it will help.

MarcinZiabek avatar Jun 16 '23 15:06 MarcinZiabek

Unfortunately that didn't work either. With both of the solutions tried I still got the same error. I think using a Windows hosted build agent and Azure Function might be the easiest way to fix it in this case. It's unfortunate that it's that of a hassle to get SkiaSharp to work like this. Thanks for thinking of a solution, if someone has a working fix that would be awesome of course, also for future reference.

bjarnekrottje avatar Jun 17 '23 11:06 bjarnekrottje

I am also seeing a related issue, I really want to not have wasted lots of time building templates, only for it not to be feasable to run on our pods.

Stack trace, (omitted sensitive 5 rows from the bottom and replaced company name). This is when running Azure Devops build pipeline which is ubuntu-latest, with no special things added:

   System.DllNotFoundException : Unable to load shared library 'libHarfBuzzSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable: 
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/runtimes/linux-x64/native/libHarfBuzzSharp.so: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/7.0.4/libHarfBuzzSharp.so: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/libHarfBuzzSharp.so: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/runtimes/linux-x64/native/liblibHarfBuzzSharp.so: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/7.0.4/liblibHarfBuzzSharp.so: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/liblibHarfBuzzSharp.so: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/runtimes/linux-x64/native/libHarfBuzzSharp: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/7.0.4/libHarfBuzzSharp: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/libHarfBuzzSharp: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/runtimes/linux-x64/native/liblibHarfBuzzSharp: cannot open shared object file: No such file or directory
/usr/share/dotnet/shared/Microsoft.NETCore.App/7.0.4/liblibHarfBuzzSharp: cannot open shared object file: No such file or directory
/workspace/37/s/CompanyName.Common.Tests/bin/Release/net7.0/liblibHarfBuzzSharp: cannot open shared object file: No such file or directory

  Stack Trace:
     at HarfBuzzSharp.HarfBuzzApi.hb_buffer_create()
   at HarfBuzzSharp.Buffer..ctor()
   at QuestPDF.Drawing.TextShaper.Shape(String text)
   at QuestPDF.Elements.Text.Items.TextBlockSpan.MeasureWithoutCache(TextMeasurementRequest request)
   at QuestPDF.Elements.Text.Items.TextBlockSpan.Measure(TextMeasurementRequest request)
   at QuestPDF.Elements.Text.TextBlock.<DivideTextItemsIntoLines>g__GetNextLine|25_0(<>c__DisplayClass25_0&)
   at QuestPDF.Elements.Text.TextBlock.DivideTextItemsIntoLines(Single availableWidth, Single availableHeight)+MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at QuestPDF.Elements.Decoration.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Elements.Padding.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Elements.Constrained.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Elements.Layers.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Elements.Column.PlanLayout(Size availableSpace)
   at QuestPDF.Elements.Column.Measure(Size availableSpace)
   at QuestPDF.Infrastructure.ContainerElement.Measure(Size availableSpace)
   at QuestPDF.Drawing.DocumentGenerator.RenderPass[TCanvas](PageContext pageContext, TCanvas canvas, Container content, DebuggingState debuggingState)
   at QuestPDF.Drawing.DocumentGenerator.RenderDocument[TCanvas](TCanvas canvas, IDocument document)
   at QuestPDF.Drawing.DocumentGenerator.GeneratePdf(Stream stream, IDocument document)
   at QuestPDF.Fluent.GenerateExtensions.GeneratePdf(IDocument document, Stream stream)
...omitted 5 internal lines

frankhaugen avatar Jun 22 '23 06:06 frankhaugen

@frankhaugen Did you have a chance to analyze the build artifact to ensure that the libHarfBuzzSharp.so file is in one of the directories listed in the exception message?

@bjarnekrottje Following your investigation, it seems to be an issue with the build agent. Would you please specify the runtime in the dotnet publish action? https://github.com/mono/SkiaSharp/issues/1341#issuecomment-1233819996

MarcinZiabek avatar Jun 22 '23 07:06 MarcinZiabek

@frankhaugen Did you have a chance to analyze the build artifact to ensure that the libHarfBuzzSharp.so file is in one of the directories listed in the exception message?

Nope nothing, I found only these items when doing ls in .../native/:
libSkiaSharp.so libe_sqlite3.so

Thank you @MarcinZiabek for the extremely fast reply 😺

frankhaugen avatar Jun 22 '23 09:06 frankhaugen

I don't use Azure Functions but I Do use Linux in app service and I add this libraries to my project for it to work: HarfBuzzSharp.NativeAssets.Linux SkiaSharp.NativeAssets.Linux.NoDependencies

It's worth the try.

blogcraft avatar Jun 30 '23 02:06 blogcraft

I don't use Azure Functions but I Do use Linux in app service and I add this libraries to my project for it to work: HarfBuzzSharp.NativeAssets.Linux SkiaSharp.NativeAssets.Linux.NoDependencies

It's worth the try.

A bit late, but this was/is the solution for my issue

frankhaugen avatar Jan 02 '24 11:01 frankhaugen

Commenting just to let everyone know that I had the same problem and the packages listed by @frankhaugen fixed it for me. I'm running a .NET 8 Web API on Azure Container Apps, if anyone find himself here.

tiaringhio avatar Jan 04 '24 21:01 tiaringhio