HiQPdf Next Markdown to PDF Converter for .NET

HiQPdf Next HTML to PDF Converter for .NET

The Markdown to PDF Converter is a component of the HiQPdf Next Library for .NET that enables accurate conversion of complex MD documents to PDF. You can enhance the generated PDF with a table of contents, headers, footers and stamps or apply security features.

You can deploy the library on a variety of Windows and Linux platforms, including Azure App Service and Functions or Docker.

Download Now
Online Demo
Documentation

The Markdown to PDF Converter is a component of the HiQPdf Next Library for .NET that enables accurate conversion of complex Markdown documents to PDF. You can see the list of all HiQPdf Next components on the library page.

Compatibility Platforms

HiQPdf Next for .NET can run on a variety of Windows and Linux platforms in web, console and desktop applications across all modern .NET platforms. The library components can be used in Azure App Service and Azure Functions environments on both Windows and Linux. Deployment to Docker Windows and Linux containers is also supported.
The .NET library targets .NET Standard 2.0, which makes it compatible with a wide range of .NET Core and .NET Framework applications.

Getting Started

The online documentation contains Getting Started guides for Windows, Linux, Azure App Service and Azure Functions, with detailed instructions for integrating the library into your application and complete C# examples for each important feature of the library.
You can see the current capabilities of the library by checking the online demo application for this library and the API reference in the online documentation.

Download Demo Application

You can also download a free trial package for .NET, which includes an ASP.NET demo application project with complete C# source code as a starting point for experimenting with your own usage scenarios.
Running the samples in the demo application that involve Markdown to PDF conversion features on Linux platforms might require installing some dependency packages. The documentation includes an entire section dedicated to building, publishing and running the demo application on multiple platforms.

NuGet Packages

For Windows deployments add a reference to the HiQPdf.Next.MarkdownToPdf.Windows NuGet Package and for Linux deployments add a reference to the HiQPdf.Next.MarkdownToPdf.Linux NuGet Package.
The package for Windows is referenced by the HiQPdf.Next.Windows metapackage for all components and the package for Linux is referenced by the HiQPdf.Next.Linux metapackage for all components.
There are also multiplatform metapackages that reference both the Windows and Linux Markdown to PDF packages: HiQPdf.Next.MarkdownToPdf for the Markdown to PDF functionality and HiQPdf.Next for the entire HiQPdf Next library.

Installation

The Markdown to PDF Converter component uses a platform specific runtime. On Windows platforms, the runtime generally does not require the installation of additional dependencies. On Linux platforms installing some dependency packages might be necessary, depending on the exact version of Linux you are using. In online documentation in the Getting Started and Publish guides you can find instructions about Linux dependencies installation on a variety of Linux platforms.

HiQPdf.Next Namespace

All components of the HiQPdf Next for .NET library share the same HiQPdf.Next namespace and can be used together in the same application. To use the library in your own code, add the using directive at the top of your C# source file, as shown below.
// Include the HiQPdf.Next namespace at the top of your C# file
using HiQPdf.Next;

Sample Code

After you add the reference to the NuGet package to your project, use the sample code below to convert a Markdown document to a PDF document that you can either save to a file or send to the browser for download.
// Create a Markdown to PDF converter object with default settings
MarkdownToPdfConverter markdownToPdfConverter = new MarkdownToPdfConverter();

// Convert the Markdown string to a PDF document in a memory buffer
byte[] outPdfBuffer = markdownToPdfConverter.ConvertStringToPdf(markdownString, baseUrl);

Features List

HiQPdf Next Markdown to PDF for .NET offers advanced options for converting Markdown to PDF. You can apply custom CSS styling rules to the generated PDF document. You can also automatically generate a table of contents. You can reflow the content using your own custom PDF page size, orientation and margins. You can apply HTML headers, footers, stamps, security features and digital signatures to the generated PDF documents.

Convert Markdown From a String or From File to PDF

You can convert Markdown documents from a string or from a file to PDF in a memory buffer or in a file. When you produce the result in a memory buffer you can further save the content in a file or send it in a stream as a response to a browser.

Apply Custom Style Sheets

The Markdown to PDF converter allows you to apply custom CSS styling rules to the generated PDF document to customize the appearance of various elements from Markdown similar to their HTML equivalents.

Configure PDF Page Settings

You can use your own custom PDF page size, orientation and margins to reflow the content in the generated PDF document.

Add HTML header, footer or stamps to the generated PDF

You can add a header, footer and stamps from a URL or from an HTML string. The header, footer and stamps can include variables for page number or total number of pages and support automatic resizing.

Automatically Create a Table of Contents Based on Headings in Markdown

The converter can automatically create a table of contents based on the titles and subtitles defined in the Markdown document using heading elements. The creation and appearance of the table of contents can be controlled by a variety of properties.

Control the PDF Document Display with PDF Viewer Preferences

The converter offers the possibility to control how the generated PDF document is displayed in a PDF viewer such as Adobe Reader. There are options to hide the PDF viewer menu and toolbar, to initially display the Bookmarks, Thumbnails or Attachments panel and to display the PDF pages in one or two columns.

Create Secured and Digitally Signed PDF Documents

The converter offers you multiple possibilities to secure and to set the permitted operations on the generated PDF document. You can password protect the PDF document with separate passwords to open the PDF document and to edit the permissions. It is also possible to add digital signatures to the generated PDF document using a certificate with private and public keys.

Asynchronous Markdown to PDF Methods

There are asynchronous variants of the Markdown to PDF methods with the Async suffix that allow the Markdown to PDF conversion to run in parallel using async and await.

Available on Both Windows and Linux Platforms

HiQPdf Next for .NET can run on both Windows 64-bit and Linux 64-bit platforms. There are different NuGet packages for Windows and Linux, including the same .NET library but with different native runtimes. For Windows, the minimum required version is Windows 10 or Windows Server 2016.

Built for .NET Standard 2.0 for Maximum Compatibility

The .NET library targets .NET Standard 2.0, making it compatible with a wide range of .NET Core and .NET Framework applications. It is compatible with .NET 10.0, 9.0, 8.0, 7.0, 6.0, .NET Standard 2.0 and .NET Framework 4.6.2 to 4.8.1.

Fully Compatible with Azure App Service and Azure Functions on Both Windows and Linux

The converter can run without restrictions in your Azure App Service and Azure Functions .NET Core applications targeting both Windows and Linux platforms. Web fonts and other features are fully supported by HiQPdf Next for .NET. Online documentation offers detailed usage instructions for Azure applications targeting both Windows and Linux.

NuGet Packages for Windows and Linux

HiQPdf Next for .NET is delivered as NuGet packages for Windows and Linux. The packages include the .NET Standard 2.0 library, the same for both platforms, and the specific native runtime for each platform.
ASP.NET Core Demo Application with C# Code for All Features
The zip package that can be downloaded from the website contains the project for the ASP.NET Core demo application with C# sample code for all major library features.

Simple and Flexible Licensing with a Single License for All Libraries

The license for HiQPdf Next for .NET works with both the classic HiQPdf Library for .NET and the multi-platform client-server solution. There are no additional runtime or deployment costs charged for using our software component in your applications.

HiQPdf Next for .NET - Markdown to PDF C# Code Sample for ASP.NET Core

using System;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using HiQPdf_Next_AspNetDemo.Models;

using HiQPdf.Next;

namespace HiQPdf_Next_AspNetDemo.Controllers
{
    public class MarkdownToPdfController : Controller
    {
        private readonly IWebHostEnvironment m_hostingEnvironment;

        public MarkdownToPdfController(IWebHostEnvironment hostingEnvironment)
        {
            m_hostingEnvironment = hostingEnvironment;
        }

        public IActionResult Index()
        {
            var model = SetViewModel();

            return View(model);
        }

        [HttpPost]
        public async Task<IActionResult> ConvertMarkdownToPdf(MarkdownToPdfViewModel model)
        {
            if (!ModelState.IsValid)
            {
                var errorMessage = ModelStateHelper.GetModelErrors(ModelState);
                throw new ValidationException(errorMessage);
            }

            // Replace the demo serial number with the serial number received upon purchase
            // to run the converter in licensed mode
            Licensing.SerialNumber = "YCgJMTAE-BiwJAhIB-EhlWTlBA-UEBRQFBA-U1FOUVJO-WVlZWQ==";

            // Create a Markdown to PDF converter object with default settings
            MarkdownToPdfConverter markdownToPdfConverter = new MarkdownToPdfConverter();

            // Set custom styling rules using CSS syntax
            markdownToPdfConverter.PdfDocumentOptions.StyleSheet = model.StyleSheet;

            // Set whether a table of contents is automatically generated from headings
            markdownToPdfConverter.PdfDocumentOptions.GenerateTableOfContents = model.GenerateToc;

            // Set PDF page size which can be a predefined size like A4 or a custom size in points 
            // Leave it not set to have a default A4 PDF page
            markdownToPdfConverter.PdfDocumentOptions.PageSize = SelectedPdfPageSize(model.PdfPageSize);

            // Set PDF page orientation to Portrait or Landscape
            // Leave it not set to have a default Portrait orientation for PDF page
            markdownToPdfConverter.PdfDocumentOptions.PageOrientation = SelectedPdfPageOrientation(model.PdfPageOrientation);

            // Set PDF page margins in points or leave them not set to have a PDF page without margins
            markdownToPdfConverter.PdfDocumentOptions.Margins.Left = model.LeftMargin;
            markdownToPdfConverter.PdfDocumentOptions.Margins.Right = model.RightMargin;
            markdownToPdfConverter.PdfDocumentOptions.Margins.Top = model.TopMargin;
            markdownToPdfConverter.PdfDocumentOptions.Margins.Bottom = model.BottomMargin;

            // Set the Markdown viewer zoom percentage
            markdownToPdfConverter.PdfDocumentOptions.Zoom = model.MarkdownViewerZoom;

            // Set PDF header and footer
            SetHeader(markdownToPdfConverter, model);
            SetFooter(markdownToPdfConverter, model);

            string markdownString = null;
            string baseUrl = null;

            if (model.MarkdownSource == "Url")
            {
                // Obtain the Markdown string and base URL from a Markdown file using UTF-8 encoding
                string markdownUrl = model.MarkdownFileUrl?.Trim();
                if (string.IsNullOrWhiteSpace(markdownUrl))
                    throw new Exception("No Markdown file provided: upload a file or specify a URL");

                byte[] inputMarkdownBytes = null;

                try
                {
                    if (markdownUrl.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
                    {
                        string localPath = new Uri(markdownUrl).LocalPath;
                        inputMarkdownBytes = await System.IO.File.ReadAllBytesAsync(localPath);
                    }
                    else
                    {
                        using var httpClient = new System.Net.Http.HttpClient();
                        inputMarkdownBytes = await httpClient.GetByteArrayAsync(markdownUrl);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Could not download the Markdown file from URL", ex);
                }

                markdownString = Encoding.UTF8.GetString(inputMarkdownBytes);
                baseUrl = markdownUrl;
            }
            else
            {
                // The Markdown string and base URL are provided directly
                markdownString = model.MarkdownString;
                baseUrl = model.BaseUrl;
            }

            // Convert the Markdown string to a PDF document in a memory buffer
            byte[] outPdfBuffer = markdownToPdfConverter.ConvertToPdf(markdownString, baseUrl);

            // Send the PDF file to browser
            FileResult fileResult = new FileContentResult(outPdfBuffer, "application/pdf");
            // send as attachment
            fileResult.FileDownloadName = "Markdown_to_Pdf.pdf";

            return fileResult;
        }

        private void SetHeader(MarkdownToPdfConverter markdownToPdfConverter, MarkdownToPdfViewModel model)
        {
            bool headerEnabled = model.HeaderEnabled;
            if (!headerEnabled)
                return;

            // Set the header HTML from a URL or from an HTML string
            bool headerHtmlFromUrl = model.HeaderHtmlSource == "Url";
            if (headerHtmlFromUrl)
            {
                string headerUrl = model.HeaderUrl;

                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.HtmlSourceUrl = headerUrl;
            }
            else
            {
                string headerHtml = model.HeaderHtml;
                string headerHtmlBaseUrl = model.HeaderHtmlBaseUrl;

                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.Html = headerHtml;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.HtmlBaseUrl = headerHtmlBaseUrl;
            }

            // Enable automatic height adjustment based on header HTML content
            bool autoSizeHeaderContentHeight = model.AutoSizeHeaderContentHeight;
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.AutoSizeContentHeight = autoSizeHeaderContentHeight;

            // Set the minimum and maximum content height used when AutoSizeContentHeight is enabled
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.MinContentHeight = model.HeaderMinContentHeight;
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.MaxContentHeight = model.HeaderMaxContentHeight;

            // Set a fixed height for the header if AutoResizeHeight is disabled
            if (model.HeaderHeight.HasValue)
            {
                int headerHeight = model.HeaderHeight.Value;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.Height = headerHeight;
            }

            // If AutoResizeHeight is enabled and both Height and FitHeight are set,
            // the content may be scaled down to fit the specified height
            bool fitHeaderHeight = model.FitHeaderHeight;
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.FitHeight = fitHeaderHeight;

            // Enable automatic top margin adjustment in the PDF based on the header
            bool autoResizeTopMargin = model.AutoResizeTopMargin;
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.AutoResizePdfMargins = autoResizeTopMargin;

            // Set header visibility on specific PDF pages: first page, odd-numbered pages and even-numbered pages
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.ShowInFirstPage = model.ShowHeaderInFirstPage;
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.ShowInOddPages = model.ShowHeaderInOddPages;
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.ShowInEvenPages = model.ShowHeaderInEvenPages;

            // Reserve space for the header on all pages, regardless of visibility
            // If false, the document will be rendered using print styles instead of screen styles
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.ReserveSpaceAlways = model.ReserveHeaderSpace;

            // Optimize the header rendering time by providing a hint if the HTML template contains variables such as { page_number} or { total_pages}
            markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.SkipVariablesParsing = model.SkipHeaderVariablesParsing;

            // Optionally set additional time to wait for the asynchronous header HTML content before rendering
            if (model.HeaderWaitBeforeConvert.HasValue && model.HeaderWaitBeforeConvert.Value > 0)
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlHeader.WaitBeforeConvert = model.HeaderWaitBeforeConvert.Value;
        }

        private void SetFooter(MarkdownToPdfConverter markdownToPdfConverter, MarkdownToPdfViewModel model)
        {
            bool footerEnabled = model.FooterEnabled;
            if (footerEnabled)
            {
                // Set the footer HTML from a URL or from an HTML string
                bool footerHtmlFromUrl = model.FooterHtmlSource == "Url";
                if (footerHtmlFromUrl)
                {
                    string footerUrl = model.FooterUrl;

                    markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.HtmlSourceUrl = footerUrl;
                }
                else
                {
                    string footerHtml = model.FooterHtml;
                    string footerHtmlBaseUrl = model.FooterHtmlBaseUrl;

                    markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.Html = footerHtml;
                    markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.HtmlBaseUrl = footerHtmlBaseUrl;
                }

                // Enable automatic height adjustment based on footer HTML content
                bool autoSizeFooterContentHeight = model.AutoSizeFooterContentHeight;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.AutoSizeContentHeight = autoSizeFooterContentHeight;

                // Set the minimum and maximum content height used when AutoSizeContentHeight is enabled
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.MinContentHeight = model.FooterMinContentHeight;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.MaxContentHeight = model.FooterMaxContentHeight;

                // Set a fixed height for the footer if AutoResizeHeight is disabled
                if (model.FooterHeight.HasValue)
                {
                    int footerHeight = model.FooterHeight.Value;
                    markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.Height = footerHeight;
                }

                // If AutoResizeHeight is enabled and both Height and FitHeight are set,
                // the content may be scaled down to fit the specified height
                bool fitFooterHeight = model.FitFooterHeight;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.FitHeight = fitFooterHeight;

                // Enable automatic bottom margin adjustment in the PDF based on the footer
                bool autoResizeBottomMargin = model.AutoResizeBottomMargin;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.AutoResizePdfMargins = autoResizeBottomMargin;

                // Set footer visibility on specific PDF pages: first page, odd-numbered pages and even-numbered pages
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.ShowInFirstPage = model.ShowFooterInFirstPage;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.ShowInOddPages = model.ShowFooterInOddPages;
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.ShowInEvenPages = model.ShowFooterInEvenPages;

                // Reserve space for the footer on all pages, regardless of visibility
                // If false, the document will be rendered using print styles instead of screen styles
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.ReserveSpaceAlways = model.ReserveFooterSpace;

                // Optimize the footer rendering time by providing a hint if the HTML template contains variables such as { page_number} or { total_pages}
                markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.SkipVariablesParsing = model.SkipFooterVariablesParsing;

                // Optionally set additional time to wait for the asynchronous footer HTML content before rendering
                if (model.FooterWaitBeforeConvert.HasValue && model.FooterWaitBeforeConvert.Value > 0)
                    markdownToPdfConverter.PdfDocumentOptions.PdfHtmlFooter.WaitBeforeConvert = model.FooterWaitBeforeConvert.Value;
            }
        }

        private PdfPageSize SelectedPdfPageSize(string selectedValue)
        {
            switch (selectedValue)
            {
                case "A0":
                    return PdfPageSize.A0;
                case "A1":
                    return PdfPageSize.A1;
                case "A10":
                    return PdfPageSize.A10;
                case "A2":
                    return PdfPageSize.A2;
                case "A3":
                    return PdfPageSize.A3;
                case "A4":
                    return PdfPageSize.A4;
                case "A5":
                    return PdfPageSize.A5;
                case "A6":
                    return PdfPageSize.A6;
                case "A7":
                    return PdfPageSize.A7;
                case "A8":
                    return PdfPageSize.A8;
                case "A9":
                    return PdfPageSize.A9;
                case "ArchA":
                    return PdfPageSize.ArchA;
                case "ArchB":
                    return PdfPageSize.ArchB;
                case "ArchC":
                    return PdfPageSize.ArchC;
                case "ArchD":
                    return PdfPageSize.ArchD;
                case "ArchE":
                    return PdfPageSize.ArchE;
                case "B0":
                    return PdfPageSize.B0;
                case "B1":
                    return PdfPageSize.B1;
                case "B2":
                    return PdfPageSize.B2;
                case "B3":
                    return PdfPageSize.B3;
                case "B4":
                    return PdfPageSize.B4;
                case "B5":
                    return PdfPageSize.B5;
                case "Flsa":
                    return PdfPageSize.Flsa;
                case "HalfLetter":
                    return PdfPageSize.HalfLetter;
                case "Ledger":
                    return PdfPageSize.Ledger;
                case "Legal":
                    return PdfPageSize.Legal;
                case "Letter":
                    return PdfPageSize.Letter;
                case "Letter11x17":
                    return PdfPageSize.Letter11x17;
                case "Note":
                    return PdfPageSize.Note;
                default:
                    return PdfPageSize.A4;
            }
        }

        private PdfPageOrientation SelectedPdfPageOrientation(string selectedValue)
        {
            return selectedValue == "Portrait" ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape;
        }

        private MarkdownToPdfViewModel SetViewModel()
        {
            var model = new MarkdownToPdfViewModel();

            var contentRootPath = System.IO.Path.Combine(m_hostingEnvironment.ContentRootPath, "wwwroot");

            HttpRequest request = ControllerContext.HttpContext.Request;
            UriBuilder uriBuilder = new UriBuilder();
            uriBuilder.Scheme = request.Scheme;
            uriBuilder.Host = request.Host.Host;
            if (request.Host.Port != null)
                uriBuilder.Port = (int)request.Host.Port;
            uriBuilder.Path = request.PathBase.ToString() + request.Path.ToString();
            uriBuilder.Query = request.QueryString.ToString();

            string currentPageUrl = uriBuilder.Uri.AbsoluteUri;
            string rootUrl = currentPageUrl.Substring(0, currentPageUrl.Length - "MarkdownToPdf".Length);

            model.MarkdownFileUrl = rootUrl + "/DemoFiles/Markdown/Markdown_Document.md";
            model.MarkdownString = System.IO.File.ReadAllText(System.IO.Path.Combine(contentRootPath, "DemoFiles/Markdown/Markdown_Document.md"));
            model.BaseUrl = rootUrl + "/DemoFiles/Markdown/";
            model.StyleSheet = System.IO.File.ReadAllText(System.IO.Path.Combine(contentRootPath, "DemoFiles/Markdown/Markdown_Style.css"));
            model.HeaderHtml = System.IO.File.ReadAllText(System.IO.Path.Combine(contentRootPath, "DemoFiles/Html/Header_HTML.html"));
            model.FooterHtml = System.IO.File.ReadAllText(System.IO.Path.Combine(contentRootPath, "DemoFiles/Html/Footer_HTML.html"));
            model.HeaderHtmlBaseUrl = rootUrl + "DemoFiles/Html/";
            model.HeaderUrl = rootUrl + "DemoFiles/Html/Header_HTML.html";
            model.FooterHtmlBaseUrl = rootUrl + "DemoFiles/Html/";
            model.FooterUrl = rootUrl + "DemoFiles/Html/Footer_HTML.html";

            return model;
        }
    }
}