how to take snapshot from rendered HTML in WPF - c#

Is there any way to take a snapshot of rendered html, i.e from webBrowser control and make an image object of the output?
is it possible by code besides using webbrowser, bcoz i think it needs to be visible on screen before the source is render as output.
i have html string which contains some picture text and table. of height of almost 700px, could be less or more on other scenario.
please guide me on this.

Use a RenderTargetBitmap instance. This allows you to render a control without displaying it on screen. Be careful to give it a width and height ;)
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.rendertargetbitmap%28v=vs.110%29.aspx
Sample from MSDN
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Globalization;
namespace SDKSample
{
public partial class RenderTargetBitmapExample : Page
{
public RenderTargetBitmapExample()
{
Image myImage = new Image();
FormattedText text = new FormattedText("ABC",
new CultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface(this.FontFamily, FontStyles.Normal, FontWeights.Normal, new FontStretch()),
this.FontSize,
this.Foreground);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawText(text, new Point(2, 2));
drawingContext.Close();
RenderTargetBitmap bmp = new RenderTargetBitmap(180, 180, 120, 96, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
myImage.Source = bmp;
// Add Image to the UI
StackPanel myStackPanel = new StackPanel();
myStackPanel.Children.Add(myImage);
this.Content = myStackPanel;
}
}
}

Related

Draw a bitmap in WPF

I am trying to draw some string to bitmap at certain position and copy a barcode bitmap to the new bitmap.I have not done with graphics before so i don't know where to start.
Can anyone guide me on this?my output of the bitmap is a receipt like.
Here is a solution. Please make a grid or canvas and put the barcode image and use a label with desired text and put the label on desired location relative to barcode grid. So, trick is you can immediately take screenshot of this grid using following code.Then, you are done.
public void ConvertToBitmapSource(UIElement element)
{
var target = new RenderTargetBitmap(
(int)element.RenderSize.Width, (int)element.RenderSize.Height,
96, 96, PixelFormats.Pbgra32);
target.Render(element);
var encoder = new PngBitmapEncoder();
var outputFrame = BitmapFrame.Create(target);
encoder.Frames.Add(outputFrame);
using (var file = File.OpenWrite("TestImage.png"))
{
encoder.Save(file);
}
}

Dynamically adding shapes to a panel

Alright, i have been struggling with a specific issue. On a web page in C# i would like to build a diagram. It will consist of some rectangles (i want the rectangles to have extra functionality later, later it will have arrows and other images).
After quite some research i discovered that it is not possible to dynamically draw stuff from the c# code to the user web page.
All the things i can find tell me to make a bitmap and then save it to the outputstream or something, which i did. But it had a strange result, after clicking the button (it should build a rectangle and add it to the panel) :
http://imgur.com/ddfjgAZ
code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//toegevoegd
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Text;
namespace Drawing.Usercontrols
{
public partial class WebUserControl1 : System.Web.UI.UserControl
{
Panel Panel1 = new Panel();
Panel Panel2 = new Panel();
View View1 = new View(); //how does this even work
Bitmap objBitmap = new Bitmap(400, 440);
protected void Page_Load(object sender, EventArgs e)
{
//adding a square... on a Panel...
Panel2.Width=500;
Panel2.Height = 500;
Panel2.BackColor = Color.BlueViolet;
this.Controls.Add(Panel2);
//Making a graphicsobject from the bitmap
Graphics objGraphics = Graphics.FromImage(objBitmap);
//objGraphics.Clear(Color.Blue);
Pen p = new Pen(Color.Yellow, 3);
Brush x = new SolidBrush(Color.BlueViolet);
//This piece will put up a sweet ellipse on the rectangle
Rectangle rect = new Rectangle(200, 100, 60, 20);
objGraphics.DrawEllipse(p, rect);
Panel1.Controls.Add(Panel2);
//This will add the bitmap to the page, but not really, everything on the page will dissapear and this dude will show up
//objBitmap.Save(Response.OutputStream, ImageFormat.Jpeg);
//This will add an image from the internet to the panel, which works
//Panel2.Controls.Add(new System.Web.UI.WebControls.Image { ImageUrl = String.Format("http://ecx.images-amazon.com/images/I/518rMVxp3qL._SL75_SS50_.jpg") });
// textboxes adding
this.Controls.Add(Panel1);
Label FeedbackLabel = new Label();
TextBox Inputtextbox = new TextBox();
Button Submitbutton = new Button();
FeedbackLabel.ID = "lblFeedback";
FeedbackLabel.Text = "Hello world";
Submitbutton.ID = "btnSubmit";
Submitbutton.Text = "Submit";
Inputtextbox.ID = "txtInput";
Submitbutton.Click += SubmitButton_Click;
Panel1.Controls.Add(FeedbackLabel);
Panel1.Controls.Add(Submitbutton);
Panel1.Controls.Add(Inputtextbox);
}
protected void SubmitButton_Click(object sender, EventArgs e)
{
// this is the part where the black rectangle is drawn
objBitmap.Save(Response.OutputStream, ImageFormat.Jpeg);
}
}
}
So my final question is: Can i build rectangles with shapes in it and place them across a panel/layout
(later i want to use those rectangles as objects (adding onclick events to them, but i wont bother you with that just yet)
Thanks a lot in advance!

Convert Canvas to BitMapImage & crop to path

I have a UserControl in a windows phone 8 app, where the user draws on this Usercontrol. I would like to convert this to an image, an example could be bitmap.
I have found this "converting a canvas into bitmap image in android", but I need it for Windows Phone 8.
The usercontrol is positioned in a canvas. The optimal would be if I only converted the usercontrol with information to an image. But If this cant be done then the canvas. If it has to be the Canvas, is it possible to set the background around the usercontrol to be invisible, since this information is not wanted.
EDIT
Maybe this link can help somebody.
How to render a WPF UserControl to a bitmap without creating a window
I will post my solution when it is done, I will also look into converting a bitmap back to usercontrol, if someone has looked into this please inform me about this.
EDIT 2
Has someone used this library
http://writeablebitmapex.codeplex.com/
Should be pretty light weight and can see there is a function crop image. So maybe this is exactly what I need.
EDIT 3
So I have been looking more into this and finally found something that was exactly what I wanted, see http://www.kunal-chowdhury.com/2012/12/how-to-crop-image-based-on-shape-or-path.html
But I cannot seem to get this working. Has anyone an idea for this?
solution
I use writeablebitmap to capture the ui element and i save it to isolatedstorage using mediastream. I can then reload it and use the image as imagesource and thereby crop the element to the wished shape.
This allows you to do it with pure WPF code using a RendeTargetBitmap, DrawingVisual and a VisualBrush.
Get System.Drawing.Bitmap of a WPF Area using VisualBrush
Posted as an answer because I had to do some hunting for a solution using visual brush and not a window.
Here is the the important code, horribly stolen:
public BitmapSource ConvertToBitmapSource(UIElement element)
{
var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
var brush = new VisualBrush(element);
var visual = new DrawingVisual();
var drawingContext = visual.RenderOpen();
drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
new Point(element.RenderSize.Width, element.RenderSize.Height)));
drawingContext.Close();
target.Render(visual);
return target;
}
If you want to include some extra masking here, push an opacity mask while drawing.
drawingContext.PushOpacityMask(brush);
However, you should not have to as the same effect can be accomplished through XAML using the opacity mask property on your target element.
Here is my sample XAML
<Window x:Class="UIToBitmap.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Ellipse x:Name="circle" Height="100" Width="100" Fill="Blue"/>
<Grid Background="Black">
<Image x:Name="Image" Height="100"></Image>
</Grid>
</StackPanel>
</Window>
And the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace UIToBitmap
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var source = ConvertToBitmapSource(circle);
Image.Source = source;
}
public BitmapSource ConvertToBitmapSource(UIElement element)
{
var target = new RenderTargetBitmap((int)(element.RenderSize.Width), (int)(element.RenderSize.Height), 96, 96, PixelFormats.Pbgra32);
var brush = new VisualBrush(element);
var visual = new DrawingVisual();
var drawingContext = visual.RenderOpen();
drawingContext.DrawRectangle(brush, null, new Rect(new Point(0, 0),
new Point(element.RenderSize.Width, element.RenderSize.Height)));
drawingContext.PushOpacityMask(brush);
drawingContext.Close();
target.Render(visual);
return target;
}
}
}
Note that the circle that is drawn on the black grid retains its opacity - only the circle is drawn.
public static class SBA
{
public static WriteableBitmap SaveAsWriteableBitmap(Canvas surface)
{
if (surface == null) return null;
// Save current canvas transform
Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
// Get the size of canvas
Size size = new Size(surface.ActualWidth, surface.ActualHeight);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Get the size of canvas
size = new Size(surface.ActualWidth, surface.ActualHeight);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap = new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
//Restore previously saved layout
surface.LayoutTransform = transform;
//create and return a new WriteableBitmap using the RenderTargetBitmap
return new WriteableBitmap(renderBitmap);
}
public static BitmapImage WriteableBitmapToBitmapImage(WriteableBitmap wbm)
{
BitmapImage bmImage = new BitmapImage();
using (MemoryStream stream = new MemoryStream())
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbm));
encoder.Save(stream);
bmImage.BeginInit();
bmImage.CacheOption = BitmapCacheOption.OnLoad;
bmImage.StreamSource = stream;
bmImage.EndInit();
bmImage.Freeze();
}
return bmImage;
}
public static BitmapImage CanvasToBitmap(Canvas c)
{
return WriteableBitmapToBitmapImage(SaveAsWriteableBitmap(c));
}
}
for your need there is third party library called WriteableBitmapEx that has lots of other feature that you may be in need in future.so just install this library from NugetPackageManager and then you can make a writeableBitmap of any control on the UI and then convert it to any image. i have a sample for it in which i have convert an button to an writeablebitmap and then save it to medialibrary(photos) in phone.here is the simple code but make sure you have install the WriteableBitmapEx.
here btn a Button defined in xaml..
private void btn_Click_1(object sender, RoutedEventArgs e)
{
BitmapImage img = new BitmapImage();
imagebitmap = new WriteableBitmap(btn, null);
imagebitmap.SaveToMediaLibrary("hello", false);
}
you have to use this code directly. hope it helps you
solution I use writeablebitmap to capture the ui element and i save it to isolatedstorage using mediastream. I can then reload it and use the image as imagesource and thereby crop the element to the wished shape.

How to save Canvas-Control at specific position (in a viewbox) to a png in c# wpf?

I want to develope a .NET application with WPF.
In the end there should be a Viewbox or something similar, this Viewbox should contain a canvas and in this canvas there could be various things like filled rectangles, ellipses etc. ( like a drawing application eg. paint).
Now I want to implement the functionallity to save the content of the Canvas to a PNG. I tried rendering the Canvas to a RenderTargetBitmap and then saving it.
The problem here is that I am not able to set specific coordinates, the only thing I can set is the Size (Canvas width & height) of the RenderTargetBitmap, but it will start rendering the Size starting from (0|0) where my Canvas element starts somewhere else. Is there some work arround for that??
The next Problem is, there should be the posibilty to add pictures as sub-element of the root-Canvas, but how could I care about quality and that everything in the Viewbox is printed?
Thank you very much!
------ EDIT ------
I think I've got a Solution:
void SaveUsingEncoder(Canvas myCanvas, string fileName)
{
PngBitmapEncoder encoder = new PngBitmapEncoder();
RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)myCanvas.ActualWidth,
(int)myCanvas.ActualHeight,
96,
96,
PixelFormats.Pbgra32);
Rect bounds = VisualTreeHelper.GetDescendantBounds(myCanvas);
Console.WriteLine(bounds.X + "|" + bounds.Y + " " + bounds.Width + "|" + bounds.Height);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(myCanvas);
ctx.DrawRectangle(vb, null, new Rect(bounds.Location, bounds.Size));
}
bitmap.Render(dv);
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
This saves the complete canvas for me
at specific position -
I think in your case use Margins.

Merging graphics objects does not render text correctly

I'm trying to solve a problem with a custom control ported from a VCL C++ application. The idea is that individual parts of the control are drawn first on a new Graphics object and then merged with the Graphics object from the control's paint method.
I've created a simplified example:
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public class Form1 : Form
{
private PictureBox pictureBox;
public Form1()
{
pictureBox = new PictureBox();
pictureBox.Dock = DockStyle.Fill;
pictureBox.Paint += new PaintEventHandler(pictureBox_Paint);
ClientSize = new Size(100, 50);
Controls.Add(pictureBox);
}
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
SolidBrush blackBrush = new SolidBrush(Color.Black);
Bitmap bitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
Graphics graphics = Graphics.FromImage(bitmap);
Font font = new Font(pictureBox.Font, FontStyle.Regular);
graphics.DrawString("simple test", font, Brushes.Black, 0, 0);
e.Graphics.DrawImage(bitmap, 0, 0);
}
}
}
Running the above code results in the text being drawn too thick:
When i modify the code to draw the text directly to the Graphics object of the PictureBox i get the correct result:
This problem only occurs with text rendering. Lines and other shapes are rendered correctly. Any ideas how to solve this?
Thanks in advance.
This happens because you forgot to initialize the bitmap pixels. By default they are Color.Transparent, which is black with an alpha of 0. This goes wrong when you draw anti-aliased text into the bitmap, the aliasing algorithm will draw pixels that blend from the foreground (black) to the background (also black). Blobby letters that are not anti-aliased is the result.
This is not a problem in the 2nd screenshot because the background pixels were drawn to battleship gray by the default Form.OnPaintBackground() method. Simply add the Graphics.Clear() method to fix your problem:
using (var bitmap = new Bitmap(pictureBox.Width, pictureBox.Height)) {
using (var graphics = Graphics.FromImage(bitmap)) {
graphics.Clear(this.BackColor); // <== NOTE: added
graphics.DrawString("simple test", pictureBox1.Font, Brushes.Black, 0, 0);
}
e.Graphics.DrawImage(bitmap, 0, 0);
}
With using statements added to prevent your program from crashing when the garbage collector doesn't run often enough.

Categories