Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Bitmap types, Binary Resources and Westwind.Globalization


:P
On this page:

For most applications when it comes to localization, most of the tasks at hand have to do with string resource localization. However, resources in .NET also support binary resource streams so you can store images and other data in resource files. Part of the reason for this is that Resx resources serve more purposes than localization – they are also meant as a way to store reusable larger size content outside of the actual source code of an application.

Behind the scenes .NET stores resources as files that are linked into Resx files as project relative paths and then sucked into the actual satellite resource assembly when the project is compiled. The invariant/default resource assembly is merged into the main assembly while any other localized versions are stored externally in the bin folder with the appropriate locale extension like Resources.de-DE.dll.

Inside of the actual .Resx file, when you open it with the plain XML editor in Visual Studio or a text editor from Windows you get:

  <data name="Calendar.png" type="System.Resources.ResXFileRef, System.Windows.Forms">
    <value>..\Resources\Calendar.png.png;System.Drawing.Bitmap, System.Drawing, 
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
</value> </data>

The strongly type property on the Resource then is generated returning a BMP image like this:

public static System.Drawing.Bitmap Calendar_png {
    get {
        object obj = ResourceManager.GetObject("Calendar.png", resourceCulture);
        return ((System.Drawing.Bitmap)(obj));
    }
}

Now this works just fine, but I wonder what the value of this functionality really is – unless you’re building a Windows desktop application for WinForms or WPF. Bitmaps are huge memory hogs especially if you import them generically without applying appropriate encoding and they don’t retain the image type information very well. If you load a binary jpeg image into a bitmap it recognizes the jpeg image, but it loses a buch of the internal encoding and formatting information so that when you save it you have to explicitly specify the image type, encoders etc. all over again – all the original image information and settings are lost.

Are BitMaps as Resources Useful?

In short if you want to do something other than directly display the images as is as you might in Windows apps, it’s very rare that you will use bitmaps directly in code. Instead you’re much more likely to take the image and stream it somewhere – either to disk, or out to a network stream in which case Bitmaps are actually a royal pain in the butt.

What am I talking about? Behind the scenes Bitmap uses GDI+ and there are a number of issues with this. First GDI+ is not Web friendly – supposedly it doesn’t support multi-threaded operation and while I’ve not run into any major issues with this for small image conversion routines I have heard others doing more intense image processing on busy sites that have. Microsoft doesn’t recommend using GDI+ processing in Web apps – even though everybody does it anyway until it breaks.

The other major issue is image conversion. There are all sorts of weird quirks you have to be aware of when exporting images and these quirks are often specific to specific image formats. For example, if you want to export PNG files and save them to disk you can’t write the image back to the same file you loaded it from because the file is locked – it works fine in other image formats. JPEG formats have major issues saving out to disk often failing with mysterious ‘A generic error occurred in GDI+’ errors. In fact this post is in response to one of those errors that I simply could not resolve:

public static string BitmapToEmbeddedHtmlImage(Bitmap bitmap, ImageFormat format, string extraAttributes = null)
{            
    byte[] data;
    using (var ms = new MemoryStream(1024))
    {
        if (format == ImageFormat.Jpeg)
        {
            EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality,(long) 85);
            ImageCodecInfo jpegCodec = ImageUtils.Encoders["image/jpeg"];
            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;
            bitmap.Save((MemoryStream) ms, jpegCodec, encoderParams);                 
        }
        else 
            bitmap.Save(ms, format);

        data= ms.ToArray();
    }

    return BitmapToEmbeddedHtmlImage(data, format, extraAttributes);
}

The code works fine with binary image data for PNG, GIF, TIFF and BMP images, but with JPEG (first using the same generic code as the other formats and then using this specialized code that includes the specific encoder) it simply fails with  A generic error occurred in GDI+. I’ve not been able to figure out why this is happening. And this isn’t the first time I’ve run into this issue.

Later:
As it turns out I found the culprit for errors which is that *some* jpeg images require that the stream that the BitMap is read in from  has to be kept open beyond the initial creation of the bitmap. Specifically, if I do new BitMap(ms) the MemoryStream instance cannot be disposed explicitly after the Bitmap creation. Oddly thought this does not occur with all jpeg images – just with some of them, and not at all with other bitmap types. I ended up special casing jpegs where the memory stream is not immediately disposed but just assigned rather than using a using statement to wrap the MemoryStream. Here’s a link to an example that demonstrates the problem.

Bitmaps also rely on System.Drawing which is one of the API’s that’s going away in the new CoreCLR and ASP.NET vNext (if you’re not using the full CLR).

So I’m wondering if I’m doing anybody any favor by actually going through the trouble of supporting Bitmap types at all or whether it’s just easier and cleaner to simply return raw binary data (byte[]) instead.

Binary Resources in Westwind.Globalization

And this is a perfect segue of how this relates to Westwind.Globalization which is a database resource manager/provider library that includes a host of tools for working with database or resx resources.

The primary target for this library are Web applications, and here is where I’m struggling to see that the generic Bitmap format even makes any sense.

Over the last few days I implemented support for uploading binary resources into a resource via the Web admin interface, so you can easily upload images. Here’s what the new UI for this looks like:

ResourceUpload

The file resource upload button lets you pick a file from the local machine and it uploads it to the server as a resource. On the server the binary data is stored as a raw byte[] stream in the database and the actual resource items show the values as filename plus the type (the same way that Resx does sans the path).

ResourcesInEditor

Note that you can create separate files for each locale so you can have different images for different languages for example.  Or you can use a single image on the Invariant locale and let Resource Fallback handle serving that image to all locales. This is all as you’d expect.

Additionally if you now generate strongly typed resources from the database resources in this application you also get the type specified above – in this case System.Drawing.Bitmap because the value is an image.

public static System.Drawing.Bitmap PingPongImage
{
    get
    {
        return (System.Drawing.Bitmap) GeneratedResourceHelper.GetResourceObject("Resources",
"PingPongImage",ResourceManager,GeneratedResourceSettings.ResourceAccessMode); } }

This code looks slightly different than the Resx generated files as GetResourceObject can handle Resx resources, the DbResourceManager with any of its support Resource sources, or the ASP.NET ResourceProvider.

Bitmap – now what?

Strongly typed resources are nice as you can simply reference resources from classes and get Intellisense. There’s nothing like getting Intellisense, strong type compile time checking and refactoring of resource names so you can do stuff like this in an ASP.NET Razor page for example:

Intellisense[4]

But what the heck do you do with a Bitmap image?

This comes back to the function I showed earlier that takes a bitmap image and converts it to directly embed into an HTML document as a base64 encoded string. But as I mentioned – I ran into major issues with certain image types – some JPeg images in particular.

The same function would be a heck of a lot cleaner and simpler if we could just feed the binary content directly:

/// <summary>
/// Renders an HTML IMG tag that contains a raw byte stream's image content
/// inline of the HTML document. Userful for resources.
/// </summary>
/// <param name="data"></param>
/// <param name="format"></param>
/// <param name="extraAttributes"></param>
/// <returns></returns>
public static string BitmapToEmbeddedHtmlImage(byte[] data, ImageFormat format, string extraAttributes = null)
{
    string contentType = "image/jpeg";
    if (format == ImageFormat.Png)
        contentType = "image/png";
    else if (format == ImageFormat.Gif)
        contentType = "image/gif";
    else if (format == ImageFormat.Bmp)
        contentType = "image/bmp";


    StringBuilder sb = new StringBuilder();
    sb.Append("<img src=\"data:" + contentType + ";base64,");
    sb.Append(Convert.ToBase64String(data));
    sb.Append("\"");

    if (!string.IsNullOrEmpty(extraAttributes))
        sb.Append(" " + extraAttributes);

    sb.Append(" />");
    return sb.ToString();
}

Which has to do no special formatting of the data. It simply takes the raw data converts it and off it goes – in this case to convert to base64 and embed, or in the case of the JavaScript resource handler that can serve individual resources like images.

To Bitmap or Not To Bitmap

So I have this quandry that I’m trying to resolve. Should I keep close compatibility with .NET’s Resx implementation and store image resources as bitmaps or do what I think is the much more prudent thing and simply store the raw data and return the data in that same raw data.

The only downside to raw data that I see is that it would potentially break non-Web projects that use image resources. Since the new version of Westwind.Globalization supports importing arbitrary Resx resources and then exporting them again there’s a small chance that this would be a problem. For everything else – especially Web projects I suspect raw resources are the preferred way to go.

What do you think?


The Voices of Reason


 

frank
March 27, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

It sounds like if someone needs a localized embedded image for a web app, the easiest thing for them to do is have a string resource that contains the data url. Then the processing concerns go away.

Rick Strahl
March 28, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

@Frank - yup totally agree. I don't believe it really makes sense to store binary resources, but in this case I have to support it since that's what Resx supports. You can also use this stuff outside of ASP.NET where you might need to have some resource access.

Visar Gashi
April 01, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

I agree with the comment as well, storing the string for a web application should be sufficient. This might be more useful for thick client apps, I am assuming this works for mobile as well. Perhaps for standardization, you can include the functionality, but discourage its use for web applications?

Great library by the way, I have done something similar for multiple projects, evolving the idea with each one, but never going far enough to build a library. I hope to use yours for my next gig.

David McQuiggin
April 11, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

Personally, although I admire the implementation, I agree with the suggestion that it is better to store a Uri or relative path.

For example, think of the situation where you will be using a Content Delivery Network... or a different media such as video.. a Uri can cope neatly with both scenarios. Typically I have uploaded such media content to Azure Blob storage, which is dirt cheap, with a database entry and a simple utility to allow me to manage them (crud operations on content). These can then be versioned, deployed to a CDN.

Slightly off-topic: I have found this to also be the case when investigating embedding Views, JavaScript etc in a DLL that could be used in a Plugin architecture; ultimately you have to add the complexity of locating, extracting and rendering the content, and you have to inflict custom view page base classes, custom resource handlers etc on *every* page or partial you are rendering, when in fact it is only required for a small percentage of an application. The same will most likely be true of images.

But its great library; I especially like your admin interface. I have a slightly different approach that ends up with the same result; I use T4 templates to scan ViewModels properties and meta data, validation messages, strings and HTML elements that have a specific HTML5 data element, which are replaceable tokens as I explain below.

This creates a relevant database entry with the default content in the invariant language, and a static class providing static fields for returning text according to a key, and that is localised from the database values via an injected provider that can offer different pass through cache options (NullCache, InMemoryCache, RedisCache). The T4 uses CodeDom FileDom to manipulate say the HTML, to replace the original content (of say a div), with a call to the static property of my 'Translations' class, and also adds attributes to the element.

These attributes enable me to hook in Mercury Editor to perform WYSIWG edit in place, updating, previewing and saving the content via WebApi to update the database, for the currently selected culture. This allows editing content and adjusting it to account for differing text length to obtain correct flow within the design. The admin area also allows importing, exporting and changing localised route names etc.

The performance is very good.

Rick Strahl
April 11, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

@David - you get no argument from me when it comes to embedding binary resources - I wouldn't do it, but again it's something that is supported in native Resx so I have to support it. I was just curious if others are using embedded binary resources for anything whether it's images or text etc. If there's *anything* stored this way that has a good reason then the feature has to be there.

Luckily the choice of whether you use that particular functionality is entirely up to the developer and like you I would opt for external resources.

Thanks for bringing up your T4 implementation. Sounds interesting. Have you shared this anywhere? Sounds that would be really interesting to check out.

Personally I'm not a fan of T4 and code generation in general, but I like the idea that the model can hold translation values on it. Of course this would end up making the model much larger than usual because it would have to account for all translated values. And therein lies the rub. Ultimately I don't really want to have to do extra work other than embedding localized values into the UI *in one place*. I also would prefer that it works with plain HTML pages and not just with .NET related code.

I've been playing around with live editing of resources in different ways. The library has had support for WebForms and resource editing since WebForms actually had meta data that described the localization items. I had some logic that would find all the localized controls and then inject edit buttons into the page so you could jump to the appropriate item in the editor.

I've been thinking to do the same for MVC/HTML pages, but I realize that it won't be as smooth an implementation as the WebForms way because there's simply no metadata there - that would have to be embedded into the document. I've been thinking about an approach like this:

<body data-resource-set="MyPage">
<div data-resource-id="HelloWorld">@DbRes.T("HelloWorld")</div>
</body>


where these data-resource-xxx attributes are used by JavaScript to provide either pop up editors or link to the resource page. It adds extra noise but it would work for even plain HTML at least.

David McQuiggin
April 12, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

@Rick - The View Models do not actually contain translations...

a) I use a class to contain read only properties for each 'key' that will be translated, e.g. 'RequiredFieldAttribute', which returns a value from a single method that detects the CurrentUICulture (you could easily pass that through in non-web scenarios), and returns the relevant value from a pass-through cache implementation which is injected.

b) When the T4 for ViewModels is run it creates default translations for all DataAnnotations in the database (if they do not already exist), and as it walks the Code Dom of the target namespace and processes the ViewModels for translatable values, if a translation does not exist in the database, an entry is created for the default culture, and an attribute that calls the Translate method for say, "DisplayName", is updated or added to the property in the View Model, with the relevant key.

Apart from that, the ViewModel is not altered. In fact I am actually in the process of testing a 'buddy class for meta data approach', which I have used for data annotations on DTOs, and may work out to be cleaner.

c) A similar approach is taken for Views, but it is a bit more messy as it has to work with token replacement rather than Code Dom, but it basically does the same thing, if an element has an HTML-5 data attribute that indicates it should be translated, then if it has existing text, that is added to the database, and a call to the ViewTranslations class is inserted in its place. Additionally, attributes to support in place translation are added, that are similar to the example you post above. They describe the editor type for Mercury editor, and the result is that there is design time alteration of code, runtime WYSIWYG editing of text, and good performance through caching, and requiring any custom model binders, custom base view pages, reflection etc.

d) I use T4 for several other things depending on the scenario - I derive constants for route names, controller and actions are callable by strongly typed, parameterised Url and Html.Action etc helpers (I dont use T4MVC as it far too heavy for my needs). I use other T4s to create 'strongly typed' AJAX calls to my controllers, to create client side model definitions from my View Models in TypeScript if I am creating an SPA, etc...

P.S Looking forward to seeing your article about use of your framework with SPAs!

David

David McQuiggin
April 12, 2015

# re: Bitmap types, Binary Resources and Westwind.Globalization

By the way Rick, I render my html for SPA views from MVC, for many reasons, but also due to the localisation scenario (HTML text, JavaScript messages, and Routes). Subsequently all data is handled via WebAPI. Its a very nice fit.

Smith
September 03, 2017

# re: Bitmap types, Binary Resources and Westwind.Globalization

Hello,

Do you have any WinForms example where Westwind globalization is used?

Regards


Rick Strahl
September 03, 2017

# re: Bitmap types, Binary Resources and Westwind.Globalization

@Smith - no example, but you can generate RESX resources and strongly typed resources to use as you would any other resources in any other .NET project.

What I do is this:

I use this for class libraries, and Windows desktop apps, WPF and anything that requires RESX resources outside of a Web project.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024