// // Class: documentPageCache // // Cache that holds a number of pages in a document. // Part of KDVI- A previewer for TeX DVI files. // // (C) 2004 Stefan Kebekus. Distributed under the GPL. #include #include #include #include "documentPageCache.h" #include "documentRenderer.h" #include "kvsprefs.h" #include "renderedDocumentPagePixmap.h" //#define documentPageCache_DEBUG DocumentPageCache::DocumentPageCache() : maxMemory(2*16777216), LRUCache(maxMemory, 200) { LRUCache.setAutoDelete(true); resolutionInDPI = 0.0; Length w,h; w.setLength_in_mm(200); h.setLength_in_mm(300); userPreferredSize.setPageSize(w,h); useDocumentSpecifiedSize = true; } DocumentPageCache::~DocumentPageCache() { } void DocumentPageCache::setRenderer(DocumentRenderer *_renderer) { clear(); renderer = _renderer; } SimplePageSize DocumentPageCache::sizeOfPage(const PageNumber& page) const { // Paranoid safety checks if (!page.isValid()) { kdError(1223) << "DocumentPageCache::sizeOfPage( " << page << ") called with invalid page number." << endl; return SimplePageSize(); } if (renderer.isNull()) { kdError(1223) << "DocumentPageCache::sizeOfPage( " << page << ") called when no renderer was set." << endl; return SimplePageSize(); } SimplePageSize s = renderer->sizeOfPage(page); if (!useDocumentSpecifiedSize) s = userPreferredSize; if (!s.isValid()) { // If the size is invalid use the size of the first Page in the document // as an estimate. s = renderer->sizeOfPage(1); if (!s.isValid()) s = userPreferredSize; } return s; } void DocumentPageCache::setResolution(double res) { resolutionInDPI = res; } TQSize DocumentPageCache::sizeOfPageInPixel(const PageNumber& pg) const { // Paranoid safety checks if (renderer.isNull()) { kdError(1223) << "DocumentPageCache::sizeOfPageInPixel( " << pg << " ) called but no renderer was set" << endl; return TQSize(); } if (!pg.isValid()) { kdError(1223) << "DocumentPageCache::sizeOfPageInPixel( " << pg << " ) called with invalid argument" << endl; return TQSize(); } SimplePageSize ps = sizeOfPage(pg); if (ps.isValid()) return ps.sizeInPixel(resolutionInDPI); return userPreferredSize.sizeInPixel(resolutionInDPI); } bool DocumentPageCache::isPageCached(const PageNumber& pageNumber, const TQSize& size) { // Paranoid checks if (renderer.isNull()) { kdError(1223) << "DocumentPageCache::isPageCached(..) called but no renderer was set" << endl; return false; } if (!pageNumber.isValid()) { kdError(1223) << "DocumentPageCache::isPageCached( " << pageNumber << " ) called, with invalid argument." << endl; return false; } if (renderer->totalPages() < pageNumber) { kdError(1223) << "DocumentPageCache::isPageCached( " << pageNumber << " ) called but document contains only " << renderer->totalPages() << " pages." << endl; return false; } TQString key = createKey(pageNumber, size); // Check if the page that we are looking for is in the cache. // We are not accessing the page, so we don't want it to be moved into the front. RenderedDocumentPagePixmap* page = LRUCache.find(key, false); if (page) return true; else return false; } TQString DocumentPageCache::createKey(const PageNumber& pageNumber, const TQSize& size) { TQString key; key = TQString::number(pageNumber) + ":" + TQString::number(size.width()) + ":" + TQString::number(size.height()); return key; } TQString DocumentPageCache::createKey(const PageNumber& pageNumber) { TQSize pageSize = sizeOfPageInPixel(pageNumber); TQString key; key = TQString::number(pageNumber) + ":" + TQString::number(pageSize.width()) + ":" + TQString::number(pageSize.height()); return key; } bool DocumentPageCache::isPageCached(const PageNumber& pageNumber) { // Paranoid checks if (renderer.isNull()) { kdError(1223) << "DocumentPageCache::isPageCached(..) called but no renderer was set" << endl; return false; } if (!pageNumber.isValid()) { kdError(1223) << "DocumentPageCache::isPageCached( " << pageNumber << " ) called, with invalid argument." << endl; return false; } if (renderer->totalPages() < pageNumber) { kdError(1223) << "DocumentPageCache::isPageCached( " << pageNumber << " ) called but document contains only " << renderer->totalPages() << " pages." << endl; return false; } return isPageCached(pageNumber, sizeOfPageInPixel(pageNumber)); } RenderedDocumentPagePixmap* DocumentPageCache::getPage(const PageNumber& pageNr) { #ifdef DocumentPageCache_DEBUG kdDebug(1223) << "DocumentPageCache::getPage( pageNr=" << pageNr << " )" << endl; #endif // Paranoid checks if (renderer.isNull()) { kdError(1223) << "DocumentPageCache::getPage(..) called but no renderer was set" << endl; return 0; } if (!pageNr.isValid()) { kdError(1223) << "DocumentPageCache::getPage( " << pageNr << " ) called, with invalid argument." << endl; return 0; } if (renderer->totalPages() < pageNr) { kdError(1223) << "DocumentPageCache::getPage( " << pageNr << " ) called but document contains only " << renderer->totalPages() << " pages." << endl; return 0; } // First check if the page that we are looking for is in the cache RenderedDocumentPagePixmap* page; page = LRUCache.find(createKey(pageNr)); if (page) return page; // The page was not found in the cache, so we have to make a new // page and add this to the cache. page = createDocumentPagePixmap(); // If that failed, issue an error message and quit. if (page == 0) { kdError(1223) << "DocumentPageCache::getPage(..) cannot allocate DocumentPage structure" << endl; return 0; } // Now 'page' contains a point to a page structure that we can // use. Add the page to the cache, and apply the renderer to the page. page->setPageNumber(pageNr); if (!renderer.isNull()) { if (resolutionInDPI > 0.0) { page->resize(sizeOfPageInPixel(pageNr)); TQApplication::setOverrideCursor( waitCursor ); renderer->drawPage(resolutionInDPI, page); TQApplication::restoreOverrideCursor(); // We always set the cache capacity to be at least n times the cost of the page we want to insert. // Where n is the number of pages that can be visible at the same time at very high zoomlevels. // n depends on the layout mode. // If these pages are not all in the cache, scrolling the view becomes very slow, because for each // paint event the pages need to be rerendered. // We set n for each viewmode differently so that the user is able to reduce memory consuption by // switching to a simpler viewmode like Single Page. int n = 4; switch (KVSPrefs::viewMode()) { case KVSPrefs::EnumViewMode::SinglePage: n = 1; break; case KVSPrefs::EnumViewMode::Continuous: n = 2; break; default: n = 4; } LRUCache.setMaxCost(TQMAX(page->memory() * n, maxMemory)); if (!LRUCache.insert(createKey(pageNr), page, page->memory())) { kdError() << "DocumentPageCache::getPage(): inserting pagestructure into the cache failed.\n This should never happen. If you see this message, something is very wrong." << endl; } } else kdError(1223) << "DocumentPageCache::getPage() called, but no resolution or negative resolution was set" << endl; } return page; } RenderedDocumentPagePixmap* DocumentPageCache::createDocumentPagePixmap() const { return new RenderedDocumentPagePixmap(); } void DocumentPageCache::clear() { LRUCache.clear(); } void DocumentPageCache::setUserPreferredSize(const SimplePageSize& s) { bool sizeChanged = !userPreferredSize.isNearlyEqual(s); userPreferredSize = s; if (sizeChanged) emit(paperSizeChanged()); } void DocumentPageCache::setUseDocumentSpecifiedSize(bool b) { bool valChanged = (useDocumentSpecifiedSize == b); useDocumentSpecifiedSize = b; if (valChanged) emit(paperSizeChanged()); } TQPixmap DocumentPageCache::createThumbnail(const PageNumber& pageNr, int width) { // Paranoid checks if (renderer.isNull()) { kdError(1223) << "DocumentPageCache::createThumbnail(..) called but no renderer was set" << endl; thumbnailPage.resize(0,0); return thumbnailPage; } if (renderer->totalPages() < pageNr) { kdError(1223) << "DocumentPageCache::createThumbnail( " << pageNr << ", width ) called but document contains only " << renderer->totalPages() << " pages." << endl; thumbnailPage.resize(0,0); return thumbnailPage; } if (!pageNr.isValid()) { kdError(1223) << "DocumentPageCache::createThumbnail(..) called for page with invalid page specification" << endl; thumbnailPage.resize(0,0); return thumbnailPage; } if (!sizeOfPage().isValid()) { kdError(1223) << "DocumentPageCache::createThumbnail(..) called for page with invalid size" << endl; thumbnailPage.resize(0,0); return thumbnailPage; } thumbnailPage.setPageNumber(pageNr); thumbnailPage.resize(width, (int)(width/sizeOfPage(pageNr).aspectRatio() + 0.5 ) ); renderer->drawThumbnail((double)(width)/sizeOfPage(pageNr).width().getLength_in_inch(), &thumbnailPage); if (KVSPrefs::changeColors() && KVSPrefs::renderMode() != KVSPrefs::EnumRenderMode::Paper) { return thumbnailPage.accessiblePixmap(); } else { return thumbnailPage; } } void DocumentPageCache::deselectText() { userSelection.clear(); emit textSelected(false); } void DocumentPageCache::selectText(const TextSelection& selection) { userSelection = selection; emit textSelected(!userSelection.isEmpty()); } #include "documentPageCache.moc"