Refactoring - prepare for undo/redo and document modification

This commit is contained in:
Jakub Melka
2020-04-25 14:21:06 +02:00
parent 76af397b07
commit 57b3711210
28 changed files with 318 additions and 114 deletions

View File

@ -1185,13 +1185,17 @@ void PDFAnnotationManager::drawPage(QPainter* painter,
} }
} }
void PDFAnnotationManager::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity) void PDFAnnotationManager::setDocument(const PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {
m_document = document; m_document = document;
m_optionalActivity = optionalContentActivity; m_optionalActivity = document.getOptionalContentActivity();
m_pageAnnotations.clear();
if (document.hasReset() || document.hasFlag(PDFModifiedDocument::Annotation))
{
m_pageAnnotations.clear();
}
} }
} }

View File

@ -44,6 +44,7 @@ class PDFObjectStorage;
class PDFDrawWidgetProxy; class PDFDrawWidgetProxy;
class PDFFontCache; class PDFFontCache;
class PDFFormManager; class PDFFormManager;
class PDFModifiedDocument;
class PDFOptionalContentActivity; class PDFOptionalContentActivity;
using TextAlignment = Qt::Alignment; using TextAlignment = Qt::Alignment;
@ -1287,7 +1288,9 @@ public:
const QMatrix& pagePointToDevicePointMatrix, const QMatrix& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override; QList<PDFRenderError>& errors) const override;
void setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity); /// Set document
/// \param document New document
void setDocument(const PDFModifiedDocument& document);
Target getTarget() const; Target getTarget() const;
void setTarget(Target target); void setTarget(Target target);

View File

@ -57,7 +57,7 @@ void PDFAsynchronousPageCompiler::start()
} }
} }
void PDFAsynchronousPageCompiler::stop() void PDFAsynchronousPageCompiler::stop(bool clearCache)
{ {
switch (m_state) switch (m_state)
{ {
@ -75,7 +75,11 @@ void PDFAsynchronousPageCompiler::stop()
taskItem.second.taskWatcher->waitForFinished(); taskItem.second.taskWatcher->waitForFinished();
} }
m_tasks.clear(); m_tasks.clear();
m_cache.clear();
if (clearCache)
{
m_cache.clear();
}
m_state = State::Inactive; m_state = State::Inactive;
break; break;
@ -92,7 +96,7 @@ void PDFAsynchronousPageCompiler::stop()
void PDFAsynchronousPageCompiler::reset() void PDFAsynchronousPageCompiler::reset()
{ {
stop(); stop(true);
start(); start();
} }
@ -291,7 +295,7 @@ void PDFAsynchronousTextLayoutCompiler::start()
} }
} }
void PDFAsynchronousTextLayoutCompiler::stop() void PDFAsynchronousTextLayoutCompiler::stop(bool clearCache)
{ {
switch (m_state) switch (m_state)
{ {
@ -303,7 +307,12 @@ void PDFAsynchronousTextLayoutCompiler::stop()
// Stop the engine // Stop the engine
m_state = State::Stopping; m_state = State::Stopping;
m_textLayoutCompileFutureWatcher.waitForFinished(); m_textLayoutCompileFutureWatcher.waitForFinished();
m_textLayouts = std::nullopt;
if (clearCache)
{
m_textLayouts = std::nullopt;
}
m_state = State::Inactive; m_state = State::Inactive;
break; break;
} }
@ -319,7 +328,7 @@ void PDFAsynchronousTextLayoutCompiler::stop()
void PDFAsynchronousTextLayoutCompiler::reset() void PDFAsynchronousTextLayoutCompiler::reset()
{ {
stop(); stop(true);
start(); start();
} }

View File

@ -48,8 +48,13 @@ public:
void start(); void start();
/// Stops the engine and all underlying asynchronous tasks. Also /// Stops the engine and all underlying asynchronous tasks. Also
/// clears the cache. Call this function only if engine is active. /// clears the cache if needed. Call this function only if engine is active.
void stop(); /// 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. /// Resets the engine - calls stop and then calls start.
void reset(); void reset();
@ -107,8 +112,12 @@ public:
void start(); void start();
/// Stops the engine and all underlying asynchronous tasks. Also /// Stops the engine and all underlying asynchronous tasks. Also
/// clears the cache. Call this function only if engine is active. /// clears the cache if parameter \p clearCache. Call this function
void stop(); /// 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. /// Resets the engine - calls stop and then calls start.
void reset(); void reset();

View File

@ -479,6 +479,59 @@ private:
PDFCatalog m_catalog; PDFCatalog m_catalog;
}; };
/// Helper class for document updates (for example, add/delete annotations,
/// fill form fields, do some other minor operations) and also for major
/// updates (document reset). It also contains modification flags, which are
/// hints for update operations (for example, if only annotations are changed,
/// then rebuilding outline tree is not needed). At least one of the flags
/// must be set. Reset flag is conservative, so it should be set, when document
/// changes are not known.
class PDFModifiedDocument
{
public:
enum ModificationFlag
{
None = 0x0000, ///< No flag
Reset = 0x0001, ///< Whole document content is changed (for example, new document is being set)
Annotation = 0x0002, ///< Annotations changed
FormField = 0x0004, ///< Form field content changed
};
Q_DECLARE_FLAGS(ModificationFlags, ModificationFlag)
explicit inline PDFModifiedDocument() = default;
explicit inline PDFModifiedDocument(PDFDocument* document, PDFOptionalContentActivity* optionalContentActivity) :
m_document(document),
m_optionalContentActivity(optionalContentActivity),
m_flags(Reset)
{
Q_ASSERT(document);
}
explicit inline PDFModifiedDocument(PDFDocument* document, PDFOptionalContentActivity* optionalContentActivity, ModificationFlags flags) :
m_document(document),
m_optionalContentActivity(optionalContentActivity),
m_flags(flags)
{
Q_ASSERT(document);
}
PDFDocument* getDocument() const { return m_document; }
PDFOptionalContentActivity* getOptionalContentActivity() const { return m_optionalContentActivity; }
void setOptionalContentActivity(PDFOptionalContentActivity* optionalContentActivity) { m_optionalContentActivity = optionalContentActivity; }
bool hasReset() const { return m_flags.testFlag(Reset); }
bool hasFlag(ModificationFlag flag) const { return m_flags.testFlag(flag); }
operator PDFDocument*() const { return m_document; }
private:
PDFDocument* m_document = nullptr;
PDFOptionalContentActivity* m_optionalContentActivity = nullptr;
ModificationFlags m_flags = Reset;
};
// Implementation // Implementation
inline inline

View File

@ -49,14 +49,20 @@ PDFDrawSpaceController::~PDFDrawSpaceController()
} }
void PDFDrawSpaceController::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity) void PDFDrawSpaceController::setDocument(const PDFModifiedDocument& document)
{ {
if (document != m_document) if (document != m_document)
{ {
m_document = document; m_document = document;
m_fontCache.setDocument(document); m_fontCache.setDocument(document);
m_optionalContentActivity = optionalContentActivity; m_optionalContentActivity = document.getOptionalContentActivity();
recalculate();
// If document is not being reset, then recalculation is not needed,
// pages should remain the same.
if (document.hasReset())
{
recalculate();
}
} }
} }
@ -411,14 +417,22 @@ PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
} }
void PDFDrawWidgetProxy::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity) void PDFDrawWidgetProxy::setDocument(const PDFModifiedDocument& document)
{ {
m_compiler->stop(); if (getDocument() != document)
m_textLayoutCompiler->stop(); {
m_controller->setDocument(document, optionalContentActivity); m_compiler->stop(document.hasReset());
connect(optionalContentActivity, &PDFOptionalContentActivity::optionalContentGroupStateChanged, this, &PDFDrawWidgetProxy::onOptionalContentGroupStateChanged); m_textLayoutCompiler->stop(document.hasReset());
m_compiler->start(); m_controller->setDocument(document);
m_textLayoutCompiler->start();
if (PDFOptionalContentActivity* optionalContentActivity = document.getOptionalContentActivity())
{
connect(optionalContentActivity, &PDFOptionalContentActivity::optionalContentGroupStateChanged, this, &PDFDrawWidgetProxy::onOptionalContentGroupStateChanged, Qt::UniqueConnection);
}
m_compiler->start();
m_textLayoutCompiler->start();
}
} }
void PDFDrawWidgetProxy::init(PDFWidget* widget) void PDFDrawWidgetProxy::init(PDFWidget* widget)
@ -1312,8 +1326,8 @@ void PDFDrawWidgetProxy::setFeatures(PDFRenderer::Features features)
{ {
if (m_features != features) if (m_features != features)
{ {
m_compiler->stop(); m_compiler->stop(true);
m_textLayoutCompiler->stop(); m_textLayoutCompiler->stop(true);
m_features = features; m_features = features;
m_compiler->start(); m_compiler->start();
m_textLayoutCompiler->start(); m_textLayoutCompiler->start();
@ -1325,7 +1339,7 @@ void PDFDrawWidgetProxy::setPreferredMeshResolutionRatio(PDFReal ratio)
{ {
if (m_meshQualitySettings.preferredMeshResolutionRatio != ratio) if (m_meshQualitySettings.preferredMeshResolutionRatio != ratio)
{ {
m_compiler->stop(); m_compiler->stop(true);
m_meshQualitySettings.preferredMeshResolutionRatio = ratio; m_meshQualitySettings.preferredMeshResolutionRatio = ratio;
m_compiler->start(); m_compiler->start();
emit pageImageChanged(true, { }); emit pageImageChanged(true, { });
@ -1336,7 +1350,7 @@ void PDFDrawWidgetProxy::setMinimalMeshResolutionRatio(PDFReal ratio)
{ {
if (m_meshQualitySettings.minimalMeshResolutionRatio != ratio) if (m_meshQualitySettings.minimalMeshResolutionRatio != ratio)
{ {
m_compiler->stop(); m_compiler->stop(true);
m_meshQualitySettings.minimalMeshResolutionRatio = ratio; m_meshQualitySettings.minimalMeshResolutionRatio = ratio;
m_compiler->start(); m_compiler->start();
emit pageImageChanged(true, { }); emit pageImageChanged(true, { });
@ -1347,7 +1361,7 @@ void PDFDrawWidgetProxy::setColorTolerance(PDFReal colorTolerance)
{ {
if (m_meshQualitySettings.tolerance != colorTolerance) if (m_meshQualitySettings.tolerance != colorTolerance)
{ {
m_compiler->stop(); m_compiler->stop(true);
m_meshQualitySettings.tolerance = colorTolerance; m_meshQualitySettings.tolerance = colorTolerance;
m_compiler->start(); m_compiler->start();
emit pageImageChanged(true, { }); emit pageImageChanged(true, { });

View File

@ -56,8 +56,7 @@ public:
/// in that case, draw space is cleared. Optional content activity can be nullptr, /// in that case, draw space is cleared. Optional content activity can be nullptr,
/// in that case, no content is suppressed. /// in that case, no content is suppressed.
/// \param document Document /// \param document Document
/// \param optionalContentActivity Optional content activity void setDocument(const PDFModifiedDocument& document);
void setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity);
/// Sets the page layout. Page layout can be one of the PDF's page layouts. /// Sets the page layout. Page layout can be one of the PDF's page layouts.
/// \param pageLayout Page layout /// \param pageLayout Page layout
@ -193,8 +192,7 @@ public:
/// in that case, draw space is cleared. Optional content activity can be nullptr, /// in that case, draw space is cleared. Optional content activity can be nullptr,
/// in that case, no content is suppressed. /// in that case, no content is suppressed.
/// \param document Document /// \param document Document
/// \param optionalContentActivity Optional content activity void setDocument(const PDFModifiedDocument& document);
void setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity);
void init(PDFWidget* widget); void init(PDFWidget* widget);

View File

@ -66,9 +66,9 @@ PDFWidget::~PDFWidget()
} }
void PDFWidget::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity) void PDFWidget::setDocument(const PDFModifiedDocument& document)
{ {
m_proxy->setDocument(document, optionalContentActivity); m_proxy->setDocument(document);
m_pageRenderingErrors.clear(); m_pageRenderingErrors.clear();
} }

View File

@ -32,6 +32,7 @@ class PDFCMSManager;
class PDFToolManager; class PDFToolManager;
class PDFDrawWidget; class PDFDrawWidget;
class PDFDrawWidgetProxy; class PDFDrawWidgetProxy;
class PDFModifiedDocument;
class PDFWidgetAnnotationManager; class PDFWidgetAnnotationManager;
class IDrawWidgetInputInterface; class IDrawWidgetInputInterface;
@ -64,8 +65,7 @@ public:
/// in that case, widget contents are cleared. Optional content activity can be nullptr, /// in that case, widget contents are cleared. Optional content activity can be nullptr,
/// if this occurs, no content is suppressed. /// if this occurs, no content is suppressed.
/// \param document Document /// \param document Document
/// \param optionalContentActivity Optional content activity void setDocument(const PDFModifiedDocument& document);
void setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity);
/// Update rendering engine according the settings /// Update rendering engine according the settings
/// \param engine Engine type /// \param engine Engine type

View File

@ -1591,14 +1591,20 @@ FontType PDFTrueTypeFont::getFontType() const
return FontType::TrueType; return FontType::TrueType;
} }
void PDFFontCache::setDocument(const PDFDocument* document) void PDFFontCache::setDocument(const PDFModifiedDocument& document)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
if (m_document != document) if (m_document != document)
{ {
m_document = document; m_document = document;
m_fontCache.clear();
m_realizedFontCache.clear(); // Jakub Melka: If document has not reset flag, then fonts of the
// document remains the same. So it is not needed to clear font cache.
if (document.hasReset())
{
m_fontCache.clear();
m_realizedFontCache.clear();
}
} }
} }

View File

@ -35,6 +35,7 @@ class QPainterPath;
namespace pdf namespace pdf
{ {
class PDFDocument; class PDFDocument;
class PDFModifiedDocument;
class PDFRenderErrorReporter; class PDFRenderErrorReporter;
using CID = unsigned int; using CID = unsigned int;
@ -396,9 +397,10 @@ public:
} }
/// Sets the document to the cache. Whole cache is cleared. /// Sets the document to the cache. Whole cache is cleared,
/// if it is needed.
/// \param document Document to be setted /// \param document Document to be setted
void setDocument(const PDFDocument* document); void setDocument(const PDFModifiedDocument& document);
/// Retrieves font from the cache. If font can't be accessed or created, /// Retrieves font from the cache. If font can't be accessed or created,
/// then exception is thrown. /// then exception is thrown.

View File

@ -69,6 +69,21 @@ void PDFFormField::fillWidgetToFormFieldMapping(PDFWidgetToFormFieldMapping& map
} }
} }
void PDFFormField::reloadValue(const PDFObjectStorage* storage, PDFObject parentValue)
{
Q_ASSERT(storage);
if (const PDFDictionary* fieldDictionary = storage->getDictionaryFromObject(storage->getObjectByReference(getSelfReference())))
{
m_value = fieldDictionary->hasKey("V") ? fieldDictionary->get("V") : parentValue;
}
for (const PDFFormFieldPointer& childField : m_childFields)
{
childField->reloadValue(storage, m_value);
}
}
PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField) PDFFormFieldPointer PDFFormField::parse(const PDFObjectStorage* storage, PDFObjectReference reference, PDFFormField* parentField)
{ {
PDFFormFieldPointer result; PDFFormFieldPointer result;
@ -308,23 +323,31 @@ const PDFDocument* PDFFormManager::getDocument() const
return m_document; return m_document;
} }
void PDFFormManager::setDocument(const PDFDocument* document) void PDFFormManager::setDocument(const PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {
m_document = document; m_document = document;
if (m_document) if (document.hasReset())
{ {
m_form = PDFForm::parse(&m_document->getStorage(), m_document->getCatalog()->getFormObject()); if (m_document)
} {
else m_form = PDFForm::parse(&m_document->getStorage(), m_document->getCatalog()->getFormObject());
{ }
// Clean the form else
m_form = PDFForm(); {
} // Clean the form
m_form = PDFForm();
}
updateWidgetToFormFieldMapping(); updateWidgetToFormFieldMapping();
}
else if (document.hasFlag(PDFModifiedDocument::FormField))
{
// Just update field values
updateFieldValues();
}
} }
} }
@ -338,6 +361,17 @@ void PDFFormManager::setAppearanceFlags(FormAppearanceFlags flags)
m_flags = flags; m_flags = flags;
} }
void PDFFormManager::updateFieldValues()
{
if (m_document)
{
for (const PDFFormFieldPointer& childField : m_form.getFormFields())
{
childField->reloadValue(&m_document->getStorage(), PDFObject());
}
}
}
void PDFFormManager::updateWidgetToFormFieldMapping() void PDFFormManager::updateWidgetToFormFieldMapping()
{ {
m_widgetToFormField.clear(); m_widgetToFormField.clear();

View File

@ -25,9 +25,9 @@
namespace pdf namespace pdf
{ {
class PDFObjectStorage;
class PDFFormField; class PDFFormField;
class PDFObjectStorage;
class PDFModifiedDocument;
using PDFFormFieldPointer = QSharedPointer<PDFFormField>; using PDFFormFieldPointer = QSharedPointer<PDFFormField>;
using PDFFormFields = std::vector<PDFFormFieldPointer>; using PDFFormFields = std::vector<PDFFormFieldPointer>;
@ -187,6 +187,9 @@ public:
/// \param mapping Form field mapping /// \param mapping Form field mapping
void fillWidgetToFormFieldMapping(PDFWidgetToFormFieldMapping& mapping); void fillWidgetToFormFieldMapping(PDFWidgetToFormFieldMapping& mapping);
/// Reloads value from object storage. Actually stored value is lost.
void reloadValue(const PDFObjectStorage* storage, PDFObject parentValue);
/// Parses form field from the object reference. If some error occurs /// Parses form field from the object reference. If some error occurs
/// then null pointer is returned, no exception is thrown. /// then null pointer is returned, no exception is thrown.
/// \param storage Storage /// \param storage Storage
@ -378,7 +381,7 @@ public:
void setAnnotationManager(PDFAnnotationManager* annotationManager); void setAnnotationManager(PDFAnnotationManager* annotationManager);
const PDFDocument* getDocument() const; const PDFDocument* getDocument() const;
void setDocument(const PDFDocument* document); void setDocument(const PDFModifiedDocument& document);
/// Returns default form apperance flags /// Returns default form apperance flags
static constexpr FormAppearanceFlags getDefaultApperanceFlags() { return HighlightFields | HighlightRequiredFields; } static constexpr FormAppearanceFlags getDefaultApperanceFlags() { return HighlightFields | HighlightRequiredFields; }
@ -387,6 +390,7 @@ public:
void setAppearanceFlags(FormAppearanceFlags flags); void setAppearanceFlags(FormAppearanceFlags flags);
private: private:
void updateFieldValues();
void updateWidgetToFormFieldMapping(); void updateWidgetToFormFieldMapping();
PDFDrawWidgetProxy* m_proxy; PDFDrawWidgetProxy* m_proxy;

View File

@ -40,12 +40,18 @@ PDFTreeItemModel::PDFTreeItemModel(QObject* parent) :
} }
void PDFTreeItemModel::setDocument(const PDFDocument* document) void PDFTreeItemModel::setDocument(const PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {
m_document = document; m_document = document;
update();
// We must only update only, when document has been reset, i.e.
// all document content has been changed.
if (document.hasReset())
{
update();
}
} }
} }
@ -727,20 +733,29 @@ void PDFThumbnailsItemModel::setThumbnailsSize(int size)
} }
} }
void PDFThumbnailsItemModel::setDocument(const PDFDocument* document) void PDFThumbnailsItemModel::setDocument(const PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {
beginResetModel(); if (document.hasReset() || document.hasFlag(PDFModifiedDocument::Annotation))
m_thumbnailCache.clear();
m_document = document;
m_pageCount = 0;
if (m_document)
{ {
m_pageCount = static_cast<int>(m_document->getCatalog()->getPageCount()); beginResetModel();
m_thumbnailCache.clear();
m_document = document;
m_pageCount = 0;
if (m_document)
{
m_pageCount = static_cast<int>(m_document->getCatalog()->getPageCount());
}
endResetModel();
}
else
{
// Soft reset
m_document = document;
Q_ASSERT(!m_document || m_pageCount == static_cast<int>(m_document->getCatalog()->getPageCount()));
} }
endResetModel();
} }
} }
@ -778,4 +793,19 @@ QString PDFThumbnailsItemModel::getKey(int pageIndex) const
return QString("PDF_THUMBNAIL_%1").arg(pageIndex); return QString("PDF_THUMBNAIL_%1").arg(pageIndex);
} }
PDFAttachmentsTreeItem::PDFAttachmentsTreeItem(PDFAttachmentsTreeItem* parent, QIcon icon, QString title, QString description, const PDFFileSpecification* fileSpecification) :
PDFTreeItem(parent),
m_icon(qMove(icon)),
m_title(qMove(title)),
m_description(qMove(description)),
m_fileSpecification(std::make_unique<PDFFileSpecification>(*fileSpecification))
{
}
PDFAttachmentsTreeItem::~PDFAttachmentsTreeItem()
{
}
} // namespace pdf } // namespace pdf

View File

@ -30,6 +30,7 @@ namespace pdf
class PDFAction; class PDFAction;
class PDFDocument; class PDFDocument;
class PDFOutlineItem; class PDFOutlineItem;
class PDFModifiedDocument;
class PDFFileSpecification; class PDFFileSpecification;
class PDFOptionalContentActivity; class PDFOptionalContentActivity;
class PDFDrawWidgetProxy; class PDFDrawWidgetProxy;
@ -66,13 +67,15 @@ private:
QList<PDFTreeItem*> m_children; QList<PDFTreeItem*> m_children;
}; };
/// Root of all tree item models /// Root of all tree item models. Reimplementations of this model
/// must handle "soft" document updates, such as only annotations changed etc.
/// Model should be rebuilded only, if it is neccessary.
class PDFFORQTLIBSHARED_EXPORT PDFTreeItemModel : public QAbstractItemModel class PDFFORQTLIBSHARED_EXPORT PDFTreeItemModel : public QAbstractItemModel
{ {
public: public:
explicit PDFTreeItemModel(QObject* parent); explicit PDFTreeItemModel(QObject* parent);
void setDocument(const pdf::PDFDocument* document); void setDocument(const PDFModifiedDocument& document);
bool isEmpty() const; bool isEmpty() const;
@ -172,26 +175,19 @@ private:
class PDFAttachmentsTreeItem : public PDFTreeItem class PDFAttachmentsTreeItem : public PDFTreeItem
{ {
public: public:
explicit PDFAttachmentsTreeItem(PDFAttachmentsTreeItem* parent, QIcon icon, QString title, QString description, const PDFFileSpecification* fileSpecification) : explicit PDFAttachmentsTreeItem(PDFAttachmentsTreeItem* parent, QIcon icon, QString title, QString description, const PDFFileSpecification* fileSpecification);
PDFTreeItem(parent), virtual ~PDFAttachmentsTreeItem() override;
m_icon(qMove(icon)),
m_title(qMove(title)),
m_description(qMove(description)),
m_fileSpecification(fileSpecification)
{
}
const QIcon& getIcon() const { return m_icon; } const QIcon& getIcon() const { return m_icon; }
const QString& getTitle() const { return m_title; } const QString& getTitle() const { return m_title; }
const QString& getDescription() const { return m_description; } const QString& getDescription() const { return m_description; }
const PDFFileSpecification* getFileSpecification() const { return m_fileSpecification; } const PDFFileSpecification* getFileSpecification() const { return m_fileSpecification.get(); }
private: private:
QIcon m_icon; QIcon m_icon;
QString m_title; QString m_title;
QString m_description; QString m_description;
const PDFFileSpecification* m_fileSpecification; std::unique_ptr<PDFFileSpecification> m_fileSpecification;
}; };
class PDFFORQTLIBSHARED_EXPORT PDFAttachmentsTreeItemModel : public PDFTreeItemModel class PDFFORQTLIBSHARED_EXPORT PDFAttachmentsTreeItemModel : public PDFTreeItemModel
@ -235,7 +231,7 @@ public:
virtual QVariant data(const QModelIndex& index, int role) const override; virtual QVariant data(const QModelIndex& index, int role) const override;
void setThumbnailsSize(int size); void setThumbnailsSize(int size);
void setDocument(const PDFDocument* document); void setDocument(const PDFModifiedDocument& document);
/// Sets the extra item width/height for size hint. This space will be added to the size hint (pixmap size) /// Sets the extra item width/height for size hint. This space will be added to the size hint (pixmap size)
void setExtraItemSizeHint(int width, int height) { m_extraItemWidthHint = width; m_extraItemHeighHint = height; } void setExtraItemSizeHint(int width, int height) { m_extraItemWidthHint = width; m_extraItemHeighHint = height; }

View File

@ -343,6 +343,16 @@ OCState PDFOptionalContentActivity::getState(PDFObjectReference ocg) const
return OCState::Unknown; return OCState::Unknown;
} }
void PDFOptionalContentActivity::setDocument(const PDFDocument* document)
{
if (m_document != document)
{
Q_ASSERT(document);
m_document = document;
m_properties = document->getCatalog()->getOptionalContentProperties();
}
}
void PDFOptionalContentActivity::setState(PDFObjectReference ocg, OCState state, bool preserveRadioButtons) void PDFOptionalContentActivity::setState(PDFObjectReference ocg, OCState state, bool preserveRadioButtons)
{ {
auto it = m_states.find(ocg); auto it = m_states.find(ocg);

View File

@ -167,6 +167,11 @@ public:
/// \param ocg Optional content group /// \param ocg Optional content group
OCState getState(PDFObjectReference ocg) const; OCState getState(PDFObjectReference ocg) const;
/// Sets document to this object. Optional content settings
/// must be compatible and applicable to new document.
/// \param document Document
void setDocument(const PDFDocument* document);
/// Sets the state of optional content group. If optional content group doesn't exist, /// Sets the state of optional content group. If optional content group doesn't exist,
/// then nothing happens. If optional content group is contained in radio button group, then /// then nothing happens. If optional content group is contained in radio button group, then
/// all other optional content groups in the group are switched off, if we are /// all other optional content groups in the group are switched off, if we are

View File

@ -434,9 +434,13 @@ void PDFRasterizerPool::render(const std::vector<PDFInteger>& pageIndices,
emit renderError(PDFRenderError(error.type, PDFTranslationContext::tr("Page %1: %2").arg(pageIndex + 1).arg(error.message))); emit renderError(PDFRenderError(error.type, PDFTranslationContext::tr("Page %1: %2").arg(pageIndex + 1).arg(error.message)));
} }
// We can const-cast here, because we do not modify the document in annotation manager.
// Annotations are just rendered to the target picture.
PDFModifiedDocument modifiedDocument(const_cast<PDFDocument*>(m_document), const_cast<PDFOptionalContentActivity*>(m_optionalContentActivity));
// Annotation manager // Annotation manager
PDFAnnotationManager annotationManager(m_fontCache, m_cmsManager, m_optionalContentActivity, m_meshQualitySettings, m_features, PDFAnnotationManager::Target::Print, nullptr); PDFAnnotationManager annotationManager(m_fontCache, m_cmsManager, m_optionalContentActivity, m_meshQualitySettings, m_features, PDFAnnotationManager::Target::Print, nullptr);
annotationManager.setDocument(m_document, m_optionalContentActivity); annotationManager.setDocument(modifiedDocument);
// Render page to image // Render page to image
PDFRasterizer* rasterizer = acquire(); PDFRasterizer* rasterizer = acquire();

View File

@ -60,12 +60,17 @@ PDFWidgetTool::~PDFWidgetTool()
} }
void PDFWidgetTool::setDocument(const PDFDocument* document) void PDFWidgetTool::setDocument(const PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {
// We must turn off the tool, if we are changing the document // We must turn off the tool, if we are changing the document. We turn off tool,
setActive(false); // only if whole document is being reset.
if (document.hasReset())
{
setActive(false);
}
m_document = document; m_document = document;
for (PDFWidgetTool* tool : m_toolStack) for (PDFWidgetTool* tool : m_toolStack)
@ -728,7 +733,7 @@ PDFToolManager::PDFToolManager(PDFDrawWidgetProxy* proxy, Actions actions, QObje
} }
} }
void PDFToolManager::setDocument(const PDFDocument* document) void PDFToolManager::setDocument(const PDFModifiedDocument& document)
{ {
for (PDFWidgetTool* tool : m_tools) for (PDFWidgetTool* tool : m_tools)
{ {

View File

@ -48,7 +48,7 @@ public:
/// Sets document, shuts down the tool, if it is active, and document /// Sets document, shuts down the tool, if it is active, and document
/// is changing. /// is changing.
/// \param document Document /// \param document Document
void setDocument(const PDFDocument* document); void setDocument(const PDFModifiedDocument& document);
/// Sets tool as active or inactive. If tool is active, then it is processed /// Sets tool as active or inactive. If tool is active, then it is processed
/// in draw widget proxy events (such as drawing etc.). /// in draw widget proxy events (such as drawing etc.).
@ -416,7 +416,7 @@ public:
/// Sets document /// Sets document
/// \param document Document /// \param document Document
void setDocument(const PDFDocument* document); void setDocument(const PDFModifiedDocument& document);
enum PredefinedTools enum PredefinedTools
{ {

View File

@ -49,14 +49,20 @@ PDFAdvancedFindWidget::~PDFAdvancedFindWidget()
delete ui; delete ui;
} }
void PDFAdvancedFindWidget::setDocument(const pdf::PDFDocument* document) void PDFAdvancedFindWidget::setDocument(const pdf::PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {
m_document = document; m_document = document;
m_findResults.clear();
updateUI(); // If document is not being reset, then page text should remain the same,
updateResultsUI(); // so, there is no need to clear the results.
if (document.hasReset())
{
m_findResults.clear();
updateUI();
updateResultsUI();
}
} }
} }

View File

@ -56,7 +56,7 @@ public:
const QMatrix& pagePointToDevicePointMatrix, const QMatrix& pagePointToDevicePointMatrix,
QList<pdf::PDFRenderError>& errors) const override; QList<pdf::PDFRenderError>& errors) const override;
void setDocument(const pdf::PDFDocument* document); void setDocument(const pdf::PDFModifiedDocument& document);
protected: protected:
virtual void showEvent(QShowEvent* event) override; virtual void showEvent(QShowEvent* event) override;

View File

@ -121,10 +121,10 @@ PDFSidebarWidget::~PDFSidebarWidget()
delete ui; delete ui;
} }
void PDFSidebarWidget::setDocument(const pdf::PDFDocument* document, pdf::PDFOptionalContentActivity* optionalContentActivity) void PDFSidebarWidget::setDocument(const pdf::PDFModifiedDocument& document)
{ {
m_document = document; m_document = document;
m_optionalContentActivity = optionalContentActivity; m_optionalContentActivity = document.getOptionalContentActivity();
// Update outline // Update outline
m_outlineTreeModel->setDocument(document); m_outlineTreeModel->setDocument(document);

View File

@ -36,6 +36,7 @@ namespace pdf
class PDFAction; class PDFAction;
class PDFDocument; class PDFDocument;
class PDFDrawWidgetProxy; class PDFDrawWidgetProxy;
class PDFModifiedDocument;
class PDFOutlineTreeItemModel; class PDFOutlineTreeItemModel;
class PDFThumbnailsItemModel; class PDFThumbnailsItemModel;
class PDFAttachmentsTreeItemModel; class PDFAttachmentsTreeItemModel;
@ -69,7 +70,7 @@ public:
_END _END
}; };
void setDocument(const pdf::PDFDocument* document, pdf::PDFOptionalContentActivity* optionalContentActivity); void setDocument(const pdf::PDFModifiedDocument& document);
/// Returns true, if all items in sidebar are empty /// Returns true, if all items in sidebar are empty
bool isEmpty() const; bool isEmpty() const;

View File

@ -61,7 +61,7 @@ bool PDFTextToSpeech::isValid() const
return m_document != nullptr; return m_document != nullptr;
} }
void PDFTextToSpeech::setDocument(const pdf::PDFDocument* document) void PDFTextToSpeech::setDocument(const pdf::PDFModifiedDocument& document)
{ {
if (m_document != document) if (m_document != document)
{ {

View File

@ -34,6 +34,7 @@ namespace pdf
{ {
class PDFDocument; class PDFDocument;
class PDFDrawWidgetProxy; class PDFDrawWidgetProxy;
class PDFModifiedDocument;
} }
namespace pdfviewer namespace pdfviewer
@ -66,7 +67,7 @@ public:
bool isValid() const; bool isValid() const;
/// Sets active document to text to speech engine /// Sets active document to text to speech engine
void setDocument(const pdf::PDFDocument* document); void setDocument(const pdf::PDFModifiedDocument& document);
/// Apply settings to the reader /// Apply settings to the reader
void setSettings(const PDFViewerSettings* viewerSettings); void setSettings(const PDFViewerSettings* viewerSettings);

View File

@ -959,7 +959,8 @@ void PDFViewerMainWindow::onDocumentReadingFinished()
m_recentFileManager->addRecentFile(m_fileInfo.originalFileName); m_recentFileManager->addRecentFile(m_fileInfo.originalFileName);
m_pdfDocument = result.document; m_pdfDocument = result.document;
setDocument(m_pdfDocument.data()); pdf::PDFModifiedDocument document(m_pdfDocument.data(), m_optionalContentActivity);
setDocument(document);
statusBar()->showMessage(tr("Document '%1' was successfully loaded!").arg(m_fileInfo.fileName), 4000); statusBar()->showMessage(tr("Document '%1' was successfully loaded!").arg(m_fileInfo.fileName), 4000);
break; break;
@ -977,27 +978,36 @@ void PDFViewerMainWindow::onDocumentReadingFinished()
updateActionsAvailability(); updateActionsAvailability();
} }
void PDFViewerMainWindow::setDocument(const pdf::PDFDocument* document) void PDFViewerMainWindow::setDocument(pdf::PDFModifiedDocument document)
{ {
if (m_optionalContentActivity) if (document.hasReset())
{ {
// We use deleteLater, because we want to avoid consistency problem with model if (m_optionalContentActivity)
// (we set document to the model before activity). {
m_optionalContentActivity->deleteLater(); // We use deleteLater, because we want to avoid consistency problem with model
m_optionalContentActivity = nullptr; // (we set document to the model before activity).
m_optionalContentActivity->deleteLater();
m_optionalContentActivity = nullptr;
}
if (document)
{
m_optionalContentActivity = new pdf::PDFOptionalContentActivity(document, pdf::OCUsage::View, this);
}
}
else if (m_optionalContentActivity)
{
Q_ASSERT(document);
m_optionalContentActivity->setDocument(document);
} }
if (document) document.setOptionalContentActivity(m_optionalContentActivity);
{ m_annotationManager->setDocument(document);
m_optionalContentActivity = new pdf::PDFOptionalContentActivity(document, pdf::OCUsage::View, this);
}
m_annotationManager->setDocument(document, m_optionalContentActivity);
m_formManager->setDocument(document); m_formManager->setDocument(document);
m_toolManager->setDocument(document); m_toolManager->setDocument(document);
m_textToSpeech->setDocument(document); m_textToSpeech->setDocument(document);
m_pdfWidget->setDocument(document, m_optionalContentActivity); m_pdfWidget->setDocument(document);
m_sidebarWidget->setDocument(document, m_optionalContentActivity); m_sidebarWidget->setDocument(document);
m_advancedFindWidget->setDocument(document); m_advancedFindWidget->setDocument(document);
if (m_sidebarWidget->isEmpty()) if (m_sidebarWidget->isEmpty())
@ -1034,7 +1044,7 @@ void PDFViewerMainWindow::setDocument(const pdf::PDFDocument* document)
void PDFViewerMainWindow::closeDocument() void PDFViewerMainWindow::closeDocument()
{ {
setDocument(nullptr); setDocument(pdf::PDFModifiedDocument());
m_pdfDocument.reset(); m_pdfDocument.reset();
updateActionsAvailability(); updateActionsAvailability();
} }

View File

@ -135,7 +135,7 @@ private:
void onRenderingOptionTriggered(bool checked); void onRenderingOptionTriggered(bool checked);
void openDocument(const QString& fileName); void openDocument(const QString& fileName);
void setDocument(const pdf::PDFDocument* document); void setDocument(pdf::PDFModifiedDocument document);
void closeDocument(); void closeDocument();
void setPageLayout(pdf::PageLayout pageLayout); void setPageLayout(pdf::PageLayout pageLayout);