From 0b2d94d115f1e4aedb7f311589e87b0c3b137647 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sat, 7 Mar 2020 17:38:50 +0100 Subject: [PATCH] Annotation painting --- PdfForQtLib/PdfForQtLib.pro | 1 + PdfForQtLib/sources/pdfannotation.cpp | 189 +++++++++++++++++- PdfForQtLib/sources/pdfannotation.h | 57 +++++- .../sources/pdfdocumentdrawinterface.h | 57 ++++++ PdfForQtLib/sources/pdfdocumentreader.cpp | 2 +- PdfForQtLib/sources/pdfdrawspacecontroller.h | 26 +-- PdfForQtLib/sources/pdffont.cpp | 2 +- PdfForQtLib/sources/pdffont.h | 4 +- PdfForQtLib/sources/pdfglobal.h | 2 + PdfForQtLib/sources/pdfpage.cpp | 1 + PdfForQtLib/sources/pdfpage.h | 3 + .../sources/pdfpagecontentprocessor.cpp | 12 +- PdfForQtLib/sources/pdfpagecontentprocessor.h | 28 +-- PdfForQtLib/sources/pdfpainter.h | 3 +- PdfForQtLib/sources/pdfutils.h | 14 ++ PdfForQtViewer/pdfviewermainwindow.cpp | 7 + PdfForQtViewer/pdfviewermainwindow.h | 2 + 17 files changed, 363 insertions(+), 47 deletions(-) create mode 100644 PdfForQtLib/sources/pdfdocumentdrawinterface.h diff --git a/PdfForQtLib/PdfForQtLib.pro b/PdfForQtLib/PdfForQtLib.pro index 45da8d6..76dc6b9 100644 --- a/PdfForQtLib/PdfForQtLib.pro +++ b/PdfForQtLib/PdfForQtLib.pro @@ -87,6 +87,7 @@ HEADERS += \ sources/pdfccittfaxdecoder.h \ sources/pdfcms.h \ sources/pdfcompiler.h \ + sources/pdfdocumentdrawinterface.h \ sources/pdfexecutionpolicy.h \ sources/pdffile.h \ sources/pdfitemmodels.h \ diff --git a/PdfForQtLib/sources/pdfannotation.cpp b/PdfForQtLib/sources/pdfannotation.cpp index 0bede62..c1962a1 100644 --- a/PdfForQtLib/sources/pdfannotation.cpp +++ b/PdfForQtLib/sources/pdfannotation.cpp @@ -18,6 +18,9 @@ #include "pdfannotation.h" #include "pdfdocument.h" #include "pdfencoding.h" +#include "pdfpainter.h" +#include "pdfdrawspacecontroller.h" +#include "pdfcms.h" namespace pdf { @@ -98,8 +101,10 @@ PDFAppeareanceStreams PDFAppeareanceStreams::parse(const PDFDocument* document, auto processSubdicitonary = [&result, document](Appearance appearance, PDFObject subdictionaryObject) { - if (const PDFDictionary* subdictionary = document->getDictionaryFromObject(subdictionaryObject)) + subdictionaryObject = document->getObject(subdictionaryObject); + if (subdictionaryObject.isDictionary()) { + const PDFDictionary* subdictionary = document->getDictionaryFromObject(subdictionaryObject); for (size_t i = 0; i < subdictionary->getCount(); ++i) { result.m_appearanceStreams[std::make_pair(appearance, subdictionary->getKey(i))] = subdictionary->getValue(i); @@ -732,4 +737,186 @@ PDFAnnotationAdditionalActions PDFAnnotationAdditionalActions::parse(const PDFDo return result; } +PDFAnnotationManager::PDFAnnotationManager(PDFDrawWidgetProxy* proxy, QObject* parent) : + BaseClass(parent), + m_document(nullptr), + m_proxy(proxy) +{ + Q_ASSERT(proxy); + m_proxy->registerDrawInterface(this); +} + +PDFAnnotationManager::~PDFAnnotationManager() +{ + m_proxy->unregisterDrawInterface(this); +} + +void PDFAnnotationManager::drawPage(QPainter* painter, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix) const +{ + Q_UNUSED(compiledPage); + Q_UNUSED(layoutGetter); + + PageAnnotations& annotations = getPageAnnotations(pageIndex); + if (!annotations.isEmpty()) + { + const PDFPage* page = m_document->getCatalog()->getPage(pageIndex); + Q_ASSERT(page); + + const PDFRenderer::Features features = m_proxy->getFeatures(); + PDFFontCache* fontCache = m_proxy->getFontCache(); + const PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS(); + const PDFOptionalContentActivity* optionalActivity = m_proxy->getOptionalContentActivity(); + const PDFMeshQualitySettings& meshQualitySettings = m_proxy->getMeshQualitySettings(); + fontCache->setCacheShrinkEnabled(this, false); + + for (PageAnnotation& annotation : annotations.annotations) + { + PDFObject appearanceStreamObject = m_document->getObject(getAppearanceStream(annotation)); + if (!appearanceStreamObject.isStream()) + { + // Object is not valid appearance stream + continue; + } + + PDFDocumentDataLoaderDecorator loader(m_document); + const PDFStream* formStream = appearanceStreamObject.getStream(); + const PDFDictionary* formDictionary = formStream->getDictionary(); + + QRectF annotationRectangle = annotation.annotation->getRectangle(); + QRectF formBoundingBox = loader.readRectangle(formDictionary->get("BBox"), QRectF()); + QMatrix formMatrix = loader.readMatrixFromDictionary(formDictionary, "Matrix", QMatrix()); + QByteArray content = m_document->getDecodedStream(formStream); + PDFObject resources = m_document->getObject(formDictionary->get("Resources")); + PDFObject transparencyGroup = m_document->getObject(formDictionary->get("Group")); + + if (formBoundingBox.isEmpty() || annotationRectangle.isEmpty()) + { + // Form bounding box is empty + continue; + } + + // "Unrotate" user coordinate space, if NoRotate flag is set + QMatrix userSpaceToDeviceSpace = pagePointToDevicePointMatrix; + if (annotation.annotation->getFlags().testFlag(PDFAnnotation::NoRotate)) + { + PDFReal rotationAngle = 0.0; + switch (page->getPageRotation()) + { + case PageRotation::None: + break; + + case PageRotation::Rotate90: + rotationAngle = -90.0; + break; + + case PageRotation::Rotate180: + rotationAngle = -180.0; + break; + + case PageRotation::Rotate270: + rotationAngle = -270.0; + break; + + default: + Q_ASSERT(false); + break; + } + + QMatrix rotationMatrix; + rotationMatrix.rotate(-rotationAngle); + QPointF topLeft = annotationRectangle.bottomLeft(); // Do not forget, that y is upward instead of Qt + QPointF difference = topLeft - rotationMatrix.map(topLeft); + + QMatrix finalMatrix; + finalMatrix.translate(difference.x(), difference.y()); + finalMatrix.rotate(-rotationAngle); + userSpaceToDeviceSpace = finalMatrix * userSpaceToDeviceSpace; + } + + // Jakub Melka: perform algorithm 8.1, defined in PDF 1.7 reference, + // chapter 8.4.4 Appearance streams. + + // Step 1) - calculate transformed appearance box + QRectF transformedAppearanceBox = formMatrix.mapRect(formBoundingBox); + + // Step 2) - calculate matrix A, which maps from form space to annotation space + // Matrix A transforms from form space to transformed appearance box space + const PDFReal scaleX = transformedAppearanceBox.width() / formBoundingBox.width(); + const PDFReal scaleY = transformedAppearanceBox.height() / formBoundingBox.height(); + const PDFReal translateX = annotationRectangle.left() - formBoundingBox.left() * scaleX; + const PDFReal translateY = annotationRectangle.bottom() - formBoundingBox.bottom() * scaleY; + QMatrix A(scaleX, 0.0, 0.0, scaleY, translateX, translateY); + + // Step 3) - compute final matrix AA + QMatrix AA = formMatrix * A; + + PDFPainter pdfPainter(painter, features, userSpaceToDeviceSpace, page, m_document, fontCache, cms.get(), optionalActivity, meshQualitySettings); + pdfPainter.initializeProcessor(); + + // Jakub Melka: we must check, that we do not display annotation disabled by optional content + PDFObjectReference oc = annotation.annotation->getOptionalContent(); + if (!oc.isValid() || !pdfPainter.isContentSuppressedByOC(oc)) + { + pdfPainter.processForm(AA, formBoundingBox, resources, transparencyGroup, content); + } + } + + fontCache->setCacheShrinkEnabled(this, true); + } +} + +void PDFAnnotationManager::setDocument(const PDFDocument* document) +{ + if (m_document != document) + { + m_document = document; + m_pageAnnotations.clear(); + } +} + +PDFObject PDFAnnotationManager::getAppearanceStream(PageAnnotation& pageAnnotation) const +{ + auto getAppearanceStream = [&pageAnnotation] (void) -> PDFObject + { + return pageAnnotation.annotation->getAppearanceStreams().getAppearance(pageAnnotation.appearance, pageAnnotation.annotation->getAppearanceState()); + }; + return pageAnnotation.appearanceStream.get(getAppearanceStream); +} + +PDFAnnotationManager::PageAnnotations& PDFAnnotationManager::getPageAnnotations(PDFInteger pageIndex) const +{ + Q_ASSERT(m_document); + + auto it = m_pageAnnotations.find(pageIndex); + if (it == m_pageAnnotations.cend()) + { + // Create page annotations + const PDFPage* page = m_document->getCatalog()->getPage(pageIndex); + Q_ASSERT(page); + + PageAnnotations annotations; + + const std::vector& pageAnnotations = page->getAnnotations(); + annotations.annotations.reserve(pageAnnotations.size()); + for (PDFObjectReference annotationReference : pageAnnotations) + { + PDFAnnotationPtr annotationPtr = PDFAnnotation::parse(m_document, m_document->getObjectByReference(annotationReference)); + if (annotationPtr) + { + PageAnnotation annotation; + annotation.annotation = qMove(annotationPtr); + annotations.annotations.emplace_back(qMove(annotation)); + } + } + + it = m_pageAnnotations.insert(std::make_pair(pageIndex, qMove(annotations))).first; + } + + return it->second; +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfannotation.h b/PdfForQtLib/sources/pdfannotation.h index 205a947..8cc4eca 100644 --- a/PdfForQtLib/sources/pdfannotation.h +++ b/PdfForQtLib/sources/pdfannotation.h @@ -18,11 +18,12 @@ #ifndef PDFANNOTATION_H #define PDFANNOTATION_H -#include "pdfglobal.h" +#include "pdfutils.h" #include "pdfobject.h" #include "pdfaction.h" #include "pdffile.h" #include "pdfmultimedia.h" +#include "pdfdocumentdrawinterface.h" #include @@ -31,6 +32,7 @@ namespace pdf { class PDFDocument; +class PDFDrawWidgetProxy; enum class AnnotationType { @@ -1021,6 +1023,59 @@ private: PDFReal m_relativeVerticalOffset = 0.0; }; +/// Annotation manager manages annotations for document's pages. Each page +/// can have multiple annotations, and this object caches them. Also, +/// this object builds annotation's appearance streams, if necessary. +/// This object is not thread safe, functions can't be called from another +/// thread. +class PDFFORQTLIBSHARED_EXPORT PDFAnnotationManager : public QObject, public IDocumentDrawInterface +{ + Q_OBJECT + +private: + using BaseClass = QObject; + +public: + explicit PDFAnnotationManager(PDFDrawWidgetProxy* proxy, QObject* parent); + virtual ~PDFAnnotationManager() override; + + virtual void drawPage(QPainter* painter, + PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix) const override; + + void setDocument(const PDFDocument* document); + +private: + struct PageAnnotation + { + PDFAppeareanceStreams::Appearance appearance = PDFAppeareanceStreams::Appearance::Normal; + PDFCachedItem appearanceStream; + PDFAnnotationPtr annotation; + }; + + struct PageAnnotations + { + bool isEmpty() const { return annotations.empty(); } + + std::vector annotations; + }; + + /// Returns current appearance stream for given page annotation + /// \param pageAnnotation Page annotation + PDFObject getAppearanceStream(PageAnnotation& pageAnnotation) const; + + /// Returns reference to page annotation for given page index. + /// This function requires, that pointer to m_document is valid. + /// \param pageIndex Page index (must point to valid page) + PageAnnotations& getPageAnnotations(PDFInteger pageIndex) const; + + const PDFDocument* m_document; + PDFDrawWidgetProxy* m_proxy; + mutable std::map m_pageAnnotations; +}; + } // namespace pdf #endif // PDFANNOTATION_H diff --git a/PdfForQtLib/sources/pdfdocumentdrawinterface.h b/PdfForQtLib/sources/pdfdocumentdrawinterface.h new file mode 100644 index 0000000..41dd599 --- /dev/null +++ b/PdfForQtLib/sources/pdfdocumentdrawinterface.h @@ -0,0 +1,57 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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 +// (at your option) any later version. +// +// PdfForQt 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 PDFForQt. If not, see . + +#ifndef PDFDOCUMENTDRAWINTERFACE_H +#define PDFDOCUMENTDRAWINTERFACE_H + +#include "pdfglobal.h" + +class QPainter; + +namespace pdf +{ +class PDFPrecompiledPage; +class PDFTextLayoutGetter; + +class PDFFORQTLIBSHARED_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 + virtual void drawPage(QPainter* painter, + pdf::PDFInteger pageIndex, + const PDFPrecompiledPage* compiledPage, + PDFTextLayoutGetter& layoutGetter, + const QMatrix& pagePointToDevicePointMatrix) 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; +}; + +} // namespace pdf + +#endif // PDFDOCUMENTDRAWINTERFACE_H diff --git a/PdfForQtLib/sources/pdfdocumentreader.cpp b/PdfForQtLib/sources/pdfdocumentreader.cpp index 4600e1f..33ce946 100644 --- a/PdfForQtLib/sources/pdfdocumentreader.cpp +++ b/PdfForQtLib/sources/pdfdocumentreader.cpp @@ -466,7 +466,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer) } else { - throw PDFException(PDFTranslationContext::tr("Object stream %1 is invalid.").arg(objectStreamReference.objectNumber)); + // Silently ignore this error. It is not critical, so, maybe this object will be null. } } } diff --git a/PdfForQtLib/sources/pdfdrawspacecontroller.h b/PdfForQtLib/sources/pdfdrawspacecontroller.h index cb2ccf8..90d6415 100644 --- a/PdfForQtLib/sources/pdfdrawspacecontroller.h +++ b/PdfForQtLib/sources/pdfdrawspacecontroller.h @@ -22,6 +22,7 @@ #include "pdfdocument.h" #include "pdfrenderer.h" #include "pdffont.h" +#include "pdfdocumentdrawinterface.h" #include #include @@ -40,31 +41,6 @@ class PDFTextLayoutGetter; class PDFAsynchronousPageCompiler; class PDFAsynchronousTextLayoutCompiler; -class PDFFORQTLIBSHARED_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 - virtual void drawPage(QPainter* painter, - pdf::PDFInteger pageIndex, - const PDFPrecompiledPage* compiledPage, - PDFTextLayoutGetter& layoutGetter, - const QMatrix& pagePointToDevicePointMatrix) 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; -}; - /// 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. diff --git a/PdfForQtLib/sources/pdffont.cpp b/PdfForQtLib/sources/pdffont.cpp index 0f2386f..25c83b9 100644 --- a/PdfForQtLib/sources/pdffont.cpp +++ b/PdfForQtLib/sources/pdffont.cpp @@ -1656,7 +1656,7 @@ PDFRealizedFontPointer PDFFontCache::getRealizedFont(const PDFFontPointer& font, return it->second; } -void PDFFontCache::setCacheShrinkEnabled(void* source, bool enabled) +void PDFFontCache::setCacheShrinkEnabled(const void* source, bool enabled) { QMutexLocker lock(&m_mutex); if (enabled) diff --git a/PdfForQtLib/sources/pdffont.h b/PdfForQtLib/sources/pdffont.h index 2ca40e7..adb0798 100644 --- a/PdfForQtLib/sources/pdffont.h +++ b/PdfForQtLib/sources/pdffont.h @@ -419,7 +419,7 @@ public: /// then cache can shrink. /// \param source Source object /// \param enabled Enable or disable cache shrinking - void setCacheShrinkEnabled(void* source, bool enabled); + void setCacheShrinkEnabled(const void* source, bool enabled); /// Set font cache limits void setCacheLimits(int fontCacheLimit, int instancedFontCacheLimit); @@ -434,7 +434,7 @@ private: const PDFDocument* m_document; mutable std::map m_fontCache; mutable std::map, PDFRealizedFontPointer> m_realizedFontCache; - mutable std::set m_fontCacheShrinkDisabledObjects; + mutable std::set m_fontCacheShrinkDisabledObjects; }; /// Performs mapping from CID to GID (even identity mapping, if byte array is empty) diff --git a/PdfForQtLib/sources/pdfglobal.h b/PdfForQtLib/sources/pdfglobal.h index 3dd1277..3872e6d 100644 --- a/PdfForQtLib/sources/pdfglobal.h +++ b/PdfForQtLib/sources/pdfglobal.h @@ -86,6 +86,8 @@ struct PDFObjectReference { return std::tie(objectNumber, generation) < std::tie(other.objectNumber, other.generation); } + + constexpr bool isValid() const { return objectNumber > 0; } }; /// Represents version identification diff --git a/PdfForQtLib/sources/pdfpage.cpp b/PdfForQtLib/sources/pdfpage.cpp index ee9cefb..ac92875 100644 --- a/PdfForQtLib/sources/pdfpage.cpp +++ b/PdfForQtLib/sources/pdfpage.cpp @@ -220,6 +220,7 @@ void PDFPage::parseImpl(std::vector& pages, page.m_trimBox = loader.readRectangle(dictionary->get("TrimBox"), page.getCropBox()); page.m_artBox = loader.readRectangle(dictionary->get("ArtBox"), page.getCropBox()); page.m_contents = document->getObject(dictionary->get("Contents")); + page.m_annots = loader.readReferenceArrayFromDictionary(dictionary, "Annots"); pages.emplace_back(std::move(page)); } diff --git a/PdfForQtLib/sources/pdfpage.h b/PdfForQtLib/sources/pdfpage.h index 6deab14..caedb4e 100644 --- a/PdfForQtLib/sources/pdfpage.h +++ b/PdfForQtLib/sources/pdfpage.h @@ -134,6 +134,8 @@ public: QRectF getRotatedMediaBoxMM() const; QRectF getRotatedCropBox() const; + inline const std::vector& getAnnotations() const { return m_annots; } + static QRectF getRotatedBox(const QRectF& rect, PageRotation rotation); private: @@ -158,6 +160,7 @@ private: PDFObject m_resources; PDFObject m_contents; PDFObjectReference m_pageReference; + std::vector m_annots; }; } // namespace pdf diff --git a/PdfForQtLib/sources/pdfpagecontentprocessor.cpp b/PdfForQtLib/sources/pdfpagecontentprocessor.cpp index 4c24173..f9eeef3 100644 --- a/PdfForQtLib/sources/pdfpagecontentprocessor.cpp +++ b/PdfForQtLib/sources/pdfpagecontentprocessor.cpp @@ -224,10 +224,8 @@ PDFPageContentProcessor::~PDFPageContentProcessor() PDFExecutionPolicy::endProcessingContentStream(); } -QList PDFPageContentProcessor::processContents() +void PDFPageContentProcessor::initializeProcessor() { - const PDFObject& contents = m_page->getContents(); - // Clear the old errors m_errorList.clear(); @@ -254,6 +252,14 @@ QList PDFPageContentProcessor::processContents() m_graphicState.setFillColor(m_deviceGrayColorSpace->getDefaultColor(m_CMS, m_graphicState.getRenderingIntent(), this)); m_graphicState.setStateFlags(PDFPageContentProcessorState::StateAll); updateGraphicState(); +} + +QList PDFPageContentProcessor::processContents() +{ + const PDFObject& contents = m_page->getContents(); + + // Initialize stream processor + initializeProcessor(); if (contents.isArray()) { diff --git a/PdfForQtLib/sources/pdfpagecontentprocessor.h b/PdfForQtLib/sources/pdfpagecontentprocessor.h index fe3b5c2..547bf1a 100644 --- a/PdfForQtLib/sources/pdfpagecontentprocessor.h +++ b/PdfForQtLib/sources/pdfpagecontentprocessor.h @@ -175,6 +175,22 @@ public: /// then no new error is reported. virtual void reportRenderErrorOnce(RenderErrorType type, QString message) override; + /// Processes form (XObject of type form) + /// \param Matrix Transformation matrix from form coordinate system to page coordinate system + /// \param boundingBox Bounding box, to which is drawed content clipped + /// \param resources Resources, assigned to the form + /// \param transparencyGroup Transparency group object + /// \param content Content stream of the form + void processForm(const QMatrix& matrix, const QRectF& boundingBox, const PDFObject& resources, const PDFObject& transparencyGroup, const QByteArray& content); + + /// Initialize stream processor for processing content streams. For example, + /// graphic state is initialized to default, and default color spaces are initialized. + void initializeProcessor(); + + /// Computes visibility of OCG/OCMD - returns false, if it is not suppressed, + /// or true, if it is suppressed. + virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd); + protected: class PDFLineDashPattern @@ -528,10 +544,6 @@ protected: /// Returns page bounding rectangle in device space const QRectF& getPageBoundingRectDeviceSpace() const { return m_pageBoundingRectDeviceSpace; } - /// Computes visibility of OCG/OCMD - returns false, if it is not suppressed, - /// or true, if it is suppressed. - virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd); - private: /// Initializes the resources dictionaries void initDictionaries(const PDFObject& resourcesObject); @@ -545,14 +557,6 @@ private: /// Processes single command void processCommand(const QByteArray& command); - /// Processes form (XObject of type form) - /// \param Matrix Transformation matrix from form coordinate system to page coordinate system - /// \param boundingBox Bounding box, to which is drawed content clipped - /// \param resources Resources, assigned to the form - /// \param transparencyGroup Transparency group object - /// \param content Content stream of the form - void processForm(const QMatrix& matrix, const QRectF& boundingBox, const PDFObject& resources, const PDFObject& transparencyGroup, const QByteArray& content); - /// Performs path painting /// \param path Path, which should be drawn (can be emtpy - in that case nothing happens) /// \param stroke Stroke the path diff --git a/PdfForQtLib/sources/pdfpainter.h b/PdfForQtLib/sources/pdfpainter.h index 9460d84..0e38287 100644 --- a/PdfForQtLib/sources/pdfpainter.h +++ b/PdfForQtLib/sources/pdfpainter.h @@ -47,11 +47,12 @@ public: QMatrix pagePointToDevicePointMatrix, const PDFMeshQualitySettings& meshQualitySettings); + virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) override; + protected: virtual void performUpdateGraphicsState(const PDFPageContentProcessorState& state) override; virtual void performBeginTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup); virtual void performEndTransparencyGroup(ProcessOrder order, const PDFTransparencyGroup& transparencyGroup); - virtual bool isContentSuppressedByOC(PDFObjectReference ocgOrOcmd) override; virtual void setWorldMatrix(const QMatrix& matrix) = 0; virtual void setCompositionMode(QPainter::CompositionMode mode) = 0; diff --git a/PdfForQtLib/sources/pdfutils.h b/PdfForQtLib/sources/pdfutils.h index 98ca38e..03fac55 100644 --- a/PdfForQtLib/sources/pdfutils.h +++ b/PdfForQtLib/sources/pdfutils.h @@ -28,6 +28,7 @@ #include #include +#include namespace pdf { @@ -76,6 +77,19 @@ public: return m_object; } + /// Returns the cached object. If object is dirty, then cached object is refreshed. + /// \param function Refresh function + inline const T& get(const std::function& function) + { + if (m_dirty) + { + m_object = function(); + m_dirty = false; + } + + return m_object; + } + /// Invalidates the cached item, so it must be refreshed from the cache next time, /// if it is accessed. inline void dirty() diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp index a1b0bb4..38b8955 100644 --- a/PdfForQtViewer/pdfviewermainwindow.cpp +++ b/PdfForQtViewer/pdfviewermainwindow.cpp @@ -89,6 +89,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : m_isBusy(false), m_isChangingProgressStep(false), m_toolManager(nullptr), + m_annotationManager(nullptr), m_textToSpeech(new PDFTextToSpeech(this)) { ui->setupUi(this); @@ -262,6 +263,8 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : updateMagnifierToolSettings(); connect(m_toolManager, &pdf::PDFToolManager::messageDisplayRequest, statusBar(), &QStatusBar::showMessage); + m_annotationManager = new pdf::PDFAnnotationManager(m_pdfWidget->getDrawWidgetProxy(), this); + connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::drawSpaceChanged, this, &PDFViewerMainWindow::onDrawSpaceChanged); connect(m_pdfWidget->getDrawWidgetProxy(), &pdf::PDFDrawWidgetProxy::pageLayoutChanged, this, &PDFViewerMainWindow::onPageLayoutChanged); connect(m_pdfWidget, &pdf::PDFWidget::pageRenderingErrorsChanged, this, &PDFViewerMainWindow::onPageRenderingErrorsChanged, Qt::QueuedConnection); @@ -282,6 +285,9 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) : PDFViewerMainWindow::~PDFViewerMainWindow() { + delete m_annotationManager; + m_annotationManager = nullptr; + delete ui; } @@ -967,6 +973,7 @@ void PDFViewerMainWindow::setDocument(const pdf::PDFDocument* document) m_optionalContentActivity = new pdf::PDFOptionalContentActivity(document, pdf::OCUsage::View, this); } + m_annotationManager->setDocument(document); m_toolManager->setDocument(document); m_textToSpeech->setDocument(document); m_pdfWidget->setDocument(document, m_optionalContentActivity); diff --git a/PdfForQtViewer/pdfviewermainwindow.h b/PdfForQtViewer/pdfviewermainwindow.h index b48a2fb..4b20bbf 100644 --- a/PdfForQtViewer/pdfviewermainwindow.h +++ b/PdfForQtViewer/pdfviewermainwindow.h @@ -28,6 +28,7 @@ #include "pdfwidgettool.h" #include "pdfrecentfilemanager.h" #include "pdftexttospeech.h" +#include "pdfannotation.h" #include #include @@ -179,6 +180,7 @@ private: bool m_isChangingProgressStep; pdf::PDFToolManager* m_toolManager; + pdf::PDFAnnotationManager* m_annotationManager; PDFTextToSpeech* m_textToSpeech; };