Browse Source

Previewer: integrated message about layout issue

Marcin Ziąbek 2 years ago
parent
commit
d3812fc723

+ 8 - 13
Source/QuestPDF.Previewer/CommunicationService.cs

@@ -12,7 +12,7 @@ class CommunicationService
 {
     public static CommunicationService Instance { get; } = new ();
     
-    public event Action<ICollection<PreviewPage>>? OnDocumentRefreshed;
+    public event Action<DocumentSnapshot>? OnDocumentRefreshed;
 
     private WebApplication? Application { get; set; }
 
@@ -60,20 +60,15 @@ class CommunicationService
     
     private async Task<IResult> HandleUpdatePreview(HttpRequest request)
     {
-        var command = JsonSerializer.Deserialize<DocumentSnapshot>(request.Form["command"], JsonSerializerOptions);
+        var documentSnapshot = JsonSerializer.Deserialize<DocumentSnapshot>(request.Form["command"], JsonSerializerOptions);
 
-        var pages = command
-            .Pages
-            .Select(page =>
-            {
-                using var stream = request.Form.Files[page.Id].OpenReadStream();
-                var picture = SKPicture.Deserialize(stream);
-                        
-                return new PreviewPage(picture, page.Width, page.Height);
-            })
-            .ToList();
+        foreach (var pageSnapshot in documentSnapshot.Pages)
+        {
+            using var stream = request.Form.Files[pageSnapshot.Id].OpenReadStream();
+            pageSnapshot.Picture = SKPicture.Deserialize(stream);
+        }
 
-        Task.Run(() => OnDocumentRefreshed(pages));
+        Task.Run(() => OnDocumentRefreshed(documentSnapshot));
         return Results.Ok();
     }
 }

+ 1 - 0
Source/QuestPDF.Previewer/PreviewerRefreshCommand.cs → Source/QuestPDF.Previewer/DocumentSnapshot.cs

@@ -4,6 +4,7 @@ namespace QuestPDF.Previewer;
 
 internal class DocumentSnapshot
 {
+    public bool DocumentContentHasLayoutOverflowIssues { get; set; }
     public ICollection<PageSnapshot> Pages { get; set; }
 
     public class PageSnapshot

+ 1 - 1
Source/QuestPDF.Previewer/Helpers.cs

@@ -4,7 +4,7 @@ namespace QuestPDF.Previewer;
 
 class Helpers
 {
-    public static void GeneratePdfFromDocumentSnapshots(string filePath, ICollection<PreviewPage> pages)
+    public static void GeneratePdfFromDocumentSnapshots(string filePath, ICollection<DocumentSnapshot.PageSnapshot> pages)
     {
         using var stream = File.Create(filePath);
             

+ 1 - 1
Source/QuestPDF.Previewer/InteractiveCanvas.cs

@@ -9,7 +9,7 @@ namespace QuestPDF.Previewer;
 class InteractiveCanvas : ICustomDrawOperation
 {
     public Rect Bounds { get; set; }
-    public ICollection<PreviewPage> Pages { get; set; }
+    public ICollection<DocumentSnapshot.PageSnapshot> Pages { get; set; }
 
     private float Width => (float)Bounds.Width;
     private float Height => (float)Bounds.Height;

+ 0 - 6
Source/QuestPDF.Previewer/PreviewPage.cs

@@ -1,6 +0,0 @@
-using SkiaSharp;
-
-namespace QuestPDF.Previewer
-{
-    record PreviewPage(SKPicture Picture, float Width, float Height);
-}

+ 3 - 3
Source/QuestPDF.Previewer/PreviewerControl.cs

@@ -10,10 +10,10 @@ namespace QuestPDF.Previewer
     {
         private InteractiveCanvas InteractiveCanvas { get; set; } = new ();
         
-        public static readonly StyledProperty<ObservableCollection<PreviewPage>> PagesProperty =
-            AvaloniaProperty.Register<PreviewerControl, ObservableCollection<PreviewPage>>(nameof(Pages));
+        public static readonly StyledProperty<ObservableCollection<DocumentSnapshot.PageSnapshot>> PagesProperty =
+            AvaloniaProperty.Register<PreviewerControl, ObservableCollection<DocumentSnapshot.PageSnapshot>>(nameof(Pages));
         
-        public ObservableCollection<PreviewPage>? Pages
+        public ObservableCollection<DocumentSnapshot.PageSnapshot>? Pages
         {
             get => GetValue(PagesProperty);
             set => SetValue(PagesProperty, value);

+ 12 - 0
Source/QuestPDF.Previewer/PreviewerWindow.axaml

@@ -52,6 +52,18 @@
 			                            ScrollViewportSize="{Binding ScrollViewportSize, Mode=OneWayToSource}"
 			                            Pages="{Binding Pages, Mode=OneWay}" />
 			
+			<Border IsVisible="{Binding DocumentContentHasLayoutOverflowIssues}" Grid.Row="1" Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Left" Padding="16,8" Background="#F44336" CornerRadius="8" BoxShadow="0 0 8 0 #44000000" Margin="32">
+				<StackPanel Orientation="Horizontal" Spacing="8">
+					<Viewbox Width="24" Height="24">
+						<Canvas Width="24" Height="24">
+							<Path Fill="White" Data="M20 17H22V15H20V17M20 7V13H22V7M6 16H11V18H6M6 12H14V14H6M4 2C2.89 2 2 2.89 2 4V20C2 21.11 2.89 22 4 22H16C17.11 22 18 21.11 18 20V8L12 2M4 4H11V9H16V20H4Z" />
+						</Canvas>
+					</Viewbox>
+					
+					<TextBlock VerticalAlignment="Center">Document has layout problems</TextBlock>
+				</StackPanel>
+			</Border>
+			
 			<StackPanel Grid.Row="1" Grid.Column="0" Orientation="Vertical" VerticalAlignment="Bottom" Spacing="16" Margin="32">
 				<Button Classes="actions"
 				        Command="{Binding ShowPdfCommand, Mode=OneTime}"

+ 12 - 4
Source/QuestPDF.Previewer/PreviewerWindowViewModel.cs

@@ -8,13 +8,20 @@ namespace QuestPDF.Previewer
 {
     internal class PreviewerWindowViewModel : ReactiveObject
     {
-        private ObservableCollection<PreviewPage> _pages = new();
-        public ObservableCollection<PreviewPage> Pages
+        private ObservableCollection<DocumentSnapshot.PageSnapshot> _pages = new();
+        public ObservableCollection<DocumentSnapshot.PageSnapshot> Pages
         {
             get => _pages;
             set => this.RaiseAndSetIfChanged(ref _pages, value);
         }
         
+        private bool _documentContentHasLayoutOverflowIssues;
+        public bool DocumentContentHasLayoutOverflowIssues
+        {
+            get => _documentContentHasLayoutOverflowIssues;
+            set => this.RaiseAndSetIfChanged(ref _documentContentHasLayoutOverflowIssues, value);
+        }
+        
         private float _currentScroll;
         public float CurrentScroll
         {
@@ -75,12 +82,13 @@ namespace QuestPDF.Previewer
             openBrowserProcess.Start();
         }
         
-        private void HandleUpdatePreview(ICollection<PreviewPage> pages)
+        private void HandleUpdatePreview(DocumentSnapshot documentSnapshot)
         {
             var oldPages = Pages;
             
             Pages.Clear();
-            Pages = new ObservableCollection<PreviewPage>(pages);
+            Pages = new ObservableCollection<DocumentSnapshot.PageSnapshot>(documentSnapshot.Pages);
+            DocumentContentHasLayoutOverflowIssues = documentSnapshot.DocumentContentHasLayoutOverflowIssues;
             
             foreach (var page in oldPages)
                 page.Picture.Dispose();

+ 14 - 3
Source/QuestPDF/Drawing/DocumentGenerator.cs

@@ -84,11 +84,11 @@ namespace QuestPDF.Drawing
             };
         }
 
-        internal static ICollection<PreviewerPicture> GeneratePreviewerPictures(IDocument document)
+        internal static PreviewerDocumentSnapshot GeneratePreviewerContent(IDocument document)
         {
-            var canvas = new SkiaPictureCanvas();
+            var canvas = new PreviewerCanvas();
             RenderDocument(canvas, document, DocumentSettings.Default);
-            return canvas.Pictures;
+            return canvas.GetContent();
         }
         
         private static void RenderDocument<TCanvas>(TCanvas canvas, IDocument document, DocumentSettings settings) where TCanvas : ICanvas, IRenderingCanvas
@@ -223,6 +223,7 @@ namespace QuestPDF.Drawing
             if (Settings.EnableDebugging)
             {
                 ConfigureLayoutOverflowMarker();
+                CheckIfDocumentHasLayoutOverflowIssues();
             }
 
             void ApplyLayoutDebugging()
@@ -259,6 +260,16 @@ namespace QuestPDF.Drawing
 
                 layoutOverflowPageMarker.PageNumbersWithLayoutIssues = new HashSet<int>(pageNumbersWithLayoutIssues);
             }
+
+            void CheckIfDocumentHasLayoutOverflowIssues()
+            {
+                var hasLayoutOverflowVisualizationElements = content
+                    .ExtractElementsOfType<LayoutOverflowVisualization>()
+                    .SelectMany(x => x.Flatten())
+                    .Any();
+
+                canvas.DocumentContentHasLayoutOverflowIssues |= hasLayoutOverflowVisualizationElements;
+            }
             
             void ThrowLayoutException()
             {

+ 2 - 0
Source/QuestPDF/Drawing/FreeCanvas.cs

@@ -7,6 +7,8 @@ namespace QuestPDF.Drawing
     {
         #region IRenderingCanvas
 
+        public bool DocumentContentHasLayoutOverflowIssues { get; set; }
+        
         public void BeginDocument()
         {
             

+ 21 - 9
Source/QuestPDF/Drawing/SkiaPictureCanvas.cs → Source/QuestPDF/Drawing/PreviewerCanvas.cs

@@ -1,34 +1,37 @@
 using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
 using SkiaSharp;
 
 namespace QuestPDF.Drawing
 {
-    internal class PreviewerPicture
+    internal class PreviewerPageSnapshot
     {
         public SKPicture Picture { get; set; }
         public Size Size { get; set; }
 
-        public PreviewerPicture(SKPicture picture, Size size)
+        public PreviewerPageSnapshot(SKPicture picture, Size size)
         {
             Picture = picture;
             Size = size;
         }
     }
     
-    internal class SkiaPictureCanvas : SkiaCanvasBase
+    internal class PreviewerDocumentSnapshot
+    {
+        public ICollection<PreviewerPageSnapshot> Pictures { get; set; }
+        public bool DocumentContentHasLayoutOverflowIssues { get; set; }
+    }
+    
+    internal class PreviewerCanvas : SkiaCanvasBase
     {
         private SKPictureRecorder? PictureRecorder { get; set; }
         private Size? CurrentPageSize { get; set; }
 
-        public ICollection<PreviewerPicture> Pictures { get; } = new List<PreviewerPicture>();
+        private ICollection<PreviewerPageSnapshot> PageSnapshots { get; } = new List<PreviewerPageSnapshot>();
         
         public override void BeginDocument()
         {
-            Pictures.Clear();
+            PageSnapshots.Clear();
         }
 
         public override void BeginPage(Size size)
@@ -44,12 +47,21 @@ namespace QuestPDF.Drawing
             var picture = PictureRecorder?.EndRecording();
             
             if (picture != null && CurrentPageSize.HasValue)
-                Pictures.Add(new PreviewerPicture(picture, CurrentPageSize.Value));
+                PageSnapshots.Add(new PreviewerPageSnapshot(picture, CurrentPageSize.Value));
 
             PictureRecorder?.Dispose();
             PictureRecorder = null;
         }
 
         public override void EndDocument() { }
+
+        public PreviewerDocumentSnapshot GetContent()
+        {
+            return new PreviewerDocumentSnapshot
+            {
+                Pictures = PageSnapshots,
+                DocumentContentHasLayoutOverflowIssues = DocumentContentHasLayoutOverflowIssues,
+            };
+        }
     }
 }

+ 2 - 0
Source/QuestPDF/Drawing/SkiaCanvasBase.cs

@@ -8,6 +8,8 @@ namespace QuestPDF.Drawing
     {
         internal SKCanvas Canvas { get; set; }
 
+        public bool DocumentContentHasLayoutOverflowIssues { get; set; }
+        
         public abstract void BeginDocument();
         public abstract void EndDocument();
         

+ 2 - 0
Source/QuestPDF/Infrastructure/IRenderingCanvas.cs

@@ -2,6 +2,8 @@
 {
     internal interface IRenderingCanvas
     {
+        bool DocumentContentHasLayoutOverflowIssues { get; set; }
+        
         void BeginDocument();
         void EndDocument();
         

+ 3 - 3
Source/QuestPDF/Previewer/PreviewerExtensions.cs

@@ -39,16 +39,16 @@ namespace QuestPDF.Previewer
                 var pictures = GetPictures();
                 return previewerService.RefreshPreview(pictures);
                 
-                ICollection<PreviewerPicture> GetPictures()
+                PreviewerDocumentSnapshot GetPictures()
                 {
                     try
                     {
-                        return DocumentGenerator.GeneratePreviewerPictures(document);
+                        return DocumentGenerator.GeneratePreviewerContent(document);
                     }
                     catch (Exception exception)
                     {
                         var exceptionDocument = new ExceptionDocument(exception);
-                        return DocumentGenerator.GeneratePreviewerPictures(exceptionDocument);
+                        return DocumentGenerator.GeneratePreviewerContent(exceptionDocument);
                     }
                 }
             }

+ 1 - 0
Source/QuestPDF/Previewer/PreviewerRefreshCommand.cs

@@ -8,6 +8,7 @@ namespace QuestPDF.Previewer
 {
     internal class PreviewerRefreshCommand
     {
+        public bool DocumentContentHasLayoutOverflowIssues { get; set; }
         public ICollection<Page> Pages { get; set; }
 
         public class Page

+ 4 - 3
Source/QuestPDF/Previewer/PreviewerService.cs

@@ -131,13 +131,13 @@ namespace QuestPDF.Previewer
             }
         }
         
-        public async Task RefreshPreview(ICollection<PreviewerPicture> pictures)
+        public async Task RefreshPreview(PreviewerDocumentSnapshot previewerDocumentSnapshot)
         {
             using var multipartContent = new MultipartFormDataContent();
 
             var pages = new List<PreviewerRefreshCommand.Page>();
             
-            foreach (var picture in pictures)
+            foreach (var picture in previewerDocumentSnapshot.Pictures)
             {
                 var page = new PreviewerRefreshCommand.Page
                 {
@@ -153,6 +153,7 @@ namespace QuestPDF.Previewer
 
             var command = new PreviewerRefreshCommand
             {
+                DocumentContentHasLayoutOverflowIssues = previewerDocumentSnapshot.DocumentContentHasLayoutOverflowIssues,
                 Pages = pages
             };
             
@@ -160,7 +161,7 @@ namespace QuestPDF.Previewer
 
             using var _ = await HttpClient.PostAsync("/update/preview", multipartContent);
 
-            foreach (var picture in pictures)
+            foreach (var picture in previewerDocumentSnapshot.Pictures)
                 picture.Picture.Dispose();
         }
     }