dlemstra / Magick.NET

The .NET library for ImageMagick
Apache License 2.0
3.34k stars 405 forks source link

BitmapFrame.Create() throws ArgumentException: 'Value does not fall within the expected range.' when XmpProfile is set #1652

Open aureole82 opened 1 month ago

aureole82 commented 1 month ago

Magick.NET version

Magick.NET-Q16-AnyCPU 13.9.0

Environment (Operating system, version and so on)

Windows 11 23H2

Description

I have a .NET Framework 4.8 environment and need Magick.NET and old PresentationCore to work together. In general I'd like to use Magick.NET to generate images with Exif and Xmp metadata + need to make sure PresentationCore can still read this data.

Steps to Reproduce

  1. Load image with ITPC data, like var image = new MagickImage("sample_640x426_iptc+xmp.jpg")
  2. Retrieve XMP data: var xml = image.GetXmpProfile().ToXDocument(), optional: do some manipulation.
  3. Assign XMP data: image.SetProfile(new XmpProfile(xml))
  4. Generate image: image.ToByteArray()
  5. Let's check XMP data with the good old BitmapMetadata class:
    BitmapMetadata bitmapMetadata;
    using (var stream = new MemoryStream(bytes))
    {
    // Use BitmapCacheOption.OnLoad to read the whole stream immediately (allows to close the stream before using BitmapMetadata).
    var bitmapSource = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    bitmapMetadata = (BitmapMetadata)bitmapSource.Metadata;
    }
    Console.WriteLine($"Title: {bitmapMetadata.Title}");
    Console.WriteLine("Headline: " + bitmapMetadata.GetQuery("/xmp/photoshop:Headline"));

BitmapFrame.Create() breaks with

System.ArgumentException
  HResult=0x80070057
  Message=Value does not fall within the expected range.
  Source=PresentationCore
  StackTrace:
  at System.Windows.Media.Imaging.BitmapDecoder.SetupDecoderFromUriOrStream(Uri uri, Stream stream, BitmapCacheOption cacheOption, Guid& clsId, Boolean& isOriginalWritable, Stream& uriStream, UnmanagedMemoryStream& unmanagedMemoryStream, SafeFileHandle& safeFilehandle)
  at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
  at System.Windows.Media.Imaging.BitmapDecoder.Create(Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
  at System.Windows.Media.Imaging.BitmapFrame.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy)
  at System.Windows.Media.Imaging.BitmapFrame.Create(Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
  at ImageExtensions.GetIptcData(Byte[] image) in C:\Sources\ExifAndXmpConsole\ExifAndXmpConsole\Program.cs:line 36
  at ImageExtensions.LogIptcAndXmp(Byte[] bytes) in C:\Sources\ExifAndXmpConsole\ExifAndXmpConsole\Program.cs:line 43
  at Programm.Main(String[] args) in C:\Sources\ExifAndXmpConsole\ExifAndXmpConsole\Program.cs:line 22

Inner Exception 1:
ArgumentException: Value does not fall within the expected range.

Magick.NET seems to corrupt the metadata so that at least Window's PresentationCore can no longer create a BitmapMetadata instance.

Sample solution: ExifAndXmpConsole.zip

dlemstra commented 1 month ago

It looks like this is caused by the ToXDocument and FromXDocument calls. The FromXDocument seems to add a xml declaration (<?xml version="1.0" encoding="utf-8"?>) that breaks the BitmapDecoder. I just pushed a patch to remove this from the FromXDocument method.

aureole82 commented 1 month ago

Does my sample project work with version 13.9.1? I get still the same issue. :-(

dlemstra commented 1 month ago

I tested this with a patched version of your project and that worked. Not sure why it's not working. Will take a look at this again sometime next week.

dlemstra commented 1 month ago

Turns out this was caused by the utf-8 byte order mark at the start of the stream. With my local testing I did something that would not cause it to be written but that did not end up in the release. It might take a while before I will publish a new release.