1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7
8 #include <cstdlib>
9
10 #include <QByteArray>
11 #include <QCursor>
12 #include <QDebug>
13 #include <QDrag>
14 #include <QFile>
15 #include <QInputDialog>
16 #include <QList>
17 #include <QMimeData>
18 #include <QRegExp>
19 #include <QStack>
20
21 #include <poppler/ErrorCodes.h>
22 #include <poppler/GlobalParams.h>
23 #include <poppler/OptionalContent.h>
24 #include <poppler/PageTransition.h>
25 #include <poppler/ViewerPreferences.h>
26 #include <poppler/poppler-config.h>
27 #include <poppler/cpp/poppler-version.h>
28 #include <poppler/SplashOutputDev.h>
29 #include <poppler/splash/SplashBitmap.h>
30
31 #include "importpdf.h"
32 #include "importpdfconfig.h"
33 #include "pdftextrecognition.h"
34 #include "slaoutput.h"
35
36 #include "commonstrings.h"
37 #include "loadsaveplugin.h"
38 #include "pagesize.h"
39 #include "pdfimportoptions.h"
40 #include "pdfoptions.h"
41 #include "prefscontext.h"
42 #include "prefsfile.h"
43 #include "prefsmanager.h"
44 #include "prefstable.h"
45 #include "rawimage.h"
46 #include "scclocale.h"
47 #include "sccolorengine.h"
48 #include "scconfig.h"
49 #include "scmimedata.h"
50 #include "scpaths.h"
51 #include "scribus.h"
52 #include "scribusXml.h"
53 #include "scribuscore.h"
54 #include "sctextstream.h"
55 #include "selection.h"
56 #include "undomanager.h"
57 #include "util.h"
58 #include "util_formats.h"
59 #include "util_math.h"
60
61 #include "ui/customfdialog.h"
62 #include "ui/missing.h"
63 #include "ui/multiprogressdialog.h"
64 #include "ui/propertiespalette.h"
65
PdfPlug(ScribusDoc * doc,int flags)66 PdfPlug::PdfPlug(ScribusDoc* doc, int flags)
67 {
68 m_tmpSele = new Selection(this, false);
69 m_Doc = doc;
70 m_importerFlags = flags;
71 m_interactive = (flags & LoadSavePlugin::lfInteractive);
72 m_noDialogs = (flags & LoadSavePlugin::lfNoDialogs);
73 }
74
readThumbnail(const QString & fName)75 QImage PdfPlug::readThumbnail(const QString& fName)
76 {
77 QString pdfFile = QDir::toNativeSeparators(fName);
78 #if POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 83, 0)
79 globalParams.reset(new GlobalParams());
80 #else
81 globalParams = new GlobalParams();
82 #endif
83 if (globalParams)
84 {
85 #if defined(Q_OS_WIN32) && POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 62, 0)
86 GooString *fname = new GooString(pdfFile.toUtf8().data());
87 #else
88 GooString *fname = new GooString(QFile::encodeName(pdfFile).data());
89 #endif
90 globalParams->setErrQuiet(gTrue);
91 PDFDoc *pdfDoc = new PDFDoc(fname, nullptr, nullptr, nullptr);
92 if (pdfDoc)
93 {
94 if (pdfDoc->getErrorCode() == errEncrypted)
95 {
96 delete pdfDoc;
97 #if POPPLER_ENCODED_VERSION < POPPLER_VERSION_ENCODE(0, 83, 0)
98 delete globalParams;
99 #endif
100 return QImage();
101 }
102 if (pdfDoc->isOk())
103 {
104 double h = pdfDoc->getPageMediaHeight(1);
105 double w = pdfDoc->getPageMediaWidth(1);
106 double scale = qMin(500.0 / h, 500.0 / w);
107 double hDPI = 72.0 * scale;
108 double vDPI = 72.0 * scale;
109 SplashColor bgColor;
110 bgColor[0] = 255;
111 bgColor[1] = 255;
112 bgColor[2] = 255;
113 SplashOutputDev *dev = new SplashOutputDev(splashModeXBGR8, 4, gFalse, bgColor, gTrue);
114 dev->setVectorAntialias(gTrue);
115 dev->setFreeTypeHinting(gTrue, gFalse);
116 dev->startDoc(pdfDoc);
117 pdfDoc->displayPage(dev, 1, hDPI, vDPI, 0, gTrue, gFalse, gFalse);
118 SplashBitmap *bitmap = dev->getBitmap();
119 int bw = bitmap->getWidth();
120 int bh = bitmap->getHeight();
121 SplashColorPtr dataPtr = bitmap->getDataPtr();
122 if (QSysInfo::BigEndian == QSysInfo::ByteOrder)
123 {
124 uchar c;
125 int count = bw * bh * 4;
126 for (int k = 0; k < count; k += 4)
127 {
128 c = dataPtr[k];
129 dataPtr[k] = dataPtr[k+3];
130 dataPtr[k+3] = c;
131 c = dataPtr[k+1];
132 dataPtr[k+1] = dataPtr[k+2];
133 dataPtr[k+2] = c;
134 }
135 }
136 // construct a qimage SHARING the raw bitmap data in memory
137 QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 );
138 QImage image = tmpimg.copy();
139 image.setText("XSize", QString("%1").arg(w));
140 image.setText("YSize", QString("%1").arg(h));
141 delete dev;
142 delete pdfDoc;
143 #if POPPLER_ENCODED_VERSION < POPPLER_VERSION_ENCODE(0, 83, 0)
144 delete globalParams;
145 #endif
146 return image;
147 }
148 delete pdfDoc;
149 #if POPPLER_ENCODED_VERSION < POPPLER_VERSION_ENCODE(0, 83, 0)
150 delete globalParams;
151 #endif
152 }
153 }
154 return QImage();
155 }
156
import(const QString & fNameIn,const TransactionSettings & trSettings,int flags,bool showProgress)157 bool PdfPlug::import(const QString& fNameIn, const TransactionSettings& trSettings, int flags, bool showProgress)
158 {
159 #ifdef Q_OS_MACOS
160 showProgress = false;
161 #endif
162 bool success = false;
163 m_interactive = (flags & LoadSavePlugin::lfInteractive);
164 m_importerFlags = flags;
165 m_cancel = false;
166 bool ret = false;
167 QFileInfo fi = QFileInfo(fNameIn);
168 if ( !ScCore->usingGUI() )
169 {
170 m_interactive = false;
171 showProgress = false;
172 }
173 m_baseFile = QDir::cleanPath(QDir::toNativeSeparators(fi.absolutePath()+"/"));
174 if ( showProgress )
175 {
176 ScribusMainWindow* mw = (m_Doc==nullptr) ? ScCore->primaryMainWindow() : m_Doc->scMW();
177 m_progressDialog = new MultiProgressDialog( tr("Importing: %1").arg(fi.fileName()), CommonStrings::tr_Cancel, mw );
178 QStringList barNames("GI");
179 QStringList barTexts(tr("Analyzing File:"));
180 QList<bool> barsNumeric;
181 barsNumeric << false;
182 m_progressDialog->addExtraProgressBars(barNames, barTexts, barsNumeric);
183 m_progressDialog->setOverallTotalSteps(3);
184 m_progressDialog->setOverallProgress(0);
185 m_progressDialog->setProgress("GI", 0);
186 m_progressDialog->show();
187 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelRequested()));
188 qApp->processEvents();
189 }
190 else
191 m_progressDialog = nullptr;
192 /* Set default Page to size defined in Preferences */
193 if (m_progressDialog)
194 {
195 m_progressDialog->setOverallProgress(1);
196 qApp->processEvents();
197 }
198 double docWidth = PrefsManager::instance().appPrefs.docSetupPrefs.pageWidth;
199 double docHeight = PrefsManager::instance().appPrefs.docSetupPrefs.pageHeight;
200 if (!m_interactive || (flags & LoadSavePlugin::lfInsertPage))
201 {
202 m_Doc->setPage(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, false);
203 m_Doc->addPage(0);
204 m_Doc->view()->addPage(0, true);
205 }
206 else
207 {
208 if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
209 {
210 m_Doc = ScCore->primaryMainWindow()->doFileNew(docWidth, docHeight, 0, 0, 0, 0, 0, 0, false, 0, 0, 0, 0, 1, "Custom", true);
211 ScCore->primaryMainWindow()->HaveNewDoc();
212 ret = true;
213 }
214 }
215
216 if ((ret) || (!m_interactive))
217 {
218 if (docWidth > docHeight)
219 m_Doc->setPageOrientation(1);
220 else
221 m_Doc->setPageOrientation(0);
222 m_Doc->setPageSize("Custom");
223 }
224 if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
225 m_Doc->view()->deselectItems();
226 m_elements.clear();
227 m_Doc->setLoading(true);
228 m_Doc->DoDrawing = false;
229 if ((!(flags & LoadSavePlugin::lfLoadAsPattern)) && (m_Doc->view() != nullptr))
230 m_Doc->view()->updatesOn(false);
231 m_Doc->scMW()->setScriptRunning(true);
232 qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
233 QString CurDirP = QDir::currentPath();
234 QDir::setCurrent(fi.path());
235 if (convert(fNameIn))
236 {
237 m_tmpSele->clear();
238 QDir::setCurrent(CurDirP);
239 if ((m_elements.count() == 1) && (!(m_importerFlags & LoadSavePlugin::lfCreateDoc)))
240 {
241 PageItem *gr = m_elements[0];
242 if (gr->isGroup())
243 m_Doc->resizeGroupToContents(gr);
244 }
245 if ((m_elements.count() > 1) && (!(m_importerFlags & LoadSavePlugin::lfCreateDoc)))
246 {
247 PageItem *gr = m_Doc->groupObjectsList(m_elements);
248 m_Doc->resizeGroupToContents(gr);
249 }
250 m_Doc->DoDrawing = true;
251 m_Doc->scMW()->setScriptRunning(false);
252 m_Doc->setLoading(false);
253 qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
254 if ((m_elements.count() > 0) && (!ret) && (m_interactive))
255 {
256 if (flags & LoadSavePlugin::lfScripted)
257 {
258 bool loadF = m_Doc->isLoading();
259 m_Doc->setLoading(false);
260 m_Doc->changed();
261 m_Doc->setLoading(loadF);
262 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
263 {
264 m_Doc->m_Selection->delaySignalsOn();
265 for (int dre=0; dre < m_elements.count(); ++dre)
266 {
267 m_Doc->m_Selection->addItem(m_elements.at(dre), true);
268 }
269 m_Doc->m_Selection->delaySignalsOff();
270 m_Doc->m_Selection->setGroupRect();
271 if (m_Doc->view() != nullptr)
272 m_Doc->view()->updatesOn(true);
273 }
274 }
275 else
276 {
277 m_Doc->DragP = true;
278 m_Doc->DraggedElem = nullptr;
279 m_Doc->DragElements.clear();
280 m_Doc->m_Selection->delaySignalsOn();
281 for (int dre = 0; dre < m_elements.count(); ++dre)
282 {
283 m_tmpSele->addItem(m_elements.at(dre), true);
284 }
285 m_tmpSele->setGroupRect();
286 ScElemMimeData* md = ScriXmlDoc::writeToMimeData(m_Doc, m_tmpSele);
287 m_Doc->itemSelection_DeleteItem(m_tmpSele);
288 m_Doc->view()->updatesOn(true);
289 m_Doc->m_Selection->delaySignalsOff();
290 // We must copy the TransationSettings object as it is owned
291 // by handleObjectImport method afterwards
292 TransactionSettings* transacSettings = new TransactionSettings(trSettings);
293 m_Doc->view()->handleObjectImport(md, transacSettings);
294 m_Doc->DragP = false;
295 m_Doc->DraggedElem = nullptr;
296 m_Doc->DragElements.clear();
297 }
298 }
299 else
300 {
301 m_Doc->changed();
302 m_Doc->reformPages();
303 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
304 m_Doc->view()->updatesOn(true);
305 }
306 success = true;
307 }
308 else
309 {
310 QDir::setCurrent(CurDirP);
311 m_Doc->DoDrawing = true;
312 m_Doc->scMW()->setScriptRunning(false);
313 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
314 m_Doc->view()->updatesOn(true);
315 qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
316 success = false;
317 }
318 if (m_interactive)
319 m_Doc->setLoading(false);
320 //CB If we have a gui we must refresh it if we have used the progressbar
321 if (!(flags & LoadSavePlugin::lfLoadAsPattern))
322 {
323 if ((showProgress) && (!m_interactive))
324 m_Doc->view()->DrawNew();
325 }
326 qApp->restoreOverrideCursor();
327 return success;
328 }
329
~PdfPlug()330 PdfPlug::~PdfPlug()
331 {
332 delete m_progressDialog;
333 delete m_tmpSele;
334 }
335
convert(const QString & fn)336 bool PdfPlug::convert(const QString& fn)
337 {
338 bool firstPg = true;
339 int currentLayer = m_Doc->activeLayer();
340 int baseLayer = m_Doc->activeLayer();
341 m_importedColors.clear();
342 if (m_progressDialog)
343 {
344 m_progressDialog->setOverallProgress(2);
345 m_progressDialog->setLabel("GI", tr("Generating Items"));
346 m_progressDialog->setBusyIndicator("GI");
347 qApp->processEvents();
348 }
349
350 #if POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 83, 0)
351 globalParams.reset(new GlobalParams());
352 #else
353 globalParams = new GlobalParams();
354 #endif
355 GooString *userPW = nullptr;
356 if (globalParams)
357 {
358 #if defined(Q_OS_WIN32) && POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 62, 0)
359 GooString *fname = new GooString(fn.toUtf8().data());
360 #else
361 GooString *fname = new GooString(QFile::encodeName(fn).data());
362 #endif
363 globalParams->setErrQuiet(gTrue);
364 GBool hasOcg = gFalse;
365 QList<OptionalContentGroup*> ocgGroups;
366 // globalParams->setPrintCommands(gTrue);
367 PDFDoc *pdfDoc = new PDFDoc(fname, nullptr, nullptr, nullptr);
368 if (pdfDoc)
369 {
370 if (pdfDoc->getErrorCode() == errEncrypted)
371 {
372 delete pdfDoc;
373 pdfDoc = nullptr;
374 if (m_progressDialog)
375 m_progressDialog->hide();
376 qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
377 ScribusMainWindow* mw = m_Doc->scMW();
378 bool ok;
379 QString text = QInputDialog::getText(mw, tr("Open PDF-File"), tr("Password"), QLineEdit::Normal, "", &ok);
380 if (ok && !text.isEmpty())
381 {
382 #if defined(Q_OS_WIN32) && POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 62, 0)
383 fname = new GooString(fn.toUtf8().data());
384 #else
385 fname = new GooString(QFile::encodeName(fn).data());
386 #endif
387 userPW = new GooString(text.toLocal8Bit().data());
388 pdfDoc = new PDFDoc(fname, userPW, userPW, nullptr);
389 qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
390 }
391 if ((!pdfDoc) || (pdfDoc->getErrorCode() != errNone))
392 {
393 if (m_progressDialog)
394 m_progressDialog->close();
395 delete pdfDoc;
396 #if POPPLER_ENCODED_VERSION < POPPLER_VERSION_ENCODE(0, 83, 0)
397 delete globalParams;
398 #endif
399 return false;
400 }
401 if (m_progressDialog)
402 m_progressDialog->show();
403 }
404 if (pdfDoc->isOk())
405 {
406 std::vector<int> pageNs;
407 QString pageString = "*";
408 m_pdfDoc = pdfDoc;
409 double hDPI = 72.0;
410 double vDPI = 72.0;
411 int firstPage = 1;
412 int lastPage = pdfDoc->getNumPages();
413 GBool useMediaBox = gTrue;
414 GBool crop = gTrue;
415 GBool printing = gFalse;
416 const PDFRectangle *mediaBox = pdfDoc->getPage(1)->getMediaBox();
417 QRectF mediaRect = QRectF(QPointF(mediaBox->x1, mediaBox->y1), QPointF(mediaBox->x2, mediaBox->y2)).normalized();
418 bool boxesAreDifferent = false;
419 if (getCBox(Crop_Box, 1) != mediaRect)
420 boxesAreDifferent = true;
421 else if (getCBox(Trim_Box, 1) != mediaRect)
422 boxesAreDifferent = true;
423 else if (getCBox(Bleed_Box, 1) != mediaRect)
424 boxesAreDifferent = true;
425 else if (getCBox(Art_Box, 1) != mediaRect)
426 boxesAreDifferent = true;
427 bool cropped = false;
428 bool importTextAsVectors = true;
429 int contentRect = Media_Box;
430 if ((m_interactive && !m_noDialogs) || (m_importerFlags & LoadSavePlugin::lfCreateDoc))
431 {
432 if (m_progressDialog)
433 m_progressDialog->hide();
434 qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
435 PdfImportOptions *optImp = new PdfImportOptions(ScCore->primaryMainWindow());
436 QFileInfo fi = QFileInfo(fn);
437 optImp->setUpOptions(fi.fileName(), firstPage, lastPage, m_interactive, boxesAreDifferent, this);
438 if (!optImp->exec())
439 {
440 if (m_progressDialog)
441 m_progressDialog->close();
442 delete optImp;
443 delete pdfDoc;
444 #if POPPLER_ENCODED_VERSION < POPPLER_VERSION_ENCODE(0, 83, 0)
445 delete globalParams;
446 #endif
447 return false;
448 }
449 pageString = optImp->getPagesString();
450 contentRect = optImp->getCropBox();
451 cropped = optImp->croppingEnabled();
452 if (!cropped)
453 crop = cropped;
454 importTextAsVectors = optImp->getImportAsVectors();
455 // When displaying pages slices, we should always set useMediaBox to true
456 // in order to use MediaBox (x, y) as coordinate system
457 if (contentRect != Media_Box)
458 useMediaBox = gFalse;
459 if (cropped)
460 useMediaBox = gTrue;
461 delete optImp;
462 qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
463 if (m_progressDialog)
464 m_progressDialog->show();
465 }
466
467 parsePagesString(pageString, &pageNs, lastPage);
468 if (pageNs.size() <= 0)
469 return false;
470
471 firstPage = pageNs[0];
472 SlaOutputDev* dev = nullptr;
473 if (importTextAsVectors)
474 dev = new SlaOutputDev(m_Doc, &m_elements, &m_importedColors, m_importerFlags);
475 else
476 dev = new PdfTextOutputDev(m_Doc, &m_elements, &m_importedColors, m_importerFlags);
477
478 if (dev->isOk())
479 {
480 OCGs* ocg = pdfDoc->getOptContentConfig();
481 if (ocg)
482 {
483 hasOcg = ocg->hasOCGs();
484 if (hasOcg)
485 {
486 QStringList ocgNames;
487 Array *order = ocg->getOrderArray();
488 if (order)
489 {
490 for (int i = 0; i < order->getLength (); ++i)
491 {
492 Object orderItem = order->get(i);
493 if (orderItem.isDict())
494 {
495 POPPLER_CONST_075 Object POPPLER_REF ref = order->getNF(i);
496 if (ref.isRef())
497 {
498 OptionalContentGroup *oc = ocg->findOcgByRef(ref.getRef());
499 QString ocgName = UnicodeParsedString(oc->getName());
500 if (!ocgNames.contains(ocgName))
501 {
502 ocgGroups.prepend(oc);
503 ocgNames.append(ocgName);
504 }
505 }
506 }
507 else
508 {
509 #if POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 69, 0)
510 const auto& ocgs = ocg->getOCGs ();
511 for (const auto& ocg : ocgs)
512 {
513 OptionalContentGroup *oc = ocg.second.get();
514 QString ocgName = UnicodeParsedString(oc->getName());
515 if (!ocgNames.contains(ocgName))
516 {
517 ocgGroups.prepend(oc);
518 ocgNames.append(ocgName);
519 }
520 }
521 #else
522 GooList *ocgs = ocg->getOCGs ();
523 for (int i = 0; i < ocgs->getLength (); ++i)
524 {
525 OptionalContentGroup *oc = (OptionalContentGroup *)ocgs->get(i);
526 QString ocgName = UnicodeParsedString(oc->getName());
527 if (!ocgNames.contains(ocgName))
528 {
529 ocgGroups.prepend(oc);
530 ocgNames.append(ocgName);
531 }
532 }
533 #endif
534 }
535 }
536 }
537 else
538 {
539 #if POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 69, 0)
540 const auto& ocgs = ocg->getOCGs ();
541 for (const auto& ocg : ocgs)
542 {
543 OptionalContentGroup *oc = ocg.second.get();
544 QString ocgName = UnicodeParsedString(oc->getName());
545 if (!ocgNames.contains(ocgName))
546 {
547 ocgGroups.prepend(oc);
548 ocgNames.append(ocgName);
549 }
550 }
551 #else
552 GooList *ocgs = ocg->getOCGs ();
553 for (int i = 0; i < ocgs->getLength (); ++i)
554 {
555 OptionalContentGroup *oc = (OptionalContentGroup *)ocgs->get(i);
556 QString ocgName = UnicodeParsedString(oc->getName());
557 if (!ocgNames.contains(ocgName))
558 {
559 ocgGroups.prepend(oc);
560 ocgNames.append(ocgName);
561 }
562 }
563 #endif
564 }
565 }
566 }
567
568 const int zeroRotate = 0;
569 dev->startDoc(pdfDoc, pdfDoc->getXRef(), pdfDoc->getCatalog());
570 dev->rotate = pdfDoc->getPageRotate(firstPage);
571 bool rotated = dev->rotate == 90 || dev->rotate == 270;
572
573 if (m_importerFlags & LoadSavePlugin::lfCreateDoc)
574 {
575 if (hasOcg)
576 {
577 QString actL(m_Doc->activeLayerName());
578 for (int i = 0; i < ocgGroups.count(); i++)
579 {
580 OptionalContentGroup *oc = ocgGroups[i];
581 if (actL != UnicodeParsedString(oc->getName()))
582 currentLayer = m_Doc->addLayer(UnicodeParsedString(oc->getName()), false);
583 else
584 currentLayer = m_Doc->layerIDFromName(UnicodeParsedString(oc->getName()));
585 if (oc->getState() == OptionalContentGroup::On)
586 m_Doc->setLayerVisible(currentLayer, true);
587 else if (oc->getViewState() == OptionalContentGroup::ocUsageOn)
588 m_Doc->setLayerVisible(currentLayer, true);
589 else
590 m_Doc->setLayerVisible(currentLayer, false);
591 if ((oc->getPrintState() == OptionalContentGroup::ocUsageOn) || (oc->getPrintState() == OptionalContentGroup::ocUsageUnset))
592 m_Doc->setLayerPrintable(currentLayer, true);
593 else
594 m_Doc->setLayerPrintable(currentLayer, false);
595 oc->setState(OptionalContentGroup::Off);
596 }
597 dev->layersSetByOCG = true;
598 }
599
600 Object info = pdfDoc->getDocInfo();
601 if (info.isDict())
602 {
603 Object obj;
604 Dict *infoDict = info.getDict();
605 obj = infoDict->lookup((char*) "Title");
606 if (obj.isString())
607 {
608 m_Doc->documentInfo().setTitle(UnicodeParsedString(obj.getString()));
609 }
610 obj = infoDict->lookup((char*) "Author");
611 if (obj.isString())
612 {
613 m_Doc->documentInfo().setAuthor(UnicodeParsedString(obj.getString()));
614 }
615 obj = infoDict->lookup((char*) "Subject");
616 if (obj.isString())
617 {
618 m_Doc->documentInfo().setSubject(UnicodeParsedString(obj.getString()));
619 }
620 obj = infoDict->lookup((char*) "Keywords");
621 if (obj.isString())
622 {
623 // s1 = obj.getString();
624 m_Doc->documentInfo().setKeywords(UnicodeParsedString(obj.getString()));
625 }
626 }
627 info = Object();
628
629 if (cropped)
630 {
631 QRectF crBox = getCBox(contentRect, pageNs[0]);
632 if (rotated)
633 {
634 m_Doc->setPageWidth(crBox.height());
635 m_Doc->setPageHeight(crBox.width());
636 }
637 else
638 {
639 m_Doc->setPageHeight(crBox.height());
640 m_Doc->setPageWidth(crBox.width());
641 }
642 }
643 else
644 {
645 if (rotated)
646 {
647 m_Doc->setPageWidth(pdfDoc->getPageMediaHeight(pageNs[0]));
648 m_Doc->setPageHeight(pdfDoc->getPageMediaWidth(pageNs[0]));
649 }
650 else
651 {
652 m_Doc->setPageHeight(pdfDoc->getPageMediaHeight(pageNs[0]));
653 m_Doc->setPageWidth(pdfDoc->getPageMediaWidth(pageNs[0]));
654 }
655 }
656 m_Doc->setPageSize("Custom");
657 // m_Doc->pdfOptions().PresentVals.clear();
658 for (size_t i = 0; i < pageNs.size(); ++i)
659 {
660 int pp = pageNs[i];
661 m_Doc->setActiveLayer(baseLayer);
662 if (firstPg)
663 firstPg = false;
664 else
665 m_Doc->addPage(i);
666 QRectF mdBox = getCBox(0, pp);
667 QRectF crBox = getCBox(contentRect, pp);
668 if (cropped)
669 {
670 if (rotated)
671 {
672 m_Doc->currentPage()->setInitialWidth(crBox.height());
673 m_Doc->currentPage()->setInitialHeight(crBox.width());
674 m_Doc->currentPage()->setWidth(crBox.height());
675 m_Doc->currentPage()->setHeight(crBox.width());
676 dev->cropOffsetX = crBox.y();
677 dev->cropOffsetY = crBox.x();
678 }
679 else
680 {
681 m_Doc->currentPage()->setInitialHeight(crBox.height());
682 m_Doc->currentPage()->setInitialWidth(crBox.width());
683 m_Doc->currentPage()->setHeight(crBox.height());
684 m_Doc->currentPage()->setWidth(crBox.width());
685 dev->cropOffsetX = crBox.x();
686 dev->cropOffsetY = crBox.y();
687 }
688 }
689 else
690 {
691 if (rotated)
692 {
693 m_Doc->currentPage()->setInitialWidth(pdfDoc->getPageMediaHeight(pp));
694 m_Doc->currentPage()->setInitialHeight(pdfDoc->getPageMediaWidth(pp));
695 m_Doc->currentPage()->setWidth(pdfDoc->getPageMediaHeight(pp));
696 m_Doc->currentPage()->setHeight(pdfDoc->getPageMediaWidth(pp));
697 }
698 else
699 {
700 m_Doc->currentPage()->setInitialHeight(pdfDoc->getPageMediaHeight(pp));
701 m_Doc->currentPage()->setInitialWidth(pdfDoc->getPageMediaWidth(pp));
702 m_Doc->currentPage()->setHeight(pdfDoc->getPageMediaHeight(pp));
703 m_Doc->currentPage()->setWidth(pdfDoc->getPageMediaWidth(pp));
704 }
705 }
706 m_Doc->currentPage()->setMasterPageNameNormal();
707 m_Doc->currentPage()->setSize("Custom");
708 m_Doc->reformPages(true);
709 if (hasOcg)
710 {
711 for (int j = 0; j < ocgGroups.count(); j++)
712 {
713 OptionalContentGroup *oc = ocgGroups[j];
714 oc->setState(OptionalContentGroup::On);
715 if (cropped)
716 pdfDoc->displayPageSlice(dev, pp, hDPI, vDPI, zeroRotate, useMediaBox, crop, printing, crBox.x() - mdBox.x(), mdBox.bottom() - crBox.bottom(), crBox.width(), crBox.height(), nullptr, nullptr, dev->annotations_callback, dev);
717 else
718 pdfDoc->displayPage(dev, pp, hDPI, vDPI, zeroRotate, useMediaBox, crop, printing, nullptr, nullptr, dev->annotations_callback, dev);
719 oc->setState(OptionalContentGroup::Off);
720 }
721 }
722 else
723 {
724 if (cropped)
725 pdfDoc->displayPageSlice(dev, pp, hDPI, vDPI, zeroRotate, useMediaBox, crop, printing, crBox.x() - mdBox.x(), mdBox.bottom() - crBox.bottom(), crBox.width(), crBox.height(), nullptr, nullptr, dev->annotations_callback, dev);
726 else
727 pdfDoc->displayPage(dev, pp, hDPI, vDPI, zeroRotate, useMediaBox, crop, printing, nullptr, nullptr, dev->annotations_callback, dev);
728 }
729
730 PDFPresentationData ef;
731 Object trans = pdfDoc->getPage(pp)->getTrans();
732 Object *transi = &trans;
733 if (transi->isDict())
734 {
735 m_Doc->pdfOptions().PresentMode = true;
736 PageTransition *pgTrans = new PageTransition(transi);
737 ef.pageViewDuration = pdfDoc->getPage(pp)->getDuration();
738 ef.pageEffectDuration = pgTrans->getDuration();
739 ef.Dm = pgTrans->getAlignment() == transitionHorizontal ? 0 : 1;
740 ef.M = pgTrans->getDirection() == transitionInward ? 0 : 1;
741 int ang = pgTrans->getAngle();
742 if (ang == 0)
743 ef.Di = 0;
744 else if (ang == 270)
745 ef.Di = 1;
746 else if (ang == 90)
747 ef.Di = 2;
748 else if (ang == 180)
749 ef.Di = 3;
750 else if (ang == 315)
751 ef.Di = 4;
752 PageTransitionType trType = pgTrans->getType();
753 if (trType == transitionReplace)
754 ef.effectType = 0;
755 else if (trType == transitionBlinds)
756 ef.effectType = 1;
757 else if (trType == transitionBox)
758 ef.effectType = 2;
759 else if (trType == transitionDissolve)
760 ef.effectType = 3;
761 else if (trType == transitionGlitter)
762 ef.effectType = 4;
763 else if (trType == transitionSplit)
764 ef.effectType = 5;
765 else if (trType == transitionWipe)
766 ef.effectType = 6;
767 else if (trType == transitionPush)
768 ef.effectType = 7;
769 else if (trType == transitionCover)
770 ef.effectType = 8;
771 else if (trType == transitionUncover)
772 ef.effectType = 9;
773 else if (trType == transitionFade)
774 ef.effectType = 10;
775 delete pgTrans;
776 }
777 m_Doc->currentPage()->PresentVals = ef;
778 }
779 int numjs = pdfDoc->getCatalog()->numJS();
780 if (numjs > 0)
781 {
782 NameTree *jsNameTreeP = new NameTree();
783 Object catDict = pdfDoc->getXRef()->getCatalog();
784 if (catDict.isDict())
785 {
786 Object names = catDict.dictLookup("Names");
787 if (names.isDict())
788 {
789 Object obj = names.dictLookup("JavaScript");
790 jsNameTreeP->init(pdfDoc->getXRef(), &obj);
791 }
792 for (int a = 0; a < numjs; a++)
793 {
794 m_Doc->JavaScripts.insert(UnicodeParsedString(jsNameTreeP->getName(a)), UnicodeParsedString(pdfDoc->getCatalog()->getJS(a)));
795 }
796 names = catDict.dictLookup("OpenAction");
797 if (names.isDict())
798 {
799 #if POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 86, 0)
800 std::unique_ptr<LinkAction> linkActionUPtr = LinkAction::parseAction(&names, pdfDoc->getCatalog()->getBaseURI());
801 LinkAction *linkAction = linkActionUPtr.get();
802 #else
803 LinkAction *linkAction = nullptr;
804 linkAction = LinkAction::parseAction(&names, pdfDoc->getCatalog()->getBaseURI());
805 #endif
806 if (linkAction && (linkAction->getKind() == actionJavaScript))
807 {
808 LinkJavaScript *jsa = (LinkJavaScript*) linkAction;
809 if (jsa->isOk())
810 {
811 QString script = UnicodeParsedString(jsa->getScript());
812 if (script.startsWith("this."))
813 {
814 script.remove(0, 5);
815 script.remove("()");
816 if (m_Doc->JavaScripts.contains(script))
817 m_Doc->pdfOptions().openAction = script;
818 }
819 }
820 }
821 }
822 }
823 delete jsNameTreeP;
824 }
825 m_Doc->pdfOptions().Version = (PDFVersion::Version) qMin(16, qMax(13, pdfDoc->getPDFMajorVersion() * 10 + pdfDoc->getPDFMinorVersion()));
826 ViewerPreferences *viewPrefs = pdfDoc->getCatalog()->getViewerPreferences();
827 if (viewPrefs)
828 {
829 m_Doc->pdfOptions().Binding = viewPrefs->getDirection() == ViewerPreferences::directionL2R ? 0 : 1;
830 m_Doc->pdfOptions().hideMenuBar = viewPrefs->getHideMenubar();
831 m_Doc->pdfOptions().hideToolBar = viewPrefs->getHideToolbar();
832 m_Doc->pdfOptions().fitWindow = viewPrefs->getFitWindow();
833 }
834 Catalog::PageMode pgm = pdfDoc->getCatalog()->getPageMode();
835 m_Doc->pdfOptions().displayFullscreen = (pgm == Catalog::pageModeFullScreen);
836 m_Doc->pdfOptions().displayThumbs = (pgm == Catalog::pageModeThumbs);
837 m_Doc->pdfOptions().displayBookmarks = (pgm == Catalog::pageModeOutlines);
838 m_Doc->pdfOptions().displayLayers = (pgm == Catalog::pageModeOC);
839 Catalog::PageLayout pgl = pdfDoc->getCatalog()->getPageLayout();
840 if (pgl == Catalog::pageLayoutSinglePage)
841 m_Doc->pdfOptions().PageLayout = PDFOptions::SinglePage;
842 else if (pgl == Catalog::pageLayoutOneColumn)
843 m_Doc->pdfOptions().PageLayout = PDFOptions::OneColumn;
844 else if ((pgl == Catalog::pageLayoutTwoColumnLeft) || (pgl == Catalog::pageLayoutTwoPageLeft))
845 {
846 m_Doc->setPagePositioning(1);
847 m_Doc->setPageSetFirstPage(1, 0);
848 m_Doc->pdfOptions().PageLayout = PDFOptions::TwoColumnLeft;
849 }
850 else if ((pgl == Catalog::pageLayoutTwoColumnRight) || (pgl == Catalog::pageLayoutTwoPageRight))
851 {
852 m_Doc->setPagePositioning(1);
853 m_Doc->setPageSetFirstPage(1, 1);
854 m_Doc->pdfOptions().PageLayout = PDFOptions::TwoColumnRight;
855 }
856 }
857 else
858 {
859 if (hasOcg)
860 {
861 for (int a = 0; a < ocgGroups.count(); a++)
862 {
863 ocgGroups[a]->setState(OptionalContentGroup::On);
864 }
865 }
866 pdfDoc->displayPage(dev, firstPage, hDPI, vDPI, zeroRotate, useMediaBox, crop, printing, nullptr, nullptr, dev->annotations_callback, dev);
867 }
868 }
869 delete dev;
870 }
871 }
872 delete pdfDoc;
873 }
874 #if POPPLER_ENCODED_VERSION >= POPPLER_VERSION_ENCODE(0, 83, 0)
875 globalParams.release();
876 #else
877 delete globalParams;
878 globalParams = nullptr;
879 #endif
880
881 // qDebug() << "converting finished";
882 // qDebug() << "Imported" << m_elements.count() << "Elements";
883
884 if (m_elements.count() == 0)
885 {
886 for (int i = 0; i < m_importedColors.count(); i++)
887 {
888 m_Doc->PageColors.remove(m_importedColors[i]);
889 }
890 }
891
892 if (m_progressDialog)
893 m_progressDialog->close();
894 return true;
895 }
896
readPreview(int pgNum,int width,int height,int box)897 QImage PdfPlug::readPreview(int pgNum, int width, int height, int box)
898 {
899 if (!m_pdfDoc)
900 return QImage();
901
902 double h = m_pdfDoc->getPageMediaHeight(pgNum);
903 double w = m_pdfDoc->getPageMediaWidth(pgNum);
904 double scale = qMin(height / h, width / w);
905 double hDPI = 72.0 * scale;
906 double vDPI = 72.0 * scale;
907 SplashColor bgColor;
908 bgColor[0] = 255;
909 bgColor[1] = 255;
910 bgColor[2] = 255;
911 SplashOutputDev *dev = new SplashOutputDev(splashModeXBGR8, 4, gFalse, bgColor, gTrue);
912 dev->setVectorAntialias(gTrue);
913 dev->setFreeTypeHinting(gTrue, gFalse);
914 dev->startDoc(m_pdfDoc);
915 m_pdfDoc->displayPage(dev, pgNum, hDPI, vDPI, 0, gTrue, gFalse, gFalse);
916 SplashBitmap *bitmap = dev->getBitmap();
917 int bw = bitmap->getWidth();
918 int bh = bitmap->getHeight();
919 SplashColorPtr dataPtr = bitmap->getDataPtr();
920 if (QSysInfo::BigEndian == QSysInfo::ByteOrder)
921 {
922 uchar c;
923 int count = bw * bh * 4;
924 for (int k = 0; k < count; k += 4)
925 {
926 c = dataPtr[k];
927 dataPtr[k] = dataPtr[k+3];
928 dataPtr[k+3] = c;
929 c = dataPtr[k+1];
930 dataPtr[k+1] = dataPtr[k+2];
931 dataPtr[k+2] = c;
932 }
933 }
934 // construct a qimage SHARING the raw bitmap data in memory
935 QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 );
936 QImage image = tmpimg.copy();
937 image.setText("XSize", QString("%1").arg(w));
938 image.setText("YSize", QString("%1").arg(h));
939 if (box > Media_Box)
940 {
941 QRectF cRect = getCBox(box, pgNum);
942 QRectF mediaRect = getCBox(0, pgNum);
943 cRect.moveTo(cRect.x() - mediaRect.x(), cRect.y() - mediaRect.y());
944 QPainter pp;
945 pp.begin(&image);
946 pp.setBrush(Qt::NoBrush);
947 pp.setPen(QPen(Qt::red, 3.0));
948 pp.translate(0, bh);
949 pp.scale(scale, -scale);
950 pp.drawRect(cRect);
951 pp.end();
952 }
953 delete dev;
954 return image;
955 }
956
getCBox(int box,int pgNum)957 QRectF PdfPlug::getCBox(int box, int pgNum)
958 {
959 const PDFRectangle *cBox = nullptr;
960 if (box == Media_Box)
961 cBox = m_pdfDoc->getPage(pgNum)->getMediaBox();
962 else if (box == Bleed_Box)
963 cBox = m_pdfDoc->getPage(pgNum)->getBleedBox();
964 else if (box == Trim_Box)
965 cBox = m_pdfDoc->getPage(pgNum)->getTrimBox();
966 else if (box == Crop_Box)
967 cBox = m_pdfDoc->getPage(pgNum)->getCropBox();
968 else if (box == Art_Box)
969 cBox = m_pdfDoc->getPage(pgNum)->getArtBox();
970 QRectF cRect = QRectF(QPointF(cBox->x1, cBox->y1), QPointF(cBox->x2, cBox->y2)).normalized();
971 return cRect;
972 }
973
UnicodeParsedString(POPPLER_CONST GooString * s1)974 QString PdfPlug::UnicodeParsedString(POPPLER_CONST GooString *s1)
975 {
976 if ( !s1 || s1->getLength() == 0 )
977 return QString();
978 GBool isUnicode;
979 int i;
980 Unicode u;
981 QString result;
982 if ((s1->getChar(0) & 0xff) == 0xfe && (s1->getLength() > 1 && (s1->getChar(1) & 0xff) == 0xff))
983 {
984 isUnicode = gTrue;
985 i = 2;
986 result.reserve((s1->getLength() - 2) / 2);
987 }
988 else
989 {
990 isUnicode = gFalse;
991 i = 0;
992 result.reserve(s1->getLength());
993 }
994 while (i < s1->getLength())
995 {
996 if (isUnicode)
997 {
998 u = ((s1->getChar(i) & 0xff) << 8) | (s1->getChar(i+1) & 0xff);
999 i += 2;
1000 }
1001 else
1002 {
1003 u = s1->getChar(i) & 0xff;
1004 ++i;
1005 }
1006 // #15616: imagemagick may write unicode strings incorrectly in PDF
1007 if (u == 0)
1008 continue;
1009 result += QChar( u );
1010 }
1011 return result;
1012 }
1013
UnicodeParsedString(const std::string & s1)1014 QString PdfPlug::UnicodeParsedString(const std::string& s1)
1015 {
1016 if (s1.length() == 0)
1017 return QString();
1018 GBool isUnicode;
1019 size_t i;
1020 Unicode u;
1021 QString result;
1022 if ((s1.at(0) & 0xff) == 0xfe && (s1.length() > 1 && (s1.at(1) & 0xff) == 0xff))
1023 {
1024 isUnicode = gTrue;
1025 i = 2;
1026 result.reserve((s1.length() - 2) / 2);
1027 }
1028 else
1029 {
1030 isUnicode = gFalse;
1031 i = 0;
1032 result.reserve(s1.length());
1033 }
1034 while (i < s1.length())
1035 {
1036 if (isUnicode)
1037 {
1038 u = ((s1.at(i) & 0xff) << 8) | (s1.at(i+1) & 0xff);
1039 i += 2;
1040 }
1041 else
1042 {
1043 u = s1.at(i) & 0xff;
1044 ++i;
1045 }
1046 // #15616: imagemagick may write unicode strings incorrectly in PDF
1047 if (u == 0)
1048 continue;
1049 result += QChar( u );
1050 }
1051 return result;
1052 }
1053