Printing your graph.

Feb 11, 2010 at 5:47 AM

Hey guy's.

Hier is some code to print your whole graph to an XPS document. I wont use this to print to printer because it only prints what fits on one page but it works really well to print to XPS.

You will need: Microsoft XML Paper Specification Essentials Pack from:

http://www.microsoft.com/downloads/details.aspx?FamilyId=B8DCFFDD-E3A5-44CC-8021-7649FD37FFEE&displaylang=en

            //Print the whole document onto a single huge XPS page.
            PrintDialog printDialog = new PrintDialog();

            //Remember to choose the "Microsoft XPS Document Writer".
            if (printDialog.ShowDialog() == true)
            { 
                //Change graphLayout to your graphLayout.
                //"My Canvas" is the name of the print item in the queue.
                printDialog.PrintVisual(graphLayout, "My Canvas"); 
            }

I am busy making functionality to print a graph over multiple pages when it is huge. It would really help if I could GET and SET the X and Y of vertices.

Hope this helps someone.

rootme

Feb 12, 2010 at 7:01 PM
Edited Feb 12, 2010 at 7:13 PM

Since I was trying the same and have code, I figured I'd share.  Please let me know if you see any glaring problems or know of a better way.

First, get the graph as a FixedDocument.  This is similar to getting it as an image, but we retain the vectored graphics this way so that when we eventually print, we get crisp graphics.  The graph itself is a FrameworkElement so send it in as the parameter:

		//This is used to get the diagram for printing.  The resulting fixed document will have one page
		//only that contains the diagram at a 1:1 ratio on a custom page size that is just big enough to
		//hold the diagram.  The print dialog uses this to perform the print out by using custom
		//paginators to break the page into printable pages.  It is very much like printing an image,
		//where this returns the image to be printed.  This image, however, uses vectored graphics so it
		//retains its look and feel no matter the size or dots per inch (dpi).
		public static FixedDocument GetDocument(FrameworkElement toPrint)
		{
			FixedDocument fixedDoc = new FixedDocument();
			toPrint.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			toPrint.Arrange(new Rect(new Point(0, 0), toPrint.DesiredSize));

			Size size = toPrint.DesiredSize;
			
			VisualBrush vb = new VisualBrush(toPrint);
			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, size.Width, size.Height);
			PageContent pageContent = new PageContent();
			FixedPage page = new FixedPage();
			((IAddChild)pageContent).AddChild(page);
			fixedDoc.Pages.Add(pageContent);
			page.Width = size.Width;
			page.Height = size.Height;
			Canvas canvas = new Canvas();
			canvas.Width = size.Width;
			canvas.Height = size.Height;
			canvas.Background = vb;
			page.Children.Add(canvas);

			return fixedDoc;
		}

Create a new instance of the below class using the FixedDocument.DocumentPaginator as the first param, Size(816, 1056) as the second for 8.5 by 11 and Thickness(48) for 1/2 inch margins all the way around.  Use the instance to print or with a DocumentPageView.

 

	//Breaks the original 1:1 page into as many pages as needed to print out on the device at a 1:1
	//ratio.
	public class NormalPaginator : DocumentPaginator
	{
		Size m_PageSize;
		Thickness m_Margins;
		DocumentPaginator m_Paginator;
		Size m_ContentArea;

		public NormalPaginator(DocumentPaginator paginator, Size pageSize, Thickness margins)
		{
			m_PageSize = pageSize;
			m_Margins = margins;
			m_Paginator = paginator;
			m_ContentArea = new Size(m_PageSize.Width - (m_Margins.Left + m_Margins.Right),
									 m_PageSize.Height - (m_Margins.Top + m_Margins.Bottom));
		}

		public override DocumentPage GetPage(int pageNumber)
		{
			DocumentPage page = m_Paginator.GetPage(0);
			int rowpagecount = (int)(Math.Ceiling(page.ContentBox.Width / m_ContentArea.Width));
			int row = pageNumber / rowpagecount;
			int column = pageNumber % rowpagecount;

			ContainerVisual newPage = new ContainerVisual();

			ContainerVisual content = new ContainerVisual();
			VisualBrush vb = new VisualBrush(page.Visual);
			vb.Stretch = Stretch.Uniform;
			vb.AlignmentX = AlignmentX.Center;
			vb.AlignmentY = AlignmentY.Center;
			vb.ViewboxUnits = BrushMappingMode.Absolute;
			vb.TileMode = TileMode.None;
			vb.Viewbox = new Rect(column * m_ContentArea.Width, row * m_ContentArea.Height, m_ContentArea.Width, m_ContentArea.Height);
			Canvas canvas = new Canvas();
			canvas.Background = vb;
			canvas.Height = m_ContentArea.Height;
			canvas.Width = m_ContentArea.Width;
			canvas.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			canvas.Arrange(new Rect(new Point(0, 0), m_ContentArea));
			content.Children.Add(canvas);
			newPage.Children.Add(content);
			newPage.Transform = new TranslateTransform(m_Margins.Left, m_Margins.Top);


			return new DocumentPage(newPage, m_PageSize, 
					      new Rect(m_Margins.Left, m_Margins.Top, m_ContentArea.Width, m_ContentArea.Height),
					      new Rect(m_Margins.Left, m_Margins.Top, m_ContentArea.Width, m_ContentArea.Height));
		}

		public override bool IsPageCountValid
		{
			get { return true; }
		}

		public override int PageCount
		{
			get 
			{
				DocumentPage page = m_Paginator.GetPage(0);

				return (int)(Math.Ceiling(page.ContentBox.Width / m_ContentArea.Width) * 
					    Math.Ceiling(page.ContentBox.Height / m_ContentArea.Height));
			}
		}

		public override Size PageSize
		{
			get
			{
				return m_PageSize;
			}
			set
			{
				m_PageSize = value;
			}
		}

		public override IDocumentPaginatorSource Source
		{
			get { return null; }
		}
	}

Hope that helps,

Scott

Feb 15, 2010 at 6:34 AM

Hey Scott,

Looks good, just 2 things:

1. "...we get crisp graphics...". Um, I don't. Can't see why. My pages come out blurry and unusable. Maybe I'm missing something.

My vertices have .gif images in them, but it prints fine using my code from above. Maybe it has something to do with the VisualBrush?

2. I just quickly whipped together some code to draw the lines of the print pages on the graph so the user can move vertices away from the edge of the page.

I use the same page size as you to draw the pages but when I print them they are cut off and printed over more than one page. Have a look and please let me know

if you see where I'm going wrong:

PrintDialog printDialog = new PrintDialog();

//Set printable area
double printableAreaHeight = 1056;
double printableAreaWidth = 816;

//Get actual graph width and height.
double graphWidth = graphLayout.ActualWidth;
double graphHeight = graphLayout.ActualHeight;

//Calculate the number of pages high and wide.
double numPagesHigh = Math.Ceiling(graphHeight / PrintableAreaHeight);
double numPagesWide = Math.Ceiling(graphWidth / printableAreaWidth);

//Set the color of the dashed line, with rounded edge's etc.
path.Stroke = Brushes.Black;
path.StrokeDashCap = PenLineCap.Round;
path.StrokeDashOffset = 10;
path.StrokeThickness = 10;

//Create a stroe array to make the dashed line.
path.StrokeDashArray = new DoubleCollection() { 5, 8 };

//Create a new rectangle the size of the printable page.
Rect rect = new Rect(new Size(printableAreaWidth, printableAreaHeight));
            
//Loop for each row and column to draw the print rectangles on the canvas.
            for (int pageRow = 0; pageRow < numPagesHigh; pageRow++)
                for (int pageCol = 0; pageCol < numPagesWide; pageCol++)
                {
                    //Create a new rectanglegeometry, set its location and draw it,
                    //also add it to the list in case it needs to be deleted later.
                    RectangleGeometry rec = new RectangleGeometry(rect);
                    rect.Location = new Point((printableAreaWidth * pageCol), (printableAreaHeight * pageRow));
                    MainGeometryGroup.Children.Add(rec);
                    g.Add(rec);
                }

            //Draw the last rectangle in the bottom right.
            RectangleGeometry rec2 = new RectangleGeometry(rect);
            rect.Location = new Point((graphWidth - printableAreaWidth), (graphHeight - printableAreaHeight));
            MainGeometryGroup.Children.Add(rec2);
            g.Add(rec2);

Where the following is inside my graph layout in the xaml:

<Path Name="path" Stroke="Gray" StrokeThickness="1">
         <Path.Data>
              <GeometryGroup x:Name="MainGeometryGroup"/>
         </Path.Data>
</Path>

Eagerly awaiting your reply!

rootme

 

Feb 15, 2010 at 12:06 PM

Hey Scott,

I started using a DocumentViewer to create a sort of a print preview. When using the DocumentViewer's print, the xps is very blurry, the same issue I'm having with the code you posted.

It seems that my xps looks fine only if I use "PrintVisual"...

Any ideas?

rootme

Feb 16, 2010 at 3:39 PM

The only issue I can see is the possibility of your GIFs causing issues.  I use WPF borders and text blocks for most of mine so they automatically scale, but the routine I sent should just print out as is.  Have you tried saving the original 1:1 page and looking at it instead of just after pagination?  Something in my pagination routine may be messing up your diagram.  If so, I'd like to see your corrected version.

Here was my original version before adding margins and the documentpaginator.  It totally goes off the selected printer and prints to the full print area, margins are just the non-printable area.

		public static FixedDocument GetFixedDocument(FrameworkElement toPrint, PrintDialog printDialog)
		{
			PrintCapabilities capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
			Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
			Size visibleSize = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
			FixedDocument fixedDoc = new FixedDocument();
			//If the toPrint visual is not displayed on screen we neeed to measure and arrange it   
			toPrint.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
			toPrint.Arrange(new Rect(new Point(0, 0), toPrint.DesiredSize));
			//   
			Size size = toPrint.DesiredSize;
			
			double xOffset = 0;
			double yOffset = 0;
			while (yOffset < size.Height)
			{
				while (xOffset < size.Width)
				{
					VisualBrush vb = new VisualBrush(toPrint);
					vb.Stretch = Stretch.None;
					vb.AlignmentX = AlignmentX.Left;
					vb.AlignmentY = AlignmentY.Top;
					vb.ViewboxUnits = BrushMappingMode.Absolute;
					vb.TileMode = TileMode.None;
					vb.Viewbox = new Rect(xOffset, yOffset, visibleSize.Width, visibleSize.Height);
					PageContent pageContent = new PageContent();
					FixedPage page = new FixedPage();
					((IAddChild)pageContent).AddChild(page);
					fixedDoc.Pages.Add(pageContent);
					page.Width = pageSize.Width;
					page.Height = pageSize.Height;
					Canvas canvas = new Canvas();
					FixedPage.SetLeft(canvas, capabilities.PageImageableArea.OriginWidth);
					FixedPage.SetTop(canvas, capabilities.PageImageableArea.OriginHeight);
					canvas.Width = visibleSize.Width;
					canvas.Height = visibleSize.Height;
					canvas.Background = vb;
					page.Children.Add(canvas);
					xOffset += visibleSize.Width;
				}
				xOffset = 0;
				yOffset += visibleSize.Height;
			}
			return fixedDoc;
		}

Try it and see if you get different results.

Let me know what you find,

Scott

Feb 16, 2010 at 5:48 PM

I was looking at your second problem more and noticed that I don't see where you are taking margins into consideration.  816x1056 is the ENTIRE 8.5x11 page.  Most printers have a non-printable area around the page so you can never actually print an entire 8.5x11 page, but slightly smaller.  This may be the root of your problems overall.  If you try to use zero as your margin, you will encounter problems.  The smallest margin you can have is based on the non-printable area, which is printer specific and can actually be different on all 4 sides of the print page.  You should always check these for the selected printer (see the PageImageableArea or PrintDocumentImageableArea classes in the Framework) or use an actual margin of your own larger than the non-printable area.  On most printers the 1/2 inch margin I had in there will work, but always check your printer to make sure.  I had a printer once that would allow nothing less than a 0.55 inch margin on the bottom of the page.

Mar 16, 2010 at 8:29 PM

Hey,

Sorry, been extremely busy at work: lots of overtime so I did not have much time to work on this lately.

I have good news. I have made an alternative solution for my printing problem: I save it as a good quality PNG image, then split the image into chunks and throw it in a document and voila! It should work for anyone who has similar issues of quality with printing graphics.

I am busy making a class out of it, i.e. headings, page numbers, etc. and will post a beta here soon for scrutiny.

Just running into a slight snag regarding the printable area and margins(image gets clipped): but the solution is in your code(clipping does not happen in your code), I will try sort that out now.

Regarding your previous post about the printer margins being absent: Good point!

Sorry for the long absence.

rootme

Aug 18, 2010 at 11:12 AM

Hi rootme,

just wondering if you had code you could make availble to grab the png image at a high resolution

thanks

greycloud

Aug 18, 2010 at 4:42 PM
No sorry. Im having problems with quality and high resolution is quite some time away...

On Wed, Aug 18, 2010 at 1:12 PM, GreyCloud <notifications@codeplex.com> wrote:

From: GreyCloud

Hi rootme,

just wondering if you had code you could make availble to grab the png image at a high resolution

thanks

greycloud

Read the full discussion online.

To add a post to this discussion, reply to this email (graphsharp@discussions.codeplex.com)

To start a new discussion for this project, email graphsharp@discussions.codeplex.com

You are receiving this email because you subscribed to this discussion on CodePlex. You can unsubscribe on CodePlex.com.

Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com


Aug 18, 2010 at 6:55 PM

I managed to print to png using this code, though changing the DPI in the code does not make the image any larger ?? the code to print to xps above also works though i have yet to try to paginate them correctly when printing.

Sep 8, 2010 at 7:29 PM

Just uploaded a patch that contains the print code above in the MainWindow.  If you want to see it "live" grab it and try it out.