# Add images to PDF in C# and .NET

> Place JPEG, PNG, GIF, BMP, TIFF, WebP and SVG images on PDF pages in C#, fit them to boxes, and reuse pages of other PDFs as watermarks. MIT licensed.

Available since 1.1.

Images, vector drawings and pages of other PDFs, placed on a page wherever you want them.

## Short answer

Load an image with `PdfImage.Load` or an SVG drawing with `PdfSvg.Load`, then place it with
`DrawImage` or `DrawSvg` on a page's canvas. Positions and sizes are points from the top left of
the page. Each image is written once per document, however often it is drawn.

```csharp
PdfImage photo = PdfImage.Load("photo.jpg");      // JPEG copied as is, EXIF orientation honored
PdfImage logo = PdfImage.Load("logo.png");        // PNG alpha becomes a soft mask
PdfSvg badge = PdfSvg.Load("badge.svg");          // SVG stays vector: sharp at any zoom

g.DrawImage(photo, 40, 40, 320, 200);             // stretched to the rectangle, in points
g.DrawImage(logo, 380, 40, 150, 50);
g.DrawSvg(badge, 380, 110, 150, 60);              // viewBox and preserveAspectRatio decide the fit
g.DrawImage(logo, 40, 290);                       // natural size: pixels at their recorded resolution, else 96 dpi
```

![A photo, a transparent PNG logo and an SVG badge placed on a page](https://www.jadev-corp.com/previews/images.webp)

## Image formats

| Format | How it is embedded |
|---|---|
| JPEG | Copied without recompression. EXIF orientation, Adobe CMYK, ICC profiles and JFIF density are honored |
| PNG | All color types and bit depths, interlaced or not. Simple PNGs keep their compressed data; an alpha channel or tRNS transparency becomes a soft mask. ICC profiles and pixel density are kept |
| GIF | GIF 87a and 89a, the first frame, interlaced or not, with its transparent color as a soft mask |
| BMP | 1 to 32 bits per pixel, RLE4 and RLE8, bit fields, top down rows, alpha, or an embedded JPEG or PNG. Decoded and Flate compressed; palette images keep their palette |
| TIFF | Strips and tiles, chunky and planar; uncompressed, PackBits, LZW and Deflate with the predictor, CCITT modified Huffman, Group 3 and Group 4, JPEG; bilevel, gray, palette, RGB, CMYK and YCbCr samples of 1 to 16 bits, associated and unassociated alpha, BigTIFF. A page stored as one strip of fax or JPEG data is embedded as is; other pages are decoded and Flate compressed |
| WebP | Lossy and lossless, with alpha, the first frame of an animation. Lossy WebP decodes to the same pixels as libwebp |
| SVG | `PdfSvg`: drawn as PDF vectors, see below |

`PdfImage.Load` takes a path, a stream or a byte array, and reports its `Format`, `PixelWidth`,
`PixelHeight`, `HorizontalResolution`, `VerticalResolution` and `HasTransparency`. Alpha becomes a
soft mask. Decoding is bounded, and an unsupported or damaged file raises a `FormatException`.

```csharp
PdfImage webp = PdfImage.Load("photo.webp");     // lossy or lossless WebP, alpha as a soft mask
PdfImage bmp = PdfImage.Load("logo.bmp");        // BMP, 1 to 32 bits per pixel
PdfImage fax = PdfImage.Load("scan.tif", 1);     // page 2 of a multi-page TIFF, from 0
Console.WriteLine($"{webp.Format} {webp.PixelWidth}x{webp.PixelHeight}, {bmp.Format}, {fax.Format}");
Console.WriteLine($"scan.tif has {PdfImage.GetPageCount("scan.tif")} pages");
```

A TIFF file can hold several pages. `PdfImage.GetPageCount` counts them and `PdfImage.Load(path,
page)` loads one, from 0; other formats have only page 0. The
[TIFF to PDF recipe](https://www.jadev-corp.com/recipes/tiff-to-pdf/) puts every page of a TIFF on its own PDF page.

## Size and placement

`DrawImage(image, x, y, width, height)` stretches the image to the rectangle. `DrawImage(image, x,
y)` draws it at its natural size: its pixels at the resolution the file records, else 96 per inch.
`Width` and `Height` give that natural size in points.

```csharp
Console.WriteLine($"photo.jpg: {photo.PixelWidth}x{photo.PixelHeight} pixels, {photo.Width:F0}x{photo.Height:F0} points at natural size");
```

## Fit, alignment and opacity

`DrawImage(image, x, y, width, height, fit, alignment, opacity)` fits an image into a box the way
CSS `object-fit` does, places it inside the box where it does not fill it, clips whatever spills
over, and fades it as a whole on top of its own alpha.

```csharp
// Each box is 120 by 120 points; the photo is 640 by 400 pixels.
PdfImageFit[] fits = [PdfImageFit.Fill, PdfImageFit.Contain, PdfImageFit.Cover, PdfImageFit.None];
foreach (PdfImageFit fit in fits)
{
    g.DrawImage(webp, x, 60, 120, 120, fit, PdfImageAlignment.Center);
    g.DrawRectangle(frame, null, x, 60, 120, 120);
    g.DrawString(fit.ToString(), label, 10, PdfColor.Black, x, 196);
    x += 130;
}

// Aligned to the top left of its box, and faded to 40 percent.
g.DrawImage(webp, 40, 220, 250, 120, PdfImageFit.Contain, PdfImageAlignment.TopLeft, opacity: 0.4);
```

![One photo fitted into four boxes with Fill, Contain, Cover and None, then faded to 40 percent](https://www.jadev-corp.com/previews/images-fit.webp)

| `PdfImageFit` | Effect |
|---|---|
| `Fill` | Stretched to the box, aspect ratio ignored |
| `Contain` | Scaled to fit inside the box, aspect ratio kept: the whole image shows |
| `Cover` | Scaled to cover the box, aspect ratio kept: what spills over is clipped |
| `None` | At its natural size, clipped to the box |
| `ScaleDown` | At its natural size, or as `Contain` when that is smaller |

`PdfImageAlignment` places the image: `TopLeft`, `Top`, `TopRight`, `Left`, `Center` (the default),
`Right`, `BottomLeft`, `Bottom` or `BottomRight`.

Images follow the current transform and clip of the canvas, so `RotateTransform`, `ScaleTransform`
and `IntersectClip` work on them as on shapes. See [Drawing](https://www.jadev-corp.com/guides/drawing/).

## SVG

`PdfSvg.Load` reads a file, a stream or bytes, and `PdfSvg.Parse` reads a string. `DrawSvg(svg, x,
y, width, height)` scales the drawing into the rectangle; its `viewBox` and
`preserveAspectRatio` decide how it fits.

Supported: shapes, paths with arcs, groups, `use` and `symbol`, nested viewports, transforms, solid
colors, linear and radial gradients, clip paths, opacity, dashes, text in the standard fonts,
embedded JPEG and PNG images, and `<style>` rules with type, class and id selectors. Doctypes are
skipped, so entity expansion cannot be abused, and size, depth and `use` nesting are capped.

## Watermarks with a reusable drawing

`PdfForm.Create` makes a reusable drawing (a form XObject). Draw it once, then place it on every
page: each page refers to the same object.

```csharp
PdfDocument report = PdfDocument.Open("report.pdf");

// Draw the stamp once as a reusable form; every page then refers to the same object.
PdfFont font = PdfStandardFont.HelveticaBold;
PdfForm stamp = PdfForm.Create(report, font.MeasureString("CONFIDENTIAL", 56), 56);
using (PdfGraphics s = stamp.CreateGraphics())
{
    s.DrawString("CONFIDENTIAL", font, 56, PdfColor.FromHex("#DC2626").WithAlpha(0.25), 0, font.Ascent * 56);
}

foreach (PdfPage page in report.Pages)
{
    using PdfGraphics g = page.CreateGraphics();          // foreground: over the existing content
    g.TranslateTransform(page.Width / 2, page.Height / 2);    // origin at the page center
    g.RotateTransform(-45);                                    // counterclockwise
    g.DrawForm(stamp, -stamp.Width / 2, -stamp.Height / 2);
}
report.Save("report-watermarked.pdf");
```

![A report page with a translucent diagonal CONFIDENTIAL stamp](https://www.jadev-corp.com/previews/images-watermark.webp)

## Pages of other PDFs: n-up and handouts

`PdfForm.FromPage` turns any page of any document into a drawing you can scale and place. It shows
the page's visible area (its crop box), upright as the page's rotation shows it, and copies its
fonts and images into the target document once.

```csharp
// Four pages of the manual on one landscape sheet: a handout.
PdfDocument manual = PdfDocument.Open("manual.pdf");
PdfDocument handout = PdfDocument.Create();
PdfPage sheet = handout.Pages.Add(PdfRectangle.FromSize(842, 595));
using (PdfGraphics g = sheet.CreateGraphics())
{
    double w = sheet.Width / 2, h = sheet.Height / 2;
    for (int i = 0; i < 4; i++)
    {
        PdfForm miniature = PdfForm.FromPage(handout, manual.Pages[i]);   // fonts and images copied once
        double x = i % 2 * w, y = i / 2 * h;
        double scale = Math.Min((w - 20) / miniature.Width, (h - 20) / miniature.Height);
        double mw = miniature.Width * scale, mh = miniature.Height * scale;
        g.DrawForm(miniature, x + (w - mw) / 2, y + (h - mh) / 2, mw, mh);
        g.DrawRectangle(new PdfPen(PdfColor.Gray(0.7), 0.5), null, x + (w - mw) / 2, y + (h - mh) / 2, mw, mh);
    }
}
handout.Save("handout.pdf");
```

![Four pages of a manual laid out on one landscape sheet](https://www.jadev-corp.com/previews/images-nup.webp)

## Letterheads and stationery

Draw a page of a letterhead PDF underneath the content of every page with the background layer:

```csharp
// A page of another PDF as the background of every page: a letterhead designed elsewhere.
PdfDocument letterhead = PdfDocument.Create();
PdfPage design = letterhead.Pages.Add(PdfRectangle.A4);
using (PdfGraphics d = design.CreateGraphics())
{
    d.DrawRectangle(null, PdfColor.FromHex("#0F766E"), 0, 0, design.Width, 24);
    d.DrawImage(PdfImage.Load("logo.png"), 455, 40, 100, 33);
}

PdfDocument contract = PdfDocument.Open("contract.pdf");
PdfForm background = PdfForm.FromPage(contract, letterhead.Pages[0]);
foreach (PdfPage page in contract.Pages)
{
    using PdfGraphics g = page.CreateGraphics(PdfGraphicsLayer.Background);
    g.DrawForm(background, 0, 0, page.Width, page.Height);
}
contract.Save("contract-letterhead.pdf");
```

## Limits

- GIF and WebP animations contribute their first frame only.
- SVG filters, masks, patterns, external references and animation are not drawn.
- `PdfForm.FromPage` does not carry annotations, which are not part of a page's content. It throws
  when the source document does not permit copying its content.

## Related

- [Drawing](https://www.jadev-corp.com/guides/drawing/)
- [Edit pages](https://www.jadev-corp.com/guides/edit-pages/): import whole pages instead of placing them as drawings
- [Extract images](https://www.jadev-corp.com/guides/extract-images/): read images back out of a PDF
- [Convert a TIFF to PDF](https://www.jadev-corp.com/recipes/tiff-to-pdf/)
- API: [PdfImage](https://www.jadev-corp.com/api/pdfnetstack-drawing/pdfimage/), [PdfImageFit](https://www.jadev-corp.com/api/pdfnetstack-drawing/pdfimagefit/), [PdfImageAlignment](https://www.jadev-corp.com/api/pdfnetstack-drawing/pdfimagealignment/), [PdfSvg](https://www.jadev-corp.com/api/pdfnetstack-drawing/pdfsvg/), [PdfForm](https://www.jadev-corp.com/api/pdfnetstack-drawing/pdfform/), [PdfGraphics](https://www.jadev-corp.com/api/pdfnetstack-drawing/pdfgraphics/)

## Does PdfNetStack recompress JPEG images when it places them?

No. A JPEG is copied into the PDF as is, and its EXIF orientation is honored so photos stand upright.

## Does SVG stay vector?

Yes. PdfSvg draws shapes, paths, gradients, clipping and text as PDF vectors, so the drawing stays sharp at any zoom. Filters, masks and patterns are not drawn.

Source: https://www.jadev-corp.com/pdfnetstack/guides/images/
