EvoPdf Classic → EvoPdf Next

Migrating an HTML to PDF integration from Classic to Next

An application can move to EvoPdf Next in two ways: keep its Classic code and run it on the Next engine through the EvoPdf.HtmlToPdf platform packages, or port the code to the native EvoPdf Next API. Either way the application gets the Chromium rendering engine and runs on every platform EvoPdf Next supports. The HTML to PDF license covers both editions.

Choose the approach

Two ways to migrate

Option 1: keep the Classic code

  • Replace the EvoPdf.HtmlToPdf package with the package of your platform: EvoPdf.HtmlToPdf.Windows, EvoPdf.HtmlToPdf.Linux, EvoPdf.HtmlToPdf.MacOS and the .Arm64 variants
  • The EvoPdf namespace, the classes and the members stay the same, so the code compiles and runs as it is
  • The PDF documents keep the Classic page layout
  • The new options of EvoPdf Next, such as PDF/UA and electronic invoices, are available from the Classic code through the Next converter behind each Classic converter
  • Suited to applications with PDF code spread over many projects, or when the move has to be quick

The package change

Option 2: port the code to the Next API

  • Replace the Classic package with the EvoPdf Next package of your platform and use the EvoPdf.Next namespace
  • The shape of a conversion stays the same; the changes are in the license key, the page sizing options and the headers and footers
  • The application uses the native API of EvoPdf Next directly, with all its options
  • Suited to new code and to applications you develop further

The porting steps

The two can be combined. Start with the platform packages and port the code one file at a time: each Classic converter gives access to the EvoPdf Next converter it uses and both produce the PDF as a byte array, so Classic and Next code work side by side in the same application. The Classic library runs on Windows only; with either option the application also runs on Linux (x64 and ARM64) and macOS (Apple Silicon) and directly in Azure App Service for Windows.
Option 1

Keep the Classic code: the platform packages

The EvoPdf.HtmlToPdf.Windows, EvoPdf.HtmlToPdf.Linux and EvoPdf.HtmlToPdf.MacOS packages, with their .Arm64 variants, carry the Classic API, the EvoPdf namespace included, and render with the engine of EvoPdf Next. Replace the Classic package with the package of your platform; the code stays as it is and the output keeps the Classic page layout. The porting steps of option 2 are not needed.

Project file
dotnet remove package EvoPdf.HtmlToPdf
dotnet add package EvoPdf.HtmlToPdf.Windows
using EvoPdf;   // unchanged

var converter = new HtmlToPdfConverter();
converter.LicenseKey = "…";
// the Next converter behind it, for the new options
converter.NextConverterCreated += next =>
    next.PdfDocumentOptions.PdfStandard = EvoPdf.Next.PdfStandard.PdfUa1;
byte[] pdf = converter.ConvertUrl(url);
Every Classic class and member is there. Each converter exposes the EvoPdf Next converter it creates, so the new options, such as PDF/UA or electronic invoices, can be used from the Classic code. The results are plain PDF data, so the Classic code and the native Next API, PdfEditor included, work on the same documents and the application can move to the native API one file at a time. The packages bring the engine of their platform; a multi-platform application references two of them, for example EvoPdf.HtmlToPdf.Windows and EvoPdf.HtmlToPdf.Linux. The Classic demo application on the Next engine shows the packages at work.
Option 2

Port the code to the Next API

The class is still called HtmlToPdfConverter and the shape of a conversion (create, set options, call ConvertUrl or ConvertHtml) is the same. What changes is the way the PDF page is sized and the way headers and footers are described. The three steps below list the differences you will actually hit, with the Classic and Next code side by side. These parts of the code stay the same:

The conversion calls

  • ConvertUrl(url), ConvertHtml(html, baseUrl), the …ToFile variants
  • One converter instance per conversion
  • ConversionDelay, NavigationTimeout

The option groups

  • HtmlViewerWidth, the browser window width
  • PdfDocumentOptions with PdfPageSize, PdfPageOrientation, margins in points
  • Security, viewer preferences, digital signatures

Licensing

  • The HTML to PDF license and the Toolkit license cover Classic and Next
  • Same key, set in a different place (see below)
  • Demo mode without a key, with watermarked output

Authentication, redirects and cookies

Option 2 · Step 1

Packages, namespace, license key

Replace the Classic package with the Next package for your deployment target, change the using and move the license key from the converter instance to the static Licensing class.

Classic
using EvoPdf;

var converter = new HtmlToPdfConverter();
converter.LicenseKey = "…";
converter.ConversionDelay = 2;
byte[] pdf = converter.ConvertUrl(url);
using EvoPdf.Next;

Licensing.LicenseKey = "…";   // once per process
var converter = new HtmlToPdfConverter();
converter.ConversionDelay = 2;
byte[] pdf = converter.ConvertUrl(url);
Next is a .NET Standard 2.0 library with a platform-specific runtime inside the package: EvoPdf.Next.HtmlToPdf.Windows, .Linux, .MacOS, or the .Arm64 variants. Windows and macOS need nothing else; on Linux a few system packages are required; see Getting Started on Linux.
Option 2 · Step 2

The page sizing model is different

This is the change most likely to alter the look of your PDFs, because the two editions start from opposite defaults.

Classic

HtmlViewerWidth is the browser window width in pixels and the HTML is laid out at that width. The PDF page is PdfPageSize (A4 by default) and, because FitWidth is true by default, the rendered content is scaled down to fit the fixed page width. With FitWidth = false, AutoSizePdfPage instead grows the page to show the content unscaled; ClipHtmlView forces the content to exactly the viewer width.

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

// or the page grows to show the content unscaled
converter.PdfDocumentOptions.FitWidth = false;
converter.PdfDocumentOptions.AutoSizePdfPage = true;

HtmlViewerWidth (default 1024 px) is again the browser window width. By default the page is a fixed A4 with that window scaled to it: the HTML is laid out at 1024 pixels and drawn at 77.47 percent, the same scale Classic applied, so a new converter gives the Classic output with no settings. FitBrowserWindowToPage(PdfPageSize.A4) makes the same settings explicitly, for another page size or after changing the margins. With AutoResizePdfPageWidth = true the page instead follows the window: HtmlViewerWidth × 0.75 points wide (768 pt for 1024 px) with the HTML drawn 1:1, the PageWidthFromBrowserWindow layout.

// the default: an A4 page, the 1024 px window
// scaled to it at 77.47 percent
converter.FitBrowserWindowToPage(PdfPageSize.A4);

// another page size and orientation
converter.FitBrowserWindowToPage(PdfPageSize.Letter,
    PdfPageOrientation.Landscape);

// the page follows the window, content drawn 1:1
converter.PageWidthFromBrowserWindow(1024);

Pages wider than the browser window

Classic scaled the rendering to the width the HTML actually had, so a 1024 pixel layout on A4 came out at 77.47 percent and a 1600 pixel page at 50 percent. Next scales the browser window, 1024 pixels by default, which gives the same result for a page designed for a desktop screen. Content wider than the window is laid out at its own width and scaled to the page, so nothing is cut; for a 1:1 rendering, give the window the width of the page. A 1600 pixel page on A4 therefore needs FitBrowserWindowToPage(PdfPageSize.A4, PdfPageOrientation.Portrait, 1600), zoom 49.58, or a page as wide as the content with PageWidthFromBrowserWindow(1600).

Practical rule

Decide once whether you want the content to follow the page (the default, FitBrowserWindowToPage with a standard PdfPageSize) or the page to follow the content (PageWidthFromBrowserWindow). When replacing Classic, the default is the answer: it reproduces the Classic page size, layout width and scale, so your users see what they see today. HTML templates designed for A4 or Letter use LayoutAtPageWidth instead and are laid out 1:1. PageWidthFromBrowserWindow(1024, singlePage: true) is the Next equivalent of the Classic single-page output. Compare a few documents before switching. The zoom values for other page sizes and margins are in HTML to PDF Page Setup and Scaling and the full comparison in Migrate HTML to PDF Page Setup and Headers from EvoPdf Classic.

Option 2 · Step 3

Headers and footers are described as HTML, not drawn as elements

In Classic a header is a container you add elements to (HtmlToPdfElement, TextElement, LineElement), with per-page visibility decided in the PrepareRenderPdfPageEvent handler. In Next a header is an HTML document with its own options object; page numbers are variables inside that HTML and per-page visibility is three booleans, or the OnPageRendering callback for other rules.

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

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

// line under the header
LineElement line = new LineElement(0, 59, headerWidth, 59);
line.ForeColor = Color.Gray;
converter.PdfHeaderOptions.AddElement(line);

// page numbers: a TextElement with &p; and &P; placeholders
converter.PdfFooterOptions.AddElement(
    new TextElement(0, 20, "Page &p; of &P;", font));

// hide on the first page, from an event handler
converter.PrepareRenderPdfPageEvent += (p) =>
    { if (p.PageNumber == 1) p.Page.ShowHeader = false; };
var header = converter.PdfDocumentOptions.PdfHtmlHeader;

// or header.Html with header.HtmlBaseUrl
header.HtmlSourceUrl = headerUrl;
// or header.AutoSizeContentHeight = true
header.Height = 60;
header.FitHeight = true;
// make room in the top margin
header.AutoResizePdfMargins = true;

// line under the header: CSS in the header HTML

// page numbers: variables in the footer HTML
var footer = converter.PdfDocumentOptions.PdfHtmlFooter;
footer.Html = "<div style=\"text-align:center\">" +
    "Page {page_number} of {total_pages}</div>";

// per-page visibility: no event handler
header.ShowInFirstPage = false;
header.ShowInOddPages = true;
header.ShowInEvenPages = true;
// keep the space on the pages without header
header.ReserveSpaceAlways = true;
Anything you drew with TextElement, ImageElement or LineElement becomes markup in the header or footer HTML. Header and footer HTML is rendered by the same engine as the page, so web fonts, CSS and images work there too. Full sample: HTML Header and Footer with Page Numbers.
Reference

Option map

ClassicNextNote
using EvoPdf;using EvoPdf.Next;Namespace
converter.LicenseKeyLicensing.LicenseKeyStatic, set once
PdfDocumentOptions.FitWidth = true (default)FitBrowserWindowToPage(PdfPageSize.A4) (default)Fixed page width, the 1024 pixel layout scaled to it, zoom 77.47 on A4 and 79.69 on Letter
PdfDocumentOptions.AutoSizePdfPagePageWidthFromBrowserWindow()Page width follows the viewer width, content drawn 1:1
HtmlViewerHeight (whole page)HtmlViewerHeight (2048)The whole page is always converted; the height only sizes the window while the page loads
HtmlViewerZoomHtmlViewerZoomThe print scale: the layout width is divided by it and the output is multiplied by it, from 10 to 200
PdfDocumentOptions.SinglePagePageWidthFromBrowserWindow(1024, singlePage: true)Whole content on one page
PdfDocumentOptions.ShowHeader + PdfHeaderOptionsPdfDocumentOptions.PdfHtmlHeaderSame for footer
PdfHeaderOptions.HeaderHeightPdfHtmlHeader.Height / AutoSizeContentHeightMin/Max content height available
PdfHeaderOptions.AddElement(HtmlToPdfElement)PdfHtmlHeader.Html or HtmlSourceUrlOne HTML document per header/footer
TextElement with &p; / &P;{page_number} / {total_pages} in the HTMLSkipVariablesParsing when not used
PdfFooterOptions.PageNumberingStartIndexPdfHtmlFooter.PageNumberOffset / TotalPagesOffsetOffsets for the page counters
PrepareRenderPdfPageEvent → Page.ShowHeaderPdfHtmlHeader.ShowInFirstPage / ShowInOddPages / ShowInEvenPagesNo event handler; OnPageRendering for other rules
PdfDocumentOptions.TopSpacingPdfHtmlHeader.MarginsThe template margins are part of the header height; with AutoResizePdfMargins the top margin becomes the header height
StretchToFitHtmlViewerZoom above 100A 600 pixel page fills an A4 page at zoom 132
ClipHtmlViewnoneContent wider than the browser window is laid out at its own width and scaled to the page (AutoResizeHtmlViewerWidth, true by default)
Engine behavior

Beyond the API: what the new rendering engine changes in layout, fonts, JavaScript and image loading

Both engines render with the screen media type by default and both expose MediaType, so that part does not change. What changes is the engine underneath: Next renders exactly like a current Chrome, so pages look the way your browser shows them and a few things Classic handled its own way now follow the browser. This applies to both options, the platform packages and the code ported to the Next API.

Layout follows current web standards

  • Flexbox, CSS grid, custom properties, modern selectors and ES2015+ JavaScript render as in Chrome. Layouts that carried workarounds for the old engine can drop them and pages that looked "acceptable" only because a rule was ignored may now look different
  • Print CSS is honored: @page, break-before/after/inside, page-break-*, so page breaks you controlled from the API can move into the stylesheet
  • Content is laid out at 96 DPI in a browser window HtmlViewerWidth pixels wide, 1024 by default; step 2 shows how that window is placed on the page

Text and fonts

  • Font fallback, hinting and metrics are Chrome's. Line breaks and therefore page breaks, can shift by a line compared with Classic output on the same document
  • Web fonts (including WOFF2) are downloaded and embedded as subsets
  • Text stays selectable and searchable; with PDF/UA output it is also tagged

JavaScript and dynamic content

  • Scripts run in a real browser engine: charts, frameworks and async data that the old engine could not execute now render and pages that finish loading late need ConversionDelay or TriggeringMode, as before
  • JavaScriptEnabled, NavigationTimeout and HtmlViewerWidth keep their role

Resources and security defaults

  • Lazy-loaded images are fetched by default (LoadLazyImages); images that the old engine skipped will appear
  • Local files, insecure content and blocked hosts are governed by explicit options (LocalFilesEnabled and the template-level equivalents). Check them if your HTML references file paths or mixed content
  • Sites served only over HTTP/2 or TLS 1.3 load directly; no need to download the HTML yourself and convert it as a string, as some Classic integrations did

Suggested order: with the platform packages, run your own pages through the Classic demo application on the Next engine and compare a few documents with the Classic output. When porting, run the Next demo application on your own pages first (online or the downloaded project), settle the page sizing options on two or three representative documents, then port the headers and footers. Most integrations need changes in exactly those two places and nowhere else. If you work with an AI coding assistant, the EvoPdf agent skills on GitHub include a migration skill that applies these steps to your code.

Try your pages on the Next engine

The demo application runs without a license key, with a stamp on the output, so you can compare Classic and Next side by side before changing anything in production.