mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-28 17:37:46 +01:00
Asynchronous calculation of document text layout
This commit is contained in:
parent
c832c4ecef
commit
e9481fc446
@ -18,6 +18,7 @@
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfcms.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfprogress.h"
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
@ -74,7 +75,6 @@ void PDFAsynchronousPageCompiler::stop()
|
||||
}
|
||||
m_tasks.clear();
|
||||
m_cache.clear();
|
||||
m_textLayouts.dirty();
|
||||
|
||||
m_state = State::Inactive;
|
||||
break;
|
||||
@ -121,7 +121,7 @@ const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getCompiledPage(PDFIntege
|
||||
return compiledPage;
|
||||
};
|
||||
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(false);
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(this, false);
|
||||
CompileTask& task = m_tasks[pageIndex];
|
||||
task.taskFuture = QtConcurrent::run(compilePage);
|
||||
task.taskWatcher = new QFutureWatcher<PDFPrecompiledPage>(this);
|
||||
@ -132,17 +132,6 @@ const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getCompiledPage(PDFIntege
|
||||
return page;
|
||||
}
|
||||
|
||||
PDFTextLayout PDFAsynchronousPageCompiler::getTextLayout(PDFInteger pageIndex)
|
||||
{
|
||||
if (m_state != State::Active || !m_proxy->getDocument())
|
||||
{
|
||||
// Engine is not active, always return empty layout
|
||||
return PDFTextLayout();
|
||||
}
|
||||
|
||||
return m_textLayouts.get(this, &PDFAsynchronousPageCompiler::getTextLayoutsImpl).getTextLayout(pageIndex);
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::onPageCompiled()
|
||||
{
|
||||
std::vector<PDFInteger> compiledPages;
|
||||
@ -182,7 +171,7 @@ void PDFAsynchronousPageCompiler::onPageCompiled()
|
||||
}
|
||||
|
||||
// We allow font cache shrinking, when we aren't doing something in parallel.
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(m_tasks.empty());
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(this, m_tasks.empty());
|
||||
|
||||
if (!compiledPages.empty())
|
||||
{
|
||||
@ -268,38 +257,152 @@ void PDFTextLayoutGenerator::performOutputCharacter(const PDFTextCharacterInfo&
|
||||
m_textLayout.addCharacter(info);
|
||||
}
|
||||
|
||||
PDFTextLayoutStorage PDFAsynchronousPageCompiler::getTextLayoutsImpl()
|
||||
PDFAsynchronousTextLayoutCompiler::PDFAsynchronousTextLayoutCompiler(PDFDrawWidgetProxy* proxy) :
|
||||
BaseClass(proxy),
|
||||
m_proxy(proxy)
|
||||
{
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(false);
|
||||
const PDFCatalog* catalog = m_proxy->getDocument()->getCatalog();
|
||||
connect(&m_textLayoutCompileFutureWatcher, &QFutureWatcher<PDFTextLayoutStorage>::finished, this, &PDFAsynchronousTextLayoutCompiler::onTextLayoutCreated);
|
||||
}
|
||||
|
||||
PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS();
|
||||
PDFTextLayoutStorage result(catalog->getPageCount());
|
||||
QMutex mutex;
|
||||
auto generateTextLayout = [this, &result, &mutex, &cms, catalog](PDFInteger pageIndex)
|
||||
void PDFAsynchronousTextLayoutCompiler::start()
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
if (!catalog->getPage(pageIndex))
|
||||
case State::Inactive:
|
||||
{
|
||||
// Invalid page index
|
||||
result.setTextLayout(pageIndex, PDFTextLayout(), &mutex);
|
||||
return;
|
||||
m_state = State::Active;
|
||||
break;
|
||||
}
|
||||
|
||||
const PDFPage* page = catalog->getPage(pageIndex);
|
||||
Q_ASSERT(page);
|
||||
case State::Active:
|
||||
break; // We have nothing to do...
|
||||
|
||||
PDFTextLayoutGenerator generator(m_proxy->getFeatures(), page, m_proxy->getDocument(), m_proxy->getFontCache(), cms.data(), m_proxy->getOptionalContentActivity(), QMatrix(), m_proxy->getMeshQualitySettings());
|
||||
generator.processContents();
|
||||
result.setTextLayout(pageIndex, generator.createTextLayout(), &mutex);
|
||||
case State::Stopping:
|
||||
{
|
||||
// We shouldn't call this function while stopping!
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::stop()
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case State::Inactive:
|
||||
break; // We have nothing to do...
|
||||
|
||||
case State::Active:
|
||||
{
|
||||
// Stop the engine
|
||||
m_state = State::Stopping;
|
||||
m_textLayoutCompileFutureWatcher.waitForFinished();
|
||||
m_textLayouts = std::nullopt;
|
||||
m_state = State::Inactive;
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Stopping:
|
||||
{
|
||||
// We shouldn't call this function while stopping!
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::reset()
|
||||
{
|
||||
stop();
|
||||
start();
|
||||
}
|
||||
|
||||
PDFTextLayout PDFAsynchronousTextLayoutCompiler::getTextLayout(PDFInteger pageIndex)
|
||||
{
|
||||
if (m_state != State::Active || !m_proxy->getDocument())
|
||||
{
|
||||
// Engine is not active, always return empty layout
|
||||
return PDFTextLayout();
|
||||
}
|
||||
|
||||
if (m_textLayouts)
|
||||
{
|
||||
return m_textLayouts->getTextLayout(pageIndex);
|
||||
}
|
||||
|
||||
return PDFTextLayout();
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::makeTextLayout()
|
||||
{
|
||||
if (m_state != State::Active || !m_proxy->getDocument())
|
||||
{
|
||||
// Engine is not active, do not calculate layout
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_textLayouts.has_value())
|
||||
{
|
||||
// Value is computed already
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_textLayoutCompileFuture.isRunning())
|
||||
{
|
||||
// Text layout is already being processed
|
||||
return;
|
||||
}
|
||||
|
||||
ProgressStartupInfo info;
|
||||
info.showDialog = true;
|
||||
info.text = tr("Generating text layouts for pages...");
|
||||
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(this, false);
|
||||
const PDFCatalog* catalog = m_proxy->getDocument()->getCatalog();
|
||||
m_proxy->getProgress()->start(catalog->getPageCount(), qMove(info));
|
||||
|
||||
PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS();
|
||||
|
||||
auto createTextLayout = [this, cms, catalog]() -> PDFTextLayoutStorage
|
||||
{
|
||||
PDFTextLayoutStorage result(catalog->getPageCount());
|
||||
QMutex mutex;
|
||||
auto generateTextLayout = [this, &result, &mutex, cms, catalog](PDFInteger pageIndex)
|
||||
{
|
||||
if (!catalog->getPage(pageIndex))
|
||||
{
|
||||
// Invalid page index
|
||||
result.setTextLayout(pageIndex, PDFTextLayout(), &mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
const PDFPage* page = catalog->getPage(pageIndex);
|
||||
Q_ASSERT(page);
|
||||
|
||||
PDFTextLayoutGenerator generator(m_proxy->getFeatures(), page, m_proxy->getDocument(), m_proxy->getFontCache(), cms.data(), m_proxy->getOptionalContentActivity(), QMatrix(), m_proxy->getMeshQualitySettings());
|
||||
generator.processContents();
|
||||
result.setTextLayout(pageIndex, generator.createTextLayout(), &mutex);
|
||||
m_proxy->getProgress()->step();
|
||||
};
|
||||
|
||||
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
|
||||
std::for_each(std::execution::parallel_policy(), pageRange.begin(), pageRange.end(), generateTextLayout);
|
||||
return result;
|
||||
};
|
||||
|
||||
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
|
||||
std::for_each(std::execution::parallel_policy(), pageRange.begin(), pageRange.end(), generateTextLayout);
|
||||
Q_ASSERT(!m_textLayoutCompileFuture.isRunning());
|
||||
m_textLayoutCompileFuture = QtConcurrent::run(createTextLayout);
|
||||
m_textLayoutCompileFutureWatcher.setFuture(m_textLayoutCompileFuture);
|
||||
}
|
||||
|
||||
// We allow font cache shrinking, when we aren't doing something in parallel.
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(m_tasks.empty());
|
||||
void PDFAsynchronousTextLayoutCompiler::onTextLayoutCreated()
|
||||
{
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(this, true);
|
||||
m_proxy->getProgress()->finish();
|
||||
|
||||
return result;
|
||||
m_textLayouts = m_textLayoutCompileFuture.result();
|
||||
emit textLayoutChanged();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -73,11 +73,6 @@ public:
|
||||
/// \param compile Compile the page, if it is not found in the cache
|
||||
const PDFPrecompiledPage* getCompiledPage(PDFInteger pageIndex, bool compile);
|
||||
|
||||
/// Returns text layout of the page. If page index is invalid,
|
||||
/// then empty text layout is returned.
|
||||
/// \param pageIndex Page index
|
||||
PDFTextLayout getTextLayout(PDFInteger pageIndex);
|
||||
|
||||
signals:
|
||||
void pageImageChanged(bool all, const std::vector<PDFInteger>& pages);
|
||||
void renderingError(PDFInteger pageIndex, const QList<PDFRenderError>& errors);
|
||||
@ -85,9 +80,6 @@ signals:
|
||||
private:
|
||||
void onPageCompiled();
|
||||
|
||||
/// Returns text layouts for all pages
|
||||
PDFTextLayoutStorage getTextLayoutsImpl();
|
||||
|
||||
struct CompileTask
|
||||
{
|
||||
QFuture<PDFPrecompiledPage> taskFuture;
|
||||
@ -98,7 +90,57 @@ private:
|
||||
State m_state = State::Inactive;
|
||||
QCache<PDFInteger, PDFPrecompiledPage> m_cache;
|
||||
std::map<PDFInteger, CompileTask> m_tasks;
|
||||
PDFCachedItem<PDFTextLayoutStorage> m_textLayouts;
|
||||
};
|
||||
|
||||
class PDFAsynchronousTextLayoutCompiler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = QObject;
|
||||
|
||||
public:
|
||||
explicit PDFAsynchronousTextLayoutCompiler(PDFDrawWidgetProxy* proxy);
|
||||
|
||||
/// Starts the engine. Call this function only if the engine
|
||||
/// is stopped.
|
||||
void start();
|
||||
|
||||
/// Stops the engine and all underlying asynchronous tasks. Also
|
||||
/// clears the cache. Call this function only if engine is active.
|
||||
void stop();
|
||||
|
||||
/// Resets the engine - calls stop and then calls start.
|
||||
void reset();
|
||||
|
||||
enum class State
|
||||
{
|
||||
Inactive,
|
||||
Active,
|
||||
Stopping
|
||||
};
|
||||
|
||||
/// Returns text layout of the page. If page index is invalid,
|
||||
/// then empty text layout is returned.
|
||||
/// \param pageIndex Page index
|
||||
PDFTextLayout getTextLayout(PDFInteger pageIndex);
|
||||
|
||||
/// Create text layout for the document. Function is asynchronous,
|
||||
/// it returns immediately. After text layout is created, signal
|
||||
/// \p textLayoutChanged is emitted.
|
||||
void makeTextLayout();
|
||||
|
||||
signals:
|
||||
void textLayoutChanged();
|
||||
|
||||
private:
|
||||
void onTextLayoutCreated();
|
||||
|
||||
PDFDrawWidgetProxy* m_proxy;
|
||||
State m_state = State::Inactive;
|
||||
std::optional<PDFTextLayoutStorage> m_textLayouts;
|
||||
QFuture<PDFTextLayoutStorage> m_textLayoutCompileFuture;
|
||||
QFutureWatcher<PDFTextLayoutStorage> m_textLayoutCompileFutureWatcher;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -269,7 +269,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
|
||||
};
|
||||
|
||||
// Now, we are ready to scan all objects
|
||||
progressStart(occupiedEntries.size());
|
||||
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Reading contents of document..."));
|
||||
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
|
||||
progressFinish();
|
||||
|
||||
@ -369,7 +369,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
|
||||
objects[entry.reference.objectNumber].object = securityHandler->decryptObject(objects[entry.reference.objectNumber].object, entry.reference);
|
||||
};
|
||||
|
||||
progressStart(occupiedEntries.size());
|
||||
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Decrypting encrypted contents of document..."));
|
||||
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), decryptEntry);
|
||||
progressFinish();
|
||||
}
|
||||
@ -529,11 +529,15 @@ int PDFDocumentReader::findFromEnd(const char* what, const QByteArray& byteArray
|
||||
return FIND_NOT_FOUND_RESULT;
|
||||
}
|
||||
|
||||
void PDFDocumentReader::progressStart(size_t stepCount)
|
||||
void PDFDocumentReader::progressStart(size_t stepCount, QString text)
|
||||
{
|
||||
if (m_progress)
|
||||
{
|
||||
m_progress->start(stepCount);
|
||||
ProgressStartupInfo info;
|
||||
info.showDialog = !text.isEmpty();
|
||||
info.text = qMove(text);
|
||||
|
||||
m_progress->start(stepCount, qMove(info));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ private:
|
||||
/// \returns Position of string, or FIND_NOT_FOUND_RESULT
|
||||
int findFromEnd(const char* what, const QByteArray& byteArray, int limit);
|
||||
|
||||
void progressStart(size_t stepCount);
|
||||
void progressStart(size_t stepCount, QString text);
|
||||
void progressStep();
|
||||
void progressFinish();
|
||||
|
||||
|
@ -59,6 +59,11 @@ void PDFDrawSpaceController::setDocument(const PDFDocument* document, const PDFO
|
||||
}
|
||||
}
|
||||
|
||||
void PDFDrawSpaceController::onOptionalContentGroupStateChanged()
|
||||
{
|
||||
emit pageImageChanged(true, { });
|
||||
}
|
||||
|
||||
void PDFDrawSpaceController::setPageLayout(PageLayout pageLayout)
|
||||
{
|
||||
if (m_pageLayoutMode != pageLayout)
|
||||
@ -138,11 +143,6 @@ QSizeF PDFDrawSpaceController::getReferenceBoundingBox() const
|
||||
return rect.size();
|
||||
}
|
||||
|
||||
void PDFDrawSpaceController::onOptionalContentGroupStateChanged()
|
||||
{
|
||||
emit pageImageChanged(true, { });
|
||||
}
|
||||
|
||||
void PDFDrawSpaceController::recalculate()
|
||||
{
|
||||
if (!m_document)
|
||||
@ -400,7 +400,9 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
|
||||
m_verticalScrollbar(nullptr),
|
||||
m_features(PDFRenderer::getDefaultFeatures()),
|
||||
m_compiler(new PDFAsynchronousPageCompiler(this)),
|
||||
m_rasterizer(new PDFRasterizer(this))
|
||||
m_textLayoutCompiler(new PDFAsynchronousTextLayoutCompiler(this)),
|
||||
m_rasterizer(new PDFRasterizer(this)),
|
||||
m_progress(nullptr)
|
||||
{
|
||||
m_controller = new PDFDrawSpaceController(this);
|
||||
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
|
||||
@ -408,6 +410,7 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
|
||||
connect(m_controller, &PDFDrawSpaceController::pageImageChanged, this, &PDFDrawWidgetProxy::pageImageChanged);
|
||||
connect(m_compiler, &PDFAsynchronousPageCompiler::renderingError, this, &PDFDrawWidgetProxy::renderingError);
|
||||
connect(m_compiler, &PDFAsynchronousPageCompiler::pageImageChanged, this, &PDFDrawWidgetProxy::pageImageChanged);
|
||||
connect(m_textLayoutCompiler, &PDFAsynchronousTextLayoutCompiler::textLayoutChanged, this, &PDFDrawWidgetProxy::onTextLayoutChanged);
|
||||
}
|
||||
|
||||
PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
|
||||
@ -418,8 +421,10 @@ PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
|
||||
void PDFDrawWidgetProxy::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity)
|
||||
{
|
||||
m_compiler->stop();
|
||||
m_textLayoutCompiler->stop();
|
||||
m_controller->setDocument(document, optionalContentActivity);
|
||||
m_compiler->start();
|
||||
m_textLayoutCompiler->start();
|
||||
}
|
||||
|
||||
void PDFDrawWidgetProxy::init(PDFWidget* widget)
|
||||
@ -626,7 +631,8 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
|
||||
// Draw text blocks/text lines, if it is enabled
|
||||
if (m_features.testFlag(PDFRenderer::DebugTextBlocks))
|
||||
{
|
||||
PDFTextLayout layout = m_compiler->getTextLayout(item.pageIndex);
|
||||
m_textLayoutCompiler->makeTextLayout();
|
||||
PDFTextLayout layout = m_textLayoutCompiler->getTextLayout(item.pageIndex);
|
||||
const PDFTextBlocks& textBlocks = layout.getTextBlocks();
|
||||
|
||||
painter->save();
|
||||
@ -648,7 +654,8 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
|
||||
}
|
||||
if (m_features.testFlag(PDFRenderer::DebugTextLines))
|
||||
{
|
||||
PDFTextLayout layout = m_compiler->getTextLayout(item.pageIndex);
|
||||
m_textLayoutCompiler->makeTextLayout();
|
||||
PDFTextLayout layout = m_textLayoutCompiler->getTextLayout(item.pageIndex);
|
||||
const PDFTextBlocks& textBlocks = layout.getTextBlocks();
|
||||
|
||||
painter->save();
|
||||
@ -989,6 +996,11 @@ QRectF PDFDrawWidgetProxy::fromDeviceSpace(const QRectF& rect) const
|
||||
rect.height() * m_deviceSpaceUnitToPixel);
|
||||
}
|
||||
|
||||
void PDFDrawWidgetProxy::onTextLayoutChanged()
|
||||
{
|
||||
emit repaintNeeded();
|
||||
}
|
||||
|
||||
bool PDFDrawWidgetProxy::isBlockMode() const
|
||||
{
|
||||
switch (m_controller->getPageLayout())
|
||||
@ -1137,8 +1149,10 @@ void PDFDrawWidgetProxy::setFeatures(PDFRenderer::Features features)
|
||||
if (m_features != features)
|
||||
{
|
||||
m_compiler->stop();
|
||||
m_textLayoutCompiler->stop();
|
||||
m_features = features;
|
||||
m_compiler->start();
|
||||
m_textLayoutCompiler->start();
|
||||
emit pageImageChanged(true, { });
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,12 @@ class QScrollBar;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFProgress;
|
||||
class PDFWidget;
|
||||
class IDrawWidget;
|
||||
class PDFCMSManager;
|
||||
class PDFAsynchronousPageCompiler;
|
||||
class PDFAsynchronousTextLayoutCompiler;
|
||||
|
||||
/// This class controls draw space - page layout. Pages are divided into blocks
|
||||
/// each block can contain one or multiple pages. Units are in milimeters.
|
||||
@ -274,6 +276,8 @@ public:
|
||||
const PDFMeshQualitySettings& getMeshQualitySettings() const { return m_meshQualitySettings; }
|
||||
PDFAsynchronousPageCompiler* getCompiler() const { return m_compiler; }
|
||||
const PDFCMSManager* getCMSManager() const;
|
||||
PDFProgress* getProgress() const { return m_progress; }
|
||||
void setProgress(PDFProgress* progress) { m_progress = progress; }
|
||||
|
||||
void setFeatures(PDFRenderer::Features features);
|
||||
void setPreferredMeshResolutionRatio(PDFReal ratio);
|
||||
@ -326,6 +330,7 @@ private:
|
||||
/// Converts rectangle from device space to the pixel space
|
||||
QRectF fromDeviceSpace(const QRectF& rect) const;
|
||||
|
||||
void onTextLayoutChanged();
|
||||
void onColorManagementSystemChanged();
|
||||
void onHorizontalScrollbarValueChanged(int value);
|
||||
void onVerticalScrollbarValueChanged(int value);
|
||||
@ -408,8 +413,14 @@ private:
|
||||
/// Page compiler
|
||||
PDFAsynchronousPageCompiler* m_compiler;
|
||||
|
||||
/// Text layout compiler
|
||||
PDFAsynchronousTextLayoutCompiler* m_textLayoutCompiler;
|
||||
|
||||
/// Page image rasterizer for thumbnails
|
||||
PDFRasterizer* m_rasterizer;
|
||||
|
||||
/// Progress
|
||||
PDFProgress* m_progress;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -1610,7 +1610,7 @@ PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject) const
|
||||
// We must create the font
|
||||
PDFFontPointer font = PDFFont::createFont(fontObject, m_document);
|
||||
|
||||
if (m_fontCacheShrinkEnabled && m_fontCache.size() >= m_fontCacheLimit)
|
||||
if (m_fontCacheShrinkDisabledObjects.empty() && m_fontCache.size() >= m_fontCacheLimit)
|
||||
{
|
||||
// We have exceeded the cache limit. Clear the cache.
|
||||
m_fontCache.clear();
|
||||
@ -1638,7 +1638,7 @@ PDFRealizedFontPointer PDFFontCache::getRealizedFont(const PDFFontPointer& font,
|
||||
// We must create the realized font
|
||||
PDFRealizedFontPointer realizedFont = PDFRealizedFont::createRealizedFont(font, size, reporter);
|
||||
|
||||
if (m_fontCacheShrinkEnabled && m_realizedFontCache.size() >= m_realizedFontCacheLimit)
|
||||
if (m_fontCacheShrinkDisabledObjects.empty() && m_realizedFontCache.size() >= m_realizedFontCacheLimit)
|
||||
{
|
||||
m_realizedFontCache.clear();
|
||||
}
|
||||
@ -1649,13 +1649,19 @@ PDFRealizedFontPointer PDFFontCache::getRealizedFont(const PDFFontPointer& font,
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void PDFFontCache::setCacheShrinkEnabled(bool enabled)
|
||||
void PDFFontCache::setCacheShrinkEnabled(void* source, bool enabled)
|
||||
{
|
||||
if (m_fontCacheShrinkEnabled != enabled)
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (enabled)
|
||||
{
|
||||
m_fontCacheShrinkEnabled = enabled;
|
||||
m_fontCacheShrinkDisabledObjects.erase(source);
|
||||
lock.unlock();
|
||||
shrink();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fontCacheShrinkDisabledObjects.insert(source);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFFontCache::setCacheLimits(int fontCacheLimit, int instancedFontCacheLimit)
|
||||
@ -1670,9 +1676,9 @@ void PDFFontCache::setCacheLimits(int fontCacheLimit, int instancedFontCacheLimi
|
||||
|
||||
void PDFFontCache::shrink()
|
||||
{
|
||||
if (m_fontCacheShrinkEnabled)
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_fontCacheShrinkDisabledObjects.empty())
|
||||
{
|
||||
QMutexLocker lock(&m_mutex);
|
||||
if (m_fontCache.size() >= m_fontCacheLimit)
|
||||
{
|
||||
m_fontCache.clear();
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <QSharedPointer>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
class QPainterPath;
|
||||
@ -388,7 +389,6 @@ class PDFFontCache
|
||||
{
|
||||
public:
|
||||
inline explicit PDFFontCache(size_t fontCacheLimit, size_t realizedFontCacheLimit) :
|
||||
m_fontCacheShrinkEnabled(true),
|
||||
m_fontCacheLimit(fontCacheLimit),
|
||||
m_realizedFontCacheLimit(realizedFontCacheLimit),
|
||||
m_document(nullptr)
|
||||
@ -414,7 +414,12 @@ public:
|
||||
|
||||
/// Sets or unsets font shrinking (i.e. font can be deleted from the cache). In multithreading environment,
|
||||
/// font deletion is not thread safe. For this reason, disable font deletion by calling this function.
|
||||
void setCacheShrinkEnabled(bool enabled);
|
||||
/// First parameter, \p source determines which object enables cache shrinking (so some objects can
|
||||
/// enable shrinking, while some objects will disable it). Only if all objects enables cache shrinking,
|
||||
/// then cache can shrink.
|
||||
/// \param source Source object
|
||||
/// \param enabled Enable or disable cache shrinking
|
||||
void setCacheShrinkEnabled(void* source, bool enabled);
|
||||
|
||||
/// Set font cache limits
|
||||
void setCacheLimits(int fontCacheLimit, int instancedFontCacheLimit);
|
||||
@ -423,13 +428,13 @@ public:
|
||||
void shrink();
|
||||
|
||||
private:
|
||||
bool m_fontCacheShrinkEnabled;
|
||||
size_t m_fontCacheLimit;
|
||||
size_t m_realizedFontCacheLimit;
|
||||
mutable QMutex m_mutex;
|
||||
const PDFDocument* m_document;
|
||||
mutable std::map<PDFObjectReference, PDFFontPointer> m_fontCache;
|
||||
mutable std::map<std::pair<PDFFontPointer, PDFReal>, PDFRealizedFontPointer> m_realizedFontCache;
|
||||
mutable std::set<void*> m_fontCacheShrinkDisabledObjects;
|
||||
};
|
||||
|
||||
/// Performs mapping from CID to GID (even identity mapping, if byte array is empty)
|
||||
|
@ -20,7 +20,13 @@
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
void PDFProgress::start(size_t stepCount)
|
||||
PDFProgress::PDFProgress(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
qRegisterMetaType<pdf::ProgressStartupInfo>();
|
||||
}
|
||||
|
||||
void PDFProgress::start(size_t stepCount, ProgressStartupInfo startupInfo)
|
||||
{
|
||||
Q_ASSERT(stepCount > 0);
|
||||
|
||||
@ -28,7 +34,7 @@ void PDFProgress::start(size_t stepCount)
|
||||
m_stepCount = stepCount;
|
||||
m_percentage = 0;
|
||||
|
||||
emit progressStarted();
|
||||
emit progressStarted(qMove(startupInfo));
|
||||
}
|
||||
|
||||
void PDFProgress::step()
|
||||
|
@ -27,19 +27,25 @@
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
struct ProgressStartupInfo
|
||||
{
|
||||
bool showDialog = false;
|
||||
QString text;
|
||||
};
|
||||
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFProgress : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFProgress(QObject* parent) : QObject(parent) { }
|
||||
explicit PDFProgress(QObject* parent);
|
||||
|
||||
void start(size_t stepCount);
|
||||
void start(size_t stepCount, ProgressStartupInfo startupInfo);
|
||||
void step();
|
||||
void finish();
|
||||
|
||||
signals:
|
||||
void progressStarted();
|
||||
void progressStarted(ProgressStartupInfo info);
|
||||
void progressStep(int percentage);
|
||||
void progressFinished();
|
||||
|
||||
@ -49,6 +55,8 @@ private:
|
||||
std::atomic<int> m_percentage = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ProgressStartupInfo)
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFPROGRESS_H
|
||||
|
@ -73,7 +73,9 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
|
||||
m_isLoadingUI(false),
|
||||
m_progress(new pdf::PDFProgress(this)),
|
||||
m_taskbarButton(new QWinTaskbarButton(this)),
|
||||
m_progressTaskbarIndicator(nullptr)
|
||||
m_progressTaskbarIndicator(nullptr),
|
||||
m_progressDialog(nullptr),
|
||||
m_isBusy(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@ -159,6 +161,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
|
||||
setCentralWidget(m_pdfWidget);
|
||||
setFocusProxy(m_pdfWidget);
|
||||
m_pdfWidget->updateCacheLimits(m_settings->getCompiledPageCacheLimit() * 1024, m_settings->getThumbnailsCacheLimit(), m_settings->getFontCacheLimit(), m_settings->getInstancedFontCacheLimit());
|
||||
m_pdfWidget->getDrawWidgetProxy()->setProgress(m_progress);
|
||||
|
||||
m_sidebarWidget = new PDFSidebarWidget(m_pdfWidget->getDrawWidgetProxy(), this);
|
||||
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
|
||||
@ -507,21 +510,46 @@ void PDFViewerMainWindow::onActionTriggered(const pdf::PDFAction* action)
|
||||
}
|
||||
}
|
||||
|
||||
void PDFViewerMainWindow::onProgressStarted()
|
||||
void PDFViewerMainWindow::onProgressStarted(pdf::ProgressStartupInfo info)
|
||||
{
|
||||
Q_ASSERT(!m_progressDialog);
|
||||
if (info.showDialog)
|
||||
{
|
||||
m_progressDialog = new QProgressDialog(info.text, QString(), 0, 100, this);
|
||||
m_progressDialog->setWindowModality(Qt::WindowModal);
|
||||
m_progressDialog->setCancelButton(nullptr);
|
||||
}
|
||||
|
||||
m_progressTaskbarIndicator->setRange(0, 100);
|
||||
m_progressTaskbarIndicator->reset();
|
||||
m_progressTaskbarIndicator->show();
|
||||
m_isBusy = true;
|
||||
|
||||
updateActionsAvailability();
|
||||
}
|
||||
|
||||
void PDFViewerMainWindow::onProgressStep(int percentage)
|
||||
{
|
||||
if (m_progressDialog)
|
||||
{
|
||||
m_progressDialog->setValue(percentage);
|
||||
}
|
||||
|
||||
m_progressTaskbarIndicator->setValue(percentage);
|
||||
}
|
||||
|
||||
void PDFViewerMainWindow::onProgressFinished()
|
||||
{
|
||||
if (m_progressDialog)
|
||||
{
|
||||
m_progressDialog->hide();
|
||||
m_progressDialog->deleteLater();
|
||||
m_progressDialog = nullptr;
|
||||
}
|
||||
m_progressTaskbarIndicator->hide();
|
||||
m_isBusy = false;
|
||||
|
||||
updateActionsAvailability();
|
||||
}
|
||||
|
||||
void PDFViewerMainWindow::readSettings()
|
||||
@ -697,19 +725,20 @@ void PDFViewerMainWindow::updateUI(bool fullUpdate)
|
||||
|
||||
void PDFViewerMainWindow::updateActionsAvailability()
|
||||
{
|
||||
const bool isReading = m_futureWatcher.isRunning();
|
||||
const bool isBusy = m_futureWatcher.isRunning() || m_isBusy;
|
||||
const bool hasDocument = m_pdfDocument;
|
||||
const bool hasValidDocument = !isReading && hasDocument;
|
||||
const bool hasValidDocument = !isBusy && hasDocument;
|
||||
|
||||
ui->actionOpen->setEnabled(!isReading);
|
||||
ui->actionOpen->setEnabled(!isBusy);
|
||||
ui->actionClose->setEnabled(hasValidDocument);
|
||||
ui->actionQuit->setEnabled(!isReading);
|
||||
ui->actionOptions->setEnabled(!isReading);
|
||||
ui->actionAbout->setEnabled(!isReading);
|
||||
ui->actionQuit->setEnabled(!isBusy);
|
||||
ui->actionOptions->setEnabled(!isBusy);
|
||||
ui->actionAbout->setEnabled(!isBusy);
|
||||
ui->actionFitPage->setEnabled(hasValidDocument);
|
||||
ui->actionFitWidth->setEnabled(hasValidDocument);
|
||||
ui->actionFitHeight->setEnabled(hasValidDocument);
|
||||
ui->actionRendering_Errors->setEnabled(hasValidDocument);
|
||||
setEnabled(!isBusy);
|
||||
}
|
||||
|
||||
void PDFViewerMainWindow::onViewerSettingsChanged()
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <QWinTaskbarButton>
|
||||
#include <QWinTaskbarProgress>
|
||||
#include <QFutureWatcher>
|
||||
#include <QProgressDialog>
|
||||
|
||||
class QLabel;
|
||||
class QSpinBox;
|
||||
@ -99,7 +100,7 @@ private:
|
||||
void onPageZoomSpinboxEditingFinished();
|
||||
void onActionTriggered(const pdf::PDFAction* action);
|
||||
|
||||
void onProgressStarted();
|
||||
void onProgressStarted(pdf::ProgressStartupInfo info);
|
||||
void onProgressStep(int percentage);
|
||||
void onProgressFinished();
|
||||
|
||||
@ -156,6 +157,9 @@ private:
|
||||
|
||||
QFuture<AsyncReadingResult> m_future;
|
||||
QFutureWatcher<AsyncReadingResult> m_futureWatcher;
|
||||
|
||||
QProgressDialog* m_progressDialog;
|
||||
bool m_isBusy;
|
||||
};
|
||||
|
||||
} // namespace pdfviewer
|
||||
|
Loading…
x
Reference in New Issue
Block a user