diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs index 730ca898d4..c0d609be9c 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs @@ -12,7 +12,7 @@ using ITextDocument = iText.Layout.Document; namespace Volo.Docs.Projects.Pdf.IText; -public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer +public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer, ITransientDependency { protected IOptions Options { get; } @@ -45,51 +45,6 @@ public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer return Task.FromResult(pdfStream); } - public virtual async Task MergePdfFilesAsync(List pdfFiles, string title, bool disposeStreams = true) - { - var mergedStream = new MemoryStream(); - var mergedPdfWriter = new PdfWriter(mergedStream); - var mergedPdfDocument = new iText.Kernel.Pdf.PdfDocument(mergedPdfWriter); - mergedPdfDocument.GetDocumentInfo().SetTitle(title); - mergedPdfWriter.SetCloseStream(false); - - foreach (var pdfFile in pdfFiles) - { - try - { - using var reader = new PdfReader(pdfFile); - using var sourcePdf = new iText.Kernel.Pdf.PdfDocument(reader); - - var pageCount = sourcePdf.GetNumberOfPages(); - - for (var i = 1; i <= pageCount; i++) - { - var page = sourcePdf.GetPage(i); - mergedPdfDocument.AddPage(page.CopyTo(mergedPdfDocument)); - } - } - catch (Exception ex) - { - throw new Exception($"Error merging PDF file {pdfFile}: {ex.Message}", ex); - } - finally - { - try - { - await pdfFile.DisposeAsync(); - } - catch - { - // Ignore any exceptions during disposal - } - } - } - - mergedPdfDocument.Close(); - mergedStream.Position = 0; - return mergedStream; - } - private void BuildPdfOutlines(PdfOutline parentOutline, List pdfDocumentNodes) { foreach (var pdfDocumentNode in pdfDocumentNodes) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs index c93d5fe06e..618f823f80 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs @@ -30,9 +30,7 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency protected IDocumentSource DocumentSource { get; set; } protected DocumentParams DocumentParams { get; set; } protected Project Project { get; set; } - protected List AllPdfDocuments { get; } = []; - - protected int ChunkSize { get; set; } = 60; + protected List AllPdfDocuments { get; private set; } = []; public ProjectPdfGenerator( IDocumentSourceFactory documentStoreFactory, @@ -61,30 +59,31 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency DocumentParams = await GetDocumentParamsAsync(project, version, languageCode); var navigation = await GetNavigationAsync(project, version, languageCode); + AllPdfDocuments = new List(); await SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode); var title = Options.Value.CalculatePdfFileTitle?.Invoke(project) ?? project.Name; - var tempStreams = new List(); + var tempStreams = new List<(PdfDocument pdfDocument, Stream stream)>(); try { - var documentChunks = ChunkDocuments(AllPdfDocuments); - Logger.LogInformation("Documents split into {ChunkCount} chunks for processing", documentChunks.Count); - - foreach (var (chunk, index) in documentChunks.Select((chunk, index) => (chunk, index))) + var index = 0; + foreach (var document in AllPdfDocuments) { try { - Logger.LogInformation("Processing chunk {Index}/{Total}", index + 1, documentChunks.Count); + Logger.LogInformation("Processing chunk {Index}/{Total}", index + 1, AllPdfDocuments.Count); - var chunkHtml = await BuildHtmlAsync(chunk); + var chunkHtml = await BuildHtmlAsync([document]); - var pdfStream = await HtmlToPdfRenderer.RenderAsync($"{title} - Part {index + 1}", chunkHtml, chunk); + var pdfStream = await HtmlToPdfRenderer.RenderAsync($"{title} - Part {document.Title}", chunkHtml, [document]); Logger.LogInformation("Chunk {Index} rendered to PDF", index + 1); - tempStreams.Add(pdfStream); + tempStreams.Add((document, pdfStream)); + + index++; } catch (Exception e) { @@ -94,7 +93,7 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency Logger.LogInformation("All chunks processed, merging PDF files"); - var mergedPdfStream = await MergePdfFilesAsync(tempStreams, title, disposeStreams: true); + var mergedPdfStream = await MergePdfFilesAsync(tempStreams, title); await ProjectPdfFileStore.SetAsync(project, version, languageCode, mergedPdfStream); } catch(Exception e) @@ -104,7 +103,7 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency { try { - await tempStream.DisposeAsync(); + await tempStream.stream.DisposeAsync(); } catch { @@ -119,34 +118,6 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency } } - protected virtual List> ChunkDocuments(List documents) - { - var flatDocuments = FlattenDocuments(documents); - - return flatDocuments - .Select((doc, index) => new { doc, index }) - .GroupBy(x => x.index / ChunkSize) - .Select(g => g.Select(x => x.doc).ToList()) - .ToList(); - } - - protected virtual List FlattenDocuments(List documents) - { - var result = new List(); - - foreach (var document in documents) - { - result.Add(document); - - if (document.HasChildren) - { - result.AddRange(FlattenDocuments(document.Children)); - } - } - - return result; - } - protected virtual async Task BuildHtmlAsync(List pdfDocuments) { var htmlContent = await ConvertDocumentsToHtmlAsync(pdfDocuments); @@ -182,7 +153,7 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency return contentBuilder.ToString(); } - protected virtual async Task MergePdfFilesAsync(List pdfFiles, string title, bool disposeStreams = true) + protected virtual async Task MergePdfFilesAsync(List<(PdfDocument pdfDocument, Stream stream)> pdfFiles, string title) { if (pdfFiles.Count == 0) { @@ -194,22 +165,18 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency { using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, true); - var index = 0; - foreach (var pdfFile in pdfFiles) + foreach (var (doc, pdfFile) in pdfFiles) { if (pdfFile.CanSeek) + { pdfFile.Position = 0; + } - var entry = zipArchive.CreateEntry($"{title}_{index}.pdf", CompressionLevel.Fastest); + var entry = zipArchive.CreateEntry($"{title}_{doc.Title}.pdf", CompressionLevel.Fastest); await using var entryStream = entry.Open(); await pdfFile.CopyToAsync(entryStream); - if (disposeStreams) - { - await pdfFile.DisposeAsync(); - } - - index++; + await pdfFile.DisposeAsync(); } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Puppeteer/PuppeteerHtmlToPdfRenderer.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Puppeteer/PuppeteerHtmlToPdfRenderer.cs index a47269ac97..3d1074408d 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Puppeteer/PuppeteerHtmlToPdfRenderer.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Puppeteer/PuppeteerHtmlToPdfRenderer.cs @@ -7,7 +7,7 @@ using Volo.Abp.DependencyInjection; namespace Volo.Docs.Projects.Pdf.Puppeteer; -public class PuppeteerHtmlToPdfRenderer : IHtmlToPdfRenderer, ITransientDependency +public class PuppeteerHtmlToPdfRenderer : IHtmlToPdfRenderer { public async Task RenderAsync(string title, string html, List documents) { @@ -16,7 +16,7 @@ public class PuppeteerHtmlToPdfRenderer : IHtmlToPdfRenderer, ITransientDependen Browser = SupportedBrowser.Chromium }.DownloadAsync(); - await using var browser = await PuppeteerSharp.Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, Timeout = 600000, Browser = SupportedBrowser.Chromium }); + await using var browser = await PuppeteerSharp.Puppeteer.LaunchAsync(new LaunchOptions { Headless = false, Timeout = 600000, Browser = SupportedBrowser.Chromium }); await using var page = await browser.NewPageAsync(); await page.SetContentAsync(html, new NavigationOptions