I'm using PdfSharp to save a WPF Window into a PDF. I'm getting System.OutOfMemoryException when executing the following code:-
using (MemoryStream lMemoryStream = new MemoryStream())
{
Package package = Package.Open(lMemoryStream, FileMode.Create);
var doc = new System.Windows.Xps.Packaging.XpsDocument(package);
XpsDocumentWriter writer = System.Windows.Xps.Packaging.XpsDocument.CreateXpsDocumentWriter(doc);
double dpiScale = 600.0 / 96.0;
var renderBitmap = new RenderTargetBitmap(Convert.ToInt32(this.Width * dpiScale),
Convert.ToInt32(this.Height * dpiScale),
600.0,
600.0,
PixelFormats.Pbgra32);
renderBitmap.Render(this);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawImage(renderBitmap, new Rect(0, 0, this.Width, this.Height));
}
writer.Write(visual);
doc.Close();
package.Close();
var pdfXpsDoc = PdfSharp.Xps.XpsModel.XpsDocument.Open(lMemoryStream);
XpsConverter.Convert(pdfXpsDoc, _pdfFileName, 0);
}
In the above snippet, if I change the 600.0 value to 300.0 in dpiScale while creating RenderTargetBitmap, I don't get the OutOfMemoryException but the quality of the saved PDF is not good.
How to dispose the PdfSharp XpsDocument? I believe it is causing some memory leak.
Related
I'm trying to create a simple sketching application.
But I've ran into a weird problem. I have a Surface Pro 3 that I'm working on, with a DPI of 144, according to some system settings.
When I save the image from my app, using 96 as the dpi, it produces an image that's just a little bit smaller. Which is kind of weird.
Is there a way that I can either a) scale the canvas/strokes up that I'm saving, or tell the RenderTargetBitmap to scale properly? If I just stick 144 in there, I get the proper scale for the strokes, but my canvas size is borked.
My canvas saving code looks like this:
public void saveCanvas(object sender, RoutedEventArgs e){
this.save_filename = this.save_filename ?? this.getSaveFilename();
if (save_filename != null){
var cantwo = new InkCanvas();
cantwo.Strokes.Clear();
cantwo.Strokes.Add(this.canvas.Strokes);
cantwo.Background = this.canvas.Background;
var size = new Size(this.canvas.ActualWidth, this.canvas.ActualHeight);
cantwo.Height = size.Height;
cantwo.Width = size.Width;
cantwo.Measure(size);
cantwo.Arrange(new Rect(size));
var transform = this.canvas.LayoutTransform;
var rtb = new RenderTargetBitmap((int)this.canvas.ActualWidth, (int)this.canvas.ActualHeight, dpiX, dpiY, PixelFormats.Default);
rtb.Render(cantwo);
try {
using(var fs = File.Open(this.save_filename, FileMode.Create)){
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
encoder.Save(fs);
}
}
catch(IOException){
MessageBox.Show("Failed to save image", "ERROR: Save Failed");
}
// Restore transformation if there was one.
this.canvas.LayoutTransform = transform;
}
}
How can I save my image at the same size/resolution/dpi as my canvas? (Or draw on my canvas at the same dpi/scale as I save my image)?
Instead of creating a second InkCanvas, draw a Rectangle that is filled with a VisualBrush into a DrawingVisual:
var rect = new Rect(canvas.RenderSize);
var visual = new DrawingVisual();
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new VisualBrush(canvas), null, rect);
}
var rtb = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
rtb.Render(visual);
In my WPF project I use the Control.Datavisualization.Charting.Chart control to plot some line series.
I also would like to store the Chart into an image, so I do the following (after series creation):
this.chart.Series.Clear();
this.chart.Series.Add(lineCurve);
this.chart.Series.Add(lineBilinear);
//store the image file in folder
DrawUtils.saveChartToPng(this.chart, path);
where saveChartToPng() is as follows:
public static void saveChartToPng(Chart chart, String filename)
{
Rect bounds = VisualTreeHelper.GetDescendantBounds(chart);
double dpi = 96d;
RenderTargetBitmap rtb = new RenderTargetBitmap((int)bounds.Width, (int)bounds.Height, dpi, dpi, System.Windows.Media.PixelFormats.Default);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(chart);
dc.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
//endcode as PNG
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes(filename, ms.ToArray());
return;
}
After the saveChartToPng() the chart is saved to an image, but series are not present in the image. Series are visible in the chart inside the window.
What am I missing before calling the saveChartToPng()?
I'd like to create a bitmap from a WPF control. I see some examples in this forum (one above all: Render a "not visible" WPF controls to an bitmap image ), but they can render well only if the WPF control is already shown on the screen.
I have to create a control in a model to associate the bitmap result into an his internal bitmap field.
I followed the example into the above thread, but the result was a bitmap with only a part of the content of the control (as if it was not completely rendered).
How perform a complete render before the image render aquisition?
This is my source code:
if( spChart == null){
String s = "<SparrowChart xmlns=\"http://sparrowtoolkit.codeplex.com/wpf\">" +
" <SparrowChart.XAxis>" +
" <LinearXAxis/>" +
" </SparrowChart.XAxis>" +
" <SparrowChart.YAxis>" +
" <LinearYAxis/>" +
" </SparrowChart.YAxis>" +
"</SparrowChart>";
System.IO.StringReader stringReader = new System.IO.StringReader(s);
System.Xml.XmlReader xmlReader;
xmlReader = System.Xml.XmlReader.Create(stringReader);
spChart = (Sparrow.Chart.SparrowChart)System.Windows.Markup.XamlReader.Load(xmlReader);
spChart.XAxes[0].MinValue = 0;
spChart.XAxes[0].MaxValue = 10;
spChart.YAxes[0].MinValue = 0;
spChart.YAxes[0].MaxValue = 10;
spChart.Series.Clear();
} else {
spChart.Series.Clear();
}
List<System.Drawing.PointF> points = new List<System.Drawing.PointF> {new System.Drawing.PointF(3, 7),
new System.Drawing.PointF(5, 2),
new System.Drawing.PointF(8, 4),
new System.Drawing.PointF(4, 6)};
Sparrow.Chart.SeriesBase LS = new Sparrow.Chart.SplineSeries();
foreach(System.Drawing.PointF x in points) {
Sparrow.Chart.DoublePoint newPoint = new Sparrow.Chart.DoublePoint();
newPoint.Data=x.X;
newPoint.Value=x.Y;
}
spChart.Series.Add(LS);
LS = new Sparrow.Chart.ScatterSeries();
foreach(System.Drawing.PointF x in points) {
Sparrow.Chart.DoublePoint newPoint = new Sparrow.Chart.DoublePoint();
newPoint.Data=x.X;
newPoint.Value=x.Y;
}
spChart.Series.Add(LS);
spChart.Measure(new System.Windows.Size(Double.PositiveInfinity, Double.PositiveInfinity));
spChart.Arrange(new System.Windows.Rect(new System.Windows.Size(1000, 1000)));
spChart.UpdateLayout();
RenderTargetBitmap rtb = new RenderTargetBitmap((int)SpChart.ActualWidth, (int)SpChart.ActualHeight, 96, 96, Windows.Media.PixelFormats.Pbgra32);
rtb.Render(spChart);
PngBitmapEncoder png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream stream = New MemoryStream();
png.Save(stream);
Bitmap tmpBitmap = new Bitmap(Image.FromStream(stream));
bitmapToRender = MyBitmap.Clone();
MyBitmap.Dispose();
Thank you
Lucio
For me, this one works:
Size size = new Size(432, 460);
frameworkElement.Measure(size);
frameworkElement.Arrange(new Rect(new Point(), size));
frameworkElement.UpdateLayout();
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)frameworkElement.ActualWidth, (int)frameworkElement.ActualHeight, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(frameworkElement);
FormatConvertedBitmap monoBitmap = new FormatConvertedBitmap(renderTargetBitmap, PixelFormats.BlackWhite, null, 0);
BmpBitmapEncoder bmpImage = new BmpBitmapEncoder();
bmpImage.Frames.Add(BitmapFrame.Create(monoBitmap));
byte[] bmpData;
using (MemoryStream buffer = new MemoryStream())
{
bmpImage.Save(buffer);
bmpData = buffer.ToArray();
}
Sorry for resulting in monochrome bitmap, that's just what I used. This can simply be converted to any bitmap type as a result.
Actually, I have to print the view from a Viewport3D, obviously, I use a RenderTargetBitmap. The problem is that if the resolution of the rendered image got high, some triangles of my scene don't appear on my final image.
For example, my viewport can be 1024*768 and the resolution I use with my RenderTargetBitmap would be 3 times viewport's resolution.
http://imgur.com/PS2F9D9
I already solved the problem in a certain way... In fact, triangles don't appear when I use a big scale. If I lower the size of my RenderTargetBitmap, it will contain everything.
Actually, I have more or less 1024*768 at 96dpi. If I want to impress at 300dpi, I need to get a huge image so I would like to avoid this last solution.
Some code :
public static RenderTargetBitmap CaptureEcran(Viewport3D p_viewPort, int p_scale)
{
RenderTargetBitmap l_bmp;
p_scale = p_scale > 5 ? 5 : p_scale;
l_bmp = new RenderTargetBitmap(p_scale * Convert.ToInt32(p_viewPort.ActualWidth), p_scale * Convert.ToInt32(p_viewPort.ActualHeight), p_scale * 96.0, p_scale * 96.0, PixelFormats.Pbgra32);
DrawingVisual vis = new DrawingVisual();
DrawingContext dc = vis.RenderOpen();
dc.DrawRectangle(System.Windows.Media.Brushes.White, null, new Rect(0, 0, p_scale * p_viewPort.ActualWidth, p_scale * p_viewPort.ActualHeight));
dc.Close();
l_bmp.Render(vis);
p_viewPort.UpdateLayout();
l_bmp.Render(p_viewPort);
return l_bmp;
}
public static void SaveImage(RenderTargetBitmap renderTargetBitmap, string m_impression)
{
System.Windows.Forms.FolderBrowserDialog l_fBD = new System.Windows.Forms.FolderBrowserDialog();
l_fBD.ShowDialog();
string l_path = l_fBD.SelectedPath + "\\" + CurrentUser() + "__" + CurrentDate() + "__." + m_impression.ToLower();
FileStream stream = new FileStream(l_path, FileMode.Create);
BitmapEncoder l_encoder = null;
switch(m_impression){
case "PNG":
PngBitmapEncoder l_png = new PngBitmapEncoder();
l_encoder = l_png;
break;
case "JPEG":
JpegBitmapEncoder l_jpeg = new JpegBitmapEncoder();
l_jpeg.QualityLevel = 30;
l_encoder = l_jpeg;
break;
}
l_encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
l_encoder.Save(stream);
stream.Close();
}
My call is :
SaveImage(CaptureEcran(m_viewPortCourant.ViewPort3D,5), m_impression);
Where m_impression is .png or .jpg
It finally works by using a VisualBrush containing the viewport and drawn into the DrawingContext.
DrawingVisual vis = new DrawingVisual();
DrawingContext dc = vis.RenderOpen();
VisualBrush sourceBrush = new VisualBrush(p_viewPort);
dc.DrawRectangle(System.Windows.Media.Brushes.White, null, new Rect(0, 0, p_viewPort.ActualWidth * p_scale, p_viewPort.ActualHeight * p_scale));
dc.DrawRectangle(sourceBrush, null, new Rect(new System.Windows.Point(0, 0), new Vector(p_viewPort.ActualWidth, p_viewPort.ActualHeight)));
dc.Close();
l_bmp.Render(vis);
I'm trying to generate a simple tiff image at runtime. This image consists of white background and image downloaded from remote server.
Here's the code I've written for reaching that goal:
const string url = "http://localhost/barcode.gif";
var size = new Size(794, 1123);
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(new SolidColorBrush(Colors.White), null, new Rect(size));
var image = new BitmapImage(new Uri(url));
drawingContext.DrawImage(image, new Rect(0, 0, 180, 120));
}
var targetBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Default);
targetBitmap.Render(drawingVisual);
var convertedBitmap = new FormatConvertedBitmap(targetBitmap, PixelFormats.BlackWhite, null, 0);
var encoder = new TiffBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(convertedBitmap));
using (var fs = new FileStream("out.tif", FileMode.Create))
{
encoder.Save(fs);
}
The code works and generates "out.tif" file. But the output file has just a white background, without image received from remote server.
What can be the problem? I have tried the following code in a variety of ways, but everytime with no luck.
I found this reading about the FormatConvertedBitmap class. Maybe give it a shot
FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();
// BitmapSource objects like FormatConvertedBitmap can only have their properties
// changed within a BeginInit/EndInit block.
newFormatedBitmapSource.BeginInit();
// Use the BitmapSource object defined above as the source for this new
// BitmapSource (chain the BitmapSource objects together).
newFormatedBitmapSource.Source = targetBitmap;
// Set the new format to BlackWhite.
newFormatedBitmapSource.DestinationFormat = PixelFormats.BlackWhite;
newFormatedBitmapSource.EndInit();