Browse Source

Merge branch 'main' into feat-lazy

Marcin Ziąbek 10 months ago
parent
commit
fd7df72328
41 changed files with 1109 additions and 109 deletions
  1. 2 2
      .github/workflows/main.yml
  2. 13 2
      LICENSE.md
  3. 98 52
      README.md
  4. 33 0
      Source/QuestPDF.DocumentationExamples/AlignmentExamples.cs
  5. 33 0
      Source/QuestPDF.DocumentationExamples/AspectRatioExamples.cs
  6. 54 0
      Source/QuestPDF.DocumentationExamples/BackgroundExamples.cs
  7. 69 0
      Source/QuestPDF.DocumentationExamples/BorderExamples.cs
  8. 38 0
      Source/QuestPDF.DocumentationExamples/ColorsExamples.cs
  9. 62 0
      Source/QuestPDF.DocumentationExamples/ColumnExamples.cs
  10. 65 0
      Source/QuestPDF.DocumentationExamples/ConstrainedExamples.cs
  11. 64 0
      Source/QuestPDF.DocumentationExamples/ContentDirectionExamples.cs
  12. 38 0
      Source/QuestPDF.DocumentationExamples/DebugAreaExamples.cs
  13. 42 0
      Source/QuestPDF.DocumentationExamples/DecorationExamples.cs
  14. 40 0
      Source/QuestPDF.DocumentationExamples/DefaultTextStyleExamples.cs
  15. 57 0
      Source/QuestPDF.DocumentationExamples/FlipExamples.cs
  16. 13 0
      Source/QuestPDF.DocumentationExamples/LicenseSetup.cs
  17. 55 0
      Source/QuestPDF.DocumentationExamples/PaddingExamples.cs
  18. 28 0
      Source/QuestPDF.DocumentationExamples/QuestPDF.DocumentationExamples.csproj
  19. 74 0
      Source/QuestPDF.DocumentationExamples/RowExamples.cs
  20. 45 0
      Source/QuestPDF.DocumentationExamples/ScaleExamples.cs
  21. 91 0
      Source/QuestPDF.Examples/RoundedCornersExample.cs
  22. 6 0
      Source/QuestPDF.sln
  23. 9 10
      Source/QuestPDF/Drawing/DocumentGenerator.cs
  24. 33 7
      Source/QuestPDF/Elements/EnsureSpace.cs
  25. 5 0
      Source/QuestPDF/Elements/Table/Table.cs
  26. 6 2
      Source/QuestPDF/Fluent/DebugExtensions.cs
  27. 8 7
      Source/QuestPDF/Fluent/ElementExtensions.cs
  28. 3 0
      Source/QuestPDF/Helpers/Helpers.cs
  29. 12 14
      Source/QuestPDF/Helpers/NativeDependencyCompatibilityChecker.cs
  30. 1 1
      Source/QuestPDF/Helpers/NativeDependencyProvider.cs
  31. 4 4
      Source/QuestPDF/Infrastructure/LicenseType.cs
  32. 6 5
      Source/QuestPDF/Qpdf/QpdfNativeDependencyCompatibilityChecker.cs
  33. 1 2
      Source/QuestPDF/Resources/ReleaseNotes.txt
  34. BIN
      Source/QuestPDF/Runtimes/linux-arm64/native/libqpdf.so
  35. BIN
      Source/QuestPDF/Runtimes/linux-musl-x64/native/libqpdf.so
  36. BIN
      Source/QuestPDF/Runtimes/linux-x64/native/libqpdf.so
  37. BIN
      Source/QuestPDF/Runtimes/osx-arm64/native/libqpdf.dylib
  38. BIN
      Source/QuestPDF/Runtimes/osx-x64/native/libqpdf.dylib
  39. BIN
      Source/QuestPDF/Runtimes/win-x64/native/qpdf.dll
  40. BIN
      Source/QuestPDF/Runtimes/win-x86/native/qpdf.dll
  41. 1 1
      Source/QuestPDF/Settings.cs

+ 2 - 2
.github/workflows/main.yml

@@ -67,12 +67,12 @@ jobs:
       - name: Install Dependencies required for QPDF (Linux)
         if: matrix.runtime.name == 'linux-x64' || matrix.runtime.name == 'linux-arm64'
         shell: bash
-        run: apt install openssl gnutls-bin libjpeg-dev --yes
+        run: apt install openssl libjpeg-turbo8 --yes
           
 
       - name: Install Dependencies required for QPDF (Linux MUSL)
         if: matrix.runtime.name == 'linux-musl-x64'
-        run: apk add openssl gnutls libjpeg-turbo
+        run: apk add openssl libjpeg-turbo
 
 
       - name: Setup dotnet

+ 13 - 2
LICENSE.md

@@ -1,5 +1,8 @@
 # QuestPDF License
 
+ 
+<img src="https://github.com/user-attachments/assets/6bc20263-d916-425a-9031-8f372aeb9cfa" width="48px" />
+
 ## License Selection Guide
 
 Welcome to QuestPDF! This guide will help you understand how to select the appropriate license for our library, based on your usage context.
@@ -37,7 +40,10 @@ Enterprise License - If your company has more than 10 developers using QuestPDF,
 
 Remember, purchasing a license isn't just about adhering to our guidelines, but also supporting the development of QuestPDF. Your contribution helps us to improve the library and offer top-notch support to all users.
 
-
+<br>
+<br>
+<br>
+<img src="https://github.com/user-attachments/assets/8fc05829-2253-4114-93e8-b46199dd1218" width="48px" />
 
 ## QuestPDF Community MIT License
 
@@ -53,7 +59,12 @@ The above copyright notice and this permission notice shall be included in all c
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-
+<br>
+<br>
+<br>
+<img src="https://github.com/user-attachments/assets/690ad53b-bf57-4a5c-b3ac-cc3f6d009d2f" width="48px" />
+&nbsp;
+<img src="https://github.com/user-attachments/assets/4feff827-8b66-48e4-a3c2-f63e0237cb6f" width="48px" />
 
 ## QuestPDF Professional and Enterprise Use License
 

+ 98 - 52
README.md

@@ -9,86 +9,133 @@
 [![GitHub Repo stars](https://img.shields.io/github/stars/QuestPDF/QuestPDF?style=for-the-badge)](https://github.com/QuestPDF/QuestPDF/stargazers)
 [![Nuget version](https://img.shields.io/nuget/v/QuestPdf?style=for-the-badge)](https://www.nuget.org/packages/QuestPDF/)
 [![Nuget download](https://img.shields.io/nuget/dt/QuestPDF?style=for-the-badge)](https://www.nuget.org/packages/QuestPDF/)
-[![QuestPDF License](https://img.shields.io/badge/LICENSE%20details-Community%20MIT%20and%20professional-green?style=for-the-badge)](https://www.questpdf.com/pricing.html)
+[![QuestPDF License](https://img.shields.io/badge/LICENSE%20details-Community%20MIT%20and%20professional-green?style=for-the-badge)](https://www.questpdf.com/license/)
 
 <br />
 
-
 ### QuestPDF is a modern open-source .NET library for PDF document generation. Offering comprehensive layout engine powered by concise and discoverable C# Fluent API.
 
-<img src="https://github.com/QuestPDF/QuestPDF-Documentation/blob/main/docs/public/previewer/animation.gif?raw=true" width="100%">
-
-<table>
-<tr>
-    <td>👨‍💻</td>
-    <td>Design PDF documents using C# and employ a code-only approach. Utilize your version control system to its fullest potential.</td>
-</tr>
-<tr>
-    <td>🧱</td>
-    <td>Compose PDF document with a range of powerful and predictable structural elements, such as text, image, border, table, and many more.</td>
-</tr>
-<tr>
-    <td>⚙️</td>
-    <td>Utilize a comprehensive layout engine, specifically designed for PDF document generation and paging support.</td>
-</tr>
-<tr>
-    <td>📖</td>
-    <td>Write code using concise and easy-to-understand C# Fluent API. Utilize IntelliSense to quickly discover available options.</td>
-</tr>
-<tr>
-    <td>🔗</td>
-    <td>Don't be limited to any proprietary scripting language or format. Follow your experience and leverage all modern C# features.</td>
-</tr>
-<tr>
-    <td>⌛</td>
-    <td>Save time thanks to a hot-reload capability, allowing real-time PDF document preview without code recompilation.</td>
-</tr>
-</table>
+https://github.com/user-attachments/assets/a674c413-34c4-47b5-b559-f279b1bf46c0
 
 <br />
-
 <br />
-
+<br />
 
 ## Please help by giving a star
 
-Choosing a project dependency could be difficult. We need to ensure stability and maintainability of our projects. Surveys show that GitHub stars count play an important factor when assessing library quality. 
+GitHub stars guide developers toward great tools. If you find this project valuable, please give it a star – it helps the community and takes just a second! ⭐
 
-⭐ Please give this repository a star. It takes seconds and help thousands of developers! ⭐
-
-<img src="https://github.com/user-attachments/assets/9aa3bd4f-a061-42bd-9fe9-0a2ddc48ba1d" width="700" />
+<img src="https://github.com/user-attachments/assets/10aa9123-c395-4dc9-8977-aa9531e07227" width="700" />
 
+<br />
 
 ## QuestPDF Companion App
 
-The QuestPDF Companion application is a tool designed to simplify and speed up your development lifecycle. First, it shows a preview of your document. But the real magic starts with the hot-reload capability! It observes your code and updates the preview every time you change the implementation. Get real-time results without the need of code recompilation. Save time and enjoy the task!
+Accelerate your development with live document preview powered by the hot-reload capability, eliminating the need for code recompilation:
+- Explore document structure and hierarchy
+- Quickly magnify and measure content
+- Debug runtime exceptions with stack traces and code snippets
+- Identify, understand and solve layout errors
 
 <picture>
   <source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/7ab596d4-eebc-44e6-b36d-c358b16ed0ba">
   <source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/39d4c08c-6a78-4743-8837-208c0c1718fd">
-  <img src="https://github.com/user-attachments/assets/ce394258-1f10-498d-b65f-26c9fbed2994">
+  <img src="https://github.com/user-attachments/assets/ce394258-1f10-498d-b65f-26c9fbed2994" width="600">
 </picture>
 
 [![Companion App](https://img.shields.io/badge/%F0%9F%9A%80%20read-companion%20app-blue?style=for-the-badge)](https://www.questpdf.com/companion/features.html)
 
+<br />
 
-## Please share with the community
+## What you need is here
 
-As an open-source project without funding, I cannot afford advertising QuestPDF in a typical way. Instead, the library relies on community interactions. Please consider sharing a post about QuestPDF and the value it provides. It really does help!
+`Comprehensive Layout Engine` - A layout engine tailored for document generation, offering advanced paging and precise content control.
 
-[![Share on Reddit](https://img.shields.io/badge/share%20on-reddit-red?logo=reddit&style=for-the-badge)](https://reddit.com/submit?url=https://github.com/QuestPDF/QuestPDF&title=Check%20out%20QuestPDF%20%F0%9F%8E%8A%20a%20modern%20open-source%20.NET%20library%20%20for%20PDF%20document%20generation%20%F0%9F%9A%80)
-[![Share on Twitter](https://img.shields.io/badge/share%20on-twitter-03A9F4?logo=twitter&style=for-the-badge)](https://twitter.com/share?url=https://github.com/QuestPDF/QuestPDF&text=Check%20out%20QuestPDF%20%F0%9F%8E%8A%20a%20modern%20open-source%20.NET%20library%20%20for%20PDF%20document%20generation%20%F0%9F%9A%80%20%23dotnet%20%23csharp%20%23questpdf)
-[![Share on HackerNews](https://img.shields.io/badge/share%20on-hacker%20news-orange?logo=ycombinator&style=for-the-badge)](https://news.ycombinator.com/submitlink?u=https://github.com/QuestPDF/QuestPDF&t=QuestPDF%20-%20a%20modern%20open-source%20.NET%20library%20%20for%20PDF%20document%20generation)
-[![Share on Facebook](https://img.shields.io/badge/share%20on-facebook-1976D2?logo=facebook&style=for-the-badge)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/QuestPDF/QuestPDF)
+`Rich Toolkit` - Craft documents with intuitive, reusable components and over 50 layout elements for complex designs.
+
+`High Performance` - Generate thousands of pages per second with minimal CPU and memory usage.
+
+`Advanced Language Support` - Seamlessly create multilingual documents with support for RTL, text shaping, and bi-directional content.
 
 <br />
 
+## Code-Focused Paradigm
 
-## Let's get started
+Using C# to design PDF documents leverages powerful control structures like if-statements, for-loops, and methods, enabling dynamic and highly customizable content generation.
+
+It promotes best practices such as modular design and reusability while seamlessly integrating with source control systems for collaboration and versioning.
+
+```csharp
+.Column(column =>
+{
+    if (Model.Comments != null)
+        column.Item().Text(Model.Comments);
+
+    foreach(var item in Model.Items)
+       column.Item().Element(c => CreateItem(c, item);
+});
+```
 
-Begin exploring the QuestPDF library today. You are 250 lines of C# code away from creating a fully functional PDF invoice implementation.
+```diff
+void CreateItem(IContainer container, Item item)
+{
+    container
+-       .Background(Colors.Grey.Lighten2)
++       .Background(item.Color)
+        .Padding(10)
+        .Text(item.Text);
+}
+```
 
-Read the Getting Started tutorial to familiarize yourself with general library architecture, important layout structures as well as to better understand helpful patterns and practices. Easily start designing your PDF documents, reports, invoices and even more.
+<br />
+
+## Multiplatform
+
+The library supports all major operating systems, integrates seamlessly with leading IDEs as well as popular cloud platforms and technologies to ensure maximum flexibility.
+
+- `Technologies`: modern dotnet, legacy .NET Framework, Docker
+- `Operating systems`: Windows, Linux, MacOS
+- `Cloud providers`: Azure, AWS, Google Cloud
+- `IDE`: Visual Studio, Visual Code, JetBrains Rider, others
+
+<br />
+
+## Perform common PDF operations
+
+- Merge documents
+- Attach files
+- Extract pages
+- Encrypt / decrypt
+- Extend metadata
+- Limit access
+- Optimize for Web
+- Overlay / underlay
+
+```csharp
+DocumentOperation
+    .LoadFile("input.pdf")
+    .TakePages("1-10")
+    .MergeFile("appendix.pdf", "1-z") // all pages
+    .AddAttachment(new DocumentAttachment
+    {
+        FilePath = "metadata.xml"
+    })
+    .Encrypt(new Encryption256Bit
+    {
+        OwnerPassword = "mypassword",
+        AllowPrinting = true,
+        AllowContentExtraction = false
+    })
+    .Save("final-document.pdf");
+```
+
+[![Getting started tutorial](https://img.shields.io/badge/%F0%9F%9A%80%20read-document%20operations-blue?style=for-the-badge)](https://www.questpdf.com/concepts/document-operations.html)
+
+<br />
+
+## Let's get started
+
+Follow our detailed tutorial, and see how easy it is to produce a fully functional invoice with fewer than 250 lines of C# code.
 
 [![Getting started tutorial](https://img.shields.io/badge/%F0%9F%9A%80%20read-getting%20started-blue?style=for-the-badge)](https://www.questpdf.com/getting-started.html)
 
@@ -97,13 +144,12 @@ Read the Getting Started tutorial to familiarize yourself with general library a
 <br />
 
 
-## Library License
-
-We identify the importance of the library in your projects, so we want to ensure you can safely and confidently continue the development.
+## Sustainable and Fair License
 
-Being a healthy and growing community is the primary goal that motivates us to pursue professionalism.
+By offering free access to most users and premium licenses for larger organizations, the project maintains its commitment to excellence while ensuring sustainable, long-term development for all.
 
-The library is available for free to the vast majority of users. However, please look at the QuestPDF License and Pricing webpage for more details:
+> [!WARNING]
+> The library is free to use for any individual or business with less than 1 million USD annual gross revenue, or operates as a non-profit organization, or is a FOSS project.
 
 [![Library license details](https://img.shields.io/badge/%F0%9F%93%9C%0A%20read-license%20details-blue?style=for-the-badge)](https://www.questpdf.com/license/)
 

+ 33 - 0
Source/QuestPDF.DocumentationExamples/AlignmentExamples.cs

@@ -0,0 +1,33 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class AlignmentExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(300)
+                        .Height(300)
+                        .AlignBottom()
+                        .AlignCenter()
+                        .Background(Colors.Grey.Lighten2)
+                        .Padding(10)
+                        .Text("Lorem ipsum");
+                });
+            })
+            .GenerateImages(x => "alignment.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 33 - 0
Source/QuestPDF.DocumentationExamples/AspectRatioExamples.cs

@@ -0,0 +1,33 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class AspectRatioExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(300)
+                        .Height(300)
+                        .AspectRatio(3f/4f, AspectRatioOption.FitArea)
+                        .Background(Colors.Grey.Lighten2)
+                        .AlignCenter()
+                        .AlignMiddle()
+                        .Text("3:4 Content Area");
+                });
+            })
+            .GenerateImages(x => "aspect-ratio.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 54 - 0
Source/QuestPDF.DocumentationExamples/BackgroundExamples.cs

@@ -0,0 +1,54 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class BackgroundExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    var colors = new[]
+                    {
+                        Colors.LightBlue.Darken4,
+                        Colors.LightBlue.Darken3,
+                        Colors.LightBlue.Darken2,
+                        Colors.LightBlue.Darken1,
+    
+                        Colors.LightBlue.Medium,
+    
+                        Colors.LightBlue.Lighten1,
+                        Colors.LightBlue.Lighten2,
+                        Colors.LightBlue.Lighten3,
+                        Colors.LightBlue.Lighten4,
+                        Colors.LightBlue.Lighten5,
+    
+                        Colors.LightBlue.Accent1,
+                        Colors.LightBlue.Accent2,
+                        Colors.LightBlue.Accent3,
+                        Colors.LightBlue.Accent4,
+                    };
+                    
+                    page.Content()
+                        .Height(100)
+                        .Width(280)
+                        .Row(row =>
+                        {
+                            foreach (var color in colors)
+                                row.RelativeItem().Background(color);
+                        });
+                });
+            })
+            .GenerateImages(x => "background.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 69 - 0
Source/QuestPDF.DocumentationExamples/BorderExamples.cs

@@ -0,0 +1,69 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class BorderExamples
+{
+    [Test]
+    public void SimpleExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    
+                    page.Content()
+                        .Width(150)
+                        .Padding(25)  
+                        .BorderLeft(4)
+                        .BorderTop(6)
+                        .BorderRight(8) 
+                        .BorderBottom(10)
+                        .BorderColor(Colors.LightBlue.Darken3)
+                        .Background(Colors.Grey.Lighten3)
+                        .Padding(25) 
+                        .Text("Text");
+                });
+            })
+            .GenerateImages(x => "border-simple.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+    
+    [Test]
+    public void ManyExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    
+                    page.Content()
+                        .Width(150)
+                        .Padding(25)
+
+                        .BorderTop(5)
+                        .BorderColor(Colors.LightGreen.Darken2)
+
+                        .Container()
+
+                        .BorderBottom(10)
+                        .BorderColor(Colors.LightBlue.Darken2)
+                        
+                        .Background(Colors.Grey.Lighten3)
+                        .Padding(25)
+                        .Text("Text")
+                        .FontSize(20);
+                });
+            })
+            .GenerateImages(x => "border-many.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 38 - 0
Source/QuestPDF.DocumentationExamples/ColorsExamples.cs

@@ -0,0 +1,38 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class ColorsExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content() 
+                        .Width(175)
+                        .Padding(20)
+                        .Border(1)
+                        .BorderColor("#03A9F4")
+                        .Background(Colors.LightBlue.Lighten5)
+                        .Padding(20)
+                        .Text("Blue text")
+                        .Bold()
+                        .FontColor(Colors.LightBlue.Darken4)
+                        .Underline()
+                        .DecorationWavy()
+                        .DecorationColor(0xFF0000);
+                });
+            })
+            .GenerateImages(x => "colors.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 62 - 0
Source/QuestPDF.DocumentationExamples/ColumnExamples.cs

@@ -0,0 +1,62 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class ColumnExamples
+{
+    [Test]
+    public void SimpleExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    
+                    page.Content()
+                        .Width(250)
+                        .Padding(25)
+                        .Column(column =>
+                        {
+                            column.Item().Background(Colors.Grey.Medium).Height(50);
+                            column.Item().Background(Colors.Grey.Lighten1).Height(75);
+                            column.Item().Background(Colors.Grey.Lighten2).Height(100);
+                        });
+                });
+            })
+            .GenerateImages(x => "column-simple.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+    
+    [Test]
+    public void ManyExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+                    
+                    page.Content()
+                        .Width(250)
+                        .Padding(25)
+                        .Column(column =>
+                        {
+                            column.Spacing(25);
+                            
+                            column.Item().Background(Colors.Grey.Medium).Height(50);
+                            column.Item().Background(Colors.Grey.Lighten1).Height(75);
+                            column.Item().Background(Colors.Grey.Lighten2).Height(100);
+                        });
+                });
+            })
+            .GenerateImages(x => "column-spacing.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 65 - 0
Source/QuestPDF.DocumentationExamples/ConstrainedExamples.cs

@@ -0,0 +1,65 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class ConstrainedExamples
+{
+    [Test]
+    public void WidthExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(300)
+                        .Padding(25)
+                        .Column(column =>
+                        {
+                            column.Spacing(25);
+                            
+                            column.Item()
+                                .MinWidth(200)
+                                .Background(Colors.Grey.Lighten3)
+                                .Text("Lorem ipsum");
+                            
+                            column.Item()
+                                .MaxWidth(100)
+                                .Background(Colors.Grey.Lighten3)
+                                .Text("dolor sit amet");
+                        });
+                });
+            })
+            .GenerateImages(x => "width.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+    
+    [Test]
+    public void HeightExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(300)
+                        .Padding(25)
+                        .Height(100)
+                        .AspectRatio(2f, AspectRatioOption.FitHeight)
+                        .Background(Colors.Grey.Lighten1);
+                });
+            })
+            .GenerateImages(x => "height.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 64 - 0
Source/QuestPDF.DocumentationExamples/ContentDirectionExamples.cs

@@ -0,0 +1,64 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class ContentDirectionExamples
+{
+    [Test]
+    public void LeftToRightExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(250)
+                        .ContentFromLeftToRight()
+                        .Row(row =>
+                        {
+                            row.Spacing(5);
+    
+                            row.AutoItem().Height(50).Width(50).Background(Colors.Red.Lighten1);
+                            row.AutoItem().Height(50).Width(50).Background(Colors.Green.Lighten1);
+                            row.AutoItem().Height(50).Width(75).Background(Colors.Blue.Lighten1);
+                        });
+                });
+            })
+            .GenerateImages(x => "content-direction-ltr.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+    
+    [Test]
+    public void RightToLeftExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(250)
+                        .ContentFromRightToLeft()
+                        .Row(row =>
+                        {
+                            row.Spacing(5);
+    
+                            row.AutoItem().Height(50).Width(50).Background(Colors.Red.Lighten1);
+                            row.AutoItem().Height(50).Width(50).Background(Colors.Green.Lighten1);
+                            row.AutoItem().Height(50).Width(75).Background(Colors.Blue.Lighten1);
+                        });
+                });
+            })
+            .GenerateImages(x => "content-direction-rtl.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 38 - 0
Source/QuestPDF.DocumentationExamples/DebugAreaExamples.cs

@@ -0,0 +1,38 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class DebugAreaExamples
+{
+    [Test]
+    public void LeftToRightExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(250)
+                        .Height(250)
+                        .Padding(25)
+                        .DebugArea("Grid example", Colors.Blue.Medium)
+                        .Grid(grid =>
+                        {
+                            grid.Columns(3);
+                            grid.Spacing(5);
+
+                            foreach (var _ in Enumerable.Range(0, 8))
+                                grid.Item().Height(50).Placeholder();
+                        });
+                });
+            })
+            .GenerateImages(x => "debug-area.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 216 });
+    }
+}

+ 42 - 0
Source/QuestPDF.DocumentationExamples/DecorationExamples.cs

@@ -0,0 +1,42 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class DecorationExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .MaxHeight(300)
+                        .MaxWidth(300)
+                        .Decoration(decoration =>
+                        {
+                            decoration
+                                .Before()
+                                .Background(Colors.Grey.Medium)
+                                .Padding(10)
+                                .Text("Notes").FontColor("#FFF").Bold();
+
+                            decoration
+                                .Content()
+                                .Background(Colors.Grey.Lighten3)
+                                .Padding(10)
+                                .Text(Helpers.Placeholders.LoremIpsum());
+                        });
+                });
+            })
+            .GenerateImages(x => $"decoration-{x}.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 40 - 0
Source/QuestPDF.DocumentationExamples/DefaultTextStyleExamples.cs

@@ -0,0 +1,40 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class DefaultTextStyleExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(400)
+                        .Padding(25)
+                        .DefaultTextStyle(x => x.Bold().Underline())
+                        .Column(column =>
+                        { 
+                            column.Spacing(10);
+                            
+                            column.Item().Text("Inherited bold and underline");
+                            column.Item().Text("Disabled underline, inherited bold and adjusted font color").Underline(false).FontColor(Colors.Green.Darken2);
+    
+                            column.Item()
+                                .DefaultTextStyle(x => x.DecorationWavy().FontColor(Colors.LightBlue.Darken3))
+                                .Text("Changed underline type and adjusted font color");
+                        }); 
+                });
+            })
+            .GenerateImages(x => "default-text-style.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 57 - 0
Source/QuestPDF.DocumentationExamples/FlipExamples.cs

@@ -0,0 +1,57 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class FlipExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(350)
+                        .Height(350)
+                        .Padding(20)
+                        .Grid(grid =>
+                        {
+                            grid.Columns(2);
+                            grid.Spacing(10);
+    
+                            foreach (var turns in Enumerable.Range(0, 4))
+                            {
+                                grid.Item()
+                                    .Width(150)
+                                    .Height(150)
+                                    .Background(Colors.Grey.Lighten2)
+                                    .Padding(10)
+                                    .Element(element =>
+                                    {
+                                        if (turns == 1 || turns == 2)
+                                            element = element.FlipHorizontal();
+
+                                        if (turns == 2 || turns == 3)
+                                            element = element.FlipVertical();
+                
+                                        return element;
+                                    })
+                                    .Shrink()
+                                    .Background(Colors.White)
+                                    .Padding(10)
+                                    .Text($"Flipped {turns}").FontSize(16);
+                            }
+                        });
+                });
+            })
+            .GenerateImages(x => "flip.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 13 - 0
Source/QuestPDF.DocumentationExamples/LicenseSetup.cs

@@ -0,0 +1,13 @@
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+[SetUpFixture]
+public class LicenseSetup
+{
+    [OneTimeSetUp]
+    public static void Setup()
+    {
+        QuestPDF.Settings.License = LicenseType.Community;
+    }
+}

+ 55 - 0
Source/QuestPDF.DocumentationExamples/PaddingExamples.cs

@@ -0,0 +1,55 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class PaddingExamples
+{
+    [Test]
+    public void SimpleExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(250)
+                        .PaddingVertical(10)
+                        .PaddingLeft(20)
+                        .PaddingRight(40)
+                        .Background(Colors.Grey.Lighten2)
+                        .Text("Sample text");
+                });
+            })
+            .GenerateImages(x => "padding-simple.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+    
+    [Test]
+    public void NegativeExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(250)
+                        .Padding(50)
+                        .Background(Colors.Grey.Lighten2)
+                        .PaddingHorizontal(-25)
+                        .Text("Sample text with negative padding");
+                });
+            })
+            .GenerateImages(x => "padding-negative.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 28 - 0
Source/QuestPDF.DocumentationExamples/QuestPDF.DocumentationExamples.csproj

@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+
+        <IsPackable>false</IsPackable>
+        <IsTestProject>true</IsTestProject>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="coverlet.collector" Version="6.0.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
+        <PackageReference Include="NUnit" Version="3.14.0"/>
+        <PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
+        <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <Using Include="NUnit.Framework"/>
+    </ItemGroup>
+
+    <ItemGroup>
+      <ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
+    </ItemGroup>
+
+</Project>

+ 74 - 0
Source/QuestPDF.DocumentationExamples/RowExamples.cs

@@ -0,0 +1,74 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class RowExamples
+{
+    [Test]
+    public void SimpleExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Padding(25)
+                        .Width(325)
+                        .Row(row =>
+                        {
+                            row.ConstantItem(100)
+                                .Background(Colors.Grey.Medium)
+                                .Padding(10)
+                                .Text("100pt");
+    
+                            row.RelativeItem()
+                                .Background(Colors.Grey.Lighten1)
+                                .Padding(10)
+                                .Text("75pt");
+    
+                            row.RelativeItem(2)
+                                .Background(Colors.Grey.Lighten2)
+                                .Padding(10)
+                                .Text("150pt");
+                        });
+                });
+            })
+            .GenerateImages(x => "row-simple.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+    
+    [Test]
+    public void SpacingExample()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Padding(25)
+                        .Width(220)
+                        .Height(50)
+                        .Row(row =>
+                        {
+                            row.Spacing(10);
+        
+                            row.RelativeItem(2).Background(Colors.Grey.Medium);
+                            row.RelativeItem(3).Background(Colors.Grey.Lighten1);
+                            row.RelativeItem(5).Background(Colors.Grey.Lighten2);
+                        });
+                });
+            })
+            .GenerateImages(x => "row-spacing.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 45 - 0
Source/QuestPDF.DocumentationExamples/ScaleExamples.cs

@@ -0,0 +1,45 @@
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.DocumentationExamples;
+
+public class ScaleExamples
+{
+    [Test]
+    public void Example()
+    {
+        Document
+            .Create(document =>
+            {
+                document.Page(page =>
+                {
+                    page.MinSize(new PageSize(0, 0));
+                    page.MaxSize(new PageSize(1000, 1000));
+                    page.DefaultTextStyle(x => x.FontSize(20));
+
+                    page.Content()
+                        .Width(350)
+                        .Padding(25)
+                        .Column(column =>
+                        {
+                            column.Spacing(10);
+                            
+                            var scales = new[] { 0.75f, 1f, 1.25f, 1.5f };
+
+                            foreach (var scale in scales)
+                            {
+                                column
+                                    .Item()
+                                    .Background(Colors.Grey.Lighten3)
+                                    .Scale(scale)
+                                    .Padding(10)
+                                    .Text($"Content scale: {scale}")
+                                    .FontSize(20);
+                            }
+                        });
+                });
+            })
+            .GenerateImages(x => "scale.webp", new ImageGenerationSettings() { ImageFormat = ImageFormat.Webp, ImageCompressionQuality = ImageCompressionQuality.VeryHigh, RasterDpi = 144 });
+    }
+}

+ 91 - 0
Source/QuestPDF.Examples/RoundedCornersExample.cs

@@ -0,0 +1,91 @@
+using System;
+using NUnit.Framework;
+using QuestPDF.Examples.Engine;
+using QuestPDF.Fluent;
+using QuestPDF.Helpers;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Examples;
+
+public class RoundedCornersExample
+{
+    [Test]
+    public void ItemTypes()
+    {
+        RenderingTest
+            .Create()
+            .ProducePdf()
+            .PageSize(650, 300)
+            .ShowResults()
+            .Render(container =>
+            {
+                var roundedRectangle = new RoundedRectangleParameters
+                {
+                    TopLeftRadius = 5,
+                    TopRightRadius = 10,
+                    BottomRightRadius = 15,
+                    BottomLeftRadius = 20,
+                    FillColor = Colors.Blue.Lighten3
+                };
+
+                container
+                    .Padding(10)
+                    .Shrink()
+                    .Layers(layers =>
+                    {
+                        layers.Layer().Svg(roundedRectangle.GenerateSVG);
+                        layers.PrimaryLayer().Padding(10).Text(Placeholders.Sentence());
+                    });
+            });
+    }
+}
+
+public class RoundedRectangleParameters
+{
+    public double TopLeftRadius { get; set; }
+    public double TopRightRadius { get; set; }
+    public double BottomLeftRadius { get; set; }
+    public double BottomRightRadius { get; set; }
+    public string FillColor { get; set; }
+    
+    public string GenerateSVG(Size size)
+    {
+        var maxAllowedRadiusX = size.Width / 2.0;
+        var maxAllowedRadiusY = size.Height / 2.0;
+        
+        TopLeftRadius = Math.Min(TopLeftRadius, Math.Min(maxAllowedRadiusX, maxAllowedRadiusY));
+        TopRightRadius = Math.Min(TopRightRadius, Math.Min(maxAllowedRadiusX, maxAllowedRadiusY));
+        BottomRightRadius = Math.Min(BottomRightRadius, Math.Min(maxAllowedRadiusX, maxAllowedRadiusY));
+        BottomLeftRadius = Math.Min(BottomLeftRadius, Math.Min(maxAllowedRadiusX, maxAllowedRadiusY));
+        
+        var pathData = string.Format(System.Globalization.CultureInfo.InvariantCulture,
+            "M {0},0 " +
+            "H {1} " +
+            "A {2},{2} 0 0 1 {3},{4} " +
+            "V {5} " +
+            "A {6},{6} 0 0 1 {7},{8} " +
+            "H {9} " +
+            "A {10},{10} 0 0 1 {11},{12} " +
+            "V {13} " +
+            "A {14},{14} 0 0 1 {15},0 Z",
+            TopLeftRadius,
+            size.Width - TopRightRadius,
+            TopRightRadius, size.Width, TopRightRadius,
+            size.Height - BottomRightRadius,
+            BottomRightRadius, size.Width - BottomRightRadius, size.Height,
+            BottomLeftRadius,
+            BottomLeftRadius, 0, size.Height - BottomLeftRadius,
+            TopLeftRadius,
+            TopLeftRadius, TopLeftRadius);
+        
+        return string.Format(System.Globalization.CultureInfo.InvariantCulture,
+            "<svg size.Width=\"{0}\" size.Height=\"{1}\" xmlns=\"http://www.w3.org/2000/svg\" " +
+            "viewBox=\"0 0 {0} {1}\">" +
+            "<path d=\"{2}\" fill=\"{3}\" />" +
+            "</svg>",
+            size.Width, size.Height,
+            pathData,
+            FillColor);
+    }
+}
+

+ 6 - 0
Source/QuestPDF.sln

@@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Companion.TestRunn
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.ZUGFeRD", "QuestPDF.ZUGFeRD\QuestPDF.ZUGFeRD.csproj", "{B301CCFC-9E8B-4C4E-B34A-C95CF6D03367}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.DocumentationExamples", "QuestPDF.DocumentationExamples\QuestPDF.DocumentationExamples.csproj", "{91293E51-0830-4E30-8AD5-75536F19DB95}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -53,5 +55,9 @@ Global
 		{B301CCFC-9E8B-4C4E-B34A-C95CF6D03367}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B301CCFC-9E8B-4C4E-B34A-C95CF6D03367}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B301CCFC-9E8B-4C4E-B34A-C95CF6D03367}.Release|Any CPU.Build.0 = Release|Any CPU
+		{91293E51-0830-4E30-8AD5-75536F19DB95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{91293E51-0830-4E30-8AD5-75536F19DB95}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{91293E51-0830-4E30-8AD5-75536F19DB95}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{91293E51-0830-4E30-8AD5-75536F19DB95}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 EndGlobal

+ 9 - 10
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -71,16 +71,15 @@ namespace QuestPDF.Drawing
             const string newParagraph = "\n\n";
 
             var exceptionMessage = 
-                $"QuestPDF is a modern open-source library. " +
-                $"We identify the importance of the library in your projects and therefore want to make sure you can safely and confidently continue the development. " +
-                $"Being a healthy and growing community is the primary goal that motivates us to pursue professionalism. {newParagraph}" +
-                $"Please refer to the QuestPDF License and Pricing webpage for more details. (https://www.questpdf.com/license/) {newParagraph}" +
-                $"If you are an existing QuestPDF user and for any reason cannot update, you can stay with the 2022.12.X release with the extended quality support but without any new features, improvements, or optimizations. " +
-                $"That release will always be available under the MIT license, free for commercial usage. We are planning to sunset support for the 2022.12.X branch around Q1 2024. Until then, it will continue to receive quality and bug-fix updates. {newParagraph}" +
-                $"The library does not require any license key. We trust our users, and therefore the process is simple. " +
-                $"To disable license validation and turn off this exception, please configure an eligible license using the QuestPDF.Settings.License API, for example: {newParagraph}" +
-                $"\"QuestPDF.Settings.License = LicenseType.Community;\" {newParagraph}" +
-                $"Learn more on: https://www.questpdf.com/license/configuration.html {newParagraph}";
+                $"{newParagraph}{newParagraph}Welcome to QuestPDF! 👋 {newParagraph}" +
+                $"QuestPDF is an open-source library committed to long-term sustainability and continuous improvement. {newParagraph}" +
+                $"To maintain high-quality development and support while keeping the library free for most users, we use a fair pricing model where only larger organizations help by providing necessary funding for the project. {newParagraph}" +
+                $"If your organization’s annual gross revenue exceeds $1M USD, a Commercial License is required for production use (you can freely evaluate and test QuestPDF in non-production environments at no cost). " +
+                $"In that case, please share this information with your team or management. By purchasing a license, you directly contribute to the ongoing development and reliability of QuestPDF. {newParagraph}" +
+                $"For details on the license types and determining which applies to you, please visit: https://www.questpdf.com/license/ {newParagraph}" +
+                $"We trust our users. To continue, no license key is needed. Instead, simply select and configure the appropriate license in your code. For example, if you qualify for the Community license, add: {newParagraph}" +
+                $"> QuestPDF.Settings.License = LicenseType.Community; {newParagraph}" +
+                $"Thank you for supporting QuestPDF! ❤️ By choosing the right license, you help ensure that our project remains transparent, sustainable, and continuously improving for everyone. 🚀 {newParagraph}{newParagraph}";
             
             throw new Exception(exceptionMessage)
             {

+ 33 - 7
Source/QuestPDF/Elements/EnsureSpace.cs

@@ -3,21 +3,47 @@ using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Elements
 {
-    internal sealed class EnsureSpace : ContainerElement
+    internal sealed class EnsureSpace : ContainerElement, IStateful
     {
-        public const float DefaultMinHeight = 150;
-        public float MinHeight { get; set; } = DefaultMinHeight;
+        public float? MinHeight { get; set; } = null;
 
         internal override SpacePlan Measure(Size availableSpace)
         {
             var measurement = base.Measure(availableSpace);
 
-            if (measurement.Type == SpacePlanType.PartialRender && availableSpace.Height < MinHeight)
-                return SpacePlan.Wrap("The available vertical space is smaller than requested in the constraint.");
+            if (IsFirstPageRendered)
+                return measurement;
 
-            return measurement;
+            if (measurement.Type != SpacePlanType.PartialRender)
+                return measurement;
+
+            if (MinHeight == null || MinHeight <= availableSpace.Height)
+                return measurement;
+            
+            return SpacePlan.Wrap("The available vertical space is smaller than requested in the constraint.");
+        }
+        
+        internal override void Draw(Size availableSpace)
+        {
+            base.Draw(availableSpace);
+            IsFirstPageRendered = true;
         }
 
-        internal override string? GetCompanionHint() => $"at least {MinHeight}";
+        internal override string? GetCompanionHint() => MinHeight.HasValue ? $"at least {MinHeight.Value}" : null;
+        
+        #region IStateful
+        
+        private bool IsFirstPageRendered { get; set; }
+
+        public void ResetState(bool hardReset = false)
+        {
+            if (hardReset)
+                IsFirstPageRendered = false;
+        }
+        
+        public object GetState() => IsFirstPageRendered;
+        public void SetState(object state) => IsFirstPageRendered = (bool) state;
+    
+        #endregion
     }
 }

+ 5 - 0
Source/QuestPDF/Elements/Table/Table.cs

@@ -17,6 +17,7 @@ namespace QuestPDF.Elements.Table
         
         // cache
         private bool CacheInitialized { get; set; }
+        private bool HasRelativeColumns { get; set; }
         private int StartingRowsCount { get; set; }
         private int RowsCount { get; set; }
         private int MaxRow { get; set; }
@@ -39,6 +40,7 @@ namespace QuestPDF.Elements.Table
             if (CacheInitialized)
                 return;
 
+            HasRelativeColumns = Columns.Any(x => x.RelativeSize > 0);
             StartingRowsCount = Cells.Select(x => x.Row).DefaultIfEmpty(0).Max();
             RowsCount = Cells.Select(x => x.Row + x.RowSpan - 1).DefaultIfEmpty(0).Max();
             Cells = Cells.OrderBy(x => x.Row).ThenBy(x => x.Column).ToList();
@@ -84,6 +86,9 @@ namespace QuestPDF.Elements.Table
             if (IsRendered)
                 return SpacePlan.Empty();
             
+            if (HasRelativeColumns && availableSpace.Width < Size.Epsilon)
+                return SpacePlan.Wrap("Insufficient space to render columns of relative size.");
+            
             UpdateColumnsWidth(availableSpace.Width);
             var renderingCommands = PlanLayout(availableSpace);
 

+ 6 - 2
Source/QuestPDF/Fluent/DebugExtensions.cs

@@ -31,8 +31,12 @@ namespace QuestPDF.Fluent
         }
 
         /// <summary>
-        /// <para>Inserts a virtual debug element visible in the "element trace" provided along with the <see cref="DocumentLayoutException" />.</para>
-        /// <para>Helps with understanding and navigation of that "element trace" tree hierarchy.</para>
+        /// <para>
+        ///     Inserts a virtual debug element visible in the document hierarchy tree in the QuestPDF Companion App,
+        ///     as well as in the enhanced debugging message provided by the <see cref="DocumentLayoutException" />.
+        /// </para>
+        /// 
+        /// <para>It helps with understanding and navigation of the document hierarchy.</para>
         /// <a href="https://www.questpdf.com/api-reference/debug-pointer.html">Learn more</a>
         /// </summary>
         /// <remarks>

+ 8 - 7
Source/QuestPDF/Fluent/ElementExtensions.cs

@@ -111,7 +111,7 @@ namespace QuestPDF.Fluent
         }
         
         /// <summary>
-        /// Constrains its content to maintain a given aspect ratio.
+        /// Constrains its content to maintain a given width-to-height ratio.
         /// <a href="https://www.questpdf.com/api-reference/aspect-ratio.html">Learn more</a>
         /// </summary>
         /// <remarks>
@@ -163,13 +163,14 @@ namespace QuestPDF.Fluent
         }
 
         /// <summary>
-        /// If the container spans multiple pages, its content appears only on the first one.
+        /// <para>The ShowOnce element modifies how content is displayed across multiple pages.</para>
+        /// <para>
+        /// By default, all elements are fully rendered only once and never repeated.
+        /// However, in some contexts such as page headers and footers, as well as decoration before and after slots, the content is repeated on every page.
+        /// To prevent this, you can use the ShowOnce element.
+        /// </para>
         /// <a href="https://www.questpdf.com/api-reference/show-once.html">Learn more</a>
         /// </summary>
-        /// <remarks>
-        /// <para>This element is useful if you wish to display a header on every page but want certain elements visible only on the first page.</para>
-        /// <para>Depending on the content, certain elements (such as Row or Table) may repeatedly draw their items across multiple pages. Use the ShowOnce element to modify this behavior if it's not desired.</para>
-        /// </remarks>
         /// <example>
         /// <para>Combine this element with SkipOnce to achieve more complex behaviors, e.g.:</para>
         /// <para><c>.SkipOnce().ShowOnce()</c> ensures the child element is displayed only on the second page.</para>
@@ -226,7 +227,7 @@ namespace QuestPDF.Fluent
         /// <remarks>
         /// This is especially useful for elements like tables, where you'd want to display several rows together. By setting the minHeight, you can avoid scenarios where only a single row appears at the page's end, ensuring a more cohesive presentation.
         /// </remarks>
-        public static IContainer EnsureSpace(this IContainer element, float minHeight = Elements.EnsureSpace.DefaultMinHeight)
+        public static IContainer EnsureSpace(this IContainer element, float? minHeight = null)
         {
             return element.Element(new EnsureSpace
             {

+ 3 - 0
Source/QuestPDF/Helpers/Helpers.cs

@@ -125,6 +125,9 @@ namespace QuestPDF.Helpers
 
         internal static SkImage ResizeAndCompressImage(this SkImage image, ImageSize targetResolution, ImageCompressionQuality compressionQuality)
         {
+            if (targetResolution.Width == 0 || targetResolution.Height == 0)
+                targetResolution = new ImageSize(1, 1);
+            
             return image.ResizeAndCompress(targetResolution.Width, targetResolution.Height, compressionQuality.ToQualityValue(), compressionQuality.ToDownsamplingStrategy());
         }
 

+ 12 - 14
Source/QuestPDF/Helpers/NativeDependencyCompatibilityChecker.cs

@@ -25,7 +25,6 @@ namespace QuestPDF.Helpers
             if (IsCompatibilityChecked)
                 return;
             
-            const string exceptionBaseMessage = "The QuestPDF library has encountered an issue while loading one of its dependencies.";
             const string paragraph = "\n\n";
                 
             // test with dotnet-based mechanism where native files are provided
@@ -52,21 +51,17 @@ namespace QuestPDF.Helpers
             {
                 var supportedRuntimes = string.Join(", ", NativeDependencyProvider.SupportedPlatforms);
                 var currentRuntime = NativeDependencyProvider.GetRuntimePlatform();
-                
-                var message = 
-                    $"{exceptionBaseMessage}{paragraph}" +
-                    "Your runtime is currently not supported by QuestPDF. " +
-                    $"Currently supported runtimes are: {supportedRuntimes}. ";
+                var isRuntimeSupported = NativeDependencyProvider.SupportedPlatforms.Contains(currentRuntime);
 
-                if (NativeDependencyProvider.SupportedPlatforms.Contains(currentRuntime))
-                {
-                    message += $"{paragraph}It appears that your current operating system distribution may be outdated. For optimal compatibility, please consider updating it to a more recent version.";
-                }
-                else
+                var message = $"The QuestPDF library has encountered an issue while loading one of its dependencies.";
+                
+                if (!isRuntimeSupported)
                 {
-                    message += $"{paragraph}Your current runtime is detected as '{currentRuntime}'.";
+                    message += $"{paragraph}Your runtime is not supported by QuestPDF. " +
+                                $"The following runtimes are supported: {supportedRuntimes}. " +
+                                $"Your current runtime is detected as '{currentRuntime}'. ";
                 }
-
+                
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                     message += $"{paragraph}Please always set the 'Platform target' to either 'X86' or 'X64' in your startup project settings. Please do not use the 'Any CPU' option.";
                 
@@ -76,7 +71,10 @@ namespace QuestPDF.Helpers
                 var hint = ExceptionHint.Invoke();
                 
                 if (!string.IsNullOrEmpty(hint))
-                    message += $"{paragraph}{ExceptionHint}";
+                    message += $"{paragraph}{hint}";
+
+                if (isRuntimeSupported)
+                    message += $"{paragraph}If the problem persists, it may mean that your current operating system distribution is outdated. For optimal compatibility, please consider updating it to a more recent version.";
                 
                 throw new Exception(message, innerException);
             }

+ 1 - 1
Source/QuestPDF/Helpers/NativeDependencyProvider.cs

@@ -140,7 +140,7 @@ internal static class NativeDependencyProvider
         }
     }
 
-    static void CopyFileIfNewer(string sourcePath, string targetPath)
+    private static void CopyFileIfNewer(string sourcePath, string targetPath)
     {
         if (!File.Exists(sourcePath))
             throw new FileNotFoundException($"Source file not found: {sourcePath}");

+ 4 - 4
Source/QuestPDF/Infrastructure/LicenseType.cs

@@ -2,25 +2,25 @@
 {
     /// <summary>
     /// The QuestPDF library is available under a hybrid license favorable to both community and business users.
-    /// For a comprehensive overview, please visit the <a href="https://www.questpdf.com/license/">License and Pricing details webpage</a>.
+    /// For a comprehensive overview, please visit the <a href="https://www.questpdf.com/license">License and Pricing details webpage</a>.
     /// </summary>
     public enum LicenseType
     {
         /// <summary>
         /// The QuestPDF Community MIT License is applicable mainly for companies and individuals with less than $1M USD annual gross revenue.
-        /// <a href="https://www.questpdf.com/license/summary/community.html">Learn more</a>
+        /// <a href="https://www.questpdf.com/license">Learn more</a>
         /// </summary>
         Community,
         
         /// <summary>
         /// The QuestPDF Professional License is applicable for individuals and companies with at most 10 software developers.
-        /// <a href="https://www.questpdf.com/license/summary/professional.html">Learn more</a>
+        /// <a href="https://www.questpdf.com/license">Learn more</a>
         /// </summary>
         Professional,
         
         /// <summary>
         /// The QuestPDF Enterprise License is applicable for individuals and companies with any number of software developers.
-        /// <a href="https://www.questpdf.com/license/summary/enterprise.html">Learn more</a>
+        /// <a href="https://www.questpdf.com/license">Learn more</a>
         /// </summary>
         Enterprise
     }

+ 6 - 5
Source/QuestPDF/Qpdf/QpdfNativeDependencyCompatibilityChecker.cs

@@ -31,15 +31,16 @@ internal static class QpdfNativeDependencyCompatibilityChecker
         if (!platform.StartsWith("linux"))
             return string.Empty;
         
-        const string openSslHint = "Please also ensure that the OpenSSL library is installed on your system with version at least 3.0.0.";
-        
         var command = platform switch
         {
-            "linux-x64" or "linux-arm64" => "apt install openssl-bin gnutls-bin libjpeg-dev",
-            "linux-musl-x64" => "apk add openssl gnutls libjpeg-turbo",
+            "linux-x64" or "linux-arm64" => "apt install openssl libjpeg-turbo8",
+            "linux-musl-x64" => "apk add openssl libjpeg-turbo",
             _ => throw new NotSupportedException()
         };
         
-        return $"Installing additional dependencies may help. Likely command: '{command}'. {openSslHint}";
+        const string openSslHint = "Please also ensure that the OpenSSL library is installed on your system with version at least 3.0.0.";
+        const string qpdfHint = "Do NOT install the qpdf package.";
+        
+        return $"Installing additional dependencies may help. Please try the following command: '{command}'. {openSslHint} {qpdfHint}";
     }
 }

+ 1 - 2
Source/QuestPDF/Resources/ReleaseNotes.txt

@@ -1,8 +1,7 @@
 Version 2025.1.0-alpha0
 - Implemented a Lazy element that delays content creation to reduce memory usage and enhance garbage collection efficiency in large documents.
 
-
 Version 2025.1.0-rc0
 - Optimization: releasing native objects as early as possible to reduce peak memory usage,
 - Various performance and memory optimizations,
-- Increased maximum level of parallelism from 2 to 4.
+- Increased maximum level of parallelism from 2 to 4.

BIN
Source/QuestPDF/Runtimes/linux-arm64/native/libqpdf.so


BIN
Source/QuestPDF/Runtimes/linux-musl-x64/native/libqpdf.so


BIN
Source/QuestPDF/Runtimes/linux-x64/native/libqpdf.so


BIN
Source/QuestPDF/Runtimes/osx-arm64/native/libqpdf.dylib


BIN
Source/QuestPDF/Runtimes/osx-x64/native/libqpdf.dylib


BIN
Source/QuestPDF/Runtimes/win-x64/native/qpdf.dll


BIN
Source/QuestPDF/Runtimes/win-x86/native/qpdf.dll


+ 1 - 1
Source/QuestPDF/Settings.cs

@@ -9,7 +9,7 @@ namespace QuestPDF
     {
         /// <summary>
         /// <para>Please kindly select license type that applies to your usage of the QuestPDF library.</para>
-        /// <para>For more details, please check the <a href="https://www.questpdf.com/pricing.html">QuestPDF License and Pricing webpage</a></para>
+        /// <para>For more details, please check the <a href="https://www.questpdf.com/license">QuestPDF License and Pricing webpage</a></para>
         /// </summary>
         public static LicenseType? License { get; set; }