How to insert a dynamically generated bitmap into PDF document using PDFsharp? - c#

I am trying to use PDFsharp to insert a dynamically generated bitmap of a QR Code in to a PDF document. I don't want to save the bitmap to the file but just want to insert it into the PDF. The problem I'm having is the DrawImage command is looking for a string where the image file is located. But I don't want to save the file, I just want to insert it into the PDF document. Is there a way of doing this?
var QRCode_BMP = _generalCode.QR_CodeGenerator(AddReviewPath); //This generates the bitmap
MemoryStream streamQR = new MemoryStream();
QRCode_BMP.Save(streamQR, System.Drawing.Imaging.ImageFormat.Jpeg); //save bitmap into memory stream in jpeg format System.Drawing.Image QR_Jpeg = System.Drawing.Image.FromStream(streamQR);// save memory stream to image file
XImage xImage = XImage.FromGdiPlusImage(QR_Jpeg);
gfx = XGraphics.FromPdfPage(page);
DrawImage(gfx, xImage, 0, 0, 100, 100); //This is not working
QRCode_BMP.Dispose();
streamQR.Close();
gfx.Dispose();

You create a QR code in QRCode_BMP and then you create an XImage from QR_Jpeg and write it is not working.
QRCode_BMP is only used to create a stream that is never used. We don't see where QR_Jpeg is coming from.
Provide a complete sample.
BTW: You can use XImage.FromStream to use the stream you created.
P.S.: IMHO JPEG is a bad choice for QR codes. Just use BMP and PDFsharp will use a lossless compression.

This is how I made it work;
PdfDocument pdf = PdfGenerator.GeneratePdf("<b>some html here</b>", PageSize.A4);
QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode("some text here", QRCodeGenerator.ECCLevel.Q);
QRCode qrCode = new QRCode(qrCodeData);
Bitmap qrCodeImage = qrCode.GetGraphic(10);
PdfPage page = pdf.Pages[0]; //I will add it to 1st page
// Get an XGraphics object for drawing
XGraphics gfx = XGraphics.FromPdfPage(page);
XImage image = XImage.FromGdiPlusImage(qrCodeImage); //you can use XImage.FromGdiPlusImage to get the bitmap object as image (not a stream)
gfx.DrawImage(image, 50, 50, 150, 150);
//save your pdf, dispose other objects

Using XImage.FromStream like #I liked the old Stack Overflow mentioned, in your posted code you should be able to just use:
var QRCode_BMP = _generalCode.QR_CodeGenerator(AddReviewPath); //This generates the bitmap
MemoryStream streamQR = new MemoryStream();
QRCode_BMP.Save(streamQR, System.Drawing.Imaging.ImageFormat.Jpeg); //save bitmap into memory stream in jpeg format System.Drawing.Image QR_Jpeg = System.Drawing.Image.FromStream(streamQR);// save memory stream to image file
//XImage xImage = XImage.FromGdiPlusImage(QR_Jpeg); // <-- Removed
gfx = XGraphics.FromPdfPage(page);
gfx.DrawImage(XImage.FromStream(streamQR), 0, 0, 100, 100); // <-- Added
//DrawImage(gfx, xImage, 0, 0, 100, 100); //This is not working // <-- Removed
QRCode_BMP.Dispose();
streamQR.Close();
gfx.Dispose();
For general usage of streams for XGraphics.DrawImage (library PDFSharp) here's some code I use to print SVG vector path data to PDF:
System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
// YouTube like button SVG vector path data:
path.Data = Geometry.Parse("M12.42,14A1.54,1.54,0,0,0,14,12.87l1-4.24C15.12,7.76,15,7,14,7H10l1.48-3.54A1.17,1.17,0,0,0,10.24,2a1.49,1.49,0,0,0-1.08.46L5,7H1v7ZM9.89,3.14A.48.48,0,0,1,10.24,3a.29.29,0,0,1,.23.09S9,6.61,9,6.61L8.46,8H14c0,.08-1,4.65-1,4.65a.58.58,0,0,1-.58.35H6V7.39ZM2,8H5v5H2Z");
// Visual check of the path:
// https://yqnn.github.io/svg-path-editor/
// Color area bordered through path of SVG vector:
path.Fill = new SolidColorBrush(Colors.Black);
// Upscale path, if final image is blurry:
double scale = 2;
path.RenderTransform = new ScaleTransform(scale, scale);
Rect bounds = path.Data.GetRenderBounds(null);
// Increase render bounds (here: "+ 4"), if parts of the path in the final image are cut off (test it with "scale = 1" before upscaling):
bounds.Width = (bounds.Width + 4) * scale;
bounds.Height = (bounds.Height + 4) * scale;
path.Measure(bounds.Size);
path.Arrange(bounds);
RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)bounds.Width, (int)bounds.Height, 96, 96, PixelFormats.Pbgra32);
bitmap.Render(path);
// Transparent areas are replaced with black pixels when using a "BmpBitmapEncoder", so instead use a "PngBitmapEncoder":
PngBitmapEncoder encoderPng = new PngBitmapEncoder();
encoderPng.Frames.Add(BitmapFrame.Create(bitmap));
using (MemoryStream stream = new MemoryStream())
{
encoderPng.Save(stream);
// Draw image to PDF using "XGraphics.DrawImage":
gfx.DrawImage(XImage.FromStream(stream), 10, 100, 14.05, 12.05);
}

Related

WPF How to copy a portion of a canvas into an image and overwrite the image file

What I'm trying to do:
Load images from a number of .png files saved to disk (if the file doesn't exist, create a new image and fill it with a given colour)
Add these images to a Canvas (they are the same size as the canvas and are expected to mostly be off screen with a portion overlapping the actual Canvas area)
Draw onto the Canvas using lines, ellipses, etc
(On save) take the Canvas and convert it to a flat image held in memory (let's call it the CanvasImage)
For each image I loaded in step 1, calculate the portion that overlaps the actual Canvas area and copy that portion of the CanvasImage into the appropriate portion of the original image
Save the resulting images to disk, overwriting the existing files
I've had different portions of this working at different times, using code I found by searching for how to do each exact step, but never the full process. I'm mostly struggling with steps 4 to 6 as I don't really understand all the different image-related classes, how you convert between them, and which to use at each step in the process. Though I'm also having some issues with step 1, as I need to be able to overwrite the files at the end.
I've read a lot of the related questions (overwriting images, converting a canvas to an image, copying a portion of an image, etc) and tried the solutions (some of which worked in more simple examples), but I'm struggling to put it all together.
Any help or advice would be greatly appreciated. If there's anything you'd like me to elaborate on I'd be happy to, and if you'd like to see some of the code I can share it (I've just tried a lot of different things so I'm not sure how useful it would be)
I've managed to solve the problem. I'll post the code below for each step I had trouble with in case anyone else will find it useful. The code here is slightly modified to suit the general case.
Step 1: Loading an image from the disk that can be overwritten
public static System.Windows.Controls.Image LoadImage(string filePath)
{
var image = new System.Windows.Controls.Image();
image.Stretch = Stretch.Uniform;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bitmapImage.UriSource = new Uri(filePath, UriKind.Absolute);
bitmapImage.EndInit();
image.Width = bitmapImage.PixelWidth;
image.Height = bitmapImage.PixelHeight;
image.Source = bitmapImage;
return image;
}
Step 4: Convert canvas to a BitmapImage
public static BitmapImage Convert(Canvas canvas)
{
var sizeRender = new Size(canvas.ActualWidth, canvas.ActualHeight);
var dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(canvas);
ctx.DrawRectangle(vb, null, new Rect(new Point(), sizeRender));
}
var renderBitmap =
new RenderTargetBitmap(
(int)canvas.ActualWidth,
(int)canvas.ActualHeight,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(dv);
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
BitmapImage bitmap;
using (var stream = new MemoryStream())
{
pngEncoder.Save(stream);
bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = stream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
}
return bitmap;
}
Step 5: Copy a portion of an image into a different image
public static void SaveImage(Image inputImage, BitmapImage canvasImage, string fileName)
{
... Calculate the portion that overlaps (left, top, width, height) ...
var canvasImageAsBitmap = BitmapConverter.ConvertBitmapImageToBitmap(canvasImage);
var croppingRectangle = new Rectangle(left, top, width, height);
var croppedRegion = canvasImageAsBitmap.Clone(croppingRectangle, canvasImageAsBitmap.PixelFormat);
var image = BitmapConverter.ConvertBitmapImageToBitmap((BitmapImage)inputImage.Source);
var bitmap = new Bitmap((int)inputImage.Width, (int)inputImage.Height);
... Calculate the position of the cropped image in the input image (xPos, yPos) ...
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.DrawImage(image, 0, 0);
graphics.DrawImage(croppedRegion, xPos, yPos);
graphics.Flush();
}
bitmap.Save(fileName);
}
This step relies on the following code to work:
public static Bitmap ConvertBitmapImageToBitmap(BitmapImage bitmapImage)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
encoder.Save(memoryStream);
Bitmap bitmap = new Bitmap(memoryStream);
return new Bitmap(bitmap);
}
}
Step 6: Overwrite the image file to save in the disk.
This is just the line bitmap.Save(fileName); shown in Step 5, which works due to the implementation of Step 1

NGraphics converting SVG to PNG does not scale the image properly

I have svg icon that I am trying to convert to PNG. I am trying to make the PNG 100x100 pixels, but instead of scaling the image to this size, it gets cropped. What I am doing wrong?
using (StreamReader reader = File.OpenText(svgpath))
{
SvgReader svgReader = new SvgReader(reader);
Graphic gr = svgReader.Graphic;
Size size = new Size(100, 100);
var c = PlatformSupport.CreateImageCanvas(size);
gr.Draw(c);
MemoryStream stream = new MemoryStream();
c.GetImage().SaveAsPng(stream);
byte[] fileContent = stream.ToArray();
File.WriteAllBytes(resultPath, fileContent);
}

PDFsharp image to XImage

I created a barcode as an image in my ASP.NET MVC app.
BarcodeLib.Barcode barcode = new BarcodeLib.Barcode()
{
IncludeLabel = false,
Alignment = AlignmentPositions.LEFT,
Width = element.BarcodeWidth,
Height = element.BarcodeHeight,
RotateFlipType = RotateFlipType.RotateNoneFlipNone,
BackColor = Color.Transparent,
ForeColor = Color.Black,
ImageFormat = System.Drawing.Imaging.ImageFormat.Png
};
This will create a barcode with the BarcodeLib.
How do I convert it to an XImage of PDFsharp?
Image img = barcode.Encode(TYPE.CODE128, "123456789");
gfx.DrawImage(xImage, 0, 0, 100, 100);
Solved it this way:
Image img = barcode.Encode(TYPE.CODE128, Name); // this is the image
MemoryStream strm = new MemoryStream();
img.Save(strm, System.Drawing.Imaging.ImageFormat.Png);
XImage xfoto = XImage.FromStream(strm);
If you use the GDI build of PDFsharp then you can call the XImage.FromImage method.
With any build of PDFsharp you can write an PNG image to a MemoryStream and then get an XImage from that MemoryStream.
Actually method had been changed in .net core version. It takes Func as parameter. Attention please: It's unofficial version for .net core.
XImage xImage = XImage.FromStream(() => new MemoryStream(yourByte[]));
Also,
If you know image path you can use that
XImage xImage = XImage.FromFile(imagePath)

Adding footer/EXIF (binary data) to JPG image in C#

Hello all I have an image and I want to add binary data as a footer to that image.
RGBImage rgbImage = (RGBImage) RGBImage.LoadImage(#"test.tiff");
byte[] bytes = File.ReadAllBytes(#"C:\TEMP\gili.bin");
int padding =(int) Math.Ceiling((double)bytes.Length/(rgbImage.Width*3));
byte[] newMakerNoteImage = new byte[rgbImage[0].Data.Length + (rgbImage.Width * 3 * padding)];
Buffer.BlockCopy(rgbImage[0].Data, 0, newMakerNoteImage, 0, rgbImage[0].Data.Length);
Buffer.BlockCopy(bytes, 0, newMakerNoteImage, rgbImage[0].Data.Length, bytes.Length);
BitmapPalette myPalette = BitmapPalettes.WebPalette;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
rgbImage.Width,
rgbImage.Height,
96,
96,
PixelFormats.Bgr24,
myPalette,
newMakerNoteImage,
rgbImage.Width * 3);
FileStream stream = new FileStream(#"C:\TEMP\new.jpg", FileMode.Create);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = false;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(stream);
the image outputs fine however the binary data is not added to the end of the image.
Can you tell me if I'm doing it correctly?
I think i might be looking at it all wrong and I need to use EXIF in order to add this makernote data into the image. the data shoudln't be visible to the user of the image.
here is my tested solution for EXIF/MakerNote creation
As far as I have read 37500 is the makernote hexdecimal tag inside EXIF.
http://nicholasarmstrong.com/2010/02/exif-quick-reference/
public void CreateMakerNoteJpgImage(byte[] makerNoteArray, string path)
{
BitmapPalette myPalette = BitmapPalettes.WebPalette;
// Creates a new empty image with the pre-defined palette
BitmapSource image = BitmapSource.Create(
Width,
Height,
96,
96,
PixelFormats.Bgr24,
myPalette,
_channels[0].Data,
Width * 3);
FileStream stream = new FileStream(path, FileMode.Create);
BitmapMetadata metadata = new BitmapMetadata("jpg");
//adding makernote data into EXIF of the jpeg image
metadata.SetQuery("/app1/ifd/exif:{uint=37500}", makerNoteArray);
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.FlipHorizontal = false;
encoder.FlipVertical = false;
encoder.QualityLevel = 30;
BitmapFrame frame = BitmapFrame.Create(image, null, metadata, null);
encoder.Frames.Add(frame);
encoder.Save(stream);
}

Saving Panel as an Image

I'm doing this paint application. It's kind of simple. It consist of a panel where I will draw on and then finally I will save as JPG or BMP or PNG file.
My application work perfectly but the problem I'm facing is that when I'm saving the output is not what drawn on the panel its black Image nothing just black.
all my work is been saved as
Thepic = new Bitmap(panel1.ClientRectangle.Width, this.ClientRectangle.Height);
and on the mouse (down,up thing) I have
snapshot = (Bitmap)tempDraw.Clone();
and it saved the work normally but again the rsult is black Image not what the panel contain.
I think the problem may be that you're using the "Clone" method.
Try "DrawToBitmap" - that's worked for me in the past.
Here's a sample that saves a bitmap from a control called "plotPrinter":
int width = plotPrinter.Size.Width;
int height = plotPrinter.Size.Height;
Bitmap bm = new Bitmap(width, height);
plotPrinter.DrawToBitmap(bm, new Rectangle(0, 0, width, height));
bm.Save(#"D:\TestDrawToBitmap.bmp", ImageFormat.Bmp);
Be aware of saving directly to the C directly as this is not
permitted with newer versions of window, try using SaveFileDialog.
SaveFileDialog sf = new SaveFileDialog();
sf.Filter = "Bitmap Image (.bmp)|*.bmp|Gif Image (.gif)|*.gif|JPEG Image (.jpeg)|*.jpeg|Png Image (.png)|*.png|Tiff Image (.tiff)|*.tiff|Wmf Image (.wmf)|*.wmf";
sf.ShowDialog();
var path = sf.FileName;
You could try this, this works for me.
I used MemoryStream.
MemoryStream ms = new MemoryStream();
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, new System.Drawing.Rectangle(0, 0, panel1.Width, panel1.Height));
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); //you could ave in BPM, PNG etc format.
byte[] Pic_arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(Pic_arr, 0, Pic_arr.Length);
ms.Close();

Categories