Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / Windows Forms
Article

Output graphics files using your printing code

Rate me:
Please Sign up or sign in to vote.
4.88/5 (25 votes)
21 Mar 2005CPOL3 min read 116K   3.2K   56   17
"Print" your PrintDocument to common graphics file formats.

PrintControllerFileDemo

Introduction

Would you like your application to support saving to different graphics file formats? Provided you have implemented Print Preview, it turns out to be easier than you think. This is a small article based around the GDI+ Image.Save method.

Using the code

Using the code could not be easier.

Just add the PrintControllerFile.cs file to your project, and use it like so:

C#
using ( PrintDocument doc = new MyPrintDocument( ... ) )
{
    ImageFormat format = ImageFormat.Jpeg;
    float scale = 1f;
    long quality = 75L;
    string output = "Output";

    PrintController controller = 
      new PrintControllerFile( format, scale, quality, output );
    
    doc.PrintController = 
      new PrintControllerWithStatusDialog( controller, "Exporting" );

    doc.Print();
}

The parameters of the PrintControllerFile constructor are:

  • format: One of the static formats in the ImageFormat class.
  • scale: A simple scaling ratio to control the size of the output.
  • quality: Only used by the JPEG encoder. Default is about 75.
  • output: The base filepath of the output files.

Et voila! The output is a collection of graphics files, one for each page.

How it works

Basically, PrintControllerFile uses PreviewPrintController to generate Metafiles, and then saves these as the output.

This method has the advantage that the output will always look the same as in your Print Preview.

It also saves us a lot of work. If you look at PreviewPrintController in Reflector, you will find that it sets up a Graphics object for each page using various GDI+ methods. We want our output to be identical to the Print Preview, so it makes sense to let PreviewPrintController do this work.

So PrintControllerFile derives from PreviewPrintController, and overrides the OnEndPage method to save each page as it is generated. I could have let PreviewPrintController generate all the pages, and then save them, but I wanted to save each page as soon as it is generated so that if you cancel the print, then you get the output up to that point.

C#
internal class PrintControllerFile : PreviewPrintController
{
    ...
    
    public override void OnEndPage( PrintDocument document, PrintPageEventArgs e )
    {
        base.OnEndPage( document, e );

        // Get the current Metafile
        PreviewPageInfo[] ppia = GetPreviewPageInfo();
        PreviewPageInfo ppi = ppia[ ppia.Length - 1 ];
        Image image = ppi.Image;
        _Metafile = ( Metafile ) image;

        if ( _Format == ImageFormat.Emf )
          { _Metafile.Save( PagePath, _Format ); return; }
        if ( _Format == ImageFormat.Wmf )
          { _Metafile.Save( PagePath, _Format ); return; }

        SaveViaBitmap( document, e );
    }
    
    ...
}

Saving the output for the Enhanced Metafile and Windows Metafile formats is as easy as calling Save on the Metafile instance with the required format.

However, this method doesn't work for the other graphics formats because you have no control over the output. For example, saving a standard page as a bitmap results in a file over 100MB in size. Obviously, this is not what is required.

The solution is to create a Bitmap image with the required properties, play the Metafile onto it, and then save the Bitmap. This gives us the control over the output that we require.

C#
protected void SaveViaBitmap( PrintDocument document, PrintPageEventArgs e )
{
    int width  = e.PageBounds.Width  ;
    int height = e.PageBounds.Height ;

    using ( Bitmap bitmap = new Bitmap( ( int ) ( width * _Scale ),
                                      ( int ) ( height * _Scale ) ) )
    using ( Graphics graphics = Graphics.FromImage( bitmap ) )
    {
        graphics.Clear( Color.White );

        if ( _Scale != 1 ) graphics.ScaleTransform( _Scale, _Scale );

        Point point = new Point( 0, 0 );
        Graphics.EnumerateMetafileProc callback =
          new Graphics.EnumerateMetafileProc( PlayRecord );

        graphics.EnumerateMetafile( _Metafile, point, callback );

        Save( bitmap );
    }
}

The only parameter I needed to setup the Bitmap object was a scale parameter. The size of the output is set by multiplying the PageBounds of the PrintPageEventArgs by the scale parameter. Actually, you normally want the output to be the standard size, but values of scale from about 0.5 to 2.0 might be useful. If a scale is set, then a ScaleTransform is applied to the Graphics object, so your printing code doesn't have to be modified.

Creating a Graphics object from the Bitmap and playing the metafile onto it is then just boiler-plate stuff.

Saving the Bitmap in the required format is then simple, apart from for the JPEG format, which requires a Quality parameter to be set for the encoder. This value must be in the range from 0 (highest compression, lowest quality), to 100 (lowest compression, highest quality). The default value seems to be about 75.

C#
protected void Save( Bitmap bitmap )
{
    if ( _Format == ImageFormat.Jpeg )
    {
        EncoderParameters parameters = new EncoderParameters( 1 );
        EncoderParameter parameter =
          new EncoderParameter( Encoder.Quality, _Quality );
        parameters.Param[ 0 ] = parameter;

        bitmap.Save( PagePath, _Codec, parameters );
        return;
    }

    bitmap.Save( PagePath, _Format );
}

The main file, PrintControllerFile.cs, is only about 200 lines long, and is fairly self-explanatory. Take a look, if you're interested.

Points of Interest

The Image.Save method is very powerful, but cannot be used directly on Metafiles. Creating an intermediary Bitmap, and then saving that, gives us the control we need over the output.

It would be nice if there was more control, but if you just want to produce graphics files which you can modify in an image editor, then this just works.

History

22nd March 2005 - Version 1.0

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
I discovered C# and .NET 1.0 Beta 1 in late 2000 and loved them immediately.
I have been writing software professionally in C# ever since

In real life, I have spent 3 years travelling abroad,
I have held a UK Private Pilots Licence for 20 years,
and I am a PADI Divemaster.

I now live near idyllic Bournemouth in England.

I can work 'virtually' anywhere!

Comments and Discussions

 
GeneralMy vote of 5 Pin
tanoshiikanchi7-Nov-19 19:36
tanoshiikanchi7-Nov-19 19:36 
QuestionVB.Net version available? Pin
Richard Sauceda8-Oct-08 5:18
Richard Sauceda8-Oct-08 5:18 
AnswerRe: VB.Net version available? Pin
Nicholas Butler8-Oct-08 5:29
sitebuilderNicholas Butler8-Oct-08 5:29 
GeneralGreat example Pin
Bill Seddon16-Nov-07 13:55
Bill Seddon16-Nov-07 13:55 
GeneralAbout VB version.. Pin
Elven Wong28-Mar-07 20:24
Elven Wong28-Mar-07 20:24 
QuestionResizing the text size of header in a listcontrol Pin
Vinod Moorkkan23-Aug-06 20:26
Vinod Moorkkan23-Aug-06 20:26 
QuestionVery Nice Solution Pin
bhalmadhukar23-Aug-06 20:12
bhalmadhukar23-Aug-06 20:12 
QuestionMetafile.Save Pin
dag@itbase.no28-Apr-06 23:38
dag@itbase.no28-Apr-06 23:38 
AnswerRe: Metafile.Save Pin
Cathy Grimes26-Jun-07 4:42
Cathy Grimes26-Jun-07 4:42 
GeneralPrinting tiny controls, when painting Pin
jjohn20059-Feb-06 21:21
jjohn20059-Feb-06 21:21 
GeneralHelp Pin
Prankrishna19-Jan-06 6:47
Prankrishna19-Jan-06 6:47 
GeneralPower of simplicity Pin
Me2d9-Dec-05 11:55
Me2d9-Dec-05 11:55 
QuestionCan i save .pcx format? Pin
massacre1-Dec-05 17:48
massacre1-Dec-05 17:48 
Can i save .pcx format?
GeneralNice solution... Pin
Robert Rohde18-May-05 5:04
Robert Rohde18-May-05 5:04 
GeneralRe: Nice solution... Pin
Nicholas Butler18-May-05 19:39
sitebuilderNicholas Butler18-May-05 19:39 
GeneralRe: Nice solution... Pin
Anonymous13-Jun-05 23:29
Anonymous13-Jun-05 23:29 
GeneralRe: Nice solution... Pin
Pete Goodsall6-Aug-05 4:45
Pete Goodsall6-Aug-05 4:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.