Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8551e1f33f |
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -8,6 +8,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.UnitTests", "Quest
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer", "QuestPDF.Previewer\QuestPDF.Previewer.csproj", "{B4D3CBBC-2F38-434F-9347-6E5297737047}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Previewer.Example", "QuestPDF.Previewer.Example\QuestPDF.Previewer.Example.csproj", "{012F05A0-C30B-4112-8EB0-E70CE1D31E57}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -30,5 +34,13 @@ Global
|
|||||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B4D3CBBC-2F38-434F-9347-6E5297737047}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B4D3CBBC-2F38-434F-9347-6E5297737047}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B4D3CBBC-2F38-434F-9347-6E5297737047}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B4D3CBBC-2F38-434F-9347-6E5297737047}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{012F05A0-C30B-4112-8EB0-E70CE1D31E57}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
Reference in New Issue
Block a user