diff --git a/QuestPDF.Examples/QuestPDF.Examples.csproj b/QuestPDF.Examples/QuestPDF.Examples.csproj index d52452a..bad553c 100644 --- a/QuestPDF.Examples/QuestPDF.Examples.csproj +++ b/QuestPDF.Examples/QuestPDF.Examples.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false diff --git a/QuestPDF.ReportSample/Layouts/SectionTemplate.cs b/QuestPDF.ReportSample/Layouts/SectionTemplate.cs index 83c55cf..35384c7 100644 --- a/QuestPDF.ReportSample/Layouts/SectionTemplate.cs +++ b/QuestPDF.ReportSample/Layouts/SectionTemplate.cs @@ -83,6 +83,8 @@ namespace QuestPDF.ReportSample.Layouts .Range(0, model.PhotoCount) .ToList() .ForEach(x => grid.Item().AspectRatio(4 / 3f).Component()); + + //grid.Item().AspectRatio(4 / 3f).Width(100000).Component(); }); } } diff --git a/QuestPDF.ReportSample/Tests.cs b/QuestPDF.ReportSample/Tests.cs index 8055600..6cc6b2f 100644 --- a/QuestPDF.ReportSample/Tests.cs +++ b/QuestPDF.ReportSample/Tests.cs @@ -25,7 +25,7 @@ namespace QuestPDF.ReportSample [Test] public void GenerateAndShowPdf() { - Report.ShowInPreviewer(); + Report.ShowInPreviewer(5000); //ImagePlaceholder.Solid = true; // diff --git a/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj b/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj index 84c2cc2..8a3c1a6 100644 --- a/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj +++ b/QuestPDF.UnitTests/QuestPDF.UnitTests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false diff --git a/QuestPDF/Drawing/Proxy/Helpers.cs b/QuestPDF/Drawing/Proxy/Helpers.cs index a998c6d..2b21acc 100644 --- a/QuestPDF/Drawing/Proxy/Helpers.cs +++ b/QuestPDF/Drawing/Proxy/Helpers.cs @@ -10,7 +10,7 @@ namespace QuestPDF.Drawing.Proxy; internal static class Helpers { - public static IEnumerable GetElementConfiguration(this IElement element) + public static IReadOnlyCollection GetElementConfiguration(this IElement element) { return element .GetType() diff --git a/QuestPDF/Previewer/ApiRequests.cs b/QuestPDF/Previewer/ApiRequests.cs new file mode 100644 index 0000000..8b356cf --- /dev/null +++ b/QuestPDF/Previewer/ApiRequests.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using QuestPDF.Previewer.Inspection; + +namespace QuestPDF.Previewer; + +internal sealed class IncompatibleVersionApiRequest +{ + +} + +internal sealed class NotifyPresenceApiRequest +{ + public string Id { get; set; } + public string LibraryVersion { get; set; } + public bool IsDotnet6OrBeyond { get; set; } + public bool IsDotnet3OrBeyond { get; set; } + public bool IsExecutedInUnitTest { get; set; } +} + +internal sealed class DocumentPreviewApiRequest +{ + public ICollection PageSnapshots { get; set; } + public InspectionElement DocumentHierarchy { get; set; } + + public class PageSnapshot + { + public string ResourceId { get; } = Guid.NewGuid().ToString("N"); + public int PageNumber { get; set; } + + public float Width { get; set; } + public float Height { get; set; } + } +} + +internal sealed class GenericErrorApiRequest +{ + public GenericError? Error { get; set; } +} + +internal sealed class LayoutErrorApiRequest +{ + public LayoutRenderingTrace? Trace { get; set; } +} \ No newline at end of file diff --git a/QuestPDF/Previewer/Models.cs b/QuestPDF/Previewer/Models.cs index ea02eb9..cae512d 100644 --- a/QuestPDF/Previewer/Models.cs +++ b/QuestPDF/Previewer/Models.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using QuestPDF.Drawing; using QuestPDF.Infrastructure; +using QuestPDF.Previewer.Inspection; namespace QuestPDF.Previewer; @@ -32,3 +34,17 @@ internal sealed class LayoutRenderingTrace public SpacePlan SpacePlan { get; set; } public IReadOnlyCollection Children { get; set; } } + + + +internal sealed class GenericError +{ + public string ExceptionType { get; set; } + public string Message { get; set; } + public string StackTrace { get; set; } + public GenericError? InnerException { get; set; } +} + + + + diff --git a/QuestPDF/Previewer/PreviewerExtensions.cs b/QuestPDF/Previewer/PreviewerExtensions.cs index 4174164..d51d42f 100644 --- a/QuestPDF/Previewer/PreviewerExtensions.cs +++ b/QuestPDF/Previewer/PreviewerExtensions.cs @@ -12,19 +12,19 @@ namespace QuestPDF.Previewer { public static class Extensions { - public static void ShowInPreviewer(this IDocument document, int port = 5000) + public static void ShowInPreviewer(this IDocument document, int port = 12500) { document.ShowInPreviewerAsync(port).ConfigureAwait(true).GetAwaiter().GetResult(); } - public static async Task ShowInPreviewerAsync(this IDocument document, int port = 5000, CancellationToken cancellationToken = default) + public static async Task ShowInPreviewerAsync(this IDocument document, int port = 12500, CancellationToken cancellationToken = default) { QuestPDF.Settings.EnableDebugging = true; var previewerService = new PreviewerService(port); using var cancellationTokenSource = new CancellationTokenSource(); - previewerService.OnPreviewerStopped += () => cancellationTokenSource.Cancel(); + previewerService.OnLostConnection += () => cancellationTokenSource.Cancel(); await previewerService.Connect(); await RefreshPreview(); @@ -35,14 +35,13 @@ namespace QuestPDF.Previewer while (true) { - //if (cancellationToken.IsCancellationRequested) - // break; + if (cancellationToken.IsCancellationRequested) + break; + // ReSharper disable once MethodSupportsCancellation await Task.Delay(TimeSpan.FromMilliseconds(1000)); } - - //await previewerService.Disconnect(); - + Task RefreshPreview() { try diff --git a/QuestPDF/Previewer/PreviewerService.cs b/QuestPDF/Previewer/PreviewerService.cs index b153d4c..27efc91 100644 --- a/QuestPDF/Previewer/PreviewerService.cs +++ b/QuestPDF/Previewer/PreviewerService.cs @@ -4,72 +4,29 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Net.Http; +using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using System.Threading; using System.Threading.Tasks; -using QuestPDF.Drawing; using QuestPDF.Drawing.Exceptions; using QuestPDF.Previewer.Inspection; namespace QuestPDF.Previewer { - internal sealed class NotifyPresenceRequest - { - public string Id { get; set; } - public string LibraryVersion { get; set; } - public bool IsDotnet6OrBeyond { get; set; } - public bool IsDotnet3OrBeyond { get; set; } - public bool IsExecutedInUnitTest { get; set; } - } - - internal sealed class GenericError - { - public string ExceptionType { get; set; } - public string Message { get; set; } - public string StackTrace { get; set; } - public GenericError? InnerException { get; set; } - } - - internal sealed class ShowGenericErrorApiRequest - { - public GenericError? Error { get; set; } - } - - internal sealed class ShowLayoutErrorApiRequest - { - public LayoutRenderingTrace? Trace { get; set; } - } - - - internal sealed class UpdateDocumentPreviewApiRequest - { - public ICollection PageSnapshots { get; set; } - public InspectionElement DocumentHierarchy { get; set; } - - public class PageSnapshot - { - public string ResourceId { get; } = Guid.NewGuid().ToString("N"); - public int PageNumber { get; set; } - - public float Width { get; set; } - public float Height { get; set; } - } - } - - - internal class PreviewerService { private string ClientId { get; } = Guid.NewGuid().ToString("D"); + private string LibraryVersion { get; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString(); private int Port { get; } private HttpClient HttpClient { get; } - public event Action? OnPreviewerStopped; - - private const int RequiredPreviewerVersionMajor = 2022; - private const int RequiredPreviewerVersionMinor = 11; + public event Action? OnLostConnection; + + private const int RequiredPreviewerVersionMajor = 2023; + private const int RequiredPreviewerVersionMinor = 1; public PreviewerService(int port) { @@ -80,7 +37,48 @@ namespace QuestPDF.Previewer Timeout = TimeSpan.FromSeconds(1) }; } + + internal static readonly JsonSerializerOptions JsonSerializerOptions = new() + { + PropertyNameCaseInsensitive = true, + MaxDepth = 256, + Converters = + { + new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) + } + }; + #region Connection + + public async Task Connect() + { + var isAvailable = await IsPreviewerAvailable(); + + if (!isAvailable) + { + StartPreviewer(); + await WaitForConnection(); + } + + var previewerVersion = await GetPreviewerVersion(); + CheckVersionCompatibility(previewerVersion); + + StartNotifyPresenceTask(); + } + + private async Task IsPreviewerAvailable() + { + try + { + using var result = await HttpClient.GetAsync("/ping"); + return result.IsSuccessStatusCode; + } + catch + { + return false; + } + } + private void StartPreviewer() { try @@ -105,72 +103,99 @@ namespace QuestPDF.Previewer } } - public async Task Connect() + private async Task WaitForConnection() { - var isAvailable = await IsPreviewerAvailable(); - - if (!isAvailable) - { - //StartPreviewer(); - //await WaitForConnection(); - } + using var cancellationTokenSource = new CancellationTokenSource(); + cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10)); - //var previewerVersion = await GetPreviewerVersion(); - //CheckVersionCompatibility(previewerVersion); - - StartNotifyPresenceTask(); + var cancellationToken = cancellationTokenSource.Token; + + while (true) + { + // ReSharper disable once MethodSupportsCancellation + await Task.Delay(TimeSpan.FromMilliseconds(250)); + + if (cancellationToken.IsCancellationRequested) + throw new Exception($"Cannot connect to the QuestPDF Previewer tool. Please make sure that your Operating System does not block HTTP connections on port {Port}."); + + var isConnected = await IsPreviewerAvailable(); + + if (isConnected) + break; + } } - - private async Task IsPreviewerAvailable() + + #endregion + + #region Checking compatibility + + private async Task GetPreviewerVersion() { - try - { - using var result = await HttpClient.GetAsync("/ping"); - return result.IsSuccessStatusCode; - } - catch - { - return false; - } + using var result = await HttpClient.GetAsync("/version"); + return await result.Content.ReadFromJsonAsync(); } - public void StartNotifyPresenceTask() + private async Task CheckVersionCompatibility(Version version) + { + if (version.Major == RequiredPreviewerVersionMajor && version.Minor == RequiredPreviewerVersionMinor) + return; + + await ShowIncompatibleVersion(); + + throw new Exception($"Previewer version is not compatible. Possible solutions: " + + $"1) Update the QuestPDF library to newer version. " + + $"2) Update the QuestPDF previewer tool using the following command: 'dotnet tool update --global QuestPDF.Previewer --version {RequiredPreviewerVersionMajor}.{RequiredPreviewerVersionMinor}'"); + } + + private async Task ShowIncompatibleVersion() + { + var payload = new IncompatibleVersionApiRequest(); + await HttpClient.PostAsJsonAsync("/update/incompatibleVersion", payload, JsonSerializerOptions); + } + + #endregion + + #region Reporting presence + + private void StartNotifyPresenceTask() { Task.Run(async () => { while (true) { - NotifyPresence(); + await NotifyPresence(); await Task.Delay(1000); } }); } - public async Task NotifyPresence() + private async Task NotifyPresence() { - var payload = new NotifyPresenceRequest + var payload = new NotifyPresenceApiRequest { Id = ClientId, - LibraryVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString(), + LibraryVersion = LibraryVersion, IsDotnet6OrBeyond = RuntimeDetector.IsNet6OrGreater, IsDotnet3OrBeyond = RuntimeDetector.IsNet3OrGreater, IsExecutedInUnitTest = UnitTestDetector.RunningInUnitTest }; - await HttpClient.PostAsJsonAsync("/v1/notify/presence", payload); + await HttpClient.PostAsJsonAsync("/update/notifyPresence", payload); } + #endregion - + #region Updating state + public async Task ShowDocumentPreview(DocumentPreviewResult documentPreviewResult) { using var multipartContent = new MultipartFormDataContent(); - var pages = new List(); + var pages = new List(); foreach (var snapshot in documentPreviewResult.PageSnapshots) { - var page = new UpdateDocumentPreviewApiRequest.PageSnapshot + var page = new DocumentPreviewApiRequest.PageSnapshot { PageNumber = documentPreviewResult.PageSnapshots.IndexOf(snapshot) + 1, Width = snapshot.Size.Width, @@ -183,17 +208,17 @@ namespace QuestPDF.Previewer multipartContent.Add(new StreamContent(pictureStream), "snapshots", page.ResourceId); } - var request = new UpdateDocumentPreviewApiRequest + var request = new DocumentPreviewApiRequest { - PageSnapshots = pages, + PageSnapshots = pages, DocumentHierarchy = documentPreviewResult.DocumentHierarchy }; - var json = JsonSerializer.Serialize(request); + var json = JsonSerializer.Serialize(request, JsonSerializerOptions); var jsonContent = new StringContent(json, Encoding.UTF8, "application/json"); multipartContent.Add(jsonContent, "request"); - using var response = await HttpClient.PostAsync("/v1/update/preview/document", multipartContent); + using var response = await HttpClient.PostAsync("/update/documentPreview", multipartContent); response.EnsureSuccessStatusCode(); foreach (var picture in documentPreviewResult.PageSnapshots) @@ -202,12 +227,12 @@ namespace QuestPDF.Previewer public async Task ShowGenericError(Exception exception) { - var payload = new ShowGenericErrorApiRequest + var payload = new GenericErrorApiRequest { Error = MapException(exception) }; - await HttpClient.PostAsJsonAsync("/v1/update/error/generic", payload); + await HttpClient.PostAsJsonAsync("/update/genericError", payload, JsonSerializerOptions); static GenericError? MapException(Exception? exception) { @@ -226,69 +251,15 @@ namespace QuestPDF.Previewer public async Task ShowLayoutError(DocumentLayoutException exception) { - var payload = new ShowLayoutErrorApiRequest + var payload = new LayoutErrorApiRequest { Trace = exception.ElementTrace }; - - var jsonSerializerOptions = new JsonSerializerOptions - { - MaxDepth = 256, - Converters = - { - new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) - } - }; - await HttpClient.PostAsJsonAsync("/v1/update/error/layout", payload, jsonSerializerOptions); + await HttpClient.PostAsJsonAsync("/update/layoutError", payload, JsonSerializerOptions); } - - - - // - // private async Task GetPreviewerVersion() - // { - // using var result = await HttpClient.GetAsync("/version"); - // return await result.Content.ReadFromJsonAsync(); - // } - // - - - - - - // - // private void CheckVersionCompatibility(Version version) - // { - // if (version.Major == RequiredPreviewerVersionMajor && version.Minor == RequiredPreviewerVersionMinor) - // return; - // - // throw new Exception($"Previewer version is not compatible. Possible solutions: " + - // $"1) Update the QuestPDF library to newer version. " + - // $"2) Update the QuestPDF previewer tool using the following command: 'dotnet tool update --global QuestPDF.Previewer --version {RequiredPreviewerVersionMajor}.{RequiredPreviewerVersionMinor}'"); - // } - // - // private async Task WaitForConnection() - // { - // using var cancellationTokenSource = new CancellationTokenSource(); - // cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10)); - // - // var cancellationToken = cancellationTokenSource.Token; - // - // while (true) - // { - // await Task.Delay(TimeSpan.FromMilliseconds(250)); - // - // if (cancellationToken.IsCancellationRequested) - // throw new Exception($"Cannot connect to the QuestPDF Previewer tool. Please make sure that your Operating System does not block HTTP connections on port {Port}."); - // - // var isConnected = await IsPreviewerAvailable(); - // - // if (isConnected) - // break; - // } - // } + #endregion } }