Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Silverlight 5: Vector and Bitmap Printing for Reports and more

Pete Brown - 11 June 2011

Silverlight 4 introduced bitmap-based printing to Silverlight developers. It was great for printscreen-type single-page jobs, but when it came down to anything more than two or three pages, it just took too long? Why? Printing in Silverlight 4 would send a bitmap representing the entire page to the printer - a serious amount of data.

NOTE: This post covers features not yet released in the public Silverlight 5 beta, but planned for RTM. It's possible that APIs or the features themselves may change before release. It's possible that the final behavior will be different from what I write about here. /warned

The main use-case for bitmap printing was printscreen type work anyway, not reporting. The fact that some of us built report writers around it was just a bonus :) Now, with Silverlight 5, we have Postscript-based vector printing, providing both a faster and higher resolution general printing mechanism, and a great framework upon which others can build their own report writers.

Printing Basics

Just as it was in Silverlight 4, Printing is centered around the PrintDocument class, found in System.Windows.Printing. This class has three primary events: BeginPrint, EndPrint, and PrintPage, which are your hooks into the printing system. You do setup in BeginPrint, teardown in EndPrint, and all the actual page production in PrintPage.

Page Markup

Here's the simple test page XAML I used for this printing example.

<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Print Bitmap"
Height="23"
HorizontalAlignment="Left"
Margin="141,79,0,0"
Name="PrintBitmap"
VerticalAlignment="Top"
Width="95"
Click="PrintBitmap_Click" />
<Button Content="Print Vector"
Height="23"
HorizontalAlignment="Left"
Margin="141,108,0,0"
Name="PrintVector"
VerticalAlignment="Top"
Width="95"
Click="PrintVector_Click" />
<Button Content="Force Vector"
Height="23"
HorizontalAlignment="Left"
Margin="141,137,0,0"
Name="PrintVectorForced"
VerticalAlignment="Top"
Width="95"
Click="PrintVectorForced_Click" />
</Grid>

The application UI looks really simple, just three buttons on a page. This is one of my finest designs.

image

Next, I'll wire up an event handler for each button, and use it to demonstrate the behavior of the three different printing approaches.

Page Code for Basic Vector Printing

Here's the code for a basic vector print of 30 rows.

// Tests basic (not forced) vector printing    
private void PrintVector_Click(object sender, RoutedEventArgs e)
{
PrintDocument doc = new PrintDocument();

doc.PrintPage += (s, ea) =>
{
StackPanel printPanel = new StackPanel();

Random rnd = new Random();

for (int i = 0; i < 30; i++)
{
TextBlock row = new TextBlock();
row.Text = "This is row " + i + " of the current page being printed in vector mode.";

printPanel.Children.Add(row);
}

ea.PageVisual = printPanel;
ea.HasMorePages = false;
};

PrinterFallbackSettings settings = new PrinterFallbackSettings();

doc.Print("Silverlight Vector Print");
}

Note that the PageVisual is assigned after the printPanel is populated. If you assign it prior, and do not force a recalculation of layout (in my example, the panel isn't in the visual tree, but layout is calculated with you assign PageVisual), you'll get a StackPanel with 30 items all piled on each other in the same row. The easiest way to fix this is to assign the PageVisual after the visual has all its children populated.

You could also point the PageVisual to an on-screen visual if you desire. If you're going to do that, you'll need to unhook the visual from the tree first, as a single element cannot have two parents.

If you have no more pages to print other than this one, set HasMorePages to false. If you have additional pages after this one, set it to true.

Printer Fallback Settings and Forcing Vector Printing Mode

New in Silverlight 5 is the PrinterFallbackSettings class. This class is used by one of the overloads of PrintDocument.Print to set two options: ForceVector and OpacityThreshold.

In the previous example, if you had any elements that had opacity other than 1.0, perspective transforms, or other things PostScript doesn't understand, Silverlight would silently fall back to bitmap-based printing.

ForceVector forces Silverlight to print in vector mode, assuming you have a PostScript-enabled printer driver, even when postscript-incompatible items exist in the element tree assigned to PageVisual. You use this in tandem with OpacityThreshold. The Opacity threshold sets the value over which Silverlight will treat an element's opacity as 1.0 to support PostScript printing.

// tests trying to force vector printing mode
private void PrintVectorForced_Click(object sender, RoutedEventArgs e)
{
PrintDocument doc = new PrintDocument();

doc.PrintPage += (s, ea) =>
{
StackPanel printPanel = new StackPanel();

Random rnd = new Random();

for (int i = 0; i < 30; i++)
{
TextBlock row = new TextBlock();
row.Opacity = (rnd.Next(3, 10)) / 10.0;
row.Text = "This is row " + i + " of the current page being printed. Opacity is " + row.Opacity;

printPanel.Children.Add(row);
}

ea.PageVisual = printPanel;
ea.HasMorePages = false;
};

PrinterFallbackSettings settings = new PrinterFallbackSettings();
settings.ForceVector = true;
settings.OpacityThreshold = 0.5;

doc.Print("Silverlight Forced Vector Print", settings);
}

If your content or your printer doesn't support PostScript printing, Silverlight automatically falls back to sending an uncompressed bitmap to the printer. If your printer doesn't support PostScript, you'll see the effect of opacity in the printed results (some items lighter colored than others, for example) as the fallback bitmap mode supports opacity.

Does my Printer Support PostScript?

Vector printing requires PostScript support in your printer driver (and printer). Compressed bitmap support also requires PostScript. Full stop.

If you're a home user, there's a reasonable chance your selected printer driver is not set up for PostScript printing unless you're on a Mac, or you do a lot of print publishing work. In many cases, the printers support PostScript emulation, but the default drivers don't include that functionality. For example, my HP LaserJet 1320 uses the HP Universal PCL5 Printing Driver which does not include PostScript support. The heavy-duty office printers used in many offices often have built-in firmware-level fast PostScript support, and/or appropriate drivers installed. In most cases, you simply need select a different driver. Your mileage will vary, but this is something you'll want to check out before you start building printing into your applications.

Those are the two options for printing in vector mode. Another type of printing, introduced in Silverlight 4, is bitmap-based printing.

Printing in Bitmap Mode

Sometimes you know you want to print in bitmap mode. Rather than let vector mode fall back to bitmap, you can simply force bitmap printing from the start. If you have a PostScript compatible printer and driver, this is quite a bit faster than it was in Silverlight 4, as the bitmap is compressed. If you don't have a PostScript driver, it sends a plain old uncompressed bitmap just like Silverlight 4.

// tests printing in bitmap mode
private void PrintBitmap_Click(object sender, RoutedEventArgs e)
{
PrintDocument doc = new PrintDocument();

doc.PrintPage += (s, ea) =>
{
StackPanel printPanel = new StackPanel();

Random rnd = new Random();

for (int i = 0; i < 30; i++)
{
TextBlock row = new TextBlock();
row.Opacity = (rnd.Next(3, 10)) / 10.0;
row.Text = "This is row " + i + " of the current page being printed in bitmap mode. Opacity is " + row.Opacity;

printPanel.Children.Add(row);
}

ea.PageVisual = printPanel;
ea.HasMorePages = false;
};

doc.PrintBitmap("Silverlight Bitmap Print");
}

Bitmap mode will preserve the opacity settings, as well as ensure render transforms are printed (assuming you apply them) etc. It's not the best approach for printing a report, but it's the highest-fidelity approach for printing visuals when you want to do the equivalent of a print-screen.

The resolution of the bitmap sent is set to the selected printer resolution, typically 600dpi.

Efficient Printing

So, for the most efficient printing of multi-page reports, you'll want to make sure you do the following:

  • Have a PostScript-compatible printer with an appropriate PostScript driver (typically ends with " PS")
  • Avoid Opacity other than 1.0 in your elements to be printed (or use the appropriate fallback settings)
  • Leave out perspective transforms, 3d, and other things not compatible with PostScript printing.

Where to Go From Here

In Silverlight 4 in Action, and expanded to include vector printing in Silverlight 5 in Action, as well as the codeplex project mentioned earlier on this post, I show how to handle multi-page printing scenarios, set up headers and footers, handle dynamic row sizes and other typical reporting requirements. While this isn't a replacement for a real report writer, it will be useful for small reports and even larger ones until other sources step up with fully-featured writers.

(There is no source code to download with this post)

     
posted by Pete Brown on Saturday, June 11, 2011
filed under:      

28 comments for “Silverlight 5: Vector and Bitmap Printing for Reports and more”

  1. Thomas H. Lainersays:
    Pete,

    I don't get it. The initial version of SilverLight printing is so slow, it is unusable. Now we're being told the new improved version wil only work with particular printer drivers. I would guess that a majority of my customers do not have installed a PostScript print driver on their Windows computer. Has Microsoft decided that Apple got it right by dictating that all printers use PostScript drivers?

    What happened to the idea that the Windows OS should hide device specifics from the application program?
  2. Raysays:
    Thanks,Peter.But "only work with particular printer drivers" makes me weak......When Can we get all of this API?Cause our team will start a new project in July.....
  3. Petesays:
    @Thomas

    I'll need to check with the team more. From what they've told me, PostScript drivers are very common. I'm not seeing that in my *very limited* research.

    Also, despite it being SL4 bitmap approach, I've found it to be faster for some reason. Something else for me to look into.

    It's definitely not doom and gloom here. I'll post more testing once we release.

    @Ray

    See response to Thomas. As far as release: I'm not aware of any release in July. If you write to the current printer API, it's identical with only a couple tiny additions, so you should be fine for your project to start.

    @MrMe

    There's no PDF parsing built-in. You'd have to do that yourself, or rely on a third-party.

    Pete
  4. Petesays:
    I spoke with the team. Why did they pick PostScript? There were several reasons.

    #1 is compatibility between Windows and Mac. We do get dinged for not having cross-platform support for Silverlgiht features, and really do try to do this when we can.

    #2 is not relying on the (being phased out) GDI stack that Windows Forms uses

    #3 was not to use XPS. Woah! Wait a minute? Why not use that? Well, turns out the rendering stack for that, as used in WPF, is absolutely enormous. Porting that over to Silverlight would have bloated the runtime by a huge amount. As you know, Silverlight has always been slim and trim, so there have been lots of similar trade-offs over time. In addition, XPS doesn't have very good support for XP, Vista, or Mac, so that would have been very difficult and resulted in even more code added.

    #4 Xaml -> Postscript is actually a reasonable translation process, not requiring an enormous amount of code.

    We know the PS solution isn't going to work for everyone, but it rose to the top of the options when you considered those three points above.

    I'm working to get a little PS detector app out so you can see if your printers support PS or not. That will at least provide a helpful data point for you.

    Pete
  5. Petesays:
    @Kuben

    Silverlight 5. I'd expect it nearer the end of the year. Thanks for the enthusiasm :)

    The beta is out now with bitmap printing, so you can start development now if you'd like; the API is identical except for a couple small additions.

    Pete
  6. Thomas H. Laniersays:
    Maybe another option to have considered would have been a PDF generator. Almost everybody can view/print a PDF file. I have many customers who wouldn't have a clue how to install a PostScript printer driver for their printer.

    I've always wanted someone to explain to me why it seems that every new version of Windows has required a new printer driver. The installation of printer drivers in Windows has also seemed way too complicated. Also, how does the normal customer decide whether to install a PCL 5, PCL 6, or PostScript driver? Do most people know the difference or why one is better than the other? Another question is why it takes a 100MB plus download to install the driver? Many times when you buy a new printer, the driver that comes with the printer is buggy. The user then has to know to go on-line and download a new version. They then have to know what version of Windows they have, whether it's 32 bits or 64 bits, and what type of driver to pick. For instance, HP will have both printer specific drivers and "Universal Print Drivers". How does the customer choose? Many users have multiple printers. Some are local and some are on the network. Installing printer drivers for network printers can be a nightmare. Do you select to install it as a local printer or a network printer? Surprisingly, many network printers are installed by selecting to install a local printer. What port do you use? Do you really think the average user can do this?

    When a customer runs my app and they decide to print something, what are they going to think when I have to display a message "Sorry this program can't print to your printer because you've got the wrong type printer driver installed"?
  7. Thomas H. Laniersays:
    Here's an interesting fact I just read that favors the idea of generating PDF.

    "Chrome 12 boasts a PDF reader as well, so you don't have to worry about installing any Adobe plugins for viewing specialized Web content. When you load a PDF, an intuitive toolbar shows when your mouse cursor is in the southeast vicinity of the browser window. From this, you can have the document fill the width of the window, show a full page, or zoom in and out. By default, you can select text for cutting and pasting, but I couldn't copy and paste images. You can print the PDF as you would any Web page."
  8. Martinsays:
    Hi Pete,
    I am confused. Flash seems to print well no matter what type of driver a printer uses, the Flash runtime is small and it is cross-platform too. Why is this not possible with Silverlight?
    Thanks, Martin
  9. Petesays:
    @All

    Maybe I was't clear. Even without a PostScript driver, printing works. If you have a PostScript driver, printing works *faster*

    I do agree that a completely transparent approach (with no driver dependency) would have been a nicer approach. The team just didn't have time for something like that.

    Flash *appears* to use the same approach:
    http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/printing/PrintJob.html
    "The Flash Player printing feature supports PostScript and non-PostScript printers. Non-PostScript printers convert vectors to bitmaps."

    Pete
  10. Andrewsays:
    On silverlight 4, when we want to print, printer dialog is a MUST :(
    some silverlight's developer complain, and the answer was because by design
    which i think its flawed design

    is on silverlight 5, we can skip the printer dialog and print it programmatically ?
    some apps, like kiosk or pos solution, need not user to select what printer they need to choose to print...

    some solution, we can print use default printer,
    we can query available printer, is user have printer or not
  11. Noematasays:
    If runtime size considerations are a concern, why not allow for a staggered runtime download scenario so that those that need high quality printing could opt in for the needed extras.

    Given the availability of high bandwidth internet connections, runtime size considerations are quickly becoming a non-issue.

    XPS is a cool technology, it really should be broadly supported and encouraged by Microsoft (I've made the switch away from PDF). Having cross platform XPS support would also go a long way to encouraging XPS adoption.

    XPS is supprisingly accessable. I've had more luck with homebrew generation of XPS than either postscript or PDF.
  12. Petesays:
    @Andrew

    I answered on twitter. As far as I know, there have been no changes from the SL4 permissions model here

    @Noemata

    Sounds like a vote for XPS. :) I'll pass that to the team.

    In reality, it's runtime size plus the effort required (the code for those other platforms has to be written/tested). It just couldn't fit into this release.
  13. Paulussays:
    For your information,

    I just printed a 4 page diagram+datagrid combination produced using WPF+XPS.

    45 sec. later, on a HP LaserJet 1010 Series printer antiquity connected to a 5 year old Windows XP SP3 1.5 GHz machine the result was available and showed an unbelievable crisp output even on the fine print as well as impeccable gray scale output.

    So, thumbs up for XPS ! Paulus
  14. teeksays:
    "As far as I know, there have been no changes from the SL4 permissions model here"

    this doesnt sound good to me. and means that we have to build something to work around this. As our application will print out a lot of documents, and the user wants to choose once in a config where each document-type goes. Not at every printout. They really count clicks in applications. Means that the print dialog will not be accepted.
    Do i really have to use something like google cloud print?! or even write an own implementation of it (if I do not want to or when I am not allowed to host my documents in the cloud?!)
  15. DrPizzasays:
    > I'll need to check with the team more. From what they've told me, PostScript drivers are very common. I'm not seeing that in my *very limited* research.

    They are very common, but that doesn't mean that most people have them. Professional office lasers are PostScript, but the inkjet you got at home sure ain't.

    Just use XPS. As a bonus you can spool XPS to a file and it's actually useful (because the built-in XPS viewer is quite good) whereas PostScript is worthless--no built-in viewer, no usable third-party viewer (seriously, gsview? No thanks). Even PDF requires third-party software.

    Really it blows my mind that Microsoft would pick PostScript instead of XPS. XPS works, and well. Just how big an impact on download size are we talking here?
  16. Dennysays:
    Looks more and more to me like WPF and SIlverlight will at some point merge.

    I have seen and read Micosoft news that tends to say "Silverlight on windows phones and desktop"
    Silverlight on Mac at one time was going to get hooks to do automation / scripting but that has not happened.
    MS has been crtying about how great HTML 5 will be, and no news on MS porting Silverlight to any new devices or OS's.

    this printing stuff is just sad..... weak and not any real use in developing an app for comercial sales.

    so in another 3-5 years where will silverlight be? GONE! too limited, no other platforms and left by the side of the road.
    Sad but true i think.
  17. Satyadevsays:
    We built a complected report that has styled DataGrid with several data templates in side it and could print pages ranging from 2 to 20.

    With SL4 after a printing few times(each time it prints all pages) we are getting all blank pages and we need to restart the SL client to get the printing going again.

    We are currently evaluating SL5 Beta on post script enabled printer but we are seeing

    "limitcheck/OFFENDING COMMAND: gsave" in the second page (printed by the printer itself)

    after printing few rows in the first page in a multi page scenario.

    One thing I noticed is that printing is faster with SL5, but not even printing two pages.

    This is really frustrating me a lot. Do you have any insight into it?

    Thanks in advance
  18. Scottsays:
    Thanks for the update Pete, its very promising to see that printing is not being taken lightly by the silverlight teams. I have a client that is using Printing with Silverlight 4 for QR Code labels and sheets, it produces beautiful high quality labels but it is rather slow due to large sizes.

    I am a full time network engineer, and just my 2 cents, but all of our devices DO in fact include PostScript capabilities anymore at no extra charge, but we do have to specifically install a new printer using the PostScript driver which I typically never do because our other applications require features not supported by it.

    Thanks for the tutorial!
  19. Niels Gebauersays:
    Pete,

    Have you tried David Poll's CollectionPrinter with Silverlight 5 RC?

    The reason for the question is, I have created a Document Print Control based on some of the priciples of your Collection Printer. It works fine with Silverlight 4, however, when I use the Silverlight 5 RC release, I am having problems.
    1) Images are not shown on the pages
    2) When I set HasMorePages to false, it continues to call the PrintPage event 9 times.

    I then tried David's Collection Printer with SL 5RC and it also does not show the images.

    Are there know problems with SL 5 RC printing?

    Thanks
    Niels
  20. orchard themessays:
    I've always had weird issues with XPS printing (in WPF) wanting to rasterize sometimes and not other times... printing to a pdf file will sometimes result in nice vectored output, unless something is done not quire perfectly and then either a whole page or even the whole document becomes one big ugly raster. This has actually been the most frustrating part of WPF dev I've experienced. Wonder if things are better with this now.

    I'm guessing if we PS print, to a pdf printer (acrobat for example), we should consistently get nice vector output. Might be time to move the wpf app over to silverlight now.
  21. Joshsays:
    I did a lot of research on Silverlight 4 before I selected it as the platform upon which I would build the next generation product for my company. Had I known about the limitations of printing from Silverlight at that time, I would never have chosen Silverlight. I just can't believe more people aren't screaming bloody murder about the shoddy printing functionality of Silverlight. There is no way that SL can be taken seriously as a LOB development platform with these limitations. This limitation ought to be presented up front so that developers can make an educated decision, but it isn't talked about anywhere officially- at least I never encountered it in dozens of hours spent researching SL. Unfortunately it wasn't until I was well into the project that I encountered these shortcomings. My project routinely generates large reports, in excess of 20 pages. These reports are literally UNPRINTABLE because of the size; they cause the printer spooler to crash and do not print, or if they do print it takes around 20 or 30 minutes. As this is a product that will be distributed to thousands of home and business users, there is no way that we can control what kind of printer or driver the users have. I was very hopeful that SL5 was going to help with this problem, but the approach they took makes no sense as it really doesn't help most users, only those with special PS printers, which despite the assertion from MS, are in no way common at all, it's like 1% of printers that people use, maybe a little higher if only considering mac users.

    At any rate, once again I am left utterly disappointed in MS and the SL team for failing to adequately address this incredibly serious shortcoming. I wish I could file a class action suit against MS for the 12 months of development that were wasted building a project that in the end is unusable because it cannot print! I think that they have intentionally hidden this shortcoming so as to trick more developers into selecting SL as their LOB development platform.

    For now my only option has been to disable any ability to print from my app, and instead force users to save reports as PDF and then open and print directly. I've also been toying with displaying the PDFs natively in acrobat by saving the PDFs on the server and then using a hyperlinkbutton to open the PDF directly in a new window, which takes it out of Silverlight and into the browser. This approach so far seems to work, both in browser and OOB, Mac and PC, although there are some annoyances in this approach such as popup blockers getting in the way, and in FF on Mac it automatically downloads the PDF to the users desktop before displaying it. But at least with this approach the reports can be printed.

    Hopefully they come up with something better in SL6, but who knows how long we'll be waiting to find out, and probably end up being just as disappointed in whatever half-assed solution they come up with. :(

  22. Chandan Deysays:
    Hi Pete,
    Its really useful.
    Now I have a requirement in my reports. I want to print data in header.
    Like ......
    ABC Summary Report Page 1
    Summary Report For : Accounts
    Summary Report From : 1/April/2013 To : 4/April/2013

    ABC Summary Report Page 2
    Summary Report For : Accounts
    Summary Report From : 1/April/2013 To : 4/April/2013

    etc....

    I was try to achieve this by add an event handler as like BeginBuildReportFooter ...... but failed to success
    Report.BeginBuildReportHeader += (s, e) =>
    {
    // set the running total as the context for the report footer
    e.DataContext = _totals;
    };

    Thanks,
    Chandan

Comment on this Post

Remember me