QuestPDF running on a linux Azure Function produces a SkiaSharp error
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.
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.
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.
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 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
@frankhaugen Did you have a chance to analyze the build artifact to ensure that the
libHarfBuzzSharp.sofile 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 😺
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.
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.LinuxSkiaSharp.NativeAssets.Linux.NoDependenciesIt's worth the try.
A bit late, but this was/is the solution for my issue
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.