DEV Community

IronSoftware
IronSoftware

Posted on

Migrating from Ghostscript GPL to IronPDF: what actually changes in your code


Checklist: What to Read Before You Start

Before writing a single line of replacement code, confirm these items.

  • [ ] Identify your Ghostscript invocation pattern — subprocess call, Ghostscript.NET wrapper, or direct P/Invoke?
  • [ ] Audit GPL exposure — Ghostscript GPL is AGPL-licensed; distributing software that links against it may require your source to be open or a commercial Ghostscript license. Verify with your legal team.
  • [ ] List every gs argument combination in your codebase — each combination is a feature that needs a migration target.
  • [ ] Determine which operations you actually use — Ghostscript does a lot; you probably use 10% of it.
  • [ ] Check IronPDF feature overlap — IronPDF is not a PostScript interpreter; some Ghostscript use cases have no IronPDF equivalent.

Why Migrate (Without Drama)

Teams using Ghostscript in .NET applications often encounter a familiar set of issues depending on their use case and deployment environment:

  1. GPL/AGPL license obligations — Ghostscript under GPL requires derivative works to also be GPL unless a commercial license is purchased; this drives migration for commercial software.
  2. No first-party .NET library — Ghostscript requires subprocess invocation or third-party wrappers; both add fragility.
  3. Subprocess fragility — path resolution, process management, and argument escaping are error-prone across environments.
  4. Native binary dependency — Ghostscript must be installed on every machine, container, and CI runner; version mismatches cause subtle failures.
  5. No HTML-to-PDF capability — Ghostscript doesn't render HTML; if you've added a web-based report layer, you're already using a second tool.
  6. Error handling — Ghostscript returns exit codes and stderr output; structured error handling requires parsing text output.
  7. Thread safety — running multiple Ghostscript processes concurrently requires process management; a managed library is simpler.
  8. Windows vs. Linux binary differencesgswin64c.exe vs. gs adds conditional logic to process invocations.
  9. Upgrade fragility — Ghostscript version upgrades sometimes change argument behavior or output format.
  10. Container overheadapt-get install ghostscript in Dockerfiles pulls native dependencies; image sizes grow and dependency audits become more complex.

Comparison Table

Aspect Ghostscript (via .NET wrapper/subprocess) IronPDF
Focus PostScript/PDF interpretation, rasterization, conversion HTML-to-PDF rendering + document manipulation
Pricing GPL (free with AGPL conditions) or commercial license Commercial license
API Style Subprocess args / P/Invoke / wrapper library Fluent .NET library
Learning Curve High — PostScript model, arg-based configuration Medium — .NET-native API
HTML Rendering Not supported Primary capability (Chromium-based)
Page Indexing 1-based in most CLI contexts 0-based
Thread Safety Process-per-operation (subprocess) or verify wrapper Renderer-per-thread recommended
Namespace Ghostscript.NET (third-party) or System.Diagnostics.Process IronPdf

Migration Complexity Assessment

Effort by Feature

Feature Effort Notes
HTML to PDF Low (IronPDF) Ghostscript can't do this; IronPDF is new capability
PDF to image (rasterize) Medium-High Ghostscript strength; verify IronPDF rasterization
PDF merge Low IronPDF supports; Ghostscript merge via -sDEVICE=pdfwrite
PDF split Medium Both support; different API shape
PDF compression Medium Ghostscript -dPDFSETTINGS vs IronPDF compression options
PostScript to PDF N/A for IronPDF Ghostscript-only; no IronPDF equivalent
Font embedding Medium Handled differently in Chromium-based rendering
Password protection Low IronPDF supports natively
Watermarking Low IronPDF supports natively
Batch processing Medium Subprocess batching → managed async pattern

Decision Matrix

Business Scenario Recommendation
PostScript processing required Retain Ghostscript; no IronPDF equivalent
HTML-to-PDF primary workload IronPDF migration is well-scoped
PDF-to-image rasterization required Verify IronPDF raster export; test quality
GPL license compliance concern Commercial Ghostscript license or migrate; verify with legal

Before You Start

Prerequisites

Find Ghostscript References in Your Codebase

# Find subprocess calls to gs/gswin64c
rg "gswin|ghostscript|\"gs\"" --type cs -i

# Find Ghostscript.NET wrapper references
rg "Ghostscript\.NET" --type cs

# Find process start calls (broader scan)
rg "Process\.Start" --type cs

# Find NuGet package references
rg -i "ghostscript" **/*.csproj
Enter fullscreen mode Exit fullscreen mode

Remove Ghostscript Wrapper, Add IronPDF

# Remove Ghostscript.NET wrapper (if used — verify package name)
dotnet remove package Ghostscript.NET

# Add IronPDF
dotnet add package IronPdf

dotnet restore
Enter fullscreen mode Exit fullscreen mode

Note: The Ghostscript native binary installation (OS package) is separate from any NuGet wrapper. If you remove all Ghostscript usages, you can also remove the native binary from container images, Dockerfiles, and CI provisioning scripts.


Quick Start Migration (3 Steps)

Step 1: License Configuration

Before (Ghostscript — no .NET license initialization)

// Ghostscript doesn't have a .NET license initialization step.
// Licensing is handled at the OS/binary level.
// GPL usage is governed by Ghostscript's AGPL license terms — verify legal compliance.
using System.Diagnostics;

// Typical Ghostscript process setup:
var psi = new ProcessStartInfo
{
    FileName  = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                    ? "gswin64c.exe"
                    : "gs",
    Arguments = "-dNOPAUSE -dBATCH ...", // varies per operation
    RedirectStandardOutput = true,
    RedirectStandardError  = true,
    UseShellExecute = false
};
Enter fullscreen mode Exit fullscreen mode

After (IronPDF)

using IronPdf;

// See: https://ironpdf.com/how-to/license-keys/
IronPdf.License.LicenseKey = "YOUR_IRONPDF_KEY";
// One line. No binary path resolution. No platform branching.
Enter fullscreen mode Exit fullscreen mode

Step 2: Namespace Imports

Before

using System.Diagnostics;
using System.Runtime.InteropServices;
// If using Ghostscript.NET wrapper:
// using Ghostscript.NET;
// using Ghostscript.NET.Processor;
Enter fullscreen mode Exit fullscreen mode

After

using IronPdf;
using IronPdf.Rendering;
using IronPdf.Editing;
Enter fullscreen mode Exit fullscreen mode

Step 3: Basic Conversion

Before (Ghostscript PDF merge via subprocess)

using System.Diagnostics;
using System.Runtime.InteropServices;

// Ghostscript PDF merge — subprocess approach
static void MergeWithGhostscript(string[] inputs, string output)
{
    string gs = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                    ? "gswin64c.exe"
                    : "gs";

    // Build input file args
    string inputArgs = string.Join(" ", inputs.Select(f => $"\"{f}\""));

    var psi = new ProcessStartInfo
    {
        FileName  = gs,
        Arguments = $"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite " +
                    $"-sOutputFile=\"{output}\" {inputArgs}",
        RedirectStandardOutput = true,
        RedirectStandardError  = true,
        UseShellExecute = false
    };

    using var proc = Process.Start(psi)!;
    proc.WaitForExit();

    if (proc.ExitCode != 0)
    {
        string error = proc.StandardError.ReadToEnd();
        throw new Exception($"Ghostscript exited {proc.ExitCode}: {error}");
    }
}
Enter fullscreen mode Exit fullscreen mode

After (IronPDF)

using IronPdf;

// See: https://ironpdf.com/how-to/merge-or-split-pdfs/
IronPdf.License.LicenseKey = "YOUR_KEY";

static void MergeWithIronPdf(string[] inputs, string output)
{
    var docs = inputs.Select(PdfDocument.FromFile).ToList();
    using var merged = PdfDocument.Merge(docs);
    merged.SaveAs(output);
    foreach (var d in docs) d.Dispose();
}
Enter fullscreen mode Exit fullscreen mode

API Mapping Tables

Namespace Mapping

Ghostscript Approach IronPDF Equivalent Purpose
System.Diagnostics.Process IronPdf (no subprocess) Process management → managed API
Ghostscript.NET.Processor ChromePdfRenderer Document processing
-sDEVICE=pdfwrite args PdfDocument operations PDF output device

Core Class/Concept Mapping

Ghostscript Concept IronPDF Equivalent Description
gs process ChromePdfRenderer / PdfDocument Core processing unit
-sOutputFile= arg pdf.SaveAs(path) Output path
Exit code check try/catch (PdfException) Error handling
Stdin/file input PdfDocument.FromFile(path) Document loading

Document Loading

Operation Ghostscript IronPDF
Load PDF for processing Input file arg to process PdfDocument.FromFile(path)
Load from bytes Temp file + process PdfDocument.FromBytes(bytes)
Load from stream Temp file + process PdfDocument.FromStream(stream)
HTML to PDF Not supported renderer.RenderHtmlAsPdf(html)

Page Operations

Operation Ghostscript CLI IronPDF
Page count Parse output or use wrapper pdf.PageCount
Extract page range -dFirstPage=N -dLastPage=M pdf.CopyPages(indices)
Page size Parse pagesize output pdf.Pages[0].Width/Height
Rotate -c "<< /Rotate 90 >> setpagedevice" (complex) page.Rotation = 90

Merge/Split Operations

Operation Ghostscript IronPDF
Merge PDFs -sDEVICE=pdfwrite + multiple input files PdfDocument.Merge(docs)
Split by page range -dFirstPage=N -dLastPage=M per range pdf.CopyPages(indices)

Four Complete Before/After Migrations

1. HTML to PDF

Before (Ghostscript — not applicable)

// Ghostscript does not render HTML to PDF.
// If you have an HTML-to-PDF requirement with Ghostscript in your stack,
// you're likely using a second tool (wkhtmltopdf, headless Chrome, etc.)
// or converting HTML→PostScript→PDF via an intermediate step.

// Ghostscript can convert PostScript to PDF:
// gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf in.ps
// But this is PostScript, not HTML.

Console.WriteLine("Ghostscript cannot render HTML; this is new capability in IronPDF");
Enter fullscreen mode Exit fullscreen mode

After (IronPDF)

using IronPdf;

// See: https://ironpdf.com/how-to/html-string-to-pdf/
IronPdf.License.LicenseKey = "YOUR_KEY";

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;

string html = "<html><body><h1>Report</h1><p>Generated via IronPDF.</p></body></html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Console.WriteLine($"Created {pdf.PageCount} page(s)");
Enter fullscreen mode Exit fullscreen mode

2. Merge PDFs

Before (Ghostscript subprocess)

using System.Diagnostics;
using System.Runtime.InteropServices;

class MergeExample
{
    static void Main(string[] args)
    {
        string[] inputFiles = { "file1.pdf", "file2.pdf", "file3.pdf" };
        string outputFile   = "merged.pdf";

        string gs = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                        ? "gswin64c.exe" : "gs";

        // Escape and join input paths
        string inputs = string.Join(" ", inputFiles.Select(f => $"\"{f}\""));

        var psi = new ProcessStartInfo
        {
            FileName  = gs,
            Arguments = $"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite " +
                        $"-dCompatibilityLevel=1.7 " +
                        $"-sOutputFile=\"{outputFile}\" {inputs}",
            RedirectStandardOutput = true,
            RedirectStandardError  = true,
            UseShellExecute        = false
        };

        using var proc = Process.Start(psi)!;
        string stderr = proc.StandardError.ReadToEnd();
        proc.WaitForExit();

        if (proc.ExitCode != 0)
            throw new Exception($"Ghostscript merge failed (exit {proc.ExitCode}): {stderr}");

        Console.WriteLine($"Merged to {outputFile}");
    }
}
Enter fullscreen mode Exit fullscreen mode

After (IronPDF)

using IronPdf;

// See: https://ironpdf.com/how-to/merge-or-split-pdfs/
IronPdf.License.LicenseKey = "YOUR_KEY";

var docs = new[] { "file1.pdf", "file2.pdf", "file3.pdf" }
              .Select(PdfDocument.FromFile)
              .ToList();

using var merged = PdfDocument.Merge(docs);
merged.SaveAs("merged.pdf");
foreach (var d in docs) d.Dispose();
Console.WriteLine($"Merged: {merged.PageCount} pages");
Enter fullscreen mode Exit fullscreen mode

3. Watermark

Before (Ghostscript — PostScript-based, complex)

using System.Diagnostics;
using System.IO;

class WatermarkExample
{
    static void Main(string[] args)
    {
        // Ghostscript watermarking requires injecting PostScript content
        // via a stamp/overlay file — a non-trivial approach

        // Step 1: Create a PostScript watermark file
        string watermarkPs = Path.GetTempFileName() + ".ps";
        File.WriteAllText(watermarkPs, @"
            /watermark {
                gsave
                0.3 setgray
                /Helvetica-Bold 40 selectfont
                200 400 moveto
                45 rotate
                (CONFIDENTIAL) show
                grestore
            } def
        ");

        // Step 2: Apply via Ghostscript — exact args vary
        // This is a simplified illustration; real implementation is more involved
        string gs = System.Runtime.InteropServices.RuntimeInformation
                         .IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)
                         ? "gswin64c.exe" : "gs";

        var psi = new ProcessStartInfo
        {
            FileName  = gs,
            Arguments = $"-dNOPAUSE -dBATCH -sDEVICE=pdfwrite " +
                        $"-sOutputFile=\"watermarked.pdf\" " +
                        $"\"{watermarkPs}\" \"input.pdf\"",
            UseShellExecute = false
        };
        using var proc = Process.Start(psi)!;
        proc.WaitForExit();
        File.Delete(watermarkPs);
    }
}
Enter fullscreen mode Exit fullscreen mode

After (IronPDF)

using IronPdf;
using IronPdf.Editing;

// See: https://ironpdf.com/how-to/custom-watermark/
IronPdf.License.LicenseKey = "YOUR_KEY";

using var pdf = PdfDocument.FromFile("input.pdf");

var stamp = new TextStamper
{
    Text                = "CONFIDENTIAL",
    FontSize            = 40,
    Opacity             = 0.3,
    Rotation            = -45,
    VerticalAlignment   = VerticalAlignment.Middle,
    HorizontalAlignment = HorizontalAlignment.Center
};

pdf.ApplyStamp(stamp);
pdf.SaveAs("watermarked.pdf");
Enter fullscreen mode Exit fullscreen mode

4. Password Protection

Before (Ghostscript)

using System.Diagnostics;
using System.Runtime.InteropServices;

class SecurityExample
{
    static void Main(string[] args)
    {
        string gs = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                        ? "gswin64c.exe" : "gs";

        // Ghostscript password protection via -sOwnerPassword / -sUserPassword
        var psi = new ProcessStartInfo
        {
            FileName  = gs,
            Arguments = "-dNOPAUSE -dBATCH -sDEVICE=pdfwrite " +
                        "-dEncryptionR=3 -dKeyLength=128 " +
                        "-sOwnerPassword=owner456 " +
                        "-sUserPassword=user123 " +
                        "-dPermissions=-3904 " +      // permission bitmask — verify values
                        "-sOutputFile=\"protected.pdf\" \"input.pdf\"",
            RedirectStandardError = true,
            UseShellExecute       = false
        };

        using var proc = Process.Start(psi)!;
        proc.WaitForExit();

        if (proc.ExitCode != 0)
            throw new Exception($"Ghostscript encryption failed: {proc.StandardError.ReadToEnd()}");

        Console.WriteLine("Protected PDF created");
    }
}
Enter fullscreen mode Exit fullscreen mode

After (IronPDF)

using IronPdf;
using IronPdf.Security;

// See: https://ironpdf.com/how-to/pdf-permissions-passwords/
IronPdf.License.LicenseKey = "YOUR_KEY";

using var pdf = PdfDocument.FromFile("input.pdf");

pdf.SecuritySettings.UserPassword  = "user123";
pdf.SecuritySettings.OwnerPassword = "owner456";
pdf.SecuritySettings.AllowUserPrinting         = PdfPrintSecurity.FullPrintRights;
pdf.SecuritySettings.AllowUserCopyPasteContent = false;

pdf.SaveAs("protected.pdf");
Enter fullscreen mode Exit fullscreen mode

Critical Migration Notes

Page Indexing

Ghostscript CLI uses 1-based page numbering (-dFirstPage=1). IronPDF uses 0-based indexing. Adjust any page number calculations.

// Ghostscript: -dFirstPage=1 -dLastPage=3 extracts pages 1, 2, 3
// IronPDF equivalent:
int[] pageIndices = { 0, 1, 2 }; // 0-based: first three pages
using var extracted = pdf.CopyPages(pageIndices);
Enter fullscreen mode Exit fullscreen mode

Error Handling: Exit Codes vs Exceptions

Ghostscript returns exit codes; IronPDF raises exceptions. Remove all ExitCode != 0 checks and replace with try/catch.

// Replace exit code checks:
// if (proc.ExitCode != 0) throw new Exception(...)

// With exception handling:
try
{
    using var pdf = PdfDocument.FromFile("input.pdf");
    // operations
}
catch (IronPdf.Exceptions.PdfException ex)
{
    // Structured exception with message, no stderr parsing
    Console.Error.WriteLine(ex.Message);
}
Enter fullscreen mode Exit fullscreen mode

No PostScript Support

IronPDF cannot interpret PostScript (.ps) files. If your pipeline processes .ps input, you have two options: convert PostScript to PDF first (retain Ghostscript for that step only), or restructure the input pipeline to start from HTML/PDF.


Performance Considerations

Subprocess Overhead Elimination

Ghostscript subprocess invocations carry process creation overhead per operation. IronPDF operates in-process. For high-throughput pipelines, this overhead reduction is measurable — but benchmark against your actual workload.

Parallel Processing

Ghostscript parallel processing requires managing multiple processes. IronPDF parallel processing uses Task/Parallel with one ChromePdfRenderer per task:

// See: https://ironpdf.com/examples/parallel/
var results = await Task.WhenAll(
    htmlDocuments.Select(async html =>
    {
        var r = new ChromePdfRenderer();
        using var pdf = await r.RenderHtmlAsPdfAsync(html);
        return pdf.BinaryData;
    })
);
Enter fullscreen mode Exit fullscreen mode

Container Image Size

Removing Ghostscript from Docker images reduces the native dependency footprint. IronPDF adds Chromium binaries. Net effect on image size depends on your Ghostscript version and IronPDF version — measure before and after.

# Before: Ghostscript in Dockerfile
# RUN apt-get update && apt-get install -y ghostscript

# After: remove above line; IronPDF handles Chromium deps automatically
Enter fullscreen mode Exit fullscreen mode

Migration Checklist

Pre-Migration

  • [ ] Audit all Ghostscript subprocess invocations — list every argument pattern
  • [ ] Identify PostScript processing (.ps inputs) — these cannot be migrated to IronPDF
  • [ ] Confirm GPL/AGPL license compliance intent with legal team
  • [ ] Verify IronPDF covers all required operations (especially rasterization if needed)
  • [ ] Obtain IronPDF license key; confirm activation
  • [ ] Review https://ironpdf.com/how-to/license-keys/
  • [ ] Create migration branch
  • [ ] Document current Ghostscript version in all environments

Code Migration

  • [ ] Remove Ghostscript.NET NuGet package (if used)
  • [ ] Add IronPdf NuGet package
  • [ ] Replace all Process.Start("gs*") calls
  • [ ] Replace Ghostscript.NET wrapper calls
  • [ ] Implement IronPdf.License.LicenseKey initialization at startup
  • [ ] Replace merge subprocess calls with PdfDocument.Merge(...)
  • [ ] Replace security subprocess calls with pdf.SecuritySettings
  • [ ] Replace watermark subprocess/PS calls with TextStamper / ImageStamper
  • [ ] Replace page index logic: 1-based → 0-based
  • [ ] Replace exit code checks with try/catch

Testing

  • [ ] Visual comparison of merged PDFs (page order, content)
  • [ ] Test password protection roundtrip
  • [ ] Test watermark rendering on 3+ page types
  • [ ] Verify page extraction by index (check off-by-one)
  • [ ] Test concurrent operations under load
  • [ ] Compare file sizes for compressed outputs
  • [ ] Verify no gs or gswin64c references remain in code or scripts

Post-Migration

  • [ ] Remove Ghostscript from all Dockerfiles and provisioning scripts
  • [ ] Remove Ghostscript from CI/CD runner setup
  • [ ] Remove Ghostscript license keys or binaries from deployment packages
  • [ ] Update deployment docs: remove Ghostscript installation steps

Where to Go From Here

The Ghostscript migration is often the most impactful from an architecture perspective, because Ghostscript isn't a .NET library — it's a native binary invoked via subprocess. Replacing it with a managed NuGet package removes an entire category of deployment complexity: binary path resolution, platform-specific process names, stderr parsing for error detection, and native package management in containers.

The capability gaps are real: PostScript interpretation and certain rasterization workflows don't have direct IronPDF equivalents. For those, retaining a minimal Ghostscript deployment or restructuring the pipeline is the honest answer.

A practical question for the comments: when you've audited your Ghostscript subprocess calls, how many distinct argument patterns did you find, and which ones proved hardest to map to a managed library? The range of Ghostscript usage in production .NET applications is wider than most expect.


Top comments (0)