mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Issue #118: Move compiler and draw space controller
This commit is contained in:
@@ -23,7 +23,6 @@ add_library(Pdf4QtLibCore SHARED
|
||||
sources/pdfccittfaxdecoder.cpp
|
||||
sources/pdfcertificatemanager.cpp
|
||||
sources/pdfcms.cpp
|
||||
sources/pdfcompiler.cpp
|
||||
sources/pdfdiff.cpp
|
||||
sources/pdfdocumentbuilder.cpp
|
||||
sources/pdfdocumentmanipulator.cpp
|
||||
@@ -67,7 +66,6 @@ add_library(Pdf4QtLibCore SHARED
|
||||
sources/pdfcatalog.cpp
|
||||
sources/pdfpage.cpp
|
||||
sources/pdfstreamfilters.cpp
|
||||
sources/pdfdrawspacecontroller.cpp
|
||||
sources/pdfcolorspaces.cpp
|
||||
sources/pdfrenderer.cpp
|
||||
sources/pdfpagecontentprocessor.cpp
|
||||
@@ -82,6 +80,10 @@ add_library(Pdf4QtLibCore SHARED
|
||||
sources/pdfimageconversion.cpp
|
||||
sources/pdfcolorconvertor.h
|
||||
sources/pdfcolorconvertor.cpp
|
||||
sources/pdftextlayoutgenerator.h
|
||||
sources/pdftextlayoutgenerator.cpp
|
||||
sources/pdfwidgetsnapshot.cpp
|
||||
sources/pdfwidgetsnapshot.h
|
||||
cmaps.qrc
|
||||
)
|
||||
|
||||
|
@@ -19,7 +19,6 @@
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfencoding.h"
|
||||
#include "pdfpainter.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfcms.h"
|
||||
#include "pdfpagecontentprocessor.h"
|
||||
#include "pdfparser.h"
|
||||
@@ -3114,7 +3113,7 @@ void PDFWidgetAnnotation::draw(AnnotationDrawParameters& parameters) const
|
||||
case PDFFormField::FieldType::Text:
|
||||
case PDFFormField::FieldType::Choice:
|
||||
{
|
||||
m_parameters.formManager->drawFormField(parameters, false);
|
||||
parameters.formManager->drawFormField(formField, parameters, false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -25,11 +25,11 @@
|
||||
#include "pdfcms.h"
|
||||
#include "pdfmultimedia.h"
|
||||
#include "pdfmeshqualitysettings.h"
|
||||
#include "pdfdocumentdrawinterface.h"
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdfblendfunction.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfcolorconvertor.h"
|
||||
#include "pdftextlayout.h"
|
||||
|
||||
#include <QCursor>
|
||||
#include <QPainterPath>
|
||||
@@ -1432,7 +1432,7 @@ private:
|
||||
/// this object builds annotation's appearance streams, if necessary. This
|
||||
/// manager is intended to non-gui rendering. If widget annotation manager is used,
|
||||
/// then this object is not thread safe.
|
||||
class PDF4QTLIBCORESHARED_EXPORT PDFAnnotationManager : public QObject, public IDocumentDrawInterface
|
||||
class PDF4QTLIBCORESHARED_EXPORT PDFAnnotationManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -1463,7 +1463,7 @@ public:
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QTransform& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
QList<PDFRenderError>& errors) const;
|
||||
|
||||
/// Set document
|
||||
/// \param document New document
|
||||
|
@@ -1,635 +0,0 @@
|
||||
// Copyright (C) 2019-2022 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfcms.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfprogress.h"
|
||||
#include "pdfexecutionpolicy.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include <execution>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFAsynchronousPageCompilerWorkerThread::PDFAsynchronousPageCompilerWorkerThread(PDFAsynchronousPageCompiler* parent) :
|
||||
QThread(parent),
|
||||
m_compiler(parent),
|
||||
m_mutex(&m_compiler->m_mutex),
|
||||
m_waitCondition(&m_compiler->m_waitCondition)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompilerWorkerThread::run()
|
||||
{
|
||||
QMutexLocker locker(m_mutex);
|
||||
while (!isInterruptionRequested())
|
||||
{
|
||||
if (m_waitCondition->wait(locker.mutex(), QDeadlineTimer(QDeadlineTimer::Forever)))
|
||||
{
|
||||
while (!isInterruptionRequested())
|
||||
{
|
||||
std::vector<PDFAsynchronousPageCompiler::CompileTask> tasks;
|
||||
for (auto& task : m_compiler->m_tasks)
|
||||
{
|
||||
if (!task.second.finished)
|
||||
{
|
||||
tasks.push_back(task.second);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tasks.empty())
|
||||
{
|
||||
locker.unlock();
|
||||
|
||||
// Perform page compilation
|
||||
auto proxy = m_compiler->getProxy();
|
||||
proxy->getFontCache()->setCacheShrinkEnabled(this, false);
|
||||
|
||||
auto compilePage = [this, proxy](PDFAsynchronousPageCompiler::CompileTask& task) -> PDFPrecompiledPage
|
||||
{
|
||||
PDFPrecompiledPage compiledPage;
|
||||
PDFCMSPointer cms = proxy->getCMSManager()->getCurrentCMS();
|
||||
PDFRenderer renderer(proxy->getDocument(), proxy->getFontCache(), cms.data(), proxy->getOptionalContentActivity(), proxy->getFeatures(), proxy->getMeshQualitySettings());
|
||||
renderer.setOperationControl(m_compiler);
|
||||
renderer.compile(&task.precompiledPage, task.pageIndex);
|
||||
task.finished = true;
|
||||
return compiledPage;
|
||||
};
|
||||
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, tasks.begin(), tasks.end(), compilePage);
|
||||
|
||||
proxy->getFontCache()->setCacheShrinkEnabled(this, true);
|
||||
|
||||
// Relock the mutex to write the tasks
|
||||
locker.relock();
|
||||
|
||||
// Now, write compiled pages
|
||||
bool isSomethingWritten = false;
|
||||
for (auto& task : tasks)
|
||||
{
|
||||
if (task.finished)
|
||||
{
|
||||
isSomethingWritten = true;
|
||||
m_compiler->m_tasks[task.pageIndex] = std::move(task);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSomethingWritten)
|
||||
{
|
||||
// Why we are unlocking the mutex? Because
|
||||
// we do not want to emit signals with locked mutexes.
|
||||
// If direct connection is applied, this can lead to deadlock.
|
||||
locker.unlock();
|
||||
Q_EMIT pageCompiled();
|
||||
locker.relock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PDFAsynchronousPageCompiler::PDFAsynchronousPageCompiler(PDFDrawWidgetProxy* proxy) :
|
||||
BaseClass(proxy),
|
||||
m_proxy(proxy)
|
||||
{
|
||||
m_cache.setMaxCost(128 * 1024 * 1024);
|
||||
}
|
||||
|
||||
PDFAsynchronousPageCompiler::~PDFAsynchronousPageCompiler()
|
||||
{
|
||||
stop(true);
|
||||
}
|
||||
|
||||
bool PDFAsynchronousPageCompiler::isOperationCancelled() const
|
||||
{
|
||||
return m_state == State::Stopping;
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::start()
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case State::Inactive:
|
||||
{
|
||||
Q_ASSERT(!m_thread);
|
||||
m_state = State::Active;
|
||||
m_thread = new PDFAsynchronousPageCompilerWorkerThread(this);
|
||||
connect(m_thread, &PDFAsynchronousPageCompilerWorkerThread::pageCompiled, this, &PDFAsynchronousPageCompiler::onPageCompiled);
|
||||
m_thread->start();
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Active:
|
||||
break; // We have nothing to do...
|
||||
|
||||
case State::Stopping:
|
||||
{
|
||||
// We shouldn't call this function while stopping!
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::stop(bool clearCache)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case State::Inactive:
|
||||
{
|
||||
Q_ASSERT(!m_thread);
|
||||
break; // We have nothing to do...
|
||||
}
|
||||
|
||||
case State::Active:
|
||||
{
|
||||
// Stop the engine
|
||||
m_state = State::Stopping;
|
||||
|
||||
Q_ASSERT(m_thread);
|
||||
m_thread->requestInterruption();
|
||||
m_waitCondition.wakeAll();
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
m_thread = nullptr;
|
||||
|
||||
// It is safe to do not use mutex, because
|
||||
// we have ended the work thread.
|
||||
m_tasks.clear();
|
||||
|
||||
if (clearCache)
|
||||
{
|
||||
m_cache.clear();
|
||||
}
|
||||
|
||||
m_state = State::Inactive;
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Stopping:
|
||||
{
|
||||
// We shouldn't call this function while stopping!
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::reset()
|
||||
{
|
||||
stop(true);
|
||||
start();
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::setCacheLimit(int limit)
|
||||
{
|
||||
m_cache.setMaxCost(limit);
|
||||
}
|
||||
|
||||
const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getCompiledPage(PDFInteger pageIndex, bool compile)
|
||||
{
|
||||
if (m_state != State::Active || !m_proxy->getDocument())
|
||||
{
|
||||
// Engine is not active, always return nullptr
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PDFPrecompiledPage* page = m_cache.object(pageIndex);
|
||||
|
||||
if (!page && compile)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (!m_tasks.count(pageIndex))
|
||||
{
|
||||
m_tasks.insert(std::make_pair(pageIndex, CompileTask(pageIndex)));
|
||||
m_waitCondition.wakeOne();
|
||||
}
|
||||
}
|
||||
|
||||
if (page)
|
||||
{
|
||||
page->markAccessed();
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::smartClearCache(const int milisecondsLimit, const std::vector<PDFInteger>& activePages)
|
||||
{
|
||||
if (m_state != State::Active)
|
||||
{
|
||||
// Jakub Melka: Cache clearing can be done only in active state
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
Q_ASSERT(std::is_sorted(activePages.cbegin(), activePages.cend()));
|
||||
|
||||
QList<PDFInteger> pageIndices = m_cache.keys();
|
||||
for (const PDFInteger pageIndex : pageIndices)
|
||||
{
|
||||
if (std::binary_search(activePages.cbegin(), activePages.cend(), pageIndex))
|
||||
{
|
||||
// We do not remove active page
|
||||
continue;
|
||||
}
|
||||
|
||||
const PDFPrecompiledPage* page = m_cache.object(pageIndex);
|
||||
if (page && page->hasExpired(milisecondsLimit))
|
||||
{
|
||||
m_cache.remove(pageIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousPageCompiler::onPageCompiled()
|
||||
{
|
||||
std::vector<PDFInteger> compiledPages;
|
||||
std::map<PDFInteger, PDFRenderError> errors;
|
||||
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
// Search all tasks for finished tasks
|
||||
for (auto it = m_tasks.begin(); it != m_tasks.end();)
|
||||
{
|
||||
CompileTask& task = it->second;
|
||||
if (task.finished)
|
||||
{
|
||||
if (m_state == State::Active)
|
||||
{
|
||||
// If we are in active state, try to store precompiled page
|
||||
PDFPrecompiledPage* page = new PDFPrecompiledPage(std::move(task.precompiledPage));
|
||||
page->markAccessed();
|
||||
qint64 memoryConsumptionEstimate = page->getMemoryConsumptionEstimate();
|
||||
if (m_cache.insert(it->first, page, memoryConsumptionEstimate))
|
||||
{
|
||||
compiledPages.push_back(it->first);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't insert page to the cache, because cache size is too small. We will
|
||||
// emit error string to inform the user, that cache is too small.
|
||||
QString message = PDFTranslationContext::tr("Precompiled page size is too high (%1 kB). Cache size is %2 kB. Increase the cache size!").arg(memoryConsumptionEstimate / 1024).arg(m_cache.maxCost() / 1024);
|
||||
errors[it->first] = PDFRenderError(RenderErrorType::Error, message);
|
||||
}
|
||||
}
|
||||
|
||||
it = m_tasks.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just increment the counter
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& error : errors)
|
||||
{
|
||||
Q_EMIT renderingError(error.first, { error.second });
|
||||
}
|
||||
|
||||
if (!compiledPages.empty())
|
||||
{
|
||||
Q_ASSERT(std::is_sorted(compiledPages.cbegin(), compiledPages.cend()));
|
||||
Q_EMIT pageImageChanged(false, compiledPages);
|
||||
}
|
||||
}
|
||||
|
||||
PDFTextLayout PDFTextLayoutGenerator::createTextLayout()
|
||||
{
|
||||
m_textLayout.perform();
|
||||
m_textLayout.optimize();
|
||||
return qMove(m_textLayout);
|
||||
}
|
||||
|
||||
bool PDFTextLayoutGenerator::isContentSuppressedByOC(PDFObjectReference ocgOrOcmd)
|
||||
{
|
||||
if (m_features.testFlag(PDFRenderer::IgnoreOptionalContent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return PDFPageContentProcessor::isContentSuppressedByOC(ocgOrOcmd);
|
||||
}
|
||||
|
||||
bool PDFTextLayoutGenerator::isContentKindSuppressed(ContentKind kind) const
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ContentKind::Shapes:
|
||||
case ContentKind::Text:
|
||||
case ContentKind::Images:
|
||||
case ContentKind::Shading:
|
||||
return true;
|
||||
|
||||
case ContentKind::Tiling:
|
||||
return false; // Tiling can have text
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PDFTextLayoutGenerator::performOutputCharacter(const PDFTextCharacterInfo& info)
|
||||
{
|
||||
if (!isContentSuppressed() && !info.character.isSpace())
|
||||
{
|
||||
m_textLayout.addCharacter(info);
|
||||
}
|
||||
}
|
||||
|
||||
PDFAsynchronousTextLayoutCompiler::PDFAsynchronousTextLayoutCompiler(PDFDrawWidgetProxy* proxy) :
|
||||
BaseClass(proxy),
|
||||
m_proxy(proxy),
|
||||
m_isRunning(false),
|
||||
m_cache(std::bind(&PDFAsynchronousTextLayoutCompiler::createTextLayout, this, std::placeholders::_1))
|
||||
{
|
||||
connect(&m_textLayoutCompileFutureWatcher, &QFutureWatcher<PDFTextLayoutStorage>::finished, this, &PDFAsynchronousTextLayoutCompiler::onTextLayoutCreated);
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::start()
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case State::Inactive:
|
||||
{
|
||||
m_state = State::Active;
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Active:
|
||||
break; // We have nothing to do...
|
||||
|
||||
case State::Stopping:
|
||||
{
|
||||
// We shouldn't call this function while stopping!
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::stop(bool clearCache)
|
||||
{
|
||||
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();
|
||||
|
||||
if (clearCache)
|
||||
{
|
||||
m_textLayouts = std::nullopt;
|
||||
m_cache.clear();
|
||||
}
|
||||
|
||||
m_state = State::Inactive;
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Stopping:
|
||||
{
|
||||
// We shouldn't call this function while stopping!
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::reset()
|
||||
{
|
||||
stop(true);
|
||||
start();
|
||||
}
|
||||
|
||||
PDFTextLayout PDFAsynchronousTextLayoutCompiler::createTextLayout(PDFInteger pageIndex)
|
||||
{
|
||||
PDFTextLayout result;
|
||||
|
||||
if (isTextLayoutReady())
|
||||
{
|
||||
result = getTextLayout(pageIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_state != State::Active || !m_proxy->getDocument())
|
||||
{
|
||||
// Engine is not active, do not calculate layout
|
||||
return result;
|
||||
}
|
||||
|
||||
const PDFCatalog* catalog = m_proxy->getDocument()->getCatalog();
|
||||
if (pageIndex < 0 || pageIndex >= PDFInteger(catalog->getPageCount()))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!catalog->getPage(pageIndex))
|
||||
{
|
||||
// Invalid page index
|
||||
return result;
|
||||
}
|
||||
|
||||
const PDFPage* page = catalog->getPage(pageIndex);
|
||||
Q_ASSERT(page);
|
||||
|
||||
bool guard = false;
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(&guard, false);
|
||||
|
||||
PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS();
|
||||
PDFTextLayoutGenerator generator(m_proxy->getFeatures(), page, m_proxy->getDocument(), m_proxy->getFontCache(), cms.data(), m_proxy->getOptionalContentActivity(), QTransform(), m_proxy->getMeshQualitySettings());
|
||||
generator.processContents();
|
||||
result = generator.createTextLayout();
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(&guard, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
PDFTextLayoutGetter PDFAsynchronousTextLayoutCompiler::getTextLayoutLazy(PDFInteger pageIndex)
|
||||
{
|
||||
return PDFTextLayoutGetter(&m_cache, pageIndex);
|
||||
}
|
||||
|
||||
PDFTextSelection PDFAsynchronousTextLayoutCompiler::getTextSelectionAll(QColor color) const
|
||||
{
|
||||
PDFTextSelection result;
|
||||
|
||||
if (m_textLayouts)
|
||||
{
|
||||
const PDFTextLayoutStorage& textLayouts = *m_textLayouts;
|
||||
|
||||
QMutex mutex;
|
||||
PDFIntegerRange<size_t> pageRange(0, textLayouts.getCount());
|
||||
auto selectPageText = [&mutex, &textLayouts, &result, color](PDFInteger pageIndex)
|
||||
{
|
||||
PDFTextLayout textLayout = textLayouts.getTextLayout(pageIndex);
|
||||
PDFTextSelectionItems items;
|
||||
|
||||
const PDFTextBlocks& blocks = textLayout.getTextBlocks();
|
||||
for (size_t blockId = 0, blockCount = blocks.size(); blockId < blockCount; ++blockId)
|
||||
{
|
||||
const PDFTextBlock& block = blocks[blockId];
|
||||
const PDFTextLines& lines = block.getLines();
|
||||
|
||||
if (!lines.empty())
|
||||
{
|
||||
const PDFTextLine& lastLine = lines.back();
|
||||
Q_ASSERT(!lastLine.getCharacters().empty());
|
||||
|
||||
PDFCharacterPointer ptrStart;
|
||||
ptrStart.pageIndex = pageIndex;
|
||||
ptrStart.blockIndex = blockId;
|
||||
ptrStart.lineIndex = 0;
|
||||
ptrStart.characterIndex = 0;
|
||||
|
||||
PDFCharacterPointer ptrEnd;
|
||||
ptrEnd.pageIndex = pageIndex;
|
||||
ptrEnd.blockIndex = blockId;
|
||||
ptrEnd.lineIndex = lines.size() - 1;
|
||||
ptrEnd.characterIndex = lastLine.getCharacters().size() - 1;
|
||||
|
||||
items.emplace_back(ptrStart, ptrEnd);
|
||||
}
|
||||
}
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
result.addItems(qMove(items), color);
|
||||
};
|
||||
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pageRange.begin(), pageRange.end(), selectPageText);
|
||||
}
|
||||
|
||||
result.build();
|
||||
return result;
|
||||
}
|
||||
|
||||
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_isRunning)
|
||||
{
|
||||
// Text layout is already being processed
|
||||
return;
|
||||
}
|
||||
|
||||
// Jakub Melka: Mark, that we are running (test for future is not enough,
|
||||
// because future can finish before this function exits, for example)
|
||||
m_isRunning = true;
|
||||
|
||||
ProgressStartupInfo info;
|
||||
info.showDialog = true;
|
||||
info.text = tr("Indexing document contents...");
|
||||
|
||||
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(), QTransform(), m_proxy->getMeshQualitySettings());
|
||||
generator.processContents();
|
||||
result.setTextLayout(pageIndex, generator.createTextLayout(), &mutex);
|
||||
m_proxy->getProgress()->step();
|
||||
};
|
||||
|
||||
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
|
||||
PDFExecutionPolicy::execute(PDFExecutionPolicy::Scope::Page, pageRange.begin(), pageRange.end(), generateTextLayout);
|
||||
return result;
|
||||
};
|
||||
|
||||
Q_ASSERT(!m_textLayoutCompileFuture.isRunning());
|
||||
m_textLayoutCompileFuture = QtConcurrent::run(createTextLayout);
|
||||
m_textLayoutCompileFutureWatcher.setFuture(m_textLayoutCompileFuture);
|
||||
}
|
||||
|
||||
void PDFAsynchronousTextLayoutCompiler::onTextLayoutCreated()
|
||||
{
|
||||
m_proxy->getFontCache()->setCacheShrinkEnabled(this, true);
|
||||
m_proxy->getProgress()->finish();
|
||||
m_cache.clear();
|
||||
|
||||
m_textLayouts = m_textLayoutCompileFuture.result();
|
||||
m_isRunning = false;
|
||||
Q_EMIT textLayoutChanged();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
@@ -1,263 +0,0 @@
|
||||
// Copyright (C) 2019-2021 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFCOMPILER_H
|
||||
#define PDFCOMPILER_H
|
||||
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdfpainter.h"
|
||||
#include "pdftextlayout.h"
|
||||
|
||||
#include <QCache>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QWaitCondition>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFDrawWidgetProxy;
|
||||
class PDFAsynchronousPageCompiler;
|
||||
|
||||
class PDFAsynchronousPageCompilerWorkerThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFAsynchronousPageCompilerWorkerThread(PDFAsynchronousPageCompiler* parent);
|
||||
|
||||
signals:
|
||||
void pageCompiled();
|
||||
|
||||
protected:
|
||||
virtual void run() override;
|
||||
|
||||
private:
|
||||
PDFAsynchronousPageCompiler* m_compiler;
|
||||
QMutex* m_mutex;
|
||||
QWaitCondition* m_waitCondition;
|
||||
};
|
||||
|
||||
/// Asynchronous page compiler compiles pages asynchronously, and stores them in the
|
||||
/// cache. Cache size can be set. This object is designed to cooperate with
|
||||
/// draw widget proxy.
|
||||
class PDFAsynchronousPageCompiler : public QObject, public PDFOperationControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = QObject;
|
||||
|
||||
public:
|
||||
explicit PDFAsynchronousPageCompiler(PDFDrawWidgetProxy* proxy);
|
||||
virtual ~PDFAsynchronousPageCompiler();
|
||||
|
||||
/// 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 if needed. Call this function only if engine is active.
|
||||
/// Cache is cleared only, if \p clearCache parameter is being set to true.
|
||||
/// Set it to false, if "soft" document update occurs (this means change
|
||||
/// to the document, which doesn't modify page content in precompiled
|
||||
/// pages (graphic content / number of pages change).
|
||||
/// \param clearCache Clear cache
|
||||
void stop(bool clearCache);
|
||||
|
||||
/// Resets the engine - calls stop and then calls start.
|
||||
void reset();
|
||||
|
||||
/// Sets cache limit in bytes
|
||||
/// \param limit Cache limit [bytes]
|
||||
void setCacheLimit(int limit);
|
||||
|
||||
enum class State
|
||||
{
|
||||
Inactive,
|
||||
Active,
|
||||
Stopping
|
||||
};
|
||||
|
||||
/// Returns current state of compiler
|
||||
State getState() const { return m_state; }
|
||||
|
||||
/// Return proxy
|
||||
PDFDrawWidgetProxy* getProxy() const { return m_proxy; }
|
||||
|
||||
/// Tries to retrieve precompiled page from the cache. If page is not found,
|
||||
/// then nullptr is returned (no exception is thrown). If \p compile is set to true,
|
||||
/// and page is not found, and compiler is active, then new asynchronous compile
|
||||
/// task is performed.
|
||||
/// \param pageIndex Index of page
|
||||
/// \param compile Compile the page, if it is not found in the cache
|
||||
const PDFPrecompiledPage* getCompiledPage(PDFInteger pageIndex, bool compile);
|
||||
|
||||
/// Performs smart cache clear. Too old pages are removed from the cache,
|
||||
/// but only if these pages are not in active pages. Use this function to
|
||||
/// clear cache to avoid huge memory consumption.
|
||||
/// \param milisecondsLimit Pages with access time above this limit will be erased
|
||||
/// \param activePages Sorted vector of active pages, which should remain in cache
|
||||
void smartClearCache(const int milisecondsLimit, const std::vector<PDFInteger>& activePages);
|
||||
|
||||
/// Is operation being cancelled?
|
||||
virtual bool isOperationCancelled() const override;
|
||||
|
||||
signals:
|
||||
void pageImageChanged(bool all, const std::vector<pdf::PDFInteger>& pages);
|
||||
void renderingError(pdf::PDFInteger pageIndex, const QList<pdf::PDFRenderError>& errors);
|
||||
|
||||
private:
|
||||
friend class PDFAsynchronousPageCompilerWorkerThread;
|
||||
|
||||
void onPageCompiled();
|
||||
|
||||
struct CompileTask
|
||||
{
|
||||
CompileTask() = default;
|
||||
CompileTask(PDFInteger pageIndex) : pageIndex(pageIndex) { }
|
||||
|
||||
PDFInteger pageIndex = 0;
|
||||
bool finished = false;
|
||||
PDFPrecompiledPage precompiledPage;
|
||||
};
|
||||
|
||||
State m_state = State::Inactive;
|
||||
QMutex m_mutex;
|
||||
QWaitCondition m_waitCondition;
|
||||
PDFAsynchronousPageCompilerWorkerThread* m_thread = nullptr;
|
||||
|
||||
PDFDrawWidgetProxy* m_proxy;
|
||||
QCache<PDFInteger, PDFPrecompiledPage> m_cache;
|
||||
|
||||
/// This task is protected by mutex. Every access to this
|
||||
/// variable must be done with locked mutex.
|
||||
std::map<PDFInteger, CompileTask> m_tasks;
|
||||
};
|
||||
|
||||
class PDF4QTLIBCORESHARED_EXPORT 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 if parameter \p clearCache. Call this function
|
||||
/// only if engine is active. Clear cache should be set to false,
|
||||
/// only if "soft" document update appears (no text on page is being
|
||||
/// changed).
|
||||
/// \param clearCache Clear cache
|
||||
void stop(bool clearCache);
|
||||
|
||||
/// Resets the engine - calls stop and then calls start.
|
||||
void reset();
|
||||
|
||||
enum class State
|
||||
{
|
||||
Inactive,
|
||||
Active,
|
||||
Stopping
|
||||
};
|
||||
|
||||
/// Creates text layout of the page synchronously. If page index is invalid,
|
||||
/// then empty text layout is returned. Compiler must be active to get
|
||||
/// valid text layout.
|
||||
/// \param pageIndex Page index
|
||||
PDFTextLayout createTextLayout(PDFInteger pageIndex);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Returns getter for text layout of the page. If page index is invalid,
|
||||
/// then empty text layout getter is returned.
|
||||
/// \param pageIndex Page index
|
||||
PDFTextLayoutGetter getTextLayoutLazy(PDFInteger pageIndex);
|
||||
|
||||
/// Select all texts on all pages using \p color color.
|
||||
/// \param color Color to be used for text selection
|
||||
PDFTextSelection getTextSelectionAll(QColor color) const;
|
||||
|
||||
/// Create text layout for the document. Function is asynchronous,
|
||||
/// it returns immediately. After text layout is created, signal
|
||||
/// \p textLayoutChanged is emitted.
|
||||
void makeTextLayout();
|
||||
|
||||
/// Returns true, if text layout is ready
|
||||
bool isTextLayoutReady() const { return m_textLayouts.has_value(); }
|
||||
|
||||
/// Returns text layout storage (if it is ready), or nullptr
|
||||
const PDFTextLayoutStorage* getTextLayoutStorage() const { return isTextLayoutReady() ? &m_textLayouts.value() : nullptr; }
|
||||
|
||||
signals:
|
||||
void textLayoutChanged();
|
||||
|
||||
private:
|
||||
void onTextLayoutCreated();
|
||||
|
||||
PDFDrawWidgetProxy* m_proxy;
|
||||
State m_state = State::Inactive;
|
||||
bool m_isRunning;
|
||||
std::optional<PDFTextLayoutStorage> m_textLayouts;
|
||||
QFuture<PDFTextLayoutStorage> m_textLayoutCompileFuture;
|
||||
QFutureWatcher<PDFTextLayoutStorage> m_textLayoutCompileFutureWatcher;
|
||||
PDFTextLayoutCache m_cache;
|
||||
};
|
||||
|
||||
class PDFTextLayoutGenerator : public PDFPageContentProcessor
|
||||
{
|
||||
using BaseClass = PDFPageContentProcessor;
|
||||
|
||||
public:
|
||||
explicit PDFTextLayoutGenerator(PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFCMS* cms,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
QTransform pagePointToDevicePointMatrix,
|
||||
const PDFMeshQualitySettings& meshQualitySettings) :
|
||||
BaseClass(page, document, fontCache, cms, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
|
||||
m_features(features)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Creates text layout from the text
|
||||
PDFTextLayout createTextLayout();
|
||||
|
||||
protected:
|
||||
virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) override;
|
||||
virtual bool isContentKindSuppressed(ContentKind kind) const override;
|
||||
virtual void performOutputCharacter(const PDFTextCharacterInfo& info) override;
|
||||
|
||||
private:
|
||||
PDFRenderer::Features m_features;
|
||||
PDFTextLayout m_textLayout;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFCOMPILER_H
|
@@ -21,9 +21,9 @@
|
||||
#include "pdfexecutionpolicy.h"
|
||||
#include "pdffont.h"
|
||||
#include "pdfcms.h"
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfconstants.h"
|
||||
#include "pdfalgorithmlcs.h"
|
||||
#include "pdfpainter.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
@@ -1,147 +0,0 @@
|
||||
// Copyright (C) 2020-2021 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFDOCUMENTDRAWINTERFACE_H
|
||||
#define PDFDOCUMENTDRAWINTERFACE_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
#include "pdfexception.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
class QPainter;
|
||||
class QKeyEvent;
|
||||
class QMouseEvent;
|
||||
class QWheelEvent;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFPrecompiledPage;
|
||||
class PDFTextLayoutGetter;
|
||||
|
||||
class PDF4QTLIBCORESHARED_EXPORT IDocumentDrawInterface
|
||||
{
|
||||
public:
|
||||
explicit inline IDocumentDrawInterface() = default;
|
||||
virtual ~IDocumentDrawInterface() = default;
|
||||
|
||||
/// Performs drawing of additional graphics onto the painter using precompiled page,
|
||||
/// optionally text layout and page point to device point matrix.
|
||||
/// \param painter Painter
|
||||
/// \param pageIndex Page index
|
||||
/// \param compiledPage Compiled page
|
||||
/// \param layoutGetter Layout getter
|
||||
/// \param pagePointToDevicePointMatrix Matrix mapping page space to device point space
|
||||
/// \param[out] errors Output parameter - rendering errors
|
||||
virtual void drawPage(QPainter* painter,
|
||||
pdf::PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QTransform& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const;
|
||||
|
||||
/// Performs drawing of additional graphics after all pages are drawn onto the painter.
|
||||
/// \param painter Painter
|
||||
/// \param rect Draw rectangle (usually viewport rectangle of the pdf widget)
|
||||
virtual void drawPostRendering(QPainter* painter, QRect rect) const;
|
||||
};
|
||||
|
||||
/// Input interface for handling events. Implementations should react on these events,
|
||||
/// and set it to accepted, if they were consumed by the interface. Interface, which
|
||||
/// consumes mouse press event, should also consume mouse release event.
|
||||
class IDrawWidgetInputInterface
|
||||
{
|
||||
public:
|
||||
explicit inline IDrawWidgetInputInterface() = default;
|
||||
virtual ~IDrawWidgetInputInterface() = default;
|
||||
|
||||
enum InputPriority
|
||||
{
|
||||
ToolPriority = 10,
|
||||
FormPriority = 20,
|
||||
AnnotationPriority = 30,
|
||||
UserPriority = 40
|
||||
};
|
||||
|
||||
/// Handles shortcut override event. Accept this event, when you want given
|
||||
/// key sequence to be propagated to keyPressEvent.
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) = 0;
|
||||
|
||||
/// Handles key press event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) = 0;
|
||||
|
||||
/// Handles key release event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) = 0;
|
||||
|
||||
/// Handles mouse press event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) = 0;
|
||||
|
||||
/// Handles mouse double click event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) = 0;
|
||||
|
||||
/// Handles mouse release event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) = 0;
|
||||
|
||||
/// Handles mouse move event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) = 0;
|
||||
|
||||
/// Handles mouse wheel event from widget
|
||||
/// \param widget Widget
|
||||
/// \param event Event
|
||||
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) = 0;
|
||||
|
||||
/// Returns tooltip
|
||||
virtual QString getTooltip() const = 0;
|
||||
|
||||
/// Returns current cursor
|
||||
virtual const std::optional<QCursor>& getCursor() const = 0;
|
||||
|
||||
/// Returns input priority (interfaces with higher priority
|
||||
/// will get input events before interfaces with lower priority)
|
||||
virtual int getInputPriority() const = 0;
|
||||
|
||||
class Comparator
|
||||
{
|
||||
public:
|
||||
explicit constexpr inline Comparator() = default;
|
||||
|
||||
bool operator()(IDrawWidgetInputInterface* left, IDrawWidgetInputInterface* right) const
|
||||
{
|
||||
return std::make_pair(left->getInputPriority(), left) < std::make_pair(right->getInputPriority(), right);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr Comparator getComparator() { return Comparator(); }
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFDOCUMENTDRAWINTERFACE_H
|
@@ -18,16 +18,16 @@
|
||||
#include "pdfdocumenttextflow.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfstructuretree.h"
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfexecutionpolicy.h"
|
||||
#include "pdfconstants.h"
|
||||
#include "pdfcms.h"
|
||||
#include "pdftextlayoutgenerator.h"
|
||||
#include "pdfpagecontentprocessor.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
|
||||
class PDFStructureTreeReferenceCollector : public PDFStructureTreeAbstractVisitor
|
||||
{
|
||||
public:
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,569 +0,0 @@
|
||||
// Copyright (C) 2019-2021 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFDRAWSPACECONTROLLER_H
|
||||
#define PDFDRAWSPACECONTROLLER_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdffont.h"
|
||||
#include "pdfdocumentdrawinterface.h"
|
||||
|
||||
#include <QRectF>
|
||||
#include <QObject>
|
||||
#include <QMarginsF>
|
||||
|
||||
class QPainter;
|
||||
class QScrollBar;
|
||||
class QTimer;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFProgress;
|
||||
class PDFWidget;
|
||||
class PDFCMSManager;
|
||||
class PDFTextLayoutGetter;
|
||||
class PDFWidgetAnnotationManager;
|
||||
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.
|
||||
/// Pages are layouted in zoom-independent mode.
|
||||
class PDFDrawSpaceController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFDrawSpaceController(QObject* parent);
|
||||
virtual ~PDFDrawSpaceController() override;
|
||||
|
||||
/// Sets the document and recalculates the draw space. Document can be nullptr,
|
||||
/// in that case, draw space is cleared. Optional content activity can be nullptr,
|
||||
/// in that case, no content is suppressed.
|
||||
/// \param document Document
|
||||
void setDocument(const PDFModifiedDocument& document);
|
||||
|
||||
/// Sets the page layout. Page layout can be one of the PDF's page layouts.
|
||||
/// \param pageLayout Page layout
|
||||
void setPageLayout(PageLayout pageLayout);
|
||||
|
||||
/// Returns the page layout
|
||||
PageLayout getPageLayout() const { return m_pageLayoutMode; }
|
||||
|
||||
/// Returns the block count
|
||||
size_t getBlockCount() const { return m_blockItems.size(); }
|
||||
|
||||
/// Return the bounding rectangle of the block. If block doesn't exist,
|
||||
/// then invalid rectangle is returned (no exception is thrown).
|
||||
/// \param blockIndex Index of the block
|
||||
QRectF getBlockBoundingRectangle(size_t blockIndex) const;
|
||||
|
||||
/// Represents layouted page. This structure contains index of the block, index of the
|
||||
/// page and page rectangle, in which the page is contained.
|
||||
struct LayoutItem
|
||||
{
|
||||
constexpr inline explicit LayoutItem() : blockIndex(-1), pageIndex(-1), groupIndex(-1) { }
|
||||
constexpr inline explicit LayoutItem(PDFInteger blockIndex, PDFInteger pageIndex, PDFInteger groupIndex, const QRectF& pageRectMM) :
|
||||
blockIndex(blockIndex), pageIndex(pageIndex), groupIndex(groupIndex), pageRectMM(pageRectMM) { }
|
||||
|
||||
bool operator ==(const LayoutItem&) const = default;
|
||||
|
||||
bool isValid() const { return pageIndex != -1; }
|
||||
|
||||
PDFInteger blockIndex;
|
||||
PDFInteger pageIndex;
|
||||
PDFInteger groupIndex; ///< Page group index
|
||||
QRectF pageRectMM;
|
||||
};
|
||||
|
||||
using LayoutItems = std::vector<LayoutItem>;
|
||||
|
||||
/// Returns the layout items for desired block. If block doesn't exist,
|
||||
/// then empty array is returned.
|
||||
/// \param blockIndex Index of the block
|
||||
LayoutItems getLayoutItems(size_t blockIndex) const;
|
||||
|
||||
/// Returns layout for single page. If page index is invalid,
|
||||
/// or page layout cannot be found, then invalid layout item is returned.
|
||||
/// \param pageIndex Page index
|
||||
LayoutItem getLayoutItemForPage(PDFInteger pageIndex) const;
|
||||
|
||||
/// Returns the document
|
||||
const PDFDocument* getDocument() const { return m_document; }
|
||||
|
||||
/// Returns the font cache
|
||||
const PDFFontCache* getFontCache() const { return &m_fontCache; }
|
||||
|
||||
/// Returns the font cache
|
||||
PDFFontCache* getFontCache() { return &m_fontCache; }
|
||||
|
||||
/// Returns optional content activity
|
||||
const PDFOptionalContentActivity* getOptionalContentActivity() const { return m_optionalContentActivity; }
|
||||
|
||||
/// Returns reference bounding box for correct calculation of zoom fit/fit vertical/fit horizontal.
|
||||
/// If zoom is set in a way to display this bounding box on a screen, then it is assured that
|
||||
/// any page on the screen will fit this bounding box, regardless of mode (single/two columns, etc.).
|
||||
QSizeF getReferenceBoundingBox() const;
|
||||
|
||||
/// Returns page rotation
|
||||
PageRotation getPageRotation() const { return m_pageRotation; }
|
||||
|
||||
/// Sets page rotation
|
||||
void setPageRotation(PageRotation pageRotation);
|
||||
|
||||
/// Set custom layout. Custom layout provides a way how to define
|
||||
/// custom page layout, including blocks. Block indices must be properly defined,
|
||||
/// that means block index must start by zero and must be continuous. If this
|
||||
/// criteria are not fulfilled, behaviour is undefined.
|
||||
void setCustomLayout(LayoutItems customLayoutItems);
|
||||
|
||||
/// Returns custom layout
|
||||
const LayoutItems& getCustomLayout() const { return m_customLayoutItems; }
|
||||
|
||||
signals:
|
||||
void drawSpaceChanged();
|
||||
void repaintNeeded();
|
||||
void pageImageChanged(bool all, const std::vector<PDFInteger>& pages);
|
||||
|
||||
private:
|
||||
/// Recalculates the draw space. Preserves setted page rotation.
|
||||
void recalculate();
|
||||
|
||||
/// Clears the draw space. Emits signal if desired.
|
||||
void clear(bool emitSignal);
|
||||
|
||||
/// Represents data for the single block. Contains block size in milimeters.
|
||||
struct LayoutBlock
|
||||
{
|
||||
constexpr inline explicit LayoutBlock() = default;
|
||||
constexpr inline explicit LayoutBlock(const QRectF& blockRectMM) : blockRectMM(blockRectMM) { }
|
||||
|
||||
QRectF blockRectMM;
|
||||
};
|
||||
|
||||
using BlockItems = std::vector<LayoutBlock>;
|
||||
|
||||
const PDFDocument* m_document;
|
||||
const PDFOptionalContentActivity* m_optionalContentActivity;
|
||||
|
||||
PageLayout m_pageLayoutMode;
|
||||
LayoutItems m_layoutItems;
|
||||
BlockItems m_blockItems;
|
||||
PDFReal m_verticalSpacingMM;
|
||||
PDFReal m_horizontalSpacingMM;
|
||||
PageRotation m_pageRotation;
|
||||
LayoutItems m_customLayoutItems;
|
||||
|
||||
/// Font cache
|
||||
PDFFontCache m_fontCache;
|
||||
};
|
||||
|
||||
/// Snapshot for current widget viewable items.
|
||||
struct PDFWidgetSnapshot
|
||||
{
|
||||
struct SnapshotItem
|
||||
{
|
||||
PDFInteger pageIndex = -1; ///< Index of page
|
||||
QRectF rect; ///< Page rectangle on viewport
|
||||
QTransform pageToDeviceMatrix; ///< Transforms page coordinates to widget coordinates
|
||||
const PDFPrecompiledPage* compiledPage = nullptr; ///< Compiled page (can be nullptr)
|
||||
};
|
||||
|
||||
bool hasPage(PDFInteger pageIndex) const { return getPageSnapshot(pageIndex) != nullptr; }
|
||||
const SnapshotItem* getPageSnapshot(PDFInteger pageIndex) const;
|
||||
|
||||
using SnapshotItems = std::vector<SnapshotItem>;
|
||||
SnapshotItems items;
|
||||
};
|
||||
|
||||
/// This is a proxy class to draw space controller using widget. We have two spaces, pixel space
|
||||
/// (on the controlled widget) and device space (device is draw space controller).
|
||||
class PDF4QTLIBCORESHARED_EXPORT PDFDrawWidgetProxy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFDrawWidgetProxy(QObject* parent);
|
||||
virtual ~PDFDrawWidgetProxy() override;
|
||||
|
||||
/// Sets the document and updates the draw space. Document can be nullptr,
|
||||
/// in that case, draw space is cleared. Optional content activity can be nullptr,
|
||||
/// in that case, no content is suppressed.
|
||||
/// \param document Document
|
||||
void setDocument(const PDFModifiedDocument& document);
|
||||
|
||||
void init(PDFWidget* widget);
|
||||
|
||||
/// Updates the draw space area
|
||||
void update();
|
||||
|
||||
/// Creates page point to device point matrix for the given rectangle. It creates transformation
|
||||
/// from page's media box to the target rectangle.
|
||||
/// \param page Page, for which we want to create matrix
|
||||
/// \param rectangle Page rectangle, to which is page media box transformed
|
||||
QTransform createPagePointToDevicePointMatrix(const PDFPage* page, const QRectF& rectangle) const;
|
||||
|
||||
/// Draws the actually visible pages on the painter using the rectangle.
|
||||
/// Rectangle is space in the widget, which is used for painting the PDF.
|
||||
/// This function is using drawPages function to draw all pages. After that,
|
||||
/// custom drawing is performed.
|
||||
/// \sa drawPages
|
||||
/// \param painter Painter to paint the PDF pages
|
||||
/// \param rect Rectangle in which the content is painted
|
||||
void draw(QPainter* painter, QRect rect);
|
||||
|
||||
/// Draws the actually visible pages on the painter using the rectangle.
|
||||
/// Rectangle is space in the widget, which is used for painting the PDF.
|
||||
/// \param painter Painter to paint the PDF pages
|
||||
/// \param rect Rectangle in which the content is painted
|
||||
/// \param features Rendering features
|
||||
void drawPages(QPainter* painter, QRect rect, PDFRenderer::Features features);
|
||||
|
||||
/// Draws thumbnail image of the given size (so larger of the page size
|
||||
/// width or height equals to pixel size and the latter size is rescaled
|
||||
/// using the aspect ratio)
|
||||
/// \param pixelSize Pixel size
|
||||
QImage drawThumbnailImage(PDFInteger pageIndex, int pixelSize) const;
|
||||
|
||||
enum Operation
|
||||
{
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
ZoomFit,
|
||||
ZoomFitWidth,
|
||||
ZoomFitHeight,
|
||||
NavigateDocumentStart,
|
||||
NavigateDocumentEnd,
|
||||
NavigateNextPage,
|
||||
NavigatePreviousPage,
|
||||
NavigateNextStep,
|
||||
NavigatePreviousStep,
|
||||
RotateRight,
|
||||
RotateLeft
|
||||
};
|
||||
|
||||
/// Performs the desired operation (for example navigation).
|
||||
/// \param operation Operation to be performed
|
||||
void performOperation(Operation operation);
|
||||
|
||||
/// Scrolls by pixels, if it is possible. If it is not possible to scroll,
|
||||
/// then nothing happens. Returns pixel offset, by which view camera was moved.
|
||||
/// \param offset Offset in pixels
|
||||
QPoint scrollByPixels(QPoint offset);
|
||||
|
||||
/// Sets the zoom. Tries to preserve current offsets (so the current visible
|
||||
/// area will be visible after the zoom).
|
||||
/// \param zoom New zoom
|
||||
void zoom(PDFReal zoom);
|
||||
|
||||
enum class ZoomHint
|
||||
{
|
||||
Fit,
|
||||
FitWidth,
|
||||
FitHeight
|
||||
};
|
||||
|
||||
/// Calculates zoom using given hint (i.e. to fill whole space, fill vertical,
|
||||
/// or fill horizontal).
|
||||
/// \param hint Zoom hint type
|
||||
PDFReal getZoomHint(ZoomHint hint) const;
|
||||
|
||||
/// Go to the specified page
|
||||
/// \param pageIndex Page to scroll to
|
||||
void goToPage(PDFInteger pageIndex);
|
||||
|
||||
/// Go to the specified page and ensures point on the page is visible
|
||||
/// \param pageIndex Page to scroll to
|
||||
/// \param ensureVisibleRect Rectangle on page, which should be visible
|
||||
void goToPageAndEnsureVisible(PDFInteger pageIndex, QRectF ensureVisibleRect);
|
||||
|
||||
/// Returns current zoom from widget space to device space. So, for example 2.00 corresponds to 200% zoom,
|
||||
/// and each 1 cm of widget area corresponds to 0.5 cm of the device space area.
|
||||
PDFReal getZoom() const { return m_zoom; }
|
||||
|
||||
/// Sets the page layout. Page layout can be one of the PDF's page layouts.
|
||||
/// \param pageLayout Page layout
|
||||
void setPageLayout(PageLayout pageLayout);
|
||||
|
||||
/// Sets custom page layout. If this function is used, page layout mode
|
||||
/// must be set to 'Custom'.
|
||||
/// \param layoutItems Layout items
|
||||
void setCustomPageLayout(PDFDrawSpaceController::LayoutItems layoutItems);
|
||||
|
||||
/// Returns the page layout
|
||||
PageLayout getPageLayout() const { return m_controller->getPageLayout(); }
|
||||
|
||||
/// Returns pages, which are intersecting rectangle (even partially)
|
||||
/// \param rect Rectangle to test
|
||||
std::vector<PDFInteger> getPagesIntersectingRect(QRect rect) const;
|
||||
|
||||
/// Returns sorted vector of page indices, which should remain in the cache
|
||||
std::vector<PDFInteger> getActivePages() const;
|
||||
|
||||
/// Returns page, under which is point. If no page is under the point,
|
||||
/// then -1 is returned. Point is in widget coordinates. If \p pagePoint
|
||||
/// is not nullptr, then point in page coordinate space is set here.
|
||||
/// \param point Point
|
||||
/// \param pagePoint Point in page coordinate system
|
||||
PDFInteger getPageUnderPoint(QPoint point, QPointF* pagePoint) const;
|
||||
|
||||
/// Returns bounding box of pages, which are intersecting rectangle (even partially)
|
||||
/// \param rect Rectangle to test
|
||||
QRect getPagesIntersectingRectBoundingBox(QRect rect) const;
|
||||
|
||||
/// Returns true, if we are in the block mode (multiple blocks with separate pages),
|
||||
/// or continuous mode (single block with continuous list of separated pages).
|
||||
bool isBlockMode() const;
|
||||
|
||||
/// Updates renderer (in current implementation, renderer for page thumbnails)
|
||||
/// using given parameters.
|
||||
/// \param useOpenGL Use OpenGL for rendering?
|
||||
/// \param surfaceFormat Surface format for OpenGL rendering
|
||||
void updateRenderer(bool useOpenGL, const QSurfaceFormat& surfaceFormat);
|
||||
|
||||
/// Prefetches (prerenders) pages after page with pageIndex, i.e., prepares
|
||||
/// for non-flickering scroll operation.
|
||||
void prefetchPages(PDFInteger pageIndex);
|
||||
|
||||
static constexpr PDFReal ZOOM_STEP = 1.2;
|
||||
|
||||
const PDFDocument* getDocument() const { return m_controller->getDocument(); }
|
||||
PDFFontCache* getFontCache() const { return m_controller->getFontCache(); }
|
||||
const PDFOptionalContentActivity* getOptionalContentActivity() const { return m_controller->getOptionalContentActivity(); }
|
||||
PDFRenderer::Features getFeatures() const;
|
||||
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; }
|
||||
PDFAsynchronousTextLayoutCompiler* getTextLayoutCompiler() const { return m_textLayoutCompiler; }
|
||||
PDFWidget* getWidget() const { return m_widget; }
|
||||
bool isUsingOpenGL() const { return m_useOpenGL; }
|
||||
const QSurfaceFormat& getSurfaceFormat() const { return m_surfaceFormat; }
|
||||
PageRotation getPageRotation() const { return m_controller->getPageRotation(); }
|
||||
|
||||
void setFeatures(PDFRenderer::Features features);
|
||||
void setPreferredMeshResolutionRatio(PDFReal ratio);
|
||||
void setMinimalMeshResolutionRatio(PDFReal ratio);
|
||||
void setColorTolerance(PDFReal colorTolerance);
|
||||
|
||||
static constexpr PDFReal getMinZoom() { return MIN_ZOOM; }
|
||||
static constexpr PDFReal getMaxZoom() { return MAX_ZOOM; }
|
||||
|
||||
void registerDrawInterface(IDocumentDrawInterface* drawInterface) { m_drawInterfaces.insert(drawInterface); }
|
||||
void unregisterDrawInterface(IDocumentDrawInterface* drawInterface) { m_drawInterfaces.erase(drawInterface); }
|
||||
|
||||
/// Returns current paper color
|
||||
QColor getPaperColor();
|
||||
|
||||
/// Transforms pixels to device space
|
||||
/// \param pixel Value in pixels
|
||||
PDFReal transformPixelToDeviceSpace(PDFReal pixel) const { return pixel * m_pixelToDeviceSpaceUnit; }
|
||||
|
||||
/// Transforms value in device space to pixel value
|
||||
/// \param deviceSpaceValue Value in device space
|
||||
PDFReal transformDeviceSpaceToPixel(PDFReal deviceSpaceValue) const { return deviceSpaceValue * m_deviceSpaceUnitToPixel; }
|
||||
|
||||
/// Returns snapshot of current view area
|
||||
PDFWidgetSnapshot getSnapshot() const;
|
||||
|
||||
/// Sets page group transparency settings. All pages with a given group index
|
||||
/// will be displayed with this transparency settings.
|
||||
/// \param groupIndex Group index
|
||||
/// \param drawPaper Draw background paper
|
||||
/// \param transparency Page graphics transparency
|
||||
void setGroupTransparency(PDFInteger groupIndex, bool drawPaper = true, PDFReal transparency = 1.0);
|
||||
|
||||
PDFWidgetAnnotationManager* getAnnotationManager() const;
|
||||
|
||||
signals:
|
||||
void drawSpaceChanged();
|
||||
void pageLayoutChanged();
|
||||
void renderingError(pdf::PDFInteger pageIndex, const QList<pdf::PDFRenderError>& errors);
|
||||
void repaintNeeded();
|
||||
void pageImageChanged(bool all, const std::vector<PDFInteger>& pages);
|
||||
void textLayoutChanged();
|
||||
|
||||
private:
|
||||
struct LayoutItem
|
||||
{
|
||||
constexpr inline explicit LayoutItem() : pageIndex(-1), groupIndex(-1) { }
|
||||
constexpr inline explicit LayoutItem(PDFInteger pageIndex, PDFInteger groupIndex, const QRect& pageRect) :
|
||||
pageIndex(pageIndex), groupIndex(groupIndex), pageRect(pageRect) { }
|
||||
|
||||
|
||||
PDFInteger pageIndex;
|
||||
PDFInteger groupIndex; ///< Used to create group of pages (for transparency and overlay)
|
||||
QRect pageRect;
|
||||
};
|
||||
|
||||
struct Layout
|
||||
{
|
||||
inline void clear()
|
||||
{
|
||||
items.clear();
|
||||
blockRect = QRect();
|
||||
}
|
||||
|
||||
std::vector<LayoutItem> items;
|
||||
QRect blockRect;
|
||||
};
|
||||
|
||||
struct GroupInfo
|
||||
{
|
||||
bool operator==(const GroupInfo&) const = default;
|
||||
|
||||
bool drawPaper = true;
|
||||
PDFReal transparency = 1.0;
|
||||
};
|
||||
|
||||
static constexpr size_t INVALID_BLOCK_INDEX = std::numeric_limits<size_t>::max();
|
||||
|
||||
// Minimal/maximal zoom is from 8% to 6400 %, according to the PDF 1.7 Reference,
|
||||
// Appendix C.
|
||||
|
||||
static constexpr PDFReal MIN_ZOOM = 8.0 / 100.0;
|
||||
static constexpr PDFReal MAX_ZOOM = 6400.0 / 100.0;
|
||||
|
||||
static constexpr qint64 CACHE_CLEAR_TIMEOUT = 5000;
|
||||
static constexpr qint64 CACHE_PAGE_EXPIRATION_TIMEOUT = 30000;
|
||||
|
||||
/// Converts rectangle from device space to the pixel space
|
||||
QRectF fromDeviceSpace(const QRectF& rect) const;
|
||||
|
||||
void performPageCacheClear();
|
||||
|
||||
void onTextLayoutChanged();
|
||||
void onOptionalContentGroupStateChanged();
|
||||
void onColorManagementSystemChanged();
|
||||
void onHorizontalScrollbarValueChanged(int value);
|
||||
void onVerticalScrollbarValueChanged(int value);
|
||||
|
||||
void setHorizontalOffset(int value);
|
||||
void setVerticalOffset(int value);
|
||||
void setBlockIndex(int index);
|
||||
|
||||
void updateHorizontalScrollbarFromOffset();
|
||||
void updateVerticalScrollbarFromOffset();
|
||||
|
||||
GroupInfo getGroupInfo(int groupIndex) const;
|
||||
|
||||
template<typename T>
|
||||
struct Range
|
||||
{
|
||||
constexpr inline Range() : min(T()), max(T()) { }
|
||||
constexpr inline Range(T value) : min(value), max(value) { }
|
||||
constexpr inline Range(T min, T max) : min(min), max(max) { }
|
||||
|
||||
T min;
|
||||
T max;
|
||||
|
||||
constexpr inline T bound(T value) { return qBound(min, value, max); }
|
||||
};
|
||||
|
||||
static constexpr bool ENABLE_OPENGL_FOR_THUMBNAILS = false;
|
||||
|
||||
/// Flag, disables the update
|
||||
bool m_updateDisabled;
|
||||
|
||||
/// Current block (in the draw space controller)
|
||||
size_t m_currentBlock;
|
||||
|
||||
/// Number of pixels (fractional) per milimeter (unit is pixel/mm) of the screen,
|
||||
/// so, size of the area in milimeters can be computed as pixelCount * m_pixelPerMM [mm].
|
||||
PDFReal m_pixelPerMM;
|
||||
|
||||
/// Zoom from widget space to device space. So, for example 2.00 corresponds to 200% zoom,
|
||||
/// and each 1 cm of widget area corresponds to 0.5 cm of the device space area.
|
||||
PDFReal m_zoom;
|
||||
|
||||
/// Converts pixel to device space units (mm) using zoom
|
||||
PDFReal m_pixelToDeviceSpaceUnit;
|
||||
|
||||
/// Converts device space units (mm) to real pixels using zoom
|
||||
PDFReal m_deviceSpaceUnitToPixel;
|
||||
|
||||
/// Actual vertical offset of the draw space area in the widget (so block will be drawn
|
||||
/// with this vertical offset)
|
||||
PDFInteger m_verticalOffset;
|
||||
|
||||
/// Range of vertical offset
|
||||
Range<PDFInteger> m_verticalOffsetRange;
|
||||
|
||||
/// Actual horizontal offset of the draw space area in the widget (so block will be drawn
|
||||
/// with this horizontal offset)
|
||||
PDFInteger m_horizontalOffset;
|
||||
|
||||
/// Range for horizontal offset
|
||||
Range<PDFInteger> m_horizontalOffsetRange;
|
||||
|
||||
/// Draw space controller
|
||||
PDFDrawSpaceController* m_controller;
|
||||
|
||||
/// Controlled draw widget (proxy is for this widget)
|
||||
PDFWidget* m_widget;
|
||||
|
||||
/// Vertical scrollbar
|
||||
QScrollBar* m_verticalScrollbar;
|
||||
|
||||
/// Horizontal scrollbar
|
||||
QScrollBar* m_horizontalScrollbar;
|
||||
|
||||
/// Current page layout
|
||||
Layout m_layout;
|
||||
|
||||
/// Renderer features
|
||||
PDFRenderer::Features m_features;
|
||||
|
||||
/// Mesh quality settings
|
||||
PDFMeshQualitySettings m_meshQualitySettings;
|
||||
|
||||
/// Page compiler
|
||||
PDFAsynchronousPageCompiler* m_compiler;
|
||||
|
||||
/// Text layout compiler
|
||||
PDFAsynchronousTextLayoutCompiler* m_textLayoutCompiler;
|
||||
|
||||
/// Page image rasterizer for thumbnails
|
||||
PDFRasterizer* m_rasterizer;
|
||||
|
||||
/// Progress
|
||||
PDFProgress* m_progress;
|
||||
|
||||
/// Cache clear timer
|
||||
QTimer* m_cacheClearTimer;
|
||||
|
||||
/// Additional drawing interfaces
|
||||
std::set<IDocumentDrawInterface*> m_drawInterfaces;
|
||||
|
||||
/// Use OpenGL for rendering?
|
||||
bool m_useOpenGL;
|
||||
|
||||
/// Surface format for OpenGL
|
||||
QSurfaceFormat m_surfaceFormat;
|
||||
|
||||
/// Page group info for rendering. Group of pages
|
||||
/// can be rendered with transparency or without paper
|
||||
/// as overlay.
|
||||
std::map<PDFInteger, GroupInfo> m_groupInfos;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFDRAWSPACECONTROLLER_H
|
@@ -34,7 +34,6 @@
|
||||
#include <QReadWriteLock>
|
||||
#include <QPainterPath>
|
||||
#include <QDataStream>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include "Windows.h"
|
||||
@@ -553,7 +552,7 @@ public:
|
||||
virtual bool isHorizontalWritingSystem() const = 0;
|
||||
|
||||
/// Dumps information about the font
|
||||
virtual void dumpFontToTreeItem(QTreeWidgetItem* item) const { Q_UNUSED(item); }
|
||||
virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const { Q_UNUSED(treeFactory); }
|
||||
|
||||
/// Returns postscript name of the font
|
||||
virtual QString getPostScriptName() const { return QString(); }
|
||||
@@ -592,7 +591,7 @@ public:
|
||||
|
||||
virtual void fillTextSequence(const QByteArray& byteArray, TextSequence& textSequence, PDFRenderErrorReporter* reporter) override;
|
||||
virtual bool isHorizontalWritingSystem() const override { return !m_isVertical; }
|
||||
virtual void dumpFontToTreeItem(QTreeWidgetItem* item) const override;
|
||||
virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const override;
|
||||
virtual QString getPostScriptName() const override { return m_postScriptName; }
|
||||
virtual CharacterInfos getCharacterInfos() const override;
|
||||
|
||||
@@ -883,33 +882,33 @@ CharacterInfos PDFRealizedFontImpl::getCharacterInfos() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void PDFRealizedFontImpl::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
void PDFRealizedFontImpl::dumpFontToTreeItem(ITreeFactory* treeFactory) const
|
||||
{
|
||||
QTreeWidgetItem* root = new QTreeWidgetItem(item, { PDFTranslationContext::tr("Details") });
|
||||
treeFactory->pushItem({ PDFTranslationContext::tr("Details") });
|
||||
|
||||
if (m_face->family_name)
|
||||
{
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Font"), QString::fromLatin1(m_face->family_name) });
|
||||
treeFactory->addItem({ PDFTranslationContext::tr("Font"), QString::fromLatin1(m_face->family_name) });
|
||||
}
|
||||
if (m_face->style_name)
|
||||
{
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Style"), QString::fromLatin1(m_face->style_name) });
|
||||
treeFactory->addItem({ PDFTranslationContext::tr("Style"), QString::fromLatin1(m_face->style_name) });
|
||||
}
|
||||
|
||||
QString yesString = PDFTranslationContext::tr("Yes");
|
||||
QString noString = PDFTranslationContext::tr("No");
|
||||
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Glyph count"), QString::number(m_face->num_glyphs) });
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Is CID keyed"), (m_face->face_flags & FT_FACE_FLAG_CID_KEYED) ? yesString : noString });
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Is bold"), (m_face->style_flags & FT_STYLE_FLAG_BOLD) ? yesString : noString });
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Is italics"), (m_face->style_flags & FT_STYLE_FLAG_ITALIC) ? yesString : noString });
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Has vertical writing system"), (m_face->face_flags & FT_FACE_FLAG_VERTICAL) ? yesString : noString });
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Has SFNT storage scheme"), (m_face->face_flags & FT_FACE_FLAG_SFNT) ? yesString : noString });
|
||||
new QTreeWidgetItem(root, { PDFTranslationContext::tr("Has glyph names"), (m_face->face_flags & FT_FACE_FLAG_GLYPH_NAMES) ? yesString : noString });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Glyph count"), QString::number(m_face->num_glyphs) });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Is CID keyed"), (m_face->face_flags & FT_FACE_FLAG_CID_KEYED) ? yesString : noString });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Is bold"), (m_face->style_flags & FT_STYLE_FLAG_BOLD) ? yesString : noString });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Is italics"), (m_face->style_flags & FT_STYLE_FLAG_ITALIC) ? yesString : noString });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Has vertical writing system"), (m_face->face_flags & FT_FACE_FLAG_VERTICAL) ? yesString : noString });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Has SFNT storage scheme"), (m_face->face_flags & FT_FACE_FLAG_SFNT) ? yesString : noString });
|
||||
treeFactory->addItem( { PDFTranslationContext::tr("Has glyph names"), (m_face->face_flags & FT_FACE_FLAG_GLYPH_NAMES) ? yesString : noString });
|
||||
|
||||
if (m_face->num_charmaps > 0)
|
||||
{
|
||||
QTreeWidgetItem* encodingRoot = new QTreeWidgetItem(item, { PDFTranslationContext::tr("Encoding") });
|
||||
treeFactory->pushItem({ PDFTranslationContext::tr("Encoding") });
|
||||
for (FT_Int i = 0; i < m_face->num_charmaps; ++i)
|
||||
{
|
||||
FT_CharMap charMap = m_face->charmaps[i];
|
||||
@@ -979,9 +978,12 @@ void PDFRealizedFontImpl::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
}
|
||||
|
||||
QString encodingString = PDFTranslationContext::tr("Platform/Encoding = %1 %2").arg(charMap->platform_id).arg(charMap->encoding_id);
|
||||
new QTreeWidgetItem(encodingRoot, { encodingName, encodingString });
|
||||
treeFactory->addItem({ encodingName, encodingString });
|
||||
}
|
||||
treeFactory->popItem();
|
||||
}
|
||||
|
||||
treeFactory->popItem();
|
||||
}
|
||||
|
||||
int PDFRealizedFontImpl::outlineMoveTo(const FT_Vector* to, void* user)
|
||||
@@ -1085,9 +1087,9 @@ bool PDFRealizedFont::isHorizontalWritingSystem() const
|
||||
return m_impl->isHorizontalWritingSystem();
|
||||
}
|
||||
|
||||
void PDFRealizedFont::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
void PDFRealizedFont::dumpFontToTreeItem(ITreeFactory* treeFactory) const
|
||||
{
|
||||
m_impl->dumpFontToTreeItem(item);
|
||||
m_impl->dumpFontToTreeItem(treeFactory);
|
||||
}
|
||||
|
||||
QString PDFRealizedFont::getPostScriptName() const
|
||||
@@ -1885,9 +1887,9 @@ PDFInteger PDFSimpleFont::getGlyphAdvance(size_t index) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PDFSimpleFont::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
void PDFSimpleFont::dumpFontToTreeItem(ITreeFactory* treeFactory) const
|
||||
{
|
||||
BaseClass::dumpFontToTreeItem(item);
|
||||
BaseClass::dumpFontToTreeItem(treeFactory);
|
||||
|
||||
QString encodingTypeString;
|
||||
switch (m_encodingType)
|
||||
@@ -1935,7 +1937,7 @@ void PDFSimpleFont::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
}
|
||||
}
|
||||
|
||||
new QTreeWidgetItem(item, { PDFTranslationContext::tr("Encoding"), encodingTypeString });
|
||||
treeFactory->addItem({ PDFTranslationContext::tr("Encoding"), encodingTypeString });
|
||||
}
|
||||
|
||||
PDFType1Font::PDFType1Font(FontType fontType,
|
||||
@@ -1962,9 +1964,9 @@ FontType PDFType1Font::getFontType() const
|
||||
return m_fontType;
|
||||
}
|
||||
|
||||
void PDFType1Font::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
void PDFType1Font::dumpFontToTreeItem(ITreeFactory* treeFactory) const
|
||||
{
|
||||
BaseClass::dumpFontToTreeItem(item);
|
||||
BaseClass::dumpFontToTreeItem(treeFactory);
|
||||
|
||||
if (m_standardFontType != StandardFontType::Invalid)
|
||||
{
|
||||
@@ -2005,7 +2007,7 @@ void PDFType1Font::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
break;
|
||||
}
|
||||
|
||||
new QTreeWidgetItem(item, { PDFTranslationContext::tr("Standard font"), standardFontTypeString });
|
||||
treeFactory->addItem({ PDFTranslationContext::tr("Standard font"), standardFontTypeString });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2598,9 +2600,9 @@ FontType PDFType3Font::getFontType() const
|
||||
return FontType::Type3;
|
||||
}
|
||||
|
||||
void PDFType3Font::dumpFontToTreeItem(QTreeWidgetItem* item) const
|
||||
void PDFType3Font::dumpFontToTreeItem(ITreeFactory* treeFactory) const
|
||||
{
|
||||
new QTreeWidgetItem(item, { PDFTranslationContext::tr("Character count"), QString::number(m_characterContentStreams.size()) });
|
||||
treeFactory->addItem({ PDFTranslationContext::tr("Character count"), QString::number(m_characterContentStreams.size()) });
|
||||
}
|
||||
|
||||
double PDFType3Font::getWidth(int characterIndex) const
|
||||
|
@@ -30,7 +30,6 @@
|
||||
#include <unordered_map>
|
||||
|
||||
class QPainterPath;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
@@ -56,6 +55,16 @@ enum class TextRenderingMode
|
||||
Clip = 7
|
||||
};
|
||||
|
||||
class ITreeFactory
|
||||
{
|
||||
public:
|
||||
virtual ~ITreeFactory() = default;
|
||||
|
||||
virtual void pushItem(QStringList texts) = 0;
|
||||
virtual void addItem(QStringList texts) = 0;
|
||||
virtual void popItem() = 0;
|
||||
};
|
||||
|
||||
/// Item of the text sequence (either single character, or advance)
|
||||
struct TextSequenceItem
|
||||
{
|
||||
@@ -261,7 +270,7 @@ public:
|
||||
bool isHorizontalWritingSystem() const;
|
||||
|
||||
/// Adds information about the font into tree item
|
||||
void dumpFontToTreeItem(QTreeWidgetItem* item) const;
|
||||
void dumpFontToTreeItem(ITreeFactory* treeFactory) const;
|
||||
|
||||
/// Returns postscript name of the font
|
||||
QString getPostScriptName() const;
|
||||
@@ -303,7 +312,7 @@ public:
|
||||
const CIDSystemInfo* getCIDSystemInfo() const { return &m_CIDSystemInfo; }
|
||||
|
||||
/// Adds information about the font into tree item
|
||||
virtual void dumpFontToTreeItem(QTreeWidgetItem* item) const { Q_UNUSED(item); }
|
||||
virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const { Q_UNUSED(treeFactory); }
|
||||
|
||||
/// Creates font from the object. If font can't be created, exception is thrown.
|
||||
/// \param object Font dictionary
|
||||
@@ -351,7 +360,7 @@ public:
|
||||
/// Returns the glyph advance (or zero, if glyph advance is invalid)
|
||||
PDFInteger getGlyphAdvance(size_t index) const;
|
||||
|
||||
virtual void dumpFontToTreeItem(QTreeWidgetItem* item) const override;
|
||||
virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const override;
|
||||
|
||||
protected:
|
||||
QByteArray m_name;
|
||||
@@ -384,7 +393,7 @@ public:
|
||||
virtual ~PDFType1Font() override = default;
|
||||
|
||||
virtual FontType getFontType() const override;
|
||||
virtual void dumpFontToTreeItem(QTreeWidgetItem*item) const override;
|
||||
virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const override;
|
||||
|
||||
/// Returns the assigned standard font (or invalid, if font is not standard)
|
||||
StandardFontType getStandardFontType() const { return m_standardFontType; }
|
||||
@@ -598,7 +607,7 @@ public:
|
||||
PDFFontCMap toUnicode);
|
||||
|
||||
virtual FontType getFontType() const override;
|
||||
virtual void dumpFontToTreeItem(QTreeWidgetItem*item) const override;
|
||||
virtual void dumpFontToTreeItem(ITreeFactory* treeFactory) const override;
|
||||
virtual const PDFFontCMap* getToUnicode() const override { return &m_toUnicode; }
|
||||
|
||||
/// Returns width of the character. If character doesn't exist, then zero is returned.
|
||||
|
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "pdfform.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfdocumentbuilder.h"
|
||||
#include "pdfpainterutils.h"
|
||||
#include "pdfdbgheap.h"
|
||||
@@ -912,6 +911,19 @@ bool PDFFormManager::isEditorDrawEnabled(const PDFObjectReference& reference) co
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PDFFormManager::isEditorDrawEnabled(const PDFFormField* formField) const
|
||||
{
|
||||
Q_UNUSED(formField);
|
||||
return false;
|
||||
}
|
||||
|
||||
void PDFFormManager::drawFormField(const PDFFormField* formField, AnnotationDrawParameters& parameters, bool edit) const
|
||||
{
|
||||
Q_UNUSED(formField);
|
||||
Q_UNUSED(parameters);
|
||||
Q_UNUSED(edit);
|
||||
}
|
||||
|
||||
void PDFFormManager::updateFieldValues()
|
||||
{
|
||||
if (m_document)
|
||||
|
@@ -21,7 +21,6 @@
|
||||
#include "pdfobject.h"
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfannotation.h"
|
||||
#include "pdfdocumentdrawinterface.h"
|
||||
#include "pdfsignaturehandler.h"
|
||||
#include "pdfxfaengine.h"
|
||||
|
||||
|
@@ -23,8 +23,8 @@
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdfpagecontentprocessor.h"
|
||||
#include "pdftextlayout.h"
|
||||
#include "pdfsnapper.h"
|
||||
#include "pdfcolorconvertor.h"
|
||||
#include "pdfsnapper.h"
|
||||
|
||||
#include <QPen>
|
||||
#include <QBrush>
|
||||
@@ -370,13 +370,13 @@ class PDF4QTLIBCORESHARED_EXPORT PDFPrecompiledPageGenerator : public PDFPainter
|
||||
|
||||
public:
|
||||
explicit PDFPrecompiledPageGenerator(PDFPrecompiledPage* precompiledPage,
|
||||
PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFCMS* cms,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
const PDFMeshQualitySettings& meshQualitySettings);
|
||||
PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFCMS* cms,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
const PDFMeshQualitySettings& meshQualitySettings);
|
||||
|
||||
protected:
|
||||
virtual void performPathPainting(const QPainterPath& path, bool stroke, bool fill, bool text, Qt::FillRule fillRule) override;
|
||||
|
@@ -16,9 +16,10 @@
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfsnapper.h"
|
||||
#include "pdfcompiler.h"
|
||||
#include "pdfdrawspacecontroller.h"
|
||||
#include "pdfutils.h"
|
||||
#include "pdfdbgheap.h"
|
||||
#include "pdfpainter.h"
|
||||
#include "pdfwidgetsnapshot.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
|
71
Pdf4QtLibCore/sources/pdftextlayoutgenerator.cpp
Normal file
71
Pdf4QtLibCore/sources/pdftextlayoutgenerator.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2023 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdftextlayoutgenerator.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFTextLayout PDFTextLayoutGenerator::createTextLayout()
|
||||
{
|
||||
m_textLayout.perform();
|
||||
m_textLayout.optimize();
|
||||
return qMove(m_textLayout);
|
||||
}
|
||||
|
||||
bool PDFTextLayoutGenerator::isContentSuppressedByOC(PDFObjectReference ocgOrOcmd)
|
||||
{
|
||||
if (m_features.testFlag(PDFRenderer::IgnoreOptionalContent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return PDFPageContentProcessor::isContentSuppressedByOC(ocgOrOcmd);
|
||||
}
|
||||
|
||||
bool PDFTextLayoutGenerator::isContentKindSuppressed(ContentKind kind) const
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ContentKind::Shapes:
|
||||
case ContentKind::Text:
|
||||
case ContentKind::Images:
|
||||
case ContentKind::Shading:
|
||||
return true;
|
||||
|
||||
case ContentKind::Tiling:
|
||||
return false; // Tiling can have text
|
||||
|
||||
default:
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PDFTextLayoutGenerator::performOutputCharacter(const PDFTextCharacterInfo& info)
|
||||
{
|
||||
if (!isContentSuppressed() && !info.character.isSpace())
|
||||
{
|
||||
m_textLayout.addCharacter(info);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pdf
|
55
Pdf4QtLibCore/sources/pdftextlayoutgenerator.h
Normal file
55
Pdf4QtLibCore/sources/pdftextlayoutgenerator.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2023 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfpagecontentprocessor.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
class PDFTextLayoutGenerator : public PDFPageContentProcessor
|
||||
{
|
||||
using BaseClass = PDFPageContentProcessor;
|
||||
|
||||
public:
|
||||
explicit PDFTextLayoutGenerator(PDFRenderer::Features features,
|
||||
const PDFPage* page,
|
||||
const PDFDocument* document,
|
||||
const PDFFontCache* fontCache,
|
||||
const PDFCMS* cms,
|
||||
const PDFOptionalContentActivity* optionalContentActivity,
|
||||
QTransform pagePointToDevicePointMatrix,
|
||||
const PDFMeshQualitySettings& meshQualitySettings) :
|
||||
BaseClass(page, document, fontCache, cms, optionalContentActivity, pagePointToDevicePointMatrix, meshQualitySettings),
|
||||
m_features(features)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Creates text layout from the text
|
||||
PDFTextLayout createTextLayout();
|
||||
|
||||
protected:
|
||||
virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) override;
|
||||
virtual bool isContentKindSuppressed(ContentKind kind) const override;
|
||||
virtual void performOutputCharacter(const PDFTextCharacterInfo& info) override;
|
||||
|
||||
private:
|
||||
PDFRenderer::Features m_features;
|
||||
PDFTextLayout m_textLayout;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
34
Pdf4QtLibCore/sources/pdfwidgetsnapshot.cpp
Normal file
34
Pdf4QtLibCore/sources/pdfwidgetsnapshot.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2023 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfwidgetsnapshot.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
const PDFWidgetSnapshot::SnapshotItem* PDFWidgetSnapshot::getPageSnapshot(PDFInteger pageIndex) const
|
||||
{
|
||||
auto it = std::find_if(items.cbegin(), items.cend(), [pageIndex](const auto& item) { return item.pageIndex == pageIndex; });
|
||||
if (it != items.cend())
|
||||
{
|
||||
return &*it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
45
Pdf4QtLibCore/sources/pdfwidgetsnapshot.h
Normal file
45
Pdf4QtLibCore/sources/pdfwidgetsnapshot.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2023 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfglobal.h"
|
||||
|
||||
#include <QRectF>
|
||||
#include <QTransform>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFPrecompiledPage;
|
||||
|
||||
/// Snapshot for current widget viewable items.
|
||||
struct PDFWidgetSnapshot
|
||||
{
|
||||
struct SnapshotItem
|
||||
{
|
||||
PDFInteger pageIndex = -1; ///< Index of page
|
||||
QRectF rect; ///< Page rectangle on viewport
|
||||
QTransform pageToDeviceMatrix; ///< Transforms page coordinates to widget coordinates
|
||||
const PDFPrecompiledPage* compiledPage = nullptr; ///< Compiled page (can be nullptr)
|
||||
};
|
||||
|
||||
bool hasPage(PDFInteger pageIndex) const { return getPageSnapshot(pageIndex) != nullptr; }
|
||||
const SnapshotItem* getPageSnapshot(PDFInteger pageIndex) const;
|
||||
|
||||
using SnapshotItems = std::vector<SnapshotItem>;
|
||||
SnapshotItems items;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
Reference in New Issue
Block a user