Compare commits

..

19 Commits

Author SHA1 Message Date
MarcinZiabek 4c679978be Fixes 2023-05-14 23:50:18 +02:00
MarcinZiabek 4fcd955238 Testing project adjustments 2022-12-22 00:33:11 +01:00
MarcinZiabek a9c06db2e0 Improved hierarchy extraction 2022-12-12 00:42:24 +01:00
MarcinZiabek 2f55b8d386 Document hierarchy collection fixes 2022-12-11 00:53:04 +01:00
MarcinZiabek e4a5c471ee Preview: do not collect information about simple containers 2022-12-10 16:45:03 +01:00
MarcinZiabek 4e3d462e8d Simplified document hierarchy collection 2022-12-08 23:53:16 +01:00
MarcinZiabek 423ca330f7 Improved collecting layout information when the InfiniteLayoutException is thrown 2022-12-08 23:48:17 +01:00
MarcinZiabek 57c2a8cb38 Added test project 2022-12-06 22:33:05 +01:00
MarcinZiabek 7c51649415 Fixed incompatible version notification 2022-12-06 22:32:53 +01:00
MarcinZiabek 423c630a47 Previewer ConnectionService refactoring 2022-12-06 20:54:12 +01:00
MarcinZiabek 42c6c75862 Layout debugging fixes, minor code refactoring 2022-12-01 01:11:29 +01:00
MarcinZiabek 559de4520a Improved debug pointers 2022-11-25 21:28:12 +01:00
MarcinZiabek 73e66c293b Previewer: added presence notification 2022-11-25 21:28:03 +01:00
MarcinZiabek 164bdd76d8 Added detector that checks if code is being run via unit test 2022-11-24 22:44:31 +01:00
MarcinZiabek d62cec4f66 Implemented gathering document hierarchy structure 2022-11-20 18:27:34 +01:00
MarcinZiabek baa0feb5e6 Previewer: sending details about layout overflow exception 2022-11-20 15:28:56 +01:00
MarcinZiabek 57e68e293d Communication is working, showing generic error, showing simple preview 2022-11-19 22:26:25 +01:00
MarcinZiabek 320a6e221d Deleted old previewer 2022-11-19 11:48:45 +01:00
MarcinZiabek d92231f5d4 Updated dependencies 2022-11-19 11:47:42 +01:00
356 changed files with 1914 additions and 3944 deletions
-54
View File
@@ -1,54 +0,0 @@
name: Build And Create Nuget Package
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
schedule:
- cron: '3 0 * * 0'
jobs:
main:
runs-on: ${{ matrix.environment }}
strategy:
matrix:
environment:
- macos-latest
- ubuntu-latest
- windows-latest
env:
DOTNET_NOLOGO: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
UNIT_TEST_PROJECT: QuestPDF.UnitTests/QuestPDF.UnitTests.csproj
steps:
- name: 📝 Fetch Sources 📝
uses: actions/checkout@v2
- name: ⚙ Setup .NET 6.0 SDK ⚙
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- name: 🔄 Restore Nuget Packages 🔄
shell: bash
run: dotnet restore
working-directory: ./Source
- name: 🛠 Build Solution 🛠
shell: bash
run: dotnet build -c Release --no-restore
working-directory: ./Source
- name: Upload build artifacts
uses: actions/upload-artifact@v3
if: ${{ matrix.environment == 'windows-latest' }}
with:
name: Build Package
path: |
**/*.nupkg
**/*.snupkg
!.nuget
-19
View File
@@ -1,19 +0,0 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
- 'releases/*'
jobs:
qodana:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2023.1.0
View File
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 QuestPDF
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-13
View File
@@ -1,13 +0,0 @@
# QuestPDF Community MIT License
## License Permissions
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
## Copyright
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
## Limitation Of Liability
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,29 +0,0 @@
# QuestPDF Professional And Enterprise Use License
## Do No Harm
By downloading or using the Software, the Licensee agrees not to utilize the software in a manner which is disparaging to QuestPDF, and not to rent, lease or otherwise transfer rights to the Software.
## License Permissions
Grants the use of the Software by a specified number of developers to create and deploy closed-source software for unlimited end user organizations ("The Organization") in multiple locations. This license covers unlimited applications or projects. The Software may be deployed upon any number of machines for the end-use of The Organization. This license also intrinsically covers for development, staging and production servers for each project.
Grants the use of the Software by a specified number of developers to create and deploy closed-source software for unlimited end user organizations ("The Organization") in multiple locations. This license covers unlimited applications or projects. The Software may be deployed upon any number of machines for the end-use of The Organization. This license also intrinsically covers for development, staging and production servers for each project.
## License Fees
A. If you wish to use the Software in a production environment, you may download and use the Software for one year upon payment of the appropriate license fee as indicated on the pricing page in accordance with the terms and conditions of this Agreement.
B. If you wish to use the Software in a non-production environment, you may download and access the source and/or binaries at no charge solely for testing and evaluation purposes and in accordance with all license limitations and restrictions set forth in this Agreement.
## Ownership
QuestPDF shall at all times retain ownership of the QuestPDF Software library and all subsequent copies.
## Copyright
Title, ownership rights, and intellectual property rights in and to the Software shall remain with QuestPDF. The Software is protected by the international copyright laws. Title, ownership rights, and intellectual property rights in and to the content accessed through the Software is the property of the applicable content owner and may be protected by applicable copyright or other law. This License gives you no rights to such content.
## Limitation Of Liability
THIS SOFTWARE IS PROVIDED "AS IS," WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. QUESTPDF AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL QUESTPDF OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF QUESTPDF HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-23
View File
@@ -1,23 +0,0 @@
# What License Do You Need?
## General License Overview
Open-source projects are an important foundation for the software development ecosystem. Modern applications are depending more and more upon the work of enthusiast and experts. Open-source is not anymore just sharing knowledge and passion with everybody. Over last years, maintaining and developing those projects have became a highly responsible task.
We identify the importance of the QuestPDF library in your projects, and therefore want to make sure you can safely and confidently continue the development. We strongly believe that a dual-licensing model is the best solution for all users: providing the best development experience and feature set for everybody and every project, but also offering stability for every professional and enterprise product.
Being a healthy and growing community is our primary goal that motivates us to pursue professionalism.
[Read more about exact terms](https://www.questpdf.com/pricing.html)
## QuestPDF Professional Or Enterprise Use License
If you are consuming the QuestPDF library as a Direct Package Dependency for usage in a Closed Source software in the capacity of a for-profit company/individual with more than 1M USD annual gross revenue, you must purchase the `QuestPDF Professional or Enterprise` License, depending on number of software developers. [Read more](https://www.questpdf.com/license-commercial.html)
## QuestPDF Community MIT License
We love and highly appreciate the .NET Community and therefore the vast majority of users are welcome to use the library completely for free. If you do not meet the criteria described above, you are eligible to use the `QuestPDF Community MIT` License, without any limitations, even for commercial usage. [Read more](https://www.questpdf.com/license-community.html)
If you still want to support library development, please consider purchasing the Professional License.
@@ -1,5 +1,4 @@
using System.Drawing; using System.Drawing;
using System.Globalization;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using QuestPDF.Examples.Engine; using QuestPDF.Examples.Engine;
@@ -44,7 +43,7 @@ namespace QuestPDF.Examples
.Height(50) .Height(50)
.AlignCenter() .AlignCenter()
.AlignMiddle() .AlignMiddle()
.Text(i.ToString(CultureInfo.InvariantCulture)) .Text(i)
.FontSize(16 + i / 4); .FontSize(16 + i / 4);
} }
}); });
@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using QuestPDF.Elements; using QuestPDF.Elements;
@@ -50,15 +49,15 @@ namespace QuestPDF.Examples
{ {
container.MinimalBox().Decoration(decoration => container.MinimalBox().Decoration(decoration =>
{ {
decoration.Before().Element(header); decoration.Header().Element(header);
decoration.Content().Column(column => decoration.Content().Box().Stack(stack =>
{ {
foreach (var row in rows) foreach (var row in rows)
column.Item().Element(row.Element); stack.Item().Element(row.Element);
}); });
decoration.After().Element(footer); decoration.Footer().Element(footer);
}); });
}); });
@@ -83,14 +82,15 @@ namespace QuestPDF.Examples
.BorderBottom(1) .BorderBottom(1)
.BorderColor(Colors.Grey.Darken2) .BorderColor(Colors.Grey.Darken2)
.Padding(5) .Padding(5)
.DefaultTextStyle(TextStyle.Default.SemiBold())
.Row(row => .Row(row =>
{ {
row.ConstantItem(30).Text("#"); var textStyle = TextStyle.Default.SemiBold();
row.RelativeItem().Text("Item name");
row.ConstantItem(50).AlignRight().Text("Count"); row.ConstantItem(30).Text("#", textStyle);
row.ConstantItem(50).AlignRight().Text("Price"); row.RelativeItem().Text("Item name", textStyle);
row.ConstantItem(50).AlignRight().Text("Total"); row.ConstantItem(50).AlignRight().Text("Count", textStyle);
row.ConstantItem(50).AlignRight().Text("Price", textStyle);
row.ConstantItem(50).AlignRight().Text("Total", textStyle);
}); });
}); });
} }
@@ -105,8 +105,7 @@ namespace QuestPDF.Examples
.Width(context.AvailableSize.Width) .Width(context.AvailableSize.Width)
.Padding(5) .Padding(5)
.AlignRight() .AlignRight()
.DefaultTextStyle(TextStyle.Default.FontSize(14).SemiBold()) .Text($"Subtotal: {total}$", TextStyle.Default.Size(14).SemiBold());
.Text($"Subtotal: {total}$");
}); });
} }
@@ -127,9 +126,9 @@ namespace QuestPDF.Examples
.Padding(5) .Padding(5)
.Row(row => .Row(row =>
{ {
row.ConstantItem(30).Text((index + 1).ToString(CultureInfo.InvariantCulture)); row.ConstantItem(30).Text(index + 1);
row.RelativeItem().Text(item.ItemName); row.RelativeItem().Text(item.ItemName);
row.ConstantItem(50).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture)); row.ConstantItem(50).AlignRight().Text(item.Count);
row.ConstantItem(50).AlignRight().Text($"{item.Price}$"); row.ConstantItem(50).AlignRight().Text($"{item.Price}$");
row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$"); row.ConstantItem(50).AlignRight().Text($"{item.Count*item.Price}$");
}); });
@@ -165,7 +164,7 @@ namespace QuestPDF.Examples
.Decoration(decoration => .Decoration(decoration =>
{ {
decoration decoration
.Before() .Header()
.PaddingBottom(5) .PaddingBottom(5)
.Text(text => .Text(text =>
{ {
@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Globalization;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using QuestPDF.Elements; using QuestPDF.Elements;
@@ -97,9 +96,9 @@ namespace QuestPDF.Examples
{ {
var item = Items[index]; var item = Items[index];
table.Cell().Element(Style).Text((index + 1).ToString(CultureInfo.InvariantCulture)); table.Cell().Element(Style).Text(index + 1);
table.Cell().Element(Style).Text(item.ItemName); table.Cell().Element(Style).Text(item.ItemName);
table.Cell().Element(Style).AlignRight().Text(item.Count.ToString(CultureInfo.InvariantCulture)); table.Cell().Element(Style).AlignRight().Text(item.Count);
table.Cell().Element(Style).AlignRight().Text($"{item.Price}$"); table.Cell().Element(Style).AlignRight().Text($"{item.Price}$");
table.Cell().Element(Style).AlignRight().Text($"{item.Count*item.Price}$"); table.Cell().Element(Style).AlignRight().Text($"{item.Count*item.Price}$");
@@ -13,22 +13,25 @@ namespace QuestPDF.Examples.Engine
private Action<IDocumentContainer> Content { get; } private Action<IDocumentContainer> Content { get; }
private int MaxPages { get; } private int MaxPages { get; }
private bool ApplyCaching { get; }
private bool ApplyDebugging { get; }
public SimpleDocument(Action<IDocumentContainer> content, int maxPages, bool applyCaching, bool applyDebugging) public SimpleDocument(Action<IDocumentContainer> content, int maxPages, bool applyCaching, bool applyDebugging)
{ {
Content = content; Content = content;
MaxPages = maxPages; MaxPages = maxPages;
ApplyCaching = applyCaching;
QuestPDF.Settings.EnableCaching = applyCaching; ApplyDebugging = applyDebugging;
QuestPDF.Settings.EnableDebugging = applyDebugging;
QuestPDF.Settings.DocumentLayoutExceptionThreshold = MaxPages;
} }
public DocumentSettings GetSettings() public DocumentMetadata GetMetadata()
{ {
return new DocumentSettings() return new DocumentMetadata()
{ {
ImageRasterDpi = PageSizes.PointsPerInch * ImageScalingFactor RasterDpi = PageSizes.PointsPerInch * ImageScalingFactor,
DocumentLayoutExceptionThreshold = MaxPages,
ApplyCaching = ApplyCaching,
ApplyDebugging = ApplyDebugging
}; };
} }
@@ -5,7 +5,6 @@ using QuestPDF.Drawing.Exceptions;
using QuestPDF.Examples.Engine; using QuestPDF.Examples.Engine;
using QuestPDF.Fluent; using QuestPDF.Fluent;
using QuestPDF.Helpers; using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples namespace QuestPDF.Examples
{ {
@@ -51,64 +50,6 @@ namespace QuestPDF.Examples
}); });
} }
[Test]
public void ScalingImageWithAlpha()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(page =>
{
page.Padding(25).Layers(layers =>
{
layers.Layer().Image(Placeholders.Image);
layers.PrimaryLayer().Padding(25).Image("multilingual.png");
});
});
}
[Test]
public void DpiSetting()
{
RenderingTest
.Create()
.PageSize(400, 600)
.ProduceImages()
.ShowResults()
.Render(page =>
{
page.Padding(10).Column(column =>
{
column.Spacing(10);
column.Item().Image("photo.jpg").WithRasterDpi(16);
column.Item().Image("photo.jpg").WithRasterDpi(72);
});
});
}
[Test]
public void CompressionSetting()
{
RenderingTest
.Create()
.PageSize(400, 600)
.ProduceImages()
.ShowResults()
.Render(page =>
{
page.Padding(10).Column(column =>
{
column.Spacing(10);
column.Item().Image("photo.jpg").WithCompressionQuality(ImageCompressionQuality.VeryLow).WithRasterDpi(72);
column.Item().Image("photo.jpg").WithCompressionQuality(ImageCompressionQuality.High).WithRasterDpi(72);
});
});
}
[Test] [Test]
public void Exception() public void Exception()
{ {
@@ -120,42 +120,5 @@ namespace QuestPDF.Examples
.Background(Placeholders.BackgroundColor()); .Background(Placeholders.BackgroundColor());
} }
} }
[Test]
public void RepeatingInlinedInHeader_Test()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(document =>
{
document.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(1, Unit.Inch);
page.PageColor(Colors.White);
page.Header()
.Inlined(inlined =>
{
inlined.Spacing(10);
foreach (var i in Enumerable.Range(5, 5))
inlined.Item().Width(i * 10).Height(20).Background(Colors.Red.Medium);
});
page.Content()
.PaddingVertical(20)
.Column(column =>
{
column.Spacing(25);
foreach (var i in Enumerable.Range(10, 20))
column.Item().Width(i * 10).Height(50).Background(Colors.Grey.Lighten2);
});
});
});
}
} }
} }
@@ -19,12 +19,10 @@ namespace QuestPDF.Examples
{ {
var url = "https://picsum.photos/300/200"; var url = "https://picsum.photos/300/200";
if (Greyscale) if(Greyscale)
url += "?grayscale"; url += "?grayscale";
using var client = new WebClient(); using var client = new WebClient();
client.Headers.Add("user-agent", "QuestPDF/1.0 Unit Testing");
var response = client.DownloadData(url); var response = client.DownloadData(url);
container.Image(response); container.Image(response);
} }
@@ -61,4 +59,4 @@ namespace QuestPDF.Examples
}); });
} }
} }
} }
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
@@ -9,7 +9,7 @@
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="microcharts" Version="0.9.5.9" /> <PackageReference Include="microcharts" Version="0.9.5.9" />
<PackageReference Include="nunit" Version="3.13.3" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" /> <PackageReference Include="SkiaSharp" Version="2.88.3" />
<PackageReference Include="Svg.Skia" Version="0.5.18" /> <PackageReference Include="Svg.Skia" Version="0.5.18" />
@@ -32,12 +32,6 @@
<None Update="pdf-icon.svg"> <None Update="pdf-icon.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="multilingual.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="photo.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Globalization;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using QuestPDF.Drawing; using QuestPDF.Drawing;
@@ -300,71 +299,18 @@ namespace QuestPDF.Examples
columns.RelativeColumn(); columns.RelativeColumn();
columns.RelativeColumn(); columns.RelativeColumn();
columns.RelativeColumn(); columns.RelativeColumn();
columns.RelativeColumn();
columns.RelativeColumn();
}); });
var image = Placeholders.Image(400, 300); table.Cell().RowSpan(4).Element(Block).Text("1");
table.Cell().Image(image); table.Cell().RowSpan(2).Element(Block).Text("2");
table.Cell().Image(image); table.Cell().RowSpan(1).Element(Block).Text("3");
table.Cell().Image(image); table.Cell().RowSpan(1).Element(Block).Text("4");
table.Cell().Image(image);
table.Cell().Image(image);
table.Cell().Image(image);
});
});
}
[Test]
public void Bug_RowSpanWorksIncorrectly()
{
// https://github.com/QuestPDF/QuestPDF/issues/552
RenderingTest
.Create()
.ProduceImages()
.PageSize(PageSizes.A5.Landscape())
.ShowResults()
.Render(container =>
{
container.Padding(20).Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(1);
columns.RelativeColumn(2);
columns.RelativeColumn(2);
columns.RelativeColumn(2);
});
foreach (var i in Enumerable.Range(6, 9))
{
table.Cell().Element(CellStyleMainTable).Text($"{i:00}:00");
foreach (var j in Enumerable.Range(1, 3)) table.Cell().RowSpan(2).Element(Block).Text("5");
table.Cell().Element(CellStyleMainTable).Text(""); table.Cell().RowSpan(1).Element(Block).Text("6");
} table.Cell().RowSpan(1).Element(Block).Text("7");
});
static IContainer CellStyleMainTable(IContainer container)
{
return container
.Border(0.5f).BorderColor(Colors.Blue.Lighten4)
.Background(Colors.Blue.Lighten5)
.PaddingVertical(5);
}
table.Cell().Row(1).RowSpan(3).Column(2).Element(BlockAccepted).Text("3 rows");
table.Cell().Row(1).RowSpan(6).Column(3).Element(BlockAccepted).Text("6 rows");
table.Cell().Row(3).RowSpan(5).Column(4).Element(BlockAccepted).Text("5 rows");
static IContainer BlockAccepted(IContainer container)
{
return container
.Border(1.5f).BorderColor(Colors.Green.Lighten2)
.Background(Colors.Green.Lighten4);
}
});
}); });
} }
@@ -443,12 +389,12 @@ namespace QuestPDF.Examples
table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name); table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
// inches // inches
table.Cell().Element(CellStyle).Text(page.width.ToString(CultureInfo.InvariantCulture)); table.Cell().Element(CellStyle).Text(page.width);
table.Cell().Element(CellStyle).Text(page.height.ToString(CultureInfo.InvariantCulture)); table.Cell().Element(CellStyle).Text(page.height);
// points // points
table.Cell().Element(CellStyle).Text((page.width * inchesToPoints).ToString(CultureInfo.InvariantCulture)); table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
table.Cell().Element(CellStyle).Text((page.height * inchesToPoints).ToString(CultureInfo.InvariantCulture)); table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White); IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White);
} }
@@ -7,7 +7,6 @@ using QuestPDF.Examples.Engine;
using QuestPDF.Fluent; using QuestPDF.Fluent;
using QuestPDF.Helpers; using QuestPDF.Helpers;
using QuestPDF.Infrastructure; using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.Examples namespace QuestPDF.Examples
{ {
@@ -123,132 +122,7 @@ namespace QuestPDF.Examples
}); });
}); });
} }
[Test]
public void LetterSpacing()
{
RenderingTest
.Create()
.PageSize(500, 700)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Column(column =>
{
var letterSpacing = new[] { -0.1f, 0f, 0.2f };
var paragraph = Placeholders.Sentence().ToUpper();
foreach (var spacing in letterSpacing)
{
column
.Item()
.Border(1)
.Padding(10)
.Column(nestedColumn =>
{
nestedColumn.Item()
.Text(paragraph)
.FontSize(16)
.LetterSpacing(spacing);
nestedColumn.Item()
.Text($"Letter spacing of {spacing} em")
.FontSize(10)
.Italic()
.FontColor(Colors.Blue.Medium);
});
}
});
});
}
[Test]
public void LetterSpacing_Arabic()
{
RenderingTest
.Create()
.PageSize(500, 700)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(50)
.Column(column =>
{
var letterSpacing = new[] { -0.1f, 0f, 0.2f };
var paragraph = "ينا الألم. في بعض الأحيان ونظراً للالتزامات التي يفرضها علينا";
foreach (var spacing in letterSpacing)
{
column
.Item()
.Border(1)
.Padding(10)
.Column(nestedColumn =>
{
nestedColumn.Item()
.Text(paragraph)
.FontSize(16)
.FontFamily(Fonts.Calibri)
.LetterSpacing(spacing);
nestedColumn.Item()
.Text($"Letter spacing of {spacing} em")
.FontSize(10)
.Italic()
.FontColor(Colors.Blue.Medium);
});
}
});
});
}
[Test]
public void LetterSpacing_Unicode()
{
RenderingTest
.Create()
.PageSize(500, 700)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(50)
.Column(column =>
{
var letterSpacing = new[] { 0f, 0.5f };
var paragraph = "Ţ̴̡̧̤̮̺̤̗͎̱̹͙͎͖͂̿̓́̉̊̀̍͜h̵̞̘͇̾̎̏̅į̵̹̖͔͉̰̎̉̄̐̏͑͂̅̃̃͘͝s̷͓͉̭̭̯̬̥̻̰̩̦̑̀̀͌́̒̍̒̌̇͛̀͛́̎ ̷̡̡̟͕̳̺̝̼͇͔̬̟̖͍̈́̽͜͝͝i̶͔͚̟̊̐͛́͛̄̌ṡ̸̡̤̪͙͍̥͙̟̼̝̰̥͈̿̓̄̿̓͠ ̶̢̦̙͍̯̖̱̰̯͕͔͎̯̝̎͑t̸͖̲̱̼̎͐̎̉̾̎̾̌̅̔̏͘ȩ̶̝̫̙͓̙̣̔̀̌̔̋̂̑̈́̏̀̈͘̕͜͝s̸̫̝̮̻̼͐̅̄̎̎̑͝ț̷̨̢̨̻͈̮̞̆͗̓͊̃̌͂̑̉̕̕͜͝͝";
foreach (var spacing in letterSpacing)
{
column.Item()
.Text($"Letter spacing of {spacing} em")
.FontSize(10)
.Italic()
.FontColor(Colors.Blue.Medium);
column.Item()
.PaddingVertical(50)
.Text(paragraph)
.FontSize(16)
.FontFamily(Fonts.Calibri)
.LetterSpacing(spacing);
}
});
});
}
[Test] [Test]
public void SuperscriptSubscript_Simple() public void SuperscriptSubscript_Simple()
{ {
@@ -649,7 +523,7 @@ namespace QuestPDF.Examples
page.Content().Column(column => page.Content().Column(column =>
{ {
column.Item().Text((string) null); column.Item().Text(null);
column.Item().Text(text => column.Item().Text(text =>
{ {
@@ -681,7 +555,7 @@ namespace QuestPDF.Examples
page.Content().Column(column => page.Content().Column(column =>
{ {
column.Item().Text((string) null); column.Item().Text(null);
column.Item().Text(text => column.Item().Text(text =>
{ {
@@ -912,132 +786,5 @@ namespace QuestPDF.Examples
}); });
}); });
} }
[Test]
public void DetectSpanPositionExample()
{
RenderingTest
.Create()
.PageSize(new PageSize(650, 800))
.ProduceImages()
.ShowResults()
.Render(container =>
{
var fontSize = 20;
var paint = new SKPaint
{
Color = SKColors.Red,
TextSize = fontSize
};
var fontMetrics = paint.FontMetrics;
var start = 0f;
var end = 0f;
// corner case: what if text is paged? clamp start and end?
container
.Padding(25)
.DefaultTextStyle(x => x.FontSize(fontSize).FontFamily("Calibri"))
.Layers(layers =>
{
layers.PrimaryLayer().Text(text =>
{
text.Span(Placeholders.Paragraph());
text.Span(" - ");
// record start
text.Element().Width(0).Height(0)
.Canvas((canvas, size) => start = canvas.TotalMatrix.TransY / canvas.TotalMatrix.ScaleY);
text.Span(Placeholders.LoremIpsum()).BackgroundColor(Colors.Red.Lighten4);
// record end
text.Element().Width(0).Height(0)
.Canvas((canvas, size) => end = canvas.TotalMatrix.TransY / canvas.TotalMatrix.ScaleY);
text.Span(" - ");
text.Span(Placeholders.Paragraph());
});
layers.Layer().Canvas((canvas, size) =>
{
canvas.Save();
canvas.Translate(-canvas.TotalMatrix.TransX / canvas.TotalMatrix.ScaleX, -canvas.TotalMatrix.TransY / canvas.TotalMatrix.ScaleY);
canvas.DrawRect(10, start + fontMetrics.Ascent, 5, end - start + (fontMetrics.Bottom - fontMetrics.Ascent), paint);
canvas.Restore();
});
});
});
}
[Test]
public void InconsistentLineHeightWhenUsingNewLineTest()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Background(Colors.Grey.Lighten4)
.Text(text =>
{
text.DefaultTextStyle(x => x.FontSize(16));
text.Line(Placeholders.Paragraph());
text.Line("");
text.Line(Placeholders.Paragraph());
text.Line(Placeholders.Label()).FontSize(48);
text.Line(Placeholders.Paragraph());
text.Line("");
text.Line(Placeholders.Paragraph());
});
});
}
[Test]
public void FontFallback_Nested()
{
RenderingTest
.Create()
.ProduceImages()
.ShowResults()
.RenderDocument(container =>
{
container.Page(page =>
{
page.Margin(50);
page.PageColor(Colors.White);
page.Size(PageSizes.A5.Landscape());
page.DefaultTextStyle(x => x
.FontSize(24)
.Bold()
.FontFamily("Times New Roman")
.Fallback(y => y
.FontFamily("Microsoft YaHei")
.Underline()
.BackgroundColor(Colors.Red.Lighten2)));
page.Content().Text(text =>
{
text.Line("Default times new roman 中文文本 text.");
text.Line("Normal weight and green 中文文本 text.").NormalWeight().BackgroundColor(Colors.Green.Lighten2);
text.Line("Strikethrough without underline 中文文本 text.").Strikethrough().Underline(false);
text.Line("Lato italic 中文文本 text.").FontFamily("Lato").Italic();
});
});
});
}
} }
} }

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -31,7 +31,7 @@ namespace QuestPDF.ReportSample
public static string FormatAsRomanNumeral(this int number) public static string FormatAsRomanNumeral(this int number)
{ {
if (number < 0 || number > 3999) if (number < 0 || number > 3999)
throw new ArgumentOutOfRangeException(nameof(number), "Number should be in range from 1 to 3999"); throw new ArgumentOutOfRangeException("Number should be in range from 1 to 3999");
return RomanNumeralCache.GetOrAdd(number, x => return RomanNumeralCache.GetOrAdd(number, x =>
{ {
@@ -83,6 +83,8 @@ namespace QuestPDF.ReportSample.Layouts
.Range(0, model.PhotoCount) .Range(0, model.PhotoCount)
.ToList() .ToList()
.ForEach(x => grid.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>()); .ForEach(x => grid.Item().AspectRatio(4 / 3f).Component<ImagePlaceholder>());
//grid.Item().AspectRatio(4 / 3f).Width(100000).Component<ImagePlaceholder>();
}); });
} }
} }
+75
View File
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Running;
using Microsoft.VisualStudio.TestPlatform.TestHost;
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: 256)]
[MinColumn, MaxColumn, MeanColumn, MedianColumn]
public class PerformanceTests
{
private PageContext PageContext { get; set; }
private DocumentMetadata Metadata { get; set; }
private Container Content { get; set; }
[Test]
public void Run()
{
ImagePlaceholder.Solid = true;
var configuration = ManualConfig
.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator);
BenchmarkRunner.Run<PerformanceTests>(configuration);
}
[IterationSetup]
public void GenerateReportData()
{
ImagePlaceholder.Solid = true;
var model = DataSource.GetReport();
var report = new StandardReport(model);
Metadata = report.GetMetadata();
var documentContainer = new DocumentContainer();
report.Compose(documentContainer);
Content = documentContainer.Compose();
PageContext = new PageContext();
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content);
var sw = new Stopwatch();
sw.Start();
Content.VisitChildren(x =>
{
if (x is ICacheable)
x.CreateProxy(y => new CacheProxy(y));
});
sw.Stop();
Console.WriteLine($"Creating cache took: {sw.ElapsedMilliseconds}");
}
[Benchmark]
public void GenerationTest()
{
DocumentGenerator.RenderPass(PageContext, new FreeCanvas(), Content);
}
}
}
@@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="nunit" Version="3.13.2" /> <PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" /> <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" /> <PackageReference Include="SkiaSharp" Version="2.88.3" />

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 993 B

After

Width:  |  Height:  |  Size: 993 B

+60
View File
@@ -0,0 +1,60 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using QuestPDF.Previewer;
using QuestPDF.ReportSample.Layouts;
namespace QuestPDF.ReportSample
{
public class ReportGeneration
{
private StandardReport Report { get; set; }
[SetUp]
public void SetUp()
{
var model = DataSource.GetReport();
Report = new StandardReport(model);
}
[Test]
public void GenerateAndShowPdf()
{
Report.ShowInPreviewer();
//ImagePlaceholder.Solid = true;
//
// var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
// Report.GeneratePdf(path);
// Process.Start("explorer.exe", path);
}
[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 pageContext = new PageContext();
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content);
DocumentGenerator.RenderPass(pageContext, new FreeCanvas(), content);
}
}
}
@@ -86,43 +86,5 @@ namespace QuestPDF.UnitTests
.ExpectCanvasTranslate(new Position(-300, 0)) .ExpectCanvasTranslate(new Position(-300, 0))
.CheckDrawResult(); .CheckDrawResult();
} }
[Test]
public void Draw_HorizontalCenter_VerticalNone()
{
TestPlan
.For(x => new Alignment
{
Horizontal = HorizontalAlignment.Center,
Vertical = null,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(150, 0))
.ExpectChildDraw(new Size(100, 300))
.ExpectCanvasTranslate(new Position(-150, 0))
.CheckDrawResult();
}
[Test]
public void Draw_HorizontalNone_VerticalMiddle()
{
TestPlan
.For(x => new Alignment
{
Horizontal = null,
Vertical = VerticalAlignment.Middle,
Child = x.CreateChild()
})
.DrawElement(new Size(400, 300))
.ExpectChildMeasure(expectedInput: new Size(400, 300), returns: SpacePlan.FullRender(new Size(100, 50)))
.ExpectCanvasTranslate(new Position(0, 125))
.ExpectChildDraw(new Size(400, 50))
.ExpectCanvasTranslate(new Position(0, -125))
.CheckDrawResult();
}
} }
} }
@@ -17,8 +17,6 @@ namespace QuestPDF.UnitTests
TestPlan TestPlan
.For(x => new DynamicImage .For(x => new DynamicImage
{ {
TargetDpi = DocumentSettings.DefaultRasterDpi,
CompressionQuality = ImageCompressionQuality.High,
Source = GenerateImage Source = GenerateImage
}) })
.MeasureElement(new Size(300, 200)) .MeasureElement(new Size(300, 200))
@@ -31,8 +29,6 @@ namespace QuestPDF.UnitTests
TestPlan TestPlan
.For(x => new DynamicImage .For(x => new DynamicImage
{ {
TargetDpi = DocumentSettings.DefaultRasterDpi,
CompressionQuality = ImageCompressionQuality.High,
Source = size => null Source = size => null
}) })
.DrawElement(new Size(300, 200)) .DrawElement(new Size(300, 200))
@@ -45,8 +41,6 @@ namespace QuestPDF.UnitTests
TestPlan TestPlan
.For(x => new DynamicImage .For(x => new DynamicImage
{ {
TargetDpi = DocumentSettings.DefaultRasterDpi,
CompressionQuality = ImageCompressionQuality.High,
Source = GenerateImage Source = GenerateImage
}) })
.DrawElement(new Size(300, 200)) .DrawElement(new Size(300, 200))
@@ -57,13 +51,11 @@ namespace QuestPDF.UnitTests
[Test] [Test]
public void Draw_PassesCorrectSizeToSource() public void Draw_PassesCorrectSizeToSource()
{ {
ImageSize passedSize = default; Size passedSize = default;
TestPlan TestPlan
.For(x => new DynamicImage .For(x => new DynamicImage
{ {
TargetDpi = DocumentSettings.DefaultRasterDpi * 3,
CompressionQuality = ImageCompressionQuality.High,
Source = size => Source = size =>
{ {
passedSize = size; passedSize = size;
@@ -74,13 +66,12 @@ namespace QuestPDF.UnitTests
.ExpectCanvasDrawImage(Position.Zero, new Size(400, 300)) .ExpectCanvasDrawImage(Position.Zero, new Size(400, 300))
.CheckDrawResult(); .CheckDrawResult();
passedSize.Width.Should().Be(1200); passedSize.Should().BeEquivalentTo(new Size(400, 300));
passedSize.Height.Should().Be(900);
} }
byte[] GenerateImage(ImageSize size) byte[] GenerateImage(Size size)
{ {
var image = GenerateImage(size.Width, size.Height); var image = GenerateImage((int) size.Width, (int) size.Height);
return image.Encode(SKEncodedImageFormat.Png, 100).ToArray(); return image.Encode(SKEncodedImageFormat.Png, 100).ToArray();
} }
+62
View File
@@ -0,0 +1,62 @@
using NUnit.Framework;
using QuestPDF.Drawing;
using QuestPDF.Elements;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine;
using SkiaSharp;
namespace QuestPDF.UnitTests
{
[TestFixture]
public class ImageTests
{
[Test]
public void Measure_TakesAvailableSpaceRegardlessOfSize()
{
TestPlan
.For(x => new Image
{
InternalImage = GenerateImage(400, 300)
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(SpacePlan.FullRender(300, 200));
}
[Test]
public void Draw_TakesAvailableSpaceRegardlessOfSize()
{
TestPlan
.For(x => new Image
{
InternalImage = GenerateImage(400, 300)
})
.DrawElement(new Size(300, 200))
.ExpectCanvasDrawImage(new Position(0, 0), new Size(300, 200))
.CheckDrawResult();
}
[Test]
public void Fluent_RecognizesImageProportions()
{
var image = GenerateImage(600, 200).Encode(SKEncodedImageFormat.Png, 100).ToArray();
TestPlan
.For(x =>
{
var container = new Container();
container.Image(image);
return container;
})
.MeasureElement(new Size(300, 200))
.CheckMeasureResult(SpacePlan.FullRender(300, 100));;
}
SKImage GenerateImage(int width, int height)
{
var imageInfo = new SKImageInfo(width, height);
using var surface = SKSurface.Create(imageInfo);
return surface.Snapshot();
}
}
}
+54
View File
@@ -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();
}
}
}
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.8.0" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
</Project>

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