Print an XPS OM
Describes how to send an XPS OM to a printer as an XPS document.
For instructions on how to print an XPS OM that contains a complete XPS document, see Print a complete XPS OM. To contain an XPS document, an XPS OM must include the items listed in Create a Blank XPS OM.
For instructions on how to print an XPS OM that is being created or processed one page at a time, see Incrementally print an XPS OM.
Before using these code examples in your program, read the disclaimer in Common XPS Document Programming Tasks.
In this topic, you will learn how to perform the following tasks:
- Print a complete XPS OM
- Incrementally print an XPS OM
- Related topics
Print a complete XPS OM
When an XPS OM contains a complete XPS document, the WriteToStream method of the IXpsOMPackage interface can send the contents of the XPS OM to a printer or a print queue.
To detect when the print job has completed, create an event handle as shown in the following example.
HANDLE completionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == completionEvent)
{
hr = HRESULT_FROM_WIN32(GetLastError());
// The method can continue, but print spooling completion
// cannot be checked without a valid event handle.
}
To print a complete XPS OM:
- Create a new print job stream by calling StartXpsPrintJob.
- Send the contents of the XPS OM to the stream by calling the package's WriteToStream method.
- Close the print job stream by calling the stream's Close method.
- Wait for the print job to signal that it has completed.
- Check the completion status.
- Close and release resources.
IXpsPrintJob *job = NULL;
IXpsPrintJobStream *jobStream = NULL;
hr = StartXpsPrintJob(
printerName,
NULL,
NULL,
NULL,
completionEvent,
NULL,
0,
&job,
&jobStream,
NULL);
// Write package to print job stream
hr = package->WriteToStream (jobStream, FALSE);
// Close the stream to tell the print job
// that the entire document has been sent.
hr = jobStream->Close();
// Wait for the print job to finish spooling...
if (NULL != completionEvent) {
if (WaitForSingleObject(completionEvent, INFINITE) == WAIT_OBJECT_0)
{
// Get the print job status to see why the wait completed.
// Note that without waiting for a completion event,
// the print job may not be complete when the status is queried.
XPS_JOB_STATUS jobStatus;
hr = job->GetJobStatus(&jobStatus);
// Evaluate the job status returned.
switch (jobStatus.completion)
{
case XPS_JOB_COMPLETED:
// The job completed as expected.
hr = S_OK;
break;
case XPS_JOB_CANCELLED:
// The job was canceled.
hr = E_FAIL;
break;
case XPS_JOB_FAILED:
// The job failed,
// jobStatus.jobStatus has the reason.
hr = E_FAIL;
break;
default:
// An unexpected value was returned.
hr = E_UNEXPECTED;
break;
}
// Release completion event handle
CloseHandle(completionEvent);
}
else
{ // there was a problem, set hr to error status
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
// hr contains the result of the print operation
CoUninitialize(); // if COM is no longer needed in this thread
Incrementally print an XPS OM
You can send the document components of an XPS OM to a printer job incrementally, by creating an XPS print job stream and then passing the individual document components to the print job stream, one at a time. The sequence in which the document components are sent determines how they will appear in the finished document. Thus, before a program can call the code in this example, it must correctly organize the document components.
Before using XPS OM interfaces, initialize COM in the thread as shown in the following example code.
HRESULT hr;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
To monitor the print job completion, create an event handle as shown in the following example code.
HANDLE completionEvent = NULL;
completionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == completionEvent)
{
hr = HRESULT_FROM_WIN32(GetLastError());
// The method can continue, but print spooling completion
// cannot be checked without a valid event handle.
}
Create a new print job stream and a new package writer. Pass each of the document components to the corresponding package writer methods in the same sequence as they will appear in the finished document.
Start each document new, then add pages to it. After passing all document components to the print job stream, close the stream, wait for the print job to complete, and then close and release open resources.
- Create a new print job stream by calling StartXpsPrintJob.
- Create a part URI for the FixedDocumentSequence part.
- Create a new package writer on the print job stream.
- For each document to be written:
- Create a new part URI for the FixedDocument part.
- Start a new document in the package writer.
- For each page in the current document, create a part URI for the FixedPage part and add the page to the package writer.
- After all pages have been added to the package writer, close it.
- Close the print job stream.
- Wait for the print job to complete.
- Check the completion status.
- Close and release open resources.
IXpsPrintJob* job = NULL;
IXpsPrintJobStream* jobStream = NULL;
hr = StartXpsPrintJob(
argv[1],
NULL,
NULL,
NULL,
completionEvent,
NULL,
0,
&job,
&jobStream,
NULL);
// Note the implicit requirement that CoInitializeEx
// has previously been called from this thread.
IXpsOMObjectFactory *xpsFactory = NULL;
hr = CoCreateInstance(
__uuidof(XpsOMObjectFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IXpsOMObjectFactory),
reinterpret_cast<void**>(&xpsFactory)
);
// Create part URI for FixedDocumentSequence part
// This can use a static string because there is only one
// FixedDocumentSequence part in the print job.
IOpcPartUri *partUri = NULL;
hr = xpsFactory->CreatePartUri(L"/FixedDocumentSequence.fdseq", &partUri);
// Create the package writer on the print job stream
// Note that the interleaving parameter set to
// XPS_INTERLEAVING_ON, the package writer will create
// empty print ticket parts when a NULL pointer is
// passed in the print ticket argument of this method,
// the StartNewDocument method, and the AddPage method.
// For more information, see the help for these methods.
IXpsOMPackageWriter *packageWriter = NULL;
hr = xpsFactory->CreatePackageWriterOnStream(
jobStream,
TRUE,
XPS_INTERLEAVING_ON, // to create blank print ticket objects
partUri,
NULL,
NULL,
NULL,
NULL,
&packageWriter);
// release partUri after it's been used to create new doc. seq.
if (partUri)
{
partUri->Release();
partUri = NULL;
}
// Add document content to the print job stream.
int docNumber = 1;
int docsInPackage = 1; // Change this value as required.
while (docNumber <= docsInPackage) {
// Create a unique part URI for the current document.
WCHAR DocPartUri[MAX_PATH];
hr = MakeDocumentPartUri (docNumber, MAX_PATH, DocPartUri);
hr = xpsFactory->CreatePartUri(DocPartUri, &partUri);
// Initialize the new document in the package writer.
hr = packageWriter->StartNewDocument(partUri, NULL, NULL, NULL, NULL);
// release part URI after it's been used to create new doc.
if (partUri)
{
partUri->Release();
partUri = NULL;
}
// Add the pages
int pageNumber = 1;
int pagesInDocument = 1; // Change this value as required.
while (pageNumber <= pagesInDocument) {
// Create a unique part URI for the current page
WCHAR PagePartUri[MAX_PATH];
hr = MakePagePartUri (
docNumber,
pageNumber,
MAX_PATH,
PagePartUri);
hr = xpsFactory->CreatePartUri(PagePartUri, &partUri);
// create page in OM
XPS_SIZE pageSize = {816, 1056};
IXpsOMPage *xpsPage = NULL;
hr = xpsFactory->CreatePage(
&pageSize,
L"en-US",
partUri,
&xpsPage);
// release pagePartUri after it's been used to create the page
if (partUri)
{
partUri->Release();
partUri = NULL;
}
// add content to the page or retrieve
// the page from the XPS OM.
// (not shown in this example)
// add page to document
hr = packageWriter->AddPage(
xpsPage,
&pageSize,
NULL,
NULL,
NULL,
NULL);
if (xpsPage)
{
xpsPage->Release();
xpsPage = NULL;
}
// go to the next page
pageNumber++;
}
// the fixed document does not need to be closed.
// it will be closed when a new fixed doc is opened
// or the package is closed.
// go to the next document
docNumber++;
}
// Close the package writer when finished
hr = packageWriter->Close();
if (SUCCEEDED(hr))
{
// Close the print stream to tell the print
// job that the all document contents have
// been sent
hr = jobStream->Close();
// Wait for the print job to finish spooling...
if (NULL != completionEvent) {
if (WaitForSingleObject(completionEvent, INFINITE) == WAIT_OBJECT_0)
{
// Get the print job status to see why the wait completed.
// Note that without waiting for a completion event,
// the print job may not be complete when the status is queried.
XPS_JOB_STATUS jobStatus;
hr = job->GetJobStatus(&jobStatus);
// Evaluate the job status returned.
switch (jobStatus.completion)
{
case XPS_JOB_COMPLETED:
// The job completed as expected.
hr = S_OK;
break;
case XPS_JOB_CANCELLED:
// The job was canceled.
hr = E_FAIL;
break;
case XPS_JOB_FAILED:
// The job failed,
// jobStatus.jobStatus has the reason.
hr = E_FAIL;
break;
default:
// An unexpected value was returned.
hr = E_UNEXPECTED;
break;
}
// Release completion event handle
CloseHandle(completionEvent);
}
else
{ // there was a problem, set hr to error status
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
}
else
{
// cancel the job, if one exists, because
// the close call returned an error
if (job) job->Cancel();
}
// hr contains the result of the print operation
// free/release pointers and handles used.
if (packageWriter)
{
packageWriter->Release();
packageWriter = NULL;
}
if (partUri)
{
partUri->Release();
partUri = NULL;
}
if (xpsFactory)
{
xpsFactory->Release();
xpsFactory = NULL;
}
if (jobStream)
{
jobStream->Release();
jobStream = NULL;
}
if (job)
{
job->Release();
job = NULL;
}
if (completionEvent)
{
CloseHandle(completionEvent);
completionEvent = NULL;
}
CoUninitialize(); // If done with COM in this thread.
When the program is writing the document components incrementally, as shown in this example, it must generate the part names for each document part that it sends to the print job stream. In the preceding example, the FixedDocumentSequence part URI is created from a static string because there is one and only one such part in the XPS document. The URI of each FixedPage and FixedDocument part must be unique within the XPS document. Building the part URI by using the index of these components can help ensure that the resulting URI string is unique within the XPS document.
HRESULT MakeDocumentPartUri (
__in int docNumber,
__in DWORD partUriStringLength,
__inout LPWSTR partUriStringBuffer
)
{
// create a Part URI string using the document number
// that was passed as an argument
// for example, "/Documents/1/FixedDocument.fdoc"
// where "1" specifies the document number, which would
// change with each document printed
return S_OK;
}
HRESULT MakePagePartUri (
__in int docNumber,
__in int pageNumber,
__in DWORD partUriStringLength,
__inout LPWSTR partUriStringBuffer
)
{
// create a Part URI string using the document number
// and page number that were passed as an argument
// for example: "/Documents/1/Pages/1.fpage"
// where the first "1" between Documents and Pages
// specifies the document number, which would change with
// each document. The second "1" specifies the page number,
// which would change with each page in the document.
return S_OK;
}
For more information on the structure of an XPS document, see the XML Paper Specification.
Related topics
Next Steps
Write an XPS OM to an XPS Document
Used in This Section
For More Information