📄 bkflt.cpp
字号:
/*++
Copyright (c) 2005 Microsoft Corporation
All rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
File Name:
bkflt.cpp
Abstract:
Booklet filter implementation. This class derives from the Xps filter
class and implements the necessary part handlers to support booklet
printing. The booklet filter is responsible for re-ordering pages and re-uses
the NUp filter to provide 2-up and offset support.
--*/
#include "precomp.h"
#include "debug.h"
#include "globals.h"
#include "xdstring.h"
#include "xdexcept.h"
#include "pthndlr.h"
#include "bkflt.h"
#include "bksax.h"
#include "bkpthndlr.h"
using XDPrintSchema::Binding::BindingData;
//
// {7DFC96C6-CEA2-46d8-B354-887C47B7986D}
//
CLSID CBookletFilter::CLSID_BookletFilter = {0x7dfc96c6, 0xcea2, 0x46d8, {0xb3, 0x54, 0x88, 0x7c, 0x47, 0xb7, 0x98, 0x6d}};
/*++
Routine Name:
CBookletFilter::CBookletFilter
Routine Description:
Default constructor for the booklet filter which initialises the
filter to sensible default values
Arguments:
None
Return Value:
None
--*/
CBookletFilter::CBookletFilter() :
m_bSendAllDocs(TRUE),
m_bookScope(CBkPTProperties::None)
{
}
/*++
Routine Name:
CBookletFilter::~CBookletFilter
Routine Description:
Default destructor for the booklet filter
Arguments:
None
Return Value:
None
--*/
CBookletFilter::~CBookletFilter()
{
}
/*++
Routine Name:
CBookletFilter::GetCLSID
Routine Description:
Method to obtain the class ID for the booklet filter
Arguments:
None
Return Value:
Reference to the class ID for the booklet filter
--*/
CONST CLSID&
CBookletFilter::GetCLSID(
VOID
)
{
return CLSID_BookletFilter;
}
/*++
Routine Name:
CNUpFilter::ProcessPart
Routine Description:
Method for processing each fixed document sequence part in a container
Arguments:
pFDS - Pointer to the fixed document sequence to process
Return Value:
HRESULT
S_OK - On success
E_* - On error
--*/
HRESULT
CBookletFilter::ProcessPart(
__inout IFixedDocumentSequence* pFDS
)
{
VERBOSE("Processing Fixed Document Sequence part with booklet filter handler\n");
HRESULT hr = S_OK;
if (SUCCEEDED(hr = CHECK_POINTER(pFDS, E_POINTER)))
{
//
// Get the PT manager to return the FixedDocumentSequence ticket.
//
IXMLDOMDocument2* pPT = NULL;
if (SUCCEEDED(hr = m_ptManager.SetTicket(pFDS)) &&
SUCCEEDED(hr = m_ptManager.GetTicket(kPTJobScope, &pPT)))
{
//
// Set the binding scope from the PrintTicket
//
hr = SetBindingScope(pPT);
}
}
if (SUCCEEDED(hr))
{
hr = m_pXDWriter->SendFixedDocumentSequence(pFDS);
}
ERR_ON_HR(hr);
return hr;
}
/*++
Routine Name:
CBookletFilter::ProcessPart
Routine Description:
Method for processing each fixed document part in a container
Arguments:
pFD - Pointer to the fixed document to process
Return Value:
HRESULT
S_OK - On success
S_FALSE - When not enabled in the PT
E_* - On error
--*/
HRESULT
CBookletFilter::ProcessPart(
__inout IFixedDocument* pFD
)
{
VERBOSE("Processing Fixed Document part with booklet filter handler\n");
HRESULT hr = S_OK;
if (SUCCEEDED(hr = CHECK_POINTER(pFD, E_POINTER)) &&
SUCCEEDED(hr = m_ptManager.SetTicket(pFD)))
{
//
// If we are in a JobBook session we want to maintain the current
// JobBook settings
//
if (m_bookScope != CBkPTProperties::Job)
{
//
// Flush any outstanding pages in case we have just completed a
// DocNUp sequence
//
hr = FlushCache();
//
// Get the PT manager to return the FixedDocument ticket.
//
IXMLDOMDocument2* pPT = NULL;
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = m_ptManager.GetTicket(kPTDocumentScope, &pPT)))
{
//
// Set the binding scope from the PrintTicket
//
hr = SetBindingScope(pPT);
}
}
}
if (SUCCEEDED(hr) &&
m_bSendAllDocs)
{
hr = m_pXDWriter->SendFixedDocument(pFD);
//
// If we are JobBindAllDocuments we only ever send one doc - now we have
// sent the first document we can test to see if we need to send all of them
//
if (m_bookScope == CBkPTProperties::Job)
{
m_bSendAllDocs = FALSE;
}
}
ERR_ON_HR(hr);
return hr;
}
/*++
Routine Name:
CBookletFilter::ProcessPart
Routine Description:
Method for processing each fixed page part in a container
Arguments:
pFP - Pointer to the fixed page to process
Return Value:
HRESULT
S_OK - On success
E_* - On error
--*/
HRESULT
CBookletFilter::ProcessPart(
__inout IFixedPage* pFP
)
{
ASSERTMSG(m_pXDWriter != NULL, "XD writer is not initialised.\n");
VERBOSE("Processing Fixed Page with booklet filter handler\n");
HRESULT hr = S_OK;
if (SUCCEEDED(hr = CHECK_POINTER(pFP, E_POINTER)) &&
SUCCEEDED(hr = CHECK_POINTER(m_pXDWriter, E_PENDING)))
{
//
// Check if we are processing a booklet job
//
if (m_bookScope != CBkPTProperties::None)
{
//
// Cache pages for reordering
//
try
{
m_cacheFP.push_back(pFP);
}
catch (exception& DBG_ONLY(e))
{
ERR(e.what());
hr = E_FAIL;
}
}
else
{
hr = m_pXDWriter->SendFixedPage(pFP);
}
}
ERR_ON_HR(hr);
return hr;
}
/*++
Routine Name:
CBookletFilter::Finalize
Routine Description:
Method to flush the cache of pages as the last action of the filter
Arguments:
None
Return Value:
HRESULT
S_OK - On success
E_* - On error
--*/
HRESULT
CBookletFilter::Finalize(
VOID
)
{
//
// Just flush the cache of pages
//
return FlushCache();
}
/*++
Routine Name:
CBookletFilter::FlushCache
Routine Description:
Method to send the cached collection of pages in the correct order back
Arguments:
None
Return Value:
HRESULT
S_OK - On success
E_* - On error
--*/
HRESULT
CBookletFilter::FlushCache(
VOID
)
{
HRESULT hr = S_OK;
if (m_pXDWriter == NULL)
{
hr = E_PENDING;
}
size_t cPages = 0;
try
{
cPages = m_cacheFP.size();
}
catch (exception& DBG_ONLY(e))
{
ERR(e.what());
hr = E_FAIL;
}
if (SUCCEEDED(hr) &&
cPages > 0 &&
m_bookScope != CBkPTProperties::None)
{
//
// We may need to add a pad page if the page count is odd
//
CComPtr<IFixedPage> pNewFP(NULL);
if (cPages%2 == 1 &&
SUCCEEDED(hr = CreatePadPage(&pNewFP)))
{
//
// We successfully created our pad page; add it to the cache
//
try
{
m_cacheFP.push_back(pNewFP);
}
catch (exception& DBG_ONLY(e))
{
ERR(e.what());
hr = E_FAIL;
}
cPages++;
}
if (SUCCEEDED(hr))
{
try
{
//
// Re-order pages in the cache
//
map<size_t, IFixedPage*> reorderedPages;
size_t newIndex = 0;
size_t pageIndex = 0;
for (pageIndex = 0; pageIndex < cPages/2; pageIndex++)
{
reorderedPages[newIndex] = m_cacheFP[pageIndex];
newIndex += 2;
}
newIndex = cPages - 1;
for (pageIndex = cPages/2; pageIndex < cPages; pageIndex++)
{
reorderedPages[newIndex] = m_cacheFP[pageIndex];
newIndex -= 2;
}
//
// Write out reordered pages
//
for (pageIndex = 0; pageIndex < cPages && SUCCEEDED(hr); pageIndex++)
{
hr = m_pXDWriter->SendFixedPage(reorderedPages[pageIndex]);
}
//
// Clean out the cache
//
m_cacheFP.clear();
}
catch (exception& DBG_ONLY(e))
{
ERR(e.what());
hr = E_FAIL;
}
}
}
ERR_ON_HR(hr);
return hr;
}
/*++
Routine Name:
CBookletFilter::CreatePadPage
Routine Description:
Method to create a pad page which is required for odd page counts to
ensure pages are correctly ordered for presentation as a booklet
Arguments:
ppNewPage - Pointer to a pointer to the newly created fixed page
Return Value:
HRESULT
S_OK - On success
E_* - On error
--*/
HRESULT
CBookletFilter::CreatePadPage(
__deref_out IFixedPage** ppNewPage
)
{
HRESULT hr = S_OK;
//
// Validate parameters and members before proceeding
//
if (SUCCEEDED(hr = CHECK_POINTER(ppNewPage, E_POINTER)) &&
SUCCEEDED(hr = CHECK_POINTER(m_pXDWriter, E_PENDING)))
{
*ppNewPage = NULL;
PCWSTR pszPageName = NULL;
try
{
//
// Create a unique name for the pad page for this print session using GetTickCount()
//
CStringXDW szNewPageName;
szNewPageName.Format(L"/Pad_page_%u.xaml", GetTickCount());
pszPageName = szNewPageName.GetBuffer();
//
// Create a new empty page and retrieve a writer. Also get a
// reader from the first page so we can copy the FixedPage root
// element. This ensures the page sizes match.
//
CComPtr<IPrintWriteStream> pWriter(NULL);
CComPtr<ISAXXMLReader> pSaxRdr(NULL);
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = m_pXDWriter->GetNewEmptyPart(pszPageName,
IID_IFixedPage,
reinterpret_cast<PVOID*>(ppNewPage),
&pWriter)) &&
SUCCEEDED(hr = pSaxRdr.CoCreateInstance(CLSID_SAXXMLReader60)))
{
//
// We use a simple SAX handler which copies only the root
// element and discards all other content.
//
CBkSaxHandler bkSaxHndlr(pWriter);
CComPtr<IPrintReadStream> pReader(NULL);
IFixedPage* pFP = NULL;
pFP = m_cacheFP[0];
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = pSaxRdr->putContentHandler(&bkSaxHndlr)) &&
SUCCEEDED(hr = pFP->GetStream(&pReader)))
{
CComPtr<ISequentialStream> pReadStreamToSeq(NULL);
pReadStreamToSeq.Attach(new pfp::PrintReadStreamToSeqStream(pReader));
if (SUCCEEDED(hr = CHECK_POINTER(pReadStreamToSeq, E_OUTOFMEMORY)))
{
hr = pSaxRdr->parse(CComVariant(static_cast<ISequentialStream*>(pReadStreamToSeq)));
}
}
pWriter->Close();
}
}
catch (CXDException& e)
{
hr = e;
}
}
ERR_ON_HR(hr);
return hr;
}
/*++
Routine Name:
CBookletFilter::SetBindingScope
Routine Description:
Method to retrieve the binding scope from a PrintTicket
Arguments:
pPT - Pointer to the PrintTicket to retrieve the scope from
Return Value:
HRESULT
S_OK - On success
S_FALSE - Booklet settings not present in the PT
E_* - On error
--*/
HRESULT
CBookletFilter::SetBindingScope(
__in IXMLDOMDocument2* pPT
)
{
HRESULT hr = S_OK;
if (SUCCEEDED(hr = CHECK_POINTER(pPT, E_POINTER)))
{
try
{
BindingData bindingData;
CBookPTHandler bkPTHandler(pPT);
//
// Retrieve the booklet properties from the ticket via the handler
//
if (SUCCEEDED(hr) &&
SUCCEEDED(hr = bkPTHandler.GetData(&bindingData)))
{
CBkPTProperties bookletPTProps(bindingData);
//
// Retrieve the booklet scope
//
hr = bookletPTProps.GetScope(&m_bookScope);
}
else if (hr == E_ELEMENT_NOT_FOUND)
{
//
// Booklet PT settings are not present - reset hr to S_FALSE and proceed
//
hr = S_FALSE;
}
}
catch (CXDException& e)
{
hr = e;
}
}
return hr;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -