Code refactorization + cleanup
This commit is contained in:
@@ -2,10 +2,7 @@
|
||||
using System.ComponentModel;
|
||||
using Avalonia.Threading;
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Previewer
|
||||
{
|
||||
@@ -17,8 +14,8 @@ namespace QuestPDF.Previewer
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private ObservableCollection<RenderedPageInfo> _pages = new();
|
||||
public ObservableCollection<RenderedPageInfo> Pages
|
||||
private ObservableCollection<PreviewPage> _pages = new();
|
||||
public ObservableCollection<PreviewPage> Pages
|
||||
{
|
||||
get => _pages;
|
||||
set
|
||||
@@ -48,21 +45,23 @@ namespace QuestPDF.Previewer
|
||||
public void UpdateDocument(IDocument? document)
|
||||
{
|
||||
Document = document;
|
||||
if (document != null)
|
||||
|
||||
if (document == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
IsRendering = true;
|
||||
RenderDocument(document);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RenderDocument(CreateExceptionDocument(ex));
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRendering = false;
|
||||
}
|
||||
IsRendering = true;
|
||||
RenderDocument(document);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var exceptionDocument = new ExceptionDocument(exception);
|
||||
RenderDocument(exceptionDocument);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRendering = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,44 +81,7 @@ namespace QuestPDF.Previewer
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
Pages.Clear();
|
||||
Pages = new ObservableCollection<RenderedPageInfo>(canvas.Pictures);
|
||||
});
|
||||
}
|
||||
|
||||
private static IDocument CreateExceptionDocument(Exception exception)
|
||||
{
|
||||
return Fluent.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);
|
||||
}
|
||||
});
|
||||
});
|
||||
Pages = new ObservableCollection<PreviewPage>(canvas.Pictures);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
using QuestPDF.Drawing;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
|
||||
namespace QuestPDF.Previewer;
|
||||
|
||||
public class ExceptionDocument : IDocument
|
||||
{
|
||||
private Exception Exception { get; }
|
||||
|
||||
public ExceptionDocument(Exception exception)
|
||||
{
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
public DocumentMetadata GetMetadata()
|
||||
{
|
||||
return DocumentMetadata.Default;
|
||||
}
|
||||
|
||||
public void Compose(IDocumentContainer 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(currentException.GetType().Name).FontSize(20).SemiBold();
|
||||
column.Item().Text(currentException.Message).FontSize(14);
|
||||
column.Item().PaddingTop(10).Text(currentException.StackTrace).FontSize(10).Light();
|
||||
|
||||
currentException = currentException.InnerException;
|
||||
|
||||
if (currentException != null)
|
||||
column.Item().PaddingVertical(15).LineHorizontal(2).LineColor(Colors.Red.Medium);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using Avalonia;
|
||||
using Avalonia;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.SceneGraph;
|
||||
using Avalonia.Skia;
|
||||
using QuestPDF.Helpers;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Previewer;
|
||||
@@ -11,7 +9,7 @@ namespace QuestPDF.Previewer;
|
||||
class InteractiveCanvas : ICustomDrawOperation
|
||||
{
|
||||
public Rect Bounds { get; set; }
|
||||
public ICollection<RenderedPageInfo> Pages { get; set; }
|
||||
public ICollection<PreviewPage> Pages { get; set; }
|
||||
|
||||
private float Width => (float)Bounds.Width;
|
||||
private float Height => (float)Bounds.Height;
|
||||
@@ -26,6 +24,8 @@ class InteractiveCanvas : ICustomDrawOperation
|
||||
private const float PageSpacing = 50f;
|
||||
private const float SafeZone = 50f;
|
||||
|
||||
#region transformations
|
||||
|
||||
private void LimitScale()
|
||||
{
|
||||
Scale = Math.Max(Scale, MinScale);
|
||||
@@ -79,15 +79,15 @@ class InteractiveCanvas : ICustomDrawOperation
|
||||
LimitTranslate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region rendering
|
||||
|
||||
public void Render(IDrawingContextImpl context)
|
||||
{
|
||||
if (Pages.Count <= 0)
|
||||
return;
|
||||
|
||||
LimitScale();
|
||||
LimitTranslate();
|
||||
|
||||
|
||||
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
|
||||
|
||||
if (canvas == null)
|
||||
@@ -95,7 +95,6 @@ class InteractiveCanvas : ICustomDrawOperation
|
||||
|
||||
var originalMatrix = canvas.TotalMatrix;
|
||||
|
||||
|
||||
canvas.Translate(Width / 2, Height / 2);
|
||||
|
||||
canvas.Scale(Scale);
|
||||
@@ -104,7 +103,7 @@ class InteractiveCanvas : ICustomDrawOperation
|
||||
foreach (var page in Pages)
|
||||
{
|
||||
canvas.Translate(-page.Size.Width / 2f, 0);
|
||||
DrawPageShadow(canvas, page.Size);
|
||||
DrawBlankPage(canvas, page.Size);
|
||||
canvas.DrawPicture(page.Picture);
|
||||
canvas.Translate(page.Size.Width / 2f, page.Size.Height + PageSpacing);
|
||||
}
|
||||
@@ -119,19 +118,27 @@ class InteractiveCanvas : ICustomDrawOperation
|
||||
public bool Equals(ICustomDrawOperation? other) => false;
|
||||
public bool HitTest(Point p) => true;
|
||||
|
||||
#region page shadow
|
||||
|
||||
private static readonly SKImageFilter PageShadow1 = SKImageFilter.CreateDropShadowOnly(0, 6, 6, 6, SKColors.Black.WithAlpha(64));
|
||||
private static readonly SKImageFilter PageShadow2 = SKImageFilter.CreateDropShadowOnly(0, 10, 14, 14, SKColors.Black.WithAlpha(32));
|
||||
#endregion
|
||||
|
||||
private static SKPaint PageShadowPaint = new SKPaint
|
||||
#region blank page
|
||||
|
||||
private static SKPaint BlankPagePaint = new SKPaint
|
||||
{
|
||||
ImageFilter = SKImageFilter.CreateBlendMode(SKBlendMode.Overlay, PageShadow1, PageShadow2)
|
||||
Color = SKColors.White
|
||||
};
|
||||
|
||||
private void DrawPageShadow(SKCanvas canvas, QuestPDF.Infrastructure.Size size)
|
||||
private static SKPaint BlankPageShadowPaint = new SKPaint
|
||||
{
|
||||
canvas.DrawRect(0, 0, size.Width, size.Height, PageShadowPaint);
|
||||
ImageFilter = SKImageFilter.CreateBlendMode(
|
||||
SKBlendMode.Overlay,
|
||||
SKImageFilter.CreateDropShadowOnly(0, 6, 6, 6, SKColors.Black.WithAlpha(64)),
|
||||
SKImageFilter.CreateDropShadowOnly(0, 10, 14, 14, SKColors.Black.WithAlpha(32)))
|
||||
};
|
||||
|
||||
private void DrawBlankPage(SKCanvas canvas, QuestPDF.Infrastructure.Size size)
|
||||
{
|
||||
canvas.DrawRect(0, 0, size.Width, size.Height, BlankPageShadowPaint);
|
||||
canvas.DrawRect(0, 0, size.Width, size.Height, BlankPagePaint);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -4,16 +4,15 @@ using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Previewer
|
||||
{
|
||||
public record RenderedPageInfo(SKPicture Picture, Size Size);
|
||||
public record PreviewPage(SKPicture Picture, Size Size);
|
||||
|
||||
internal sealed class PreviewerCanvas : SkiaCanvasBase, IRenderingCanvas
|
||||
{
|
||||
private SKPictureRecorder? _currentRecorder;
|
||||
private SKPictureRecorder? PictureRecorder { get; set; }
|
||||
private Size? CurrentPageSize { get; set; }
|
||||
|
||||
public ICollection<PreviewPage> Pictures { get; } = new List<PreviewPage>();
|
||||
|
||||
public ICollection<RenderedPageInfo> Pictures { get; } = new List<RenderedPageInfo>();
|
||||
|
||||
private Size? _currentSize;
|
||||
|
||||
public override void BeginDocument()
|
||||
{
|
||||
Pictures.Clear();
|
||||
@@ -21,23 +20,21 @@ namespace QuestPDF.Previewer
|
||||
|
||||
public override void BeginPage(Size size)
|
||||
{
|
||||
_currentSize = size;
|
||||
_currentRecorder = new SKPictureRecorder();
|
||||
Canvas = _currentRecorder.BeginRecording(new SKRect(0, 0, size.Width, size.Height));
|
||||
|
||||
using var paint = new SKPaint() { Color = SKColors.White };
|
||||
Canvas.DrawRect(0, 0, size.Width, size.Height, paint);
|
||||
CurrentPageSize = size;
|
||||
PictureRecorder = new SKPictureRecorder();
|
||||
|
||||
Canvas = PictureRecorder.BeginRecording(new SKRect(0, 0, size.Width, size.Height));
|
||||
}
|
||||
|
||||
public override void EndPage()
|
||||
{
|
||||
var picture = _currentRecorder?.EndRecording();
|
||||
var picture = PictureRecorder?.EndRecording();
|
||||
|
||||
if (picture != null && _currentSize.HasValue)
|
||||
Pictures.Add(new RenderedPageInfo(picture, _currentSize.Value));
|
||||
if (picture != null && CurrentPageSize.HasValue)
|
||||
Pictures.Add(new PreviewPage(picture, CurrentPageSize.Value));
|
||||
|
||||
_currentRecorder?.Dispose();
|
||||
_currentRecorder = null;
|
||||
PictureRecorder?.Dispose();
|
||||
PictureRecorder = null;
|
||||
}
|
||||
|
||||
public override void EndDocument() { }
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace QuestPDF.Previewer
|
||||
{
|
||||
internal class PreviewerControl : Control
|
||||
{
|
||||
public static readonly StyledProperty<ObservableCollection<RenderedPageInfo>?> PagesProperty =
|
||||
AvaloniaProperty.Register<PreviewerControl, ObservableCollection<RenderedPageInfo>>(nameof(Pages));
|
||||
public static readonly StyledProperty<ObservableCollection<PreviewPage>?> PagesProperty =
|
||||
AvaloniaProperty.Register<PreviewerControl, ObservableCollection<PreviewPage>>(nameof(Pages));
|
||||
|
||||
private InteractiveCanvas InteractiveCanvas { get; set; } = new InteractiveCanvas();
|
||||
|
||||
public ObservableCollection<RenderedPageInfo>? Pages
|
||||
public ObservableCollection<PreviewPage>? Pages
|
||||
{
|
||||
get => GetValue(PagesProperty);
|
||||
set => SetValue(PagesProperty, value);
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Rendering.SceneGraph;
|
||||
using Avalonia.Skia;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace QuestPDF.Previewer
|
||||
{
|
||||
internal sealed class SkCustomDrawOperation : ICustomDrawOperation
|
||||
{
|
||||
private readonly Action<SKCanvas> _renderFunc;
|
||||
public Rect Bounds { get; }
|
||||
|
||||
public SkCustomDrawOperation(Rect bounds, Action<SKCanvas> renderFunc)
|
||||
{
|
||||
Bounds = bounds;
|
||||
_renderFunc = renderFunc;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
public bool Equals(ICustomDrawOperation? other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public bool HitTest(Point p)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Render(IDrawingContextImpl context)
|
||||
{
|
||||
var canvas = (context as ISkiaDrawingContextImpl)?.SkCanvas;
|
||||
if (canvas == null)
|
||||
throw new InvalidOperationException($"Context needs to be ISkiaDrawingContextImpl but got {nameof(context)}");
|
||||
|
||||
_renderFunc(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using QuestPDF.Fluent;
|
||||
using QuestPDF.Helpers;
|
||||
using QuestPDF.Infrastructure;
|
||||
@@ -47,7 +48,8 @@ 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");
|
||||
|
||||
Reference in New Issue
Block a user