Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d39819bf8d |
-131
@@ -1,131 +0,0 @@
|
||||
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\GithubRepos\QuestPDF codebase based on best match to current usage at 16.03.2022
|
||||
# You can modify the rules from these initially generated values to suit your own policies
|
||||
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
|
||||
[*.cs]
|
||||
|
||||
|
||||
#Core editorconfig formatting - indentation
|
||||
|
||||
#use soft tabs (spaces) for indentation
|
||||
indent_style = space
|
||||
|
||||
#Formatting - new line options
|
||||
|
||||
#place catch statements on a new line
|
||||
csharp_new_line_before_catch = true
|
||||
#place else statements on a new line
|
||||
csharp_new_line_before_else = true
|
||||
#require members of anonymous types to be on separate lines
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
#require members of object intializers to be on separate lines
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
#require braces to be on a new line for object_collection_array_initializers, methods, anonymous_types, control_blocks, types, and lambdas (also known as "Allman" style)
|
||||
csharp_new_line_before_open_brace =all
|
||||
|
||||
#Formatting - organize using options
|
||||
|
||||
#sort System.* using directives alphabetically, and place them before other usings
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
#Formatting - spacing options
|
||||
|
||||
#require NO space between a cast and the value
|
||||
csharp_space_after_cast = false
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
#require a space after a keyword in a control flow statement such as a for loop
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
#remove space within empty argument list parentheses
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
#remove space between method call name and opening parenthesis
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
#remove space within empty parameter list parentheses for a method declaration
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
|
||||
#Formatting - wrapping options
|
||||
|
||||
#leave code block on single line
|
||||
csharp_preserve_single_line_blocks = true
|
||||
|
||||
#Style - Code block preferences
|
||||
|
||||
#prefer no curly braces if allowed
|
||||
csharp_prefer_braces = false:suggestion
|
||||
|
||||
#Style - expression bodied member options
|
||||
|
||||
#prefer block bodies for constructors
|
||||
csharp_style_expression_bodied_constructors = false:suggestion
|
||||
#prefer block bodies for methods
|
||||
csharp_style_expression_bodied_methods = false:suggestion
|
||||
#prefer expression-bodied members for properties
|
||||
csharp_style_expression_bodied_properties = true:suggestion
|
||||
|
||||
#Style - expression level options
|
||||
|
||||
#prefer out variables to be declared inline in the argument list of a method call when possible
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
#Style - Expression-level preferences
|
||||
|
||||
#prefer objects to be initialized using object initializers when possible
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
#prefer inferred anonymous type member names
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
|
||||
#prefer inferred tuple element names
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
|
||||
#Style - implicit and explicit types
|
||||
|
||||
#prefer var over explicit type in all cases, unless overridden by another code style rule
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
#prefer var is used to declare variables with built-in system types such as int
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
#Style - language keyword and framework type options
|
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
|
||||
#Style - Miscellaneous preferences
|
||||
|
||||
#prefer local functions over anonymous functions
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
|
||||
#Style - modifier options
|
||||
|
||||
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
|
||||
#Style - Modifier preferences
|
||||
|
||||
#when this rule is set to a list of modifiers, prefer the specified ordering.
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
|
||||
#Style - Pattern matching
|
||||
|
||||
#prefer pattern matching instead of is expression with type casts
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
|
||||
#Style - qualification options
|
||||
|
||||
#prefer fields not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
#prefer methods not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
#prefer properties not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
|
||||
[*.{cs,vb}]
|
||||
tab_width=4
|
||||
indent_size=4
|
||||
@@ -1,70 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '22 16 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -1 +0,0 @@
|
||||
github: QuestPDF
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class BarCode
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
FontManager.RegisterFontType("LibreBarcode39", File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 100)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -8,27 +6,26 @@ using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
[TestFixture]
|
||||
public class BarcodeExamples
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
public void Barcode()
|
||||
{
|
||||
FontManager.RegisterFont(File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 200)
|
||||
.ShowResults()
|
||||
.PageSize(300, 300)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*")
|
||||
.FontFamily("Libre Barcode 39")
|
||||
.FontSize(64);
|
||||
.Background("#FFF")
|
||||
.Padding(25)
|
||||
.Stack(stack =>
|
||||
{
|
||||
stack.Item().Border(1).Background(Colors.Grey.Lighten3).Padding(5).Text("Barcode Example");
|
||||
stack.Item().Border(1).Padding(5).AlignCenter().Text("*123456789*", TextStyle.Default.FontType("CarolinaBar-Demo-25E2").Size(20));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
using Microcharts;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class CanvasExamples
|
||||
{
|
||||
[Test]
|
||||
public void BorderRadius()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(175, 100)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(25)
|
||||
.MinimalBox()
|
||||
.Layers(layers =>
|
||||
{
|
||||
layers.Layer().Canvas((canvas, size) =>
|
||||
{
|
||||
DrawRoundedRectangle(Colors.White, false);
|
||||
DrawRoundedRectangle(Colors.Blue.Darken2, true);
|
||||
|
||||
void DrawRoundedRectangle(string color, bool isStroke)
|
||||
{
|
||||
using var paint = new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(color),
|
||||
IsStroke = isStroke,
|
||||
StrokeWidth = 2,
|
||||
IsAntialias = true
|
||||
};
|
||||
|
||||
canvas.DrawRoundRect(0, 0, size.Width, size.Height, 20, 20, paint);
|
||||
}
|
||||
});
|
||||
|
||||
layers
|
||||
.PrimaryLayer()
|
||||
.PaddingVertical(10)
|
||||
.PaddingHorizontal(20)
|
||||
.Text("Sample text")
|
||||
.FontSize(16)
|
||||
.FontColor(Colors.Blue.Darken2)
|
||||
.SemiBold();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,17 +52,14 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.Padding(25)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column
|
||||
stack
|
||||
.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text("Chart example")
|
||||
.FontSize(20)
|
||||
.SemiBold()
|
||||
.FontColor(Colors.Blue.Medium);
|
||||
.Text("Chart example", TextStyle.Default.Size(20).SemiBold().Color(Colors.Blue.Medium));
|
||||
|
||||
column
|
||||
stack
|
||||
.Item()
|
||||
.Border(1)
|
||||
.ExtendHorizontal()
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ColumnExamples
|
||||
{
|
||||
[Test]
|
||||
public void Column()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ShowResults()
|
||||
.ProducePdf()
|
||||
.Render(container =>
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
foreach (var i in Enumerable.Range(0, 10))
|
||||
column.Item().Element(Block);
|
||||
|
||||
static void Block(IContainer container)
|
||||
{
|
||||
container
|
||||
.Width(72)
|
||||
.Height(3.5f, Unit.Inch)
|
||||
.Height(1.5f, Unit.Inch)
|
||||
.Background(Placeholders.BackgroundColor());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability_NoItems()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 150)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Column(column => { });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace QuestPDF.Examples
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(x => GenerateStructure(x, 12));
|
||||
.Render(x => GenerateStructure(x, 16));
|
||||
}
|
||||
|
||||
private void GenerateStructure(IContainer container, int level)
|
||||
@@ -36,8 +36,8 @@ namespace QuestPDF.Examples
|
||||
.BorderColor(Colors.Black)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeItem().Element(x => GenerateStructure(x, level));
|
||||
row.RelativeItem().Element(x => GenerateStructure(x, level));
|
||||
row.RelativeColumn().Element(x => GenerateStructure(x, level));
|
||||
row.RelativeColumn().Element(x => GenerateStructure(x, level));
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -45,10 +45,10 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Border(level / 4f)
|
||||
.BorderColor(Colors.Black)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Item().Element(x => GenerateStructure(x, level));
|
||||
column.Item().Element(x => GenerateStructure(x, level));
|
||||
stack.Item().Element(x => GenerateStructure(x, level));
|
||||
stack.Item().Element(x => GenerateStructure(x, level));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ContentDirectionExamples
|
||||
{
|
||||
private Action<IContainer> ContentDirectionTemplate(Action<IContainer> content)
|
||||
{
|
||||
return container =>
|
||||
{
|
||||
container
|
||||
.MinimalBox()
|
||||
.ExtendHorizontal()
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.ConstantColumn(1);
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().ContentFromLeftToRight().Element(HeaderCell("Left-to-right"));
|
||||
header.Cell().LineVertical(1).LineColor(Colors.Grey.Medium);
|
||||
header.Cell().ContentFromRightToLeft().Element(HeaderCell("Right-to-left"));
|
||||
|
||||
static Action<IContainer> HeaderCell(string label)
|
||||
{
|
||||
return container => container
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.PaddingHorizontal(15)
|
||||
.PaddingVertical(5)
|
||||
.Text(label)
|
||||
.FontSize(18)
|
||||
.SemiBold();
|
||||
}
|
||||
});
|
||||
|
||||
table.Cell().Element(TestCell).ContentFromLeftToRight().Element(content);
|
||||
table.Cell().LineVertical(1).LineColor(Colors.Grey.Medium);
|
||||
table.Cell().Element(TestCell).ContentFromRightToLeft().Element(content);
|
||||
|
||||
static IContainer TestCell(IContainer container)
|
||||
{
|
||||
return container.Padding(15);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Page()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.RenderDocument(document =>
|
||||
{
|
||||
document.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A5);
|
||||
page.Margin(20);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.DefaultTextStyle(x => x.FontFamily("Calibri").FontSize(20));
|
||||
page.ContentFromRightToLeft();
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
|
||||
column.Item()
|
||||
.Text("مثال على الفاتورة") // example invoice
|
||||
.FontSize(32).FontColor(Colors.Blue.Darken2).SemiBold();
|
||||
|
||||
column.Item().Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.ConstantColumn(75);
|
||||
columns.ConstantColumn(100);
|
||||
});
|
||||
|
||||
table.Cell().Element(HeaderStyle).Text("وصف السلعة"); // item description
|
||||
table.Cell().Element(HeaderStyle).Text("كمية"); // quantity
|
||||
table.Cell().Element(HeaderStyle).Text("سعر"); // price
|
||||
|
||||
var items = new[]
|
||||
{
|
||||
"دورة البرمجة", // programming course
|
||||
"دورة تصميم الرسومات", // graphics design course
|
||||
"تحليل وتصميم الخوارزميات", // analysis and design of algorithms
|
||||
};
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var price = Placeholders.Random.NextDouble() * 100;
|
||||
|
||||
table.Cell().Text(item);
|
||||
table.Cell().Text(Placeholders.Random.Next(1, 10));
|
||||
table.Cell().Text($"USD${price:F2}");
|
||||
}
|
||||
|
||||
static IContainer HeaderStyle(IContainer x) => x.BorderBottom(1).PaddingVertical(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Column()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
|
||||
column.Item().Height(50).Width(50).Background(Colors.Red.Lighten1);
|
||||
column.Item().Height(50).Width(100).Background(Colors.Green.Lighten1);
|
||||
column.Item().Height(50).Width(150).Background(Colors.Blue.Lighten1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Row()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Row(row =>
|
||||
{
|
||||
row.Spacing(5);
|
||||
|
||||
row.AutoItem().Height(50).Width(50).Background(Colors.Red.Lighten1);
|
||||
row.AutoItem().Height(50).Width(50).Background(Colors.Green.Lighten1);
|
||||
row.AutoItem().Height(50).Width(75).Background(Colors.Blue.Lighten1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Table()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Cell().Height(50).Background(Colors.Red.Lighten1);
|
||||
table.Cell().Height(50).Background(Colors.Green.Lighten1);
|
||||
table.Cell().Height(50).Background(Colors.Blue.Lighten1);
|
||||
table.Cell().ColumnSpan(2).Height(50).Background(Colors.Orange.Lighten1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Constrained()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Width(50).Height(50).Background(Colors.Red.Lighten1);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unconstrained()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container
|
||||
.Width(100)
|
||||
.Height(100)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
|
||||
.Unconstrained()
|
||||
|
||||
.Width(50)
|
||||
.Height(50)
|
||||
.Background(Colors.Red.Lighten1);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Inlined()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
column.Item().Text("Default alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(null));
|
||||
|
||||
column.Item().Text("Left alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(InlinedAlignment.Left));
|
||||
|
||||
column.Item().Text("Center alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(InlinedAlignment.Center));
|
||||
|
||||
column.Item().Text("Right alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(InlinedAlignment.Right));
|
||||
});
|
||||
|
||||
static Action<IContainer> ContentWithAlignment(InlinedAlignment? alignment)
|
||||
{
|
||||
return container =>
|
||||
{
|
||||
container.Inlined(inlined =>
|
||||
{
|
||||
inlined.Spacing(5);
|
||||
|
||||
inlined.Alignment(alignment);
|
||||
|
||||
inlined.Item().Height(50).Width(50).Background(Colors.Red.Lighten1);
|
||||
inlined.Item().Height(50).Width(75).Background(Colors.Green.Lighten1);
|
||||
inlined.Item().Height(50).Width(100).Background(Colors.Blue.Lighten1);
|
||||
inlined.Item().Height(50).Width(125).Background(Colors.Orange.Lighten1);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Text()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
column.Item().Text("Default alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(null));
|
||||
|
||||
column.Item().Text("Left alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Left));
|
||||
|
||||
column.Item().Text("Center alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Center));
|
||||
|
||||
column.Item().Text("Right alignment").FontSize(14).SemiBold();
|
||||
column.Item().Element(ContentWithAlignment(HorizontalAlignment.Right));
|
||||
});
|
||||
|
||||
static Action<IContainer> ContentWithAlignment(HorizontalAlignment? alignment)
|
||||
{
|
||||
return container =>
|
||||
{
|
||||
container.Text(text =>
|
||||
{
|
||||
text.Alignment = alignment; // internal API
|
||||
|
||||
text.Span("Lorem ipsum").Bold().FontColor(Colors.Red.Medium);
|
||||
text.Element().Width(5);
|
||||
text.Span(Placeholders.LoremIpsum());
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Decoration()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Decoration(decoration =>
|
||||
{
|
||||
decoration.Before().Background(Colors.Green.Lighten1).Padding(5).Text("Before").FontSize(16);
|
||||
decoration.Content().Background(Colors.Green.Lighten2).Padding(5).Text("Content").FontSize(16);
|
||||
decoration.After().Background(Colors.Green.Lighten3).Padding(5).Text("After").FontSize(16);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Dynamic()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(ContentDirectionTemplate(Content));
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container.Dynamic(new SimpleDynamic());
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleDynamic : IDynamicComponent<int>
|
||||
{
|
||||
public int State { get; set; }
|
||||
|
||||
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||
{
|
||||
var content = context.CreateElement(container =>
|
||||
{
|
||||
container.Row(row =>
|
||||
{
|
||||
row.ConstantItem(50).Background(Colors.Red.Lighten2).Height(50);
|
||||
row.ConstantItem(75).Background(Colors.Green.Lighten2).Height(50);
|
||||
row.ConstantItem(100).Background(Colors.Blue.Lighten2).Height(50);
|
||||
});
|
||||
});
|
||||
|
||||
return new DynamicComponentComposeResult
|
||||
{
|
||||
Content = content,
|
||||
HasMoreContent = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,10 @@ namespace QuestPDF.Examples
|
||||
|
||||
page.Header().Text("Header");
|
||||
|
||||
page.Content().PaddingVertical(10).Border(1).Padding(10).Column(column =>
|
||||
page.Content().PaddingVertical(10).Border(1).Padding(10).Stack(stack =>
|
||||
{
|
||||
foreach (var index in Enumerable.Range(1, 100))
|
||||
column.Item().Text($"Line {index}").FontColor(Placeholders.Color());
|
||||
stack.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
|
||||
});
|
||||
|
||||
page.Footer().Text("Footer");
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace QuestPDF.Examples
|
||||
public class DebuggingTesting
|
||||
{
|
||||
[Test]
|
||||
public void Column()
|
||||
public void Stack()
|
||||
{
|
||||
Assert.Throws<DocumentLayoutException>(() =>
|
||||
{
|
||||
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
|
||||
.Width(100)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.DebugPointer("Example debug pointer")
|
||||
.Column(x =>
|
||||
.Stack(x =>
|
||||
{
|
||||
x.Item().Text("Test");
|
||||
x.Item().Width(150);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
@@ -24,13 +24,13 @@ namespace QuestPDF.Examples
|
||||
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A4);
|
||||
page.PageColor(Colors.White);
|
||||
page.Background(Colors.White);
|
||||
|
||||
page.Content().Column(column =>
|
||||
page.Content().Stack(stack =>
|
||||
{
|
||||
column.Item().Text(Placeholders.Sentence());
|
||||
stack.Item().Text(Placeholders.Sentence());
|
||||
|
||||
column.Item().Text(text =>
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
// text in this block is additionally semibold
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold());
|
||||
@@ -38,7 +38,7 @@ namespace QuestPDF.Examples
|
||||
text.Line(Placeholders.Sentence());
|
||||
|
||||
// this text has size 20 but also semibold and red
|
||||
text.Span(Placeholders.Sentence()).FontColor(Colors.Red.Medium);
|
||||
text.Span(Placeholders.Sentence(), TextStyle.Default.Color(Colors.Red.Medium));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class DefaultTextStyleExamples
|
||||
{
|
||||
[Test]
|
||||
public void Placeholder()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(220, 270)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.DefaultTextStyle(TextStyle.Default.Bold().Underline())
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text("Default style applies to all children");
|
||||
column.Item().Text("You can override certain styles").Underline(false).FontColor(Colors.Green.Darken2);
|
||||
|
||||
column.Item().PaddingTop(10).Border(1).Grid(grid =>
|
||||
{
|
||||
grid.Columns(4);
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 16))
|
||||
{
|
||||
grid.Item()
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Lighten1)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.Width(50)
|
||||
.Height(50)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text(i)
|
||||
.FontSize(16 + i / 4);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class DifferentHeaderOnFirstPageExample
|
||||
{
|
||||
[Test]
|
||||
public void Placeholder()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A6)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A6);
|
||||
page.Margin(5);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Header().Column(column =>
|
||||
{
|
||||
column.Item().ShowOnce().Background(Colors.Blue.Lighten2).Height(60);
|
||||
column.Item().SkipOnce().Background(Colors.Green.Lighten2).Height(40);
|
||||
});
|
||||
|
||||
page.Content().PaddingVertical(10).Column(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
foreach (var _ in Enumerable.Range(0, 13))
|
||||
column.Item().Background(Colors.Grey.Lighten2).Height(40);
|
||||
});
|
||||
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(16));
|
||||
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" / ");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
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));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
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());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
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,12 +40,10 @@ namespace QuestPDF.Examples
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.Header()
|
||||
.Background(Colors.Grey.Medium)
|
||||
.Padding(10)
|
||||
.Text("Notes")
|
||||
.FontSize(16)
|
||||
.FontColor("#FFF");
|
||||
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
@@ -68,27 +66,27 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(20)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Item()
|
||||
stack.Item()
|
||||
.PaddingBottom(10)
|
||||
.AlignCenter()
|
||||
.Text("This Row element is 700pt wide");
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.ConstantItem(100)
|
||||
row.ConstantColumn(100)
|
||||
.Background(Colors.Grey.Lighten1)
|
||||
.Padding(10)
|
||||
.ExtendVertical()
|
||||
.Text("This column is 100 pt wide");
|
||||
|
||||
row.RelativeItem()
|
||||
row.RelativeColumn()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(10)
|
||||
.Text("This column takes 1/3 of the available space (200pt)");
|
||||
|
||||
row.RelativeItem(2)
|
||||
row.RelativeColumn(2)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.Padding(10)
|
||||
.Text("This column takes 2/3 of the available space (400pt)");
|
||||
@@ -111,15 +109,15 @@ namespace QuestPDF.Examples
|
||||
.Row(row =>
|
||||
{
|
||||
row.Spacing(20);
|
||||
row.RelativeItem(2).Border(1).Background(Colors.Grey.Lighten1);
|
||||
row.RelativeItem(3).Border(1).Background(Colors.Grey.Lighten2);
|
||||
row.RelativeItem(4).Border(1).Background(Colors.Grey.Lighten3);
|
||||
row.RelativeColumn(2).Border(1).Background(Colors.Grey.Lighten1);
|
||||
row.RelativeColumn(3).Border(1).Background(Colors.Grey.Lighten2);
|
||||
row.RelativeColumn(4).Border(1).Background(Colors.Grey.Lighten3);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void column()
|
||||
public void Stack()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
@@ -129,13 +127,13 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(15)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Spacing(15);
|
||||
stack.Spacing(15);
|
||||
|
||||
column.Item().Background(Colors.Grey.Medium).Height(50);
|
||||
column.Item().Background(Colors.Grey.Lighten1).Height(100);
|
||||
column.Item().Background(Colors.Grey.Lighten2).Height(150);
|
||||
stack.Item().Background(Colors.Grey.Medium).Height(50);
|
||||
stack.Item().Background(Colors.Grey.Lighten1).Height(100);
|
||||
stack.Item().Background(Colors.Grey.Lighten2).Height(150);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -269,12 +267,12 @@ namespace QuestPDF.Examples
|
||||
layers
|
||||
.PrimaryLayer()
|
||||
.Padding(25)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
stack.Spacing(5);
|
||||
|
||||
foreach (var _ in Enumerable.Range(0, 7))
|
||||
column.Item().Text(Placeholders.Sentence());
|
||||
stack.Item().Text(Placeholders.Sentence());
|
||||
});
|
||||
|
||||
// layer above the main content
|
||||
@@ -282,15 +280,12 @@ namespace QuestPDF.Examples
|
||||
.Layer()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("Watermark")
|
||||
.FontSize(48)
|
||||
.Bold()
|
||||
.FontColor(Colors.Green.Lighten3);
|
||||
.Text("Watermark", TextStyle.Default.Size(48).Bold().Color(Colors.Green.Lighten3));
|
||||
|
||||
layers
|
||||
.Layer()
|
||||
.AlignBottom()
|
||||
.Text(text => text.CurrentPageNumber().FontSize(16).FontColor(Colors.Green.Medium));
|
||||
.Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -309,16 +304,16 @@ namespace QuestPDF.Examples
|
||||
// {
|
||||
// page.Header().PageNumber("Page {pdf:currentPage}");
|
||||
//
|
||||
// page.Content().Height(300).column(content =>
|
||||
// page.Content().Height(300).Stack(content =>
|
||||
// {
|
||||
// content.Item().Height(200).Background(Colors.Grey.Lighten2);
|
||||
//
|
||||
// content.Item().EnsureSpace(100).column(column =>
|
||||
// content.Item().EnsureSpace(100).Stack(stack =>
|
||||
// {
|
||||
// column.Spacing(10);
|
||||
// stack.Spacing(10);
|
||||
//
|
||||
// foreach (var _ in Enumerable.Range(0, 4))
|
||||
// column.Item().Height(50).Background(Colors.Green.Lighten1);
|
||||
// stack.Item().Height(50).Background(Colors.Green.Lighten1);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
@@ -383,7 +378,7 @@ namespace QuestPDF.Examples
|
||||
.Row(row =>
|
||||
{
|
||||
foreach (var color in colors)
|
||||
row.RelativeItem().Background(color);
|
||||
row.RelativeColumn().Background(color);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -421,9 +416,7 @@ namespace QuestPDF.Examples
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Padding(10)
|
||||
.Text(font)
|
||||
.FontFamily(font)
|
||||
.FontSize(16);
|
||||
.Text(font, TextStyle.Default.FontType(font).Size(16));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -444,10 +437,10 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
layers.Layer().Text("Something else");
|
||||
|
||||
layers.PrimaryLayer().Column(column =>
|
||||
layers.PrimaryLayer().Stack(stack =>
|
||||
{
|
||||
column.Item().PaddingTop(20).Text("Text 1");
|
||||
column.Item().PaddingTop(40).Text("Text 2");
|
||||
stack.Item().PaddingTop(20).Text("Text 1");
|
||||
stack.Item().PaddingTop(40).Text("Text 2");
|
||||
});
|
||||
|
||||
layers.Layer().Canvas((canvas, size) =>
|
||||
@@ -463,7 +456,7 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
|
||||
layers.Layer().Background("#8F00").Extend();
|
||||
layers.Layer().PaddingTop(40).Text("It works!").FontSize(24);
|
||||
layers.Layer().PaddingTop(40).Text("It works!", TextStyle.Default.Size(24));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -481,10 +474,10 @@ namespace QuestPDF.Examples
|
||||
.Padding(15)
|
||||
.Border(4)
|
||||
.BorderColor(Colors.Blue.Medium)
|
||||
//.MinimalBox()
|
||||
//.Box()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(15)
|
||||
.Text("Test of the \n box element").FontSize(20);
|
||||
.Text("Test of the \n box element", TextStyle.Default.Size(20));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -508,14 +501,13 @@ namespace QuestPDF.Examples
|
||||
.SemiBold();
|
||||
|
||||
decoration
|
||||
.Before()
|
||||
.Header()
|
||||
.PaddingBottom(10)
|
||||
.Text("Example: scale component")
|
||||
.Style(headerFontStyle);
|
||||
.Text("Example: scale component", headerFontStyle);
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
|
||||
|
||||
@@ -527,14 +519,13 @@ namespace QuestPDF.Examples
|
||||
|
||||
var fontStyle = TextStyle.Default.Size(16);
|
||||
|
||||
column
|
||||
stack
|
||||
.Item()
|
||||
.Border(1)
|
||||
.Background(fontColor)
|
||||
.Scale(scale)
|
||||
.Padding(5)
|
||||
.Text($"Content with {scale} scale.")
|
||||
.Style(fontStyle);
|
||||
.Text($"Content with {scale} scale.", fontStyle);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -551,7 +542,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container
|
||||
.Background("#FFF")
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
|
||||
.Padding(25)
|
||||
|
||||
@@ -564,8 +555,7 @@ namespace QuestPDF.Examples
|
||||
.BorderColor(Colors.Green.Darken1)
|
||||
|
||||
.Padding(50)
|
||||
.Text("Moved text")
|
||||
.FontSize(25);
|
||||
.Text("Moved text", TextStyle.Default.Size(25));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -598,11 +588,10 @@ namespace QuestPDF.Examples
|
||||
|
||||
return element;
|
||||
})
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Background(Colors.White)
|
||||
.Padding(10)
|
||||
.Text($"Rotated {turns * 90}°")
|
||||
.FontSize(16);
|
||||
.Text($"Rotated {turns * 90}°", TextStyle.Default.Size(16));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -697,11 +686,10 @@ namespace QuestPDF.Examples
|
||||
|
||||
return element;
|
||||
})
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Background(Colors.White)
|
||||
.Padding(10)
|
||||
.Text($"Flipped {turns}")
|
||||
.FontSize(16);
|
||||
.Text($"Flipped {turns}", TextStyle.Default.Size(16));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -720,14 +708,14 @@ namespace QuestPDF.Examples
|
||||
.Border(2)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantItem(25)
|
||||
row.ConstantColumn(25)
|
||||
.Border(1)
|
||||
.RotateLeft()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("Sample text");
|
||||
|
||||
row.RelativeItem().Border(1).Padding(5).Text(Placeholders.Paragraph());
|
||||
row.RelativeColumn().Border(1).Padding(5).Text(Placeholders.Paragraph());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -742,12 +730,12 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.PaddingLeft(50)
|
||||
.Column(column =>
|
||||
.PaddingLeft(75)
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
|
||||
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
|
||||
|
||||
column
|
||||
stack
|
||||
.Item()
|
||||
|
||||
// creates an infinite space for its child
|
||||
@@ -763,7 +751,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
.Background(Colors.Blue.Darken1);
|
||||
|
||||
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
|
||||
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -778,13 +766,13 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem().LabelCell("Label 1");
|
||||
row.RelativeColumn().LabelCell("Label 1");
|
||||
|
||||
row.RelativeItem(3).Grid(grid =>
|
||||
row.RelativeColumn(3).Grid(grid =>
|
||||
{
|
||||
grid.Columns(3);
|
||||
|
||||
@@ -796,11 +784,11 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem().ValueCell().Text("Value 1");
|
||||
row.RelativeColumn().ValueCell().Text("Value 1");
|
||||
|
||||
row.RelativeItem(3).Grid(grid =>
|
||||
row.RelativeColumn(3).Grid(grid =>
|
||||
{
|
||||
grid.Columns(3);
|
||||
|
||||
@@ -812,10 +800,10 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem().LabelCell("Label 6");
|
||||
row.RelativeItem().ValueCell().Text("Value 6");
|
||||
row.RelativeColumn().LabelCell("Label 6");
|
||||
row.RelativeColumn().ValueCell().Text("Value 6");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,11 +20,7 @@ namespace QuestPDF.Examples.Engine
|
||||
private Size Size { get; set; }
|
||||
private int? MaxPagesThreshold { get; set; }
|
||||
private bool ShowResult { get; set; }
|
||||
private bool ApplyCaching { get; set; }
|
||||
private bool ApplyDebugging { get; set; }
|
||||
private RenderingTestResult ResultType { get; set; } = RenderingTestResult.Images;
|
||||
|
||||
private bool ShowingResultsEnabled = true;
|
||||
|
||||
private RenderingTest()
|
||||
{
|
||||
@@ -71,24 +67,6 @@ namespace QuestPDF.Examples.Engine
|
||||
return this;
|
||||
}
|
||||
|
||||
public RenderingTest MaxPages(int value)
|
||||
{
|
||||
MaxPagesThreshold = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RenderingTest EnableCaching(bool value = true)
|
||||
{
|
||||
ApplyCaching = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RenderingTest EnableDebugging(bool value = true)
|
||||
{
|
||||
ApplyDebugging = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Render(Action<IContainer> content)
|
||||
{
|
||||
RenderDocument(container =>
|
||||
@@ -100,11 +78,16 @@ namespace QuestPDF.Examples.Engine
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void MaxPages(int value)
|
||||
{
|
||||
MaxPagesThreshold = value;
|
||||
}
|
||||
|
||||
public void RenderDocument(Action<IDocumentContainer> content)
|
||||
{
|
||||
MaxPagesThreshold ??= ResultType == RenderingTestResult.Pdf ? 1000 : 10;
|
||||
var document = new SimpleDocument(content, MaxPagesThreshold.Value, ApplyCaching, ApplyDebugging);
|
||||
var document = new SimpleDocument(content, MaxPagesThreshold.Value);
|
||||
|
||||
Render(document);
|
||||
}
|
||||
@@ -116,7 +99,7 @@ namespace QuestPDF.Examples.Engine
|
||||
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
|
||||
document.GenerateImages(fileNameSchema);
|
||||
|
||||
if (ShowResult && ShowingResultsEnabled)
|
||||
if (ShowResult)
|
||||
Process.Start("explorer", fileNameSchema(0));
|
||||
}
|
||||
|
||||
@@ -125,7 +108,7 @@ namespace QuestPDF.Examples.Engine
|
||||
var fileName = $"{FileNamePrefix}.pdf";
|
||||
document.GeneratePdf(fileName);
|
||||
|
||||
if (ShowResult && ShowingResultsEnabled)
|
||||
if (ShowResult)
|
||||
Process.Start("explorer", fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,15 +13,11 @@ namespace QuestPDF.Examples.Engine
|
||||
|
||||
private Action<IDocumentContainer> Content { get; }
|
||||
private int MaxPages { get; }
|
||||
private bool ApplyCaching { get; }
|
||||
private bool ApplyDebugging { get; }
|
||||
|
||||
public SimpleDocument(Action<IDocumentContainer> content, int maxPages, bool applyCaching, bool applyDebugging)
|
||||
public SimpleDocument(Action<IDocumentContainer> content, int maxPages)
|
||||
{
|
||||
Content = content;
|
||||
MaxPages = maxPages;
|
||||
ApplyCaching = applyCaching;
|
||||
ApplyDebugging = applyDebugging;
|
||||
}
|
||||
|
||||
public DocumentMetadata GetMetadata()
|
||||
@@ -29,9 +25,7 @@ namespace QuestPDF.Examples.Engine
|
||||
return new DocumentMetadata()
|
||||
{
|
||||
RasterDpi = PageSizes.PointsPerInch * ImageScalingFactor,
|
||||
DocumentLayoutExceptionThreshold = MaxPages,
|
||||
ApplyCaching = ApplyCaching,
|
||||
ApplyDebugging = ApplyDebugging
|
||||
DocumentLayoutExceptionThreshold = MaxPages
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,19 +21,19 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.PageColor(Colors.White);
|
||||
page.Background(Colors.White);
|
||||
|
||||
page.Header().Text("With ensure space").SemiBold();
|
||||
page.Header().Text("With ensure space", TextStyle.Default.SemiBold());
|
||||
|
||||
page.Content().Column(column =>
|
||||
page.Content().Stack(stack =>
|
||||
{
|
||||
column
|
||||
stack
|
||||
.Item()
|
||||
.ExtendHorizontal()
|
||||
.Height(75)
|
||||
.Background(Colors.Grey.Lighten2);
|
||||
|
||||
column
|
||||
stack
|
||||
.Item()
|
||||
.EnsureSpace(100)
|
||||
.Text(Placeholders.LoremIpsum());
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ExecutionOrderExamples
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 300)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.DefaultTextStyle(TextStyle.Default.Size(18))
|
||||
.Padding(25)
|
||||
.Row(row =>
|
||||
{
|
||||
row.Spacing(25);
|
||||
|
||||
row.RelativeItem()
|
||||
.Border(1)
|
||||
.Padding(15)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Text("Lorem ipsum");
|
||||
|
||||
row.RelativeItem()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(15)
|
||||
.Text("dolor sit amet");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Example2()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 200)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Border(2)
|
||||
.Width(150)
|
||||
.Height(150)
|
||||
|
||||
.Background(Colors.Blue.Lighten2)
|
||||
.PaddingTop(50)
|
||||
|
||||
.Background(Colors.Green.Lighten2)
|
||||
.PaddingRight(50)
|
||||
|
||||
.Background(Colors.Red.Lighten2)
|
||||
.PaddingBottom(50)
|
||||
|
||||
.Background(Colors.Amber.Lighten2)
|
||||
.PaddingLeft(50)
|
||||
|
||||
.Background(Colors.Grey.Lighten2);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,14 +12,12 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
return container
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Background(dark ? Colors.Grey.Lighten3 : Colors.White)
|
||||
.Padding(5);
|
||||
.Background(dark ? Colors.Grey.Lighten2 : Colors.White)
|
||||
.Padding(10);
|
||||
}
|
||||
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text).SemiBold();
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.Medium());
|
||||
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text);
|
||||
}
|
||||
|
||||
public class FrameExample
|
||||
@@ -36,14 +34,14 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(25)
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
for(var i = 1; i <= 4; i++)
|
||||
for(var i=1; i<=4; i++)
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(2).LabelCell(Placeholders.Label());
|
||||
row.RelativeItem(3).ValueCell().Text(Placeholders.Paragraph());
|
||||
row.RelativeColumn(2).LabelCell(Placeholders.Label());
|
||||
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
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}");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Examples.Engine;
|
||||
@@ -20,36 +19,21 @@ namespace QuestPDF.Examples
|
||||
.ShowResults()
|
||||
.Render(page =>
|
||||
{
|
||||
page.Padding(25).Column(column =>
|
||||
page.Padding(25).Stack(stack =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
stack.Spacing(25);
|
||||
|
||||
column.Item().Image("logo.png");
|
||||
stack.Item().Image("logo.png");
|
||||
|
||||
var binaryData = File.ReadAllBytes("logo.png");
|
||||
column.Item().Image(binaryData);
|
||||
stack.Item().Image(binaryData);
|
||||
|
||||
using var stream = new FileStream("logo.png", FileMode.Open);
|
||||
column.Item().Image(stream);
|
||||
stack.Item().Image(stream);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DynamicImage()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(450, 350)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(page =>
|
||||
{
|
||||
page.Padding(25)
|
||||
.Image(Placeholders.Image);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Exception()
|
||||
{
|
||||
@@ -63,43 +47,5 @@ namespace QuestPDF.Examples
|
||||
.Render(page => page.Image("non_existent.png"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReusingTheSameImageFileShouldBePossible()
|
||||
{
|
||||
var fileName = Path.GetTempFileName() + ".jpg";
|
||||
|
||||
try
|
||||
{
|
||||
var image = Placeholders.Image(300, 100);
|
||||
|
||||
using var file = File.Create(fileName);
|
||||
file.Write(image);
|
||||
file.Dispose();
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
|
||||
column.Item().Image(fileName);
|
||||
column.Item().Image(fileName);
|
||||
column.Item().Image(fileName);
|
||||
});
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace QuestPDF.Examples
|
||||
.Padding(25)
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration.Before().Text(text =>
|
||||
decoration.Header().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace QuestPDF.Examples
|
||||
decoration
|
||||
.Content()
|
||||
.PaddingTop(25)
|
||||
//.MinimalBox()
|
||||
//.Box()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Inlined(inlined =>
|
||||
@@ -74,8 +74,7 @@ namespace QuestPDF.Examples
|
||||
.PrimaryLayer()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text(sizeText)
|
||||
.FontSize(15);
|
||||
.Text(sizeText, TextStyle.Default.Size(15));
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -95,7 +94,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten4)
|
||||
.Inlined(inlined =>
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class LineExamples
|
||||
{
|
||||
[Test]
|
||||
public void LineHorizontal()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(15)
|
||||
.MinimalBox()
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text("Above text");
|
||||
column.Item().PaddingVertical(5).LineHorizontal(1).LineColor(Colors.Grey.Medium);
|
||||
column.Item().Text("Below text");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LineVertical()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(15)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Row(row =>
|
||||
{
|
||||
row.AutoItem().Text("Left text");
|
||||
row.AutoItem().PaddingHorizontal(10).LineVertical(1).LineColor(Colors.Grey.Medium);
|
||||
row.AutoItem().Text("Right text");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(25)
|
||||
.Column(column =>
|
||||
.Stack(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class MinimalApiExamples
|
||||
{
|
||||
[Test]
|
||||
public void MinimalApi()
|
||||
{
|
||||
Document
|
||||
.Create(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(2, Unit.Centimetre);
|
||||
page.PageColor(Colors.White);
|
||||
page.DefaultTextStyle(x => x.FontSize(20));
|
||||
|
||||
page.Header()
|
||||
.Text("Hello PDF!")
|
||||
.SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(1, Unit.Centimetre)
|
||||
.Column(x =>
|
||||
{
|
||||
x.Spacing(20);
|
||||
|
||||
x.Item().Text(Placeholders.LoremIpsum());
|
||||
x.Item().Image(Placeholders.Image(200, 100));
|
||||
});
|
||||
|
||||
page.Footer()
|
||||
.AlignCenter()
|
||||
.Text(x =>
|
||||
{
|
||||
x.Span("Page ");
|
||||
x.CurrentPageNumber();
|
||||
});
|
||||
});
|
||||
})
|
||||
.GeneratePdf("hello.pdf");
|
||||
|
||||
Process.Start("explorer.exe", "hello.pdf");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,8 +50,7 @@ namespace QuestPDF.Examples
|
||||
.Background("FFF")
|
||||
.Padding(5)
|
||||
.AlignCenter()
|
||||
.Text("Sample text")
|
||||
.FontFamily("Segoe UI emoji");
|
||||
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +63,7 @@ namespace QuestPDF.Examples
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Column(column =>
|
||||
.Stack(column =>
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
@@ -114,14 +113,14 @@ namespace QuestPDF.Examples
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Column(column =>
|
||||
.Stack(column =>
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.Height(150)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeItem()
|
||||
row.RelativeColumn()
|
||||
.Extend()
|
||||
.Background("FFF")
|
||||
|
||||
@@ -129,7 +128,7 @@ namespace QuestPDF.Examples
|
||||
.Width(50)
|
||||
.Background("444");
|
||||
|
||||
row.RelativeItem()
|
||||
row.RelativeColumn()
|
||||
.Extend()
|
||||
.Background("BBB")
|
||||
|
||||
@@ -143,7 +142,7 @@ namespace QuestPDF.Examples
|
||||
.Height(150)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeItem()
|
||||
row.RelativeColumn()
|
||||
.Extend()
|
||||
.Background("BBB")
|
||||
|
||||
@@ -151,7 +150,7 @@ namespace QuestPDF.Examples
|
||||
.Width(50)
|
||||
.Background("444");
|
||||
|
||||
row.RelativeItem()
|
||||
row.RelativeColumn()
|
||||
.Extend()
|
||||
.Background("BBB")
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class PageBackgroundForegroundExample
|
||||
{
|
||||
[Test]
|
||||
public void PageBackgroundForeground()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.MaxPages(100)
|
||||
.ShowResults()
|
||||
.RenderDocument(document =>
|
||||
{
|
||||
document.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(1, Unit.Inch);
|
||||
page.DefaultTextStyle(TextStyle.Default.FontSize(16));
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
const string transparentBlue = "#662196f3";
|
||||
|
||||
page.Background()
|
||||
.AlignTop()
|
||||
.ExtendHorizontal()
|
||||
.Height(200)
|
||||
.Background(transparentBlue);
|
||||
|
||||
page.Foreground()
|
||||
.AlignBottom()
|
||||
.ExtendHorizontal()
|
||||
.Height(250)
|
||||
.Background(transparentBlue);
|
||||
|
||||
page.Header().Text("Background and foreground").Bold().FontColor(Colors.Blue.Darken2).FontSize(36);
|
||||
|
||||
page.Content().PaddingVertical(25).Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
|
||||
foreach (var i in Enumerable.Range(0, 100))
|
||||
column.Item().Background(Colors.Grey.Lighten2).Height(75);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
|
||||
<PackageReference Include="microcharts" Version="0.9.5.9" />
|
||||
<PackageReference Include="nunit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<PackageReference Include="Svg.Skia" Version="0.5.18" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -29,9 +27,6 @@
|
||||
<None Update="logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="pdf-icon.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class RowExamples
|
||||
{
|
||||
[Test]
|
||||
public void ItemTypes()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(650, 300)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().LabelCell("Total width: 600px");
|
||||
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.ConstantItem(150).ValueCell("150px");
|
||||
row.ConstantItem(100).ValueCell("100px");
|
||||
row.RelativeItem(4).ValueCell("200px");
|
||||
row.RelativeItem(3).ValueCell("150px");
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.ConstantItem(100).ValueCell("100px");
|
||||
row.ConstantItem(50).ValueCell("50px");
|
||||
row.RelativeItem(2).ValueCell("100px");
|
||||
row.RelativeItem(1).ValueCell("50px");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability()
|
||||
{
|
||||
// up to version 2021.12, this code would always result with the infinite layout exception
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 150)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().ShowOnce().Element(CreateBox).Text("X");
|
||||
column.Item().Element(CreateBox).Text("1");
|
||||
column.Item().Element(CreateBox).Text("2");
|
||||
});
|
||||
|
||||
row.RelativeItem().Column(column =>
|
||||
{
|
||||
column.Item().Element(CreateBox).Text("1");
|
||||
column.Item().Element(CreateBox).Text("2");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
static IContainer CreateBox(IContainer container)
|
||||
{
|
||||
return container
|
||||
.ExtendHorizontal()
|
||||
.ExtendVertical()
|
||||
.Background(Colors.Grey.Lighten4)
|
||||
.Border(1)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.ShowOnce();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability_NoItems()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 150)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Row(row => { });
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RowElementForRelativeHeightDivision()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 400)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.AlignLeft()
|
||||
.RotateRight()
|
||||
.Row(row =>
|
||||
{
|
||||
row.Spacing(20);
|
||||
|
||||
row.RelativeItem(1).Element(Content);
|
||||
row.RelativeItem(2).Element(Content);
|
||||
row.RelativeItem(3).Element(Content);
|
||||
|
||||
void Content(IContainer container)
|
||||
{
|
||||
container
|
||||
.RotateLeft()
|
||||
.Border(1)
|
||||
.Background(Placeholders.BackgroundColor())
|
||||
.Padding(5)
|
||||
.Text(Placeholders.Label());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ScaleToFitExamples
|
||||
{
|
||||
[Test]
|
||||
public void ScaleToFit()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container.Padding(25).Column(column =>
|
||||
{
|
||||
var text = Placeholders.Paragraph();
|
||||
|
||||
foreach (var i in Enumerable.Range(2, 5))
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Width(i * 40)
|
||||
.Height(i * 20)
|
||||
.ScaleToFit()
|
||||
.Text(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,20 +21,20 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.PageColor(Colors.White);
|
||||
page.Background(Colors.White);
|
||||
|
||||
page.Header().Text("With show once").SemiBold();
|
||||
page.Header().Text("With show once", TextStyle.Default.SemiBold());
|
||||
|
||||
page.Content().PaddingVertical(5).Row(row =>
|
||||
{
|
||||
row.RelativeItem()
|
||||
row.RelativeColumn()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.ShowOnce()
|
||||
.Text(Placeholders.Label());
|
||||
|
||||
row.RelativeItem(2)
|
||||
row.RelativeColumn(2)
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Text(Placeholders.Paragraph());
|
||||
|
||||
@@ -21,18 +21,17 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.PageColor(Colors.White);
|
||||
page.Background(Colors.White);
|
||||
|
||||
page.Header().Column(column =>
|
||||
page.Header().Stack(stack =>
|
||||
{
|
||||
column.Item().ShowOnce().Text("This header is visible on the first page.");
|
||||
column.Item().SkipOnce().Text("This header is visible on the second page and all following.");
|
||||
stack.Item().ShowOnce().Text("This header is visible on the first page.");
|
||||
stack.Item().SkipOnce().Text("This header is visible on the second page and all following.");
|
||||
});
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(10)
|
||||
.Text(Placeholders.Paragraphs())
|
||||
.FontColor(Colors.Grey.Medium);
|
||||
.Text(Placeholders.Paragraphs(), TextStyle.Default.Color(Colors.Grey.Medium));
|
||||
|
||||
page.Footer().Text(text =>
|
||||
{
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class StopPaging
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 250)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(14))
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Medium));
|
||||
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
});
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
column.Item().StopPaging().Text(Placeholders.LoremIpsum());
|
||||
column.Item().ExtendHorizontal().Height(75).Background(Colors.Grey.Lighten2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
using Svg.Skia;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class SvgImageExample
|
||||
{
|
||||
[Test]
|
||||
public void BorderRadius()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(175, 100)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(25)
|
||||
.Canvas((canvas, space) =>
|
||||
{
|
||||
using var svg = new SKSvg();
|
||||
svg.Load("pdf-icon.svg");
|
||||
|
||||
canvas.DrawPicture(svg.Picture);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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}");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,500 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using IContainer = QuestPDF.Infrastructure.IContainer;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class TableExamples
|
||||
{
|
||||
[Test]
|
||||
public void BasicPlacement()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(220, 220)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
// by using custom 'Element' method, we can reuse visual configuration
|
||||
table.Cell().Row(1).Column(4).Element(Block).Text("A");
|
||||
table.Cell().Row(2).Column(2).Element(Block).Text("B");
|
||||
table.Cell().Row(3).Column(3).Element(Block).Text("C");
|
||||
table.Cell().Row(4).Column(1).Element(Block).Text("D");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PagingSupport()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(420, 220)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
// by using custom 'Element' method, we can reuse visual configuration
|
||||
table.Cell().Element(Block).Text(Placeholders.Label());
|
||||
table.Cell().Element(Block).Text(Placeholders.Label());
|
||||
table.Cell().Element(Block).Text(Placeholders.Paragraph());
|
||||
table.Cell().Element(Block).Text(Placeholders.Label());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultCellStyle()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(220, 120)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Cell().Row(1).Column(1).Element(Block).Text("A");
|
||||
table.Cell().Row(2).Column(2).Element(Block).Text("B");
|
||||
table.Cell().Row(1).Column(3).Element(Block).Text("C");
|
||||
table.Cell().Row(2).Column(4).Element(Block).Text("D");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ColumnsDefinition()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(320, 80)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.ConstantColumn(50);
|
||||
columns.ConstantColumn(100);
|
||||
columns.RelativeColumn(2);
|
||||
columns.RelativeColumn(3);
|
||||
});
|
||||
|
||||
table.Cell().ColumnSpan(4).LabelCell("Total width: 300px");
|
||||
table.Cell().ValueCell("50px");
|
||||
table.Cell().ValueCell("100px");
|
||||
table.Cell().ValueCell("100px");
|
||||
table.Cell().ValueCell("150px");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PartialAutoPlacement()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(220, 220)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Cell().Element(Block).Text("A");
|
||||
table.Cell().Row(2).Column(2).Element(Block).Text("B");
|
||||
table.Cell().Element(Block).Text("C");
|
||||
table.Cell().Row(3).Column(3).Element(Block).Text("D");
|
||||
table.Cell().ColumnSpan(2).Element(Block).Text("E");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtendLastCellsToTableBottom()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(220, 170)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.ExtendLastCellsToTableBottom();
|
||||
|
||||
table.Cell().Row(1).Column(1).Element(Block).Text("A");
|
||||
table.Cell().Row(3).Column(1).Element(Block).Text("B");
|
||||
table.Cell().Row(2).Column(2).Element(Block).Text("C");
|
||||
table.Cell().Row(3).Column(3).Element(Block).Text("D");
|
||||
table.Cell().Row(2).RowSpan(2).Column(4).Element(Block).Text("E");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Overlapping()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(170, 170)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Cell().Row(1).RowSpan(3).Column(1).ColumnSpan(3).Background(Colors.Grey.Lighten3).MinHeight(150);
|
||||
table.Cell().Row(1).RowSpan(2).Column(1).ColumnSpan(2).Background(Colors.Grey.Lighten1).MinHeight(100);
|
||||
table.Cell().Row(3).Column(3).Background(Colors.Grey.Darken1).MinHeight(50);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Spans()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(220, 170)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Cell().RowSpan(2).ColumnSpan(2).Element(Block).Text("1");
|
||||
table.Cell().ColumnSpan(2).Element(Block).Text("2");
|
||||
table.Cell().Element(Block).Text("3");
|
||||
table.Cell().Element(Block).Text("4");
|
||||
table.Cell().RowSpan(2).Element(Block).Text("5");
|
||||
table.Cell().ColumnSpan(2).Element(Block).Text("6");
|
||||
table.Cell().RowSpan(2).Element(Block).Text("7");
|
||||
table.Cell().Element(Block).Text("8");
|
||||
table.Cell().Element(Block).Text("9");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(300, 300)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.Cell().RowSpan(4).Element(Block).Text("1");
|
||||
|
||||
table.Cell().RowSpan(2).Element(Block).Text("2");
|
||||
table.Cell().RowSpan(1).Element(Block).Text("3");
|
||||
table.Cell().RowSpan(1).Element(Block).Text("4");
|
||||
|
||||
table.Cell().RowSpan(2).Element(Block).Text("5");
|
||||
table.Cell().RowSpan(1).Element(Block).Text("6");
|
||||
table.Cell().RowSpan(1).Element(Block).Text("7");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TableHeader()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.PageSize(500, 200)
|
||||
.ShowResults()
|
||||
.EnableDebugging()
|
||||
.Render(container =>
|
||||
{
|
||||
var pageSizes = new List<(string name, double width, double height)>()
|
||||
{
|
||||
("Letter (ANSI A)", 8.5f, 11),
|
||||
("Legal", 8.5f, 14),
|
||||
("Ledger (ANSI B)", 11, 17),
|
||||
("Tabloid (ANSI B)", 17, 11),
|
||||
("ANSI C", 22, 17),
|
||||
("ANSI D", 34, 22),
|
||||
("ANSI E", 44, 34)
|
||||
};
|
||||
|
||||
const int inchesToPoints = 72;
|
||||
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
IContainer DefaultCellStyle(IContainer container, string backgroundColor)
|
||||
{
|
||||
return container
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Lighten1)
|
||||
.Background(backgroundColor)
|
||||
.PaddingVertical(5)
|
||||
.PaddingHorizontal(10)
|
||||
.AlignCenter()
|
||||
.AlignMiddle();
|
||||
}
|
||||
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.RelativeColumn();
|
||||
|
||||
columns.ConstantColumn(75);
|
||||
columns.ConstantColumn(75);
|
||||
|
||||
columns.ConstantColumn(75);
|
||||
columns.ConstantColumn(75);
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().RowSpan(2).Element(CellStyle).ExtendHorizontal().AlignLeft().Text("Document type");
|
||||
|
||||
header.Cell().ColumnSpan(2).Element(CellStyle).Text("Inches");
|
||||
header.Cell().ColumnSpan(2).Element(CellStyle).Text("Points");
|
||||
|
||||
header.Cell().Element(CellStyle).Text("Width");
|
||||
header.Cell().Element(CellStyle).Text("Height");
|
||||
|
||||
header.Cell().Element(CellStyle).Text("Width");
|
||||
header.Cell().Element(CellStyle).Text("Height");
|
||||
|
||||
// you can extend already existing styles by creating additional methods
|
||||
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.Grey.Lighten3);
|
||||
});
|
||||
|
||||
foreach (var page in pageSizes)
|
||||
{
|
||||
table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
|
||||
|
||||
// inches
|
||||
table.Cell().Element(CellStyle).Text(page.width);
|
||||
table.Cell().Element(CellStyle).Text(page.height);
|
||||
|
||||
// points
|
||||
table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
|
||||
table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
|
||||
|
||||
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PerformanceText_TemperatureReport()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(PageSizes.A4)
|
||||
.MaxPages(10_000)
|
||||
.EnableCaching()
|
||||
.EnableDebugging(false)
|
||||
.ShowResults()
|
||||
.Render(container => GeneratePerformanceStructure(container, 250));
|
||||
}
|
||||
|
||||
public static void GeneratePerformanceStructure(IContainer container, int repeats)
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
//.Background(Colors.Blue.Lighten2)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
//.Background(Colors.Red.Lighten2)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.ConstantColumn(100);
|
||||
columns.RelativeColumn();
|
||||
columns.ConstantColumn(100);
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
table.ExtendLastCellsToTableBottom();
|
||||
|
||||
foreach (var i in Enumerable.Range(0, repeats))
|
||||
{
|
||||
table.Cell().RowSpan(3).LabelCell("Project");
|
||||
table.Cell().RowSpan(3).ShowEntire().ValueCell(Placeholders.Sentence());
|
||||
|
||||
table.Cell().LabelCell("Report number");
|
||||
table.Cell().ValueCell(i.ToString());
|
||||
|
||||
table.Cell().LabelCell("Date");
|
||||
table.Cell().ValueCell(Placeholders.ShortDate());
|
||||
|
||||
table.Cell().LabelCell("Inspector");
|
||||
table.Cell().ValueCell("Marcin Ziąbek");
|
||||
|
||||
table.Cell().ColumnSpan(2).LabelCell("Morning weather");
|
||||
table.Cell().ColumnSpan(2).LabelCell("Evening weather");
|
||||
|
||||
table.Cell().ValueCell("Time");
|
||||
table.Cell().ValueCell("7:13");
|
||||
|
||||
table.Cell().ValueCell("Time");
|
||||
table.Cell().ValueCell("18:25");
|
||||
|
||||
table.Cell().ValueCell("Description");
|
||||
table.Cell().ValueCell("Sunny");
|
||||
|
||||
table.Cell().ValueCell("Description");
|
||||
table.Cell().ValueCell("Windy");
|
||||
|
||||
table.Cell().ValueCell("Wind");
|
||||
table.Cell().ValueCell("Mild");
|
||||
|
||||
table.Cell().ValueCell("Wind");
|
||||
table.Cell().ValueCell("Strong");
|
||||
|
||||
table.Cell().ValueCell("Temperature");
|
||||
table.Cell().ValueCell("17°C");
|
||||
|
||||
table.Cell().ValueCell("Temperature");
|
||||
table.Cell().ValueCell("32°C");
|
||||
|
||||
table.Cell().LabelCell("Remarks");
|
||||
table.Cell().ColumnSpan(3).ValueCell(Placeholders.Paragraph());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// this method uses a higher order function to define a custom and dynamic style
|
||||
static IContainer Block(IContainer container)
|
||||
{
|
||||
return container
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.ShowOnce()
|
||||
.MinWidth(50)
|
||||
.MinHeight(50)
|
||||
.Padding(10)
|
||||
.AlignCenter()
|
||||
.AlignMiddle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace QuestPDF.Examples
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.RenderDocument(x => ComposeBook(x, chapters));
|
||||
.Render(x => ComposeBook(x, chapters));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -31,7 +31,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
var chapters = GetBookChapters().ToList();
|
||||
|
||||
var results = PerformTest(128).ToList();
|
||||
var results = PerformTest(16).ToList();
|
||||
|
||||
Console.WriteLine($"Min: {results.Min():F}");
|
||||
Console.WriteLine($"Max: {results.Max():F}");
|
||||
@@ -42,8 +42,8 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProducePdf()
|
||||
.RenderDocument(x => ComposeBook(x, chapters));
|
||||
.ProducePdf()
|
||||
.Render(x => ComposeBook(x, chapters));
|
||||
}
|
||||
|
||||
IEnumerable<float> PerformTest(int attempts)
|
||||
@@ -102,105 +102,112 @@ namespace QuestPDF.Examples
|
||||
}
|
||||
}
|
||||
|
||||
private void ComposeBook(IDocumentContainer container, ICollection<BookChapter> chapters)
|
||||
private void ComposeBook(IContainer container, ICollection<BookChapter> chapters)
|
||||
{
|
||||
var subtitleStyle = TextStyle.Default.Size(24).SemiBold().Color(Colors.Blue.Medium);
|
||||
var normalStyle = TextStyle.Default.Size(14);
|
||||
|
||||
ComposePage(container);
|
||||
|
||||
container.Page(page =>
|
||||
void ComposePage(IContainer container)
|
||||
{
|
||||
page.Margin(50);
|
||||
|
||||
page.Content().PaddingVertical(10).Column(column =>
|
||||
{
|
||||
column.Item().Element(Title);
|
||||
column.Item().PageBreak();
|
||||
column.Item().Element(TableOfContents);
|
||||
column.Item().PageBreak();
|
||||
container
|
||||
.Padding(50)
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Content()
|
||||
.Stack(stack =>
|
||||
{
|
||||
stack.Item().Element(Title);
|
||||
stack.Item().PageBreak();
|
||||
stack.Item().Element(TableOfContents);
|
||||
stack.Item().PageBreak();
|
||||
|
||||
Chapters(column);
|
||||
Chapters(stack);
|
||||
|
||||
column.Item().Element(Acknowledgements);
|
||||
});
|
||||
|
||||
page.Footer().Element(Footer);
|
||||
});
|
||||
stack.Item().Element(Acknowledgements);
|
||||
});
|
||||
|
||||
decoration.Footer().Element(Footer);
|
||||
});
|
||||
}
|
||||
|
||||
void Title(IContainer container)
|
||||
{
|
||||
container
|
||||
.Extend()
|
||||
.PaddingBottom(200)
|
||||
.AlignBottom()
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Item().Text("Quo Vadis").FontSize(72).Bold().FontColor(Colors.Blue.Darken2);
|
||||
column.Item().Text("Henryk Sienkiewicz").FontSize(24).FontColor(Colors.Grey.Darken2);
|
||||
stack.Item().Text("Quo Vadis", TextStyle.Default.Size(72).Bold().Color(Colors.Blue.Darken2));
|
||||
stack.Item().Text("Henryk Sienkiewicz", TextStyle.Default.Size(24).Color(Colors.Grey.Darken2));
|
||||
});
|
||||
}
|
||||
|
||||
void TableOfContents(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
container.Stack(stack =>
|
||||
{
|
||||
SectionTitle(column, "Spis treści");
|
||||
SectionTitle(stack, "Spis treści");
|
||||
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
column.Item().InternalLink(chapter.Title).Row(row =>
|
||||
stack.Item().InternalLink(chapter.Title).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text(chapter.Title).Style(normalStyle);
|
||||
row.ConstantItem(100).AlignRight().Text(text => text.BeginPageNumberOfSection(chapter.Title).Style(normalStyle));
|
||||
row.RelativeColumn().Text(chapter.Title, normalStyle);
|
||||
row.ConstantColumn(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Chapters(ColumnDescriptor column)
|
||||
void Chapters(StackDescriptor stack)
|
||||
{
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
column.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
|
||||
stack.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
|
||||
}
|
||||
}
|
||||
|
||||
void Chapter(IContainer container, string title, string content)
|
||||
{
|
||||
container.Column(column =>
|
||||
container.Stack(stack =>
|
||||
{
|
||||
SectionTitle(column, title);
|
||||
SectionTitle(stack, title);
|
||||
|
||||
column.Item().Text(text =>
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
text.ParagraphSpacing(5);
|
||||
text.Span(content).Style(normalStyle);
|
||||
text.Span(content, normalStyle);
|
||||
});
|
||||
|
||||
column.Item().PageBreak();
|
||||
stack.Item().PageBreak();
|
||||
});
|
||||
}
|
||||
|
||||
void Acknowledgements(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
container.Stack(stack =>
|
||||
{
|
||||
SectionTitle(column, "Podziękowania");
|
||||
SectionTitle(stack, "Podziękowania");
|
||||
|
||||
column.Item().Text(text =>
|
||||
stack.Item().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(normalStyle);
|
||||
|
||||
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
|
||||
text.Hyperlink("wolnelektury.pl", "https://wolnelektury.pl/").FontColor(Colors.Blue.Medium).Underline();
|
||||
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underline());
|
||||
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SectionTitle(ColumnDescriptor column, string text)
|
||||
void SectionTitle(StackDescriptor stack, string text)
|
||||
{
|
||||
column.Item().Location(text).Text(text).Style(subtitleStyle);
|
||||
column.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
||||
stack.Item().Location(text).Text(text, subtitleStyle);
|
||||
stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
||||
}
|
||||
|
||||
void Footer(IContainer container)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements.Text;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -12,32 +11,12 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
public class TextExamples
|
||||
{
|
||||
[Test]
|
||||
public void SimpleText()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 100)
|
||||
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(Placeholders.Paragraph());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SimpleTextBlock()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(600, 300)
|
||||
.PageSize(500, 300)
|
||||
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
@@ -45,187 +24,42 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.MaxWidth(300)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
text.Span("This is a normal text, followed by an ");
|
||||
text.Span("underlined red text").FontColor(Colors.Red.Medium).Underline();
|
||||
text.Span(".");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[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();
|
||||
text.Span("underlined red text.", TextStyle.Default.Size(20).Color(Colors.Red.Medium).Underline());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[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]
|
||||
public void ParagraphSpacing()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 500)
|
||||
.PageSize(500, 300)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.Line(Placeholders.Paragraph());
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 3))
|
||||
{
|
||||
text.Span($"Paragraph {i}: ", TextStyle.Default.SemiBold());
|
||||
text.Line(Placeholders.Paragraph());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -242,12 +76,12 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
text.Span("This is a random image aligned to the baseline: ");
|
||||
|
||||
text.Element()
|
||||
@@ -274,7 +108,7 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
@@ -286,13 +120,13 @@ namespace QuestPDF.Examples
|
||||
|
||||
text.Line(Placeholders.LoremIpsum());
|
||||
|
||||
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <").Underline();
|
||||
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <", TextStyle.Default.Underline());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Textcolumn()
|
||||
public void TextStack()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
@@ -304,7 +138,7 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
@@ -333,14 +167,12 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.Bold());
|
||||
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
@@ -350,14 +182,14 @@ namespace QuestPDF.Examples
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
text.Span("this is a bold text, ").Bold();
|
||||
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
|
||||
text.Span("and this is slightly bigger text.").FontSize(16);
|
||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
|
||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.EmptyLine();
|
||||
@@ -369,7 +201,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
@@ -378,7 +210,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span(Placeholders.Paragraphs()).Italic();
|
||||
text.Span(Placeholders.Paragraphs(), TextStyle.Default.Italic());
|
||||
|
||||
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
|
||||
});
|
||||
@@ -398,23 +230,23 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Box()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.FontSize(20).BackgroundColor(Colors.Red.Lighten4));
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
text.Span("this is a bold text, ").Bold();
|
||||
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
|
||||
text.Span("and this is slightly bigger text.").FontSize(16);
|
||||
text.Span("this is a bold text, ", TextStyle.Default.Bold());
|
||||
text.Span("this is a red and underlined text, ", TextStyle.Default.Color(Colors.Red.Medium).Underline());
|
||||
text.Span("and this is slightly bigger text.", TextStyle.Default.Size(16));
|
||||
|
||||
text.Span("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.EmptyLine();
|
||||
@@ -423,7 +255,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
text.Line($"{i}: {Placeholders.Paragraph()}");
|
||||
|
||||
text.Hyperlink("Please visit QuestPDF website. ", "https://www.questpdf.com");
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
|
||||
text.Span("This is page number ");
|
||||
text.CurrentPageNumber();
|
||||
@@ -435,356 +267,5 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MeasureIssueWhenSpaceAtLineEnd()
|
||||
{
|
||||
// issue 135
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Size(PageSizes.A4);
|
||||
|
||||
page.Content().Text("This is a specially crafted sentence with a specially chosen length for demonstration of the bug that occurs ;;;;;. ").FontSize(11).BackgroundColor(Colors.Red.Lighten3);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EmptyText()
|
||||
{
|
||||
// issue 135
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Size(PageSizes.A4);
|
||||
|
||||
page.Content().Text(" ").FontSize(11).BackgroundColor(Colors.Red.Lighten3);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Whitespaces()
|
||||
{
|
||||
// issue 135
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Size(PageSizes.A4);
|
||||
|
||||
page.Content().Text(" x ").FontSize(11).BackgroundColor(Colors.Red.Lighten3);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DrawingNullTextShouldNotThrowException()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(50);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Size(PageSizes.A4);
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
column.Item().Text(null);
|
||||
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.Span(null);
|
||||
text.Line(null);
|
||||
text.Hyperlink(null, "http://www.questpdf.com");
|
||||
text.TotalPages().Format(x => null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[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(250, 100)
|
||||
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.MinimalBox()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Text("خوارزمية ترتيب")
|
||||
.FontFamily(Fonts.Calibri)
|
||||
.FontSize(30);
|
||||
});
|
||||
}
|
||||
|
||||
[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: 😊😅🥳👍❤😍👌");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordWrappingStability()
|
||||
{
|
||||
// instruction: check if any characters repeat when performing the word-wrapping algorithm
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
var text = "Lorem ipsum dolor sit amet consectetuer";
|
||||
|
||||
container
|
||||
.Padding(20)
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
foreach (var width in Enumerable.Range(25, 200))
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.MaxWidth(width)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.Text(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AdvancedLanguagesSupport()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(new PageSize(400, 400))
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
var text = "في المعلوماتية أو الرياضيات، خوارزمية الترتيب هي خوارزمية تمكن من تنظيم مجموعة عناصر حسب ترتيب محدد.";
|
||||
|
||||
container
|
||||
.Padding(20)
|
||||
.ContentFromRightToLeft()
|
||||
.Text(text)
|
||||
.FontFamily(Fonts.Calibri)
|
||||
.FontSize(22);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordWrappingWhenRightToLeft()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(new PageSize(1000, 500))
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
var text = "في المعلوماتية أو الرياضيات، خوارزمية الترتيب هي خوارزمية تمكن من تنظيم مجموعة عناصر حسب ترتيب محدد.";
|
||||
|
||||
container
|
||||
.Padding(25)
|
||||
.ContentFromRightToLeft()
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
|
||||
foreach (var size in new[] { 36, 34, 32, 30, 15 })
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.ShowEntire()
|
||||
.MaxWidth(size * 25)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.MinimalBox()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Text(text)
|
||||
.FontSize(20)
|
||||
.FontFamily("Segoe UI");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ForcingTextDirection()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(new PageSize(1000, 500))
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.DefaultTextStyle(x => x.FontSize(24).FontFamily("Calibri"))
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
var word = "الجوريتم";
|
||||
var definition = "algorithm in Arabic";
|
||||
|
||||
var text = $"{word} - {definition}";
|
||||
|
||||
// text direction is automatically detected using the first word
|
||||
column.Item().Text(text);
|
||||
|
||||
// it is possible to force specific content direction
|
||||
column.Item().Text(text).DirectionFromLeftToRight();
|
||||
column.Item().Text(text).DirectionFromRightToLeft();
|
||||
|
||||
// to combine text in various content directions, split it into segments
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.Span(word);
|
||||
text.Span(" - ");
|
||||
text.Span(definition);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="75.320129mm"
|
||||
height="92.604164mm"
|
||||
viewBox="0 0 75.320129 92.604164"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
sodipodi:docname="PDF file icon.svg">
|
||||
<title
|
||||
id="title2682">PDF file icon</title>
|
||||
<defs
|
||||
id="defs2">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath839">
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="rect841"
|
||||
width="68.688263"
|
||||
height="67.886459"
|
||||
x="-74.033661"
|
||||
y="141.44913" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="122.12727"
|
||||
inkscape:cy="167.02823"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g899"
|
||||
showgrid="false"
|
||||
inkscape:object-nodes="false"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="705"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-page="false"
|
||||
inkscape:snap-others="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>PDF file icon</dc:title>
|
||||
<dc:date>08/10/2018</dc:date>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Adobe Systems</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>CMetalCore</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:description>Fuente del texto "PDF":
|
||||
Franklin Gothic Medium Cond</dc:description>
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Capa 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(36.076172,-93.731774)">
|
||||
<g
|
||||
id="g899"
|
||||
transform="matrix(1.4843054,0,0,1.4843054,17.471885,-90.243502)">
|
||||
<g
|
||||
id="g876">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff2116;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.11666656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m -29.632812,123.94727 c -3.551967,0 -6.44336,2.89347 -6.44336,6.44531 v 49.49804 c 0,3.55185 2.891393,6.44532 6.44336,6.44532 H 8.2167969 c 3.5519661,0 6.4433591,-2.89335 6.4433591,-6.44532 v -40.70117 c 0,0 0.101353,-1.19181 -0.416015,-2.35156 -0.484969,-1.08711 -1.275391,-1.84375 -1.275391,-1.84375 a 1.0584391,1.0584391 0 0 0 -0.0059,-0.008 L 3.5722246,125.7752 a 1.0584391,1.0584391 0 0 0 -0.015625,-0.0156 c 0,0 -0.8017392,-0.76344 -1.9902344,-1.27344 -1.39939552,-0.6005 -2.8417968,-0.53711 -2.8417968,-0.53711 l 0.021484,-0.002 z"
|
||||
id="path890" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.11666656;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m -29.632812,126.06445 h 28.3789058 a 1.0584391,1.0584391 0 0 0 0.021484,0 c 0,0 1.13480448,0.011 1.96484378,0.36719 0.79889772,0.34282 1.36536982,0.86176 1.36914062,0.86524 1.25e-5,1e-5 0.00391,0.004 0.00391,0.004 l 9.3671868,9.18945 c 0,0 0.564354,0.59582 0.837891,1.20899 0.220779,0.49491 0.234375,1.40039 0.234375,1.40039 a 1.0584391,1.0584391 0 0 0 -0.002,0.0449 v 40.74609 c 0,2.41592 -1.910258,4.32813 -4.3261717,4.32813 H -29.632812 c -2.415914,0 -4.326172,-1.91209 -4.326172,-4.32813 v -49.49804 c 0,-2.41603 1.910258,-4.32813 4.326172,-4.32813 z"
|
||||
id="rect2684" />
|
||||
<path
|
||||
sodipodi:nodetypes="scccscccscccccssscacccsccccccccccccccccccccaccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m -23.40766,161.09299 c -1.45669,-1.45669 0.11934,-3.45839 4.39648,-5.58397 l 2.69124,-1.33743 1.04845,-2.29399 c 0.57665,-1.26169 1.43729,-3.32036 1.91254,-4.5748 l 0.8641,-2.28082 -0.59546,-1.68793 c -0.73217,-2.07547 -0.99326,-5.19438 -0.52872,-6.31588 0.62923,-1.51909 2.69029,-1.36323 3.50626,0.26515 0.63727,1.27176 0.57212,3.57488 -0.18329,6.47946 l -0.6193,2.38125 0.5455,0.92604 c 0.30003,0.50932 1.1764,1.71867 1.9475,2.68743 l 1.44924,1.80272 1.8033728,-0.23533 c 5.72900399,-0.74758 7.6912472,0.523 7.6912472,2.34476 0,2.29921 -4.4984914,2.48899 -8.2760865,-0.16423 -0.8499666,-0.59698 -1.4336605,-1.19001 -1.4336605,-1.19001 0,0 -2.3665326,0.48178 -3.531704,0.79583 -1.202707,0.32417 -1.80274,0.52719 -3.564509,1.12186 0,0 -0.61814,0.89767 -1.02094,1.55026 -1.49858,2.4279 -3.24833,4.43998 -4.49793,5.1723 -1.3991,0.81993 -2.86584,0.87582 -3.60433,0.13733 z m 2.28605,-0.81668 c 0.81883,-0.50607 2.47616,-2.46625 3.62341,-4.28553 l 0.46449,-0.73658 -2.11497,1.06339 c -3.26655,1.64239 -4.76093,3.19033 -3.98386,4.12664 0.43653,0.52598 0.95874,0.48237 2.01093,-0.16792 z m 21.21809,-5.95578 c 0.80089,-0.56097 0.68463,-1.69142 -0.22082,-2.1472 -0.70466,-0.35471 -1.2726074,-0.42759 -3.1031574,-0.40057 -1.1249,0.0767 -2.9337647,0.3034 -3.2403347,0.37237 0,0 0.993716,0.68678 1.434896,0.93922 0.58731,0.33544 2.0145161,0.95811 3.0565161,1.27706 1.02785,0.31461 1.6224,0.28144 2.0729,-0.0409 z m -8.53152,-3.54594 c -0.4847,-0.50952 -1.30889,-1.57296 -1.83152,-2.3632 -0.68353,-0.89643 -1.02629,-1.52887 -1.02629,-1.52887 0,0 -0.4996,1.60694 -0.90948,2.57394 l -1.27876,3.16076 -0.37075,0.71695 c 0,0 1.971043,-0.64627 2.97389,-0.90822 1.0621668,-0.27744 3.21787,-0.70134 3.21787,-0.70134 z m -2.74938,-11.02573 c 0.12363,-1.0375 0.1761,-2.07346 -0.15724,-2.59587 -0.9246,-1.01077 -2.04057,-0.16787 -1.85154,2.23517 0.0636,0.8084 0.26443,2.19033 0.53292,3.04209 l 0.48817,1.54863 0.34358,-1.16638 c 0.18897,-0.64151 0.47882,-2.02015 0.64411,-3.06364 z"
|
||||
style="fill:#ff2116;fill-opacity:1;stroke-width:0.26458335"
|
||||
id="path2697" />
|
||||
<g
|
||||
id="g858">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path845"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -20.930423,167.83862 h 2.364986 q 1.133514,0 1.840213,0.2169 0.706698,0.20991 1.189489,0.9446 0.482795,0.72769 0.482795,1.75625 0,0.94459 -0.391832,1.6233 -0.391833,0.67871 -1.056548,0.97958 -0.65772,0.30087 -2.02913,0.30087 h -0.818651 v 3.72941 h -1.581322 z m 1.581322,1.22447 v 3.33058 h 0.783664 q 1.049552,0 1.44838,-0.39184 0.405826,-0.39183 0.405826,-1.27345 0,-0.65772 -0.265887,-1.06355 -0.265884,-0.41282 -0.587747,-0.50378 -0.314866,-0.098 -1.000572,-0.098 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path847"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -13.842461,167.83862 h 2.148082 q 1.560333,0 2.4909318,0.55276 0.9375993,0.55276 1.4133973,1.6443 0.482791,1.09153 0.482791,2.42096 0,1.3994 -0.4338151,2.49793 -0.4268149,1.09153 -1.3154348,1.76324 -0.8816233,0.67172 -2.5189212,0.67172 h -2.267031 z m 1.581326,1.26645 v 7.018 h 0.657715 q 1.378411,0 2.001144,-0.9516 0.6227329,-0.95858 0.6227329,-2.5539 0,-3.5125 -2.6238769,-3.5125 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path849"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Franklin Gothic Medium Cond';-inkscape-font-specification:'Franklin Gothic Medium Cond';letter-spacing:0px;word-spacing:4.26000023px;fill:#2c2c2c;fill-opacity:1;stroke:none;stroke-width:0.35824656px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m -5.7889096,167.83862 h 5.30372941 v 1.26645 H -4.2075842 v 2.85478 h 2.9807225 v 1.26646 h -2.9807225 v 4.16322 h -1.5813254 z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -25,32 +24,5 @@ namespace QuestPDF.ReportSample
|
||||
Latitude = Helpers.Random.NextDouble() * 180f - 90f
|
||||
};
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<int, string> RomanNumeralCache = new ConcurrentDictionary<int, string>();
|
||||
|
||||
public static string FormatAsRomanNumeral(this int number)
|
||||
{
|
||||
if (number < 0 || number > 3999)
|
||||
throw new ArgumentOutOfRangeException("Number should be in range from 1 to 3999");
|
||||
|
||||
return RomanNumeralCache.GetOrAdd(number, x =>
|
||||
{
|
||||
if (x >= 1000) return "M" + FormatAsRomanNumeral(x - 1000);
|
||||
if (x >= 900) return "CM" + FormatAsRomanNumeral(x - 900);
|
||||
if (x >= 500) return "D" + FormatAsRomanNumeral(x - 500);
|
||||
if (x >= 400) return "CD" + FormatAsRomanNumeral(x - 400);
|
||||
if (x >= 100) return "C" + FormatAsRomanNumeral(x - 100);
|
||||
if (x >= 90) return "XC" + FormatAsRomanNumeral(x - 90);
|
||||
if (x >= 50) return "L" + FormatAsRomanNumeral(x - 50);
|
||||
if (x >= 40) return "XL" + FormatAsRomanNumeral(x - 40);
|
||||
if (x >= 10) return "X" + FormatAsRomanNumeral(x - 10);
|
||||
if (x >= 9) return "IX" + FormatAsRomanNumeral(x - 9);
|
||||
if (x >= 5) return "V" + FormatAsRomanNumeral(x - 5);
|
||||
if (x >= 4) return "IV" + FormatAsRomanNumeral(x - 4);
|
||||
if (x >= 1) return "I" + FormatAsRomanNumeral(x - 1);
|
||||
|
||||
return string.Empty;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,58 +26,58 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
private void ComposeHeader(IContainer container)
|
||||
{
|
||||
container.Background(Colors.Grey.Lighten3).Border(1).Column(column =>
|
||||
container.Background(Colors.Grey.Lighten3).Border(1).Stack(stack =>
|
||||
{
|
||||
column.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
|
||||
stack.Item().ShowOnce().Padding(5).AlignMiddle().Row(row =>
|
||||
{
|
||||
row.RelativeItem(2).AlignMiddle().Text("PRIMARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
|
||||
row.RelativeItem(1).AlignRight().MinimalBox().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
|
||||
row.RelativeColumn(2).AlignMiddle().Text("PRIMARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
|
||||
row.RelativeColumn(1).AlignRight().Box().AlignMiddle().Background(Colors.Blue.Darken2).Padding(30);
|
||||
});
|
||||
column.Item().SkipOnce().Padding(5).Row(row =>
|
||||
stack.Item().SkipOnce().Padding(5).Row(row =>
|
||||
{
|
||||
row.RelativeItem(2).Text("SECONDARY HEADER").FontColor(Colors.Grey.Darken3).FontSize(30).Bold();
|
||||
row.RelativeItem(1).AlignRight().MinimalBox().Background(Colors.Blue.Lighten4).Padding(15);
|
||||
row.RelativeColumn(2).Text("SECONDARY HEADER", TextStyle.Default.Color(Colors.Grey.Darken3).Size(30).Bold());
|
||||
row.RelativeColumn(1).AlignRight().Box().Background(Colors.Blue.Lighten4).Padding(15);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeContent(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
container.Stack(stack =>
|
||||
{
|
||||
column.Item().PaddingVertical(80).Text("First");
|
||||
column.Item().PageBreak();
|
||||
column.Item().PaddingVertical(80).Text("Second");
|
||||
column.Item().PageBreak();
|
||||
column.Item().PaddingVertical(80).Text("Third");
|
||||
column.Item().PageBreak();
|
||||
stack.Item().PaddingVertical(80).Text("First");
|
||||
stack.Item().PageBreak();
|
||||
stack.Item().PaddingVertical(80).Text("Second");
|
||||
stack.Item().PageBreak();
|
||||
stack.Item().PaddingVertical(80).Text("Third");
|
||||
stack.Item().PageBreak();
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeFooter(IContainer container)
|
||||
{
|
||||
container.Background(Colors.Grey.Lighten3).Column(column =>
|
||||
container.Background(Colors.Grey.Lighten3).Stack(stack =>
|
||||
{
|
||||
column.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||
stack.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text(x =>
|
||||
row.RelativeColumn().Text(x =>
|
||||
{
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
row.RelativeItem().AlignRight().Text("Footer for header");
|
||||
row.RelativeColumn().AlignRight().Text("Footer for header");
|
||||
});
|
||||
|
||||
column.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||
stack.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text(x =>
|
||||
row.RelativeColumn().Text(x =>
|
||||
{
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
row.RelativeItem().AlignRight().Text("Footer for every page except header");
|
||||
row.RelativeColumn().AlignRight().Text("Footer for every page except header");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -18,11 +17,11 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
container
|
||||
.ShowEntire()
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
column.Item().Element(PhotoWithMaps);
|
||||
column.Item().Element(PhotoDetails);
|
||||
stack.Spacing(5);
|
||||
stack.Item().Element(PhotoWithMaps);
|
||||
stack.Item().Element(PhotoDetails);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,14 +30,14 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
container
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeItem(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
row.RelativeColumn(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
|
||||
row.RelativeItem().PaddingLeft(5).Column(column =>
|
||||
row.RelativeColumn().PaddingLeft(5).Stack(stack =>
|
||||
{
|
||||
column.Spacing(7f);
|
||||
stack.Spacing(7f);
|
||||
|
||||
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
stack.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -48,7 +47,7 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
container.Border(0.75f).BorderColor(Colors.Grey.Medium).Grid(grid =>
|
||||
{
|
||||
grid.Columns(6);
|
||||
|
||||
|
||||
grid.Item().LabelCell().Text("Date");
|
||||
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty);
|
||||
grid.Item().LabelCell().Text("Location");
|
||||
|
||||
@@ -21,26 +21,25 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.Header()
|
||||
.PaddingBottom(5)
|
||||
.Text(Model.Title)
|
||||
.Style(Typography.Headline);
|
||||
.Text(Model.Title, Typography.Headline);
|
||||
|
||||
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Column(column =>
|
||||
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
|
||||
{
|
||||
foreach (var part in Model.Parts)
|
||||
{
|
||||
column.Item().EnsureSpace(25).Row(row =>
|
||||
stack.Item().EnsureSpace(25).Row(row =>
|
||||
{
|
||||
row.ConstantItem(150).LabelCell().Text(part.Label);
|
||||
var frame = row.RelativeItem().ValueCell();
|
||||
row.ConstantColumn(150).LabelCell().Text(part.Label);
|
||||
var frame = row.RelativeColumn().ValueCell();
|
||||
|
||||
if (part is ReportSectionText text)
|
||||
frame.ShowEntire().Text(text.Text);
|
||||
|
||||
|
||||
if (part is ReportSectionMap map)
|
||||
frame.Element(x => MapElement(x, map));
|
||||
|
||||
|
||||
if (part is ReportSectionPhotos photos)
|
||||
frame.Element(x => PhotosElement(x, photos));
|
||||
});
|
||||
@@ -57,12 +56,12 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
return;
|
||||
}
|
||||
|
||||
container.ShowEntire().Column(column =>
|
||||
container.ShowEntire().Stack(stack =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
stack.Spacing(5);
|
||||
|
||||
column.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
column.Item().Text(model.Location.Format());
|
||||
stack.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
stack.Item().Text(model.Location.Format());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,7 +69,7 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
if (model.PhotoCount == 0)
|
||||
{
|
||||
container.Text("No photos").Style(Typography.Normal);
|
||||
container.Text("No photos", Typography.Normal);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,8 +82,6 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
.Range(0, model.PhotoCount)
|
||||
.ToList()
|
||||
.ForEach(x => grid.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>());
|
||||
|
||||
//grid.Item().AspectRatio(4 / 3f).Width(100000).Component<ImagePlaceholder>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,28 +39,28 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
text.CurrentPageNumber().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" / ");
|
||||
text.TotalPages().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeHeader(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
container.Stack(stack =>
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(50);
|
||||
|
||||
row.RelativeItem().PaddingTop(-10).Text(Model.Title).Style(Typography.Title);
|
||||
row.ConstantItem(90).Hyperlink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
|
||||
row.RelativeColumn().PaddingTop(-10).Text(Model.Title, Typography.Title);
|
||||
row.ConstantColumn(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
|
||||
});
|
||||
|
||||
column.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
|
||||
stack.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
|
||||
|
||||
column.Item().ShowOnce().Grid(grid =>
|
||||
stack.Item().ShowOnce().Grid(grid =>
|
||||
{
|
||||
grid.Columns(2);
|
||||
grid.Spacing(5);
|
||||
@@ -69,7 +69,7 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
grid.Item().Text(text =>
|
||||
{
|
||||
text.Span($"{field.Label}: ").SemiBold();
|
||||
text.Span($"{field.Label}: ", TextStyle.Default.SemiBold());
|
||||
text.Span(field.Value);
|
||||
});
|
||||
}
|
||||
@@ -79,23 +79,23 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
void ComposeContent(IContainer container)
|
||||
{
|
||||
container.PaddingVertical(20).Column(column =>
|
||||
container.PaddingVertical(20).Stack(stack =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
stack.Spacing(20);
|
||||
|
||||
stack.Item().Component(new TableOfContentsTemplate(Model.Sections));
|
||||
|
||||
column.Item().Component(new TableOfContentsTemplate(Model.Sections));
|
||||
|
||||
column.Item().PageBreak();
|
||||
stack.Item().PageBreak();
|
||||
|
||||
foreach (var section in Model.Sections)
|
||||
column.Item().Section(section.Title).Component(new SectionTemplate(section));
|
||||
|
||||
column.Item().PageBreak();
|
||||
column.Item().Section("Photos");
|
||||
stack.Item().Location(section.Title).Component(new SectionTemplate(section));
|
||||
|
||||
stack.Item().PageBreak();
|
||||
stack.Item().Location("Photos");
|
||||
|
||||
foreach (var photo in Model.Photos)
|
||||
column.Item().Component(new PhotoTemplate(photo));
|
||||
stack.Item().Component(new PhotoTemplate(photo));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
@@ -22,19 +19,18 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.Header()
|
||||
.PaddingBottom(5)
|
||||
.Text("Table of contents")
|
||||
.Style(Typography.Headline);
|
||||
.Text("Table of contents", Typography.Headline);
|
||||
|
||||
decoration.Content().Column(column =>
|
||||
decoration.Content().Stack(stack =>
|
||||
{
|
||||
column.Spacing(5);
|
||||
stack.Spacing(5);
|
||||
|
||||
for (var i = 0; i < Sections.Count; i++)
|
||||
column.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
|
||||
stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
|
||||
|
||||
column.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
||||
stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -42,39 +38,13 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
private void DrawLink(IContainer container, int number, string locationName)
|
||||
{
|
||||
container
|
||||
.SectionLink(locationName)
|
||||
.InternalLink(locationName)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantItem(20).Text($"{number}.");
|
||||
row.AutoItem().Text(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.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x =>
|
||||
{
|
||||
var formatted = x == 1 ? "1 page long" : $"{x} pages long";
|
||||
return $" ({formatted})";
|
||||
});
|
||||
});
|
||||
row.ConstantColumn(25).Text($"{number}.");
|
||||
row.RelativeColumn().Text(locationName);
|
||||
row.ConstantColumn(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Drawing.Proxy;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.ReportSample.Layouts;
|
||||
|
||||
@@ -51,12 +50,12 @@ namespace QuestPDF.ReportSample
|
||||
Content = documentContainer.Compose();
|
||||
|
||||
PageContext = new PageContext();
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content);
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, Metadata, null);
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Content.VisitChildren(x =>
|
||||
Content.HandleVisitor(x =>
|
||||
{
|
||||
if (x is ICacheable)
|
||||
x.CreateProxy(y => new CacheProxy(y));
|
||||
@@ -69,7 +68,7 @@ namespace QuestPDF.ReportSample
|
||||
[Benchmark]
|
||||
public void GenerationTest()
|
||||
{
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content);
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, Metadata, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>8</LangVersion>
|
||||
<RootNamespace>QuestPDF.ReportSample</RootNamespace>
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -6,7 +6,6 @@ using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer;
|
||||
using QuestPDF.ReportSample.Layouts;
|
||||
|
||||
namespace QuestPDF.ReportSample
|
||||
@@ -25,13 +24,9 @@ namespace QuestPDF.ReportSample
|
||||
[Test]
|
||||
public void GenerateAndShowPdf()
|
||||
{
|
||||
Report.ShowInPreviewer();
|
||||
|
||||
//ImagePlaceholder.Solid = true;
|
||||
//
|
||||
// var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
|
||||
// Report.GeneratePdf(path);
|
||||
// Process.Start("explorer.exe", path);
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
|
||||
Report.GeneratePdf(path);
|
||||
Process.Start("explorer.exe", path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -51,10 +46,11 @@ namespace QuestPDF.ReportSample
|
||||
Report.Compose(container);
|
||||
var content = container.Compose();
|
||||
|
||||
var metadata = Report.GetMetadata();
|
||||
var pageContext = new PageContext();
|
||||
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content);
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content);
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, metadata, null);
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, metadata, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
public static class Typography
|
||||
{
|
||||
public static TextStyle Title => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Darken3).FontSize(26).Black();
|
||||
public static TextStyle Headline => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Medium).FontSize(16).SemiBold();
|
||||
public static TextStyle Normal => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Black).FontSize(10).LineHeight(1.2f);
|
||||
public static TextStyle Title => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Darken3).Size(26).Black();
|
||||
public static TextStyle Headline => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Medium).Size(16).SemiBold();
|
||||
public static TextStyle Normal => TextStyle.Default.FontType(Fonts.Verdana).Color(Colors.Black).Size(10).LineHeight(1.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,9 +153,7 @@ namespace QuestPDF.UnitTests
|
||||
Ratio = 2f
|
||||
})
|
||||
.DrawElement(new Size(500, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(400, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -170,45 +168,7 @@ namespace QuestPDF.UnitTests
|
||||
Ratio = 2f
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(400, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DrawChild_PerWidth_RightToLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new AspectRatio
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
Option = AspectRatioOption.FitArea,
|
||||
Ratio = 2f,
|
||||
ContentDirection = ContentDirection.RightToLeft
|
||||
})
|
||||
.DrawElement(new Size(500, 200))
|
||||
.ExpectCanvasTranslate(100, 0)
|
||||
.ExpectChildDraw(new Size(400, 200))
|
||||
.ExpectCanvasTranslate(-100, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DrawChild_PerHeight_RightToLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new AspectRatio
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
Option = AspectRatioOption.FitArea,
|
||||
Ratio = 2f,
|
||||
ContentDirection = ContentDirection.RightToLeft
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(400, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace QuestPDF.UnitTests
|
||||
Color = Colors.Red.Medium
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectCanvasDrawRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
|
||||
.ExpectCanvasDrawFilledRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace QuestPDF.UnitTests
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectCanvasDrawRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
|
||||
.ExpectCanvasDrawFilledRectangle(new Position(0, 0), new Size(400, 300), Colors.Red.Medium)
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using NUnit.Framework;
|
||||
using System.Drawing;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
using Size = QuestPDF.Infrastructure.Size;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
@@ -32,7 +34,29 @@ namespace QuestPDF.UnitTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_HorizontalRight_VerticalTop()
|
||||
public void Draw_SameWidths_Optimized()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Border
|
||||
{
|
||||
Top = 10,
|
||||
Right = 10,
|
||||
Bottom = 10,
|
||||
Left = 10,
|
||||
|
||||
Color = Colors.Red.Medium,
|
||||
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.ExpectCanvasDrawStrokedRectangle(new Size(400, 300), Colors.Red.Medium, 10)
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_VariousWidths()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Border
|
||||
@@ -48,10 +72,10 @@ namespace QuestPDF.UnitTests
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.ExpectCanvasDrawRectangle(new Position(-20, -5), new Size(430, 10), Colors.Red.Medium) // top
|
||||
.ExpectCanvasDrawRectangle(new Position(-20, -5), new Size(40, 320), Colors.Red.Medium) // left
|
||||
.ExpectCanvasDrawRectangle(new Position(-20, 285), new Size(430, 30), Colors.Red.Medium) // bottom
|
||||
.ExpectCanvasDrawRectangle(new Position(390, -5), new Size(20, 320), Colors.Red.Medium) // right
|
||||
.ExpectCanvasDrawFilledRectangle(new Position(-20, -5), new Size(430, 10), Colors.Red.Medium) // top
|
||||
.ExpectCanvasDrawFilledRectangle(new Position(-20, -5), new Size(40, 320), Colors.Red.Medium) // left
|
||||
.ExpectCanvasDrawFilledRectangle(new Position(-20, 285), new Size(430, 30), Colors.Red.Medium) // bottom
|
||||
.ExpectCanvasDrawFilledRectangle(new Position(390, -5), new Size(20, 320), Colors.Red.Medium) // right
|
||||
.ExpectChildDraw(new Size(400, 300))
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ namespace QuestPDF.UnitTests
|
||||
public class BoxTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<MinimalBox>();
|
||||
public void Measure() => SimpleContainerTests.Measure<Box>();
|
||||
|
||||
[Test]
|
||||
public void Draw_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new MinimalBox
|
||||
.For(x => new Box
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
@@ -29,15 +29,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new MinimalBox
|
||||
.For(x => new Box
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(200, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -45,49 +43,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_FullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new MinimalBox
|
||||
.For(x => new Box
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(500, 400))
|
||||
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(300, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(300, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_PartialRender_RightToLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new MinimalBox
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ContentDirection = ContentDirection.RightToLeft
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.PartialRender(200, 100))
|
||||
.ExpectCanvasTranslate(200, 0)
|
||||
.ExpectChildDraw(new Size(200, 100))
|
||||
.ExpectCanvasTranslate(-200, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_FullRender_RightToLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new MinimalBox
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ContentDirection = ContentDirection.RightToLeft
|
||||
})
|
||||
.MeasureElement(new Size(500, 400))
|
||||
.ExpectChildMeasure(expectedInput: new Size(500, 400), returns: SpacePlan.FullRender(350, 200))
|
||||
.ExpectCanvasTranslate(150, 0)
|
||||
.ExpectChildDraw(new Size(350, 200))
|
||||
.ExpectCanvasTranslate(-150, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,27 +9,35 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class DecorationTests
|
||||
{
|
||||
private Decoration CreateDecoration(TestPlan testPlan)
|
||||
{
|
||||
return new Decoration
|
||||
{
|
||||
Before = testPlan.CreateChild("before"),
|
||||
Content = testPlan.CreateChild("content"),
|
||||
After = testPlan.CreateChild("after"),
|
||||
};
|
||||
}
|
||||
|
||||
#region Measure
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenBeforeReturnsWrap()
|
||||
public void Measure_ReturnsWrap_WhenDecorationReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.Wrap())
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenDecorationReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.PartialRender(300, 200))
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
@@ -37,100 +45,89 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsWrap_WhenContentReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenAfterReturnsWrap()
|
||||
public void Measure_ReturnsPartialRender_WhenContentReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.Wrap())
|
||||
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(200, 150))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(400, 250));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenBeforeReturnsPartialRender()
|
||||
public void Measure_ReturnsFullRender_WhenContentReturnsFullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.PartialRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(200, 150))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(400, 250));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenAfterReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.PartialRender(100, 50))
|
||||
.ExpectChildMeasure("content", new Size(400, 250), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenContentReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.PartialRender(150, 100))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(150, 200));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenContentReturnsFullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(150, 100))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(150, 200));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Draw
|
||||
|
||||
[Test]
|
||||
public void Draw_Prepend()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Prepend,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
||||
.ExpectChildDraw("decoration", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 100)
|
||||
.ExpectChildDraw("content", new Size(400, 200))
|
||||
.ExpectCanvasTranslate(0, -100)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_Append()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateDecoration)
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(200, 40))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(200, 60))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.FullRender(300, 100))
|
||||
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("before", new Size(300, 40))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
|
||||
.ExpectCanvasTranslate(0, 40)
|
||||
.ExpectChildDraw("content", new Size(300, 100))
|
||||
.ExpectCanvasTranslate(0, -40)
|
||||
|
||||
.ExpectCanvasTranslate(0, 140)
|
||||
.ExpectChildDraw("after", new Size(300, 60))
|
||||
.ExpectCanvasTranslate(0, -140)
|
||||
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
||||
.ExpectChildDraw("content", new Size(400, 200))
|
||||
.ExpectCanvasTranslate(0, 200)
|
||||
.ExpectChildDraw("decoration", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, -200)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
||||
public class ExternalLinkTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Hyperlink>();
|
||||
public void Measure() => SimpleContainerTests.Measure<ExternalLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using SkiaSharp;
|
||||
using static SkiaSharp.SKFontStyleSlant;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FontStyleSetTests
|
||||
{
|
||||
private void ExpectComparisonOrder(SKFontStyle target, SKFontStyle[] styles)
|
||||
{
|
||||
for (var i = 0; i < styles.Length - 1; i++)
|
||||
{
|
||||
var currentStyle = styles[i];
|
||||
var nextStyle = styles[i + 1];
|
||||
|
||||
FontStyleSet.IsBetterMatch(target, currentStyle, nextStyle).Should().BeTrue();
|
||||
FontStyleSet.IsBetterMatch(target, nextStyle, currentStyle).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_CondensedWidth()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Upright),
|
||||
new SKFontStyle(500, 4, Upright),
|
||||
new SKFontStyle(500, 3, Upright),
|
||||
new SKFontStyle(500, 6, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ExpandedWidth()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 6, Upright),
|
||||
new SKFontStyle(500, 7, Upright),
|
||||
new SKFontStyle(500, 8, Upright),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 6, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ItalicSlant()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Italic),
|
||||
new SKFontStyle(500, 5, Oblique),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Italic), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ObliqueSlant()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Oblique),
|
||||
new SKFontStyle(500, 5, Italic),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Oblique), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_UprightSlant()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Upright),
|
||||
new SKFontStyle(500, 5, Oblique),
|
||||
new SKFontStyle(500, 5, Italic)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ThinWeight()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(300, 5, Upright),
|
||||
new SKFontStyle(200, 5, Upright),
|
||||
new SKFontStyle(100, 5, Upright),
|
||||
new SKFontStyle(400, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(300, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_RegularWeight()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Upright),
|
||||
new SKFontStyle(300, 5, Upright),
|
||||
new SKFontStyle(100, 5, Upright),
|
||||
new SKFontStyle(600, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(400, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_BoldWeight()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(600, 5, Upright),
|
||||
new SKFontStyle(700, 5, Upright),
|
||||
new SKFontStyle(800, 5, Upright),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(600, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_RespectsPriority()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(600, 5, Italic),
|
||||
new SKFontStyle(600, 6, Upright),
|
||||
new SKFontStyle(500, 6, Italic)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,26 +39,26 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Column(column =>
|
||||
expected.Stack(stack =>
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(6).Element(childA);
|
||||
row.RelativeItem(4).Element(childB);
|
||||
row.RelativeItem(2);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(2);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(4).Element(childC);
|
||||
row.RelativeItem(2).Element(childD);
|
||||
row.RelativeItem(6);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(6);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(8).Element(childE);
|
||||
row.RelativeItem(4);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,29 +93,29 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Column(column =>
|
||||
expected.Stack(stack =>
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(1);
|
||||
row.RelativeItem(6).Element(childA);
|
||||
row.RelativeItem(4).Element(childB);
|
||||
row.RelativeItem(1);
|
||||
row.RelativeColumn(1);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(1);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(3);
|
||||
row.RelativeItem(4).Element(childC);
|
||||
row.RelativeItem(2).Element(childD);
|
||||
row.RelativeItem(3);
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(3);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(2);
|
||||
row.RelativeItem(8).Element(childE);
|
||||
row.RelativeItem(2);
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,26 +150,26 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Column(column =>
|
||||
expected.Stack(stack =>
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(2);
|
||||
row.RelativeItem(6).Element(childA);
|
||||
row.RelativeItem(4).Element(childB);
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(6);
|
||||
row.RelativeItem(4).Element(childC);
|
||||
row.RelativeItem(2).Element(childD);
|
||||
row.RelativeColumn(6);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.RelativeItem(4);
|
||||
row.RelativeItem(8).Element(childE);
|
||||
row.RelativeColumn(4);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,36 +210,36 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Column(column =>
|
||||
expected.Stack(stack =>
|
||||
{
|
||||
column.Spacing(20);
|
||||
stack.Spacing(20);
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeItem(3);
|
||||
row.RelativeItem(5).Element(childA);
|
||||
row.RelativeItem(5).Element(childB);
|
||||
row.RelativeItem(3);
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(5).Element(childA);
|
||||
row.RelativeColumn(5).Element(childB);
|
||||
row.RelativeColumn(3);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeItem(3);
|
||||
row.RelativeItem(10).Element(childC);
|
||||
row.RelativeItem(3);
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(10).Element(childC);
|
||||
row.RelativeColumn(3);
|
||||
});
|
||||
|
||||
column.Item().Row(row =>
|
||||
stack.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeItem(2);
|
||||
row.RelativeItem(12).Element(childD);
|
||||
row.RelativeItem(2);
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(12).Element(childD);
|
||||
row.RelativeColumn(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
||||
public class InternalLinkTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
|
||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
||||
public class InternalLocationTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
|
||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
public class PreviewerTests
|
||||
{
|
||||
[Test]
|
||||
public void SendError()
|
||||
{
|
||||
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 =>
|
||||
{
|
||||
//throw new Exception("Dupa!");
|
||||
x.Span("Page ");
|
||||
x.CurrentPageNumber();
|
||||
});
|
||||
});
|
||||
})
|
||||
.ShowInPreviewer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="6.8.0" />
|
||||
<PackageReference Include="nunit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.88.3" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.1.0" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class RowTests
|
||||
{
|
||||
#region Measure
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenLeftChildReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenRightChildReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
|
||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsPartialRender_WhenLeftChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.PartialRender(250, 150))
|
||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(350, 150));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsPartialRender_WhenRightChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
|
||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.PartialRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(350, 150));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsFullRender_WhenBothChildrenReturnFullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(200, 150))
|
||||
.ExpectChildMeasure("right", new Size(200, 300), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(300, 150));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Draw
|
||||
|
||||
[Test]
|
||||
public void Draw()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
|
||||
.ExpectChildDraw("left", new Size(250, 300))
|
||||
.ExpectCanvasTranslate(250, 0)
|
||||
.ExpectChildDraw("right", new Size(150, 300))
|
||||
.ExpectCanvasTranslate(-250, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Structure
|
||||
|
||||
[Test]
|
||||
public void Structure_RelativeColumnsHandling()
|
||||
{
|
||||
// arrange
|
||||
var childA = TestPlan.CreateUniqueElement();
|
||||
var childB = TestPlan.CreateUniqueElement();
|
||||
var childC = TestPlan.CreateUniqueElement();
|
||||
var childD = TestPlan.CreateUniqueElement();
|
||||
var childE = TestPlan.CreateUniqueElement();
|
||||
|
||||
const int spacing = 25;
|
||||
var availableSpace = new Size(1100, 400);
|
||||
|
||||
// act
|
||||
var value = new Container();
|
||||
|
||||
value.Row(row =>
|
||||
{
|
||||
row.Spacing(spacing);
|
||||
|
||||
row.ConstantColumn(150).Element(childA);
|
||||
row.ConstantColumn(250).Element(childB);
|
||||
row.RelativeColumn(1).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(3).Element(childE);
|
||||
});
|
||||
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Row(row =>
|
||||
{
|
||||
row.Spacing(spacing);
|
||||
|
||||
row.ConstantColumn(150).Element(childA);
|
||||
row.ConstantColumn(250).Element(childB);
|
||||
row.ConstantColumn(100).Element(childC);
|
||||
row.ConstantColumn(200).Element(childD);
|
||||
row.ConstantColumn(300).Element(childE);
|
||||
});
|
||||
|
||||
TestPlan.CompareOperations(value, expected, availableSpace);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Structure_Tree()
|
||||
{
|
||||
// arrange
|
||||
var childA = TestPlan.CreateUniqueElement();
|
||||
var childB = TestPlan.CreateUniqueElement();
|
||||
var childC = TestPlan.CreateUniqueElement();
|
||||
var childD = TestPlan.CreateUniqueElement();
|
||||
var childE = TestPlan.CreateUniqueElement();
|
||||
|
||||
const int spacing = 25;
|
||||
var availableSpace = new Size(1200, 400);
|
||||
|
||||
// act
|
||||
var value = new Container();
|
||||
|
||||
value.Row(row =>
|
||||
{
|
||||
row.Spacing(spacing);
|
||||
|
||||
row.ConstantColumn(150).Element(childA);
|
||||
row.ConstantColumn(200).Element(childB);
|
||||
row.ConstantColumn(250).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(3).Element(childE);
|
||||
});
|
||||
|
||||
// assert
|
||||
var expected = new BinaryRow
|
||||
{
|
||||
Left = new BinaryRow
|
||||
{
|
||||
Left = new BinaryRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 150,
|
||||
MaxWidth = 150,
|
||||
Child = childA
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
}
|
||||
},
|
||||
Right = new BinaryRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 200,
|
||||
MaxWidth = 200,
|
||||
Child = childB
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
}
|
||||
}
|
||||
},
|
||||
Right = new BinaryRow
|
||||
{
|
||||
Left = new BinaryRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 250,
|
||||
MaxWidth = 250,
|
||||
Child = childC
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
}
|
||||
},
|
||||
Right = new BinaryRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 200,
|
||||
MaxWidth = 200,
|
||||
Child = childD
|
||||
},
|
||||
Right = new BinaryRow
|
||||
{
|
||||
Left = new Constrained
|
||||
{
|
||||
MinWidth = 25,
|
||||
MaxWidth = 25
|
||||
},
|
||||
Right = new Constrained
|
||||
{
|
||||
MinWidth = 300,
|
||||
MaxWidth = 300,
|
||||
Child = childE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestPlan.CompareOperations(value, expected, availableSpace);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
@@ -9,40 +8,19 @@ using QuestPDF.UnitTests.TestEngine;
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ColumnTests
|
||||
public class StackTests
|
||||
{
|
||||
private Column CreateColumnWithTwoItems(TestPlan testPlan)
|
||||
{
|
||||
return new Column
|
||||
{
|
||||
Items =
|
||||
{
|
||||
new ColumnItem
|
||||
{
|
||||
Child = testPlan.CreateChild("first")
|
||||
},
|
||||
new ColumnItem
|
||||
{
|
||||
Child = testPlan.CreateChild("second")
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Column CreateColumnWithTwoItemsWhereFirstIsFullyRendered(TestPlan testPlan)
|
||||
{
|
||||
var column = CreateColumnWithTwoItems(testPlan);
|
||||
column.Items.First().IsRendered = true;
|
||||
return column;
|
||||
}
|
||||
|
||||
#region Measure
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenFirstChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
@@ -52,7 +30,11 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(300, 200))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(300, 200));
|
||||
@@ -62,7 +44,11 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||
@@ -73,7 +59,11 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(300, 150))
|
||||
@@ -84,13 +74,33 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(100, 50))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(200, 150));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Measure_UsesEmpty_WhenFirstChildIsRendered()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second"),
|
||||
|
||||
IsFirstRendered = true
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(200, 300));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Draw
|
||||
@@ -99,7 +109,11 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckDrawResult();
|
||||
@@ -109,12 +123,14 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildPartiallyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(200, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -122,13 +138,15 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -136,13 +154,15 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
|
||||
.ExpectCanvasTranslate(0, 100)
|
||||
.ExpectChildDraw("second", new Size(400, 150))
|
||||
.ExpectCanvasTranslate(0, -100)
|
||||
@@ -153,13 +173,15 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
|
||||
.ExpectCanvasTranslate(0, 100)
|
||||
.ExpectChildDraw("second", new Size(400, 150))
|
||||
.ExpectCanvasTranslate(0, -100)
|
||||
@@ -170,13 +192,19 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_UsesEmpty_WhenFirstChildIsRendered()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second"),
|
||||
|
||||
IsFirstRendered = true
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.PartialRender(200, 300))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("second", new Size(400, 300))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckState<Column>(x => x.Items.First().IsRendered)
|
||||
.CheckState<BinaryStack>(x => x.IsFirstRendered)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -184,14 +212,97 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second"),
|
||||
|
||||
IsFirstRendered = true
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("second", new Size(400, 300), SpacePlan.FullRender(200, 300))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("second", new Size(400, 300))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult()
|
||||
.CheckState<Column>(x => !x.Items.First().IsRendered);
|
||||
.CheckState<BinaryStack>(x => !x.IsFirstRendered);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Structure
|
||||
|
||||
[Test]
|
||||
public void Structure_Simple()
|
||||
{
|
||||
// arrange
|
||||
var childA = TestPlan.CreateUniqueElement();
|
||||
var childB = TestPlan.CreateUniqueElement();
|
||||
var childC = TestPlan.CreateUniqueElement();
|
||||
var childD = TestPlan.CreateUniqueElement();
|
||||
var childE = TestPlan.CreateUniqueElement();
|
||||
|
||||
const int spacing = 20;
|
||||
|
||||
// act
|
||||
var structure = new Container();
|
||||
|
||||
structure.Stack(stack =>
|
||||
{
|
||||
stack.Spacing(spacing);
|
||||
|
||||
stack.Item().Element(childA);
|
||||
stack.Item().Element(childB);
|
||||
stack.Item().Element(childC);
|
||||
stack.Item().Element(childD);
|
||||
stack.Item().Element(childE);
|
||||
});
|
||||
|
||||
// assert
|
||||
var expected = new Padding
|
||||
{
|
||||
Bottom = -spacing,
|
||||
|
||||
Child = new BinaryStack
|
||||
{
|
||||
First = new BinaryStack
|
||||
{
|
||||
First = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childA
|
||||
},
|
||||
Second = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childB
|
||||
}
|
||||
},
|
||||
Second = new BinaryStack
|
||||
{
|
||||
First = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childC
|
||||
},
|
||||
Second = new BinaryStack
|
||||
{
|
||||
First = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childD
|
||||
},
|
||||
Second = new Padding
|
||||
{
|
||||
Bottom = spacing,
|
||||
Child = childE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TestPlan.CompareOperations(structure, expected);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -11,18 +10,21 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
public Action<float> RotateFunc { get; set; }
|
||||
public Action<float, float> ScaleFunc { get; set; }
|
||||
public Action<SKImage, Position, Size> DrawImageFunc { get; set; }
|
||||
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
||||
public Action<string, Position, TextStyle> DrawTextFunc { get; set; }
|
||||
public Action<Position, Size, string> DrawFilledRectangleFunc { get; set; }
|
||||
public Action<Size, string, float> DrawStrokedRectangleFunc { get; set; }
|
||||
|
||||
public void Translate(Position vector) => TranslateFunc(vector);
|
||||
public void Rotate(float angle) => RotateFunc(angle);
|
||||
public void Scale(float scaleX, float scaleY) => ScaleFunc(scaleX, scaleY);
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
|
||||
public void DrawFilledRectangle(Position vector, Size size, string color) => DrawFilledRectangleFunc(vector, size, color);
|
||||
public void DrawStrokedRectangle(Size size, string color, float width) => DrawStrokedRectangleFunc(size, color, width);
|
||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||
|
||||
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
|
||||
public void DrawSectionLink(string sectionName, Size size) => throw new NotImplementedException();
|
||||
public void DrawSection(string sectionName) => throw new NotImplementedException();
|
||||
|
||||
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocation(string locationName) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine.Operations;
|
||||
using SkiaSharp;
|
||||
@@ -15,12 +14,13 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
public void Rotate(float angle) => Operations.Add(new CanvasRotateOperation(angle));
|
||||
public void Scale(float scaleX, float scaleY) => Operations.Add(new CanvasScaleOperation(scaleX, scaleY));
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawRectangleOperation(vector, size, color));
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
|
||||
public void DrawFilledRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawFilledRectangleOperation(vector, size, color));
|
||||
public void DrawStrokedRectangle(Size size, string color, float width) => Operations.Add(new CanvasDrawStrokedRectangleOperation(size, color, width));
|
||||
public void DrawText(string text, Position position, TextStyle style) => Operations.Add(new CanvasDrawTextOperation(text, position, style));
|
||||
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
|
||||
|
||||
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
|
||||
public void DrawSectionLink(string sectionName, Size size) => throw new NotImplementedException();
|
||||
public void DrawSection(string sectionName) => throw new NotImplementedException();
|
||||
public void DrawExternalLink(string url, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocationLink(string locationName, Size size) => throw new NotImplementedException();
|
||||
public void DrawLocation(string locationName) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawRectangleOperation : OperationBase
|
||||
internal class CanvasDrawFilledRectangleOperation : OperationBase
|
||||
{
|
||||
public Position Position { get; }
|
||||
public Size Size { get; }
|
||||
public string Color { get; }
|
||||
|
||||
public CanvasDrawRectangleOperation(Position position, Size size, string color)
|
||||
public CanvasDrawFilledRectangleOperation(Position position, Size size, string color)
|
||||
{
|
||||
Position = position;
|
||||
Size = size;
|
||||
@@ -0,0 +1,18 @@
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.UnitTests.TestEngine.Operations
|
||||
{
|
||||
internal class CanvasDrawStrokedRectangleOperation : OperationBase
|
||||
{
|
||||
public Size Size { get; }
|
||||
public string Color { get; }
|
||||
public float Width { get; set; }
|
||||
|
||||
public CanvasDrawStrokedRectangleOperation(Size size, string color, float width)
|
||||
{
|
||||
Size = size;
|
||||
Color = color;
|
||||
Width = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,17 +70,40 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
Assert.AreEqual(expected.ScaleX, scaleX, "Scale X");
|
||||
Assert.AreEqual(expected.ScaleY, scaleY, "Scale Y");
|
||||
},
|
||||
DrawRectFunc = (position, size, color) =>
|
||||
DrawFilledRectangleFunc = (position, size, color) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawRectangleOperation>();
|
||||
var expected = GetExpected<CanvasDrawFilledRectangleOperation>();
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X, "Draw rectangle: X");
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw rectangle: Y");
|
||||
Assert.AreEqual(expected.Position.X, position.X, "Draw filled rectangle: X");
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw filled rectangle: Y");
|
||||
|
||||
Assert.AreEqual(expected.Size.Width, size.Width, "Draw rectangle: width");
|
||||
Assert.AreEqual(expected.Size.Height, size.Height, "Draw rectangle: height");
|
||||
Assert.AreEqual(expected.Size.Width, size.Width, "Draw filled rectangle: width");
|
||||
Assert.AreEqual(expected.Size.Height, size.Height, "Draw filled rectangle: height");
|
||||
|
||||
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
|
||||
Assert.AreEqual(expected.Color, color, "Draw filled rectangle: color");
|
||||
},
|
||||
DrawStrokedRectangleFunc = (size, color, width) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawStrokedRectangleOperation>();
|
||||
|
||||
Assert.AreEqual(expected.Size.Width, size.Width, "Draw stroked rectangle: width");
|
||||
Assert.AreEqual(expected.Size.Height, size.Height, "Draw stroked rectangle: height");
|
||||
|
||||
Assert.AreEqual(expected.Color, color, "Draw stroked rectangle: color");
|
||||
Assert.AreEqual(expected.Width, width, "Draw stroked rectangle: width");
|
||||
},
|
||||
DrawTextFunc = (text, position, style) =>
|
||||
{
|
||||
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) =>
|
||||
{
|
||||
@@ -183,9 +206,19 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
return AddOperation(new CanvasRotateOperation(angle));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawRectangle(Position position, Size size, string color)
|
||||
public TestPlan ExpectCanvasDrawFilledRectangle(Position position, Size size, string color)
|
||||
{
|
||||
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
|
||||
return AddOperation(new CanvasDrawFilledRectangleOperation(position, size, color));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawStrokedRectangle(Size size, string color, float width)
|
||||
{
|
||||
return AddOperation(new CanvasDrawStrokedRectangleOperation(size, color, width));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawText(string text, Position position, TextStyle style)
|
||||
{
|
||||
return AddOperation(new CanvasDrawTextOperation(text, position, style));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawImage(Position position, Size size)
|
||||
@@ -195,7 +228,7 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public TestPlan CheckMeasureResult(SpacePlan expected)
|
||||
{
|
||||
Element.InjectDependencies(null, Canvas);
|
||||
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
|
||||
|
||||
var actual = Element.Measure(OperationInput);
|
||||
|
||||
@@ -210,7 +243,7 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public TestPlan CheckDrawResult()
|
||||
{
|
||||
Element.InjectDependencies(null, Canvas);
|
||||
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
|
||||
Element.Draw(OperationInput);
|
||||
return this;
|
||||
}
|
||||
@@ -253,10 +286,10 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
availableSpace ??= new Size(400, 300);
|
||||
|
||||
var canvas = new FreeCanvas();
|
||||
value.InjectDependencies(null, canvas);
|
||||
value.HandleVisitor(x => x.Initialize(null, canvas));
|
||||
var valueMeasure = value.Measure(availableSpace.Value);
|
||||
|
||||
expected.InjectDependencies(null, canvas);
|
||||
expected.HandleVisitor(x => x.Initialize(null, canvas));
|
||||
var expectedMeasure = expected.Measure(availableSpace.Value);
|
||||
|
||||
valueMeasure.Should().BeEquivalentTo(expectedMeasure);
|
||||
@@ -267,11 +300,11 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
availableSpace ??= new Size(400, 300);
|
||||
|
||||
var valueCanvas = new OperationRecordingCanvas();
|
||||
value.InjectDependencies(null, valueCanvas);
|
||||
value.HandleVisitor(x => x.Initialize(null, valueCanvas));
|
||||
value.Draw(availableSpace.Value);
|
||||
|
||||
var expectedCanvas = new OperationRecordingCanvas();
|
||||
expected.InjectDependencies(null, expectedCanvas);
|
||||
expected.HandleVisitor(x => x.Initialize(null, expectedCanvas));
|
||||
expected.Draw(availableSpace.Value);
|
||||
|
||||
valueCanvas.Operations.Should().BeEquivalentTo(expectedCanvas.Operations);
|
||||
|
||||
@@ -77,9 +77,7 @@ namespace QuestPDF.UnitTests
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, SpacePlan.PartialRender(1200, 1600))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(1200, 1600))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -93,43 +91,7 @@ namespace QuestPDF.UnitTests
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, SpacePlan.FullRender(1600, 1000))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(1600, 1000))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_WhenChildPartiallyRenders_RightToLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ContentDirection = ContentDirection.RightToLeft
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, SpacePlan.PartialRender(1200, 1600))
|
||||
.ExpectCanvasTranslate(-1200, 0)
|
||||
.ExpectChildDraw(new Size(1200, 1600))
|
||||
.ExpectCanvasTranslate(1200, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Draw_WhenChildFullyRenders_RightToLeft()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Unconstrained
|
||||
{
|
||||
Child = x.CreateChild(),
|
||||
ContentDirection = ContentDirection.RightToLeft
|
||||
})
|
||||
.DrawElement(new Size(900, 800))
|
||||
.ExpectChildMeasure(Size.Max, SpacePlan.FullRender(1600, 1000))
|
||||
.ExpectCanvasTranslate(-1600, 0)
|
||||
.ExpectChildDraw(new Size(1600, 1000))
|
||||
.ExpectCanvasTranslate(1600, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.UnitTests", "Quest
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
|
||||
EndProject
|
||||
Project("{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}") = "SimpleApp", "SimpleApp\SimpleApp.csproj", "{B361D452-80A8-493A-BEA6-AB268E99153E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -37,9 +30,5 @@ Global
|
||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B361D452-80A8-493A-BEA6-AB268E99153E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B361D452-80A8-493A-BEA6-AB268E99153E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B361D452-80A8-493A-BEA6-AB268E99153E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B361D452-80A8-493A-BEA6-AB268E99153E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("QuestPDF.UnitTests")]
|
||||
[assembly: InternalsVisibleTo("QuestPDF.Examples")]
|
||||
[assembly: InternalsVisibleTo("QuestPDF.ReportSample")]
|
||||
@@ -16,14 +16,13 @@ namespace QuestPDF.Drawing
|
||||
var container = new Container();
|
||||
|
||||
container
|
||||
.DebugPointer("Document")
|
||||
.Column(column =>
|
||||
.Stack(stack =>
|
||||
{
|
||||
Pages
|
||||
.SelectMany(x => new List<Action>()
|
||||
{
|
||||
() => column.Item().PageBreak(),
|
||||
() => column.Item().DebugPointer("Page").Component(x)
|
||||
() => stack.Item().PageBreak(),
|
||||
() => stack.Item().Component(x)
|
||||
})
|
||||
.Skip(1)
|
||||
.ToList()
|
||||
|
||||
@@ -7,10 +7,7 @@ using QuestPDF.Drawing.Proxy;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Elements.Text;
|
||||
using QuestPDF.Elements.Text.Items;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer.Inspection;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
@@ -18,8 +15,6 @@ namespace QuestPDF.Drawing
|
||||
{
|
||||
internal static void GeneratePdf(Stream stream, IDocument document)
|
||||
{
|
||||
CheckIfStreamIsCompatible(stream);
|
||||
|
||||
var metadata = document.GetMetadata();
|
||||
var canvas = new PdfCanvas(stream, metadata);
|
||||
RenderDocument(canvas, document);
|
||||
@@ -27,21 +22,10 @@ namespace QuestPDF.Drawing
|
||||
|
||||
internal static void GenerateXps(Stream stream, IDocument document)
|
||||
{
|
||||
CheckIfStreamIsCompatible(stream);
|
||||
|
||||
var metadata = document.GetMetadata();
|
||||
var canvas = new XpsCanvas(stream, metadata);
|
||||
RenderDocument(canvas, document);
|
||||
}
|
||||
|
||||
private static void CheckIfStreamIsCompatible(Stream stream)
|
||||
{
|
||||
if (!stream.CanWrite)
|
||||
throw new ArgumentException("The library requires a Stream object with the 'write' capability available (the CanWrite flag). Please consider using the MemoryStream class.");
|
||||
|
||||
if (!stream.CanSeek)
|
||||
throw new ArgumentException("The library requires a Stream object with the 'seek' capability available (the CanSeek flag). Please consider using the MemoryStream class.");
|
||||
}
|
||||
|
||||
internal static ICollection<byte[]> GenerateImages(IDocument document)
|
||||
{
|
||||
@@ -52,49 +36,30 @@ namespace QuestPDF.Drawing
|
||||
return canvas.Images;
|
||||
}
|
||||
|
||||
internal static DocumentPreviewResult GeneratePreviewerPictures(IDocument document)
|
||||
{
|
||||
var canvas = new SkiaPictureCanvas();
|
||||
InspectionElement documentHierarchy = null;
|
||||
|
||||
RenderDocument(canvas, document, true, x => documentHierarchy = x);
|
||||
|
||||
return new DocumentPreviewResult
|
||||
{
|
||||
PageSnapshots = canvas.Pictures.ToList(),
|
||||
DocumentHierarchy = documentHierarchy
|
||||
};
|
||||
}
|
||||
|
||||
internal static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document, bool applyInspection = false, Action<InspectionElement> inspectionResultHandler = null)
|
||||
private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
|
||||
where TCanvas : ICanvas, IRenderingCanvas
|
||||
{
|
||||
var container = new DocumentContainer();
|
||||
document.Compose(container);
|
||||
var content = container.Compose();
|
||||
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
||||
ApplyContentDirection(content, ContentDirection.LeftToRight);
|
||||
|
||||
if (Settings.EnableCaching)
|
||||
var metadata = document.GetMetadata();
|
||||
var pageContext = new PageContext();
|
||||
|
||||
var debuggingState = metadata.ApplyDebugging ? ApplyDebugging(content) : null;
|
||||
|
||||
if (metadata.ApplyCaching)
|
||||
ApplyCaching(content);
|
||||
|
||||
var pageContext = new PageContext();
|
||||
RenderPass(pageContext, new FreeCanvas(), content);
|
||||
|
||||
if (applyInspection)
|
||||
ApplyInspection(content);
|
||||
|
||||
RenderPass(pageContext, canvas, content);
|
||||
|
||||
if (applyInspection)
|
||||
inspectionResultHandler(content.ExtractDocumentHierarchy());
|
||||
RenderPass(pageContext, new FreeCanvas(), content, metadata, debuggingState);
|
||||
RenderPass(pageContext, canvas, content, metadata, debuggingState);
|
||||
}
|
||||
|
||||
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content)
|
||||
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata, DebuggingState? debuggingState)
|
||||
where TCanvas : ICanvas, IRenderingCanvas
|
||||
{
|
||||
InjectDependencies(content, pageContext, canvas);
|
||||
content.VisitChildren(x => (x as IStateResettable)?.ResetState());
|
||||
content.HandleVisitor(x => x?.Initialize(pageContext, canvas));
|
||||
content.HandleVisitor(x => (x as IStateResettable)?.ResetState());
|
||||
|
||||
canvas.BeginDocument();
|
||||
|
||||
@@ -103,6 +68,7 @@ namespace QuestPDF.Drawing
|
||||
while(true)
|
||||
{
|
||||
pageContext.SetPageNumber(currentPage);
|
||||
debuggingState?.Reset();
|
||||
|
||||
var spacePlan = content.Measure(Size.Max);
|
||||
|
||||
@@ -125,7 +91,7 @@ namespace QuestPDF.Drawing
|
||||
|
||||
canvas.EndPage();
|
||||
|
||||
if (currentPage >= Settings.DocumentLayoutExceptionThreshold)
|
||||
if (currentPage >= documentMetadata.DocumentLayoutExceptionThreshold)
|
||||
{
|
||||
canvas.EndDocument();
|
||||
ThrowLayoutException();
|
||||
@@ -142,99 +108,59 @@ namespace QuestPDF.Drawing
|
||||
void ThrowLayoutException()
|
||||
{
|
||||
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 {Settings.DocumentLayoutExceptionThreshold} pages. " +
|
||||
$"In this case, please increase the value {nameof(QuestPDF)}.{nameof(Settings)}.{nameof(Settings.DocumentLayoutExceptionThreshold)} static property. " +
|
||||
$"1) Your document and its layout configuration is correct but the content takes more than {documentMetadata.DocumentLayoutExceptionThreshold} pages. " +
|
||||
$"In this case, please increase the value {nameof(DocumentMetadata)}.{nameof(DocumentMetadata.DocumentLayoutExceptionThreshold)} property configured in the {nameof(IDocument.GetMetadata)} method. " +
|
||||
$"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.";
|
||||
|
||||
content.RemoveExistingProxies();
|
||||
content.RemoveExistingContainers();
|
||||
content.ApplyLayoutDebugging();
|
||||
content.Measure(Size.Max);
|
||||
|
||||
var layoutTrace = content.CollectLayoutErrorTrace();
|
||||
|
||||
throw new DocumentLayoutException(message, layoutTrace);
|
||||
throw new DocumentLayoutException(message)
|
||||
{
|
||||
ElementTrace = debuggingState?.BuildTrace() ?? "Debug trace is available only in the DEBUG mode."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal static void InjectDependencies(this Element content, IPageContext pageContext, ICanvas canvas)
|
||||
{
|
||||
content.VisitChildren(x =>
|
||||
{
|
||||
if (x == null)
|
||||
return;
|
||||
|
||||
x.PageContext = pageContext;
|
||||
x.Canvas = canvas;
|
||||
});
|
||||
}
|
||||
|
||||
private static void ApplyCaching(Container content)
|
||||
{
|
||||
content.VisitChildren(x =>
|
||||
content.HandleVisitor(x =>
|
||||
{
|
||||
if (x is ICacheable)
|
||||
x.CreateProxy(y => new CacheProxy(y));
|
||||
});
|
||||
}
|
||||
|
||||
private static void ApplyInspection(Container content)
|
||||
private static DebuggingState ApplyDebugging(Container content)
|
||||
{
|
||||
content.VisitChildren(x =>
|
||||
var debuggingState = new DebuggingState();
|
||||
|
||||
content.HandleVisitor(x =>
|
||||
{
|
||||
x.CreateProxy(y => y is ElementProxy or Container ? y : new InspectionProxy(y));
|
||||
x.CreateProxy(y => new DebuggingProxy(debuggingState, y));
|
||||
});
|
||||
}
|
||||
|
||||
internal static void ApplyContentDirection(this Element? content, ContentDirection direction)
|
||||
{
|
||||
if (content == null)
|
||||
return;
|
||||
|
||||
if (content is ContentDirectionSetter contentDirectionSetter)
|
||||
{
|
||||
ApplyContentDirection(contentDirectionSetter.Child, contentDirectionSetter.ContentDirection);
|
||||
return;
|
||||
}
|
||||
|
||||
if (content is IContentDirectionAware contentDirectionAware)
|
||||
contentDirectionAware.ContentDirection = direction;
|
||||
|
||||
foreach (var child in content.GetChildren())
|
||||
ApplyContentDirection(child, direction);
|
||||
return debuggingState;
|
||||
}
|
||||
|
||||
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
||||
internal static void ApplyDefaultTextStyle(this Element content, TextStyle documentDefaultTextStyle)
|
||||
{
|
||||
if (content == null)
|
||||
return;
|
||||
documentDefaultTextStyle.ApplyGlobalStyle(TextStyle.LibraryDefault);
|
||||
|
||||
if (content is TextBlock textBlock)
|
||||
content.HandleVisitor(element =>
|
||||
{
|
||||
foreach (var textBlockItem in textBlock.Items)
|
||||
{
|
||||
if (textBlockItem is TextBlockSpan textSpan)
|
||||
{
|
||||
textSpan.Style = textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||
}
|
||||
else if (textBlockItem is TextBlockElement textElement)
|
||||
{
|
||||
ApplyDefaultTextStyle(textElement.Element, documentDefaultTextStyle);
|
||||
}
|
||||
}
|
||||
var text = element as TextBlock;
|
||||
|
||||
return;
|
||||
}
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
if (content is DynamicHost dynamicHost)
|
||||
dynamicHost.TextStyle = dynamicHost.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||
|
||||
if (content is DefaultTextStyle defaultTextStyleElement)
|
||||
documentDefaultTextStyle = defaultTextStyleElement.TextStyle.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||
foreach (var child in text.Children)
|
||||
{
|
||||
if (child is TextBlockSpan textSpan)
|
||||
textSpan.Style.ApplyGlobalStyle(documentDefaultTextStyle);
|
||||
|
||||
foreach (var child in content.GetChildren())
|
||||
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
||||
if (child is TextBlockElement textElement)
|
||||
ApplyDefaultTextStyle(textElement.Element, documentDefaultTextStyle);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
@@ -19,26 +18,14 @@ namespace QuestPDF.Drawing
|
||||
public DateTime CreationDate { get; set; } = DateTime.Now;
|
||||
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
||||
|
||||
[Obsolete("This API has been moved since version 2022.9. Please use the QuestPDF.Settings.DocumentLayoutExceptionThreshold static property.")]
|
||||
public int DocumentLayoutExceptionThreshold
|
||||
{
|
||||
get => Settings.DocumentLayoutExceptionThreshold;
|
||||
set => Settings.DocumentLayoutExceptionThreshold = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// If the number of generated pages exceeds this threshold
|
||||
/// (likely due to infinite layout), the exception is thrown.
|
||||
/// </summary>
|
||||
public int DocumentLayoutExceptionThreshold { get; set; } = 250;
|
||||
|
||||
[Obsolete("This API has been moved since version 2022.9. Please use the QuestPDF.Settings.EnableCaching static property.")]
|
||||
public bool ApplyCaching
|
||||
{
|
||||
get => Settings.EnableCaching;
|
||||
set => Settings.EnableCaching = value;
|
||||
}
|
||||
|
||||
[Obsolete("The new implementation for debugging layout issues does not introduce additional performance overhead. Therefore, this setting is no longer used since the 2023.1 release. Please remove this setter from your code.")]
|
||||
public bool ApplyDebugging
|
||||
{
|
||||
get => Settings.EnableDebugging;
|
||||
set => Settings.EnableDebugging = value;
|
||||
}
|
||||
public bool ApplyCaching { get; set; } = !System.Diagnostics.Debugger.IsAttached;
|
||||
public bool ApplyDebugging { get; set; } = System.Diagnostics.Debugger.IsAttached;
|
||||
|
||||
public static DocumentMetadata Default => new DocumentMetadata();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,17 @@ namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class DocumentComposeException : Exception
|
||||
{
|
||||
internal DocumentComposeException(string message) : base(message)
|
||||
public DocumentComposeException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentComposeException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentComposeException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@ namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class DocumentDrawingException : Exception
|
||||
{
|
||||
internal DocumentDrawingException(string message) : base(message)
|
||||
public DocumentDrawingException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal DocumentDrawingException(string message, Exception inner) : base(message, inner)
|
||||
|
||||
public DocumentDrawingException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentDrawingException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
using System;
|
||||
using QuestPDF.Previewer;
|
||||
|
||||
namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class DocumentLayoutException : Exception
|
||||
{
|
||||
internal LayoutErrorTrace? ElementTrace { get; }
|
||||
|
||||
internal DocumentLayoutException(string message, LayoutErrorTrace? elementTrace = null) : base(message)
|
||||
public string ElementTrace { get; set; }
|
||||
|
||||
public DocumentLayoutException()
|
||||
{
|
||||
ElementTrace = elementTrace;
|
||||
|
||||
}
|
||||
|
||||
public DocumentLayoutException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentLayoutException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-163
@@ -1,211 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using HarfBuzzSharp;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
public static class FontManager
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, FontStyleSet> StyleSets = new();
|
||||
private static readonly ConcurrentDictionary<TextStyle, SKFontMetrics> FontMetrics = new();
|
||||
private static readonly ConcurrentDictionary<TextStyle, SKPaint> FontPaints = new();
|
||||
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();
|
||||
private static ConcurrentDictionary<string, SKTypeface> Typefaces = new ConcurrentDictionary<string, SKTypeface>();
|
||||
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
|
||||
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
||||
|
||||
static FontManager()
|
||||
{
|
||||
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);
|
||||
Typefaces.TryAdd(fontName, SKTypeface.FromStream(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)
|
||||
{
|
||||
return ColorPaints.GetOrAdd(color, Convert);
|
||||
return ColorPaint.GetOrAdd(color, Convert);
|
||||
|
||||
static SKPaint Convert(string color)
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(color),
|
||||
IsAntialias = true
|
||||
Color = SKColor.Parse(color)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static SKPaint ToPaint(this TextStyle style)
|
||||
{
|
||||
return FontPaints.GetOrAdd(style, Convert);
|
||||
|
||||
return Paints.GetOrAdd(style.Key, key => Convert(style));
|
||||
|
||||
static SKPaint Convert(TextStyle style)
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(style.Color),
|
||||
Typeface = GetTypeface(style),
|
||||
TextSize = (style.Size ?? 12) * GetTextScale(style),
|
||||
IsAntialias = true,
|
||||
TextSize = (style.Size ?? 12),
|
||||
TextEncoding = SKTextEncoding.Utf32
|
||||
};
|
||||
}
|
||||
|
||||
static SKTypeface GetTypeface(TextStyle style)
|
||||
{
|
||||
var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (Typefaces.TryGetValue(style.FontType, out var result))
|
||||
return result;
|
||||
|
||||
var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
|
||||
var fontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
|
||||
|
||||
if (StyleSets.TryGetValue(style.FontFamily, out var fontStyleSet))
|
||||
return fontStyleSet.Match(fontStyle);
|
||||
|
||||
var fontFromDefaultSource = SKFontManager.Default.MatchFamily(style.FontFamily, fontStyle);
|
||||
|
||||
if (fontFromDefaultSource != null)
|
||||
return fontFromDefaultSource;
|
||||
|
||||
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()
|
||||
};
|
||||
return SKTypeface.FromFamilyName(style.FontType, (int)(style.FontWeight ?? FontWeight.Normal), (int)SKFontStyleWidth.Normal, slant)
|
||||
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
||||
{
|
||||
return FontMetrics.GetOrAdd(style, 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());
|
||||
return FontMetrics.GetOrAdd(style.Key, key => style.ToPaint().FontMetrics);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
internal class FontStyleSet
|
||||
{
|
||||
private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles { get; } = new();
|
||||
|
||||
public void Add(SKTypeface typeface)
|
||||
{
|
||||
var style = typeface.FontStyle;
|
||||
Styles.AddOrUpdate(style, _ => typeface, (_, _) => typeface);
|
||||
}
|
||||
|
||||
public SKTypeface? Match(SKFontStyle target)
|
||||
{
|
||||
SKFontStyle? bestStyle = null;
|
||||
SKTypeface? bestTypeface = null;
|
||||
|
||||
foreach (var entry in Styles)
|
||||
{
|
||||
if (IsBetterMatch(target, entry.Key, bestStyle))
|
||||
{
|
||||
bestStyle = entry.Key;
|
||||
bestTypeface = entry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return bestTypeface;
|
||||
}
|
||||
|
||||
private static Dictionary<SKFontStyleSlant, List<SKFontStyleSlant>> SlantFallbacks = new()
|
||||
{
|
||||
{ SKFontStyleSlant.Italic, new() { SKFontStyleSlant.Italic, SKFontStyleSlant.Oblique, SKFontStyleSlant.Upright } },
|
||||
{ SKFontStyleSlant.Oblique, new() { SKFontStyleSlant.Oblique, SKFontStyleSlant.Italic, SKFontStyleSlant.Upright } },
|
||||
{ SKFontStyleSlant.Upright, new() { SKFontStyleSlant.Upright, SKFontStyleSlant.Oblique, SKFontStyleSlant.Italic } },
|
||||
};
|
||||
|
||||
// Checks whether style a is a better match for the target then style b. Uses the CSS font style matching algorithm
|
||||
internal static bool IsBetterMatch(SKFontStyle? target, SKFontStyle? a, SKFontStyle? b)
|
||||
{
|
||||
// A font is better than no font
|
||||
if (b == null)
|
||||
return true;
|
||||
|
||||
if (a == null)
|
||||
return false;
|
||||
|
||||
// First check font width
|
||||
// For normal and condensed widths prefer smaller widths
|
||||
// For expanded widths prefer larger widths
|
||||
if (target.Width <= (int)SKFontStyleWidth.Normal)
|
||||
{
|
||||
if (a.Width <= target.Width && b.Width > target.Width)
|
||||
return true;
|
||||
|
||||
if (a.Width > target.Width && b.Width <= target.Width)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.Width >= target.Width && b.Width < target.Width)
|
||||
return true;
|
||||
|
||||
if (a.Width < target.Width && b.Width >= target.Width)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer closest match
|
||||
var widthDifferenceA = Math.Abs(a.Width - target.Width);
|
||||
var widthDifferenceB = Math.Abs(b.Width - target.Width);
|
||||
|
||||
if (widthDifferenceA < widthDifferenceB)
|
||||
return true;
|
||||
|
||||
if (widthDifferenceB < widthDifferenceA)
|
||||
return false;
|
||||
|
||||
// Prefer closest slant based on provided fallback list
|
||||
var slantFallback = SlantFallbacks[target.Slant];
|
||||
var slantIndexA = slantFallback.IndexOf(a.Slant);
|
||||
var slantIndexB = slantFallback.IndexOf(b.Slant);
|
||||
|
||||
if (slantIndexA < slantIndexB)
|
||||
return true;
|
||||
|
||||
if (slantIndexB < slantIndexA)
|
||||
return false;
|
||||
|
||||
// Check weight last
|
||||
// For thin (<400) weights, prefer thinner weights
|
||||
// For regular (400-500) weights, prefer other regular weights, then use rule for thin or bold
|
||||
// For bold (>500) weights, prefer thicker weights
|
||||
// Behavior for values other than multiples of 100 is not given in the specification
|
||||
|
||||
if (target.Weight >= 400 && target.Weight <= 500)
|
||||
{
|
||||
if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500))
|
||||
return true;
|
||||
|
||||
if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.Weight < 450)
|
||||
{
|
||||
if (a.Weight <= target.Weight && b.Weight > target.Weight)
|
||||
return true;
|
||||
|
||||
if (a.Weight > target.Weight && b.Weight <= target.Weight)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.Weight >= target.Weight && b.Weight < target.Weight)
|
||||
return true;
|
||||
|
||||
if (a.Weight < target.Weight && b.Weight >= target.Weight)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer closest weight
|
||||
var weightDifferenceA = Math.Abs(a.Weight - target.Weight);
|
||||
var weightDifferenceB = Math.Abs(b.Weight - target.Weight);
|
||||
|
||||
return weightDifferenceA < weightDifferenceB;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,17 @@ namespace QuestPDF.Drawing
|
||||
|
||||
}
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color)
|
||||
public void DrawFilledRectangle(Position vector, Size size, string color)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DrawStrokedRectangle(Size size, string color, float width)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style)
|
||||
public void DrawText(string text, Position position, TextStyle style)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -51,17 +56,17 @@ namespace QuestPDF.Drawing
|
||||
|
||||
}
|
||||
|
||||
public void DrawHyperlink(string url, Size size)
|
||||
public void DrawExternalLink(string url, Size size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DrawSectionLink(string sectionName, Size size)
|
||||
public void DrawLocationLink(string locationName, Size size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DrawSection(string sectionName)
|
||||
public void DrawLocation(string locationName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Helpers;
|
||||
using System.IO;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
@@ -9,23 +7,11 @@ namespace QuestPDF.Drawing
|
||||
internal class PdfCanvas : SkiaDocumentCanvasBase
|
||||
{
|
||||
public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
|
||||
: base(CreatePdf(stream, documentMetadata))
|
||||
: base(SKDocument.CreatePdf(stream, MapMetadata(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)
|
||||
{
|
||||
return new SKDocumentPdfMetadata
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace QuestPDF.Drawing.Proxy
|
||||
base.Draw(availableSpace);
|
||||
}
|
||||
|
||||
private static bool IsClose(float x, float y)
|
||||
private bool IsClose(float x, float y)
|
||||
{
|
||||
return Math.Abs(x - y) < Size.Epsilon;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy
|
||||
{
|
||||
public class DebugStackItem
|
||||
{
|
||||
public IElement Element { get; internal set; }
|
||||
public Size AvailableSpace { get; internal set; }
|
||||
public SpacePlan SpacePlan { get; internal set; }
|
||||
|
||||
public ICollection<DebugStackItem> Stack { get; internal set; } = new List<DebugStackItem>();
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy
|
||||
{
|
||||
internal class DebuggingProxy : ElementProxy
|
||||
{
|
||||
internal List<MeasureDetails> Measurements { get; } = new();
|
||||
private DebuggingState DebuggingState { get; }
|
||||
|
||||
public DebuggingProxy(Element child)
|
||||
public DebuggingProxy(DebuggingState debuggingState, Element child)
|
||||
{
|
||||
DebuggingState = debuggingState;
|
||||
Child = child;
|
||||
}
|
||||
|
||||
internal override SpacePlan Measure(Size availableSpace)
|
||||
{
|
||||
var spacePlan = Child.Measure(availableSpace);
|
||||
Measurements.Add(new MeasureDetails(availableSpace, spacePlan));
|
||||
DebuggingState.RegisterMeasure(Child, availableSpace);
|
||||
var spacePlan = base.Measure(availableSpace);
|
||||
DebuggingState.RegisterMeasureResult(Child, spacePlan);
|
||||
|
||||
return spacePlan;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct MeasureDetails
|
||||
{
|
||||
public Size AvailableSpace { get; }
|
||||
public SpacePlan SpacePlan { get;}
|
||||
|
||||
public MeasureDetails(Size availableSpace, SpacePlan spacePlan)
|
||||
{
|
||||
AvailableSpace = availableSpace;
|
||||
SpacePlan = spacePlan;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy
|
||||
{
|
||||
internal class DebuggingState
|
||||
{
|
||||
private DebugStackItem Root { get; set; }
|
||||
private Stack<DebugStackItem> Stack { get; set; }
|
||||
|
||||
public DebuggingState()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Root = null;
|
||||
Stack = new Stack<DebugStackItem>();
|
||||
}
|
||||
|
||||
public void RegisterMeasure(IElement element, Size availableSpace)
|
||||
{
|
||||
if (element.GetType() == typeof(Container))
|
||||
return;
|
||||
|
||||
var item = new DebugStackItem
|
||||
{
|
||||
Element = element,
|
||||
AvailableSpace = availableSpace
|
||||
};
|
||||
|
||||
Root ??= item;
|
||||
|
||||
if (Stack.Any())
|
||||
Stack.Peek().Stack.Add(item);
|
||||
|
||||
Stack.Push(item);
|
||||
}
|
||||
|
||||
public void RegisterMeasureResult(IElement element, SpacePlan spacePlan)
|
||||
{
|
||||
if (element.GetType() == typeof(Container))
|
||||
return;
|
||||
|
||||
var item = Stack.Pop();
|
||||
|
||||
if (item.Element != element)
|
||||
throw new Exception();
|
||||
|
||||
item.SpacePlan = spacePlan;
|
||||
}
|
||||
|
||||
public string BuildTrace()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
var nestingLevel = 0;
|
||||
|
||||
Traverse(Root);
|
||||
return builder.ToString();
|
||||
|
||||
void Traverse(DebugStackItem item)
|
||||
{
|
||||
var indent = new string(' ', nestingLevel * 4);
|
||||
var title = item.Element.GetType().Name;
|
||||
|
||||
if (item.Element is DebugPointer debugPointer)
|
||||
{
|
||||
title = debugPointer.Target;
|
||||
title += debugPointer.Highlight ? " 🌟" : string.Empty;
|
||||
}
|
||||
|
||||
if (item.SpacePlan.Type != SpacePlanType.FullRender)
|
||||
title = "🔥 " + title;
|
||||
|
||||
builder.AppendLine(indent + title);
|
||||
builder.AppendLine(indent + new string('-', title.Length));
|
||||
|
||||
builder.AppendLine(indent + "Available space: " + item.AvailableSpace);
|
||||
builder.AppendLine(indent + "Requested space: " + item.SpacePlan);
|
||||
|
||||
foreach (var configuration in GetElementConfiguration(item.Element))
|
||||
builder.AppendLine(indent + configuration);
|
||||
|
||||
builder.AppendLine();
|
||||
|
||||
nestingLevel++;
|
||||
item.Stack.ToList().ForEach(Traverse);
|
||||
nestingLevel--;
|
||||
}
|
||||
|
||||
static IEnumerable<string> GetElementConfiguration(IElement element)
|
||||
{
|
||||
if (element is DebugPointer)
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
return element
|
||||
.GetType()
|
||||
.GetProperties()
|
||||
.Select(x => new
|
||||
{
|
||||
Property = x.Name.PrettifyName(),
|
||||
Value = x.GetValue(element)
|
||||
})
|
||||
.Where(x => !(x.Value is IElement))
|
||||
.Where(x => x.Value is string || !(x.Value is IEnumerable))
|
||||
.Where(x => !(x.Value is TextStyle))
|
||||
.Select(x => $"{x.Property}: {FormatValue(x.Value)}");
|
||||
|
||||
string FormatValue(object value)
|
||||
{
|
||||
const int maxLength = 100;
|
||||
|
||||
var text = value?.ToString() ?? "-";
|
||||
|
||||
if (text.Length < maxLength)
|
||||
return text;
|
||||
|
||||
return text.AsSpan(0, maxLength).ToString() + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy;
|
||||
|
||||
internal static class Helpers
|
||||
{
|
||||
public static IReadOnlyCollection<DocumentElementProperty> GetElementConfiguration(this IElement element)
|
||||
{
|
||||
return element
|
||||
.GetType()
|
||||
.GetProperties()
|
||||
.Select(x => new
|
||||
{
|
||||
Property = x.Name.PrettifyName(),
|
||||
Value = x.GetValue(element)
|
||||
})
|
||||
.Where(x => !(x.Value is IElement))
|
||||
.Where(x => x.Value is string || !(x.Value is IEnumerable))
|
||||
.Where(x => !(x.Value is TextStyle))
|
||||
.Select(x => new DocumentElementProperty
|
||||
{
|
||||
Label = x.Property,
|
||||
Value = FormatValue(x.Value)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
string FormatValue(object value)
|
||||
{
|
||||
const int maxLength = 100;
|
||||
|
||||
var text = value?.ToString() ?? "-";
|
||||
|
||||
if (text.Length < maxLength)
|
||||
return text;
|
||||
|
||||
return text.AsSpan(0, maxLength).ToString() + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer.Inspection;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy
|
||||
{
|
||||
internal class InspectionProxy : ElementProxy
|
||||
{
|
||||
public Dictionary<int, InspectionStateItem> Statistics { get; set; } = new();
|
||||
|
||||
public InspectionProxy(Element child)
|
||||
{
|
||||
Child = child;
|
||||
}
|
||||
|
||||
internal override void Draw(Size availableSpace)
|
||||
{
|
||||
if (Canvas is SkiaCanvasBase canvas)
|
||||
{
|
||||
var matrix = canvas.Canvas.TotalMatrix;
|
||||
|
||||
var inspectionItem = new InspectionStateItem
|
||||
{
|
||||
Element = Child,
|
||||
Position = new Position(matrix.TransX, matrix.TransY),
|
||||
Size = availableSpace
|
||||
};
|
||||
|
||||
Statistics[PageContext.CurrentPage] = inspectionItem;
|
||||
}
|
||||
|
||||
base.Draw(availableSpace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Linq;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy
|
||||
{
|
||||
internal static class LayoutErrorAnalyzer
|
||||
{
|
||||
public static void ApplyLayoutDebugging(this Container content)
|
||||
{
|
||||
content.VisitChildren(x =>
|
||||
{
|
||||
x.CreateProxy(y => y is ElementProxy ? y : new DebuggingProxy(y));
|
||||
});
|
||||
}
|
||||
|
||||
public static void RemoveExistingProxies(this Container content)
|
||||
{
|
||||
content.VisitChildren(x =>
|
||||
{
|
||||
x.CreateProxy(y => y is ElementProxy proxy ? proxy.Child : y);
|
||||
});
|
||||
}
|
||||
|
||||
public static void RemoveExistingContainers(this Container content)
|
||||
{
|
||||
content.VisitChildren(x =>
|
||||
{
|
||||
x.CreateProxy(y => y is Container container ? container.Child : y);
|
||||
});
|
||||
}
|
||||
|
||||
public static LayoutErrorTrace CollectLayoutErrorTrace(this Container content)
|
||||
{
|
||||
var proxies = content.ExtractProxyOfType<DebuggingProxy>();
|
||||
return Map(proxies);
|
||||
|
||||
LayoutErrorTrace Map(TreeNode<DebuggingProxy> proxy)
|
||||
{
|
||||
var element = proxy.Value.Child;
|
||||
|
||||
return new LayoutErrorTrace
|
||||
{
|
||||
ElementType = element.GetType().Name,
|
||||
IsSingleChildContainer = element is ContainerElement,
|
||||
ElementProperties = element.GetElementConfiguration().ToList(),
|
||||
|
||||
Measurements = proxy
|
||||
.Value
|
||||
.Measurements
|
||||
.Select(x => new LayoutErrorMeasurement
|
||||
{
|
||||
AvailableSpace = new QuestPDF.Previewer.Size()
|
||||
{
|
||||
Width = x.AvailableSpace.Width,
|
||||
Height = x.AvailableSpace.Height
|
||||
},
|
||||
SpacePlan = new QuestPDF.Previewer.SpacePlan()
|
||||
{
|
||||
Width = x.SpacePlan.Width,
|
||||
Height = x.SpacePlan.Height,
|
||||
Type = x.SpacePlan.Type
|
||||
}
|
||||
})
|
||||
.ToList(),
|
||||
|
||||
Children = proxy.Children.Select(Map).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Concurrent;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
internal class SkiaCache
|
||||
{
|
||||
private static ConcurrentDictionary<string, SKPaint> RectangleFillPaints = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, SKPaint> RectangleStrokePaints = new ConcurrentDictionary<string, SKPaint>();
|
||||
|
||||
public static SKPaint GetRectangleFillPaint(string color)
|
||||
{
|
||||
return RectangleFillPaints.GetOrAdd(color, _ => Convert());
|
||||
|
||||
SKPaint Convert()
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(color),
|
||||
Style = SKPaintStyle.Fill
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static SKPaint GetRectangleStrokePaint(string color, float width)
|
||||
{
|
||||
var key = $"{color}|{width}";
|
||||
return RectangleStrokePaints.GetOrAdd(key, _ => Convert());
|
||||
|
||||
SKPaint Convert()
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(color),
|
||||
Style = SKPaintStyle.Stroke,
|
||||
StrokeWidth = width
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
@@ -19,18 +18,24 @@ namespace QuestPDF.Drawing
|
||||
Canvas.Translate(vector.X, vector.Y);
|
||||
}
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color)
|
||||
public void DrawFilledRectangle(Position vector, Size size, string color)
|
||||
{
|
||||
if (size.Width < Size.Epsilon || size.Height < Size.Epsilon)
|
||||
return;
|
||||
|
||||
var paint = color.ColorToPaint();
|
||||
var paint = SkiaCache.GetRectangleFillPaint(color);
|
||||
Canvas.DrawRect(vector.X, vector.Y, size.Width, size.Height, paint);
|
||||
}
|
||||
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style)
|
||||
|
||||
public void DrawStrokedRectangle(Size size, string color, float width)
|
||||
{
|
||||
Canvas.DrawText(skTextBlob, position.X, position.Y, style.ToPaint());
|
||||
var paint = SkiaCache.GetRectangleStrokePaint(color, width);
|
||||
Canvas.DrawRect(0, 0, size.Width, size.Height, paint);
|
||||
}
|
||||
|
||||
public void DrawText(string text, Position vector, TextStyle style)
|
||||
{
|
||||
Canvas.DrawText(text, vector.X, vector.Y, style.ToPaint());
|
||||
}
|
||||
|
||||
public void DrawImage(SKImage image, Position vector, Size size)
|
||||
@@ -38,19 +43,19 @@ namespace QuestPDF.Drawing
|
||||
Canvas.DrawImage(image, new SKRect(vector.X, vector.Y, size.Width, size.Height));
|
||||
}
|
||||
|
||||
public void DrawHyperlink(string url, Size size)
|
||||
public void DrawExternalLink(string url, Size size)
|
||||
{
|
||||
Canvas.DrawUrlAnnotation(new SKRect(0, 0, size.Width, size.Height), url);
|
||||
}
|
||||
|
||||
public void DrawSectionLink(string sectionName, Size size)
|
||||
public void DrawLocationLink(string locationName, Size size)
|
||||
{
|
||||
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), sectionName);
|
||||
Canvas.DrawLinkDestinationAnnotation(new SKRect(0, 0, size.Width, size.Height), locationName);
|
||||
}
|
||||
|
||||
public void DrawSection(string sectionName)
|
||||
public void DrawLocation(string locationName)
|
||||
{
|
||||
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), sectionName);
|
||||
Canvas.DrawNamedDestinationAnnotation(new SKPoint(0, 0), locationName);
|
||||
}
|
||||
|
||||
public void Rotate(float angle)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user