Migrate HTML to PDF Page Setup and Headers from EvoPdf Classic

EvoPdf Classic and EvoPdf Next share the HtmlToPdfConverter class name, the conversion methods and most option names, but the two rendering engines place the HTML on the PDF page in different ways and describe headers and footers differently. This topic covers the two areas where an integration moved from Classic changes its output or its code: the page setup with its scaling rules and the headers and footers. The namespace, package and license key changes are covered in the Classic to Next migration guide on the website.

The Classic options referred to below are documented in HTML Content Destination and Scaling in PDF and in the EvoPdf Classic API reference. The Next options are explained in HTML to PDF Page Setup and Scaling.

Two Ways of Placing HTML on a Page

Classic renders the HTML in a virtual browser window of HtmlViewerWidth pixels, 1024 by default, or wider when the content does not fit unless ClipHtmlView is set. It then places the rendered content on the PDF page as one block that it can scale. The scaling options act on that block: FitWidth shrinks it to the page width, StretchToFit enlarges it, AutoSizePdfPage grows the page around it, FitHeight and SinglePage act on its height and X, Y, Width and Height give it a destination rectangle. The HTML layout itself never changes with the page size.

Next prints the page with a Chromium engine, the way a browser prints. The HTML is laid out for the page: with a fixed page size it is laid out at the width of the page, and when the page follows the browser window it is laid out at the window width and drawn 1:1. Scaling is done with the zoom, which changes the layout width and the drawing scale together. A new converter uses a fixed A4 page with a zoom of 77.47, which gives the same output as the Classic default. There is no destination rectangle: the content area is the page minus its margins. CSS rules that a browser honors when printing, @page, page breaks and @media print, are honored here too.

The practical consequence is that a Classic setting has a Next equivalent in most cases, but the equivalent is rarely the option with the same name. A few Classic behaviors have no equivalent at all. The table in the next section is the complete map.

Page Setup Options: Classic to Next

Classic

Next

Notes

HtmlViewerWidth (1024)

HtmlViewerWidth (1024)

Same role while the page loads. In Next it is also the layout width and the page width when AutoResizePdfPageWidth is true; with a fixed page the layout width comes from the page

HtmlViewerHeight (whole page)

HtmlViewerHeight (2048)

In Classic it limited the conversion to the top of the page. In Next the whole page is always converted; the height only sizes the window while the page loads

HtmlViewerZoom

HtmlViewerZoom

In Next the zoom is the print scale: the layout width is divided by it and the output is multiplied by it. Values from 10 to 200

ClipHtmlView

none

Content wider than the layout width is shrunk to fit by up to 1.5 times and cut at the right edge beyond that; see below

PdfDocumentOptions.FitWidth = true (default)

FitBrowserWindowToPage(PdfPageSize.A4) (default)

Classic laid the page out at 1024 pixels and shrank it to the page width. Next does the same by default; the method sets it explicitly. See the first code sample below

FitWidth = false, AutoSizePdfPage = true

PageWidthFromBrowserWindow()

The page width follows the content in both. In Next the page height comes from PdfPageSize, or from the content with AutoResizePdfPageHeight

FitWidth = false, AutoSizePdfPage = false

none

Classic cut the content at the page width. Next shrinks it to fit first, see ClipHtmlView

StretchToFit

a zoom above 100

A 600 pixel page on A4 fills the page width at zoom 132: 793 / 1.32 = 600 pixels of layout width, drawn at 132 percent

FitHeight

none

Next never scales to the page height. For a single page use AutoResizePdfPageHeight

SinglePage

AutoResizePdfPageHeight = true

With PageWidthFromBrowserWindow(1024, singlePage: true). The page is at least as tall as the viewer height, so set a small HtmlViewerHeight for short pages. The Classic limit of 14400 points does not apply

X, Y, Width, Height

page margins

There is no destination rectangle. X and Width become the left and right margins, on every page. Y, an offset on the first page only, has no equivalent; a top margin applies to every page. Content placed at a position on an existing page is added with an HTML template, see Add HTML Stamp with Page Numbering to Existing PDF

TopSpacing, BottomSpacing

header and footer template Margins

The bottom margin of the header template and the top margin of the footer template are the space between them and the content. Without a header or footer, use the page margins

PdfPageSize, PdfPageOrientation, margins

same

Same names and units. In Next a @page { margin } rule in the HTML overrides the margins set in code

MediaType (screen)

MediaType (screen)

Same default. Next applies the print media rules on its own for RepeatTableHeaderFooter and for headers hidden on some pages with ReserveSpaceAlways = false

Why the Text Can Be Small

The most frequent question about Classic output was why the text came out small. The answer was FitWidth: the page was rendered at the viewer width, 1024 pixels, and the rendering was scaled down to the PDF page, so on A4 a 16 px font became 9.3 points. Next does the same thing by default, with FitBrowserWindowToPage: the page is laid out as in a 1024 pixel browser window and drawn at 77.47 percent on A4, the same size as in Classic. The text is small for the same reason in both: a desktop layout is wider than a sheet of paper, and keeping it means scaling it.

The difference is that in Next the choice is explicit. Three cases:

  • For the text at the size given in the CSS, call LayoutAtPageWidth: the page is laid out at the width of the paper and drawn 1:1. A responsive page then shows the layout it has at that width, usually the tablet one.

  • For a page with a fixed width, give the browser window that width: FitBrowserWindowToPage(PdfPageSize.A4, PdfPageOrientation.Portrait, 1600) for a 1600 pixel page. The result is the Classic output, drawn at 49.58 percent, complete and small.

  • When the content is wider than the browser window, the browser shrinks the page to fit by at most 1.5 times and cuts what still does not fit at the right edge. A 1600 pixel page in the default 1024 pixel window loses its rightmost part; the window width above is the fix.

Page Setup Code: Classic and Next Side by Side

The samples set only the options that differ. The zoom values assume A4 portrait without margins. The page setup topic has the values for other pages.

The Classic Default: 1024 Pixel Layout Fitted to an A4 Page

Classic, with the default values written out:

C#
using EvoPdf;

HtmlToPdfConverter converter = new HtmlToPdfConverter();
converter.LicenseKey = "...";

converter.HtmlViewerWidth = 1024;
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
converter.PdfDocumentOptions.FitWidth = true;

byte[] pdf = converter.ConvertUrl(url);

Next, same output with no settings at all: a new converter is an A4 page with the layout width at 1024 pixels and the drawing scale at 77.47 percent, the scale Classic applied to a 1024 pixel rendering. The method call below makes the same settings explicitly and is the one to use for another page size.

C#
using EvoPdf.Next;

Licensing.LicenseKey = "...";
HtmlToPdfConverter converter = new HtmlToPdfConverter();

byte[] pdf = converter.ConvertUrl(url);

// The same settings made explicitly
converter.FitBrowserWindowToPage(PdfPageSize.A4);

// The same settings made one by one
converter.PdfDocumentOptions.AutoResizePdfPageWidth = false;
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
converter.HtmlViewerZoom = 77.47;
converter.HtmlViewerWidth = 1024;

If the pages you convert are HTML templates designed for A4 rather than web pages, call LayoutAtPageWidth(PdfPageSize.A4) instead: zoom 100, the viewer width at 793 and the print media type, so that the template is laid out at the width it was designed for and drawn 1:1.

A Page with a Fixed Width Larger Than the Viewer

Classic rendered a 1600 pixel wide page in full and fitted it to A4 with FitWidth. In Next, choose between the same small output on A4 and a page as wide as the content:

C#
HtmlToPdfConverter converter = new HtmlToPdfConverter();

// Same as Classic: A4 page, a 1600 pixel window scaled to it, drawn at 49.58 percent
converter.FitBrowserWindowToPage(PdfPageSize.A4, PdfPageOrientation.Portrait, 1600);

// Or a 1200 point wide page with the content at 1:1
converter.PageWidthFromBrowserWindow(1600);

AutoSizePdfPage and SinglePage

Classic grew the page around the unscaled content with FitWidth = false and AutoSizePdfPage = true. It put everything on one page with SinglePage. Next does both with one method call.

C#
// Classic
converter.PdfDocumentOptions.FitWidth = false;
converter.PdfDocumentOptions.AutoSizePdfPage = true;
converter.PdfDocumentOptions.SinglePage = true;

// Next
converter.PageWidthFromBrowserWindow(1024, singlePage: true);
converter.HtmlViewerHeight = 1;

StretchToFit

Classic enlarged a narrow page to the page width with StretchToFit. Next does it with a zoom above 100, computed from the page content width and the width of the HTML: for a 600 pixel page on A4, 793 / 600 = 1.32.

C#
// Classic
converter.PdfDocumentOptions.FitWidth = true;
converter.PdfDocumentOptions.StretchToFit = true;

// Next: a 600 pixel window enlarged to the A4 page, drawn at 132 percent
converter.FitBrowserWindowToPage(PdfPageSize.A4, PdfPageOrientation.Portrait, 600);

Headers and Footers

In Classic a header is a container of PDF elements. You set PdfDocumentOptions.ShowHeader, give PdfHeaderOptions a height and a background color and add elements to it: an HtmlToPdfElement for HTML, a TextElement with the &p; and &P; placeholders for page numbers, a LineElement for a rule. Everything that varies from page to page is done in the handler of the PrepareRenderPdfPageEvent event, raised for every page before it is rendered. The handler can hide the header or footer on that page with Page.ShowHeader and Page.ShowFooter. It can also replace them on that page with a template of another height and other content, through Page.AddHeaderTemplate(height) and Page.AddFooterTemplate(height).

In Next a header is an HTML document. PdfHtmlHeader and PdfHtmlFooter take the HTML as a string or a URL. The page numbers are the {page_number} and {total_pages} variables inside that HTML. Lines and backgrounds are CSS. The height is measured from the content or fixed with Height, and visibility is decided once for the whole document by ShowInFirstPage, ShowInOddPages and ShowInEvenPages, or page by page by the OnPageRendering callback. There is no per page event and one header template per document, so a different header for the first page or for a section of the document is built differently, as shown in the samples below.

Classic

Next

Notes

PdfDocumentOptions.ShowHeader, ShowFooter

set PdfHtmlHeader.Html or HtmlSourceUrl

A header exists when its HTML is set

PdfHeaderOptions.HeaderHeight

PdfHtmlHeader.Height or AutoSizeContentHeight

By default the height is measured from the rendered HTML, between MinContentHeight and MaxContentHeight. With a positive Height and FitHeight the content is scaled down to fit that height

PdfHeaderOptions.HeaderBackColor

CSS in the header HTML

A background on the body element of the header document

PdfHeaderOptions.AddElement(HtmlToPdfElement)

PdfHtmlHeader.Html, HtmlBaseUrl, HtmlSourceUrl

One HTML document per header. Everything that was a separate element becomes markup in it

TextElement with &p; and &P;

{page_number} and {total_pages} in the HTML

Set SkipVariablesParsing when the HTML has no variables, to skip the parsing

LineElement

CSS border

A bottom border on the header body, a top border on the footer body

PdfFooterOptions.PageNumberingStartIndex

PageNumberOffset, TotalPagesOffset

Offsets added to the two variables; positive or negative

PrepareRenderPdfPageEvent with Page.ShowHeader

ShowInFirstPage, ShowInOddPages, ShowInEvenPages, OnPageRendering

The three properties cover the usual cases. The callback receives the page number and returns false to skip the header on that page

PrepareRenderPdfPageEvent with Page.AddHeaderTemplate

an HTML template on the page, or separate conversions merged

Different content on selected pages: see the samples below. A different height on selected pages needs separate conversions

space reserved on pages without a header

ReserveSpaceAlways

true reserves the header space on every page; false gives the space back to the content on the pages where the header is hidden

TopSpacing, Y

header template Margins.Bottom

The template margins are part of the header height, so the top page margin grows with them when AutoResizePdfMargins is true

header in PDF pages inserted before or after the HTML

ShowOnlyInHtmlToPdfPages = false

With AddStartPdf and AddEndPdf; the CountStartPages and CountEndPages properties decide whether those pages count in the page numbers

header on a Document holding several HtmlToPdfElement objects

DelayContentRendering and PdfMerge

See Add Header and Footer to PDF from Multiple HTML

Next also has a lighter header and footer, the one of the browser itself, enabled with EnableHeaderFooter and described in Add HTML in Header and Footer Using Browser Mode. It is drawn inside the page margins you set and uses CSS class names for the page number, the date, the title and the URL. It has no per page visibility and no measured height, so it is the right choice only for a simple line of text on every page.

A Header with HTML and Page Numbers

Classic:

C#
using EvoPdf;
using System.Drawing;

converter.PdfDocumentOptions.ShowHeader = true;
converter.PdfHeaderOptions.HeaderHeight = 60;
converter.PdfHeaderOptions.HeaderBackColor = Color.White;

HtmlToPdfElement headerHtml = new HtmlToPdfElement(headerHtmlUrl);
headerHtml.FitHeight = true;
converter.PdfHeaderOptions.AddElement(headerHtml);

float headerWidth = converter.PdfDocumentOptions.PdfPageSize.Width -
    converter.PdfDocumentOptions.LeftMargin - converter.PdfDocumentOptions.RightMargin;
LineElement headerLine = new LineElement(0, 59, headerWidth, 59);
headerLine.ForeColor = Color.Gray;
converter.PdfHeaderOptions.AddElement(headerLine);

converter.PdfDocumentOptions.ShowFooter = true;
converter.PdfFooterOptions.FooterHeight = 40;
TextElement footerText = new TextElement(0, 15, "Page &p; of &P;",
    new Font(new FontFamily("Times New Roman"), 10, GraphicsUnit.Point));
footerText.TextAlign = HorizontalTextAlign.Right;
converter.PdfFooterOptions.AddElement(footerText);

Next, with the line and the background moved into the header HTML:

C#
using EvoPdf.Next;

PdfHtmlHeaderFooter header = converter.PdfDocumentOptions.PdfHtmlHeader;
header.HtmlSourceUrl = headerHtmlUrl;
header.Height = 60;
header.FitHeight = true;

PdfHtmlHeaderFooter footer = converter.PdfDocumentOptions.PdfHtmlFooter;
footer.Html = "<div style=\"font-family: 'Times New Roman'; font-size: 10pt; text-align: right\">" +
    "Page {page_number} of {total_pages}</div>";
footer.HtmlBaseUrl = baseUrl;
footer.Height = 40;

The header HTML with the background and the bottom rule:

XML
<!DOCTYPE html>
<html>
<body style="margin: 0; background: white; border-bottom: 1px solid gray; font-family: Arial; font-size: 12pt">
    <div style="padding: 8px">Quarterly report</div>
</body>
</html>

Header Hidden on the First Page

Classic, in the event handler:

C#
converter.PrepareRenderPdfPageEvent += (eventParams) =>
{
    if (eventParams.PageNumber == 1)
        eventParams.Page.ShowHeader = false;
};

Next, without a handler:

C#
converter.PdfDocumentOptions.PdfHtmlHeader.ShowInFirstPage = false;

// Give the header space back to the content on the first page
converter.PdfDocumentOptions.PdfHtmlHeader.ReserveSpaceAlways = false;

For a rule that the three visibility properties cannot express, for example a header on pages 3 to 10 only, use the callback. It runs for every page where the header would be rendered and receives the page number in the final document.

C#
converter.PdfDocumentOptions.PdfHtmlHeader.OnPageRendering = (placement) =>
{
    return placement.DocumentPageNumber >= 3 && placement.DocumentPageNumber <= 10;
};

A Different Header on the First Page

Classic replaced the document header on page 1 with Page.AddHeaderTemplate(80) in the event handler and drew other elements in it. In Next the document header is hidden on the first page, its space is still reserved there. A second HTML template is drawn in that space on the first page only. The template is created with AddHtmlTemplate(Int32, Int32, Int32, Int32, String, String) and has the same visibility properties as the header.

C#
HtmlToPdfConverter converter = new HtmlToPdfConverter();

// A fixed page size, so that the page width is known for the template below
converter.PdfDocumentOptions.AutoResizePdfPageWidth = false;
converter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;

// The document header, hidden on the first page, with its space kept on every page
PdfHtmlHeaderFooter header = converter.PdfDocumentOptions.PdfHtmlHeader;
header.HtmlSourceUrl = headerHtmlUrl;
header.Height = 60;
header.ShowInFirstPage = false;
header.ReserveSpaceAlways = true;

// The alternative header: an HTML template of the same height, drawn at the top of the first page only
int pageWidth = converter.PdfDocumentOptions.PdfPageSize.Width;
PdfHtmlTemplate firstPageHeader = converter.PdfDocumentOptions.AddHtmlTemplate(0, 0, pageWidth, 60,
    firstPageHeaderHtml, baseUrl);
firstPageHeader.ShowInFirstPage = true;
firstPageHeader.ShowInOddPages = false;
firstPageHeader.ShowInEvenPages = false;

byte[] pdf = converter.ConvertUrl(url);

The same pattern gives a different footer on the last page, with a template whose OnPageRendering callback returns true when placement.DocumentPageNumber == placement.DocumentPageCount. A different header for the even pages takes two templates: ShowInOddPages = false on one and ShowInEvenPages = false on the other. The reserved space is the same on all pages, so the alternative templates share the height of the document header.

A Different Header and Height for Each Section of a Document

When sections of a document need headers of different heights, convert each section with its own header and footer and join the results with PdfMerge. The page number variables of the second and later sections continue the numbering through the PageNumberOffset and TotalPagesOffset properties. The total page count is not known before all the sections are converted. Either convert the sections once to count the pages and once more with the offsets, or leave {total_pages} out of the section headers and add it with a footer drawn on the merged document, as described in Add Header and Footer to PDF from Multiple HTML.

C#
using PdfMerge pdfMerge = new PdfMerge();

// Section 1: a tall header with the report title
HtmlToPdfConverter section1 = new HtmlToPdfConverter();
section1.PdfDocumentOptions.PdfHtmlHeader.Html = titleHeaderHtml;
section1.PdfDocumentOptions.PdfHtmlHeader.HtmlBaseUrl = baseUrl;
section1.PdfDocumentOptions.PdfHtmlHeader.Height = 120;
section1.PdfDocumentOptions.PdfHtmlFooter.Html = "<div>Page {page_number}</div>";
section1.PdfDocumentOptions.PdfHtmlFooter.HtmlBaseUrl = baseUrl;
int section1Pages = pdfMerge.AddPdf(section1.ConvertHtml(section1Html, baseUrl));

// Section 2: a one line header, page numbers continue after section 1
HtmlToPdfConverter section2 = new HtmlToPdfConverter();
section2.PdfDocumentOptions.PdfHtmlHeader.Html = lineHeaderHtml;
section2.PdfDocumentOptions.PdfHtmlHeader.HtmlBaseUrl = baseUrl;
section2.PdfDocumentOptions.PdfHtmlHeader.Height = 30;
section2.PdfDocumentOptions.PdfHtmlFooter.Html = "<div>Page {page_number}</div>";
section2.PdfDocumentOptions.PdfHtmlFooter.HtmlBaseUrl = baseUrl;
section2.PdfDocumentOptions.PdfHtmlFooter.PageNumberOffset = section1Pages;
pdfMerge.AddPdf(section2.ConvertHtml(section2Html, baseUrl));

byte[] pdf = pdfMerge.Save();

Migration Checklist

  1. Decide the page model for each kind of document: web pages on a standard page (the default), HTML templates for paper with LayoutAtPageWidth, snapshots with PageWidthFromBrowserWindow.

  2. Check pages with fixed widths above 1.5 times the content width: they were small in Classic and are cut in Next until the zoom or the page width is adjusted.

  3. Look for @page rules in the HTML: their margins and size now take effect. A margin rule replaces the margins set in code.

  4. Replace the header and footer elements with two HTML documents and move page numbers, lines and colors into their markup. Replace the event handler with the visibility properties or the callback. Replace per page templates with HTML templates on selected pages or with sections merged after conversion.

  5. Compare two or three representative documents converted with both engines before switching, page count and font size included.

See Also