Compare commits
397 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c679978be | |||
| 4fcd955238 | |||
| a9c06db2e0 | |||
| 2f55b8d386 | |||
| e4a5c471ee | |||
| 4e3d462e8d | |||
| 423ca330f7 | |||
| 57c2a8cb38 | |||
| 7c51649415 | |||
| 423c630a47 | |||
| 42c6c75862 | |||
| 559de4520a | |||
| 73e66c293b | |||
| 164bdd76d8 | |||
| d62cec4f66 | |||
| baa0feb5e6 | |||
| 57e68e293d | |||
| 320a6e221d | |||
| d92231f5d4 | |||
| 1060e4a44a | |||
| 983edd5f76 | |||
| 5315165cf5 | |||
| ee3d87010e | |||
| 3ac91c4971 | |||
| 8e450934ab | |||
| bf193eec23 | |||
| c9104540aa | |||
| af60d84abe | |||
| d56117647b | |||
| 117913693c | |||
| 2ba716a5ba | |||
| be2108f6fe | |||
| c08bb963a6 | |||
| 8ed8897fd1 | |||
| 2a0e91af68 | |||
| f647b79a0a | |||
| b1286bb536 | |||
| 43d1a55df0 | |||
| d1cd7b06db | |||
| 7bce01d6e6 | |||
| e3ff2960e1 | |||
| 5d9e6f327b | |||
| b895931eb9 | |||
| 86a35b7cd3 | |||
| 65990ebf59 | |||
| c348106b3f | |||
| 9d86e9efd3 | |||
| 9770575f9a | |||
| e6e6febf8b | |||
| 5cbacc7ea1 | |||
| 8214a8429d | |||
| 389b8ce304 | |||
| e1f7cff4aa | |||
| 6801425082 | |||
| ec31c9b063 | |||
| 6c8867e1b3 | |||
| 20d86cbfde | |||
| 16a164e5b8 | |||
| 261f087b46 | |||
| c876350b05 | |||
| 58f3932241 | |||
| 350abc2838 | |||
| 5d4c9a5dd2 | |||
| b31d097b61 | |||
| 9f6c091706 | |||
| af2229eabe | |||
| b86946b396 | |||
| e1ae97d3ca | |||
| a7ac747776 | |||
| fab0f5bfe1 | |||
| f5f90bd235 | |||
| 603dc13d0f | |||
| e277c6a8dc | |||
| 5ff9d03f20 | |||
| 9ce9ca85be | |||
| 553c8ab719 | |||
| e768e9b06d | |||
| fd913a777c | |||
| ed9e6daec5 | |||
| ee6249a658 | |||
| b307304f46 | |||
| f028f82e11 | |||
| 719a3385f6 | |||
| 2adff11400 | |||
| 34fed6d547 | |||
| db2df75624 | |||
| 6b535752df | |||
| 556f87ff25 | |||
| fbebbd85eb | |||
| 4650c2a4ea | |||
| 5390fc3f1b | |||
| 1ba01a1cf5 | |||
| d4448437ac | |||
| bc853f48fc | |||
| 0468dd5f02 | |||
| 3ba09ea826 | |||
| 92abd32aae | |||
| e17867d1f3 | |||
| bd71f30c78 | |||
| 6a78a4ebdb | |||
| 9fc721d66b | |||
| 7d62dead86 | |||
| 52ad0f5c24 | |||
| 53107073a6 | |||
| c71bb3ea59 | |||
| 6cced3c143 | |||
| 425ea59cfe | |||
| 4d326496c6 | |||
| b7b6488d16 | |||
| f0ba5fc32d | |||
| 04da32e0e7 | |||
| 5bdb338996 | |||
| 06b30f2ad6 | |||
| d39f68a974 | |||
| db9d8b5fcf | |||
| a31a588b24 | |||
| cb2a3c6363 | |||
| e775f62fab | |||
| 056e3f244c | |||
| cd3f7f5a25 | |||
| a5805f6e43 | |||
| abe7b2f14c | |||
| 3e485a572c | |||
| fd0bb5b089 | |||
| cc51b62b73 | |||
| 8e16b5ad4f | |||
| 71e2743409 | |||
| ba9d3aa04b | |||
| 2a0b133185 | |||
| 6058dac524 | |||
| 25b8f62245 | |||
| 2c31f3d5b5 | |||
| f7cd89ccf6 | |||
| 6914a9063a | |||
| b87e8d5d4e | |||
| d740d67a64 | |||
| a14e9ca61f | |||
| 44b3ff8d0d | |||
| df6b88e14a | |||
| d4103797aa | |||
| d894914e7e | |||
| 90d1ce7af7 | |||
| 5fa32a06cc | |||
| acdf1ed1aa | |||
| a0b32e5ecf | |||
| 539dc83303 | |||
| d97fa2cef1 | |||
| 6d122c784d | |||
| 28743a81d1 | |||
| 1047854f77 | |||
| c5fbf7263c | |||
| fae2205ff8 | |||
| 1cbe4b1dc8 | |||
| 32b94cea43 | |||
| 0ca55a91e0 | |||
| 09e6b5253f | |||
| bacb136a3f | |||
| b7bc640e31 | |||
| 6e6f884a5f | |||
| b47a8ac409 | |||
| e51082400c | |||
| 52cfa3ab7e | |||
| cd181b1e2e | |||
| e71601ad0c | |||
| 3041aa53cc | |||
| 0ebf2833af | |||
| 25e6d9fbca | |||
| f80149792d | |||
| 2beee1d22e | |||
| 1af7c0a144 | |||
| eeca67e5bc | |||
| 4da3ef5dfa | |||
| f3d4151273 | |||
| 9a7e397db2 | |||
| d81f21e658 | |||
| 78909c6edf | |||
| a2d81b6adf | |||
| 4a761c023f | |||
| 24bbd06dff | |||
| 44bd646bab | |||
| 5f0291b2c4 | |||
| 1b91015d8f | |||
| 2b6444463d | |||
| a2629575e8 | |||
| c8824021cc | |||
| e1101d870d | |||
| 152304b467 | |||
| 8a1be90c4f | |||
| bc8a9e5dc5 | |||
| e54e09de15 | |||
| c49a424f87 | |||
| c63b0658f3 | |||
| 82cd1d7835 | |||
| 3ffab6e933 | |||
| e4d55e0298 | |||
| a6f4fd7f49 | |||
| 048a4748b2 | |||
| 8cc2fb8696 | |||
| 8e7b528cd9 | |||
| 26193313b8 | |||
| 555825e8be | |||
| ddde8e7517 | |||
| d2c82b5988 | |||
| d82aae95b5 | |||
| 5c11a2ab6a | |||
| 03e3a80fd9 | |||
| 4692c5ce62 | |||
| 919f916219 | |||
| 7a32f774a1 | |||
| fba71e6392 | |||
| e27585a504 | |||
| 9be497ee41 | |||
| 0fc0db55d2 | |||
| 9e7a1ed49d | |||
| c137bcfcde | |||
| b80b3414d8 | |||
| 5f7cee3420 | |||
| 435c42ab9e | |||
| 7b4923b45d | |||
| 8732b7e50b | |||
| 6b31322f92 | |||
| 6155d21207 | |||
| a0909eb057 | |||
| 11ff53946d | |||
| 0711bca1e1 | |||
| 297ca48c70 | |||
| d2601e9e5a | |||
| 421a926780 | |||
| 307759d1b3 | |||
| 00bcb2c25b | |||
| e430484e85 | |||
| d24d887a3a | |||
| 404914ea7e | |||
| 545b76fd40 | |||
| c801894ec4 | |||
| c21744e7de | |||
| 734d116bfe | |||
| 0abb67b748 | |||
| 828bb86737 | |||
| 040c47e017 | |||
| ff95fe287a | |||
| 47e9a49fe5 | |||
| b2826a06b8 | |||
| 321cba32ce | |||
| 26fdbb714d | |||
| f0cec8988a | |||
| ea2102fe4c | |||
| 6cca3ed057 | |||
| 9afe4ec104 | |||
| 846679007f | |||
| 0946607154 | |||
| d5f86e0d70 | |||
| 346513ee85 | |||
| d18ee5f282 | |||
| a85bc0abc1 | |||
| 82e39df572 | |||
| 1311b6db32 | |||
| b5645bfbd5 | |||
| 8217b3b54a | |||
| 726759f47a | |||
| bbd9c3379e | |||
| 697c5e238d | |||
| 21a92be87e | |||
| b0cca1ff48 | |||
| 78c5049eda | |||
| 3d0ca07b43 | |||
| a67fa754af | |||
| 208235ad1e | |||
| f24416474a | |||
| 01c2e8d3f5 | |||
| b3cef238ec | |||
| aede62c135 | |||
| db9db95dcc | |||
| 92263685d7 | |||
| ba4be521aa | |||
| f6967dfe8b | |||
| ff3dfe7831 | |||
| 5a1990d857 | |||
| 1b9b164583 | |||
| 9c64a6bc8d | |||
| 5402e5fedc | |||
| 2c14f91ebe | |||
| b533a32b61 | |||
| 262bd3c182 | |||
| fdf8e3eb1a | |||
| fcff989ab2 | |||
| 054e7a5f20 | |||
| a92cc25fb1 | |||
| 8db283d559 | |||
| 456c7a735b | |||
| 5bf2a9a3d3 | |||
| 8a64abaac2 | |||
| 68df390d0e | |||
| d73c694452 | |||
| 69e268733c | |||
| 657bbe2aac | |||
| 4479a49ad0 | |||
| 63288dfb60 | |||
| fc325d8b76 | |||
| 5ffeaa3075 | |||
| fe431fbc12 | |||
| e0de408821 | |||
| 8ce6afef5a | |||
| 1c70b20028 | |||
| 3aef4ec4c9 | |||
| 3649607bd9 | |||
| 9a6b11ab85 | |||
| ac29ed4794 | |||
| 5f7da8b5b3 | |||
| 78eef45ce2 | |||
| 5424d9f0a0 | |||
| 946fe91a98 | |||
| b5839d3989 | |||
| 2ca6d1cb47 | |||
| 49a3371af7 | |||
| 3f3ada6d8a | |||
| ba409a8712 | |||
| b96c67a8af | |||
| b9fbb9267f | |||
| 272b0fc11a | |||
| b2cc7bbd36 | |||
| e3ce194fd1 | |||
| 9b892c86ed | |||
| c9833d410d | |||
| f8057a840c | |||
| a2ce31022c | |||
| 763fdfac0f | |||
| ba8b35b1c0 | |||
| 648a359d15 | |||
| 630d3389d3 | |||
| a7c3c88e81 | |||
| 9524cc6eb1 | |||
| 4152b67679 | |||
| 8020cb30cb | |||
| 9358fb8937 | |||
| eb297177ea | |||
| 7f651932b9 | |||
| caaae5fa1c | |||
| f76caa28b1 | |||
| ea92655ef5 | |||
| 3f8463e9b7 | |||
| 02e25b193d | |||
| 7cc272ff9e | |||
| b8ea30be23 | |||
| 06418238dc | |||
| ed73c5b82d | |||
| 96732b5856 | |||
| c7008ad940 | |||
| 940616cd3e | |||
| 01a80032b0 | |||
| 3e5e3843d2 | |||
| d3911d5687 | |||
| 82e4dd36f0 | |||
| 561d6a004c | |||
| 59b77e55d4 | |||
| ea398c562f | |||
| cb493562fc | |||
| 5f636beb9d | |||
| 5b85ae2627 | |||
| 10683776bb | |||
| f2c47c4fd3 | |||
| 77b8f04659 | |||
| 5235ada66f | |||
| 4ce66878f2 | |||
| 79cc5f1507 | |||
| 7c7d461c4b | |||
| 63af713935 | |||
| 2419a25cbd | |||
| 014ec15908 | |||
| 82e29d5fdd | |||
| c64754241d | |||
| 9de87061e4 | |||
| 86cfd22316 | |||
| 520bfc840c | |||
| 22cdad69b7 | |||
| d260fa7587 | |||
| 011668fb4c | |||
| 5a886452cf | |||
| b044208195 | |||
| dadb8a5087 | |||
| 11f0809efc | |||
| 0a6673dd4f | |||
| 525bf534e5 | |||
| fd696c5b66 | |||
| 0ccc8cc236 | |||
| 9ef444462a | |||
| 479915b68f | |||
| 5deea91fe4 | |||
| 7174a2afa1 | |||
| 1371fe1df9 | |||
| bb00dfd4d4 | |||
| 70e5783f0e | |||
| e8b7f38187 | |||
| c2ba2addfc | |||
| e379502a64 | |||
| caa779e1e0 | |||
| 4938e97e6d |
+131
@@ -0,0 +1,131 @@
|
||||
# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\GithubRepos\QuestPDF codebase based on best match to current usage at 16.03.2022
|
||||
# You can modify the rules from these initially generated values to suit your own policies
|
||||
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
|
||||
[*.cs]
|
||||
|
||||
|
||||
#Core editorconfig formatting - indentation
|
||||
|
||||
#use soft tabs (spaces) for indentation
|
||||
indent_style = space
|
||||
|
||||
#Formatting - new line options
|
||||
|
||||
#place catch statements on a new line
|
||||
csharp_new_line_before_catch = true
|
||||
#place else statements on a new line
|
||||
csharp_new_line_before_else = true
|
||||
#require members of anonymous types to be on separate lines
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
#require members of object intializers to be on separate lines
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
#require braces to be on a new line for object_collection_array_initializers, methods, anonymous_types, control_blocks, types, and lambdas (also known as "Allman" style)
|
||||
csharp_new_line_before_open_brace =all
|
||||
|
||||
#Formatting - organize using options
|
||||
|
||||
#sort System.* using directives alphabetically, and place them before other usings
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
#Formatting - spacing options
|
||||
|
||||
#require NO space between a cast and the value
|
||||
csharp_space_after_cast = false
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
#require a space after a keyword in a control flow statement such as a for loop
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
#require a space before the colon for bases or interfaces in a type declaration
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
#remove space within empty argument list parentheses
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
#remove space between method call name and opening parenthesis
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
#remove space within empty parameter list parentheses for a method declaration
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
|
||||
#Formatting - wrapping options
|
||||
|
||||
#leave code block on single line
|
||||
csharp_preserve_single_line_blocks = true
|
||||
|
||||
#Style - Code block preferences
|
||||
|
||||
#prefer no curly braces if allowed
|
||||
csharp_prefer_braces = false:suggestion
|
||||
|
||||
#Style - expression bodied member options
|
||||
|
||||
#prefer block bodies for constructors
|
||||
csharp_style_expression_bodied_constructors = false:suggestion
|
||||
#prefer block bodies for methods
|
||||
csharp_style_expression_bodied_methods = false:suggestion
|
||||
#prefer expression-bodied members for properties
|
||||
csharp_style_expression_bodied_properties = true:suggestion
|
||||
|
||||
#Style - expression level options
|
||||
|
||||
#prefer out variables to be declared inline in the argument list of a method call when possible
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
#Style - Expression-level preferences
|
||||
|
||||
#prefer objects to be initialized using object initializers when possible
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
#prefer inferred anonymous type member names
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
|
||||
#prefer inferred tuple element names
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
|
||||
#Style - implicit and explicit types
|
||||
|
||||
#prefer var over explicit type in all cases, unless overridden by another code style rule
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
#prefer var is used to declare variables with built-in system types such as int
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
#Style - language keyword and framework type options
|
||||
|
||||
#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
|
||||
#Style - Miscellaneous preferences
|
||||
|
||||
#prefer local functions over anonymous functions
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
|
||||
#Style - modifier options
|
||||
|
||||
#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods.
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
|
||||
#Style - Modifier preferences
|
||||
|
||||
#when this rule is set to a list of modifiers, prefer the specified ordering.
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
|
||||
#Style - Pattern matching
|
||||
|
||||
#prefer pattern matching instead of is expression with type casts
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
|
||||
#Style - qualification options
|
||||
|
||||
#prefer fields not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
#prefer methods not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
#prefer properties not to be prefaced with this. or Me. in Visual Basic
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
|
||||
[*.{cs,vb}]
|
||||
tab_width=4
|
||||
indent_size=4
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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
|
||||
@@ -0,0 +1 @@
|
||||
github: QuestPDF
|
||||
@@ -1,33 +0,0 @@
|
||||
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)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*", TextStyle.Default.FontType("LibreBarcode39").Size(64));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -6,27 +8,27 @@ using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
[TestFixture]
|
||||
public class BarcodeExamples
|
||||
{
|
||||
[Test]
|
||||
public void Barcode()
|
||||
public void Example()
|
||||
{
|
||||
FontManager.RegisterFont(File.OpenRead("LibreBarcode39-Regular.ttf"));
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.PageSize(400, 200)
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.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));
|
||||
});
|
||||
.Background(Colors.White)
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("*QuestPDF*")
|
||||
.FontFamily("Libre Barcode 39")
|
||||
.FontSize(64);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using Microcharts;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ChartExample
|
||||
{
|
||||
[Test]
|
||||
public void MicrochartChart()
|
||||
{
|
||||
var entries = new[]
|
||||
{
|
||||
new ChartEntry(212)
|
||||
{
|
||||
Label = "UWP",
|
||||
ValueLabel = "112",
|
||||
Color = SKColor.Parse("#2c3e50")
|
||||
},
|
||||
new ChartEntry(248)
|
||||
{
|
||||
Label = "Android",
|
||||
ValueLabel = "648",
|
||||
Color = SKColor.Parse("#77d065")
|
||||
},
|
||||
new ChartEntry(128)
|
||||
{
|
||||
Label = "iOS",
|
||||
ValueLabel = "428",
|
||||
Color = SKColor.Parse("#b455b6")
|
||||
},
|
||||
new ChartEntry(514)
|
||||
{
|
||||
Label = "Forms",
|
||||
ValueLabel = "214",
|
||||
Color = SKColor.Parse("#3498db")
|
||||
}
|
||||
};
|
||||
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 600)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.Padding(25)
|
||||
.Column(column =>
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.PaddingBottom(10)
|
||||
.Text("Chart example")
|
||||
.FontSize(20)
|
||||
.SemiBold()
|
||||
.FontColor(Colors.Blue.Medium);
|
||||
|
||||
column
|
||||
.Item()
|
||||
.Border(1)
|
||||
.ExtendHorizontal()
|
||||
.Height(300)
|
||||
.Canvas((canvas, size) =>
|
||||
{
|
||||
var chart = new BarChart
|
||||
{
|
||||
Entries = entries,
|
||||
|
||||
LabelOrientation = Orientation.Horizontal,
|
||||
ValueLabelOrientation = Orientation.Horizontal,
|
||||
|
||||
IsAnimated = false,
|
||||
};
|
||||
|
||||
chart.DrawContent(canvas, (int)size.Width, (int)size.Height);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ColumnExamples
|
||||
{
|
||||
[Test]
|
||||
public void Column()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ShowResults()
|
||||
.ProducePdf()
|
||||
.Render(container =>
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
foreach (var i in Enumerable.Range(0, 10))
|
||||
column.Item().Element(Block);
|
||||
|
||||
static void Block(IContainer container)
|
||||
{
|
||||
container
|
||||
.Width(72)
|
||||
.Height(3.5f, Unit.Inch)
|
||||
.Height(1.5f, Unit.Inch)
|
||||
.Background(Placeholders.BackgroundColor());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stability_NoItems()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.MaxPages(100)
|
||||
.PageSize(250, 150)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Column(column => { });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ComplexLayoutBenchmark
|
||||
{
|
||||
[Test]
|
||||
public void ComplexLayout()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(x => GenerateStructure(x, 12));
|
||||
}
|
||||
|
||||
private void GenerateStructure(IContainer container, int level)
|
||||
{
|
||||
if (level <= 0)
|
||||
{
|
||||
container.Background(Placeholders.BackgroundColor()).Height(10);
|
||||
return;
|
||||
}
|
||||
|
||||
level--;
|
||||
|
||||
if (level % 3 == 0)
|
||||
{
|
||||
container
|
||||
.Border(level / 4f)
|
||||
.BorderColor(Colors.Black)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeItem().Element(x => GenerateStructure(x, level));
|
||||
row.RelativeItem().Element(x => GenerateStructure(x, level));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
container
|
||||
.Border(level / 4f)
|
||||
.BorderColor(Colors.Black)
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Element(x => GenerateStructure(x, level));
|
||||
column.Item().Element(x => GenerateStructure(x, level));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
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).Stack(stack =>
|
||||
page.Content().PaddingVertical(10).Border(1).Padding(10).Column(column =>
|
||||
{
|
||||
foreach (var index in Enumerable.Range(1, 100))
|
||||
stack.Item().Text($"Line {index}", TextStyle.Default.Color(Placeholders.Color()));
|
||||
column.Item().Text($"Line {index}").FontColor(Placeholders.Color());
|
||||
});
|
||||
|
||||
page.Footer().Text("Footer");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -8,26 +9,68 @@ namespace QuestPDF.Examples
|
||||
public class DebuggingTesting
|
||||
{
|
||||
[Test]
|
||||
public void Stack()
|
||||
public void Column()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 360)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.Padding(15)
|
||||
.Grid(grid =>
|
||||
{
|
||||
grid.Spacing(15);
|
||||
|
||||
grid.Item().Background(Colors.Grey.Medium).Text(Placeholders.LoremIpsum());
|
||||
grid.Item().DebugPointer("Test").Background(Colors.Grey.Lighten1).Height(1000); // <-- problem
|
||||
grid.Item().Background(Colors.Grey.Lighten2).Height(150);
|
||||
});
|
||||
});
|
||||
Assert.Throws<DocumentLayoutException>(() =>
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 360)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.Width(100)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.DebugPointer("Example debug pointer")
|
||||
.Column(x =>
|
||||
{
|
||||
x.Item().Text("Test");
|
||||
x.Item().Width(150);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Simple()
|
||||
{
|
||||
Assert.Throws<DocumentLayoutException>(() =>
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 360)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.Width(100)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.Width(150)
|
||||
.Text("Test");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DebugPointer()
|
||||
{
|
||||
Assert.Throws<DocumentLayoutException>(() =>
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 360)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.Padding(10)
|
||||
.Width(100)
|
||||
.DebugPointer("Example debug pointer")
|
||||
.Width(150)
|
||||
.Text("Example");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class DefaultTextStyleExample
|
||||
{
|
||||
[Test]
|
||||
public void DefaultTextStyle()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
// all text in this set of pages has size 20
|
||||
page.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A4);
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
column.Item().Text(Placeholders.Sentence());
|
||||
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
// text in this block is additionally semibold
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold());
|
||||
|
||||
text.Line(Placeholders.Sentence());
|
||||
|
||||
// this text has size 20 but also semibold and red
|
||||
text.Span(Placeholders.Sentence()).FontColor(Colors.Red.Medium);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public struct FibonacciHeaderState
|
||||
{
|
||||
public int Previous { get; set; }
|
||||
public int Current { get; set; }
|
||||
}
|
||||
|
||||
public class FibonacciHeader : IDynamicComponent<FibonacciHeaderState>
|
||||
{
|
||||
public FibonacciHeaderState State { get; set; }
|
||||
|
||||
public static readonly string[] ColorsTable =
|
||||
{
|
||||
Colors.Red.Lighten2,
|
||||
Colors.Orange.Lighten2,
|
||||
Colors.Green.Lighten2,
|
||||
};
|
||||
|
||||
public FibonacciHeader(int previous, int current)
|
||||
{
|
||||
State = new FibonacciHeaderState
|
||||
{
|
||||
Previous = previous,
|
||||
Current = current
|
||||
};
|
||||
}
|
||||
|
||||
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||
{
|
||||
var content = context.CreateElement(container =>
|
||||
{
|
||||
var colorIndex = State.Current % ColorsTable.Length;
|
||||
var color = ColorsTable[colorIndex];
|
||||
|
||||
var ratio = (float)State.Current / State.Previous;
|
||||
|
||||
container
|
||||
.Background(color)
|
||||
.Height(50)
|
||||
.AlignMiddle()
|
||||
.AlignCenter()
|
||||
.Text($"{State.Current} / {State.Previous} = {ratio:N5}");
|
||||
});
|
||||
|
||||
State = new FibonacciHeaderState
|
||||
{
|
||||
Previous = State.Current,
|
||||
Current = State.Previous + State.Current
|
||||
};
|
||||
|
||||
return new DynamicComponentComposeResult
|
||||
{
|
||||
Content = content,
|
||||
HasMoreContent = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class DynamicFibonacci
|
||||
{
|
||||
[Test]
|
||||
public static void Dynamic()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ShowResults()
|
||||
.MaxPages(100)
|
||||
.ProduceImages()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A6);
|
||||
page.PageColor(Colors.White);
|
||||
page.Margin(1, Unit.Centimetre);
|
||||
page.DefaultTextStyle(x => x.FontSize(18));
|
||||
|
||||
page.Header().Dynamic(new FibonacciHeader(17, 19));
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
foreach (var i in Enumerable.Range(0, 50))
|
||||
column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50);
|
||||
});
|
||||
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" / ");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
-43
@@ -15,25 +15,29 @@ namespace QuestPDF.Examples
|
||||
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 OrdersTable : IDynamicComponent
|
||||
public class OptimizedOrdersTable : IDynamicComponent<OrdersTableState>
|
||||
{
|
||||
private ICollection<OrderItem> Items { get; }
|
||||
private ICollection<OrderItem> ItemsLeft { get; set; }
|
||||
|
||||
public OrdersTable(ICollection<OrderItem> items)
|
||||
public OrdersTableState State { get; set; }
|
||||
|
||||
public OptimizedOrdersTable(ICollection<OrderItem> items)
|
||||
{
|
||||
Items = items;
|
||||
|
||||
State = new OrdersTableState
|
||||
{
|
||||
ShownItemsCount = 0
|
||||
};
|
||||
}
|
||||
|
||||
public void Compose(DynamicContext context, IDynamicContainer container)
|
||||
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||
{
|
||||
if (context.Operation == DynamicLayoutOperation.Reset)
|
||||
{
|
||||
ItemsLeft = new List<OrderItem>(Items);
|
||||
return;
|
||||
}
|
||||
|
||||
var header = ComposeHeader(context);
|
||||
var sampleFooter = ComposeFooter(context, Enumerable.Empty<OrderItem>());
|
||||
var decorationHeight = header.Size.Height + sampleFooter.Size.Height;
|
||||
@@ -41,24 +45,32 @@ namespace QuestPDF.Examples
|
||||
var rows = GetItemsForPage(context, decorationHeight).ToList();
|
||||
var footer = ComposeFooter(context, rows.Select(x => x.Item));
|
||||
|
||||
if (ItemsLeft.Count > rows.Count)
|
||||
container.HasMoreContent();
|
||||
|
||||
if (context.Operation == DynamicLayoutOperation.Draw)
|
||||
ItemsLeft = ItemsLeft.Skip(rows.Count).ToList();
|
||||
|
||||
container.Box().Decoration(decoration =>
|
||||
var content = context.CreateElement(container =>
|
||||
{
|
||||
decoration.Header().Element(header);
|
||||
|
||||
decoration.Content().Box().Stack(stack =>
|
||||
container.MinimalBox().Decoration(decoration =>
|
||||
{
|
||||
foreach (var row in rows)
|
||||
stack.Item().Element(row.Element);
|
||||
});
|
||||
decoration.Header().Element(header);
|
||||
|
||||
decoration.Footer().Element(footer);
|
||||
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)
|
||||
@@ -66,6 +78,7 @@ namespace QuestPDF.Examples
|
||||
return context.CreateElement(element =>
|
||||
{
|
||||
element
|
||||
.Width(context.AvailableSize.Width)
|
||||
.BorderBottom(1)
|
||||
.BorderColor(Colors.Grey.Darken2)
|
||||
.Padding(5)
|
||||
@@ -73,11 +86,11 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
var textStyle = TextStyle.Default.SemiBold();
|
||||
|
||||
row.ConstantColumn(30).Text("#", textStyle);
|
||||
row.RelativeColumn().Text("Item name", textStyle);
|
||||
row.ConstantColumn(50).AlignRight().Text("Count", textStyle);
|
||||
row.ConstantColumn(50).AlignRight().Text("Price", textStyle);
|
||||
row.ConstantColumn(50).AlignRight().Text("Total", textStyle);
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -89,6 +102,7 @@ namespace QuestPDF.Examples
|
||||
return context.CreateElement(element =>
|
||||
{
|
||||
element
|
||||
.Width(context.AvailableSize.Width)
|
||||
.Padding(5)
|
||||
.AlignRight()
|
||||
.Text($"Subtotal: {total}$", TextStyle.Default.Size(14).SemiBold());
|
||||
@@ -98,23 +112,25 @@ namespace QuestPDF.Examples
|
||||
private IEnumerable<(OrderItem Item, IDynamicElement Element)> GetItemsForPage(DynamicContext context, float decorationHeight)
|
||||
{
|
||||
var totalHeight = decorationHeight;
|
||||
var counter = Items.Count - ItemsLeft.Count + 1;
|
||||
|
||||
foreach (var orderItem in ItemsLeft)
|
||||
|
||||
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.ConstantColumn(30).Text(counter++);
|
||||
row.RelativeColumn().Text(orderItem.ItemName);
|
||||
row.ConstantColumn(50).AlignRight().Text(orderItem.Count);
|
||||
row.ConstantColumn(50).AlignRight().Text($"{orderItem.Price}$");
|
||||
row.ConstantColumn(50).AlignRight().Text($"{orderItem.Count*orderItem.Price}$");
|
||||
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}$");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -124,12 +140,12 @@ namespace QuestPDF.Examples
|
||||
break;
|
||||
|
||||
totalHeight += elementHeight;
|
||||
yield return (orderItem, element);
|
||||
yield return (item, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DynamicExamples
|
||||
public static class DynamicOptimizedExamples
|
||||
{
|
||||
[Test]
|
||||
public static void Dynamic()
|
||||
@@ -137,7 +153,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
@@ -153,7 +168,7 @@ namespace QuestPDF.Examples
|
||||
.PaddingBottom(5)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Darken2).Size(16));
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold().FontColor(Colors.Blue.Darken2).FontSize(16));
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" of ");
|
||||
@@ -162,7 +177,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Dynamic(new OrdersTable(items));
|
||||
.Dynamic(new OptimizedOrdersTable(items));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class FooterWithAlternatingAlignment : IDynamicComponent<int>
|
||||
{
|
||||
public int State { get; set; }
|
||||
|
||||
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||
{
|
||||
var content = context.CreateElement(element =>
|
||||
{
|
||||
element
|
||||
.Element(x => context.PageNumber % 2 == 0 ? x.AlignLeft() : x.AlignRight())
|
||||
.Text(x =>
|
||||
{
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
});
|
||||
|
||||
return new DynamicComponentComposeResult()
|
||||
{
|
||||
Content = content,
|
||||
HasMoreContent = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class DynamicPageNumberLeftRightExamples
|
||||
{
|
||||
[Test]
|
||||
public static void Dynamic()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.MaxPages(100)
|
||||
.ShowResults()
|
||||
.ProduceImages()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A6);
|
||||
page.PageColor(Colors.White);
|
||||
page.Margin(1, Unit.Centimetre);
|
||||
page.DefaultTextStyle(x => x.FontSize(18));
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
foreach (var i in Enumerable.Range(0, 50))
|
||||
column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50);
|
||||
});
|
||||
|
||||
page.Footer().Dynamic(new FooterWithAlternatingAlignment());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ProgressHeader : IDynamicComponent<int>
|
||||
{
|
||||
public int State { get; set; }
|
||||
|
||||
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||
{
|
||||
var content = context.CreateElement(container =>
|
||||
{
|
||||
var width = context.AvailableSize.Width * context.PageNumber / context.TotalPages;
|
||||
|
||||
container
|
||||
.Background(Colors.Blue.Lighten2)
|
||||
.Height(25)
|
||||
.Width(width)
|
||||
.Background(Colors.Blue.Darken1);
|
||||
});
|
||||
|
||||
return new DynamicComponentComposeResult
|
||||
{
|
||||
Content = content,
|
||||
HasMoreContent = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class DynamicProgressHeader
|
||||
{
|
||||
[Test]
|
||||
public static void Dynamic()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ShowResults()
|
||||
.MaxPages(100)
|
||||
.ProduceImages()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A6);
|
||||
page.Margin(1, Unit.Centimetre);
|
||||
page.PageColor(Colors.White);
|
||||
page.DefaultTextStyle(x => x.FontSize(20));
|
||||
|
||||
page.Header().Dynamic(new ProgressHeader());
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
foreach (var i in Enumerable.Range(0, 100))
|
||||
column.Item().PaddingTop(25).Background(Colors.Grey.Lighten2).Height(50);
|
||||
});
|
||||
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.FontSize(20));
|
||||
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" / ");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class OrdersTable : IDynamicComponent<OrdersTableState>
|
||||
{
|
||||
private IList<OrderItem> Items { get; }
|
||||
public OrdersTableState State { get; set; }
|
||||
|
||||
public OrdersTable(IList<OrderItem> items)
|
||||
{
|
||||
Items = items;
|
||||
|
||||
State = new OrdersTableState
|
||||
{
|
||||
ShownItemsCount = 0
|
||||
};
|
||||
}
|
||||
|
||||
public DynamicComponentComposeResult Compose(DynamicContext context)
|
||||
{
|
||||
var possibleItems = Enumerable
|
||||
.Range(1, Items.Count - State.ShownItemsCount)
|
||||
.Select(itemsToDisplay => ComposeContent(context, itemsToDisplay))
|
||||
.TakeWhile(x => x.Size.Height <= context.AvailableSize.Height)
|
||||
.ToList();
|
||||
|
||||
State = new OrdersTableState
|
||||
{
|
||||
ShownItemsCount = State.ShownItemsCount + possibleItems.Count
|
||||
};
|
||||
|
||||
return new DynamicComponentComposeResult
|
||||
{
|
||||
Content = possibleItems.Last(),
|
||||
HasMoreContent = State.ShownItemsCount < Items.Count
|
||||
};
|
||||
}
|
||||
|
||||
private IDynamicElement ComposeContent(DynamicContext context, int itemsToDisplay)
|
||||
{
|
||||
var total = Items.Skip(State.ShownItemsCount).Take(itemsToDisplay).Sum(x => x.Count * x.Price);
|
||||
|
||||
return context.CreateElement(container =>
|
||||
{
|
||||
container
|
||||
.MinimalBox()
|
||||
.Width(context.AvailableSize.Width)
|
||||
.Table(table =>
|
||||
{
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
columns.ConstantColumn(30);
|
||||
columns.RelativeColumn();
|
||||
columns.ConstantColumn(50);
|
||||
columns.ConstantColumn(50);
|
||||
columns.ConstantColumn(50);
|
||||
});
|
||||
|
||||
table.Header(header =>
|
||||
{
|
||||
header.Cell().Element(Style).Text("#");
|
||||
header.Cell().Element(Style).Text("Item name");
|
||||
header.Cell().Element(Style).AlignRight().Text("Count");
|
||||
header.Cell().Element(Style).AlignRight().Text("Price");
|
||||
header.Cell().Element(Style).AlignRight().Text("Total");
|
||||
|
||||
IContainer Style(IContainer container)
|
||||
{
|
||||
return container
|
||||
.DefaultTextStyle(x => x.SemiBold())
|
||||
.BorderBottom(1)
|
||||
.BorderColor(Colors.Grey.Darken2)
|
||||
.Padding(5);
|
||||
}
|
||||
});
|
||||
|
||||
table.Footer(footer =>
|
||||
{
|
||||
footer
|
||||
.Cell().ColumnSpan(5)
|
||||
.AlignRight()
|
||||
.PaddingTop(10)
|
||||
.Text($"Subtotal: {total}$", TextStyle.Default.Bold());
|
||||
});
|
||||
|
||||
foreach (var index in Enumerable.Range(State.ShownItemsCount, itemsToDisplay))
|
||||
{
|
||||
var item = Items[index];
|
||||
|
||||
table.Cell().Element(Style).Text(index + 1);
|
||||
table.Cell().Element(Style).Text(item.ItemName);
|
||||
table.Cell().Element(Style).AlignRight().Text(item.Count);
|
||||
table.Cell().Element(Style).AlignRight().Text($"{item.Price}$");
|
||||
table.Cell().Element(Style).AlignRight().Text($"{item.Count*item.Price}$");
|
||||
|
||||
IContainer Style(IContainer container)
|
||||
{
|
||||
return container
|
||||
.BorderBottom(1)
|
||||
.BorderColor(Colors.Grey.Lighten2)
|
||||
.Padding(5);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static class DynamicSimpleTableExample
|
||||
{
|
||||
[Test]
|
||||
public static void Dynamic()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ShowResults()
|
||||
.ProduceImages()
|
||||
.Render(container =>
|
||||
{
|
||||
var items = Enumerable.Range(0, 15).Select(x => new OrderItem()).ToList();
|
||||
|
||||
container
|
||||
.Background(Colors.White)
|
||||
.Padding(25)
|
||||
.DefaultTextStyle(x => x.FontSize(16))
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Header()
|
||||
.PaddingBottom(5)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold().FontColor(Colors.Blue.Darken2));
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" / ");
|
||||
text.TotalPages();
|
||||
});
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Dynamic(new OrdersTable(items));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 150)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -33,7 +32,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -42,10 +40,12 @@ namespace QuestPDF.Examples
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Header()
|
||||
.Before()
|
||||
.Background(Colors.Grey.Medium)
|
||||
.Padding(10)
|
||||
.Text("Notes", TextStyle.Default.Size(16).Color("#FFF"));
|
||||
.Text("Notes")
|
||||
.FontSize(16)
|
||||
.FontColor("#FFF");
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
@@ -63,33 +63,32 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(740, 200)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(20)
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
stack.Item()
|
||||
column.Item()
|
||||
.PaddingBottom(10)
|
||||
.AlignCenter()
|
||||
.Text("This Row element is 700pt wide");
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.ConstantColumn(100)
|
||||
row.ConstantItem(100)
|
||||
.Background(Colors.Grey.Lighten1)
|
||||
.Padding(10)
|
||||
.ExtendVertical()
|
||||
.Text("This column is 100 pt wide");
|
||||
|
||||
row.RelativeColumn()
|
||||
row.RelativeItem()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(10)
|
||||
.Text("This column takes 1/3 of the available space (200pt)");
|
||||
|
||||
row.RelativeColumn(2)
|
||||
row.RelativeItem(2)
|
||||
.Background(Colors.Grey.Lighten3)
|
||||
.Padding(10)
|
||||
.Text("This column takes 2/3 of the available space (400pt)");
|
||||
@@ -104,7 +103,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(740, 200)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -113,32 +111,31 @@ namespace QuestPDF.Examples
|
||||
.Row(row =>
|
||||
{
|
||||
row.Spacing(20);
|
||||
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);
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Stack()
|
||||
public void column()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 360)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(15)
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
stack.Spacing(15);
|
||||
column.Spacing(15);
|
||||
|
||||
stack.Item().Background(Colors.Grey.Medium).Height(50);
|
||||
stack.Item().Background(Colors.Grey.Lighten1).Height(100);
|
||||
stack.Item().Background(Colors.Grey.Lighten2).Height(150);
|
||||
column.Item().Background(Colors.Grey.Medium).Height(50);
|
||||
column.Item().Background(Colors.Grey.Lighten1).Height(100);
|
||||
column.Item().Background(Colors.Grey.Lighten2).Height(150);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -149,7 +146,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(210, 210)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -172,7 +168,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 200)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
var text = "";
|
||||
@@ -195,7 +190,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 230)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
var textStyle = TextStyle.Default.Size(14);
|
||||
@@ -230,7 +224,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 200)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -260,7 +253,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 250)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -277,12 +269,12 @@ namespace QuestPDF.Examples
|
||||
layers
|
||||
.PrimaryLayer()
|
||||
.Padding(25)
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
stack.Spacing(5);
|
||||
column.Spacing(5);
|
||||
|
||||
foreach (var _ in Enumerable.Range(0, 7))
|
||||
stack.Item().Text(Placeholders.Sentence());
|
||||
column.Item().Text(Placeholders.Sentence());
|
||||
});
|
||||
|
||||
// layer above the main content
|
||||
@@ -290,12 +282,15 @@ namespace QuestPDF.Examples
|
||||
.Layer()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("Watermark", TextStyle.Default.Size(48).Bold().Color(Colors.Green.Lighten3));
|
||||
.Text("Watermark")
|
||||
.FontSize(48)
|
||||
.Bold()
|
||||
.FontColor(Colors.Green.Lighten3);
|
||||
|
||||
layers
|
||||
.Layer()
|
||||
.AlignBottom()
|
||||
.Text(text => text.CurrentPageNumber(TextStyle.Default.Size(16).Color(Colors.Green.Medium)));
|
||||
.Text(text => text.CurrentPageNumber().FontSize(16).FontColor(Colors.Green.Medium));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -314,16 +309,16 @@ namespace QuestPDF.Examples
|
||||
// {
|
||||
// page.Header().PageNumber("Page {pdf:currentPage}");
|
||||
//
|
||||
// page.Content().Height(300).Stack(content =>
|
||||
// page.Content().Height(300).column(content =>
|
||||
// {
|
||||
// content.Item().Height(200).Background(Colors.Grey.Lighten2);
|
||||
//
|
||||
// content.Item().EnsureSpace(100).Stack(stack =>
|
||||
// content.Item().EnsureSpace(100).column(column =>
|
||||
// {
|
||||
// stack.Spacing(10);
|
||||
// column.Spacing(10);
|
||||
//
|
||||
// foreach (var _ in Enumerable.Range(0, 4))
|
||||
// stack.Item().Height(50).Background(Colors.Green.Lighten1);
|
||||
// column.Item().Height(50).Background(Colors.Green.Lighten1);
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
@@ -336,7 +331,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -381,7 +375,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(450, 150)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -390,7 +383,7 @@ namespace QuestPDF.Examples
|
||||
.Row(row =>
|
||||
{
|
||||
foreach (var color in colors)
|
||||
row.RelativeColumn().Background(color);
|
||||
row.RelativeItem().Background(color);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -414,7 +407,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 175)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -429,7 +421,9 @@ namespace QuestPDF.Examples
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Padding(10)
|
||||
.Text(font, TextStyle.Default.FontType(font).Size(16));
|
||||
.Text(font)
|
||||
.FontFamily(font)
|
||||
.FontSize(16);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -441,7 +435,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -451,10 +444,10 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
layers.Layer().Text("Something else");
|
||||
|
||||
layers.PrimaryLayer().Stack(stack =>
|
||||
layers.PrimaryLayer().Column(column =>
|
||||
{
|
||||
stack.Item().PaddingTop(20).Text("Text 1");
|
||||
stack.Item().PaddingTop(40).Text("Text 2");
|
||||
column.Item().PaddingTop(20).Text("Text 1");
|
||||
column.Item().PaddingTop(40).Text("Text 2");
|
||||
});
|
||||
|
||||
layers.Layer().Canvas((canvas, size) =>
|
||||
@@ -470,7 +463,7 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
|
||||
layers.Layer().Background("#8F00").Extend();
|
||||
layers.Layer().PaddingTop(40).Text("It works!", TextStyle.Default.Size(24));
|
||||
layers.Layer().PaddingTop(40).Text("It works!").FontSize(24);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -481,7 +474,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 150)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -489,10 +481,10 @@ namespace QuestPDF.Examples
|
||||
.Padding(15)
|
||||
.Border(4)
|
||||
.BorderColor(Colors.Blue.Medium)
|
||||
//.Box()
|
||||
//.MinimalBox()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(15)
|
||||
.Text("Test of the \n box element", TextStyle.Default.Size(20));
|
||||
.Text("Test of the \n box element").FontSize(20);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -502,7 +494,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 175)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -517,13 +508,14 @@ namespace QuestPDF.Examples
|
||||
.SemiBold();
|
||||
|
||||
decoration
|
||||
.Header()
|
||||
.Before()
|
||||
.PaddingBottom(10)
|
||||
.Text("Example: scale component", headerFontStyle);
|
||||
.Text("Example: scale component")
|
||||
.Style(headerFontStyle);
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
var scales = new[] { 0.8f, 0.9f, 1.1f, 1.2f };
|
||||
|
||||
@@ -535,13 +527,14 @@ namespace QuestPDF.Examples
|
||||
|
||||
var fontStyle = TextStyle.Default.Size(16);
|
||||
|
||||
stack
|
||||
column
|
||||
.Item()
|
||||
.Border(1)
|
||||
.Background(fontColor)
|
||||
.Scale(scale)
|
||||
.Padding(5)
|
||||
.Text($"Content with {scale} scale.", fontStyle);
|
||||
.Text($"Content with {scale} scale.")
|
||||
.Style(fontStyle);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -554,12 +547,11 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 200)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
|
||||
.Padding(25)
|
||||
|
||||
@@ -572,7 +564,8 @@ namespace QuestPDF.Examples
|
||||
.BorderColor(Colors.Green.Darken1)
|
||||
|
||||
.Padding(50)
|
||||
.Text("Moved text", TextStyle.Default.Size(25));
|
||||
.Text("Moved text")
|
||||
.FontSize(25);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -582,7 +575,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(650, 450)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -606,10 +598,11 @@ namespace QuestPDF.Examples
|
||||
|
||||
return element;
|
||||
})
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Background(Colors.White)
|
||||
.Padding(10)
|
||||
.Text($"Rotated {turns * 90}°", TextStyle.Default.Size(16));
|
||||
.Text($"Rotated {turns * 90}°")
|
||||
.FontSize(16);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -621,7 +614,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -648,7 +640,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -680,7 +671,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(350, 350)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -707,10 +697,11 @@ namespace QuestPDF.Examples
|
||||
|
||||
return element;
|
||||
})
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Background(Colors.White)
|
||||
.Padding(10)
|
||||
.Text($"Flipped {turns}", TextStyle.Default.Size(16));
|
||||
.Text($"Flipped {turns}")
|
||||
.FontSize(16);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -722,7 +713,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 200)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -730,14 +720,14 @@ namespace QuestPDF.Examples
|
||||
.Border(2)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25)
|
||||
row.ConstantItem(25)
|
||||
.Border(1)
|
||||
.RotateLeft()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text("Sample text");
|
||||
|
||||
row.RelativeColumn().Border(1).Padding(5).Text(Placeholders.Paragraph());
|
||||
row.RelativeItem().Border(1).Padding(5).Text(Placeholders.Paragraph());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -748,17 +738,16 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 350)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.PaddingLeft(75)
|
||||
.Stack(stack =>
|
||||
.PaddingLeft(50)
|
||||
.Column(column =>
|
||||
{
|
||||
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
|
||||
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten4);
|
||||
|
||||
stack
|
||||
column
|
||||
.Item()
|
||||
|
||||
// creates an infinite space for its child
|
||||
@@ -774,7 +763,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
.Background(Colors.Blue.Darken1);
|
||||
|
||||
stack.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
|
||||
column.Item().Width(300).Height(150).Background(Colors.Blue.Lighten3);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -785,18 +774,17 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 225)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().LabelCell("Label 1");
|
||||
row.RelativeItem().LabelCell("Label 1");
|
||||
|
||||
row.RelativeColumn(3).Grid(grid =>
|
||||
row.RelativeItem(3).Grid(grid =>
|
||||
{
|
||||
grid.Columns(3);
|
||||
|
||||
@@ -808,11 +796,11 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().ValueCell().Text("Value 1");
|
||||
row.RelativeItem().ValueCell().Text("Value 1");
|
||||
|
||||
row.RelativeColumn(3).Grid(grid =>
|
||||
row.RelativeItem(3).Grid(grid =>
|
||||
{
|
||||
grid.Columns(3);
|
||||
|
||||
@@ -824,10 +812,10 @@ namespace QuestPDF.Examples
|
||||
});
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn().LabelCell("Label 6");
|
||||
row.RelativeColumn().ValueCell().Text("Value 6");
|
||||
row.RelativeItem().LabelCell("Label 6");
|
||||
row.RelativeItem().ValueCell().Text("Value 6");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -839,7 +827,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(600, 310)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples.Engine
|
||||
@@ -17,20 +18,25 @@ namespace QuestPDF.Examples.Engine
|
||||
{
|
||||
private string FileNamePrefix = "test";
|
||||
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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static RenderingTest Create()
|
||||
public static RenderingTest Create([CallerMemberName] string fileName = "test")
|
||||
{
|
||||
return new RenderingTest();
|
||||
return new RenderingTest().FileName(fileName);
|
||||
}
|
||||
|
||||
public RenderingTest FileName([CallerMemberName] string fileName = "test")
|
||||
public RenderingTest FileName(string fileName)
|
||||
{
|
||||
FileNamePrefix = fileName;
|
||||
return this;
|
||||
@@ -65,20 +71,52 @@ 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)
|
||||
{
|
||||
var container = new Container();
|
||||
content(container);
|
||||
RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(new PageSize(Size.Width, Size.Height));
|
||||
page.Content().Container().Background(Colors.White).Element(content);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var maxPages = ResultType == RenderingTestResult.Pdf ? 1000 : 10;
|
||||
var document = new SimpleDocument(container, Size, maxPages);
|
||||
public void RenderDocument(Action<IDocumentContainer> content)
|
||||
{
|
||||
MaxPagesThreshold ??= ResultType == RenderingTestResult.Pdf ? 1000 : 10;
|
||||
var document = new SimpleDocument(content, MaxPagesThreshold.Value, ApplyCaching, ApplyDebugging);
|
||||
|
||||
Render(document);
|
||||
}
|
||||
|
||||
private void Render(IDocument document)
|
||||
{
|
||||
if (ResultType == RenderingTestResult.Images)
|
||||
{
|
||||
Func<int, string> fileNameSchema = i => $"{FileNamePrefix}-${i}.png";
|
||||
document.GenerateImages(fileNameSchema);
|
||||
|
||||
if (ShowResult)
|
||||
if (ShowResult && ShowingResultsEnabled)
|
||||
Process.Start("explorer", fileNameSchema(0));
|
||||
}
|
||||
|
||||
@@ -87,7 +125,7 @@ namespace QuestPDF.Examples.Engine
|
||||
var fileName = $"{FileNamePrefix}.pdf";
|
||||
document.GeneratePdf(fileName);
|
||||
|
||||
if (ShowResult)
|
||||
if (ShowResult && ShowingResultsEnabled)
|
||||
Process.Start("explorer", fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using QuestPDF.Drawing;
|
||||
using System;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
@@ -10,15 +11,17 @@ namespace QuestPDF.Examples.Engine
|
||||
{
|
||||
public const int ImageScalingFactor = 2;
|
||||
|
||||
private IContainer Container { get; }
|
||||
private Size Size { get; }
|
||||
private Action<IDocumentContainer> Content { get; }
|
||||
private int MaxPages { get; }
|
||||
private bool ApplyCaching { get; }
|
||||
private bool ApplyDebugging { get; }
|
||||
|
||||
public SimpleDocument(IContainer container, Size size, int maxPages)
|
||||
public SimpleDocument(Action<IDocumentContainer> content, int maxPages, bool applyCaching, bool applyDebugging)
|
||||
{
|
||||
Container = container;
|
||||
Size = size;
|
||||
Content = content;
|
||||
MaxPages = maxPages;
|
||||
ApplyCaching = applyCaching;
|
||||
ApplyDebugging = applyDebugging;
|
||||
}
|
||||
|
||||
public DocumentMetadata GetMetadata()
|
||||
@@ -26,17 +29,15 @@ namespace QuestPDF.Examples.Engine
|
||||
return new DocumentMetadata()
|
||||
{
|
||||
RasterDpi = PageSizes.PointsPerInch * ImageScalingFactor,
|
||||
DocumentLayoutExceptionThreshold = MaxPages
|
||||
DocumentLayoutExceptionThreshold = MaxPages,
|
||||
ApplyCaching = ApplyCaching,
|
||||
ApplyDebugging = ApplyDebugging
|
||||
};
|
||||
}
|
||||
|
||||
public void Compose(IDocumentContainer container)
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(new PageSize(Size.Width, Size.Height));
|
||||
page.Content().Container().Background(Colors.White).Element(Container as Container);
|
||||
});
|
||||
Content(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class EnsureSpaceExample
|
||||
{
|
||||
[Test]
|
||||
public void EnsureSpaceWith()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Header().Text("With ensure space").SemiBold();
|
||||
|
||||
page.Content().Column(column =>
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.ExtendHorizontal()
|
||||
.Height(75)
|
||||
.Background(Colors.Grey.Lighten2);
|
||||
|
||||
column
|
||||
.Item()
|
||||
.EnsureSpace(100)
|
||||
.Text(Placeholders.LoremIpsum());
|
||||
});
|
||||
|
||||
page.Footer().Text(text =>
|
||||
{
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" out of ");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ExecutionOrderExamples
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(400, 300)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.DefaultTextStyle(TextStyle.Default.Size(18))
|
||||
.Padding(25)
|
||||
.Row(row =>
|
||||
{
|
||||
row.Spacing(25);
|
||||
|
||||
row.RelativeItem()
|
||||
.Border(1)
|
||||
.Padding(15)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Text("Lorem ipsum");
|
||||
|
||||
row.RelativeItem()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Padding(15)
|
||||
.Text("dolor sit amet");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Example2()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 200)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Border(2)
|
||||
.Width(150)
|
||||
.Height(150)
|
||||
|
||||
.Background(Colors.Blue.Lighten2)
|
||||
.PaddingTop(50)
|
||||
|
||||
.Background(Colors.Green.Lighten2)
|
||||
.PaddingRight(50)
|
||||
|
||||
.Background(Colors.Red.Lighten2)
|
||||
.PaddingBottom(50)
|
||||
|
||||
.Background(Colors.Amber.Lighten2)
|
||||
.PaddingLeft(50)
|
||||
|
||||
.Background(Colors.Grey.Lighten2);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,14 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
return container
|
||||
.Border(1)
|
||||
.Background(dark ? Colors.Grey.Lighten2 : Colors.White)
|
||||
.Padding(10);
|
||||
.BorderColor(Colors.Grey.Medium)
|
||||
.Background(dark ? Colors.Grey.Lighten3 : Colors.White)
|
||||
.Padding(5);
|
||||
}
|
||||
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text, TextStyle.Default.Medium());
|
||||
public static void LabelCell(this IContainer container, string text) => container.Cell(true).Text(text).SemiBold();
|
||||
public static IContainer ValueCell(this IContainer container) => container.Cell(false);
|
||||
public static void ValueCell(this IContainer container, string text) => container.ValueCell().Text(text);
|
||||
}
|
||||
|
||||
public class FrameExample
|
||||
@@ -28,21 +30,20 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(550, 400)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(25)
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
for(var i=1; i<=4; i++)
|
||||
for(var i = 1; i <= 4; i++)
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2).LabelCell(Placeholders.Label());
|
||||
row.RelativeColumn(3).ValueCell().Text(Placeholders.Paragraph());
|
||||
row.RelativeItem(2).LabelCell(Placeholders.Label());
|
||||
row.RelativeItem(3).ValueCell().Text(Placeholders.Paragraph());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ProcessRunningTime
|
||||
{
|
||||
public TimeSpan FluentTime { get; set; }
|
||||
public TimeSpan GenerationTime { get; set; }
|
||||
public float Size { get; set; }
|
||||
}
|
||||
|
||||
public class GenerationBenchmark
|
||||
{
|
||||
public const int TestSize = 4096;
|
||||
|
||||
[Test]
|
||||
public void BenchmarkAsync()
|
||||
{
|
||||
RunTest(() => Enumerable
|
||||
.Range(0, TestSize)
|
||||
.AsParallel() // difference
|
||||
.Select(GenerateAndCollect)
|
||||
.ToList());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BenchmarkSync()
|
||||
{
|
||||
RunTest(() => Enumerable
|
||||
.Range(0, TestSize)
|
||||
.Select(GenerateAndCollect)
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public void RunTest(Func<IEnumerable<ProcessRunningTime>> handler)
|
||||
{
|
||||
var totalFluentTime = TimeSpan.Zero;
|
||||
var totalGenerationTime = TimeSpan.Zero;
|
||||
|
||||
var stopWatch = new Stopwatch();
|
||||
|
||||
stopWatch.Start();
|
||||
var results = handler();
|
||||
stopWatch.Stop();
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
totalFluentTime += result.FluentTime;
|
||||
totalGenerationTime += result.GenerationTime;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Fluent: {totalFluentTime:g}");
|
||||
Console.WriteLine($"Generation: {totalGenerationTime:g}");
|
||||
Console.WriteLine($"Total: {stopWatch.Elapsed:g}");
|
||||
}
|
||||
|
||||
static ProcessRunningTime GenerateAndCollect(int attemptNumber)
|
||||
{
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
var container = new Container();
|
||||
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text($"Attempts {attemptNumber}");
|
||||
|
||||
const int numberOfRows = 100;
|
||||
const int numberOfColumns = 10;
|
||||
|
||||
for (var y = 0; y < numberOfRows; y++)
|
||||
{
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
for (var x = 0; x < numberOfColumns; x++)
|
||||
{
|
||||
row.RelativeItem()
|
||||
|
||||
.Background(Colors.Red.Lighten5)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Lighten4)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Lighten3)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Lighten2)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Lighten1)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Medium)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Darken1)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Darken2)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Darken3)
|
||||
.Padding(3)
|
||||
|
||||
.Background(Colors.Red.Darken4)
|
||||
.Height(3);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var fluentTime = stopwatch.Elapsed;
|
||||
|
||||
stopwatch.Reset();
|
||||
stopwatch.Start();
|
||||
|
||||
var size = Document
|
||||
.Create(x => x.Page(page => page.Content().Element(container)))
|
||||
.GeneratePdf()
|
||||
.Length;
|
||||
|
||||
var generationTime = stopwatch.Elapsed;
|
||||
|
||||
return new ProcessRunningTime
|
||||
{
|
||||
FluentTime = fluentTime,
|
||||
GenerationTime = generationTime,
|
||||
Size = size
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class GridBenchmark
|
||||
{
|
||||
[Test]
|
||||
public void Benchmark()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ShowResults()
|
||||
.MaxPages(10_000)
|
||||
.EnableCaching(true)
|
||||
.EnableDebugging(false)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Grid(grid =>
|
||||
{
|
||||
const int numberOfRows = 100_000;
|
||||
const int numberOfColumns = 10;
|
||||
|
||||
grid.Columns(numberOfColumns);
|
||||
|
||||
foreach (var row in Enumerable.Range(0, numberOfRows))
|
||||
foreach (var column in Enumerable.Range(0, numberOfColumns))
|
||||
grid.Item().Background(Placeholders.BackgroundColor()).Padding(5).Text($"{row}_{column}");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ImageExamples
|
||||
{
|
||||
[Test]
|
||||
public void LoadingImage()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(page =>
|
||||
{
|
||||
page.Padding(25).Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
|
||||
column.Item().Image("logo.png");
|
||||
|
||||
var binaryData = File.ReadAllBytes("logo.png");
|
||||
column.Item().Image(binaryData);
|
||||
|
||||
using var stream = new FileStream("logo.png", FileMode.Open);
|
||||
column.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()
|
||||
{
|
||||
Assert.Throws<DocumentComposeException>(() =>
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A2)
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class InlinedExamples
|
||||
{
|
||||
[Test]
|
||||
public void Inlined()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(800, 650)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration.Before().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" / ");
|
||||
text.TotalPages();
|
||||
});
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.PaddingTop(25)
|
||||
//.MinimalBox()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Inlined(inlined =>
|
||||
{
|
||||
inlined.Spacing(25);
|
||||
|
||||
inlined.AlignSpaceAround();
|
||||
inlined.BaselineMiddle();
|
||||
|
||||
var random = new Random(123);
|
||||
|
||||
foreach (var _ in Enumerable.Range(0, 50))
|
||||
{
|
||||
var width = random.Next(2, 7);
|
||||
var height = random.Next(2, 7);
|
||||
|
||||
var sizeText = $"{width}×{height}";
|
||||
|
||||
inlined
|
||||
.Item()
|
||||
.Border(1)
|
||||
.Width(width * 25)
|
||||
.Height(height * 25)
|
||||
.Background(Placeholders.BackgroundColor())
|
||||
.Layers(layers =>
|
||||
{
|
||||
layers.Layer().Grid(grid =>
|
||||
{
|
||||
grid.Columns(width);
|
||||
Enumerable.Range(0, width * height).ToList().ForEach(x => grid.Item().Border(1).BorderColor(Colors.White).Width(25).Height(25));
|
||||
});
|
||||
|
||||
layers
|
||||
.PrimaryLayer()
|
||||
.AlignCenter()
|
||||
.AlignMiddle()
|
||||
.Text(sizeText)
|
||||
.FontSize(15);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Inline_AlignLeft_BaselineBottom()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(800, 600)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Background(Colors.Grey.Lighten4)
|
||||
.Inlined(inlined =>
|
||||
{
|
||||
inlined.VerticalSpacing(50);
|
||||
inlined.HorizontalSpacing(25);
|
||||
inlined.AlignRight();
|
||||
inlined.BaselineMiddle();
|
||||
|
||||
foreach (var _ in Enumerable.Range(0, 100))
|
||||
inlined.Item().Element(RandomBlock);
|
||||
});
|
||||
});
|
||||
|
||||
void RandomBlock(IContainer container)
|
||||
{
|
||||
container
|
||||
.Width(Placeholders.Random.Next(1, 5) * 20)
|
||||
.Height(Placeholders.Random.Next(1, 5) * 20)
|
||||
.Border(1)
|
||||
.BorderColor(Colors.Grey.Darken2)
|
||||
.Background(Placeholders.BackgroundColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class LineExamples
|
||||
{
|
||||
[Test]
|
||||
public void LineHorizontal()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(15)
|
||||
.MinimalBox()
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Column(column =>
|
||||
{
|
||||
column.Item().Text("Above text");
|
||||
column.Item().PaddingVertical(5).LineHorizontal(1).LineColor(Colors.Grey.Medium);
|
||||
column.Item().Text("Below text");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LineVertical()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A5)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(15)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(16))
|
||||
.Row(row =>
|
||||
{
|
||||
row.AutoItem().Text("Left text");
|
||||
row.AutoItem().PaddingHorizontal(10).LineVertical(1).LineColor(Colors.Grey.Medium);
|
||||
row.AutoItem().Text("Right text");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,13 +36,14 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(350, 280)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Background("#FFF")
|
||||
.Padding(25)
|
||||
.Stack(column =>
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(10);
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class MinimalApiExamples
|
||||
{
|
||||
[Test]
|
||||
public void MinimalApi()
|
||||
{
|
||||
Document
|
||||
.Create(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(2, Unit.Centimetre);
|
||||
page.PageColor(Colors.White);
|
||||
page.DefaultTextStyle(x => x.FontSize(20));
|
||||
|
||||
page.Header()
|
||||
.Text("Hello PDF!")
|
||||
.SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(1, Unit.Centimetre)
|
||||
.Column(x =>
|
||||
{
|
||||
x.Spacing(20);
|
||||
|
||||
x.Item().Text(Placeholders.LoremIpsum());
|
||||
x.Item().Image(Placeholders.Image(200, 100));
|
||||
});
|
||||
|
||||
page.Footer()
|
||||
.AlignCenter()
|
||||
.Text(x =>
|
||||
{
|
||||
x.Span("Page ");
|
||||
x.CurrentPageNumber();
|
||||
});
|
||||
});
|
||||
})
|
||||
.GeneratePdf("hello.pdf");
|
||||
|
||||
Process.Start("explorer.exe", "hello.pdf");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 300)
|
||||
.FileName()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
@@ -37,7 +36,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 150)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
@@ -52,7 +50,8 @@ namespace QuestPDF.Examples
|
||||
.Background("FFF")
|
||||
.Padding(5)
|
||||
.AlignCenter()
|
||||
.Text("Sample text", TextStyle.Default.FontType("Segoe UI emoji"));
|
||||
.Text("Sample text")
|
||||
.FontFamily("Segoe UI emoji");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,11 +61,10 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 150)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Stack(column =>
|
||||
.Column(column =>
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
@@ -113,18 +111,17 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(200, 150)
|
||||
.FileName()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Stack(column =>
|
||||
.Column(column =>
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.Height(150)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeColumn()
|
||||
row.RelativeItem()
|
||||
.Extend()
|
||||
.Background("FFF")
|
||||
|
||||
@@ -132,7 +129,7 @@ namespace QuestPDF.Examples
|
||||
.Width(50)
|
||||
.Background("444");
|
||||
|
||||
row.RelativeColumn()
|
||||
row.RelativeItem()
|
||||
.Extend()
|
||||
.Background("BBB")
|
||||
|
||||
@@ -146,7 +143,7 @@ namespace QuestPDF.Examples
|
||||
.Height(150)
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeColumn()
|
||||
row.RelativeItem()
|
||||
.Extend()
|
||||
.Background("BBB")
|
||||
|
||||
@@ -154,7 +151,7 @@ namespace QuestPDF.Examples
|
||||
.Width(50)
|
||||
.Background("444");
|
||||
|
||||
row.RelativeColumn()
|
||||
row.RelativeItem()
|
||||
.Extend()
|
||||
.Background("BBB")
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class PageBackgroundForegroundExample
|
||||
{
|
||||
[Test]
|
||||
public void PageBackgroundForeground()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.MaxPages(100)
|
||||
.ShowResults()
|
||||
.RenderDocument(document =>
|
||||
{
|
||||
document.Page(page =>
|
||||
{
|
||||
page.Size(PageSizes.A4);
|
||||
page.Margin(1, Unit.Inch);
|
||||
page.DefaultTextStyle(TextStyle.Default.FontSize(16));
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
const string transparentBlue = "#662196f3";
|
||||
|
||||
page.Background()
|
||||
.AlignTop()
|
||||
.ExtendHorizontal()
|
||||
.Height(200)
|
||||
.Background(transparentBlue);
|
||||
|
||||
page.Foreground()
|
||||
.AlignBottom()
|
||||
.ExtendHorizontal()
|
||||
.Height(250)
|
||||
.Background(transparentBlue);
|
||||
|
||||
page.Header().Text("Background and foreground").Bold().FontColor(Colors.Blue.Darken2).FontSize(36);
|
||||
|
||||
page.Content().PaddingVertical(25).Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
|
||||
foreach (var i in Enumerable.Range(0, 100))
|
||||
column.Item().Background(Colors.Grey.Lighten2).Height(75);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.3" />
|
||||
<PackageReference Include="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" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -23,6 +26,12 @@
|
||||
<None Update="LibreBarcode39-Regular.ttf">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="pdf-icon.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
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());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ScaleToFitExamples
|
||||
{
|
||||
[Test]
|
||||
public void ScaleToFit()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container.Padding(25).Column(column =>
|
||||
{
|
||||
var text = Placeholders.Paragraph();
|
||||
|
||||
foreach (var i in Enumerable.Range(2, 5))
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Width(i * 40)
|
||||
.Height(i * 20)
|
||||
.ScaleToFit()
|
||||
.Text(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class ShowOnceExample
|
||||
{
|
||||
[Test]
|
||||
public void ShowOnce()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Header().Text("With show once").SemiBold();
|
||||
|
||||
page.Content().PaddingVertical(5).Row(row =>
|
||||
{
|
||||
row.RelativeItem()
|
||||
.Background(Colors.Grey.Lighten2)
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.ShowOnce()
|
||||
.Text(Placeholders.Label());
|
||||
|
||||
row.RelativeItem(2)
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Text(Placeholders.Paragraph());
|
||||
});
|
||||
|
||||
page.Footer().Text(text =>
|
||||
{
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" out of ");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class SkipOnceExample
|
||||
{
|
||||
[Test]
|
||||
public void SkipOnce()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.RenderDocument(container =>
|
||||
{
|
||||
container.Page(page =>
|
||||
{
|
||||
page.Margin(20);
|
||||
page.Size(PageSizes.A7.Landscape());
|
||||
page.PageColor(Colors.White);
|
||||
|
||||
page.Header().Column(column =>
|
||||
{
|
||||
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.");
|
||||
});
|
||||
|
||||
page.Content()
|
||||
.PaddingVertical(10)
|
||||
.Text(Placeholders.Paragraphs())
|
||||
.FontColor(Colors.Grey.Medium);
|
||||
|
||||
page.Footer().Text(text =>
|
||||
{
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
text.Span(" out of ");
|
||||
text.TotalPages();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class StopPaging
|
||||
{
|
||||
[Test]
|
||||
public void Example()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(300, 250)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.DefaultTextStyle(TextStyle.Default.Size(14))
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Before()
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.SemiBold().Color(Colors.Blue.Medium));
|
||||
|
||||
text.Span("Page ");
|
||||
text.CurrentPageNumber();
|
||||
});
|
||||
|
||||
decoration
|
||||
.Content()
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
column.Item().StopPaging().Text(Placeholders.LoremIpsum());
|
||||
column.Item().ExtendHorizontal().Height(75).Background(Colors.Grey.Lighten2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Examples.Engine;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
|
||||
namespace QuestPDF.Examples
|
||||
{
|
||||
public class TableBenchmark
|
||||
{
|
||||
[Test]
|
||||
public void Benchmark()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.ProducePdf()
|
||||
.PageSize(PageSizes.A4)
|
||||
.ShowResults()
|
||||
.MaxPages(10_000)
|
||||
.EnableCaching(true)
|
||||
.EnableDebugging(false)
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(10)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Table(table =>
|
||||
{
|
||||
const int numberOfRows = 100_000;
|
||||
const int numberOfColumns = 10;
|
||||
|
||||
table.ColumnsDefinition(columns =>
|
||||
{
|
||||
foreach (var _ in Enumerable.Range(0, numberOfColumns))
|
||||
columns.RelativeColumn();
|
||||
});
|
||||
|
||||
foreach (var row in Enumerable.Range(0, numberOfRows))
|
||||
foreach (var column in Enumerable.Range(0, numberOfColumns))
|
||||
table.Cell().Background(Placeholders.BackgroundColor()).Padding(5).Text($"{row}_{column}");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,9 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(x => ComposeBook(x, chapters));
|
||||
.RenderDocument(x => ComposeBook(x, chapters));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -32,7 +31,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
var chapters = GetBookChapters().ToList();
|
||||
|
||||
var results = PerformTest(16).ToList();
|
||||
var results = PerformTest(128).ToList();
|
||||
|
||||
Console.WriteLine($"Min: {results.Min():F}");
|
||||
Console.WriteLine($"Max: {results.Max():F}");
|
||||
@@ -43,9 +42,8 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.Render(x => ComposeBook(x, chapters));
|
||||
.RenderDocument(x => ComposeBook(x, chapters));
|
||||
}
|
||||
|
||||
IEnumerable<float> PerformTest(int attempts)
|
||||
@@ -104,112 +102,105 @@ namespace QuestPDF.Examples
|
||||
}
|
||||
}
|
||||
|
||||
private void ComposeBook(IContainer container, ICollection<BookChapter> chapters)
|
||||
private void ComposeBook(IDocumentContainer container, ICollection<BookChapter> chapters)
|
||||
{
|
||||
var subtitleStyle = TextStyle.Default.Size(24).SemiBold().Color(Colors.Blue.Medium);
|
||||
var normalStyle = TextStyle.Default.Size(14);
|
||||
|
||||
ComposePage(container);
|
||||
|
||||
void ComposePage(IContainer container)
|
||||
container.Page(page =>
|
||||
{
|
||||
container
|
||||
.Padding(50)
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Content()
|
||||
.Stack(stack =>
|
||||
{
|
||||
stack.Item().Element(Title);
|
||||
stack.Item().PageBreak();
|
||||
stack.Item().Element(TableOfContents);
|
||||
stack.Item().PageBreak();
|
||||
page.Margin(50);
|
||||
|
||||
page.Content().PaddingVertical(10).Column(column =>
|
||||
{
|
||||
column.Item().Element(Title);
|
||||
column.Item().PageBreak();
|
||||
column.Item().Element(TableOfContents);
|
||||
column.Item().PageBreak();
|
||||
|
||||
Chapters(stack);
|
||||
Chapters(column);
|
||||
|
||||
stack.Item().Element(Acknowledgements);
|
||||
});
|
||||
column.Item().Element(Acknowledgements);
|
||||
});
|
||||
|
||||
page.Footer().Element(Footer);
|
||||
});
|
||||
|
||||
decoration.Footer().Element(Footer);
|
||||
});
|
||||
}
|
||||
|
||||
void Title(IContainer container)
|
||||
{
|
||||
container
|
||||
.Extend()
|
||||
.PaddingBottom(200)
|
||||
.AlignBottom()
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
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));
|
||||
column.Item().Text("Quo Vadis").FontSize(72).Bold().FontColor(Colors.Blue.Darken2);
|
||||
column.Item().Text("Henryk Sienkiewicz").FontSize(24).FontColor(Colors.Grey.Darken2);
|
||||
});
|
||||
}
|
||||
|
||||
void TableOfContents(IContainer container)
|
||||
{
|
||||
container.Stack(stack =>
|
||||
container.Column(column =>
|
||||
{
|
||||
SectionTitle(stack, "Spis treści");
|
||||
SectionTitle(column, "Spis treści");
|
||||
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
stack.Item().InternalLink(chapter.Title).Row(row =>
|
||||
column.Item().InternalLink(chapter.Title).Row(row =>
|
||||
{
|
||||
row.RelativeColumn().Text(chapter.Title, normalStyle);
|
||||
row.ConstantColumn(100).AlignRight().Text(text => text.PageNumberOfLocation(chapter.Title, normalStyle));
|
||||
row.RelativeItem().Text(chapter.Title).Style(normalStyle);
|
||||
row.ConstantItem(100).AlignRight().Text(text => text.BeginPageNumberOfSection(chapter.Title).Style(normalStyle));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Chapters(StackDescriptor stack)
|
||||
void Chapters(ColumnDescriptor column)
|
||||
{
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
stack.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
|
||||
column.Item().Element(container => Chapter(container, chapter.Title, chapter.Content));
|
||||
}
|
||||
}
|
||||
|
||||
void Chapter(IContainer container, string title, string content)
|
||||
{
|
||||
container.Stack(stack =>
|
||||
container.Column(column =>
|
||||
{
|
||||
SectionTitle(stack, title);
|
||||
SectionTitle(column, title);
|
||||
|
||||
stack.Item().Text(text =>
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.ParagraphSpacing(5);
|
||||
text.Span(content, normalStyle);
|
||||
text.Span(content).Style(normalStyle);
|
||||
});
|
||||
|
||||
stack.Item().PageBreak();
|
||||
column.Item().PageBreak();
|
||||
});
|
||||
}
|
||||
|
||||
void Acknowledgements(IContainer container)
|
||||
{
|
||||
container.Stack(stack =>
|
||||
container.Column(column =>
|
||||
{
|
||||
SectionTitle(stack, "Podziękowania");
|
||||
SectionTitle(column, "Podziękowania");
|
||||
|
||||
stack.Item().Text(text =>
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(normalStyle);
|
||||
|
||||
text.Span("Ten dokument został wygenerowany na podstawie książki w formacie TXT opublikowanej w serwisie ");
|
||||
text.ExternalLocation("wolnelektury.pl", "https://wolnelektury.pl/", normalStyle.Color(Colors.Blue.Medium).Underline());
|
||||
text.Hyperlink("wolnelektury.pl", "https://wolnelektury.pl/").FontColor(Colors.Blue.Medium).Underline();
|
||||
text.Span(". Dziękuję za wspieranie polskiego czytelnictwa!");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SectionTitle(StackDescriptor stack, string text)
|
||||
void SectionTitle(ColumnDescriptor column, string text)
|
||||
{
|
||||
stack.Item().Location(text).Text(text, subtitleStyle);
|
||||
stack.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
||||
column.Item().Location(text).Text(text).Style(subtitleStyle);
|
||||
column.Item().PaddingTop(10).PaddingBottom(50).BorderBottom(1).BorderColor(Colors.Grey.Lighten2).ExtendHorizontal();
|
||||
}
|
||||
|
||||
void Footer(IContainer container)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
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,55 +13,219 @@ namespace QuestPDF.Examples
|
||||
public class TextExamples
|
||||
{
|
||||
[Test]
|
||||
public void SimpleTextBlock()
|
||||
public void SimpleText()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 300)
|
||||
.FileName()
|
||||
.PageSize(500, 100)
|
||||
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(Placeholders.Paragraph());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SimpleTextBlock()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(600, 300)
|
||||
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.MaxWidth(300)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||
text.Span("This is a normal text, followed by an ");
|
||||
text.Span("underlined red text.", TextStyle.Default.Size(20).Color(Colors.Red.Medium).Underline());
|
||||
text.Span("underlined red text").FontColor(Colors.Red.Medium).Underline();
|
||||
text.Span(".");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextWeight()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 500)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(20)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.FontFamily(Fonts.Calibri).FontSize(20));
|
||||
|
||||
text.Line("Thin").Thin();
|
||||
text.Line("ExtraLight").ExtraLight();
|
||||
text.Line("Light").Light();
|
||||
text.Line("NormalWeight").NormalWeight();
|
||||
text.Line("Medium").Medium();
|
||||
text.Line("SemiBold").SemiBold();
|
||||
text.Line("Bold").Bold();
|
||||
text.Line("ExtraBold").ExtraBold();
|
||||
text.Line("Black").Black();
|
||||
text.Line("ExtraBlack").ExtraBlack();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LineHeight()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 700)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.Column(column =>
|
||||
{
|
||||
var lineHeights = new[] { 0.8f, 1f, 1.5f };
|
||||
var paragraph = Placeholders.Paragraph();
|
||||
|
||||
foreach (var lineHeight in lineHeights)
|
||||
{
|
||||
column
|
||||
.Item()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(paragraph)
|
||||
.FontSize(16)
|
||||
.LineHeight(lineHeight);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuperscriptSubscript_Simple()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 500)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(20)
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(20)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.FontSize(20));
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
var highlight = TextStyle.Default.BackgroundColor(Colors.Green.Lighten3);
|
||||
|
||||
text.Span("E=mc").Style(highlight);
|
||||
text.Span("2").Superscript().Style(highlight);
|
||||
text.Span(" is the equation of mass–energy equivalence.");
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("H").Style(highlight);
|
||||
text.Span("2").Subscript().Style(highlight);
|
||||
text.Span("O").Style(highlight);
|
||||
text.Span(" is the chemical formula for water.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SuperscriptSubscript_Effects()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(800, 400)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(25)
|
||||
.DefaultTextStyle(x => x.FontSize(30))
|
||||
.Column(column =>
|
||||
{
|
||||
column.Spacing(25);
|
||||
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.Underline());
|
||||
|
||||
text.Span("Underline of the superscript (E = mc");
|
||||
text.Span("2").Superscript();
|
||||
text.Span(") should be at the same height as for normal text.");
|
||||
});
|
||||
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.Underline());
|
||||
|
||||
text.Span("Underline of the subscript(H");
|
||||
text.Span("2").Subscript();
|
||||
text.Span("O) should be slightly lower than a normal text.");
|
||||
});
|
||||
|
||||
column.Item().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.Strikethrough());
|
||||
|
||||
text.Span("Strikethrough of both superscript (E=mc");
|
||||
text.Span("2").Superscript();
|
||||
text.Span(") and subscript(H");
|
||||
text.Span("2").Subscript();
|
||||
text.Span("O) should be visible in the middle of the text.");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParagraphSpacing()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 300)
|
||||
.FileName()
|
||||
.PageSize(500, 500)
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
foreach (var i in Enumerable.Range(1, 3))
|
||||
{
|
||||
text.Span($"Paragraph {i}: ", TextStyle.Default.SemiBold());
|
||||
text.Line(Placeholders.Paragraph());
|
||||
}
|
||||
text.Line(Placeholders.Paragraph());
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -71,19 +236,18 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(500, 200)
|
||||
.FileName()
|
||||
.ProduceImages()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
{
|
||||
container
|
||||
.Padding(5)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default.Size(20));
|
||||
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
|
||||
text.Span("This is a random image aligned to the baseline: ");
|
||||
|
||||
text.Element()
|
||||
@@ -103,7 +267,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
@@ -111,7 +274,7 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
@@ -123,18 +286,17 @@ 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? <", TextStyle.Default.Underline());
|
||||
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <").Underline();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextStack()
|
||||
public void Textcolumn()
|
||||
{
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
@@ -142,7 +304,7 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
@@ -164,7 +326,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
@@ -172,12 +333,14 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(x => x.Bold());
|
||||
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
@@ -187,14 +350,14 @@ namespace QuestPDF.Examples
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
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("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.EmptyLine();
|
||||
|
||||
text.Span("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.EmptyLine();
|
||||
@@ -206,7 +369,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
@@ -215,7 +378,7 @@ namespace QuestPDF.Examples
|
||||
|
||||
text.EmptyLine();
|
||||
|
||||
text.Span(Placeholders.Paragraphs(), TextStyle.Default.Italic());
|
||||
text.Span(Placeholders.Paragraphs()).Italic();
|
||||
|
||||
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
|
||||
});
|
||||
@@ -228,7 +391,6 @@ namespace QuestPDF.Examples
|
||||
RenderingTest
|
||||
.Create()
|
||||
.PageSize(PageSizes.A4)
|
||||
.FileName()
|
||||
.ProducePdf()
|
||||
.ShowResults()
|
||||
.Render(container =>
|
||||
@@ -236,23 +398,23 @@ namespace QuestPDF.Examples
|
||||
container
|
||||
.Padding(20)
|
||||
.Padding(10)
|
||||
.Box()
|
||||
.MinimalBox()
|
||||
.Border(1)
|
||||
.Padding(5)
|
||||
.Padding(10)
|
||||
.Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(TextStyle.Default);
|
||||
text.DefaultTextStyle(TextStyle.Default.FontSize(20).BackgroundColor(Colors.Red.Lighten4));
|
||||
text.AlignLeft();
|
||||
text.ParagraphSpacing(10);
|
||||
|
||||
text.Span("This text is a normal text, ");
|
||||
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("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("The new text element also supports injecting custom content between words: ");
|
||||
text.Element().PaddingBottom(-10).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
|
||||
text.Span(".");
|
||||
|
||||
text.EmptyLine();
|
||||
@@ -261,7 +423,7 @@ namespace QuestPDF.Examples
|
||||
{
|
||||
text.Line($"{i}: {Placeholders.Paragraph()}");
|
||||
|
||||
text.ExternalLocation("Please visit QuestPDF website", "https://www.questpdf.com");
|
||||
text.Hyperlink("Please visit QuestPDF website. ", "https://www.questpdf.com");
|
||||
|
||||
text.Span("This is page number ");
|
||||
text.CurrentPageNumber();
|
||||
@@ -273,5 +435,356 @@ 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,149 @@
|
||||
<?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>
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -7,9 +7,6 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
public static class DataSource
|
||||
{
|
||||
public static int SectionCounter { get; set; }
|
||||
public static int FieldCounter { get; set; }
|
||||
|
||||
public static ReportModel GetReport()
|
||||
{
|
||||
return new ReportModel
|
||||
@@ -89,7 +86,6 @@ namespace QuestPDF.ReportSample
|
||||
return new ReportSectionMap
|
||||
{
|
||||
Label = "Location",
|
||||
ImageSource = Placeholders.Image,
|
||||
Location = Helpers.RandomLocation()
|
||||
};
|
||||
}
|
||||
@@ -99,10 +95,7 @@ namespace QuestPDF.ReportSample
|
||||
return new ReportSectionPhotos
|
||||
{
|
||||
Label = "Photos",
|
||||
Photos = Enumerable
|
||||
.Range(0, Helpers.Random.Next(1, 15))
|
||||
.Select(x => Placeholders.Image(400, 300))
|
||||
.ToList()
|
||||
PhotoCount = Helpers.Random.Next(1, 15)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -110,14 +103,9 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
return new ReportPhoto()
|
||||
{
|
||||
PhotoData = Placeholders.Image(800, 600),
|
||||
|
||||
Comments = Placeholders.Sentence(),
|
||||
Date = DateTime.Now - TimeSpan.FromDays(Helpers.Random.NextDouble() * 100),
|
||||
Location = Helpers.RandomLocation(),
|
||||
|
||||
MapContextSource = x => Placeholders.Image(400, 300),
|
||||
MapDetailsSource = x => Placeholders.Image(400, 300)
|
||||
Location = Helpers.RandomLocation()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -24,5 +25,32 @@ 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
public class DifferentHeadersTemplate : IDocument
|
||||
{
|
||||
public DocumentMetadata GetMetadata() => DocumentMetadata.Default;
|
||||
|
||||
public void Compose(IDocumentContainer container)
|
||||
{
|
||||
container
|
||||
.Page(page =>
|
||||
{
|
||||
page.Margin(40);
|
||||
|
||||
page.Size(PageSizes.A4);
|
||||
|
||||
page.Header().Element(ComposeHeader);
|
||||
page.Content().Element(ComposeContent);
|
||||
page.Footer().Element(ComposeFooter);
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeHeader(IContainer container)
|
||||
{
|
||||
container.Background(Colors.Grey.Lighten3).Border(1).Column(column =>
|
||||
{
|
||||
column.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);
|
||||
});
|
||||
column.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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeContent(IContainer container)
|
||||
{
|
||||
container.Column(column =>
|
||||
{
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeFooter(IContainer container)
|
||||
{
|
||||
container.Background(Colors.Grey.Lighten3).Column(column =>
|
||||
{
|
||||
column.Item().ShowOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text(x =>
|
||||
{
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
row.RelativeItem().AlignRight().Text("Footer for header");
|
||||
});
|
||||
|
||||
column.Item().SkipOnce().Background(Colors.Grey.Lighten3).Row(row =>
|
||||
{
|
||||
row.RelativeItem().Text(x =>
|
||||
{
|
||||
x.CurrentPageNumber();
|
||||
x.Span(" / ");
|
||||
x.TotalPages();
|
||||
});
|
||||
row.RelativeItem().AlignRight().Text("Footer for every page except header");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
public class ImagePlaceholder : IComponent
|
||||
{
|
||||
public static bool Solid { get; set; } = false;
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
if (Solid)
|
||||
container.Background(Placeholders.Color());
|
||||
|
||||
else
|
||||
container.Image(Placeholders.Image);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
public class ImageTemplate : IComponent
|
||||
{
|
||||
private Func<Size, byte[]> Source { get; }
|
||||
private float AspectRatio { get; }
|
||||
|
||||
public ImageTemplate(byte[] source, float aspectRatio = 1.333333f) : this(_ => source, aspectRatio)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ImageTemplate(Func<Size, byte[]> source, float aspectRatio = 1.333333f)
|
||||
{
|
||||
Source = source;
|
||||
AspectRatio = aspectRatio;
|
||||
}
|
||||
|
||||
public void Compose(IContainer container)
|
||||
{
|
||||
container
|
||||
.AspectRatio(AspectRatio)
|
||||
.Background("#EEEEEE")
|
||||
.Image(Source(Size.Zero));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -17,11 +18,11 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
container
|
||||
.ShowEntire()
|
||||
.Stack(stack =>
|
||||
.Column(column =>
|
||||
{
|
||||
stack.Spacing(5);
|
||||
stack.Item().Element(PhotoWithMaps);
|
||||
stack.Item().Element(PhotoDetails);
|
||||
column.Spacing(5);
|
||||
column.Item().Element(PhotoWithMaps);
|
||||
column.Item().Element(PhotoDetails);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,14 +31,14 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
container
|
||||
.Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2).AspectRatio(4 / 3f).Background(Colors.Grey.Lighten3);
|
||||
row.RelativeItem(2).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
|
||||
row.RelativeColumn().PaddingLeft(5).Stack(stack =>
|
||||
row.RelativeItem().PaddingLeft(5).Column(column =>
|
||||
{
|
||||
stack.Spacing(7f);
|
||||
column.Spacing(7f);
|
||||
|
||||
stack.Item().AspectRatio(4 / 3f).Background(Colors.Grey.Lighten3);
|
||||
stack.Item().AspectRatio(4 / 3f).Background(Colors.Grey.Lighten3);
|
||||
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
column.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -47,14 +48,14 @@ 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");
|
||||
grid.Item(2).ValueCell().Text(Model.Location.Format());
|
||||
|
||||
grid.Item().LabelCell().Text("Date", Typography.Normal);
|
||||
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty, Typography.Normal);
|
||||
grid.Item().LabelCell().Text("Location", Typography.Normal);
|
||||
grid.Item(2).ValueCell().Text(Model.Location.Format(), Typography.Normal);
|
||||
|
||||
grid.Item().LabelCell().Text("Comments", Typography.Normal);
|
||||
grid.Item(5).ValueCell().Text(Model.Comments, Typography.Normal);
|
||||
grid.Item().LabelCell().Text("Comments");
|
||||
grid.Item(5).ValueCell().Text(Model.Comments);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -20,25 +21,26 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Header()
|
||||
.Before()
|
||||
.PaddingBottom(5)
|
||||
.Text(Model.Title, Typography.Headline);
|
||||
.Text(Model.Title)
|
||||
.Style(Typography.Headline);
|
||||
|
||||
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Stack(stack =>
|
||||
decoration.Content().Border(0.75f).BorderColor(Colors.Grey.Medium).Column(column =>
|
||||
{
|
||||
foreach (var part in Model.Parts)
|
||||
{
|
||||
stack.Item().EnsureSpace(25).Row(row =>
|
||||
column.Item().EnsureSpace(25).Row(row =>
|
||||
{
|
||||
row.ConstantColumn(150).LabelCell().Text(part.Label, Typography.Normal);
|
||||
var frame = row.RelativeColumn().ValueCell();
|
||||
row.ConstantItem(150).LabelCell().Text(part.Label);
|
||||
var frame = row.RelativeItem().ValueCell();
|
||||
|
||||
if (part is ReportSectionText text)
|
||||
frame.ShowEntire().Text(text.Text, Typography.Normal);
|
||||
|
||||
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));
|
||||
});
|
||||
@@ -49,26 +51,26 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
void MapElement(IContainer container, ReportSectionMap model)
|
||||
{
|
||||
if (model.ImageSource == null || model.Location == null)
|
||||
if (model.Location == null)
|
||||
{
|
||||
container.Text("No location provided", Typography.Normal);
|
||||
container.Text("No location provided");
|
||||
return;
|
||||
}
|
||||
|
||||
container.ShowEntire().Stack(stack =>
|
||||
container.ShowEntire().Column(column =>
|
||||
{
|
||||
stack.Spacing(5);
|
||||
column.Spacing(5);
|
||||
|
||||
stack.Item().MaxWidth(250).AspectRatio(4 / 3f).Background(Colors.Grey.Lighten3);
|
||||
stack.Item().Text(model.Location.Format(), Typography.Normal);
|
||||
column.Item().MaxWidth(250).AspectRatio(4 / 3f).Component<ImagePlaceholder>();
|
||||
column.Item().Text(model.Location.Format());
|
||||
});
|
||||
}
|
||||
|
||||
void PhotosElement(IContainer container, ReportSectionPhotos model)
|
||||
{
|
||||
if (model.Photos.Count == 0)
|
||||
if (model.PhotoCount == 0)
|
||||
{
|
||||
container.Text("No photos", Typography.Normal);
|
||||
container.Text("No photos").Style(Typography.Normal);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,7 +79,12 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
grid.Spacing(5);
|
||||
grid.Columns(3);
|
||||
|
||||
model.Photos.ForEach(x => grid.Item().AspectRatio(4 / 3f).Background(Colors.Grey.Lighten3));
|
||||
Enumerable
|
||||
.Range(0, model.PhotoCount)
|
||||
.ToList()
|
||||
.ForEach(x => grid.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>());
|
||||
|
||||
//grid.Item().AspectRatio(4 / 3f).Width(100000).Component<ImagePlaceholder>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
container
|
||||
.Page(page =>
|
||||
{
|
||||
page.DefaultTextStyle(Typography.Normal);
|
||||
|
||||
page.MarginVertical(40);
|
||||
page.MarginHorizontal(50);
|
||||
|
||||
@@ -37,30 +39,28 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
page.Footer().AlignCenter().Text(text =>
|
||||
{
|
||||
text.DefaultTextStyle(Typography.Normal);
|
||||
|
||||
text.CurrentPageNumber();
|
||||
text.CurrentPageNumber().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
text.Span(" / ");
|
||||
text.TotalPages();
|
||||
text.TotalPages().Format(x => x?.FormatAsRomanNumeral() ?? "-----");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void ComposeHeader(IContainer container)
|
||||
{
|
||||
container.Stack(stack =>
|
||||
container.Column(column =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(50);
|
||||
|
||||
row.RelativeColumn().PaddingTop(-10).Text(Model.Title, Typography.Title);
|
||||
row.ConstantColumn(150).ExternalLink("https://www.questpdf.com").Background(Colors.Grey.Lighten3);
|
||||
row.RelativeItem().PaddingTop(-10).Text(Model.Title).Style(Typography.Title);
|
||||
row.ConstantItem(90).Hyperlink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
|
||||
});
|
||||
|
||||
stack.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
|
||||
column.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
|
||||
|
||||
stack.Item().ShowOnce().Grid(grid =>
|
||||
column.Item().ShowOnce().Grid(grid =>
|
||||
{
|
||||
grid.Columns(2);
|
||||
grid.Spacing(5);
|
||||
@@ -69,8 +69,8 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
grid.Item().Text(text =>
|
||||
{
|
||||
text.Span($"{field.Label}: ", Typography.Normal.SemiBold());
|
||||
text.Span(field.Value, Typography.Normal);
|
||||
text.Span($"{field.Label}: ").SemiBold();
|
||||
text.Span(field.Value);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -79,23 +79,23 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
|
||||
void ComposeContent(IContainer container)
|
||||
{
|
||||
container.PaddingVertical(20).Stack(stack =>
|
||||
container.PaddingVertical(20).Column(column =>
|
||||
{
|
||||
stack.Spacing(20);
|
||||
|
||||
stack.Item().Component(new TableOfContentsTemplate(Model.Sections));
|
||||
column.Spacing(20);
|
||||
|
||||
stack.Item().PageBreak();
|
||||
column.Item().Component(new TableOfContentsTemplate(Model.Sections));
|
||||
|
||||
column.Item().PageBreak();
|
||||
|
||||
foreach (var section in Model.Sections)
|
||||
stack.Item().Location(section.Title).Component(new SectionTemplate(section));
|
||||
|
||||
stack.Item().PageBreak();
|
||||
stack.Item().Location("Photos");
|
||||
column.Item().Section(section.Title).Component(new SectionTemplate(section));
|
||||
|
||||
column.Item().PageBreak();
|
||||
column.Item().Section("Photos");
|
||||
|
||||
foreach (var photo in Model.Photos)
|
||||
stack.Item().Component(new PhotoTemplate(photo));
|
||||
column.Item().Component(new PhotoTemplate(photo));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.ReportSample.Layouts
|
||||
{
|
||||
@@ -19,18 +22,19 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
.Decoration(decoration =>
|
||||
{
|
||||
decoration
|
||||
.Header()
|
||||
.Before()
|
||||
.PaddingBottom(5)
|
||||
.Text("Table of contents", Typography.Headline);
|
||||
.Text("Table of contents")
|
||||
.Style(Typography.Headline);
|
||||
|
||||
decoration.Content().Stack(stack =>
|
||||
decoration.Content().Column(column =>
|
||||
{
|
||||
stack.Spacing(5);
|
||||
column.Spacing(5);
|
||||
|
||||
for (var i = 0; i < Sections.Count; i++)
|
||||
stack.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
|
||||
column.Item().Element(c => DrawLink(c, i+1, Sections[i].Title));
|
||||
|
||||
stack.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
||||
column.Item().Element(c => DrawLink(c, Sections.Count+1, "Photos"));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -38,13 +42,39 @@ namespace QuestPDF.ReportSample.Layouts
|
||||
private void DrawLink(IContainer container, int number, string locationName)
|
||||
{
|
||||
container
|
||||
.InternalLink(locationName)
|
||||
.SectionLink(locationName)
|
||||
.Row(row =>
|
||||
{
|
||||
row.ConstantColumn(25).Text($"{number}.", Typography.Normal);
|
||||
row.RelativeColumn().Text(locationName, Typography.Normal);
|
||||
row.ConstantColumn(150).AlignRight().Text(text => text.PageNumberOfLocation(locationName, Typography.Normal));
|
||||
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})";
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,22 +44,16 @@ namespace QuestPDF.ReportSample
|
||||
|
||||
public class ReportSectionMap : ReportSectionElement
|
||||
{
|
||||
public Func<Size, byte[]> ImageSource { get; set; }
|
||||
public Location Location { get; set; }
|
||||
}
|
||||
|
||||
public class ReportSectionPhotos : ReportSectionElement
|
||||
{
|
||||
public List<byte[]> Photos { get; set; }
|
||||
public int PhotoCount { get; set; }
|
||||
}
|
||||
|
||||
public class ReportPhoto
|
||||
{
|
||||
public byte[] PhotoData { get; set; }
|
||||
|
||||
public Func<Size, byte[]> MapDetailsSource { get; set; }
|
||||
public Func<Size, byte[]> MapContextSource { get; set; }
|
||||
|
||||
{
|
||||
public Location Location { get; set; }
|
||||
|
||||
public DateTime? Date { get; set; }
|
||||
|
||||
@@ -11,12 +11,13 @@ using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Drawing.Proxy;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.ReportSample.Layouts;
|
||||
|
||||
namespace QuestPDF.ReportSample
|
||||
{
|
||||
[SimpleJob(RunStrategy.Monitoring, launchCount: 0, warmupCount: 1, targetCount: 100)]
|
||||
[SimpleJob(RunStrategy.Monitoring, launchCount: 0, warmupCount: 1, targetCount: 256)]
|
||||
[MinColumn, MaxColumn, MeanColumn, MedianColumn]
|
||||
public class PerformanceTests
|
||||
{
|
||||
@@ -27,6 +28,8 @@ namespace QuestPDF.ReportSample
|
||||
[Test]
|
||||
public void Run()
|
||||
{
|
||||
ImagePlaceholder.Solid = true;
|
||||
|
||||
var configuration = ManualConfig
|
||||
.Create(DefaultConfig.Instance)
|
||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator);
|
||||
@@ -37,6 +40,8 @@ namespace QuestPDF.ReportSample
|
||||
[IterationSetup]
|
||||
public void GenerateReportData()
|
||||
{
|
||||
ImagePlaceholder.Solid = true;
|
||||
|
||||
var model = DataSource.GetReport();
|
||||
var report = new StandardReport(model);
|
||||
Metadata = report.GetMetadata();
|
||||
@@ -46,12 +51,12 @@ namespace QuestPDF.ReportSample
|
||||
Content = documentContainer.Compose();
|
||||
|
||||
PageContext = new PageContext();
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, Metadata, null);
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content);
|
||||
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Content.HandleVisitor(x =>
|
||||
Content.VisitChildren(x =>
|
||||
{
|
||||
if (x is ICacheable)
|
||||
x.CreateProxy(y => new CacheProxy(y));
|
||||
@@ -64,7 +69,7 @@ namespace QuestPDF.ReportSample
|
||||
[Benchmark]
|
||||
public void GenerationTest()
|
||||
{
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content, Metadata, null);
|
||||
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net6.0</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.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" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.Previewer;
|
||||
using QuestPDF.ReportSample.Layouts;
|
||||
|
||||
namespace QuestPDF.ReportSample
|
||||
@@ -22,26 +23,38 @@ namespace QuestPDF.ReportSample
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GenerateAndShow()
|
||||
public void GenerateAndShowPdf()
|
||||
{
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
|
||||
Report.GeneratePdf(path);
|
||||
Report.ShowInPreviewer();
|
||||
|
||||
//ImagePlaceholder.Solid = true;
|
||||
//
|
||||
// var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
|
||||
// Report.GeneratePdf(path);
|
||||
// Process.Start("explorer.exe", path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GenerateAndShowXps()
|
||||
{
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.xps");
|
||||
Report.GenerateXps(path);
|
||||
Process.Start("explorer.exe", path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Profile()
|
||||
{
|
||||
ImagePlaceholder.Solid = true;
|
||||
|
||||
var container = new DocumentContainer();
|
||||
Report.Compose(container);
|
||||
var content = container.Compose();
|
||||
|
||||
var metadata = Report.GetMetadata();
|
||||
var pageContext = new PageContext();
|
||||
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, metadata, null);
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content, metadata, null);
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content);
|
||||
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ namespace QuestPDF.ReportSample
|
||||
{
|
||||
public static class Typography
|
||||
{
|
||||
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.Calibri).Color(Colors.Black).Size(11).LineHeight(1.1f);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,9 @@ namespace QuestPDF.UnitTests
|
||||
Ratio = 2f
|
||||
})
|
||||
.DrawElement(new Size(500, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw(new Size(400, 200))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -168,7 +170,45 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ namespace QuestPDF.UnitTests
|
||||
public class BoxTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<Box>();
|
||||
public void Measure() => SimpleContainerTests.Measure<MinimalBox>();
|
||||
|
||||
[Test]
|
||||
public void Draw_Wrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Box
|
||||
.For(x => new MinimalBox
|
||||
{
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
@@ -29,13 +29,15 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Box
|
||||
.For(x => new MinimalBox
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -43,13 +45,49 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_FullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Box
|
||||
.For(x => new MinimalBox
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
@@ -8,19 +9,40 @@ using QuestPDF.UnitTests.TestEngine;
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StackTests
|
||||
public class ColumnTests
|
||||
{
|
||||
private Column CreateColumnWithTwoItems(TestPlan testPlan)
|
||||
{
|
||||
return new Column
|
||||
{
|
||||
Items =
|
||||
{
|
||||
new ColumnItem
|
||||
{
|
||||
Child = testPlan.CreateChild("first")
|
||||
},
|
||||
new ColumnItem
|
||||
{
|
||||
Child = testPlan.CreateChild("second")
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Column CreateColumnWithTwoItemsWhereFirstIsFullyRendered(TestPlan testPlan)
|
||||
{
|
||||
var column = CreateColumnWithTwoItems(testPlan);
|
||||
column.Items.First().IsRendered = true;
|
||||
return column;
|
||||
}
|
||||
|
||||
#region Measure
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenFirstChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
@@ -30,11 +52,7 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsPartialRender_WhenFirstChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.PartialRender(300, 200))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(300, 200));
|
||||
@@ -44,11 +62,7 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsPartialRender_WhenSecondChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||
@@ -59,11 +73,7 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsPartialRender_WhenSecondChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.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))
|
||||
@@ -74,33 +84,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsFullRender_WhenSecondChildReturnsFullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.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
|
||||
@@ -109,11 +99,7 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckDrawResult();
|
||||
@@ -123,14 +109,12 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildPartiallyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.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();
|
||||
}
|
||||
|
||||
@@ -138,15 +122,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildWraps()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.Wrap())
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -154,15 +136,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildPartiallyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.PartialRender(250, 150))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectCanvasTranslate(0, 100)
|
||||
.ExpectChildDraw("second", new Size(400, 150))
|
||||
.ExpectCanvasTranslate(0, -100)
|
||||
@@ -173,15 +153,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_WhenFirstChildFullyRenders_AndSecondChildFullyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second")
|
||||
})
|
||||
.For(CreateColumnWithTwoItems)
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("first", new Size(400, 300), SpacePlan.FullRender(200, 100))
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectChildMeasure("second", new Size(400, 200), SpacePlan.FullRender(250, 150))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectChildDraw("first", new Size(400, 100))
|
||||
.ExpectCanvasTranslate(0, 0)
|
||||
.ExpectCanvasTranslate(0, 100)
|
||||
.ExpectChildDraw("second", new Size(400, 150))
|
||||
.ExpectCanvasTranslate(0, -100)
|
||||
@@ -192,19 +170,13 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_UsesEmpty_WhenFirstChildIsRendered()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second"),
|
||||
|
||||
IsFirstRendered = true
|
||||
})
|
||||
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
|
||||
.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<BinaryStack>(x => x.IsFirstRendered)
|
||||
.CheckState<Column>(x => x.Items.First().IsRendered)
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
@@ -212,97 +184,14 @@ namespace QuestPDF.UnitTests
|
||||
public void Draw_TogglesFirstRenderedFlag_WhenSecondFullyRenders()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryStack
|
||||
{
|
||||
First = x.CreateChild("first"),
|
||||
Second = x.CreateChild("second"),
|
||||
|
||||
IsFirstRendered = true
|
||||
})
|
||||
.For(CreateColumnWithTwoItemsWhereFirstIsFullyRendered)
|
||||
.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<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);
|
||||
.CheckState<Column>(x => !x.Items.First().IsRendered);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -9,6 +9,8 @@ namespace QuestPDF.UnitTests
|
||||
[TestFixture]
|
||||
public class ConstrainedTests
|
||||
{
|
||||
#region Height
|
||||
|
||||
[Test]
|
||||
public void Measure_MinHeight_ExpectWrap()
|
||||
{
|
||||
@@ -88,5 +90,91 @@ namespace QuestPDF.UnitTests
|
||||
.ExpectChildMeasure(new Size(400, 100), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Width
|
||||
|
||||
[Test]
|
||||
public void Measure_MinWidth_ExpectWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Constrained
|
||||
{
|
||||
MinWidth = 100
|
||||
})
|
||||
.MeasureElement(new Size(50, 400))
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_MinWidth_ExtendHeight()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Constrained
|
||||
{
|
||||
MinWidth = 100,
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(200, 400))
|
||||
.ExpectChildMeasure(new Size(200, 400), SpacePlan.FullRender(50, 400))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(100, 400));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_MinWidth_PassHeight()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Constrained
|
||||
{
|
||||
MinWidth = 100,
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(200, 400))
|
||||
.ExpectChildMeasure(new Size(200, 400), SpacePlan.FullRender(150, 400))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(150, 400));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_MaxWidth_Empty()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Constrained
|
||||
{
|
||||
MaxWidth = 100
|
||||
})
|
||||
.MeasureElement(new Size(150, 400))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(0, 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_MaxWidth_PartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Constrained
|
||||
{
|
||||
MaxWidth = 100,
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(200, 400))
|
||||
.ExpectChildMeasure(new Size(100, 400), SpacePlan.PartialRender(75, 400))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(75, 400));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_MaxWidth_ExpectWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new Constrained
|
||||
{
|
||||
MaxWidth = 100,
|
||||
Child = x.CreateChild()
|
||||
})
|
||||
.MeasureElement(new Size(200, 400))
|
||||
.ExpectChildMeasure(new Size(100, 400), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,35 +9,27 @@ 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_WhenDecorationReturnsWrap()
|
||||
public void Measure_ReturnsWrap_WhenBeforeReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.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))
|
||||
.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))
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
@@ -45,89 +37,100 @@ namespace QuestPDF.UnitTests
|
||||
public void Measure_ReturnsWrap_WhenContentReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("decoration", new Size(400, 300), SpacePlan.FullRender(300, 100))
|
||||
.ExpectChildMeasure("before", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("after", new Size(400, 300), SpacePlan.FullRender(100, 50))
|
||||
.ExpectChildMeasure("content", new Size(400, 200), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsPartialRender_WhenContentReturnsPartialRender()
|
||||
public void Measure_ReturnsWrap_WhenAfterReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.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));
|
||||
.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());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsFullRender_WhenContentReturnsFullRender()
|
||||
public void Measure_ReturnsWrap_WhenBeforeReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.For(CreateDecoration)
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.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));
|
||||
.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());
|
||||
}
|
||||
|
||||
[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(x => new BinaryDecoration
|
||||
{
|
||||
Type = DecorationType.Append,
|
||||
DecorationElement = x.CreateChild("decoration"),
|
||||
ContentElement = x.CreateChild("content")
|
||||
})
|
||||
.For(CreateDecoration)
|
||||
.DrawElement(new Size(400, 300))
|
||||
.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)
|
||||
.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)
|
||||
|
||||
.CheckDrawResult();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace QuestPDF.UnitTests
|
||||
.ExpectCanvasDrawImage(Position.Zero, new Size(400, 300))
|
||||
.CheckDrawResult();
|
||||
|
||||
passedSize.Should().Be(new Size(400, 300));
|
||||
passedSize.Should().BeEquivalentTo(new Size(400, 300));
|
||||
}
|
||||
|
||||
byte[] GenerateImage(Size size)
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
||||
public class ExternalLinkTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<ExternalLink>();
|
||||
public void Measure() => SimpleContainerTests.Measure<Hyperlink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using SkiaSharp;
|
||||
using static SkiaSharp.SKFontStyleSlant;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FontStyleSetTests
|
||||
{
|
||||
private void ExpectComparisonOrder(SKFontStyle target, SKFontStyle[] styles)
|
||||
{
|
||||
for (var i = 0; i < styles.Length - 1; i++)
|
||||
{
|
||||
var currentStyle = styles[i];
|
||||
var nextStyle = styles[i + 1];
|
||||
|
||||
FontStyleSet.IsBetterMatch(target, currentStyle, nextStyle).Should().BeTrue();
|
||||
FontStyleSet.IsBetterMatch(target, nextStyle, currentStyle).Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_CondensedWidth()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Upright),
|
||||
new SKFontStyle(500, 4, Upright),
|
||||
new SKFontStyle(500, 3, Upright),
|
||||
new SKFontStyle(500, 6, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ExpandedWidth()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 6, Upright),
|
||||
new SKFontStyle(500, 7, Upright),
|
||||
new SKFontStyle(500, 8, Upright),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 6, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ItalicSlant()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Italic),
|
||||
new SKFontStyle(500, 5, Oblique),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Italic), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ObliqueSlant()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Oblique),
|
||||
new SKFontStyle(500, 5, Italic),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Oblique), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_UprightSlant()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Upright),
|
||||
new SKFontStyle(500, 5, Oblique),
|
||||
new SKFontStyle(500, 5, Italic)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_ThinWeight()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(300, 5, Upright),
|
||||
new SKFontStyle(200, 5, Upright),
|
||||
new SKFontStyle(100, 5, Upright),
|
||||
new SKFontStyle(400, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(300, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_RegularWeight()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(500, 5, Upright),
|
||||
new SKFontStyle(300, 5, Upright),
|
||||
new SKFontStyle(100, 5, Upright),
|
||||
new SKFontStyle(600, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(400, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_IsBetterMatch_BoldWeight()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(600, 5, Upright),
|
||||
new SKFontStyle(700, 5, Upright),
|
||||
new SKFontStyle(800, 5, Upright),
|
||||
new SKFontStyle(500, 5, Upright)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(600, 5, Upright), styles);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FontStyleSet_RespectsPriority()
|
||||
{
|
||||
var styles = new[]
|
||||
{
|
||||
new SKFontStyle(600, 5, Italic),
|
||||
new SKFontStyle(600, 6, Upright),
|
||||
new SKFontStyle(500, 6, Italic)
|
||||
};
|
||||
|
||||
ExpectComparisonOrder(new SKFontStyle(500, 5, Upright), styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,26 +39,26 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Column(column =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeItem(6).Element(childA);
|
||||
row.RelativeItem(4).Element(childB);
|
||||
row.RelativeItem(2);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(6);
|
||||
row.RelativeItem(4).Element(childC);
|
||||
row.RelativeItem(2).Element(childD);
|
||||
row.RelativeItem(6);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(4);
|
||||
row.RelativeItem(8).Element(childE);
|
||||
row.RelativeItem(4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,29 +93,29 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Column(column =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(1);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeColumn(1);
|
||||
row.RelativeItem(1);
|
||||
row.RelativeItem(6).Element(childA);
|
||||
row.RelativeItem(4).Element(childB);
|
||||
row.RelativeItem(1);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeItem(3);
|
||||
row.RelativeItem(4).Element(childC);
|
||||
row.RelativeItem(2).Element(childD);
|
||||
row.RelativeItem(3);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeItem(2);
|
||||
row.RelativeItem(8).Element(childE);
|
||||
row.RelativeItem(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,26 +150,26 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Column(column =>
|
||||
{
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(6).Element(childA);
|
||||
row.RelativeColumn(4).Element(childB);
|
||||
row.RelativeItem(2);
|
||||
row.RelativeItem(6).Element(childA);
|
||||
row.RelativeItem(4).Element(childB);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(6);
|
||||
row.RelativeColumn(4).Element(childC);
|
||||
row.RelativeColumn(2).Element(childD);
|
||||
row.RelativeItem(6);
|
||||
row.RelativeItem(4).Element(childC);
|
||||
row.RelativeItem(2).Element(childD);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.RelativeColumn(4);
|
||||
row.RelativeColumn(8).Element(childE);
|
||||
row.RelativeItem(4);
|
||||
row.RelativeItem(8).Element(childE);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,36 +210,36 @@ namespace QuestPDF.UnitTests
|
||||
// assert
|
||||
var expected = new Container();
|
||||
|
||||
expected.Stack(stack =>
|
||||
expected.Column(column =>
|
||||
{
|
||||
stack.Spacing(20);
|
||||
column.Spacing(20);
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(5).Element(childA);
|
||||
row.RelativeColumn(5).Element(childB);
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeItem(3);
|
||||
row.RelativeItem(5).Element(childA);
|
||||
row.RelativeItem(5).Element(childB);
|
||||
row.RelativeItem(3);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeColumn(10).Element(childC);
|
||||
row.RelativeColumn(3);
|
||||
row.RelativeItem(3);
|
||||
row.RelativeItem(10).Element(childC);
|
||||
row.RelativeItem(3);
|
||||
});
|
||||
|
||||
stack.Item().Row(row =>
|
||||
column.Item().Row(row =>
|
||||
{
|
||||
row.Spacing(30);
|
||||
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeColumn(12).Element(childD);
|
||||
row.RelativeColumn(2);
|
||||
row.RelativeItem(2);
|
||||
row.RelativeItem(12).Element(childD);
|
||||
row.RelativeItem(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
||||
public class InternalLinkTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
||||
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace QuestPDF.UnitTests
|
||||
public class InternalLocationTests
|
||||
{
|
||||
[Test]
|
||||
public void Measure() => SimpleContainerTests.Measure<InternalLink>();
|
||||
public void Measure() => SimpleContainerTests.Measure<SectionLink>();
|
||||
|
||||
// TODO: consider tests for the Draw method
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
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>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Elements;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine;
|
||||
|
||||
namespace QuestPDF.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class RowTests
|
||||
{
|
||||
#region Measure
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenLeftChildReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsWrap_WhenRightChildReturnsWrap()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
|
||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.Wrap())
|
||||
.CheckMeasureResult(SpacePlan.Wrap());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsPartialRender_WhenLeftChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.PartialRender(250, 150))
|
||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(350, 150));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsPartialRender_WhenRightChildReturnsPartialRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
|
||||
.ExpectChildMeasure("right", new Size(150, 300), SpacePlan.PartialRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.PartialRender(350, 150));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Measure_ReturnsFullRender_WhenBothChildrenReturnFullRender()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.MeasureElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(200, 150))
|
||||
.ExpectChildMeasure("right", new Size(200, 300), SpacePlan.FullRender(100, 100))
|
||||
.CheckMeasureResult(SpacePlan.FullRender(300, 150));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Draw
|
||||
|
||||
[Test]
|
||||
public void Draw()
|
||||
{
|
||||
TestPlan
|
||||
.For(x => new BinaryRow
|
||||
{
|
||||
Left = x.CreateChild("left"),
|
||||
Right = x.CreateChild("right")
|
||||
})
|
||||
.DrawElement(new Size(400, 300))
|
||||
.ExpectChildMeasure("left", new Size(400, 300), SpacePlan.FullRender(250, 150))
|
||||
.ExpectChildDraw("left", new Size(250, 300))
|
||||
.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,4 +1,5 @@
|
||||
using System;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
@@ -10,7 +11,6 @@ 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<string, Position, TextStyle> DrawTextFunc { get; set; }
|
||||
public Action<Position, Size, string> DrawRectFunc { get; set; }
|
||||
|
||||
public void Translate(Position vector) => TranslateFunc(vector);
|
||||
@@ -18,11 +18,11 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
public void Scale(float scaleX, float scaleY) => ScaleFunc(scaleX, scaleY);
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color) => DrawRectFunc(vector, size, color);
|
||||
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
|
||||
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
|
||||
|
||||
public void 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();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Infrastructure;
|
||||
using QuestPDF.UnitTests.TestEngine.Operations;
|
||||
using SkiaSharp;
|
||||
@@ -15,11 +16,11 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
public void Scale(float scaleX, float scaleY) => Operations.Add(new CanvasScaleOperation(scaleX, scaleY));
|
||||
|
||||
public void DrawRectangle(Position vector, Size size, string color) => Operations.Add(new CanvasDrawRectangleOperation(vector, size, color));
|
||||
public void DrawText(string text, Position position, TextStyle style) => Operations.Add(new CanvasDrawTextOperation(text, position, style));
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
|
||||
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
|
||||
|
||||
public void 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();
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -82,19 +82,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
Assert.AreEqual(expected.Color, color, "Draw rectangle: color");
|
||||
},
|
||||
DrawTextFunc = (text, position, style) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawTextOperation>();
|
||||
|
||||
Assert.AreEqual(expected.Text, text);
|
||||
|
||||
Assert.AreEqual(expected.Position.X, position.X, "Draw text: X");
|
||||
Assert.AreEqual(expected.Position.Y, position.Y, "Draw text: Y");
|
||||
|
||||
Assert.AreEqual(expected.Style.Color, style.Color, "Draw text: color");
|
||||
Assert.AreEqual(expected.Style.FontType, style.FontType, "Draw text: font");
|
||||
Assert.AreEqual(expected.Style.Size, style.Size, "Draw text: size");
|
||||
},
|
||||
DrawImageFunc = (image, position, size) =>
|
||||
{
|
||||
var expected = GetExpected<CanvasDrawImageOperation>();
|
||||
@@ -201,11 +188,6 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
return AddOperation(new CanvasDrawRectangleOperation(position, size, color));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawText(string text, Position position, TextStyle style)
|
||||
{
|
||||
return AddOperation(new CanvasDrawTextOperation(text, position, style));
|
||||
}
|
||||
|
||||
public TestPlan ExpectCanvasDrawImage(Position position, Size size)
|
||||
{
|
||||
return AddOperation(new CanvasDrawImageOperation(position, size));
|
||||
@@ -213,7 +195,7 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public TestPlan CheckMeasureResult(SpacePlan expected)
|
||||
{
|
||||
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
|
||||
Element.InjectDependencies(null, Canvas);
|
||||
|
||||
var actual = Element.Measure(OperationInput);
|
||||
|
||||
@@ -228,7 +210,7 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
|
||||
public TestPlan CheckDrawResult()
|
||||
{
|
||||
Element.HandleVisitor(x => x?.Initialize(null, Canvas));
|
||||
Element.InjectDependencies(null, Canvas);
|
||||
Element.Draw(OperationInput);
|
||||
return this;
|
||||
}
|
||||
@@ -271,10 +253,10 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
availableSpace ??= new Size(400, 300);
|
||||
|
||||
var canvas = new FreeCanvas();
|
||||
value.HandleVisitor(x => x.Initialize(null, canvas));
|
||||
value.InjectDependencies(null, canvas);
|
||||
var valueMeasure = value.Measure(availableSpace.Value);
|
||||
|
||||
expected.HandleVisitor(x => x.Initialize(null, canvas));
|
||||
expected.InjectDependencies(null, canvas);
|
||||
var expectedMeasure = expected.Measure(availableSpace.Value);
|
||||
|
||||
valueMeasure.Should().BeEquivalentTo(expectedMeasure);
|
||||
@@ -285,11 +267,11 @@ namespace QuestPDF.UnitTests.TestEngine
|
||||
availableSpace ??= new Size(400, 300);
|
||||
|
||||
var valueCanvas = new OperationRecordingCanvas();
|
||||
value.HandleVisitor(x => x.Initialize(null, valueCanvas));
|
||||
value.InjectDependencies(null, valueCanvas);
|
||||
value.Draw(availableSpace.Value);
|
||||
|
||||
var expectedCanvas = new OperationRecordingCanvas();
|
||||
expected.HandleVisitor(x => x.Initialize(null, expectedCanvas));
|
||||
expected.InjectDependencies(null, expectedCanvas);
|
||||
expected.Draw(availableSpace.Value);
|
||||
|
||||
valueCanvas.Operations.Should().BeEquivalentTo(expectedCanvas.Operations);
|
||||
|
||||
@@ -77,7 +77,9 @@ 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();
|
||||
}
|
||||
|
||||
@@ -91,7 +93,43 @@ 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,6 +8,13 @@ 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
|
||||
@@ -30,5 +37,9 @@ 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
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("QuestPDF.UnitTests")]
|
||||
[assembly: InternalsVisibleTo("QuestPDF.Examples")]
|
||||
[assembly: InternalsVisibleTo("QuestPDF.ReportSample")]
|
||||
@@ -16,13 +16,14 @@ namespace QuestPDF.Drawing
|
||||
var container = new Container();
|
||||
|
||||
container
|
||||
.Stack(stack =>
|
||||
.DebugPointer("Document")
|
||||
.Column(column =>
|
||||
{
|
||||
Pages
|
||||
.SelectMany(x => new List<Action>()
|
||||
{
|
||||
() => stack.Item().PageBreak(),
|
||||
() => stack.Item().Component(x)
|
||||
() => column.Item().PageBreak(),
|
||||
() => column.Item().DebugPointer("Page").Component(x)
|
||||
})
|
||||
.Skip(1)
|
||||
.ToList()
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
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
|
||||
{
|
||||
@@ -12,11 +18,31 @@ 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var metadata = document.GetMetadata();
|
||||
@@ -26,36 +52,49 @@ namespace QuestPDF.Drawing
|
||||
return canvas.Images;
|
||||
}
|
||||
|
||||
private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document)
|
||||
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)
|
||||
where TCanvas : ICanvas, IRenderingCanvas
|
||||
{
|
||||
var container = new DocumentContainer();
|
||||
document.Compose(container);
|
||||
var content = container.Compose();
|
||||
ApplyDefaultTextStyle(content, TextStyle.LibraryDefault);
|
||||
ApplyContentDirection(content, ContentDirection.LeftToRight);
|
||||
|
||||
var metadata = document.GetMetadata();
|
||||
var pageContext = new PageContext();
|
||||
|
||||
DebuggingState debuggingState = null;
|
||||
|
||||
if (System.Diagnostics.Debugger.IsAttached)
|
||||
{
|
||||
debuggingState = ApplyDebugging(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Settings.EnableCaching)
|
||||
ApplyCaching(content);
|
||||
}
|
||||
|
||||
RenderPass(pageContext, new FreeCanvas(), content, metadata, debuggingState);
|
||||
RenderPass(pageContext, canvas, content, metadata, debuggingState);
|
||||
var pageContext = new PageContext();
|
||||
RenderPass(pageContext, new FreeCanvas(), content);
|
||||
|
||||
if (applyInspection)
|
||||
ApplyInspection(content);
|
||||
|
||||
RenderPass(pageContext, canvas, content);
|
||||
|
||||
if (applyInspection)
|
||||
inspectionResultHandler(content.ExtractDocumentHierarchy());
|
||||
}
|
||||
|
||||
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content, DocumentMetadata documentMetadata, DebuggingState? debuggingState)
|
||||
internal static void RenderPass<TCanvas>(PageContext pageContext, TCanvas canvas, Container content)
|
||||
where TCanvas : ICanvas, IRenderingCanvas
|
||||
{
|
||||
content.HandleVisitor(x => x?.Initialize(pageContext, canvas));
|
||||
content.HandleVisitor(x => (x as IStateResettable)?.ResetState());
|
||||
InjectDependencies(content, pageContext, canvas);
|
||||
content.VisitChildren(x => (x as IStateResettable)?.ResetState());
|
||||
|
||||
canvas.BeginDocument();
|
||||
|
||||
@@ -64,7 +103,6 @@ namespace QuestPDF.Drawing
|
||||
while(true)
|
||||
{
|
||||
pageContext.SetPageNumber(currentPage);
|
||||
debuggingState?.Reset();
|
||||
|
||||
var spacePlan = content.Measure(Size.Max);
|
||||
|
||||
@@ -87,7 +125,7 @@ namespace QuestPDF.Drawing
|
||||
|
||||
canvas.EndPage();
|
||||
|
||||
if (currentPage >= documentMetadata.DocumentLayoutExceptionThreshold)
|
||||
if (currentPage >= Settings.DocumentLayoutExceptionThreshold)
|
||||
{
|
||||
canvas.EndDocument();
|
||||
ThrowLayoutException();
|
||||
@@ -103,32 +141,100 @@ namespace QuestPDF.Drawing
|
||||
|
||||
void ThrowLayoutException()
|
||||
{
|
||||
throw new DocumentLayoutException("Composed layout generates infinite document.")
|
||||
{
|
||||
ElementTrace = debuggingState?.BuildTrace() ?? "Debug trace is available only in the DEBUG mode."
|
||||
};
|
||||
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. " +
|
||||
$"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);
|
||||
}
|
||||
}
|
||||
|
||||
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.HandleVisitor(x =>
|
||||
content.VisitChildren(x =>
|
||||
{
|
||||
if (x is ICacheable)
|
||||
x.CreateProxy(y => new CacheProxy(y));
|
||||
});
|
||||
}
|
||||
|
||||
private static DebuggingState ApplyDebugging(Container content)
|
||||
private static void ApplyInspection(Container content)
|
||||
{
|
||||
var debuggingState = new DebuggingState();
|
||||
|
||||
content.HandleVisitor(x =>
|
||||
content.VisitChildren(x =>
|
||||
{
|
||||
x.CreateProxy(y => new DebuggingProxy(debuggingState, y));
|
||||
x.CreateProxy(y => y is ElementProxy or Container ? y : new InspectionProxy(y));
|
||||
});
|
||||
}
|
||||
|
||||
internal static void ApplyContentDirection(this Element? content, ContentDirection direction)
|
||||
{
|
||||
if (content == null)
|
||||
return;
|
||||
|
||||
return debuggingState;
|
||||
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);
|
||||
}
|
||||
|
||||
internal static void ApplyDefaultTextStyle(this Element? content, TextStyle documentDefaultTextStyle)
|
||||
{
|
||||
if (content == null)
|
||||
return;
|
||||
|
||||
if (content is TextBlock textBlock)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 content.GetChildren())
|
||||
ApplyDefaultTextStyle(child, documentDefaultTextStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
@@ -18,12 +19,27 @@ namespace QuestPDF.Drawing
|
||||
public DateTime CreationDate { get; set; } = DateTime.Now;
|
||||
public DateTime ModifiedDate { get; set; } = DateTime.Now;
|
||||
|
||||
/// <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.DocumentLayoutExceptionThreshold static property.")]
|
||||
public int DocumentLayoutExceptionThreshold
|
||||
{
|
||||
get => Settings.DocumentLayoutExceptionThreshold;
|
||||
set => Settings.DocumentLayoutExceptionThreshold = value;
|
||||
}
|
||||
|
||||
[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 static DocumentMetadata Default => new DocumentMetadata();
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,7 @@ namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class DocumentComposeException : Exception
|
||||
{
|
||||
public DocumentComposeException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentComposeException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentComposeException(string message, Exception inner) : base(message, inner)
|
||||
internal DocumentComposeException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -4,17 +4,12 @@ namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class DocumentDrawingException : Exception
|
||||
{
|
||||
public DocumentDrawingException()
|
||||
internal DocumentDrawingException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentDrawingException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentDrawingException(string message, Exception inner) : base(message, inner)
|
||||
|
||||
internal DocumentDrawingException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
using System;
|
||||
using QuestPDF.Previewer;
|
||||
|
||||
namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class DocumentLayoutException : Exception
|
||||
{
|
||||
public string ElementTrace { get; set; }
|
||||
|
||||
public DocumentLayoutException()
|
||||
{
|
||||
|
||||
}
|
||||
internal LayoutErrorTrace? ElementTrace { get; }
|
||||
|
||||
public DocumentLayoutException(string message) : base(message)
|
||||
internal DocumentLayoutException(string message, LayoutErrorTrace? elementTrace = null) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DocumentLayoutException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
|
||||
ElementTrace = elementTrace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace QuestPDF.Drawing.Exceptions
|
||||
{
|
||||
public class InitializationException : Exception
|
||||
{
|
||||
internal InitializationException(string documentType, Exception innerException) : base(CreateMessage(documentType, innerException.Message), innerException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private static string CreateMessage(string documentType, string innerExceptionMessage)
|
||||
{
|
||||
var (libraryName, nugetConvention) = GetLibraryName();
|
||||
|
||||
return $"Cannot create the {documentType} document using the {libraryName} library. " +
|
||||
$"This exception usually means that, on your operating system where you run the application, {libraryName} requires installing additional dependencies. " +
|
||||
$"Such dependencies are available as additional nuget packages, for example {nugetConvention}.Linux.NoDependencies. " +
|
||||
$"Some operating systems may require installing multiple nugets, e.g. MacOS may need both {nugetConvention}.macOS.NoDependencies and {nugetConvention}.Linux.NoDependencies." +
|
||||
$"Please refer to the {libraryName} documentation for more details. " +
|
||||
$"Also, please consult the inner exception that has been originally thrown by the dependency library.";
|
||||
|
||||
(string GetLibraryName, string nugetConvention) GetLibraryName()
|
||||
{
|
||||
if (innerExceptionMessage.Contains("libSkiaSharp"))
|
||||
return ("SkiaSharp", "SkiaSharp.NativeAssets");
|
||||
|
||||
if (innerExceptionMessage.Contains("libHarfBuzzSharp"))
|
||||
return ("HarfBuzzSharp", "HarfBuzzSharp.NativeAssets");
|
||||
|
||||
// default
|
||||
return ("SkiaSharp-related", "*.NativeAssets");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+164
-19
@@ -1,66 +1,211 @@
|
||||
using System;
|
||||
using System.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 ConcurrentDictionary<string, SKTypeface> Typefaces = new ConcurrentDictionary<string, SKTypeface>();
|
||||
private static ConcurrentDictionary<string, SKFontMetrics> FontMetrics = new ConcurrentDictionary<string, SKFontMetrics>();
|
||||
private static ConcurrentDictionary<string, SKPaint> Paints = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static ConcurrentDictionary<string, SKPaint> ColorPaint = new ConcurrentDictionary<string, SKPaint>();
|
||||
private static 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();
|
||||
|
||||
public static void RegisterFontType(string fontName, Stream stream)
|
||||
static FontManager()
|
||||
{
|
||||
Typefaces.TryAdd(fontName, SKTypeface.FromStream(stream));
|
||||
RegisterLibraryDefaultFonts();
|
||||
}
|
||||
|
||||
private static void RegisterFontType(SKData fontData, string? customName = null)
|
||||
{
|
||||
foreach (var index in Enumerable.Range(0, 256))
|
||||
{
|
||||
var typeface = SKTypeface.FromData(fontData, index);
|
||||
|
||||
if (typeface == null)
|
||||
break;
|
||||
|
||||
var typefaceName = customName ?? typeface.FamilyName;
|
||||
|
||||
var fontStyleSet = StyleSets.GetOrAdd(typefaceName, _ => new FontStyleSet());
|
||||
fontStyleSet.Add(typeface);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Since version 2022.8 this method has been renamed. Please use the RegisterFontWithCustomName method.")]
|
||||
public static void RegisterFontType(string fontName, Stream stream)
|
||||
{
|
||||
RegisterFontWithCustomName(fontName, stream);
|
||||
}
|
||||
|
||||
public static void RegisterFontWithCustomName(string fontName, Stream stream)
|
||||
{
|
||||
using var fontData = SKData.Create(stream);
|
||||
RegisterFontType(fontData);
|
||||
RegisterFontType(fontData, customName: fontName);
|
||||
}
|
||||
|
||||
public static void RegisterFont(Stream stream)
|
||||
{
|
||||
using var fontData = SKData.Create(stream);
|
||||
RegisterFontType(fontData);
|
||||
}
|
||||
|
||||
public static void RegisterFontFromEmbeddedResource(string pathName)
|
||||
{
|
||||
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(pathName);
|
||||
RegisterFont(stream);
|
||||
}
|
||||
|
||||
private static void RegisterLibraryDefaultFonts()
|
||||
{
|
||||
var fontFileNames = new[]
|
||||
{
|
||||
"Lato-Black.ttf",
|
||||
"Lato-BlackItalic.ttf",
|
||||
|
||||
"Lato-Bold.ttf",
|
||||
"Lato-BoldItalic.ttf",
|
||||
|
||||
"Lato-Regular.ttf",
|
||||
"Lato-Italic.ttf",
|
||||
|
||||
"Lato-Light.ttf",
|
||||
"Lato-LightItalic.ttf",
|
||||
|
||||
"Lato-Thin.ttf",
|
||||
"Lato-ThinItalic.ttf"
|
||||
};
|
||||
|
||||
fontFileNames
|
||||
.Select(x => $"QuestPDF.Resources.DefaultFont.{x}")
|
||||
.ToList()
|
||||
.ForEach(RegisterFontFromEmbeddedResource);
|
||||
}
|
||||
|
||||
internal static SKPaint ColorToPaint(this string color)
|
||||
{
|
||||
return ColorPaint.GetOrAdd(color, Convert);
|
||||
return ColorPaints.GetOrAdd(color, Convert);
|
||||
|
||||
static SKPaint Convert(string color)
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(color)
|
||||
Color = SKColor.Parse(color),
|
||||
IsAntialias = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static SKPaint ToPaint(this TextStyle style)
|
||||
{
|
||||
return Paints.GetOrAdd(style.ToString(), key => Convert(style));
|
||||
|
||||
return FontPaints.GetOrAdd(style, Convert);
|
||||
|
||||
static SKPaint Convert(TextStyle style)
|
||||
{
|
||||
return new SKPaint
|
||||
{
|
||||
Color = SKColor.Parse(style.Color),
|
||||
Typeface = GetTypeface(style),
|
||||
TextSize = style.Size,
|
||||
TextEncoding = SKTextEncoding.Utf32
|
||||
TextSize = (style.Size ?? 12) * GetTextScale(style),
|
||||
IsAntialias = true,
|
||||
};
|
||||
}
|
||||
|
||||
static SKTypeface GetTypeface(TextStyle style)
|
||||
{
|
||||
if (Typefaces.TryGetValue(style.FontType, out var result))
|
||||
return result;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
var slant = style.IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
|
||||
if (fontFromDefaultSource != null)
|
||||
return fontFromDefaultSource;
|
||||
|
||||
var availableFontNames = string.Join(", ", SKFontManager.Default.GetFontFamilies());
|
||||
|
||||
return SKTypeface.FromFamilyName(style.FontType, (int)style.FontWeight, (int)SKFontStyleWidth.Normal, slant)
|
||||
?? throw new ArgumentException($"The typeface {style.FontType} could not be found.");
|
||||
throw new ArgumentException(
|
||||
$"The typeface '{style.FontFamily}' could not be found. " +
|
||||
$"Please consider the following options: " +
|
||||
$"1) install the font on your operating system or execution environment. " +
|
||||
$"2) load a font file specifically for QuestPDF usage via the QuestPDF.Drawing.FontManager.RegisterFontType(Stream fileContentStream) static method. " +
|
||||
$"Available font family names: [{availableFontNames}]");
|
||||
}
|
||||
|
||||
static float GetTextScale(TextStyle style)
|
||||
{
|
||||
return style.FontPosition switch
|
||||
{
|
||||
FontPosition.Normal => 1f,
|
||||
FontPosition.Subscript => 0.625f,
|
||||
FontPosition.Superscript => 0.625f,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
|
||||
{
|
||||
return FontMetrics.GetOrAdd(style.ToString(), key => style.ToPaint().FontMetrics);
|
||||
return FontMetrics.GetOrAdd(style, key => key.NormalPosition().ToPaint().FontMetrics);
|
||||
}
|
||||
|
||||
internal static Font ToShaperFont(this TextStyle style)
|
||||
{
|
||||
return ShaperFonts.GetOrAdd(style, key =>
|
||||
{
|
||||
var typeface = key.ToPaint().Typeface;
|
||||
|
||||
using var harfBuzzBlob = typeface.OpenStream(out var ttcIndex).ToHarfBuzzBlob();
|
||||
|
||||
using var face = new Face(harfBuzzBlob, ttcIndex)
|
||||
{
|
||||
Index = ttcIndex,
|
||||
UnitsPerEm = typeface.UnitsPerEm,
|
||||
GlyphCount = typeface.GlyphCount
|
||||
};
|
||||
|
||||
var font = new Font(face);
|
||||
font.SetScale(TextShaper.FontShapingScale, TextShaper.FontShapingScale);
|
||||
font.SetFunctionsOpenType();
|
||||
|
||||
return font;
|
||||
});
|
||||
}
|
||||
|
||||
internal static TextShaper ToTextShaper(this TextStyle style)
|
||||
{
|
||||
return TextShapers.GetOrAdd(style, key => new TextShaper(key));
|
||||
}
|
||||
|
||||
internal static SKFont ToFont(this TextStyle style)
|
||||
{
|
||||
return Fonts.GetOrAdd(style, key => key.ToPaint().ToFont());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
internal class FontStyleSet
|
||||
{
|
||||
private ConcurrentDictionary<SKFontStyle, SKTypeface> Styles { get; } = new();
|
||||
|
||||
public void Add(SKTypeface typeface)
|
||||
{
|
||||
var style = typeface.FontStyle;
|
||||
Styles.AddOrUpdate(style, _ => typeface, (_, _) => typeface);
|
||||
}
|
||||
|
||||
public SKTypeface? Match(SKFontStyle target)
|
||||
{
|
||||
SKFontStyle? bestStyle = null;
|
||||
SKTypeface? bestTypeface = null;
|
||||
|
||||
foreach (var entry in Styles)
|
||||
{
|
||||
if (IsBetterMatch(target, entry.Key, bestStyle))
|
||||
{
|
||||
bestStyle = entry.Key;
|
||||
bestTypeface = entry.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return bestTypeface;
|
||||
}
|
||||
|
||||
private static Dictionary<SKFontStyleSlant, List<SKFontStyleSlant>> SlantFallbacks = new()
|
||||
{
|
||||
{ SKFontStyleSlant.Italic, new() { SKFontStyleSlant.Italic, SKFontStyleSlant.Oblique, SKFontStyleSlant.Upright } },
|
||||
{ SKFontStyleSlant.Oblique, new() { SKFontStyleSlant.Oblique, SKFontStyleSlant.Italic, SKFontStyleSlant.Upright } },
|
||||
{ SKFontStyleSlant.Upright, new() { SKFontStyleSlant.Upright, SKFontStyleSlant.Oblique, SKFontStyleSlant.Italic } },
|
||||
};
|
||||
|
||||
// Checks whether style a is a better match for the target then style b. Uses the CSS font style matching algorithm
|
||||
internal static bool IsBetterMatch(SKFontStyle? target, SKFontStyle? a, SKFontStyle? b)
|
||||
{
|
||||
// A font is better than no font
|
||||
if (b == null)
|
||||
return true;
|
||||
|
||||
if (a == null)
|
||||
return false;
|
||||
|
||||
// First check font width
|
||||
// For normal and condensed widths prefer smaller widths
|
||||
// For expanded widths prefer larger widths
|
||||
if (target.Width <= (int)SKFontStyleWidth.Normal)
|
||||
{
|
||||
if (a.Width <= target.Width && b.Width > target.Width)
|
||||
return true;
|
||||
|
||||
if (a.Width > target.Width && b.Width <= target.Width)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.Width >= target.Width && b.Width < target.Width)
|
||||
return true;
|
||||
|
||||
if (a.Width < target.Width && b.Width >= target.Width)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer closest match
|
||||
var widthDifferenceA = Math.Abs(a.Width - target.Width);
|
||||
var widthDifferenceB = Math.Abs(b.Width - target.Width);
|
||||
|
||||
if (widthDifferenceA < widthDifferenceB)
|
||||
return true;
|
||||
|
||||
if (widthDifferenceB < widthDifferenceA)
|
||||
return false;
|
||||
|
||||
// Prefer closest slant based on provided fallback list
|
||||
var slantFallback = SlantFallbacks[target.Slant];
|
||||
var slantIndexA = slantFallback.IndexOf(a.Slant);
|
||||
var slantIndexB = slantFallback.IndexOf(b.Slant);
|
||||
|
||||
if (slantIndexA < slantIndexB)
|
||||
return true;
|
||||
|
||||
if (slantIndexB < slantIndexA)
|
||||
return false;
|
||||
|
||||
// Check weight last
|
||||
// For thin (<400) weights, prefer thinner weights
|
||||
// For regular (400-500) weights, prefer other regular weights, then use rule for thin or bold
|
||||
// For bold (>500) weights, prefer thicker weights
|
||||
// Behavior for values other than multiples of 100 is not given in the specification
|
||||
|
||||
if (target.Weight >= 400 && target.Weight <= 500)
|
||||
{
|
||||
if ((a.Weight >= 400 && a.Weight <= 500) && !(b.Weight >= 400 && b.Weight <= 500))
|
||||
return true;
|
||||
|
||||
if (!(a.Weight >= 400 && a.Weight <= 500) && (b.Weight >= 400 && b.Weight <= 500))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.Weight < 450)
|
||||
{
|
||||
if (a.Weight <= target.Weight && b.Weight > target.Weight)
|
||||
return true;
|
||||
|
||||
if (a.Weight > target.Weight && b.Weight <= target.Weight)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.Weight >= target.Weight && b.Weight < target.Weight)
|
||||
return true;
|
||||
|
||||
if (a.Weight < target.Weight && b.Weight >= target.Weight)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer closest weight
|
||||
var weightDifferenceA = Math.Abs(a.Weight - target.Weight);
|
||||
var weightDifferenceB = Math.Abs(b.Weight - target.Weight);
|
||||
|
||||
return weightDifferenceA < weightDifferenceB;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace QuestPDF.Drawing
|
||||
|
||||
}
|
||||
|
||||
public void DrawText(string text, Position position, TextStyle style)
|
||||
public void DrawText(SKTextBlob skTextBlob, Position position, TextStyle style)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -51,17 +51,17 @@ namespace QuestPDF.Drawing
|
||||
|
||||
}
|
||||
|
||||
public void DrawExternalLink(string url, Size size)
|
||||
public void DrawHyperlink(string url, Size size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DrawLocationLink(string locationName, Size size)
|
||||
public void DrawSectionLink(string sectionName, Size size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DrawLocation(string locationName)
|
||||
public void DrawSection(string sectionName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,47 +1,31 @@
|
||||
using System.IO;
|
||||
using QuestPDF.Infrastructure;
|
||||
using System;
|
||||
using System.IO;
|
||||
using QuestPDF.Drawing.Exceptions;
|
||||
using QuestPDF.Helpers;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Drawing
|
||||
{
|
||||
internal class PdfCanvas : SkiaCanvasBase
|
||||
internal class PdfCanvas : SkiaDocumentCanvasBase
|
||||
{
|
||||
private SKDocument Document { get; }
|
||||
|
||||
public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
|
||||
{
|
||||
Document = SKDocument.CreatePdf(stream, MapMetadata(documentMetadata));
|
||||
}
|
||||
|
||||
~PdfCanvas()
|
||||
{
|
||||
Document.Dispose();
|
||||
}
|
||||
|
||||
public override void BeginDocument()
|
||||
public PdfCanvas(Stream stream, DocumentMetadata documentMetadata)
|
||||
: base(CreatePdf(stream, documentMetadata))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void EndDocument()
|
||||
private static SKDocument CreatePdf(Stream stream, DocumentMetadata documentMetadata)
|
||||
{
|
||||
Canvas?.Dispose();
|
||||
|
||||
Document?.Close();
|
||||
Document?.Dispose();
|
||||
try
|
||||
{
|
||||
return SKDocument.CreatePdf(stream, MapMetadata(documentMetadata));
|
||||
}
|
||||
catch (TypeInitializationException exception)
|
||||
{
|
||||
throw new InitializationException("PDF", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public override void BeginPage(Size size)
|
||||
{
|
||||
Canvas = Document.BeginPage(size.Width, size.Height);
|
||||
}
|
||||
|
||||
public override void EndPage()
|
||||
{
|
||||
Document.EndPage();
|
||||
Canvas.Dispose();
|
||||
}
|
||||
|
||||
private static SKDocumentPdfMetadata MapMetadata(DocumentMetadata metadata)
|
||||
{
|
||||
return new SKDocumentPdfMetadata
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace QuestPDF.Drawing.Proxy
|
||||
base.Draw(availableSpace);
|
||||
}
|
||||
|
||||
private bool IsClose(float x, float y)
|
||||
private static bool IsClose(float x, float y)
|
||||
{
|
||||
return Math.Abs(x - y) < Size.Epsilon;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
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,24 +1,34 @@
|
||||
using QuestPDF.Infrastructure;
|
||||
using System.Collections.Generic;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Drawing.Proxy
|
||||
{
|
||||
internal class DebuggingProxy : ElementProxy
|
||||
{
|
||||
private DebuggingState DebuggingState { get; }
|
||||
internal List<MeasureDetails> Measurements { get; } = new();
|
||||
|
||||
public DebuggingProxy(DebuggingState debuggingState, Element child)
|
||||
public DebuggingProxy(Element child)
|
||||
{
|
||||
DebuggingState = debuggingState;
|
||||
Child = child;
|
||||
}
|
||||
|
||||
internal override SpacePlan Measure(Size availableSpace)
|
||||
{
|
||||
DebuggingState.RegisterMeasure(Child, availableSpace);
|
||||
var spacePlan = base.Measure(availableSpace);
|
||||
DebuggingState.RegisterMeasureResult(Child, spacePlan);
|
||||
|
||||
var spacePlan = Child.Measure(availableSpace);
|
||||
Measurements.Add(new MeasureDetails(availableSpace, spacePlan));
|
||||
return spacePlan;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct MeasureDetails
|
||||
{
|
||||
public Size AvailableSpace { get; }
|
||||
public SpacePlan SpacePlan { get;}
|
||||
|
||||
public MeasureDetails(Size availableSpace, SpacePlan spacePlan)
|
||||
{
|
||||
AvailableSpace = availableSpace;
|
||||
SpacePlan = spacePlan;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
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.Wrap)
|
||||
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 IEnumerable<IElement>))
|
||||
.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() + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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() + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user