To add an image to a PDF in C#, open the file for editing with OnePDF, add the image to the document with AddPngImage or AddJpegImage, and draw it on a page with DrawImage at a position and size in points. For a watermark, AddTextStamp and AddImageStamp place text or an image on every page, rotated, at any opacity, over or under the content. The page’s existing content stays byte for byte as it was. OnePDF is pure C# with no native binaries, licensed MIT OR Apache-2.0. Every sample was compiled and run against OnePDF 1.11.0 on .NET 10, and each output passes qpdf --check.
Before you start
dotnet new console -o PdfStamp cd PdfStamp dotnet add package RecursiveAI.OnePdf --version 1.11.0
OnePDF targets .NET 10. The samples edit report.pdf, a six-page US Letter report, and the IRS Form W-9 (fw9.pdf). logo.png is a 240 × 240 PNG with transparency and photo.jpg an 800 × 500 JPEG. Any PDF and images will do.
Add an image to an existing PDF
using OnePdf; using PdfDocument doc = PdfDocument.Open("report.pdf", new PdfOpenOptions { AllowEditing = true }); // Add each image to the document once; it can then be drawn on any page. PdfCreatedImage logo = doc.AddPngImage(File.ReadAllBytes("logo.png")); // alpha channel kept as a soft mask PdfCreatedImage photo = doc.AddJpegImage(File.ReadAllBytes("photo.jpg")); // embedded as is, no recompression PdfPage page = doc.Pages[0]; Console.WriteLine($"page 1 is {page.MediaBox.Width} x {page.MediaBox.Height} points"); using (PdfPageContentBuilder b = page.BeginContent()) // drawn over the existing content { b.DrawImage(logo, 522, 726, 36, 36); // x, y of the lower left corner, width, height b.DrawImage(photo, 54, 72, 240, 150); // 800 x 500 px scaled to 240 x 150 pt b.Commit(); // nothing is written before Commit } doc.Save("report-images.pdf");
page 1 is 612 x 792 pointsThe logo now sits in the top right corner of page 1 and the photo near the bottom left. pdfimages -list shows what went into the file: the logo as a 240 × 240 RGB image with a separate gray soft mask, which keeps its transparency, and the photo as a jpeg image, 800 × 500. The JPEG bytes in the PDF are byte for byte the bytes of photo.jpg.
Coordinates and sizes
- Positions are in points (1/72 inch) from the lower left corner of the page, with y going up. A US Letter page is 612 × 792 points and A4 is 595 × 842.
page.MediaBoxgives the size of any page. DrawImage(image, x, y, width, height)stretches the image to the box. To keep the aspect ratio, compute one side from the other: an 800 × 500 photo drawn 240 points wide is 150 points high.- An image’s resolution on paper is its pixel width divided by the box width in inches. The photo, 800 pixels across 240 points (3.33 inches), prints at 240 dpi.
- Content you draw doesn’t turn with a page that has
/Rotateset. The stamp helpers below compensate for it.
JPEG, PNG and everything else
AddJpegImageembeds the JPEG file as it is, without decoding or recompressing. Baseline and progressive JPEGs with 1, 3 or 4 components work. Arithmetic-coded and lossless JPEGs are refused.AddPngImagereads a PNG and stores it losslessly. An alpha channel becomes a soft mask.- Add an image once and draw it on as many pages as you like. It is stored once.
- Other formats need converting first, as below, or raw samples through
AddRawImage.
Add a watermark to every page
A watermark is the same content repeated on each page. The stamp helpers draw it once into a shared form and place that form on the pages you choose, all of them by default, centered on the visible area of each page:
using OnePdf; using PdfDocument doc = PdfDocument.Open("report.pdf", new PdfOpenOptions { AllowEditing = true }); // A diagonal text watermark on every page, red at 15% opacity, centered on each page. PdfCreatedFont bold = doc.AddStandardFont("Helvetica-Bold"); doc.AddTextStamp("CONFIDENTIAL", bold, 72, new PdfStampOptions { Opacity = 0.15, RotationDegrees = 45, Color = (0.7, 0.1, 0.1) }); // A faint logo under the text of the first page only. PdfCreatedImage logo = doc.AddPngImage(File.ReadAllBytes("logo.png")); doc.AddImageStamp(logo, 300, 300, new PdfStampOptions { Opacity = 0.08, Placement = PdfContentPlacement.Underlay }, pageIndexes: [0]); doc.Save("report-watermarked.pdf"); Console.WriteLine($"watermarked {doc.PageCount} pages");
watermarked 6 pagesEvery page now has “CONFIDENTIAL” across it at 45 degrees in light red, and the first page has the logo, large and faint, behind its text. The watermark is one shared form, stored once however many pages show it.
PdfStampOptions sets:
Opacityfrom 0 to 1, and aBlendMode,RotationDegrees, counterclockwise as the reader sees the page, andColoras RGB components from 0 to 1,Placement:Overlay(the default) draws on top,Underlayunder the page’s content, so the page’s text and images sit on top of it,CenterXandCenterYto put the stamp somewhere other than the middle of the page.
The text stamp uses Helvetica Bold, one of the standard PDF fonts that viewers supply, so no font is embedded. That keeps the file small, but a standard font can’t go into a PDF/A file or show characters beyond its Latin set. For either, embed a TrueType or OpenType font with doc.AddFont(File.ReadAllBytes("YourFont.ttf")) and pass that instead.
A watermark drawn this way is part of the page content: anyone with a PDF editor can delete it again, and text extraction will return it with the page text. If the goal is to stop the information leaking, redact it instead.
Add text to an existing PDF
The same page builder writes text. Here every page of the W-9 gets a received date and a page number in its bottom margin, plus a right-aligned label:
using OnePdf; using PdfDocument doc = PdfDocument.Open("fw9.pdf", new PdfOpenOptions { AllowEditing = true }); PdfCreatedFont font = doc.AddStandardFont("Helvetica"); // Text in the bottom margin of every page: a line, and a box that wraps. for (int i = 0; i < doc.PageCount; i++) { using PdfPageContentBuilder b = doc.Pages[i].BeginContent(); b.SetFillRgb(0.1, 0.2, 0.6); b.SetFont(font, 8); b.DrawText($"Received 2026-10-26 · page {i + 1} of {doc.PageCount}", 36, 14); b.DrawTextAligned("Example Co. vendor file", 36, 14, 540, PdfTextAlignment.Right); b.Commit(); } doc.Save("fw9-received.pdf"); Console.WriteLine("stamped");
stampedDrawText places a line with its baseline starting at x, y. DrawTextAligned aligns one line inside a span, and DrawTextBox wraps a paragraph into a box, left, centered, right or justified, and returns any text that didn’t fit. MeasureText gives the width of a string in the current font and size, so you can place things exactly.
This text isn’t shaped: the characters are drawn one after another, with no ligatures and no right-to-left reordering. That is right for Latin, Greek and Cyrillic. For Arabic, Hebrew, Hindi and other complex scripts, lay the text out with OnePdf.Layout, which shapes it: see Arabic and Hindi text in a C# PDF.
Add a WebP, TIFF, GIF or BMP image
OnePDF reads JPEG and PNG itself. For anything else, decode it with an image library and hand OnePDF a PNG. With OneImage, our pure C# image library, which reads PNG, JPEG, WebP, GIF, TIFF, BMP and ICO, that is one line. Add RecursiveAI.OneImage (1.10.2 here) to the project:
using OneImage; using OnePdf; // OnePDF takes JPEG and PNG directly. Convert anything else (WebP, TIFF, GIF, BMP) to PNG first. using var png = new MemoryStream(); Image.Load("harbor.webp").Save(png, PngFormat.Instance); using PdfDocument doc = PdfDocument.Open("report.pdf", new PdfOpenOptions { AllowEditing = true }); PdfCreatedImage picture = doc.AddPngImage(png.ToArray()); using (PdfPageContentBuilder b = doc.Pages[1].BeginContent()) { b.DrawImage(picture, 54, 72, 240, 150); b.Commit(); } doc.Save("report-webp.pdf"); Console.WriteLine($"{png.Length:N0} bytes of PNG placed on page 2");
27,214 bytes of PNG placed on page 2A photo converted this way is stored losslessly, so it takes more room than the JPEG it might have been. For photos, converting to JPEG and using AddJpegImage gives a smaller file.
Images in a new document
To put images into a PDF you are generating, use OnePdf.Layout instead of coordinates: col.Item().Width(120).Image(bytes).AltText("Company logo") places an image in the flow, scaled to fit, and tags it for screen readers. SVG goes in as vector graphics with Svg(...). See create a PDF in C#.
Where next
- Add an image or a watermark, the short cookbook recipe.
- Content creation: every drawing call, fonts, gradients and coordinates.
- Merge PDF files in C#, to add a cover page before stamping.
- Redact a PDF in C#, to remove content rather than cover it.