Magick.NET icon indicating copy to clipboard operation
Magick.NET copied to clipboard

Since upgrading to 14.6.0 my integration test fails

Open silentbobbert opened this issue 9 months ago • 10 comments

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

Being Responsible.pdf

silentbobbert avatar Apr 04 '25 11:04 silentbobbert

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.

dlemstra avatar Apr 05 '25 15:04 dlemstra

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!

silentbobbert avatar Apr 06 '25 06:04 silentbobbert

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.

dlemstra avatar Apr 06 '25 07:04 dlemstra

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?

dlemstra avatar May 02 '25 16:05 dlemstra

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: @.***>

silentbobbert avatar May 02 '25 20:05 silentbobbert

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: @.***>

silentbobbert avatar May 09 '25 14:05 silentbobbert

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.

dlemstra avatar May 09 '25 18:05 dlemstra

intermediate.bmp.txt

I had to add .txt extension to upload the file. Github doesn't allow bmp files to be uploaded!

silentbobbert avatar May 09 '25 18:05 silentbobbert

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.

dlemstra avatar May 10 '25 08:05 dlemstra

Thanks Dirk, this is great news 👍🏻

silentbobbert avatar May 10 '25 13:05 silentbobbert