Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Most iTextSharp HTML-to-PDF failures begin before PDF generation: the converter was given ASPX, Razor, controls, JavaScript, or malformed markup instead of finished HTML. Render the ASP.NET page first, capture the exact HTML, validate it as XHTML, use matching iTextSharp and XML Worker 5 assemblies, then generate and close the PDF before reading its stream. This sequence isolates nearly every failure without guessing at CSS.

Understand what iTextSharp is actually converting

iTextSharp 5 does not print a browser tab. It receives a string or stream containing HTML and parses the elements and CSS that its parser supports. It does not execute ASP.NET controls, Razor syntax, server-side code, or JavaScript. As iText’s documentation puts it, “XML Worker won’t resolve ASP pages, nor execute JavaScript.” The pdfHTML guidance makes the same boundary explicit: “The pdfHTML add-on parses HTML and CSS. That’s it.”

Your pipeline therefore has two separate stages:

  1. Rendering: ASP.NET executes the page, view, controls, data binding, authentication and layout code and produces ordinary HTML.
  2. Conversion: iTextSharp parses that finished HTML/XHTML and supported CSS and writes PDF objects.

A browser successfully displaying the URL proves only that a browser can execute the page. It does not prove that XML Worker can parse the same source or reproduce the browser’s layout.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

First triage: capture the exact HTML input

Do not start by changing PDF settings. Save the rendered response immediately before it enters XML Worker. The saved file should contain the expected body text, table rows, inline values, style references and image URLs. It must not contain <asp:GridView>, Razor expressions such as @Model.Name, an error page, a login form or an empty shell that depends on JavaScript.

Render an ASP.NET page to a string

For Web Forms, render the control tree with Server.Execute (or use the rendering method already present in your application). For MVC, request the action through a server-side renderer and capture its returned HTML. The important result is a string containing the completed document, not the particular framework helper.

public string RenderPageToHtml(string relativePath)
{
    using (var writer = new StringWriter(CultureInfo.InvariantCulture))
    {
        var context = new HttpContextWrapper(HttpContext.Current);
        Server.Execute(relativePath, writer, true);
        return writer.ToString();
    }
}

Log the HTML only in a controlled diagnostic environment: it can contain personal data, tokens or hidden fields. Open the captured file in a text editor and run it through an XHTML/HTML validator. Then make a minimal reproduction containing one heading, one paragraph, one table and one image; add the original sections back incrementally.

Use XML Worker instead of the legacy HTMLWorker

HTMLWorker is an old, limited parser and does not parse CSS files. In an iText 5 application that needs stylesheets and broader XHTML support, use XML Worker with itextsharp.xmlworker.dll. XML Worker is still not a full browser engine: unsupported selectors, layout rules, scripts and complex table behavior can be ignored or produce malformed output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Required assemblies

  • itextsharp.dll
  • The matching itextsharp.xmlworker.dll

Keep the releases aligned. A local build can succeed while production fails if the application’s bin directory contains an older or different pair. Check the deployed files, assembly versions and any binding redirects, not just the project references.

A minimal, correct iTextSharp 5 conversion

The following Web Forms/MVC-compatible method accepts already-rendered HTML, parses it with XML Worker and returns PDF bytes. It uses a MemoryStream, opens the document before parsing, and closes it before reading the stream.

using System;
using System.IO;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.tool.xml;
using iTextSharp.tool.xml.pipeline.css;
using iTextSharp.tool.xml.pipeline.end;
using iTextSharp.tool.xml.pipeline.html;

public static byte[] HtmlToPdf(string html, string basePath)
{
    using (var output = new MemoryStream())
    using (var document = new Document(PageSize.A4, 36, 36, 36, 36))
    {
        var writer = PdfWriter.GetInstance(document, output);
        document.Open();

        var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true);
        if (!string.IsNullOrWhiteSpace(basePath))
        {
            cssResolver.AddCssFile(basePath, true);
        }

        var pipeline = new CssResolverPipeline(
            cssResolver,
            new HtmlPipeline(
                new HtmlPipelineContext(null),
                new PdfWriterPipeline(document, writer)));

        var worker = XMLWorkerHelper.GetInstance().GetXMLWorker(pipeline, true);
        using (var xmlReader = XMLWorkerHelper.GetInstance()
                   .GetDefaultXmlParser(true))
        using (var input = new StringReader(html))
        {
            xmlReader.Parse(input);
        }

        document.Close();
        return output.ToArray();
    }
}

In many applications the simpler helper is sufficient:

using (var output = new MemoryStream())
using (var document = new Document())
{
    PdfWriter writer = PdfWriter.GetInstance(document, output);
    document.Open();
    using (var reader = new StringReader(renderedHtml))
    {
        XMLWorkerHelper.GetInstance().ParseXHtml(writer, document, reader);
    }
    document.Close();
    byte[] pdf = output.ToArray();
    Response.Clear();
    Response.ContentType = "application/pdf";
    Response.AddHeader("Content-Disposition", "inline; filename=report.pdf");
    Response.BinaryWrite(pdf);
    Response.End();
}

Adapt the API calls to the exact XML Worker package version in your application. The lifecycle is the key point: open, parse, close, then read and send the bytes. Reading the stream before closing the document can yield an incomplete file or a PDF with no pages.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make the input compatible with XML Worker

Markup

  • Send one well-formed document with properly nested and closed tags.
  • Close table rows and cells explicitly; do not rely on browser error recovery.
  • Use XHTML-style empty elements where your parser configuration requires them.
  • Remove framework placeholders, comments containing template syntax and conditional server markup.
  • Start with a small document and add sections until the failing construct is identified.

CSS

Use CSS properties known to be supported by your XML Worker version and test each layout rule. Browser-only features, modern flex/grid behavior, pseudo-elements, animations, client-side calculated dimensions and complex selectors may not render as expected. External stylesheets and fonts must be resolvable from the conversion process; a relative browser URL may point nowhere when the converter runs on a server.

Images and resources

Check every image URL from the server’s point of view. Authentication-protected images, hostnames that resolve only on a developer workstation, mixed-content URLs and filesystem paths frequently become blank boxes. Use absolute, reachable URLs or a resource provider that maps approved files. Never pass untrusted user input directly into a filesystem path or unrestricted URL fetch.

JavaScript and dynamic content

XML Worker does not execute scripts. If a chart, table or value appears only after JavaScript runs, generate its final HTML or an image on the server before conversion. Likewise, an ASP.NET update panel or client-side template must be rendered into ordinary markup first.

Interpret common symptoms without over-diagnosing

“The document has no pages”

Official iText guidance notes that this can mean the application did not actually pass HTML. Verify that the captured string is non-empty, contains visible content and reaches the parser. It can also follow from an exception or an input that produces no supported flow elements, so treat the message as a prompt to inspect the input rather than a universal diagnosis.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Blank or partially blank PDF

  • Confirm the response is not an authentication or error page.
  • Check that the document was opened and closed in the correct order.
  • Replace external CSS and images with inline, local test resources.
  • Remove unsupported layout rules and reintroduce them one at a time.
  • Ensure text color is not equal to the page background and that content is not positioned outside the page.

CSS appears to be ignored

Check that you are not using HTMLWorker, that the stylesheet path is resolvable, and that selectors and properties are supported by XML Worker. Inline one rule in the test document; if it works, the problem is loading or support for the external stylesheet rather than PDF output.

Rows, rowspan or columns break

Complex HTML tables are a frequent compatibility boundary. Validate every row has the expected cells, simplify nested tables, set explicit widths and test the smallest table that reproduces the issue. A browser’s table repair and layout algorithm is more capable than XML Worker’s parser.

Works locally, fails after deployment

Compare deployed itextsharp.dll and itextsharp.xmlworker.dll versions, binding redirects, file permissions, working directories, outbound network access and installed fonts. Confirm that the production process can reach every stylesheet and image URL. A mismatch in the two DLLs is especially likely when compilation succeeded on one machine.

Use an incremental troubleshooting checklist

  1. Capture and retain the final rendered HTML.
  2. Open it as text and verify data, styles and resources.
  3. Validate and reduce it to a minimal XHTML sample.
  4. Replace HTMLWorker with XML Worker if CSS is required.
  5. Confirm matching core and XML Worker assemblies in both development and deployment.
  6. Test images, CSS and fonts from the conversion server.
  7. Open the PDF document before parsing and close it before reading the output stream.
  8. Record the exact exception, package versions and first failing HTML construct.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and security considerations

Rendering and conversion consume separate resources. Cache stable HTML or data when appropriate, but do not reuse a mutable Document or PdfWriter across requests. Limit input size, page count and external resource access; otherwise a user-controlled URL or huge document can exhaust memory or create server-side request forgery risk. Set timeouts for any resource fetches performed by your own rendering layer, and avoid logging complete production documents.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For repeatable output, pin the iTextSharp/XML Worker versions, deploy the same fonts and test representative documents after every upgrade. Treat unsupported CSS as a design constraint: create a PDF-specific template rather than trying to force browser-only effects through the parser.

Maintain iTextSharp or migrate to iText Core/pdfHTML?

Consideration Continue iTextSharp/XML Worker Evaluate iText Core with pdfHTML
Existing application Usually the smaller change for a stable legacy ASP.NET system. Requires integration and regression work.
HTML/CSS needs Suitable only for the subset your templates and XML Worker version support. Designed as the newer HTML/CSS conversion path; verify required features in your version.
Lifecycle iText identifies iText 5/iTextSharp as end-of-life. iText’s recommended direction for new implementations.
Licensing and support Check current AGPL or commercial terms for your deployment. Check the same project-specific licensing and framework requirements before migration.
Evidence for this application No independent benchmark establishes a universal winner. Choose by compatibility testing, not a generic performance claim.

For a working legacy report, fixing the rendered input and supported markup is often safer than a rewrite. For new work or planned modernization, evaluate Core/pdfHTML with a representative template set and confirm current framework compatibility, licensing and support terms.

Or skip the browser setup

If your real requirement is a clean image or PDF of a public web page rather than an ASP.NET-generated document, ScreenshotNeo provides a single HTTP call. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result. It also offers an MCP server for AI agents with take_screenshot, get_page_info and capture_pdf.

See the parameter reference in the ScreenshotNeo documentation. cURL:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Every feature is available on every plan. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Sign up free for ScreenshotNeo.

Frequently Asked Questions

Can XML Worker convert an ASPX URL directly?

No. Request or render the page first and pass the resulting HTML; XML Worker does not resolve ASP.NET pages or execute their server code.

Why does the same HTML work in Chrome but not in iTextSharp?

Chrome is a full browser with JavaScript execution and extensive layout support. XML Worker supports a narrower XHTML and CSS subset, so validate and simplify the input for that parser.

Should every existing iTextSharp project be rewritten immediately?

No. Fix a stable legacy pipeline when that is the lower-risk option; evaluate iText Core/pdfHTML for new work or planned modernization.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.