Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Convert HTML to PDF by Using Microsoft Edge in ASP.NET WebForms

5.00/5 (8 votes)
1 Dec 2022CPOL5 min read 10.6K   206  
Generate PDF from HTML with MS Edge
Use MS Edge as PDF Generator to Convert HTML to PDF

Image 1

Introduction

If you are running on Windows Server 2019, you may download Microsoft Edge at: https://www.microsoft.com/en-us/edge/download

Here is the basic command line for doing the job:

*Note: the comand lines displayed below are separated for documentation purpose, but in runtime, all arguments must be on the same line (no line breaks).

msedge
--headless
--disable-gpu
--run-all-compositor-stages-before-draw
--print-to-pdf="{filePath}"
{url}

Example:

msedge --headless --disable-gpu --run-all-compositor-stages-before-draw
--print-to-pdf="D:\\mysite\temp\pdf\2059060194.pdf"
http://localhost:50964/temp/pdf/2059060194.html

Based on this, I have written a simple C# class library to automate this:

To begin, you can either download the source code and add the C# class file "pdf_edge.cs" into your project, or install the Nuget Package(Html-PDF-Edge);

Then, at your project:

To generate PDF and download as attachment:

C#
pdf_edge.GeneratePdfAttachment(html, "file.pdf");

To generate PDF and display in browser:

C#
pdf_edge.GeneratePdfInline(html);

Background

Previously, I posted an [article about Using Chrome as PDF generator] to convert HTML to PDF.

Later, I found out that Microsoft Edge can also do the same thing. Since Microsoft Edge is also a chromium based web browser, both Chrome and Edge shared the same parameters.

I have tested this implementation (using Edge) in the following environments:

  • Local IIS hosting
  • Web Shared Hosting (smarterasp.net)
  • VPS Web Hosting

All the above environments are able to generate PDF without issues. It runs smoothly without the need to configure the permission, Application Pool Identity and Website IIS authentication. I have a more seamless integration experience as compared to using Chrome.

The following screenshot shows that the execution of MS Edge is allowed even with default permission settings:

Image 2

Chrome.exe, however is not so permissive in most environments. This is because executing an EXE over a web server is generally prohibited due to security issues.

For Chrome.exe, I failed to run it at web hosting environment (smarterasp.net).

Even in Local IIS hosting, I have to set the Application Pool Identify to "LocalSystem" in order for Chrome.exe to run properly.

Image 3

But Microsoft Edge does not have such requirements. Microsoft Edge is able to be executed with lowest/default permission and security settings.

Therefore, it is highly recommended to use Microsoft Edge than Chrome.exe.

Important CSS Properties

There are a few necessary CSS that you have to include in the HTML page in order for this to work properly.

  1. Set page margin to 0 (zero).
  2. Set paper size.
  3. Wrap all content within a "div" with fixed width and margin.
  4. Use CSS of page-break-always to split between pages.
  5. All fonts must already be installed or hosted on your website.
  6. URL links for images, external CSS stylesheet reference must include the root path.

1. Set Page Margin to 0 (zero)

CSS
@page {
    margin: 0;
}

The purpose of doing this is to hide the header and footer:

Image 4

2. Set Paper Size

Example 1

CSS
@page {
    margin: 0;
    size: A4 portrait;
}

Example 2

CSS
@page {
    margin: 0;
    size: letter landscape;
}

Example 3

custom size (inch) *width then height

CSS
@page {
    margin: 0;
    size: 4in 6in;
}

Example 4

custom size (cm) *width then height

CSS
@page {
    margin: 0;
    size: 14cm 14cm;
}

For more options/info on the CSS of @page, you may refer to this link.

3. Wrap All Content Within a DIV with Fixed Width and Margin

Example:

HTML
<div class="page">
    <h1>Page 1</h1>
    <img src="/pdf.jpg" style="width: 100%; height: auto;" />
    <!-- The rest of the body content -->
</div>

Style the "div" with class "page" (act as the main block/wrapper/container). Since the page has zero margin, we need to manually specified the top margin in CSS:

CSS
.page {
    width: 18cm;
    margin: auto;
    margin-top: 10mm;
}

The width has to be specified.

The "margin: auto" will align the div block at center horizontally.

"margin-top: 10mm", will provide space between the main block and the edge of the paper at top section.

4. Use CSS of "Page-Break-After" to Split Between Pages

To split pages, use a "div" and style with CSS of "page-break-after".

CSS
page-break-after: always

Example:

HTML
<div class="page">
    <h1>Page 1</h1>
</div>

<div style="page-break-after: always"></div>

<div class="page">
    <h1>Page 2</h1>
</div>

<div style="page-break-after: always"></div>

<div class="page">
    <h1>Page 3</h1>
</div>

5. All Fonts Must Already Installed or Hosted in Your Website

The font rendering might not be working properly if the fonts are hosted at 3rd party's server, for example: Google Fonts. Try installing the fonts into your server Windows OS or host the fonts within your website.

6. URL Links for Images, External CSS Stylesheet Reference must Include the Root Path

For example, the following img tag might not be rendered properly. The image has the potential to be missing in the final rendered PDF output.

HTML
<img src="logo.png" />
<img src="images/logo.png" />

Instead, include the root path like this:

HTML
<img src="/logo.png" />
<img src="/images/logo.png" />

The Class Object of "pdf_edge.cs"

Here I'll explain a bit how the C# class works in behind.

As mentioned, the real work is done within the C# class of "pdf_edge.cs".

Start off by adding 2 using statements:

C#
using System.Diagnostics;
using System.IO;

Here is the main method that runs Microsoft Edge for converting HTML into PDF file:

C#
public static void GeneratePdf(string url, string filePath)
{
    using (var p = new Process())
    {
        p.StartInfo.FileName = "msedge";

        // arguments have to be in single line, no line breaks
        p.StartInfo.Arguments = $"--headless --disable-gpu 
            --run-all-compositor-stages-before-draw
            --print-to-pdf=\"{filePath}\" {url}";
        p.Start();
        p.WaitForExit();
    }
}

The enum for defining the Transmit Method:

C#
public enum TransmitMethod
{
    None,
    Attachment,
    Inline
}

Here is the code for preparing the URL for Microsoft Edge to generate the PDF:

C#
static void EdgePublish(string html, TransmitMethod transmitMethod, string filename)
{
    // Create a temporary folder for storing the PDF

    string folderTemp = HttpContext.Current.Server.MapPath("~/temp/pdf");

    if (!Directory.Exists(folderTemp))
    {
        Directory.CreateDirectory(folderTemp);
    }

    // Create 2 temporary filename

    Random rd = new Random();
    string randomstr = rd.Next(100000000, int.MaxValue).ToString();
            
    string fileHtml = HttpContext.Current.Server.MapPath($"~/temp/pdf/{randomstr}.html");
    string filePdf = HttpContext.Current.Server.MapPath($"~/temp/pdf/{randomstr}.pdf");

    // Create the HTML file

    File.WriteAllText(fileHtml, html);

    // Obtain the URL of the HTML file

    var r = HttpContext.Current.Request.Url;
    string url = $"{r.Scheme}://{r.Host}:{r.Port}/temp/pdf/{randomstr}.html";

    // Create the PDF file

    GeneratePdf(url, filePdf);

    // Obtain the file size

    FileInfo fi = new FileInfo(filePdf);
    string filelength = fi.Length.ToString();

    // Load the file into memory (byte array)

    byte[] ba = File.ReadAllBytes(filePdf);

    // Delete the 2 temp files from server

    try
    {
        File.Delete(filePdf);
    }
    catch { }

    try
    {
        File.Delete(fileHtml);
    }
    catch { }

    // set the transmission type

    HttpContext.Current.Response.Clear();

    if (transmitMethod == TransmitMethod.Inline)
    {
        // display in browser
        HttpContext.Current.Response.AddHeader("Content-Disposition", "inline");
    }
    else if (transmitMethod == TransmitMethod.Attachment)
    {
        // as downloadable document
        HttpContext.Current.Response.AddHeader("Content-Disposition", 
            $"attachment; filename=\"{filename}\"");
    }

    // finally, transmit the pdf for download

    HttpContext.Current.Response.ContentType = "application/pdf";
    HttpContext.Current.Response.AddHeader("Content-Length", filelength);
    HttpContext.Current.Response.BinaryWrite(ba);
    HttpContext.Current.Response.End();
}

Displaying A Loading GIF While Generating PDF

After the user clicked on the "Generate PDF" button, due to that the web server might takes some (short) time to process the PDF, the page might appears to be "freezed" or no response (but actually yes). This might makes the user become nervous and re-click on the button multiple times. Therefore, it's a good idea to display a message or GIF animated loading image to give the user a piece of mind that the server is in the process of generating the PDF.

Here is one of the example of a message box.

HTML
<div id="divLoading" class="divLoading" onclick="hideLoading();">
    <img src="5348585/loading.gif" /><br />
    Generating PDF...
</div>

The Image: loading.gif

Image 5

Style the message box:

CSS
.divLoading {
    width: 360px;
    font-size: 20pt;
    font-style: italic;
    font-family: Arial;
    z-index: 9;
    position: fixed;
    top: calc(50vh - 150px);
    left: calc(50vw - 130px);
    border: 10px solid #7591ef;
    border-radius: 25px;
    padding: 10px;
    text-align: center;
    background: #dce5ff;
    display: none;
    font-weight: bold;
}

Here is how it looks like:

Image 6

Example of Javascript that displays the message box:

JavaScript
<script type="text/javascript">
    function showLoading() {
        let d = document.getElementById("divLoading");
        d.style.display = "block";
        setTimeout(hideLoading, 2000);
    }

    function hideLoading() {
        let d = document.getElementById("divLoading");
        d.style.display = "none";
    }
</script>

The effect of this implementation can be viewed at the live demo site.

Cheers :)

*Bonus - Generate PDF in WinForms

Well, turns out that, Microsoft Edge can also be used in WinForms to Convert HTML to PDF.

Since this is running in WinForms, there is no web server to supply an URL. Therefore, in stead of using an URL to tell MS Edge the source of HTML, we can tell Microsoft Edge to load a local file.

So, just replace the URL:

http://localhost:59403/temp/82348723.html

to local file path:

C:\web\temp\82348723.html

and execute the command line like this:

msedge --headless --disable-gpu 
--run-all-compositor-stages-before-draw
--print-to-pdf="C:\file.pdf"
"C:\web\temp\82348723.html"

*Note: during real execution, all arguments must be on the same line.

In C#,

C#
using (var p = new Process())
{
    p.StartInfo.FileName = "msedge";
    p.StartInfo.Arguments = $@"--headless --disable-gpu --run-all-compositor-stages-before-draw --print-to-pdf=""C:\file.pdf"" ""C:\web\temp\82348723.html""";
    p.Start();
    p.WaitForExit();
}

and for all the media and resources, do not use root path.

This will be rendered:

<img src="images/logo.png" />
<img src="../images/logo.png" />

This will not be rendered:

<img src="/images/logo.png" />
<img src="/upfolder/images/logo.png" />

The images can also be rendered without saving as a physical file by encoding it as base64 images, for example:

HTML
<img src="data:image/png;base64,iVBORw0KGgoAAA.................." />

History

  • 2nd December, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)