ststeiger/PdfSharpCore

Dependency issue with new version of the ImageSharp

dejanmauer opened this issue · 28 comments

Let say, I would like to load image (png) from file:
XImage image = XImage.FromFile("d:\\temp\\qrcode.png");

In case I have reference in my project to ImageSharp > 2.1.6 I get this error:

System.MissingMethodException: 'Method not found: 'SixLabors.ImageSharp.Image`1<!!0> SixLabors.ImageSharp.Image.LoadPixelData(Byte[], Int32, Int32)'.'

Can this be fixed to keep compatibiliry with new versions of ImageSharp (3.X)?

@dejanmauer - lots of folks are running into this issue. I am really hopeful that @ststeiger will enable us to use the latest version of ImageSharp easily.

I've created this branch:
https://github.com/TonyValenti/PdfSharpCore

That enables the latest version of ImageSharp for modern .NET versions and uses the older version for legacy .NET versions.

Here's to hoping @ststeiger incorporates it!

There is now a security issue on latest working version of image sharp (2.1.6):
GHSA-65x7-c272-7g7r

Edit:
In the mean time, there is a fixed 2.1.7 version available from ImageSharp: https://github.com/SixLabors/ImageSharp/releases/tag/v2.1.7

@ststeiger Can you please comment on all these requests on updating for ImageSharp. This is becoming an issue.

Later today I'll be submitting a PR that contains everything necessary to resolve this security issue.

Will it include being able to use v3 of ImageSharp. And the bigger question, will @ststeiger respond and approve the PR.

Yes. V3 for modern .NET and V2 for framework.

We'll have to wait and see if the maintainer of this package accepts it.

Hi @ststeiger -
I just created this PR:
https://github.com/ststeiger/PdfSharpCore/compare/master...TonyValenti:PdfSharpCore:master?expand=1

Which upgrades various packages, including ImageSharp, to their latest stable, secure versions.

@johnwc - my PR is here if you want to take a look.

What are the main differences between the two versions of ImageSharpImageSource? The two look almost exactly the same, minus a few method call differences. If that is the case, wouldn't it be better to just make the compile time directives within the few minor differences, rather than duplicate the code for each? I think it would be easier to maintain that way.

@johnwc -
Here is a screen shot showing the compare:
image

In my experience, when you're dealing with subtle variances like this, you can easily get yourself in trouble by having a single file where you #if things in and out. It is much safer and easier to maintain to branch the file.

It would be better to maintain one file with a few compile time directives, than two files with almost same code. Think of someone else coming in and only changing one file for a security flaw. If it was in the same file, it would be seen and updated at the same time. Also, visual studio makes it very clear on what directive is currently being worked on and debugged with its coloring of the enabled directive. In my 25 years of development, I have never come across an issue of compile time directives being in a file in relation to this version vs that version compiling. The directives make it very clear what is going on.

That's not a big deal to me either way as long as we can get the maintainer to merge and publish an update.

Is there any news on this subject ? We are using this great library but this issue starts to be a big pain on our side :(

Thanks !

Bump. :)

same problem here ;(

It turns out it's actually pretty easy to work around this by providing a new, ImageSharp 2/3 compatible ImageSource to the ImageSource.ImageSourceImpl static function.

Just create the class:

public class ImageSharp3CompatibleImageSource<TPixel> : ImageSource where TPixel : unmanaged, IPixel<TPixel> {
	public static IImageSource FromImageSharpImage(
		Image<TPixel> image,
		IImageFormat imgFormat,
		int? quality = 75) =>
		new ImageSharpImageSourceImpl<TPixel>("*" + Guid.NewGuid().ToString("B"), image, quality ?? 75, imgFormat is PngFormat);

	protected override IImageSource FromBinaryImpl(
		string name,
		Func<byte[]> imageSource,
		int? quality = 75) {
		Image<TPixel> image = Image.Load<TPixel>(imageSource());
		return new ImageSharpImageSourceImpl<TPixel>(name, image, quality ?? 75, image.Metadata.DecodedImageFormat is PngFormat);
	}

	protected override IImageSource FromFileImpl(string path, int? quality = 75) {
		Image<TPixel> image = Image.Load<TPixel>(path);
		return new ImageSharpImageSourceImpl<TPixel>(path, image, quality ?? 75, image.Metadata.DecodedImageFormat is PngFormat);
	}

	protected override IImageSource FromStreamImpl(
		string name,
		Func<Stream> imageStream,
		int? quality = 75) {
		using (Stream stream = imageStream()) {
			Image<TPixel> image = Image.Load<TPixel>(stream);
			return new ImageSharpImageSourceImpl<TPixel>(name, image, quality ?? 75, image.Metadata.DecodedImageFormat is PngFormat);
		}
	}

	private class ImageSharpImageSourceImpl<TPixel2>(
		string name,
		Image<TPixel2> image,
		int quality,
		bool isTransparent)
		: IImageSource
		where TPixel2 : unmanaged, IPixel<TPixel2> {
		private Image<TPixel2> Image { get; } = image;

		public int Width => Image.Width;

		public int Height => Image.Height;

		public string Name { get; } = name;

		public bool Transparent { get; internal set; } = isTransparent;

		public void SaveAsJpeg(MemoryStream ms) =>
			Image.SaveAsJpeg(ms, new JpegEncoder() {
				Quality = quality
			});

		public void SaveAsPdfBitmap(MemoryStream ms) {
			BmpEncoder encoder = new BmpEncoder() {
				BitsPerPixel = BmpBitsPerPixel.Pixel32
			};
			Image.Save(ms, encoder);
		}
	}
}

And provide it to the static property at application startup (in the Program.cs for instance):

ImageSource.ImageSourceImpl = new ImageSharp3CompatibleImageSource<Rgba32>();

Did anyone else notice that someone just merged an update for dependencies, but did not include any dependency updates for ImageSharp?

Did anyone else notice that someone just merged an update for dependencies, but did not include any dependency updates for ImageSharp?

I assumed it had to do with the new licensing model for ImageSharp and thus wasn't expecting an update... ever.

@robbaman @ststeiger @TonyValenti ImageSharp supposedly already gave the green light to this project for using it under the new license without issue.

Yes they did but I believe that does not matter to @ststeiger

Yes they did but I believe that does not matter to @ststeiger

Can you point us to where they did?

@robbaman
That works great. Unfortunately I have to insert a background image. Is there a way to use your code here?

var background = XImage.FromStream(() => File.OpenRead(Invoice.BackgroundImage));
for (var i = 1; i <= pages; ++i)
{
    var pageInfo = renderer.DocumentRenderer.FormattedDocument.GetPageInfo(i);

    var page = renderer.PdfDocument.AddPage();
    var gfx = XGraphics.FromPdfPage(page, XPageDirection.Downwards);
    gfx.DrawImage(background, 0, 0, pageInfo.Width, pageInfo.Height);
    renderer.DocumentRenderer.RenderPage(gfx, i);
}

Hi @IngoManthey

Yes it should. The code I mentioned sets a delegate in a static property. This delegate is used by the XImage.FromStream call on the first line of your code sample.

Hallo@IngoManthey

Ja, das sollte es. Der von mir erwähnte Code setzt einen Delegaten in einer statischen Eigenschaft. Dieser Delegate wird vom XImage.FromStreamAufruf in der ersten Zeile Ihres Codebeispiels verwendet.

Yes it was my mistake, now everything works. Thanks for the answer