Since upgrading to 14.6.0 my integration test fails
Magick.NET version
Magick.NET-Q8-AnyCPU 14.6.0
Environment (Operating system, version and so on)
Windows 11
Description
I use Syncfusion.DocIORenderer.NET to take the first page of a PDF or Word doc and create a BMP image, I then feed this image into Magick to create a JPG thumbnail of the image. This has been working for at least a couple of years using previous versions of both packages. Upon updating to Magick.NET-Q8-AnyCPU 14.6.0 I am getting an exception:
Message:
ImageMagick.MagickCoderErrorException : compression not supported `' @ error/bmp.c/ReadBMPImage/836
Stack Trace:
NativeInstance.CheckException(IntPtr exception, IntPtr result)
NativeMagickImage.ReadStream(IMagickSettings`1 settings, ReadWriteStreamDelegate reader, SeekStreamDelegate seeker, TellStreamDelegate teller, Void* data)
NativeMagickImage.ReadStream(IMagickSettings`1 settings, ReadWriteStreamDelegate reader, SeekStreamDelegate seeker, TellStreamDelegate teller)
MagickImage.Read(Stream stream, IMagickReadSettings`1 readSettings, Boolean ping)
MagickImage.Read(Stream stream, IMagickReadSettings`1 readSettings)
MagickImage.Read(Stream stream, MagickFormat format)
MagickImage.Read(Stream stream)
MagickImage.ctor(Stream stream)
ThumbNailServiceBase.CreateThumbnail(Stream inputFile, Double originalWidth, Double originalHeight) line 11
PdfThumbnailService.GenerateThumbNail(Stream pdfFile) line 45
CreateThumbnailFromPDFTests.GivenPDF_WhenIRunTheThumbnailFunction_ThenIExpectAJPGImageThumbnail() line 18
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Steps to Reproduce
using PdfLibCore;
using PdfLibCore.Enums;
public class PdfThumbnailService : ThumbNailServiceBase
{
public override Stream? GenerateThumbNail(Stream pdfFile)
{
using var pdfDocument = new PdfDocument(pdfFile);
using var pdfPage = pdfDocument.Pages[0];
Func<PdfPage, PdfiumBitmap> getBitMapInOrientation = page =>
{
if (pdfPage.Orientation == PageOrientations.Normal)
{
return new PdfiumBitmap((int)pdfPage.Size.Width, (int)pdfPage.Size.Height, true);
}
else
{
return new PdfiumBitmap((int)pdfPage.Size.Height, (int)pdfPage.Size.Width, true);
}
};
using var bitmap = getBitMapInOrientation(pdfPage);
pdfPage.Render(bitmap, pdfPage.Orientation, RenderingFlags.LcdText);
int dpiX = 96, dpiY = 96;
using var stream = bitmap.AsBmpStream(dpiX, dpiY);
int width, height;
if (pdfPage.Orientation == PageOrientations.Normal)
{
width = (int)pdfPage.Size.Width;
height = (int)pdfPage.Size.Height;
}
else
{
width = (int)pdfPage.Size.Height;
height = (int)pdfPage.Size.Width;
}
return CreateThumbnail(stream, width, height);
}
}
Base class
using ImageMagick;
public abstract class ThumbNailServiceBase : IThumbNailService
{
public abstract Stream? GenerateThumbNail(Stream inputFile);
protected static Stream? CreateThumbnail(Stream inputFile, double originalWidth, double originalHeight)
{
MagickImage image = new(inputFile);
return GenerateThumb(originalWidth, originalHeight, image);
}
private static Stream GenerateThumb(double originalWidth, double originalHeight, MagickImage image)
{
// Set the desired thumbnail size
uint width = 200;
var thumbnailSize = new MagickGeometry(width, (uint)(width * (originalHeight / originalWidth)));
// Generate the thumbnail
image.Thumbnail(thumbnailSize);
MemoryStream thumbnail = new();
image.Write(thumbnail, MagickFormat.Jpg);
return thumbnail;
}
protected static Stream? CreateThumbnailFromImage(Stream inputFile)
{
inputFile.Seek(0, SeekOrigin.Begin);
using MagickImage image = new(inputFile);
uint originalWidth = image.Width;
uint originalHeight = image.Height;
return GenerateThumb(originalWidth, originalHeight, image);
}
}
Integration Test
public class CreateThumbnailFromPDFTests
{
[Fact]
public void GivenPDF_WhenIRunTheThumbnailFunction_ThenIExpectAJPGImageThumbnail()
{
// Arrange
var filePath = @"ThumbnailTests/TestFiles/PDF/Being Responsible.pdf";
using var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
var pdfThumbGenerator = new PdfThumbnailService();
// Action
// EXCEPTION THROWN IN THIS METHOD CALL
using var actual = pdfThumbGenerator.GenerateThumbNail(fs);
using (FileStream fileStream = File.Create(@"ThumbnailTests/TestFiles/PDF/Being Responsible.jpg"))
{
// Ensure the stream is at the beginning (important for copying the entire stream)
actual.Seek(0, SeekOrigin.Begin);
// Copy the stream to the file
actual.CopyTo(fileStream);
}
// Assert
Assert.NotNull(actual);
}
}
Images
This is happening because we recently added an extra check when reading bmp files. According to the specs (https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader) it is not allowed to have a negative height when the bmp file is compressed:
For compressed formats, biHeight must be positive, regardless of image orientation.
And the library that you seem to be using is using a negative value here: https://github.com/jbaarssen/PdfLibCore/blob/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/src/PdfLibCore/BmpStream.cs#L69 while setting the compression to bitfields: https://github.com/jbaarssen/PdfLibCore/blob/6b155b257e49ac02da2bc33f52c4f199d50cf5bf/src/PdfLibCore/BmpStream.cs#L13.
Thank you for explaining the cause, do you have a solution? Do I need to report the problem to Syncfusion regarding pdfcore and the BMP images it creates? I am feeling it might be unreasonable to expect every vendor that creates software that produces BMPs to make a change to correct this and it might be better catered for with some sort of flag or setting in MagickImage?? What do you think? Id like to be able to take this and future updates of MagickImage, but wont be able to unless I/we can solve it. Thanks!
The ImageMagick library is very strict about input images and pdfcore seems to be writing an invalid file. And we will not be going to introduce a flag that allows reading these kind of invalid files. So yes you will need to reach out to Syncfusion about this. You seem to be paying for that so reporting this as a bug in their product should probably get this resolved pretty quickly.
I am re-opening this issue because I want to check something. I just pushed a patch that allows reading files with the compression set to BI_BITFIELDS. Is it possible to share the bmp file so I can check if ImageMagick will read it again?
Hi there
Thanks for your efforts. I will create a test that writes the bmp file to disk. So far it's all achieved with memory streams, as I don't need to retain the intermediate bmp.
I'm away for a few days so I will be able to do it when I get back.
I have a word doc that converts to pdf, I then take the first page and output it as a thumbnail jpeg. This seems to be the easiest way to get a thumbnail of a word document. It all happens in memory at present.
Thank you Ian
On Fri, 2 May 2025, 17:01 Dirk Lemstra, @.***> wrote:
dlemstra left a comment (dlemstra/Magick.NET#1820) https://github.com/dlemstra/Magick.NET/issues/1820#issuecomment-2847581600
I am re-opening this issue because I want to check something. I just pushed a patch that allows reading files with the compression set to BI_BITFIELDS. Is it possible to share the bmp file so I can check if ImageMagick will read it again?
— Reply to this email directly, view it on GitHub https://github.com/dlemstra/Magick.NET/issues/1820#issuecomment-2847581600, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAY3NBHY252SPZEDM6TLFU324OJF5AVCNFSM6AAAAAB2OPB452VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQNBXGU4DCNRQGA . You are receiving this because you authored the thread.Message ID: @.***>
Hi Dirk
I copied the memory stream holding the bmp to a file stream and wrote it out to disk.
Hopefully this helps you?
Thanks Ian
On Fri, 2 May 2025 at 21:40, Ian Robertson @.***> wrote:
Hi there
Thanks for your efforts. I will create a test that writes the bmp file to disk. So far it's all achieved with memory streams, as I don't need to retain the intermediate bmp.
I'm away for a few days so I will be able to do it when I get back.
I have a word doc that converts to pdf, I then take the first page and output it as a thumbnail jpeg. This seems to be the easiest way to get a thumbnail of a word document. It all happens in memory at present.
Thank you Ian
On Fri, 2 May 2025, 17:01 Dirk Lemstra, @.***> wrote:
dlemstra left a comment (dlemstra/Magick.NET#1820) https://github.com/dlemstra/Magick.NET/issues/1820#issuecomment-2847581600
I am re-opening this issue because I want to check something. I just pushed a patch that allows reading files with the compression set to BI_BITFIELDS. Is it possible to share the bmp file so I can check if ImageMagick will read it again?
— Reply to this email directly, view it on GitHub https://github.com/dlemstra/Magick.NET/issues/1820#issuecomment-2847581600, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAY3NBHY252SPZEDM6TLFU324OJF5AVCNFSM6AAAAAB2OPB452VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDQNBXGU4DCNRQGA . You are receiving this because you authored the thread.Message ID: @.***>
I am not sure if you are aware about this but when you reply through email attachments are not attached to the issue. Can you do that here instead: https://github.com/dlemstra/Magick.NET/issues/1820.
I had to add .txt extension to upload the file. Github doesn't allow bmp files to be uploaded!
It looks like my patches fixed this issue. Turns out this was an issue on our side and this will be resolved in the next release.
Thanks Dirk, this is great news 👍🏻