How to Refry a PDF in .NET with C#

Refry a PDF with .NET with C# through a PDF to PostScript to PDF workflow using pdfRest resource IDs.
Share this page

This .NET walkthrough uses C# and HttpClient to submit the request. It completes a PDF -> PostScript -> PDF roundtrip by passing a resource ID from /postscript into /pdf. The complete sample is included below so .NET with C# developers can see the file handling, request construction, and response path together.

Why Refry a PDF in .NET with C#?

PDF refrying converts a PDF to PostScript and then converts the PostScript into a new PDF. It can fit a print or prepress integration that needs a PostScript-based derivative or rebuilt page content for an established downstream requirement.

The C# complex-flow example demonstrates how a .NET application passes resource IDs through that multi-call job. It creates PostScript from the source PDF, uses the returned ID in the conversion back to PDF, and writes the final bytes only after the second response succeeds. A hosted worker can retain each stage’s output ID for supportability while temporary resources follow service cleanup policy.

The result may no longer retain the source PDF’s tags, form behavior, layers, or other PDF-specific structures. Preserve the original before choosing refrying for a downstream requirement.

C# Code Example for PDF Refrying

/*
 * What this sample does:
 * - Converts PDF to PostScript and then converts the PostScript back to PDF.
 * - This PDF -> PostScript -> PDF roundtrip is commonly called PDF refrying.
 * - Some print, prepress, and legacy production workflows use it to rebuild,
 *   flatten, or normalize page content for downstream systems.
 * - Refrying is intentionally lossy and may remove tags, forms, layers,
 *   annotations, transparency, metadata, and editability.
 * - The PostScript-to-PDF step uses a custom .joboptions profile in this
 *   sample. A .joboptions file contains Adobe Distiller-compatible conversion
 *   settings; it is optional, and default settings are used when omitted.
 * - pdfRest applies the profile with Datalogics PDF Converter SDK. Datalogics
 *   maintains the SDK in partnership with Adobe, using the same Adobe
 *   technology that powers Distiller.
 *
 * Setup (environment):
 * - Copy .env.example to .env
 * - Set PDFREST_API_KEY=your_api_key_here
 * - Optional: set PDFREST_URL to override the API region. For EU/GDPR
 *   compliance and proximity, use PDFREST_URL=https://eu-api.pdfrest.com
 *
 * Usage:
 *   dotnet run -- refry-pdf   [outputPdf]
 *
 * Output:
 * - Prints each API result and downloads refried.pdf unless outputPdf is set.
 */

using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;

namespace Samples.ComplexFlowExamples;

public static class RefryPdf
{
    public static async Task Execute(string[] args)
    {
        if (args.Length < 2)
        {
            throw new ArgumentException("refry-pdf requires   [outputPdf]");
        }

        var inputPath = args[0];
        var jobOptionsPath = args[1];
        var outputPath = args.Length > 2 ? args[2] : Path.Combine("Complex Flow Examples", "refried.pdf");
        var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY");
        if (string.IsNullOrWhiteSpace(apiKey))
        {
            throw new InvalidOperationException("Missing PDFREST_API_KEY");
        }

        var baseUrl = (Environment.GetEnvironmentVariable("PDFREST_URL") ?? "https://api.pdfrest.com").TrimEnd('/');
        using var client = new HttpClient(new HttpClientHandler { UseCookies = false })
        {
            BaseAddress = new Uri(baseUrl),
            Timeout = TimeSpan.FromMinutes(2),
        };
        client.DefaultRequestHeaders.TryAddWithoutValidation("Api-Key", apiKey);
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var postscript = await ConvertToPostscript(client, inputPath);
        var finalPdf = await ConvertToPdf(client, postscript["outputId"]!.Value()!, jobOptionsPath);
        var finalId = finalPdf["outputId"]!.Value()!;

        var bytes = await client.GetByteArrayAsync($"resource/{Uri.EscapeDataString(finalId)}?format=file");
        await File.WriteAllBytesAsync(outputPath, bytes);
        Console.WriteLine(finalPdf.ToString());
        Console.WriteLine($"Created {Path.GetFullPath(outputPath)}");
    }

    private static async Task ConvertToPostscript(HttpClient client, string inputPath)
    {
        using var form = new MultipartFormDataContent();
        var input = new ByteArrayContent(await File.ReadAllBytesAsync(inputPath));
        input.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
        form.Add(input, "file", Path.GetFileName(inputPath));
        form.Add(new StringContent("3"), "ps_level");
        form.Add(new StringContent("all"), "page_range");
        form.Add(new StringContent("true"), "binary_output");
        form.Add(new StringContent("1"), "scale");
        form.Add(new StringContent("false"), "rotate");
        form.Add(new StringContent("true"), "shrink_to_fit");
        form.Add(new StringContent("true"), "print_annotations");
        form.Add(new StringContent("refry_intermediate"), "output");
        return await Post(client, "postscript", form);
    }

    private static async Task ConvertToPdf(
        HttpClient client,
        string postscriptId,
        string jobOptionsPath)
    {
        using var form = new MultipartFormDataContent();
        form.Add(new StringContent(postscriptId), "id");
        var jobOptions = new ByteArrayContent(await File.ReadAllBytesAsync(jobOptionsPath));
        jobOptions.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        form.Add(jobOptions, "job_options", Path.GetFileName(jobOptionsPath));
        form.Add(new StringContent("refried"), "output");
        return await Post(client, "pdf", form);
    }

    private static async Task Post(
        HttpClient client,
        string endpoint,
        HttpContent content)
    {
        using var response = await client.PostAsync(endpoint, content);
        var text = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"{endpoint}: {(int)response.StatusCode}");
        if (!response.IsSuccessStatusCode)
        {
            throw new InvalidOperationException($"{endpoint} failed: {text}");
        }

        return JObject.Parse(text);
    }
}

Source: View the sample on GitHub

Breaking Down the Code

Configure the service and input

var outputPath = args.Length > 2 ? args[2] : Path.Combine("Complex Flow Examples", "refried.pdf");
        var apiKey = Environment.GetEnvironmentVariable("PDFREST_API_KEY");
        if (string.IsNullOrWhiteSpace(apiKey))
        {
            throw new InvalidOperationException("Missing PDFREST_API_KEY");
        }

For this PDF Refrying example, .NET with C# coordinates the two dependent API calls. The C# samples use framework HttpClient APIs and disposable content objects. API credentials come from PDFREST_API_KEY, keeping the secret out of the source file and request body. The sample’s placeholder path and API key make the moving pieces visible; replace them with a real local path and protected runtime credential before using the request.

Pass the Intermediate Resource to the Next Step

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var postscript = await ConvertToPostscript(client, inputPath);
        var finalPdf = await ConvertToPdf(client, postscript["outputId"]!.Value()!, jobOptionsPath);
        var finalId = finalPdf["outputId"]!.Value()!;

        var bytes = await client.GetByteArrayAsync($"resource/{Uri.EscapeDataString(finalId)}?format=file");
        await File.WriteAllBytesAsync(outputPath, bytes);
        Console.WriteLine(finalPdf.ToString());
        Console.WriteLine($"Created {Path.GetFullPath(outputPath)}");
    }

In the .NET with C# workflow, outputId from /postscript becomes the id passed to /pdf. The second request also supplies a custom .joboptions file so the return conversion has an explicit Adobe Distiller-compatible profile, even though default settings are available when the profile is omitted.

Build the multipart request

private static async Task ConvertToPostscript(HttpClient client, string inputPath)
    {
        using var form = new MultipartFormDataContent();
        var input = new ByteArrayContent(await File.ReadAllBytesAsync(inputPath));
        input.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
        form.Add(input, "file", Path.GetFileName(inputPath));
        form.Add(new StringContent("3"), "ps_level");
        form.Add(new StringContent("all"), "page_range");
        form.Add(new StringContent("true"), "binary_output");
        form.Add(new StringContent("1"), "scale");
        form.Add(new StringContent("false"), "rotate");

For the PDF Refrying form, MultipartFormDataContent owns the uploaded streams and text values. The .NET runtime calculates the multipart boundary and content type when HttpClient sends the container.

Read the output resource

string endpoint,
        HttpContent content)
    {
        using var response = await client.PostAsync(endpoint, content);
        var text = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"{endpoint}: {(int)response.StatusCode}");
        if (!response.IsSuccessStatusCode)
        {
            throw new InvalidOperationException($"{endpoint} failed: {text}");
        }

        return JObject.Parse(text);

After the PDF Refrying call, the client reads the response text and reports non-success status through the process exit code. A successful JSON body identifies the managed output resource, while an unsuccessful body should be surfaced to the calling application.

Beyond the Tutorial

You completed the two-stage refry process in .NET with C# and saved the final PDF after both calls succeeded. The intermediate response ID is the important handoff between conversion stages, not merely a status value to print and discard.

Try representative pdf refrying inputs in API Lab. For the authoritative request fields, accepted values, and response contract, consult the pdfRest API Reference Guide. The same request pattern can then be adapted to your .NET with C# application’s error handling, retention policy, and delivery workflow.

Generate a self-service API Key now!
Create your FREE API Key to start processing PDFs in seconds, only possible with pdfRest.