System.Drawing.Image to SkiaSharp - c#

I am trying to move my code from using System.Drawing.Image to using SkiaSharp as recommended here.
I trying to find similar operations for working with Tif files from a stream.
Currently, the following is what I have using System.Drawing.Image:
System.Drawing.Image MyImage = System.Drawing.Image.FromStream(inStream);
PdfDocument doc = new PdfDocument();
for (int PageIndex = 0; PageIndex < MyImage.GetFrameCount(FrameDimension.Page); PageIndex++)
{
MyImage.SelectActiveFrame(FrameDimension.Page, PageIndex);
XImage img = XImage.FromGdiPlusImage(MyImage);
var page = new PdfPage();
page.Width = MyImage.Width;
page.Height = MyImage.Height;
doc.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[PageIndex]);
xgr.DrawImage(img, 0, 0, page.Width, page.Height);
}
doc.Save(outStream);
MyImage.Dispose();
My current work with SkiaSharp is the following though it does not work correctly. Note: The following code has the following error and I am trying to figure out why in addition to how to select active frames: Unhandled exception. System.ObjectDisposedException: Cannot access a closed Stream. likely due to SKCodec codec = SkiaSharp.SKCodec.Create(inStream); but I am not sure why.
PdfDocument doc = new PdfDocument();
doc.Info.CreationDate = new DateTime();
using MemoryStream inStream = new MemoryStream(data);
using var imgStream = new SKManagedStream(inStream, false);
using var skData = SKData.Create(imgStream);
using SKCodec codec = SKCodec.Create(skData);
// TODO: codec is null!
for (int PageIndex = 0; PageIndex < codec.FrameCount; PageIndex++)
{
SKImageInfo imageInfo = new SKImageInfo(codec.Info.Width, codec.Info.Height);
// create bitmap stub
using SKBitmap skBitmap = new SKBitmap(imageInfo);
IntPtr pixelsPtr = skBitmap.GetPixels();
// decode particular frame into the bitmap
codec.GetPixels(imageInfo, pixelsPtr, new SKCodecOptions(PageIndex));
// encode bitmap back
using SKData encodedData = skBitmap.Encode(SKEncodedImageFormat.Png, 100);
using Stream frameStream = encodedData.AsStream();
XImage img = XImage.FromStream(frameStream);
var page = new PdfPage();
doc.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[PageIndex]);
xgr.DrawImage(img, 0, 0, page.Width, page.Height);
}
doc.Save(destination);
These are other things I have tried:
Using SKCodecOptions to set the FrameIndex but how would I use it since it used for GetPixels() not Decode().
There are no properties, I found, that control FrameIndex within a SKBitmap.
The additional conversion from Image to XImage is a bit confusing as from the source here, it appears to be a constructor that creates an XImage straight from an Image. I would need to create an XImage from a SkiaSharp.SKBitmap instead I believe thought I'm not quite sure how to do this at the moment such as use an existing method like FromStream() though I don't know the differences between the uses necesarily.

You get the exception because decoding SKBitmap with
SkiaSharp.SKBitmap MyImage = SkiaSharp.SKBitmap.Decode(inStream);
disposes the inStream.
Anyway, to get the particular frame using SkiaSharp, you need to get pixels from the codec:
using MemoryStream inStream = new MemoryStream(data);
PdfDocument doc = new PdfDocument();
doc.Info.CreationDate = new DateTime();
using SKCodec codec = SkiaSharp.SKCodec.Create(inStream);
for (int PageIndex = 0; PageIndex < codec.FrameCount; PageIndex++)
{
SKImageInfo imageInfo = new SKImageInfo(codec.Info.Width, codec.Info.Height);
// create bitmap stub
using SKBitmap skBitmap = new SKBitmap(imageInfo);
IntPtr pixelsPtr = skBitmap.GetPixels();
// decode particular frame into the bitmap
codec.GetPixels(imageInfo, pixelsPtr, new SKCodecOptions(PageIndex));
// encode bitmap back
using SKData encodedData = skBitmap.Encode(SKEncodedImageFormat.Png, 100);
using Stream frameStream = encodedData.AsStream();
XImage img = XImage.FromStream(frameStream);
var page = new PdfPage();
doc.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[PageIndex]);
xgr.DrawImage(img, 0, 0, page.Width, page.Height);
}
doc.Save(destination);

Related

how create pdf after image Draw c#

I'm working with PdfSharp to create some pdf files
I am trying to write an image after that save pdf and force download user
but after show box to download and open file, pdf was empty
var foo = new PrivateFontCollection();
foo.AddFontFile(HttpContext.Current.Server.MapPath("~/assets/css/base/fonts/Coustom.ttf"));
var Font_6 = new Font((FontFamily)foo.Families[0], 6f);
StringFormat rf = new StringFormat();
rf.LineAlignment = StringAlignment.Far;
rf.Alignment = StringAlignment.Far;
Brush _RedColor = new SolidBrush(Color.FromArgb(63, 110, 123));
System.Drawing.Image imgBackground = System.Drawing.Image.FromFile(Server.MapPath("../assets/EmptyDoc/Letterhead-FA-A4.jpg"));
int _X = 2300;
int _Y = 450;
using (Graphics graphics = Graphics.FromImage(imgBackground))
{
graphics.DrawString("TEST", Font_6, _RedColor, new PointF((_X - 40), (_Y)), rf);
}
imgBackground.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
PdfDocument document = new PdfDocument();
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
byte[] fileContents = null;
MemoryStream memoryStream = new MemoryStream();
document.Save(memoryStream, true);
fileContents = memoryStream.ToArray();
Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=TEST.pdf");
Response.BinaryWrite(fileContents);
With PdfSharp, you can add an image as the following :
using (FileStream stream = new FileStream(pngFile, FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
XImage image = XImage.FromStream(stream);
XGraphics graphics = XGraphics.FromPdfPage(this.currentPage);
graphics.DrawImage(image, 0 , 0, 200, 100);
}
you just need to replace pngFile by the full path of your drawing file.
Edit :
After reading your code, you could try to add this after the line XGraphics gfx = XGraphics.FromPdfPage(page); :
XImage image = XImage.FromStream(strm);
XGraphics graphics = XGraphics.FromPdfPage(page);
graphics.DrawImage(image, 0 , 0, 200, 100);
Then replace
imgBackground.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
by
MemoryStream strm = new MemoryStream();
imgBackground.Save(strm, System.Drawing.Imaging.ImageFormat.Png);
Seems the function doesn't accept your Stream, so try with a MemoryStream

Images don't display in generated PDF file

I have an HTML page rendered via ASP.NET. When I generate a PDF through HtmlRenderer.PdfSharp the images appear as a red X (missing image) in the PDF while the rest of the HTML appears properly. The images are not links, they are binary objects.
private void PdfFormat(string _body, ref MemoryStream _stream)
{
Bitmap bitmap = new Bitmap(790, 1800);
Graphics g = Graphics.FromImage(bitmap);
XGraphics xg = XGraphics.FromGraphics(g, new XSize(bitmap.Width, bitmap.Height));
TheArtOfDev.HtmlRenderer.PdfSharp.HtmlContainer container = new TheArtOfDev.HtmlRenderer.PdfSharp.HtmlContainer();
container.SetHtml(_body);
PdfDocument pdf = new PdfDocument();
PdfPage page = new PdfPage();
XImage img = XImage.FromGdiPlusImage(bitmap);
pdf.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(pdf.Pages[0]);
container.PerformLayout(xgr);
container.PerformPaint(xgr);
xgr.DrawImage(img, 0, 0);
pdf.Save(_stream, false);
}
the problem was in the decoded body not in the pdf generator. will close this issue and open a new one.
You can use html2canvas, this will surely solve your problem.
const doc = new jsPDF();
var canvasElement = document.createElement('canvas');
html2canvas(this.$refs.content, { canvas: canvasElement
}).then(function (canvas) {
const img = canvas.toDataURL("image/jpeg", 0.8);
doc.addImage(img,'JPEG',30,20);
doc.save("pdfName.pdf");
});
Don't forgot to import:
import jsPDF from 'jspdf'
import html2canvas from "html2canvas"

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

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);
}

Saving RichTextBox FlowDocument to image

i'am making a programm where i want my RichTextBox
content (text+images) to be saved as an image (jpg/png). I tried to use this solution
but i get only black filled image from
SaveUIAsGraphicFile()
I also tried to create FormattedText from my rtb control, printing it works fine, but its not possible to insert images in there. Maybe it is possible to print FlowDocument somehow?
You could use something like the following method to create a bitmap from a FlowDocument:
public BitmapSource FlowDocumentToBitmap(FlowDocument document, Size size)
{
document = CloneDocument(document);
var paginator = ((IDocumentPaginatorSource)document).DocumentPaginator;
paginator.PageSize = size;
var visual = new DrawingVisual();
using (var drawingContext = visual.RenderOpen())
{
// draw white background
drawingContext.DrawRectangle(Brushes.White, null, new Rect(size));
}
visual.Children.Add(paginator.GetPage(0).Visual);
var bitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height,
96, 96, PixelFormats.Pbgra32);
bitmap.Render(visual);
return bitmap;
}
public FlowDocument CloneDocument(FlowDocument document)
{
var copy = new FlowDocument();
var sourceRange = new TextRange(document.ContentStart, document.ContentEnd);
var targetRange = new TextRange(copy.ContentStart, copy.ContentEnd);
using (var stream = new MemoryStream())
{
sourceRange.Save(stream, DataFormats.XamlPackage);
targetRange.Load(stream, DataFormats.XamlPackage);
}
return copy;
}
and then use it like shown below to save a RichTextBox's Document to an image file.
var doc = richTextBox.Document;
var bm = FlowDocumentToBitmap(doc, new Size(richTextBox.ActualWidth, richTextBox.ActualHeight));
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bm));
using (var stream = new FileStream("doc.jpg", FileMode.Create))
{
encoder.Save(stream);
}
You can spend HOURS chasing around trying to figure out why the width is wrong when in reality its trying to paginate in columns. Set the document's columnwidth to the full width of your output bitmap.
public Bitmap FlowDocumentToBitmap(FlowDocument document, Size size)
{
document = CloneDocument(document);
document.ColumnWidth = size.Width;// <- Add this line

How to convert a XPS file to an image in high quality (rather than blurry low resolution)?

I'm trying to convert an XPS with WPF.
The idea is that these images can be loaded with silverlight 4, for this I am using the following code:
// XPS Document
XpsDocument xpsDoc = new XpsDocument(xpsFileName, System.IO.FileAccess.Read);
FixedDocumentSequence docSeq = xpsDoc.GetFixedDocumentSequence();
// The number of pages
PageCount = docSeq.References[0].GetDocument(false).Pages.Count;
DocumentPage sizePage = docSeq.DocumentPaginator.GetPage(0);
PageHeight = sizePage.Size.Height;
PageWidth = sizePage.Size.Width;
// Scale dimensions from 96 dpi to 600 dpi.
double scale = 300/ 96;
// Convert a XPS page to a PNG file
for (int pageNum = 0; pageNum < PageCount; pageNum++)
{
DocumentPage docPage = docSeq.DocumentPaginator.GetPage(pageNum);
BitmapImage bitmap = new BitmapImage();
RenderTargetBitmap renderTarget =
new RenderTargetBitmap((int)(scale * (docPage.Size.Height + 1)),
(int)(scale * (docPage.Size.Height + 1)),
scale * 96,
scale * 96, PixelFormats.Pbgra32);
renderTarget.Render(docPage.Visual);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
FileStream pageOutStream = new FileStream(name + ".Page" + pageNum + ".png", FileMode.Create, FileAccess.Write);
encoder.Save(pageOutStream);
pageOutStream.Close();
This code is taken from http://xpsreader.codeplex.com/ a project to convert an XPS document.
works great!
But the problem is that the image is low resolution and blurry.
I researched and found that RenderTargetBitmap and find on this page:
http://www.codeproject.com/Questions/213737/Render-target-bitmap-quality-issues
The issue here is you Have That does not use hardware RenderTargetBitmap rendering.
One solution is to use DirectX with WPF to do this, but have not found any clear example to show me the right way to do it.
I appreciate suggestions. Thanks in advance.
Update:I attached the XPS document, I am trying to convert the image
Please download test.xps
There is a project named xps2img on sourceforge.net which converts an xps to image. It is made in C# and also contains the source code. Check it out. It will help you to achieve what you want.
http://sourceforge.net/projects/xps2img/files/
I saw in this post and in many others that peoples have problems with conversion of DocumentPage to Image and saving it on HDD.
This method took all pages from document viewer and save them on HDD as jpg images.
public void SaveDocumentPagesToImages(IDocumentPaginatorSource document, string dirPath)
{
if (string.IsNullOrEmpty(dirPath)) return;
if (dirPath[dirPath.Length - 1] != '\\')
dirPath += "\\";
if (!Directory.Exists(dirPath)) return;
MemoryStream[] streams = null;
try
{
int pageCount = document.DocumentPaginator.PageCount;
DocumentPage[] pages = new DocumentPage[pageCount];
for (int i = 0; i < pageCount; i++)
pages[i] = document.DocumentPaginator.GetPage(i);
streams = new MemoryStream[pages.Count()];
for (int i = 0; i < pages.Count(); i++)
{
DocumentPage source = pages[i];
streams[i] = new MemoryStream();
RenderTargetBitmap renderTarget =
new RenderTargetBitmap((int)source.Size.Width,
(int)source.Size.Height,
96, // WPF (Avalon) units are 96dpi based
96,
System.Windows.Media.PixelFormats.Default);
renderTarget.Render(source.Visual);
JpegBitmapEncoder encoder = new JpegBitmapEncoder(); // Choose type here ie: JpegBitmapEncoder, etc
encoder.QualityLevel = 100;
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
encoder.Save(streams[i]);
FileStream file = new FileStream(dirPath + "Page_" + (i+1) + ".jpg", FileMode.CreateNew);
file.Write(streams[i].GetBuffer(), 0, (int)streams[i].Length);
file.Close();
streams[i].Position = 0;
}
}
catch (Exception e1)
{
throw e1;
}
finally
{
if (streams != null)
{
foreach (MemoryStream stream in streams)
{
stream.Close();
stream.Dispose();
}
}
}
}
A nuget package based on xps2img is now available:
https://www.nuget.org/packages/xps2img/
Api available here:
https://github.com/peters/xps2img#usage
private IList<byte[]> GetTifPagesFromXps(string xXpsFileName, double xQuality)
{
using (var xpsDoc = new XpsDocument(xXpsFileName, FileAccess.Read))
{
var docSeq = xpsDoc.GetFixedDocumentSequence();
var tifPages = new List<byte[]>();
for (var i = 0; i < docSeq.DocumentPaginator.PageCount; i++)
{
using (var docPage = docSeq.DocumentPaginator.GetPage(i))
{
var renderTarget = new RenderTargetBitmap((int)(docPage.Size.Width * xQuality), (int)(docPage.Size.Height * xQuality), 96 * xQuality, 96 * xQuality, PixelFormats.Default);
renderTarget.Render(docPage.Visual);
var jpegEncoder = new JpegBitmapEncoder { QualityLevel = 100 };
jpegEncoder.Frames.Add(BitmapFrame.Create(renderTarget));
byte[] buffer;
using (var memoryStream = new MemoryStream())
{
jpegEncoder.Save(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
buffer = memoryStream.GetBuffer();
}
tifPages.Add(buffer);
}
}
xpsDoc.Close();
return tifPages.ToArray();
}
}

Categories