Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF image vector format export (XPS?)

Our tool allows export to PNG, which works very nicely. Now, I would like to add export to some vector format. I tried XPS, but the results are not satisfying at all.

Take a look at a comparison http://www.jakubmaly.cz/xps-vs-png.png. The picture on the left comes from an XPS export, the picture on the right from PNG export, the XPS picture is visibly blurred when opened in XPS Viewer and zoomed 100%.

Are there any settings that I am missing or why is it so?

Thanks, Jakub.

A sample xps output can be found here: http://www.jakubmaly.cz/files/a.xps. This is the code that does the XPS export:

if (!boundingRectangle.HasValue)
{
    boundingRectangle = new Rect(0, 0, frameworkElement.ActualWidth, frameworkElement.ActualHeight);
}

// Save current canvas transorm
Transform transform = frameworkElement.LayoutTransform;
// Temporarily reset the layout transform before saving
frameworkElement.LayoutTransform = null;


// Get the size of the canvas
Size size = new Size(boundingRectangle.Value.Width, boundingRectangle.Value.Height);
// Measure and arrange elements
frameworkElement.Measure(size);
frameworkElement.Arrange(new Rect(size));

// Open new package
System.IO.Packaging.Package package = System.IO.Packaging.Package.Open(filename, FileMode.Create);
// Create new xps document based on the package opened
XpsDocument doc = new XpsDocument(package);
// Create an instance of XpsDocumentWriter for the document
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
// Write the canvas (as Visual) to the document
writer.Write(frameworkElement);
// Close document
doc.Close();
// Close package
package.Close();

// Restore previously saved layout
frameworkElement.LayoutTransform = transform;
like image 675
j_maly Avatar asked Jun 27 '11 08:06

j_maly


2 Answers

Interesting (and annoying) issue - you may want to check out the lengthy answer from Jo0815 to Printing XpsDocument causes resampled images (96dpi?) - FixedDocument prints sharp, quoting a Microsoft support response - a couple of excerpts:

Some vector features from WPF cannot be emulated in our GDI code and we resort to converting subsets of the scene to GDI bitmaps. These bitmaps are the cause of the blurred zooming.

[...]

These bitmaps are the cause of the blurred zooming. The problem is that the WPF is being rasterised to a bitmap at the -wrong resolution. The print path is designed to rasterise unsupported features into a bitmap, but it is supposed to do it at device resolution. Instead the rasterisation is always being done at 96dpi. That's fine for a screen but produces blurred output for a 600dpi printer. [emphasis mine]

Please note that the latter will apply for nowadays higher DPI screens as well of course, I've encountered blurring like this various times already - do you by chance use a high DPI monitor?

Now, apparently Microsoft is not entirely in control of the apparatus regarding this:

Additionally the problem only occurs when printing XPS and isn't a problem when printing XAML directly. I'm pretty sure there is documentation somewhere that says XPS will print at device resolution. [...] It is something we plan to improve in the next version of the product but not for Win 7. The problem is that when printing XAML it will correctly render the image at 600dpi, but when printing XPS it will still render the image at 96dpi. Since XAML is converted to XPS before printing it seems highly odd that one method of printing XPS produces different results to another method of printing XPS. [emphasis mine]

[...]

There is no UI to configure the XPS Document Writer DPI. If you later print a generated XPS document at a different DPI from the writers internal default you may get poor results for bitmap content. With GDI printers you can control the final DPI and your final desitination is usally paper - no chance to reprint the document.

Conclusion

In conclusion, I'd still try to adjust PrintTicket.PageResolution Property within Néstor Sánchez' approach (+1), if your use case does allow this (though I remotely recall reading somewhere, that this doesn't have any effect as well); section Bitmap Resolution and Pixel Format in Using the XPS Rasterization Service confirms the issue he encountered with FixedDocument:

XPS rasterizer object for a fixed page must know the resolution at which the page will be rendered. The XPSDrv filter specifies this resolution, in dots per inch (DPI), as an input parameter [...] For example, if a display device has a resolution of 600 DPI, and a fixed page describes a standard letter-size page, a bitmap image of the entire page has the following dimensions [...]

Workaround

As a potential workaround you might want to explore alexandrud's solution for the related question How to convert a XPS file to an image in high quality (rather than blurry low resolution)?, which recommends using xps2img, a XPS (XML Paper Specification) document to set of images conversion utility. In particular it Allows to specify images size or DPI, which might help depending on the print path solution applied in turn.

Good luck!

like image 66
Steffen Opel Avatar answered Sep 23 '22 08:09

Steffen Opel


I've had a similar problem. My image was very blurry when passed to XPS intermediated thru a FixedDocument. The solution was to write the image directly to the XPS...

/// <summary>
/// Saves the supplied visual Source, within the specified Bounds, as XPS in the specified File-Name.
/// Returns error message or null when succeeded.
/// </summary>
public static string SaveVisualAsXPS(Visual Source, Size Bounds, string FileName)
{
    string ErrorMessage = null;

    try
    {
        using (var Container = Package.Open(FileName, FileMode.Create))
        {
            using (var TargetDocument = new XpsDocument(Container, CompressionOption.Maximum))
            {
                var Writer = XpsDocument.CreateXpsDocumentWriter(TargetDocument);
                var Ticket = GetPrintTicketFromPrinter();
                if (Ticket == null)
                    return "No printer is defined.";

                Ticket.PageMediaSize = new PageMediaSize(Bounds.Width, Bounds.Height);
                var SourceVisual = Source;
                Writer.Write(SourceVisual, Ticket);
            }
        }
    }
    catch (Exception Problem)
    {
        ErrorMessage = "Cannot export document to XPS.\nProblem: " + Problem.Message;
    }

    return ErrorMessage;
}

Giving a print-ticket with the exact width and height avoids scaling (that was I wanted in my case). Get the function from the example in: http://msdn.microsoft.com/en-us/library/system.printing.printticket.aspx

like image 2
Néstor Sánchez A. Avatar answered Sep 22 '22 08:09

Néstor Sánchez A.