Tuesday, July 29, 2008

Printing ServerReport in background

Recently I wanted to print a server report presented with a ReportViwer component in a windows forms application without showing any GUI. Another thing was to investigate the feasibility of enabling automatic printing server reports capabilities in background from within an application providing printing preferences via ApplicationSettings.

ReportViewer source code investigation using Reflector showed that it provides only PrintDialog() public API that will show a modal PrintDialog dialog thus requiring user interaction.
Later I found a
www.gotreportviewer.com site with an example of local report printing without showing any GUI using LocalReport.Render() method. ServerReport does not provide such method so I adapted the code for printing ServerReport in background.
First page is rendered using ServerReport.Render() public API. It will return streamIds list for each page that report consists of and that could be used to render all other pages via ServerReport.RenderStream() public API.

string sExtension;
string sEncoding;
string sMimeType;
string[] sStreamIds;
Warning[] warnings;

// render into EMF — one page per one stream
// render first page and get streamIds for the rest pages
byte[] bt = report.Render(”Image”
, GetDeviceInfo()
, out sMimeType
, out sEncoding
, out sExtension
, out sStreamIds
, out warnings);
SaveToStream(bt);

// iterate through each streamId and save
foreach (string sStreamId in sStreamIds)
{
bt = report.RenderStream(”Image”, sStreamId, GetDeviceInfo(), out sMimeType, out sEncoding);
SaveToStream(bt);
}


Image Device Info tips:

1. Try to provide paper dimensions that correspond to report’s or adjust reports one to papers’. Otherwise report will be stretched across the paper sheet.
2. StartPage default value is 1 and EndDate equels StartDate - so do not surprise is only first page is printed.

Everything above said developed into:

using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using Microsoft.Reporting.WinForms;
using System.IO;
using System.Drawing.Imaging;

namespace ServerReportPrinting
{
class Program
{
#region Class Fields
///

/// Defines current page that is being printed.
///

private static int m_nCurrentPageIndex;
///

/// List of streams for storing rendered report pages.
///

private static IList
<Stream> m_streams = new List<Stream>();
#endregion

#region Class methods
static void Main(string[] args)
{
PrintReport();
}
#endregion

#region Class Utility methods
private static void PrintReport()
{
ExportToEMF();
// export to EMF was successfull
// proceed with printing
Print();
}
///

/// Exports report representation into EMF format.
///

private static void ExportToEMF()
{
// initialize ReportViewer
ReportViewer rptViewer = new ReportViewer();
rptViewer.ServerReport.ReportServerUrl = new Uri(@"http://localhost/reportserver", System.UriKind.Absolute);
//rptViewer.ServerReport.ReportPath = @"Here goes server report path";
rptViewer.ProcessingMode = ProcessingMode.Remote;
ServerReport report = rptViewer.ServerReport;
// print
string sExtension;
string sEncoding;
string sMimeType;
string[] sStreamIds;
Warning[] warnings;

// render into EMF -- one page per one stream
// render first page and get streamIds for the rest pages
byte[] bt = report.Render("Image"
, GetDeviceInfo()
, out sMimeType
, out sEncoding
, out sExtension
, out sStreamIds
, out warnings);
SaveToStream(bt);

// iterate through each streamId and save
foreach (string sStreamId in sStreamIds)
{
bt = report.RenderStream("Image", sStreamId, GetDeviceInfo(), out sMimeType, out sEncoding);
SaveToStream(bt);
}

// set stream positions to 0
foreach (Stream stream in m_streams)
stream.Position = 0;
}
///

/// Creates a memory stream that will store rendered report definition.
///

private static void SaveToStream(byte[] bStr)
{
Stream fs = new MemoryStream();
fs.Write(bStr, 0, bStr.Length);
m_streams.Add(fs);
}
///

/// Create print document and print it.
///

private static void Print()
{
// print document
if (m_streams == null || m_streams.Count == 0)
return;

PrintDocument printDoc = new PrintDocument();
if (!printDoc.PrinterSettings.IsValid)
{
Console.WriteLine("Can't find printer");
return;
}

printDoc.PrintPage += new PrintPageEventHandler(PrintPage);
printDoc.Print();
}
private static string GetDeviceInfo()
{

return "<DeviceInfo>"
+ " <OutputFormat>EMF</OutputFormat>"
+ "
<StartPage>1</StartPage>"
+ "
<EndPage>1</EndPage>"
+ "
<PageWidth>8.5in</PageWidth>"
+ "
<PageHeight>11in</PageHeight>"
+ "
<MarginTop>0.25in</MarginTop>"
+ "
<MarginLeft>0.25in</MarginLeft>"
+ "
<MarginRight>0.25in</MarginRight>"
+ "
<MarginBottom>0.25in</MarginBottom>"
+ "
</DeviceInfo>";
}
///

/// Handles page printing.
///

private static void PrintPage(object sender, PrintPageEventArgs ev)
{
Metafile pageImage = new Metafile(m_streams[m_nCurrentPageIndex]);
ev.Graphics.DrawImage(pageImage, ev.PageBounds);

m_nCurrentPageIndex++;
ev.HasMorePages = (m_nCurrentPageIndex  < m_streams.Count );

}
       #endregion
}
}


Ensure that you have a report deployed to your report server.
Hope this helps somebody.
Regards,
Oleh


5 comments:

Stefan said...

Hello!

Very clever made! However, the last (big) piece of code doesn't show completely. Would you provide that, please?

Best Regards,
Stefan

Oleh Svintsitskyy said...

Hello Stefan,

Thanks!
I have updated the post.

You can contact me via email (oleh.svintsitskiy@gmail.com) in case you have any further questions.

It's interesting to know where/how you will be using it.

Regards,
Oleh

Anonymous said...

Great post and very useful, thanks!

Beej said...

Thanks so much for this - I've been trawling the web for ages and not been able to find a clean solution that actually works - until I read your post. I used your solution and it worked first time! You're a legend mate, thanks again.

Beej said...

Actually in the end I found it seemed to struggle to handle multiple pages - I only ever got the first page to print. In the end I found a similar solution to yours which seems to work a treat: http://blogs.msdn.com/b/brianhartman/archive/2009/02/27/manually-printing-a-report.aspx