PNG-to-PDF in .NET is about embedding a raster image into a PDF container. iText 7 is the full-featured option — control page dimensions, image scaling, and document properties. PdfSharp is a lightweight MIT-licensed alternative for simpler use cases. The ChangeThisFile API handles it server-side via a single POST when you don't want a PDF library in your dependency tree.
Method 1: iText 7 (precise layout, multi-image PDF)
iText 7 is the most capable PDF library in .NET. Fine-grained control over page size, image placement, compression, and metadata.
dotnet add package itext7
using iText.IO.Image;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Layout;
using iText.Layout.Element;
using iText.Layout.Properties;
public static class PngToPdf
{
/// <summary>Converts one or more PNG files into a single PDF.</summary>
public static void Convert(
IEnumerable<string> pngPaths,
string outputPath,
bool fitToImage = true)
{
using var writer = new PdfWriter(outputPath);
using var pdfDoc = new PdfDocument(writer);
using var doc = new Document(pdfDoc);
doc.SetMargins(0, 0, 0, 0);
foreach (var pngPath in pngPaths)
{
var imageData = ImageDataFactory.Create(pngPath);
var image = new iText.Layout.Element.Image(imageData);
if (fitToImage)
{
// Make each page exactly the size of the image
var pageSize = new PageSize(
imageData.GetWidth(), imageData.GetHeight());
pdfDoc.AddNewPage(pageSize);
image.SetFixedPosition(
pdfDoc.GetNumberOfPages(), 0, 0);
image.SetWidth(UnitValue.CreatePointValue(imageData.GetWidth()));
}
else
{
// A4 page with auto-scaled image
pdfDoc.AddNewPage(PageSize.A4);
image.SetAutoScale(true);
}
doc.Add(image);
}
}
}
// Single PNG
PngToPdf.Convert(new[] { "image.png" }, "image.pdf");
// Multiple PNGs in one PDF
PngToPdf.Convert(new[] { "page1.png", "page2.png", "page3.png" }, "document.pdf");
iText 7 is AGPL — free for open-source projects. Commercial use without open-sourcing your code requires an iText license. For MIT-licensed alternatives, see PdfSharp below.
Method 2: PdfSharp (MIT license, simpler API)
PdfSharp is fully MIT-licensed and handles standard PNG-to-PDF without the AGPL concern. Less feature-rich than iText but sufficient for most image-to-PDF needs.
dotnet add package PdfSharp
using PdfSharp.Drawing;
using PdfSharp.Pdf;
public static class PngToPdfSharp
{
public static void Convert(
IEnumerable<string> pngPaths,
string outputPath)
{
using var document = new PdfDocument();
document.Info.Title = "Converted Images";
foreach (var pngPath in pngPaths)
{
using var xImage = XImage.FromFile(pngPath);
// Create a page the exact size of the image
var page = document.AddPage();
page.Width = XUnit.FromPoint(xImage.PointWidth);
page.Height = XUnit.FromPoint(xImage.PointHeight);
using var gfx = XGraphics.FromPdfPage(page);
gfx.DrawImage(xImage, 0, 0, page.Width, page.Height);
}
document.Save(outputPath);
}
}
PdfSharp 6+ adds .NET 6/7/8 support. Use the NuGet package PdfSharp (not the older PdfSharp-WPF) for cross-platform compatibility.
Method 3: ChangeThisFile API (HttpClient, no PDF library)
POST the PNG, receive a PDF. Free tier: 1,000 conversions/month.
# Verify with curl first
curl -X POST https://changethisfile.com/v1/convert \
-H "Authorization: Bearer ctf_sk_your_key" \
-F "file=@image.png" \
-F "target=pdf" \
--output image.pdf
using System.Net.Http;
using System.Net.Http.Headers;
public class PdfConvertService
{
private readonly HttpClient _http;
private const string ApiKey = "ctf_sk_your_key_here";
public PdfConvertService(IHttpClientFactory factory)
=> _http = factory.CreateClient("ctf");
public async Task ConvertPngToPdfAsync(
string pngPath,
string pdfPath,
CancellationToken ct = default)
{
await using var fileStream = File.OpenRead(pngPath);
using var form = new MultipartFormDataContent();
var fileContent = new StreamContent(fileStream);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue("image/png");
form.Add(fileContent, "file", Path.GetFileName(pngPath));
form.Add(new StringContent("pdf"), "target");
using var request = new HttpRequestMessage(HttpMethod.Post, "/v1/convert")
{
Content = form,
Headers = { Authorization =
new AuthenticationHeaderValue("Bearer", ApiKey) }
};
using var response = await _http.SendAsync(request, ct);
response.EnsureSuccessStatusCode();
await using var outStream = File.Create(pdfPath);
await response.Content.CopyToAsync(outStream, ct);
}
}
When to use each
| Approach | Best for | Tradeoff |
|---|---|---|
| iText 7 | Multi-image PDFs, custom page sizes, document metadata | AGPL — commercial use requires iText license |
| PdfSharp | Simple image-to-PDF, MIT license, no licensing concern | Less feature-rich than iText; no HTML renderer |
| ChangeThisFile API | No PDF library dep, single-image conversions, free tier | Network call; 25MB file limit on free tier; one image per call |
Production tips
- Use IHttpClientFactory for the API. Register a named client in
Program.csand injectIHttpClientFactory— avoids socket exhaustion on high-volume endpoints. - Pass CancellationToken through all async calls. Wire it from
HttpContext.RequestAbortedin ASP.NET controllers so cancelled requests clean up correctly. - Set margins to zero for image-sized pages. iText and PdfSharp both default to margins. Set them to zero when you want the PDF page to be exactly the image size.
- Dispose XImage properly in PdfSharp. XImage holds a GDI+ handle on Windows. Always use
using var xImage = XImage.FromFile(path)— leaking XImage objects causes GDI handle exhaustion on long-running processes. - Consider compression. PDFs embedding large PNGs can be huge. iText 7 compresses image streams by default. For PdfSharp, ensure the PDF is saved with compression enabled (the default).
PdfSharp is the cleanest choice for most .NET projects — MIT license, simple API, cross-platform. iText 7 is the better option when you need multi-image layouts, document metadata, or more PDF control. The ChangeThisFile API removes both dependencies for single-image conversions. Free tier: 1,000 conversions/month.