Compare commits
256 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5cc0397acb | |||
| 73448f483c | |||
| 7b7fac07b3 | |||
| 759655059d | |||
| fa3d912e41 | |||
| 9ce9ca85be | |||
| 553c8ab719 | |||
| e768e9b06d | |||
| fd913a777c | |||
| ed9e6daec5 | |||
| ee6249a658 | |||
| b307304f46 | |||
| f028f82e11 | |||
| 719a3385f6 | |||
| 2adff11400 | |||
| 34fed6d547 | |||
| db2df75624 | |||
| 6b535752df | |||
| 556f87ff25 | |||
| fbebbd85eb | |||
| 4650c2a4ea | |||
| 5390fc3f1b | |||
| 1ba01a1cf5 | |||
| d4448437ac | |||
| bc853f48fc | |||
| 0468dd5f02 | |||
| 3ba09ea826 | |||
| 92abd32aae | |||
| e17867d1f3 | |||
| bd71f30c78 | |||
| 6a78a4ebdb | |||
| 9fc721d66b | |||
| 7d62dead86 | |||
| 52ad0f5c24 | |||
| 53107073a6 | |||
| c71bb3ea59 | |||
| 6cced3c143 | |||
| 425ea59cfe | |||
| 4d326496c6 | |||
| b7b6488d16 | |||
| f0ba5fc32d | |||
| 04da32e0e7 | |||
| 5bdb338996 | |||
| 06b30f2ad6 | |||
| d39f68a974 | |||
| db9d8b5fcf | |||
| a31a588b24 | |||
| cb2a3c6363 | |||
| e775f62fab | |||
| 056e3f244c | |||
| cd3f7f5a25 | |||
| a5805f6e43 | |||
| abe7b2f14c | |||
| 3e485a572c | |||
| fd0bb5b089 | |||
| cc51b62b73 | |||
| 8e16b5ad4f | |||
| 71e2743409 | |||
| ba9d3aa04b | |||
| 2a0b133185 | |||
| 6058dac524 | |||
| 25b8f62245 | |||
| 2c31f3d5b5 | |||
| f7cd89ccf6 | |||
| 6914a9063a | |||
| b87e8d5d4e | |||
| d740d67a64 | |||
| a14e9ca61f | |||
| 44b3ff8d0d | |||
| df6b88e14a | |||
| d4103797aa | |||
| d894914e7e | |||
| 90d1ce7af7 | |||
| 5fa32a06cc | |||
| acdf1ed1aa | |||
| a0b32e5ecf | |||
| 539dc83303 | |||
| d97fa2cef1 | |||
| 6d122c784d | |||
| 28743a81d1 | |||
| 1047854f77 | |||
| c5fbf7263c | |||
| fae2205ff8 | |||
| 1cbe4b1dc8 | |||
| 32b94cea43 | |||
| 0ca55a91e0 | |||
| 09e6b5253f | |||
| bacb136a3f | |||
| b7bc640e31 | |||
| 6e6f884a5f | |||
| b47a8ac409 | |||
| e51082400c | |||
| 52cfa3ab7e | |||
| cd181b1e2e | |||
| e71601ad0c | |||
| 3041aa53cc | |||
| 0ebf2833af | |||
| 25e6d9fbca | |||
| f80149792d | |||
| 2beee1d22e | |||
| 1af7c0a144 | |||
| eeca67e5bc | |||
| 4da3ef5dfa | |||
| f3d4151273 | |||
| 9a7e397db2 | |||
| d81f21e658 | |||
| 78909c6edf | |||
| a2d81b6adf | |||
| 4a761c023f | |||
| 24bbd06dff | |||
| 44bd646bab | |||
| 5f0291b2c4 | |||
| 1b91015d8f | |||
| 2b6444463d | |||
| a2629575e8 | |||
| c8824021cc | |||
| e1101d870d | |||
| 152304b467 | |||
| 8a1be90c4f | |||
| bc8a9e5dc5 | |||
| e54e09de15 | |||
| c49a424f87 | |||
| c63b0658f3 | |||
| 82cd1d7835 | |||
| 3ffab6e933 | |||
| e4d55e0298 | |||
| a6f4fd7f49 | |||
| 048a4748b2 | |||
| 8cc2fb8696 | |||
| 8e7b528cd9 | |||
| 26193313b8 | |||
| 555825e8be | |||
| ddde8e7517 | |||
| d2c82b5988 | |||
| d82aae95b5 | |||
| 5c11a2ab6a | |||
| 03e3a80fd9 | |||
| 4692c5ce62 | |||
| 919f916219 | |||
| 7a32f774a1 | |||
| fba71e6392 | |||
| e27585a504 | |||
| 9be497ee41 | |||
| 0fc0db55d2 | |||
| 9e7a1ed49d | |||
| c137bcfcde | |||
| b80b3414d8 | |||
| 5f7cee3420 | |||
| 435c42ab9e | |||
| 7b4923b45d | |||
| 8732b7e50b | |||
| 6b31322f92 | |||
| 6155d21207 | |||
| a0909eb057 | |||
| 11ff53946d | |||
| 0711bca1e1 | |||
| 297ca48c70 | |||
| d2601e9e5a | |||
| 421a926780 | |||
| 307759d1b3 | |||
| 00bcb2c25b | |||
| e430484e85 | |||
| d24d887a3a | |||
| 404914ea7e | |||
| 545b76fd40 | |||
| c801894ec4 | |||
| c21744e7de | |||
| 734d116bfe | |||
| 0abb67b748 | |||
| 828bb86737 | |||
| 040c47e017 | |||
| ff95fe287a | |||
| 47e9a49fe5 | |||
| b2826a06b8 | |||
| 321cba32ce | |||
| 26fdbb714d | |||
| f0cec8988a | |||
| ea2102fe4c | |||
| 6cca3ed057 | |||
| 9afe4ec104 | |||
| 846679007f | |||
| 0946607154 | |||
| d5f86e0d70 | |||
| 346513ee85 | |||
| d18ee5f282 | |||
| a85bc0abc1 | |||
| 82e39df572 | |||
| 1311b6db32 | |||
| b5645bfbd5 | |||
| 8217b3b54a | |||
| 726759f47a | |||
| bbd9c3379e | |||
| 697c5e238d | |||
| 21a92be87e | |||
| b0cca1ff48 | |||
| 78c5049eda | |||
| 3d0ca07b43 | |||
| a67fa754af | |||
| 208235ad1e | |||
| f24416474a | |||
| 01c2e8d3f5 | |||
| b3cef238ec | |||
| aede62c135 | |||
| db9db95dcc | |||
| 92263685d7 | |||
| ba4be521aa | |||
| f6967dfe8b | |||
| ff3dfe7831 | |||
| 5a1990d857 | |||
| 1b9b164583 | |||
| 9c64a6bc8d | |||
| 5402e5fedc | |||
| 2c14f91ebe | |||
| b533a32b61 | |||
| 262bd3c182 | |||
| fdf8e3eb1a | |||
| fcff989ab2 | |||
| 054e7a5f20 | |||
| a92cc25fb1 | |||
| 8db283d559 | |||
| 456c7a735b | |||
| 5bf2a9a3d3 | |||
| 8a64abaac2 | |||
| 68df390d0e | |||
| d73c694452 | |||
| 69e268733c | |||
| 657bbe2aac | |||
| 4479a49ad0 | |||
| 63288dfb60 | |||
| fc325d8b76 | |||
| 5ffeaa3075 | |||
| fe431fbc12 | |||
| e0de408821 | |||
| 8ce6afef5a | |||
| 1c70b20028 | |||
| 3aef4ec4c9 | |||
| 3649607bd9 | |||
| 9a6b11ab85 | |||
| ac29ed4794 | |||
| 5f7da8b5b3 | |||
| 78eef45ce2 | |||
| 5424d9f0a0 | |||
| 946fe91a98 | |||
| b5839d3989 | |||
| 2ca6d1cb47 | |||
| 49a3371af7 | |||
| 3f3ada6d8a | |||
| b96c67a8af | |||
| b9fbb9267f | |||
| 272b0fc11a | |||
| b2cc7bbd36 | |||
| f8057a840c | |||
| a2ce31022c | |||
| ba8b35b1c0 | |||
| 648a359d15 | |||
| cb493562fc |
+131
@@ -0,0 +1,131 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
github: QuestPDF
|
||||||
@@ -13,11 +13,11 @@ namespace QuestPDF.Examples
|
|||||||
[Test]
|
[Test]
|
||||||
public void Example()
|
public void Example()
|
||||||
{
|
{
|
||||||
FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
|
FontManager.RegisterFont(File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||||
|
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
.PageSize(400, 100)
|
.PageSize(400, 200)
|
||||||
.ShowResults()
|
.ShowResults()
|
||||||
.Render(container =>
|
.Render(container =>
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,9 @@ namespace QuestPDF.Examples
|
|||||||
.Background(Colors.White)
|
.Background(Colors.White)
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.AlignMiddle()
|
.AlignMiddle()
|
||||||
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
|
.Text("*QuestPDF*")
|
||||||
|
.FontFamily("Libre Barcode 39")
|
||||||
|
.FontSize(64);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ namespace QuestPDF.Examples
|
|||||||
.PrimaryLayer()
|
.PrimaryLayer()
|
||||||
.PaddingVertical(10)
|
.PaddingVertical(10)
|
||||||
.PaddingHorizontal(20)
|
.PaddingHorizontal(20)
|
||||||
.Text("Sample text", TextStyle.Default.Size(16).Color(Colors.Blue.Darken2).SemiBold());
|
.Text("Sample text")
|
||||||
|
.FontSize(16)
|
||||||
|
.FontColor(Colors.Blue.Darken2)
|
||||||
|
.SemiBold();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,14 +52,17 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Background(Colors.White)
|
.Background(Colors.White)
|
||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.PaddingBottom(10)
|
.PaddingBottom(10)
|
||||||
.Text("Chart example", TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium));
|
.Text("Chart example")
|
||||||
|
.FontSize(20)
|
||||||
|
.SemiBold()
|
||||||
|
.FontColor(Colors.Blue.Medium);
|
||||||
|
|
||||||
stack
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.ExtendHorizontal()
|
.ExtendHorizontal()
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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 => { });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ namespace QuestPDF.Examples
|
|||||||
.PageSize(PageSizes.A4)
|
.PageSize(PageSizes.A4)
|
||||||
.ProducePdf()
|
.ProducePdf()
|
||||||
.ShowResults()
|
.ShowResults()
|
||||||
.Render(x => GenerateStructure(x, 16));
|
.Render(x => GenerateStructure(x, 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateStructure(IContainer container, int level)
|
private void GenerateStructure(IContainer container, int level)
|
||||||
@@ -36,8 +36,8 @@ namespace QuestPDF.Examples
|
|||||||
.BorderColor(Colors.Black)
|
.BorderColor(Colors.Black)
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().Element(x => GenerateStructure(x, level));
|
row.RelativeItem().Element(x => GenerateStructure(x, level));
|
||||||
row.RelativeColumn().Element(x => GenerateStructure(x, level));
|
row.RelativeItem().Element(x => GenerateStructure(x, level));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -45,10 +45,10 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Border(level / 4f)
|
.Border(level / 4f)
|
||||||
.BorderColor(Colors.Black)
|
.BorderColor(Colors.Black)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Element(x => GenerateStructure(x, level));
|
column.Item().Element(x => GenerateStructure(x, level));
|
||||||
stack.Item().Element(x => GenerateStructure(x, level));
|
column.Item().Element(x => GenerateStructure(x, level));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
page.Header().Text("Header");
|
page.Header().Text("Header");
|
||||||
|
|
||||||
page.Content().PaddingVertical(10).Border(1).Padding(10).Stack(stack =>
|
page.Content().PaddingVertical(10).Border(1).Padding(10).Column(column =>
|
||||||
{
|
{
|
||||||
foreach (var index in Enumerable.Range(1, 100))
|
foreach (var index in Enumerable.Range(1, 100))
|
||||||
stack.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
|
column.Item().Text($"Line {index}").FontColor(Placeholders.Color());
|
||||||
});
|
});
|
||||||
|
|
||||||
page.Footer().Text("Footer");
|
page.Footer().Text("Footer");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace QuestPDF.Examples
|
|||||||
public class DebuggingTesting
|
public class DebuggingTesting
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void Stack()
|
public void Column()
|
||||||
{
|
{
|
||||||
Assert.Throws<DocumentLayoutException>(() =>
|
Assert.Throws<DocumentLayoutException>(() =>
|
||||||
{
|
{
|
||||||
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
|
|||||||
.Width(100)
|
.Width(100)
|
||||||
.Background(Colors.Grey.Lighten3)
|
.Background(Colors.Grey.Lighten3)
|
||||||
.DebugPointer("Example debug pointer")
|
.DebugPointer("Example debug pointer")
|
||||||
.Stack(x =>
|
.Column(x =>
|
||||||
{
|
{
|
||||||
x.Item().Text("Test");
|
x.Item().Text("Test");
|
||||||
x.Item().Width(150);
|
x.Item().Width(150);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
.ProduceImages()
|
.ProducePdf()
|
||||||
.ShowResults()
|
.ShowResults()
|
||||||
.RenderDocument(container =>
|
.RenderDocument(container =>
|
||||||
{
|
{
|
||||||
@@ -24,13 +24,13 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
page.Margin(20);
|
page.Margin(20);
|
||||||
page.Size(PageSizes.A4);
|
page.Size(PageSizes.A4);
|
||||||
page.Background(Colors.White);
|
page.PageColor(Colors.White);
|
||||||
|
|
||||||
page.Content().Stack(stack =>
|
page.Content().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Text(Placeholders.Sentence());
|
column.Item().Text(Placeholders.Sentence());
|
||||||
|
|
||||||
stack.Item().Text(text =>
|
column.Item().Text(text =>
|
||||||
{
|
{
|
||||||
// text in this block is additionally semibold
|
// text in this block is additionally semibold
|
||||||
text.DefaultTextStyle(TextStyle.Default.SemiBold());
|
text.DefaultTextStyle(TextStyle.Default.SemiBold());
|
||||||
@@ -38,7 +38,7 @@ namespace QuestPDF.Examples
|
|||||||
text.Line(Placeholders.Sentence());
|
text.Line(Placeholders.Sentence());
|
||||||
|
|
||||||
// this text has size 20 but also semibold and red
|
// this text has size 20 but also semibold and red
|
||||||
text.Span(Placeholders.Sentence(), TextStyle.Default.Color(Colors.Red.Medium));
|
text.Span(Placeholders.Sentence()).FontColor(Colors.Red.Medium);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.DefaultTextStyle(TextStyle.Default.Bold().Underline())
|
.DefaultTextStyle(TextStyle.Default.Bold().Underline())
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Text("Default style applies to all children", TextStyle.Default);
|
column.Item().Text("Default style applies to all children");
|
||||||
stack.Item().Text("You can override certain styles", TextStyle.Default.Underline(false).Color(Colors.Green.Darken2));
|
column.Item().Text("You can override certain styles").Underline(false).FontColor(Colors.Green.Darken2);
|
||||||
|
|
||||||
stack.Item().PaddingTop(10).Border(1).Grid(grid =>
|
column.Item().PaddingTop(10).Border(1).Grid(grid =>
|
||||||
{
|
{
|
||||||
grid.Columns(4);
|
grid.Columns(4);
|
||||||
|
|
||||||
@@ -43,7 +43,8 @@ namespace QuestPDF.Examples
|
|||||||
.Height(50)
|
.Height(50)
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.AlignMiddle()
|
.AlignMiddle()
|
||||||
.Text(i, TextStyle.Default.Size(16 + i / 4));
|
.Text(i)
|
||||||
|
.FontSize(16 + i / 4);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,21 +23,21 @@ namespace QuestPDF.Examples
|
|||||||
container.Page(page =>
|
container.Page(page =>
|
||||||
{
|
{
|
||||||
page.Size(PageSizes.A6);
|
page.Size(PageSizes.A6);
|
||||||
page.Margin(30);
|
page.Margin(5);
|
||||||
page.Background(Colors.White);
|
page.PageColor(Colors.White);
|
||||||
|
|
||||||
page.Header().Stack(stack =>
|
page.Header().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
|
column.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
|
||||||
stack.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
|
column.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
|
||||||
});
|
});
|
||||||
|
|
||||||
page.Content().PaddingVertical(10).Stack(stack =>
|
page.Content().PaddingVertical(10).Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(10);
|
column.Spacing(10);
|
||||||
|
|
||||||
foreach (var _ in Enumerable.Range(0, 13))
|
foreach (var _ in Enumerable.Range(0, 13))
|
||||||
stack.Item().Background(Colors.Grey.Lighten2).Height(40);
|
column.Item().Background(Colors.Grey.Lighten2).Height(40);
|
||||||
});
|
});
|
||||||
|
|
||||||
page.Footer().AlignCenter().Text(text =>
|
page.Footer().AlignCenter().Text(text =>
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public struct FibonacciHeaderState
|
||||||
|
{
|
||||||
|
public int Previous { get; set; }
|
||||||
|
public int Current { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FibonacciHeader : IDynamicComponent<FibonacciHeaderState>
|
||||||
|
{
|
||||||
|
public FibonacciHeaderState State { get; set; }
|
||||||
|
|
||||||
|
public static readonly string[] ColorsTable =
|
||||||
|
{
|
||||||
|
Colors.Red.Lighten2,
|
||||||
|
Colors.Orange.Lighten2,
|
||||||
|
Colors.Green.Lighten2,
|
||||||
|
};
|
||||||
|
|
||||||
|
public FibonacciHeader(int previous, int current)
|
||||||
|
{
|
||||||
|
State = new FibonacciHeaderState
|
||||||
|
{
|
||||||
|
Previous = previous,
|
||||||
|
Current = current
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||||
|
{
|
||||||
|
var content = context.CreateElement(container =>
|
||||||
|
{
|
||||||
|
var colorIndex = State.Current % ColorsTable.Length;
|
||||||
|
var color = ColorsTable[colorIndex];
|
||||||
|
|
||||||
|
var ratio = (float)State.Current / State.Previous;
|
||||||
|
|
||||||
|
container
|
||||||
|
.Background(color)
|
||||||
|
.Height(50)
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.Text($"{State.Current} / {State.Previous} = {ratio:N5}");
|
||||||
|
});
|
||||||
|
|
||||||
|
State = new FibonacciHeaderState
|
||||||
|
{
|
||||||
|
Previous = State.Current,
|
||||||
|
Current = State.Previous + State.Current
|
||||||
|
};
|
||||||
|
|
||||||
|
return new DynamicComponentComposeResult
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
HasMoreContent = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DynamicFibonacci
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public static void Dynamic()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ShowResults()
|
||||||
|
.MaxPages(100)
|
||||||
|
.ProduceImages()
|
||||||
|
.RenderDocument(container =>
|
||||||
|
{
|
||||||
|
container.Page(page =>
|
||||||
|
{
|
||||||
|
page.Size(PageSizes.A6);
|
||||||
|
page.PageColor(Colors.White);
|
||||||
|
page.Margin(1, Unit.Centimetre);
|
||||||
|
page.DefaultTextStyle(x => x.FontSize(18));
|
||||||
|
|
||||||
|
page.Header().Dynamic(new FibonacciHeader(17, 19));
|
||||||
|
|
||||||
|
page.Content().Column(column =>
|
||||||
|
{
|
||||||
|
foreach (var i in Enumerable.Range(0, 50))
|
||||||
|
column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
page.Footer().AlignCenter().Text(text =>
|
||||||
|
{
|
||||||
|
text.CurrentPageNumber();
|
||||||
|
text.Span(" / ");
|
||||||
|
text.TotalPages();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class OrderItem
|
||||||
|
{
|
||||||
|
public string ItemName { get; set; } = Placeholders.Label();
|
||||||
|
public int Price { get; set; } = Placeholders.Random.Next(1, 11) * 10;
|
||||||
|
public int Count { get; set; } = Placeholders.Random.Next(1, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct OrdersTableState
|
||||||
|
{
|
||||||
|
public int ShownItemsCount { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OptimizedOrdersTable : IDynamicComponent<OrdersTableState>
|
||||||
|
{
|
||||||
|
private ICollection<OrderItem> Items { get; }
|
||||||
|
public OrdersTableState State { get; set; }
|
||||||
|
|
||||||
|
public OptimizedOrdersTable(ICollection<OrderItem> items)
|
||||||
|
{
|
||||||
|
Items = items;
|
||||||
|
|
||||||
|
State = new OrdersTableState
|
||||||
|
{
|
||||||
|
ShownItemsCount = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||||
|
{
|
||||||
|
var header = ComposeHeader(context);
|
||||||
|
var sampleFooter = ComposeFooter(context, Enumerable.Empty<OrderItem>());
|
||||||
|
var decorationHeight = header.Size.Height + sampleFooter.Size.Height;
|
||||||
|
|
||||||
|
var rows = GetItemsForPage(context, decorationHeight).ToList();
|
||||||
|
var footer = ComposeFooter(context, rows.Select(x => x.Item));
|
||||||
|
|
||||||
|
var content = context.CreateElement(container =>
|
||||||
|
{
|
||||||
|
container.MinimalBox().Decoration(decoration =>
|
||||||
|
{
|
||||||
|
decoration.Header().Element(header);
|
||||||
|
|
||||||
|
decoration.Content().Box().Stack(stack =>
|
||||||
|
{
|
||||||
|
foreach (var row in rows)
|
||||||
|
stack.Item().Element(row.Element);
|
||||||
|
});
|
||||||
|
|
||||||
|
decoration.Footer().Element(footer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
State = new OrdersTableState
|
||||||
|
{
|
||||||
|
ShownItemsCount = State.ShownItemsCount + rows.Count
|
||||||
|
};
|
||||||
|
|
||||||
|
return new DynamicComponentComposeResult
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
HasMoreContent = State.ShownItemsCount < Items.Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDynamicElement ComposeHeader(DynamicContext context)
|
||||||
|
{
|
||||||
|
return context.CreateElement(element =>
|
||||||
|
{
|
||||||
|
element
|
||||||
|
.Width(context.AvailableSize.Width)
|
||||||
|
.BorderBottom(1)
|
||||||
|
.BorderColor(Colors.Grey.Darken2)
|
||||||
|
.Padding(5)
|
||||||
|
.Row(row =>
|
||||||
|
{
|
||||||
|
var textStyle = TextStyle.Default.SemiBold();
|
||||||
|
|
||||||
|
row.ConstantItem(30).Text("#", textStyle);
|
||||||
|
row.RelativeItem().Text("Item name", textStyle);
|
||||||
|
row.ConstantItem(50).AlignRight().Text("Count", textStyle);
|
||||||
|
row.ConstantItem(50).AlignRight().Text("Price", textStyle);
|
||||||
|
row.ConstantItem(50).AlignRight().Text("Total", textStyle);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDynamicElement ComposeFooter(DynamicContext context, IEnumerable<OrderItem> items)
|
||||||
|
{
|
||||||
|
var total = items.Sum(x => x.Count * x.Price);
|
||||||
|
|
||||||
|
return context.CreateElement(element =>
|
||||||
|
{
|
||||||
|
element
|
||||||
|
.Width(context.AvailableSize.Width)
|
||||||
|
.Padding(5)
|
||||||
|
.AlignRight()
|
||||||
|
.Text($"Subtotal: {total}$", TextStyle.Default.Size(14).SemiBold());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight)
|
||||||
|
{
|
||||||
|
var totalHeight = decorationHeight;
|
||||||
|
|
||||||
|
foreach (var index in Enumerable.Range(State.ShownItemsCount, Items.Count - State.ShownItemsCount))
|
||||||
|
{
|
||||||
|
var item = Items.ElementAt(index);
|
||||||
|
|
||||||
|
var element = context.CreateElement(content =>
|
||||||
|
{
|
||||||
|
content
|
||||||
|
.Width(context.AvailableSize.Width)
|
||||||
|
.BorderBottom(1)
|
||||||
|
.BorderColor(Colors.Grey.Lighten2)
|
||||||
|
.Padding(5)
|
||||||
|
.Row(row =>
|
||||||
|
{
|
||||||
|
row.ConstantItem(30).Text(index + 1);
|
||||||
|
row.RelativeItem().Text(item.ItemName);
|
||||||
|
row.ConstantItem(50).AlignRight().Text(item.Count);
|
||||||
|
row.ConstantItem(50).AlignRight().Text($"{item.Price}$");
|
||||||
|
row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var elementHeight = element.Size.Height;
|
||||||
|
|
||||||
|
if (totalHeight + elementHeight > context.AvailableSize.Height)
|
||||||
|
break;
|
||||||
|
|
||||||
|
totalHeight += elementHeight;
|
||||||
|
yield return (item, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DynamicOptimizedExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public static void Dynamic()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(PageSizes.A5)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
var items = Enumerable.Range(0, 25).Select(x => new OrderItem()).ToList();
|
||||||
|
|
||||||
|
container
|
||||||
|
.Background(Colors.White)
|
||||||
|
.Padding(25)
|
||||||
|
.Decoration(decoration =>
|
||||||
|
{
|
||||||
|
decoration
|
||||||
|
.Header()
|
||||||
|
.PaddingBottom(5)
|
||||||
|
.Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(TextStyle.Default.SemiBold().FontColor(Colors.Blue.Darken2).FontSize(16));
|
||||||
|
text.Span("Page ");
|
||||||
|
text.CurrentPageNumber();
|
||||||
|
text.Span(" of ");
|
||||||
|
text.TotalPages();
|
||||||
|
});
|
||||||
|
|
||||||
|
decoration
|
||||||
|
.Content()
|
||||||
|
.Dynamic(new OptimizedOrdersTable(items));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class FooterWithAlternatingAlignment : IDynamicComponent<int>
|
||||||
|
{
|
||||||
|
public int State { get; set; }
|
||||||
|
|
||||||
|
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||||
|
{
|
||||||
|
var content = context.CreateElement(element =>
|
||||||
|
{
|
||||||
|
element
|
||||||
|
.Element(x => context.PageNumber % 2 == 0 ? x.AlignLeft() : x.AlignRight())
|
||||||
|
.Text(x =>
|
||||||
|
{
|
||||||
|
x.CurrentPageNumber();
|
||||||
|
x.Span(" / ");
|
||||||
|
x.TotalPages();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return new DynamicComponentComposeResult()
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
HasMoreContent = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DynamicPageNumberLeftRightExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public static void Dynamic()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(PageSizes.A5)
|
||||||
|
.MaxPages(100)
|
||||||
|
.ShowResults()
|
||||||
|
.ProduceImages()
|
||||||
|
.RenderDocument(container =>
|
||||||
|
{
|
||||||
|
container.Page(page =>
|
||||||
|
{
|
||||||
|
page.Size(PageSizes.A6);
|
||||||
|
page.PageColor(Colors.White);
|
||||||
|
page.Margin(1, Unit.Centimetre);
|
||||||
|
page.DefaultTextStyle(x => x.FontSize(18));
|
||||||
|
|
||||||
|
page.Content().Column(column =>
|
||||||
|
{
|
||||||
|
foreach (var i in Enumerable.Range(0, 50))
|
||||||
|
column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
page.Footer().Dynamic(new FooterWithAlternatingAlignment());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class ProgressHeader : IDynamicComponent<int>
|
||||||
|
{
|
||||||
|
public int State { get; set; }
|
||||||
|
|
||||||
|
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||||
|
{
|
||||||
|
var content = context.CreateElement(container =>
|
||||||
|
{
|
||||||
|
var width = context.AvailableSize.Width * context.PageNumber / context.TotalPages;
|
||||||
|
|
||||||
|
container
|
||||||
|
.Background(Colors.Blue.Lighten2)
|
||||||
|
.Height(25)
|
||||||
|
.Width(width)
|
||||||
|
.Background(Colors.Blue.Darken1);
|
||||||
|
});
|
||||||
|
|
||||||
|
return new DynamicComponentComposeResult
|
||||||
|
{
|
||||||
|
Content = content,
|
||||||
|
HasMoreContent = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DynamicProgressHeader
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public static void Dynamic()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ShowResults()
|
||||||
|
.MaxPages(100)
|
||||||
|
.ProduceImages()
|
||||||
|
.RenderDocument(container =>
|
||||||
|
{
|
||||||
|
container.Page(page =>
|
||||||
|
{
|
||||||
|
page.Size(PageSizes.A6);
|
||||||
|
page.Margin(1, Unit.Centimetre);
|
||||||
|
page.PageColor(Colors.White);
|
||||||
|
page.DefaultTextStyle(x => x.FontSize(20));
|
||||||
|
|
||||||
|
page.Header().Dynamic(new ProgressHeader());
|
||||||
|
|
||||||
|
page.Content().Column(column =>
|
||||||
|
{
|
||||||
|
foreach (var i in Enumerable.Range(0, 100))
|
||||||
|
column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
page.Footer().AlignCenter().Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(x => x.FontSize(20));
|
||||||
|
|
||||||
|
text.CurrentPageNumber();
|
||||||
|
text.Span(" / ");
|
||||||
|
text.TotalPages();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class OrdersTable : IDynamicComponent<OrdersTableState>
|
||||||
|
{
|
||||||
|
private IList<OrderItem> Items { get; }
|
||||||
|
public OrdersTableState State { get; set; }
|
||||||
|
|
||||||
|
public OrdersTable(IList<OrderItem> items)
|
||||||
|
{
|
||||||
|
Items = items;
|
||||||
|
|
||||||
|
State = new OrdersTableState
|
||||||
|
{
|
||||||
|
ShownItemsCount = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||||
|
{
|
||||||
|
var possibleItems = Enumerable
|
||||||
|
.Range(1, Items.Count - State.ShownItemsCount)
|
||||||
|
.Select(itemsToDisplay => ComposeContent(context, itemsToDisplay))
|
||||||
|
.TakeWhile(x => x.Size.Height <= context.AvailableSize.Height)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
State = new OrdersTableState
|
||||||
|
{
|
||||||
|
ShownItemsCount = State.ShownItemsCount + possibleItems.Count
|
||||||
|
};
|
||||||
|
|
||||||
|
return new DynamicComponentComposeResult
|
||||||
|
{
|
||||||
|
Content = possibleItems.Last(),
|
||||||
|
HasMoreContent = State.ShownItemsCount < Items.Count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDynamicElement ComposeContent(DynamicContext context, int itemsToDisplay)
|
||||||
|
{
|
||||||
|
var total = Items.Skip(State.ShownItemsCount).Take(itemsToDisplay).Sum(x => x.Count * x.Price);
|
||||||
|
|
||||||
|
return context.CreateElement(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.MinimalBox()
|
||||||
|
.Width(context.AvailableSize.Width)
|
||||||
|
.Table(table =>
|
||||||
|
{
|
||||||
|
table.ColumnsDefinition(columns =>
|
||||||
|
{
|
||||||
|
columns.ConstantColumn(30);
|
||||||
|
columns.RelativeColumn();
|
||||||
|
columns.ConstantColumn(50);
|
||||||
|
columns.ConstantColumn(50);
|
||||||
|
columns.ConstantColumn(50);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.Header(header =>
|
||||||
|
{
|
||||||
|
header.Cell().Element(Style).Text("#");
|
||||||
|
header.Cell().Element(Style).Text("Item name");
|
||||||
|
header.Cell().Element(Style).AlignRight().Text("Count");
|
||||||
|
header.Cell().Element(Style).AlignRight().Text("Price");
|
||||||
|
header.Cell().Element(Style).AlignRight().Text("Total");
|
||||||
|
|
||||||
|
IContainer Style(IContainer container)
|
||||||
|
{
|
||||||
|
return container
|
||||||
|
.DefaultTextStyle(x => x.SemiBold())
|
||||||
|
.BorderBottom(1)
|
||||||
|
.BorderColor(Colors.Grey.Darken2)
|
||||||
|
.Padding(5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
table.Footer(footer =>
|
||||||
|
{
|
||||||
|
footer
|
||||||
|
.Cell().ColumnSpan(5)
|
||||||
|
.AlignRight()
|
||||||
|
.PaddingTop(10)
|
||||||
|
.Text($"Subtotal: {total}$", TextStyle.Default.Bold());
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var index in Enumerable.Range(State.ShownItemsCount, itemsToDisplay))
|
||||||
|
{
|
||||||
|
var item = Items[index];
|
||||||
|
|
||||||
|
table.Cell().Element(Style).Text(index + 1);
|
||||||
|
table.Cell().Element(Style).Text(item.ItemName);
|
||||||
|
table.Cell().Element(Style).AlignRight().Text(item.Count);
|
||||||
|
table.Cell().Element(Style).AlignRight().Text($"{item.Price}$");
|
||||||
|
table.Cell().Element(Style).AlignRight().Text($"{item.Count*item.Price}$");
|
||||||
|
|
||||||
|
IContainer Style(IContainer container)
|
||||||
|
{
|
||||||
|
return container
|
||||||
|
.BorderBottom(1)
|
||||||
|
.BorderColor(Colors.Grey.Lighten2)
|
||||||
|
.Padding(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DynamicSimpleTableExample
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public static void Dynamic()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(PageSizes.A5)
|
||||||
|
.ShowResults()
|
||||||
|
.ProduceImages()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
var items = Enumerable.Range(0, 15).Select(x => new OrderItem()).ToList();
|
||||||
|
|
||||||
|
container
|
||||||
|
.Background(Colors.White)
|
||||||
|
.Padding(25)
|
||||||
|
.DefaultTextStyle(x => x.FontSize(16))
|
||||||
|
.Decoration(decoration =>
|
||||||
|
{
|
||||||
|
decoration
|
||||||
|
.Header()
|
||||||
|
.PaddingBottom(5)
|
||||||
|
.Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(TextStyle.Default.SemiBold().FontColor(Colors.Blue.Darken2));
|
||||||
|
text.Span("Page ");
|
||||||
|
text.CurrentPageNumber();
|
||||||
|
text.Span(" / ");
|
||||||
|
text.TotalPages();
|
||||||
|
});
|
||||||
|
|
||||||
|
decoration
|
||||||
|
.Content()
|
||||||
|
.Dynamic(new OrdersTable(items));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,10 +40,12 @@ namespace QuestPDF.Examples
|
|||||||
.Decoration(decoration =>
|
.Decoration(decoration =>
|
||||||
{
|
{
|
||||||
decoration
|
decoration
|
||||||
.Header()
|
.Before()
|
||||||
.Background(Colors.Grey.Medium)
|
.Background(Colors.Grey.Medium)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
|
.Text("Notes")
|
||||||
|
.FontSize(16)
|
||||||
|
.FontColor("#FFF");
|
||||||
|
|
||||||
decoration
|
decoration
|
||||||
.Content()
|
.Content()
|
||||||
@@ -66,27 +68,27 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Background("#FFF")
|
.Background("#FFF")
|
||||||
.Padding(20)
|
.Padding(20)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item()
|
column.Item()
|
||||||
.PaddingBottom(10)
|
.PaddingBottom(10)
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.Text("This Row element is 700pt wide");
|
.Text("This Row element is 700pt wide");
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.ConstantColumn(100)
|
row.ConstantItem(100)
|
||||||
.Background(Colors.Grey.Lighten1)
|
.Background(Colors.Grey.Lighten1)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.ExtendVertical()
|
.ExtendVertical()
|
||||||
.Text("This column is 100 pt wide");
|
.Text("This column is 100 pt wide");
|
||||||
|
|
||||||
row.RelativeColumn()
|
row.RelativeItem()
|
||||||
.Background(Colors.Grey.Lighten2)
|
.Background(Colors.Grey.Lighten2)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text("This column takes 1/3 of the available space (200pt)");
|
.Text("This column takes 1/3 of the available space (200pt)");
|
||||||
|
|
||||||
row.RelativeColumn(2)
|
row.RelativeItem(2)
|
||||||
.Background(Colors.Grey.Lighten3)
|
.Background(Colors.Grey.Lighten3)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text("This column takes 2/3 of the available space (400pt)");
|
.Text("This column takes 2/3 of the available space (400pt)");
|
||||||
@@ -109,15 +111,15 @@ namespace QuestPDF.Examples
|
|||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.Spacing(20);
|
row.Spacing(20);
|
||||||
row.RelativeColumn(2).Border(1).Background(Colors.Grey.Lighten1);
|
row.RelativeItem(2).Border(1).Background(Colors.Grey.Lighten1);
|
||||||
row.RelativeColumn(3).Border(1).Background(Colors.Grey.Lighten2);
|
row.RelativeItem(3).Border(1).Background(Colors.Grey.Lighten2);
|
||||||
row.RelativeColumn(4).Border(1).Background(Colors.Grey.Lighten3);
|
row.RelativeItem(4).Border(1).Background(Colors.Grey.Lighten3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Stack()
|
public void column()
|
||||||
{
|
{
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
@@ -127,13 +129,13 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Background("#FFF")
|
.Background("#FFF")
|
||||||
.Padding(15)
|
.Padding(15)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(15);
|
column.Spacing(15);
|
||||||
|
|
||||||
stack.Item().Background(Colors.Grey.Medium).Height(50);
|
column.Item().Background(Colors.Grey.Medium).Height(50);
|
||||||
stack.Item().Background(Colors.Grey.Lighten1).Height(100);
|
column.Item().Background(Colors.Grey.Lighten1).Height(100);
|
||||||
stack.Item().Background(Colors.Grey.Lighten2).Height(150);
|
column.Item().Background(Colors.Grey.Lighten2).Height(150);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -267,12 +269,12 @@ namespace QuestPDF.Examples
|
|||||||
layers
|
layers
|
||||||
.PrimaryLayer()
|
.PrimaryLayer()
|
||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(5);
|
column.Spacing(5);
|
||||||
|
|
||||||
foreach (var _ in Enumerable.Range(0, 7))
|
foreach (var _ in Enumerable.Range(0, 7))
|
||||||
stack.Item().Text(Placeholders.Sentence());
|
column.Item().Text(Placeholders.Sentence());
|
||||||
});
|
});
|
||||||
|
|
||||||
// layer above the main content
|
// layer above the main content
|
||||||
@@ -280,12 +282,15 @@ namespace QuestPDF.Examples
|
|||||||
.Layer()
|
.Layer()
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.AlignMiddle()
|
.AlignMiddle()
|
||||||
.Text("Watermark", TextStyle.Default.Size(48).Bold().Color(Colors.Green.Lighten3));
|
.Text("Watermark")
|
||||||
|
.FontSize(48)
|
||||||
|
.Bold()
|
||||||
|
.FontColor(Colors.Green.Lighten3);
|
||||||
|
|
||||||
layers
|
layers
|
||||||
.Layer()
|
.Layer()
|
||||||
.AlignBottom()
|
.AlignBottom()
|
||||||
.Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
|
.Text(text => text.CurrentPageNumber().FontSize(16).FontColor(Colors.Green.Medium));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -304,16 +309,16 @@ namespace QuestPDF.Examples
|
|||||||
// {
|
// {
|
||||||
// page.Header().PageNumber("Page {pdf:currentPage}");
|
// page.Header().PageNumber("Page {pdf:currentPage}");
|
||||||
//
|
//
|
||||||
// page.Content().Height(300).Stack(content =>
|
// page.Content().Height(300).column(content =>
|
||||||
// {
|
// {
|
||||||
// content.Item().Height(200).Background(Colors.Grey.Lighten2);
|
// content.Item().Height(200).Background(Colors.Grey.Lighten2);
|
||||||
//
|
//
|
||||||
// content.Item().EnsureSpace(100).Stack(stack =>
|
// content.Item().EnsureSpace(100).column(column =>
|
||||||
// {
|
// {
|
||||||
// stack.Spacing(10);
|
// column.Spacing(10);
|
||||||
//
|
//
|
||||||
// foreach (var _ in Enumerable.Range(0, 4))
|
// foreach (var _ in Enumerable.Range(0, 4))
|
||||||
// stack.Item().Height(50).Background(Colors.Green.Lighten1);
|
// column.Item().Height(50).Background(Colors.Green.Lighten1);
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
@@ -378,7 +383,7 @@ namespace QuestPDF.Examples
|
|||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
foreach (var color in colors)
|
foreach (var color in colors)
|
||||||
row.RelativeColumn().Background(color);
|
row.RelativeItem().Background(color);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -416,7 +421,9 @@ namespace QuestPDF.Examples
|
|||||||
.Border(1)
|
.Border(1)
|
||||||
.BorderColor(Colors.Grey.Medium)
|
.BorderColor(Colors.Grey.Medium)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text(font, TextStyle.Default.FontType(font).Size(16));
|
.Text(font)
|
||||||
|
.FontFamily(font)
|
||||||
|
.FontSize(16);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -437,10 +444,10 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
layers.Layer().Text("Something else");
|
layers.Layer().Text("Something else");
|
||||||
|
|
||||||
layers.PrimaryLayer().Stack(stack =>
|
layers.PrimaryLayer().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().PaddingTop(20).Text("Text 1");
|
column.Item().PaddingTop(20).Text("Text 1");
|
||||||
stack.Item().PaddingTop(40).Text("Text 2");
|
column.Item().PaddingTop(40).Text("Text 2");
|
||||||
});
|
});
|
||||||
|
|
||||||
layers.Layer().Canvas((canvas, size) =>
|
layers.Layer().Canvas((canvas, size) =>
|
||||||
@@ -456,7 +463,7 @@ namespace QuestPDF.Examples
|
|||||||
});
|
});
|
||||||
|
|
||||||
layers.Layer().Background("#8F00").Extend();
|
layers.Layer().Background("#8F00").Extend();
|
||||||
layers.Layer().PaddingTop(40).Text("It works!", TextStyle.Default.Size(24));
|
layers.Layer().PaddingTop(40).Text("It works!").FontSize(24);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -477,7 +484,7 @@ namespace QuestPDF.Examples
|
|||||||
//.MinimalBox()
|
//.MinimalBox()
|
||||||
.Background(Colors.Grey.Lighten2)
|
.Background(Colors.Grey.Lighten2)
|
||||||
.Padding(15)
|
.Padding(15)
|
||||||
.Text("Test of the \n box element", TextStyle.Default.Size(20));
|
.Text("Test of the \n box element").FontSize(20);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,13 +508,14 @@ namespace QuestPDF.Examples
|
|||||||
.SemiBold();
|
.SemiBold();
|
||||||
|
|
||||||
decoration
|
decoration
|
||||||
.Header()
|
.Before()
|
||||||
.PaddingBottom(10)
|
.PaddingBottom(10)
|
||||||
.Text("Example: scale component", headerFontStyle);
|
.Text("Example: scale component")
|
||||||
|
.Style(headerFontStyle);
|
||||||
|
|
||||||
decoration
|
decoration
|
||||||
.Content()
|
.Content()
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
|
var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
|
||||||
|
|
||||||
@@ -519,13 +527,14 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
var fontStyle = TextStyle.Default.Size(16);
|
var fontStyle = TextStyle.Default.Size(16);
|
||||||
|
|
||||||
stack
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.Background(fontColor)
|
.Background(fontColor)
|
||||||
.Scale(scale)
|
.Scale(scale)
|
||||||
.Padding(5)
|
.Padding(5)
|
||||||
.Text($"Content with {scale} scale.", fontStyle);
|
.Text($"Content with {scale} scale.")
|
||||||
|
.Style(fontStyle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -555,7 +564,8 @@ namespace QuestPDF.Examples
|
|||||||
.BorderColor(Colors.Green.Darken1)
|
.BorderColor(Colors.Green.Darken1)
|
||||||
|
|
||||||
.Padding(50)
|
.Padding(50)
|
||||||
.Text("Moved text", TextStyle.Default.Size(25));
|
.Text("Moved text")
|
||||||
|
.FontSize(25);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,7 +601,8 @@ namespace QuestPDF.Examples
|
|||||||
.MinimalBox()
|
.MinimalBox()
|
||||||
.Background(Colors.White)
|
.Background(Colors.White)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text($"Rotated {turns * 90}°", TextStyle.Default.Size(16));
|
.Text($"Rotated {turns * 90}°")
|
||||||
|
.FontSize(16);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -689,7 +700,8 @@ namespace QuestPDF.Examples
|
|||||||
.MinimalBox()
|
.MinimalBox()
|
||||||
.Background(Colors.White)
|
.Background(Colors.White)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text($"Flipped {turns}", TextStyle.Default.Size(16));
|
.Text($"Flipped {turns}")
|
||||||
|
.FontSize(16);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -708,14 +720,14 @@ namespace QuestPDF.Examples
|
|||||||
.Border(2)
|
.Border(2)
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.ConstantColumn(25)
|
row.ConstantItem(25)
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.RotateLeft()
|
.RotateLeft()
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.AlignMiddle()
|
.AlignMiddle()
|
||||||
.Text("Sample text");
|
.Text("Sample text");
|
||||||
|
|
||||||
row.RelativeColumn().Border(1).Padding(5).Text(Placeholders.Paragraph());
|
row.RelativeItem().Border(1).Padding(5).Text(Placeholders.Paragraph());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -730,12 +742,12 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
container
|
container
|
||||||
.Padding(25)
|
.Padding(25)
|
||||||
.PaddingLeft(75)
|
.PaddingLeft(50)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
|
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
|
||||||
|
|
||||||
stack
|
column
|
||||||
.Item()
|
.Item()
|
||||||
|
|
||||||
// creates an infinite space for its child
|
// creates an infinite space for its child
|
||||||
@@ -751,7 +763,7 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
.Background(Colors.Blue.Darken1);
|
.Background(Colors.Blue.Darken1);
|
||||||
|
|
||||||
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
|
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -766,13 +778,13 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
container
|
container
|
||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().LabelCell("Label 1");
|
row.RelativeItem().LabelCell("Label 1");
|
||||||
|
|
||||||
row.RelativeColumn(3).Grid(grid =>
|
row.RelativeItem(3).Grid(grid =>
|
||||||
{
|
{
|
||||||
grid.Columns(3);
|
grid.Columns(3);
|
||||||
|
|
||||||
@@ -784,11 +796,11 @@ namespace QuestPDF.Examples
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().ValueCell().Text("Value 1");
|
row.RelativeItem().ValueCell().Text("Value 1");
|
||||||
|
|
||||||
row.RelativeColumn(3).Grid(grid =>
|
row.RelativeItem(3).Grid(grid =>
|
||||||
{
|
{
|
||||||
grid.Columns(3);
|
grid.Columns(3);
|
||||||
|
|
||||||
@@ -800,10 +812,10 @@ namespace QuestPDF.Examples
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().LabelCell("Label 6");
|
row.RelativeItem().LabelCell("Label 6");
|
||||||
row.RelativeColumn().ValueCell().Text("Value 6");
|
row.RelativeItem().ValueCell().Text("Value 6");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace QuestPDF.Examples.Engine
|
|||||||
private bool ApplyCaching { get; set; }
|
private bool ApplyCaching { get; set; }
|
||||||
private bool ApplyDebugging { get; set; }
|
private bool ApplyDebugging { get; set; }
|
||||||
private RenderingTestResult ResultType { get; set; } = RenderingTestResult.Images;
|
private RenderingTestResult ResultType { get; set; } = RenderingTestResult.Images;
|
||||||
|
|
||||||
|
private bool ShowingResultsEnabled = true;
|
||||||
|
|
||||||
private RenderingTest()
|
private RenderingTest()
|
||||||
{
|
{
|
||||||
@@ -114,7 +116,7 @@ namespace QuestPDF.Examples.Engine
|
|||||||
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
|
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
|
||||||
document.GenerateImages(fileNameSchema);
|
document.GenerateImages(fileNameSchema);
|
||||||
|
|
||||||
if (ShowResult)
|
if (ShowResult && ShowingResultsEnabled)
|
||||||
Process.Start("explorer", fileNameSchema(0));
|
Process.Start("explorer", fileNameSchema(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +125,7 @@ namespace QuestPDF.Examples.Engine
|
|||||||
var fileName = $"{FileNamePrefix}.pdf";
|
var fileName = $"{FileNamePrefix}.pdf";
|
||||||
document.GeneratePdf(fileName);
|
document.GeneratePdf(fileName);
|
||||||
|
|
||||||
if (ShowResult)
|
if (ShowResult && ShowingResultsEnabled)
|
||||||
Process.Start("explorer", fileName);
|
Process.Start("explorer", fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,19 +21,19 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
page.Margin(20);
|
page.Margin(20);
|
||||||
page.Size(PageSizes.A7.Landscape());
|
page.Size(PageSizes.A7.Landscape());
|
||||||
page.Background(Colors.White);
|
page.PageColor(Colors.White);
|
||||||
|
|
||||||
page.Header().Text("With ensure space", TextStyle.Default.SemiBold());
|
page.Header().Text("With ensure space").SemiBold();
|
||||||
|
|
||||||
page.Content().Stack(stack =>
|
page.Content().Column(column =>
|
||||||
{
|
{
|
||||||
stack
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.ExtendHorizontal()
|
.ExtendHorizontal()
|
||||||
.Height(75)
|
.Height(75)
|
||||||
.Background(Colors.Grey.Lighten2);
|
.Background(Colors.Grey.Lighten2);
|
||||||
|
|
||||||
stack
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.EnsureSpace(100)
|
.EnsureSpace(100)
|
||||||
.Text(Placeholders.LoremIpsum());
|
.Text(Placeholders.LoremIpsum());
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,9 +17,9 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(5);
|
.Padding(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.SemiBold());
|
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text).SemiBold();
|
||||||
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||||
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text, TextStyle.Default);
|
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FrameExample
|
public class FrameExample
|
||||||
@@ -36,14 +36,14 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Background("#FFF")
|
.Background("#FFF")
|
||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
for(var i = 1; i <= 4; i++)
|
for(var i = 1; i <= 4; i++)
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).LabelCell(Placeholders.Label());
|
row.RelativeItem(2).LabelCell(Placeholders.Label());
|
||||||
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
|
row.RelativeItem(3).ValueCell().Text(Placeholders.Paragraph());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class ProcessRunningTime
|
||||||
|
{
|
||||||
|
public TimeSpan FluentTime { get; set; }
|
||||||
|
public TimeSpan GenerationTime { get; set; }
|
||||||
|
public float Size { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenerationBenchmark
|
||||||
|
{
|
||||||
|
public const int TestSize = 4096;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BenchmarkAsync()
|
||||||
|
{
|
||||||
|
RunTest(() => Enumerable
|
||||||
|
.Range(0, TestSize)
|
||||||
|
.AsParallel() // difference
|
||||||
|
.Select(GenerateAndCollect)
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BenchmarkSync()
|
||||||
|
{
|
||||||
|
RunTest(() => Enumerable
|
||||||
|
.Range(0, TestSize)
|
||||||
|
.Select(GenerateAndCollect)
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunTest(Func<IEnumerable<ProcessRunningTime>> handler)
|
||||||
|
{
|
||||||
|
var totalFluentTime = TimeSpan.Zero;
|
||||||
|
var totalGenerationTime = TimeSpan.Zero;
|
||||||
|
|
||||||
|
var stopWatch = new Stopwatch();
|
||||||
|
|
||||||
|
stopWatch.Start();
|
||||||
|
var results = handler();
|
||||||
|
stopWatch.Stop();
|
||||||
|
|
||||||
|
foreach (var result in results)
|
||||||
|
{
|
||||||
|
totalFluentTime += result.FluentTime;
|
||||||
|
totalGenerationTime += result.GenerationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Fluent: {totalFluentTime:g}");
|
||||||
|
Console.WriteLine($"Generation: {totalGenerationTime:g}");
|
||||||
|
Console.WriteLine($"Total: {stopWatch.Elapsed:g}");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProcessRunningTime GenerateAndCollect(int attemptNumber)
|
||||||
|
{
|
||||||
|
var stopwatch = new Stopwatch();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
var container = new Container();
|
||||||
|
|
||||||
|
container
|
||||||
|
.Padding(10)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.Column(column =>
|
||||||
|
{
|
||||||
|
column.Item().Text($"Attempts {attemptNumber}");
|
||||||
|
|
||||||
|
const int numberOfRows = 100;
|
||||||
|
const int numberOfColumns = 10;
|
||||||
|
|
||||||
|
for (var y = 0; y < numberOfRows; y++)
|
||||||
|
{
|
||||||
|
column.Item().Row(row =>
|
||||||
|
{
|
||||||
|
for (var x = 0; x < numberOfColumns; x++)
|
||||||
|
{
|
||||||
|
row.RelativeItem()
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten5)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten4)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten3)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten2)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Lighten1)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Medium)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken1)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken2)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken3)
|
||||||
|
.Padding(3)
|
||||||
|
|
||||||
|
.Background(Colors.Red.Darken4)
|
||||||
|
.Height(3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var fluentTime = stopwatch.Elapsed;
|
||||||
|
|
||||||
|
stopwatch.Reset();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
var size = Document
|
||||||
|
.Create(x => x.Page(page => page.Content().Element(container)))
|
||||||
|
.GeneratePdf()
|
||||||
|
.Length;
|
||||||
|
|
||||||
|
var generationTime = stopwatch.Elapsed;
|
||||||
|
|
||||||
|
return new ProcessRunningTime
|
||||||
|
{
|
||||||
|
FluentTime = fluentTime,
|
||||||
|
GenerationTime = generationTime,
|
||||||
|
Size = size
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class GridBenchmark
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Benchmark()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProducePdf()
|
||||||
|
.PageSize(PageSizes.A4)
|
||||||
|
.ShowResults()
|
||||||
|
.MaxPages(10_000)
|
||||||
|
.EnableCaching(true)
|
||||||
|
.EnableDebugging(false)
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(10)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.Grid(grid =>
|
||||||
|
{
|
||||||
|
const int numberOfRows = 100_000;
|
||||||
|
const int numberOfColumns = 10;
|
||||||
|
|
||||||
|
grid.Columns(numberOfColumns);
|
||||||
|
|
||||||
|
foreach (var row in Enumerable.Range(0, numberOfRows))
|
||||||
|
foreach (var column in Enumerable.Range(0, numberOfColumns))
|
||||||
|
grid.Item().Background(Placeholders.BackgroundColor()).Padding(5).Text($"{row}_{column}");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,17 +19,17 @@ namespace QuestPDF.Examples
|
|||||||
.ShowResults()
|
.ShowResults()
|
||||||
.Render(page =>
|
.Render(page =>
|
||||||
{
|
{
|
||||||
page.Padding(25).Stack(stack =>
|
page.Padding(25).Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(25);
|
column.Spacing(25);
|
||||||
|
|
||||||
stack.Item().Image("logo.png");
|
column.Item().Image("logo.png");
|
||||||
|
|
||||||
var binaryData = File.ReadAllBytes("logo.png");
|
var binaryData = File.ReadAllBytes("logo.png");
|
||||||
stack.Item().Image(binaryData);
|
column.Item().Image(binaryData);
|
||||||
|
|
||||||
using var stream = new FileStream("logo.png", FileMode.Open);
|
using var stream = new FileStream("logo.png", FileMode.Open);
|
||||||
stack.Item().Image(stream);
|
column.Item().Image(stream);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Decoration(decoration =>
|
.Decoration(decoration =>
|
||||||
{
|
{
|
||||||
decoration.Header().Text(text =>
|
decoration.Before().Text(text =>
|
||||||
{
|
{
|
||||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||||
|
|
||||||
@@ -74,7 +74,8 @@ namespace QuestPDF.Examples
|
|||||||
.PrimaryLayer()
|
.PrimaryLayer()
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.AlignMiddle()
|
.AlignMiddle()
|
||||||
.Text(sizeText, TextStyle.Default.Size(15));
|
.Text(sizeText)
|
||||||
|
.FontSize(15);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ namespace QuestPDF.Examples
|
|||||||
container
|
container
|
||||||
.Background("#FFF")
|
.Background("#FFF")
|
||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Stack(column =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
column.Spacing(10);
|
column.Spacing(10);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,8 @@ namespace QuestPDF.Examples
|
|||||||
.Background("FFF")
|
.Background("FFF")
|
||||||
.Padding(5)
|
.Padding(5)
|
||||||
.AlignCenter()
|
.AlignCenter()
|
||||||
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
|
.Text("Sample text")
|
||||||
|
.FontFamily("Segoe UI emoji");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ namespace QuestPDF.Examples
|
|||||||
.Render(container =>
|
.Render(container =>
|
||||||
{
|
{
|
||||||
container
|
container
|
||||||
.Stack(column =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
column
|
column
|
||||||
.Item()
|
.Item()
|
||||||
@@ -113,14 +114,14 @@ namespace QuestPDF.Examples
|
|||||||
.Render(container =>
|
.Render(container =>
|
||||||
{
|
{
|
||||||
container
|
container
|
||||||
.Stack(column =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
column
|
column
|
||||||
.Item()
|
.Item()
|
||||||
.Height(150)
|
.Height(150)
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn()
|
row.RelativeItem()
|
||||||
.Extend()
|
.Extend()
|
||||||
.Background("FFF")
|
.Background("FFF")
|
||||||
|
|
||||||
@@ -128,7 +129,7 @@ namespace QuestPDF.Examples
|
|||||||
.Width(50)
|
.Width(50)
|
||||||
.Background("444");
|
.Background("444");
|
||||||
|
|
||||||
row.RelativeColumn()
|
row.RelativeItem()
|
||||||
.Extend()
|
.Extend()
|
||||||
.Background("BBB")
|
.Background("BBB")
|
||||||
|
|
||||||
@@ -142,7 +143,7 @@ namespace QuestPDF.Examples
|
|||||||
.Height(150)
|
.Height(150)
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn()
|
row.RelativeItem()
|
||||||
.Extend()
|
.Extend()
|
||||||
.Background("BBB")
|
.Background("BBB")
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ namespace QuestPDF.Examples
|
|||||||
.Width(50)
|
.Width(50)
|
||||||
.Background("444");
|
.Background("444");
|
||||||
|
|
||||||
row.RelativeColumn()
|
row.RelativeItem()
|
||||||
.Extend()
|
.Extend()
|
||||||
.Background("BBB")
|
.Background("BBB")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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,12 +6,12 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||||
<PackageReference Include="microcharts" Version="0.9.5.9" />
|
<PackageReference Include="microcharts" Version="0.9.5.9" />
|
||||||
<PackageReference Include="nunit" Version="3.13.2" />
|
<PackageReference Include="nunit" Version="3.13.3" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
|
||||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
<PackageReference Include="SkiaSharp" Version="2.80.4" />
|
||||||
<PackageReference Include="Svg.Skia" Version="0.5.10" />
|
<PackageReference Include="Svg.Skia" Version="0.5.10" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class RelativePaddingExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void ItemTypes()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.PageSize(250, 250)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Width(250)
|
||||||
|
.Height(250)
|
||||||
|
|
||||||
|
.Padding(50)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
|
||||||
|
.RelativePaddingLeft(0.1f)
|
||||||
|
.RelativePaddingTop(0.2f)
|
||||||
|
.RelativePaddingRight(0.3f)
|
||||||
|
.RelativePaddingBottom(0.4f)
|
||||||
|
|
||||||
|
.Background(Colors.Grey.Darken2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class RelativePositionExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void ItemTypes()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.PageSize(500, 500)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(100)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.RelativePositionVertical(0.5f, -0.5f)
|
||||||
|
.RelativePositionHorizontal(1f, -0.5f)
|
||||||
|
.RelativeWidth(0.4f)
|
||||||
|
.RelativeHeight(0.6f)
|
||||||
|
.Background(Colors.Grey.Darken2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class RelativeSizeExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void ItemTypes()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.PageSize(600, 600)
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.Width(400)
|
||||||
|
.Height(400)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.Container()
|
||||||
|
.AlignMiddle()
|
||||||
|
.AlignCenter()
|
||||||
|
.RelativeWidth(0.25f)
|
||||||
|
.RelativeHeight(0.5f)
|
||||||
|
.Background(Colors.Grey.Darken2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace QuestPDF.Examples
|
|||||||
public class RowExamples
|
public class RowExamples
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void ColumnTypes()
|
public void ItemTypes()
|
||||||
{
|
{
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
@@ -22,25 +22,24 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(25)
|
.Padding(25)
|
||||||
.MinimalBox()
|
.MinimalBox()
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().LabelCell("Total width: 600px");
|
column.Item().LabelCell("Total width: 600px");
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.ConstantColumn(150).ValueCell("150px");
|
row.ConstantItem(150).ValueCell("150px");
|
||||||
row.ConstantColumn(100).ValueCell("100px");
|
row.ConstantItem(100).ValueCell("100px");
|
||||||
row.RelativeColumn(4).ValueCell("200px");
|
row.RelativeItem(4).ValueCell("200px");
|
||||||
row.RelativeColumn(3).ValueCell("150px");
|
row.RelativeItem(3).ValueCell("150px");
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.ConstantColumn(100).ValueCell("100px");
|
row.ConstantItem(100).ValueCell("100px");
|
||||||
row.ConstantColumn(50).ValueCell("50px");
|
row.ConstantItem(50).ValueCell("50px");
|
||||||
row.RelativeColumn(3).ValueCell("300px");
|
row.RelativeItem(2).ValueCell("100px");
|
||||||
row.RelativeColumn(1).ValueCell("100px");
|
row.RelativeItem(1).ValueCell("50px");
|
||||||
row.ConstantColumn(50).ValueCell("50px");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -63,17 +62,17 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(25)
|
.Padding(25)
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().Stack(stack =>
|
row.RelativeItem().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().ShowOnce().Element(CreateBox).Text("X");
|
column.Item().ShowOnce().Element(CreateBox).Text("X");
|
||||||
stack.Item().Element(CreateBox).Text("1");
|
column.Item().Element(CreateBox).Text("1");
|
||||||
stack.Item().Element(CreateBox).Text("2");
|
column.Item().Element(CreateBox).Text("2");
|
||||||
});
|
});
|
||||||
|
|
||||||
row.RelativeColumn().Stack(stack =>
|
row.RelativeItem().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Element(CreateBox).Text("1");
|
column.Item().Element(CreateBox).Text("1");
|
||||||
stack.Item().Element(CreateBox).Text("2");
|
column.Item().Element(CreateBox).Text("2");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -90,5 +89,21 @@ namespace QuestPDF.Examples
|
|||||||
.ShowOnce();
|
.ShowOnce();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Stability_NoItems()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProducePdf()
|
||||||
|
.MaxPages(100)
|
||||||
|
.PageSize(250, 150)
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(25)
|
||||||
|
.Row(row => { });
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,20 +21,20 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
page.Margin(20);
|
page.Margin(20);
|
||||||
page.Size(PageSizes.A7.Landscape());
|
page.Size(PageSizes.A7.Landscape());
|
||||||
page.Background(Colors.White);
|
page.PageColor(Colors.White);
|
||||||
|
|
||||||
page.Header().Text("With show once", TextStyle.Default.SemiBold());
|
page.Header().Text("With show once").SemiBold();
|
||||||
|
|
||||||
page.Content().PaddingVertical(5).Row(row =>
|
page.Content().PaddingVertical(5).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn()
|
row.RelativeItem()
|
||||||
.Background(Colors.Grey.Lighten2)
|
.Background(Colors.Grey.Lighten2)
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.Padding(5)
|
.Padding(5)
|
||||||
.ShowOnce()
|
.ShowOnce()
|
||||||
.Text(Placeholders.Label());
|
.Text(Placeholders.Label());
|
||||||
|
|
||||||
row.RelativeColumn(2)
|
row.RelativeItem(2)
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.Padding(5)
|
.Padding(5)
|
||||||
.Text(Placeholders.Paragraph());
|
.Text(Placeholders.Paragraph());
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class ShrinkExamples
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Without()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Horizontal()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.ShrinkHorizontal()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Vertical()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.ShrinkVertical()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Shrink_Both()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(300, 200)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Border(2)
|
||||||
|
.Shrink()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Padding(20)
|
||||||
|
.Text("This is test.")
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,17 +21,18 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
page.Margin(20);
|
page.Margin(20);
|
||||||
page.Size(PageSizes.A7.Landscape());
|
page.Size(PageSizes.A7.Landscape());
|
||||||
page.Background(Colors.White);
|
page.PageColor(Colors.White);
|
||||||
|
|
||||||
page.Header().Stack(stack =>
|
page.Header().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().ShowOnce().Text("This header is visible on the first page.");
|
column.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.");
|
column.Item().SkipOnce().Text("This header is visible on the second page and all following.");
|
||||||
});
|
});
|
||||||
|
|
||||||
page.Content()
|
page.Content()
|
||||||
.PaddingVertical(10)
|
.PaddingVertical(10)
|
||||||
.Text(Placeholders.Paragraphs(), TextStyle.Default.Color(Colors.Grey.Medium));
|
.Text(Placeholders.Paragraphs())
|
||||||
|
.FontColor(Colors.Grey.Medium);
|
||||||
|
|
||||||
page.Footer().Text(text =>
|
page.Footer().Text(text =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Examples.Engine;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
|
||||||
|
namespace QuestPDF.Examples
|
||||||
|
{
|
||||||
|
public class TableBenchmark
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Benchmark()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProducePdf()
|
||||||
|
.PageSize(PageSizes.A4)
|
||||||
|
.ShowResults()
|
||||||
|
.MaxPages(10_000)
|
||||||
|
.EnableCaching(true)
|
||||||
|
.EnableDebugging(false)
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(10)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.Table(table =>
|
||||||
|
{
|
||||||
|
const int numberOfRows = 100_000;
|
||||||
|
const int numberOfColumns = 10;
|
||||||
|
|
||||||
|
table.ColumnsDefinition(columns =>
|
||||||
|
{
|
||||||
|
foreach (var _ in Enumerable.Range(0, numberOfColumns))
|
||||||
|
columns.RelativeColumn();
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var row in Enumerable.Range(0, numberOfRows))
|
||||||
|
foreach (var column in Enumerable.Range(0, numberOfColumns))
|
||||||
|
table.Cell().Background(Placeholders.BackgroundColor()).Padding(5).Text($"{row}_{column}");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
|
|||||||
.PageSize(PageSizes.A4)
|
.PageSize(PageSizes.A4)
|
||||||
.ProducePdf()
|
.ProducePdf()
|
||||||
.ShowResults()
|
.ShowResults()
|
||||||
.Render(x => ComposeBook(x, chapters));
|
.RenderDocument(x => ComposeBook(x, chapters));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -31,7 +31,7 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
var chapters = GetBookChapters().ToList();
|
var chapters = GetBookChapters().ToList();
|
||||||
|
|
||||||
var results = PerformTest(16).ToList();
|
var results = PerformTest(128).ToList();
|
||||||
|
|
||||||
Console.WriteLine($"Min: {results.Min():F}");
|
Console.WriteLine($"Min: {results.Min():F}");
|
||||||
Console.WriteLine($"Max: {results.Max():F}");
|
Console.WriteLine($"Max: {results.Max():F}");
|
||||||
@@ -42,8 +42,8 @@ namespace QuestPDF.Examples
|
|||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
.PageSize(PageSizes.A4)
|
.PageSize(PageSizes.A4)
|
||||||
.ProducePdf()
|
.ProducePdf()
|
||||||
.Render(x => ComposeBook(x, chapters));
|
.RenderDocument(x => ComposeBook(x, chapters));
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<float> PerformTest(int attempts)
|
IEnumerable<float> PerformTest(int attempts)
|
||||||
@@ -102,112 +102,105 @@ namespace QuestPDF.Examples
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComposeBook(IContainer container, ICollection<BookChapter> chapters)
|
private void ComposeBook(IDocumentContainer container, ICollection<BookChapter> chapters)
|
||||||
{
|
{
|
||||||
var subtitleStyle = TextStyle.Default.Size(24).SemiBold().Color(Colors.Blue.Medium);
|
var subtitleStyle = TextStyle.Default.Size(24).SemiBold().Color(Colors.Blue.Medium);
|
||||||
var normalStyle = TextStyle.Default.Size(14);
|
var normalStyle = TextStyle.Default.Size(14);
|
||||||
|
|
||||||
ComposePage(container);
|
|
||||||
|
|
||||||
void ComposePage(IContainer container)
|
container.Page(page =>
|
||||||
{
|
{
|
||||||
container
|
page.Margin(50);
|
||||||
.Padding(50)
|
|
||||||
.Decoration(decoration =>
|
page.Content().PaddingVertical(10).Column(column =>
|
||||||
{
|
{
|
||||||
decoration
|
column.Item().Element(Title);
|
||||||
.Content()
|
column.Item().PageBreak();
|
||||||
.Stack(stack =>
|
column.Item().Element(TableOfContents);
|
||||||
{
|
column.Item().PageBreak();
|
||||||
stack.Item().Element(Title);
|
|
||||||
stack.Item().PageBreak();
|
|
||||||
stack.Item().Element(TableOfContents);
|
|
||||||
stack.Item().PageBreak();
|
|
||||||
|
|
||||||
Chapters(stack);
|
Chapters(column);
|
||||||
|
|
||||||
stack.Item().Element(Acknowledgements);
|
column.Item().Element(Acknowledgements);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
page.Footer().Element(Footer);
|
||||||
|
});
|
||||||
|
|
||||||
decoration.Footer().Element(Footer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Title(IContainer container)
|
void Title(IContainer container)
|
||||||
{
|
{
|
||||||
container
|
container
|
||||||
.Extend()
|
.Extend()
|
||||||
.PaddingBottom(200)
|
.PaddingBottom(200)
|
||||||
.AlignBottom()
|
.AlignBottom()
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
|
column.Item().Text("Quo Vadis").FontSize(72).Bold().FontColor(Colors.Blue.Darken2);
|
||||||
stack.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
|
column.Item().Text("Henryk Sienkiewicz").FontSize(24).FontColor(Colors.Grey.Darken2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableOfContents(IContainer container)
|
void TableOfContents(IContainer container)
|
||||||
{
|
{
|
||||||
container.Stack(stack =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
SectionTitle(stack, "Spis treści");
|
SectionTitle(column, "Spis treści");
|
||||||
|
|
||||||
foreach (var chapter in chapters)
|
foreach (var chapter in chapters)
|
||||||
{
|
{
|
||||||
stack.Item().InternalLink(chapter.Title).Row(row =>
|
column.Item().InternalLink(chapter.Title).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().Text(chapter.Title, normalStyle);
|
row.RelativeItem().Text(chapter.Title).Style(normalStyle);
|
||||||
row.ConstantColumn(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
|
row.ConstantItem(100).AlignRight().Text(text => text.BeginPageNumberOfSection(chapter.Title).Style(normalStyle));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chapters(StackDescriptor stack)
|
void Chapters(ColumnDescriptor column)
|
||||||
{
|
{
|
||||||
foreach (var chapter in chapters)
|
foreach (var chapter in chapters)
|
||||||
{
|
{
|
||||||
stack.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
|
column.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chapter(IContainer container, string title, string content)
|
void Chapter(IContainer container, string title, string content)
|
||||||
{
|
{
|
||||||
container.Stack(stack =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
SectionTitle(stack, title);
|
SectionTitle(column, title);
|
||||||
|
|
||||||
stack.Item().Text(text =>
|
column.Item().Text(text =>
|
||||||
{
|
{
|
||||||
text.ParagraphSpacing(5);
|
text.ParagraphSpacing(5);
|
||||||
text.Span(content, normalStyle);
|
text.Span(content).Style(normalStyle);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().PageBreak();
|
column.Item().PageBreak();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Acknowledgements(IContainer container)
|
void Acknowledgements(IContainer container)
|
||||||
{
|
{
|
||||||
container.Stack(stack =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
SectionTitle(stack, "Podziękowania");
|
SectionTitle(column, "Podziękowania");
|
||||||
|
|
||||||
stack.Item().Text(text =>
|
column.Item().Text(text =>
|
||||||
{
|
{
|
||||||
text.DefaultTextStyle(normalStyle);
|
text.DefaultTextStyle(normalStyle);
|
||||||
|
|
||||||
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
|
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
|
||||||
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underline());
|
text.Hyperlink("wolnelektury.pl", "https://wolnelektury.pl/").FontColor(Colors.Blue.Medium).Underline();
|
||||||
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
|
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SectionTitle(StackDescriptor stack, string text)
|
void SectionTitle(ColumnDescriptor column, string text)
|
||||||
{
|
{
|
||||||
stack.Item().Location(text).Text(text, subtitleStyle);
|
column.Item().Location(text).Text(text).Style(subtitleStyle);
|
||||||
stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
column.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Footer(IContainer container)
|
void Footer(IContainer container)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using QuestPDF.Elements.Text;
|
||||||
using QuestPDF.Examples.Engine;
|
using QuestPDF.Examples.Engine;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
@@ -12,11 +13,11 @@ namespace QuestPDF.Examples
|
|||||||
public class TextExamples
|
public class TextExamples
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void SimpleTextBlock()
|
public void SimpleText()
|
||||||
{
|
{
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
.PageSize(500, 300)
|
.PageSize(500, 100)
|
||||||
|
|
||||||
.ProduceImages()
|
.ProduceImages()
|
||||||
.ShowResults()
|
.ShowResults()
|
||||||
@@ -27,21 +28,192 @@ namespace QuestPDF.Examples
|
|||||||
.MinimalBox()
|
.MinimalBox()
|
||||||
.Border(1)
|
.Border(1)
|
||||||
.Padding(10)
|
.Padding(10)
|
||||||
|
.Text(Placeholders.Paragraph());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SimpleTextBlock()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(600, 300)
|
||||||
|
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(5)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.MaxWidth(300)
|
||||||
|
.Padding(10)
|
||||||
.Text(text =>
|
.Text(text =>
|
||||||
{
|
{
|
||||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||||
text.Span("This is a normal text, followed by an ");
|
text.Span("This is a normal text, followed by an ");
|
||||||
text.Span("underlined red text.", TextStyle.Default.Size(20).Color(Colors.Red.Medium).Underline());
|
text.Span("underlined red text").FontColor(Colors.Red.Medium).Underline();
|
||||||
|
text.Span(".");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TextWeight()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(500, 500)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.Padding(20)
|
||||||
|
.Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(x => x.FontFamily(Fonts.Calibri).FontSize(20));
|
||||||
|
|
||||||
|
text.Line("Thin").Thin();
|
||||||
|
text.Line("ExtraLight").ExtraLight();
|
||||||
|
text.Line("Light").Light();
|
||||||
|
text.Line("NormalWeight").NormalWeight();
|
||||||
|
text.Line("Medium").Medium();
|
||||||
|
text.Line("SemiBold").SemiBold();
|
||||||
|
text.Line("Bold").Bold();
|
||||||
|
text.Line("ExtraBold").ExtraBold();
|
||||||
|
text.Line("Black").Black();
|
||||||
|
text.Line("ExtraBlack").ExtraBlack();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void LineHeight()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(500, 700)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.Column(column =>
|
||||||
|
{
|
||||||
|
var lineHeights = new[] { 0.8f, 1f, 1.5f };
|
||||||
|
var paragraph = Placeholders.Paragraph();
|
||||||
|
|
||||||
|
foreach (var lineHeight in lineHeights)
|
||||||
|
{
|
||||||
|
column
|
||||||
|
.Item()
|
||||||
|
.Border(1)
|
||||||
|
.Padding(10)
|
||||||
|
.Text(paragraph)
|
||||||
|
.FontSize(16)
|
||||||
|
.LineHeight(lineHeight);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SuperscriptSubscript_Simple()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(500, 500)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(20)
|
||||||
|
.MinimalBox()
|
||||||
|
.Border(1)
|
||||||
|
.Padding(20)
|
||||||
|
.Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(x => x.FontSize(20));
|
||||||
|
text.ParagraphSpacing(10);
|
||||||
|
|
||||||
|
var highlight = TextStyle.Default.BackgroundColor(Colors.Green.Lighten3);
|
||||||
|
|
||||||
|
text.Span("E=mc").Style(highlight);
|
||||||
|
text.Span("2").Superscript().Style(highlight);
|
||||||
|
text.Span(" is the equation of mass–energy equivalence.");
|
||||||
|
|
||||||
|
text.EmptyLine();
|
||||||
|
|
||||||
|
text.Span("H").Style(highlight);
|
||||||
|
text.Span("2").Subscript().Style(highlight);
|
||||||
|
text.Span("O").Style(highlight);
|
||||||
|
text.Span(" is the chemical formula for water.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SuperscriptSubscript_Effects()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(800, 400)
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(25)
|
||||||
|
.DefaultTextStyle(x => x.FontSize(30))
|
||||||
|
.Column(column =>
|
||||||
|
{
|
||||||
|
column.Spacing(25);
|
||||||
|
|
||||||
|
column.Item().Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(x => x.Underline());
|
||||||
|
|
||||||
|
text.Span("Underline of the superscript (E = mc");
|
||||||
|
text.Span("2").Superscript();
|
||||||
|
text.Span(") should be at the same height as for normal text.");
|
||||||
|
});
|
||||||
|
|
||||||
|
column.Item().Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(x => x.Underline());
|
||||||
|
|
||||||
|
text.Span("Underline of the subscript(H");
|
||||||
|
text.Span("2").Subscript();
|
||||||
|
text.Span("O) should be slightly lower than a normal text.");
|
||||||
|
});
|
||||||
|
|
||||||
|
column.Item().Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(x => x.Strikethrough());
|
||||||
|
|
||||||
|
text.Span("Strikethrough of both superscript (E=mc");
|
||||||
|
text.Span("2").Superscript();
|
||||||
|
text.Span(") and subscript(H");
|
||||||
|
text.Span("2").Subscript();
|
||||||
|
text.Span("O) should be visible in the middle of the text.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void ParagraphSpacing()
|
public void ParagraphSpacing()
|
||||||
{
|
{
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
.PageSize(500, 300)
|
.PageSize(500, 500)
|
||||||
.ProduceImages()
|
.ProduceImages()
|
||||||
.ShowResults()
|
.ShowResults()
|
||||||
.Render(container =>
|
.Render(container =>
|
||||||
@@ -53,13 +225,7 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text(text =>
|
.Text(text =>
|
||||||
{
|
{
|
||||||
text.ParagraphSpacing(10);
|
text.Line(Placeholders.Paragraph());
|
||||||
|
|
||||||
foreach (var i in Enumerable.Range(1, 3))
|
|
||||||
{
|
|
||||||
text.Span($"Paragraph {i}: ", TextStyle.Default.SemiBold());
|
|
||||||
text.Line(Placeholders.Paragraph());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -81,7 +247,7 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text(text =>
|
.Text(text =>
|
||||||
{
|
{
|
||||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||||
text.Span("This is a random image aligned to the baseline: ");
|
text.Span("This is a random image aligned to the baseline: ");
|
||||||
|
|
||||||
text.Element()
|
text.Element()
|
||||||
@@ -120,13 +286,13 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
text.Line(Placeholders.LoremIpsum());
|
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? <", TextStyle.Default.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? <").Underline();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TextStack()
|
public void Textcolumn()
|
||||||
{
|
{
|
||||||
RenderingTest
|
RenderingTest
|
||||||
.Create()
|
.Create()
|
||||||
@@ -173,6 +339,8 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text(text =>
|
.Text(text =>
|
||||||
{
|
{
|
||||||
|
text.DefaultTextStyle(x => x.Bold());
|
||||||
|
|
||||||
text.DefaultTextStyle(TextStyle.Default);
|
text.DefaultTextStyle(TextStyle.Default);
|
||||||
text.AlignLeft();
|
text.AlignLeft();
|
||||||
text.ParagraphSpacing(10);
|
text.ParagraphSpacing(10);
|
||||||
@@ -182,14 +350,14 @@ namespace QuestPDF.Examples
|
|||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
|
|
||||||
text.Span("This text is a normal text, ");
|
text.Span("This text is a normal text, ");
|
||||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
text.Span("this is a bold text, ").Bold();
|
||||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
|
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
|
||||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
text.Span("and this is slightly bigger text.").FontSize(16);
|
||||||
|
|
||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
|
|
||||||
text.Span("The new text element also supports injecting custom content between words: ");
|
text.Span("The new text element also supports injecting custom content between words: ");
|
||||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
|
||||||
text.Span(".");
|
text.Span(".");
|
||||||
|
|
||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
@@ -201,7 +369,7 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
|
|
||||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||||
|
|
||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
|
|
||||||
@@ -210,7 +378,7 @@ namespace QuestPDF.Examples
|
|||||||
|
|
||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
|
|
||||||
text.Span(Placeholders.Paragraphs(), TextStyle.Default.Italic());
|
text.Span(Placeholders.Paragraphs()).Italic();
|
||||||
|
|
||||||
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
|
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
|
||||||
});
|
});
|
||||||
@@ -236,17 +404,17 @@ namespace QuestPDF.Examples
|
|||||||
.Padding(10)
|
.Padding(10)
|
||||||
.Text(text =>
|
.Text(text =>
|
||||||
{
|
{
|
||||||
text.DefaultTextStyle(TextStyle.Default);
|
text.DefaultTextStyle(TextStyle.Default.FontSize(20).BackgroundColor(Colors.Red.Lighten4));
|
||||||
text.AlignLeft();
|
text.AlignLeft();
|
||||||
text.ParagraphSpacing(10);
|
text.ParagraphSpacing(10);
|
||||||
|
|
||||||
text.Span("This text is a normal text, ");
|
text.Span("This text is a normal text, ");
|
||||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
text.Span("this is a bold text, ").Bold();
|
||||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
|
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
|
||||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
text.Span("and this is slightly bigger text.").FontSize(16);
|
||||||
|
|
||||||
text.Span("The new text element also supports injecting custom content between words: ");
|
text.Span("The new text element also supports injecting custom content between words: ");
|
||||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
|
||||||
text.Span(".");
|
text.Span(".");
|
||||||
|
|
||||||
text.EmptyLine();
|
text.EmptyLine();
|
||||||
@@ -255,7 +423,7 @@ namespace QuestPDF.Examples
|
|||||||
{
|
{
|
||||||
text.Line($"{i}: {Placeholders.Paragraph()}");
|
text.Line($"{i}: {Placeholders.Paragraph()}");
|
||||||
|
|
||||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
text.Hyperlink("Please visit QuestPDF website. ", "https://www.questpdf.com");
|
||||||
|
|
||||||
text.Span("This is page number ");
|
text.Span("This is page number ");
|
||||||
text.CurrentPageNumber();
|
text.CurrentPageNumber();
|
||||||
@@ -267,5 +435,227 @@ 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BreakingLongWord()
|
||||||
|
{
|
||||||
|
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.DefaultTextStyle(x => x.BackgroundColor(Colors.Red.Lighten3).FontSize(24));
|
||||||
|
|
||||||
|
text.Span(" " + Placeholders.LoremIpsum());
|
||||||
|
text.Span(" 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 ").WrapAnywhere();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TextShaping_Unicode()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(600, 100)
|
||||||
|
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(35)
|
||||||
|
.MinimalBox()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Text(text =>
|
||||||
|
{
|
||||||
|
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||||
|
|
||||||
|
text.Span("Complex Unicode structure: ");
|
||||||
|
|
||||||
|
|
||||||
|
text.Span("T̶̖̔͆͆̽̔ḩ̷̼̫̐̈́̀͜͝͝ì̶͇̤͓̱̣͇͓͉̎s̵̡̟̹͍̜͉̗̾͛̈̐́͋͂͝͠ͅ ̴̨͙͍͇̭̒͗̀́͝ì̷̡̺͉̼̏̏̉̌͝s̷͍͙̗̰̖͙̈̑̂̔͑͊̌̓̊̇͜ ̶̛̼͚͊̅͘ṭ̷̨̘̣̙̖͉͌̏̂̅͑̄̽̕͝ȅ̶̲̲̙̭͈̬̣͔̝͔̈́͝s̸̢̯̪̫͓̭̮̓̀͆͜ț̸̢͉̞̥̤̏̌̓͝").FontFamily(Fonts.Calibri).FontColor(Colors.Red.Medium);
|
||||||
|
|
||||||
|
|
||||||
|
text.Span(".");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TextShaping_Arabic()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.PageSize(500, 100)
|
||||||
|
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.Render(container =>
|
||||||
|
{
|
||||||
|
container
|
||||||
|
.Padding(25)
|
||||||
|
.MinimalBox()
|
||||||
|
.Background(Colors.Grey.Lighten2)
|
||||||
|
.Text("ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا")
|
||||||
|
.FontFamily(Fonts.Calibri)
|
||||||
|
.FontSize(20);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void FontFallback()
|
||||||
|
{
|
||||||
|
RenderingTest
|
||||||
|
.Create()
|
||||||
|
.ProduceImages()
|
||||||
|
.ShowResults()
|
||||||
|
.RenderDocument(container =>
|
||||||
|
{
|
||||||
|
container.Page(page =>
|
||||||
|
{
|
||||||
|
page.Margin(50);
|
||||||
|
page.PageColor(Colors.White);
|
||||||
|
page.DefaultTextStyle(x => x
|
||||||
|
.Fallback(y => y.FontFamily("Segoe UI Emoji")
|
||||||
|
.Fallback(y => y.FontFamily("Microsoft YaHei"))));
|
||||||
|
|
||||||
|
page.Size(PageSizes.A4);
|
||||||
|
|
||||||
|
page.Content().Text(t =>
|
||||||
|
{
|
||||||
|
t.Line("This is normal text.");
|
||||||
|
t.EmptyLine();
|
||||||
|
|
||||||
|
t.Line("Following line should use font fallback:");
|
||||||
|
t.Line("中文文本");
|
||||||
|
t.EmptyLine();
|
||||||
|
|
||||||
|
t.Line("The following line contains a mix of known and unknown characters.");
|
||||||
|
t.Line("Mixed line: This 中文 is 文文 a mixed 本 本 line 本 中文文本!");
|
||||||
|
t.EmptyLine();
|
||||||
|
|
||||||
|
t.Line("Emojis work out of the box because of font fallback: 😊😅🥳👍❤😍👌");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
using QuestPDF.Previewer;
|
||||||
|
using QuestPDF.ReportSample;
|
||||||
|
using QuestPDF.ReportSample.Layouts;
|
||||||
|
|
||||||
|
//ImagePlaceholder.Solid = true;
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.ShowInPreviewer();
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\QuestPDF.ReportSample\QuestPDF.ReportSample.csproj" />
|
||||||
|
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer;
|
||||||
|
|
||||||
|
class CommunicationService
|
||||||
|
{
|
||||||
|
public static CommunicationService Instance { get; } = new ();
|
||||||
|
|
||||||
|
public event Action<ICollection<PreviewPage>>? OnDocumentRefreshed;
|
||||||
|
|
||||||
|
private WebApplication? Application { get; set; }
|
||||||
|
|
||||||
|
private readonly JsonSerializerOptions JsonSerializerOptions = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
private CommunicationService()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Start(int port)
|
||||||
|
{
|
||||||
|
var builder = WebApplication.CreateBuilder();
|
||||||
|
builder.Services.AddLogging(x => x.ClearProviders());
|
||||||
|
builder.WebHost.UseKestrel(options => options.Limits.MaxRequestBodySize = null);
|
||||||
|
Application = builder.Build();
|
||||||
|
|
||||||
|
Application.MapGet("ping", HandlePing);
|
||||||
|
Application.MapGet("version", HandleVersion);
|
||||||
|
Application.MapPost("update/preview", HandleUpdatePreview);
|
||||||
|
|
||||||
|
return Application.RunAsync($"http://localhost:{port}/");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Stop()
|
||||||
|
{
|
||||||
|
await Application.StopAsync();
|
||||||
|
await Application.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IResult> HandlePing()
|
||||||
|
{
|
||||||
|
return OnDocumentRefreshed == null
|
||||||
|
? Results.StatusCode(StatusCodes.Status503ServiceUnavailable)
|
||||||
|
: Results.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IResult> HandleVersion()
|
||||||
|
{
|
||||||
|
return Results.Json(GetType().Assembly.GetName().Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IResult> HandleUpdatePreview(HttpRequest request)
|
||||||
|
{
|
||||||
|
var command = JsonSerializer.Deserialize<DocumentSnapshot>(request.Form["command"], JsonSerializerOptions);
|
||||||
|
|
||||||
|
var pages = command
|
||||||
|
.Pages
|
||||||
|
.Select(page =>
|
||||||
|
{
|
||||||
|
using var stream = request.Form.Files[page.Id].OpenReadStream();
|
||||||
|
var picture = SKPicture.Deserialize(stream);
|
||||||
|
|
||||||
|
return new PreviewPage(picture, page.Width, page.Height);
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
Task.Run(() => OnDocumentRefreshed(pages));
|
||||||
|
return Results.Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer;
|
||||||
|
|
||||||
|
class Helpers
|
||||||
|
{
|
||||||
|
public static void GeneratePdfFromDocumentSnapshots(string filePath, ICollection<PreviewPage> pages)
|
||||||
|
{
|
||||||
|
using var stream = File.Create(filePath);
|
||||||
|
|
||||||
|
using var document = SKDocument.CreatePdf(stream);
|
||||||
|
|
||||||
|
foreach (var page in pages)
|
||||||
|
{
|
||||||
|
using var canvas = document.BeginPage(page.Width, page.Height);
|
||||||
|
canvas.DrawPicture(page.Picture);
|
||||||
|
document.EndPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
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.Height) + (Pages.Count - 1) * PageSpacing;
|
||||||
|
public float TotalHeight => TotalPagesHeight + SafeZone * 2 / Scale;
|
||||||
|
public float MaxWidth => Pages.Any() ? Pages.Max(x => x.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();
|
||||||
|
|
||||||
|
TranslateX -= x / Scale - x / oldScale;
|
||||||
|
TranslateY -= y / Scale - 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.Width / 2f, 0);
|
||||||
|
DrawBlankPage(canvas, page.Width, page.Height);
|
||||||
|
canvas.DrawPicture(page.Picture);
|
||||||
|
canvas.Translate(page.Width / 2f, page.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, float width, float height)
|
||||||
|
{
|
||||||
|
canvas.DrawRect(0, 0, width, height, BlankPageShadowPaint);
|
||||||
|
canvas.DrawRect(0, 0, width, 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer
|
||||||
|
{
|
||||||
|
record PreviewPage(SKPicture Picture, float Width, float Height);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<Application x:Class="QuestPDF.Previewer.PreviewerApp"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Name="QuestPDF Document Preview">
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme Mode="Dark" />
|
||||||
|
</Application.Styles>
|
||||||
|
</Application>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer
|
||||||
|
{
|
||||||
|
internal class PreviewerApp : Application
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
desktop.MainWindow = new PreviewerWindow()
|
||||||
|
{
|
||||||
|
DataContext = new PreviewerWindowViewModel()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer;
|
||||||
|
|
||||||
|
internal class DocumentSnapshot
|
||||||
|
{
|
||||||
|
public ICollection<PageSnapshot> Pages { get; set; }
|
||||||
|
|
||||||
|
public class PageSnapshot
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public float Width { get; set; }
|
||||||
|
public float Height { get; set; }
|
||||||
|
|
||||||
|
public SKPicture Picture { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<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"
|
||||||
|
x:DataType="previewer:PreviewerWindowViewModel"
|
||||||
|
x:CompileBindings="True"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
ExtendClientAreaToDecorationsHint="true"
|
||||||
|
ExtendClientAreaTitleBarHeightHint="-1"
|
||||||
|
Background="#666"
|
||||||
|
Icon="/Resources/Logo.png"
|
||||||
|
UseLayoutRounding="True"
|
||||||
|
Title="QuestPDF Document Preview">
|
||||||
|
|
||||||
|
<Window.Styles>
|
||||||
|
<Style Selector="Button.actions">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||||
|
<Setter Property="Padding" Value="10"/>
|
||||||
|
<Setter Property="CornerRadius" Value="100"/>
|
||||||
|
<Setter Property="Background" Value="#888"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button:pointerover /template/ ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="#999"/>
|
||||||
|
</Style>
|
||||||
|
</Window.Styles>
|
||||||
|
|
||||||
|
<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 Pages, Mode=OneWay}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Vertical" VerticalAlignment="Bottom" Spacing="16" Margin="32">
|
||||||
|
<Button Classes="actions"
|
||||||
|
Command="{Binding ShowPdfCommand, Mode=OneTime}"
|
||||||
|
IsVisible="{Binding !!Pages.Count}"
|
||||||
|
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 Classes="actions"
|
||||||
|
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 Classes="actions"
|
||||||
|
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>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer
|
||||||
|
{
|
||||||
|
class PreviewerWindow : Window
|
||||||
|
{
|
||||||
|
public PreviewerWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnClosed(EventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using ReactiveUI;
|
||||||
|
using Unit = System.Reactive.Unit;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
|
||||||
|
namespace QuestPDF.Previewer
|
||||||
|
{
|
||||||
|
internal class PreviewerWindowViewModel : ReactiveObject
|
||||||
|
{
|
||||||
|
private ObservableCollection<PreviewPage> _pages = new();
|
||||||
|
public ObservableCollection<PreviewPage> Pages
|
||||||
|
{
|
||||||
|
get => _pages;
|
||||||
|
set => this.RaiseAndSetIfChanged(ref _pages, 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()
|
||||||
|
{
|
||||||
|
CommunicationService.Instance.OnDocumentRefreshed += HandleUpdatePreview;
|
||||||
|
|
||||||
|
ShowPdfCommand = ReactiveCommand.Create(ShowPdf);
|
||||||
|
ShowDocumentationCommand = ReactiveCommand.Create(() => OpenLink("https://www.questpdf.com/api-reference/index.html"));
|
||||||
|
SponsorProjectCommand = ReactiveCommand.Create(() => OpenLink("https://github.com/sponsors/QuestPDF"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowPdf()
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.pdf");
|
||||||
|
Helpers.GeneratePdfFromDocumentSnapshots(filePath, Pages);
|
||||||
|
|
||||||
|
OpenLink(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenLink(string path)
|
||||||
|
{
|
||||||
|
using var openBrowserProcess = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new()
|
||||||
|
{
|
||||||
|
UseShellExecute = true,
|
||||||
|
FileName = path
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
openBrowserProcess.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleUpdatePreview(ICollection<PreviewPage> pages)
|
||||||
|
{
|
||||||
|
var oldPages = Pages;
|
||||||
|
|
||||||
|
Pages.Clear();
|
||||||
|
Pages = new ObservableCollection<PreviewPage>(pages);
|
||||||
|
|
||||||
|
foreach (var page in oldPages)
|
||||||
|
page.Picture.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.ReactiveUI;
|
||||||
|
using QuestPDF.Previewer;
|
||||||
|
|
||||||
|
var applicationPort = GetCommunicationPort();
|
||||||
|
CommunicationService.Instance.Start(applicationPort);
|
||||||
|
|
||||||
|
if(Application.Current?.ApplicationLifetime is ClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
desktop.MainWindow = new PreviewerWindow()
|
||||||
|
{
|
||||||
|
DataContext = new PreviewerWindowViewModel()
|
||||||
|
};
|
||||||
|
|
||||||
|
desktop.MainWindow.Show();
|
||||||
|
desktop.Start(Array.Empty<string>());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppBuilder
|
||||||
|
.Configure(() => new PreviewerApp())
|
||||||
|
.UsePlatformDetect()
|
||||||
|
.UseReactiveUI()
|
||||||
|
.StartWithClassicDesktopLifetime(Array.Empty<string>());
|
||||||
|
|
||||||
|
static int GetCommunicationPort()
|
||||||
|
{
|
||||||
|
const int defaultApplicationPort = 12500;
|
||||||
|
|
||||||
|
var arguments = Environment.GetCommandLineArgs();
|
||||||
|
|
||||||
|
if (arguments.Length < 2)
|
||||||
|
return defaultApplicationPort;
|
||||||
|
|
||||||
|
return int.TryParse(arguments[1], out var port) ? port : defaultApplicationPort;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Authors>MarcinZiabek</Authors>
|
||||||
|
<Company>CodeFlint</Company>
|
||||||
|
<PackageId>QuestPDF.Previewer</PackageId>
|
||||||
|
<Version>2022.9.1</Version>
|
||||||
|
<PackAsTool>true</PackAsTool>
|
||||||
|
<ToolCommandName>questpdf-previewer</ToolCommandName>
|
||||||
|
<PackageDescription>QuestPDF is an open-source, modern and battle-tested library that can help you with generating PDF documents by offering friendly, discoverable and predictable C# fluent API.</PackageDescription>
|
||||||
|
<PackageReleaseNotes>Initial release.</PackageReleaseNotes>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<PackageIcon>Logo.png</PackageIcon>
|
||||||
|
<PackageIconUrl>https://www.questpdf.com/images/package-logo.png</PackageIconUrl>
|
||||||
|
<PackageProjectUrl>https://www.questpdf.com/</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/QuestPDF/library.git</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<Copyright>Marcin Ziąbek, QuestPDF contributors</Copyright>
|
||||||
|
<PackageTags>pdf report file export generate generation tool create creation render portable document format quest html library converter open source free standard core previewer</PackageTags>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
|
<OutputType>exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AvaloniaResource Include="Resources\Logo.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Resources\Logo.png" />
|
||||||
|
<None Include="Resources\Logo.png">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<Visible>false</Visible>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
<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="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.80.4" />
|
||||||
|
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||||
|
<PackageReference Include="SkiaSharp" Version="2.80.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
Install nuget locally (in directory where nupkg file is located)
|
||||||
|
|
||||||
|
```
|
||||||
|
dotnet tool install --global --add-source . QuestPDF.Previewer --global
|
||||||
|
```
|
||||||
|
|
||||||
|
Run on default port
|
||||||
|
|
||||||
|
```
|
||||||
|
questpdf-previewer
|
||||||
|
```
|
||||||
|
|
||||||
|
Run on custom port
|
||||||
|
|
||||||
|
```
|
||||||
|
questpdf-previewer 12500
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove nuget locally
|
||||||
|
|
||||||
|
```
|
||||||
|
dotnet tool uninstall QuestPDF.Previewer --global
|
||||||
|
```
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@@ -24,5 +25,32 @@ namespace QuestPDF.ReportSample
|
|||||||
Latitude = Helpers.Random.NextDouble() * 180f - 90f
|
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)
|
private void ComposeHeader(IContainer container)
|
||||||
{
|
{
|
||||||
container.Background(Colors.Grey.Lighten3).Border(1).Stack(stack =>
|
container.Background(Colors.Grey.Lighten3).Border(1).Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
|
column.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).AlignMiddle().Text("PRIMARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
|
row.RelativeItem(2).AlignMiddle().Text("PRIMARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
|
||||||
row.RelativeColumn(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
|
row.RelativeItem(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
|
||||||
});
|
});
|
||||||
stack.Item().SkipOnce().Padding(5).Row(row =>
|
column.Item().SkipOnce().Padding(5).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).Text("SECONDARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
|
row.RelativeItem(2).Text("SECONDARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
|
||||||
row.RelativeColumn(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
|
row.RelativeItem(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComposeContent(IContainer container)
|
private void ComposeContent(IContainer container)
|
||||||
{
|
{
|
||||||
container.Stack(stack =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().PaddingVertical(80).Text("First");
|
column.Item().PaddingVertical(80).Text("First");
|
||||||
stack.Item().PageBreak();
|
column.Item().PageBreak();
|
||||||
stack.Item().PaddingVertical(80).Text("Second");
|
column.Item().PaddingVertical(80).Text("Second");
|
||||||
stack.Item().PageBreak();
|
column.Item().PageBreak();
|
||||||
stack.Item().PaddingVertical(80).Text("Third");
|
column.Item().PaddingVertical(80).Text("Third");
|
||||||
stack.Item().PageBreak();
|
column.Item().PageBreak();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComposeFooter(IContainer container)
|
private void ComposeFooter(IContainer container)
|
||||||
{
|
{
|
||||||
container.Background(Colors.Grey.Lighten3).Stack(stack =>
|
container.Background(Colors.Grey.Lighten3).Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
column.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().Text(x =>
|
row.RelativeItem().Text(x =>
|
||||||
{
|
{
|
||||||
x.CurrentPageNumber();
|
x.CurrentPageNumber();
|
||||||
x.Span(" / ");
|
x.Span(" / ");
|
||||||
x.TotalPages();
|
x.TotalPages();
|
||||||
});
|
});
|
||||||
row.RelativeColumn().AlignRight().Text("Footer for header");
|
row.RelativeItem().AlignRight().Text("Footer for header");
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
column.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn().Text(x =>
|
row.RelativeItem().Text(x =>
|
||||||
{
|
{
|
||||||
x.CurrentPageNumber();
|
x.CurrentPageNumber();
|
||||||
x.Span(" / ");
|
x.Span(" / ");
|
||||||
x.TotalPages();
|
x.TotalPages();
|
||||||
});
|
});
|
||||||
row.RelativeColumn().AlignRight().Text("Footer for every page except header");
|
row.RelativeItem().AlignRight().Text("Footer for every page except header");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Helpers;
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
@@ -17,11 +18,11 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
{
|
{
|
||||||
container
|
container
|
||||||
.ShowEntire()
|
.ShowEntire()
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(5);
|
column.Spacing(5);
|
||||||
stack.Item().Element(PhotoWithMaps);
|
column.Item().Element(PhotoWithMaps);
|
||||||
stack.Item().Element(PhotoDetails);
|
column.Item().Element(PhotoDetails);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,14 +31,14 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
container
|
container
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
row.RelativeItem(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||||
|
|
||||||
row.RelativeColumn().PaddingLeft(5).Stack(stack =>
|
row.RelativeItem().PaddingLeft(5).Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(7f);
|
column.Spacing(7f);
|
||||||
|
|
||||||
stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||||
stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -47,7 +48,7 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
container.Border(0.75f).BorderColor(Colors.Grey.Medium).Grid(grid =>
|
container.Border(0.75f).BorderColor(Colors.Grey.Medium).Grid(grid =>
|
||||||
{
|
{
|
||||||
grid.Columns(6);
|
grid.Columns(6);
|
||||||
|
|
||||||
grid.Item().LabelCell().Text("Date");
|
grid.Item().LabelCell().Text("Date");
|
||||||
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty);
|
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty);
|
||||||
grid.Item().LabelCell().Text("Location");
|
grid.Item().LabelCell().Text("Location");
|
||||||
|
|||||||
@@ -21,25 +21,26 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
.Decoration(decoration =>
|
.Decoration(decoration =>
|
||||||
{
|
{
|
||||||
decoration
|
decoration
|
||||||
.Header()
|
.Before()
|
||||||
.PaddingBottom(5)
|
.PaddingBottom(5)
|
||||||
.Text(Model.Title, Typography.Headline);
|
.Text(Model.Title)
|
||||||
|
.Style(Typography.Headline);
|
||||||
|
|
||||||
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
|
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Column(column =>
|
||||||
{
|
{
|
||||||
foreach (var part in Model.Parts)
|
foreach (var part in Model.Parts)
|
||||||
{
|
{
|
||||||
stack.Item().EnsureSpace(25).Row(row =>
|
column.Item().EnsureSpace(25).Row(row =>
|
||||||
{
|
{
|
||||||
row.ConstantColumn(150).LabelCell().Text(part.Label);
|
row.ConstantItem(150).LabelCell().Text(part.Label);
|
||||||
var frame = row.RelativeColumn().ValueCell();
|
var frame = row.RelativeItem().ValueCell();
|
||||||
|
|
||||||
if (part is ReportSectionText text)
|
if (part is ReportSectionText text)
|
||||||
frame.ShowEntire().Text(text.Text);
|
frame.ShowEntire().Text(text.Text);
|
||||||
|
|
||||||
if (part is ReportSectionMap map)
|
if (part is ReportSectionMap map)
|
||||||
frame.Element(x => MapElement(x, map));
|
frame.Element(x => MapElement(x, map));
|
||||||
|
|
||||||
if (part is ReportSectionPhotos photos)
|
if (part is ReportSectionPhotos photos)
|
||||||
frame.Element(x => PhotosElement(x, photos));
|
frame.Element(x => PhotosElement(x, photos));
|
||||||
});
|
});
|
||||||
@@ -56,12 +57,12 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.ShowEntire().Stack(stack =>
|
container.ShowEntire().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(5);
|
column.Spacing(5);
|
||||||
|
|
||||||
stack.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
column.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||||
stack.Item().Text(model.Location.Format());
|
column.Item().Text(model.Location.Format());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
{
|
{
|
||||||
if (model.PhotoCount == 0)
|
if (model.PhotoCount == 0)
|
||||||
{
|
{
|
||||||
container.Text("No photos", Typography.Normal);
|
container.Text("No photos").Style(Typography.Normal);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,28 +39,28 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
|
|
||||||
page.Footer().AlignCenter().Text(text =>
|
page.Footer().AlignCenter().Text(text =>
|
||||||
{
|
{
|
||||||
text.CurrentPageNumber();
|
text.CurrentPageNumber().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||||
text.Span(" / ");
|
text.Span(" / ");
|
||||||
text.TotalPages();
|
text.TotalPages().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComposeHeader(IContainer container)
|
private void ComposeHeader(IContainer container)
|
||||||
{
|
{
|
||||||
container.Stack(stack =>
|
container.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.Spacing(50);
|
row.Spacing(50);
|
||||||
|
|
||||||
row.RelativeColumn().PaddingTop(-10).Text(Model.Title, Typography.Title);
|
row.RelativeItem().PaddingTop(-10).Text(Model.Title).Style(Typography.Title);
|
||||||
row.ConstantColumn(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
|
row.ConstantItem(90).Hyperlink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
|
column.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
|
||||||
|
|
||||||
stack.Item().ShowOnce().Grid(grid =>
|
column.Item().ShowOnce().Grid(grid =>
|
||||||
{
|
{
|
||||||
grid.Columns(2);
|
grid.Columns(2);
|
||||||
grid.Spacing(5);
|
grid.Spacing(5);
|
||||||
@@ -69,7 +69,7 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
{
|
{
|
||||||
grid.Item().Text(text =>
|
grid.Item().Text(text =>
|
||||||
{
|
{
|
||||||
text.Span($"{field.Label}: ", TextStyle.Default.SemiBold());
|
text.Span($"{field.Label}: ").SemiBold();
|
||||||
text.Span(field.Value);
|
text.Span(field.Value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -79,23 +79,23 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
|
|
||||||
void ComposeContent(IContainer container)
|
void ComposeContent(IContainer container)
|
||||||
{
|
{
|
||||||
container.PaddingVertical(20).Stack(stack =>
|
container.PaddingVertical(20).Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(20);
|
column.Spacing(20);
|
||||||
|
|
||||||
stack.Item().Component(new TableOfContentsTemplate(Model.Sections));
|
|
||||||
|
|
||||||
stack.Item().PageBreak();
|
column.Item().Component(new TableOfContentsTemplate(Model.Sections));
|
||||||
|
|
||||||
|
column.Item().PageBreak();
|
||||||
|
|
||||||
foreach (var section in Model.Sections)
|
foreach (var section in Model.Sections)
|
||||||
stack.Item().Location(section.Title).Component(new SectionTemplate(section));
|
column.Item().Section(section.Title).Component(new SectionTemplate(section));
|
||||||
|
|
||||||
stack.Item().PageBreak();
|
column.Item().PageBreak();
|
||||||
stack.Item().Location("Photos");
|
column.Item().Section("Photos");
|
||||||
|
|
||||||
foreach (var photo in Model.Photos)
|
foreach (var photo in Model.Photos)
|
||||||
stack.Item().Component(new PhotoTemplate(photo));
|
column.Item().Component(new PhotoTemplate(photo));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace QuestPDF.ReportSample.Layouts
|
namespace QuestPDF.ReportSample.Layouts
|
||||||
{
|
{
|
||||||
@@ -19,18 +22,19 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
.Decoration(decoration =>
|
.Decoration(decoration =>
|
||||||
{
|
{
|
||||||
decoration
|
decoration
|
||||||
.Header()
|
.Before()
|
||||||
.PaddingBottom(5)
|
.PaddingBottom(5)
|
||||||
.Text("Table of contents", Typography.Headline);
|
.Text("Table of contents")
|
||||||
|
.Style(Typography.Headline);
|
||||||
|
|
||||||
decoration.Content().Stack(stack =>
|
decoration.Content().Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(5);
|
column.Spacing(5);
|
||||||
|
|
||||||
for (var i = 0; i < Sections.Count; i++)
|
for (var i = 0; i < Sections.Count; i++)
|
||||||
stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
|
column.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
|
||||||
|
|
||||||
stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
column.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -38,13 +42,37 @@ namespace QuestPDF.ReportSample.Layouts
|
|||||||
private void DrawLink(IContainer container, int number, string locationName)
|
private void DrawLink(IContainer container, int number, string locationName)
|
||||||
{
|
{
|
||||||
container
|
container
|
||||||
.InternalLink(locationName)
|
.SectionLink(locationName)
|
||||||
.Row(row =>
|
.Row(row =>
|
||||||
{
|
{
|
||||||
row.ConstantColumn(25).Text($"{number}.");
|
row.ConstantItem(20).Text($"{number}.");
|
||||||
row.RelativeColumn().Text(locationName);
|
row.AutoItem().Text(locationName);
|
||||||
row.ConstantColumn(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName));
|
|
||||||
|
row.RelativeItem().PaddingHorizontal(2).AlignBottom().TranslateY(-3).Height(1).Canvas((canvas, space) =>
|
||||||
|
{
|
||||||
|
// best to statically cache
|
||||||
|
using var paint = new SKPaint
|
||||||
|
{
|
||||||
|
StrokeWidth = space.Height,
|
||||||
|
PathEffect = SKPathEffect.CreateDash(new float[] { 1, 3 }, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas.DrawLine(0, 0, space.Width, 0, paint);
|
||||||
|
});
|
||||||
|
|
||||||
|
row.AutoItem().Text(text =>
|
||||||
|
{
|
||||||
|
text.BeginPageNumberOfSection(locationName);
|
||||||
|
text.Span(" - ");
|
||||||
|
text.EndPageNumberOfSection(locationName);
|
||||||
|
|
||||||
|
var lengthStyle = TextStyle.Default.FontColor(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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace QuestPDF.ReportSample
|
|||||||
Content = documentContainer.Compose();
|
Content = documentContainer.Compose();
|
||||||
|
|
||||||
PageContext = new PageContext();
|
PageContext = new PageContext();
|
||||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, Metadata, null);
|
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, null);
|
||||||
|
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
@@ -69,7 +69,7 @@ namespace QuestPDF.ReportSample
|
|||||||
[Benchmark]
|
[Benchmark]
|
||||||
public void GenerationTest()
|
public void GenerationTest()
|
||||||
{
|
{
|
||||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, Metadata, null);
|
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<LangVersion>8</LangVersion>
|
<LangVersion>8</LangVersion>
|
||||||
<RootNamespace>QuestPDF.ReportSample</RootNamespace>
|
<RootNamespace>QuestPDF.ReportSample</RootNamespace>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<PackageReference Include="nunit" Version="3.13.2" />
|
<PackageReference Include="nunit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
<PackageReference Include="SkiaSharp" Version="2.80.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -24,7 +24,7 @@ namespace QuestPDF.ReportSample
|
|||||||
[Test]
|
[Test]
|
||||||
public void GenerateAndShowPdf()
|
public void GenerateAndShowPdf()
|
||||||
{
|
{
|
||||||
ImagePlaceholder.Solid = true;
|
//ImagePlaceholder.Solid = true;
|
||||||
|
|
||||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
|
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
|
||||||
Report.GeneratePdf(path);
|
Report.GeneratePdf(path);
|
||||||
@@ -48,11 +48,10 @@ namespace QuestPDF.ReportSample
|
|||||||
Report.Compose(container);
|
Report.Compose(container);
|
||||||
var content = container.Compose();
|
var content = container.Compose();
|
||||||
|
|
||||||
var metadata = Report.GetMetadata();
|
|
||||||
var pageContext = new PageContext();
|
var pageContext = new PageContext();
|
||||||
|
|
||||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, metadata, null);
|
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, null);
|
||||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, metadata, null);
|
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,8 @@ namespace QuestPDF.ReportSample
|
|||||||
{
|
{
|
||||||
public static class Typography
|
public static class Typography
|
||||||
{
|
{
|
||||||
public static TextStyle Title => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Darken3).Size(26).Black();
|
public static TextStyle Title => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Darken3).FontSize(26).Black();
|
||||||
public static TextStyle Headline => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Medium).Size(16).SemiBold();
|
public static TextStyle Headline => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Medium).FontSize(16).SemiBold();
|
||||||
public static TextStyle Normal => TextStyle.Default.FontType(Fonts.Verdana).Color(Colors.Black).Size(10).LineHeight(1.2f);
|
public static TextStyle Normal => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Black).FontSize(10).LineHeight(1.2f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using NUnit.Framework;
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
using QuestPDF.Drawing;
|
using QuestPDF.Drawing;
|
||||||
using QuestPDF.Elements;
|
using QuestPDF.Elements;
|
||||||
using QuestPDF.Fluent;
|
using QuestPDF.Fluent;
|
||||||
@@ -8,19 +9,40 @@ using QuestPDF.UnitTests.TestEngine;
|
|||||||
namespace QuestPDF.UnitTests
|
namespace QuestPDF.UnitTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class StackTests
|
public class ColumnTests
|
||||||
{
|
{
|
||||||
|
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
|
#region Measure
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure_ReturnsWrap_WhenFirstChildWraps()
|
public void Measure_ReturnsWrap_WhenFirstChildWraps()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
||||||
.CheckMeasureResult(SpacePlan.Wrap());
|
.CheckMeasureResult(SpacePlan.Wrap());
|
||||||
@@ -30,11 +52,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
|
public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(300, 200))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(300, 200))
|
||||||
.CheckMeasureResult(SpacePlan.PartialRender(300, 200));
|
.CheckMeasureResult(SpacePlan.PartialRender(300, 200));
|
||||||
@@ -44,11 +62,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
|
public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||||
@@ -59,11 +73,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
|
public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(300, 150))
|
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(300, 150))
|
||||||
@@ -74,33 +84,13 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
|
public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(100, 50))
|
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(100, 50))
|
||||||
.CheckMeasureResult(SpacePlan.FullRender(200, 150));
|
.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
|
#endregion
|
||||||
|
|
||||||
#region Draw
|
#region Draw
|
||||||
@@ -109,11 +99,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_WhenFirstChildWraps()
|
public void Draw_WhenFirstChildWraps()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
||||||
.CheckDrawResult();
|
.CheckDrawResult();
|
||||||
@@ -123,14 +109,12 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_WhenFirstChildPartiallyRenders()
|
public void Draw_WhenFirstChildPartiallyRenders()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(200, 100))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.ExpectChildDraw("first", new Size(400, 100))
|
.ExpectChildDraw("first", new Size(400, 100))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.CheckDrawResult();
|
.CheckDrawResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,15 +122,13 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
|
public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||||
.ExpectChildDraw("first", new Size(400, 100))
|
|
||||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
|
.ExpectChildDraw("first", new Size(400, 100))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.CheckDrawResult();
|
.CheckDrawResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,15 +136,13 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
|
public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||||
.ExpectChildDraw("first", new Size(400, 100))
|
|
||||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
|
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
|
.ExpectChildDraw("first", new Size(400, 100))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.ExpectCanvasTranslate(0, 100)
|
.ExpectCanvasTranslate(0, 100)
|
||||||
.ExpectChildDraw("second", new Size(400, 150))
|
.ExpectChildDraw("second", new Size(400, 150))
|
||||||
.ExpectCanvasTranslate(0, -100)
|
.ExpectCanvasTranslate(0, -100)
|
||||||
@@ -173,15 +153,13 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
|
public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItems)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second")
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||||
.ExpectChildDraw("first", new Size(400, 100))
|
|
||||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
|
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
|
.ExpectChildDraw("first", new Size(400, 100))
|
||||||
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.ExpectCanvasTranslate(0, 100)
|
.ExpectCanvasTranslate(0, 100)
|
||||||
.ExpectChildDraw("second", new Size(400, 150))
|
.ExpectChildDraw("second", new Size(400, 150))
|
||||||
.ExpectCanvasTranslate(0, -100)
|
.ExpectCanvasTranslate(0, -100)
|
||||||
@@ -192,19 +170,13 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_UsesEmpty_WhenFirstChildIsRendered()
|
public void Draw_UsesEmpty_WhenFirstChildIsRendered()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second"),
|
|
||||||
|
|
||||||
IsFirstRendered = true
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.PartialRender(200, 300))
|
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.PartialRender(200, 300))
|
||||||
.ExpectCanvasTranslate(0, 0)
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.ExpectChildDraw("second", new Size(400, 300))
|
.ExpectChildDraw("second", new Size(400, 300))
|
||||||
.ExpectCanvasTranslate(0, 0)
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.CheckState<BinaryStack>(x => x.IsFirstRendered)
|
.CheckState<Column>(x => x.Items.First().IsRendered)
|
||||||
.CheckDrawResult();
|
.CheckDrawResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,97 +184,14 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
|
public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryStack
|
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
|
||||||
{
|
|
||||||
First = x.CreateChild("first"),
|
|
||||||
Second = x.CreateChild("second"),
|
|
||||||
|
|
||||||
IsFirstRendered = true
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
|
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
|
||||||
.ExpectCanvasTranslate(0, 0)
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.ExpectChildDraw("second", new Size(400, 300))
|
.ExpectChildDraw("second", new Size(400, 300))
|
||||||
.ExpectCanvasTranslate(0, 0)
|
.ExpectCanvasTranslate(0, 0)
|
||||||
.CheckDrawResult()
|
.CheckDrawResult()
|
||||||
.CheckState<BinaryStack>(x => !x.IsFirstRendered);
|
.CheckState<Column>(x => !x.Items.First().IsRendered);
|
||||||
}
|
|
||||||
|
|
||||||
#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
|
#endregion
|
||||||
@@ -9,35 +9,27 @@ namespace QuestPDF.UnitTests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class DecorationTests
|
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
|
#region Measure
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure_ReturnsWrap_WhenDecorationReturnsWrap()
|
public void Measure_ReturnsWrap_WhenBeforeReturnsWrap()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryDecoration
|
.For(CreateDecoration)
|
||||||
{
|
|
||||||
Type = DecorationType.Append,
|
|
||||||
DecorationElement = x.CreateChild("decoration"),
|
|
||||||
ContentElement = x.CreateChild("content")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.Wrap())
|
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.Wrap())
|
||||||
.CheckMeasureResult(SpacePlan.Wrap());
|
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||||
}
|
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||||
|
|
||||||
[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());
|
.CheckMeasureResult(SpacePlan.Wrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,89 +37,100 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_ReturnsWrap_WhenContentReturnsWrap()
|
public void Measure_ReturnsWrap_WhenContentReturnsWrap()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryDecoration
|
.For(CreateDecoration)
|
||||||
{
|
|
||||||
Type = DecorationType.Append,
|
|
||||||
DecorationElement = x.CreateChild("decoration"),
|
|
||||||
ContentElement = x.CreateChild("content")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
.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.Wrap())
|
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.Wrap())
|
||||||
.CheckMeasureResult(SpacePlan.Wrap());
|
.CheckMeasureResult(SpacePlan.Wrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure_ReturnsPartialRender_WhenContentReturnsPartialRender()
|
public void Measure_ReturnsWrap_WhenAfterReturnsWrap()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryDecoration
|
.For(CreateDecoration)
|
||||||
{
|
|
||||||
Type = DecorationType.Append,
|
|
||||||
DecorationElement = x.CreateChild("decoration"),
|
|
||||||
ContentElement = x.CreateChild("content")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(200, 150))
|
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.Wrap())
|
||||||
.CheckMeasureResult(SpacePlan.PartialRender(400, 250));
|
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||||
|
.CheckMeasureResult(SpacePlan.Wrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure_ReturnsFullRender_WhenContentReturnsFullRender()
|
public void Measure_ReturnsWrap_WhenBeforeReturnsPartialRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryDecoration
|
.For(CreateDecoration)
|
||||||
{
|
|
||||||
Type = DecorationType.Append,
|
|
||||||
DecorationElement = x.CreateChild("decoration"),
|
|
||||||
ContentElement = x.CreateChild("content")
|
|
||||||
})
|
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.PartialRender(100, 50))
|
||||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(200, 150))
|
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||||
.CheckMeasureResult(SpacePlan.FullRender(400, 250));
|
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||||
|
.CheckMeasureResult(SpacePlan.Wrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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
|
#endregion
|
||||||
|
|
||||||
#region Draw
|
#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]
|
[Test]
|
||||||
public void Draw_Append()
|
public void Draw_Append()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new BinaryDecoration
|
.For(CreateDecoration)
|
||||||
{
|
|
||||||
Type = DecorationType.Append,
|
|
||||||
DecorationElement = x.CreateChild("decoration"),
|
|
||||||
ContentElement = x.CreateChild("content")
|
|
||||||
})
|
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(200, 40))
|
||||||
.ExpectChildDraw("content", new Size(400, 200))
|
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(200, 60))
|
||||||
.ExpectCanvasTranslate(0, 200)
|
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(300, 100))
|
||||||
.ExpectChildDraw("decoration", new Size(400, 100))
|
|
||||||
.ExpectCanvasTranslate(0, -200)
|
.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)
|
||||||
|
|
||||||
.CheckDrawResult();
|
.CheckDrawResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public class ExternalLinkTests
|
public class ExternalLinkTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure() => SimpleContainerTests.Measure<ExternalLink>();
|
public void Measure() => SimpleContainerTests.Measure<Hyperlink>();
|
||||||
|
|
||||||
// TODO: consider tests for the Draw method
|
// TODO: consider tests for the Draw method
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,26 +39,26 @@ namespace QuestPDF.UnitTests
|
|||||||
// assert
|
// assert
|
||||||
var expected = new Container();
|
var expected = new Container();
|
||||||
|
|
||||||
expected.Stack(stack =>
|
expected.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(6).Element(childA);
|
row.RelativeItem(6).Element(childA);
|
||||||
row.RelativeColumn(4).Element(childB);
|
row.RelativeItem(4).Element(childB);
|
||||||
row.RelativeColumn(2);
|
row.RelativeItem(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(4).Element(childC);
|
row.RelativeItem(4).Element(childC);
|
||||||
row.RelativeColumn(2).Element(childD);
|
row.RelativeItem(2).Element(childD);
|
||||||
row.RelativeColumn(6);
|
row.RelativeItem(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(8).Element(childE);
|
row.RelativeItem(8).Element(childE);
|
||||||
row.RelativeColumn(4);
|
row.RelativeItem(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -93,29 +93,29 @@ namespace QuestPDF.UnitTests
|
|||||||
// assert
|
// assert
|
||||||
var expected = new Container();
|
var expected = new Container();
|
||||||
|
|
||||||
expected.Stack(stack =>
|
expected.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(1);
|
row.RelativeItem(1);
|
||||||
row.RelativeColumn(6).Element(childA);
|
row.RelativeItem(6).Element(childA);
|
||||||
row.RelativeColumn(4).Element(childB);
|
row.RelativeItem(4).Element(childB);
|
||||||
row.RelativeColumn(1);
|
row.RelativeItem(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(3);
|
row.RelativeItem(3);
|
||||||
row.RelativeColumn(4).Element(childC);
|
row.RelativeItem(4).Element(childC);
|
||||||
row.RelativeColumn(2).Element(childD);
|
row.RelativeItem(2).Element(childD);
|
||||||
row.RelativeColumn(3);
|
row.RelativeItem(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2);
|
row.RelativeItem(2);
|
||||||
row.RelativeColumn(8).Element(childE);
|
row.RelativeItem(8).Element(childE);
|
||||||
row.RelativeColumn(2);
|
row.RelativeItem(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -150,26 +150,26 @@ namespace QuestPDF.UnitTests
|
|||||||
// assert
|
// assert
|
||||||
var expected = new Container();
|
var expected = new Container();
|
||||||
|
|
||||||
expected.Stack(stack =>
|
expected.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(2);
|
row.RelativeItem(2);
|
||||||
row.RelativeColumn(6).Element(childA);
|
row.RelativeItem(6).Element(childA);
|
||||||
row.RelativeColumn(4).Element(childB);
|
row.RelativeItem(4).Element(childB);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(6);
|
row.RelativeItem(6);
|
||||||
row.RelativeColumn(4).Element(childC);
|
row.RelativeItem(4).Element(childC);
|
||||||
row.RelativeColumn(2).Element(childD);
|
row.RelativeItem(2).Element(childD);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeColumn(4);
|
row.RelativeItem(4);
|
||||||
row.RelativeColumn(8).Element(childE);
|
row.RelativeItem(8).Element(childE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -210,36 +210,36 @@ namespace QuestPDF.UnitTests
|
|||||||
// assert
|
// assert
|
||||||
var expected = new Container();
|
var expected = new Container();
|
||||||
|
|
||||||
expected.Stack(stack =>
|
expected.Column(column =>
|
||||||
{
|
{
|
||||||
stack.Spacing(20);
|
column.Spacing(20);
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.Spacing(30);
|
row.Spacing(30);
|
||||||
|
|
||||||
row.RelativeColumn(3);
|
row.RelativeItem(3);
|
||||||
row.RelativeColumn(5).Element(childA);
|
row.RelativeItem(5).Element(childA);
|
||||||
row.RelativeColumn(5).Element(childB);
|
row.RelativeItem(5).Element(childB);
|
||||||
row.RelativeColumn(3);
|
row.RelativeItem(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.Spacing(30);
|
row.Spacing(30);
|
||||||
|
|
||||||
row.RelativeColumn(3);
|
row.RelativeItem(3);
|
||||||
row.RelativeColumn(10).Element(childC);
|
row.RelativeItem(10).Element(childC);
|
||||||
row.RelativeColumn(3);
|
row.RelativeItem(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
stack.Item().Row(row =>
|
column.Item().Row(row =>
|
||||||
{
|
{
|
||||||
row.Spacing(30);
|
row.Spacing(30);
|
||||||
|
|
||||||
row.RelativeColumn(2);
|
row.RelativeItem(2);
|
||||||
row.RelativeColumn(12).Element(childD);
|
row.RelativeItem(12).Element(childD);
|
||||||
row.RelativeColumn(2);
|
row.RelativeItem(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public class InternalLinkTests
|
public class InternalLinkTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
|
||||||
|
|
||||||
// TODO: consider tests for the Draw method
|
// TODO: consider tests for the Draw method
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
|||||||
public class InternalLocationTests
|
public class InternalLocationTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
|
||||||
|
|
||||||
// TODO: consider tests for the Draw method
|
// TODO: consider tests for the Draw method
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentAssertions" Version="6.1.0" />
|
<PackageReference Include="FluentAssertions" Version="6.7.0" />
|
||||||
<PackageReference Include="nunit" Version="3.13.2" />
|
<PackageReference Include="nunit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
<PackageReference Include="SkiaSharp" Version="2.80.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,268 +0,0 @@
|
|||||||
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))
|
|
||||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.FullRender(150, 200))
|
|
||||||
.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,18 +7,20 @@ using QuestPDF.UnitTests.TestEngine;
|
|||||||
namespace QuestPDF.UnitTests
|
namespace QuestPDF.UnitTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class BoxTests
|
public class ShrinkTests
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void Measure() => SimpleContainerTests.Measure<MinimalBox>();
|
public void Measure() => SimpleContainerTests.Measure<Shrink>();
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Draw_Wrap()
|
public void Draw_Wrap()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new MinimalBox
|
.For(x => new Shrink
|
||||||
{
|
{
|
||||||
Child = x.CreateChild()
|
Child = x.CreateChild(),
|
||||||
|
ShrinkVertical = true,
|
||||||
|
ShrinkHorizontal = true
|
||||||
})
|
})
|
||||||
.DrawElement(new Size(400, 300))
|
.DrawElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.Wrap())
|
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.Wrap())
|
||||||
@@ -29,9 +31,11 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_PartialRender()
|
public void Measure_PartialRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new MinimalBox
|
.For(x => new Shrink
|
||||||
{
|
{
|
||||||
Child = x.CreateChild()
|
Child = x.CreateChild(),
|
||||||
|
ShrinkVertical = true,
|
||||||
|
ShrinkHorizontal = true
|
||||||
})
|
})
|
||||||
.MeasureElement(new Size(400, 300))
|
.MeasureElement(new Size(400, 300))
|
||||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
|
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
|
||||||
@@ -43,9 +47,11 @@ namespace QuestPDF.UnitTests
|
|||||||
public void Measure_FullRender()
|
public void Measure_FullRender()
|
||||||
{
|
{
|
||||||
TestPlan
|
TestPlan
|
||||||
.For(x => new MinimalBox
|
.For(x => new Shrink
|
||||||
{
|
{
|
||||||
Child = x.CreateChild()
|
Child = x.CreateChild(),
|
||||||
|
ShrinkVertical = true,
|
||||||
|
ShrinkHorizontal = true
|
||||||
})
|
})
|
||||||
.MeasureElement(new Size(500, 400))
|
.MeasureElement(new Size(500, 400))
|
||||||
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
|
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
@@ -10,7 +11,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
public Action<float> RotateFunc { get; set; }
|
public Action<float> RotateFunc { get; set; }
|
||||||
public Action<float, float> ScaleFunc { get; set; }
|
public Action<float, float> ScaleFunc { get; set; }
|
||||||
public Action<SKImage, Position, Size> DrawImageFunc { 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> DrawRectFunc { get; set; }
|
||||||
|
|
||||||
public void Translate(Position vector) => TranslateFunc(vector);
|
public void Translate(Position vector) => TranslateFunc(vector);
|
||||||
@@ -18,11 +18,11 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
public void Scale(float scaleX, float scaleY) => ScaleFunc(scaleX, scaleY);
|
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 DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
|
||||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||||
|
|
||||||
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
|
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
|
||||||
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
|
public void DrawSectionLink(string sectionName, Size size) => throw new NotImplementedException();
|
||||||
public void DrawLocation(string locationName) => throw new NotImplementedException();
|
public void DrawSection(string sectionName) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using QuestPDF.Drawing;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using QuestPDF.UnitTests.TestEngine.Operations;
|
using QuestPDF.UnitTests.TestEngine.Operations;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@@ -15,11 +16,11 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
public void Scale(float scaleX, float scaleY) => Operations.Add(new CanvasScaleOperation(scaleX, scaleY));
|
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 DrawRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawRectangleOperation(vector, size, color));
|
||||||
public void DrawText(string text, Position position, TextStyle style) => Operations.Add(new CanvasDrawTextOperation(text, position, style));
|
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
|
||||||
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
|
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
|
||||||
|
|
||||||
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
|
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
|
||||||
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
|
public void DrawSectionLink(string sectionName, Size size) => throw new NotImplementedException();
|
||||||
public void DrawLocation(string locationName) => throw new NotImplementedException();
|
public void DrawSection(string sectionName) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,19 +82,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
|
|
||||||
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
|
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
|
||||||
},
|
},
|
||||||
DrawTextFunc = (text, position, style) =>
|
|
||||||
{
|
|
||||||
var expected = GetExpected<CanvasDrawTextOperation>();
|
|
||||||
|
|
||||||
Assert.AreEqual(expected.Text, text);
|
|
||||||
|
|
||||||
Assert.AreEqual(expected.Position.X, position.X, "Draw text: X");
|
|
||||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw text: Y");
|
|
||||||
|
|
||||||
Assert.AreEqual(expected.Style.Color, style.Color, "Draw text: color");
|
|
||||||
Assert.AreEqual(expected.Style.FontType, style.FontType, "Draw text: font");
|
|
||||||
Assert.AreEqual(expected.Style.Size, style.Size, "Draw text: size");
|
|
||||||
},
|
|
||||||
DrawImageFunc = (image, position, size) =>
|
DrawImageFunc = (image, position, size) =>
|
||||||
{
|
{
|
||||||
var expected = GetExpected<CanvasDrawImageOperation>();
|
var expected = GetExpected<CanvasDrawImageOperation>();
|
||||||
@@ -201,11 +188,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
|||||||
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
|
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestPlan ExpectCanvasDrawText(string text, Position position, TextStyle style)
|
|
||||||
{
|
|
||||||
return AddOperation(new CanvasDrawTextOperation(text, position, style));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestPlan ExpectCanvasDrawImage(Position position, Size size)
|
public TestPlan ExpectCanvasDrawImage(Position position, Size size)
|
||||||
{
|
{
|
||||||
return AddOperation(new CanvasDrawImageOperation(position, size));
|
return AddOperation(new CanvasDrawImageOperation(position, size));
|
||||||
|
|||||||
@@ -8,6 +8,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.UnitTests", "Quest
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
|
||||||
EndProject
|
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
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer", "QuestPDF.Previewer\QuestPDF.Previewer.csproj", "{B2FF6003-3A45-4A78-A85D-B86C7F01D054}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -30,5 +39,13 @@ Global
|
|||||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.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
|
||||||
|
{B2FF6003-3A45-4A78-A85D-B86C7F01D054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B2FF6003-3A45-4A78-A85D-B86C7F01D054}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B2FF6003-3A45-4A78-A85D-B86C7F01D054}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B2FF6003-3A45-4A78-A85D-B86C7F01D054}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
|
||||||
[assembly: InternalsVisibleTo("QuestPDF.UnitTests")]
|
|
||||||
[assembly: InternalsVisibleTo("QuestPDF.Examples")]
|
|
||||||
[assembly: InternalsVisibleTo("QuestPDF.ReportSample")]
|
|
||||||
@@ -16,13 +16,13 @@ namespace QuestPDF.Drawing
|
|||||||
var container = new Container();
|
var container = new Container();
|
||||||
|
|
||||||
container
|
container
|
||||||
.Stack(stack =>
|
.Column(column =>
|
||||||
{
|
{
|
||||||
Pages
|
Pages
|
||||||
.SelectMany(x => new List<Action>()
|
.SelectMany(x => new List<Action>()
|
||||||
{
|
{
|
||||||
() => stack.Item().PageBreak(),
|
() => column.Item().PageBreak(),
|
||||||
() => stack.Item().Component(x)
|
() => column.Item().Component(x)
|
||||||
})
|
})
|
||||||
.Skip(1)
|
.Skip(1)
|
||||||
.ToList()
|
.ToList()
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
internal static void GeneratePdf(Stream stream, IDocument document)
|
internal static void GeneratePdf(Stream stream, IDocument document)
|
||||||
{
|
{
|
||||||
|
CheckIfStreamIsCompatible(stream);
|
||||||
|
|
||||||
var metadata = document.GetMetadata();
|
var metadata = document.GetMetadata();
|
||||||
var canvas = new PdfCanvas(stream, metadata);
|
var canvas = new PdfCanvas(stream, metadata);
|
||||||
RenderDocument(canvas, document);
|
RenderDocument(canvas, document);
|
||||||
@@ -24,10 +26,21 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
internal static void GenerateXps(Stream stream, IDocument document)
|
internal static void GenerateXps(Stream stream, IDocument document)
|
||||||
{
|
{
|
||||||
|
CheckIfStreamIsCompatible(stream);
|
||||||
|
|
||||||
var metadata = document.GetMetadata();
|
var metadata = document.GetMetadata();
|
||||||
var canvas = new XpsCanvas(stream, metadata);
|
var canvas = new XpsCanvas(stream, metadata);
|
||||||
RenderDocument(canvas, document);
|
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)
|
internal static ICollection<byte[]> GenerateImages(IDocument document)
|
||||||
{
|
{
|
||||||
@@ -38,7 +51,14 @@ namespace QuestPDF.Drawing
|
|||||||
return canvas.Images;
|
return canvas.Images;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
|
internal static ICollection<PreviewerPicture> GeneratePreviewerPictures(IDocument document)
|
||||||
|
{
|
||||||
|
var canvas = new SkiaPictureCanvas();
|
||||||
|
RenderDocument(canvas, document);
|
||||||
|
return canvas.Pictures;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
|
||||||
where TCanvas : ICanvas, IRenderingCanvas
|
where TCanvas : ICanvas, IRenderingCanvas
|
||||||
{
|
{
|
||||||
var container = new DocumentContainer();
|
var container = new DocumentContainer();
|
||||||
@@ -46,19 +66,17 @@ namespace QuestPDF.Drawing
|
|||||||
var content = container.Compose();
|
var content = container.Compose();
|
||||||
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
||||||
|
|
||||||
var metadata = document.GetMetadata();
|
var debuggingState = Settings.EnableDebugging ? ApplyDebugging(content) : null;
|
||||||
var pageContext = new PageContext();
|
|
||||||
|
|
||||||
var debuggingState = metadata.ApplyDebugging ? ApplyDebugging(content) : null;
|
|
||||||
|
|
||||||
if (metadata.ApplyCaching)
|
if (Settings.EnableCaching)
|
||||||
ApplyCaching(content);
|
ApplyCaching(content);
|
||||||
|
|
||||||
RenderPass(pageContext, new FreeCanvas(), content, metadata, debuggingState);
|
var pageContext = new PageContext();
|
||||||
RenderPass(pageContext, canvas, content, metadata, debuggingState);
|
RenderPass(pageContext, new FreeCanvas(), content, debuggingState);
|
||||||
|
RenderPass(pageContext, canvas, content, debuggingState);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata, DebuggingState? debuggingState)
|
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DebuggingState? debuggingState)
|
||||||
where TCanvas : ICanvas, IRenderingCanvas
|
where TCanvas : ICanvas, IRenderingCanvas
|
||||||
{
|
{
|
||||||
content.VisitChildren(x => x?.Initialize(pageContext, canvas));
|
content.VisitChildren(x => x?.Initialize(pageContext, canvas));
|
||||||
@@ -94,7 +112,7 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
canvas.EndPage();
|
canvas.EndPage();
|
||||||
|
|
||||||
if (currentPage >= documentMetadata.DocumentLayoutExceptionThreshold)
|
if (currentPage >= Settings.DocumentLayoutExceptionThreshold)
|
||||||
{
|
{
|
||||||
canvas.EndDocument();
|
canvas.EndDocument();
|
||||||
ThrowLayoutException();
|
ThrowLayoutException();
|
||||||
@@ -111,15 +129,14 @@ namespace QuestPDF.Drawing
|
|||||||
void ThrowLayoutException()
|
void ThrowLayoutException()
|
||||||
{
|
{
|
||||||
var message = $"Composed layout generates infinite document. This may happen in two cases. " +
|
var message = $"Composed layout generates infinite document. This may happen in two cases. " +
|
||||||
$"1) Your document and its layout configuration is correct but the content takes more than {documentMetadata.DocumentLayoutExceptionThreshold} pages. " +
|
$"1) Your document and its layout configuration is correct but the content takes more than {Settings.DocumentLayoutExceptionThreshold} pages. " +
|
||||||
$"In this case, please increase the value {nameof(DocumentMetadata)}.{nameof(DocumentMetadata.DocumentLayoutExceptionThreshold)} property configured in the {nameof(IDocument.GetMetadata)} method. " +
|
$"In this case, please increase the value {nameof(QuestPDF)}.{nameof(Settings)}.{nameof(Settings.DocumentLayoutExceptionThreshold)} static property. " +
|
||||||
$"2) The layout configuration of your document is invalid. Some of the elements require more space than is provided." +
|
$"2) The layout configuration of your document is invalid. Some of the elements require more space than is provided." +
|
||||||
$"Please analyze your documents structure to detect this element and fix its size constraints.";
|
$"Please analyze your documents structure to detect this element and fix its size constraints.";
|
||||||
|
|
||||||
throw new DocumentLayoutException(message)
|
var elementTrace = debuggingState?.BuildTrace() ?? "Debug trace is available only in the DEBUG mode.";
|
||||||
{
|
|
||||||
ElementTrace = debuggingState?.BuildTrace() ?? "Debug trace is available only in the DEBUG mode."
|
throw new DocumentLayoutException(message, elementTrace);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +161,7 @@ namespace QuestPDF.Drawing
|
|||||||
return debuggingState;
|
return debuggingState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
||||||
{
|
{
|
||||||
if (content == null)
|
if (content == null)
|
||||||
return;
|
return;
|
||||||
@@ -155,7 +172,7 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
if (textBlockItem is TextBlockSpan textSpan)
|
if (textBlockItem is TextBlockSpan textSpan)
|
||||||
{
|
{
|
||||||
textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
textSpan.Style = textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||||
}
|
}
|
||||||
else if (textBlockItem is TextBlockElement textElement)
|
else if (textBlockItem is TextBlockElement textElement)
|
||||||
{
|
{
|
||||||
@@ -166,16 +183,14 @@ namespace QuestPDF.Drawing
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetTextStyle = documentDefaultTextStyle;
|
if (content is DynamicHost dynamicHost)
|
||||||
|
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||||
|
|
||||||
if (content is DefaultTextStyle defaultTextStyleElement)
|
if (content is DefaultTextStyle defaultTextStyleElement)
|
||||||
{
|
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||||
defaultTextStyleElement.TextStyle.ApplyParentStyle(documentDefaultTextStyle);
|
|
||||||
targetTextStyle = defaultTextStyleElement.TextStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var child in content.GetChildren())
|
foreach (var child in content.GetChildren())
|
||||||
ApplyDefaultTextStyle(child, targetTextStyle);
|
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using QuestPDF.Infrastructure;
|
||||||
|
|
||||||
namespace QuestPDF.Drawing
|
namespace QuestPDF.Drawing
|
||||||
{
|
{
|
||||||
@@ -18,14 +19,26 @@ namespace QuestPDF.Drawing
|
|||||||
public DateTime CreationDate { get; set; } = DateTime.Now;
|
public DateTime CreationDate { get; set; } = DateTime.Now;
|
||||||
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
/// <summary>
|
[Obsolete("This API has been moved since version 2022.9. Please use the QuestPDF.Settings.DocumentLayoutExceptionThreshold static property.")]
|
||||||
/// If the number of generated pages exceeds this threshold
|
public int DocumentLayoutExceptionThreshold
|
||||||
/// (likely due to infinite layout), the exception is thrown.
|
{
|
||||||
/// </summary>
|
get => Settings.DocumentLayoutExceptionThreshold;
|
||||||
public int DocumentLayoutExceptionThreshold { get; set; } = 250;
|
set => Settings.DocumentLayoutExceptionThreshold = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool ApplyCaching { get; set; } = !System.Diagnostics.Debugger.IsAttached;
|
[Obsolete("This API has been moved since version 2022.9. Please use the QuestPDF.Settings.EnableCaching static property.")]
|
||||||
public bool ApplyDebugging { get; set; } = System.Diagnostics.Debugger.IsAttached;
|
public bool ApplyCaching
|
||||||
|
{
|
||||||
|
get => Settings.EnableCaching;
|
||||||
|
set => Settings.EnableCaching = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("This API has been moved since version 2022.9. Please use the QuestPDF.Settings.EnableDebugging static property.")]
|
||||||
|
public bool ApplyDebugging
|
||||||
|
{
|
||||||
|
get => Settings.EnableDebugging;
|
||||||
|
set => Settings.EnableDebugging = value;
|
||||||
|
}
|
||||||
|
|
||||||
public static DocumentMetadata Default => new DocumentMetadata();
|
public static DocumentMetadata Default => new DocumentMetadata();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,7 @@ namespace QuestPDF.Drawing.Exceptions
|
|||||||
{
|
{
|
||||||
public class DocumentComposeException : Exception
|
public class DocumentComposeException : Exception
|
||||||
{
|
{
|
||||||
public DocumentComposeException()
|
internal DocumentComposeException(string message) : base(message)
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentComposeException(string message) : base(message)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentComposeException(string message, Exception inner) : base(message, inner)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,12 @@ namespace QuestPDF.Drawing.Exceptions
|
|||||||
{
|
{
|
||||||
public class DocumentDrawingException : Exception
|
public class DocumentDrawingException : Exception
|
||||||
{
|
{
|
||||||
public DocumentDrawingException()
|
internal DocumentDrawingException(string message) : base(message)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DocumentDrawingException(string message) : base(message)
|
internal DocumentDrawingException(string message, Exception inner) : base(message, inner)
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentDrawingException(string message, Exception inner) : base(message, inner)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,21 +4,11 @@ namespace QuestPDF.Drawing.Exceptions
|
|||||||
{
|
{
|
||||||
public class DocumentLayoutException : Exception
|
public class DocumentLayoutException : Exception
|
||||||
{
|
{
|
||||||
public string ElementTrace { get; set; }
|
public string? ElementTrace { get; }
|
||||||
|
|
||||||
public DocumentLayoutException()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentLayoutException(string message) : base(message)
|
internal DocumentLayoutException(string message, string? elementTrace = null) : base(message)
|
||||||
{
|
{
|
||||||
|
ElementTrace = elementTrace;
|
||||||
}
|
|
||||||
|
|
||||||
public DocumentLayoutException(string message, Exception inner) : base(message, inner)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace QuestPDF.Drawing.Exceptions
|
||||||
|
{
|
||||||
|
public class InitializationException : Exception
|
||||||
|
{
|
||||||
|
internal InitializationException(string documentType, Exception innerException) : base(CreateMessage(documentType, innerException.Message), innerException)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateMessage(string documentType, string innerExceptionMessage)
|
||||||
|
{
|
||||||
|
var (libraryName, nugetConvention) = GetLibraryName();
|
||||||
|
|
||||||
|
return $"Cannot create the {documentType} document using the {libraryName} library. " +
|
||||||
|
$"This exception usually means that, on your operating system where you run the application, {libraryName} requires installing additional dependencies. " +
|
||||||
|
$"Such dependencies are available as additional nuget packages, for example {nugetConvention}.Linux.NoDependencies. " +
|
||||||
|
$"Some operating systems may require installing multiple nugets, e.g. MacOS may need both {nugetConvention}.macOS.NoDependencies and {nugetConvention}.Linux.NoDependencies." +
|
||||||
|
$"Please refer to the {libraryName} documentation for more details. " +
|
||||||
|
$"Also, please consult the inner exception that has been originally thrown by the dependency library.";
|
||||||
|
|
||||||
|
(string GetLibraryName, string nugetConvention) GetLibraryName()
|
||||||
|
{
|
||||||
|
if (innerExceptionMessage.Contains("libSkiaSharp"))
|
||||||
|
return ("SkiaSharp", "SkiaSharp.NativeAssets");
|
||||||
|
|
||||||
|
if (innerExceptionMessage.Contains("libHarfBuzzSharp"))
|
||||||
|
return ("HarfBuzzSharp", "HarfBuzzSharp.NativeAssets");
|
||||||
|
|
||||||
|
// default
|
||||||
|
return ("SkiaSharp-related", "*.NativeAssets");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+164
-19
@@ -1,66 +1,211 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarfBuzzSharp;
|
||||||
|
using QuestPDF.Fluent;
|
||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
using SkiaSharp.HarfBuzz;
|
||||||
|
|
||||||
namespace QuestPDF.Drawing
|
namespace QuestPDF.Drawing
|
||||||
{
|
{
|
||||||
public static class FontManager
|
public static class FontManager
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<string, SKTypeface> Typefaces = new ConcurrentDictionary<string, SKTypeface>();
|
private static readonly ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
|
||||||
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
|
private static readonly ConcurrentDictionary<TextStyle, SKFontMetrics> FontMetrics = new();
|
||||||
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
private static readonly ConcurrentDictionary<TextStyle, SKPaint> FontPaints = new();
|
||||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
private static readonly ConcurrentDictionary<string, SKPaint> ColorPaints = new();
|
||||||
|
private static readonly ConcurrentDictionary<TextStyle, Font> ShaperFonts = new();
|
||||||
|
private static readonly ConcurrentDictionary<TextStyle, SKFont> Fonts = new();
|
||||||
|
private static readonly ConcurrentDictionary<TextStyle, TextShaper> TextShapers = new();
|
||||||
|
|
||||||
public static void RegisterFontType(string fontName, Stream stream)
|
static FontManager()
|
||||||
{
|
{
|
||||||
Typefaces.TryAdd(fontName, SKTypeface.FromStream(stream));
|
RegisterLibraryDefaultFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.8 this method has been renamed. Please use the RegisterFontWithCustomName method.")]
|
||||||
|
public static void RegisterFontType(string fontName, Stream stream)
|
||||||
|
{
|
||||||
|
RegisterFontWithCustomName(fontName, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterFontWithCustomName(string fontName, Stream stream)
|
||||||
|
{
|
||||||
|
using var fontData = SKData.Create(stream);
|
||||||
|
RegisterFontType(fontData);
|
||||||
|
RegisterFontType(fontData, customName: fontName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterFont(Stream stream)
|
||||||
|
{
|
||||||
|
using var fontData = SKData.Create(stream);
|
||||||
|
RegisterFontType(fontData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterFontFromEmbeddedResource(string pathName)
|
||||||
|
{
|
||||||
|
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(pathName);
|
||||||
|
RegisterFont(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterLibraryDefaultFonts()
|
||||||
|
{
|
||||||
|
var fontFileNames = new[]
|
||||||
|
{
|
||||||
|
"Lato-Black.ttf",
|
||||||
|
"Lato-BlackItalic.ttf",
|
||||||
|
|
||||||
|
"Lato-Bold.ttf",
|
||||||
|
"Lato-BoldItalic.ttf",
|
||||||
|
|
||||||
|
"Lato-Regular.ttf",
|
||||||
|
"Lato-Italic.ttf",
|
||||||
|
|
||||||
|
"Lato-Light.ttf",
|
||||||
|
"Lato-LightItalic.ttf",
|
||||||
|
|
||||||
|
"Lato-Thin.ttf",
|
||||||
|
"Lato-ThinItalic.ttf"
|
||||||
|
};
|
||||||
|
|
||||||
|
fontFileNames
|
||||||
|
.Select(x => $"QuestPDF.Resources.DefaultFont.{x}")
|
||||||
|
.ToList()
|
||||||
|
.ForEach(RegisterFontFromEmbeddedResource);
|
||||||
|
}
|
||||||
|
|
||||||
internal static SKPaint ColorToPaint(this string color)
|
internal static SKPaint ColorToPaint(this string color)
|
||||||
{
|
{
|
||||||
return ColorPaint.GetOrAdd(color, Convert);
|
return ColorPaints.GetOrAdd(color, Convert);
|
||||||
|
|
||||||
static SKPaint Convert(string color)
|
static SKPaint Convert(string color)
|
||||||
{
|
{
|
||||||
return new SKPaint
|
return new SKPaint
|
||||||
{
|
{
|
||||||
Color = SKColor.Parse(color)
|
Color = SKColor.Parse(color),
|
||||||
|
IsAntialias = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static SKPaint ToPaint(this TextStyle style)
|
internal static SKPaint ToPaint(this TextStyle style)
|
||||||
{
|
{
|
||||||
return Paints.GetOrAdd(style.Key, key => Convert(style));
|
return FontPaints.GetOrAdd(style, Convert);
|
||||||
|
|
||||||
static SKPaint Convert(TextStyle style)
|
static SKPaint Convert(TextStyle style)
|
||||||
{
|
{
|
||||||
return new SKPaint
|
return new SKPaint
|
||||||
{
|
{
|
||||||
Color = SKColor.Parse(style.Color),
|
Color = SKColor.Parse(style.Color),
|
||||||
Typeface = GetTypeface(style),
|
Typeface = GetTypeface(style),
|
||||||
TextSize = (style.Size ?? 12),
|
TextSize = (style.Size ?? 12) * GetTextScale(style),
|
||||||
TextEncoding = SKTextEncoding.Utf32
|
IsAntialias = true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static SKTypeface GetTypeface(TextStyle style)
|
static SKTypeface GetTypeface(TextStyle style)
|
||||||
{
|
{
|
||||||
if (Typefaces.TryGetValue(style.FontType, out var result))
|
var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
|
||||||
return result;
|
|
||||||
|
// superscript and subscript use slightly bolder font to match visually line thickness
|
||||||
|
if (style.FontPosition is FontPosition.Superscript or FontPosition.Subscript)
|
||||||
|
{
|
||||||
|
var weightValue = (int)weight;
|
||||||
|
weightValue = Math.Min(weightValue + 100, 1000);
|
||||||
|
|
||||||
|
weight = (SKFontStyleWeight) (weightValue);
|
||||||
|
}
|
||||||
|
|
||||||
var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
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);
|
||||||
|
|
||||||
return SKTypeface.FromFamilyName(style.FontType, (int)(style.FontWeight ?? FontWeight.Normal), (int)SKFontStyleWidth.Normal, slant)
|
if (fontFromDefaultSource != null)
|
||||||
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
|
return fontFromDefaultSource;
|
||||||
|
|
||||||
|
var availableFontNames = string.Join(", ", SKFontManager.Default.GetFontFamilies());
|
||||||
|
|
||||||
|
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. " +
|
||||||
|
$"Available font family names: [{availableFontNames}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
static float GetTextScale(TextStyle style)
|
||||||
|
{
|
||||||
|
return style.FontPosition switch
|
||||||
|
{
|
||||||
|
FontPosition.Normal => 1f,
|
||||||
|
FontPosition.Subscript => 0.625f,
|
||||||
|
FontPosition.Superscript => 0.625f,
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
||||||
{
|
{
|
||||||
return FontMetrics.GetOrAdd(style.Key, key => style.ToPaint().FontMetrics);
|
return FontMetrics.GetOrAdd(style, key => key.NormalPosition().ToPaint().FontMetrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Font ToShaperFont(this TextStyle style)
|
||||||
|
{
|
||||||
|
return ShaperFonts.GetOrAdd(style, key =>
|
||||||
|
{
|
||||||
|
var typeface = key.ToPaint().Typeface;
|
||||||
|
|
||||||
|
using var harfBuzzBlob = typeface.OpenStream(out var ttcIndex).ToHarfBuzzBlob();
|
||||||
|
|
||||||
|
using var face = new Face(harfBuzzBlob, ttcIndex)
|
||||||
|
{
|
||||||
|
Index = ttcIndex,
|
||||||
|
UnitsPerEm = typeface.UnitsPerEm,
|
||||||
|
GlyphCount = typeface.GlyphCount
|
||||||
|
};
|
||||||
|
|
||||||
|
var font = new Font(face);
|
||||||
|
font.SetScale(TextShaper.FontShapingScale, TextShaper.FontShapingScale);
|
||||||
|
font.SetFunctionsOpenType();
|
||||||
|
|
||||||
|
return font;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TextShaper ToTextShaper(this TextStyle style)
|
||||||
|
{
|
||||||
|
return TextShapers.GetOrAdd(style, key => new TextShaper(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SKFont ToFont(this TextStyle style)
|
||||||
|
{
|
||||||
|
return Fonts.GetOrAdd(style, key => key.ToPaint().ToFont());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,7 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawText(string text, Position position, TextStyle style)
|
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -51,17 +51,17 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawExternalLink(string url, Size size)
|
public void DrawHyperlink(string url, Size size)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawLocationLink(string locationName, Size size)
|
public void DrawSectionLink(string sectionName, Size size)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawLocation(string locationName)
|
public void DrawSection(string sectionName)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
using QuestPDF.Infrastructure;
|
using System.IO;
|
||||||
|
using QuestPDF.Drawing.Exceptions;
|
||||||
|
using QuestPDF.Helpers;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace QuestPDF.Drawing
|
namespace QuestPDF.Drawing
|
||||||
@@ -7,11 +9,23 @@ namespace QuestPDF.Drawing
|
|||||||
internal class PdfCanvas : SkiaDocumentCanvasBase
|
internal class PdfCanvas : SkiaDocumentCanvasBase
|
||||||
{
|
{
|
||||||
public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
|
public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
|
||||||
: base(SKDocument.CreatePdf(stream, MapMetadata(documentMetadata)))
|
: base(CreatePdf(stream, documentMetadata))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SKDocument CreatePdf(Stream stream, DocumentMetadata documentMetadata)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return SKDocument.CreatePdf(stream, MapMetadata(documentMetadata));
|
||||||
|
}
|
||||||
|
catch (TypeInitializationException exception)
|
||||||
|
{
|
||||||
|
throw new InitializationException("PDF", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
|
private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
|
||||||
{
|
{
|
||||||
return new SKDocumentPdfMetadata
|
return new SKDocumentPdfMetadata
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using QuestPDF.Infrastructure;
|
using QuestPDF.Infrastructure;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
using SkiaSharp.HarfBuzz;
|
||||||
|
|
||||||
namespace QuestPDF.Drawing
|
namespace QuestPDF.Drawing
|
||||||
{
|
{
|
||||||
@@ -27,9 +28,9 @@ namespace QuestPDF.Drawing
|
|||||||
Canvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
|
Canvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawText(string text, Position vector, TextStyle style)
|
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style)
|
||||||
{
|
{
|
||||||
Canvas.DrawText(text, vector.X, vector.Y, style.ToPaint());
|
Canvas.DrawText(skTextBlob, position.X, position.Y, style.ToPaint());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawImage(SKImage image, Position vector, Size size)
|
public void DrawImage(SKImage image, Position vector, Size size)
|
||||||
@@ -37,19 +38,19 @@ namespace QuestPDF.Drawing
|
|||||||
Canvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
|
Canvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawExternalLink(string url, Size size)
|
public void DrawHyperlink(string url, Size size)
|
||||||
{
|
{
|
||||||
Canvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
|
Canvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawLocationLink(string locationName, Size size)
|
public void DrawSectionLink(string sectionName, Size size)
|
||||||
{
|
{
|
||||||
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
|
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), sectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawLocation(string locationName)
|
public void DrawSection(string sectionName)
|
||||||
{
|
{
|
||||||
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
|
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), sectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Rotate(float angle)
|
public void Rotate(float angle)
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ namespace QuestPDF.Drawing
|
|||||||
{
|
{
|
||||||
internal class SkiaDocumentCanvasBase : SkiaCanvasBase
|
internal class SkiaDocumentCanvasBase : SkiaCanvasBase
|
||||||
{
|
{
|
||||||
private SKDocument Document { get; }
|
private SKDocument? Document { get; }
|
||||||
|
|
||||||
public SkiaDocumentCanvasBase(SKDocument document)
|
protected SkiaDocumentCanvasBase(SKDocument document)
|
||||||
{
|
{
|
||||||
Document = document;
|
Document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
~SkiaDocumentCanvasBase()
|
~SkiaDocumentCanvasBase()
|
||||||
{
|
{
|
||||||
Document.Dispose();
|
Document?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void BeginDocument()
|
public override void BeginDocument()
|
||||||
@@ -25,7 +25,7 @@ namespace QuestPDF.Drawing
|
|||||||
|
|
||||||
public override void EndDocument()
|
public override void EndDocument()
|
||||||
{
|
{
|
||||||
Canvas.Dispose();
|
Canvas?.Dispose();
|
||||||
|
|
||||||
Document.Close();
|
Document.Close();
|
||||||
Document.Dispose();
|
Document.Dispose();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user