Convert XPS files into PNG files in C#

[Convert XPS files]

This example lets you convert XPS files into PNG files. Note that it only seems to work with XPS (XMP Paper Specification) files and not OXPS (Open XMP Paper Specification) files.

The example does three interesting things: convert XPS files into PNG files, display PNG files in a TabControl, and scale images.

Convert XPS Files into PNG Files

The example uses the following XpsToPng method to convert XPS files into PNG files.

// Convert an XPS file into a PNG file.
public int XpsToPng(string xps_file)
{
    // Make sure this is an xps file.
    if (!xps_file.ToLower().EndsWith(".xps"))
        throw new ArgumentException(
            "Method XpsToPng only works for .xps files.");

    // Get the file's name without the .xps on the end.
    string file_prefix =
        xps_file.Substring(0, xps_file.Length - 4);

    // Load the XPS document.
    XpsDocument xps_doc =
        new XpsDocument(xps_file, FileAccess.Read);

    // Get a fixed paginator for the document.
    IDocumentPaginatorSource page_source =
        xps_doc.GetFixedDocumentSequence();
    DocumentPaginator paginator =
        page_source.DocumentPaginator;

    // Process the document's pages.
    int num_pages = paginator.PageCount;
    for (int i = 0; i < num_pages; i++)
    {
        using (DocumentPage page = paginator.GetPage(i))
        {
            // Render the page into the memory stream.
            int width = (int)page.Size.Width;
            int height = (int)page.Size.Height;
            RenderTargetBitmap bitmap =
                new RenderTargetBitmap(
                    width, height, 96, 96,
                    PixelFormats.Default);
            bitmap.Render(page.Visual);

            // Save the PNG file.
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(bitmap));

            using (MemoryStream stream = new MemoryStream())
            {
                encoder.Save(stream);

                using (FileStream file = new FileStream(
                    file_prefix + (i + 1).ToString() + ".png",
                    FileMode.Create))
                {
                    file.Write(stream.GetBuffer(), 0,
                        (int)stream.Length);
                    file.Close();
                }
            }
        }
    }
    return num_pages;
}

This method checks that the input file's name ends with ".xps." It then finds the file name without the extension so it can use it later to generate the names of the output PNG files.

Next the method creates an XpsDocument representing the XPS file. It uses the document's GetFixedDocumentSequence method to get a paginator source and then uses that object's DocumentPaginator property to get a paginator for the document.

A paginator is an object that can produce the pages that should be displayed for the document. Because XPS is a fixed layout specification (as opposed to HTML and other formats that can flow across pages depending on the page sizes), the paginator produces pages with fixed sizes and the text and other items in fixed locations.

The method next gets the page count from the paginator and loops through the page numbers.

For each page number, the method calls the paginator's GetPage method. It then creates a RenderTargetBitmap the same size as the page and makes it render the page's visual into itself.

At this point the program has the image of the page and could display it in an Image control. For this example I decided to keep the creation of the PNG files and their display separate. That way you could use the XpsToPng method to convert XPS files even if you don't want to immediately display them.

Now the method creates a PngBitmapEncoder and adds the bitmap to it. It then makes a MemoryStream and makes the encoder save its data into the stream.

Finally the method creates a FileStream and writes the MemoryStream into it, thus creating the PNG file. (If this seems a bit backwards, I agree. That's the way WPF is sometimes.)

The XpsToPng method finishes by returning the number of pages it converted into PNG files.

Display PNG Files in a TabControl

The main program saves the number of pages converted and then calls the following DisplayPngFiles method to show the PNG files.

// Display the converted png files in the TabControl.
private void DisplayPngFiles(string xps_file, int num_pages)
{
    string file_prefix =
        xps_file.Substring(0, xps_file.Length - 4);

    tabResults.Items.Clear();
    for (int i = 1; i <= num_pages; i++)
    {
        // Load the png file into an Image control.
        Image image = new Image();
        image.Source = new BitmapImage(
            new Uri(file_prefix + i.ToString() + ".png",
                UriKind.Absolute));

        // Load the Image into a ScrollViewer.
        ScrollViewer viewer = new ScrollViewer();
        viewer.Background = Brushes.Black;
        viewer.Content = image;
        viewer.VerticalScrollBarVisibility =
            ScrollBarVisibility.Auto;
        viewer.HorizontalScrollBarVisibility =
            ScrollBarVisibility.Auto;

        // Make a TabItem to display the ScrollViewer.
        TabItem tab_item = new TabItem();
        tabResults.Items.Add(tab_item);
        tab_item.Header = "Page " + i.ToString();
        tab_item.Content = viewer;
    }
}

This method clears the items in the program's TabControl and loops through the page numbers for the PNG files it created.

For each file the method creates an Image control. It makes a Uri holding the file's name, uses it to create a BitmapImage, and displays it in the Image control.

Next the method creates a ScrollViewer and places the Image control inside it.

Finally the method creates a new TabItem and adds it to the TabControl. It sets the item's Header property to show the page number and sets the item's Content to the ScrollViewer.

Scale Images

Whenever you change the selected scale or load a new XPS file, the program calls the following SizeImages method.

// The current scale.
private double ImageScale = 1;
...
// Set the appropriate image sizes.
private void SizeImages()
{
    if (tabResults == null) return;

    foreach (TabItem tab_item in tabResults.Items)
    {
        ScrollViewer viewer = tab_item.Content as ScrollViewer;
        Image image = viewer.Content as Image;
        image.Width = image.Source.Width * ImageScale;
        image.Height = image.Source.Height * ImageScale;
    }
}

This method loops through the tabs in the TabControl. It converts each tab's content object into a ScrollViewer and then converts the ScrollViewer's content object into an Image control. The method then sets the Image control's size to be the same size as the image it contains multiplied by the current scale factor.

The Image controls' Stretch properties default to Uniform so they stretch their images to make them as large as possible while still fitting. The ScrollViewer controls then provide scroll bars if needed to let the user see the Image controls.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.

This entry was posted in files, graphics, system and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *