Compare commits

..

1 Commits

Author SHA1 Message Date
MarcinZiabek 8551e1f33f Basic previewer implementation 2022-03-19 03:08:37 +01:00
331 changed files with 2076 additions and 7343 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
-1
View File
@@ -1 +0,0 @@
github: QuestPDF
View File
@@ -1,5 +1,4 @@
using System.Drawing;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
@@ -44,7 +43,7 @@ namespace QuestPDF.Examples
.Height(50)
.AlignCenter()
.AlignMiddle()
.Text(i.ToString(CultureInfo.InvariantCulture))
.Text(i)
.FontSize(16 + i / 4);
}
});
+51
View File
@@ -0,0 +1,51 @@
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 Exception()
{
Assert.Throws<DocumentComposeException>(() =>
{
RenderingTest
.Create()
.PageSize(PageSizes.A2)
.ProducePdf()
.ShowResults()
.Render(page => page.Image("non_existent.png"));
});
}
}
}
@@ -120,42 +120,5 @@ namespace QuestPDF.Examples
.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";
if (Greyscale)
if(Greyscale)
url += "?grayscale";
using var client = new WebClient();
client.Headers.Add("user-agent", "QuestPDF/1.0 Unit Testing");
var response = client.DownloadData(url);
container.Image(response);
}
@@ -61,4 +59,4 @@ namespace QuestPDF.Examples
});
}
}
}
}
@@ -6,13 +6,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="microcharts" Version="0.9.5.9" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
<PackageReference Include="Svg.Skia" Version="0.5.18" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
<PackageReference Include="Svg.Skia" Version="0.5.10" />
</ItemGroup>
<ItemGroup>
@@ -105,41 +105,5 @@ namespace QuestPDF.Examples
.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());
}
});
});
}
}
}
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using NUnit.Framework;
using QuestPDF.Drawing;
@@ -300,71 +299,18 @@ namespace QuestPDF.Examples
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().Image(image);
table.Cell().Image(image);
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");
table.Cell().RowSpan(2).Element(Block).Text("2");
table.Cell().RowSpan(1).Element(Block).Text("3");
table.Cell().RowSpan(1).Element(Block).Text("4");
foreach (var j in Enumerable.Range(1, 3))
table.Cell().Element(CellStyleMainTable).Text("");
}
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);
}
});
table.Cell().RowSpan(2).Element(Block).Text("5");
table.Cell().RowSpan(1).Element(Block).Text("6");
table.Cell().RowSpan(1).Element(Block).Text("7");
});
});
}
@@ -443,12 +389,12 @@ namespace QuestPDF.Examples
table.Cell().Element(CellStyle).ExtendHorizontal().AlignLeft().Text(page.name);
// inches
table.Cell().Element(CellStyle).Text(page.width.ToString(CultureInfo.InvariantCulture));
table.Cell().Element(CellStyle).Text(page.height.ToString(CultureInfo.InvariantCulture));
table.Cell().Element(CellStyle).Text(page.width);
table.Cell().Element(CellStyle).Text(page.height);
// points
table.Cell().Element(CellStyle).Text((page.width * inchesToPoints).ToString(CultureInfo.InvariantCulture));
table.Cell().Element(CellStyle).Text((page.height * inchesToPoints).ToString(CultureInfo.InvariantCulture));
table.Cell().Element(CellStyle).Text(page.width * inchesToPoints);
table.Cell().Element(CellStyle).Text(page.height * inchesToPoints);
IContainer CellStyle(IContainer container) => DefaultCellStyle(container, Colors.White);
}
@@ -31,7 +31,7 @@ namespace QuestPDF.Examples
{
var chapters = GetBookChapters().ToList();
var results = PerformTest(128).ToList();
var results = PerformTest(16).ToList();
Console.WriteLine($"Min: {results.Min():F}");
Console.WriteLine($"Max: {results.Max():F}");
@@ -111,7 +111,7 @@ namespace QuestPDF.Examples
{
page.Margin(50);
page.Content().PaddingVertical(10).Column(column =>
page.Content().Column(column =>
{
column.Item().Element(Title);
column.Item().PageBreak();
+375
View File
@@ -0,0 +1,375 @@
using System;
using System.Linq;
using System.Text;
using NUnit.Framework;
using QuestPDF.Examples.Engine;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Examples
{
public class TextExamples
{
[Test]
public void SimpleTextBlock()
{
RenderingTest
.Create()
.PageSize(500, 300)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(5)
.MinimalBox()
.Border(1)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.Span("This is a normal text, followed by an ");
text.Span("underlined red text").FontColor(Colors.Red.Medium).Underline();
text.Span(".");
});
});
}
[Test]
public void ParagraphSpacing()
{
RenderingTest
.Create()
.PageSize(500, 300)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(5)
.MinimalBox()
.Border(1)
.Padding(10)
.Text(text =>
{
text.ParagraphSpacing(10);
foreach (var i in Enumerable.Range(1, 3))
{
text.Span($"Paragraph {i}: ").SemiBold();
text.Line(Placeholders.Paragraph());
}
});
});
}
[Test]
public void CustomElement()
{
RenderingTest
.Create()
.PageSize(500, 200)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(5)
.MinimalBox()
.Border(1)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.Span("This is a random image aligned to the baseline: ");
text.Element()
.PaddingBottom(-6)
.Height(24)
.Width(48)
.Image(Placeholders.Image);
text.Span(".");
});
});
}
[Test]
public void TextElements()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Border(1)
.Padding(5)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default);
text.AlignLeft();
text.ParagraphSpacing(10);
text.Line(Placeholders.LoremIpsum());
text.Span($"This is target text that should show up. {DateTime.UtcNow:T} > This is a short sentence that will be wrapped into second line hopefully, right? <").Underline();
});
});
}
[Test]
public void Textcolumn()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Border(1)
.Padding(5)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default);
text.AlignLeft();
text.ParagraphSpacing(10);
foreach (var i in Enumerable.Range(1, 100))
text.Line($"{i}: {Placeholders.Paragraph()}");
});
});
}
[Test]
public void SpaceIssue()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Border(1)
.Padding(5)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(x => x.Bold());
text.DefaultTextStyle(TextStyle.Default);
text.AlignLeft();
text.ParagraphSpacing(10);
text.Span(Placeholders.LoremIpsum());
text.EmptyLine();
text.Span("This text is a normal text, ");
text.Span("this is a bold text, ").Bold();
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
text.Span("and this is slightly bigger text.").FontSize(16);
text.EmptyLine();
text.Span("The new text element also supports injecting custom content between words: ");
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
text.Span(".");
text.EmptyLine();
text.Span("This is page number ");
text.CurrentPageNumber();
text.Span(" out of ");
text.TotalPages();
text.EmptyLine();
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
text.EmptyLine();
text.Span(Placeholders.Paragraphs());
text.EmptyLine();
text.Span(Placeholders.Paragraphs()).Italic();
text.Line("This is target text that does not show up. " + Placeholders.Paragraph());
});
});
}
[Test]
public void HugeList()
{
RenderingTest
.Create()
.PageSize(PageSizes.A4)
.ProducePdf()
.ShowResults()
.Render(container =>
{
container
.Padding(20)
.Padding(10)
.MinimalBox()
.Border(1)
.Padding(5)
.Padding(10)
.Text(text =>
{
text.DefaultTextStyle(TextStyle.Default.FontSize(20));
text.AlignLeft();
text.ParagraphSpacing(10);
text.Span("This text is a normal text, ");
text.Span("this is a bold text, ").Bold();
text.Span("this is a red and underlined text, ").FontColor(Colors.Red.Medium).Underline();
text.Span("and this is slightly bigger text.").FontSize(16);
text.Span("The new text element also supports injecting custom content between words: ");
text.Element().PaddingBottom(-4).Height(16).Width(32).Image(Placeholders.Image);
text.Span(".");
text.EmptyLine();
foreach (var i in Enumerable.Range(1, 100))
{
text.Line($"{i}: {Placeholders.Paragraph()}");
text.Hyperlink("Please visit QuestPDF website", "https://www.questpdf.com");
text.Span("This is page number ");
text.CurrentPageNumber();
text.Span(" out of ");
text.TotalPages();
text.EmptyLine();
}
});
});
}
[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);
});
});
});
});
}
}
}

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

+80
View File
@@ -0,0 +1,80 @@
using System.Diagnostics;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using QuestPDF.Previewer;
Document
.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.PageColor(Colors.White);
page.DefaultTextStyle(x => x.FontSize(14));
page.Header()
.Text("Hello PDF!")
.SemiBold().FontSize(36).FontColor(Colors.Blue.Medium);
page.Content()
.PaddingVertical(1, Unit.Centimetre)
.Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.ConstantColumn(30);
columns.RelativeColumn();
columns.ConstantColumn(75);
columns.ConstantColumn(75);
columns.ConstantColumn(75);
});
table.Header(header =>
{
var textStyle = TextStyle.Default.SemiBold();
header.Cell().Element(DefaultCellStyle).Text("#").Style(textStyle);
header.Cell().Element(DefaultCellStyle).Text("Item name").Style(textStyle);
header.Cell().Element(DefaultCellStyle).AlignRight().Text("Price").Style(textStyle);
header.Cell().Element(DefaultCellStyle).AlignRight().Text("Count").Style(textStyle);
header.Cell().Element(DefaultCellStyle).AlignRight().Text("Total").Style(textStyle);
static IContainer DefaultCellStyle(IContainer container)
{
return container.PaddingBottom(5).BorderBottom(1).PaddingBottom(5);
}
});
foreach(var i in Enumerable.Range(1, 100))
{
var price = Placeholders.Random.Next(100, 999) / 100f;
var count = Placeholders.Random.Next(1, 10);
var total = price * count;
table.Cell().Element(DefaultCellStyle).Text(i);
table.Cell().Element(DefaultCellStyle).Text(Placeholders.Label());
table.Cell().Element(DefaultCellStyle).AlignRight().Text($"{price:N2} $");
table.Cell().Element(DefaultCellStyle).AlignRight().Text(count);
table.Cell().Element(DefaultCellStyle).AlignRight().Text($"{total:N2} $");
static IContainer DefaultCellStyle(IContainer container)
{
return container.PaddingBottom(5).BorderBottom(1).BorderColor(Colors.Grey.Lighten1).PaddingBottom(5);
}
}
});
page.Footer()
.AlignCenter()
.Text(x =>
{
x.Span("Page ");
x.CurrentPageNumber();
x.Span(" of ");
x.TotalPages();
});
});
})
.ShowInPreviewer();
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF.Previewer\QuestPDF.Previewer.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="HotReloadManager.cs" />
</ItemGroup>
</Project>
@@ -0,0 +1,125 @@
using System.Diagnostics;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
namespace QuestPDF.Previewer;
public static class DocumentPreviewerExtensions
{
public static void ShowInPreviewer(this IDocument document)
{
ArgumentNullException.ThrowIfNull(document);
var builder = WebApplication.CreateBuilder();
var app = builder.Build();
var pdfDocumentCache = GeneratePdf(document);
var refreshFlag = false;
static byte[] GenerateDocumentAboutException(Exception exception)
{
return Document.Create(document =>
{
document.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(1, Unit.Inch);
page.PageColor(Colors.Red.Lighten4);
page.DefaultTextStyle(x => x.FontSize(16));
page.Header()
.BorderBottom(2)
.BorderColor(Colors.Red.Medium)
.PaddingBottom(5)
.Text("Ooops! Something went wrong...").FontSize(28).FontColor(Colors.Red.Medium).Bold();
page.Content().PaddingVertical(20).Column(column =>
{
var currentException = exception;
while (currentException != null)
{
column.Item().Text(exception.GetType().Name).FontSize(20).SemiBold();
column.Item().Text(exception.Message).FontSize(14);
column.Item().PaddingTop(10).Text(exception.StackTrace).FontSize(10).Light();
currentException = currentException.InnerException;
if (currentException != null)
column.Item().PaddingVertical(15).LineHorizontal(2).LineColor(Colors.Red.Medium);
}
});
});
}).GeneratePdf();
}
static byte[] GeneratePdf(IDocument document)
{
try
{
return document.GeneratePdf();
}
catch(Exception exception)
{
return GenerateDocumentAboutException(exception);
}
}
HotReloadManager.OnApplicationChanged += () =>
{
pdfDocumentCache = GeneratePdf(document);
refreshFlag = true;
};
app.MapGet("/", () =>
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "QuestPDF.Previewer.index.html";
using var stream = assembly.GetManifestResourceStream(resourceName);
using var reader = new StreamReader(stream);
var result = reader.ReadToEnd();
return Results.Content(result, "text/html");
});
app.MapGet("/render", () =>
{
refreshFlag = false;
return Results.File(pdfDocumentCache, "application/pdf");
});
app.MapGet("/listen", async () =>
{
foreach (var i in Enumerable.Range(0, 1000))
{
await Task.Delay(TimeSpan.FromMilliseconds(100));
if (!refreshFlag)
continue;
return Results.Text("true");
}
return Results.Text("false");
});
app.Lifetime.ApplicationStarted.Register(() =>
{
var openBrowserProcess = new Process()
{
StartInfo = new()
{
UseShellExecute = true,
FileName = app.Urls.First()
}
};
openBrowserProcess.Start();
});
app.Run();
}
}
+9
View File
@@ -0,0 +1,9 @@
using System.Reflection.Metadata;
[assembly: MetadataUpdateHandler(typeof(HotReloadManager))]
internal static class HotReloadManager
{
public static event Action? OnApplicationChanged;
public static void UpdateApplication(Type[]? _) => OnApplicationChanged?.Invoke();
}
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
<ItemGroup>
<None Remove="index.html" />
<EmbeddedResource Include="index.html" />
</ItemGroup>
<ItemGroup>
<Content Remove="appsettings.Development.json" />
<Content Remove="appsettings.json" />
</ItemGroup>
</Project>
+117
View File
@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>QuestPDF Previewer</title>
<style>
html {
background-color: #333;
}
body {
margin: 0;
}
embed {
border: none;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.alert {
position: absolute;
top: 0;
left: 0;
right: 0;
width: 100%;
padding: 8px 16px;
color: #FFFA;
font-family: sans-serif;
font-size: 14px;
line-height: 1.5;
box-shadow: 0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12) !important;
}
#recommended-alert {
background-color: #616161;
}
#recommended-alert .close {
color: #FFF;
cursor: pointer;
}
#connection-issue-alert {
visibility: hidden;
background-color: #B71C1C;
color: white;
}
</style>
</head>
<body>
<embed id="previewer" src="/render#toolbar=0&scrollbar=0" />
<div id="recommended-alert" class="alert">
The recommended browser for this previewer is Microsoft Edge. It does preserve document scroll position when refreshing document, giving better overall experience.
<a class="close" onclick="closeRecommendation()">Click to close</a>
</div>
<div id="connection-issue-alert" class="alert">
Cannot connect to QuestPDF previewer host. Possibly your debugging session has ended. Please close this browser.
</div>
<script>
const recommendationAlert = document.getElementById("recommended-alert");
const connectionIssueAlert = document.getElementById("connection-issue-alert");
function closeRecommendation() {
recommendationAlert.parentElement.removeChild(recommendationAlert);
localStorage.setItem("show-recommendation-alert", 'false');
}
function refreshRecommendation() {
const showRecommendation = localStorage.getItem("show-recommendation-alert");
if (showRecommendation !== 'false')
return;
recommendationAlert.parentElement.removeChild(recommendationAlert);
}
function changeConnectionIssueAlertVisibility(show) {
connectionIssueAlert.style.setProperty("visibility", show ? "visible" : "hidden");
}
async function refreshDocumentLoop() {
window.addEventListener("change", () => reloading = true);
while (true) {
try {
const response = await fetch('/listen');
const value = await response.text();
if (value === "true")
window.location.reload();
changeConnectionIssueAlertVisibility(false);
}
catch {
changeConnectionIssueAlertVisibility(true);
}
}
}
refreshRecommendation();
refreshDocumentLoop();
</script>
</body>
</html>
@@ -31,7 +31,7 @@ namespace QuestPDF.ReportSample
public static string FormatAsRomanNumeral(this int number)
{
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 =>
{
@@ -1,4 +1,3 @@
using System;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
@@ -48,7 +47,7 @@ namespace QuestPDF.ReportSample.Layouts
container.Border(0.75f).BorderColor(Colors.Grey.Medium).Grid(grid =>
{
grid.Columns(6);
grid.Item().LabelCell().Text("Date");
grid.Item(2).ValueCell().Text(Model.Date?.ToString("g") ?? string.Empty);
grid.Item().LabelCell().Text("Location");
@@ -55,7 +55,7 @@ namespace QuestPDF.ReportSample.Layouts
row.Spacing(50);
row.RelativeItem().PaddingTop(-10).Text(Model.Title).Style(Typography.Title);
row.ConstantItem(90).Hyperlink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
row.ConstantItem(90).ExternalLink("https://www.questpdf.com").MaxHeight(30).Component<ImagePlaceholder>();
});
column.Item().ShowOnce().PaddingVertical(15).Border(1f).BorderColor(Colors.Grey.Lighten1).ExtendHorizontal();
@@ -88,14 +88,14 @@ namespace QuestPDF.ReportSample.Layouts
column.Item().PageBreak();
foreach (var section in Model.Sections)
column.Item().Section(section.Title).Component(new SectionTemplate(section));
column.Item().Location(section.Title).Component(new SectionTemplate(section));
column.Item().PageBreak();
column.Item().Section("Photos");
column.Item().Location("Photos");
foreach (var photo in Model.Photos)
column.Item().Component(new PhotoTemplate(photo));
});
}
}
}
}
@@ -3,7 +3,6 @@ using System.Drawing;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SkiaSharp;
namespace QuestPDF.ReportSample.Layouts
{
@@ -42,39 +41,24 @@ namespace QuestPDF.ReportSample.Layouts
private void DrawLink(IContainer container, int number, string locationName)
{
container
.SectionLink(locationName)
.InternalLink(locationName)
.Row(row =>
{
row.ConstantItem(20).Text($"{number}.");
row.AutoItem().Text(locationName);
row.RelativeItem().PaddingHorizontal(2).AlignBottom().TranslateY(-3).Height(1).Canvas((canvas, space) =>
{
// best to statically cache
using var paint = new SKPaint
{
StrokeWidth = space.Height,
PathEffect = SKPathEffect.CreateDash(new float[] { 1, 3 }, 0)
};
canvas.DrawLine(0, 0, space.Width, 0, paint);
});
row.AutoItem().Text(text =>
row.ConstantItem(25).Text($"{number}.");
row.RelativeItem().Text(locationName);
row.ConstantItem(150).AlignRight().Text(text =>
{
text.BeginPageNumberOfSection(locationName);
text.Span(" - ");
text.EndPageNumberOfSection(locationName);
var lengthStyle = TextStyle.Default.FontColor(Colors.Grey.Medium);
var lengthStyle = TextStyle.Default.Color(Colors.Grey.Medium);
text.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x =>
{
var formatted = x == 1 ? "1 page long" : $"{x} pages long";
return $" ({formatted})";
});
text.Span(" (").Style(lengthStyle);
text.TotalPagesWithinSection(locationName).Style(lengthStyle).Format(x => x == 1 ? "1 page long" : $"{x} pages long");
text.Span(")").Style(lengthStyle);
});
});
}
}
}
}
+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, Metadata, null);
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, Metadata, null);
}
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>8</LangVersion>
<RootNamespace>QuestPDF.ReportSample</RootNamespace>
@@ -10,9 +10,9 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
<ItemGroup>

Before

Width:  |  Height:  |  Size: 993 B

After

Width:  |  Height:  |  Size: 993 B

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

@@ -38,5 +38,21 @@ namespace QuestPDF.ReportSample
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);
}
}
}
@@ -6,8 +6,8 @@ namespace QuestPDF.ReportSample
{
public static class Typography
{
public static TextStyle Title => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Darken3).FontSize(26).Black();
public static TextStyle Headline => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Blue.Medium).FontSize(16).SemiBold();
public static TextStyle Normal => TextStyle.Default.FontFamily(Fonts.Lato).FontColor(Colors.Black).FontSize(10).LineHeight(1.2f);
public static TextStyle Title => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Darken3).Size(26).Black();
public static TextStyle Headline => TextStyle.Default.FontType(Fonts.Calibri).Color(Colors.Blue.Medium).Size(16).SemiBold();
public static TextStyle Normal => TextStyle.Default.FontType(Fonts.Verdana).Color(Colors.Black).Size(10).LineHeight(1.2f);
}
}
}
@@ -86,43 +86,5 @@ namespace QuestPDF.UnitTests
.ExpectCanvasTranslate(new Position(-300, 0))
.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();
}
}
}
@@ -153,9 +153,7 @@ namespace QuestPDF.UnitTests
Ratio = 2f
})
.DrawElement(new Size(500, 200))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
@@ -170,45 +168,7 @@ namespace QuestPDF.UnitTests
Ratio = 2f
})
.DrawElement(new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
[Test]
public void DrawChild_PerWidth_RightToLeft()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f,
ContentDirection = ContentDirection.RightToLeft
})
.DrawElement(new Size(500, 200))
.ExpectCanvasTranslate(100, 0)
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(-100, 0)
.CheckDrawResult();
}
[Test]
public void DrawChild_PerHeight_RightToLeft()
{
TestPlan
.For(x => new AspectRatio
{
Child = x.CreateChild(),
Option = AspectRatioOption.FitArea,
Ratio = 2f,
ContentDirection = ContentDirection.RightToLeft
})
.DrawElement(new Size(400, 300))
.ExpectCanvasTranslate(0, 0)
.ExpectChildDraw(new Size(400, 200))
.ExpectCanvasTranslate(0, 0)
.CheckDrawResult();
}
}
@@ -35,9 +35,7 @@ namespace QuestPDF.UnitTests
})
.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();
}
@@ -51,43 +49,7 @@ namespace QuestPDF.UnitTests
})
.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();
}
}
@@ -3,28 +3,18 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>9</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.8.0" />
<PackageReference Include="FluentAssertions" Version="6.1.0" />
<PackageReference Include="nunit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="SkiaSharp" Version="2.88.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="SkiaSharp" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QuestPDF\QuestPDF.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\FontContent.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="Resources\FontEmbeddedResource.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>
@@ -1,5 +1,4 @@
using System;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
using SkiaSharp;
@@ -11,6 +10,7 @@ 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,7 +18,7 @@ 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(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
public void DrawText(string text, Position position, TextStyle style) => DrawTextFunc(text, position, style);
public void DrawImage(SKImage image, Position position, Size size) => DrawImageFunc(image, position, size);
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using QuestPDF.Drawing;
using QuestPDF.Infrastructure;
using QuestPDF.UnitTests.TestEngine.Operations;
using SkiaSharp;
@@ -16,7 +15,7 @@ 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(SKTextBlob skTextBlob, Position position, TextStyle style) => throw new NotImplementedException();
public void DrawText(string text, Position position, TextStyle style) => Operations.Add(new CanvasDrawTextOperation(text, position, style));
public void DrawImage(SKImage image, Position position, Size size) => Operations.Add(new CanvasDrawImageOperation(position, size));
public void DrawHyperlink(string url, Size size) => throw new NotImplementedException();

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