diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj index 1c7593897..34b1d5c30 100644 --- a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Core/LINGYUN.Abp.Exporter.Core.csproj @@ -15,12 +15,11 @@ - + - - + diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN.Abp.Exporter.Pdf.LibreOffice.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN.Abp.Exporter.Pdf.LibreOffice.csproj new file mode 100644 index 000000000..b14819d8c --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN.Abp.Exporter.Pdf.LibreOffice.csproj @@ -0,0 +1,21 @@ + + + + + + + net8.0;net9.0 + LINGYUN.Abp.Exporter.Pdf.LibreOffice + LINGYUN.Abp.Exporter.Pdf.LibreOffice + false + false + false + enable + + + + + + + + diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/AbpExporterPdfLibreOfficeModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/AbpExporterPdfLibreOfficeModule.cs new file mode 100644 index 000000000..d686cbc12 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/AbpExporterPdfLibreOfficeModule.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Exporter.Pdf.LibreOffice; + +[DependsOn(typeof(AbpExporterPdfModule))] +public class AbpExporterPdfLibreOfficeModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + if (!LibreOfficeCommands.IsLibreOffliceInstalled()) + { + throw new Volo.Abp.AbpInitializationException("Libreoffice not installed in the current operation environment of the host, please refer to the document after installation using ` AbpExporterPdfLibreOfficeModule ` module."); + } + context.Services.AddTransient(); + } +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/LibreOfficeCommands.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/LibreOfficeCommands.cs new file mode 100644 index 000000000..5209364f6 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/LibreOfficeCommands.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Exporter.Pdf.LibreOffice; + +public class LibreOfficeCommands +{ + public static string WindowsCli { get; set; } = "soffice.com"; + public static string WindowsCliDir { get; set; } = "C:\\Program Files\\LibreOffice\\program\\"; + + public static string UnixCli { get; set; } = "libreoffice"; + public static string UnixCliDir { get; set; } = ""; + + public static string GetCli() + { + if (OperatingSystem.IsWindows()) + { + return Path.Combine(WindowsCliDir, WindowsCli); + } + + // 详细的操作系统版本: https://zh-cn.libreoffice.org/get-help/system-requirements/ + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + return Path.Combine(UnixCliDir, UnixCli); + } + + throw new PlatformNotSupportedException($"The current platform {Environment.OSVersion.ToString()} does not support the libreoffice runtime library"); + } + + public static bool IsLibreOffliceInstalled() + { + return LibreOfficeCommands.IsLibreOfficeAvailable(GetCli()); + } + /// + /// LibreOffice是否可用 + /// + /// + /// + public static bool IsLibreOfficeAvailable(string commandFile) + { + try + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = commandFile, + Arguments = "--version", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + var output = process.StandardOutput.ReadToEnd(); + + process.WaitForExit(); + + return process.ExitCode == 0 && output.Contains("LibreOffice"); + } + catch + { + return false; + } + } + /// + /// Excel转换为Pdf + /// + /// + /// + /// + public async static Task ExcelToPdf(string excelFile, string outputPath, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + var start = new ProcessStartInfo + { + FileName = GetCli(), + Arguments = $"--headless --convert-to pdf \"{excelFile}\" --outdir \"{outputPath}\"", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + var process = new Process + { + StartInfo = start, + }; + process.Start(); + + await process.WaitForExitAsync(cancellationToken); + + if (process.ExitCode != 0) + { + throw new Exception($"Excel failed to convert to PDF. Error code: {process.ExitCode}"); + } + } +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/LibreOfficeExcelToPdfProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/LibreOfficeExcelToPdfProvider.cs new file mode 100644 index 000000000..0241e0d03 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/LINGYUN/Abp/Exporter/Pdf/LibreOffice/LibreOfficeExcelToPdfProvider.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.IO; + +namespace LINGYUN.Abp.Exporter.Pdf.LibreOffice; +public class LibreOfficeExcelToPdfProvider : IExcelToPdfProvider +{ + public async virtual Task ParseAsync(Stream excelStream, CancellationToken cancellationToken = default) + { + var outputPath = Path.Combine(Path.GetTempPath(), "excel2pdf"); + + DirectoryHelper.CreateIfNotExists(outputPath); + + var templateFileId = Guid.NewGuid().ToString(); + var tempExcelFile = Path.Combine(outputPath, $"{templateFileId}.xlsx"); + var tempPdfFile = Path.Combine(outputPath, $"{templateFileId}.pdf"); + + try + { + if (!File.Exists(tempExcelFile)) + { + using (var excelFile = File.Create(tempExcelFile)) + { + await excelStream.CopyToAsync(excelFile, cancellationToken); + } + } + + await LibreOfficeCommands.ExcelToPdf(tempExcelFile, outputPath, cancellationToken); + + var pdfStream = new MemoryStream(); + + using (var pdfFileStream = File.OpenRead(tempPdfFile)) + { + await pdfFileStream.CopyToAsync(pdfStream, cancellationToken); + pdfStream.Seek(0, SeekOrigin.Begin); + } + + return pdfStream; + } + catch + { + throw; + } + finally + { + FileHelper.DeleteIfExists(tempExcelFile); + FileHelper.DeleteIfExists(tempPdfFile); + } + } +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/README.md new file mode 100644 index 000000000..8af96eb9f --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.LibreOffice/README.md @@ -0,0 +1,26 @@ +# LINGYUN.Abp.Exporter.Pdf.LibreOffice + +> LibreOffice is Free and Open Source Software. Development is open to new talent and new ideas, and our software is tested and used daily by a large and devoted user community. + +此模块使用本地 `LibreOffice` 命令行实现将Excel转换为Pdf, 请引用此模块前确保已安装有 `LibreOffice`, 如未安装在默认目录, 请在使用前手动指定安装目录. + +## 配置使用 + + +```csharp + + [DependsOn( + typeof(AbpExporterPdfLibreOfficeModule) + )] + public class YouProjectModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + // 手动指定安装目录 + LibreOfficeCommands.WindowsCliDir = "path\\to\\libreoffice"; + LibreOfficeCommands.UnixCliDir = "path/to/libreoffice"; + } + } +``` + + diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN.Abp.Exporter.Pdf.SpireLib.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN.Abp.Exporter.Pdf.SpireLib.csproj new file mode 100644 index 000000000..0935705a0 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN.Abp.Exporter.Pdf.SpireLib.csproj @@ -0,0 +1,25 @@ + + + + + + + net8.0;net9.0 + LINGYUN.Abp.Exporter.Pdf.SpireLib + LINGYUN.Abp.Exporter.Pdf.SpireLib + false + false + false + enable + + + + + + + + + + + + diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN/Abp/Exporter/Pdf/SpireLib/AbpExporterPdfSpireLibModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN/Abp/Exporter/Pdf/SpireLib/AbpExporterPdfSpireLibModule.cs new file mode 100644 index 000000000..fa3d1d0b7 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN/Abp/Exporter/Pdf/SpireLib/AbpExporterPdfSpireLibModule.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Exporter.Pdf.SpireLib; + +[DependsOn(typeof(AbpExporterPdfModule))] +public class AbpExporterPdfSpireLibModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddTransient(); + } +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN/Abp/Exporter/Pdf/SpireLib/SpireExcelToPdfProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN/Abp/Exporter/Pdf/SpireLib/SpireExcelToPdfProvider.cs new file mode 100644 index 000000000..217709507 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/LINGYUN/Abp/Exporter/Pdf/SpireLib/SpireExcelToPdfProvider.cs @@ -0,0 +1,20 @@ +using Spire.Xls; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Exporter.Pdf.SpireLib; + +public class SpireExcelToPdfProvider : IExcelToPdfProvider +{ + public virtual Task ParseAsync(Stream excelStream, CancellationToken cancellationToken = default) + { + using var workBook = new Workbook(); + Stream memoryStream = new MemoryStream(); + workBook.LoadFromStream(excelStream); + var workSheet = workBook.Worksheets[0]; + workSheet.SaveToPdfStream(memoryStream); + + return Task.FromResult(memoryStream); + } +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/README.md new file mode 100644 index 000000000..526cedb73 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf.SpireLib/README.md @@ -0,0 +1,25 @@ +# LINGYUN.Abp.Exporter.Pdf.SpireLib + +> Spire.XLS for .NET 是一款专业的 .NET Excel 组件, 它可以用在各种 .NET 框架中,包括 .NET Core、.NET 5.0、.NET 6.0、.NET 7.0、.NET Standard、 Xamarin、Mono Android、ASP.NET 和 Windows Forms 等相关的 .NET 应用程序。Spire.XLS for .NET 提供了一个对象模型 Excel API,使开发人员可以快速地在 .NET 平台上完成对 Excel 的各种编程操作,如根据模板创建新的 Excel 文档,编辑现有 Excel 文档以及对 Excel 文档进行转换。 + +此模块使用 [Spire.XLS](https://www.e-iceblue.cn/spirexls/spire-xls-for-net-program-guide-content.html) 实现将Excel转换为Pdf,请在使用前配置许可. + +## 配置使用 + + +```csharp + + [DependsOn( + typeof(AbpExporterPdfModule) + )] + public class YouProjectModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + // 配置许可 + Spire.Xls.License.LicenseProvider.SetLicense("xxx"); + } + } +``` + + diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/FodyWeavers.xml b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/FodyWeavers.xsd b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN.Abp.Exporter.Pdf.csproj b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN.Abp.Exporter.Pdf.csproj new file mode 100644 index 000000000..b2f7aee0f --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN.Abp.Exporter.Pdf.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0 + LINGYUN.Abp.Exporter.Pdf + LINGYUN.Abp.Exporter.Pdf + false + false + false + enable + + + + + + + + diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/AbpExporterPdfModule.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/AbpExporterPdfModule.cs new file mode 100644 index 000000000..ee07ff723 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/AbpExporterPdfModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Exporter.Pdf; + +public class AbpExporterPdfModule : AbpModule +{ + +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/IExcelToPdfProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/IExcelToPdfProvider.cs new file mode 100644 index 000000000..93cd59d11 --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/IExcelToPdfProvider.cs @@ -0,0 +1,10 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Exporter.Pdf; + +public interface IExcelToPdfProvider +{ + Task ParseAsync(Stream excelStream, CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/NullExcelToPdfProvider.cs b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/NullExcelToPdfProvider.cs new file mode 100644 index 000000000..293adf18e --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/LINGYUN/Abp/Exporter/Pdf/NullExcelToPdfProvider.cs @@ -0,0 +1,16 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.Exporter.Pdf; + +[Dependency(TryRegister = true)] +public class NullExcelToPdfProvider : IExcelToPdfProvider, ISingletonDependency +{ + private readonly static Stream _nullStreamCache = Stream.Null; + public virtual Task ParseAsync(Stream excelStream, CancellationToken cancellationToken = default) + { + return Task.FromResult(_nullStreamCache); + } +} diff --git a/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/README.md b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/README.md new file mode 100644 index 000000000..9ac74054d --- /dev/null +++ b/aspnet-core/framework/exporter/LINGYUN.Abp.Exporter.Pdf/README.md @@ -0,0 +1,38 @@ +# LINGYUN.Abp.Exporter.Pdf + +Pdf导出模块 + +## 配置使用 + + +```csharp + + [DependsOn( + typeof(AbpExporterPdfModule) + )] + public class YouProjectModule : AbpModule + { + + } +``` + +> 导出数据 +```csharp + public class ExportDemoClass + { + private readonly IExcelToPdfProvider _exporterProvider; + + public ExportDemoClass(IExcelToPdfProvider exporterProvider) + { + _exporterProvider = exporterProvider; + } + + public async virtual Task ExportAsync(Stream excelStream) + { + var stream = await _exporterProvider.ParseAsync(excelStream); + + return new RemoteStreamContent(stream, "demo.pdf"); + } + } +``` +