GDI+ exception on Shape.ImageData.SetImage(Image)

Our code works for an indeterminate period of time, but after that time, the call always throws an exception until the server is rebooted. It is not resolved by restarting the IIS website, its app pool, or restarting IIS.

The exception is:

System.Runtime.InteropServices.ExternalException (0x80004005): A generic error occurred in GDI+.
at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)

The code is as follows:

Document template;

using (var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    template = new Document(file);
}

var document = new Document();
var builder = new DocumentBuilder(document);
builder.Document.RemoveAllChildren();
builder.Document.AppendDocument(template, ImportFormatMode.KeepSourceFormatting);
builder.MoveToSection(nextSectionIndex);
var nodes = builder.CurrentSection.GetChildNodes(NodeType.Shape, true).Cast<Shape>().ToList();

...

Image img = new Bitmap(300, 300);
using (var fontFamily = new FontFamily("Arial"))
using (var font = new System.Drawing.Font(fontFamily, 13))
using (var textBrush = new SolidBrush(Color.LightGray))
using (var drawing = Graphics.FromImage(img))
{
    drawing.Clear(Color.White);
    drawing.TranslateTransform(-190.0F, 100.0F);
    drawing.RotateTransform(-45.0F);
    drawing.DrawString(text, font, textBrush, new RectangleF(0, 0, 500, 500));
    drawing.Save();
}

...

var node = nodeList.Single(n => n.Name.Contains("Watermark");
((Shape)node.FirstParagraph.FirstChild).ImageData.SetImage(nodeImage);

Is the call to SetImage() creating a temporary file which is deleted upon reboot?

@pmarkham No, Aspose.Words does not create any temporary files when ImageData.SetImage method is called.
In your case, as I can see, you use ImageData.SetImage overload which accepts System.Drawing.Image, in this case, Aspose.Words saves the image to stream and internally calls ImageData.SetImage overload that accepts stream. So to isolate the problem you can save your image into stream or byte array in your code and use the appropriate overload of ImageData.SetImage method.
Also, as I can guess from your code, you are replacing watermark in the document. In this case, you can try using built-in Aspose.Words API to do this using Watermark class. In your case, since you are inserting text watermark, you can use Watermark.SetText method. In this case watermark will be inserted as a WordArt object and System.Drawing (GDI+) will not be involved at all.

Thanks. I’ll try saving the image to a stream and passing that to ImageData.SetImage.

As no files are involved, can you think of any other reason a generic GDI+ exception would occur, particularly one that’s only resolved by a reboot?

The watermark we use is only on a particular part of the document, and is repeating text, so the Document.Watermark doesn’t look suitable. We also add a barcode image (generated using ZXing.Net) to the document in the same way - I’ll also save that to a stream before adding to the document with ImageData.SetImage.

@pmarkham

This looks like permission issue. But the method definitely does not save any temporary files. Even if it does, the exception occurred every time you call it. I suspect it is some kind of glitch in GDI+. Is it possible to create a simple application that will allow us to reproduce the problem? This would allow us to investigate the issue.

That’s what I thought - or a disk/quota full. Those are the only explanations for the exception that I’ve found.

Unfortunately I can’t create a simple application that reproduces the problem - the existing application runs constantly, but has only failed sporadically.

@pmarkham Thank you for additional information. I hope saving to a stream will allow you to resolve or at least isolate the issue on your side. Please let us know if you manage to catch what causes the issue on your side.

We’ve had the issue again, using Shape.ImageData.SetImage(stream).

The logged exception/call stack is:
System.Runtime.InteropServices.ExternalException (0x80004005): A generic error occurred in GDI+. at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) at
.(MemoryStream ) at
. (Byte[] ) at .(Byte[] ) at .(Byte[] )

@pmarkham Does the exception occur with some specific image? Or it occurs randomly again? What is the original image format that the stream contains? Can you convert the image in the scream to PNG first (for testing purposes)? I am doubt the problem is in Aspose.Words, I prety sure, it is in GDI+.

The exception occurs using an image generated by the previously-posted code, but now converted to a stream before calling SetImage():

using (var stream = new MemoryStream())
{
    using (var img = new Bitmap(300, 300))
    {
        using (var fontFamily = new FontFamily("Arial"))
        using (var font = new System.Drawing.Font(fontFamily, 13))
        using (var textBrush = new SolidBrush(Color.LightGray))
        using (var drawing = Graphics.FromImage(img))
        {
            drawing.Clear(Color.White);
            drawing.TranslateTransform(-190.0F, 100.0F);
            drawing.RotateTransform(-45.0F);
            drawing.DrawString(text, font, textBrush, new RectangleF(0, 0, 500, 500));
            drawing.Save();
        }

        img.Save(stream, ImageFormat.Bmp);
    }

    ((Shape)node.FirstParagraph.FirstChild).ImageData.SetImage(stream);
}

At a seemingly random point in time (the latest being 14/05/2022 01:29:32), the exception occurs, and continues to occur until the computer is rebooted. Restarting the application does not resolve the issue.

@pmarkham Thank you for additional information. In your code you save the image as BMP to stream. Aspose.Words internally converts BMP to PNG (using GDI+) that, most likely, causes the problem. Could you please try saving your image directly to PNG, so Aspose.Words does not need to perform the conversion.

PS: BMP are converted to PNG because sometimes MS Word fails on some DIB formats (32 bit color for example). To keep document stable PNG is the default image format.

Thanks, I’ll try that.

1 Like

I changed our code to generate the image using ImageSharp rather than System.Drawing, and save that to PNG stream. We’ve now had the following exception, again requiring reboot:

  : Could not create the bitmap with the specified parameters. Possible lack of system resources. ---> System.ArgumentException: Parameter is not valid.
   at System.Drawing.Bitmap..ctor(Stream stream)
   at   .(Stream )
   --- End of inner exception stack trace ---
   at   .(Stream )
   at   .(MemoryStream )
   at   . (Byte[] )
   at    .(Byte[] )
   at    .()
   at    .()
   at    .       (Byte[] )

It looks like Aspose.Words creates a System.Drawing.Bitmap from the stream.

I’ve noticed that System.Drawing Namespace | Microsoft Learn has been updated at some point and now says:

Some types in the System.Drawing namespace rely on GDI+, which is not supported in Windows services and ASP.NET Core and ASP.NET apps. These types … include System.Drawing.Bitmap

@pmarkham Unfortunately, it is difficult to say what the problem is without the code that allows us to reproduce the problem. Could you please create a simple application that will allow us to replicate the issue on our side?
Alternatively, you can try switching your project to .NET Core or .NET5+, in these versions Aspose.Words does not use System.Drawing and uses SkiaSharp to deal with graphics.

Hi again.

I’ve now created a PDF-generating .NET 6 Web API, that my original .NET Framework project now calls instead of generating the PDF itself.

This is working, except for the very first call to Shape.ImageData.SetImage(Stream).

The stream is generated using:

var barcodeWriter = new ZXing.SkiaSharp.BarcodeWriter()
{
    Format = BarcodeFormat.AZTEC
};

using (var bitmap = barcodeWriter.Write(barcodeText))
using (var image = SKImage.FromBitmap(bitmap))
using (var encoded = image.Encode())
{
    return encoded.AsStream();
}

The exception is:

Exception:

Ho: Could not create the bitmap with the specified parameters. Possible lack of system resources.: Parameter is not valid. FileFormat=Unknown
   at Eo..ctor(Stream a, Boolean b)
   at Eo..ctor(Stream a)
   at uo.a(MemoryStream a)
   at uo.F(Byte[] a)
   at W6.d(Byte[] a)
   at W6.e(Byte[] a)
   at W6.a(Stream a)
   at PdfBuilder.SetNodeImage(IEnumerable`1 nodeList, String nodeName, Stream nodeImage, String nodeVersion)

Subsequent calls to the Web API work fine. Do you know what the issue could be?

@pmarkham Is Shape.ImageData.SetImage(Stream) call is made in .NET6 application? Is it possible to create a simple console application that will allow us to reproduce the problem on our side? Have you tried passing SKBitmap into the ImageData.SetImage(SKBitmap) method?

Thanks for the suggestion of using SKBitmap - this is now working :smiley:

I’ve managed to create a console application that reproduces the problem when using a stream (is there some way to attach this?). The error sporadically occurs when using:

using (var barcodeImage = GetBarcodeImage("..."))
{
    var node = nodes.Single(n => n.Name == "tbPg1Barcode");
    ((Shape)node.FirstParagraph.FirstChild).ImageData.SetImage(barcodeImage);
}

Stream GetBarcodeImage(string data)
{
    using (var bitmap = barcodeWriter.Write(data))
    using (var image = SKImage.FromBitmap(bitmap))
    using (var encoded = image.Encode())
    {
        return encoded.AsStream();
    }
}

However, if GetBarcodeImage is inlined, the error does not occur! Bizarre.

@pmarkham It is perfect that you managed to work the problem around.
You can attach the sample project here in this thread. We will investigate it and provide you more information.

AsposeWordsBarcodeTest.zip (21.4 KB)

Sample project attached. I didn’t spot the the Upload tool before!

@pmarkham Thank you for additional information. It looks like the stream returned by SkData.AsStream cannot be properly read for some reason. Another strange thing is that the problem occurs randomly on my side. I have changed the code like this and the problem does not occur anymore:

Stream GetBarcodeImage(SKImage image)
{
    using (var encoded = image.Encode())
    {
        return new MemoryStream(encoded.ToArray());
    }
}