diff --git a/Directory.Packages.props b/Directory.Packages.props
index 62110b2326..1ba30a63dc 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -113,6 +113,7 @@
+
diff --git a/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs b/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
index 5af8b1c129..a28da09273 100644
--- a/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
+++ b/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
@@ -32,6 +32,7 @@ using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Volo.Abp.Account;
+using Volo.Abp.BackgroundJobs;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.Database;
using Volo.Abp.PermissionManagement.HttpApi;
@@ -62,7 +63,8 @@ namespace VoloDocs.Web
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpPermissionManagementHttpApiModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule)
- ,typeof(AbpCachingStackExchangeRedisModule)
+ ,typeof(AbpCachingStackExchangeRedisModule),
+ typeof(AbpBackgroundJobsModule)
)]
public class VoloDocsWebModule : AbpModule
{
@@ -72,6 +74,11 @@ namespace VoloDocs.Web
{
options.AddAssemblyResource(typeof(DocsResource), typeof(VoloDocsWebModule).Assembly);
});
+
+ PreConfigure(options =>
+ {
+ options.ApplicationName = context.Services.GetApplicationName()!;
+ });
}
public override void ConfigureServices(ServiceConfigurationContext context)
diff --git a/modules/docs/src/Volo.Docs.Admin.Application/BackgroundJobs/DocumentPdfGenerateJob.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/BackgroundJobs/DocumentPdfGenerateJob.cs
similarity index 100%
rename from modules/docs/src/Volo.Docs.Admin.Application/BackgroundJobs/DocumentPdfGenerateJob.cs
rename to modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/BackgroundJobs/DocumentPdfGenerateJob.cs
diff --git a/modules/docs/src/Volo.Docs.Admin.Application/BackgroundJobs/DocumentPdfGenerateJobArgs.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/BackgroundJobs/DocumentPdfGenerateJobArgs.cs
similarity index 87%
rename from modules/docs/src/Volo.Docs.Admin.Application/BackgroundJobs/DocumentPdfGenerateJobArgs.cs
rename to modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/BackgroundJobs/DocumentPdfGenerateJobArgs.cs
index be18746f17..ce8b734f2c 100644
--- a/modules/docs/src/Volo.Docs.Admin.Application/BackgroundJobs/DocumentPdfGenerateJobArgs.cs
+++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/BackgroundJobs/DocumentPdfGenerateJobArgs.cs
@@ -1,4 +1,5 @@
using System;
+using Volo.Abp.BackgroundJobs;
namespace Volo.Docs.Admin.BackgroundJobs;
diff --git a/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfAppService.cs b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfAppService.cs
index e06c4715ac..0cfdf48bd7 100644
--- a/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfAppService.cs
+++ b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfAppService.cs
@@ -2,6 +2,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Volo.Abp.Content;
+using Volo.Abp.Http;
using Volo.Docs.Projects;
using Volo.Docs.Projects.Pdf;
@@ -37,7 +38,7 @@ public class DocumentPdfAppService : DocsCommonAppServiceBase, IDocumentPdfAppSe
if (fileStream != null)
{
- return new RemoteStreamContent(fileStream, fileName, "application/pdf");
+ return new RemoteStreamContent(fileStream, fileName, MimeTypes.Application.Zip);
}
return null;
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
index e57312e16b..266983eb24 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
+++ b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
@@ -25,6 +25,7 @@
+
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs
index f20a0e6a51..8b8678930d 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs
@@ -30,7 +30,7 @@ public class DocsProjectPdfGeneratorOptions
///
/// The function to calculate the PDF file name.
- /// Default is "{project.ShortName}-{version}-{languageCode}.pdf".
+ /// Default is "{project.ShortName}-{version}-{languageCode}.zip".
///
public Func CalculatePdfFileName { get; set; }
@@ -108,6 +108,6 @@ public class DocsProjectPdfGeneratorOptions
width: 100%;
}";
- CalculatePdfFileName = (project, version, languageCode) => $"{project.ShortName}-{version}-{languageCode}.pdf";
+ CalculatePdfFileName = (project, version, languageCode) => $"{project.ShortName}-{version}-{languageCode}.zip";
}
}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IHtmlToPdfRenderer.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IHtmlToPdfRenderer.cs
index ad1cc5105b..56fdc2f995 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IHtmlToPdfRenderer.cs
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IHtmlToPdfRenderer.cs
@@ -6,7 +6,5 @@ namespace Volo.Docs.Projects.Pdf;
public interface IHtmlToPdfRenderer
{
- Task RenderAsync(string title, string html, List documents);
-
- Task MergePdfFilesAsync(List pdfFiles, string title, bool disposeStreams = true);
+ Task RenderAsync(string title, string html, List documents);
}
\ No newline at end of file
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 0a78ceb4fe..730ca898d4 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 ,ITransientDependency
+public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer
{
protected IOptions Options { get; }
@@ -21,7 +21,7 @@ public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer ,ITransientDependency
Options = options;
}
- public virtual Task RenderAsync(string title, string html, List documents)
+ public virtual Task RenderAsync(string title, string html, List documents)
{
var pdfStream = new MemoryStream();
var pdfWrite = new PdfWriter(pdfStream);
@@ -42,7 +42,7 @@ public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer ,ITransientDependency
textDocument.Close();
pdfStream.Position = 0;
- return Task.FromResult(pdfStream);
+ return Task.FromResult(pdfStream);
}
public virtual async Task MergePdfFilesAsync(List pdfFiles, string title, bool disposeStreams = true)
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 485903d79a..c93d5fe06e 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
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -31,7 +32,7 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency
protected Project Project { get; set; }
protected List AllPdfDocuments { get; } = [];
- protected int ChunkSize { get; set; } = 10;
+ protected int ChunkSize { get; set; } = 60;
public ProjectPdfGenerator(
IDocumentSourceFactory documentStoreFactory,
@@ -63,7 +64,7 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency
await SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode);
var title = Options.Value.CalculatePdfFileTitle?.Invoke(project) ?? project.Name;
- var tempStreams = new List();
+ var tempStreams = new List();
try
{
@@ -72,21 +73,28 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency
foreach (var (chunk, index) in documentChunks.Select((chunk, index) => (chunk, index)))
{
+ try
+ {
- Logger.LogInformation("Processing chunk {Index}/{Total}", index + 1, documentChunks.Count);
+ Logger.LogInformation("Processing chunk {Index}/{Total}", index + 1, documentChunks.Count);
- var chunkHtml = await BuildHtmlAsync(chunk);
+ var chunkHtml = await BuildHtmlAsync(chunk);
- var pdfStream = await HtmlToPdfRenderer.RenderAsync($"{title} - Part {index + 1}", chunkHtml, chunk);
+ var pdfStream = await HtmlToPdfRenderer.RenderAsync($"{title} - Part {index + 1}", chunkHtml, chunk);
- Logger.LogInformation("Chunk {Index} rendered to PDF", index + 1);
+ Logger.LogInformation("Chunk {Index} rendered to PDF", index + 1);
- tempStreams.Add(pdfStream);
+ tempStreams.Add(pdfStream);
+ }
+ catch (Exception e)
+ {
+ Logger.LogError(e, "Error processing chunk {Index} for project {ProjectName}", index + 1, project.Name);
+ }
}
Logger.LogInformation("All chunks processed, merging PDF files");
- using var mergedPdfStream = await MergePdfFilesAsync(tempStreams, title, disposeStreams: true);
+ var mergedPdfStream = await MergePdfFilesAsync(tempStreams, title, disposeStreams: true);
await ProjectPdfFileStore.SetAsync(project, version, languageCode, mergedPdfStream);
}
catch(Exception e)
@@ -174,15 +182,41 @@ 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 pdfFiles, string title, bool disposeStreams = true)
{
if (pdfFiles.Count == 0)
{
throw new ArgumentException("No PDF files to merge", nameof(pdfFiles));
}
- return await HtmlToPdfRenderer.MergePdfFilesAsync(pdfFiles, title, disposeStreams);
+ var zipStream = new MemoryStream();
+
+
+ {
+ using var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, true);
+ var index = 0;
+ foreach (var pdfFile in pdfFiles)
+ {
+ if (pdfFile.CanSeek)
+ pdfFile.Position = 0;
+
+ var entry = zipArchive.CreateEntry($"{title}_{index}.pdf", CompressionLevel.Fastest);
+ await using var entryStream = entry.Open();
+ await pdfFile.CopyToAsync(entryStream);
+
+ if (disposeStreams)
+ {
+ await pdfFile.DisposeAsync();
+ }
+
+ index++;
+ }
+ }
+
+ zipStream.Position = 0;
+ return zipStream;
}
+
protected virtual IDocumentToHtmlConverter GetDocumentToHtmlConverter(Project project, PdfDocument pdfDocument)
{
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
new file mode 100644
index 0000000000..a47269ac97
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Puppeteer/PuppeteerHtmlToPdfRenderer.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using PuppeteerSharp;
+using PuppeteerSharp.Media;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Docs.Projects.Pdf.Puppeteer;
+
+public class PuppeteerHtmlToPdfRenderer : IHtmlToPdfRenderer, ITransientDependency
+{
+ public async Task RenderAsync(string title, string html, List documents)
+ {
+ await new BrowserFetcher()
+ {
+ Browser = SupportedBrowser.Chromium
+ }.DownloadAsync();
+
+ await using var browser = await PuppeteerSharp.Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, Timeout = 600000, Browser = SupportedBrowser.Chromium });
+ await using var page = await browser.NewPageAsync();
+
+ await page.SetContentAsync(html, new NavigationOptions
+ {
+ Timeout = 600000,
+ });
+
+ var pdfOptions = new PdfOptions
+ {
+ Format = PaperFormat.A4,
+ MarginOptions = new MarginOptions { Top = "20mm", Bottom = "20mm", Left = "15mm", Right = "15mm" },
+ };
+
+ var stream = await page.PdfStreamAsync(pdfOptions);
+ await page.CloseAsync();
+ await browser.CloseAsync();
+ return stream;
+ }
+}
\ No newline at end of file