Limits, recovery and cancellation
PostScript is a programming language, so a hostile or broken file can loop forever or ask for gigabytes. XPS is XML in a ZIP package, with its own failure modes. These controls are for a host that converts files it does not trust.
Cancellation
Every public conversion has an overload with a CancellationToken as its last parameter. The
interpreter, the decoders, the rasteriser, the PDF writer and the XPS renderer poll the token, per
row, record or batch of objects. A cancelled call throws OperationCanceledException. It is never
turned into a warning or a package exception, and SuppressErrors does not suppress it. See the
family convention.
What cannot be interrupted: the Type 1 font resource patterns in the interpreter (linear time but not polled), the XML parser while it reads a single part, and the CLR's own zlib.
Interpreter limits
PsInterpreterLimits bounds a PostScript job even when the caller passes no token. Set it on
PdfSaveOptions.Limits or ImageSaveOptions.Limits.
| Limit | Default | Bounds |
|---|---|---|
MaxOperators |
400 million | Executed objects and procedure calls. A loop like {} loop reaches it in a few seconds. |
MaxOperandStackDepth |
500,000 | The operand stack. |
MaxExecutionDepth |
985 | Procedure call nesting. This is the recursion limit of the original library, so the same programs fail the same way. |
MaxNestingDepth |
10,000 | Nesting of { } in the source. |
MaxMemoryBytes |
1 GiB | Each single allocation the job asks for, and growth of the managed heap since it started. null disables both. |
MaxSuppressedErrors |
1,000 | Errors that SuppressErrors absorbs before the job stops. |
PsInterpreterLimits.Default and PsInterpreterLimits.Unlimited are provided. Exceeding a limit
throws PsResourceLimitError, which PostScript's stopped operator does not catch, so a program
cannot hide a runaway loop behind it. For rasterisation, MaxMemoryBytes also caps the page buffer;
without Limits that cap is 4 GiB.
Recovering from errors
By default the first error throws. With SuppressErrors = true, conversion keeps what it can and
records a ConversionWarning for each recovered error in the options' Warnings list.
- PostScript: the page being drawn is abandoned but kept as drawn so far, source objects are
skipped up to the next
showpage, thatshowpageruns, and interpretation continues. An exceeded limit ends the job and keeps the pages produced so far. - XPS: an element that fails is skipped and its half-drawn commands are rolled back. A page whose XML cannot be read is skipped.
using XRay.PS;
using XRay.PS.PostScript;
using XRay.PS.Xps;
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(60));
var options = new PdfSaveOptions
{
JpegQuality = 75, // re-encode raw 8-bit gray/RGB images as JPEG when that is smaller
SuppressErrors = true, // keep what was drawn, record a warning, go on after the next showpage
Limits = new PsInterpreterLimits { MaxOperators = 50_000_000 },
};
using var file = File.Create("drawing.pdf");
PsDocument.FromFile("drawing.ps", timeout.Token).ToPdf(file, options, timeout.Token);
foreach (var warning in options.Warnings)
{
Console.WriteLine($"{warning.Source} {warning.Code}: {warning.Message}");
}
// XPS: the same, with an optional page limit.
XpsDocument.FromFile("report.xps", timeout.Token).ToPdf(file, options, maxPages: 50, timeout.Token);
ConversionWarning is a record with Source, Code, Message and an optional PageNumber.
Warnings is appended to and never cleared, so use a fresh options object for each document.
Which exceptions to expect
Loading and conversion may throw only PsError subclasses (PsResourceLimitError is one),
XpsFormatException, HostErrorException, argument and invalid-operation errors, and
OperationCanceledException. A truncation and mutation test suite over PostScript, XPS and image
inputs guards this.