Read and Write GPS coordinates with RedCorners.ExifLibrary

I recently published RedCorners.ExifLibrary, a NuGet that lets you read and write EXIF metadata in image files. This is basically a fork of the ExifLibrary project by oozcitak, with four minor changes:

  • It removes dependencies to System.Drawing, making the library work across all .net Standard 2.0 compatible runtimes, including Xamarin.iOS and Xamarin.Android.
  • It adds a conversion helper method for going back and forth between floating point or Degrees/Minutes/Seconds representations for coordinates.
  • It adds helper methods to quickly read and write GPS coordinates in the image files.
  • Everything is moved to the RedCorners.ExifLibrary namespace, in order to make it possible to use both the original library and my version of it simultaneously.

It has been tested on Xamarin.iOS, Xamarin.Android, .NET Core and .NET Framework on JPEG images.

You can get the NuGet from here: https://www.nuget.org/packages/RedCorners.ExifLibrary

Some quick examples:


using RedCorners.ExifLibrary;

// Read GPS Coordinates
var file = ImageFile.FromFile(Path);
var coords = file.GetGPSCoords();
if (coords.HasValue)
    (Latitude, Longitude) = coords.Value;

// Write GPS Coordinates
var file = ImageFile.FromFile(Path);
file.SetGPSCoords(Latitude, Longitude);
file.Save(Path);

More information on GitHub.

RestSharp returns 0 for custom ASP.NET Core middleware response

A normal behavior of RestSharp is to return StatusCode = 0 for responses that won’t go through (e.g. unreachable server). However, a tricky behavior of RestSharp is to also return 0 if the response has content, but the Content-Length is undefined.

If you are confused why your RestSharp is returning 0 while Postman and Chrome work fine, make sure you are setting the response Content-Length to the actual content length.


public async Task Invoke(HttpContext context)
{
//instead of await next(context);
var result = JsonConvert.SerializeObject(new
{
Message = "Hello, World!"
});
context.Response.ContentType = "application/json; charset=utf-8";
context.Response.StatusCode = 200;
context.Response.ContentLength = result.Length + 2;
await context.Response.WriteAsync("\r\n" + result);
await context.Response.Body.FlushAsync();
}

Change Image Format on Xamarin / C#

SkiaSharp is a Xamarin-friendly NuGet that lets you do powerful image processing and rendering tasks. You can use it to quickly resize, compress or re-encode your images (i.e. change their format). This example shows how to change a JPEG file to PNG with SkiaSharp:

using (var istream = File.OpenRead(originalPath))
    using (var skBitmap = SKBitmap.Decode(istream))
        using (var image = SKImage.FromBitmap(skBitmap))
            using (var ostream = File.OpenWrite(newPath))
                image.Encode(SKEncodedImageFormat.Png, 100)
                    .SaveTo(ostream);

Images captured by iOS show up rotated on Android

When you capture a photo with TakePhotoAsync on the iOS, regardless of the SaveMetaData flag or RotateImage, it might be stored rotated. However, you won’t notice it on the iOS itself, or on Windows, since the orientation information is stored in the EXIF metadata of the captured JPEG file.

iOS or Windows show the image correctly because they take into account the EXIF orientation, but the Image view of Xamarin.Forms on Android ignores this flag, thus making the image show up as-is, i.e. rotated.

If you store the image in a different format than JPEG (e.g. PNG) on the iOS, you can also see the image rotated there, because PNG doesn’t have an orientation flag.

If you are using Xamarin.Forms, consider using CachedImage from FFImageLoading instead of the built-in Image view. CachedImage looks at the orientation information on Android, and rotates the image accordingly before rendering it.

However, if you are uploading the image to an ASP.NET core application and need to process it, you can rotate it manually based on the EXIF information. .NET core by itself doesn’t have image manipulation capabilities, but you can install the CoreCompat.System.Drawing NuGet package, which is a port of System.Drawing for .NET core.

First, load the image as an Image object:

var image = Image.FromFile(path);

Then see if it has the rotation value, and rotate accordingly:

if (image.PropertyIdList.Contains(0x112))
{
    int rotationValue = image.GetPropertyItem(0x112).Value[0];
    if (rotationValue == 8) 
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate270FlipNone);
    else if (rotationValue == 3) 
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate180FlipNone);
    else if (rotateValue == 6)
        image.RotateFlip(rotateFlipType: RotateFlipType.Rotate90FlipNone);
}

Optionally, you can save the image on the original file:

image.Save(path);

 

mpc.exe outputs an empty Resolver

In order to use MessagePack on the iOS or in general with AOT, you need to use mpc.exe to generate a resolver before building your main projects. Since Xamarin and ASP.NET are all about code sharing, usually a good practice is to create a shared project or a library containing model definitions, and referencing it in both server and client projects.

This raises the question as where to embed the script to run mpc.exe in order to facilitate the automatic generation and updating the resolver class.

One problem is that mpc.exe does not work with Shared Projects (.shproj), and as of now only accepts .csproj files as the input. So one thing to do is to create a stub/dummy project containing only the model classes, and feeding it to mpc.exe.

The frustration comes when mpc.exe doesn’t like an input. In such cases, it merely generates a blank useless resolver class (e.g. MessagePackGenerated.cs) without giving any errors. You have to manually investigate everything in order to find out what goes wrong.

One of the main cases in which I realized that mpc.exe doesn’t work properly, is when you don’t have the correct version of MessagePack NuGet installed on the stub project. Make sure you install MessagePack in that project.