是否有更好的方法?
编辑:新建议 使用PdfSharp的HTML渲染器生成PDF
(尝试了wkhtmltopdf并建议避免后)
HtmlRenderer.PdfSharp是一个完全由C#管理的代码,易于使用,线程安全且最重要的是免费(New BSD许可证)解决方案。
用法
Use Example Method.
public static Byte[] PdfSharpConvert(String html)
{
Byte[] res = null;
using (MemoryStream ms = new MemoryStream())
{
var pdf = TheArtOfDev.HtmlRenderer.PdfSharp.PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
pdf.Save(ms);
res = ms.ToArray();
}
return res;
}
--- 预编辑区域 ---
对于想在简单应用/环境中从HTML生成PDF的任何人,我将我的旧帖子留作建议。
https://www.nuget.org/packages/TuesPechkin/
特别适用于MVC Web应用程序 (但我认为您可以在任何 .net 应用程序中使用它)
https://www.nuget.org/packages/Rotativa/
他们都使用wkhtmtopdf二进制文件将html转换为pdf。它使用webkit引擎渲染页面,因此还可以解析css样式表。最近更新:2020年10月
这是我整理的HTML转PDF在.NET中的选项列表(包括一些免费和付费的选项)。
GemBox.Document
PDF Metamorphosis .Net
HtmlRenderer.PdfSharp
PuppeteerSharp
EO.Pdf
WnvHtmlToPdf_x64
IronPdf
Spire.PDF
Aspose.Html
EvoPDF
ExpertPdfHtmlToPdf
Zetpdf
PDFtron
WkHtmlToXSharp
SelectPDF
大多数HTML转PDF的转换器依赖于IE来进行HTML解析和渲染,当用户更新IE时,这可能会出现问题。 这里有一种不依赖于IE的转换器。
代码大致如下:
EO.Pdf.HtmlToPdf.ConvertHtml(htmlText, pdfFileName);
像许多其他转换器一样,您可以传递文本、文件名或URL。结果可以保存到文件或流中。
对于所有在.net 5
及以上寻找工作解决方案的人,这里有一个。
以下是我的工作解决方案。
wkhtmltopdf
:wkhtmltopdf
。public static string HtmlToPdf(string outputFilenamePrefix, string[] urls,
string[] options = null,
string pdfHtmlToPdfExePath = @"C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe")
{
string urlsSeparatedBySpaces = string.Empty;
try
{
//Determine inputs
if ((urls == null) || (urls.Length == 0))
throw new Exception("No input URLs provided for HtmlToPdf");
else
urlsSeparatedBySpaces = String.Join(" ", urls); //Concatenate URLs
string outputFilename = outputFilenamePrefix + "_" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss-fff") + ".PDF"; // assemble destination PDF file name
var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = pdfHtmlToPdfExePath,
Arguments = ((options == null) ? "" : string.Join(" ", options)) + " " + urlsSeparatedBySpaces + " " + outputFilename,
UseShellExecute = false, // needs to be false in order to redirect output
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none
WorkingDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location))
}
};
p.Start();
// read the output here...
var output = p.StandardOutput.ReadToEnd();
var errorOutput = p.StandardError.ReadToEnd();
// ...then wait n milliseconds for exit (as after exit, it can't read the output)
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
// if 0 or 2, it worked so return path of pdf
if ((returnCode == 0) || (returnCode == 2))
return outputFilename;
else
throw new Exception(errorOutput);
}
catch (Exception exc)
{
throw new Exception("Problem generating PDF from HTML, URLs: " + urlsSeparatedBySpaces + ", outputFilename: " + outputFilenamePrefix, exc);
}
}
HtmlToPdf("test", new string[] { "https://www.google.com" }, new string[] { "-s A5" });
HTML
字符串转换为PDF
,请微调上述方法,并将Arguments
替换为Process StartInfo
,如下所示:$@"/C echo | set /p=""{htmlText}"" | ""{pdfHtmlToPdfExePath}"" {((options == null) ? "" : string.Join(" ", options))} - ""C:\Users\xxxx\Desktop\{outputFilename}""";
这种方法的缺点:
wkhtmltopdf
不支持最新的HTML5
和CSS3
。因此,如果您尝试导出任何具有CSS GRID
的html,则输出结果将与预期不同。chrome headless
:var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe",
Arguments = @"/C --headless --disable-gpu --run-all-compositor-stages-before-draw --print-to-pdf-no-header --print-to-pdf=""C:/Users/Abdul Rahman/Desktop/test.pdf"" ""C:/Users/Abdul Rahman/Desktop/grid.html""",
}
};
p.Start();
// ...then wait n milliseconds for exit (as after exit, it can't read the output)
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
html
文件转换为 pdf
文件。url
转换为 pdf
,则使用以下内容作为 Process StartInfo
的 Argument
@"/C --headless --disable-gpu --run-all-compositor-stages-before-draw --print-to-pdf-no-header --print-to-pdf=""C:/Users/Abdul Rahman/Desktop/test.pdf"" ""https://www.google.com""",
此方法的缺点:
HTML5
和 CSS3
特性。输出将与您在浏览器中查看的相同,但是当通过 IIS 运行时,您需要在 LocalSystem
身份下运行应用程序的 AppliactionPool
,或者您需要为 IISUSRS
提供 read
/write
访问权限。Selenium WebDriver
:Selenium.WebDriver
和 Selenium.WebDriver.ChromeDriver
。public async Task<byte[]> ConvertHtmlToPdf(string html)
{
var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments), "ApplicationName");
Directory.CreateDirectory(directory);
var filePath = Path.Combine(directory, $"{Guid.NewGuid()}.html");
await File.WriteAllTextAsync(filePath, html);
var driverOptions = new ChromeOptions();
// In headless mode, PDF writing is enabled by default (tested with driver major version 85)
driverOptions.AddArgument("headless");
using var driver = new ChromeDriver(driverOptions);
driver.Navigate().GoToUrl(filePath);
// Output a PDF of the first page in A4 size at 90% scale
var printOptions = new Dictionary<string, object>
{
{ "paperWidth", 210 / 25.4 },
{ "paperHeight", 297 / 25.4 },
{ "scale", 0.9 },
{ "pageRanges", "1" }
};
var printOutput = driver.ExecuteChromeCommandWithResult("Page.printToPDF", printOptions) as Dictionary<string, object>;
var pdf = Convert.FromBase64String(printOutput["data"] as string);
File.Delete(filePath);
return pdf;
}
这种方法的优点:
这种方法的缺点:
如果我们在docker中运行应用程序,则可以克服上述缺点。我们所需要做的就是在使用Dockerfile构建应用程序映像时安装Chrome。
使用此方法,请确保在.csproj文件中添加<PublishChromeDriver>true</PublishChromeDriver>,如下所示:
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<PublishChromeDriver>true</PublishChromeDriver>
</PropertyGroup>
发布项目时,将会发布 chrome driver
。
这是我的工作项目存储库链接 - HtmlToPdf
JavaScript
中的 window.print()
从浏览器生成 PDF如果用户从浏览器使用您的应用程序,则可以依赖于 JavaScript
并使用必要的 print media css
和 window.print()
从浏览器生成 PDF。例如,在库存应用程序中从浏览器生成发票。
此方法的优点:
此方法的缺点:
Blazor
这样的 SPA
中,我们需要通过 iframe
对页面的部分进行打印。在尝试了多种可用选项并最终实现了基于Selenium
的解决方案后,我花了将近两天时间才得出上述答案,并且它已经可以正常工作。希望这能帮助你并节省你的时间。
您可以使用Google Chrome的无头模式打印到PDF功能。我发现这是最简单但也是最强大的方法。
var url = "https://dev59.com/AnRB5IYBdhLWcg3wpYmo";
var chromePath = @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
var output = Path.Combine(Environment.CurrentDirectory, "printout.pdf");
using (var p = new Process())
{
p.StartInfo.FileName = chromePath;
p.StartInfo.Arguments = $"--headless --disable-gpu --print-to-pdf={output} {url}";
p.Start();
p.WaitForExit();
}
测试一下它:
docker run --rm -p 3000:3000 thecodingmachine/gotenberg:6
Curl示例
curl --request POST \
--url http://localhost:3000/convert/url \
--header 'Content-Type: multipart/form-data' \
--form remoteURL=https://brave.com \
--form marginTop=0 \
--form marginBottom=0 \
--form marginLeft=0 \
--form marginRight=0 \
-o result.pdf
C#示例.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.IO;
using static System.Console;
namespace Gotenberg
{
class Program
{
public static async Task Main(string[] args)
{
try
{
var client = new HttpClient();
var formContent = new MultipartFormDataContent
{
{new StringContent("https://brave.com/"), "remoteURL"},
{new StringContent("0"), "marginTop" }
};
var result = await client.PostAsync(new Uri("http://localhost:3000/convert/url"), formContent);
await File.WriteAllBytesAsync("brave.com.pdf", await result.Content.ReadAsByteArrayAsync());
}
catch (Exception ex)
{
WriteLine(ex);
}
}
}
}
编译:
csc sample.cs -langversion:latest -reference:System.Net.Http.dll && mono ./sample.exe
这是一个免费的库,非常容易使用: OpenHtmlToPdf
string timeStampForPdfName = DateTime.Now.ToString("yyMMddHHmmssff");
string serverPath = System.Web.Hosting.HostingEnvironment.MapPath("~/FolderName");
string pdfSavePath = Path.Combine(@serverPath, "FileName" + timeStampForPdfName + ".FileExtension");
//OpenHtmlToPdf Library used for Performing PDF Conversion
var pdf = Pdf.From(HTML_String).Content();
//FOr writing to file from a ByteArray
File.WriteAllBytes(pdfSavePath, pdf.ToArray()); // Requires System.Linq
对于需要将HTML转换为PDF的需求来说,有一个好消息。正如这个答案所示,W3C标准css-break-3将解决这个问题...它是候选推荐,计划在2017或2018年进行测试后成为最终推荐。
虽然并不完全标准,但有一些解决方案,比如针对C#的插件,就像print-css.rocks展示的那样。
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.tool.xml;
byte[] pdf; // result will be here
var cssText = File.ReadAllText(MapPath("~/css/test.css"));
var html = File.ReadAllText(MapPath("~/css/test.html"));
using (var memoryStream = new MemoryStream())
{
var document = new Document(PageSize.A4, 50, 50, 60, 60);
var writer = PdfWriter.GetInstance(document, memoryStream);
document.Open();
using (var cssMemoryStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(cssText)))
{
using (var htmlMemoryStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(html)))
{
XMLWorkerHelper.GetInstance().ParseXHtml(writer, document, htmlMemoryStream, cssMemoryStream);
}
}
document.Close();
pdf = memoryStream.ToArray();
}