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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPDF.Examples", "QuestPDF.Examples\QuestPDF.Examples.csproj", "{8BD0A2B4-2DC1-47BA-9724-C158320D9CAE}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user