File indexing completed on 2024-05-12 16:41:06
0001 /****************************************************************************** 0002 Copyright (C) 2009-2011 by Holger Danielsson (holger.danielsson@versanet.de) 0003 (C) 2019 by Michel Ludwig (michel.ludwig@kdemail.net) 0004 ******************************************************************************/ 0005 0006 /*************************************************************************** 0007 * * 0008 * This program is free software; you can redistribute it and/or modify * 0009 * it under the terms of the GNU General Public License as published by * 0010 * the Free Software Foundation; either version 2 of the License, or * 0011 * (at your option) any later version. * 0012 * * 0013 ***************************************************************************/ 0014 0015 0016 #include "pdfdialog.h" 0017 0018 #include <QCheckBox> 0019 #include <QDateTime> 0020 #include <QDialogButtonBox> 0021 #include <QFile> 0022 #include <QGridLayout> 0023 #include <QGroupBox> 0024 #include <QInputDialog> 0025 #include <QLabel> 0026 #include <QLayout> 0027 #include <QLineEdit> 0028 #include <QLocale> 0029 #include <QProcess> 0030 #include <QPushButton> 0031 #include <QRegExp> 0032 #include <QStandardPaths> 0033 #include <QStringList> 0034 #include <QTemporaryFile> 0035 #include <QTextStream> 0036 #include <QValidator> 0037 #include <QVBoxLayout> 0038 0039 #include <KComboBox> 0040 #include <KConfigGroup> 0041 #include <KIconLoader> 0042 #include <KLocalizedString> 0043 #include <KMessageBox> 0044 #include <KProcess> 0045 #include <KUrlRequester> 0046 0047 #include "errorhandler.h" 0048 #include "kileconfig.h" 0049 #include "kiledebug.h" 0050 0051 0052 namespace KileDialog 0053 { 0054 0055 PdfDialog::PdfDialog(QWidget *parent, 0056 const QString &texfilename,const QString &startdir, 0057 const QString &latexextensions, 0058 KileTool::Manager *manager, 0059 KileErrorHandler *errorHandler, KileWidget::OutputView *output) 0060 : QDialog(parent) 0061 , m_startdir(startdir) 0062 , m_manager(manager) 0063 , m_errorHandler(errorHandler) 0064 , m_output(output) 0065 , m_tempdir(Q_NULLPTR) 0066 , m_proc(Q_NULLPTR) 0067 , m_rearrangeButton(new QPushButton) 0068 , m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Help|QDialogButtonBox::Close)) 0069 { 0070 setWindowTitle(i18n("PDF Wizard")); 0071 setModal(true); 0072 QVBoxLayout *mainLayout = new QVBoxLayout; 0073 setLayout(mainLayout); 0074 m_rearrangeButton->setDefault(true); 0075 0076 // determine if a pdffile already exists 0077 QString pdffilename; 0078 if(!texfilename.isEmpty()) { 0079 // working with a pdf document, so we try to determine the LaTeX source file 0080 QStringList extlist = latexextensions.split(' '); 0081 for (QStringList::Iterator it = extlist.begin(); it != extlist.end(); ++it) { 0082 if (texfilename.indexOf((*it), -(*it).length()) >= 0) { 0083 pdffilename = texfilename.left(texfilename.length() - (*it).length()) + ".pdf"; 0084 if (!QFileInfo::exists(pdffilename)) 0085 pdffilename.clear(); 0086 break; 0087 } 0088 } 0089 } 0090 0091 // prepare dialog 0092 QWidget *page = new QWidget(this); 0093 mainLayout->addWidget(page); 0094 m_PdfDialog.setupUi(page); 0095 page->setMinimumWidth(500); 0096 m_PdfDialog.m_pbPrinting->setIcon(QIcon::fromTheme("printer")); 0097 m_PdfDialog.m_pbAll->setIcon(QIcon::fromTheme("list-add")); 0098 m_PdfDialog.m_pbBackgroundColor->setColor(QColor(255, 255, 224)); 0099 0100 // insert KileWidget::CategoryComboBox 0101 m_cbTask = new KileWidget::CategoryComboBox(m_PdfDialog.m_gbParameter); 0102 QGridLayout *paramLayout = static_cast<QGridLayout*>(m_PdfDialog.m_gbParameter->layout()); 0103 paramLayout->addWidget(m_cbTask, 4, 1); 0104 0105 // setup filenames 0106 const QStringList pdfMimeType = {QStringLiteral("application/pdf")}; 0107 m_PdfDialog.m_edInfile->setMimeTypeFilters(pdfMimeType); 0108 m_PdfDialog.m_edInfile->lineEdit()->setText(pdffilename); 0109 m_PdfDialog.m_edOutfile->setMimeTypeFilters(pdfMimeType); 0110 m_PdfDialog.m_edOutfile->setMode(KFile::File | KFile::LocalOnly ); 0111 m_PdfDialog.m_edOutfile->lineEdit()->setText( getOutfileName(pdffilename) ); 0112 0113 //max password length for pdf files 0114 m_PdfDialog.m_edPassword->setMaxLength(32); 0115 0116 // set an user button to execute the task and icon for help button 0117 m_rearrangeButton->setText(i18n("Re&arrange")); 0118 m_rearrangeButton->setIcon(QIcon::fromTheme("system-run")); 0119 m_PdfDialog.m_lbParameterIcon->setPixmap(QIcon::fromTheme("help-about").pixmap(KIconLoader::SizeSmallMedium)); 0120 0121 // init important variables 0122 m_numpages = 0; 0123 m_encrypted = false; 0124 m_pdftk = false; 0125 m_pdfpages = false; 0126 m_scriptrunning = false; 0127 m_pagesize = QSize(0,0); 0128 0129 // setup tasks 0130 m_tasklist << i18n("1 Page + Empty Page --> 2up") // 0 PDF_PAGE_EMPTY 0131 << i18n("1 Page + Duplicate --> 2up") // 1 PDF_PAGE_DUPLICATE 0132 << i18n("2 Pages --> 2up") // 2 PDF_2UP 0133 << i18n("2 Pages (landscape) --> 2up") // 3 PDF_2UP_LANDSCAPE 0134 << i18n("4 Pages --> 4up") // 4 PDF_4UP 0135 << i18n("4 Pages (landscape) --> 4up") // 5 PDF_4UP_LANDSCAPE 0136 << i18n("Select Even Pages") // 6 PDF_EVEN 0137 << i18n("Select Odd Pages") // 7 PDF_ODD 0138 << i18n("Select Even Pages (reverse order)") // 8 PDF_EVEN_REV 0139 << i18n("Select Odd Pages (reverse order)") // 9 PDF_ODD_REV 0140 << i18n("Reverse All Pages") // 10 PDF_REVERSE 0141 << i18n("Decrypt") // 11 PDF_DECRYPT 0142 << i18n("Select Pages") // 12 PDF_SELECT 0143 << i18n("Delete Pages") // 13 PDF_DELETE 0144 << i18n("Apply a background watermark") // 14 PDF_PDFTK_BACKGROUND 0145 << i18n("Apply a background color") // 15 PDF_PDFTK_BGCOLOR 0146 << i18n("Apply a foreground stamp") // 16 PDF_PDFTK_STAMP 0147 << i18n("pdftk: Choose Parameter") // 17 PDF_PDFTK_FREE 0148 << i18n("pdfpages: Choose Parameter") // 18 PDF_PDFPAGES_FREE 0149 ; 0150 0151 // set data for properties: key/widget 0152 m_pdfInfoKeys << "Title" << "Subject" << "Author" << "Creator" << "Producer" << "Keywords"; 0153 0154 m_pdfInfoWidget["Title"] = m_PdfDialog.m_leTitle; 0155 m_pdfInfoWidget["Subject"] = m_PdfDialog.m_leSubject; 0156 m_pdfInfoWidget["Keywords"] = m_PdfDialog.m_leKeywords; 0157 m_pdfInfoWidget["Author"] = m_PdfDialog.m_leAuthor; 0158 m_pdfInfoWidget["Creator"] = m_PdfDialog.m_leCreator; 0159 m_pdfInfoWidget["Producer"] = m_PdfDialog.m_leProducer; 0160 0161 // set data for permissions: key/widget 0162 m_pdfPermissionKeys << AllowModify << AllowCopy << AllowPrint 0163 << AllowNotes << AllowFillForms; 0164 0165 m_pdfPermissionWidgets << m_PdfDialog.m_cbModify << m_PdfDialog.m_cbCopy << m_PdfDialog.m_cbPrinting 0166 << m_PdfDialog.m_cbAnnotations << m_PdfDialog.m_cbFormFeeds; 0167 0168 m_pdfPermissionPdftk << "ModifyContents" << "CopyContents" << "Printing" 0169 << "ModifyAnnotations" << "FillIn"; 0170 0171 // default permissions 0172 m_pdfPermissionState << false << false << false << false << false; 0173 0174 // check for libpoppler pdf library 0175 #if LIBPOPPLER_AVAILABLE 0176 m_poppler = true; 0177 KILE_DEBUG_MAIN << "working with libpoppler pdf library"; 0178 #else 0179 m_poppler = false; 0180 KILE_DEBUG_MAIN << "working without libpoppler pdf library"; 0181 m_PdfDialog.tabWidget->removeTab(2); 0182 m_PdfDialog.tabWidget->removeTab(1); 0183 #endif 0184 0185 // init Dialog 0186 m_PdfDialog.m_lbParameterInfo->setTextFormat(Qt::RichText); 0187 m_PdfDialog.m_cbOverwrite->setChecked(false); 0188 updateDialog(); 0189 0190 connect(this, &PdfDialog::output, m_output, &KileWidget::OutputView::receive); 0191 connect(m_PdfDialog.m_edInfile->lineEdit(), &QLineEdit::textChanged, this, &PdfDialog::slotInputfileChanged); 0192 0193 #if LIBPOPPLER_AVAILABLE 0194 connect(m_PdfDialog.tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotTabwidgetChanged(int))); 0195 connect(m_PdfDialog.m_pbPrinting, SIGNAL(clicked()), this, SLOT(slotPrintingClicked())); 0196 connect(m_PdfDialog.m_pbAll, SIGNAL(clicked()), this, SLOT(slotAllClicked())); 0197 #endif 0198 0199 m_buttonBox->addButton(m_rearrangeButton, QDialogButtonBox::ActionRole); 0200 connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); 0201 connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0202 connect(m_buttonBox, &QDialogButtonBox::helpRequested, this, &PdfDialog::slotShowHelp); 0203 connect(m_rearrangeButton, &QPushButton::clicked, this, &PdfDialog::slotExecute); 0204 mainLayout->addWidget(m_buttonBox); 0205 0206 // find available utilities for this dialog 0207 executeScript("kpsewhich pdfpages.sty", QString(), PDF_SCRIPTMODE_TOOLS); 0208 } 0209 0210 PdfDialog::~PdfDialog() 0211 { 0212 if (m_cbTask->currentIndex() != -1) { 0213 KileConfig::setPdfWizardLastTask(m_cbTask->currentIndex()); 0214 } 0215 delete m_tempdir; 0216 delete m_proc; 0217 } 0218 0219 void PdfDialog::initUtilities() 0220 { 0221 // find pdfpages.sty? 0222 m_pdfpages = m_outputtext.contains("pdfpages.sty"); 0223 0224 // additionally look for pdftk 0225 m_pdftk = !QStandardPaths::findExecutable("pdftk").isEmpty(); 0226 0227 //m_pdfpages = false; // <----------- only for testing HACK 0228 //m_pdftk = false; // <----------- only for testing HACK 0229 0230 KILE_DEBUG_MAIN << "Looking for pdf tools: pdftk=" << m_pdftk << " pdfpages.sty=" << m_pdfpages; 0231 0232 #if !LIBPOPPLER_AVAILABLE 0233 m_imagemagick = KileConfig::imagemagick(); 0234 0235 // we can't use libpoppler pdf library and need to find another method to determine the number of pdf pages 0236 // Kile will use three options before giving up 0237 if ( m_pdftk ) 0238 m_numpagesMode = PDF_SCRIPTMODE_NUMPAGES_PDFTK; 0239 else if ( m_imagemagick ) 0240 m_numpagesMode = PDF_SCRIPTMODE_NUMPAGES_IMAGEMAGICK; 0241 else 0242 m_numpagesMode = PDF_SCRIPTMODE_NUMPAGES_GHOSTSCRIPT; 0243 #endif 0244 0245 // no pdftk, so properties and permissions are readonly 0246 if ( !m_pdftk ) { 0247 // set readonly properties 0248 for (QStringList::const_iterator it = m_pdfInfoKeys.constBegin(); it != m_pdfInfoKeys.constEnd(); ++it) { 0249 m_pdfInfoWidget[*it]->setReadOnly(true); 0250 } 0251 #if LIBPOPPLER_AVAILABLE 0252 // connect permission widgets 0253 for (int i=0; i<m_pdfPermissionKeys.size(); ++i) { 0254 connect(m_pdfPermissionWidgets.at(i), SIGNAL(clicked(bool)), this, SLOT(slotPermissionClicked(bool))); 0255 } 0256 #endif 0257 } 0258 0259 // if we found at least one utility, we can enable some connections 0260 if ( m_pdftk || m_pdfpages) { 0261 connect(m_PdfDialog.m_edOutfile->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotOutputfileChanged(QString))); 0262 connect(m_PdfDialog.m_cbOverwrite, SIGNAL(stateChanged(int)), this, SLOT(slotOverwriteChanged(int))); 0263 connect(m_cbTask, SIGNAL(activated(int)), this, SLOT(slotTaskChanged(int))); 0264 } 0265 0266 // setup dialog 0267 slotInputfileChanged(m_PdfDialog.m_edInfile->lineEdit()->text()); 0268 } 0269 0270 // read properties and permissions from the PDF document 0271 void PdfDialog::pdfParser(const QString &filename) 0272 { 0273 #if LIBPOPPLER_AVAILABLE 0274 Poppler::Document *doc = Poppler::Document::load(filename); 0275 if ( !doc || doc->isLocked() ) { 0276 KILE_DEBUG_MAIN << "Error: could not open pdf document '" << filename << "'"; 0277 return; 0278 } 0279 KILE_DEBUG_MAIN << "Parse pdf document: " << filename; 0280 0281 // read encryption 0282 m_encrypted = doc->isEncrypted(); 0283 m_PdfDialog.m_lbEncryption->setText( (m_encrypted) ? i18n("yes") : i18n("no") ); 0284 0285 // read properties 0286 for (QStringList::const_iterator it = m_pdfInfoKeys.constBegin(); it != m_pdfInfoKeys.constEnd(); ++it) { 0287 QString value = doc->info(*it); 0288 m_pdfInfo[*it] = value; 0289 m_pdfInfoWidget[*it]->setText(value); 0290 } 0291 0292 // read creation date and modification date 0293 m_PdfDialog.m_lbCreationDate->setText(QLocale().toString(doc->date("CreationDate"))); 0294 m_PdfDialog.m_lbModDate->setText(QLocale().toString(doc->date("ModDate"))); 0295 0296 // read PDF version 0297 Poppler::Document::PdfVersion pdfVersion = doc->getPdfVersion(); 0298 m_PdfDialog.m_lbFormat->setText( QString("PDF version %1.%2").arg(pdfVersion.major).arg(pdfVersion.minor) ); 0299 0300 // read permissions 0301 for (int i=0; i<m_pdfPermissionKeys.size(); ++i) { 0302 bool value = isAllowed( doc, (PDF_Permission)m_pdfPermissionKeys.at(i) ); 0303 m_pdfPermissionWidgets.at(i)->setChecked(value); 0304 0305 if ( !m_pdftk ) { 0306 m_pdfPermissionState[i] = value; 0307 } 0308 } 0309 0310 // determine and set number of pages 0311 setNumberOfPages( doc->numPages() ); 0312 0313 // look if all pages have the same size 0314 m_pagesize = allPagesSize(doc); 0315 0316 delete doc; 0317 #else 0318 /* libpoppler pdf library is not available: 0319 * - we use a brute force method to determine, if this file is encrypted 0320 * - then we try to determine the number of pages with 0321 * - pdftk (always first choice, if installed) 0322 * - imagemagick (second choice) 0323 * - gs (third and last choice) 0324 * - if the pdf file is encrypted, pdftk will ask for a password 0325 */ 0326 0327 // look if the pdf file is encrypted (brute force) 0328 m_encrypted = readEncryption(filename); 0329 KILE_DEBUG_MAIN << "PDF encryption: " << m_encrypted; 0330 0331 // determine the number of pages of the pdf file 0332 determineNumberOfPages(filename,m_encrypted); 0333 KILE_DEBUG_MAIN << "PDF number of pages: " << m_numpages; 0334 0335 // clear pagesize 0336 m_pagesize = QSize(0,0); 0337 #endif 0338 0339 0340 } 0341 0342 #if LIBPOPPLER_AVAILABLE 0343 bool PdfDialog::isAllowed(Poppler::Document *doc, PDF_Permission permission) const 0344 { 0345 bool b = true; 0346 switch ( permission ) 0347 { 0348 case AllowModify: 0349 b = doc->okToChange(); 0350 break; 0351 case AllowCopy: 0352 b = doc->okToCopy(); 0353 break; 0354 case AllowPrint: 0355 b = doc->okToPrint(); 0356 break; 0357 case AllowNotes: 0358 b = doc->okToAddNotes(); 0359 break; 0360 case AllowFillForms: 0361 b = doc->okToFillForm(); 0362 break; 0363 default: 0364 ; 0365 } 0366 return b; 0367 } 0368 0369 QSize PdfDialog::allPagesSize(Poppler::Document *doc) 0370 { 0371 QSize commonsize = QSize(0,0); 0372 0373 // Access all pages of the PDF file (m_numpages is known) 0374 for ( int i=0; i<m_numpages; ++i ) { 0375 Poppler::Page *pdfpage = doc->page(i); 0376 if ( pdfpage == 0 ) { 0377 KILE_DEBUG_MAIN << "Cannot parse all pages of the PDF file"; 0378 delete pdfpage; 0379 return QSize(0,0); 0380 } 0381 0382 if ( i == 0 ) { 0383 commonsize = pdfpage->pageSize(); 0384 } else if ( commonsize != pdfpage->pageSize() ) { 0385 delete pdfpage; 0386 return QSize(0,0); 0387 } 0388 // documentation says: after the usage, the page must be deleted 0389 delete pdfpage; 0390 } 0391 0392 return commonsize; 0393 } 0394 #endif 0395 0396 void PdfDialog::setNumberOfPages(int numpages) 0397 { 0398 m_numpages = numpages; 0399 if (m_numpages > 0) { 0400 // show all, if the number of pages is known 0401 m_PdfDialog.tabWidget->widget(0)->setEnabled(true); 0402 0403 if ( m_encrypted ) 0404 m_PdfDialog.m_lbPages->setText(i18nc("%1 is the number of pages", "%1 (encrypted)", QString::number(m_numpages))); 0405 else { 0406 m_PdfDialog.m_lbPages->setText(QString::number(m_numpages)); 0407 } 0408 } 0409 else { 0410 // hide all, if the number of pages can't be determined 0411 m_PdfDialog.tabWidget->widget(0)->setEnabled(false); 0412 m_PdfDialog.m_lbPages->setText(i18n("Error: unknown number of pages")); 0413 } 0414 } 0415 0416 #if !LIBPOPPLER_AVAILABLE 0417 void PdfDialog::determineNumberOfPages(const QString &filename, bool askForPassword) 0418 { 0419 // determine the number of pages of the pdf file (delegate this task) 0420 QString command; 0421 QString passwordparam; 0422 int scriptmode = m_numpagesMode; 0423 0424 if ( scriptmode==PDF_SCRIPTMODE_NUMPAGES_PDFTK && askForPassword ) { 0425 QString password = QInputDialog::getText(this, i18n("PDFTK-Password"), 0426 i18n("This PDF file is encrypted and 'pdftk' cannot open it.\n" 0427 "Please enter the password for this PDF file\n or leave it blank to try another method: "), 0428 QLineEdit::Normal, QString()).trimmed(); 0429 if(!password.isEmpty()) { 0430 passwordparam = " input_pw " + password; 0431 } 0432 else { 0433 scriptmode = ( m_imagemagick ) ? PDF_SCRIPTMODE_NUMPAGES_IMAGEMAGICK : PDF_SCRIPTMODE_NUMPAGES_GHOSTSCRIPT; 0434 } 0435 } 0436 0437 // now take the original or changed mode 0438 if ( scriptmode == PDF_SCRIPTMODE_NUMPAGES_PDFTK ) { 0439 command = "pdftk \"" + filename + "\"" + passwordparam + " dump_data | grep NumberOfPages"; 0440 } 0441 else if ( scriptmode == PDF_SCRIPTMODE_NUMPAGES_IMAGEMAGICK ) { 0442 command = "identify -format \"%n\" \"" + filename + "\""; 0443 } 0444 else { 0445 command = "gs -q -c \"(" + filename + ") (r) file runpdfbegin pdfpagecount = quit\""; 0446 } 0447 0448 // run Process 0449 KILE_DEBUG_MAIN << "execute for NumberOfPages: " << command; 0450 executeScript(command, m_tempdir->path(), scriptmode); 0451 } 0452 0453 void PdfDialog::readNumberOfPages(int scriptmode, const QString &output) 0454 { 0455 int numpages = 0; 0456 0457 bool ok; 0458 if ( scriptmode == PDF_SCRIPTMODE_NUMPAGES_PDFTK ) { 0459 KILE_DEBUG_MAIN << "pdftk output for NumberOfPages: " << output; 0460 if ( output.contains("OWNER PASSWORD REQUIRED") ) { 0461 determineNumberOfPages(m_PdfDialog.m_edInfile->lineEdit()->text().trimmed(),true); 0462 return; 0463 } else { 0464 QRegExp re("\\d+"); 0465 if ( re.indexIn(output) >= 0) { 0466 numpages = re.cap(0).toInt(&ok); 0467 } 0468 } 0469 0470 } 0471 else { 0472 QString s = output; 0473 numpages = s.remove('\n').toInt(&ok); 0474 } 0475 0476 setNumberOfPages(numpages); 0477 } 0478 0479 bool PdfDialog::readEncryption(const QString &filename) 0480 { 0481 QFile file(filename); 0482 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 0483 return false; 0484 } 0485 0486 KILE_DEBUG_MAIN << "search for encryption "; 0487 QRegExp re("/Encrypt(\\W|\\s|$)"); 0488 QTextStream in(&file); 0489 QString line = in.readLine(); 0490 while ( !line.isNull() ) { 0491 if ( re.indexIn(line) >= 0 ) { 0492 KILE_DEBUG_MAIN << "pdf file is encrypted !!!"; 0493 return true; 0494 } 0495 line = in.readLine(); 0496 } 0497 return false; 0498 } 0499 #endif 0500 0501 void PdfDialog::clearDocumentInfo() 0502 { 0503 m_numpages = 0; 0504 m_encrypted = false; 0505 m_PdfDialog.m_lbPassword->setEnabled(false); 0506 m_PdfDialog.m_edPassword->setEnabled(false); 0507 m_PdfDialog.m_edPassword->clear(); 0508 0509 for (QStringList::const_iterator it = m_pdfInfoKeys.constBegin(); it != m_pdfInfoKeys.constEnd(); ++it) { 0510 m_pdfInfoWidget[*it]->clear(); 0511 } 0512 0513 m_PdfDialog.m_lbCreationDate->clear(); 0514 m_PdfDialog.m_lbModDate->clear(); 0515 0516 for (int i=0; i<m_pdfPermissionKeys.size(); ++i) { 0517 m_pdfPermissionWidgets.at(i)->setChecked(false); 0518 } 0519 0520 m_PdfDialog.m_lbPages->clear(); 0521 m_PdfDialog.m_lbFormat->clear(); 0522 m_PdfDialog.m_lbEncryption->clear(); 0523 } 0524 0525 void PdfDialog::updateOwnerPassword(bool infile_exists) 0526 { 0527 int tabindex = m_PdfDialog.tabWidget->currentIndex(); 0528 bool state = ( infile_exists && (m_encrypted || (!m_encrypted && tabindex==2)) ) ? m_pdftk : false; 0529 m_PdfDialog.m_lbPassword->setEnabled(state); 0530 m_PdfDialog.m_edPassword->setEnabled(state); 0531 } 0532 0533 // update dialog widgets 0534 void PdfDialog::updateDialog() 0535 { 0536 QString infile = m_PdfDialog.m_edInfile->lineEdit()->text().trimmed(); 0537 bool infile_exists = QFile(infile).exists(); 0538 0539 updateOwnerPassword(infile_exists); 0540 updateTasks(); 0541 updateToolsInfo(); 0542 0543 bool pstate = ( m_encrypted ) ? infile_exists && m_pdftk : infile_exists && (m_pdftk || m_pdfpages); 0544 m_PdfDialog.m_gbParameter->setEnabled(pstate); 0545 0546 m_PdfDialog.m_gbProperties->setEnabled(infile_exists); 0547 m_PdfDialog.m_gbPermissions->setEnabled(infile_exists); 0548 m_PdfDialog.m_lbPrinting->setEnabled(infile_exists); 0549 m_PdfDialog.m_pbPrinting->setEnabled(infile_exists); 0550 m_PdfDialog.m_lbAll->setEnabled(infile_exists); 0551 m_PdfDialog.m_pbAll->setEnabled(infile_exists); 0552 0553 // and exec button 0554 QString outfile = m_PdfDialog.m_edOutfile->lineEdit()->text().trimmed(); 0555 bool destination = m_PdfDialog.m_cbOverwrite->isChecked() || m_PdfDialog.m_cbView->isChecked(); 0556 0557 bool state = ( infile_exists && (destination || (!destination && !outfile.isEmpty())) ); 0558 if ( m_PdfDialog.tabWidget->currentIndex() == 0 ) { 0559 state = state && (m_pdfpages || m_pdftk); 0560 } 0561 else { 0562 state = state && m_pdftk; 0563 } 0564 m_rearrangeButton->setEnabled(state&&!m_scriptrunning); 0565 } 0566 0567 // update tools information 0568 void PdfDialog::updateToolsInfo() 0569 { 0570 QString info; 0571 QString newline = "<br>"; 0572 QString password = i18n("A password is necessary to set or change the current settings."); 0573 0574 int tabindex = m_PdfDialog.tabWidget->currentIndex(); 0575 if (tabindex == 2 ) { 0576 info = ( m_pdftk ) ? i18n("The permissions of this document can be changed with <i>pdftk</i>.") + newline + password 0577 : i18n("<i>pdftk</i> is not available, so no permission can be changed."); 0578 } 0579 else if ( tabindex == 1 ) { 0580 if ( ! m_pdftk ) { 0581 info = i18n("<i>pdftk</i> is not available, so no property can be changed."); 0582 } 0583 else { 0584 info = i18n("The properties of this document can be changed with <i>pdftk</i>."); 0585 if ( m_encrypted ) { 0586 info += newline + password; 0587 } 0588 } 0589 } 0590 else { // if ( tabindex == 0 ) 0591 if ( m_encrypted ) { 0592 info = ( m_pdftk ) ? i18n("This input file is encrypted, so only <i>pdftk</i> works.") + newline 0593 + i18n("A password is necessary to rearrange pages.") 0594 : i18n("This input file is encrypted, but <i>pdftk</i> is not installed."); 0595 } 0596 else { 0597 if ( m_pdftk ) { // not encrypted and pdftk 0598 info = ( m_pdfpages ) ? i18n("This wizard will use <i>pdftk</i> and the LaTeX package <i>pdfpages</i>.") 0599 : i18n("This wizard will only use <i>pdftk</i> (<i>pdfpages.sty</i> is not installed)."); 0600 } 0601 else { // not encrypted and not pdftk 0602 info = ( m_pdfpages ) ? i18n("This wizard will only use the LaTeX package <i>pdfpages</i> (<i>pdftk</i> was not found).") 0603 : i18n("This wizard can't work, because no tool was found (see help section)."); 0604 } 0605 } 0606 } 0607 0608 QString popplerinfo = (m_poppler ) ? QString() : newline + i18n("<i>(Compiled without libpoppler pdf library. Not all tasks are available.)</i>"); 0609 info += popplerinfo; 0610 0611 // set info text 0612 m_PdfDialog.m_lbParameterInfo->setText(info); 0613 } 0614 0615 // it is important to calculate the task index from the combobox index, 0616 // as not all tasks are available, when an utility was not found 0617 void PdfDialog::updateTasks() 0618 { 0619 // according to QT 4.4 docu the index of QComboBox might change if adding or removing items 0620 // but because we populate the QComboBox before we start the dialog, we can use the index here 0621 int lastindex = m_cbTask->currentIndex(); 0622 QString lasttext = m_cbTask->currentText(); 0623 0624 int group = 0; 0625 m_cbTask->clear(); 0626 if (m_pdfpages && !m_encrypted) { // task index 0627 m_cbTask->addItem( m_tasklist[PDF_PAGE_EMPTY] ); // 0 PDF_PAGE_EMPTY 0628 m_cbTask->addItem( m_tasklist[PDF_PAGE_DUPLICATE] ); // 1 PDF_PAGE_DUPLICATE 0629 m_cbTask->addItem( m_tasklist[PDF_2UP] ); // 2 PDF_2UP 0630 m_cbTask->addItem( m_tasklist[PDF_2UP_LANDSCAPE] ); // 3 PDF_2UP_LANDSCAPE 0631 m_cbTask->addItem( m_tasklist[PDF_4UP] ); // 4 PDF_4UP 0632 m_cbTask->addItem( m_tasklist[PDF_4UP_LANDSCAPE] ); // 5 PDF_4UP_LANDSCAPE 0633 group = 1; 0634 } 0635 0636 if ( (m_pdfpages && !m_encrypted) || m_pdftk ) { 0637 if ( group > 0 ) { 0638 m_cbTask->addCategoryItem(""); 0639 } 0640 m_cbTask->addItem( m_tasklist[PDF_EVEN] ); // 6 PDF_EVEN 0641 m_cbTask->addItem( m_tasklist[PDF_ODD] ); // 7 PDF_ODD 0642 m_cbTask->addItem( m_tasklist[PDF_EVEN_REV] ); // 8 PDF_EVEN_REV 0643 m_cbTask->addItem( m_tasklist[PDF_ODD_REV] ); // 9 PDF_ODD_REV 0644 m_cbTask->addItem( m_tasklist[PDF_REVERSE] ); // 10 PDF_REVERSE 0645 if (m_encrypted) { 0646 m_cbTask->addItem( m_tasklist[PDF_DECRYPT] ); // 11 PDF_DECRYPT 0647 } 0648 m_cbTask->addCategoryItem(""); 0649 m_cbTask->addItem( m_tasklist[PDF_SELECT] ); // 12 PDF_SELECT 0650 m_cbTask->addItem( m_tasklist[PDF_DELETE] ); // 13 PDF_DELETE 0651 group = 2; 0652 } 0653 0654 if (m_pdftk) { 0655 m_cbTask->addCategoryItem(""); 0656 m_cbTask->addItem( m_tasklist[PDF_PDFTK_BACKGROUND] ); // 14 PDF_PDFTK_BACKGROUND 0657 if ( ! m_pagesize.isNull() ) { 0658 m_cbTask->addItem( m_tasklist[PDF_PDFTK_BGCOLOR] ); // 15 PDF_PDFTK_BGCOLOR 0659 } 0660 m_cbTask->addItem( m_tasklist[PDF_PDFTK_STAMP] ); // 16 PDF_PDFTK_STAMP 0661 m_cbTask->addCategoryItem(""); 0662 m_cbTask->addItem( m_tasklist[PDF_PDFTK_FREE] ); // 17 PDF_PDFTK_FREE 0663 group = 3; 0664 } 0665 0666 if (m_pdfpages && !m_encrypted) { 0667 if ( group < 3 ) { 0668 m_cbTask->addCategoryItem(""); 0669 } 0670 m_cbTask->addItem( m_tasklist[PDF_PDFPAGES_FREE] ); // 17 PDF_PDFPAGES_FREE 0671 } 0672 0673 // choose one common task (need to calculate the combobox index) 0674 int index = m_cbTask->findText(lasttext); 0675 if ( lastindex==-1 || index==-1 ) { 0676 int lastTask = KileConfig::pdfWizardLastTask(); 0677 int task = ( lastTask < m_cbTask->count() ) ? lastTask : PDF_SELECT; 0678 index = m_cbTask->findText(m_tasklist[task]); 0679 if ( index == -1 ) { 0680 index = 0; 0681 } 0682 } 0683 0684 m_cbTask->setCurrentIndex(index); 0685 slotTaskChanged(index); 0686 0687 setFocusProxy(m_PdfDialog.m_edInfile); 0688 m_PdfDialog.m_edInfile->setFocus(); 0689 } 0690 0691 QString PdfDialog::getOutfileName(const QString &infile) 0692 { 0693 return ( infile.isEmpty() ) ? QString() : infile.left(infile.length()-4) + "-out" + ".pdf"; 0694 } 0695 0696 // calculate task index from comboxbox index 0697 int PdfDialog::taskIndex() 0698 { 0699 return m_tasklist.indexOf( m_cbTask->currentText() ); 0700 } 0701 0702 void PdfDialog::setPermissions(bool print, bool other) 0703 { 0704 for (int i = 0; i<m_pdfPermissionKeys.size(); ++i) { 0705 QCheckBox *box = m_pdfPermissionWidgets.at(i); 0706 bool state = ( box == m_PdfDialog.m_cbPrinting ) ? print : other; 0707 box->setChecked(state); 0708 } 0709 } 0710 0711 // read permissions 0712 QString PdfDialog::readPermissions() 0713 { 0714 QString permissions; 0715 for (int i = 0; i < m_pdfPermissionKeys.size(); ++i) { 0716 if ( m_pdfPermissionWidgets.at(i)->isChecked() ) { 0717 permissions += m_pdfPermissionPdftk.at(i) + ' '; 0718 } 0719 } 0720 return permissions; 0721 } 0722 0723 //-------------------- slots -------------------- 0724 0725 void PdfDialog::slotTabwidgetChanged(int index) 0726 { 0727 m_rearrangeButton->setText(index == 0 ? i18n("Re&arrange") : i18n("&Update")); 0728 updateDialog(); 0729 } 0730 0731 void PdfDialog::slotPrintingClicked() 0732 { 0733 if ( m_pdftk ) { 0734 setPermissions(true, false); 0735 } 0736 } 0737 0738 void PdfDialog::slotAllClicked() 0739 { 0740 if ( m_pdftk ) { 0741 setPermissions(true, true); 0742 } 0743 } 0744 0745 void PdfDialog::slotPermissionClicked(bool) 0746 { 0747 for (int i = 0; i < m_pdfPermissionKeys.size(); ++i) { 0748 QCheckBox *box = m_pdfPermissionWidgets.at(i); 0749 if ( box->isChecked() != m_pdfPermissionState[i] ) { 0750 box->setChecked( m_pdfPermissionState[i] ); 0751 } 0752 } 0753 } 0754 0755 void PdfDialog::slotInputfileChanged(const QString &text) 0756 { 0757 clearDocumentInfo(); 0758 if ( QFile(text).exists() ) { 0759 m_PdfDialog.m_edOutfile->lineEdit()->setText( getOutfileName(text) ); 0760 pdfParser(text); 0761 } 0762 0763 updateDialog(); 0764 } 0765 0766 void PdfDialog::slotOverwriteChanged(int state) 0767 { 0768 bool checked = (state!=Qt::Checked); 0769 m_PdfDialog.m_lbOutfile->setEnabled(checked); 0770 m_PdfDialog.m_edOutfile->setEnabled(checked); 0771 0772 updateDialog(); 0773 } 0774 0775 void PdfDialog::slotOutputfileChanged(const QString &) 0776 { 0777 updateDialog(); 0778 } 0779 0780 void PdfDialog::slotTaskChanged(int) 0781 { 0782 if ( m_PdfDialog.tabWidget->currentIndex() > 0 ) { 0783 return; 0784 } 0785 0786 int taskindex = taskIndex(); 0787 if ( isParameterTask(taskindex) ) { 0788 QString s,labeltext; 0789 if ( taskindex==PDF_SELECT || taskindex==PDF_DELETE ) { 0790 labeltext = i18n("Pages:"); 0791 s = i18n("Comma separated page list: 1,4-7,9"); 0792 QRegExp re("((\\d+(-\\d+)?),)*\\d+(-\\d+)?"); 0793 m_PdfDialog.m_edParameter->setValidator(new QRegExpValidator(re, m_PdfDialog.m_edParameter)); 0794 } 0795 else if (taskindex==PDF_PDFTK_FREE) { 0796 labeltext = i18n("Parameter:"); 0797 s = i18n("All options for 'pdftk'"); 0798 m_PdfDialog.m_edParameter->setValidator(0); 0799 } 0800 else { //if (taskindex==PDF_PDFPAGES_FREE) { 0801 labeltext = i18n("Parameter:"); 0802 s = i18n("All options for 'pdfpages'"); 0803 m_PdfDialog.m_edParameter->setValidator(0); 0804 } 0805 m_PdfDialog.m_lbParamInfo->setText(" (" + s + ')'); 0806 0807 m_PdfDialog.m_lbParameter->setText(labeltext); 0808 m_PdfDialog.m_lbParameter->show(); 0809 m_PdfDialog.m_edParameter->clear(); 0810 m_PdfDialog.m_edParameter->show(); 0811 m_PdfDialog.m_lbParamInfo->show(); 0812 } 0813 else { 0814 m_PdfDialog.m_lbParameter->hide(); 0815 m_PdfDialog.m_edParameter->hide(); 0816 m_PdfDialog.m_lbParamInfo->hide(); 0817 } 0818 0819 if ( isOverlayTask(taskindex) ) { 0820 m_PdfDialog.m_lbStamp->show(); 0821 m_PdfDialog.m_edStamp->show(); 0822 0823 if ( taskindex == PDF_PDFTK_BACKGROUND ) { 0824 m_PdfDialog.m_edStamp->setWhatsThis(i18n("Applies a PDF watermark to the background of a single input PDF. " 0825 "Pdftk uses only the first page from the background PDF and applies it to every page of the input PDF. " 0826 "This page is scaled and rotated as needed to fit the input page.") ); 0827 } 0828 else if ( taskindex == PDF_PDFTK_STAMP ) { 0829 m_PdfDialog.m_edStamp->setWhatsThis( i18n("Applies a foreground stamp on top of the input PDF document's pages. " 0830 "Pdftk uses only the first page from the stamp PDF and applies it to every page of the input PDF. " 0831 "This page is scaled and rotated as needed to fit the input page. " 0832 "This works best if the stamp PDF page has a transparent background.") ); 0833 } 0834 } 0835 else { 0836 m_PdfDialog.m_lbStamp->hide(); 0837 m_PdfDialog.m_edStamp->hide(); 0838 } 0839 0840 if (isBackgroundColor(taskindex)) { 0841 m_PdfDialog.m_lbBackgroundColor->show(); 0842 m_PdfDialog.m_pbBackgroundColor->show(); 0843 } 0844 else { 0845 m_PdfDialog.m_lbBackgroundColor->hide(); 0846 m_PdfDialog.m_pbBackgroundColor->hide(); 0847 } 0848 if (isOverlayTask(taskindex) || isBackgroundColor(taskindex) || isFreeTask(taskindex)) { 0849 m_rearrangeButton->setText(i18n("&Apply")); 0850 } 0851 else { 0852 m_rearrangeButton->setText(i18n("Re&arrange")); 0853 } 0854 } 0855 0856 // execute commands 0857 void PdfDialog::slotExecute() 0858 { 0859 if(!m_tempdir) { 0860 // create tempdir 0861 m_tempdir = new QTemporaryDir(QDir::tempPath() + QLatin1String("/kile-pdfwizard")); 0862 m_tempdir->setAutoRemove(true); 0863 KILE_DEBUG_MAIN << "tempdir: " << m_tempdir->path(); 0864 } 0865 0866 if(!m_tempdir->isValid()) { 0867 KMessageBox::error(this, i18n("Failed to create a temporary directory.\n\nThis wizard cannot be used.")); 0868 reject(); 0869 return; 0870 } 0871 0872 int tabindex = m_PdfDialog.tabWidget->currentIndex(); 0873 0874 switch (tabindex) { 0875 case 0: 0876 if (checkParameter()) { 0877 executeAction(); 0878 } 0879 break; 0880 case 1: 0881 if (checkProperties()) { 0882 executeProperties(); 0883 } 0884 break; 0885 case 2: 0886 if (checkPermissions()) { 0887 executePermissions(); 0888 } 0889 break; 0890 } 0891 } 0892 0893 void PdfDialog::slotShowHelp() 0894 { 0895 QString message = i18n("<center>PDF-Wizard</center><br>" 0896 "This wizard uses 'pdftk' and the LaTeX package 'pdfpages' to" 0897 "<ul>" 0898 "<li>rearrange pages of an existing PDF document</li>" 0899 "<li>read and update documentinfo of a PDF document (only pdftk)</li>" 0900 "<li>read, set or change some permissions of a PDF document (only pdftk). " 0901 "A password is necessary to set or change this document settings. " 0902 "Additionally PDF encryption is done to lock the file's content behind this password.</li>" 0903 "</ul>" 0904 "<p>The package 'pdfpages' will only work with non-encrypted documents. " 0905 "'pdftk' can handle both kind of documents, but a password is needed for encrypted files. " 0906 "If one of 'pdftk' or 'pdfpages' is not available, the possible rearrangements are reduced.</p>" 0907 "<p><i>Warning:</i> Encryption and a password does not provide any real PDF security. The content " 0908 "is encrypted, but the key is known. You should see it more as a polite but firm request " 0909 "to respect the author's wishes.</p>"); 0910 0911 #if !LIBPOPPLER_AVAILABLE 0912 message += i18n("<p><i>Information: </i>This version of Kile was compiled without libpoppler library. " 0913 "Setting, changing and removing of properties and permissions is not possible.</p>"); 0914 #endif 0915 0916 KMessageBox::information(this, message, i18n("PDF Tools")); 0917 } 0918 0919 void PdfDialog::executeAction() 0920 { 0921 QString command = buildActionCommand(); 0922 if ( command.isEmpty() ) { 0923 return; 0924 } 0925 0926 m_errorHandler->clearMessages(); 0927 QFileInfo from(m_inputfile); 0928 QFileInfo to(m_outputfile); 0929 0930 // output for log window 0931 QString program = (m_execLatex) ? i18n("LaTeX with 'pdfpages' package") : i18n("pdftk"); 0932 QString msg = i18n("Rearranging PDF file: %1", from.fileName()); 0933 if (!to.fileName().isEmpty()) 0934 msg += " ---> " + to.fileName(); 0935 m_errorHandler->printMessage(KileTool::Info, msg, program); 0936 0937 // some output logs 0938 m_output->clear(); 0939 QString s = QString("*****\n") 0940 + i18n("***** tool: ") + program + '\n' 0941 + i18n("***** input file: ") + from.fileName()+ '\n' 0942 + i18n("***** output file: ") + to.fileName()+ '\n' 0943 + i18n("***** param: ") + m_param + '\n' 0944 + i18n("***** command: ") + command + '\n' 0945 + i18n("***** viewer: ") + ((m_PdfDialog.m_cbView->isChecked()) ? i18n("yes") : i18n("no")) + '\n' 0946 + "*****\n"; 0947 emit( output(s) ); 0948 0949 // run Process 0950 executeScript(command, m_tempdir->path(), PDF_SCRIPTMODE_ACTION); 0951 } 0952 0953 void PdfDialog::executeProperties() 0954 { 0955 // create temporary file 0956 QTemporaryFile infotemp(m_tempdir->path() + QLatin1String("/kile-pdfdialog-XXXXXX.txt")); 0957 infotemp.setAutoRemove(false); 0958 0959 if(!infotemp.open()) { 0960 KILE_DEBUG_MAIN << "Could not create tempfile for key/value pairs in QString PdfDialog::executeProperties()" ; 0961 return; 0962 } 0963 QString infofile = infotemp.fileName(); 0964 0965 // create a text file with key/value pairs for pdftk 0966 QTextStream infostream(&infotemp); 0967 for (QStringList::const_iterator it = m_pdfInfoKeys.constBegin(); it != m_pdfInfoKeys.constEnd(); ++it) { 0968 infostream << "InfoKey: " << (*it) << "\n"; 0969 infostream << "InfoValue: " << m_pdfInfoWidget[*it]->text().trimmed() << "\n"; 0970 } 0971 // add modification Date 0972 QString datetime = QDateTime::currentDateTimeUtc().toString("%Y%m%d%H%M%S%:z"); 0973 datetime = datetime.replace(":","'"); 0974 infostream << "InfoKey: " << "ModDate" << "\n"; 0975 infostream << "InfoValue: " << "D:" << datetime << "'\n"; 0976 infotemp.close(); 0977 0978 // build command 0979 QString inputfile = m_PdfDialog.m_edInfile->lineEdit()->text().trimmed(); 0980 QString password = m_PdfDialog.m_edPassword->text().trimmed(); 0981 QString pdffile = m_tempdir->path() + QFileInfo(m_inputfile).baseName() + "-props.pdf"; 0982 0983 // read permissions 0984 QString permissions = readPermissions(); 0985 0986 // build param 0987 QString param = "\"" + inputfile + "\""; 0988 if ( m_encrypted ) { 0989 param += " input_pw " + password; 0990 } 0991 0992 param += " update_info " + infofile + " output \"" + pdffile+ "\""; 0993 if ( m_encrypted ) { 0994 param += " encrypt_128bit"; 0995 if ( !permissions.isEmpty() ) 0996 param += " allow " + permissions; 0997 param += " owner_pw " + password; 0998 } 0999 QString command = "pdftk " + param; 1000 1001 // move destination file 1002 m_move_filelist.clear(); 1003 m_move_filelist << pdffile << inputfile; 1004 1005 // execute script 1006 showLogs("Updating properties", inputfile, param); 1007 executeScript(command, QString(), PDF_SCRIPTMODE_PROPERTIES); 1008 1009 } 1010 1011 void PdfDialog::executePermissions() 1012 { 1013 // read permissions 1014 QString permissions = readPermissions(); 1015 1016 // build command 1017 QString inputfile = m_PdfDialog.m_edInfile->lineEdit()->text().trimmed(); 1018 QString password = m_PdfDialog.m_edPassword->text().trimmed(); 1019 QString pdffile = m_tempdir->path() + QFileInfo(m_inputfile).baseName() + "-perms.pdf"; 1020 1021 QString param = "\"" + inputfile + "\""; 1022 if ( m_encrypted ) { 1023 param += " input_pw " + password; 1024 } 1025 param += " output \"" + pdffile + "\" encrypt_128bit"; 1026 if ( !permissions.isEmpty() ) { 1027 param += " allow " + permissions; 1028 } 1029 param += " owner_pw " + password; 1030 QString command = "pdftk " + param; 1031 1032 // move destination file 1033 m_move_filelist.clear(); 1034 m_move_filelist << pdffile << inputfile; 1035 1036 // execute script 1037 showLogs("Updating permissions", inputfile, param); 1038 executeScript(command, QString(), PDF_SCRIPTMODE_PERMISSIONS); 1039 1040 } 1041 1042 void PdfDialog::showLogs(const QString &title, const QString &inputfile, const QString ¶m) 1043 { 1044 // some info for log widget 1045 m_errorHandler->clearMessages(); 1046 m_errorHandler->printMessage(KileTool::Info, title, "pdftk" ); 1047 1048 // some info for output widget 1049 QFileInfo input(inputfile); 1050 m_output->clear(); 1051 QString s = QString("*****\n") 1052 + i18n("***** tool: ") + "pdftk" + '\n' 1053 + i18n("***** input file: ") + input.fileName()+ '\n' 1054 + i18n("***** param: ") + param + '\n' 1055 + "*****\n"; 1056 emit( output(s) ); 1057 } 1058 1059 void PdfDialog::executeScript(const QString &command, const QString &dir, int scriptmode) 1060 { 1061 // delete old KProcess 1062 if(m_proc) { 1063 delete m_proc; 1064 } 1065 1066 m_scriptmode = scriptmode; 1067 m_outputtext = ""; 1068 1069 m_proc = new KProcess(); 1070 if (!dir.isEmpty()) { 1071 m_proc->setWorkingDirectory(dir); 1072 } 1073 m_proc->setShellCommand(command); 1074 m_proc->setOutputChannelMode(KProcess::MergedChannels); 1075 m_proc->setReadChannel(QProcess::StandardOutput); 1076 1077 connect(m_proc, &QProcess::readyReadStandardOutput, 1078 this, &PdfDialog::slotProcessOutput); 1079 1080 connect(m_proc, &QProcess::readyReadStandardError, 1081 this, &PdfDialog::slotProcessOutput); 1082 1083 connect(m_proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), 1084 this, &PdfDialog::slotProcessExited); 1085 1086 connect(m_proc, &QProcess::errorOccurred, 1087 this, [this]() { slotProcessExited(-1, QProcess::CrashExit); }); 1088 1089 KILE_DEBUG_MAIN << "=== PdfDialog::runPdfutils() ===================="; 1090 KILE_DEBUG_MAIN << "execute '" << command << "'"; 1091 m_scriptrunning = true; 1092 m_rearrangeButton->setEnabled(false); 1093 m_buttonBox->button(QDialogButtonBox::Close)->setEnabled(false); 1094 m_proc->start(); 1095 } 1096 1097 void PdfDialog::slotProcessOutput() 1098 { 1099 m_outputtext += m_proc->readAll(); 1100 } 1101 1102 1103 void PdfDialog::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) 1104 { 1105 if(exitCode != 0 || exitStatus != QProcess::NormalExit) { 1106 if (m_scriptmode != PDF_SCRIPTMODE_TOOLS) 1107 showError(i18n("An error occurred while executing the task.")); 1108 } 1109 else { 1110 bool state = ( exitCode == 0 ); 1111 if ( m_scriptmode == PDF_SCRIPTMODE_TOOLS ) { 1112 initUtilities(); 1113 } 1114 #if !LIBPOPPLER_AVAILABLE 1115 else if ( m_scriptmode==PDF_SCRIPTMODE_NUMPAGES_PDFTK 1116 || m_scriptmode==PDF_SCRIPTMODE_NUMPAGES_IMAGEMAGICK 1117 || m_scriptmode==PDF_SCRIPTMODE_NUMPAGES_GHOSTSCRIPT ) { 1118 readNumberOfPages(m_scriptmode,m_outputtext); 1119 } 1120 #endif 1121 else { 1122 finishPdfAction(state); 1123 } 1124 } 1125 1126 m_scriptrunning = false; 1127 m_buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); 1128 updateDialog(); 1129 } 1130 1131 void PdfDialog::finishPdfAction(bool state) 1132 { 1133 // output window 1134 emit( output(m_outputtext) ); 1135 1136 // log window 1137 QString program = (m_scriptmode==PDF_SCRIPTMODE_ACTION && m_execLatex) ? "LaTeX with 'pdfpages' package" : "pdftk"; 1138 1139 if ( state ) { 1140 m_errorHandler->printMessage(KileTool::Info, "finished", program); 1141 1142 // should we move the temporary pdf file 1143 if ( ! m_move_filelist.isEmpty() ) { 1144 QFile::remove( m_move_filelist[1] ); 1145 QFile::rename( m_move_filelist[0], m_move_filelist[1] ); 1146 KILE_DEBUG_MAIN << "move file: " << m_move_filelist[0] << " ---> " << m_move_filelist[1]; 1147 } 1148 1149 // run viewer 1150 if ( m_PdfDialog.m_cbView->isChecked() && m_scriptmode==PDF_SCRIPTMODE_ACTION ) { 1151 runViewer(); 1152 } 1153 1154 // file properties/permissions could be changed 1155 if ( (m_scriptmode==PDF_SCRIPTMODE_ACTION && m_PdfDialog.m_cbOverwrite->isChecked()) 1156 || m_scriptmode==PDF_SCRIPTMODE_PROPERTIES || m_scriptmode==PDF_SCRIPTMODE_PERMISSIONS ) { 1157 slotInputfileChanged( m_PdfDialog.m_edInfile->lineEdit()->text().trimmed() ); 1158 } 1159 } 1160 else { 1161 QString msg; 1162 if (m_outputtext.indexOf("OWNER PASSWORD") >= 0 ) { 1163 msg = i18n("Finished with an error (wrong password)"); 1164 } 1165 else { 1166 msg = i18n("Finished with an error"); 1167 } 1168 m_errorHandler->printMessage(KileTool::Error, msg, program); 1169 } 1170 } 1171 1172 void PdfDialog::runViewer() 1173 { 1174 m_errorHandler->printMessage(KileTool::Info, "Running viewer", "ViewPDF"); 1175 1176 // call ViewPDF 1177 QString cfg = KileTool::configName("ViewPDF", m_manager->config()); 1178 KileTool::View *tool = dynamic_cast<KileTool::View*>(m_manager->createTool("ViewPDF", cfg, false)); 1179 if(!tool) { 1180 m_errorHandler->printMessage(KileTool::Error, i18n("Could not create the ViewPDF tool"), i18n("ViewPDF")); 1181 return; 1182 } 1183 tool->setFlags(0); 1184 tool->setSource(m_outputfile); 1185 m_manager->run(tool); 1186 } 1187 1188 QString PdfDialog::buildActionCommand() 1189 { 1190 // build action: parameter 1191 m_execLatex = true; // default 1192 m_inputfile = m_PdfDialog.m_edInfile->lineEdit()->text().trimmed(); 1193 m_outputfile = m_PdfDialog.m_edOutfile->lineEdit()->text().trimmed(); 1194 1195 QColor bgcolor; 1196 QString bgfile; 1197 int taskindex = taskIndex(); 1198 switch (taskindex) { 1199 case PDF_PAGE_EMPTY: 1200 m_param = "nup=1x2,landscape,pages=" + buildPageRange(PDF_PAGE_EMPTY); 1201 break; 1202 1203 case PDF_PAGE_DUPLICATE: 1204 m_param = "nup=1x2,landscape,pages=" + buildPageRange(PDF_PAGE_DUPLICATE); 1205 break; 1206 1207 case PDF_2UP: 1208 m_param = "nup=1x2,landscape,pages=1-"; 1209 break; 1210 1211 case PDF_2UP_LANDSCAPE: 1212 m_param = "nup=1x2,pages=1-"; 1213 break; 1214 1215 case PDF_4UP: 1216 m_param = "nup=2x2,pages=1-"; 1217 break; 1218 1219 case PDF_4UP_LANDSCAPE: 1220 m_param = "nup=2x2,landscape,pages=1-"; 1221 break; 1222 1223 case PDF_EVEN: 1224 if ( m_pdftk ) { 1225 m_param = "cat 1-endeven"; 1226 m_execLatex = false; 1227 } 1228 else { 1229 m_param = buildPageList(true); 1230 } 1231 break; 1232 1233 case PDF_ODD: 1234 if ( m_pdftk ) { 1235 m_param = "cat 1-endodd"; 1236 m_execLatex = false; 1237 } 1238 else { 1239 m_param = buildPageList(false); 1240 } 1241 break; 1242 1243 case PDF_EVEN_REV: 1244 if ( m_pdftk ) { 1245 m_param = "cat end-1even"; 1246 m_execLatex = false; 1247 } 1248 else { 1249 m_param = buildReversPageList(true); 1250 } 1251 break; 1252 1253 case PDF_ODD_REV: 1254 if ( m_pdftk ) { 1255 m_param = "cat end-1odd"; 1256 m_execLatex = false; 1257 } 1258 else { 1259 m_param = buildReversPageList(false); 1260 } 1261 break; 1262 1263 case PDF_REVERSE: 1264 if ( m_pdftk ) { 1265 m_param = "cat end-1"; 1266 m_execLatex = false; 1267 } 1268 else { 1269 m_param = "last-1"; 1270 } 1271 break; 1272 1273 case PDF_DECRYPT: 1274 m_param.clear(); 1275 m_execLatex = false; 1276 break; 1277 1278 case PDF_SELECT: 1279 case PDF_DELETE: 1280 m_param = ( taskindex == PDF_SELECT ) ? buildSelectPageList() : buildDeletePageList(); 1281 if ( m_pdftk ) { 1282 m_param = "cat " + m_param.replace(","," "); 1283 m_execLatex = false; 1284 } 1285 else { 1286 m_param = "pages={" + m_param + "}"; 1287 } 1288 break; 1289 1290 case PDF_PDFTK_BACKGROUND: 1291 m_param = "background \"" + m_PdfDialog.m_edStamp->text().trimmed() + "\""; 1292 m_execLatex = false; 1293 break; 1294 1295 case PDF_PDFTK_BGCOLOR: 1296 bgcolor = m_PdfDialog.m_pbBackgroundColor->color(); 1297 bgfile = buildPdfBackgroundFile(&bgcolor); 1298 m_param = "background " + bgfile; 1299 m_execLatex = false; 1300 break; 1301 1302 case PDF_PDFTK_STAMP: 1303 m_param = "stamp \"" + m_PdfDialog.m_edStamp->text().trimmed() + "\""; 1304 m_execLatex = false; 1305 break; 1306 1307 case PDF_PDFTK_FREE: 1308 m_param = m_PdfDialog.m_edParameter->text().trimmed(); 1309 m_execLatex = false; 1310 break; 1311 1312 case PDF_PDFPAGES_FREE: 1313 m_param = m_PdfDialog.m_edParameter->text().trimmed(); 1314 break; 1315 } 1316 1317 // build action: command 1318 QString command,latexfile,pdffile; 1319 if ( m_execLatex ) { 1320 latexfile = buildLatexFile(m_param); 1321 pdffile = latexfile + ".pdf"; 1322 command = "pdflatex -interaction=nonstopmode " + latexfile + ".tex"; 1323 } 1324 else { 1325 pdffile = m_tempdir->path() + QFileInfo(m_inputfile).baseName() + "-temp.pdf"; 1326 command = "pdftk \"" + m_inputfile + "\""; 1327 if ( m_encrypted ) { 1328 QString password = m_PdfDialog.m_edPassword->text().trimmed(); 1329 command += " input_pw " + password; 1330 } 1331 command += " " + m_param + " output \"" + pdffile+ "\""; 1332 } 1333 1334 // additional actions 1335 bool viewer = m_PdfDialog.m_cbView->isChecked(); 1336 1337 bool equalfiles = (m_PdfDialog.m_cbOverwrite->isChecked() || m_inputfile==m_outputfile); 1338 if (equalfiles) { 1339 m_outputfile = m_inputfile; 1340 } 1341 1342 // move destination file 1343 m_move_filelist.clear(); 1344 if ( equalfiles ) { 1345 m_move_filelist << pdffile << m_inputfile; 1346 } 1347 else if ( !m_outputfile.isEmpty() ) { 1348 m_move_filelist << pdffile << m_outputfile; 1349 } 1350 1351 // viewer 1352 if ( viewer && m_outputfile.isEmpty() ) { 1353 m_outputfile = pdffile; 1354 } 1355 1356 return command; 1357 } 1358 1359 // create a temporary file to run latex with package pdfpages.sty 1360 QString PdfDialog::buildLatexFile(const QString ¶m) 1361 { 1362 QTemporaryFile temp(m_tempdir->path() + QLatin1String("/kile-pdfdialog-XXXXXX.tex")); 1363 temp.setAutoRemove(false); 1364 1365 if(!temp.open()) { 1366 KILE_DEBUG_MAIN << "Could not create tempfile in PdfDialog::buildLatexFile()" ; 1367 return QString(); 1368 } 1369 QString tempname = temp.fileName(); 1370 1371 QTextStream stream(&temp); 1372 stream << "\\documentclass[a4paper,12pt]{article}\n"; 1373 stream << "\\usepackage[final]{pdfpages}\n"; 1374 stream << "\\begin{document}\n"; 1375 stream << "\\includepdf[" << param << "]{" << m_inputfile << "}\n"; 1376 stream << "\\end{document}\n"; 1377 1378 // everything is prepared to do the job 1379 temp.close(); 1380 return(tempname.left(tempname.length() - 4)); 1381 } 1382 1383 // create a temporary pdf file to set a background color 1384 QString PdfDialog::buildPdfBackgroundFile(QColor *color) 1385 { 1386 QTemporaryFile temp(m_tempdir->path() + QLatin1String("/kile-pdfdialog-XXXXXX.pdf")); 1387 temp.setAutoRemove(false); 1388 1389 if(!temp.open()) { 1390 KILE_DEBUG_MAIN << "Could not create tempfile in PdfDialog::buildPdfBackgroundFile()" ; 1391 return QString(); 1392 } 1393 QString tempname = temp.fileName(); 1394 1395 QTextStream stream(&temp); 1396 stream << "%PDF-1.4\n"; 1397 stream << '%' << '\0' << '\0' << '\0' << '\0' << '\r'; 1398 stream << "5 0 obj \n" 1399 "<<\n" 1400 "/Type /ExtGState\n" 1401 "/OPM 1\n" 1402 ">>\n" 1403 "endobj \n" 1404 "4 0 obj \n" 1405 "<<\n" 1406 "/R7 5 0 R\n" 1407 ">>\n" 1408 "endobj \n" 1409 "6 0 obj \n" 1410 "<<\n" 1411 "/Length 83\n" 1412 ">>\n" 1413 "stream\n" 1414 "q 0.1 0 0 0.1 0 0 cm\n" 1415 "/R7 gs\n"; 1416 stream << color->redF() << " " << color->greenF() << " " << color->blueF() << " rg\n"; 1417 stream << "0 0 " << 10*m_pagesize.width() << " " << 10*m_pagesize.height() << " re\n"; 1418 stream << "f\n" 1419 "0 g\n" 1420 "Q\n" 1421 "\n" 1422 "endstream \n" 1423 "endobj \n" 1424 "3 0 obj \n" 1425 "<<\n" 1426 "/Parent 1 0 R\n"; 1427 stream << "/MediaBox [0 0 " << m_pagesize.width() << " " << m_pagesize.height() << "]\n"; 1428 stream << "/Resources \n" 1429 "<<\n" 1430 "/ExtGState 4 0 R\n" 1431 "/ProcSet [/PDF]\n" 1432 ">>\n" 1433 "/pdftk_PageNum 1\n" 1434 "/Type /Page\n" 1435 "/Contents 6 0 R\n" 1436 ">>\n" 1437 "endobj \n" 1438 "1 0 obj \n" 1439 "<<\n" 1440 "/Kids [3 0 R]\n" 1441 "/Count 1\n" 1442 "/Type /Pages\n" 1443 ">>\n" 1444 "endobj \n" 1445 "7 0 obj \n" 1446 "<<\n" 1447 "/Pages 1 0 R\n" 1448 "/Type /Catalog\n" 1449 ">>\n" 1450 "endobj \n" 1451 "8 0 obj \n" 1452 "<<\n" 1453 "/Creator ()\n" 1454 "/Producer ())\n" 1455 "/ModDate ()\n" 1456 "/CreationDate ()\n" 1457 ">>\n" 1458 "endobj xref\n" 1459 "0 9\n" 1460 "0000000000 65535 f \n" 1461 "0000000388 00000 n \n" 1462 "0000000000 65536 n \n" 1463 "0000000231 00000 n \n" 1464 "0000000062 00000 n \n" 1465 "0000000015 00000 n \n" 1466 "0000000095 00000 n \n" 1467 "0000000447 00000 n \n" 1468 "0000000498 00000 n \n" 1469 "trailer\n" 1470 "\n" 1471 "<<\n" 1472 "/Info 8 0 R\n" 1473 "/Root 7 0 R\n" 1474 "/Size 9\n" 1475 "/ID [<4a7c31ef3aeb884b18f59c2037a752f5><54079f85d95a11f3400fe5fc3cfc832b>]\n" 1476 ">>\n" 1477 "startxref\n" 1478 "721\n" 1479 "%%EOF\n"; 1480 1481 // everything is prepared to do the job 1482 temp.close(); 1483 return tempname; 1484 } 1485 1486 QString PdfDialog::buildPageRange(int type) 1487 { 1488 QString s; 1489 for (int i = 1; i <= m_numpages; ++i) { 1490 if (type == PDF_PAGE_EMPTY) { 1491 s += QString("%1,{},").arg(i); 1492 } 1493 else { 1494 s += QString("%1,%2,").arg(i).arg(i); 1495 } 1496 } 1497 1498 return "{" + s.left(s.length()-1) + "}"; 1499 } 1500 1501 QString PdfDialog::buildPageList(bool even) 1502 { 1503 QString s, number; 1504 1505 int start = ( even ) ? 2 : 1; 1506 for (int i=start; i<=m_numpages; i+=2 ) { 1507 s += number.setNum(i) + ','; 1508 } 1509 1510 if ( !s.isEmpty() ) { 1511 s.truncate(s.length()-1); 1512 } 1513 return "{" + s + "}"; 1514 } 1515 1516 QString PdfDialog::buildReversPageList(bool even) 1517 { 1518 QString s,number; 1519 1520 int last = m_numpages; 1521 if ( even ) { 1522 if ( (last & 1) == 1 ) { 1523 last--; 1524 } 1525 } 1526 else { 1527 if ( (last & 1) == 0 ) { 1528 last--; 1529 } 1530 } 1531 1532 for (int i=last; i>=1; i-=2 ) { 1533 s += number.setNum(i) + ","; 1534 } 1535 1536 if ( !s.isEmpty() ) { 1537 s.truncate(s.length()-1); 1538 } 1539 return "{" + s + "}"; 1540 } 1541 1542 QString PdfDialog::buildSelectPageList() 1543 { 1544 return m_PdfDialog.m_edParameter->text().trimmed(); 1545 } 1546 1547 QString PdfDialog::buildDeletePageList() 1548 { 1549 // m_numpages is known 1550 QString param = m_PdfDialog.m_edParameter->text().trimmed(); 1551 QRegExp re("(\\d+)-(\\d+)"); 1552 1553 // analyze delete list 1554 bool ok; 1555 QBitArray arr(m_numpages + 1,false); 1556 QStringList pagelist = param.split(','); 1557 foreach (const QString &s, pagelist) { 1558 if ( s.contains('-') && re.indexIn(s) >= 0 ) { 1559 int from = re.cap(1).toInt(&ok); 1560 int to = re.cap(2).toInt(&ok); 1561 for (int i=from; i<=to; ++i) { 1562 arr.setBit(i); 1563 } 1564 } 1565 else { 1566 arr.setBit(s.toInt(&ok)); 1567 } 1568 } 1569 1570 // build select list 1571 QString result; 1572 int page = 1; 1573 while ( page <= m_numpages ) { 1574 int from = searchPages(&arr,page,m_numpages,true); 1575 if ( from > m_numpages ) { 1576 break; 1577 } 1578 int to = searchPages(&arr,from+1,m_numpages,false) - 1; 1579 if ( !result.isEmpty() ) { 1580 result += ','; 1581 } 1582 if ( from < to ) { 1583 result += QString::number(from) + '-' + QString::number(to); 1584 } 1585 else { 1586 result += QString::number(from); 1587 } 1588 page = to + 1; 1589 } 1590 1591 return result; 1592 } 1593 1594 int PdfDialog::searchPages(QBitArray *arr, int page, int lastpage, bool value) 1595 { 1596 while ( page <= lastpage ) { 1597 if ( arr->at(page) != value ) { 1598 return page; 1599 } 1600 page++; 1601 } 1602 return lastpage + 1; 1603 } 1604 1605 bool PdfDialog::checkParameter() 1606 { 1607 if ( !checkInputFile() ) { 1608 return false; 1609 } 1610 1611 if ( m_encrypted ) { 1612 if ( !checkPassword() ) { 1613 return false; 1614 } 1615 } 1616 1617 // check parameter 1618 int taskindex = taskIndex(); 1619 if ( isParameterTask(taskindex) && m_PdfDialog.m_edParameter->text().trimmed().isEmpty() ) { 1620 showError( i18n("The utility needs some parameters in this mode.") ); 1621 return false; 1622 } 1623 1624 // check select/delete page list (m_numpages is known) 1625 if ( taskindex==PDF_SELECT || taskindex==PDF_DELETE ) { 1626 // m_numpages is known 1627 QString param = m_PdfDialog.m_edParameter->text().trimmed(); 1628 QRegExp re("(\\d+)-(\\d+)"); 1629 1630 // analyze page list 1631 bool ok; 1632 QStringList pagelist = param.split(','); 1633 foreach (const QString &s, pagelist) { 1634 if ( s.contains('-') && re.indexIn(s)>=0 ) { 1635 int from = re.cap(1).toInt(&ok); 1636 int to = re.cap(2).toInt(&ok); 1637 if ( from > to ) { 1638 showError(i18n("Illegal page list 'from-to': %1 is bigger than %2.",from,to)); 1639 return false; 1640 } 1641 if ( to > m_numpages ) { 1642 showError(i18n("Illegal pagenumber: %1.",to)); 1643 return false; 1644 } 1645 } 1646 else { 1647 int page = s.toInt(&ok); 1648 if ( page > m_numpages ) { 1649 showError(i18n("Illegal pagenumber: %1.",page)); 1650 return false; 1651 } 1652 } 1653 } 1654 } 1655 1656 // check background/stamp parameter 1657 if ( isOverlayTask(taskindex) ) { 1658 QString filename = m_PdfDialog.m_edStamp->text().trimmed(); 1659 1660 if ( filename.isEmpty() ) { 1661 QString message = ( taskindex == PDF_PDFTK_STAMP ) 1662 ? i18n("You need to define a PDF file as foreground stamp.") 1663 : i18n("You need to define a PDF file as background watermark."); 1664 showError(message); 1665 return false; 1666 } 1667 1668 QFileInfo fs(filename); 1669 if (fs.completeSuffix() != "pdf") { 1670 showError(i18n("Unknown file format: only '.pdf' is accepted as image file in this mode.")); 1671 return false; 1672 } 1673 1674 if ( !QFile::exists(filename) ) { 1675 showError(i18n("The given file doesn't exist.")); 1676 return false; 1677 } 1678 } 1679 1680 // overwrite mode: no output file is needed 1681 if ( m_PdfDialog.m_cbOverwrite->isChecked() ) { 1682 return true; 1683 } 1684 1685 // create a different output file 1686 QString outfile = m_PdfDialog.m_edOutfile->lineEdit()->text().trimmed(); 1687 if ( outfile.isEmpty() ) { 1688 showError(i18n("You need to define an output file.")); 1689 return false; 1690 } 1691 1692 // outfile file must have extension pdf 1693 QFileInfo fo(outfile); 1694 if (fo.completeSuffix() != "pdf") { 1695 showError(i18n("Unknown file format: only '.pdf' is accepted as output file.")); 1696 return false; 1697 } 1698 1699 // check, if this output file already exists 1700 if ( fo.exists() ) { 1701 QString s = i18n("A file named \"%1\" already exists. Are you sure you want to overwrite it?", fo.fileName()); 1702 if (KMessageBox::questionTwoActions(this, 1703 "<center>" + s + "</center>", 1704 i18n("PDF Tools"), 1705 KStandardGuiItem::overwrite(), KStandardGuiItem::cancel() 1706 ) == KMessageBox::SecondaryAction) { 1707 return false; 1708 } 1709 } 1710 1711 return true; 1712 } 1713 1714 bool PdfDialog::checkProperties() 1715 { 1716 if ( !checkInputFile() ) { 1717 return false; 1718 } 1719 1720 return ( m_encrypted ) ? checkPassword() : true; 1721 } 1722 1723 bool PdfDialog::checkPermissions() 1724 { 1725 if ( !checkInputFile() ) { 1726 return false; 1727 } 1728 1729 return checkPassword(); 1730 } 1731 1732 bool PdfDialog::checkInputFile() 1733 { 1734 QString infile = m_PdfDialog.m_edInfile->lineEdit()->text().trimmed(); 1735 if (infile.isEmpty()) { 1736 showError(i18n("No input file is given.")); 1737 return false; 1738 } 1739 1740 QFileInfo fi(infile); 1741 QString suffix = fi.completeSuffix(); 1742 if (suffix != "pdf") { 1743 showError(i18n("Unknown file format: only '.pdf' are accepted for input files.")); 1744 return false; 1745 } 1746 1747 if (!fi.exists()) { 1748 showError(i18n("This input file does not exist.")); 1749 return false; 1750 } 1751 1752 return true; 1753 } 1754 1755 bool PdfDialog::checkPassword() 1756 { 1757 // check password 1758 QString password = m_PdfDialog.m_edPassword->text().trimmed(); 1759 if (password.isEmpty()) { 1760 showError(i18n("No password is given.")); 1761 return false; 1762 } 1763 1764 if (password.length() < 6) { 1765 showError(i18n("The password should be at least 6 characters long.")); 1766 return false; 1767 } 1768 1769 return true; 1770 } 1771 1772 void PdfDialog::showError(const QString &text) 1773 { 1774 KMessageBox::error(this, i18n("<center>") + text + i18n("</center>"), i18n("PDF Tools")); 1775 } 1776 1777 // check tasks 1778 bool PdfDialog::isParameterTask(int task) 1779 { 1780 return ( task==PDF_SELECT || task==PDF_DELETE || task==PDF_PDFPAGES_FREE || task==PDF_PDFTK_FREE ); 1781 } 1782 1783 bool PdfDialog::isOverlayTask(int task) 1784 { 1785 return ( task==PDF_PDFTK_BACKGROUND || task==PDF_PDFTK_STAMP ); 1786 } 1787 1788 bool PdfDialog::isBackgroundColor(int task) 1789 { 1790 return ( task == PDF_PDFTK_BGCOLOR ) ? true : false; 1791 } 1792 1793 bool PdfDialog::isFreeTask(int task) 1794 { 1795 return ( task==PDF_PDFPAGES_FREE || task==PDF_PDFTK_FREE ); 1796 } 1797 1798 }