QuestPDF / QuestPDF

QuestPDF is a modern open-source .NET library for PDF document generation. Offering comprehensive layout engine powered by concise and discoverable C# Fluent API. Easily generate PDF reports, invoices, exports, etc.

Home Page:https://www.questpdf.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Sporadic AccessViolationException when UseEnvironmentFonts is false and documents are generated in parallel

ebarnard opened this issue · comments

Describe the bug
We have a suite of snapshot comparison tests for the QuestPDF components we use in our reports.

These complete without error when UseEnvironmentFonts is true, or when not run in parallel, but when it is false a test will eventually fail with an AccessViolationException.

Parallel Not Parallel
UseEnvironmentFonts = true Ok Ok
UseEnvironmentFonts = false Exception Ok

This was not an issue prior to version 2024.3.0.

To Reproduce
I don't yet have a small reproducer but am working on it. I'm filing this issue now in case there is a known or obvious fix.

Expected behavior
Document generation succeeds without throwing an AccessViolationException.

Environment
QuestPDF 2024.3.1
Windows 10 x64

Stack trace

The active test run was aborted. Reason: Test host process crashed : Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
   at QuestPDF.Skia.Text.SkParagraph+API.paragraph_plan_layout(IntPtr, Single)
--------------------------------
   at QuestPDF.Skia.Text.SkParagraph.PlanLayout(Single)
   at QuestPDF.Elements.Text.TextBlock.CalculateParagraphMetrics(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Text.TextBlock.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Padding.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Padding.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Table.Table+<>c__DisplayClass52_0.<PlanLayout>g__GetRenderingCommands|1()
   at QuestPDF.Elements.Table.Table.PlanLayout(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Table.Table.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Decoration.<PlanLayout>g__GetDecorationMeasurement|20_0(QuestPDF.Infrastructure.Element, <>c__DisplayClass20_0 ByRef)
   at QuestPDF.Elements.Decoration+<PlanLayout>d__20.MoveNext()
   at System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at System.Linq.Enumerable.ToList[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at QuestPDF.Elements.Decoration.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.EnsureSpace.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Padding.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Column.PlanLayout(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Column.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Extend.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Extend.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Decoration+<PlanLayout>d__20.MoveNext()
   at System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at System.Linq.Enumerable.ToList[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Generic.IEnumerable`1<System.__Canon>)
   at QuestPDF.Elements.Decoration.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Padding.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.Proxy.CacheProxy.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Constrained.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Elements.Layers.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Infrastructure.ContainerElement.Measure(QuestPDF.Infrastructure.Size)
   at QuestPDF.Drawing.DocumentGenerator.RenderPass[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](QuestPDF.Infrastructure.PageContext, System.__Canon, QuestPDF.Infrastructure.ContainerElement)
   at QuestPDF.Drawing.DocumentGenerator.RenderSingleDocument[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon, QuestPDF.Infrastructure.IDocument, QuestPDF.Infrastructure.DocumentSettings)
   at QuestPDF.Drawing.DocumentGenerator.RenderDocument[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__C

Thank you for reaching out 😄

I have investigated this problem and found a root cause. Your findings have helped me a lot, so thank you for sharing so many details! I don't expect any performance degradation after the fix.

Would you please test the newest 2024.3.2 version? https://github.com/QuestPDF/QuestPDF/releases/tag/2024.3.2

I've run the test suite in parallel a few times using 2024.3.2 and haven't got a single AccessViolationException so I think it's fixed.

Thank you so much for sorting this out quickly.