WPF PrintPreview Get the PageContent of DocumentPaginator Pages - wpf

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.

Related

Attach a PDF File to a Print Dialog

I'm trying to attach a PDF File to a Print Dialog, but I haven't find out the way to do it.
I'm using a WPF app, and I have some code related with printing and looks like this:
private void Imprimir()
{
try
{
FixedDocument document = null;
PageContent pageContent = null;
FixedPage fixedPage = null;
PrintDialog printDlg = new PrintDialog();
if (printDlg.ShowDialog() != true)
return;
document.DocumentPaginator.PageSize = new System.Windows.Size(1400, 1450);
fixedPage.Width = document.DocumentPaginator.PageSize.Width;
fixedPage.Height = document.DocumentPaginator.PageSize.Height;
fixedPage.Margin = new Thickness(96, 96, 0, 0);
fixedPage.Children.Add(this);
((System.Windows.Markup.IAddChild)pageContent).AddChild(fixedPage);
document.Pages.Add(pageContent);
printDlg.PrintDocument(document.DocumentPaginator, "Impresion Cierre");
fixedPage.Children.Clear();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
But, by this way I'm just printing a UI Element added to Fixed Page.
I've looked for other codes, but I find nothing.
So, I donĀ“t know if is possible to add a PDF File stored locally to the Print Dialog ?
Thanks for the help...
Well you cannot do this using PrintDialog. There are several options, depending on your goals:
var printQueue = LocalPrintServer.GetDefaultPrintQueue();
using (var input = File.OpenRead("path_to_your.pdf")) {
using (var job = printQueue.AddJob()) {
using (var output = job.JobStream) {
input.CopyTo(output);
}
}
}
Will silently send print job for your file to local print queue. Print job is configurable.
Alternatively you can use adobe reader to handle that for you (or another pdf reader installed on user's machine) but starting process with path to your pdf as FileName and Verb="print".
One more option is to use third party tools (like ghostscript) which can help you with that.

Haxe, OpenFL: draw TextField on Bitmap in a loop

So, this app I am making for mobile phones with Haxe, and OpenFL.
I have a very long text which I load from a text file, and put it into a very tall TextField. However I want to convert this into a Bitmap, due to performance issues.
Again, however, a very tall Bitmap drawn from a text field just shows blank (maybe too much data?), so I decided to split the bitmap data into "pages" bitmaps, which the user can swipe on screen.
When I add the first "page" to display, it does. But rest of the "pages" just show as a blank image.
Here's my code:
images = new Array();
var contentHeight:Float = 560;
field = new TextField();
var fieldFont = Assets.getFont("fonts/Kreon-Regular.ttf");
var format:TextFormat = new TextFormat(fieldFont.fontName, 26 /*currentZoom*/, 0xffffff);// 0x4F4F4F);
format.align = TextFormatAlign.LEFT;
field.defaultTextFormat = format;
var fieldWidth:Float = 410;
field.embedFonts = true;
field.text = fullText;
field.selectable = false;
field.wordWrap = true;
field.border = false;
field.autoSize = TextFieldAutoSize.LEFT;
field.width = fieldWidth;
//field.x = 0;
//addChild(field);
//loop through lines, if line within reach, increase clip height, else make new bd
var clipY:Float = 0;
var clipHeight:Float = 0;
trace(field.numLines);
var h_:Float = field.getLineMetrics(0).height;
var bd:BitmapData;
var mainBd:BitmapData = new BitmapData(Std.int(field.width), Std.int(field.height), true, 0x00000000);
mainBd.draw(field);
for (i in 0... field.numLines)
{
try {
h_ = field.getLineMetrics(i).height+0.2;
} catch (e:Dynamic) {}
if (clipHeight < contentHeight + h_)//line can be accomodated
{
clipHeight += h_;
}
else { //can't be accomodated, do clipping
bd = new BitmapData(Std.int(field.width), Std.int(clipHeight + 5), true, 0x00000000);
trace("clip: clipY:" + clipY + " height:" + clipHeight);
bd.copyPixels(mainBd, new Rectangle(0, clipY, field.width, clipHeight), new Point(0, clipY));
//bd.draw(field, new Matrix(), new ColorTransform(), BlendMode.NORMAL, new Rectangle(0, clipY, field.width, clipHeight), true);
images.push(new Bitmap(bd, PixelSnapping.AUTO, true));
clipY += clipHeight;
clipHeight = 0;
}
}
addChild(images[1]);
You only add one of your images (addChild(images[1]);) to view.
Also, I'd recommend, you to :
Find the exact problem with displaying it as a single TextField, start with the exact amount of text needed for it to break.
Check if there are some weird Unicode characters in your text, they may just plain break the renderer layouting for example(which should be counted as a bug in renderer, but still the way to fix it is to remove them).
If you can't fix that problem, use multiple textfields instead of bitmaps, since with a large text you have a high chance to exceed memory limits(one page of your text currently takes around 1Mb of memory, which is A LOT.
The way you do this currently wouldn't work in any case since you do the same renderer normally does. If you want to render it to partial bitmaps(and there is a real problem with rendering big texts), you need to divide the text in parts and render each part individually. (Basically the same as using multiple textfields with cacheAsBitmap).
NOTE: Bitmaps shouldn't actually be any faster than TextFields, unless you use a VERY fancy font or a lot of filters. In any case, cacheAsBitmap property should do what you want automagically, without writing all this code. But I'm 99% sure thats not the case and you don't need that.

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

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 )

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

PrintWindow WPF / DirectX

Anyone know of a way to reliably take a snapshot of a WPF window? The PrintWindow api works well for "standard" win32 windows but since WPF uses DirectX, PrintWindow fails to capture an image. I think that one would need to grab the front buffer for the DirectX object associated with the window, but I am not sure how to do that.
Thanks!
I'm not sure if this is what you mean, and I'm not sure I'm allowed to link to my blog or not, but is this any use? It basically uses a RenderTargetBitmap to generate a JPG. You can use it to "screenshot" an entire window then print that.
If this is against the rules, someone feel free to delete :)
This Method should help you print the entire WPF / XAML Window
private void PrintWindow(PrintDialog pdPrint,
System.Windows.Window wWin,
string sTitle,
System.Windows.Thickness? thMargin)
{
Grid drawing_area = new Grid();
drawing_area.Width = pdPrint.PrintableAreaWidth;
drawing_area.Height = pdPrint.PrintableAreaHeight;
Viewbox view_box = new Viewbox();
drawing_area.Children.Add(view_box);
view_box.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
view_box.VerticalAlignment = System.Windows.VerticalAlignment.Center;
if (thMargin == null)
{
view_box.Stretch = System.Windows.Media.Stretch.None;
}
else
{
view_box.Margin = thMargin.Value;
view_box.Stretch = System.Windows.Media.Stretch.Uniform;
}
VisualBrush vis_br = new VisualBrush(wWin);
System.Windows.Shapes.Rectangle win_rect = new System.Windows.Shapes.Rectangle();
view_box.Child = win_rect;
win_rect.Width = wWin.Width;
win_rect.Height = wWin.Height;
win_rect.Fill = vis_br;
win_rect.Stroke = System.Windows.Media.Brushes.Black;
win_rect.BitmapEffect = new System.Windows.Media.Effects.DropShadowBitmapEffect();
// Arrange to produce output.
Rect rect = new Rect(0, 0, pdPrint.PrintableAreaWidth, pdPrint.PrintableAreaHeight);
drawing_area.Arrange(rect);
// Print it.
pdPrint.PrintVisual(drawing_area, sTitle);
}
Regards Sean Campbell
You can use the PrintDialog.PrintVisual() method.
MSDN Link: http://msdn.microsoft.com/en-us/library/system.windows.controls.printdialog.printvisual.aspx
A sample: http://www.thejoyofcode.com/Reason_9._Printing.aspx

Resources