Save Controls.Image to PDF with more than 1 Control.Image - wpf

I'm here for a solution to a little unresolved problem. Maybe I haven't searched for the right things as I haven't found a solution and I hope you will be able to help.
So I have a WPF application where I load an image, here I have no problems. Next I have to add some points by clicking, so i have this function:
private void ClickMouse(object sender, MouseButtonEventArgs e)
{
Point p = e.GetPosition(this);
schema.PointFromScreen(p);
var myImage3 = new Image();
var bi3 = new BitmapImage();
bi3.BeginInit();
bi3.UriSource = new Uri(_clickImagePath, UriKind.Relative);
bi3.EndInit();
myImage3.Stretch = Stretch.Fill;
myImage3.Source = bi3;
myImage3.MinWidth = 30;
myImage3.MinHeight = 30;
myImage3.Width = 30;
myImage3.Height = 30;
myImage3.Name = _pointName;
var oMargin = new Thickness(p.X, p.Y, 0, 0);
myImage3.Margin = oMargin;
mainCanva.Children.Add(myImage3);
_listImg.Add(myImage3);
PointList.Items.Add(_pointName);
}
The problem is not here but I need a context.
Now on to the main problem. I have to save the final Image to make a PDF with. I use a piece of sample code I found here (http://stackoverflow.com/questions/1824989/how-to-store-system-windows-controls-image-to-local-disk) to convert to a jpg file using ITextsharp. The only thing I have on the PDF is the first picture, so I admitted that it probably take only the first image and not the other created images on it but I really don't know how to fix it. Does some fonction exist to make a kind of screenshot of the image zone? or maybe to pay attention to all image in the zone of the principal Control.Image ?
Thank you for your help.

You should use the following code to render your Canvas as an image :
var rect = new Rect(canvas.RenderSize);
var rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
//encode as PNG
varpngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
using(var ms = new System.IO.MemoryStream())
{
pngEncoder.Save(ms);
System.IO.File.WriteAllBytes("logo.png", ms.ToArray());
}
(Source : Write WPF output to image file )

Related

WPF PrintPreview Get the PageContent of DocumentPaginator Pages

I want to write my own PrintPreview for larger text using a DocumentViewer to display it later.
I have not found anything usefully for my problem.
At the moment I'm searching for a way to get the content of the individual pages.
I found a way to access the individual pages, but I can't store or get it.
Using the code:
DocumentPaginator dpPages = (DocumentPaginator)((IDocumentPaginatorSource)twhtTemp.BuildTemplateControl(txtHeader, txtContent, pdlgPrint)).DocumentPaginator;
dpPages.ComputePageCount();
var fixedDocument = new FixedDocument();
for (int iPages= 0; iPages < dpPages.PageCount; iPages++)
{
var pageContent = new PageContent();
var fixedPage = new FixedPage();
fixedPage.Width = pdlgPrint.PrintableAreaWidth;
fixedPage.Height = pdlgPrint.PrintableAreaHeight;
pageContent.Child = fixedPage;
fixedDocument.Pages.Add(pageContent);
}
I'm already adding a new page for each existing page, but I can't get the content of the page.
So far I know, I need a UIElement to add to fixedPage.Children.
Or is there some easier way to get a flowdocument to a fixed document with many fixedpages (deppending to the pages count)?
i hate it to answer my own questions.
After searching three days i asked here.
One day later i found a way...
It's been a long time since the question has been answered.
I tried Doo Dah's answer, but the problem was that it doesn't take care of the page paddings of a flowdocument.
Therefore I wrote my own solution (Doo Dah's answer helped me to complete it):
public FixedDocument Get_Fixed_From_FlowDoc(FlowDocument flowDoc, PrintDialog printDlg)
{
var fixedDocument = new FixedDocument();
try
{
if (printDlg != null)
{
pdlgPrint = printDlg;
}
if (pdlgPrint == null)
{
pdlgPrint = new PrintDialog();
}
DocumentPaginator dpPages = (DocumentPaginator)((IDocumentPaginatorSource)flowDoc).DocumentPaginator;
dpPages.ComputePageCount();
PrintCapabilities capabilities = pdlgPrint.PrintQueue.GetPrintCapabilities(pdlgPrint.PrintTicket);
for (int iPages= 0; iPages < dpPages.PageCount; iPages++)
{
var page = dpPages.GetPage(iPages);
var pageContent = new PageContent();
var fixedPage = new FixedPage();
Canvas canvas = new Canvas();
VisualBrush vb = new VisualBrush(page.Visual);
vb.Stretch = Stretch.None;
vb.AlignmentX = AlignmentX.Left;
vb.AlignmentY = AlignmentY.Top;
vb.ViewboxUnits = BrushMappingMode.Absolute;
vb.TileMode = TileMode.None;
vb.Viewbox = new Rect(0, 0, capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
FixedPage.SetLeft(canvas, 0);
FixedPage.SetTop(canvas, 0);
canvas.Width = capabilities.PageImageableArea.ExtentWidth;
canvas.Height = capabilities.PageImageableArea.ExtentHeight;
canvas.Background = vb;
fixedPage.Children.Add(canvas);
fixedPage.Width = pdlgPrint.PrintableAreaWidth;
fixedPage.Height = pdlgPrint.PrintableAreaHeight;
pageContent.Child = fixedPage;
fixedDocument.Pages.Add(pageContent);
}
dv1.ShowPageBorders = true;
}
catch (Exception)
{
throw;
}
return fixedDocument;
}
You had to build a FlowDocument of the content you will show before and pass it
to the Method.
Added the PrintDialog variable to call the Method from my preview window and can pass the current printer settings.
If you call it from your main programm, you either can pass a new PrintDialog() or null, there is no difference, because it will create a new PrintDialog if you are passing null
This worked fine for me with a Flowdocument with different types of text (header, text, font).
It should work with pictures and text mixed, or only pictures, too - it's using the visuals and not something specific from a flowdocument, therefore it should work with pagebreaks, too.
I don't tryed Shahin Dohan'S answer, because it's the same problem as so often.
It's written at MVVM and very hard to understand when another person has written it.
At my opinion it would be better to write a little example programm without mvvm and the people can adept it to mvvm or use only the code.
I understand the opportunities of mvvm, but to show someone how to works something i see only disadvantages (if you will not show a specific mvvm mechanic)
Is it possible in WPF to get content of each DocumentPage by page number?
You can follow this then add each extracted TextRange to whatever you want.

How can I capture an entire image from a canvas?

I am working on a project to number features on an image. Each item being numbered is a textblock object being printed on top of a canvas that has a jpg(or bmp/gif/png) drawn to it's background using an image brush.
When it comes time to save the graphic I want to have two options, one to save it as a new jpg (or other format) that has the textblock objects rendered into it. or option b which would be to save the placements of those textblocks to a seperate file (XML, XAML what have you) so you could come back and continue to edit the entries.
Here is some of my code.
This is where I am trying to save the image
private void miSaveImage_Click(object sender, RoutedEventArgs e)
{
ImageBrush bi = (ImageBrush)canvas.Background;
Rect bounds = VisualTreeHelper.GetDescendantBounds(canvas);
double pw = (bi.ImageSource as BitmapSource).PixelWidth;
double ph = (bi.ImageSource as BitmapSource).PixelHeight;
double px = (bi.ImageSource as BitmapSource).DpiX;
double py = (bi.ImageSource as BitmapSource).DpiY;
//Get the actual width and height of the image, then the dpi and render an image of it
RenderTargetBitmap rtb = new RenderTargetBitmap((int)pw, (int)ph, px*96, py*96, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
//create the encoder and save the image data to it.
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
try
{
//eventually I need to make this a save dialog but that will come later.
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes("Sample.png", ms.ToArray());
}
catch (Exception err)
{
MessageBox.Show(err.ToString(), "Problem saving image", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
This is the code I use to load the image
//Global members
Image i;
static Size _Size = new Size();
private void miLoadImage_Click(object sender, RoutedEventArgs e)
{
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".png";
dlg.Filter = #"All supported graphics|*.jpg;*.jpeg;*.png;*.gif;*.bmp|
JPEG Files (*.jpeg)|*.jpeg|
PNG Files (*.png)|*.png|
JPG Files (*.jpg)|*.jpg|
GIF Files (*.gif)|*.gif|
BMP Files (*.bmp)|*.bmp";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
// Get the selected file name and display in a TextBox
if (result == true)
{
// Open document
string filename = dlg.FileName;
Uri uri = new Uri(#dlg.FileName, UriKind.Relative);
//Use canvas rather than image
ImageBrush ib = new ImageBrush();
ib.ImageSource = new BitmapImage(new Uri(filename, UriKind.Relative));
canvas.Background = ib;
if (i == null)
i = new Image();
i.Source = new BitmapImage(new Uri(dlg.FileName));
_Size = new Size(i.Source.Width, i.Source.Height);
}
}
Here is the code I use to write the text to the image.
public void WriteTextToImage(Point position)
{
SolidColorBrush brush = new SolidColorBrush((Color)cpColor.SelectedColor);
//Get something to write on (not an old receipt...)
TextBlock textBlock = new TextBlock();
//Say something useful... well something atleast...
textBlock.Text = tbCurrentLabel.Text;
textBlock.FontSize = slFontSize.Value;
textBlock.Foreground = brush;
Canvas.SetLeft(textBlock, position.X);
Canvas.SetTop(textBlock, position.Y);
canvas.Children.Add(textBlock);
//Need to update the canvas, was not seeing children before doing this.
canvas.UpdateLayout();
//Iterate the label text
IterateLabel();
}
Right now I am only getting a corner of the image that is currently visible in the canvas, and that is being cropped down, by fiddling with the DPI and _Size values I can change how much of the image is shown, but it is static to that one image (If I load a different image all the values are wrong and again I only get a small portion of the image)
If someone could please help me pull my head out of (ahem) I would be most grateful!
TIA
Save entire Canvas
RenderTargetBitmap bmp = new RenderTargetBitmap((int)Cnv.ActualWidth, (int)Cnv.ActualHeight, 100.0, 100.0, PixelFormats.Default);
bmp.Render(Cnv);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
System.IO.FileStream stream = System.IO.File.Create("G:\\Canvas.png");
encoder.Save(stream);
stream.Close();
Save only Background
BitmapFrame bmp = (BitmapFrame)(Cnv.Background as ImageBrush).ImageSource;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bmp);
System.IO.FileStream stream = System.IO.File.Create("G:\\Canvas.png");
encoder.Save(stream);
stream.Close();

Silverlight: How to copy region from WriteableBitmap / BitmapImage?

I have such code for Bitmap wrapper.
I need to create overload constructor which cut some rectangle from source image and put it inside _wbmp.
Smth similar to public Bitmap(string fileName, Rectangle area).
Please share some solution.
public Bitmap(string fileName)
{
Uri uri = new Uri(fileName, UriKind.RelativeOrAbsolute);
StreamResourceInfo sri = null;
sri = Application.GetResourceStream(uri);
// Create a new WriteableBitmap object and set it to the JPEG stream.
BitmapImage bitmap = new BitmapImage();
bitmap.CreateOptions = BitmapCreateOptions.None;
bitmap.SetSource(sri.Stream);
_wbmp = new WriteableBitmap(bitmap);
}
Thank you
The WritableBitmap object has a Render method that you can use to render a new bitmap after adding some transformation. In your case you could create and new WritableBitmap with the correct new size to set the button-right corner and then add temporary image with your source and translate it to the left to set the upper left corner. Something like this:
public static WriteableBitmap CropBitmap(string fileName, int newTop, int newRight, int newBottom, int newLeft)
{
Uri uri = new Uri(fileName, UriKind.RelativeOrAbsolute);
StreamResourceInfo sri = null;
sri = Application.GetResourceStream(uri);
// Create a new WriteableBitmap object and set it to the JPEG stream.
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.CreateOptions = BitmapCreateOptions.None;
bitmapImage.SetSource(sri.Stream);
//calculate bounding box
int originalWidth = bitmapImage.PixelWidth;
int originalHeight = bitmapImage.PixelHeight;
int newSmallWidth = newRight - newLeft;
int newSmallHeight = newBottom - newTop;
//generate temporary control to render image
Image temporaryImage = new Image { Source = bitmapImage, Width = originalWidth, Height = originalHeight };
//create writeablebitmap
WriteableBitmap wb = new WriteableBitmap(newSmallWidth, newSmallHeight);
wb.Render(temporaryImage, new TranslateTransform { X = -newLeft, Y = -newTop });
wb.Invalidate();
return wb;
}

Unable to retrieve TIFF pages from TiffBitmapDecoder.Frames -- all frames are page 1

I'm trying to convert a multipage tiff image into a multipage XPS document. The problem I'm having is with the TiffBitmapDecoder and its BitmapFrames.
Here's the code:
private static void ToXpsDocument(string imageName, string xpsName)
{
using (var p = Package.Open(xpsName))
{
PackageStore.AddPackage(new Uri("pack://thedocloljk.xps"), p);
XpsDocument doc = new XpsDocument(p);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
var dec = new TiffBitmapDecoder
(new Uri(imageName),
BitmapCreateOptions.IgnoreImageCache,
BitmapCacheOption.None);
var fd = new FixedDocument();
foreach (var frame in dec.Frames)
{
var image = new System.Windows.Controls.Image();
image.Source = frame;
var fp = new FixedPage();
fp.Children.Add(image);
fp.Width = frame.Width;
fp.Height = frame.Height;
var pc = new PageContent();
(pc as IAddChild).AddChild(fp);
fd.Pages.Add(pc);
}
writer.Write(fd);
p.Flush();
p.Close();
PackageStore.RemovePackage(new Uri("pack://thedocloljk.xps"));
}
}
This results in an XPS with the correct number of pages. However, every page is a replica of the first page of the tiff. In fact, if I pick out a single frame (say, dec.Frames[4]) and write it to disk, it looks like the first page.
What the heck am I doing wrong here? Are the frames not actually the individual pages of the image? How can I get them out and work with them???
Try using the following code (the commented lines are different than your version):
foreach (var frameSource in dec.Frames) // note this line
{
var frame = BitmapFrame.Create(frameSource); // and this line
var image = new System.Windows.Controls.Image();
image.Source = frame;
var fp = new FixedPage();
fp.Children.Add(image);
fp.Width = frame.Width;
fp.Height = frame.Height;
var pc = new PageContent();
(pc as IAddChild).AddChild(fp);
fd.Pages.Add(pc);
}

Load image from file and print it using WPF... how?

I'm looking for an example of how to load an image from file and print it on a page using WPF. I'm having a hard time finding good information about WPF printing.
var bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = new Uri("");
bi.EndInit();
var vis = new DrawingVisual();
using (var dc = vis.RenderOpen())
{
dc.DrawImage(bi, new Rect { Width = bi.Width, Height = bi.Height });
}
var pdialog = new PrintDialog();
if (pdialog.ShowDialog() == true)
{
pdialog.PrintVisual(vis, "My Image");
}
If you want more control then PrintDialog.PrintVisual gives you you have to wrap your image in a FixedDocumet.
You can find simple code that creates a fixed document here:
http://www.ericsink.com/wpf3d/B_Printing.html
did playing around with this.
Tamir's answer is a great answer, but the problem is, that it's using the original size of the image.
write a solution myself , whitch doesn't stretch the image if it's smaller than the pagesize and bring the image if it's to large at the page.
It can be used for multiply copies and can be used with both orientations.
PrintDialog dlg = new PrintDialog();
if (dlg.ShowDialog() == true)
{
BitmapImage bmi = new BitmapImage(new Uri(strPath));
Image img = new Image();
img.Source = bmi;
if (bmi.PixelWidth < dlg.PrintableAreaWidth ||
bmi.PixelHeight < dlg.PrintableAreaHeight)
{
img.Stretch = Stretch.None;
img.Width = bmi.PixelWidth;
img.Height = bmi.PixelHeight;
}
if (dlg.PrintTicket.PageBorderless == PageBorderless.Borderless)
{
img.Margin = new Thickness(0);
}
else
{
img.Margin = new Thickness(48);
}
img.VerticalAlignment = VerticalAlignment.Top;
img.HorizontalAlignment = HorizontalAlignment.Left;
for (int i = 0; i < dlg.PrintTicket.CopyCount; i++)
{
dlg.PrintVisual(img, "Print a Large Image");
}
}
It only works for pictures from files with a path at the moment, but with a little bit work you can adept it and passing only a BitmapImage.
And it is useable with borderless prints (if your printer does support it)
Had to go the way with BitmapImage because it loads the default size of the image.
Windows.Controls.Image doesn't shows the right height and width if you're loading the image directly there.
I'm knowing, that the question is very old, but it was very difficult to found some useful information during searching this.
Hopefully my post will help other people.
Just load the image and apply it to a visual. Then use the PrintDialog to do the work.
...
PrintDialog printer = new PrintDialog();
if (printer.ShowDialog()) {
printer.PrintVisual(myVisual, "A Page Title");
}

Resources