Compare commits

..

1 Commits

Author SHA1 Message Date
MarcinZiabek d39819bf8d Optimized drawing border 2021-12-26 21:32:49 +01:00
162 changed files with 2317 additions and 5941 deletions
-131
View File
@@ -1,131 +0,0 @@
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\GithubRepos\QuestPDF codebase based on best match to current usage at 16.03.2022
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]
#Core editorconfig formatting - indentation
#use soft tabs (spaces) for indentation
indent_style = space
#Formatting - new line options
#place catch statements on a new line
csharp_new_line_before_catch = true
#place else statements on a new line
csharp_new_line_before_else = true
#require members of anonymous types to be on separate lines
csharp_new_line_before_members_in_anonymous_types = true
#require members of object intializers to be on separate lines
csharp_new_line_before_members_in_object_initializers = true
#require braces to be on a new line for object_collection_array_initializers, methods, anonymous_types, control_blocks, types, and lambdas (also known as "Allman" style)
csharp_new_line_before_open_brace =all
#Formatting - organize using options
#sort System.* using directives alphabetically, and place them before other usings
dotnet_sort_system_directives_first = true
#Formatting - spacing options
#require NO space between a cast and the value
csharp_space_after_cast = false
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_after_colon_in_inheritance_clause = true
#require a space after a keyword in a control flow statement such as a for loop
csharp_space_after_keywords_in_control_flow_statements = true
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_before_colon_in_inheritance_clause = true
#remove space within empty argument list parentheses
csharp_space_between_method_call_empty_parameter_list_parentheses = false
#remove space between method call name and opening parenthesis
csharp_space_between_method_call_name_and_opening_parenthesis = false
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
csharp_space_between_method_call_parameter_list_parentheses = false
#remove space within empty parameter list parentheses for a method declaration
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
csharp_space_between_method_declaration_parameter_list_parentheses = false
#Formatting - wrapping options
#leave code block on single line
csharp_preserve_single_line_blocks = true
#Style - Code block preferences
#prefer no curly braces if allowed
csharp_prefer_braces = false:suggestion
#Style - expression bodied member options
#prefer block bodies for constructors
csharp_style_expression_bodied_constructors = false:suggestion
#prefer block bodies for methods
csharp_style_expression_bodied_methods = false:suggestion
#prefer expression-bodied members for properties
csharp_style_expression_bodied_properties = true:suggestion
#Style - expression level options
#prefer out variables to be declared inline in the argument list of a method call when possible
csharp_style_inlined_variable_declaration = true:suggestion
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_member_access = true:suggestion
#Style - Expression-level preferences
#prefer objects to be initialized using object initializers when possible
dotnet_style_object_initializer = true:suggestion
#prefer inferred anonymous type member names
dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
#prefer inferred tuple element names
dotnet_style_prefer_inferred_tuple_names = true:suggestion
#Style - implicit and explicit types
#prefer var over explicit type in all cases, unless overridden by another code style rule
csharp_style_var_elsewhere = true:suggestion
#prefer var is used to declare variables with built-in system types such as int
csharp_style_var_for_built_in_types = true:suggestion
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
csharp_style_var_when_type_is_apparent = true:suggestion
#Style - language keyword and framework type options
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
#Style - Miscellaneous preferences
#prefer local functions over anonymous functions
csharp_style_pattern_local_over_anonymous_function = true:suggestion
#Style - modifier options
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
#Style - Modifier preferences
#when this rule is set to a list of modifiers, prefer the specified ordering.
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
#Style - Pattern matching
#prefer pattern matching instead of is expression with type casts
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
#Style - qualification options
#prefer fields not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_field = false:suggestion
#prefer methods not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_method = false:suggestion
#prefer properties not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_property = false:suggestion
[*.{cs,vb}]
tab_width=4
indent_size=4
-70
View File
@@ -1,70 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '22 16 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'csharp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+32
View File
@@ -0,0 +1,32 @@
using System.IO;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class BarCode
{
[Test]
public void Example()
{
FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
RenderingTest
.Create()
.PageSize(400, 100)
.ShowResults()
.Render(container =>
{
container
.Background(Colors.White)
.AlignCenter()
.AlignMiddle()
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
});
}
}
}
+12 -15
View File
@@ -1,6 +1,4 @@
using System.IO;
using NUnit.Framework;
using QuestPDF.Drawing;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
@@ -8,27 +6,26 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
[TestFixture]
public class BarcodeExamples
{
[Test]
public void Example()
public void Barcode()
{
FontManager.RegisterFont(File.OpenRead("LibreBarcode39-Regular.ttf"));
RenderingTest
.Create()
.PageSize(400, 200)
.ShowResults()
.PageSize(300, 300)
.Render(container =>
{
container
.Background(Colors.White)
.AlignCenter()
.AlignMiddle()
.Text("*QuestPDF*")
.FontFamily("Libre Barcode 39")
.FontSize(64);
.Background("#FFF")
.Padding(25)
.Stack(stack =>
{
stack.Item().Border(1).Background(Colors.Grey.Lighten3).Padding(5).Text("Barcode Example");
stack.Item().Border(1).Padding(5).AlignCenter().Text("*123456789*", TextStyle.Default.FontType("CarolinaBar-Demo-25E2").Size(20));
});
});
}
}
}
}
-60
View File
@@ -1,60 +0,0 @@
using Microcharts;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Examples
{
public class CanvasExamples
{
[Test]
public void BorderRadius()
{
RenderingTest
.Create()
.PageSize(175, 100)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Background(Colors.Grey.Lighten2)
.Padding(25)
.MinimalBox()
.Layers(layers =>
{
layers.Layer().Canvas((canvas, size) =>
{
DrawRoundedRectangle(Colors.White, false);
DrawRoundedRectangle(Colors.Blue.Darken2, true);
void DrawRoundedRectangle(string color, bool isStroke)
{
using var paint = new SKPaint
{
Color = SKColor.Parse(color),
IsStroke = isStroke,
StrokeWidth = 2,
IsAntialias = true
};
canvas.DrawRoundRect(0, 0, size.Width, size.Height, 20, 20, paint);
}
});
layers
.PrimaryLayer()
.PaddingVertical(10)
.PaddingHorizontal(20)
.Text("Sample text")
.FontSize(16)
.FontColor(Colors.Blue.Darken2)
.SemiBold();
});
});
}
}
}
+4 -7
View File
@@ -52,17 +52,14 @@ namespace QuestPDF.Examples
container
.Background(Colors.White)
.Padding(25)
.Column(column =>
.Stack(stack =>
{
column
stack
.Item()
.PaddingBottom(10)
.Text("Chart example")
.FontSize(20)
.SemiBold()
.FontColor(Colors.Blue.Medium);
.Text("Chart example", TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium));
column
stack
.Item()
.Border(1)
.ExtendHorizontal()
-56
View File
@@ -1,56 +0,0 @@
using System;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class ColumnExamples
{
[Test]
public void Column()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ShowResults()
.ProducePdf()
.Render(container =>
{
container.Column(column =>
{
foreach (var i in Enumerable.Range(0, 10))
column.Item().Element(Block);
static void Block(IContainer container)
{
container
.Width(72)
.Height(3.5f, Unit.Inch)
.Height(1.5f, Unit.Inch)
.Background(Placeholders.BackgroundColor());
}
});
});
}
[Test]
public void Stability_NoItems()
{
RenderingTest
.Create()
.ProducePdf()
.MaxPages(100)
.PageSize(250, 150)
.Render(container =>
{
container
.Padding(25)
.Column(column => { });
});
}
}
}
+6 -6
View File
@@ -16,7 +16,7 @@ namespace QuestPDF.Examples
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(x => GenerateStructure(x, 12));
.Render(x => GenerateStructure(x, 16));
}
private void GenerateStructure(IContainer container, int level)
@@ -36,8 +36,8 @@ namespace QuestPDF.Examples
.BorderColor(Colors.Black)
.Row(row =>
{
row.RelativeItem().Element(x => GenerateStructure(x, level));
row.RelativeItem().Element(x => GenerateStructure(x, level));
row.RelativeColumn().Element(x => GenerateStructure(x, level));
row.RelativeColumn().Element(x => GenerateStructure(x, level));
});
}
else
@@ -45,10 +45,10 @@ namespace QuestPDF.Examples
container
.Border(level / 4f)
.BorderColor(Colors.Black)
.Column(column =>
.Stack(stack =>
{
column.Item().Element(x => GenerateStructure(x, level));
column.Item().Element(x => GenerateStructure(x, level));
stack.Item().Element(x => GenerateStructure(x, level));
stack.Item().Element(x => GenerateStructure(x, level));
});
}
}
+2 -2
View File
@@ -22,10 +22,10 @@ namespace QuestPDF.Examples
page.Header().Text("Header");
page.Content().PaddingVertical(10).Border(1).Padding(10).Column(column =>
page.Content().PaddingVertical(10).Border(1).Padding(10).Stack(stack =>
{
foreach (var index in Enumerable.Range(1, 100))
column.Item().Text($"Line {index}").FontColor(Placeholders.Color());
stack.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
});
page.Footer().Text("Footer");
+2 -2
View File
@@ -9,7 +9,7 @@ namespace QuestPDF.Examples
public class DebuggingTesting
{
[Test]
public void Column()
public void Stack()
{
Assert.Throws<DocumentLayoutException>(() =>
{
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
.Width(100)
.Background(Colors.Grey.Lighten3)
.DebugPointer("Example debug pointer")
.Column(x =>
.Stack(x =>
{
x.Item().Text("Test");
x.Item().Width(150);
+6 -6
View File
@@ -13,7 +13,7 @@ namespace QuestPDF.Examples
{
RenderingTest
.Create()
.ProducePdf()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
@@ -24,13 +24,13 @@ namespace QuestPDF.Examples
page.Margin(20);
page.Size(PageSizes.A4);
page.PageColor(Colors.White);
page.Background(Colors.White);
page.Content().Column(column =>
page.Content().Stack(stack =>
{
column.Item().Text(Placeholders.Sentence());
stack.Item().Text(Placeholders.Sentence());
column.Item().Text(text =>
stack.Item().Text(text =>
{
// text in this block is additionally semibold
text.DefaultTextStyle(TextStyle.Default.SemiBold());
@@ -38,7 +38,7 @@ namespace QuestPDF.Examples
text.Line(Placeholders.Sentence());
// this text has size 20 but also semibold and red
text.Span(Placeholders.Sentence()).FontColor(Colors.Red.Medium);
text.Span(Placeholders.Sentence(), TextStyle.Default.Color(Colors.Red.Medium));
});
});
});
@@ -1,54 +0,0 @@
using System.Drawing;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class DefaultTextStyleExamples
{
[Test]
public void Placeholder()
{
RenderingTest
.Create()
.PageSize(220, 270)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.Render(container =>
{
container
.Padding(10)
.DefaultTextStyle(TextStyle.Default.Bold().Underline())
.Column(column =>
{
column.Item().Text("Default style applies to all children");
column.Item().Text("You can override certain styles").Underline(false).FontColor(Colors.Green.Darken2);
column.Item().PaddingTop(10).Border(1).Grid(grid =>
{
grid.Columns(4);
foreach (var i in Enumerable.Range(1, 16))
{
grid.Item()
.Border(1)
.BorderColor(Colors.Grey.Lighten1)
.Background(Colors.Grey.Lighten3)
.Width(50)
.Height(50)
.AlignCenter()
.AlignMiddle()
.Text(i)
.FontSize(16 + i / 4);
}
});
});
});
}
}
}
@@ -1,55 +0,0 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class DifferentHeaderOnFirstPageExample
{
[Test]
public void Placeholder()
{
RenderingTest
.Create()
.PageSize(PageSizes.A6)
.ProduceImages()
.ShowResults()
.EnableDebugging()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A6);
page.Margin(5);
page.PageColor(Colors.White);
page.Header().Column(column =>
{
column.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
column.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
});
page.Content().PaddingVertical(10).Column(column =>
{
column.Spacing(10);
foreach (var _ in Enumerable.Range(0, 13))
column.Item().Background(Colors.Grey.Lighten2).Height(40);
});
page.Footer().AlignCenter().Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.Size(16));
text.CurrentPageNumber();
text.Span(" / ");
text.TotalPages();
});
});
});
}
}
}
+62 -74
View File
@@ -40,12 +40,10 @@ namespace QuestPDF.Examples
.Decoration(decoration =>
{
decoration
.Before()
.Header()
.Background(Colors.Grey.Medium)
.Padding(10)
.Text("Notes")
.FontSize(16)
.FontColor("#FFF");
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
decoration
.Content()
@@ -68,27 +66,27 @@ namespace QuestPDF.Examples
container
.Background("#FFF")
.Padding(20)
.Column(column =>
.Stack(stack =>
{
column.Item()
stack.Item()
.PaddingBottom(10)
.AlignCenter()
.Text("This Row element is 700pt wide");
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.ConstantItem(100)
row.ConstantColumn(100)
.Background(Colors.Grey.Lighten1)
.Padding(10)
.ExtendVertical()
.Text("This column is 100 pt wide");
row.RelativeItem()
row.RelativeColumn()
.Background(Colors.Grey.Lighten2)
.Padding(10)
.Text("This column takes 1/3 of the available space (200pt)");
row.RelativeItem(2)
row.RelativeColumn(2)
.Background(Colors.Grey.Lighten3)
.Padding(10)
.Text("This column takes 2/3 of the available space (400pt)");
@@ -111,15 +109,15 @@ namespace QuestPDF.Examples
.Row(row =>
{
row.Spacing(20);
row.RelativeItem(2).Border(1).Background(Colors.Grey.Lighten1);
row.RelativeItem(3).Border(1).Background(Colors.Grey.Lighten2);
row.RelativeItem(4).Border(1).Background(Colors.Grey.Lighten3);
row.RelativeColumn(2).Border(1).Background(Colors.Grey.Lighten1);
row.RelativeColumn(3).Border(1).Background(Colors.Grey.Lighten2);
row.RelativeColumn(4).Border(1).Background(Colors.Grey.Lighten3);
});
});
}
[Test]
public void column()
public void Stack()
{
RenderingTest
.Create()
@@ -129,13 +127,13 @@ namespace QuestPDF.Examples
container
.Background("#FFF")
.Padding(15)
.Column(column =>
.Stack(stack =>
{
column.Spacing(15);
stack.Spacing(15);
column.Item().Background(Colors.Grey.Medium).Height(50);
column.Item().Background(Colors.Grey.Lighten1).Height(100);
column.Item().Background(Colors.Grey.Lighten2).Height(150);
stack.Item().Background(Colors.Grey.Medium).Height(50);
stack.Item().Background(Colors.Grey.Lighten1).Height(100);
stack.Item().Background(Colors.Grey.Lighten2).Height(150);
});
});
}
@@ -269,12 +267,12 @@ namespace QuestPDF.Examples
layers
.PrimaryLayer()
.Padding(25)
.Column(column =>
.Stack(stack =>
{
column.Spacing(5);
stack.Spacing(5);
foreach (var _ in Enumerable.Range(0, 7))
column.Item().Text(Placeholders.Sentence());
stack.Item().Text(Placeholders.Sentence());
});
// layer above the main content
@@ -282,15 +280,12 @@ namespace QuestPDF.Examples
.Layer()
.AlignCenter()
.AlignMiddle()
.Text("Watermark")
.FontSize(48)
.Bold()
.FontColor(Colors.Green.Lighten3);
.Text("Watermark", TextStyle.Default.Size(48).Bold().Color(Colors.Green.Lighten3));
layers
.Layer()
.AlignBottom()
.Text(text => text.CurrentPageNumber().FontSize(16).FontColor(Colors.Green.Medium));
.Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
});
});
}
@@ -309,16 +304,16 @@ namespace QuestPDF.Examples
// {
// page.Header().PageNumber("Page {pdf:currentPage}");
//
// page.Content().Height(300).column(content =>
// page.Content().Height(300).Stack(content =>
// {
// content.Item().Height(200).Background(Colors.Grey.Lighten2);
//
// content.Item().EnsureSpace(100).column(column =>
// content.Item().EnsureSpace(100).Stack(stack =>
// {
// column.Spacing(10);
// stack.Spacing(10);
//
// foreach (var _ in Enumerable.Range(0, 4))
// column.Item().Height(50).Background(Colors.Green.Lighten1);
// stack.Item().Height(50).Background(Colors.Green.Lighten1);
// });
// });
// });
@@ -383,7 +378,7 @@ namespace QuestPDF.Examples
.Row(row =>
{
foreach (var color in colors)
row.RelativeItem().Background(color);
row.RelativeColumn().Background(color);
});
});
}
@@ -421,9 +416,7 @@ namespace QuestPDF.Examples
.Border(1)
.BorderColor(Colors.Grey.Medium)
.Padding(10)
.Text(font)
.FontFamily(font)
.FontSize(16);
.Text(font, TextStyle.Default.FontType(font).Size(16));
}
});
});
@@ -444,10 +437,10 @@ namespace QuestPDF.Examples
{
layers.Layer().Text("Something else");
layers.PrimaryLayer().Column(column =>
layers.PrimaryLayer().Stack(stack =>
{
column.Item().PaddingTop(20).Text("Text 1");
column.Item().PaddingTop(40).Text("Text 2");
stack.Item().PaddingTop(20).Text("Text 1");
stack.Item().PaddingTop(40).Text("Text 2");
});
layers.Layer().Canvas((canvas, size) =>
@@ -463,7 +456,7 @@ namespace QuestPDF.Examples
});
layers.Layer().Background("#8F00").Extend();
layers.Layer().PaddingTop(40).Text("It works!").FontSize(24);
layers.Layer().PaddingTop(40).Text("It works!", TextStyle.Default.Size(24));
});
});
}
@@ -481,10 +474,10 @@ namespace QuestPDF.Examples
.Padding(15)
.Border(4)
.BorderColor(Colors.Blue.Medium)
//.MinimalBox()
//.Box()
.Background(Colors.Grey.Lighten2)
.Padding(15)
.Text("Test of the \n box element").FontSize(20);
.Text("Test of the \n box element", TextStyle.Default.Size(20));
});
}
@@ -508,14 +501,13 @@ namespace QuestPDF.Examples
.SemiBold();
decoration
.Before()
.Header()
.PaddingBottom(10)
.Text("Example: scale component")
.Style(headerFontStyle);
.Text("Example: scale component", headerFontStyle);
decoration
.Content()
.Column(column =>
.Stack(stack =>
{
var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
@@ -527,14 +519,13 @@ namespace QuestPDF.Examples
var fontStyle = TextStyle.Default.Size(16);
column
stack
.Item()
.Border(1)
.Background(fontColor)
.Scale(scale)
.Padding(5)
.Text($"Content with {scale} scale.")
.Style(fontStyle);
.Text($"Content with {scale} scale.", fontStyle);
}
});
});
@@ -551,7 +542,7 @@ namespace QuestPDF.Examples
{
container
.Background("#FFF")
.MinimalBox()
.Box()
.Padding(25)
@@ -564,8 +555,7 @@ namespace QuestPDF.Examples
.BorderColor(Colors.Green.Darken1)
.Padding(50)
.Text("Moved text")
.FontSize(25);
.Text("Moved text", TextStyle.Default.Size(25));
});
}
@@ -598,11 +588,10 @@ namespace QuestPDF.Examples
return element;
})
.MinimalBox()
.Box()
.Background(Colors.White)
.Padding(10)
.Text($"Rotated {turns * 90}°")
.FontSize(16);
.Text($"Rotated {turns * 90}°", TextStyle.Default.Size(16));
}
});
});
@@ -697,11 +686,10 @@ namespace QuestPDF.Examples
return element;
})
.MinimalBox()
.Box()
.Background(Colors.White)
.Padding(10)
.Text($"Flipped {turns}")
.FontSize(16);
.Text($"Flipped {turns}", TextStyle.Default.Size(16));
}
});
});
@@ -720,14 +708,14 @@ namespace QuestPDF.Examples
.Border(2)
.Row(row =>
{
row.ConstantItem(25)
row.ConstantColumn(25)
.Border(1)
.RotateLeft()
.AlignCenter()
.AlignMiddle()
.Text("Sample text");
row.RelativeItem().Border(1).Padding(5).Text(Placeholders.Paragraph());
row.RelativeColumn().Border(1).Padding(5).Text(Placeholders.Paragraph());
});
});
}
@@ -742,12 +730,12 @@ namespace QuestPDF.Examples
{
container
.Padding(25)
.PaddingLeft(50)
.Column(column =>
.PaddingLeft(75)
.Stack(stack =>
{
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
column
stack
.Item()
// creates an infinite space for its child
@@ -763,7 +751,7 @@ namespace QuestPDF.Examples
.Background(Colors.Blue.Darken1);
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
});
});
}
@@ -778,13 +766,13 @@ namespace QuestPDF.Examples
{
container
.Padding(25)
.Column(column =>
.Stack(stack =>
{
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem().LabelCell("Label 1");
row.RelativeColumn().LabelCell("Label 1");
row.RelativeItem(3).Grid(grid =>
row.RelativeColumn(3).Grid(grid =>
{
grid.Columns(3);
@@ -796,11 +784,11 @@ namespace QuestPDF.Examples
});
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem().ValueCell().Text("Value 1");
row.RelativeColumn().ValueCell().Text("Value 1");
row.RelativeItem(3).Grid(grid =>
row.RelativeColumn(3).Grid(grid =>
{
grid.Columns(3);
@@ -812,10 +800,10 @@ namespace QuestPDF.Examples
});
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem().LabelCell("Label 6");
row.RelativeItem().ValueCell().Text("Value 6");
row.RelativeColumn().LabelCell("Label 6");
row.RelativeColumn().ValueCell().Text("Value 6");
});
});
});
+8 -25
View File
@@ -20,11 +20,7 @@ namespace QuestPDF.Examples.Engine
private Size Size { get; set; }
private int? MaxPagesThreshold { get; set; }
private bool ShowResult { get; set; }
private bool ApplyCaching { get; set; }
private bool ApplyDebugging { get; set; }
private RenderingTestResult ResultType { get; set; } = RenderingTestResult.Images;
private bool ShowingResultsEnabled = true;
private RenderingTest()
{
@@ -71,24 +67,6 @@ namespace QuestPDF.Examples.Engine
return this;
}
public RenderingTest MaxPages(int value)
{
MaxPagesThreshold = value;
return this;
}
public RenderingTest EnableCaching(bool value = true)
{
ApplyCaching = value;
return this;
}
public RenderingTest EnableDebugging(bool value = true)
{
ApplyDebugging = value;
return this;
}
public void Render(Action<IContainer> content)
{
RenderDocument(container =>
@@ -100,11 +78,16 @@ namespace QuestPDF.Examples.Engine
});
});
}
public void MaxPages(int value)
{
MaxPagesThreshold = value;
}
public void RenderDocument(Action<IDocumentContainer> content)
{
MaxPagesThreshold ??= ResultType == RenderingTestResult.Pdf ? 1000 : 10;
var document = new SimpleDocument(content, MaxPagesThreshold.Value, ApplyCaching, ApplyDebugging);
var document = new SimpleDocument(content, MaxPagesThreshold.Value);
Render(document);
}
@@ -116,7 +99,7 @@ namespace QuestPDF.Examples.Engine
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
document.GenerateImages(fileNameSchema);
if (ShowResult && ShowingResultsEnabled)
if (ShowResult)
Process.Start("explorer", fileNameSchema(0));
}
@@ -125,7 +108,7 @@ namespace QuestPDF.Examples.Engine
var fileName = $"{FileNamePrefix}.pdf";
document.GeneratePdf(fileName);
if (ShowResult && ShowingResultsEnabled)
if (ShowResult)
Process.Start("explorer", fileName);
}
}
+2 -8
View File
@@ -13,15 +13,11 @@ namespace QuestPDF.Examples.Engine
private Action<IDocumentContainer> Content { get; }
private int MaxPages { get; }
private bool ApplyCaching { get; }
private bool ApplyDebugging { get; }
public SimpleDocument(Action<IDocumentContainer> content, int maxPages, bool applyCaching, bool applyDebugging)
public SimpleDocument(Action<IDocumentContainer> content, int maxPages)
{
Content = content;
MaxPages = maxPages;
ApplyCaching = applyCaching;
ApplyDebugging = applyDebugging;
}
public DocumentMetadata GetMetadata()
@@ -29,9 +25,7 @@ namespace QuestPDF.Examples.Engine
return new DocumentMetadata()
{
RasterDpi = PageSizes.PointsPerInch * ImageScalingFactor,
DocumentLayoutExceptionThreshold = MaxPages,
ApplyCaching = ApplyCaching,
ApplyDebugging = ApplyDebugging
DocumentLayoutExceptionThreshold = MaxPages
};
}
+5 -5
View File
@@ -21,19 +21,19 @@ namespace QuestPDF.Examples
{
page.Margin(20);
page.Size(PageSizes.A7.Landscape());
page.PageColor(Colors.White);
page.Background(Colors.White);
page.Header().Text("With ensure space").SemiBold();
page.Header().Text("With ensure space", TextStyle.Default.SemiBold());
page.Content().Column(column =>
page.Content().Stack(stack =>
{
column
stack
.Item()
.ExtendHorizontal()
.Height(75)
.Background(Colors.Grey.Lighten2);
column
stack
.Item()
.EnsureSpace(100)
.Text(Placeholders.LoremIpsum());
@@ -1,76 +0,0 @@
using NUnit.Framework;
using QuestPDF.Drawing.Exceptions;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class ExecutionOrderExamples
{
[Test]
public void Example()
{
RenderingTest
.Create()
.PageSize(400, 300)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.DefaultTextStyle(TextStyle.Default.Size(18))
.Padding(25)
.Row(row =>
{
row.Spacing(25);
row.RelativeItem()
.Border(1)
.Padding(15)
.Background(Colors.Grey.Lighten2)
.Text("Lorem ipsum");
row.RelativeItem()
.Border(1)
.Background(Colors.Grey.Lighten2)
.Padding(15)
.Text("dolor sit amet");
});
});
}
[Test]
public void Example2()
{
RenderingTest
.Create()
.PageSize(200, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.Border(2)
.Width(150)
.Height(150)
.Background(Colors.Blue.Lighten2)
.PaddingTop(50)
.Background(Colors.Green.Lighten2)
.PaddingRight(50)
.Background(Colors.Red.Lighten2)
.PaddingBottom(50)
.Background(Colors.Amber.Lighten2)
.PaddingLeft(50)
.Background(Colors.Grey.Lighten2);
});
}
}
}
+8 -10
View File
@@ -12,14 +12,12 @@ namespace QuestPDF.Examples
{
return container
.Border(1)
.BorderColor(Colors.Grey.Medium)
.Background(dark ? Colors.Grey.Lighten3 : Colors.White)
.Padding(5);
.Background(dark ? Colors.Grey.Lighten2 : Colors.White)
.Padding(10);
}
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text).SemiBold();
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.Medium());
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text);
}
public class FrameExample
@@ -36,14 +34,14 @@ namespace QuestPDF.Examples
container
.Background("#FFF")
.Padding(25)
.Column(column =>
.Stack(stack =>
{
for(var i = 1; i <= 4; i++)
for(var i=1; i<=4; i++)
{
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(2).LabelCell(Placeholders.Label());
row.RelativeItem(3).ValueCell().Text(Placeholders.Paragraph());
row.RelativeColumn(2).LabelCell(Placeholders.Label());
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
});
}
});
+5 -5
View File
@@ -19,17 +19,17 @@ namespace QuestPDF.Examples
.ShowResults()
.Render(page =>
{
page.Padding(25).Column(column =>
page.Padding(25).Stack(stack =>
{
column.Spacing(25);
stack.Spacing(25);
column.Item().Image("logo.png");
stack.Item().Image("logo.png");
var binaryData = File.ReadAllBytes("logo.png");
column.Item().Image(binaryData);
stack.Item().Image(binaryData);
using var stream = new FileStream("logo.png", FileMode.Open);
column.Item().Image(stream);
stack.Item().Image(stream);
});
});
}
+4 -5
View File
@@ -25,7 +25,7 @@ namespace QuestPDF.Examples
.Padding(25)
.Decoration(decoration =>
{
decoration.Before().Text(text =>
decoration.Header().Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.Size(20));
@@ -37,7 +37,7 @@ namespace QuestPDF.Examples
decoration
.Content()
.PaddingTop(25)
//.MinimalBox()
//.Box()
.Border(1)
.Background(Colors.Grey.Lighten2)
.Inlined(inlined =>
@@ -74,8 +74,7 @@ namespace QuestPDF.Examples
.PrimaryLayer()
.AlignCenter()
.AlignMiddle()
.Text(sizeText)
.FontSize(15);
.Text(sizeText, TextStyle.Default.Size(15));
});
}
});
@@ -95,7 +94,7 @@ namespace QuestPDF.Examples
{
container
.Padding(20)
.MinimalBox()
.Box()
.Border(1)
.Background(Colors.Grey.Lighten4)
.Inlined(inlined =>
-57
View File
@@ -1,57 +0,0 @@
using System.IO;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class LineExamples
{
[Test]
public void LineHorizontal()
{
RenderingTest
.Create()
.PageSize(PageSizes.A5)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(15)
.MinimalBox()
.DefaultTextStyle(TextStyle.Default.Size(16))
.Column(column =>
{
column.Item().Text("Above text");
column.Item().PaddingVertical(5).LineHorizontal(1).LineColor(Colors.Grey.Medium);
column.Item().Text("Below text");
});
});
}
[Test]
public void LineVertical()
{
RenderingTest
.Create()
.PageSize(PageSizes.A5)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(15)
.DefaultTextStyle(TextStyle.Default.Size(16))
.Row(row =>
{
row.AutoItem().Text("Left text");
row.AutoItem().PaddingHorizontal(10).LineVertical(1).LineColor(Colors.Grey.Medium);
row.AutoItem().Text("Right text");
});
});
}
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ namespace QuestPDF.Examples
container
.Background("#FFF")
.Padding(25)
.Column(column =>
.Stack(column =>
{
column.Spacing(10);
-53
View File
@@ -1,53 +0,0 @@
using System.Diagnostics;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class MinimalApiExamples
{
[Test]
public void MinimalApi()
{
Document
.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.PageColor(Colors.White);
page.DefaultTextStyle(x => x.FontSize(20));
page.Header()
.Text("Hello PDF!")
.SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Column(x =>
{
x.Spacing(20);
x.Item().Text(Placeholders.LoremIpsum());
x.Item().Image(Placeholders.Image(200, 100));
});
page.Footer()
.AlignCenter()
.Text(x =>
{
x.Span("Page ");
x.CurrentPageNumber();
});
});
})
.GeneratePdf("hello.pdf");
Process.Start("explorer.exe", "hello.pdf");
}
}
}
+7 -8
View File
@@ -50,8 +50,7 @@ namespace QuestPDF.Examples
.Background("FFF")
.Padding(5)
.AlignCenter()
.Text("Sample text")
.FontFamily("Segoe UI emoji");
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
});
}
@@ -64,7 +63,7 @@ namespace QuestPDF.Examples
.Render(container =>
{
container
.Column(column =>
.Stack(column =>
{
column
.Item()
@@ -114,14 +113,14 @@ namespace QuestPDF.Examples
.Render(container =>
{
container
.Column(column =>
.Stack(column =>
{
column
.Item()
.Height(150)
.Row(row =>
{
row.RelativeItem()
row.RelativeColumn()
.Extend()
.Background("FFF")
@@ -129,7 +128,7 @@ namespace QuestPDF.Examples
.Width(50)
.Background("444");
row.RelativeItem()
row.RelativeColumn()
.Extend()
.Background("BBB")
@@ -143,7 +142,7 @@ namespace QuestPDF.Examples
.Height(150)
.Row(row =>
{
row.RelativeItem()
row.RelativeColumn()
.Extend()
.Background("BBB")
@@ -151,7 +150,7 @@ namespace QuestPDF.Examples
.Width(50)
.Background("444");
row.RelativeItem()
row.RelativeColumn()
.Extend()
.Background("BBB")
@@ -1,57 +0,0 @@
using System;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class PageBackgroundForegroundExample
{
[Test]
public void PageBackgroundForeground()
{
RenderingTest
.Create()
.ProduceImages()
.MaxPages(100)
.ShowResults()
.RenderDocument(document =>
{
document.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(1, Unit.Inch);
page.DefaultTextStyle(TextStyle.Default.FontSize(16));
page.PageColor(Colors.White);
const string transparentBlue = "#662196f3";
page.Background()
.AlignTop()
.ExtendHorizontal()
.Height(200)
.Background(transparentBlue);
page.Foreground()
.AlignBottom()
.ExtendHorizontal()
.Height(250)
.Background(transparentBlue);
page.Header().Text("Background and foreground").Bold().FontColor(Colors.Blue.Darken2).FontSize(36);
page.Content().PaddingVertical(25).Column(column =>
{
column.Spacing(25);
foreach (var i in Enumerable.Range(0, 100))
column.Item().Background(Colors.Grey.Lighten2).Height(75);
});
});
});
}
}
}
@@ -6,13 +6,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="microcharts" Version="0.9.5.9" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
<PackageReference Include="Svg.Skia" Version="0.5.10" />
</ItemGroup>
<ItemGroup>
@@ -29,9 +27,6 @@
<None Update="logo.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="pdf-icon.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
-109
View File
@@ -1,109 +0,0 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class RowExamples
{
[Test]
public void ItemTypes()
{
RenderingTest
.Create()
.ProducePdf()
.PageSize(650, 300)
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.MinimalBox()
.Border(1)
.Column(column =>
{
column.Item().LabelCell("Total width: 600px");
column.Item().Row(row =>
{
row.ConstantItem(150).ValueCell("150px");
row.ConstantItem(100).ValueCell("100px");
row.RelativeItem(4).ValueCell("200px");
row.RelativeItem(3).ValueCell("150px");
});
column.Item().Row(row =>
{
row.ConstantItem(100).ValueCell("100px");
row.ConstantItem(50).ValueCell("50px");
row.RelativeItem(2).ValueCell("100px");
row.RelativeItem(1).ValueCell("50px");
});
});
});
}
[Test]
public void Stability()
{
// up to version 2021.12, this code would always result with the infinite layout exception
RenderingTest
.Create()
.ProducePdf()
.MaxPages(100)
.PageSize(250, 150)
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.Row(row =>
{
row.RelativeItem().Column(column =>
{
column.Item().ShowOnce().Element(CreateBox).Text("X");
column.Item().Element(CreateBox).Text("1");
column.Item().Element(CreateBox).Text("2");
});
row.RelativeItem().Column(column =>
{
column.Item().Element(CreateBox).Text("1");
column.Item().Element(CreateBox).Text("2");
});
});
});
static IContainer CreateBox(IContainer container)
{
return container
.ExtendHorizontal()
.ExtendVertical()
.Background(Colors.Grey.Lighten4)
.Border(1)
.AlignCenter()
.AlignMiddle()
.ShowOnce();
}
}
[Test]
public void Stability_NoItems()
{
RenderingTest
.Create()
.ProducePdf()
.MaxPages(100)
.PageSize(250, 150)
.Render(container =>
{
container
.Padding(25)
.Row(row => { });
});
}
}
}
-42
View File
@@ -1,42 +0,0 @@
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class ScaleToFitExamples
{
[Test]
public void ScaleToFit()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container.Padding(25).Column(column =>
{
var text = Placeholders.Paragraph();
foreach (var i in Enumerable.Range(2, 5))
{
column
.Item()
.MinimalBox()
.Border(1)
.Padding(5)
.Width(i * 40)
.Height(i * 20)
.ScaleToFit()
.Text(text);
}
});
});
}
}
}
+4 -4
View File
@@ -21,20 +21,20 @@ namespace QuestPDF.Examples
{
page.Margin(20);
page.Size(PageSizes.A7.Landscape());
page.PageColor(Colors.White);
page.Background(Colors.White);
page.Header().Text("With show once").SemiBold();
page.Header().Text("With show once", TextStyle.Default.SemiBold());
page.Content().PaddingVertical(5).Row(row =>
{
row.RelativeItem()
row.RelativeColumn()
.Background(Colors.Grey.Lighten2)
.Border(1)
.Padding(5)
.ShowOnce()
.Text(Placeholders.Label());
row.RelativeItem(2)
row.RelativeColumn(2)
.Border(1)
.Padding(5)
.Text(Placeholders.Paragraph());
+5 -6
View File
@@ -21,18 +21,17 @@ namespace QuestPDF.Examples
{
page.Margin(20);
page.Size(PageSizes.A7.Landscape());
page.PageColor(Colors.White);
page.Background(Colors.White);
page.Header().Column(column =>
page.Header().Stack(stack =>
{
column.Item().ShowOnce().Text("This header is visible on the first page.");
column.Item().SkipOnce().Text("This header is visible on the second page and all following.");
stack.Item().ShowOnce().Text("This header is visible on the first page.");
stack.Item().SkipOnce().Text("This header is visible on the second page and all following.");
});
page.Content()
.PaddingVertical(10)
.Text(Placeholders.Paragraphs())
.FontColor(Colors.Grey.Medium);
.Text(Placeholders.Paragraphs(), TextStyle.Default.Color(Colors.Grey.Medium));
page.Footer().Text(text =>
{
-48
View File
@@ -1,48 +0,0 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class StopPaging
{
[Test]
public void Example()
{
RenderingTest
.Create()
.PageSize(300, 250)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(25)
.DefaultTextStyle(TextStyle.Default.Size(14))
.Decoration(decoration =>
{
decoration
.Before()
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Medium));
text.Span("Page ");
text.CurrentPageNumber();
});
decoration
.Content()
.Column(column =>
{
column.Spacing(25);
column.Item().StopPaging().Text(Placeholders.LoremIpsum());
column.Item().ExtendHorizontal().Height(75).Background(Colors.Grey.Lighten2);
});
});
});
}
}
}
-36
View File
@@ -1,36 +0,0 @@
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
using Svg.Skia;
namespace QuestPDF.Examples
{
public class SvgImageExample
{
[Test]
public void BorderRadius()
{
RenderingTest
.Create()
.PageSize(175, 100)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Background(Colors.Grey.Lighten2)
.Padding(25)
.Canvas((canvas, space) =>
{
using var svg = new SKSvg();
svg.Load("pdf-icon.svg");
canvas.DrawPicture(svg.Picture);
});
});
}
}
}
-500
View File
@@ -1,500 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using IContainer = QuestPDF.Infrastructure.IContainer;
namespace QuestPDF.Examples
{
public class TableExamples
{
[Test]
public void BasicPlacement()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(220, 220)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
// by using custom 'Element' method, we can reuse visual configuration
table.Cell().Row(1).Column(4).Element(Block).Text("A");
table.Cell().Row(2).Column(2).Element(Block).Text("B");
table.Cell().Row(3).Column(3).Element(Block).Text("C");
table.Cell().Row(4).Column(1).Element(Block).Text("D");
});
});
}
[Test]
public void PagingSupport()
{
RenderingTest
.Create()
.ProducePdf()
.PageSize(420, 220)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
// by using custom 'Element' method, we can reuse visual configuration
table.Cell().Element(Block).Text(Placeholders.Label());
table.Cell().Element(Block).Text(Placeholders.Label());
table.Cell().Element(Block).Text(Placeholders.Paragraph());
table.Cell().Element(Block).Text(Placeholders.Label());
});
});
}
[Test]
public void DefaultCellStyle()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(220, 120)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.MinimalBox()
.Border(1)
.DefaultTextStyle(TextStyle.Default.Size(16))
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().Row(1).Column(1).Element(Block).Text("A");
table.Cell().Row(2).Column(2).Element(Block).Text("B");
table.Cell().Row(1).Column(3).Element(Block).Text("C");
table.Cell().Row(2).Column(4).Element(Block).Text("D");
});
});
}
[Test]
public void ColumnsDefinition()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(320, 80)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.ConstantColumn(50);
columns.ConstantColumn(100);
columns.RelativeColumn(2);
columns.RelativeColumn(3);
});
table.Cell().ColumnSpan(4).LabelCell("Total width: 300px");
table.Cell().ValueCell("50px");
table.Cell().ValueCell("100px");
table.Cell().ValueCell("100px");
table.Cell().ValueCell("150px");
});
});
}
[Test]
public void PartialAutoPlacement()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(220, 220)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().Element(Block).Text("A");
table.Cell().Row(2).Column(2).Element(Block).Text("B");
table.Cell().Element(Block).Text("C");
table.Cell().Row(3).Column(3).Element(Block).Text("D");
table.Cell().ColumnSpan(2).Element(Block).Text("E");
});
});
}
[Test]
public void ExtendLastCellsToTableBottom()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(220, 170)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.ExtendLastCellsToTableBottom();
table.Cell().Row(1).Column(1).Element(Block).Text("A");
table.Cell().Row(3).Column(1).Element(Block).Text("B");
table.Cell().Row(2).Column(2).Element(Block).Text("C");
table.Cell().Row(3).Column(3).Element(Block).Text("D");
table.Cell().Row(2).RowSpan(2).Column(4).Element(Block).Text("E");
});
});
}
[Test]
public void Overlapping()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(170, 170)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().Row(1).RowSpan(3).Column(1).ColumnSpan(3).Background(Colors.Grey.Lighten3).MinHeight(150);
table.Cell().Row(1).RowSpan(2).Column(1).ColumnSpan(2).Background(Colors.Grey.Lighten1).MinHeight(100);
table.Cell().Row(3).Column(3).Background(Colors.Grey.Darken1).MinHeight(50);
});
});
}
[Test]
public void Spans()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(220, 170)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().RowSpan(2).ColumnSpan(2).Element(Block).Text("1");
table.Cell().ColumnSpan(2).Element(Block).Text("2");
table.Cell().Element(Block).Text("3");
table.Cell().Element(Block).Text("4");
table.Cell().RowSpan(2).Element(Block).Text("5");
table.Cell().ColumnSpan(2).Element(Block).Text("6");
table.Cell().RowSpan(2).Element(Block).Text("7");
table.Cell().Element(Block).Text("8");
table.Cell().Element(Block).Text("9");
});
});
}
[Test]
public void Stability()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(300, 300)
.ShowResults()
.Render(container =>
{
container
.Padding(10)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
});
table.Cell().RowSpan(4).Element(Block).Text("1");
table.Cell().RowSpan(2).Element(Block).Text("2");
table.Cell().RowSpan(1).Element(Block).Text("3");
table.Cell().RowSpan(1).Element(Block).Text("4");
table.Cell().RowSpan(2).Element(Block).Text("5");
table.Cell().RowSpan(1).Element(Block).Text("6");
table.Cell().RowSpan(1).Element(Block).Text("7");
});
});
}
[Test]
public void TableHeader()
{
RenderingTest
.Create()
.ProduceImages()
.PageSize(500, 200)
.ShowResults()
.EnableDebugging()
.Render(container =>
{
var pageSizes = new List<(string name, double width, double height)>()
{
("Letter (ANSI A)", 8.5f, 11),
("Legal", 8.5f, 14),
("Ledger (ANSI B)", 11, 17),
("Tabloid (ANSI B)", 17, 11),
("ANSI C", 22, 17),
("ANSI D", 34, 22),
("ANSI E", 44, 34)
};
const int inchesToPoints = 72;
container
.Padding(10)
.MinimalBox()
.Border(1)
.Table(table =>
{
IContainer DefaultCellStyle(IContainer container, string backgroundColor)
{
return container
.Border(1)
.BorderColor(Colors.Grey.Lighten1)
.Background(backgroundColor)
.PaddingVertical(5)
.PaddingHorizontal(10)
.AlignCenter()
.AlignMiddle();
}
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn();
columns.ConstantColumn(75);
columns.ConstantColumn(75);
columns.ConstantColumn(75);
columns.ConstantColumn(75);
});
table.Header(header =>
{
header.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type");
header.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches");
header.Cell().ColumnSpan(2).Element(CellStyle).Text("Points");
header.Cell().Element(CellStyle).Text("Width");
header.Cell().Element(CellStyle).Text("Height");
header.Cell().Element(CellStyle).Text("Width");
header.Cell().Element(CellStyle).Text("Height");
// you can extend already existing styles by creating additional methods
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3);
});
foreach (var page in pageSizes)
{
table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
// inches
table.Cell().Element(CellStyle).Text(page.width);
table.Cell().Element(CellStyle).Text(page.height);
// points
table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White);
}
});
});
}
[Test]
public void PerformanceText_TemperatureReport()
{
RenderingTest
.Create()
.ProducePdf()
.PageSize(PageSizes.A4)
.MaxPages(10_000)
.EnableCaching()
.EnableDebugging(false)
.ShowResults()
.Render(container => GeneratePerformanceStructure(container, 250));
}
public static void GeneratePerformanceStructure(IContainer container, int repeats)
{
container
.Padding(25)
//.Background(Colors.Blue.Lighten2)
.MinimalBox()
.Border(1)
//.Background(Colors.Red.Lighten2)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.ConstantColumn(100);
columns.RelativeColumn();
columns.ConstantColumn(100);
columns.RelativeColumn();
});
table.ExtendLastCellsToTableBottom();
foreach (var i in Enumerable.Range(0, repeats))
{
table.Cell().RowSpan(3).LabelCell("Project");
table.Cell().RowSpan(3).ShowEntire().ValueCell(Placeholders.Sentence());
table.Cell().LabelCell("Report number");
table.Cell().ValueCell(i.ToString());
table.Cell().LabelCell("Date");
table.Cell().ValueCell(Placeholders.ShortDate());
table.Cell().LabelCell("Inspector");
table.Cell().ValueCell("Marcin Ziąbek");
table.Cell().ColumnSpan(2).LabelCell("Morning weather");
table.Cell().ColumnSpan(2).LabelCell("Evening weather");
table.Cell().ValueCell("Time");
table.Cell().ValueCell("7:13");
table.Cell().ValueCell("Time");
table.Cell().ValueCell("18:25");
table.Cell().ValueCell("Description");
table.Cell().ValueCell("Sunny");
table.Cell().ValueCell("Description");
table.Cell().ValueCell("Windy");
table.Cell().ValueCell("Wind");
table.Cell().ValueCell("Mild");
table.Cell().ValueCell("Wind");
table.Cell().ValueCell("Strong");
table.Cell().ValueCell("Temperature");
table.Cell().ValueCell("17°C");
table.Cell().ValueCell("Temperature");
table.Cell().ValueCell("32°C");
table.Cell().LabelCell("Remarks");
table.Cell().ColumnSpan(3).ValueCell(Placeholders.Paragraph());
}
});
}
// this method uses a higher order function to define a custom and dynamic style
static IContainer Block(IContainer container)
{
return container
.Border(1)
.Background(Colors.Grey.Lighten3)
.ShowOnce()
.MinWidth(50)
.MinHeight(50)
.Padding(10)
.AlignCenter()
.AlignMiddle();
}
}
}
+48 -41
View File
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.RenderDocument(x => ComposeBook(x, chapters));
.Render(x => ComposeBook(x, chapters));
}
[Test]
@@ -42,8 +42,8 @@ namespace QuestPDF.Examples
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.RenderDocument(x => ComposeBook(x, chapters));
.ProducePdf()
.Render(x => ComposeBook(x, chapters));
}
IEnumerable<float> PerformTest(int attempts)
@@ -102,105 +102,112 @@ namespace QuestPDF.Examples
}
}
private void ComposeBook(IDocumentContainer container, ICollection<BookChapter> chapters)
private void ComposeBook(IContainer container, ICollection<BookChapter> chapters)
{
var subtitleStyle = TextStyle.Default.Size(24).SemiBold().Color(Colors.Blue.Medium);
var normalStyle = TextStyle.Default.Size(14);
ComposePage(container);
container.Page(page =>
void ComposePage(IContainer container)
{
page.Margin(50);
page.Content().Column(column =>
{
column.Item().Element(Title);
column.Item().PageBreak();
column.Item().Element(TableOfContents);
column.Item().PageBreak();
container
.Padding(50)
.Decoration(decoration =>
{
decoration
.Content()
.Stack(stack =>
{
stack.Item().Element(Title);
stack.Item().PageBreak();
stack.Item().Element(TableOfContents);
stack.Item().PageBreak();
Chapters(column);
Chapters(stack);
column.Item().Element(Acknowledgements);
});
page.Footer().Element(Footer);
});
stack.Item().Element(Acknowledgements);
});
decoration.Footer().Element(Footer);
});
}
void Title(IContainer container)
{
container
.Extend()
.PaddingBottom(200)
.AlignBottom()
.Column(column =>
.Stack(stack =>
{
column.Item().Text("Quo Vadis").FontSize(72).Bold().FontColor(Colors.Blue.Darken2);
column.Item().Text("Henryk Sienkiewicz").FontSize(24).FontColor(Colors.Grey.Darken2);
stack.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
stack.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
});
}
void TableOfContents(IContainer container)
{
container.Column(column =>
container.Stack(stack =>
{
SectionTitle(column, "Spis treści");
SectionTitle(stack, "Spis treści");
foreach (var chapter in chapters)
{
column.Item().InternalLink(chapter.Title).Row(row =>
stack.Item().InternalLink(chapter.Title).Row(row =>
{
row.RelativeItem().Text(chapter.Title).Style(normalStyle);
row.ConstantItem(100).AlignRight().Text(text => text.BeginPageNumberOfSection(chapter.Title).Style(normalStyle));
row.RelativeColumn().Text(chapter.Title, normalStyle);
row.ConstantColumn(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
});
}
});
}
void Chapters(ColumnDescriptor column)
void Chapters(StackDescriptor stack)
{
foreach (var chapter in chapters)
{
column.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
stack.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
}
}
void Chapter(IContainer container, string title, string content)
{
container.Column(column =>
container.Stack(stack =>
{
SectionTitle(column, title);
SectionTitle(stack, title);
column.Item().Text(text =>
stack.Item().Text(text =>
{
text.ParagraphSpacing(5);
text.Span(content).Style(normalStyle);
text.Span(content, normalStyle);
});
column.Item().PageBreak();
stack.Item().PageBreak();
});
}
void Acknowledgements(IContainer container)
{
container.Column(column =>
container.Stack(stack =>
{
SectionTitle(column, "Podziękowania");
SectionTitle(stack, "Podziękowania");
column.Item().Text(text =>
stack.Item().Text(text =>
{
text.DefaultTextStyle(normalStyle);
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
text.Hyperlink("wolnelektury.pl", "https://wolnelektury.pl/").FontColor(Colors.Blue.Medium).Underline();
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underline());
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
});
});
}
void SectionTitle(ColumnDescriptor column, string text)
void SectionTitle(StackDescriptor stack, string text)
{
column.Item().Location(text).Text(text).Style(subtitleStyle);
column.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
stack.Item().Location(text).Text(text, subtitleStyle);
stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
}
void Footer(IContainer container)
+25 -129
View File
@@ -24,15 +24,14 @@ namespace QuestPDF.Examples
{
container
.Padding(5)
.MinimalBox()
.Box()
.Border(1)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.DefaultTextStyle(TextStyle.Default.Size(20));
text.Span("This is a normal text, followed by an ");
text.Span("underlined red text").FontColor(Colors.Red.Medium).Underline();
text.Span(".");
text.Span("underlined red text.", TextStyle.Default.Size(20).Color(Colors.Red.Medium).Underline());
});
});
}
@@ -49,7 +48,7 @@ namespace QuestPDF.Examples
{
container
.Padding(5)
.MinimalBox()
.Box()
.Border(1)
.Padding(10)
.Text(text =>
@@ -58,7 +57,7 @@ namespace QuestPDF.Examples
foreach (var i in Enumerable.Range(1, 3))
{
text.Span($"Paragraph {i}: ").SemiBold();
text.Span($"Paragraph {i}: ", TextStyle.Default.SemiBold());
text.Line(Placeholders.Paragraph());
}
});
@@ -77,12 +76,12 @@ namespace QuestPDF.Examples
{
container
.Padding(5)
.MinimalBox()
.Box()
.Border(1)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.DefaultTextStyle(TextStyle.Default.Size(20));
text.Span("This is a random image aligned to the baseline: ");
text.Element()
@@ -109,7 +108,7 @@ namespace QuestPDF.Examples
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Box()
.Border(1)
.Padding(5)
.Padding(10)
@@ -121,13 +120,13 @@ namespace QuestPDF.Examples
text.Line(Placeholders.LoremIpsum());
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <").Underline();
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <", TextStyle.Default.Underline());
});
});
}
[Test]
public void Textcolumn()
public void TextStack()
{
RenderingTest
.Create()
@@ -139,7 +138,7 @@ namespace QuestPDF.Examples
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Box()
.Border(1)
.Padding(5)
.Padding(10)
@@ -168,14 +167,12 @@ namespace QuestPDF.Examples
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Box()
.Border(1)
.Padding(5)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(x => x.Bold());
text.DefaultTextStyle(TextStyle.Default);
text.AlignLeft();
text.ParagraphSpacing(10);
@@ -185,14 +182,14 @@ namespace QuestPDF.Examples
text.EmptyLine();
text.Span("This text is a normal text, ");
text.Span("this is a bold text, ").Bold();
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
text.Span("and this is slightly bigger text.").FontSize(16);
text.Span("this is a bold text, ", TextStyle.Default.Bold());
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
text.EmptyLine();
text.Span("The new text element also supports injecting custom content between words: ");
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
text.Span(".");
text.EmptyLine();
@@ -204,7 +201,7 @@ namespace QuestPDF.Examples
text.EmptyLine();
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
text.EmptyLine();
@@ -213,7 +210,7 @@ namespace QuestPDF.Examples
text.EmptyLine();
text.Span(Placeholders.Paragraphs()).Italic();
text.Span(Placeholders.Paragraphs(), TextStyle.Default.Italic());
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
});
@@ -233,23 +230,23 @@ namespace QuestPDF.Examples
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Box()
.Border(1)
.Padding(5)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.DefaultTextStyle(TextStyle.Default);
text.AlignLeft();
text.ParagraphSpacing(10);
text.Span("This text is a normal text, ");
text.Span("this is a bold text, ").Bold();
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
text.Span("and this is slightly bigger text.").FontSize(16);
text.Span("this is a bold text, ", TextStyle.Default.Bold());
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
text.Span("The new text element also supports injecting custom content between words: ");
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
text.Span(".");
text.EmptyLine();
@@ -258,7 +255,7 @@ namespace QuestPDF.Examples
{
text.Line($"{i}: {Placeholders.Paragraph()}");
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
text.Span("This is page number ");
text.CurrentPageNumber();
@@ -270,106 +267,5 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void MeasureIssueWhenSpaceAtLineEnd()
{
// issue 135
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Margin(50);
page.PageColor(Colors.White);
page.Size(PageSizes.A4);
page.Content().Text("This is a specially crafted sentence with a specially chosen length for demonstration of the bug that occurs ;;;;;. ").FontSize(11).BackgroundColor(Colors.Red.Lighten3);
});
});
}
[Test]
public void EmptyText()
{
// issue 135
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Margin(50);
page.PageColor(Colors.White);
page.Size(PageSizes.A4);
page.Content().Text(" ").FontSize(11).BackgroundColor(Colors.Red.Lighten3);
});
});
}
[Test]
public void Whitespaces()
{
// issue 135
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Margin(50);
page.PageColor(Colors.White);
page.Size(PageSizes.A4);
page.Content().Text(" x ").FontSize(11).BackgroundColor(Colors.Red.Lighten3);
});
});
}
[Test]
public void DrawingNullTextShouldNotThrowException()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Margin(50);
page.PageColor(Colors.White);
page.Size(PageSizes.A4);
page.Content().Column(column =>
{
column.Item().Text(null);
column.Item().Text(text =>
{
text.Span(null);
text.Line(null);
text.Hyperlink(null, "http://www.questpdf.com");
text.TotalPages().Format(x => null);
});
});
});
});
}
}
}
-149
View File
@@ -1,149 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="75.320129mm"
height="92.604164mm"
viewBox="0 0 75.320129 92.604164"
version="1.1"
id="svg8"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="PDF file icon.svg">
<title
id="title2682">PDF file icon</title>
<defs
id="defs2">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath839">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
id="rect841"
width="68.688263"
height="67.886459"
x="-74.033661"
y="141.44913" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="122.12727"
inkscape:cy="167.02823"
inkscape:document-units="mm"
inkscape:current-layer="g899"
showgrid="false"
inkscape:object-nodes="false"
inkscape:window-width="1366"
inkscape:window-height="705"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:snap-page="false"
inkscape:snap-others="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>PDF file icon</dc:title>
<dc:date>08/10/2018</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>Adobe Systems</dc:title>
</cc:Agent>
</dc:creator>
<dc:publisher>
<cc:Agent>
<dc:title>CMetalCore</dc:title>
</cc:Agent>
</dc:publisher>
<dc:description>Fuente del texto &quot;PDF&quot;:
Franklin Gothic Medium Cond</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(36.076172,-93.731774)">
<g
id="g899"
transform="matrix(1.4843054,0,0,1.4843054,17.471885,-90.243502)">
<g
id="g876">
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff2116;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.11666656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -29.632812,123.94727 c -3.551967,0 -6.44336,2.89347 -6.44336,6.44531 v 49.49804 c 0,3.55185 2.891393,6.44532 6.44336,6.44532 H 8.2167969 c 3.5519661,0 6.4433591,-2.89335 6.4433591,-6.44532 v -40.70117 c 0,0 0.101353,-1.19181 -0.416015,-2.35156 -0.484969,-1.08711 -1.275391,-1.84375 -1.275391,-1.84375 a 1.0584391,1.0584391 0 0 0 -0.0059,-0.008 L 3.5722246,125.7752 a 1.0584391,1.0584391 0 0 0 -0.015625,-0.0156 c 0,0 -0.8017392,-0.76344 -1.9902344,-1.27344 -1.39939552,-0.6005 -2.8417968,-0.53711 -2.8417968,-0.53711 l 0.021484,-0.002 z"
id="path890" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.11666656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -29.632812,126.06445 h 28.3789058 a 1.0584391,1.0584391 0 0 0 0.021484,0 c 0,0 1.13480448,0.011 1.96484378,0.36719 0.79889772,0.34282 1.36536982,0.86176 1.36914062,0.86524 1.25e-5,1e-5 0.00391,0.004 0.00391,0.004 l 9.3671868,9.18945 c 0,0 0.564354,0.59582 0.837891,1.20899 0.220779,0.49491 0.234375,1.40039 0.234375,1.40039 a 1.0584391,1.0584391 0 0 0 -0.002,0.0449 v 40.74609 c 0,2.41592 -1.910258,4.32813 -4.3261717,4.32813 H -29.632812 c -2.415914,0 -4.326172,-1.91209 -4.326172,-4.32813 v -49.49804 c 0,-2.41603 1.910258,-4.32813 4.326172,-4.32813 z"
id="rect2684" />
<path
sodipodi:nodetypes="scccscccscccccssscacccsccccccccccccccccccccaccccccccc"
inkscape:connector-curvature="0"
d="m -23.40766,161.09299 c -1.45669,-1.45669 0.11934,-3.45839 4.39648,-5.58397 l 2.69124,-1.33743 1.04845,-2.29399 c 0.57665,-1.26169 1.43729,-3.32036 1.91254,-4.5748 l 0.8641,-2.28082 -0.59546,-1.68793 c -0.73217,-2.07547 -0.99326,-5.19438 -0.52872,-6.31588 0.62923,-1.51909 2.69029,-1.36323 3.50626,0.26515 0.63727,1.27176 0.57212,3.57488 -0.18329,6.47946 l -0.6193,2.38125 0.5455,0.92604 c 0.30003,0.50932 1.1764,1.71867 1.9475,2.68743 l 1.44924,1.80272 1.8033728,-0.23533 c 5.72900399,-0.74758 7.6912472,0.523 7.6912472,2.34476 0,2.29921 -4.4984914,2.48899 -8.2760865,-0.16423 -0.8499666,-0.59698 -1.4336605,-1.19001 -1.4336605,-1.19001 0,0 -2.3665326,0.48178 -3.531704,0.79583 -1.202707,0.32417 -1.80274,0.52719 -3.564509,1.12186 0,0 -0.61814,0.89767 -1.02094,1.55026 -1.49858,2.4279 -3.24833,4.43998 -4.49793,5.1723 -1.3991,0.81993 -2.86584,0.87582 -3.60433,0.13733 z m 2.28605,-0.81668 c 0.81883,-0.50607 2.47616,-2.46625 3.62341,-4.28553 l 0.46449,-0.73658 -2.11497,1.06339 c -3.26655,1.64239 -4.76093,3.19033 -3.98386,4.12664 0.43653,0.52598 0.95874,0.48237 2.01093,-0.16792 z m 21.21809,-5.95578 c 0.80089,-0.56097 0.68463,-1.69142 -0.22082,-2.1472 -0.70466,-0.35471 -1.2726074,-0.42759 -3.1031574,-0.40057 -1.1249,0.0767 -2.9337647,0.3034 -3.2403347,0.37237 0,0 0.993716,0.68678 1.434896,0.93922 0.58731,0.33544 2.0145161,0.95811 3.0565161,1.27706 1.02785,0.31461 1.6224,0.28144 2.0729,-0.0409 z m -8.53152,-3.54594 c -0.4847,-0.50952 -1.30889,-1.57296 -1.83152,-2.3632 -0.68353,-0.89643 -1.02629,-1.52887 -1.02629,-1.52887 0,0 -0.4996,1.60694 -0.90948,2.57394 l -1.27876,3.16076 -0.37075,0.71695 c 0,0 1.971043,-0.64627 2.97389,-0.90822 1.0621668,-0.27744 3.21787,-0.70134 3.21787,-0.70134 z m -2.74938,-11.02573 c 0.12363,-1.0375 0.1761,-2.07346 -0.15724,-2.59587 -0.9246,-1.01077 -2.04057,-0.16787 -1.85154,2.23517 0.0636,0.8084 0.26443,2.19033 0.53292,3.04209 l 0.48817,1.54863 0.34358,-1.16638 c 0.18897,-0.64151 0.47882,-2.02015 0.64411,-3.06364 z"
style="fill:#ff2116;fill-opacity:1;stroke-width:0.26458335"
id="path2697" />
<g
id="g858">
<path
inkscape:connector-curvature="0"
id="path845"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -20.930423,167.83862 h 2.364986 q 1.133514,0 1.840213,0.2169 0.706698,0.20991 1.189489,0.9446 0.482795,0.72769 0.482795,1.75625 0,0.94459 -0.391832,1.6233 -0.391833,0.67871 -1.056548,0.97958 -0.65772,0.30087 -2.02913,0.30087 h -0.818651 v 3.72941 h -1.581322 z m 1.581322,1.22447 v 3.33058 h 0.783664 q 1.049552,0 1.44838,-0.39184 0.405826,-0.39183 0.405826,-1.27345 0,-0.65772 -0.265887,-1.06355 -0.265884,-0.41282 -0.587747,-0.50378 -0.314866,-0.098 -1.000572,-0.098 z" />
<path
inkscape:connector-curvature="0"
id="path847"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -13.842461,167.83862 h 2.148082 q 1.560333,0 2.4909318,0.55276 0.9375993,0.55276 1.4133973,1.6443 0.482791,1.09153 0.482791,2.42096 0,1.3994 -0.4338151,2.49793 -0.4268149,1.09153 -1.3154348,1.76324 -0.8816233,0.67172 -2.5189212,0.67172 h -2.267031 z m 1.581326,1.26645 v 7.018 h 0.657715 q 1.378411,0 2.001144,-0.9516 0.6227329,-0.95858 0.6227329,-2.5539 0,-3.5125 -2.6238769,-3.5125 z" />
<path
inkscape:connector-curvature="0"
id="path849"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -5.7889096,167.83862 h 5.30372941 v 1.26645 H -4.2075842 v 2.85478 h 2.9807225 v 1.26646 h -2.9807225 v 4.16322 h -1.5813254 z" />
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

-80
View File
@@ -1,80 +0,0 @@
using Avalonia.Media;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF.Previewer;
using QuestPDF.ReportSample;
using QuestPDF.ReportSample.Layouts;
using Colors = QuestPDF.Helpers.Colors;
var model = DataSource.GetReport();
var report = new StandardReport(model);
report.ShowInPreviewer();
return;
Document
.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.PageColor(Colors.White);
page.DefaultTextStyle(x => x.FontSize(20));
page.Header()
.Text("Hot Reload!")
.SemiBold().FontSize(36).FontColor(Colors.Blue.Darken2);
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Column(x =>
{
x.Spacing(20);
x.Item().Table(t =>
{
t.ColumnsDefinition(c =>
{
c.RelativeColumn();
c.RelativeColumn(3);
});
t.Cell().Border(1).Background(Colors.Grey.Lighten3).Padding(5).Text("Visual Studio");
t.Cell().Border(1).Padding(5).Text("Start in debug mode with 'Hot Reload on Save' enabled.");
t.Cell().Border(1).Background(Colors.Grey.Lighten3).Padding(5).Text("Command line");
t.Cell().Border(1).Padding(5).Text("Run 'dotnet watch'.");
});
x.Item().Text("Modify this line and the preview should show your changes instantly.");
});
page.Footer()
.AlignCenter()
.Text(x =>
{
x.Span("Page ");
x.CurrentPageNumber();
});
});
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.PageColor(Colors.Red.Medium);
page.DefaultTextStyle(x => x.FontSize(20));
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Column(x =>
{
x.Spacing(20);
foreach (var i in Enumerable.Range(0, 10))
x.Item().Background(Colors.Grey.Lighten2).Height(80);
});
});
})
.ShowInPreviewer();
@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF.Previewer\QuestPDF.Previewer.csproj" />
<ProjectReference Include="..\QuestPDF.ReportSample\QuestPDF.ReportSample.csproj" />
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
</Project>
@@ -1,50 +0,0 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.ReactiveUI;
using QuestPDF.Infrastructure;
namespace QuestPDF.Previewer
{
public static class DocumentPreviewerExtensions
{
/// <summary>
/// Opens document in the QuestPDF previewer tool.
/// Improves development speed by supporting hot reloading.
/// Shows document preview and refreshes it after each code change.
/// </summary>
/// <remarks>
/// Intended for development only. Do not use in production environment.
/// </remarks>
public static void ShowInPreviewer(this IDocument document)
{
ArgumentNullException.ThrowIfNull(document);
// currently there is no way to utilize a previously run Avalonia app.
// so we need to check if the previewer was already run and show the window again.
if(Application.Current?.ApplicationLifetime is ClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new PreviewerWindow()
{
DataContext = new PreviewerWindowViewModel()
{
Document = document,
}
};
desktop.MainWindow.Show();
desktop.Start(Array.Empty<string>());
return;
}
AppBuilder
.Configure(() => new PreviewerApp()
{
Document = document,
})
.UsePlatformDetect()
.UseReactiveUI()
.StartWithClassicDesktopLifetime(Array.Empty<string>());
}
}
}
-63
View File
@@ -1,63 +0,0 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using Avalonia.Threading;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Previewer
{
internal class DocumentRenderer : INotifyPropertyChanged
{
public IDocument? Document { get; private set; }
public event PropertyChangedEventHandler? PropertyChanged;
private ObservableCollection<PreviewPage> _pages = new();
public ObservableCollection<PreviewPage> Pages
{
get => _pages;
set
{
if (_pages != value)
{
_pages = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Pages)));
}
}
}
public void UpdateDocument(IDocument? document)
{
Document = document;
if (document == null)
return;
try
{
RenderDocument(document);
}
catch (Exception exception)
{
var exceptionDocument = new ExceptionDocument(exception);
RenderDocument(exceptionDocument);
}
}
private void RenderDocument(IDocument document)
{
var canvas = new PreviewerCanvas();
DocumentGenerator.RenderDocument(canvas, document);
foreach (var pages in Pages)
pages?.Picture?.Dispose();
Dispatcher.UIThread.Post(() =>
{
Pages.Clear();
Pages = new ObservableCollection<PreviewPage>(canvas.Pictures);
});
}
}
}
-55
View File
@@ -1,55 +0,0 @@
using QuestPDF.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Previewer;
public class ExceptionDocument : IDocument
{
private Exception Exception { get; }
public ExceptionDocument(Exception exception)
{
Exception = exception;
}
public DocumentMetadata GetMetadata()
{
return DocumentMetadata.Default;
}
public void Compose(IDocumentContainer document)
{
document.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(1, Unit.Inch);
page.PageColor(Colors.Red.Lighten4);
page.DefaultTextStyle(x => x.FontSize(16));
page.Header()
.BorderBottom(2)
.BorderColor(Colors.Red.Medium)
.PaddingBottom(5)
.Text("Ooops! Something went wrong...").FontSize(28).FontColor(Colors.Red.Medium).Bold();
page.Content().PaddingVertical(20).Column(column =>
{
var currentException = Exception;
while (currentException != null)
{
column.Item().Text(currentException.GetType().Name).FontSize(20).SemiBold();
column.Item().Text(currentException.Message).FontSize(14);
column.Item().PaddingTop(10).Text(currentException.StackTrace).FontSize(10).Light();
currentException = currentException.InnerException;
if (currentException != null)
column.Item().PaddingVertical(15).LineHorizontal(2).LineColor(Colors.Red.Medium);
}
});
});
}
}
-17
View File
@@ -1,17 +0,0 @@
[assembly: System.Reflection.Metadata.MetadataUpdateHandler(typeof(QuestPDF.Previewer.HotReloadManager))]
namespace QuestPDF.Previewer
{
/// <summary>
/// Helper for subscribing to hot reload notifications.
/// </summary>
internal static class HotReloadManager
{
public static event EventHandler? UpdateApplicationRequested;
public static void UpdateApplication(Type[]? _)
{
UpdateApplicationRequested?.Invoke(null, EventArgs.Empty);
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

-205
View File
@@ -1,205 +0,0 @@
using System.Diagnostics;
using Avalonia;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Skia;
using SkiaSharp;
namespace QuestPDF.Previewer;
class InteractiveCanvas : ICustomDrawOperation
{
public Rect Bounds { get; set; }
public ICollection<PreviewPage> Pages { get; set; }
private float Width => (float)Bounds.Width;
private float Height => (float)Bounds.Height;
public float Scale { get; private set; } = 1;
public float TranslateX { get; set; }
public float TranslateY { get; set; }
private const float MinScale = 0.1f;
private const float MaxScale = 10f;
private const float PageSpacing = 25f;
private const float SafeZone = 25f;
public float TotalPagesHeight => Pages.Sum(x => x.Size.Height) + (Pages.Count - 1) * PageSpacing;
public float TotalHeight => TotalPagesHeight + SafeZone * 2 / Scale;
public float MaxWidth => Pages.Any() ? Pages.Max(x => x.Size.Width) : 0;
public float MaxTranslateY => TotalHeight - Height / Scale;
public float ScrollPercentY
{
get
{
return TranslateY / MaxTranslateY;
}
set
{
TranslateY = value * MaxTranslateY;
}
}
public float ScrollViewportSizeY
{
get
{
var viewPortSize = Height / Scale / TotalHeight;
return Math.Clamp(viewPortSize, 0, 1);
}
}
#region transformations
private void LimitScale()
{
Scale = Math.Max(Scale, MinScale);
Scale = Math.Min(Scale, MaxScale);
}
private void LimitTranslate()
{
if (TotalPagesHeight > Height / Scale)
{
TranslateY = Math.Min(TranslateY, MaxTranslateY);
TranslateY = Math.Max(TranslateY, 0);
}
else
{
TranslateY = (TotalPagesHeight - Height / Scale) / 2;
}
if (Width / Scale < MaxWidth)
{
var maxTranslateX = (Width / 2 - SafeZone) / Scale - MaxWidth / 2;
TranslateX = Math.Min(TranslateX, -maxTranslateX);
TranslateX = Math.Max(TranslateX, maxTranslateX);
}
else
{
TranslateX = 0;
}
}
public void TranslateWithCurrentScale(float x, float y)
{
TranslateX += x / Scale;
TranslateY += y / Scale;
LimitTranslate();
}
public void ZoomToPoint(float x, float y, float factor)
{
var oldScale = Scale;
Scale *= factor;
LimitScale();
factor = Scale / oldScale;
TranslateX -= x / (oldScale * factor) - x / oldScale;
TranslateY -= y / (oldScale * factor) - y / oldScale;
LimitTranslate();
}
#endregion
#region rendering
public void Render(IDrawingContextImpl context)
{
if (Pages.Count <= 0)
return;
LimitScale();
LimitTranslate();
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
if (canvas == null)
throw new InvalidOperationException($"Context needs to be ISkiaDrawingContextImpl but got {nameof(context)}");
var originalMatrix = canvas.TotalMatrix;
canvas.Translate(Width / 2, 0);
canvas.Scale(Scale);
canvas.Translate(TranslateX, -TranslateY + SafeZone / Scale);
foreach (var page in Pages)
{
canvas.Translate(-page.Size.Width / 2f, 0);
DrawBlankPage(canvas, page.Size);
canvas.DrawPicture(page.Picture);
canvas.Translate(page.Size.Width / 2f, page.Size.Height + PageSpacing);
}
canvas.SetMatrix(originalMatrix);
DrawInnerGradient(canvas);
}
public void Dispose() { }
public bool Equals(ICustomDrawOperation? other) => false;
public bool HitTest(Point p) => true;
#endregion
#region blank page
private static SKPaint BlankPagePaint = new SKPaint
{
Color = SKColors.White
};
private static SKPaint BlankPageShadowPaint = new SKPaint
{
ImageFilter = SKImageFilter.CreateBlendMode(
SKBlendMode.Overlay,
SKImageFilter.CreateDropShadowOnly(0, 6, 6, 6, SKColors.Black.WithAlpha(64)),
SKImageFilter.CreateDropShadowOnly(0, 10, 14, 14, SKColors.Black.WithAlpha(32)))
};
private void DrawBlankPage(SKCanvas canvas, QuestPDF.Infrastructure.Size size)
{
canvas.DrawRect(0, 0, size.Width, size.Height, BlankPageShadowPaint);
canvas.DrawRect(0, 0, size.Width, size.Height, BlankPagePaint);
}
#endregion
#region inner viewport gradient
private const int InnerGradientSize = (int)SafeZone;
private static readonly SKColor InnerGradientColor = SKColor.Parse("#666");
private void DrawInnerGradient(SKCanvas canvas)
{
// gamma correction
var colors = Enumerable
.Range(0, InnerGradientSize)
.Select(x => 1f - x / (float) InnerGradientSize)
.Select(x => Math.Pow(x, 2f))
.Select(x => (byte)(x * 255))
.Select(x => InnerGradientColor.WithAlpha(x))
.ToArray();
using var fogPaint = new SKPaint
{
Shader = SKShader.CreateLinearGradient(
new SKPoint(0, 0),
new SKPoint(0, InnerGradientSize),
colors,
SKShaderTileMode.Clamp)
};
canvas.DrawRect(0, 0, Width, InnerGradientSize, fogPaint);
}
#endregion
}
-7
View File
@@ -1,7 +0,0 @@
<Application x:Class="QuestPDF.Previewer.PreviewerApp"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Styles>
<FluentTheme Mode="Dark" />
</Application.Styles>
</Application>
-33
View File
@@ -1,33 +0,0 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using QuestPDF.Infrastructure;
namespace QuestPDF.Previewer
{
internal class PreviewerApp : Application
{
public IDocument? Document { get; init; }
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new PreviewerWindow()
{
DataContext = new PreviewerWindowViewModel()
{
Document = Document,
}
};
}
base.OnFrameworkInitializationCompleted();
}
}
}
-42
View File
@@ -1,42 +0,0 @@
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Previewer
{
record PreviewPage(SKPicture Picture, Size Size);
sealed class PreviewerCanvas : SkiaCanvasBase, IRenderingCanvas
{
private SKPictureRecorder? PictureRecorder { get; set; }
private Size? CurrentPageSize { get; set; }
public ICollection<PreviewPage> Pictures { get; } = new List<PreviewPage>();
public override void BeginDocument()
{
Pictures.Clear();
}
public override void BeginPage(Size size)
{
CurrentPageSize = size;
PictureRecorder = new SKPictureRecorder();
Canvas = PictureRecorder.BeginRecording(new SKRect(0, 0, size.Width, size.Height));
}
public override void EndPage()
{
var picture = PictureRecorder?.EndRecording();
if (picture != null && CurrentPageSize.HasValue)
Pictures.Add(new PreviewPage(picture, CurrentPageSize.Value));
PictureRecorder?.Dispose();
PictureRecorder = null;
}
public override void EndDocument() { }
}
}
-120
View File
@@ -1,120 +0,0 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Media;
using ReactiveUI;
namespace QuestPDF.Previewer
{
class PreviewerControl : Control
{
private InteractiveCanvas InteractiveCanvas { get; set; } = new ();
public static readonly StyledProperty<ObservableCollection<PreviewPage>> PagesProperty =
AvaloniaProperty.Register<PreviewerControl, ObservableCollection<PreviewPage>>(nameof(Pages));
public ObservableCollection<PreviewPage>? Pages
{
get => GetValue(PagesProperty);
set => SetValue(PagesProperty, value);
}
public static readonly StyledProperty<float> CurrentScrollProperty = AvaloniaProperty.Register<PreviewerControl, float>(nameof(CurrentScroll));
public float CurrentScroll
{
get => GetValue(CurrentScrollProperty);
set => SetValue(CurrentScrollProperty, value);
}
public static readonly StyledProperty<float> ScrollViewportSizeProperty = AvaloniaProperty.Register<PreviewerControl, float>(nameof(ScrollViewportSize));
public float ScrollViewportSize
{
get => GetValue(ScrollViewportSizeProperty);
set => SetValue(ScrollViewportSizeProperty, value);
}
public PreviewerControl()
{
PagesProperty.Changed.Subscribe(x =>
{
InteractiveCanvas.Pages = x.NewValue.Value;
InvalidateVisual();
});
CurrentScrollProperty.Changed.Subscribe(x =>
{
InteractiveCanvas.ScrollPercentY = x.NewValue.Value;
InvalidateVisual();
});
ClipToBounds = true;
}
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
base.OnPointerWheelChanged(e);
if ((e.KeyModifiers & KeyModifiers.Control) != 0)
{
var scaleFactor = 1 + e.Delta.Y / 10f;
var point = new Point(Bounds.Center.X, Bounds.Top) - e.GetPosition(this);
InteractiveCanvas.ZoomToPoint((float)point.X, -(float)point.Y, (float)scaleFactor);
}
if (e.KeyModifiers == KeyModifiers.None)
{
var translation = (float)e.Delta.Y * 25;
InteractiveCanvas.TranslateWithCurrentScale(0, -translation);
}
InvalidateVisual();
}
private bool IsMousePressed { get; set; }
private Vector MousePosition { get; set; }
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
if (IsMousePressed)
{
var currentPosition = e.GetPosition(this);
var translation = currentPosition - MousePosition;
InteractiveCanvas.TranslateWithCurrentScale((float)translation.X, -(float)translation.Y);
InvalidateVisual();
}
MousePosition = e.GetPosition(this);
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
IsMousePressed = true;
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
IsMousePressed = false;
}
public override void Render(DrawingContext context)
{
CurrentScroll = InteractiveCanvas.ScrollPercentY;
ScrollViewportSize = InteractiveCanvas.ScrollViewportSizeY;
InteractiveCanvas.Bounds = new Rect(0, 0, Bounds.Width, Bounds.Height);
context.Custom(InteractiveCanvas);
base.Render(context);
}
}
}
-82
View File
@@ -1,82 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:previewer="clr-namespace:QuestPDF.Previewer"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="QuestPDF.Previewer.PreviewerWindow"
WindowStartupLocation="CenterScreen"
ExtendClientAreaToDecorationsHint="true"
ExtendClientAreaTitleBarHeightHint="-1"
Background="#666"
Icon="/Images/Logo.png"
UseLayoutRounding="True"
Title="QuestPDF Document Preview">
<Panel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Center"
TextAlignment="Center" Text="QuestPDF Previewer" FontSize="14" Foreground="#DFFF" FontWeight="Regular" IsHitTestVisible="False" />
<previewer:PreviewerControl Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CurrentScroll="{Binding CurrentScroll, Mode=TwoWay}"
ScrollViewportSize="{Binding ScrollViewportSize, Mode=OneWayToSource}"
Pages="{Binding DocumentRenderer.Pages, Mode=OneWay}" />
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Vertical" VerticalAlignment="Bottom" Spacing="16" Margin="32" ZIndex="100">
<Button VerticalAlignment="Bottom" HorizontalAlignment="Left"
Padding="10" CornerRadius="100"
Command="{Binding ShowPdfCommand, Mode=OneTime}"
ToolTip.Tip="Generates PDF file and shows it in the default browser. Useful for testing compatibility and interactive links.">
<Viewbox Width="24" Height="24">
<Canvas Width="24" Height="24">
<Path Fill="White" Data="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H13C12.59,21.75 12.2,21.44 11.86,21.1C11.53,20.77 11.25,20.4 11,20H6V4H13V9H18V10.18C18.71,10.34 19.39,10.61 20,11V8L14,2M20.31,18.9C21.64,16.79 21,14 18.91,12.68C16.8,11.35 14,12 12.69,14.08C11.35,16.19 12,18.97 14.09,20.3C15.55,21.23 17.41,21.23 18.88,20.32L22,23.39L23.39,22L20.31,18.9M16.5,19A2.5,2.5 0 0,1 14,16.5A2.5,2.5 0 0,1 16.5,14A2.5,2.5 0 0,1 19,16.5A2.5,2.5 0 0,1 16.5,19Z" />
</Canvas>
</Viewbox>
</Button>
<Button VerticalAlignment="Bottom" HorizontalAlignment="Left"
Padding="10" CornerRadius="100"
Command="{Binding ShowDocumentationCommand, Mode=OneTime}"
ToolTip.Tip="Opens official QuestPDF documentation">
<Viewbox Width="24" Height="24">
<Canvas Width="24" Height="24">
<Path Fill="White" Data="M19 1L14 6V17L19 12.5V1M21 5V18.5C19.9 18.15 18.7 18 17.5 18C15.8 18 13.35 18.65 12 19.5V6C10.55 4.9 8.45 4.5 6.5 4.5C4.55 4.5 2.45 4.9 1 6V20.65C1 20.9 1.25 21.15 1.5 21.15C1.6 21.15 1.65 21.1 1.75 21.1C3.1 20.45 5.05 20 6.5 20C8.45 20 10.55 20.4 12 21.5C13.35 20.65 15.8 20 17.5 20C19.15 20 20.85 20.3 22.25 21.05C22.35 21.1 22.4 21.1 22.5 21.1C22.75 21.1 23 20.85 23 20.6V6C22.4 5.55 21.75 5.25 21 5M10 18.41C8.75 18.09 7.5 18 6.5 18C5.44 18 4.18 18.19 3 18.5V7.13C3.91 6.73 5.14 6.5 6.5 6.5C7.86 6.5 9.09 6.73 10 7.13V18.41Z" />
</Canvas>
</Viewbox>
</Button>
<Button VerticalAlignment="Bottom" HorizontalAlignment="Left"
Padding="10" CornerRadius="100"
Command="{Binding SponsorProjectCommand, Mode=OneTime}"
ToolTip.Tip="Do you like QuestPDF? Please consider sponsoring the project. It really helps!">
<Viewbox Width="24" Height="24">
<Canvas Width="24" Height="24">
<Path Fill="White" Data="M12,21.1L10.5,22.4C3.9,16.5 0.5,13.4 0.5,9.6C0.5,8.4 0.9,7.3 1.5,6.4C1.5,6.6 1.5,6.8 1.5,7C1.5,11.7 5.4,15.2 12,21.1M13.6,17C18.3,12.7 21.5,9.9 21.6,7C21.6,5 20.1,3.5 18.1,3.5C16.5,3.5 15,4.5 14.5,5.9H12.6C12,4.5 10.5,3.5 9,3.5C7,3.5 5.5,5 5.5,7C5.5,9.9 8.6,12.7 13.4,17L13.5,17.1M18,1.5C21.1,1.5 23.5,3.9 23.5,7C23.5,10.7 20.1,13.8 13.5,19.8C6.9,13.9 3.5,10.8 3.5,7C3.5,3.9 5.9,1.5 9,1.5C10.7,1.5 12.4,2.3 13.5,3.6C14.6,2.3 16.3,1.5 18,1.5Z" />
</Canvas>
</Viewbox>
</Button>
</StackPanel>
<ScrollBar Grid.Row="1" Grid.Column="1"
Orientation="Vertical"
AllowAutoHide="False"
Minimum="0" Maximum="1"
IsVisible="{Binding VerticalScrollbarVisible, Mode=OneWay}"
Value="{Binding CurrentScroll, Mode=TwoWay}"
ViewportSize="{Binding ScrollViewportSize, Mode=OneWay}"></ScrollBar>
</Grid>
</Panel>
</Window>
@@ -1,23 +0,0 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace QuestPDF.Previewer
{
class PreviewerWindow : Window
{
public PreviewerWindow()
{
InitializeComponent();
}
protected override void OnClosed(EventArgs e)
{
(DataContext as PreviewerWindowViewModel)?.UnregisterHotReloadHandler();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
@@ -1,108 +0,0 @@
using QuestPDF.Fluent;
using System.Diagnostics;
using ReactiveUI;
using QuestPDF.Infrastructure;
using Unit = System.Reactive.Unit;
using Avalonia.Threading;
namespace QuestPDF.Previewer
{
internal class PreviewerWindowViewModel : ReactiveObject
{
public DocumentRenderer DocumentRenderer { get; } = new();
private IDocument? _document;
public IDocument? Document
{
get => _document;
set
{
this.RaiseAndSetIfChanged(ref _document, value);
UpdateDocument(value);
}
}
private float _currentScroll;
public float CurrentScroll
{
get => _currentScroll;
set => this.RaiseAndSetIfChanged(ref _currentScroll, value);
}
private float _scrollViewportSize;
public float ScrollViewportSize
{
get => _scrollViewportSize;
set
{
this.RaiseAndSetIfChanged(ref _scrollViewportSize, value);
VerticalScrollbarVisible = value < 1;
}
}
private bool _verticalScrollbarVisible;
public bool VerticalScrollbarVisible
{
get => _verticalScrollbarVisible;
private set => Dispatcher.UIThread.Post(() => this.RaiseAndSetIfChanged(ref _verticalScrollbarVisible, value));
}
public ReactiveCommand<Unit, Unit> ShowPdfCommand { get; }
public ReactiveCommand<Unit, Unit> ShowDocumentationCommand { get; }
public ReactiveCommand<Unit, Unit> SponsorProjectCommand { get; }
public PreviewerWindowViewModel()
{
HotReloadManager.UpdateApplicationRequested += InvalidateDocument;
ShowPdfCommand = ReactiveCommand.Create(ShowPdf);
ShowDocumentationCommand = ReactiveCommand.Create(() => OpenLink("https://www.questpdf.com/documentation/api-reference.html"));
SponsorProjectCommand = ReactiveCommand.Create(() => OpenLink("https://github.com/sponsors/QuestPDF"));
}
public void UnregisterHotReloadHandler()
{
HotReloadManager.UpdateApplicationRequested -= InvalidateDocument;
}
private void InvalidateDocument(object? sender, EventArgs e)
{
UpdateDocument(Document);
}
private Task UpdateDocument(IDocument? document)
{
return Task.Run(() => DocumentRenderer.UpdateDocument(document));
}
private void ShowPdf()
{
var filePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.pdf");
try
{
Document?.GeneratePdf(filePath);
}
catch (Exception exception)
{
new ExceptionDocument(exception).GeneratePdf(filePath);
}
OpenLink(filePath);
}
private void OpenLink(string path)
{
var openBrowserProcess = new Process
{
StartInfo = new()
{
UseShellExecute = true,
FileName = path
}
};
openBrowserProcess.Start();
}
}
}
@@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Images\Logo.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.10" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.10" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.10" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.10" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
</Project>
-28
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using SkiaSharp;
@@ -25,32 +24,5 @@ namespace QuestPDF.ReportSample
Latitude = Helpers.Random.NextDouble() * 180f - 90f
};
}
private static readonly ConcurrentDictionary<int, string> RomanNumeralCache = new ConcurrentDictionary<int, string>();
public static string FormatAsRomanNumeral(this int number)
{
if (number < 0 || number > 3999)
throw new ArgumentOutOfRangeException("Number should be in range from 1 to 3999");
return RomanNumeralCache.GetOrAdd(number, x =>
{
if (x >= 1000) return "M" + FormatAsRomanNumeral(x - 1000);
if (x >= 900) return "CM" + FormatAsRomanNumeral(x - 900);
if (x >= 500) return "D" + FormatAsRomanNumeral(x - 500);
if (x >= 400) return "CD" + FormatAsRomanNumeral(x - 400);
if (x >= 100) return "C" + FormatAsRomanNumeral(x - 100);
if (x >= 90) return "XC" + FormatAsRomanNumeral(x - 90);
if (x >= 50) return "L" + FormatAsRomanNumeral(x - 50);
if (x >= 40) return "XL" + FormatAsRomanNumeral(x - 40);
if (x >= 10) return "X" + FormatAsRomanNumeral(x - 10);
if (x >= 9) return "IX" + FormatAsRomanNumeral(x - 9);
if (x >= 5) return "V" + FormatAsRomanNumeral(x - 5);
if (x >= 4) return "IV" + FormatAsRomanNumeral(x - 4);
if (x >= 1) return "I" + FormatAsRomanNumeral(x - 1);
return string.Empty;
});
}
}
}
@@ -26,58 +26,58 @@ namespace QuestPDF.ReportSample.Layouts
private void ComposeHeader(IContainer container)
{
container.Background(Colors.Grey.Lighten3).Border(1).Column(column =>
container.Background(Colors.Grey.Lighten3).Border(1).Stack(stack =>
{
column.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
stack.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
{
row.RelativeItem(2).AlignMiddle().Text("PRIMARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
row.RelativeItem(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
row.RelativeColumn(2).AlignMiddle().Text("PRIMARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
row.RelativeColumn(1).AlignRight().Box().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
});
column.Item().SkipOnce().Padding(5).Row(row =>
stack.Item().SkipOnce().Padding(5).Row(row =>
{
row.RelativeItem(2).Text("SECONDARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
row.RelativeItem(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
row.RelativeColumn(2).Text("SECONDARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
row.RelativeColumn(1).AlignRight().Box().Background(Colors.Blue.Lighten4).Padding(15);
});
});
}
private void ComposeContent(IContainer container)
{
container.Column(column =>
container.Stack(stack =>
{
column.Item().PaddingVertical(80).Text("First");
column.Item().PageBreak();
column.Item().PaddingVertical(80).Text("Second");
column.Item().PageBreak();
column.Item().PaddingVertical(80).Text("Third");
column.Item().PageBreak();
stack.Item().PaddingVertical(80).Text("First");
stack.Item().PageBreak();
stack.Item().PaddingVertical(80).Text("Second");
stack.Item().PageBreak();
stack.Item().PaddingVertical(80).Text("Third");
stack.Item().PageBreak();
});
}
private void ComposeFooter(IContainer container)
{
container.Background(Colors.Grey.Lighten3).Column(column =>
container.Background(Colors.Grey.Lighten3).Stack(stack =>
{
column.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
stack.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
{
row.RelativeItem().Text(x =>
row.RelativeColumn().Text(x =>
{
x.CurrentPageNumber();
x.Span(" / ");
x.TotalPages();
});
row.RelativeItem().AlignRight().Text("Footer for header");
row.RelativeColumn().AlignRight().Text("Footer for header");
});
column.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
stack.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
{
row.RelativeItem().Text(x =>
row.RelativeColumn().Text(x =>
{
x.CurrentPageNumber();
x.Span(" / ");
x.TotalPages();
});
row.RelativeItem().AlignRight().Text("Footer for every page except header");
row.RelativeColumn().AlignRight().Text("Footer for every page except header");
});
});
}
+10 -11
View File
@@ -1,4 +1,3 @@
using System;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
@@ -18,11 +17,11 @@ namespace QuestPDF.ReportSample.Layouts
{
container
.ShowEntire()
.Column(column =>
.Stack(stack =>
{
column.Spacing(5);
column.Item().Element(PhotoWithMaps);
column.Item().Element(PhotoDetails);
stack.Spacing(5);
stack.Item().Element(PhotoWithMaps);
stack.Item().Element(PhotoDetails);
});
}
@@ -31,14 +30,14 @@ namespace QuestPDF.ReportSample.Layouts
container
.Row(row =>
{
row.RelativeItem(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
row.RelativeColumn(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
row.RelativeItem().PaddingLeft(5).Column(column =>
row.RelativeColumn().PaddingLeft(5).Stack(stack =>
{
column.Spacing(7f);
stack.Spacing(7f);
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
});
});
}
@@ -48,7 +47,7 @@ namespace QuestPDF.ReportSample.Layouts
container.Border(0.75f).BorderColor(Colors.Grey.Medium).Grid(grid =>
{
grid.Columns(6);
grid.Item().LabelCell().Text("Date");
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty);
grid.Item().LabelCell().Text("Location");
@@ -21,26 +21,25 @@ namespace QuestPDF.ReportSample.Layouts
.Decoration(decoration =>
{
decoration
.Before()
.Header()
.PaddingBottom(5)
.Text(Model.Title)
.Style(Typography.Headline);
.Text(Model.Title, Typography.Headline);
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Column(column =>
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
{
foreach (var part in Model.Parts)
{
column.Item().EnsureSpace(25).Row(row =>
stack.Item().EnsureSpace(25).Row(row =>
{
row.ConstantItem(150).LabelCell().Text(part.Label);
var frame = row.RelativeItem().ValueCell();
row.ConstantColumn(150).LabelCell().Text(part.Label);
var frame = row.RelativeColumn().ValueCell();
if (part is ReportSectionText text)
frame.ShowEntire().Text(text.Text);
if (part is ReportSectionMap map)
frame.Element(x => MapElement(x, map));
if (part is ReportSectionPhotos photos)
frame.Element(x => PhotosElement(x, photos));
});
@@ -57,12 +56,12 @@ namespace QuestPDF.ReportSample.Layouts
return;
}
container.ShowEntire().Column(column =>
container.ShowEntire().Stack(stack =>
{
column.Spacing(5);
stack.Spacing(5);
column.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
column.Item().Text(model.Location.Format());
stack.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
stack.Item().Text(model.Location.Format());
});
}
@@ -70,7 +69,7 @@ namespace QuestPDF.ReportSample.Layouts
{
if (model.PhotoCount == 0)
{
container.Text("No photos").Style(Typography.Normal);
container.Text("No photos", Typography.Normal);
return;
}
+19 -19
View File
@@ -39,28 +39,28 @@ namespace QuestPDF.ReportSample.Layouts
page.Footer().AlignCenter().Text(text =>
{
text.CurrentPageNumber().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
text.CurrentPageNumber();
text.Span(" / ");
text.TotalPages().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
text.TotalPages();
});
});
}
private void ComposeHeader(IContainer container)
{
container.Column(column =>
container.Stack(stack =>
{
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.Spacing(50);
row.RelativeItem().PaddingTop(-10).Text(Model.Title).Style(Typography.Title);
row.ConstantItem(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
row.RelativeColumn().PaddingTop(-10).Text(Model.Title, Typography.Title);
row.ConstantColumn(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
});
column.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
stack.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
column.Item().ShowOnce().Grid(grid =>
stack.Item().ShowOnce().Grid(grid =>
{
grid.Columns(2);
grid.Spacing(5);
@@ -69,7 +69,7 @@ namespace QuestPDF.ReportSample.Layouts
{
grid.Item().Text(text =>
{
text.Span($"{field.Label}: ").SemiBold();
text.Span($"{field.Label}: ", TextStyle.Default.SemiBold());
text.Span(field.Value);
});
}
@@ -79,22 +79,22 @@ namespace QuestPDF.ReportSample.Layouts
void ComposeContent(IContainer container)
{
container.PaddingVertical(20).Column(column =>
container.PaddingVertical(20).Stack(stack =>
{
column.Spacing(20);
stack.Spacing(20);
stack.Item().Component(new TableOfContentsTemplate(Model.Sections));
column.Item().Component(new TableOfContentsTemplate(Model.Sections));
column.Item().PageBreak();
stack.Item().PageBreak();
foreach (var section in Model.Sections)
column.Item().Location(section.Title).Component(new SectionTemplate(section));
column.Item().PageBreak();
column.Item().Location("Photos");
stack.Item().Location(section.Title).Component(new SectionTemplate(section));
stack.Item().PageBreak();
stack.Item().Location("Photos");
foreach (var photo in Model.Photos)
column.Item().Component(new PhotoTemplate(photo));
stack.Item().Component(new PhotoTemplate(photo));
});
}
}
@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.ReportSample.Layouts
@@ -21,19 +19,18 @@ namespace QuestPDF.ReportSample.Layouts
.Decoration(decoration =>
{
decoration
.Before()
.Header()
.PaddingBottom(5)
.Text("Table of contents")
.Style(Typography.Headline);
.Text("Table of contents", Typography.Headline);
decoration.Content().Column(column =>
decoration.Content().Stack(stack =>
{
column.Spacing(5);
stack.Spacing(5);
for (var i = 0; i < Sections.Count; i++)
column.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
column.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
});
});
}
@@ -44,20 +41,9 @@ namespace QuestPDF.ReportSample.Layouts
.InternalLink(locationName)
.Row(row =>
{
row.ConstantItem(25).Text($"{number}.");
row.RelativeItem().Text(locationName);
row.ConstantItem(150).AlignRight().Text(text =>
{
text.BeginPageNumberOfSection(locationName);
text.Span(" - ");
text.EndPageNumberOfSection(locationName);
var lengthStyle = TextStyle.Default.Color(Colors.Grey.Medium);
text.Span(" (").Style(lengthStyle);
text.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x => x == 1 ? "1 page long" : $"{x} pages long");
text.Span(")").Style(lengthStyle);
});
row.ConstantColumn(25).Text($"{number}.");
row.RelativeColumn().Text(locationName);
row.ConstantColumn(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName));
});
}
}
+1 -2
View File
@@ -11,7 +11,6 @@ using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Drawing.Proxy;
using QuestPDF.Elements;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF.ReportSample.Layouts;
@@ -56,7 +55,7 @@ namespace QuestPDF.ReportSample
var sw = new Stopwatch();
sw.Start();
Content.VisitChildren(x =>
Content.HandleVisitor(x =>
{
if (x is ICacheable)
x.CreateProxy(y => new CacheProxy(y));
-2
View File
@@ -24,8 +24,6 @@ namespace QuestPDF.ReportSample
[Test]
public void GenerateAndShowPdf()
{
//ImagePlaceholder.Solid = true;
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
Report.GeneratePdf(path);
Process.Start("explorer.exe", path);
+2 -2
View File
@@ -21,7 +21,7 @@ namespace QuestPDF.UnitTests
Color = Colors.Red.Medium
})
.DrawElement(new Size(400, 300))
.ExpectCanvasDrawRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
.ExpectCanvasDrawFilledRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
.CheckDrawResult();
}
@@ -35,7 +35,7 @@ namespace QuestPDF.UnitTests
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectCanvasDrawRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
.ExpectCanvasDrawFilledRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
.ExpectChildDraw(new Size(400, 300))
.CheckDrawResult();
}
+30 -6
View File
@@ -1,9 +1,11 @@
using NUnit.Framework;
using System.Drawing;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
using Size = QuestPDF.Infrastructure.Size;
namespace QuestPDF.UnitTests
{
@@ -32,7 +34,29 @@ namespace QuestPDF.UnitTests
}
[Test]
public void Draw_HorizontalRight_VerticalTop()
public void Draw_SameWidths_Optimized()
{
TestPlan
.For(x => new Border
{
Top = 10,
Right = 10,
Bottom = 10,
Left = 10,
Color = Colors.Red.Medium,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildDraw(new Size(400, 300))
.ExpectCanvasDrawStrokedRectangle(new Size(400, 300), Colors.Red.Medium, 10)
.ExpectChildDraw(new Size(400, 300))
.CheckDrawResult();
}
[Test]
public void Draw_VariousWidths()
{
TestPlan
.For(x => new Border
@@ -48,10 +72,10 @@ namespace QuestPDF.UnitTests
})
.DrawElement(new Size(400, 300))
.ExpectChildDraw(new Size(400, 300))
.ExpectCanvasDrawRectangle(new Position(-20, -5), new Size(430, 10), Colors.Red.Medium) // top
.ExpectCanvasDrawRectangle(new Position(-20, -5), new Size(40, 320), Colors.Red.Medium) // left
.ExpectCanvasDrawRectangle(new Position(-20, 285), new Size(430, 30), Colors.Red.Medium) // bottom
.ExpectCanvasDrawRectangle(new Position(390, -5), new Size(20, 320), Colors.Red.Medium) // right
.ExpectCanvasDrawFilledRectangle(new Position(-20, -5), new Size(430, 10), Colors.Red.Medium) // top
.ExpectCanvasDrawFilledRectangle(new Position(-20, -5), new Size(40, 320), Colors.Red.Medium) // left
.ExpectCanvasDrawFilledRectangle(new Position(-20, 285), new Size(430, 30), Colors.Red.Medium) // bottom
.ExpectCanvasDrawFilledRectangle(new Position(390, -5), new Size(20, 320), Colors.Red.Medium) // right
.ExpectChildDraw(new Size(400, 300))
.CheckDrawResult();
}
+4 -4
View File
@@ -10,13 +10,13 @@ namespace QuestPDF.UnitTests
public class BoxTests
{
[Test]
public void Measure() => SimpleContainerTests.Measure<MinimalBox>();
public void Measure() => SimpleContainerTests.Measure<Box>();
[Test]
public void Draw_Wrap()
{
TestPlan
.For(x => new MinimalBox
.For(x => new Box
{
Child = x.CreateChild()
})
@@ -29,7 +29,7 @@ namespace QuestPDF.UnitTests
public void Measure_PartialRender()
{
TestPlan
.For(x => new MinimalBox
.For(x => new Box
{
Child = x.CreateChild()
})
@@ -43,7 +43,7 @@ namespace QuestPDF.UnitTests
public void Measure_FullRender()
{
TestPlan
.For(x => new MinimalBox
.For(x => new Box
{
Child = x.CreateChild()
})
+80 -83
View File
@@ -9,27 +9,35 @@ namespace QuestPDF.UnitTests
[TestFixture]
public class DecorationTests
{
private Decoration CreateDecoration(TestPlan testPlan)
{
return new Decoration
{
Before = testPlan.CreateChild("before"),
Content = testPlan.CreateChild("content"),
After = testPlan.CreateChild("after"),
};
}
#region Measure
[Test]
public void Measure_ReturnsWrap_WhenBeforeReturnsWrap()
public void Measure_ReturnsWrap_WhenDecorationReturnsWrap()
{
TestPlan
.For(CreateDecoration)
.For(x => new BinaryDecoration
{
Type = DecorationType.Append,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.Wrap())
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.Wrap())
.CheckMeasureResult(SpacePlan.Wrap());
}
[Test]
public void Measure_ReturnsWrap_WhenDecorationReturnsPartialRender()
{
TestPlan
.For(x => new BinaryDecoration
{
Type = DecorationType.Append,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.PartialRender(300, 200))
.CheckMeasureResult(SpacePlan.Wrap());
}
@@ -37,100 +45,89 @@ namespace QuestPDF.UnitTests
public void Measure_ReturnsWrap_WhenContentReturnsWrap()
{
TestPlan
.For(CreateDecoration)
.For(x => new BinaryDecoration
{
Type = DecorationType.Append,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.Wrap())
.CheckMeasureResult(SpacePlan.Wrap());
}
[Test]
public void Measure_ReturnsWrap_WhenAfterReturnsWrap()
public void Measure_ReturnsPartialRender_WhenContentReturnsPartialRender()
{
TestPlan
.For(CreateDecoration)
.For(x => new BinaryDecoration
{
Type = DecorationType.Append,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.Wrap())
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
.CheckMeasureResult(SpacePlan.Wrap());
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(200, 150))
.CheckMeasureResult(SpacePlan.PartialRender(400, 250));
}
[Test]
public void Measure_ReturnsWrap_WhenBeforeReturnsPartialRender()
public void Measure_ReturnsFullRender_WhenContentReturnsFullRender()
{
TestPlan
.For(CreateDecoration)
.For(x => new BinaryDecoration
{
Type = DecorationType.Append,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.PartialRender(100, 50))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
.CheckMeasureResult(SpacePlan.Wrap());
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(200, 150))
.CheckMeasureResult(SpacePlan.FullRender(400, 250));
}
[Test]
public void Measure_ReturnsWrap_WhenAfterReturnsPartialRender()
{
TestPlan
.For(CreateDecoration)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.PartialRender(100, 50))
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
.CheckMeasureResult(SpacePlan.Wrap());
}
[Test]
public void Measure_ReturnsWrap_WhenContentReturnsPartialRender()
{
TestPlan
.For(CreateDecoration)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(150, 100))
.CheckMeasureResult(SpacePlan.PartialRender(150, 200));
}
[Test]
public void Measure_ReturnsWrap_WhenContentReturnsFullRender()
{
TestPlan
.For(CreateDecoration)
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(150, 100))
.CheckMeasureResult(SpacePlan.FullRender(150, 200));
}
#endregion
#region Draw
[Test]
public void Draw_Prepend()
{
TestPlan
.For(x => new BinaryDecoration
{
Type = DecorationType.Prepend,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
.ExpectChildDraw("decoration", new Size(400, 100))
.ExpectCanvasTranslate(0, 100)
.ExpectChildDraw("content", new Size(400, 200))
.ExpectCanvasTranslate(0, -100)
.CheckDrawResult();
}
[Test]
public void Draw_Append()
{
TestPlan
.For(CreateDecoration)
.For(x => new BinaryDecoration
{
Type = DecorationType.Append,
DecorationElement = x.CreateChild("decoration"),
ContentElement = x.CreateChild("content")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(200, 40))
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(200, 60))
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(300, 100))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("before", new Size(300, 40))
.ExpectCanvasTranslate(0, 0)
.ExpectCanvasTranslate(0, 40)
.ExpectChildDraw("content", new Size(300, 100))
.ExpectCanvasTranslate(0, -40)
.ExpectCanvasTranslate(0, 140)
.ExpectChildDraw("after", new Size(300, 60))
.ExpectCanvasTranslate(0, -140)
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
.ExpectChildDraw("content", new Size(400, 200))
.ExpectCanvasTranslate(0, 200)
.ExpectChildDraw("decoration", new Size(400, 100))
.ExpectCanvasTranslate(0, -200)
.CheckDrawResult();
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
public class ExternalLinkTests
{
[Test]
public void Measure() => SimpleContainerTests.Measure<Hyperlink>();
public void Measure() => SimpleContainerTests.Measure<ExternalLink>();
// TODO: consider tests for the Draw method
}
-146
View File
@@ -1,146 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using QuestPDF.Drawing;
using SkiaSharp;
using static SkiaSharp.SKFontStyleSlant;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class FontStyleSetTests
{
private void ExpectComparisonOrder(SKFontStyle target, SKFontStyle[] styles)
{
for (var i = 0; i < styles.Length - 1; i++)
{
var currentStyle = styles[i];
var nextStyle = styles[i + 1];
FontStyleSet.IsBetterMatch(target, currentStyle, nextStyle).Should().BeTrue();
FontStyleSet.IsBetterMatch(target, nextStyle, currentStyle).Should().BeFalse();
}
}
[Test]
public void FontStyleSet_IsBetterMatch_CondensedWidth()
{
var styles = new[]
{
new SKFontStyle(500, 5, Upright),
new SKFontStyle(500, 4, Upright),
new SKFontStyle(500, 3, Upright),
new SKFontStyle(500, 6, Upright)
};
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_ExpandedWidth()
{
var styles = new[]
{
new SKFontStyle(500, 6, Upright),
new SKFontStyle(500, 7, Upright),
new SKFontStyle(500, 8, Upright),
new SKFontStyle(500, 5, Upright)
};
ExpectComparisonOrder(new SKFontStyle(500, 6, Upright), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_ItalicSlant()
{
var styles = new[]
{
new SKFontStyle(500, 5, Italic),
new SKFontStyle(500, 5, Oblique),
new SKFontStyle(500, 5, Upright)
};
ExpectComparisonOrder(new SKFontStyle(500, 5, Italic), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_ObliqueSlant()
{
var styles = new[]
{
new SKFontStyle(500, 5, Oblique),
new SKFontStyle(500, 5, Italic),
new SKFontStyle(500, 5, Upright)
};
ExpectComparisonOrder(new SKFontStyle(500, 5, Oblique), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_UprightSlant()
{
var styles = new[]
{
new SKFontStyle(500, 5, Upright),
new SKFontStyle(500, 5, Oblique),
new SKFontStyle(500, 5, Italic)
};
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_ThinWeight()
{
var styles = new[]
{
new SKFontStyle(300, 5, Upright),
new SKFontStyle(200, 5, Upright),
new SKFontStyle(100, 5, Upright),
new SKFontStyle(400, 5, Upright)
};
ExpectComparisonOrder(new SKFontStyle(300, 5, Upright), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_RegularWeight()
{
var styles = new[]
{
new SKFontStyle(500, 5, Upright),
new SKFontStyle(300, 5, Upright),
new SKFontStyle(100, 5, Upright),
new SKFontStyle(600, 5, Upright)
};
ExpectComparisonOrder(new SKFontStyle(400, 5, Upright), styles);
}
[Test]
public void FontStyleSet_IsBetterMatch_BoldWeight()
{
var styles = new[]
{
new SKFontStyle(600, 5, Upright),
new SKFontStyle(700, 5, Upright),
new SKFontStyle(800, 5, Upright),
new SKFontStyle(500, 5, Upright)
};
ExpectComparisonOrder(new SKFontStyle(600, 5, Upright), styles);
}
[Test]
public void FontStyleSet_RespectsPriority()
{
var styles = new[]
{
new SKFontStyle(600, 5, Italic),
new SKFontStyle(600, 6, Upright),
new SKFontStyle(500, 6, Italic)
};
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
}
}
}
+54 -54
View File
@@ -39,26 +39,26 @@ namespace QuestPDF.UnitTests
// assert
var expected = new Container();
expected.Column(column =>
expected.Stack(stack =>
{
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(6).Element(childA);
row.RelativeItem(4).Element(childB);
row.RelativeItem(2);
row.RelativeColumn(6).Element(childA);
row.RelativeColumn(4).Element(childB);
row.RelativeColumn(2);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(4).Element(childC);
row.RelativeItem(2).Element(childD);
row.RelativeItem(6);
row.RelativeColumn(4).Element(childC);
row.RelativeColumn(2).Element(childD);
row.RelativeColumn(6);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(8).Element(childE);
row.RelativeItem(4);
row.RelativeColumn(8).Element(childE);
row.RelativeColumn(4);
});
});
@@ -93,29 +93,29 @@ namespace QuestPDF.UnitTests
// assert
var expected = new Container();
expected.Column(column =>
expected.Stack(stack =>
{
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(1);
row.RelativeItem(6).Element(childA);
row.RelativeItem(4).Element(childB);
row.RelativeItem(1);
row.RelativeColumn(1);
row.RelativeColumn(6).Element(childA);
row.RelativeColumn(4).Element(childB);
row.RelativeColumn(1);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(3);
row.RelativeItem(4).Element(childC);
row.RelativeItem(2).Element(childD);
row.RelativeItem(3);
row.RelativeColumn(3);
row.RelativeColumn(4).Element(childC);
row.RelativeColumn(2).Element(childD);
row.RelativeColumn(3);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(2);
row.RelativeItem(8).Element(childE);
row.RelativeItem(2);
row.RelativeColumn(2);
row.RelativeColumn(8).Element(childE);
row.RelativeColumn(2);
});
});
@@ -150,26 +150,26 @@ namespace QuestPDF.UnitTests
// assert
var expected = new Container();
expected.Column(column =>
expected.Stack(stack =>
{
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(2);
row.RelativeItem(6).Element(childA);
row.RelativeItem(4).Element(childB);
row.RelativeColumn(2);
row.RelativeColumn(6).Element(childA);
row.RelativeColumn(4).Element(childB);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(6);
row.RelativeItem(4).Element(childC);
row.RelativeItem(2).Element(childD);
row.RelativeColumn(6);
row.RelativeColumn(4).Element(childC);
row.RelativeColumn(2).Element(childD);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.RelativeItem(4);
row.RelativeItem(8).Element(childE);
row.RelativeColumn(4);
row.RelativeColumn(8).Element(childE);
});
});
@@ -210,36 +210,36 @@ namespace QuestPDF.UnitTests
// assert
var expected = new Container();
expected.Column(column =>
expected.Stack(stack =>
{
column.Spacing(20);
stack.Spacing(20);
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.Spacing(30);
row.RelativeItem(3);
row.RelativeItem(5).Element(childA);
row.RelativeItem(5).Element(childB);
row.RelativeItem(3);
row.RelativeColumn(3);
row.RelativeColumn(5).Element(childA);
row.RelativeColumn(5).Element(childB);
row.RelativeColumn(3);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.Spacing(30);
row.RelativeItem(3);
row.RelativeItem(10).Element(childC);
row.RelativeItem(3);
row.RelativeColumn(3);
row.RelativeColumn(10).Element(childC);
row.RelativeColumn(3);
});
column.Item().Row(row =>
stack.Item().Row(row =>
{
row.Spacing(30);
row.RelativeItem(2);
row.RelativeItem(12).Element(childD);
row.RelativeItem(2);
row.RelativeColumn(2);
row.RelativeColumn(12).Element(childD);
row.RelativeColumn(2);
});
});
+1 -1
View File
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
public class InternalLinkTests
{
[Test]
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
// TODO: consider tests for the Draw method
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
public class InternalLocationTests
{
[Test]
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
// TODO: consider tests for the Draw method
}
+267
View File
@@ -0,0 +1,267 @@
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class RowTests
{
#region Measure
[Test]
public void Measure_ReturnsWrap_WhenLeftChildReturnsWrap()
{
TestPlan
.For(x => new BinaryRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.Wrap())
.CheckMeasureResult(SpacePlan.Wrap());
}
[Test]
public void Measure_ReturnsWrap_WhenRightChildReturnsWrap()
{
TestPlan
.For(x => new BinaryRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.Wrap())
.CheckMeasureResult(SpacePlan.Wrap());
}
[Test]
public void Measure_ReturnsPartialRender_WhenLeftChildReturnsPartialRender()
{
TestPlan
.For(x => new BinaryRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.PartialRender(250, 150))
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.FullRender(100, 100))
.CheckMeasureResult(SpacePlan.PartialRender(350, 150));
}
[Test]
public void Measure_ReturnsPartialRender_WhenRightChildReturnsPartialRender()
{
TestPlan
.For(x => new BinaryRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.PartialRender(100, 100))
.CheckMeasureResult(SpacePlan.PartialRender(350, 150));
}
[Test]
public void Measure_ReturnsFullRender_WhenBothChildrenReturnFullRender()
{
TestPlan
.For(x => new BinaryRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(200, 150))
.ExpectChildMeasure("right", new Size(200, 300), SpacePlan.FullRender(100, 100))
.CheckMeasureResult(SpacePlan.FullRender(300, 150));
}
#endregion
#region Draw
[Test]
public void Draw()
{
TestPlan
.For(x => new BinaryRow
{
Left = x.CreateChild("left"),
Right = x.CreateChild("right")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
.ExpectChildDraw("left", new Size(250, 300))
.ExpectCanvasTranslate(250, 0)
.ExpectChildDraw("right", new Size(150, 300))
.ExpectCanvasTranslate(-250, 0)
.CheckDrawResult();
}
#endregion
#region Structure
[Test]
public void Structure_RelativeColumnsHandling()
{
// arrange
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
var childD = TestPlan.CreateUniqueElement();
var childE = TestPlan.CreateUniqueElement();
const int spacing = 25;
var availableSpace = new Size(1100, 400);
// act
var value = new Container();
value.Row(row =>
{
row.Spacing(spacing);
row.ConstantColumn(150).Element(childA);
row.ConstantColumn(250).Element(childB);
row.RelativeColumn(1).Element(childC);
row.RelativeColumn(2).Element(childD);
row.RelativeColumn(3).Element(childE);
});
// assert
var expected = new Container();
expected.Row(row =>
{
row.Spacing(spacing);
row.ConstantColumn(150).Element(childA);
row.ConstantColumn(250).Element(childB);
row.ConstantColumn(100).Element(childC);
row.ConstantColumn(200).Element(childD);
row.ConstantColumn(300).Element(childE);
});
TestPlan.CompareOperations(value, expected, availableSpace);
}
[Test]
public void Structure_Tree()
{
// arrange
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
var childD = TestPlan.CreateUniqueElement();
var childE = TestPlan.CreateUniqueElement();
const int spacing = 25;
var availableSpace = new Size(1200, 400);
// act
var value = new Container();
value.Row(row =>
{
row.Spacing(spacing);
row.ConstantColumn(150).Element(childA);
row.ConstantColumn(200).Element(childB);
row.ConstantColumn(250).Element(childC);
row.RelativeColumn(2).Element(childD);
row.RelativeColumn(3).Element(childE);
});
// assert
var expected = new BinaryRow
{
Left = new BinaryRow
{
Left = new BinaryRow
{
Left = new Constrained
{
MinWidth = 150,
MaxWidth = 150,
Child = childA
},
Right = new Constrained
{
MinWidth = 25,
MaxWidth = 25
}
},
Right = new BinaryRow
{
Left = new Constrained
{
MinWidth = 200,
MaxWidth = 200,
Child = childB
},
Right = new Constrained
{
MinWidth = 25,
MaxWidth = 25
}
}
},
Right = new BinaryRow
{
Left = new BinaryRow
{
Left = new Constrained
{
MinWidth = 250,
MaxWidth = 250,
Child = childC
},
Right = new Constrained
{
MinWidth = 25,
MaxWidth = 25
}
},
Right = new BinaryRow
{
Left = new Constrained
{
MinWidth = 200,
MaxWidth = 200,
Child = childD
},
Right = new BinaryRow
{
Left = new Constrained
{
MinWidth = 25,
MaxWidth = 25
},
Right = new Constrained
{
MinWidth = 300,
MaxWidth = 300,
Child = childE
}
}
}
}
};
TestPlan.CompareOperations(value, expected, availableSpace);
}
#endregion
}
}
@@ -1,5 +1,4 @@
using System.Linq;
using NUnit.Framework;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
@@ -9,40 +8,19 @@ using QuestPDF.UnitTests.TestEngine;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class ColumnTests
public class StackTests
{
private Column CreateColumnWithTwoItems(TestPlan testPlan)
{
return new Column
{
Items =
{
new ColumnItem
{
Child = testPlan.CreateChild("first")
},
new ColumnItem
{
Child = testPlan.CreateChild("second")
}
}
};
}
private Column CreateColumnWithTwoItemsWhereFirstIsFullyRendered(TestPlan testPlan)
{
var column = CreateColumnWithTwoItems(testPlan);
column.Items.First().IsRendered = true;
return column;
}
#region Measure
[Test]
public void Measure_ReturnsWrap_WhenFirstChildWraps()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
.CheckMeasureResult(SpacePlan.Wrap());
@@ -52,7 +30,11 @@ namespace QuestPDF.UnitTests
public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(300, 200))
.CheckMeasureResult(SpacePlan.PartialRender(300, 200));
@@ -62,7 +44,11 @@ namespace QuestPDF.UnitTests
public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
@@ -73,7 +59,11 @@ namespace QuestPDF.UnitTests
public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(300, 150))
@@ -84,13 +74,33 @@ namespace QuestPDF.UnitTests
public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(100, 50))
.CheckMeasureResult(SpacePlan.FullRender(200, 150));
}
[Test]
public void Measure_UsesEmpty_WhenFirstChildIsRendered()
{
TestPlan
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second"),
IsFirstRendered = true
})
.MeasureElement(new Size(400, 300))
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
.CheckMeasureResult(SpacePlan.FullRender(200, 300));
}
#endregion
#region Draw
@@ -99,7 +109,11 @@ namespace QuestPDF.UnitTests
public void Draw_WhenFirstChildWraps()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
.CheckDrawResult();
@@ -109,12 +123,14 @@ namespace QuestPDF.UnitTests
public void Draw_WhenFirstChildPartiallyRenders()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(200, 100))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("first", new Size(400, 100))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
@@ -122,13 +138,15 @@ namespace QuestPDF.UnitTests
public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("first", new Size(400, 100))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
.CheckDrawResult();
}
@@ -136,13 +154,15 @@ namespace QuestPDF.UnitTests
public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("first", new Size(400, 100))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
.ExpectCanvasTranslate(0, 100)
.ExpectChildDraw("second", new Size(400, 150))
.ExpectCanvasTranslate(0, -100)
@@ -153,13 +173,15 @@ namespace QuestPDF.UnitTests
public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
{
TestPlan
.For(CreateColumnWithTwoItems)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second")
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("first", new Size(400, 100))
.ExpectCanvasTranslate(0, 0)
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
.ExpectCanvasTranslate(0, 100)
.ExpectChildDraw("second", new Size(400, 150))
.ExpectCanvasTranslate(0, -100)
@@ -170,13 +192,19 @@ namespace QuestPDF.UnitTests
public void Draw_UsesEmpty_WhenFirstChildIsRendered()
{
TestPlan
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second"),
IsFirstRendered = true
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.PartialRender(200, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("second", new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.CheckState<Column>(x => x.Items.First().IsRendered)
.CheckState<BinaryStack>(x => x.IsFirstRendered)
.CheckDrawResult();
}
@@ -184,14 +212,97 @@ namespace QuestPDF.UnitTests
public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
{
TestPlan
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
.For(x => new BinaryStack
{
First = x.CreateChild("first"),
Second = x.CreateChild("second"),
IsFirstRendered = true
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw("second", new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult()
.CheckState<Column>(x => !x.Items.First().IsRendered);
.CheckState<BinaryStack>(x => !x.IsFirstRendered);
}
#endregion
#region Structure
[Test]
public void Structure_Simple()
{
// arrange
var childA = TestPlan.CreateUniqueElement();
var childB = TestPlan.CreateUniqueElement();
var childC = TestPlan.CreateUniqueElement();
var childD = TestPlan.CreateUniqueElement();
var childE = TestPlan.CreateUniqueElement();
const int spacing = 20;
// act
var structure = new Container();
structure.Stack(stack =>
{
stack.Spacing(spacing);
stack.Item().Element(childA);
stack.Item().Element(childB);
stack.Item().Element(childC);
stack.Item().Element(childD);
stack.Item().Element(childE);
});
// assert
var expected = new Padding
{
Bottom = -spacing,
Child = new BinaryStack
{
First = new BinaryStack
{
First = new Padding
{
Bottom = spacing,
Child = childA
},
Second = new Padding
{
Bottom = spacing,
Child = childB
}
},
Second = new BinaryStack
{
First = new Padding
{
Bottom = spacing,
Child = childC
},
Second = new BinaryStack
{
First = new Padding
{
Bottom = spacing,
Child = childD
},
Second = new Padding
{
Bottom = spacing,
Child = childE
}
}
}
}
};
TestPlan.CompareOperations(structure, expected);
}
#endregion
+8 -6
View File
@@ -11,18 +11,20 @@ namespace QuestPDF.UnitTests.TestEngine
public Action<float, float> ScaleFunc { get; set; }
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
public Action<Position, Size, string> DrawRectFunc { get; set; }
public Action<Position, Size, string> DrawFilledRectangleFunc { get; set; }
public Action<Size, string, float> DrawStrokedRectangleFunc { get; set; }
public void Translate(Position vector) => TranslateFunc(vector);
public void Rotate(float angle) => RotateFunc(angle);
public void Scale(float scaleX, float scaleY) => ScaleFunc(scaleX, scaleY);
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
public void DrawFilledRectangle(Position vector, Size size, string color) => DrawFilledRectangleFunc(vector, size, color);
public void DrawStrokedRectangle(Size size, string color, float width) => DrawStrokedRectangleFunc(size, color, width);
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
public void DrawSectionLink(string sectionName, Size size) => throw new NotImplementedException();
public void DrawSection(string sectionName) => throw new NotImplementedException();
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
public void DrawLocation(string locationName) => throw new NotImplementedException();
}
}
@@ -14,12 +14,13 @@ namespace QuestPDF.UnitTests.TestEngine
public void Rotate(float angle) => Operations.Add(new CanvasRotateOperation(angle));
public void Scale(float scaleX, float scaleY) => Operations.Add(new CanvasScaleOperation(scaleX, scaleY));
public void DrawRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawRectangleOperation(vector, size, color));
public void DrawFilledRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawFilledRectangleOperation(vector, size, color));
public void DrawStrokedRectangle(Size size, string color, float width) => Operations.Add(new CanvasDrawStrokedRectangleOperation(size, color, width));
public void DrawText(string text, Position position, TextStyle style) => Operations.Add(new CanvasDrawTextOperation(text, position, style));
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
public void DrawSectionLink(string sectionName, Size size) => throw new NotImplementedException();
public void DrawSection(string sectionName) => throw new NotImplementedException();
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
public void DrawLocation(string locationName) => throw new NotImplementedException();
}
}
@@ -2,13 +2,13 @@
namespace QuestPDF.UnitTests.TestEngine.Operations
{
internal class CanvasDrawRectangleOperation : OperationBase
internal class CanvasDrawFilledRectangleOperation : OperationBase
{
public Position Position { get; }
public Size Size { get; }
public string Color { get; }
public CanvasDrawRectangleOperation(Position position, Size size, string color)
public CanvasDrawFilledRectangleOperation(Position position, Size size, string color)
{
Position = position;
Size = size;
@@ -0,0 +1,18 @@
using QuestPDF.Infrastructure;
namespace QuestPDF.UnitTests.TestEngine.Operations
{
internal class CanvasDrawStrokedRectangleOperation : OperationBase
{
public Size Size { get; }
public string Color { get; }
public float Width { get; set; }
public CanvasDrawStrokedRectangleOperation(Size size, string color, float width)
{
Size = size;
Color = color;
Width = width;
}
}
}
+31 -16
View File
@@ -70,17 +70,27 @@ namespace QuestPDF.UnitTests.TestEngine
Assert.AreEqual(expected.ScaleX, scaleX, "Scale X");
Assert.AreEqual(expected.ScaleY, scaleY, "Scale Y");
},
DrawRectFunc = (position, size, color) =>
DrawFilledRectangleFunc = (position, size, color) =>
{
var expected = GetExpected<CanvasDrawRectangleOperation>();
var expected = GetExpected<CanvasDrawFilledRectangleOperation>();
Assert.AreEqual(expected.Position.X, position.X, "Draw rectangle: X");
Assert.AreEqual(expected.Position.Y, position.Y, "Draw rectangle: Y");
Assert.AreEqual(expected.Position.X, position.X, "Draw filled rectangle: X");
Assert.AreEqual(expected.Position.Y, position.Y, "Draw filled rectangle: Y");
Assert.AreEqual(expected.Size.Width, size.Width, "Draw rectangle: width");
Assert.AreEqual(expected.Size.Height, size.Height, "Draw rectangle: height");
Assert.AreEqual(expected.Size.Width, size.Width, "Draw filled rectangle: width");
Assert.AreEqual(expected.Size.Height, size.Height, "Draw filled rectangle: height");
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
Assert.AreEqual(expected.Color, color, "Draw filled rectangle: color");
},
DrawStrokedRectangleFunc = (size, color, width) =>
{
var expected = GetExpected<CanvasDrawStrokedRectangleOperation>();
Assert.AreEqual(expected.Size.Width, size.Width, "Draw stroked rectangle: width");
Assert.AreEqual(expected.Size.Height, size.Height, "Draw stroked rectangle: height");
Assert.AreEqual(expected.Color, color, "Draw stroked rectangle: color");
Assert.AreEqual(expected.Width, width, "Draw stroked rectangle: width");
},
DrawTextFunc = (text, position, style) =>
{
@@ -92,7 +102,7 @@ namespace QuestPDF.UnitTests.TestEngine
Assert.AreEqual(expected.Position.Y, position.Y, "Draw text: Y");
Assert.AreEqual(expected.Style.Color, style.Color, "Draw text: color");
Assert.AreEqual(expected.Style.FontFamily, style.FontFamily, "Draw text: font");
Assert.AreEqual(expected.Style.FontType, style.FontType, "Draw text: font");
Assert.AreEqual(expected.Style.Size, style.Size, "Draw text: size");
},
DrawImageFunc = (image, position, size) =>
@@ -196,9 +206,14 @@ namespace QuestPDF.UnitTests.TestEngine
return AddOperation(new CanvasRotateOperation(angle));
}
public TestPlan ExpectCanvasDrawRectangle(Position position, Size size, string color)
public TestPlan ExpectCanvasDrawFilledRectangle(Position position, Size size, string color)
{
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
return AddOperation(new CanvasDrawFilledRectangleOperation(position, size, color));
}
public TestPlan ExpectCanvasDrawStrokedRectangle(Size size, string color, float width)
{
return AddOperation(new CanvasDrawStrokedRectangleOperation(size, color, width));
}
public TestPlan ExpectCanvasDrawText(string text, Position position, TextStyle style)
@@ -213,7 +228,7 @@ namespace QuestPDF.UnitTests.TestEngine
public TestPlan CheckMeasureResult(SpacePlan expected)
{
Element.VisitChildren(x => x?.Initialize(null, Canvas));
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
var actual = Element.Measure(OperationInput);
@@ -228,7 +243,7 @@ namespace QuestPDF.UnitTests.TestEngine
public TestPlan CheckDrawResult()
{
Element.VisitChildren(x => x?.Initialize(null, Canvas));
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
Element.Draw(OperationInput);
return this;
}
@@ -271,10 +286,10 @@ namespace QuestPDF.UnitTests.TestEngine
availableSpace ??= new Size(400, 300);
var canvas = new FreeCanvas();
value.VisitChildren(x => x.Initialize(null, canvas));
value.HandleVisitor(x => x.Initialize(null, canvas));
var valueMeasure = value.Measure(availableSpace.Value);
expected.VisitChildren(x => x.Initialize(null, canvas));
expected.HandleVisitor(x => x.Initialize(null, canvas));
var expectedMeasure = expected.Measure(availableSpace.Value);
valueMeasure.Should().BeEquivalentTo(expectedMeasure);
@@ -285,11 +300,11 @@ namespace QuestPDF.UnitTests.TestEngine
availableSpace ??= new Size(400, 300);
var valueCanvas = new OperationRecordingCanvas();
value.VisitChildren(x => x.Initialize(null, valueCanvas));
value.HandleVisitor(x => x.Initialize(null, valueCanvas));
value.Draw(availableSpace.Value);
var expectedCanvas = new OperationRecordingCanvas();
expected.VisitChildren(x => x.Initialize(null, expectedCanvas));
expected.HandleVisitor(x => x.Initialize(null, expectedCanvas));
expected.Draw(availableSpace.Value);
valueCanvas.Operations.Should().BeEquivalentTo(expectedCanvas.Operations);
-17
View File
@@ -8,15 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.UnitTests", "Quest
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer", "QuestPDF.Previewer\QuestPDF.Previewer.csproj", "{D63255E7-7043-4AD4-A4C9-7ACFEDBE8225}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer.Examples", "QuestPDF.Previewer.Examples\QuestPDF.Previewer.Examples.csproj", "{CA413A39-038F-4A9F-B56F-0E5413B6B158}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{73123649-216A-47B4-BFB2-DADF13FBE75D}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -39,13 +30,5 @@ Global
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.Build.0 = Release|Any CPU
{D63255E7-7043-4AD4-A4C9-7ACFEDBE8225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D63255E7-7043-4AD4-A4C9-7ACFEDBE8225}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D63255E7-7043-4AD4-A4C9-7ACFEDBE8225}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D63255E7-7043-4AD4-A4C9-7ACFEDBE8225}.Release|Any CPU.Build.0 = Release|Any CPU
{CA413A39-038F-4A9F-B56F-0E5413B6B158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA413A39-038F-4A9F-B56F-0E5413B6B158}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA413A39-038F-4A9F-B56F-0E5413B6B158}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA413A39-038F-4A9F-B56F-0E5413B6B158}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
-1
View File
@@ -1,7 +1,6 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("QuestPDF.Previewer")]
[assembly: InternalsVisibleTo("QuestPDF.UnitTests")]
[assembly: InternalsVisibleTo("QuestPDF.Examples")]
[assembly: InternalsVisibleTo("QuestPDF.ReportSample")]
+3 -3
View File
@@ -16,13 +16,13 @@ namespace QuestPDF.Drawing
var container = new Container();
container
.Column(column =>
.Stack(stack =>
{
Pages
.SelectMany(x => new List<Action>()
{
() => column.Item().PageBreak(),
() => column.Item().Component(x)
() => stack.Item().PageBreak(),
() => stack.Item().Component(x)
})
.Skip(1)
.ToList()
+20 -48
View File
@@ -7,8 +7,6 @@ using QuestPDF.Drawing.Proxy;
using QuestPDF.Elements;
using QuestPDF.Elements.Text;
using QuestPDF.Elements.Text.Items;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Drawing
@@ -17,8 +15,6 @@ namespace QuestPDF.Drawing
{
internal static void GeneratePdf(Stream stream, IDocument document)
{
CheckIfStreamIsCompatible(stream);
var metadata = document.GetMetadata();
var canvas = new PdfCanvas(stream, metadata);
RenderDocument(canvas, document);
@@ -26,21 +22,10 @@ namespace QuestPDF.Drawing
internal static void GenerateXps(Stream stream, IDocument document)
{
CheckIfStreamIsCompatible(stream);
var metadata = document.GetMetadata();
var canvas = new XpsCanvas(stream, metadata);
RenderDocument(canvas, document);
}
private static void CheckIfStreamIsCompatible(Stream stream)
{
if (!stream.CanWrite)
throw new ArgumentException("The library requires a Stream object with the 'write' capability available (the CanWrite flag). Please consider using the MemoryStream class.");
if (!stream.CanSeek)
throw new ArgumentException("The library requires a Stream object with the 'seek' capability available (the CanSeek flag). Please consider using the MemoryStream class.");
}
internal static ICollection<byte[]> GenerateImages(IDocument document)
{
@@ -51,13 +36,12 @@ namespace QuestPDF.Drawing
return canvas.Images;
}
internal static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
where TCanvas : ICanvas, IRenderingCanvas
{
var container = new DocumentContainer();
document.Compose(container);
var content = container.Compose();
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
var metadata = document.GetMetadata();
var pageContext = new PageContext();
@@ -74,8 +58,8 @@ namespace QuestPDF.Drawing
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata, DebuggingState? debuggingState)
where TCanvas : ICanvas, IRenderingCanvas
{
content.VisitChildren(x => x?.Initialize(pageContext, canvas));
content.VisitChildren(x => (x as IStateResettable)?.ResetState());
content.HandleVisitor(x => x?.Initialize(pageContext, canvas));
content.HandleVisitor(x => (x as IStateResettable)?.ResetState());
canvas.BeginDocument();
@@ -138,7 +122,7 @@ namespace QuestPDF.Drawing
private static void ApplyCaching(Container content)
{
content.VisitChildren(x =>
content.HandleVisitor(x =>
{
if (x is ICacheable)
x.CreateProxy(y => new CacheProxy(y));
@@ -149,7 +133,7 @@ namespace QuestPDF.Drawing
{
var debuggingState = new DebuggingState();
content.VisitChildren(x =>
content.HandleVisitor(x =>
{
x.CreateProxy(y => new DebuggingProxy(debuggingState, y));
});
@@ -157,38 +141,26 @@ namespace QuestPDF.Drawing
return debuggingState;
}
private static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
internal static void ApplyDefaultTextStyle(this Element content, TextStyle documentDefaultTextStyle)
{
if (content == null)
return;
documentDefaultTextStyle.ApplyGlobalStyle(TextStyle.LibraryDefault);
if (content is TextBlock textBlock)
content.HandleVisitor(element =>
{
foreach (var textBlockItem in textBlock.Items)
{
if (textBlockItem is TextBlockSpan textSpan)
{
textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
}
else if (textBlockItem is TextBlockElement textElement)
{
ApplyDefaultTextStyle(textElement.Element, documentDefaultTextStyle);
}
}
var text = element as TextBlock;
return;
}
if (text == null)
return;
var targetTextStyle = documentDefaultTextStyle;
if (content is DefaultTextStyle defaultTextStyleElement)
{
defaultTextStyleElement.TextStyle.ApplyParentStyle(documentDefaultTextStyle);
targetTextStyle = defaultTextStyleElement.TextStyle;
}
foreach (var child in content.GetChildren())
ApplyDefaultTextStyle(child, targetTextStyle);
foreach (var child in text.Children)
{
if (child is TextBlockSpan textSpan)
textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
if (child is TextBlockElement textElement)
ApplyDefaultTextStyle(textElement.Element, documentDefaultTextStyle);
}
});
}
}
}
+18 -56
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using QuestPDF.Infrastructure;
using SkiaSharp;
@@ -9,41 +8,16 @@ namespace QuestPDF.Drawing
{
public static class FontManager
{
private static ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
private static ConcurrentDictionary<object, SKFontMetrics> FontMetrics = new();
private static ConcurrentDictionary<object, SKPaint> Paints = new();
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new();
private static ConcurrentDictionary<string, SKTypeface> Typefaces = new ConcurrentDictionary<string, SKTypeface>();
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
private static void RegisterFontType(SKData fontData, string? customName = null)
{
foreach (var index in Enumerable.Range(0, 256))
{
var typeface = SKTypeface.FromData(fontData, index);
if (typeface == null)
break;
var typefaceName = customName ?? typeface.FamilyName;
var fontStyleSet = StyleSets.GetOrAdd(typefaceName, _ => new FontStyleSet());
fontStyleSet.Add(typeface);
}
}
[Obsolete("Since version 2022.3, the FontManager class offers better font type matching support. Please use the RegisterFont(Stream stream) method.")]
public static void RegisterFontType(string fontName, Stream stream)
{
using var fontData = SKData.Create(stream);
RegisterFontType(fontData);
RegisterFontType(fontData, customName: fontName);
Typefaces.TryAdd(fontName, SKTypeface.FromStream(stream));
}
public static void RegisterFont(Stream stream)
{
using var fontData = SKData.Create(stream);
RegisterFontType(fontData);
}
internal static SKPaint ColorToPaint(this string color)
{
return ColorPaint.GetOrAdd(color, Convert);
@@ -52,53 +26,41 @@ namespace QuestPDF.Drawing
{
return new SKPaint
{
Color = SKColor.Parse(color),
IsAntialias = true
Color = SKColor.Parse(color)
};
}
}
internal static SKPaint ToPaint(this TextStyle style)
{
return Paints.GetOrAdd(style.PaintKey, key => Convert(style));
return Paints.GetOrAdd(style.Key, key => Convert(style));
static SKPaint Convert(TextStyle style)
{
return new SKPaint
{
Color = SKColor.Parse(style.Color),
Typeface = GetTypeface(style),
TextSize = style.Size ?? 12,
IsAntialias = true,
TextSize = (style.Size ?? 12),
TextEncoding = SKTextEncoding.Utf32
};
}
static SKTypeface GetTypeface(TextStyle style)
{
var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
if (Typefaces.TryGetValue(style.FontType, out var result))
return result;
var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
var fontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
if (StyleSets.TryGetValue(style.FontFamily, out var fontStyleSet))
return fontStyleSet.Match(fontStyle);
var fontFromDefaultSource = SKFontManager.Default.MatchFamily(style.FontFamily, fontStyle);
if (fontFromDefaultSource != null)
return fontFromDefaultSource;
throw new ArgumentException(
$"The typeface '{style.FontFamily}' could not be found. " +
$"Please consider the following options: " +
$"1) install the font on your operating system or execution environment. " +
$"2) load a font file specifically for QuestPDF usage via the QuestPDF.Drawing.FontManager.RegisterFontType(Stream fileContentStream) static method.");
return SKTypeface.FromFamilyName(style.FontType, (int)(style.FontWeight ?? FontWeight.Normal), (int)SKFontStyleWidth.Normal, slant)
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
}
}
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
{
return FontMetrics.GetOrAdd(style.FontMetricsKey, key => style.ToPaint().FontMetrics);
return FontMetrics.GetOrAdd(style.Key, key => style.ToPaint().FontMetrics);
}
}
}
-132
View File
@@ -1,132 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using SkiaSharp;
namespace QuestPDF.Drawing
{
internal class FontStyleSet
{
private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles { get; } = new();
public void Add(SKTypeface typeface)
{
var style = typeface.FontStyle;
Styles.AddOrUpdate(style, _ => typeface, (_, _) => typeface);
}
public SKTypeface? Match(SKFontStyle target)
{
SKFontStyle? bestStyle = null;
SKTypeface? bestTypeface = null;
foreach (var entry in Styles)
{
if (IsBetterMatch(target, entry.Key, bestStyle))
{
bestStyle = entry.Key;
bestTypeface = entry.Value;
}
}
return bestTypeface;
}
private static Dictionary<SKFontStyleSlant, List<SKFontStyleSlant>> SlantFallbacks = new()
{
{ SKFontStyleSlant.Italic, new() { SKFontStyleSlant.Italic, SKFontStyleSlant.Oblique, SKFontStyleSlant.Upright } },
{ SKFontStyleSlant.Oblique, new() { SKFontStyleSlant.Oblique, SKFontStyleSlant.Italic, SKFontStyleSlant.Upright } },
{ SKFontStyleSlant.Upright, new() { SKFontStyleSlant.Upright, SKFontStyleSlant.Oblique, SKFontStyleSlant.Italic } },
};
// Checks whether style a is a better match for the target then style b. Uses the CSS font style matching algorithm
internal static bool IsBetterMatch(SKFontStyle? target, SKFontStyle? a, SKFontStyle? b)
{
// A font is better than no font
if (b == null)
return true;
if (a == null)
return false;
// First check font width
// For normal and condensed widths prefer smaller widths
// For expanded widths prefer larger widths
if (target.Width <= (int)SKFontStyleWidth.Normal)
{
if (a.Width <= target.Width && b.Width > target.Width)
return true;
if (a.Width > target.Width && b.Width <= target.Width)
return false;
}
else
{
if (a.Width >= target.Width && b.Width < target.Width)
return true;
if (a.Width < target.Width && b.Width >= target.Width)
return false;
}
// Prefer closest match
var widthDifferenceA = Math.Abs(a.Width - target.Width);
var widthDifferenceB = Math.Abs(b.Width - target.Width);
if (widthDifferenceA < widthDifferenceB)
return true;
if (widthDifferenceB < widthDifferenceA)
return false;
// Prefer closest slant based on provided fallback list
var slantFallback = SlantFallbacks[target.Slant];
var slantIndexA = slantFallback.IndexOf(a.Slant);
var slantIndexB = slantFallback.IndexOf(b.Slant);
if (slantIndexA < slantIndexB)
return true;
if (slantIndexB < slantIndexA)
return false;
// Check weight last
// For thin (<400) weights, prefer thinner weights
// For regular (400-500) weights, prefer other regular weights, then use rule for thin or bold
// For bold (>500) weights, prefer thicker weights
// Behavior for values other than multiples of 100 is not given in the specification
if (target.Weight >= 400 && target.Weight <= 500)
{
if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500))
return true;
if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500))
return false;
}
if (target.Weight < 450)
{
if (a.Weight <= target.Weight && b.Weight > target.Weight)
return true;
if (a.Weight > target.Weight && b.Weight <= target.Weight)
return false;
}
else
{
if (a.Weight >= target.Weight && b.Weight < target.Weight)
return true;
if (a.Weight < target.Weight && b.Weight >= target.Weight)
return false;
}
// Prefer closest weight
var weightDifferenceA = Math.Abs(a.Weight - target.Weight);
var weightDifferenceB = Math.Abs(b.Weight - target.Weight);
return weightDifferenceA < weightDifferenceB;
}
}
}
+9 -4
View File
@@ -36,7 +36,12 @@ namespace QuestPDF.Drawing
}
public void DrawRectangle(Position vector, Size size, string color)
public void DrawFilledRectangle(Position vector, Size size, string color)
{
}
public void DrawStrokedRectangle(Size size, string color, float width)
{
}
@@ -51,17 +56,17 @@ namespace QuestPDF.Drawing
}
public void DrawHyperlink(string url, Size size)
public void DrawExternalLink(string url, Size size)
{
}
public void DrawSectionLink(string sectionName, Size size)
public void DrawLocationLink(string locationName, Size size)
{
}
public void DrawSection(string sectionName)
public void DrawLocation(string locationName)
{
}
+1 -1
View File
@@ -1,5 +1,5 @@
using System.IO;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Drawing
+41
View File
@@ -0,0 +1,41 @@
using System.Collections.Concurrent;
using SkiaSharp;
namespace QuestPDF.Drawing
{
internal class SkiaCache
{
private static ConcurrentDictionary<string, SKPaint> RectangleFillPaints = new ConcurrentDictionary<string, SKPaint>();
private static ConcurrentDictionary<string, SKPaint> RectangleStrokePaints = new ConcurrentDictionary<string, SKPaint>();
public static SKPaint GetRectangleFillPaint(string color)
{
return RectangleFillPaints.GetOrAdd(color, _ => Convert());
SKPaint Convert()
{
return new SKPaint
{
Color = SKColor.Parse(color),
Style = SKPaintStyle.Fill
};
}
}
public static SKPaint GetRectangleStrokePaint(string color, float width)
{
var key = $"{color}|{width}";
return RectangleStrokePaints.GetOrAdd(key, _ => Convert());
SKPaint Convert()
{
return new SKPaint
{
Color = SKColor.Parse(color),
Style = SKPaintStyle.Stroke,
StrokeWidth = width
};
}
}
}
}
+13 -7
View File
@@ -18,14 +18,20 @@ namespace QuestPDF.Drawing
Canvas.Translate(vector.X, vector.Y);
}
public void DrawRectangle(Position vector, Size size, string color)
public void DrawFilledRectangle(Position vector, Size size, string color)
{
if (size.Width < Size.Epsilon || size.Height < Size.Epsilon)
return;
var paint = color.ColorToPaint();
var paint = SkiaCache.GetRectangleFillPaint(color);
Canvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
}
public void DrawStrokedRectangle(Size size, string color, float width)
{
var paint = SkiaCache.GetRectangleStrokePaint(color, width);
Canvas.DrawRect(0, 0, size.Width, size.Height, paint);
}
public void DrawText(string text, Position vector, TextStyle style)
{
@@ -37,19 +43,19 @@ namespace QuestPDF.Drawing
Canvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
}
public void DrawHyperlink(string url, Size size)
public void DrawExternalLink(string url, Size size)
{
Canvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
}
public void DrawSectionLink(string sectionName, Size size)
public void DrawLocationLink(string locationName, Size size)
{
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), sectionName);
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
}
public void DrawSection(string sectionName)
public void DrawLocation(string locationName)
{
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), sectionName);
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
}
public void Rotate(float angle)
+4 -4
View File
@@ -6,16 +6,16 @@ namespace QuestPDF.Drawing
{
internal class SkiaDocumentCanvasBase : SkiaCanvasBase
{
private SKDocument? Document { get; }
private SKDocument Document { get; }
protected SkiaDocumentCanvasBase(SKDocument document)
public SkiaDocumentCanvasBase(SKDocument document)
{
Document = document;
}
~SkiaDocumentCanvasBase()
{
Document?.Dispose();
Document.Dispose();
}
public override void BeginDocument()
@@ -25,7 +25,7 @@ namespace QuestPDF.Drawing
public override void EndDocument()
{
Canvas?.Dispose();
Canvas.Dispose();
Document.Close();
Document.Dispose();
+1 -1
View File
@@ -31,7 +31,7 @@ namespace QuestPDF.Drawing
if (Type == SpacePlanType.Wrap)
return Type.ToString();
return $"{Type} (Width: {Width:N3}, Height: {Height:N3})";
return $"{Type} (Width: {Width:N2}, Height: {Height:N2})";
}
public static implicit operator Size(SpacePlan spacePlan)
+1 -1
View File
@@ -1,5 +1,5 @@
using System.IO;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Drawing
+1 -1
View File
@@ -9,7 +9,7 @@ namespace QuestPDF.Elements
internal override void Draw(Size availableSpace)
{
Canvas.DrawRectangle(Position.Zero, availableSpace, Color);
Canvas.DrawFilledRectangle(Position.Zero, availableSpace, Color);
base.Draw(availableSpace);
}
}
+42 -22
View File
@@ -1,3 +1,4 @@
using System;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
@@ -15,31 +16,50 @@ namespace QuestPDF.Elements
internal override void Draw(Size availableSpace)
{
base.Draw(availableSpace);
Canvas.DrawRectangle(
new Position(-Left/2, -Top/2),
new Size(availableSpace.Width + Left/2 + Right/2, Top),
Color);
Canvas.DrawRectangle(
new Position(-Left/2, -Top/2),
new Size(Left, availableSpace.Height + Top/2 + Bottom/2),
Color);
Canvas.DrawRectangle(
new Position(-Left/2, availableSpace.Height-Bottom/2),
new Size(availableSpace.Width + Left/2 + Right/2, Bottom),
Color);
Canvas.DrawRectangle(
new Position(availableSpace.Width-Right/2, -Top/2),
new Size(Right, availableSpace.Height + Top/2 + Bottom/2),
Color);
if (HasEqualWidthOnAllSides())
{
Canvas.DrawStrokedRectangle(availableSpace, Color, Top);
}
else
{
DrawCustomRectangle(availableSpace);
}
}
public override string ToString()
private void DrawCustomRectangle(Size size)
{
return $"Border: Top({Top}) Right({Right}) Bottom({Bottom}) Left({Left}) Color({Color})";
Canvas.DrawFilledRectangle(
new Position(-Left / 2, -Top / 2),
new Size(size.Width + Left / 2 + Right / 2, Top),
Color);
Canvas.DrawFilledRectangle(
new Position(-Left / 2, -Top / 2),
new Size(Left, size.Height + Top / 2 + Bottom / 2),
Color);
Canvas.DrawFilledRectangle(
new Position(-Left / 2, size.Height - Bottom / 2),
new Size(size.Width + Left / 2 + Right / 2, Bottom),
Color);
Canvas.DrawFilledRectangle(
new Position(size.Width - Right / 2, -Top / 2),
new Size(Right, size.Height + Top / 2 + Bottom / 2),
Color);
}
private bool HasEqualWidthOnAllSides()
{
return IsClose(Top, Bottom) &&
IsClose(Left, Right) &&
IsClose(Top, Left);
}
private static bool IsClose(float x, float y)
{
return Math.Abs(x - y) < Size.Epsilon;
}
}
}
@@ -3,7 +3,7 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class MinimalBox : ContainerElement
internal class Box : ContainerElement
{
internal override void Draw(Size availableSpace)
{
-123
View File
@@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class ColumnItem : Container
{
public bool IsRendered { get; set; }
}
internal class ColumnItemRenderingCommand
{
public ColumnItem ColumnItem { get; set; }
public SpacePlan Measurement { get; set; }
public Size Size { get; set; }
public Position Offset { get; set; }
}
internal class Column : Element, ICacheable, IStateResettable
{
internal List<ColumnItem> Items { get; } = new();
internal float Spacing { get; set; }
public void ResetState()
{
Items.ForEach(x => x.IsRendered = false);
}
internal override IEnumerable<Element?> GetChildren()
{
return Items;
}
internal override void CreateProxy(Func<Element?, Element?> create)
{
Items.ForEach(x => x.Child = create(x.Child));
}
internal override SpacePlan Measure(Size availableSpace)
{
if (!Items.Any())
return SpacePlan.FullRender(Size.Zero);
var renderingCommands = PlanLayout(availableSpace);
if (!renderingCommands.Any())
return SpacePlan.Wrap();
var width = renderingCommands.Max(x => x.Size.Width);
var height = renderingCommands.Last().Offset.Y + renderingCommands.Last().Size.Height;
var size = new Size(width, height);
if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
return SpacePlan.Wrap();
var totalRenderedItems = Items.Count(x => x.IsRendered) + renderingCommands.Count(x => x.Measurement.Type == SpacePlanType.FullRender);
var willBeFullyRendered = totalRenderedItems == Items.Count;
return willBeFullyRendered
? SpacePlan.FullRender(size)
: SpacePlan.PartialRender(size);
}
internal override void Draw(Size availableSpace)
{
var renderingCommands = PlanLayout(availableSpace);
foreach (var command in renderingCommands)
{
if (command.Measurement.Type == SpacePlanType.FullRender)
command.ColumnItem.IsRendered = true;
var targetSize = new Size(availableSpace.Width, command.Size.Height);
Canvas.Translate(command.Offset);
command.ColumnItem.Draw(targetSize);
Canvas.Translate(command.Offset.Reverse());
}
if (Items.All(x => x.IsRendered))
ResetState();
}
private ICollection<ColumnItemRenderingCommand> PlanLayout(Size availableSpace)
{
var topOffset = 0f;
var commands = new List<ColumnItemRenderingCommand>();
foreach (var item in Items)
{
if (item.IsRendered)
continue;
var itemSpace = new Size(availableSpace.Width, availableSpace.Height - topOffset);
var measurement = item.Measure(itemSpace);
if (measurement.Type == SpacePlanType.Wrap)
break;
commands.Add(new ColumnItemRenderingCommand
{
ColumnItem = item,
Size = measurement,
Measurement = measurement,
Offset = new Position(0, topOffset)
});
if (measurement.Type == SpacePlanType.PartialRender)
break;
topOffset += measurement.Height + Spacing;
}
var targetWidth = commands.Select(x => x.Size.Width).DefaultIfEmpty(0).Max();
commands.ForEach(x => x.Size = new Size(targetWidth, x.Size.Height));
return commands;
}
}
}
+2 -5
View File
@@ -27,13 +27,10 @@ namespace QuestPDF.Elements
.Layer()
.ShowIf(!string.IsNullOrWhiteSpace(Text))
.AlignCenter()
.MinimalBox()
.Box()
.Background(Colors.White)
.Padding(2)
.Text(Text)
.FontColor(Color)
.FontFamily(Fonts.Consolas)
.FontSize(8);
.Text(Text, TextStyle.Default.Color(Color).FontType(Fonts.Consolas).Size(8));
});
}
}
+64 -77
View File
@@ -1,112 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using QuestPDF.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class DecorationItemRenderingCommand
internal enum DecorationType
{
public Element Element { get; set; }
public SpacePlan Measurement { get; set; }
public Position Offset { get; set; }
Prepend,
Append
}
internal class Decoration : Element, ICacheable
internal class BinaryDecoration : Element, ICacheable
{
internal Element Before { get; set; } = new Empty();
internal Element Content { get; set; } = new Empty();
internal Element After { get; set; } = new Empty();
public Element DecorationElement { get; set; } = Empty.Instance;
public Element ContentElement { get; set; } = Empty.Instance;
public DecorationType Type { get; set; }
internal override IEnumerable<Element?> GetChildren()
internal override void HandleVisitor(Action<Element?> visit)
{
yield return Before;
yield return Content;
yield return After;
DecorationElement?.HandleVisitor(visit);
ContentElement?.HandleVisitor(visit);
base.HandleVisitor(visit);
}
internal override void CreateProxy(Func<Element?, Element?> create)
internal override void CreateProxy(Func<Element, Element> create)
{
Before = create(Before);
Content = create(Content);
After = create(After);
DecorationElement = create(DecorationElement);
ContentElement = create(ContentElement);
}
internal override SpacePlan Measure(Size availableSpace)
{
var renderingCommands = PlanLayout(availableSpace).ToList();
if (renderingCommands.Any(x => x.Measurement.Type == SpacePlanType.Wrap))
var decorationMeasure = DecorationElement.Measure(availableSpace);
if (decorationMeasure.Type == SpacePlanType.Wrap || decorationMeasure.Type == SpacePlanType.PartialRender)
return SpacePlan.Wrap();
var width = renderingCommands.Max(x => x.Measurement.Width);
var height = renderingCommands.Sum(x => x.Measurement.Height);
var size = new Size(width, height);
var decorationSize = decorationMeasure;
var contentMeasure = ContentElement.Measure(new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height));
if (width > availableSpace.Width + Size.Epsilon || height > availableSpace.Height + Size.Epsilon)
if (contentMeasure.Type == SpacePlanType.Wrap)
return SpacePlan.Wrap();
var willBeFullyRendered = renderingCommands.All(x => x.Measurement.Type == SpacePlanType.FullRender);
return willBeFullyRendered
? SpacePlan.FullRender(size)
: SpacePlan.PartialRender(size);
var contentSize = contentMeasure;
var resultSize = new Size(availableSpace.Width, decorationSize.Height + contentSize.Height);
if (contentSize.Type == SpacePlanType.PartialRender)
return SpacePlan.PartialRender(resultSize);
if (contentSize.Type == SpacePlanType.FullRender)
return SpacePlan.FullRender(resultSize);
throw new NotSupportedException();
}
internal override void Draw(Size availableSpace)
{
var renderingCommands = PlanLayout(availableSpace).ToList();
var width = renderingCommands.Max(x => x.Measurement.Width);
foreach (var command in renderingCommands)
{
var elementSize = new Size(width, command.Measurement.Height);
Canvas.Translate(command.Offset);
command.Element.Draw(elementSize);
Canvas.Translate(command.Offset.Reverse());
}
var decorationSize = DecorationElement.Measure(availableSpace);
var contentSize = new Size(availableSpace.Width, availableSpace.Height - decorationSize.Height);
var translateHeight = Type == DecorationType.Prepend ? decorationSize.Height : contentSize.Height;
Action drawDecoration = () => DecorationElement?.Draw(new Size(availableSpace.Width, decorationSize.Height));
Action drawContent = () => ContentElement?.Draw(new Size (availableSpace.Width, contentSize.Height));
var first = Type == DecorationType.Prepend ? drawDecoration : drawContent;
var second = Type == DecorationType.Prepend ? drawContent : drawDecoration;
first();
Canvas.Translate(new Position(0, translateHeight));
second();
Canvas.Translate(new Position(0, -translateHeight));
}
}
internal class Decoration : IComponent
{
public Element Header { get; set; } = Empty.Instance;
public Element Content { get; set; } = Empty.Instance;
public Element Footer { get; set; } = Empty.Instance;
private IEnumerable<DecorationItemRenderingCommand> PlanLayout(Size availableSpace)
public void Compose(IContainer container)
{
SpacePlan GetDecorationMeasurement(Element element)
container.Element(new BinaryDecoration
{
var measurement = element.Measure(availableSpace);
return measurement.Type == SpacePlanType.FullRender
? measurement
: SpacePlan.Wrap();
}
var beforeMeasurement = GetDecorationMeasurement(Before);
var afterMeasurement = GetDecorationMeasurement(After);
var contentSpace = new Size(availableSpace.Width, availableSpace.Height - beforeMeasurement.Height - afterMeasurement.Height);
var contentMeasurement = Content.Measure(contentSpace);
yield return new DecorationItemRenderingCommand
{
Element = Before,
Measurement = beforeMeasurement,
Offset = Position.Zero
};
yield return new DecorationItemRenderingCommand
{
Element = Content,
Measurement = contentMeasurement,
Offset = new Position(0, beforeMeasurement.Height)
};
yield return new DecorationItemRenderingCommand
{
Element = After,
Measurement = afterMeasurement,
Offset = new Position(0, beforeMeasurement.Height + contentMeasurement.Height)
};
Type = DecorationType.Prepend,
DecorationElement = Header,
ContentElement = new BinaryDecoration
{
Type = DecorationType.Append,
ContentElement = Content,
DecorationElement = Footer
}
});
}
}
}
-10
View File
@@ -1,10 +0,0 @@
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class DefaultTextStyle : ContainerElement
{
public TextStyle TextStyle { get; set; } = TextStyle.Default;
}
}
@@ -3,7 +3,7 @@ using QuestPDF.Infrastructure;
namespace QuestPDF.Elements
{
internal class Hyperlink : ContainerElement
internal class ExternalLink : ContainerElement
{
public string Url { get; set; } = "https://www.questpdf.com";
@@ -14,7 +14,7 @@ namespace QuestPDF.Elements
if (targetSize.Type == SpacePlanType.Wrap)
return;
Canvas.DrawHyperlink(Url, targetSize);
Canvas.DrawExternalLink(Url, targetSize);
base.Draw(availableSpace);
}
}
+6 -6
View File
@@ -27,12 +27,12 @@ namespace QuestPDF.Elements
{
ChildrenQueue = new Queue<GridElement>(Children);
container.Column(column =>
container.Stack(stack =>
{
column.Spacing(VerticalSpacing);
stack.Spacing(VerticalSpacing);
while (ChildrenQueue.Any())
column.Item().Row(BuildRow);
stack.Item().Row(BuildRow);
});
}
@@ -65,12 +65,12 @@ namespace QuestPDF.Elements
emptySpace /= 2;
if (hasEmptySpace && Alignment != HorizontalAlignment.Left)
row.RelativeItem(emptySpace);
row.RelativeColumn(emptySpace);
elements.ForEach(x => row.RelativeItem(x.Columns).Element(x.Child));
elements.ForEach(x => row.RelativeColumn(x.Columns).Element(x.Child));
if (hasEmptySpace && Alignment != HorizontalAlignment.Right)
row.RelativeItem(emptySpace);
row.RelativeColumn(emptySpace);
}
}
}
+49 -35
View File
@@ -8,7 +8,15 @@ namespace QuestPDF.Elements
{
internal class InlinedElement : Container
{
public SpacePlan? MeasureCache { get; set; }
internal override SpacePlan Measure(Size availableSpace)
{
// TODO: once element caching proxy is introduces, this can be removed
MeasureCache ??= Child.Measure(Size.Max);
return MeasureCache.Value;
}
}
internal enum InlinedAlignment
@@ -36,11 +44,12 @@ namespace QuestPDF.Elements
ChildrenQueue = new Queue<InlinedElement>(Elements);
}
internal override IEnumerable<Element?> GetChildren()
internal override void HandleVisitor(Action<Element?> visit)
{
return Elements;
Elements.ForEach(x => x.HandleVisitor(visit));
base.HandleVisitor(visit);
}
internal override SpacePlan Measure(Size availableSpace)
{
if (!ChildrenQueue.Any())
@@ -104,11 +113,8 @@ namespace QuestPDF.Elements
foreach (var element in elements)
{
var size = (Size)element.Measure(Size.Max);
var size = element.Measure(Size.Max);
var baselineOffset = BaselineOffset(size, lineSize.Height);
if (size.Height == 0)
size = new Size(size.Width, lineSize.Height);
Canvas.Translate(new Position(0, baselineOffset));
element.Draw(size);
@@ -127,39 +133,48 @@ namespace QuestPDF.Elements
if (elements.Count == 1)
return 0;
return ElementsAlignment switch
{
InlinedAlignment.Justify => difference / (elements.Count - 1),
InlinedAlignment.SpaceAround => difference / (elements.Count + 1),
_ => HorizontalSpacing
};
if (ElementsAlignment == InlinedAlignment.Justify)
return difference / (elements.Count - 1);
if (ElementsAlignment == InlinedAlignment.SpaceAround)
return difference / (elements.Count + 1);
return HorizontalSpacing;
}
float AlignOffset()
{
var difference = availableSpace.Width - lineSize.Width - (elements.Count - 1) * HorizontalSpacing;
if (ElementsAlignment == InlinedAlignment.Left)
return 0;
if (ElementsAlignment == InlinedAlignment.Justify)
return 0;
if (ElementsAlignment == InlinedAlignment.SpaceAround)
return elementOffset;
return ElementsAlignment switch
{
InlinedAlignment.Left => 0,
InlinedAlignment.Justify => 0,
InlinedAlignment.SpaceAround => elementOffset,
InlinedAlignment.Center => difference / 2,
InlinedAlignment.Right => difference,
_ => 0
};
var difference = availableSpace.Width - lineSize.Width - (elements.Count - 1) * HorizontalSpacing;
if (ElementsAlignment == InlinedAlignment.Center)
return difference / 2;
if (ElementsAlignment == InlinedAlignment.Right)
return difference;
return 0;
}
float BaselineOffset(Size elementSize, float lineHeight)
{
var difference = lineHeight - elementSize.Height;
if (BaselineAlignment == VerticalAlignment.Top)
return 0;
return BaselineAlignment switch
{
VerticalAlignment.Top => 0,
VerticalAlignment.Middle => difference / 2,
_ => difference
};
var difference = lineHeight - elementSize.Height;
if (BaselineAlignment == VerticalAlignment.Middle)
return difference / 2;
return difference;
}
}
}
@@ -236,12 +251,11 @@ namespace QuestPDF.Elements
float GetInitialAlignmentOffset()
{
// this method makes sure that the spacing between elements is no lesser than configured
if (ElementsAlignment == InlinedAlignment.SpaceAround)
return HorizontalSpacing * 2;
return ElementsAlignment switch
{
InlinedAlignment.SpaceAround => HorizontalSpacing * 2,
_ => 0
};
return 0;
}
}
}

Some files were not shown because too many files have changed in this diff Show More