IRONSOFTWAREHOME
MIGRATION GUIDES

How to Migrate from fo.net to IronPDF

Curtis Chau
Curtis Chau
Updated: August 1, 2026

Migrating from fo.net (FO.NET) to IronPDF is a major update for your .NET PDF generation process. This guide offers a straightforward, step-by-step path to transition your codebase from outdated XSL-FO markup to modern HTML/CSS-based PDF generation, using skills your development team already has.

Why Migrate from fo.net to IronPDF

The fo.net Challenges

fo.net (FO.NET) is a C# port of an early Apache FOP build that converts XSL-FO documents to PDF. It is Apache 2.0-licensed but has several limitations for current development:

  1. Obsolete Technology: XSL-FO (Extensible Stylesheet Language Formatting Objects) is a W3C specification from 2001; the W3C closed the XSL-FO Working Group in 2013 and the language has had no further development.
  2. Complex Learning Curve: XSL-FO requires intricate XML-based markup with specialized formatting objects (fo:block, fo:table, fo:page-sequence, etc.).
  3. No HTML/CSS Support: fo.net cannot render HTML or CSS - you must transform HTML to XSL-FO yourself (typically via XSLT).
  4. Abandoned/Unmaintained: The Fonet package on nuget.org was last published in April 2011 (v1.0.0, .NET Framework 2.0). The community fork Fonet.Standard (.NET Standard 2.0) was last published in May 2020 (v1.0.5). The CodePlex repository is defunct and GitHub forks (prepare/FO.NET, nholik/FO.Net, hahmed/Fo.Net) are dormant.
  5. Windows-Only in practice: The legacy Fonet build depends on System.Drawing (GDI+) APIs that effectively limit it to Windows.
  6. Limited Modern Features: No JavaScript engine, no CSS3 (flexbox/grid), no modern web font loading.
  7. No URL Rendering: fo.net cannot directly render web pages - requires manual HTML-to-XSL-FO conversion.

fo.net vs IronPDF Comparison

Aspectfo.net (FO.NET)IronPDF
Input FormatXSL-FO (outdated XML)HTML/CSS (modern web standards)
Learning CurveSteep (XSL-FO expertise)Gentle (HTML/CSS knowledge)
MaintenanceAbandoned (last release May 2020)Actively maintained
Platform SupportWindows onlyCross-platform (.NET Framework 4.6.2+, .NET Core 3.1+, .NET 5/6/7/8/9+)
CSS SupportNoneFull CSS3 (Flexbox, Grid)
JavaScriptNoneFull JavaScript support
URL RenderingNot supportedBuilt-in
Modern FeaturesLimitedHeaders, footers, watermarks, security
DocumentationOutdatedThorough tutorials

Why the Switch Makes Sense

fo.net was designed when XSL-FO was expected to become a standard for document formatting. That expectation never materialized. HTML/CSS became the universal document format, while XSL-FO remained a niche skill - most XSL-FO resources date from 2005-2010, making maintenance increasingly difficult.

IronPDF lets you use the skills you already have to create professional PDFs on modern .NET.


Before You Start

Prerequisites

  1. .NET Environment: IronPDF supports .NET Framework 4.6.2+, .NET Core 3.1+, .NET 5/6/7/8/9+
  2. NuGet Access: Ensure you can install packages from NuGet
  3. License Key: Obtain your IronPDF license key for production use from ironpdf.com

Backup Your Project

# Create a backup branch
git checkout -b pre-ironpdf-migration
git add .
git commit -m "Backup before fo.net to IronPDF migration"
SHELL

Identify All fo.net Usage

# Find all fo.net references
grep -r "FonetDriver\|Fonet\|\.fo\"\|xsl-region" --include="*.cs" --include="*.csproj" .

# Find all XSL-FO template files
find . -name "*.fo" -o -name "*.xslfo" -o -name "*xsl-fo*"
SHELL

Document Your XSL-FO Templates

Before migration, catalog all XSL-FO files and note:

  • Page dimensions and margins
  • Fonts used
  • Tables and their structures
  • Headers and footers (fo:static-content)
  • Page numbering patterns
  • Image references

Quick Start Migration

Step 1: Update NuGet Packages

# Remove fo.net package
dotnet remove package Fonet
dotnet remove package FO.NET

# Install IronPDF
dotnet add package IronPdf
SHELL

Step 2: Update Namespaces

// Before (fo.net)
using Fonet;
using Fonet.Render.Pdf;
using System.Xml;

// After (IronPDF)
using IronPdf;
using IronPdf.Rendering;

Step 3: Initialize IronPDF

// Set license key at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";

Step 4: Basic Conversion Pattern

// Before (fo.net with XSL-FO)
FonetDriver driver = FonetDriver.Make();
using (FileStream output = new FileStream("output.pdf", FileMode.Create))
{
    driver.Render(new StringReader(xslFoContent), output);
}

// After (IronPDF with HTML)
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs("output.pdf");

Complete API Reference

Namespace Mapping

fo.net NamespaceIronPDF Equivalent
FonetIronPdf
Fonet.Render.PdfIronPdf
Fonet.LayoutN/A
Fonet.FoN/A
Fonet.ImageIronPdf

FonetDriver to ChromePdfRenderer

FonetDriver MethodIronPDF Equivalent
FonetDriver.Make()new ChromePdfRenderer()
driver.Render(inputStream, outputStream)renderer.RenderHtmlAsPdf(html)
driver.Render(inputFile, outputStream)renderer.RenderHtmlFileAsPdf(path)
driver.BaseDirectoryRenderingOptions.BaseUrl
driver.OnError += handlerTry/catch around render

RenderingOptions (PDF Configuration)

fo.net (XSL-FO Attributes)IronPDF RenderingOptions
page-heightPaperSize or SetCustomPaperSize()
page-widthPaperSize
margin-topMarginTop
margin-bottomMarginBottom
margin-leftMarginLeft
margin-rightMarginRight
reference-orientationPaperOrientation

XSL-FO to HTML Conversion Guide

XSL-FO Elements to HTML/CSS

The fundamental shift in this fo.net migration is converting XSL-FO elements to their HTML equivalents:

XSL-FO ElementHTML/CSS Equivalent
<fo:root><html>
<fo:layout-master-set>CSS @page rule
<fo:simple-page-master>CSS @page
<fo:page-sequence><body> or <div>
<fo:flow><main> or <div>
<fo:static-content>HtmlHeaderFooter
<fo:block><p>, <div>, <h1>-<h6>
<fo:inline><span>
<fo:table><table>
<fo:table-row><tr>
<fo:table-cell><td>, <th>
<fo:list-block><ul>, <ol>
<fo:list-item><li>
<fo:external-graphic><img>
<fo:page-number>{page} placeholder
<fo:page-number-citation>{total-pages}
<fo:basic-link><a href>

XSL-FO Properties to CSS

XSL-FO PropertyCSS EquivalentExample
font-familyfont-familySame syntax
font-sizefont-sizeSame syntax
font-weightfont-weightbold, normal, 700
text-aligntext-alignleft, center, right, justify
colorcolorHex, RGB, names
background-colorbackground-colorSame syntax
space-beforemargin-topBefore element
space-aftermargin-bottomAfter element
start-indentmargin-leftLeft indent
keep-togetherpage-break-inside: avoidPrevent breaks
break-before="page"page-break-before: alwaysForce page break

Code Examples

Example 1: Basic HTML to PDF

Before (fo.net with XSL-FO):

// NuGet: Install-Package Fonet
using Fonet;
using Fonet.Render.Pdf;
using System.IO;
using System.Xml;

class Program
{
    static void Main()
    {
        // fo.net requires XSL-FO format, not HTML
        // First convert HTML to XSL-FO (manual process)
        string xslFo = @"<?xml version='1.0' encoding='utf-8'?>
            <fo:root xmlns:fo='http://www.w3.org/1999/XSL/Format'>
                <fo:layout-master-set>
                    <fo:simple-page-master master-name='page'>
                        <fo:region-body/>
                    </fo:simple-page-master>
                </fo:layout-master-set>
                <fo:page-sequence master-reference='page'>
                    <fo:flow flow-name='xsl-region-body'>
                        <fo:block>Hello World</fo:block>
                    </fo:flow>
                </fo:page-sequence>
            </fo:root>";
        
        FonetDriver driver = FonetDriver.Make();
        driver.Render(new StringReader(xslFo), 
            new FileStream("output.pdf", FileMode.Create));
    }
}

After (IronPDF with HTML):

// NuGet: Install-Package IronPdf
using IronPdf;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        string html = "<h1>Hello World</h1><p>This is HTML content.</p>";
        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("output.pdf");
    }
}

The IronPDF approach reduces 25+ lines of XSL-FO markup to just 4 lines of clean C# code. For more HTML to PDF options, see the IronPDF HTML to PDF documentation.

Example 2: PDF with Custom Settings

Before (fo.net with XSL-FO):

// NuGet: Install-Package Fonet
using Fonet;
using Fonet.Render.Pdf;
using System.IO;

class Program
{
    static void Main()
    {
        // fo.net settings are configured in XSL-FO markup
        string xslFo = @"<?xml version='1.0' encoding='utf-8'?>
            <fo:root xmlns:fo='http://www.w3.org/1999/XSL/Format'>
                <fo:layout-master-set>
                    <fo:simple-page-master master-name='A4' 
                        page-height='297mm' page-width='210mm'
                        margin-top='20mm' margin-bottom='20mm'
                        margin-left='25mm' margin-right='25mm'>
                        <fo:region-body/>
                    </fo:simple-page-master>
                </fo:layout-master-set>
                <fo:page-sequence master-reference='A4'>
                    <fo:flow flow-name='xsl-region-body'>
                        <fo:block font-size='14pt'>Custom PDF</fo:block>
                    </fo:flow>
                </fo:page-sequence>
            </fo:root>";
        
        FonetDriver driver = FonetDriver.Make();
        driver.Render(new StringReader(xslFo), 
            new FileStream("custom.pdf", FileMode.Create));
    }
}

After (IronPDF with HTML):

// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Engines.Chrome;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
        renderer.RenderingOptions.MarginTop = 20;
        renderer.RenderingOptions.MarginBottom = 20;
        renderer.RenderingOptions.MarginLeft = 25;
        renderer.RenderingOptions.MarginRight = 25;
        
        string html = "<h1 style='font-size:14pt'>Custom PDF</h1>";
        var pdf = renderer.RenderHtmlAsPdf(html);
        pdf.SaveAs("custom.pdf");
    }
}

IronPDF provides programmatic rendering options instead of embedding configuration in XML markup.

Example 3: URL to PDF

Before (fo.net - not supported):

// NuGet: Install-Package Fonet
using Fonet;
using System.IO;
using System.Net;

class Program
{
    static void Main()
    {
        // fo.net does not support URL rendering directly
        // Must manually download, convert HTML to XSL-FO, then render
        string url = "https://example.com";
        string html = new WebClient().DownloadString(url);
        
        // Manual conversion from HTML to XSL-FO required (complex)
        string xslFo = ConvertHtmlToXslFo(html); // Not built-in
        
        FonetDriver driver = FonetDriver.Make();
        driver.Render(new StringReader(xslFo), 
            new FileStream("webpage.pdf", FileMode.Create));
    }
    
    static string ConvertHtmlToXslFo(string html)
    {
        // Custom implementation required - extremely complex
        throw new System.NotImplementedException();
    }
}

After (IronPDF - built-in support):

// NuGet: Install-Package IronPdf
using IronPdf;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://example.com");
        pdf.SaveAs("webpage.pdf");
    }
}

URL to PDF rendering is one of the practical wins in this fo.net migration: IronPDF handles it natively with JavaScript execution. Learn more about URL to PDF conversion.

Example 4: Headers and Footers

Before (fo.net with XSL-FO):

<fo:static-content flow-name="xsl-region-before">
    <fo:block text-align="center" font-size="10pt">
        Company Name - Confidential
    </fo:block>
</fo:static-content>

<fo:static-content flow-name="xsl-region-after">
    <fo:block text-align="right" font-size="10pt">
        Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/>
    </fo:block>
</fo:static-content>
XML

After (IronPDF):

renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter()
{
    HtmlFragment = "<div style='text-align:center; font-size:10pt;'>Company Name - Confidential</div>",
    DrawDividerLine = true
};

renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter()
{
    HtmlFragment = "<div style='text-align:right; font-size:10pt;'>Page {page} of {total-pages}</div>",
    DrawDividerLine = true
};

IronPDF replaces complex XSL-FO region definitions with simple HTML headers and footers.

Example 5: PDF Security

Before (fo.net):

// fo.net has very limited PDF security options
// Must use post-processing with another library

After (IronPDF):

using IronPdf;

public byte[] GenerateSecurePdf(string html)
{
    var renderer = new ChromePdfRenderer();
    var pdf = renderer.RenderHtmlAsPdf(html);

    // Set metadata
    pdf.MetaData.Title = "Confidential Report";
    pdf.MetaData.Author = "Company Name";

    // Password protection
    pdf.SecuritySettings.OwnerPassword = "owner123";
    pdf.SecuritySettings.UserPassword = "user456";

    // Restrict permissions
    pdf.SecuritySettings.AllowUserCopyPasteContent = false;
    pdf.SecuritySettings.AllowUserPrinting = IronPdf.Security.PdfPrintSecurity.NoPrint;
    pdf.SecuritySettings.AllowUserEdits = IronPdf.Security.PdfEditSecurity.NoEdit;

    return pdf.BinaryData;
}

Performance Considerations

Reuse ChromePdfRenderer

For optimal performance during your fo.net migration, reuse the ChromePdfRenderer instance:

// GOOD - Reuse the renderer
public class PdfService
{
    private static readonly ChromePdfRenderer _renderer = new ChromePdfRenderer();

    public byte[] Generate(string html) => _renderer.RenderHtmlAsPdf(html).BinaryData;
}

// BAD - Creating new instance each time
public byte[] GenerateBad(string html)
{
    var renderer = new ChromePdfRenderer();  // Wasteful
    return renderer.RenderHtmlAsPdf(html).BinaryData;
}

Unit Conversion Helper

fo.net XSL-FO uses various units. IronPDF uses millimeters for margins. Here's a helper class:

public static class UnitConverter
{
    public static double InchesToMm(double inches) => inches * 25.4;
    public static double PointsToMm(double points) => points * 0.352778;
    public static double PicasToMm(double picas) => picas * 4.233;
    public static double CmToMm(double cm) => cm * 10;
}

// Usage
renderer.RenderingOptions.MarginTop = UnitConverter.InchesToMm(1);  // 1 inch

Troubleshooting

Issue 1: Page Size Differences

Problem: PDF page size looks different after fo.net migration.

Solution: Map XSL-FO page dimensions correctly:

// XSL-FO: page-height='11in' page-width='8.5in' (Letter)
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;

// XSL-FO: page-height='297mm' page-width='210mm' (A4)
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;

// Custom size (in mm)
renderer.RenderingOptions.SetCustomPaperSize(210, 297);

Issue 2: fo:block to HTML Mapping

Problem: Not sure what <fo:block> should become.

Solution: Use appropriate semantic HTML:

  • Headings: <h1> through <h6>
  • Paragraphs: <p>
  • Generic containers: <div>
  • Inline text: <span>

Issue 3: Fonts Not Matching

Problem: Fonts look different from fo.net output.

Solution: Use web fonts or specify system fonts in CSS:

<style>
    @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
    body { font-family: 'Roboto', Arial, sans-serif; }
</style>
HTML

Issue 4: Page Numbers Not Working

Problem: <fo:page-number/> doesn't work.

Solution: Use IronPDF placeholders in headers/footers:

renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter()
{
    HtmlFragment = "<div style='text-align:center;'>Page {page} of {total-pages}</div>",
    MaxHeight = 15  // mm
};

Migration Checklist

Pre-Migration

  • Catalog all XSL-FO template files (.fo, .xslfo)
  • Document page dimensions and margins used
  • Note header/footer configurations (fo:static-content)
  • Identify table structures and styling
  • Backup project to version control
  • Obtain IronPDF license key

Package Migration

  • Remove Fonet or FO.NET package: dotnet remove package Fonet
  • Install IronPdf package: dotnet add package IronPdf
  • Update namespace imports from Fonet to IronPdf
  • Set IronPDF license key at startup

Code Migration

  • Replace FonetDriver.Make() with new ChromePdfRenderer()
  • Replace driver.Render() with renderer.RenderHtmlAsPdf()
  • Update file output from streams to pdf.SaveAs()
  • Replace error event handlers with try/catch
  • Convert fo:static-content to HtmlHeaderFooter
  • Replace <fo:page-number/> with {page} placeholder

Testing

  • Compare output appearance to original fo.net PDFs
  • Verify page dimensions and margins
  • Check headers and footers
  • Validate page numbers
  • Test table rendering
  • Verify image loading

Post-Migration

  • Delete .fo and .xslfo template files
  • Remove fo.net-related code and utilities
  • Update documentation

For deeper coverage of the IronPDF surface used above, see the IronPDF API reference.

Please note: FO.NET, Fonet, and Apache FOP are trademarks of their respective owners. This site is not affiliated with, endorsed by, or sponsored by the FO.NET project or the Apache Software Foundation. All product names, logos, and brands are property of their respective owners. Comparisons are for informational purposes only and reflect publicly available information at the time of writing.
Curtis Chau
Technical Writer

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.

...
Read More

Related Articles

Key in blue circle

Get your free 30-day Trial Key instantly.

Your trial license will be sent to your email address

No limitations. 100% unlocked. No credit card.

bullet_checkedNo credit card or account creation requiredNo limitations. 100% unlocked. No credit card.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
Book your free Live Demo
Booking Badge

Trusted by Millions of Engineers Worldwide

Iron Software's customer logos
Get Your No-Obligation Consult
Complete the form below or email sales@ironsoftware.com
Your details will always be kept confidential.
Trusted by Millions of Engineers Worldwide
Iron Software's customer logos
Get your free 30-day Trial Key instantly.
No credit card or account creation required