This post is about
printing XPS-files using C# and the quality issues you can have if your printer
doesn’t support direct printing of XPS-files.
Let’s assume we created and saved a XPS-file and now we’d like to print this file using Visual C#. Based on http://msdn.microsoft.com/en-us/library/ms742418(v=vs.110).aspx this approach should work:
System.Windows.Xps.Packaging.XpsDocument xpsdocument
= new System.Windows.Xps.Packaging.XpsDocument(
filename,
System.IO.FileAccess.Read);
PrintServer printserver
= new PrintServer(printservername);
PrintQueue printqueue
= printserver.GetPrintQueue(printqueuename);
PrintTicket printticket
= printqueue.DefaultPrintTicket;
System.Windows.Xps.XpsDocumentWriter xpsdocumentwriter
= PrintQueue.CreateXpsDocumentWriter(printqueue);
xpsdocumentwriter.Write(
xpsdocument.GetFixedDocumentSequence());
But there can be quality-issues depending on printqueue.IsXpsDevice
(printer supports direct XPS-printing): e.g. bad image quality (96 DPI) or wrong fonts.
There can be found many discussions when searching the web, but the most interesting point is, that there are no such issues when printing a XPS-file using Windows XPS-Viewer.
Based on https://gist.github.com/bgrainger/1541424 printing works fine using this approach:
[System.Runtime.InteropServices.ComImport,
[System.Runtime.InteropServices.ComImport,
[return: System.Runtime.InteropServices.MarshalAs(
void CreatePackageFromStream();
[System.Runtime.InteropServices.ComImport,
void WriteToStream(
[System.Runtime.InteropServices.ComImport,Let’s assume we created and saved a XPS-file and now we’d like to print this file using Visual C#. Based on http://msdn.microsoft.com/en-us/library/ms742418(v=vs.110).aspx this approach should work:
System.Windows.Xps.Packaging.XpsDocument xpsdocument
= new System.Windows.Xps.Packaging.XpsDocument(
filename,
System.IO.FileAccess.Read);
PrintServer printserver
= new PrintServer(printservername);
PrintQueue printqueue
= printserver.GetPrintQueue(printqueuename);
PrintTicket printticket
= printqueue.DefaultPrintTicket;
System.Windows.Xps.XpsDocumentWriter xpsdocumentwriter
= PrintQueue.CreateXpsDocumentWriter(printqueue);
xpsdocumentwriter.Write(
xpsdocument.GetFixedDocumentSequence());
But there can be quality-issues depending on printqueue.IsXpsDevice
(printer supports direct XPS-printing): e.g. bad image quality (96 DPI) or wrong fonts.
There can be found many discussions when searching the web, but the most interesting point is, that there are no such issues when printing a XPS-file using Windows XPS-Viewer.
Based on https://gist.github.com/bgrainger/1541424 printing works fine using this approach:
// taken from https://gist.github.com/bgrainger/1541424
internal static class NativeMethods
{
[System.Runtime.InteropServices.DllImport(
"XpsPrint.dll",
ExactSpelling = true,
CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
public static extern int StartXpsPrintJob(
string printerName,
string jobName,
string outputFileName,
IntPtr progressEvent,
Microsoft.Win32.SafeHandles.SafeWaitHandle
completionEvent,
[System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPArray)]
byte[] printablePagesOn,
int printablePagesOnCount,
out IXpsPrintJob xpsPrintJob,
out IXpsPrintJobStream documentStream,
out IXpsPrintJobStream printTicketStream);
}
System.Runtime.InteropServices.Guid(
"E974D26D-3D9B-4D47-88CC-3872F2DC3585"),
System.Runtime.InteropServices.ClassInterface(
System.Runtime.InteropServices.ClassInterfaceType.None)]
internal class XpsOMObjectFactory { }
System.Runtime.InteropServices.Guid(
"F9B2A685-A50D-4FC2-B764-B56E093EA0CA"),
System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
internal interface IXpsOMObjectFactory
{
void CreatePackage();
System.Runtime.InteropServices.UnmanagedType.Interface)]
IXpsOMPackage CreatePackageFromFile(
[System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPWStr)]
string filename,
bool reuseObjects);
void CreateStoryFragmentsResource();
void CreateDocumentStructureResource();
void CreateSignatureBlockResource();
void CreateRemoteDictionaryResource();
void CreateRemoteDictionaryResourceFromStream();
void CreatePartResources();
void CreateDocumentSequence();
void CreateDocument();
void CreatePageReference();
void CreatePage();
void CreatePageFromStream();
void CreateCanvas();
void CreateGlyphs();
void CreatePath();
void CreateGeometry();
void CreateGeometryFigure();
void CreateMatrixTransform();
void CreateSolidColorBrush();
void CreateColorProfileResource();
void CreateImageBrush();
void CreateVisualBrush();
void CreateImageResource();
void CreatePrintTicketResource();
void CreateFontResource();
void CreateGradientStop();
void CreateLinearGradientBrush();
void CreateRadialGradientBrush();
void CreateCoreProperties();
void CreateDictionary();
void CreatePartUriCollection();
void CreatePackageWriterOnFile();
void CreatePackageWriterOnStream();
void CreatePartUri();
void CreateReadOnlyStreamOnFile();
}
System.Runtime.InteropServices.Guid(
"18C3DF65-81E1-4674-91DC-FC452F5A416F"),
System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
internal interface IXpsOMPackage
{
void GetDocumentSequence();
void SetDocumentSequence();
void GetCoreProperties();
void SetCoreProperties();
void GetDiscardControlPartName();
void SetDiscardControlPartName();
void GetThumbnailResource();
void SetThumbnailResource();
void WriteToFile();
IXpsPrintJobStream stream,
bool optimizeMarkupSize);
};
//
NOTE: It appears that the IID for IXpsPrintJobStream specified in XpsPrint.h
--
//
MIDL_INTERFACE("7a77dc5f-45d6-4dff-9307-d8cb846347ca") -- is not
correct, or the object
//
doesn't implement QueryInterface correctly. However, we can QI for
ISequentialStream and
//
successfully (at least in Windows 7 SP1 x86) call the Close method as if it
existed on that
//
interface.
//
That is, we obtain the ISequentialStream interface, but work with it as the
IXpsPrintJobStream interface.
//
Thanks to
http://stackoverflow.com/questions/6123507/xps-printing-from-windows-service
for this tip.
System.Runtime.InteropServices.Guid(
"0C733A30-2A1C-11CE-ADE5-00AA0044773D"),
System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
internal interface IXpsPrintJobStream
{
// ISequentialStream methods
void Read(
[System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPArray)]
byte[] pv,
uint cb,
out uint pcbRead);
void Write(
[System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPArray)]
byte[] pv,
uint cb,
out uint pcbWritten);
//
IXpsPrintJobStream methods
void Close();
}
[System.Runtime.InteropServices.ComImport,
System.Runtime.InteropServices.Guid(
"5AB89B06-8194-425F-AB3B-D7A96E350161"),
System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
internal interface IXpsPrintJob
{
void Cancel();
IntPtr GetJobStatus();
};
"0C733A30-2A1C-11CE-ADE5-00AA0044773D"),
System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
internal interface IXpsPrintJobStream
{
// ISequentialStream methods
void Read(
[System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPArray)]
byte[] pv,
uint cb,
out uint pcbRead);
void Write(
[System.Runtime.InteropServices.MarshalAs(
System.Runtime.InteropServices.UnmanagedType.LPArray)]
byte[] pv,
uint cb,
out uint pcbWritten);
void Close();
}
System.Runtime.InteropServices.Guid(
"5AB89B06-8194-425F-AB3B-D7A96E350161"),
System.Runtime.InteropServices.InterfaceType(
System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
internal interface IXpsPrintJob
{
void Cancel();
IntPtr GetJobStatus();
};
{
public void Print(
string filename,
string printservername,
string printqueuename)
{
PrintServer printserver
= new PrintServer(printservername);
PrintQueue printqueue
=
printserver.GetPrintQueue(printqueuename);
PrintTicket printticket
= printqueue.DefaultPrintTicket;
if (printqueue.IsXpsDevice)
{
System.Windows.Xps.XpsDocumentWriter xpsdocumentwriter
System.Windows.Xps.XpsDocumentWriter xpsdocumentwriter
= PrintQueue.CreateXpsDocumentWriter(printqueue);
xpsdocumentwriter.Write(
new System.Windows.Xps.Packaging.XpsDocument(
filename,
System.IO.FileAccess.Read).GetFixedDocumentSequence());
filename,
System.IO.FileAccess.Read).GetFixedDocumentSequence());
}
else
this.PrintExtern(
filename,
printqueuename,
printqueue.DefaultPrintTicket);
}
// taken from https://gist.github.com/bgrainger/1541424
///
/// Prints the specified XPS document to a printer using the native XPS Print API.
/// Prints the specified XPS document to a printer using the native XPS Print API.
///
/// The path to the XPS document.
/// The printer name.
/// A PrintTicket with settings for this print job.
/// true
if the document was successfully
printed; otherwise,
/// This method should be called
from a background thread.
public bool PrintExtern(
string xpsFilePath, string printerName, PrintTicket printTicket)
string xpsFilePath, string printerName, PrintTicket printTicket)
{
//
try to create the XPS Object Model factory (only available on Windows 7 and
Vista with the Platform Update)
IXpsOMObjectFactory xpsFactory = null;
try
{
xpsFactory
= (IXpsOMObjectFactory)new XpsOMObjectFactory();
= (IXpsOMObjectFactory)new XpsOMObjectFactory();
}
catch
(System.Runtime.InteropServices.COMException)
{
// OS doesn't support XPS Document API
return false;
}
bool success = false;
IXpsOMPackage package = null;
try
{
// load the saved document as a native XpsOMPackage
package
= xpsFactory.CreatePackageFromFile(
xpsFilePath,
false);
= xpsFactory.CreatePackageFromFile(
xpsFilePath,
false);
using (ManualResetEvent handle
= new ManualResetEvent(false))
= new ManualResetEvent(false))
{
// attempt to start the print job
IXpsPrintJob printJob;
IXpsPrintJobStream
docStream,
ticketStream;
docStream,
ticketStream;
int hresult
= NativeMethods.StartXpsPrintJob(
printerName,
0 "somename",
null,
IntPtr.Zero,
handle.SafeWaitHandle,
null, 0, out printJob, out docStream, out ticketStream);
= NativeMethods.StartXpsPrintJob(
printerName,
0 "somename",
null,
IntPtr.Zero,
handle.SafeWaitHandle,
null, 0, out printJob, out docStream, out ticketStream);
// check for success (NOTE:
checking HRESULT value directly instead of calling Marshal.ThrowExceptionForHR
to avoid proliferation of 'catch' blocks)
if (hresult >= 0)
{
// write
the current printer settings to the print ticket stream
byte[] ticketData =
printTicket.GetXmlStream().ToArray();
uint bytesWritten;
ticketStream.Write(ticketData, (uint)ticketData.Length, out bytesWritten);
ticketStream.Close();
// write the XPS package to the
document stream
package.WriteToStream(docStream, false);
docStream.Close();
// wait for printing to finish
handle.WaitOne();
success = true;
}
}
}
catch
(System.Runtime.InteropServices.COMException)
{
// printing failed
}
catch (DllNotFoundException)
{
// OS doesn't support XPS Print API
}
catch (EntryPointNotFoundException)
{
// OS doesn't support XPS Print API
}
// force the XPS package to be released, so that the
temporary file can be deleted
if (package != null)
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(package);
return
success;
}
// END taken from https://gist.github.com/bgrainger/1541424
}
The code is kind of “quick
and dirty” but works, at least when using Windows 7.