Просмотр исходного кода

Previewer: sending details about layout overflow exception

MarcinZiabek 3 лет назад
Родитель
Сommit
baa0feb5e6

+ 1 - 1
QuestPDF.ReportSample/QuestPDF.ReportSample.csproj

@@ -10,7 +10,7 @@
     <ItemGroup>
         <PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
         <PackageReference Include="nunit" Version="3.13.3" />
-        <PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
+        <PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
         <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
         <PackageReference Include="SkiaSharp" Version="2.88.3" />
     </ItemGroup>

+ 7 - 4
QuestPDF.ReportSample/Tests.cs

@@ -6,6 +6,7 @@ using NUnit.Framework;
 using QuestPDF.Drawing;
 using QuestPDF.Fluent;
 using QuestPDF.Infrastructure;
+using QuestPDF.Previewer;
 using QuestPDF.ReportSample.Layouts;
 
 namespace QuestPDF.ReportSample
@@ -24,11 +25,13 @@ namespace QuestPDF.ReportSample
         [Test] 
         public void GenerateAndShowPdf()
         {
+            Report.ShowInPreviewer();
+            
             //ImagePlaceholder.Solid = true;
-        
-            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
-            Report.GeneratePdf(path);
-            Process.Start("explorer.exe", path);
+            //
+            // var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"test_result.pdf");
+            // Report.GeneratePdf(path);
+            // Process.Start("explorer.exe", path);
         }
         
         [Test] 

+ 1 - 1
QuestPDF/Drawing/DocumentGenerator.cs

@@ -135,7 +135,7 @@ namespace QuestPDF.Drawing
                               $"2) The layout configuration of your document is invalid. Some of the elements require more space than is provided." +
                               $"Please analyze your documents structure to detect this element and fix its size constraints.";
 
-                var elementTrace = debuggingState?.BuildTrace() ?? "Debug trace is available only in the DEBUG mode.";
+                var elementTrace = debuggingState?.BuildTrace(); // ?? "Debug trace is available only in the DEBUG mode.";
 
                 throw new DocumentLayoutException(message, elementTrace);
             }

+ 3 - 2
QuestPDF/Drawing/Exceptions/DocumentLayoutException.cs

@@ -1,12 +1,13 @@
 using System;
+using QuestPDF.Previewer;
 
 namespace QuestPDF.Drawing.Exceptions
 {
     public class DocumentLayoutException : Exception
     {
-        public string? ElementTrace { get; }
+        internal LayoutRenderingTrace? ElementTrace { get; }
 
-        internal DocumentLayoutException(string message, string? elementTrace = null) : base(message)
+        internal DocumentLayoutException(string message, LayoutRenderingTrace? elementTrace = null) : base(message)
         {
             ElementTrace = elementTrace;
         }

+ 28 - 35
QuestPDF/Drawing/Proxy/DebuggingState.cs

@@ -6,6 +6,8 @@ using System.Text;
 using QuestPDF.Elements;
 using QuestPDF.Helpers;
 using QuestPDF.Infrastructure;
+using QuestPDF.Previewer;
+using Size = QuestPDF.Infrastructure.Size;
 
 namespace QuestPDF.Drawing.Proxy
 {
@@ -57,48 +59,35 @@ namespace QuestPDF.Drawing.Proxy
             item.SpacePlan = spacePlan;
         }
         
-        public string BuildTrace()
+        public LayoutRenderingTrace BuildTrace()
         {
-            var builder = new StringBuilder();
-            var nestingLevel = 0;
+            return Traverse(Root);
 
-            Traverse(Root);
-            return builder.ToString();
-
-            void Traverse(DebugStackItem item)
+            LayoutRenderingTrace Traverse(DebugStackItem item)
             {
-                var indent = new string(' ', nestingLevel * 4);
-                var title = item.Element.GetType().Name;
-
-                if (item.Element is DebugPointer debugPointer)
+                return  new LayoutRenderingTrace
                 {
-                    title = debugPointer.Target;
-                    title += debugPointer.Highlight ? " 🌟" : string.Empty;
-                }
-                
-                if (item.SpacePlan.Type != SpacePlanType.FullRender)
-                    title = "🔥 " + title;
-
-                builder.AppendLine(indent + title);
-                builder.AppendLine(indent + new string('-', title.Length));
-                
-                builder.AppendLine(indent + "Available space: " + item.AvailableSpace);
-                builder.AppendLine(indent + "Requested space: " + item.SpacePlan);
-                
-                foreach (var configuration in GetElementConfiguration(item.Element))
-                    builder.AppendLine(indent + configuration);
-
-                builder.AppendLine();
-                
-                nestingLevel++;
-                item.Stack.ToList().ForEach(Traverse);
-                nestingLevel--;
+                    ElementType = item.Element.GetType().Name,
+                    ElementProperties = GetElementConfiguration(item.Element).ToList(),
+                    AvailableSpace = new QuestPDF.Previewer.Size()
+                    {
+                        Width = item.AvailableSpace.Width,
+                        Height = item.AvailableSpace.Height
+                    },
+                    SpacePlan = new QuestPDF.Previewer.SpacePlan()
+                    {
+                        Width = item.SpacePlan.Width,
+                        Height = item.SpacePlan.Height,
+                        Type = item.SpacePlan.Type
+                    },
+                    Children = item.Stack.Select(Traverse).ToList()
+                };
             }
 
-            static IEnumerable<string> GetElementConfiguration(IElement element)
+            static IEnumerable<DocumentElementProperty> GetElementConfiguration(IElement element)
             {
                 if (element is DebugPointer)
-                    return Enumerable.Empty<string>();
+                    return Enumerable.Empty<DocumentElementProperty>();
                 
                 return element
                     .GetType()
@@ -111,7 +100,11 @@ namespace QuestPDF.Drawing.Proxy
                     .Where(x => !(x.Value is IElement))
                     .Where(x => x.Value is string || !(x.Value is IEnumerable))
                     .Where(x => !(x.Value is TextStyle))
-                    .Select(x => $"{x.Property}: {FormatValue(x.Value)}");
+                    .Select(x => new DocumentElementProperty
+                    {
+                        Label = x.Property,
+                        Value = FormatValue(x.Value)
+                    });
 
                 string FormatValue(object value)
                 {

+ 5 - 2
QuestPDF/Previewer/Helpers.cs

@@ -1,4 +1,5 @@
-using System;
+#if NETCOREAPP3_0_OR_GREATER
+
 using System.Net.Http;
 using System.Text;
 using System.Text.Json;
@@ -16,4 +17,6 @@ internal static class Helpers
         
         return client.PostAsync(requestUri, jsonContent, cancellationToken);
     }
-}
+}
+
+#endif

+ 33 - 0
QuestPDF/Previewer/Models.cs

@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using QuestPDF.Drawing;
+using QuestPDF.Infrastructure;
+
+namespace QuestPDF.Previewer;
+
+internal sealed class DocumentElementProperty
+{
+    public string Label { get; set; }
+    public string Value { get; set; }
+}
+
+internal sealed class Size
+{
+    public float Width { get; set; }
+    public float Height { get; set; }
+}
+
+internal sealed class SpacePlan
+{
+    public float Width { get; set; }
+    public float Height { get; set; }
+    public SpacePlanType Type { get; set; }
+}
+
+internal sealed class LayoutRenderingTrace
+{
+    public string ElementType { get; set; }
+    public IReadOnlyCollection<DocumentElementProperty> ElementProperties { get; set; }
+    public Size AvailableSpace { get; set; }
+    public SpacePlan SpacePlan { get; set; }
+    public IReadOnlyCollection<LayoutRenderingTrace> Children { get; set; }
+}

+ 21 - 16
QuestPDF/Previewer/PreviewerExtensions.cs

@@ -5,6 +5,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using QuestPDF.Drawing;
+using QuestPDF.Drawing.Exceptions;
 using QuestPDF.Infrastructure;
 
 namespace QuestPDF.Previewer
@@ -16,8 +17,10 @@ namespace QuestPDF.Previewer
             document.ShowInPreviewerAsync(port).ConfigureAwait(true).GetAwaiter().GetResult();
         }
         
-        public static async Task ShowInPreviewerAsync(this IDocument document, int port = 5000)
+        public static async Task ShowInPreviewerAsync(this IDocument document, int port = 5000, CancellationToken cancellationToken = default)
         {
+            QuestPDF.Settings.EnableDebugging = true;
+            
             var previewerService = new PreviewerService(port);
             
             using var cancellationTokenSource = new CancellationTokenSource();
@@ -26,10 +29,19 @@ namespace QuestPDF.Previewer
             await previewerService.Connect();
             await RefreshPreview();
             
-            //
-            // //HotReloadManager.UpdateApplicationRequested += (_, _) => RefreshPreview();
-            //
-            // await WaitForPreviewerExit(cancellationTokenSource.Token);
+            #if NET6_0_OR_GREATER
+            HotReloadManager.UpdateApplicationRequested += (_, _) => RefreshPreview();
+            #endif
+
+            while (true)
+            {
+                //if (cancellationToken.IsCancellationRequested)
+                //    break;
+
+                await Task.Delay(TimeSpan.FromMilliseconds(1000));
+            }
+            
+            //await previewerService.Disconnect();
             
             Task RefreshPreview()
             {
@@ -38,22 +50,15 @@ namespace QuestPDF.Previewer
                     var pictures = DocumentGenerator.GeneratePreviewerPictures(document);
                     return previewerService.ShowDocumentPreview(pictures);
                 }
+                catch (DocumentLayoutException exception)
+                {
+                    return previewerService.ShowLayoutError(exception);
+                }
                 catch (Exception exception)
                 {
                     return previewerService.ShowGenericError(exception);
                 }
             }
-            //
-            // async Task WaitForPreviewerExit(CancellationToken cancellationToken)
-            // {
-            //     while (true)
-            //     {
-            //         if (cancellationToken.IsCancellationRequested)
-            //             return;
-            //     
-            //         await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
-            //     }
-            // }
         }
     }
 }

+ 0 - 23
QuestPDF/Previewer/PreviewerRefreshCommand.cs

@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using QuestPDF.Infrastructure;
-
-#if NETCOREAPP3_0_OR_GREATER
-
-namespace QuestPDF.Previewer
-{
-    internal class PreviewerRefreshCommand
-    {
-        public ICollection<Page> Pages { get; set; }
-
-        public class Page
-        {
-            public string Id { get; } = Guid.NewGuid().ToString("N");
-            
-            public float Width { get; set; }
-            public float Height { get; set; }
-        }
-    }
-}
-
-#endif

+ 79 - 87
QuestPDF/Previewer/PreviewerService.cs

@@ -2,11 +2,13 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Net.Http;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
 using QuestPDF.Drawing;
+using QuestPDF.Drawing.Exceptions;
 
 namespace QuestPDF.Previewer
 {
@@ -20,10 +22,15 @@ namespace QuestPDF.Previewer
         public GenericError? InnerException { get; set; }
     }
 
-    internal sealed class ShowLayoutErrorApiRequestVersion
+    internal sealed class ShowGenericErrorApiRequest
     {
         public GenericError? Error { get; set; }
     }
+    
+    internal sealed class ShowLayoutErrorApiRequest
+    {
+        public LayoutRenderingTrace? Trace { get; set; }
+    }
 
 
     internal sealed class UpdateDocumentPreviewApiRequest
@@ -62,6 +69,62 @@ namespace QuestPDF.Previewer
             };
         }
 
+        private void StartPreviewer()
+        {
+            try
+            {
+                var process = new Process
+                {
+                    StartInfo = new()
+                    {
+                        FileName = "questpdf-previewer",
+                        Arguments = $"{Port}",
+                        UseShellExecute = false,
+                        CreateNoWindow = true
+                    }
+                };
+                
+                process.Start();
+            }
+            catch
+            {
+                throw new Exception("Cannot start the QuestPDF Previewer tool. " +
+                                    "Please install it by executing in terminal the following command: 'dotnet tool install --global QuestPDF.Previewer'.");
+            }
+        }
+        
+        public async Task Connect()
+        {
+            var isAvailable = await IsPreviewerAvailable();
+        
+            if (!isAvailable)
+            {
+                //StartPreviewer();
+                //await WaitForConnection();
+            }
+            
+            //var previewerVersion = await GetPreviewerVersion();
+            //CheckVersionCompatibility(previewerVersion);
+        }
+        
+        public async Task Disconnect()
+        {
+            await HttpClient.GetAsync("/v1/disconnect");
+        }
+        
+        private async Task<bool> IsPreviewerAvailable()
+        {
+            try
+            {
+                using var result = await HttpClient.GetAsync("/ping");
+                return result.IsSuccessStatusCode;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+        
         public async Task ShowDocumentPreview(ICollection<PreviewerPicture> pictures)
         {
             using var multipartContent = new MultipartFormDataContent();
@@ -100,7 +163,7 @@ namespace QuestPDF.Previewer
         
         public async Task ShowGenericError(Exception exception)
         {
-            var payload = new ShowLayoutErrorApiRequestVersion
+            var payload = new ShowGenericErrorApiRequest
             {
                 Error = MapException(exception)
             };
@@ -122,32 +185,19 @@ namespace QuestPDF.Previewer
             }
         }
         
-        public async Task Connect()
+        public async Task ShowLayoutError(DocumentLayoutException exception)
         {
-            var isAvailable = await IsPreviewerAvailable();
-        
-            if (!isAvailable)
+            var payload = new ShowLayoutErrorApiRequest
             {
-                //StartPreviewer();
-                //await WaitForConnection();
-            }
-            
-            //var previewerVersion = await GetPreviewerVersion();
-            //CheckVersionCompatibility(previewerVersion);
+                Trace = exception.ElementTrace
+            };
+
+            await HttpClient.PostAsJsonAsync("/v1/update/error/layout", payload);
         }
         
-        private async Task<bool> IsPreviewerAvailable()
-        {
-            try
-            {
-                using var result = await HttpClient.GetAsync("/ping");
-                return result.IsSuccessStatusCode;
-            }
-            catch
-            {
-                return false;
-            }
-        }
+
+        
+
         //
         // private async Task<Version> GetPreviewerVersion()
         // {
@@ -155,36 +205,11 @@ namespace QuestPDF.Previewer
         //     return await result.Content.ReadFromJsonAsync<Version>();
         // }
         //
-        // private void StartPreviewer()
-        // {
-        //     try
-        //     {
-        //         var process = new Process
-        //         {
-        //             StartInfo = new()
-        //             {
-        //                 FileName = "questpdf-previewer",
-        //                 Arguments = $"{Port}",
-        //                 UseShellExecute = false,
-        //                 CreateNoWindow = true
-        //             }
-        //         };
-        //         
-        //         process.Start();
-        //
-        //         Task.Run(async () =>
-        //         {
-        //             await process.WaitForExitAsync();
-        //             process.Dispose();
-        //             OnPreviewerStopped?.Invoke();
-        //         });
-        //     }
-        //     catch
-        //     {
-        //         throw new Exception("Cannot start the QuestPDF Previewer tool. " +
-        //                             "Please install it by executing in terminal the following command: 'dotnet tool install --global QuestPDF.Previewer'.");
-        //     }
-        // }
+
+        
+
+        
+        
         //
         // private void CheckVersionCompatibility(Version version)
         // {
@@ -216,39 +241,6 @@ namespace QuestPDF.Previewer
         //             break;
         //     }
         // }
-        //
-        // public async Task RefreshPreview(ICollection<PreviewerPicture> pictures)
-        // {
-        //     using var multipartContent = new MultipartFormDataContent();
-        //
-        //     var pages = new List<PreviewerRefreshCommand.Page>();
-        //     
-        //     foreach (var picture in pictures)
-        //     {
-        //         var page = new PreviewerRefreshCommand.Page
-        //         {
-        //             Width = picture.Size.Width,
-        //             Height = picture.Size.Height
-        //         };
-        //         
-        //         pages.Add(page);
-        //
-        //         var pictureStream = picture.Picture.Serialize().AsStream();
-        //         multipartContent.Add(new StreamContent(pictureStream), page.Id, page.Id);
-        //     }
-        //
-        //     var command = new PreviewerRefreshCommand
-        //     {
-        //         Pages = pages
-        //     };
-        //     
-        //     multipartContent.Add(JsonContent.Create(command), "command");
-        //
-        //     using var _ = await HttpClient.PostAsync("/update/preview", multipartContent);
-        //
-        //     foreach (var picture in pictures)
-        //         picture.Picture.Dispose();
-        // }
     }
 }
 

+ 0 - 1
QuestPDF/QuestPDF.csproj

@@ -18,7 +18,6 @@
         <PackageLicenseExpression>MIT</PackageLicenseExpression>
         <Nullable>enable</Nullable>
         <TargetFrameworks>net462;netstandard2.0;netcoreapp2.0;netcoreapp3.0;net6.0</TargetFrameworks>
-        <TargetFrameworks>netcoreapp3.0;</TargetFrameworks>
         <IncludeSymbols>true</IncludeSymbols>
         <SymbolPackageFormat>snupkg</SymbolPackageFormat>
     </PropertyGroup>