15 #include "document_p.h" 16 #include "documentcommands_p.h" 21 #define _WIN32_WINNT 0x0500 23 #elif defined(Q_OS_FREEBSD) 26 #include <sys/types.h> 27 #include <sys/sysctl.h> 29 #include <vm/vm_param.h> 33 #include <QApplication> 34 #include <QDesktopServices> 40 #include <QMimeDatabase> 42 #include <QPrintDialog> 43 #include <QRegularExpression> 46 #include <QStandardPaths> 47 #include <QTemporaryFile> 48 #include <QTextStream> 50 #include <QUndoCommand> 52 #include <QtAlgorithms> 54 #include <KAuthorized> 55 #include <KConfigDialog> 58 #include <KLocalizedString> 59 #include <KMacroExpander> 60 #include <KMessageBox> 61 #include <KApplicationTrader> 62 #include <KPluginMetaData> 66 #include <Kdelibs4Migration> 71 #include "annotations.h" 72 #include "annotations_p.h" 73 #include "audioplayer.h" 74 #include "audioplayer_p.h" 75 #include "bookmarkmanager.h" 76 #include "chooseenginedialog_p.h" 79 #include "generator_p.h" 80 #include "interfaces/configinterface.h" 81 #include "interfaces/guiinterface.h" 82 #include "interfaces/printinterface.h" 83 #include "interfaces/saveinterface.h" 88 #include "pagecontroller_p.h" 89 #include "script/event_p.h" 91 #include "settings_core.h" 92 #include "sourcereference.h" 93 #include "sourcereference_p.h" 94 #include "texteditors_p.h" 96 #include "tilesmanager_p.h" 102 #include <config-okular.h> 110 struct AllocatedPixmap {
134 struct RunningSearch {
144 bool cachedViewportMove : 1;
145 bool isCurrentlySearching : 1;
150 #define foreachObserver(cmd) \ 152 QSet<DocumentObserver *>::const_iterator it = d->m_observers.constBegin(), end = d->m_observers.constEnd(); \ 153 for (; it != end; ++it) { \ 158 #define foreachObserverD(cmd) \ 160 QSet<DocumentObserver *>::const_iterator it = m_observers.constBegin(), end = m_observers.constEnd(); \ 161 for (; it != end; ++it) { \ 166 #define OKULAR_HISTORY_MAXSTEPS 100 167 #define OKULAR_HISTORY_SAVEDSTEPS 10 170 const int kMemCheckTime = 2000;
174 QString DocumentPrivate::pagesSizeString()
const 178 QSizeF size = m_parent->allPagesSize();
181 return localizedSize(size);
188 for (
int i = 0; i < m_pagesVector.count(); ++i) {
189 const Page *p = m_pagesVector.at(i);
191 pageSizeFrequencies[sizeString] = pageSizeFrequencies.
value(sizeString, 0) + 1;
195 int largestFrequencySeen = 0;
198 while (i != pageSizeFrequencies.
constEnd()) {
199 if (i.value() > largestFrequencySeen) {
200 largestFrequencySeen = i.value();
201 mostCommonPageSize = i.key();
205 QString finalText =
i18nc(
"@info %1 is a page size",
"Most pages are %1.", mostCommonPageSize);
214 QString DocumentPrivate::namePaperSize(
double inchesWidth,
double inchesHeight)
const 218 const QSize pointsSize(inchesWidth * 72.0, inchesHeight * 72.0);
224 return i18nc(
"paper type and orientation (eg: Portrait A4)",
"Portrait %1", paperName);
226 return i18nc(
"paper type and orientation (eg: Portrait A4)",
"Landscape %1", paperName);
230 QString DocumentPrivate::localizedSize(
const QSizeF size)
const 232 double inchesWidth = 0, inchesHeight = 0;
233 switch (m_generator->pagesSizeMetric()) {
235 inchesWidth = size.
width() / 72.0;
236 inchesHeight = size.
height() / 72.0;
240 const QSizeF dpi = m_generator->dpi();
249 return i18nc(
"%1 is width, %2 is height, %3 is paper size name",
"%1 x %2 in (%3)", inchesWidth, inchesHeight, namePaperSize(inchesWidth, inchesHeight));
251 return i18nc(
"%1 is width, %2 is height, %3 is paper size name",
"%1 x %2 mm (%3)",
QString::number(inchesWidth * 25.4,
'd', 0),
QString::number(inchesHeight * 25.4,
'd', 0), namePaperSize(inchesWidth, inchesHeight));
255 qulonglong DocumentPrivate::calculateMemoryToFree()
258 qulonglong clipValue = 0;
259 qulonglong memoryToFree = 0;
261 switch (SettingsCore::memoryLevel()) {
262 case SettingsCore::EnumMemoryLevel::Low:
263 memoryToFree = m_allocatedPixmapsTotalMemory;
266 case SettingsCore::EnumMemoryLevel::Normal: {
267 qulonglong thirdTotalMemory = getTotalMemory() / 3;
268 qulonglong freeMemory = getFreeMemory();
269 if (m_allocatedPixmapsTotalMemory > thirdTotalMemory)
270 memoryToFree = m_allocatedPixmapsTotalMemory - thirdTotalMemory;
271 if (m_allocatedPixmapsTotalMemory > freeMemory)
272 clipValue = (m_allocatedPixmapsTotalMemory - freeMemory) / 2;
275 case SettingsCore::EnumMemoryLevel::Aggressive: {
276 qulonglong freeMemory = getFreeMemory();
277 if (m_allocatedPixmapsTotalMemory > freeMemory)
278 clipValue = (m_allocatedPixmapsTotalMemory - freeMemory) / 2;
280 case SettingsCore::EnumMemoryLevel::Greedy: {
282 qulonglong freeMemory = getFreeMemory(&freeSwap);
283 const qulonglong memoryLimit = qMin(qMax(freeMemory, getTotalMemory() / 2), freeMemory + freeSwap);
284 if (m_allocatedPixmapsTotalMemory > memoryLimit)
285 clipValue = (m_allocatedPixmapsTotalMemory - memoryLimit) / 2;
289 if (clipValue > memoryToFree)
290 memoryToFree = clipValue;
295 void DocumentPrivate::cleanupPixmapMemory()
297 cleanupPixmapMemory(calculateMemoryToFree());
300 void DocumentPrivate::cleanupPixmapMemory(qulonglong memoryToFree)
302 if (memoryToFree < 1)
305 const int currentViewportPage = (*m_viewportIterator).pageNumber;
310 for (; vIt != vEnd; ++vIt)
311 visibleRects.
insert((*vIt)->pageNumber, (*vIt));
315 while (memoryToFree > 0) {
316 AllocatedPixmap *p = searchLowestPriorityPixmap(
true,
true);
320 qCDebug(OkularCoreDebug).nospace() <<
"Evicting cache pixmap observer=" << p->observer <<
" page=" << p->page;
324 m_allocatedPixmapsTotalMemory -= p->memory;
326 if (p->memory > memoryToFree)
329 memoryToFree -= p->memory;
332 m_pagesVector.at(p->page)->deletePixmap(p->observer);
342 while (memoryToFree > 0) {
345 AllocatedPixmap *p = searchLowestPriorityPixmap(
false,
true, observer);
351 TilesManager *tilesManager = m_pagesVector.at(p->page)->d->tilesManager(observer);
352 if (tilesManager && tilesManager->totalMemory() > 0) {
353 qulonglong memoryDiff = p->memory;
356 visibleRect = visibleRects[p->page]->rect;
359 tilesManager->cleanupPixmapMemory(memoryToFree, visibleRect, currentViewportPage);
361 p->memory = tilesManager->totalMemory();
362 memoryDiff -= p->memory;
363 memoryToFree = (memoryDiff < memoryToFree) ? (memoryToFree - memoryDiff) : 0;
364 m_allocatedPixmapsTotalMemory -= memoryDiff;
378 m_allocatedPixmaps += pixmapsToKeep;
387 AllocatedPixmap *DocumentPrivate::searchLowestPriorityPixmap(
bool unloadableOnly,
bool thenRemoveIt,
DocumentObserver *observer)
392 const int currentViewportPage = (*m_viewportIterator).pageNumber;
395 int maxDistance = -1;
396 while (pIt != pEnd) {
397 const AllocatedPixmap *p = *pIt;
399 if (observer ==
nullptr || p->observer == observer) {
400 const int distance = qAbs(p->page - currentViewportPage);
401 if (maxDistance < distance && (!unloadableOnly || p->observer->canUnloadPixmap(p->page))) {
402 maxDistance = distance;
403 farthestPixmap = pIt;
410 if (farthestPixmap == pEnd)
413 AllocatedPixmap *selectedPixmap = *farthestPixmap;
415 m_allocatedPixmaps.erase(farthestPixmap);
416 return selectedPixmap;
419 qulonglong DocumentPrivate::getTotalMemory()
421 static qulonglong cachedValue = 0;
425 #if defined(Q_OS_LINUX) 427 QFile memFile(QStringLiteral(
"/proc/meminfo"));
429 return (cachedValue = 134217728);
433 QString entry = readStream.readLine();
439 #elif defined(Q_OS_FREEBSD) 441 int mib[] = {CTL_HW, HW_PHYSMEM};
442 size_t len =
sizeof(physmem);
443 if (sysctl(mib, 2, &physmem, &len, NULL, 0) == 0)
444 return (cachedValue = physmem);
445 #elif defined(Q_OS_WIN) 447 stat.dwLength =
sizeof(stat);
448 GlobalMemoryStatusEx(&stat);
450 return (cachedValue = stat.ullTotalPhys);
452 return (cachedValue = 134217728);
455 qulonglong DocumentPrivate::getFreeMemory(qulonglong *freeSwap)
458 static qulonglong cachedValue = 0;
459 static qulonglong cachedFreeSwap = 0;
463 *freeSwap = cachedFreeSwap;
472 #if defined(Q_OS_LINUX) 474 QFile memFile(QStringLiteral(
"/proc/meminfo"));
480 qulonglong memoryFree = 0;
483 static const int nElems = 5;
484 QString names[nElems] = {QStringLiteral(
"MemFree:"), QStringLiteral(
"Buffers:"), QStringLiteral(
"Cached:"), QStringLiteral(
"SwapFree:"), QStringLiteral(
"SwapTotal:")};
485 qulonglong
values[nElems] = {0, 0, 0, 0, 0};
486 bool foundValues[nElems] = {
false,
false,
false,
false,
false};
488 entry = readStream.readLine();
491 for (
int i = 0; i < nElems; ++i) {
499 for (
int i = 0; found && i < nElems; ++i)
500 found = found && foundValues[i];
505 memoryFree = values[0] + values[1] + values[2] + values[3];
506 if (values[4] > memoryFree)
509 memoryFree -= values[4];
517 *freeSwap = (cachedFreeSwap = (Q_UINT64_C(1024) * values[3]));
518 return (cachedValue = (Q_UINT64_C(1024) * memoryFree));
519 #elif defined(Q_OS_FREEBSD) 520 qulonglong cache, inact, free, psize;
521 size_t cachelen, inactlen, freelen, psizelen;
522 cachelen =
sizeof(cache);
523 inactlen =
sizeof(inact);
524 freelen =
sizeof(free);
525 psizelen =
sizeof(psize);
527 if (sysctlbyname(
"vm.stats.vm.v_cache_count", &cache, &cachelen, NULL, 0) == 0 && sysctlbyname(
"vm.stats.vm.v_inactive_count", &inact, &inactlen, NULL, 0) == 0 &&
528 sysctlbyname(
"vm.stats.vm.v_free_count", &free, &freelen, NULL, 0) == 0 && sysctlbyname(
"vm.stats.vm.v_page_size", &psize, &psizelen, NULL, 0) == 0) {
530 return (cachedValue = (cache + inact + free) * psize);
534 #elif defined(Q_OS_WIN) 536 stat.dwLength =
sizeof(stat);
537 GlobalMemoryStatusEx(&stat);
542 *freeSwap = (cachedFreeSwap = stat.ullAvailPageFile);
543 return (cachedValue = stat.ullAvailPhys);
550 bool DocumentPrivate::loadDocumentInfo(LoadDocumentInfoFlags loadWhat)
555 if (m_xmlFileName.isEmpty())
558 QFile infoFile(m_xmlFileName);
559 return loadDocumentInfo(infoFile, loadWhat);
562 bool DocumentPrivate::loadDocumentInfo(
QFile &infoFile, LoadDocumentInfoFlags loadWhat)
569 if (!doc.setContent(&infoFile)) {
570 qCDebug(OkularCoreDebug) <<
"Can't load XML pair! Check for broken xml.";
581 bool loadedAnything =
false;
589 if (catName ==
QLatin1String(
"pageList") && (loadWhat & LoadPageInfo)) {
593 if (pageElement.
hasAttribute(QStringLiteral(
"number"))) {
596 int pageNumber = pageElement.
attribute(QStringLiteral(
"number")).
toInt(&ok);
599 if (ok && pageNumber >= 0 && pageNumber < (
int)m_pagesVector.count()) {
600 if (m_pagesVector[pageNumber]->d->restoreLocalContents(pageElement))
601 loadedAnything =
true;
609 else if (catName ==
QLatin1String(
"generalInfo") && (loadWhat & LoadGeneralInfo)) {
617 m_viewportHistory.
clear();
622 if (historyElement.
hasAttribute(QStringLiteral(
"viewport"))) {
625 loadedAnything =
true;
630 if (m_viewportHistory.isEmpty())
631 m_viewportIterator = m_viewportHistory.insert(m_viewportHistory.end(),
DocumentViewport());
635 int newrotation = !str.
isEmpty() ? (str.
toInt(&ok) % 4) : 0;
636 if (ok && newrotation != 0) {
637 setRotationInternal(newrotation,
false);
638 loadedAnything =
true;
646 for (
View *view : qAsConst(m_views)) {
647 if (view->name() == viewName) {
648 loadViewsInfo(view, viewElement);
649 loadedAnything =
true;
664 return loadedAnything;
675 bool newzoom_ok =
true;
676 const double newzoom = !valueString.
isEmpty() ? valueString.
toDouble(&newzoom_ok) : 1.0;
681 bool newmode_ok =
true;
682 const int newmode = !modeString.
isEmpty() ? modeString.
toInt(&newmode_ok) : 2;
688 bool newmode_ok =
true;
689 const int newmode = !modeString.
isEmpty() ? modeString.
toInt(&newmode_ok) : 2;
695 bool newmode_ok =
true;
696 const int newmode = !modeString.
isEmpty() ? modeString.
toInt(&newmode_ok) : 2;
702 bool newmode_ok =
true;
703 const int newmode = !valueString.
isEmpty() ? valueString.
toInt(&newmode_ok) : 2;
721 if (ok && zoom != 0) {
752 QUrl DocumentPrivate::giveAbsoluteUrl(
const QString &fileName)
const 757 if (!m_url.isValid())
763 bool DocumentPrivate::openRelativeFile(
const QString &fileName)
765 const QUrl newUrl = giveAbsoluteUrl(fileName);
769 qCDebug(OkularCoreDebug).nospace() <<
"openRelativeFile: '" << newUrl <<
"'";
771 emit m_parent->openUrl(newUrl);
772 return m_url == newUrl;
778 qCDebug(OkularCoreDebug) << service.
fileName();
781 qCWarning(OkularCoreDebug).nospace() <<
"Invalid plugin factory for " << service.
fileName() <<
":" << loader.errorString();
787 GeneratorInfo info(plugin, service);
788 m_loadedGenerators.insert(service.
pluginId(), info);
792 void DocumentPrivate::loadAllGeneratorLibraries()
794 if (m_generatorsLoaded)
797 loadServiceList(availableGenerators());
799 m_generatorsLoaded =
true;
804 int count = offers.
count();
808 for (
int i = 0; i < count; ++i) {
812 if (!m_loadedGenerators.isEmpty() && genIt != m_loadedGenerators.constEnd())
820 void DocumentPrivate::unloadGenerator(
const GeneratorInfo &info)
822 delete info.generator;
825 void DocumentPrivate::cacheExportFormats()
831 for (
int i = 0; i < formats.
count(); ++i) {
833 m_exportToText = formats.
at(i);
835 m_exportFormats.append(formats.
at(i));
838 m_exportCached =
true;
843 if (info.configChecked)
847 info.configChecked =
true;
851 SaveInterface *DocumentPrivate::generatorSave(GeneratorInfo &info)
853 if (info.saveChecked)
857 info.saveChecked =
true;
865 m_walletGenerator =
nullptr;
866 if (genIt != m_loadedGenerators.constEnd()) {
867 m_generator = genIt.value().generator;
869 m_generator = loadGeneratorLibrary(offer);
871 return Document::OpenError;
872 genIt = m_loadedGenerators.constFind(propName);
873 Q_ASSERT(genIt != m_loadedGenerators.constEnd());
875 Q_ASSERT_X(m_generator,
"Document::load()",
"null generator?!");
877 m_generator->d_func()->m_document =
this;
888 qCDebug(OkularCoreDebug) <<
"Output DPI:" << dpi;
889 m_generator->setDPI(dpi);
893 openResult = m_generator->loadDocumentWithPassword(docFile, m_pagesVector, password);
894 }
else if (!filedata.
isEmpty()) {
896 openResult = m_generator->loadDocumentFromDataWithPassword(filedata, m_pagesVector, password);
899 if (!m_tempFile->open()) {
901 m_tempFile =
nullptr;
903 m_tempFile->write(filedata);
904 QString tmpFileName = m_tempFile->fileName();
906 openResult = m_generator->loadDocumentWithPassword(tmpFileName, m_pagesVector, password);
912 if (openResult != Document::OpenSuccess || m_pagesVector.size() <= 0) {
913 m_generator->d_func()->m_document =
nullptr;
921 m_walletGenerator = m_generator;
922 m_generator =
nullptr;
924 qDeleteAll(m_pagesVector);
925 m_pagesVector.clear();
927 m_tempFile =
nullptr;
930 if (openResult == Document::OpenSuccess)
931 openResult = Document::OpenError;
945 bool DocumentPrivate::savePageDocumentInfo(
QTemporaryFile *infoFile,
int what)
const 947 if (infoFile->
open()) {
950 QDomProcessingInstruction xmlPi = doc.createProcessingInstruction(QStringLiteral(
"xml"), QStringLiteral(
"version=\"1.0\" encoding=\"utf-8\""));
952 QDomElement root = doc.createElement(QStringLiteral(
"documentInfo"));
956 QDomElement pageList = doc.createElement(QStringLiteral(
"pageList"));
960 for (; pIt != pEnd; ++pIt)
961 (*pIt)->d->saveLocalContents(pageList, doc, PageItems(what));
966 os.setCodec(
"UTF-8");
976 if (!m_nextDocumentDestination.isEmpty() && m_generator) {
977 DocumentViewport vp(m_parent->metaData(QStringLiteral(
"NamedViewport"), m_nextDocumentDestination).toString());
985 void DocumentPrivate::performAddPageAnnotation(
int page,
Annotation *annotation)
991 Page *kp = m_pagesVector[page];
992 if (!m_generator || !kp)
996 if (annotation->d_ptr->m_page)
1004 proxy->notifyAddition(annotation, page);
1007 notifyAnnotationChanges(page);
1011 refreshPixmaps(page);
1015 void DocumentPrivate::performRemovePageAnnotation(
int page,
Annotation *annotation)
1019 bool isExternallyDrawn;
1022 Page *kp = m_pagesVector[page];
1023 if (!m_generator || !kp)
1027 isExternallyDrawn =
true;
1029 isExternallyDrawn =
false;
1032 if (m_parent->canRemovePageAnnotation(annotation)) {
1035 proxy->notifyRemoval(annotation, page);
1040 notifyAnnotationChanges(page);
1042 if (isExternallyDrawn) {
1044 refreshPixmaps(page);
1049 void DocumentPrivate::performModifyPageAnnotation(
int page,
Annotation *annotation,
bool appearanceChanged)
1055 Page *kp = m_pagesVector[page];
1056 if (!m_generator || !kp)
1061 proxy->notifyModification(annotation, page, appearanceChanged);
1065 notifyAnnotationChanges(page);
1070 if (m_annotationBeingModified)
1073 m_annotationBeingModified =
true;
1075 m_annotationBeingModified =
false;
1079 qCDebug(OkularCoreDebug) <<
"Refreshing Pixmaps";
1080 refreshPixmaps(page);
1084 void DocumentPrivate::performSetAnnotationContents(
const QString &newContents,
Annotation *annot,
int pageNumber)
1086 bool appearanceChanged =
false;
1092 Okular::TextAnnotation *txtann =
static_cast<Okular::TextAnnotation *
>(annot);
1093 if (txtann->textType() == Okular::TextAnnotation::InPlace) {
1094 appearanceChanged =
true;
1100 Okular::LineAnnotation *lineann =
static_cast<Okular::LineAnnotation *
>(annot);
1101 if (lineann->showCaption())
1102 appearanceChanged =
true;
1113 performModifyPageAnnotation(pageNumber, annot, appearanceChanged);
1116 void DocumentPrivate::recalculateForms()
1118 const QVariant fco = m_parent->metaData(QStringLiteral(
"FormCalculateOrder"));
1120 foreach (
int formId, formCalculateOrder) {
1121 for (uint pageIdx = 0; pageIdx < m_parent->pages(); pageIdx++) {
1122 const Page *p = m_parent->page(pageIdx);
1124 bool pageNeedsRefresh =
false;
1126 if (form->
id() == formId) {
1130 std::shared_ptr<Event> event;
1134 event = Event::createFormCalculateEvent(fft, m_pagesVector[pageIdx]);
1136 m_scripter =
new Scripter(
this);
1137 m_scripter->setEvent(event.get());
1139 oldVal = fft->
text();
1142 m_parent->processAction(action);
1145 m_scripter->setEvent(
nullptr);
1146 const QString newVal =
event->value().toString();
1147 if (newVal != oldVal) {
1152 m_parent->processFormatAction(action, fft);
1154 emit m_parent->refreshFormWidget(fft);
1155 pageNeedsRefresh =
true;
1160 qWarning() <<
"Form that is part of calculate order doesn't have a calculate action";
1164 if (pageNeedsRefresh) {
1165 refreshPixmaps(p->
number());
1172 void DocumentPrivate::saveDocumentInfo()
const 1174 if (m_xmlFileName.isEmpty())
1177 QFile infoFile(m_xmlFileName);
1178 qCDebug(OkularCoreDebug) <<
"About to save document info to" << m_xmlFileName;
1180 qCWarning(OkularCoreDebug) <<
"Failed to open docdata file" << m_xmlFileName;
1185 QDomProcessingInstruction xmlPi = doc.createProcessingInstruction(QStringLiteral(
"xml"), QStringLiteral(
"version=\"1.0\" encoding=\"utf-8\""));
1187 QDomElement root = doc.createElement(QStringLiteral(
"documentInfo"));
1189 doc.appendChild(root);
1193 if (m_docdataMigrationNeeded) {
1194 QDomElement pageList = doc.createElement(QStringLiteral(
"pageList"));
1202 const PageItems saveWhat = AllPageItems | OriginalAnnotationPageItems | OriginalFormFieldPageItems;
1205 for (; pIt != pEnd; ++pIt)
1206 (*pIt)->d->saveLocalContents(pageList, doc, saveWhat);
1210 QDomElement generalInfo = doc.createElement(QStringLiteral(
"generalInfo"));
1214 QDomElement rotationNode = doc.createElement(QStringLiteral(
"rotation"));
1221 if (backIterator != m_viewportHistory.constEnd()) {
1223 int backSteps = OKULAR_HISTORY_SAVEDSTEPS;
1224 while (backSteps-- && backIterator != m_viewportHistory.constBegin())
1228 QDomElement historyNode = doc.createElement(QStringLiteral(
"history"));
1234 while (backIterator != endIt) {
1235 QString name = (backIterator == currentViewportIterator) ? QStringLiteral(
"current") : QStringLiteral(
"oldPage");
1236 QDomElement historyEntry = doc.createElement(name);
1237 historyEntry.
setAttribute(QStringLiteral(
"viewport"), (*backIterator).toString());
1243 QDomElement viewsNode = doc.createElement(QStringLiteral(
"views"));
1245 for (
View *view : qAsConst(m_views)) {
1246 QDomElement viewEntry = doc.createElement(QStringLiteral(
"view"));
1249 saveViewsInfo(view, viewEntry);
1255 os.setCodec(
"UTF-8");
1260 void DocumentPrivate::slotTimedMemoryCheck()
1263 if (SettingsCore::memoryLevel() != SettingsCore::EnumMemoryLevel::Low && m_allocatedPixmapsTotalMemory > 1024 * 1024)
1264 cleanupPixmapMemory();
1267 void DocumentPrivate::sendGeneratorPixmapRequest()
1273 const qulonglong memoryToFree = calculateMemoryToFree();
1274 const int currentViewportPage = (*m_viewportIterator).pageNumber;
1275 int maxDistance = INT_MAX;
1277 AllocatedPixmap *pixmapToReplace = searchLowestPriorityPixmap(
true);
1278 if (pixmapToReplace)
1279 maxDistance = qAbs(pixmapToReplace->page - currentViewportPage);
1284 m_pixmapRequestsMutex.lock();
1285 while (!m_pixmapRequestsStack.isEmpty() && !request) {
1288 m_pixmapRequestsStack.pop_back();
1295 const QScreen *screen =
nullptr;
1299 screen = window->
screen();
1307 m_pixmapRequestsStack.pop_back();
1312 m_pixmapRequestsStack.pop_back();
1314 }
else if (!r->d->mForce && r->
preload() && qAbs(r->
pageNumber() - currentViewportPage) >= maxDistance) {
1315 m_pixmapRequestsStack.pop_back();
1321 m_pixmapRequestsStack.pop_back();
1325 else if (!tilesManager && m_generator->hasFeature(
Generator::TiledRendering) && (long)r->
width() * (long)r->
height() > 4L * screenSize && normalizedArea < 0.75 && normalizedArea != 0) {
1327 qCDebug(OkularCoreDebug).nospace() <<
"Start using tiles on page " << r->
pageNumber() <<
" (" << r->
width() <<
"x" << r->
height() <<
" px);";
1333 tilesManager->setPixmap(pixmap,
NormalizedRect(0, 0, 1, 1),
true );
1341 r->
page()->d->setTilesManager(r->
observer(), tilesManager);
1350 while (tIt != tEnd) {
1353 tilesRect = tile.
rect();
1355 tilesRect |= tile.
rect();
1367 m_pixmapRequestsStack.pop_back();
1372 else if (tilesManager && (
long)r->
width() * (long)r->
height() < 3L * screenSize) {
1373 qCDebug(OkularCoreDebug).nospace() <<
"Stop using tiles on page " << r->
pageNumber() <<
" (" << r->
width() <<
"x" << r->
height() <<
" px);";
1380 }
else if ((
long)requestRect.
width() * (long)requestRect.
height() > 100L * screenSize && (SettingsCore::memoryLevel() != SettingsCore::EnumMemoryLevel::Greedy)) {
1381 m_pixmapRequestsStack.pop_back();
1382 if (!m_warnedOutOfMemory) {
1383 qCWarning(OkularCoreDebug).nospace() <<
"Running out of memory on page " << r->
pageNumber() <<
" (" << r->
width() <<
"x" << r->
height() <<
" px);";
1384 qCWarning(OkularCoreDebug) <<
"this message will be reported only once.";
1385 m_warnedOutOfMemory =
true;
1395 m_pixmapRequestsMutex.unlock();
1400 qulonglong pixmapBytes = 0;
1403 pixmapBytes = tm->totalMemory();
1405 pixmapBytes = 4 * request->
width() * request->
height();
1407 if (pixmapBytes > (1024 * 1024))
1408 cleanupPixmapMemory(memoryToFree );
1411 if (m_generator->canGeneratePixmap()) {
1413 qCDebug(OkularCoreDebug).nospace() <<
"sending request observer=" << request->
observer() <<
" " << requestRect.
width() <<
"x" << requestRect.
height() <<
"@" << request->
pageNumber() <<
" async == " << request->
asynchronous()
1414 <<
" isTile == " << request->
isTile();
1415 m_pixmapRequestsStack.removeAll(request);
1420 if ((
int)m_rotation % 2)
1434 m_executingPixmapRequests.push_back(request);
1435 m_pixmapRequestsMutex.unlock();
1436 m_generator->generatePixmap(request);
1438 m_pixmapRequestsMutex.unlock();
1444 void DocumentPrivate::rotationFinished(
int page,
Okular::Page *okularPage)
1446 Okular::Page *wantedPage = m_pagesVector.value(page,
nullptr);
1447 if (!wantedPage || wantedPage != okularPage)
1454 void DocumentPrivate::slotFontReadingProgress(
int page)
1456 emit m_parent->fontReadingProgress(page);
1458 if (page >= (
int)m_parent->pages() - 1) {
1459 emit m_parent->fontReadingEnded();
1460 m_fontThread =
nullptr;
1461 m_fontsCached =
true;
1468 if (m_fontsCache.indexOf(font) == -1) {
1469 m_fontsCache.append(font);
1471 emit m_parent->gotFont(font);
1475 void DocumentPrivate::slotGeneratorConfigChanged()
1481 bool configchanged =
false;
1483 for (; it != itEnd; ++it) {
1487 if (it_changed && (m_generator == it.value().generator))
1488 configchanged =
true;
1491 if (configchanged) {
1494 for (; it !=
end; ++it) {
1495 (*it)->deletePixmaps();
1499 qDeleteAll(m_allocatedPixmaps);
1500 m_allocatedPixmaps.clear();
1501 m_allocatedPixmapsTotalMemory = 0;
1508 if (SettingsCore::memoryLevel() == SettingsCore::EnumMemoryLevel::Low && !m_allocatedPixmaps.isEmpty() && !m_pagesVector.isEmpty())
1509 cleanupPixmapMemory();
1512 void DocumentPrivate::refreshPixmaps(
int pageNumber)
1514 Page *page = m_pagesVector.value(pageNumber,
nullptr);
1520 for (; it != itEnd; ++it) {
1521 const QSize size = (*it).m_pixmap->size();
1523 p->d->mForce =
true;
1524 pixmapsToRequest << p;
1538 TilesManager *tilesManager = page->d->tilesManager(observer);
1540 tilesManager->markDirty();
1542 PixmapRequest *p =
new PixmapRequest(observer, pageNumber, tilesManager->width() / qApp->devicePixelRatio(), tilesManager->height() / qApp->devicePixelRatio(), 1, PixmapRequest::Asynchronous);
1547 for (; vIt != vEnd; ++vIt) {
1548 if ((*vIt)->pageNumber == pageNumber) {
1549 visibleRect = (*vIt)->rect;
1554 if (!visibleRect.isNull()) {
1557 p->d->mForce =
true;
1568 void DocumentPrivate::_o_configChanged()
1571 calculateMaxTextPages();
1572 while (m_allocatedTextPagesFifo.count() > m_maxAllocatedTextPages) {
1573 int pageToKick = m_allocatedTextPagesFifo.takeFirst();
1574 m_pagesVector.at(pageToKick)->setTextPage(
nullptr);
1578 void DocumentPrivate::doContinueDirectionMatchSearch(
void *doContinueDirectionMatchSearchStruct)
1580 DoContinueDirectionMatchSearchStruct *searchStruct =
static_cast<DoContinueDirectionMatchSearchStruct *
>(doContinueDirectionMatchSearchStruct);
1581 RunningSearch *search = m_searches.value(searchStruct->searchID);
1583 if ((m_searchCancelled && !searchStruct->match) || !search) {
1588 search->isCurrentlySearching =
false;
1591 delete searchStruct->pagesToNotify;
1592 delete searchStruct;
1597 bool doContinue =
false;
1599 if (!searchStruct->match) {
1600 const int pageCount = m_pagesVector.count();
1601 if (search->pagesDone < pageCount) {
1603 if (searchStruct->currentPage >= pageCount) {
1604 searchStruct->currentPage = 0;
1605 emit m_parent->notice(
i18n(
"Continuing search from beginning"), 3000);
1606 }
else if (searchStruct->currentPage < 0) {
1607 searchStruct->currentPage = pageCount - 1;
1608 emit m_parent->notice(
i18n(
"Continuing search from bottom"), 3000);
1615 Page *page = m_pagesVector[searchStruct->currentPage];
1618 m_parent->requestTextPage(page->
number());
1621 searchStruct->match = page->
findText(searchStruct->searchID, search->cachedString, forward ?
FromTop :
FromBottom, search->cachedCaseSensitivity);
1622 if (!searchStruct->match) {
1624 searchStruct->currentPage++;
1626 searchStruct->currentPage--;
1627 search->pagesDone++;
1629 search->pagesDone = 1;
1633 QTimer::singleShot(0, m_parent, [
this, searchStruct] { doContinueDirectionMatchSearch(searchStruct); });
1635 doProcessSearchMatch(searchStruct->match, search, searchStruct->pagesToNotify, searchStruct->currentPage, searchStruct->searchID, search->cachedViewportMove, search->cachedColor);
1636 delete searchStruct;
1640 void DocumentPrivate::doProcessSearchMatch(
RegularAreaRect *match, RunningSearch *search,
QSet<int> *pagesToNotify,
int currentPage,
int searchID,
bool moveViewport,
const QColor &color)
1645 bool foundAMatch =
false;
1647 search->isCurrentlySearching =
false;
1653 search->continueOnPage = currentPage;
1654 search->continueOnMatch = *match;
1655 search->highlightedPages.insert(currentPage);
1657 m_pagesVector[currentPage]->d->setHighlight(searchID, match, color);
1660 pagesToNotify->
insert(currentPage);
1665 const bool matchRectFullyVisible = isNormalizedRectangleFullyVisible(matchRectWithBuffer, currentPage);
1668 if (moveViewport && !matchRectFullyVisible) {
1670 searchViewport.rePos.enabled =
true;
1671 searchViewport.rePos.normalizedX = (match->
first().left + match->
first().right) / 2.0;
1672 searchViewport.rePos.normalizedY = (match->
first().top + match->
first().bottom) / 2.0;
1673 m_parent->setViewport(searchViewport,
nullptr,
true);
1679 foreach (
int pageNumber, *pagesToNotify)
1688 delete pagesToNotify;
1691 void DocumentPrivate::doContinueAllDocumentSearch(
void *pagesToNotifySet,
void *pageMatchesMap,
int currentPage,
int searchID)
1695 RunningSearch *search = m_searches.value(searchID);
1697 if (m_searchCancelled || !search) {
1703 search->isCurrentlySearching =
false;
1706 foreach (
const MatchesVector &mv, *pageMatches)
1709 delete pagesToNotify;
1713 if (currentPage < m_pagesVector.count()) {
1715 Page *page = m_pagesVector.at(currentPage);
1716 int pageNumber = page->
number();
1720 m_parent->requestTextPage(pageNumber);
1726 lastMatch = page->
findText(searchID, search->cachedString,
NextResult, search->cachedCaseSensitivity, lastMatch);
1728 lastMatch = page->
findText(searchID, search->cachedString,
FromTop, search->cachedCaseSensitivity);
1734 (*pageMatches)[page].
append(lastMatch);
1738 QTimer::singleShot(0, m_parent, [
this, pagesToNotifySet, pageMatches, currentPage, searchID] { doContinueAllDocumentSearch(pagesToNotifySet, pageMatches, currentPage + 1, searchID); });
1743 search->isCurrentlySearching =
false;
1744 bool foundAMatch = pageMatches->
count() != 0;
1748 for (; it != itEnd; ++it) {
1750 it.
key()->d->setHighlight(searchID, match, search->cachedColor);
1753 search->highlightedPages.
insert(it.
key()->number());
1754 pagesToNotify->
insert(it.
key()->number());
1761 foreach (
int pageNumber, *pagesToNotify)
1771 delete pagesToNotify;
1775 void DocumentPrivate::doContinueGooglesDocumentSearch(
void *pagesToNotifySet,
void *pageMatchesMap,
int currentPage,
int searchID,
const QStringList &words)
1780 RunningSearch *search = m_searches.value(searchID);
1782 if (m_searchCancelled || !search) {
1788 search->isCurrentlySearching =
false;
1792 foreach (
const MatchesVector &mv, *pageMatches) {
1793 foreach (
const MatchColor &mc, mv)
1797 delete pagesToNotify;
1801 const int wordCount = words.
count();
1802 const int hueStep = (wordCount > 1) ? (60 / (wordCount - 1)) : 60;
1803 int baseHue, baseSat, baseVal;
1804 search->cachedColor.getHsv(&baseHue, &baseSat, &baseVal);
1806 if (currentPage < m_pagesVector.count()) {
1808 Page *page = m_pagesVector.at(currentPage);
1809 int pageNumber = page->
number();
1813 m_parent->requestTextPage(pageNumber);
1816 bool allMatched = wordCount > 0, anyMatched =
false;
1817 for (
int w = 0; w < wordCount; w++) {
1818 const QString &word = words[w];
1819 int newHue = baseHue - w * hueStep;
1825 bool wordMatched =
false;
1828 lastMatch = page->
findText(searchID, word,
NextResult, search->cachedCaseSensitivity, lastMatch);
1830 lastMatch = page->
findText(searchID, word,
FromTop, search->cachedCaseSensitivity);
1836 (*pageMatches)[page].
append(MatchColor(lastMatch, wordColor));
1839 allMatched = allMatched && wordMatched;
1840 anyMatched = anyMatched || wordMatched;
1845 if (!allMatched && matchAll) {
1847 foreach (
const MatchColor &mc, matches)
1849 pageMatches->
remove(page);
1852 QTimer::singleShot(0, m_parent, [
this, pagesToNotifySet, pageMatches, currentPage, searchID, words] { doContinueGooglesDocumentSearch(pagesToNotifySet, pageMatches, currentPage + 1, searchID, words); });
1857 search->isCurrentlySearching =
false;
1858 bool foundAMatch = pageMatches->
count() != 0;
1862 for (; it != itEnd; ++it) {
1863 foreach (
const MatchColor &mc, it.
value()) {
1864 it.
key()->d->setHighlight(searchID, mc.first, mc.second);
1867 search->highlightedPages.
insert(it.
key()->number());
1868 pagesToNotify->
insert(it.
key()->number());
1876 foreach (
int pageNumber, *pagesToNotify)
1886 delete pagesToNotify;
1894 bool giveDefault = option.
toBool();
1896 if ((SettingsCore::renderMode() == SettingsCore::EnumRenderMode::Paper) && SettingsCore::changeColors()) {
1897 color = SettingsCore::paperColor();
1898 }
else if (giveDefault) {
1905 switch (SettingsCore::textAntialias()) {
1906 case SettingsCore::EnumTextAntialias::Enabled:
1909 case SettingsCore::EnumTextAntialias::Disabled:
1916 switch (SettingsCore::graphicsAntialias()) {
1917 case SettingsCore::EnumGraphicsAntialias::Enabled:
1920 case SettingsCore::EnumGraphicsAntialias::Disabled:
1927 switch (SettingsCore::textHinting()) {
1928 case SettingsCore::EnumTextHinting::Enabled:
1931 case SettingsCore::EnumTextHinting::Disabled:
1940 bool DocumentPrivate::isNormalizedRectangleFullyVisible(
const Okular::NormalizedRect &rectOfInterest,
int rectPage)
1942 bool rectFullyVisible =
false;
1947 for (; (vIt != vEnd) && !rectFullyVisible; ++vIt) {
1948 if ((*vIt)->pageNumber == rectPage && (*vIt)->rect.contains(rectOfInterest.
left, rectOfInterest.
top) && (*vIt)->rect.contains(rectOfInterest.
right, rectOfInterest.
bottom)) {
1949 rectFullyVisible =
true;
1952 return rectFullyVisible;
1955 struct pdfsyncpoint {
1964 void DocumentPrivate::loadSyncFile(
const QString &filePath)
1972 const QString coreName = ts.readLine();
1974 const QString versionstr = ts.readLine();
1985 int currentpage = -1;
1989 fileStack.
push(coreName + texStr);
1991 const QSizeF dpi = m_generator->dpi();
1994 while (!ts.atEnd()) {
1995 line = ts.readLine();
1997 const int tokenSize = tokens.
count();
2001 int id = tokens.
at(1).toInt();
2007 pt.row = tokens.
at(2).toInt();
2010 pt.file = fileStack.
top();
2014 currentpage = tokens.
at(1).toInt() - 1;
2017 qCDebug(OkularCoreDebug) <<
"PdfSync: 'p*' line ignored";
2019 int id = tokens.
at(1).toInt();
2021 if (it != points.
end()) {
2022 it->x = tokens.
at(2).toInt();
2023 it->y = tokens.
at(3).toInt();
2024 it->page = currentpage;
2033 fileStack.
push(newfile);
2038 qCDebug(OkularCoreDebug) <<
"PdfSync: going one level down too much";
2040 qCDebug(OkularCoreDebug).nospace() <<
"PdfSync: unknown line format: '" << line <<
"'";
2044 for (
const pdfsyncpoint &pt : qAsConst(points)) {
2046 if (pt.page < 0 || pt.page >= m_pagesVector.size())
2050 Okular::NormalizedPoint p((pt.x * dpi.
width()) / (72.27 * 65536.0 * m_pagesVector[pt.page]->width()), (pt.y * dpi.
height()) / (72.27 * 65536.0 * m_pagesVector[pt.page]->height()));
2055 for (
int i = 0; i < refRects.size(); ++i)
2056 if (!refRects.at(i).isEmpty())
2057 m_pagesVector[i]->setSourceReferences(refRects.at(i));
2060 void DocumentPrivate::clearAndWaitForRequests()
2062 m_pixmapRequestsMutex.lock();
2065 for (; sIt != sEnd; ++sIt)
2067 m_pixmapRequestsStack.clear();
2068 m_pixmapRequestsMutex.unlock();
2071 bool startEventLoop =
false;
2073 m_pixmapRequestsMutex.lock();
2074 startEventLoop = !m_executingPixmapRequests.isEmpty();
2077 for (
PixmapRequest *executingRequest : qAsConst(m_executingPixmapRequests))
2078 executingRequest->d->mShouldAbortRender = 1;
2080 if (m_generator->d_ptr->mTextPageGenerationThread)
2081 m_generator->d_ptr->mTextPageGenerationThread->abortExtraction();
2084 m_pixmapRequestsMutex.unlock();
2085 if (startEventLoop) {
2086 m_closingLoop = &loop;
2088 m_closingLoop =
nullptr;
2090 }
while (startEventLoop);
2097 for (uint pageIdx = 0, nPages = m_parent->pages(); pageIdx < nPages; pageIdx++) {
2098 const Page *p = m_parent->
page(pageIdx);
2100 foundPage =
static_cast<int>(pageIdx);
2107 void DocumentPrivate::executeScriptEvent(
const std::shared_ptr<Event> &event,
const Okular::ScriptAction *linkscript)
2110 m_scripter =
new Scripter(
this);
2112 m_scripter->setEvent(event.get());
2116 m_scripter->setEvent(
nullptr);
2121 , d(new DocumentPrivate(this))
2123 d->m_widget = widget;
2125 d->m_viewportIterator = d->m_viewportHistory.insert(d->m_viewportHistory.end(),
DocumentViewport());
2128 connect(SettingsCore::self(), &SettingsCore::configChanged,
this, [
this] { d->_o_configChanged(); });
2133 qRegisterMetaType<Okular::FontInfo>();
2142 for (; viewIt != viewEnd; ++viewIt) {
2144 v->d_func()->document =
nullptr;
2148 delete d->m_bookmarkManager;
2152 for (; it != itEnd; ++it)
2153 d->unloadGenerator(it.value());
2154 d->m_loadedGenerators.clear();
2160 QString DocumentPrivate::docDataFileName(
const QUrl &url, qint64 document_size)
2167 qCDebug(OkularCoreDebug) <<
"creating docdata folder" << docdataDir;
2172 if (!
QFile::exists(newokularfile) && !QStandardPaths::isTestModeEnabled()) {
2175 QString oldfile = k4migration.
locateLocal(
"data", QStringLiteral(
"okular/docdata/") + fn);
2177 oldfile = k4migration.
locateLocal(
"data", QStringLiteral(
"kpdf/") + fn);
2185 return newokularfile;
2213 for (
const QString &supported : mimetypes) {
2215 if (mimeType == type && !exactMatches.
contains(md)) {
2225 if (!exactMatches.
isEmpty()) {
2226 offers = exactMatches;
2234 int offercount = offers.
size();
2235 if (offercount > 1) {
2238 const QString property = QStringLiteral(
"X-KDE-Priority");
2241 std::stable_sort(offers.
begin(), offers.
end(), cmp);
2243 if (SettingsCore::chooseGenerators()) {
2245 for (
int i = 0; i < offercount; ++i) {
2246 list << offers.
at(i).pluginId();
2248 ChooseEngineDialog choose(list, type, widget);
2253 hRank = choose.selectedGenerator();
2256 Q_ASSERT(hRank < offers.
size());
2257 return offers.
at(hRank);
2275 bool triedMimeFromFileContent =
false;
2281 d->m_docFileName = docFile;
2283 if (!d->updateMetadataXmlNameAndDocSize())
2289 qWarning() <<
"failed to read" << url << filedata;
2297 d->m_docSize = filedata.
size();
2298 triedMimeFromFileContent =
true;
2301 const bool fromFileDescriptor = fd >= 0;
2305 KPluginMetaData offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget);
2306 if (!offer.
isValid() && !triedMimeFromFileContent) {
2308 triedMimeFromFileContent =
true;
2309 if (newmime != mime) {
2311 offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget);
2319 if (!newmime.
isDefault() && newmime != mime) {
2321 offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget);
2326 d->m_openError =
i18n(
"Can not find a plugin which is able to handle the document being passed.");
2327 emit
error(d->m_openError, -1);
2328 qCWarning(OkularCoreDebug).nospace() <<
"No plugin for mimetype '" << mime.
name() <<
"'.";
2333 OpenResult openResult = d->openDocumentInternal(offer, fromFileDescriptor, docFile, filedata, password);
2334 if (openResult == OpenError) {
2336 triedOffers << offer;
2337 offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget, triedOffers);
2339 while (offer.isValid()) {
2340 openResult = d->openDocumentInternal(offer, fromFileDescriptor, docFile, filedata, password);
2342 if (openResult == OpenError) {
2343 triedOffers << offer;
2344 offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget, triedOffers);
2349 if (openResult == OpenError && !triedMimeFromFileContent) {
2351 triedMimeFromFileContent =
true;
2352 if (newmime != mime) {
2354 offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget, triedOffers);
2355 while (offer.isValid()) {
2356 openResult = d->openDocumentInternal(offer, fromFileDescriptor, docFile, filedata, password);
2358 if (openResult == OpenError) {
2359 triedOffers << offer;
2360 offer = DocumentPrivate::generatorForMimeType(mime, d->m_widget, triedOffers);
2367 if (openResult == OpenSuccess) {
2375 if (openResult != OpenSuccess) {
2381 d->m_synctex_scanner = synctex_scanner_new_with_output_file(
QFile::encodeName(docFile).constData(),
nullptr, 1);
2383 d->loadSyncFile(docFile);
2386 d->m_generatorName = offer.
pluginId();
2387 d->m_pageController =
new PageController();
2388 connect(d->m_pageController, &PageController::rotationFinished,
this, [
this](
int p,
Okular::Page *op) { d->rotationFinished(p, op); });
2390 for (
Page *p : qAsConst(d->m_pagesVector))
2393 d->m_metadataLoadingCompleted =
false;
2394 d->m_docdataMigrationNeeded =
false;
2397 if (d->m_archiveData) {
2399 d->m_archiveData->metadataFile.fileName();
2400 d->loadDocumentInfo(d->m_archiveData->metadataFile, LoadPageInfo);
2401 d->loadDocumentInfo(LoadGeneralInfo);
2403 if (d->loadDocumentInfo(LoadPageInfo))
2404 d->m_docdataMigrationNeeded =
true;
2405 d->loadDocumentInfo(LoadGeneralInfo);
2408 d->m_metadataLoadingCompleted =
true;
2409 d->m_bookmarkManager->setUrl(d->m_url);
2416 if (loadedViewport.
isValid()) {
2418 if (loadedViewport.
pageNumber >= (
int)d->m_pagesVector.size())
2419 loadedViewport.
pageNumber = d->m_pagesVector.size() - 1;
2425 if (!d->m_saveBookmarksTimer) {
2426 d->m_saveBookmarksTimer =
new QTimer(
this);
2429 d->m_saveBookmarksTimer->start(5 * 60 * 1000);
2432 if (!d->m_memCheckTimer) {
2433 d->m_memCheckTimer =
new QTimer(
this);
2436 d->m_memCheckTimer->start(kMemCheckTime);
2442 d->m_nextDocumentDestination =
QString();
2447 const QStringList docScripts = d->m_generator->metaData(QStringLiteral(
"DocumentScripts"), QStringLiteral(
"JavaScript")).toStringList();
2449 d->m_scripter =
new Scripter(d);
2450 for (
const QString &docscript : docScripts) {
2451 d->m_scripter->execute(
JavaScript, docscript);
2458 bool DocumentPrivate::updateMetadataXmlNameAndDocSize()
2465 m_docSize = fileReadTest.
size();
2468 if (m_url.isLocalFile()) {
2469 const QString filePath = docDataFileName(m_url, m_docSize);
2470 qCDebug(OkularCoreDebug) <<
"Metadata file is now:" << filePath;
2471 m_xmlFileName = filePath;
2473 qCDebug(OkularCoreDebug) <<
"Metadata file: disabled";
2482 if (d->m_generator) {
2493 if (!d->m_generator)
2498 delete d->m_pageController;
2499 d->m_pageController =
nullptr;
2501 delete d->m_scripter;
2502 d->m_scripter =
nullptr;
2505 d->clearAndWaitForRequests();
2507 if (d->m_fontThread) {
2508 disconnect(d->m_fontThread,
nullptr,
this,
nullptr);
2509 d->m_fontThread->stopExtraction();
2510 d->m_fontThread->wait();
2511 d->m_fontThread =
nullptr;
2518 if (d->m_generator && d->m_pagesVector.size() > 0) {
2519 d->saveDocumentInfo();
2520 d->m_generator->closeDocument();
2523 if (d->m_synctex_scanner) {
2524 synctex_scanner_free(d->m_synctex_scanner);
2525 d->m_synctex_scanner =
nullptr;
2529 if (d->m_memCheckTimer)
2530 d->m_memCheckTimer->stop();
2531 if (d->m_saveBookmarksTimer)
2532 d->m_saveBookmarksTimer->stop();
2534 if (d->m_generator) {
2536 d->m_generator->d_func()->m_document =
nullptr;
2538 disconnect(d->m_generator,
nullptr,
this,
nullptr);
2541 Q_ASSERT(genIt != d->m_loadedGenerators.constEnd());
2543 d->m_generator =
nullptr;
2544 d->m_generatorName =
QString();
2546 d->m_walletGenerator =
nullptr;
2549 delete d->m_tempFile;
2550 d->m_tempFile =
nullptr;
2551 delete d->m_archiveData;
2552 d->m_archiveData =
nullptr;
2554 d->m_exportCached =
false;
2555 d->m_exportFormats.clear();
2557 d->m_fontsCached =
false;
2558 d->m_fontsCache.clear();
2567 for (; pIt != pEnd; ++pIt)
2569 d->m_pagesVector.clear();
2572 qDeleteAll(d->m_allocatedPixmaps);
2573 d->m_allocatedPixmaps.clear();
2578 for (; rIt != rEnd; ++rIt)
2580 d->m_searches.clear();
2585 for (; vIt != vEnd; ++vIt)
2587 d->m_pageRects.clear();
2588 foreachObserver(notifyVisibleRectsChanged());
2592 d->m_viewportHistory.clear();
2594 d->m_viewportIterator = d->m_viewportHistory.begin();
2595 d->m_allocatedPixmapsTotalMemory = 0;
2596 d->m_allocatedTextPagesFifo.clear();
2598 d->m_pageSizes.clear();
2601 d->m_documentInfoAskedKeys.clear();
2605 d->m_undoStack->clear();
2606 d->m_docdataMigrationNeeded =
false;
2608 #if HAVE_MALLOC_TRIM 2618 Q_ASSERT(!d->m_observers.contains(pObserver));
2619 d->m_observers << pObserver;
2622 if (!d->m_pagesVector.isEmpty()) {
2631 if (d->m_observers.contains(pObserver)) {
2634 for (; it != end; ++it)
2635 (*it)->deletePixmap(pObserver);
2640 while (aIt != aEnd) {
2641 AllocatedPixmap *p = *aIt;
2642 if (p->observer == pObserver) {
2643 aIt = d->m_allocatedPixmaps.erase(aIt);
2649 for (
PixmapRequest *executingRequest : qAsConst(d->m_executingPixmapRequests)) {
2650 if (executingRequest->observer() == pObserver) {
2651 d->cancelRenderingBecauseOf(executingRequest,
nullptr);
2656 d->m_observers.remove(pObserver);
2663 bool configchanged =
false;
2664 if (d->m_generator) {
2669 if (configchanged) {
2672 for (; it != end; ++it) {
2673 (*it)->deletePixmaps();
2677 qDeleteAll(d->m_allocatedPixmaps);
2678 d->m_allocatedPixmaps.clear();
2679 d->m_allocatedPixmapsTotalMemory = 0;
2686 if (SettingsCore::memoryLevel() == SettingsCore::EnumMemoryLevel::Low && !d->m_allocatedPixmaps.isEmpty() && !d->m_pagesVector.isEmpty())
2687 d->cleanupPixmapMemory();
2692 return d->m_generator;
2697 if (d->m_generator) {
2699 return iface ?
true :
false;
2706 if (d->m_generator->canSign()) {
2707 return d->m_generator->sign(data, newPath);
2715 return d->m_generator ? d->m_generator->certificateStore() :
nullptr;
2733 if (d->m_generator && !missingKeys.
isEmpty()) {
2734 DocumentInfo info = d->m_generator->generateDocumentInfo(missingKeys);
2745 const QString pagesSize = d->pagesSizeString();
2755 d->m_documentInfo.d->values.unite(info.d->values);
2756 d->m_documentInfo.d->titles.unite(info.d->titles);
2757 result.d->values.unite(info.d->values);
2758 result.d->titles.unite(info.d->titles);
2760 d->m_documentInfoAskedKeys += keys;
2767 return d->m_generator ? d->m_generator->generateDocumentSynopsis() :
nullptr;
2772 if (!d->m_generator || !d->m_generator->hasFeature(
Generator::FontInfo) || d->m_fontThread)
2775 if (d->m_fontsCached) {
2779 for (
int i = 0; i < d->m_fontsCache.count(); ++i) {
2780 emit
gotFont(d->m_fontsCache.at(i));
2787 d->m_fontThread =
new FontExtractionThread(d->m_generator,
pages());
2788 connect(d->m_fontThread, &FontExtractionThread::gotFont,
this, [
this](
const Okular::FontInfo &f) { d->fontReadingGotFont(f); });
2789 connect(d->m_fontThread.data(), &FontExtractionThread::progress,
this, [
this](
int p) { d->slotFontReadingProgress(p); });
2791 d->m_fontThread->startExtraction(
true);
2796 if (!d->m_fontThread)
2799 disconnect(d->m_fontThread,
nullptr,
this,
nullptr);
2800 d->m_fontThread->stopExtraction();
2801 d->m_fontThread =
nullptr;
2802 d->m_fontsCache.clear();
2812 return d->m_generator ? d->m_generator->canSign() :
false;
2817 return d->m_generator ? d->m_generator->embeddedFiles() :
nullptr;
2822 return (n >= 0 && n < d->m_pagesVector.count()) ? d->m_pagesVector.at(n) :
nullptr;
2827 return (*d->m_viewportIterator);
2832 return d->m_pageRects;
2839 for (; vIt != vEnd; ++vIt)
2844 if (o != excludeObserver)
2850 return (*d->m_viewportIterator).pageNumber;
2855 return d->m_pagesVector.size();
2865 if (action ==
Okular::AllowNotes && (d->m_docdataMigrationNeeded || !d->m_annotationEditingEnabled))
2870 #if !OKULAR_FORCE_DRM 2875 return d->m_generator ? d->m_generator->isAllowed(action) :
false;
2895 if (d->m_generator) {
2896 if (d->m_pageSizes.isEmpty())
2897 d->m_pageSizes = d->m_generator->pageSizes();
2898 return d->m_pageSizes;
2905 if (!d->m_generator)
2908 d->cacheExportFormats();
2909 return !d->m_exportToText.isNull();
2914 if (!d->m_generator)
2917 d->cacheExportFormats();
2918 if (d->m_exportToText.isNull())
2921 return d->m_generator->exportTo(fileName, d->m_exportToText);
2926 if (!d->m_generator)
2929 d->cacheExportFormats();
2930 return d->m_exportFormats;
2935 return d->m_generator ? d->m_generator->exportTo(fileName, format) :
false;
2940 return d->m_viewportIterator == d->m_viewportHistory.begin();
2945 return d->m_viewportIterator == --(d->m_viewportHistory.end());
2962 name = reference.
mid(4);
2964 int nameLength = name.
length();
2966 for (i = 0; i < nameLength; ++i) {
2967 if (!name[i].isDigit())
2970 lineString = name.
left(i);
2974 lineString = lineString.
trimmed();
2977 int line = lineString.
toInt(&ok);
2983 synctex_node_p node;
2986 while ((node = synctex_scanner_next_result(d->m_synctex_scanner))) {
2990 viewport.
pageNumber = synctex_node_page(node) - 1;
2993 const QSizeF dpi = d->m_generator->dpi();
2996 double px = (synctex_node_visible_h(node) * dpi.
width()) / 72.27;
2997 double py = (synctex_node_visible_v(node) * dpi.
height()) / 72.27;
3000 viewport.
rePos.enabled =
true;
3008 return d->m_generator ? d->m_generator->metaData(key, option) :
QVariant();
3013 return d->m_rotation;
3018 bool allPagesSameSize =
true;
3020 for (
int i = 0; allPagesSameSize && i < d->m_pagesVector.count(); ++i) {
3021 const Page *p = d->m_pagesVector.at(i);
3028 if (allPagesSameSize)
3036 if (d->m_generator) {
3038 const Page *p = d->m_pagesVector.at(page);
3067 if (executingRequest.
width() != otherRequest.
width())
3071 if (executingRequest.
height() != otherRequest.
height())
3075 if (executingRequest.
isTile() != otherRequest.
isTile())
3079 if (executingRequest.
isTile()) {
3091 if (!executingRequest->d->mResultImage.isNull())
3098 TilesManager *tm = executingRequest->d->tilesManager();
3100 tm->setPixmap(
nullptr, executingRequest->
normalizedRect(),
true );
3103 PagePrivate::PixmapObject
object = executingRequest->
page()->d->m_pixmaps.take(executingRequest->
observer());
3104 delete object.m_pixmap;
3106 if (executingRequest->d->mShouldAbortRender != 0)
3109 executingRequest->d->mShouldAbortRender = 1;
3111 if (m_generator->d_ptr->mTextPageGenerationThread && m_generator->d_ptr->mTextPageGenerationThread->page() == executingRequest->
page()) {
3112 m_generator->d_ptr->mTextPageGenerationThread->abortExtraction();
3128 if (!d->m_pageController) {
3131 for (; rIt != rEnd; ++rIt)
3144 for (; rIt != rEnd; ++rIt) {
3145 Q_ASSERT((*rIt)->observer() == requesterObserver);
3146 requestedPages.
insert((*rIt)->pageNumber());
3150 d->m_pixmapRequestsMutex.lock();
3152 while (sIt != sEnd) {
3153 if ((*sIt)->observer() == requesterObserver && (removeAllPrevious || requestedPages.
contains((*sIt)->pageNumber()))) {
3156 sIt = d->m_pixmapRequestsStack.erase(sIt);
3164 qCDebug(OkularCoreDebug).nospace() <<
"request observer=" << request->
observer() <<
" " << request->
width() <<
"x" << request->
height() <<
"@" << request->
pageNumber();
3165 if (d->m_pagesVector.value(request->
pageNumber()) ==
nullptr) {
3171 request->d->mPage = d->m_pagesVector.value(request->
pageNumber());
3179 while (tIt != tEnd) {
3180 const Tile &tile = *tIt;
3183 tilesRect = tile.
rect();
3185 tilesRect |= tile.
rect();
3195 request->d->mPriority = 0;
3200 for (
PixmapRequest *executingRequest : qAsConst(d->m_executingPixmapRequests)) {
3201 bool newRequestsContainExecutingRequestPage =
false;
3202 bool requestCancelled =
false;
3205 newRequestsContainExecutingRequestPage =
true;
3208 if (shouldCancelRenderingBecauseOf(*executingRequest, *newRequest)) {
3209 requestCancelled = d->cancelRenderingBecauseOf(executingRequest, newRequest);
3214 if (!requestCancelled && removeAllPrevious && requesterObserver == executingRequest->
observer() && !newRequestsContainExecutingRequestPage) {
3215 requestCancelled = d->cancelRenderingBecauseOf(executingRequest,
nullptr);
3218 if (requestCancelled) {
3219 observersPixmapCleared << executingRequest->
observer();
3229 d->m_pixmapRequestsStack.append(request);
3232 sIt = d->m_pixmapRequestsStack.begin();
3233 sEnd = d->m_pixmapRequestsStack.end();
3234 while (sIt != sEnd && (*sIt)->priority() > request->
priority())
3236 d->m_pixmapRequestsStack.insert(sIt, request);
3239 d->m_pixmapRequestsMutex.unlock();
3246 d->sendGeneratorPixmapRequest();
3254 Page *kp = d->m_pagesVector[pageNumber];
3255 if (!d->m_generator || !kp)
3260 d->m_generator->generateTextPage(kp);
3263 void DocumentPrivate::notifyAnnotationChanges(
int page)
3268 void DocumentPrivate::notifyFormChanges(
int )
3278 annotation->d_ptr->baseTransform(t.
inverted());
3279 QUndoCommand *uc =
new AddAnnotationCommand(this->d, annotation, page);
3280 d->m_undoStack->push(uc);
3294 switch (annotation->
subType()) {
3309 Q_ASSERT(d->m_prevPropsOfAnnotBeingModified.isNull());
3310 if (!d->m_prevPropsOfAnnotBeingModified.isNull()) {
3311 qCCritical(OkularCoreDebug) <<
"Error: Document::prepareToModifyAnnotationProperties has already been called since last call to Document::modifyPageAnnotationProperties";
3319 Q_ASSERT(!d->m_prevPropsOfAnnotBeingModified.isNull());
3320 if (d->m_prevPropsOfAnnotBeingModified.isNull()) {
3321 qCCritical(OkularCoreDebug) <<
"Error: Document::prepareToModifyAnnotationProperties must be called before Annotation is modified";
3324 QDomNode prevProps = d->m_prevPropsOfAnnotBeingModified;
3326 d->m_undoStack->push(uc);
3327 d->m_prevPropsOfAnnotBeingModified.clear();
3333 QUndoCommand *uc =
new Okular::TranslateAnnotationCommand(d, annotation, page, delta, complete);
3334 d->m_undoStack->push(uc);
3340 QUndoCommand *uc =
new Okular::AdjustAnnotationCommand(d, annotation, page, delta1, delta2, complete);
3341 d->m_undoStack->push(uc);
3347 QUndoCommand *uc =
new EditAnnotationContentsCommand(d, annotation, page, newContents, newCursorPos, prevContents, prevCursorPos, prevAnchorPos);
3348 d->m_undoStack->push(uc);
3359 switch (annotation->
subType()) {
3375 QUndoCommand *uc =
new RemoveAnnotationCommand(this->d, annotation, page);
3376 d->m_undoStack->push(uc);
3381 d->m_undoStack->beginMacro(
i18nc(
"remove a collection of annotations from the page",
"remove annotations"));
3382 foreach (
Annotation *annotation, annotations) {
3383 QUndoCommand *uc =
new RemoveAnnotationCommand(this->d, annotation, page);
3384 d->m_undoStack->push(uc);
3386 d->m_undoStack->endMacro();
3389 bool DocumentPrivate::canAddAnnotationsNatively()
const 3399 bool DocumentPrivate::canModifyExternalAnnotations()
const 3409 bool DocumentPrivate::canRemoveExternalAnnotations()
const 3422 if (!d->m_generator || !kp)
3427 kp->d->setTextSelections(rect, color);
3429 kp->d->deleteTextSelections();
3437 return d->m_undoStack->canUndo();
3442 return d->m_undoStack->canRedo();
3464 qCDebug(OkularCoreDebug) <<
"invalid viewport:" << viewport.
toString();
3467 if (viewport.
pageNumber >=
int(d->m_pagesVector.count())) {
3478 const int oldPageNumber = oldViewport.
pageNumber;
3486 d->m_viewportHistory.erase(++d->m_viewportIterator, d->m_viewportHistory.end());
3489 if (d->m_viewportHistory.count() >= OKULAR_HISTORY_MAXSTEPS)
3490 d->m_viewportHistory.pop_front();
3493 d->m_viewportIterator = d->m_viewportHistory.insert(d->m_viewportHistory.end(),
viewport);
3496 const int currentViewportPage = (*d->m_viewportIterator).pageNumber;
3498 const bool currentPageChanged = (oldPageNumber != currentViewportPage);
3502 if (o != excludeObserver)
3505 if (currentPageChanged)
3515 else if (page > (
int)d->m_pagesVector.count())
3516 page = d->m_pagesVector.count() - 1;
3532 if (o != excludeObserver)
3539 if (d->m_viewportIterator != d->m_viewportHistory.begin()) {
3540 const int oldViewportPage = (*d->m_viewportIterator).pageNumber;
3543 --d->m_viewportIterator;
3544 foreachObserver(notifyViewportChanged(
true));
3546 const int currentViewportPage = (*d->m_viewportIterator).pageNumber;
3547 if (oldViewportPage != currentViewportPage)
3548 foreachObserver(notifyCurrentPageChanged(oldViewportPage, currentViewportPage));
3557 if (nextIterator != d->m_viewportHistory.constEnd()) {
3558 const int oldViewportPage = (*d->m_viewportIterator).pageNumber;
3561 ++d->m_viewportIterator;
3562 foreachObserver(notifyViewportChanged(
true));
3564 const int currentViewportPage = (*d->m_viewportIterator).pageNumber;
3565 if (oldViewportPage != currentViewportPage)
3566 foreachObserver(notifyCurrentPageChanged(oldViewportPage, currentViewportPage));
3572 d->m_nextDocumentViewport =
viewport;
3577 d->m_nextDocumentDestination = namedDestination;
3582 d->m_searchCancelled =
false;
3592 if (searchIt == d->m_searches.end()) {
3593 RunningSearch *search =
new RunningSearch();
3594 search->continueOnPage = -1;
3595 searchIt = d->m_searches.insert(searchID, search);
3597 RunningSearch *s = *searchIt;
3600 bool newText = text != s->cachedString;
3601 s->cachedString = text;
3602 s->cachedType = type;
3603 s->cachedCaseSensitivity = caseSensitivity;
3604 s->cachedViewportMove = moveViewport;
3605 s->cachedColor = color;
3606 s->isCurrentlySearching =
true;
3612 *pagesToNotify += s->highlightedPages;
3613 for (
const int pageNumber : qAsConst(s->highlightedPages)) {
3614 d->m_pagesVector.at(pageNumber)->d->deleteHighlights(searchID);
3616 s->highlightedPages.clear();
3626 QTimer::singleShot(0,
this, [
this, pagesToNotify, pageMatches, searchID] { d->doContinueAllDocumentSearch(pagesToNotify, pageMatches, 0, searchID); });
3633 const int viewportPage = (*d->m_viewportIterator).pageNumber;
3634 const int fromStartSearchPage = forward ? 0 : d->m_pagesVector.count() - 1;
3635 int currentPage = fromStart ? fromStartSearchPage : ((s->continueOnPage != -1) ? s->continueOnPage : viewportPage);
3641 if (lastPage && lastPage->
number() == s->continueOnPage) {
3643 match = lastPage->
findText(searchID, text, forward ?
FromTop : FromBottom, caseSensitivity);
3655 s->pagesDone = pagesDone;
3657 DoContinueDirectionMatchSearchStruct *searchStruct =
new DoContinueDirectionMatchSearchStruct();
3658 searchStruct->pagesToNotify = pagesToNotify;
3659 searchStruct->match = match;
3661 searchStruct->searchID = searchID;
3663 QTimer::singleShot(0,
this, [
this, searchStruct] { d->doContinueDirectionMatchSearch(searchStruct); });
3671 QTimer::singleShot(0,
this, [
this, pagesToNotify, pageMatches, searchID, words] { d->doContinueGooglesDocumentSearch(pagesToNotify, pageMatches, 0, searchID, words); });
3679 if (it == d->m_searches.constEnd()) {
3685 RunningSearch *p = *it;
3686 if (!p->isCurrentlySearching)
3687 searchText(searchID, p->cachedString,
false, p->cachedCaseSensitivity, p->cachedType, p->cachedViewportMove, p->cachedColor);
3694 if (it == d->m_searches.constEnd()) {
3700 RunningSearch *p = *it;
3701 if (!p->isCurrentlySearching)
3702 searchText(searchID, p->cachedString,
false, p->cachedCaseSensitivity, type, p->cachedViewportMove, p->cachedColor);
3708 if (!d->m_generator)
3713 if (searchIt == d->m_searches.end())
3717 RunningSearch *s = *searchIt;
3720 for (
const int pageNumber : qAsConst(s->highlightedPages)) {
3721 d->m_pagesVector.at(pageNumber)->d->deleteHighlights(searchID);
3726 foreachObserver(notifySetup(d->m_pagesVector, 0));
3729 d->m_searches.erase(searchIt);
3735 d->m_searchCancelled =
true;
3740 d->m_undoStack->undo();
3745 d->m_undoStack->redo();
3750 QUndoCommand *uc =
new EditFormTextCommand(this->d, form, pageNumber, newContents, newCursorPos, form->
text(), prevCursorPos, prevAnchorPos);
3751 d->m_undoStack->push(uc);
3757 QUndoCommand *uc =
new EditFormListCommand(this->d, form, pageNumber, newChoices, prevChoices);
3758 d->m_undoStack->push(uc);
3770 QUndoCommand *uc =
new EditFormComboCommand(this->d, form, pageNumber, newText, newCursorPos, prevText, prevCursorPos, prevAnchorPos);
3771 d->m_undoStack->push(uc);
3776 QUndoCommand *uc =
new EditFormButtonsCommand(this->d, pageNumber, formButtons, newButtonStates);
3777 d->m_undoStack->push(uc);
3782 const int numOfPages =
pages();
3784 d->refreshPixmaps(i);
3785 for (
int i =
currentPage() + 1; i < numOfPages; i++)
3786 d->refreshPixmaps(i);
3791 return d->m_bookmarkManager;
3797 uint docPages =
pages();
3800 for (uint i = 0; i < docPages; i++) {
3813 uint docPages =
pages();
3817 for (uint i = 0; i < docPages; ++i) {
3825 }
else if (startId >= 0 && endId >= 0) {
3829 if (endId - startId > 0)
3830 range += QStringLiteral(
"%1-%2").
arg(startId + 1).
arg(endId + 1);
3837 if (startId >= 0 && endId >= 0) {
3841 if (endId - startId > 0)
3842 range += QStringLiteral(
"%1-%2").
arg(startId + 1).
arg(endId + 1);
3855 bool executeNextActions =
true;
3875 qCWarning(OkularCoreDebug).nospace() <<
"Action: Error opening '" << go->
fileName() <<
"'.";
3885 d->m_nextDocumentDestination =
QString();
3894 d->openRelativeFile(fileName);
3900 QUrl url = d->giveAbsoluteUrl(fileName);
3913 KMessageBox::information(d->m_widget,
i18n(
"The document is trying to execute an external application and, for your safety, Okular does not allow that."));
3919 KMessageBox::information(d->m_widget,
i18n(
"The document is trying to execute an external application and, for your safety, Okular does not allow that."));
3940 if ((*d->m_viewportIterator).pageNumber > 0)
3944 if ((*d->m_viewportIterator).pageNumber < (
int)d->m_pagesVector.count() - 1)
3980 int lilyRow = 0, lilyCol = 0;
3984 }
else if (extractLilyPondSourceReference(browse->
url(), &lilySource, &lilyRow, &lilyCol)) {
3988 const QUrl url = browse->
url();
3992 d->openRelativeFile(url.
fileName());
3997 if (d->m_url.isValid()) {
4000 KRun *r =
new KRun(realUrl, d->m_widget);
4014 d->m_scripter =
new Scripter(d);
4025 d->m_scripter =
new Scripter(d);
4026 d->m_scripter->execute(linkrendition->
scriptType(), linkrendition->
script());
4032 d->m_generator->opaqueAction(static_cast<const BackendOpaqueAction *>(action));
4036 if (executeNextActions) {
4038 for (
const Action *a : nextActions) {
4047 qCDebug(OkularCoreDebug) <<
"Unsupported action type" << action->
actionType() <<
"for formatting.";
4052 int foundPage = d->findFieldPageNumber(fft);
4054 if (foundPage == -1) {
4055 qCDebug(OkularCoreDebug) <<
"Could not find page for formfield!";
4061 std::shared_ptr<Event>
event = Event::createFormatEvent(fft, d->m_pagesVector[foundPage]);
4065 d->executeScriptEvent(event, linkscript);
4067 const QString formattedText =
event->value().toString();
4068 if (formattedText != unformattedText) {
4074 d->refreshPixmaps(foundPage);
4077 fft->
setText(unformattedText);
4084 d->refreshPixmaps(foundPage);
4091 qCDebug(OkularCoreDebug) <<
"Unsupported action type" << action->
actionType() <<
"for keystroke.";
4095 int foundPage = d->findFieldPageNumber(fft);
4097 if (foundPage == -1) {
4098 qCDebug(OkularCoreDebug) <<
"Could not find page for formfield!";
4102 std::shared_ptr<Event>
event = Event::createKeystrokeEvent(fft, d->m_pagesVector[foundPage]);
4106 d->executeScriptEvent(event, linkscript);
4108 returnCode =
event->returnCode();
4117 int foundPage = d->findFieldPageNumber(field);
4119 if (foundPage == -1) {
4120 qCDebug(OkularCoreDebug) <<
"Could not find page for formfield!";
4124 std::shared_ptr<Event>
event = Event::createFormFocusEvent(field, d->m_pagesVector[foundPage]);
4128 d->executeScriptEvent(event, linkscript);
4137 int foundPage = d->findFieldPageNumber(fft);
4139 if (foundPage == -1) {
4140 qCDebug(OkularCoreDebug) <<
"Could not find page for formfield!";
4144 std::shared_ptr<Event>
event = Event::createFormValidateEvent(fft, d->m_pagesVector[foundPage]);
4148 d->executeScriptEvent(event, linkscript);
4149 returnCode =
event->returnCode();
4159 qCDebug(OkularCoreDebug) << url.
url() <<
"is not a local file.";
4165 qCDebug(OkularCoreDebug) <<
"No such file:" << absFileName;
4169 bool handled =
false;
4178 editors = buildEditorsMap();
4186 p = SettingsCore::externalEditorCommand();
4212 if (!d->m_synctex_scanner)
4215 const QSizeF dpi = d->m_generator->dpi();
4217 if (synctex_edit_query(d->m_synctex_scanner, pageNr + 1, absX * 72. / dpi.
width(), absY * 72. / dpi.
height()) > 0) {
4218 synctex_node_p node;
4220 while ((node = synctex_scanner_next_result(d->m_synctex_scanner))) {
4221 int line = synctex_node_line(node);
4222 int col = synctex_node_column(node);
4227 const char *name = synctex_scanner_get_name(d->m_synctex_scanner, synctex_node_tag(node));
4237 if (d->m_generator) {
4259 return d->m_generator ? d->m_generator->print(printer) :
false;
4265 if (d->m_generator) {
4270 case Generator::TemporaryFileOpenPrintError:
4271 return i18n(
"Could not open a temporary file");
4272 case Generator::FileConversionPrintError:
4273 return i18n(
"Print conversion failed");
4274 case Generator::PrintingProcessCrashPrintError:
4275 return i18n(
"Printing process crashed");
4276 case Generator::PrintingProcessStartPrintError:
4277 return i18n(
"Printing process could not start");
4278 case Generator::PrintToFilePrintError:
4279 return i18n(
"Printing to file failed");
4280 case Generator::InvalidPrinterStatePrintError:
4281 return i18n(
"Printer was in invalid state");
4282 case Generator::UnableToFindFilePrintError:
4283 return i18n(
"Unable to find file to print");
4284 case Generator::NoFileToPrintError:
4285 return i18n(
"There was no file to print");
4286 case Generator::NoBinaryToPrintError:
4287 return i18n(
"Could not find a suitable binary for printing. Make sure CUPS lpr binary is available");
4289 return i18n(
"The page print size is invalid");
4292 case Generator::UnknownPrintError:
4301 if (d->m_generator) {
4314 BackendConfigDialog *bcd =
dynamic_cast<BackendConfigDialog *
>(dialog);
4320 d->loadServiceList(offers);
4328 for (; it != itEnd; ++it) {
4329 sortedGenerators.
insert(it.key(), it.value());
4332 bool pagesAdded =
false;
4335 for (; sit != sitEnd; ++sit) {
4341 if (sit.value().generator == d->m_generator) {
4342 const int rowCount = bcd->thePageWidget()->model()->rowCount();
4358 if (md.rawData()[QStringLiteral(
"X-KDE-okularHasInternalSettings")].toBool()) {
4367 if (!d->m_generator)
4370 auto genIt = d->m_loadedGenerators.constFind(d->m_generatorName);
4371 Q_ASSERT(genIt != d->m_loadedGenerators.constEnd());
4372 return genIt.value().metadata;
4377 return DocumentPrivate::configurableGenerators().size();
4387 result << md.mimeTypes();
4393 for (
const QString &mimeName : qAsConst(result)) {
4397 for (
const QMimeType &mimeType : uniqueMimetypes) {
4398 result.
append(mimeType.name());
4402 result << QStringLiteral(
"application/vnd.kde.okular-archive");
4406 std::sort(result.
begin(), result.
end());
4408 d->m_supportedMimeTypes = result;
4415 if (!d->m_generator)
4423 if (!d->m_generator)
4430 d->saveDocumentInfo();
4432 d->clearAndWaitForRequests();
4434 qCDebug(OkularCoreDebug) <<
"Swapping backing file to" << newFileName;
4437 if (result != Generator::SwapBackingFileError) {
4442 if (result == Generator::SwapBackingFileReloadInternalData) {
4448 if (newPagesVector.
count() != d->m_pagesVector.count())
4452 for (
int i = 0; i < d->m_undoStack->count(); ++i) {
4455 if (OkularUndoCommand *ouc = dynamic_cast<OkularUndoCommand *>(uc)) {
4456 const bool success = ouc->refreshInternalPageReferences(newPagesVector);
4458 qWarning() <<
"Document::swapBackingFile: refreshInternalPageReferences failed" << ouc;
4462 qWarning() <<
"Document::swapBackingFile: Unhandled undo command" << uc;
4467 for (
int i = 0; i < d->m_pagesVector.count(); ++i) {
4471 Page *oldPage = d->m_pagesVector[i];
4472 Page *newPage = newPagesVector[i];
4473 newPage->d->adoptGeneratedContents(oldPage->d);
4475 pagePrivatesToDelete << oldPage->d;
4476 oldPage->d = newPage->d;
4477 oldPage->d->m_page = oldPage;
4478 oldPage->d->m_doc = d;
4479 newPage->d =
nullptr;
4481 annotationsToDelete << oldPage->m_annotations;
4482 rectsToDelete << oldPage->m_rects;
4483 oldPage->m_annotations = newPage->m_annotations;
4484 oldPage->m_rects = newPage->m_rects;
4486 qDeleteAll(newPagesVector);
4490 d->m_docFileName = newFileName;
4491 d->updateMetadataXmlNameAndDocSize();
4492 d->m_bookmarkManager->setUrl(d->m_url);
4494 d->m_documentInfoAskedKeys.clear();
4496 if (d->m_synctex_scanner) {
4497 synctex_scanner_free(d->m_synctex_scanner);
4498 d->m_synctex_scanner = synctex_scanner_new_with_output_file(
QFile::encodeName(newFileName).constData(),
nullptr, 1);
4500 d->loadSyncFile(newFileName);
4506 qDeleteAll(annotationsToDelete);
4507 qDeleteAll(rectsToDelete);
4508 qDeleteAll(pagePrivatesToDelete);
4518 qCDebug(OkularCoreDebug) <<
"Swapping backing archive to" << newFileName;
4520 ArchiveData *newArchive = DocumentPrivate::unpackDocumentArchive(newFileName);
4524 const QString tempFileName = newArchive->document.fileName();
4529 delete d->m_archiveData;
4530 d->m_archiveData = newArchive;
4539 d->m_undoStack->setClean();
4541 d->m_undoStack->resetClean();
4544 bool Document::isHistoryClean()
const 4546 return d->m_undoStack->isClean();
4551 if (!d->m_generator)
4553 Q_ASSERT(!d->m_generatorName.isEmpty());
4556 Q_ASSERT(genIt != d->m_loadedGenerators.end());
4574 return d->canAddAnnotationsNatively();
4588 if (!d->m_generator || fileName.
isEmpty())
4590 Q_ASSERT(!d->m_generatorName.isEmpty());
4593 Q_ASSERT(genIt != d->m_loadedGenerators.end());
4609 if (viewDoc ==
this)
4615 d->m_views.insert(view);
4616 view->d_func()->document = d;
4625 if (!viewDoc || viewDoc !=
this)
4628 view->d_func()->document =
nullptr;
4629 d->m_views.remove(view);
4636 if (d->m_generator) {
4646 ArchiveData *DocumentPrivate::unpackDocumentArchive(
const QString &archivePath)
4650 if (!mime.
inherits(QStringLiteral(
"application/vnd.kde.okular-archive")))
4653 KZip okularArchive(archivePath);
4662 for (
const QString &entry : mainDirEntries) {
4664 qWarning() <<
"Warning: Found a directory inside" << archivePath <<
" - Okular does not create files like that so it is most probably forged.";
4670 if (!mainEntry || !mainEntry->
isFile())
4673 std::unique_ptr<QIODevice> mainEntryDevice(static_cast<const KZipFileEntry *>(mainEntry)->createDevice());
4677 mainEntryDevice.reset();
4691 documentFileName = fileEl.
text();
4693 metadataFileName = fileEl.
text();
4697 if (documentFileName.
isEmpty())
4701 if (!docEntry || !docEntry->
isFile())
4704 std::unique_ptr<ArchiveData> archiveData(
new ArchiveData());
4708 if (!archiveData->document.open())
4711 archiveData->originalFileName = documentFileName;
4714 std::unique_ptr<QIODevice> docEntryDevice(static_cast<const KZipFileEntry *>(docEntry)->createDevice());
4715 copyQIODevice(docEntryDevice.get(), &archiveData->document);
4716 archiveData->document.close();
4720 if (metadataEntry && metadataEntry->
isFile()) {
4721 std::unique_ptr<QIODevice> metadataEntryDevice(static_cast<const KZipFileEntry *>(metadataEntry)->createDevice());
4723 if (archiveData->metadataFile.open()) {
4724 copyQIODevice(metadataEntryDevice.get(), &archiveData->metadataFile);
4725 archiveData->metadataFile.close();
4729 return archiveData.release();
4734 d->m_archiveData = DocumentPrivate::unpackDocumentArchive(docFile);
4735 if (!d->m_archiveData)
4738 const QString tempFileName = d->m_archiveData->document.fileName();
4743 if (ret != OpenSuccess) {
4744 delete d->m_archiveData;
4745 d->m_archiveData =
nullptr;
4753 if (!d->m_generator)
4758 QString docFileName = d->m_archiveData ? d->m_archiveData->originalFileName : d->m_url.fileName();
4762 QString docPath = d->m_docFileName;
4767 KZip okularArchive(fileName);
4778 QDomDocument contentDoc(QStringLiteral(
"OkularArchive"));
4797 bool annotationsSavedNatively =
false;
4798 bool formsSavedNatively =
false;
4800 if (!modifiedFile.
open())
4805 modifiedFile.
close();
4809 docPath = modifiedFileName;
4810 annotationsSavedNatively = d->canAddAnnotationsNatively();
4813 qCWarning(OkularCoreDebug) <<
"saveChanges failed: " << errorText;
4814 qCDebug(OkularCoreDebug) <<
"Falling back to saving a copy of the original file";
4818 PageItems saveWhat = None;
4819 if (!annotationsSavedNatively)
4820 saveWhat |= AnnotationPageItems;
4821 if (!formsSavedNatively)
4822 saveWhat |= FormFieldPageItems;
4825 if (!d->savePageDocumentInfo(&metadataFile, saveWhat))
4829 const mode_t perm = 0100644;
4830 okularArchive.
writeFile(QStringLiteral(
"content.xml"), contentDocXml, perm, user.
loginName(), userGroup.name());
4835 if (!okularArchive.
close())
4843 if (!d->m_archiveData)
4849 return d->m_archiveData->document.copy(destFileName);
4854 double width, height;
4855 int landscape, portrait;
4862 for (uint i = 0; i <
pages(); i++) {
4863 currentPage =
page(i);
4864 width = currentPage->
width();
4865 height = currentPage->
height();
4867 qSwap(width, height);
4878 d->m_annotationEditingEnabled = enable;
4879 foreachObserver(notifySetup(d->m_pagesVector, 0));
4884 if (d->m_generator) {
4885 d->m_generator->walletDataForFile(fileName, walletName, walletFolder, walletKey);
4886 }
else if (d->m_walletGenerator) {
4887 d->m_walletGenerator->walletDataForFile(fileName, walletName, walletFolder, walletKey);
4893 return d->m_docdataMigrationNeeded;
4898 if (d->m_docdataMigrationNeeded) {
4899 d->m_docdataMigrationNeeded =
false;
4900 foreachObserver(notifySetup(d->m_pagesVector, 0));
4906 return d->m_generator ? d->m_generator->layersModel() :
nullptr;
4911 return d->m_openError;
4916 QFile f(d->m_docFileName);
4934 d->refreshPixmaps(pageNumber);
4937 void DocumentPrivate::executeScript(
const QString &
function)
4940 m_scripter =
new Scripter(
this);
4949 if (!m_generator || m_closingLoop) {
4950 m_pixmapRequestsMutex.lock();
4951 m_executingPixmapRequests.removeAll(req);
4952 m_pixmapRequestsMutex.unlock();
4955 m_closingLoop->exit();
4960 if (!m_generator->canGeneratePixmap())
4961 qCDebug(OkularCoreDebug) <<
"requestDone with generator not in READY state.";
4968 for (; aIt != aEnd; ++aIt)
4970 AllocatedPixmap *p = *aIt;
4971 m_allocatedPixmaps.erase(aIt);
4972 m_allocatedPixmapsTotalMemory -= p->memory;
4978 if (m_observers.contains(observer)) {
4980 qulonglong memoryBytes = 0;
4983 memoryBytes = tm->totalMemory();
4987 AllocatedPixmap *memoryPage =
new AllocatedPixmap(req->
observer(), req->
pageNumber(), memoryBytes);
4988 m_allocatedPixmaps.append(memoryPage);
4989 m_allocatedPixmapsTotalMemory += memoryBytes;
4996 qCWarning(OkularCoreDebug) <<
"Receiving a done request for the defunct observer" << observer;
5001 m_pixmapRequestsMutex.lock();
5002 m_executingPixmapRequests.removeAll(req);
5003 m_pixmapRequestsMutex.unlock();
5007 m_pixmapRequestsMutex.lock();
5008 bool hasPixmaps = !m_pixmapRequestsStack.isEmpty();
5009 m_pixmapRequestsMutex.unlock();
5011 sendGeneratorPixmapRequest();
5014 void DocumentPrivate::setPageBoundingBox(
int page,
const NormalizedRect &boundingBox)
5017 if (!m_generator || !kp)
5033 void DocumentPrivate::calculateMaxTextPages()
5035 int multipliers = qMax(1, qRound(getTotalMemory() / 536870912.0));
5036 switch (SettingsCore::memoryLevel()) {
5037 case SettingsCore::EnumMemoryLevel::Low:
5038 m_maxAllocatedTextPages = multipliers * 2;
5041 case SettingsCore::EnumMemoryLevel::Normal:
5042 m_maxAllocatedTextPages = multipliers * 50;
5045 case SettingsCore::EnumMemoryLevel::Aggressive:
5046 m_maxAllocatedTextPages = multipliers * 250;
5049 case SettingsCore::EnumMemoryLevel::Greedy:
5050 m_maxAllocatedTextPages = multipliers * 1250;
5055 void DocumentPrivate::textGenerationDone(
Page *page)
5057 if (!m_pageController)
5061 if (m_allocatedTextPagesFifo.size() == m_maxAllocatedTextPages) {
5062 int pageToKick = m_allocatedTextPagesFifo.takeFirst();
5063 if (pageToKick != page->
number())
5065 m_pagesVector.at(pageToKick)->setTextPage(
nullptr);
5070 m_allocatedTextPagesFifo.append(page->
number());
5075 d->setRotationInternal(r,
true);
5078 void DocumentPrivate::setRotationInternal(
int r,
bool notify)