Merge pull request 'Feature pdf print' (#590) from feature/pdf-print into release/v9.3.0

Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/core/pulls/590
This commit is contained in:
Oleg Korshul
2025-12-29 11:03:41 +00:00
9 changed files with 503 additions and 25 deletions

View File

@ -54,6 +54,7 @@
#include "SrcWriter/Outline.h"
#include "SrcWriter/GState.h"
#include "SrcWriter/RedactOutputDev.h"
#include "SrcWriter/Image.h"
#define AddToObject(oVal)\
{\
@ -2908,6 +2909,387 @@ bool CPdfEditor::MergePages(const std::wstring& wsPath)
return bRes;
}
bool CPdfEditor::PrintPages(const std::vector<bool>& arrPages, int nFlag)
{
if (m_nMode != Mode::Unknown)
return false;
PdfWriter::CDocument* pDoc = m_pWriter->GetDocument();
PdfWriter::CPageTree* pPageTree = pDoc->GetPageTree();
m_mObjManager.SetDoc(pDoc);
for (int i = 0; i < arrPages.size(); ++i)
{
if (!arrPages[i])
continue;
PdfWriter::CPage* pPage = new PdfWriter::CPage(pDoc);
pDoc->AddObject(pPage);
pPageTree->AddPage(pPage);
pDoc->AddEditPage(pPage, i);
PDFDoc* pPDFDocument = NULL;
PdfReader::CPdfFontList* pFontList = NULL;
int nStartRefID = 0;
int nPageIndex = m_pReader->GetPageIndex(i, &pPDFDocument, &pFontList, &nStartRefID);
Catalog* pCatalog = pPDFDocument->getCatalog();
XRef* xref = pPDFDocument->getXRef();
Ref* pPageRef = pCatalog->getPageRef(nPageIndex);
Object pageRefObj, pageObj;
pageRefObj.initRef(pPageRef->num, pPageRef->gen);
if (!pageRefObj.fetch(xref, &pageObj)->isDict())
{
pageObj.free(); pageRefObj.free();
continue;
}
m_mObjManager.AddObj(pPageRef->num + nStartRefID, pPage);
pageRefObj.free();
bool bResources = false, bMediaBox = false, bCropBox = false, bRotate = false;
for (int nIndex = 0; nIndex < pageObj.dictGetLength(); ++nIndex)
{
Object oTemp;
char* chKey = pageObj.dictGetKey(nIndex);
if (strcmp("Resources", chKey) == 0)
{
bResources = true;
Ref oResourcesRef = { -1, -1 };
if (pageObj.dictGetValNF(nIndex, &oTemp)->isRef())
oResourcesRef = oTemp.getRef();
oTemp.free();
PdfWriter::CObjectBase* pObj = oResourcesRef.num > 0 ? m_mObjManager.GetObj(oResourcesRef.num + nStartRefID) : NULL;
if (pObj)
{
pPage->Add(chKey, pObj);
m_mObjManager.IncRefCount(oResourcesRef.num + nStartRefID);
continue;
}
if (pageObj.dictGetVal(nIndex, &oTemp)->isDict())
{
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(oResourcesRef.num < 0, false);
if (oResourcesRef.num > 0)
m_mObjManager.AddObj(oResourcesRef.num + nStartRefID, pDict);
pPage->Add(chKey, pDict);
for (int nIndex = 0; nIndex < oTemp.dictGetLength(); ++nIndex)
{
Object oRes;
char* chKey2 = oTemp.dictGetKey(nIndex);
oTemp.dictGetValNF(nIndex, &oRes);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oRes, pDoc, xref, &m_mObjManager, nStartRefID);
pDict->Add(chKey2, pBase);
oRes.free();
}
oTemp.free();
continue;
}
else
{
oTemp.free();
pageObj.dictGetValNF(nIndex, &oTemp);
}
}
else if (strcmp("Parent", chKey) == 0)
{
// Поля родителей страниц переносятся к самим страницам
oTemp.free();
continue;
}
else if (strcmp("MediaBox", chKey) == 0)
{
bMediaBox = true;
pageObj.dictGetValNF(nIndex, &oTemp);
}
else if (strcmp("CropBox", chKey) == 0)
{
bCropBox = true;
pageObj.dictGetValNF(nIndex, &oTemp);
}
else if (strcmp("Rotate", chKey) == 0)
{
bRotate = true;
pageObj.dictGetValNF(nIndex, &oTemp);
}
else if (nFlag != 3 && strcmp("Contents", chKey) == 0)
{
pageObj.dictGetValNF(nIndex, &oTemp);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID, 0, false);
pPage->Add(chKey, pBase);
continue;
}
else if (strcmp("Annots", chKey) == 0)
{
oTemp.free();
continue;
}
else
pageObj.dictGetValNF(nIndex, &oTemp);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID);
pPage->Add(chKey, pBase);
oTemp.free();
}
if (!bResources || !bMediaBox || !bCropBox || !bRotate)
{
Page* pOPage = pCatalog->getPage(nPageIndex);
if (!bMediaBox)
{
PDFRectangle* pRect = pOPage->getMediaBox();
pPage->Add("MediaBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
}
if (!bCropBox && pOPage->isCropped())
{
PDFRectangle* pRect = pOPage->getCropBox();
pPage->Add("CropBox", PdfWriter::CArrayObject::CreateBox(pRect->x1, pRect->y1, pRect->x2, pRect->y2));
}
if (!bRotate)
pPage->Add("Rotate", pOPage->getRotate());
if (!bResources)
{
Dict* pResources = pOPage->getResourceDict();
PdfWriter::CResourcesDict* pDict = pDoc->CreateResourcesDict(true, false);
pPage->Add("Resources", pDict);
for (int nIndex = 0; nIndex < pResources->getLength(); ++nIndex)
{
Object oRes;
char* chKey2 = pResources->getKey(nIndex);
pResources->getValNF(nIndex, &oRes);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oRes, pDoc, xref, &m_mObjManager, nStartRefID);
pDict->Add(chKey2, pBase);
oRes.free();
}
}
}
pPage->Fix();
pDoc->FixEditPage(pPage);
if (nFlag == 0)
{
pageObj.free();
continue;
}
double dCTM[6] = { 1, 0, 0, 1, 0, 0 };
double dInvMatrix[6] = { 1, 0, 0, 1, 0, 0 };
GetCTM(xref, &pageObj, dCTM);
double det = dCTM[0] * dCTM[3] - dCTM[1] * dCTM[2];
if (fabs(det) > 1e-10)
{
double invDet = 1.0 / det;
dInvMatrix[0] = dCTM[3] * invDet;
dInvMatrix[1] = -dCTM[1] * invDet;
dInvMatrix[2] = -dCTM[2] * invDet;
dInvMatrix[3] = dCTM[0] * invDet;
dInvMatrix[4] = (dCTM[2] * dCTM[5] - dCTM[3] * dCTM[4]) * invDet;
dInvMatrix[5] = (dCTM[1] * dCTM[4] - dCTM[0] * dCTM[5]) * invDet;
}
PdfWriter::CStream* pStream = pPage->GetStream();
pStream->WriteReal(dInvMatrix[0]);
pStream->WriteChar(' ');
pStream->WriteReal(dInvMatrix[1]);
pStream->WriteChar(' ');
pStream->WriteReal(dInvMatrix[2]);
pStream->WriteChar(' ');
pStream->WriteReal(dInvMatrix[3]);
pStream->WriteChar(' ');
pStream->WriteReal(dInvMatrix[4]);
pStream->WriteChar(' ');
pStream->WriteReal(dInvMatrix[5]);
pStream->WriteStr(" cm\012");
pPage->SetStrokeColor(0, 0, 0);
pPage->SetFillColor(0, 0, 0);
pPage->SetExtGrState(pDoc->GetExtGState(255, 255));
pPage->BeginText();
pPage->SetCharSpace(0);
pPage->SetTextRenderingMode(PdfWriter::textrenderingmode_Fill);
pPage->SetHorizontalScaling(100);
pPage->EndText();
Object oAnnots;
if (!pageObj.dictLookup("Annots", &oAnnots)->isArray())
{
pageObj.free();
continue;
}
for (int i = 0; i < oAnnots.arrayGetLength(); ++i)
{
Object oAnnot, oType, oObj;
if (!oAnnots.arrayGet(i, &oAnnot)->isDict() || !oAnnot.dictLookup("Subtype", &oType)->isName() || !oAnnot.dictLookup("F", &oObj)->isInt())
{
oAnnot.free(); oType.free(); oObj.free();
continue;
}
int nFlags = oObj.getInt();
oObj.free();
// if Invisible || Hidden || !Print
if ((nFlags & (1 << 0)) || (nFlags & (1 << 1)) || !(nFlags & (1 << 2)))
{
oAnnot.free(); oType.free();
continue;
}
char* sType = oType.getName();
if ((nFlag == 1 && (strcmp(sType, "StrikeOut") || strcmp(sType, "FreeText") || strcmp(sType, "Underline") || strcmp(sType, "Square") || strcmp(sType, "Circle") || strcmp(sType, "Polygon")
|| strcmp(sType, "PolyLine") || strcmp(sType, "Highlight") || strcmp(sType, "Line") || strcmp(sType, "Squiggly") || strcmp(sType, "Text") || strcmp(sType, "Caret") || strcmp(sType, "Ink")
|| strcmp(sType, "FileAttachment") || strcmp(sType, "Sound") || strcmp(sType, "Redact"))) || (nFlag == 2 && strcmp(sType, "Stamp")) || (nFlag == 3 && strcmp(sType, "Widget")))
{
oType.free(); oAnnot.free();
continue;
}
oType.free();
PdfWriter::CXObject* pForm = pDoc->CreateForm();
pPage->GrSave();
pPage->ExecuteXObject(pForm);
pPage->GrRestore();
// TODO Нужно ли генерировать внешний вид тем у кого его нет
Object oAP, oAPN;
if (!oAnnot.dictLookup("AP", &oAP)->isDict() || !oAP.dictLookup("N", &oAPN)->isStream())
{
oAnnot.free(); oAP.free(); oAPN.free();
continue;
}
oAP.free();
Object oTemp;
double m[6] = { 1, 0, 0, 1, 0, 0 }, bbox[4] = { 0, 0, 0, 0 }, rect[4] = { 0, 0, 0, 0 };
if (oAnnot.dictLookup("Rect", &oTemp)->isArray() && oTemp.arrayGetLength() == 4)
{
for (int j = 0; j < 4; ++j)
{
oTemp.arrayGet(j, &oObj);
rect[j] = oObj.isNum() ? oObj.getNum() : 0;
oObj.free();
}
if (rect[0] > rect[2])
std::swap(rect[0], rect[2]);
if (rect[1] > rect[3])
std::swap(rect[1], rect[3]);
}
oTemp.free();
Dict* pODict = oAPN.streamGetDict();
for (int nIndex = 0; nIndex < pODict->getLength(); ++nIndex)
{
char* chKey = pODict->getKey(nIndex);
if (strcmp("Length", chKey) == 0)
{
oTemp.free();
continue;
}
else if (strcmp("BBox", chKey) == 0 && pODict->getVal(nIndex, &oTemp)->isArray() && oTemp.arrayGetLength() == 4)
{
for (int j = 0; j < 4; ++j)
{
oTemp.arrayGet(j, &oObj);
bbox[j] = oObj.isNum() ? oObj.getNum() : 0;
oObj.free();
}
if (bbox[0] > bbox[2])
std::swap(bbox[0], bbox[2]);
if (bbox[1] > bbox[3])
std::swap(bbox[1], bbox[3]);
}
else if (strcmp("Matrix", chKey) == 0 && pODict->getVal(nIndex, &oTemp)->isArray() && oTemp.arrayGetLength() == 6)
{
for (int j = 0; j < 6; ++j)
{
oTemp.arrayGet(j, &oObj);
m[j] = oObj.getNum();
oObj.free();
}
}
oTemp.free();
pODict->getValNF(nIndex, &oTemp);
PdfWriter::CObjectBase* pBase = DictToCDictObject2(&oTemp, pDoc, xref, &m_mObjManager, nStartRefID);
pForm->Add(chKey, pBase);
oTemp.free();
}
double formXMin, formYMin, formXMax, formYMax, x, y, sx, sy;
x = bbox[0] * m[0] + bbox[1] * m[2] + m[4];
y = bbox[0] * m[1] + bbox[1] * m[3] + m[5];
formXMin = formXMax = x;
formYMin = formYMax = y;
x = bbox[0] * m[0] + bbox[3] * m[2] + m[4];
y = bbox[0] * m[1] + bbox[3] * m[3] + m[5];
if (x < formXMin)
formXMin = x;
else if (x > formXMax)
formXMax = x;
if (y < formYMin)
formYMin = y;
else if (y > formYMax)
formYMax = y;
x = bbox[2] * m[0] + bbox[1] * m[2] + m[4];
y = bbox[2] * m[1] + bbox[1] * m[3] + m[5];
if (x < formXMin)
formXMin = x;
else if (x > formXMax)
formXMax = x;
if (y < formYMin)
formYMin = y;
else if (y > formYMax)
formYMax = y;
x = bbox[2] * m[0] + bbox[3] * m[2] + m[4];
y = bbox[2] * m[1] + bbox[3] * m[3] + m[5];
if (x < formXMin)
formXMin = x;
else if (x > formXMax)
formXMax = x;
if (y < formYMin)
formYMin = y;
else if (y > formYMax)
formYMax = y;
if (formXMin == formXMax)
sx = 1;
else
sx = (rect[2] - rect[0]) / (formXMax - formXMin);
if (formYMin == formYMax)
sy = 1;
else
sy = (rect[3] - rect[1]) / (formYMax - formYMin);
double tx = -formXMin * sx + rect[0];
double ty = -formYMin * sy + rect[1];
m[0] *= sx;
m[1] *= sy;
m[2] *= sx;
m[3] *= sy;
m[4] = m[4] * sx + tx;
m[5] = m[5] * sy + ty;
pForm->Add("Matrix", PdfWriter::CArrayObject::CreateMatrix(m));
PdfWriter::CStream* pStream = pForm->GetStream();
Stream* pOStream = oAPN.getStream()->getUndecodedStream();
pOStream->reset();
int nChar = pOStream->getChar();
while (nChar != EOF)
{
pStream->WriteChar(nChar);
nChar = pOStream->getChar();
}
oAPN.free(); oAnnot.free();
}
oAnnots.free();
pageObj.free();
}
return true;
}
bool CPdfEditor::DeletePage(int nPageIndex)
{
if (m_nMode == Mode::Unknown && !IncrementalUpdates())

View File

@ -107,6 +107,8 @@ public:
void AfterSplitPages();
bool MergePages(const std::wstring& wsPath);
bool PrintPages(const std::vector<bool>& arrPages, int nFlag);
private:
bool IncrementalUpdates();
void NewFrom();

View File

@ -151,6 +151,29 @@ bool CPdfFile::MergePages(const std::wstring& wsPath, int nMaxID, const std::wst
return m_pInternal->pEditor->MergePages(wsPath);
return false;
}
bool CPdfFile::PrintPages(const std::vector<bool>& arrPages, int nFlag)
{
if (!m_pInternal->pReader)
return false;
m_pInternal->pReader->CleanUp();
RELEASEOBJECT(m_pInternal->pWriter);
m_pInternal->pWriter = new CPdfWriter(m_pInternal->pAppFonts, false, this, true, m_pInternal->wsTempFolder);
RELEASEOBJECT(m_pInternal->pEditor);
m_pInternal->pEditor = new CPdfEditor(m_pInternal->wsSrcFile, m_pInternal->wsPassword, L"", m_pInternal->pReader, m_pInternal->pWriter);
if (m_pInternal->pEditor->GetError())
{
RELEASEOBJECT(m_pInternal->pEditor);
return false;
}
bool bRes = m_pInternal->pEditor->PrintPages(arrPages, nFlag);
RELEASEOBJECT(m_pInternal->pEditor);
return bRes;
}
bool CPdfFile::MovePage(int nPageIndex, int nPos)
{
if (!m_pInternal->pEditor)

View File

@ -99,6 +99,7 @@ public:
bool AddPage (int nPageIndex);
bool MovePage (int nPageIndex, int nPos);
bool MergePages(const std::wstring& wsPath, int nMaxID = 0, const std::wstring& wsPrefixForm = L"");
bool PrintPages(const std::vector<bool>& arrPages, int nFlag);
HRESULT ChangePassword(const std::wstring& wsPath, const std::wstring& wsPassword = L"");
// --- READER ---

View File

@ -469,6 +469,21 @@ namespace PdfWriter
return pArray;
}
CArrayObject* CArrayObject::CreateMatrix(double* m)
{
CArrayObject* pArray = new CArrayObject();
if (!pArray)
return NULL;
pArray->Add(m[0]);
pArray->Add(m[1]);
pArray->Add(m[2]);
pArray->Add(m[3]);
pArray->Add(m[4]);
pArray->Add(m[5]);
return pArray;
}
CObjectBase* CArrayObject::Copy(CObjectBase* pOut) const
{
CArrayObject* pArray = pOut && pOut->GetType() == object_type_ARRAY ? (CArrayObject*)pOut : new CArrayObject();

View File

@ -434,6 +434,7 @@ namespace PdfWriter
}
static CArrayObject* CreateBox(const TBox& oBox);
static CArrayObject* CreateBox(double dL, double dB, double dR, double dT);
static CArrayObject* CreateMatrix(double* m);
virtual CObjectBase* Copy(CObjectBase* pOut = NULL) const;
void FromXml(const std::wstring& sXml);

View File

@ -235,6 +235,8 @@ namespace PdfWriter
{
m_pPages->Add(pObj);
(*m_pCount)++;
if (pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGE)
((CPage*)pObj)->Add("Parent", this);
}
CObjectBase* CPageTree::GetObj(int nPageIndex)
{
@ -259,8 +261,6 @@ namespace PdfWriter
if (nPageIndex >= m_pCount->Get())
{
AddPage(pPage);
if (pPage->GetType() == object_type_DICT && ((CDictObject*)pPage)->GetDictType() == dict_type_PAGE)
((CPage*)pPage)->Add("Parent", this);
return true;
}
int nI = 0;

View File

@ -494,7 +494,7 @@ TEST_F(CPdfFileTest, EditPdf)
TEST_F(CPdfFileTest, EditPdfFromBase64)
{
//GTEST_SKIP();
GTEST_SKIP();
NSFonts::NSApplicationFontStream::SetGlobalMemoryStorage(NSFonts::NSApplicationFontStream::CreateDefaultGlobalMemoryStorage());
@ -590,6 +590,23 @@ TEST_F(CPdfFileTest, EditPdfSign)
RELEASEOBJECT(pCertificate);
}
TEST_F(CPdfFileTest, PrintPdf)
{
//GTEST_SKIP();
LoadFromFile();
int nPages = pdfFile->GetPagesCount();
std::vector<bool> arrPages;
for (int i = 0; i < nPages; ++i)
arrPages.push_back(true);
ASSERT_TRUE(pdfFile->PrintPages(arrPages, 1));
pdfFile->SaveToFile(wsDstFile);
pdfFile->Close();
}
TEST_F(CPdfFileTest, ChangePasswordToEmpty)
{
GTEST_SKIP();

View File

@ -295,29 +295,65 @@ namespace NExtractTools
}
// from crossplatform (pdf)
std::string checkPrintPages(InputParams &params)
std::string checkPrintPages(InputParams &params, int &nType)
{
if (NULL == params.m_sJsonParams)
return "";
std::wstring sPages;
std::wstring::size_type posPrintPages = params.m_sJsonParams->find(L"\"printPages\":\"");
if (std::wstring::npos != posPrintPages)
{
posPrintPages += 14;
std::wstring::size_type posPrintPages2 = params.m_sJsonParams->find(L"\"", posPrintPages);
if (std::wstring::npos == posPrintPages2)
return "";
sPages = params.m_sJsonParams->substr(posPrintPages, posPrintPages2 - posPrintPages);
}
std::wstring::size_type posNativeOptions = params.m_sJsonParams->find(L"\"nativeOptions\"");
if (std::wstring::npos == posNativeOptions)
return "";
if (sPages.empty() && std::wstring::npos != posNativeOptions)
{
std::wstring::size_type posNativePages = params.m_sJsonParams->find(L"\"pages\":\"", posNativeOptions);
if (std::wstring::npos == posNativePages)
return "";
std::wstring::size_type posNativePages = params.m_sJsonParams->find(L"\"pages\":\"", posNativeOptions);
if (std::wstring::npos == posNativePages)
return "";
posNativePages += 9;
std::wstring::size_type posNativePages2 = params.m_sJsonParams->find(L"\"", posNativePages);
if (std::wstring::npos == posNativePages2)
return "";
posNativePages += 9;
std::wstring::size_type posNativePages2 = params.m_sJsonParams->find(L"\"", posNativePages);
if (std::wstring::npos == posNativePages2)
return "";
sPages = params.m_sJsonParams->substr(posNativePages, posNativePages2 - posNativePages);
}
std::wstring::size_type posLayout = params.m_sJsonParams->find(L"\"pdfLayout\":{");
if (std::wstring::npos != posLayout)
{
std::wstring::size_type posContent = params.m_sJsonParams->find(L"\"content\":\"", posLayout);
if (std::wstring::npos != posContent)
{
posContent += 11;
std::wstring::size_type posContent2 = params.m_sJsonParams->find(L"\"", posContent);
if (std::wstring::npos == posContent2)
return "";
std::wstring sType = params.m_sJsonParams->substr(posContent, posContent2 - posContent);
if (sType == L"doc")
nType = 0;
else if (sType == L"docAndMarkups")
nType = 1;
else if (sType == L"docAndStamps")
nType = 2;
else if (sType == L"formsOnly")
nType = 3;
}
}
std::wstring sPages = params.m_sJsonParams->substr(posNativePages, posNativePages2 - posNativePages);
if (L"all" == sPages)
return "";
if (L"current" == sPages)
if (L"current" == sPages && std::wstring::npos != posNativeOptions)
{
std::wstring::size_type posCurrentPage = params.m_sJsonParams->find(L"\"currentPage\":", posNativeOptions);
if (std::wstring::npos == posCurrentPage)
@ -347,6 +383,9 @@ namespace NExtractTools
std::vector<bool> getPrintPages(const std::string &sPages, int nPagesCount)
{
if (sPages.empty())
return std::vector<bool>(nPagesCount, true);
const char *buffer = sPages.c_str();
size_t nCur = 0;
@ -831,14 +870,15 @@ namespace NExtractTools
if (AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDF == nFormatTo)
{
std::string sPages = checkPrintPages(params);
int nType = -1;
std::string sPages = checkPrintPages(params, nType);
if (nFormatFrom == nFormatTo && !params.getIsPDFA())
{
if (!sPages.empty())
if (!sPages.empty() || nType != -1)
{
std::wstring sCurrentTmp = L"";
sCurrentTmp =NSFile::CFileBinary::CreateTempFileWithUniqueName(convertParams.m_sTempDir, L"PDF_");
sCurrentTmp = NSFile::CFileBinary::CreateTempFileWithUniqueName(convertParams.m_sTempDir, L"PDF_");
if (NSFile::CFileBinary::Exists(sCurrentTmp))
NSFile::CFileBinary::Remove(sCurrentTmp);
@ -846,17 +886,14 @@ namespace NExtractTools
oPdfPages.SetTempDirectory(convertParams.m_sTempDir);
std::wstring sPassword = params.getPassword();
if (oPdfPages.LoadFromFile(sFrom.c_str(), L"", sPassword, sPassword) && oPdfPages.EditPdf(sCurrentTmp))
if (oPdfPages.LoadFromFile(sFrom.c_str(), L"", sPassword, sPassword))
{
int nPagesCount = oPdfPages.GetPagesCount();
std::vector<bool> arPages = getPrintPages(convertParams.m_sPrintPages, nPagesCount);
std::vector<bool> arPages = getPrintPages(sPages, nPagesCount);
for (int i = 0; i < nPagesCount; ++i)
{
if (!arPages[i])
oPdfPages.DeletePage(i);
}
oPdfPages.PrintPages(arPages, nType);
oPdfPages.SaveToFile(sCurrentTmp);
oPdfPages.Close();
}
else