loading

Metadata and privacy

A photograph from a phone usually carries the location it was taken, the device's serial number and the exact time. Pass the file through untouched and you have published all of that. Bmage removes metadata by default; you have to ask for it to be kept.

See what survives

Drop a photo straight off a phone - or use a generated one that carries a camera, a serial number and GPS coordinates. It is exported as a JPEG under each policy, and each export is decoded again to read back what is really in it. Nothing is uploaded anywhere.

The three policies

C#
new ImageExportOptions { Metadata = MetadataPolicy.Remove }          // the defaultnew ImageExportOptions { Metadata = MetadataPolicy.StripSensitive }new ImageExportOptions { Metadata = MetadataPolicy.Preserve }
PolicyKeepsRemoves
RemovenothingEXIF, XMP, ICC profile, thumbnail
StripSensitivecamera, lens, exposure, dates, copyright, description, ICC profileGPS, serial numbers, owner name, user comments, maker notes, unique IDs, embedded thumbnail, XMP
Preserveeverything the target format can carrythe embedded thumbnail, which would be stale

StripSensitive drops XMP entirely: XMP can carry location and device identifiers of its own, and there is no reliable way to filter it field by field. All three policies reset the stored orientation to normal and update the recorded dimensions, because the exported pixels are already upright and already the new size - leaving a rotation flag on rotated pixels is how images end up sideways twice.

Reading what is there

C#
using var image = await loader.LoadAsync(file);var exif = image.Metadata?.Exif;​if (exif is not null){    Console.WriteLine($"{exif.Make} {exif.Model}");    Console.WriteLine($"Lens: {exif.LensModel}");    Console.WriteLine($"{exif.ExposureTime}s at f/{exif.FNumber}, ISO {exif.Iso}");    Console.WriteLine($"Taken {exif.DateTaken}");​    if (exif.Location is { } location)        Console.WriteLine($"Location: {location.Latitude}, {location.Longitude}");}​// Every tag stays reachable, typed accessor or not.var artist = exif.Primary.Get(ExifTag.Artist)?.GetString();var custom = exif.Exif.Get(0x9C9B)?.GetString();

The Headless API page shows the pattern for warning users: read the location, say it is there, and say what will happen to it.

Writing metadata

C#
var exif = new ExifData()    .WithPrimary(new ExifDirectory()        .With(ExifTag.Artist, ExifValue.FromString("Sam Rivera"))        .With(ExifTag.Copyright, ExifValue.FromString("© 2026 Sam Rivera")));​image.Metadata = new ImageMetadata(exif);​var encoded = await exporter.EncodeAsync(image, new ImageExportOptions{    Format = ImageFormat.Jpeg,    Metadata = MetadataPolicy.Preserve,});

ExifValue.FromString writes UTF-8. The EXIF specification calls the type ASCII, but cameras and editors have written UTF-8 in these fields for years, and restricting to ASCII would silently turn "©" into "?" and mangle any name with an accent.

Orientation

Phones do not rotate pixels when you turn the device; they record a rotation flag. An image that looks upright in a photo viewer appears sideways on a canvas unless that flag is applied. Bmage applies it during decoding by default; with AutoOrient = false it is reported instead, so you can decide.

C#
await loader.LoadAsync(file);                                          // uprightawait loader.LoadAsync(file, new DecodeOptions { AutoOrient = false }); // as stored​var orientation = image.Metadata!.Orientation;if (orientation.SwapsDimensions()) { /* portrait photo stored as landscape */ }

The eight EXIF orientations form the dihedral group of the square, and OrientationMath implements the algebra: Then composes two, Inverse undoes one, SwapsDimensions says whether width and height exchange, and ToMatrix gives the coordinate mapping.

What each format can carry

FormatEXIFICCXMP
JPEGyesyesyes
PNGyes (eXIf)yes (iCCP)yes (iTXt)
WebPyesyesyes
AVIFnot written by this librarynono
BMP, GIFnonono

Metadata is attached after encoding, by rewriting the container: the browser's encoders discard it, so it is re-inserted in C# according to your policy. If a rewrite fails, the correctly encoded image is returned without metadata rather than the whole export failing. Parsing is defensive throughout - bad offsets, circular references and truncated data are ignored, never thrown - because a corrupt EXIF block must never stop an image from loading.

A checklist for user uploads

  1. Leave the default MetadataPolicy.Remove unless you have a reason.
  2. If you keep metadata for a gallery, use StripSensitive, not Preserve.
  3. Tell users what is in their file before they publish it.
  4. Leave AutoOrient on so the exported pixels match what the user saw.
  5. Remember that redaction is about pixels, and metadata is a separate leak.
An unhandled error has occurred. Reload 🗙