using System.Collections.ObjectModel; using System.ComponentModel; using Avalonia.Threading; using QuestPDF.Drawing; using QuestPDF.Fluent; using QuestPDF.Helpers; using QuestPDF.Infrastructure; using SkiaSharp; namespace QuestPDF.Previewer { internal class DocumentRenderer : INotifyPropertyChanged { public float PageSpacing { get; set; } public Size Bounds { get; private set; } public IDocument? Document { get; private set; } public event PropertyChangedEventHandler? PropertyChanged; private ObservableCollection _pages = new(); public ObservableCollection Pages { get => _pages; set { if (_pages != value) { _pages = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Pages))); } } } private bool _isRendering; public bool IsRendering { get => _isRendering; private set { if (_isRendering != value) { _isRendering = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRendering))); } } } public void UpdateDocument(IDocument? document) { Document = document; if (document != null) { try { IsRendering = true; RenderDocument(document); } catch (Exception ex) { RenderDocument(CreateExceptionDocument(ex)); } finally { IsRendering = false; } } } private void RenderDocument(IDocument document) { var canvas = new PreviewerCanvas(); DocumentGenerator.RenderDocument(canvas, document); var width = canvas.Pictures.Max(p => p.Size.Width); var height = canvas.Pictures.Sum(p => p.Size.Height) + (canvas.Pictures.Count - 1) * PageSpacing; Bounds = new Size(width, height); foreach (var pages in Pages) pages?.Picture?.Dispose(); Dispatcher.UIThread.Post(() => { Pages.Clear(); Pages = new ObservableCollection(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); } }); }); }); } } }