From 38a5dd977a273228cf0eaaa4588ce58886497d78 Mon Sep 17 00:00:00 2001 From: HenningNormann Date: Fri, 1 Nov 2024 14:23:12 +0100 Subject: [PATCH] - Changed pdf generation to push html to the generator (#539) - Avoid multiple convertions between string and stream when generating pdf - Bumped storage.interface to 4.0.3 Co-authored-by: Henning Normann --- src/Storage/Altinn.Platform.Storage.csproj | 2 +- src/Storage/Clients/IPdfGeneratorClient.cs | 2 +- src/Storage/Clients/PdfGeneratorClient.cs | 21 +++++++++++----- .../Controllers/ContentOnDemandController.cs | 24 +++++++++++-------- .../Repository/PgInstanceRepository.cs | 4 ++-- .../Services/A2OndemandFormattingService.cs | 4 ++-- .../Services/IA2OndemandFormattingService.cs | 4 ++-- 7 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/Storage/Altinn.Platform.Storage.csproj b/src/Storage/Altinn.Platform.Storage.csproj index f0ee0f90..7b9115ea 100644 --- a/src/Storage/Altinn.Platform.Storage.csproj +++ b/src/Storage/Altinn.Platform.Storage.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Storage/Clients/IPdfGeneratorClient.cs b/src/Storage/Clients/IPdfGeneratorClient.cs index 7ddb588c..62238248 100644 --- a/src/Storage/Clients/IPdfGeneratorClient.cs +++ b/src/Storage/Clients/IPdfGeneratorClient.cs @@ -14,6 +14,6 @@ public interface IPdfGeneratorClient /// Generates a PDF. /// /// A stream with the binary content of the generated PDF - Task GeneratePdf(string url, bool isPortrait); + Task GeneratePdf(string html, bool isPortrait); } } diff --git a/src/Storage/Clients/PdfGeneratorClient.cs b/src/Storage/Clients/PdfGeneratorClient.cs index a4176a9a..8e8ecb0c 100644 --- a/src/Storage/Clients/PdfGeneratorClient.cs +++ b/src/Storage/Clients/PdfGeneratorClient.cs @@ -21,7 +21,7 @@ public class PdfGeneratorClient: IPdfGeneratorClient private readonly HttpClient _httpClient; private readonly ILogger _logger; private static readonly JsonSerializerOptions _jsonSerializerOptions = - new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; + new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }; /// /// Initializes a new instance of the class. @@ -40,10 +40,13 @@ public PdfGeneratorClient( } /// - public async Task GeneratePdf(string url, bool isPortrait) + public async Task GeneratePdf(string html, bool isPortrait) { - var request = new PdfGeneratorRequest() { Url = url }; - request.Options = new() { Landscape = !isPortrait }; + var request = new PdfGeneratorRequest + { + Html = html, + Options = new() { Landscape = !isPortrait } + }; string requestContent = JsonSerializer.Serialize(request, _jsonSerializerOptions); var httpResponseMessage = await _httpClient.PostAsync(_httpClient.BaseAddress, new StringContent(requestContent, Encoding.UTF8, "application/json")); @@ -73,9 +76,15 @@ public async Task GeneratePdf(string url, bool isPortrait) public class PdfGeneratorRequest { /// - /// The Url that the PDF generator will used to obtain the HTML needed to created the PDF. + /// The Url that the PDF generator will used to obtain the HTML needed to created the PDF + /// if Html is not specified. Don't use both! + /// + public string Url { get; set; } + + /// + /// The html that is input to the PDF generator if Url is not specified. Don't use both! /// - public string Url { get; set; } = string.Empty; + public string Html { get; set; } /// /// PDF generator request options. diff --git a/src/Storage/Controllers/ContentOnDemandController.cs b/src/Storage/Controllers/ContentOnDemandController.cs index f34ee645..951801a5 100644 --- a/src/Storage/Controllers/ContentOnDemandController.cs +++ b/src/Storage/Controllers/ContentOnDemandController.cs @@ -2,8 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.Json; using System.Threading.Tasks; +using System.Xml.Linq; using Altinn.Platform.Storage.Clients; using Altinn.Platform.Storage.Configuration; using Altinn.Platform.Storage.Interface.Models; @@ -151,10 +153,8 @@ public async Task GetFormdataAsPdf([FromRoute] string org, [FromRoute] s using var mergedDoc = new PdfDocument(); foreach (var xsl in xsls) { - var pdfPages = await _pdfGeneratorClient.GeneratePdf( - $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Request.Path}" - .Replace("formdatapdf", $"formdatahtml/{xsl.PageNumber}"), - xsl.IsPortrait); + string html = await GetFormdataAsHtmlString(app, instanceGuid, dataGuid, language, 3, xsl.PageNumber); + var pdfPages = await _pdfGeneratorClient.GeneratePdf(html, xsl.IsPortrait); using var pageDoc = PdfReader.Open(pdfPages, PdfDocumentOpenMode.Import); for (var i = 0; i < pageDoc.PageCount; i++) { @@ -170,9 +170,8 @@ public async Task GetFormdataAsPdf([FromRoute] string org, [FromRoute] s else { // Generate all pages in a single operation - return await _pdfGeneratorClient.GeneratePdf( - $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Request.Path}".Replace("formdatapdf", "formdatahtml"), - xsls[0].IsPortrait); + string html = await GetFormdataAsHtmlString(app, instanceGuid, dataGuid, language, 3); + return await _pdfGeneratorClient.GeneratePdf(html, xsls[0].IsPortrait); } } @@ -189,7 +188,7 @@ public async Task GetFormdataAsPdf([FromRoute] string org, [FromRoute] s [HttpGet("formdatahtml/{singlepagenr?}")] public async Task GetFormdataAsHtml([FromRoute]string org, [FromRoute] string app, [FromRoute] Guid instanceGuid, [FromRoute] Guid dataGuid, [FromRoute] string language, [FromRoute(Name = "singlepagenr")] int singlePageNr = -1) { - return await GetFormdataAsHtmlInternal(org, app, instanceGuid, dataGuid, language, 3, singlePageNr); + return await GetFormdataAsHtmlStream(app, instanceGuid, dataGuid, language, 3, singlePageNr); } /// @@ -204,10 +203,15 @@ public async Task GetFormdataAsHtml([FromRoute]string org, [FromRoute] s [HttpGet("formsummaryhtml")] public async Task GetFormSummaryAsHtml([FromRoute] string org, [FromRoute] string app, [FromRoute] Guid instanceGuid, [FromRoute] Guid dataGuid, [FromRoute] string language) { - return await GetFormdataAsHtmlInternal(org, app, instanceGuid, dataGuid, language, 2); + return await GetFormdataAsHtmlStream(app, instanceGuid, dataGuid, language, 2); } - private async Task GetFormdataAsHtmlInternal(string org, string app, Guid instanceGuid, Guid dataGuid, string language, int viewType, int singlePageNr = -1) + private async Task GetFormdataAsHtmlStream(string app, Guid instanceGuid, Guid dataGuid, string language, int viewType, int singlePageNr = -1) + { + return new MemoryStream(Encoding.UTF8.GetBytes(await GetFormdataAsHtmlString(app, instanceGuid, dataGuid, language, viewType, singlePageNr))); + } + + private async Task GetFormdataAsHtmlString(string app, Guid instanceGuid, Guid dataGuid, string language, int viewType, int singlePageNr = -1) { (Instance instance, _) = await _instanceRepository.GetOne(instanceGuid, true); Application application = await _applicationRepository.FindOne(instance.AppId, instance.Org); diff --git a/src/Storage/Repository/PgInstanceRepository.cs b/src/Storage/Repository/PgInstanceRepository.cs index 6ada8b25..6d8456fd 100644 --- a/src/Storage/Repository/PgInstanceRepository.cs +++ b/src/Storage/Repository/PgInstanceRepository.cs @@ -294,7 +294,7 @@ private async Task GetInstancesInternal( previousId = id; } - if (!reader.IsDBNull("element")) + if (!await reader.IsDBNullAsync("element")) { instance.Data.Add(await reader.GetFieldValueAsync("element")); } @@ -342,7 +342,7 @@ private async Task GetInstancesInternal( instanceInternalId = await reader.GetFieldValueAsync("id"); } - if (includeElements && !reader.IsDBNull("element")) + if (includeElements && !await reader.IsDBNullAsync("element")) { instanceData.Add(await reader.GetFieldValueAsync("element")); } diff --git a/src/Storage/Services/A2OndemandFormattingService.cs b/src/Storage/Services/A2OndemandFormattingService.cs index 8122710a..b09ce67a 100644 --- a/src/Storage/Services/A2OndemandFormattingService.cs +++ b/src/Storage/Services/A2OndemandFormattingService.cs @@ -37,9 +37,9 @@ public A2OndemandFormattingService(IA2Repository a2Repository, ILogger - public Stream GetFormdataHtml(PrintViewXslBEList printXslList, Stream xmlData, string archiveStamp) + public string GetFormdataHtml(PrintViewXslBEList printXslList, Stream xmlData, string archiveStamp) { - return new MemoryStream(Encoding.UTF8.GetBytes(GetFormdataHtmlInternal(xmlData, printXslList, archiveStamp))); + return GetFormdataHtmlInternal(xmlData, printXslList, archiveStamp); } private string GetFormdataHtmlInternal( diff --git a/src/Storage/Services/IA2OndemandFormattingService.cs b/src/Storage/Services/IA2OndemandFormattingService.cs index 0ac426ee..a865ca16 100644 --- a/src/Storage/Services/IA2OndemandFormattingService.cs +++ b/src/Storage/Services/IA2OndemandFormattingService.cs @@ -16,7 +16,7 @@ public interface IA2OndemandFormattingService /// printXslList /// xmlData /// Timestamp used for water mark - /// Html as stream - Stream GetFormdataHtml(PrintViewXslBEList printXslList, Stream xmlData, string archiveStamp); + /// Html as string + string GetFormdataHtml(PrintViewXslBEList printXslList, Stream xmlData, string archiveStamp); } }