Speedup Document Loading (#2002)

* Reduce complexity of Document::findPdfPage

Adds an index from pdf to document page number.
This reduces the complexity of Docmunent::findPdfPage from O(n) to
constant for the document size.
To keep actions snappy, interactie actions such as altering the document
simply invalidates the index.
This change dramatically improves the load times of larger pdf documents
such as books.

* Add bulk loading to xoj LoadHandler.

Accumulates the pages while parsing and adds them in a single bulk call
when finished.

* Fix formatting
presentation
Frédéric Simonis 6 years ago committed by GitHub
parent dbb3f6a5c8
commit 0c30049e10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/control/xojfile/LoadHandler.cpp
  2. 1
      src/control/xojfile/LoadHandler.h
  3. 45
      src/model/Document.cpp
  4. 28
      src/model/Document.h

@ -225,6 +225,9 @@ auto LoadHandler::parseXml() -> bool {
g_markup_parse_context_free(context);
// Add all parsed pages to the document
this->doc.addPages(pages.begin(), pages.end());
if (this->pos != PASER_POS_FINISHED && this->lastError.empty()) {
lastError = _("Document is not complete (maybe the end is cut off?)");
return false;
@ -288,7 +291,7 @@ void LoadHandler::parseContents() {
this->page = std::make_unique<XojPage>(width, height);
this->doc.addPage(this->page);
pages.push_back(this->page);
} else if (strcmp(elementName, "audio") == 0) {
this->parseAudio();
} else if (strcmp(elementName, "title") == 0) {

@ -125,6 +125,7 @@ private:
vector<double> pressureBuffer;
std::vector<PageRef> pages;
PageRef page;
Layer* layer;
Stroke* stroke;

@ -1,5 +1,6 @@
#include "Document.h"
#include <algorithm>
#include <utility>
#include <config.h>
@ -85,6 +86,7 @@ void Document::clearDocument(bool destroy) {
}
this->pages.clear();
this->pageIndex.reset();
freeTreeContentModel();
this->filename = "";
@ -169,15 +171,15 @@ auto Document::isPdfDocumentLoaded() -> bool { return pdfDocument.isLoaded(); }
auto Document::isAttachPdf() const -> bool { return this->attachPdf; }
auto Document::findPdfPage(size_t pdfPage) -> size_t {
for (size_t i = 0; i < getPageCount(); i++) {
PageRef p = this->pages[i];
if (p->getBackgroundType().isPdfPage()) {
if (p->getPdfPageNr() == pdfPage) {
return i;
}
}
// Create a page index if not already indexed.
if (!this->pageIndex)
indexPdfPages();
auto pos = this->pageIndex->find(pdfPage);
if (pos == this->pageIndex->end()) {
return -1;
} else {
return pos->second;
}
return -1;
}
void Document::buildTreeContentsModel(GtkTreeIter* parent, XojPdfBookmarkIterator* iter) {
@ -215,6 +217,18 @@ void Document::buildTreeContentsModel(GtkTreeIter* parent, XojPdfBookmarkIterato
} while (iter->next());
}
void Document::indexPdfPages() {
auto index = std::make_unique<PageIndex>();
for (size_t i = 0; i < this->pages.size(); ++i) {
const auto& p = this->pages[i];
if (p->getBackgroundType().isPdfPage()) {
index->emplace(p->getPdfPageNr(), i);
}
}
this->pageIndex.swap(index);
}
void Document::buildContentsModel() {
freeTreeContentModel();
@ -298,10 +312,11 @@ auto Document::readPdf(const Path& filename, bool initPages, bool attachToDocume
XojPdfPageSPtr page = pdfDocument.getPage(i);
auto p = std::make_shared<XojPage>(page->getWidth(), page->getHeight());
p->setBackgroundPdfPageNr(i);
addPage(std::move(p));
this->pages.emplace_back(std::move(p));
}
}
indexPdfPages();
buildContentsModel();
updateIndexPageNumbers();
@ -327,18 +342,24 @@ void Document::deletePage(size_t pNr) {
auto it = this->pages.begin() + pNr;
this->pages.erase(it);
// Reset the page index
this->pageIndex.reset();
updateIndexPageNumbers();
}
void Document::insertPage(const PageRef& p, size_t position) {
this->pages.insert(this->pages.begin() + position, p);
// Reset the page index
this->pageIndex.reset();
updateIndexPageNumbers();
}
void Document::addPage(const PageRef& p) {
this->pages.push_back(p);
// Reset the page index
this->pageIndex.reset();
updateIndexPageNumbers();
}
@ -378,11 +399,9 @@ auto Document::operator=(const Document& doc) -> Document& {
this->createBackupOnSave = doc.createBackupOnSave;
this->pdfFilename = doc.pdfFilename;
this->filename = doc.filename;
this->pages = doc.pages;
for (const PageRef& p: doc.pages) {
addPage(p);
}
indexPdfPages();
buildContentsModel();
updateIndexPageNumbers();

@ -13,7 +13,9 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "pdf/base/XojPdfBookmarkIterator.h"
@ -44,6 +46,8 @@ public:
void insertPage(const PageRef& p, size_t position);
void addPage(const PageRef& p);
template <class InputIter>
void addPages(InputIter first, InputIter last);
PageRef getPage(size_t page);
void deletePage(size_t pNr);
@ -117,6 +121,23 @@ private:
*/
vector<PageRef> pages;
/**
* Index from pdf page number to document page number
*/
using PageIndex = std::unordered_map<size_t, size_t>;
/**
* The cached page index
*/
std::unique_ptr<PageIndex> pageIndex;
/**
* Creates an index from pdf page number to document page number
*
* Clears the index first in case it is already exists.
*/
void indexPdfPages();
/**
* The bookmark contents model
*/
@ -137,3 +158,10 @@ private:
*/
GMutex documentLock{};
};
template <class InputIter>
void Document::addPages(InputIter first, InputIter last) {
this->pages.insert(this->pages.end(), first, last);
this->pageIndex.reset();
updateIndexPageNumbers();
}

Loading…
Cancel
Save