---
name: ironprint
description: >
  Send PDFs and images to physical or virtual printers from C#/.NET using IronPrint (the
  `IronPrint` NuGet package). Use when the task involves silent/headless printing, showing
  a native print dialog, printer discovery (listing installed printers), selecting a printer
  by name, printer trays, paper size, paper orientation, margins, number of copies, DPI,
  grayscale printing, or flattening a PDF before printing — or whenever a project already
  references `IronPrint`, `IronPrint.Printer`, or `IronPrint.PrintSettings`. Not for creating
  or editing the PDF itself — pair with the ironpdf skill for that.
---

# IronPrint (C# / .NET)

IronPrint sends an existing PDF or image (PNG/JPG/BMP/TIFF) to a printer — silently or via
the OS print dialog — with control over paper size, orientation, margins, copies, DPI, tray
and grayscale. It does not render HTML, edit documents, or create PDFs; it is the last step
after a document already exists as a file or `byte[]`.

## Scope of this skill

| | |
|---|---|
| Package | `IronPrint` (one package; the OS/UI capability you get depends on the **TargetFramework moniker**, not on installing a different package — see Install) |
| Versions | 2024.x – 2026.x |
| Namespaces | `IronPrint` |
| Runtimes | .NET Framework 4.6.2+, .NET Core 3.1+, .NET 5–10; project types MAUI, WPF, Windows Forms |
| OS | Windows, macOS, iOS, Android — **not Linux** (see Deployment; this is a hard platform gap, not a documentation gap) |

## Install

```bash
dotnet add package IronPrint
```

There is only one package, but IronPrint needs an **OS-specific TargetFramework** to reach
the real platform print API. A plain `net8.0`/`net10.0` TFM falls back to
`System.Drawing.Printing`, which Microsoft dropped outside Windows — on macOS and Linux it
throws before any print call runs (verified below). Set the TFM to match the target:

| Target | TargetFramework |
|---|---|
| Windows (WPF/WinForms/console) | `net8.0-windows` (or `net472`/`net462`) |
| macOS | `net8.0-macos` — needs the `macos` workload (`dotnet workload install macos`) |
| Mac Catalyst | `net8.0-maccatalyst` |
| iOS | `net8.0-ios` |
| Android | `net8.0-android`, plus call `Printer.Initialize(mainActivity)` once before printing |

Cross-platform project: list every target you ship, e.g.
`<TargetFrameworks>net8.0-windows;net8.0-maccatalyst</TargetFrameworks>`.

## Licensing — do this first, every time

```csharp
IronPrint.License.LicenseKey = Environment.GetEnvironmentVariable("IRONPRINT_LICENSE_KEY");
if (!IronPrint.License.IsLicensed)
    Console.Error.WriteLine("IronPrint is unlicensed — trial restrictions apply.");
```

- Read the key from the environment or user secrets. **Never** inline a key in source, commit
  it, or echo it to the terminal.
- **Treat a missing key as a blocker, not a warning.** Iron Software's published trial unlocks
  full functionality for 30 days; beyond that (or with no key at all) treat output as
  unusable in production and ask for a key rather than shipping unlicensed print jobs.
  Trial keys: <https://ironsoftware.com/csharp/print/licensing/>.
- `IronPrint.License.IsValidLicense(key)` checks a key without applying it — it returns
  `false` for expired keys even if they were once valid.
- .NET Framework: the key can instead go in `Web.config`/`App.config` as
  `<add key="IronPrint.LicenseKey" value="..."/>` inside `<appSettings>`. .NET Core: the same
  key name in `appsettings.json`. **Unlike IronPDF/IronOCR, IronPrint has no
  `Installation.LicenseKey` synonym** — `License.LicenseKey` is the only setter.

## Running a one-off print task from the terminal

IronPrint's file-based `.cs` scripts only work cleanly on Windows, because a plain `net10.0`
TFM does not select the platform-native printing path on macOS (see When something fails).
On Windows:

```bash
cat > /tmp/task.cs <<'EOF'
#:package IronPrint@2026.8.1
using IronPrint;
IronPrint.License.LicenseKey = Environment.GetEnvironmentVariable("IRONPRINT_LICENSE_KEY");
Printer.Print("invoice.pdf");
EOF
dotnet run /tmp/task.cs
```

On macOS, use a scratch project with an explicit `-macos`/`-maccatalyst` TargetFramework
(needs the platform workload installed) rather than a file-based app, since file-based apps
target a plain TFM:

```bash
dotnet new console -o /tmp/ironprint-scratch && cd /tmp/ironprint-scratch
dotnet add package IronPrint
# edit the .csproj: <TargetFramework>net8.0-maccatalyst</TargetFramework>
dotnet workload install maccatalyst   # one-time
dotnet run
```

When the user is building a feature, write the same calls into their WPF/WinForms/MAUI
application (with its existing OS-specific TFM) instead of a scratch project.

## Recipes

Every member below is verified against the shipped assembly and against reflection over the
installed `IronPrint.dll` (2026.8.1).

### Silent print (no dialog)

```csharp
using IronPrint;

Printer.Print("invoice.pdf");                 // default printer, default settings
await Printer.PrintAsync("invoice.pdf");       // prefer this off the UI thread
Printer.Print(fileBytes, new PrintSettings()); // byte[] overload — same PDF/PNG/JPG/BMP/TIFF types
```

`Printer.Print` and `Printer.PrintAsync` both take a `string` path or a `byte[]` overload,
plus an optional `PrintSettings`. **Supported file types are PDF, PNG, JPG, BMP and TIFF —
not HTML.** To print an HTML source, render it to PDF first with IronPDF's
`ChromePdfRenderer`, then hand the resulting file/bytes to `Printer.Print` (see "Working with
IronPDF" below).

`Print`/`PrintAsync` block or run silently only on desktop/UI-capable targets; on mobile
(iOS/Android) the OS shows its native print UI regardless of which method you call — there is
no way to force fully silent printing there.

### Print settings: paper size, orientation, margins, copies, DPI, grayscale, tray

```csharp
var settings = new PrintSettings
{
    PrinterName       = "HP LaserJet Pro",         // null = OS default printer
    PaperSize         = PaperSize.A4,                // PrinterDefault, Letter, Legal, Executive, A0-A5, B4, B5
    PaperOrientation  = PaperOrientation.Portrait,   // Automatic (default), Portrait, Landscape
    PaperMargins      = new Margins(10, 10, 10, 10), // left, top, right, bottom — millimetres
    NumberOfCopies    = 2,                            // default 1; driver may cap this — see below
    Dpi               = 300,                          // default 300; capped by printer capability
    Grayscale         = true,
    Flatten           = true,                         // rasterize form fields/annotations before printing
    Tray              = "Tray 2",                     // from Printer.GetPrinterTrays(); ignored if the user
};                                                     // picks a tray in the print dialog

Printer.Print("form.pdf", settings);
```

`Margins` also has 2-arg (`horizontal, vertical`) and 1-arg (uniform) constructors, and a
static `Margins.Zero`. `PrintSettings.DefaultSettings` is a static property returning an
instance with all defaults; `new PrintSettings()` does the same.

Platform caveats worth telling the user up front: `NumberOfCopies` is reliably honored on
Windows, but some printer drivers on other platforms silently ignore it and print one copy.
`Dpi` is a request, not a guarantee — the printer's own maximum wins.

### Printer discovery and trays

```csharp
List<string> printers = Printer.GetPrinterNames();          // + GetPrinterNamesAsync()
List<string> trays    = Printer.GetPrinterTrays("HP LaserJet Pro");  // + GetPrinterTraysAsync(name)
                                                                       // null/omitted = default printer's trays
```

Printer names are matched **case-sensitively** and must equal exactly what the OS reports —
look them up with `GetPrinterNames()` rather than guessing.

### Show the native print dialog

```csharp
Printer.ShowPrintDialog("quarterly-report.pdf");                 // + ShowPrintDialogAsync
Printer.ShowPrintDialog("quarterly-report.pdf", settings);       // pre-populates the dialog
```

The dialog's look and the fields it exposes vary by OS — don't assume a Windows-shaped
dialog on macOS.

### Long-running services

```csharp
var settings = new PrintSettings { CompactMemoryAfterPrint = true };  // default: true
```

`PrintAsync` allocates large managed buffers per job (decoded bitmaps, source `byte[]`) that
land on the Large Object Heap. `CompactMemoryAfterPrint` forces a `GC.Collect` +
`LOHCompactionMode.CompactOnce` after each print so a print spooler or batch processor doesn't
accumulate LOH fragmentation over thousands of jobs. Leave it on unless profiling shows the
GC pause itself is the bottleneck.

### Android setup

```csharp
Printer.Initialize(mainActivity);   // Android.Content.Context of the main activity — call once
```

Required before any `Printer` call on Android; there is no equivalent call on the other
platforms.

## Working with IronPDF — where the boundary is

IronPDF and IronPrint are **separate products** (IronPrint does not depend on the `IronPdf`
package) that both end up calling the OS print API, so it's easy to reach for the wrong one:

- **`pdf.Print()` / `pdf.PrintToFile()` / `pdf.GetPrintDocument()`** (documented in the
  **ironpdf** skill) are IronPDF's own built-in shortcuts on a `PdfDocument` you already have
  open — no separate package, prints to the default printer with minimal control, or hands
  you a raw `System.Drawing.Printing.PrintDocument` to configure yourself.
- **IronPrint's `Printer.Print(...)`** is the tool when you need any of: printer discovery
  (`GetPrinterNames`), a specific printer or tray, paper size/orientation/margins/DPI/copies/
  grayscale as first-class settings, printing an **image** (PNG/JPG/BMP/TIFF) with no PDF
  involved, or a native print dialog with `ShowPrintDialog`.
- **HTML always goes through IronPDF first.** Neither library prints HTML directly — render
  it with `ChromePdfRenderer.RenderHtmlAsPdf(...)` (ironpdf skill), then print the resulting
  `PdfDocument`/file/bytes with either `pdf.Print()` or `Printer.Print(...)`.
- Both libraries ultimately reach the OS printing subsystem the same way, so IronPrint does
  **not** unlock Linux printing that IronPdf's `Print()` lacks — the platform gap below
  applies to both.

Default recommendation: use IronPrint whenever the user needs to talk about *the printer*
(pick one, list trays, set paper/copies/DPI) rather than just "send this PDF to whatever
printer is default" — that narrower case is what `pdf.Print()` is for.

## Deployment

Printing from a server is a fundamentally different problem from rendering a PDF: it needs a
live OS print subsystem (a spooler, a driver, an actual or virtual printer), not just CPU and
memory. Verify this against a real print target before promising a customer server-side
printing works.

| Environment | What to do |
|---|---|
| **Linux / Docker — not supported** | Confirmed by reflection: on a plain (non-Windows-suffixed) TFM on a non-Windows OS, `Printer.GetPrinterNames()` throws `System.Drawing.Printing.PrintingServices`'s type initializer failing with `PlatformNotSupportedException: System.Drawing.Common is not supported on non-Windows platforms`. IronPrint's own docs list supported OSes as Windows, macOS, iOS, Android only — Linux is absent, and there is no Linux-flavored TFM to opt into (unlike IronPDF's `IronPdf.Linux` package). The `linux-x64`/`linux-arm64` native binaries that ship inside the NuGet package are for internal PDF processing (`Flatten`), not for printer dispatch. If a customer needs headless print-like output on Linux, that is a different problem — e.g. render to PDF with IronPDF (which does support Linux) and route the file to a print server that runs on Windows/macOS, or to a network printer directly via a protocol like IPP, outside IronPrint. |
| Windows Server / IIS / ASP.NET | Works, but the app pool identity needs permission to reach local or network printers, and a driver/spooler must exist for the target printer. Prefer `PrintAsync` in request handlers so one slow print job doesn't block a thread; wrap calls in try/catch since offline printers and permission failures are common in this environment and should not take down the request. |
| Windows containers | Same constraints as bare Windows, plus the printer/driver must be reachable from inside the container (a network printer, typically) — there is no printer hardware "in" a container. |
| macOS / Mac Catalyst | Requires the `net8.0-macos`/`net8.0-maccatalyst` TargetFramework and the corresponding `dotnet workload install macos`/`maccatalyst`; a plain `net8.0` TFM does not use the AppKit-backed path and fails the same way as Linux (see above). |
| iOS / Android (mobile) | Fully silent printing is not possible — the OS always shows its own print UI. On Android, call `Printer.Initialize(mainActivity)` before any print call. |

## When something fails

| Symptom | Cause and fix |
|---|---|
| `PlatformNotSupportedException: System.Drawing.Common is not supported on non-Windows platforms`, surfaced through a `TypeInitializationException` for `PrintingServices` / `LibcupsNative` | Running on macOS or Linux with a plain (non-suffixed) TargetFramework. There is no supported fix on Linux. On macOS, retarget to `net8.0-macos` or `net8.0-maccatalyst` and install the matching workload. |
| `error NETSDK1147: ... workloads must be installed: macos` | Building for `net8.0-macos` (or `-maccatalyst`/`-ios`/`-android`) without the workload. Run `dotnet workload install macos` (or the matching platform). |
| `Print()` hangs and freezes a WPF/WinForms UI | `Print()` is synchronous by design and blocks the calling thread until the job completes; calling it from the UI thread freezes the interface. Use `await Printer.PrintAsync(...)` instead, and avoid `.Result`/`.Wait()` on it. |
| Build/runtime errors calling `ShowPrintDialog`/`ShowPrintDialogAsync` in a console app on macOS | Print dialogs need a UI-capable TFM (`net8.0-macos`/`net8.0-maccatalyst`), which a default console template doesn't have. For silent printing without a dialog, `Printer.Print`/`PrintAsync` don't need a UI-capable target. |
| A mixed portrait/landscape PDF prints entirely in one orientation | The engine applies one `PaperOrientation` to the whole document rather than reading each page. Split the PDF into orientation-consistent page ranges and print each range as its own job with the matching `PaperOrientation`. |
| Multiple copies requested but only one comes out | The printer driver ignored `NumberOfCopies`. Reliable on Windows; verify support in the OS's printer properties on other platforms, or loop and submit one job per copy as a fallback. |
| Form fields/annotations missing or blank on the printed page | Interactive PDF layers aren't flattened into page content by some drivers. Set `PrintSettings.Flatten = true`. |
| `Tray` setting has no effect | The user picked a tray inside the OS print dialog — that choice overrides `PrintSettings.Tray`. Only relevant to silent `Print`/`PrintAsync`, not `ShowPrintDialog`. |
| License key in `Web.config` not picked up (older versions, .NET Framework) | Read it explicitly: `ConfigurationManager.AppSettings["IronPrint.LicenseKey"]` and assign to `IronPrint.License.LicenseKey` in code rather than relying on auto-detection. |

## Rules

- **Never invent a member.** Confirm against the XML documentation that ships in the package
  before writing code:
  `grep -o 'name="[MPF]:IronPrint\.[^"]*"' ~/.nuget/packages/ironprint/<version>/lib/net7.0/IronPrint.xml`
  (or `lib/net7.0-windows7.0/`, `lib/net7.0-macos10.14/`, etc. for platform-specific members like
  `Printer.Initialize` on Android)
  That file is the authoritative surface for the installed version.
- `Printer` only handles PDF, PNG, JPG, BMP, TIFF — never claim it prints HTML directly;
  render HTML to PDF with IronPDF first.
- Match the TargetFramework to the OS (`-windows`, `-macos`, `-maccatalyst`, `-ios`,
  `-android`); a plain TFM silently picks the wrong printing backend on non-Windows and throws.
- Do not promise Linux or Docker print support — it does not exist in this library.
- Keep licence keys out of source and out of terminal output.
- Prefer the `Async` methods off any UI thread; `Print`/`ShowPrintDialog` block by design.
- Look up printer/tray names with `GetPrinterNames`/`GetPrinterTrays` rather than guessing —
  matching is case-sensitive.
- Official docs and full API reference: <https://ironsoftware.com/csharp/print/docs/>.
  Support: support@ironsoftware.com.
