I want to generate a PDF by passing HTML contents to a function. I have made use of iTextSharp for this but it does not perform well when it encounters tables and the layout just gets messy.
Is there a better way?
I want to generate a PDF by passing HTML contents to a function. I have made use of iTextSharp for this but it does not perform well when it encounters tables and the layout just gets messy.
Is there a better way?
Last Updated: October 2020
This is the list of options for HTML to PDF conversion in .NET that I have put together (some free some paid)
GemBox.Document
PDF Metamorphosis .Net
HtmlRenderer.PdfSharp
PuppeteerSharp
EO.Pdf
WnvHtmlToPdf_x64
IronPdf
Spire.PDF
Aspose.Html
EvoPDF
ExpertPdfHtmlToPdf
Zetpdf
PDFtron
WkHtmlToXSharp
SelectPDF
If none of the options above help you you can always search the NuGet packages:
https://www.nuget.org/packages?q=html+pdf
For all those looking for an working solution in .net 5 and above here you go.
Here are my working solutions.
wkhtmltopdf:wkhtmltopdf latest version from here.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 string to PDF, the tweak the above method and replace the Arguments to Process StartInfo as $@"/C echo | set /p=""{htmlText}"" | ""{pdfHtmlToPdfExePath}"" {((options == null) ? "" : string.Join(" ", options))} - ""C:\Users\xxxx\Desktop\{outputFilename}""";Drawbacks of this approach:
wkhtmltopdf as of posting this answer does not support latest HTML5 and CSS3. Hence if you try to export any html that as CSS GRID then the output will not be as expected.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 file to pdf file.url to pdf then use the following as Argument to Process StartInfo@"/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""",
Drawbacks of this approach:
HTML5 and CSS3 features. Output will be same as you view in browser but when running this via IIS you need to run the AppliactionPool of your application under LocalSystem Identity or you need to provide read/write access to IISUSRS.Selenium WebDriver:Selenium.WebDriver and 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;
}
Advantage of this method:
HTML5 and CSS3 features. Output will be same as you view in browser.Drawbacks of this approach:
Selenium.WebDriver.ChromeDriver Nuget package needs to be updated. Else this will throw run time error due to version mismatch.With this approach, please make sure to add <PublishChromeDriver>true</PublishChromeDriver> in .csproj file as shown below:
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<PublishChromeDriver>true</PublishChromeDriver>
</PropertyGroup>
This will publish the chrome driver when publishing the project.
Here is the link to my working project repo - HtmlToPdf
window.print() in JavaScript to generate PDF from browserIf the users are using your app from browser then you can rely on JavaScript and use window.print() and necessary print media css to generate PDF from the browser. For example generating invoice from browser in an inventory app.
Advantage of this method:
Drawbacks of this approach:
SPA like Blazor, we need to do some workaround with iframe to print sections of page.I arrived at the above answer after almost spending 2 days with available options and finally implemented Selenium based solution and it's working. Hope this helps you and save your time.
You can use Google Chrome print-to-pdf feature from its headless mode. I found this to be the simplest yet the most robust method.
var url = "https://stackoverflow.com/questions/564650/convert-html-to-pdf-in-net";
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();
}
Quite likely most projects will wrap a C/C++ engine rather than implementing a C# solution from scratch. Try Project Gotenberg.
To test it
docker run --rm -p 3000:3000 thecodingmachine/gotenberg:6
Curl sample
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# sample.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);
}
}
}
}
To compile
csc sample.cs -langversion:latest -reference:System.Net.Http.dll && mono ./sample.exe
It depends on any other requirements you have.
A really simple but not easily deployable solution is to use a WebBrowser control to load the Html and then using the Print method printing to a locally installed PDF printer. There are several free PDF printers available and the WebBrowser control is a part of the .Net framework.
EDIT: If you Html is XHtml you can use PDFizer to do the job.
It seems like so far the best free .NET solution is the TuesPechkin library which is a wrapper around the wkhtmltopdf native library.
I've now used the single-threaded version to convert a few thousand HTML strings to PDF files and it seems to work great. It's supposed to also work in multi-threaded environments (IIS, for example) but I haven't tested that.
Also since I wanted to use the latest version of wkhtmltopdf (0.12.5 at the time of writing), I downloaded the DLL from the official website, copied it to my project root, set copy to output to true, and initialized the library like so:
var dllDir = AppDomain.CurrentDomain.BaseDirectory;
Converter = new StandardConverter(new PdfToolset(new StaticDeployment(dllDir)));
Above code will look exactly for "wkhtmltox.dll", so don't rename the file. I used the 64-bit version of the DLL.
Make sure you read the instructions for multi-threaded environments, as you will have to initialize it only once per app lifecycle so you'll need to put it in a singleton or something.
I was also looking for this a while back. I ran into HTMLDOC http://www.easysw.com/htmldoc/ which is a free open source command line app that takes an HTML file as an argument and spits out a PDF from it. It's worked for me pretty well for my side project, but it all depends on what you actually need.
The company that makes it sells the compiled binaries, but you are free to download and compile from source and use it for free. I managed to compile a pretty recent revision (for version 1.9) and I intend on releasing a binary installer for it in a few days, so if you're interested I can provide a link to it as soon as I post it.
Edit (2/25/2014): Seems like the docs and site moved to http://www.msweet.org/projects.php?Z1
This is a free library and works very easily : 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
To convert HTML to PDF in C# use ABCpdf.
ABCpdf can make use of the Gecko or Trident rendering engines, so your HTML table will look the same as it appears in FireFox and Internet Explorer.
There's an on-line demo of ABCpdf at www.abcpdfeditor.com. You could use this to check out how your tables will render first, without needing to download and install software.
For rendering entire web pages you'll need the AddImageUrl or AddImageHtml functions. But if all you want to do is simply add HTML styled text then you could try the AddHtml function, as below:
Doc theDoc = new Doc();
theDoc.FontSize = 72;
theDoc.AddHtml("<b>Some HTML styled text</b>");
theDoc.Save(Server.MapPath("docaddhtml.pdf"));
theDoc.Clear();
ABCpdf is a commercial software title, however the standard edition can often be obtained for free under special offer.
Try this PDF Duo .Net converting component for converting HTML to PDF from ASP.NET application without using additional dlls.
You can pass the HTML string or file, or stream to generate the PDF. Use the code below (Example C#):
string file_html = @"K:\hdoc.html";
string file_pdf = @"K:\new.pdf";
try
{
DuoDimension.HtmlToPdf conv = new DuoDimension.HtmlToPdf();
conv.OpenHTML(file_html);
conv.SavePDF(file_pdf);
textBox4.Text = "C# Example: Converting succeeded";
}
Info + C#/VB examples you can find at: http://www.duodimension.com/html_pdf_asp.net/component_html_pdf.aspx
Another trick you can use WebBrowser control, below is my full working code
Assigning Url to text box control in my case
protected void Page_Load(object sender, EventArgs e)
{
txtweburl.Text = "https://www.google.com/";
}
Below is code for generate screeen using thread
protected void btnscreenshot_click(object sender, EventArgs e)
{
// btnscreenshot.Visible = false;
allpanels.Visible = true;
Thread thread = new Thread(GenerateThumbnail);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private void GenerateThumbnail()
{
// btnscreenshot.Visible = false;
WebBrowser webrowse = new WebBrowser();
webrowse.ScrollBarsEnabled = false;
webrowse.AllowNavigation = true;
string url = txtweburl.Text.Trim();
webrowse.Navigate(url);
webrowse.Width = 1400;
webrowse.Height = 50000;
webrowse.DocumentCompleted += webbrowse_DocumentCompleted;
while (webrowse.ReadyState != WebBrowserReadyState.Complete)
{
System.Windows.Forms.Application.DoEvents();
}
}
In below code I am saving the pdf file after download
private void webbrowse_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// btnscreenshot.Visible = false;
string folderPath = Server.MapPath("~/ImageFiles/");
WebBrowser webrowse = sender as WebBrowser;
//Bitmap bitmap = new Bitmap(webrowse.Width, webrowse.Height);
Bitmap bitmap = new Bitmap(webrowse.Width, webrowse.Height, PixelFormat.Format16bppRgb565);
webrowse.DrawToBitmap(bitmap, webrowse.Bounds);
string Systemimagedownloadpath = System.Configuration.ConfigurationManager.AppSettings["Systemimagedownloadpath"].ToString();
string fullOutputPath = Systemimagedownloadpath + Request.QueryString["VisitedId"].ToString() + ".png";
MemoryStream stream = new MemoryStream();
bitmap.Save(fullOutputPath, System.Drawing.Imaging.ImageFormat.Jpeg);
//generating pdf code
Document pdfDoc = new Document(new iTextSharp.text.Rectangle(1100f, 20000.25f));
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
pdfDoc.Open();
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(fullOutputPath);
img.ScaleAbsoluteHeight(20000);
img.ScaleAbsoluteWidth(1024);
pdfDoc.Add(img);
pdfDoc.Close();
//Download the PDF file.
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=ImageExport.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Write(pdfDoc);
Response.End();
}
You can also refer my oldest post for more information: Navigation to the webpage was canceled getting message in asp.net web form