mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-02-27 00:47:57 +01:00
Annotation painting
This commit is contained in:
parent
92c58f68ff
commit
0b2d94d115
@ -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 \
|
||||
|
@ -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<PDFObjectReference>& 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
|
||||
|
@ -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 <QPainterPath>
|
||||
|
||||
@ -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<PDFObject> appearanceStream;
|
||||
PDFAnnotationPtr annotation;
|
||||
};
|
||||
|
||||
struct PageAnnotations
|
||||
{
|
||||
bool isEmpty() const { return annotations.empty(); }
|
||||
|
||||
std::vector<PageAnnotation> 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<PDFInteger, PageAnnotations> m_pageAnnotations;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFANNOTATION_H
|
||||
|
57
PdfForQtLib/sources/pdfdocumentdrawinterface.h
Normal file
57
PdfForQtLib/sources/pdfdocumentdrawinterface.h
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#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
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "pdfdocument.h"
|
||||
#include "pdfrenderer.h"
|
||||
#include "pdffont.h"
|
||||
#include "pdfdocumentdrawinterface.h"
|
||||
|
||||
#include <QRectF>
|
||||
#include <QObject>
|
||||
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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<PDFObjectReference, PDFFontPointer> m_fontCache;
|
||||
mutable std::map<std::pair<PDFFontPointer, PDFReal>, PDFRealizedFontPointer> m_realizedFontCache;
|
||||
mutable std::set<void*> m_fontCacheShrinkDisabledObjects;
|
||||
mutable std::set<const void*> m_fontCacheShrinkDisabledObjects;
|
||||
};
|
||||
|
||||
/// Performs mapping from CID to GID (even identity mapping, if byte array is empty)
|
||||
|
@ -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
|
||||
|
@ -220,6 +220,7 @@ void PDFPage::parseImpl(std::vector<PDFPage>& 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));
|
||||
}
|
||||
|
@ -134,6 +134,8 @@ public:
|
||||
QRectF getRotatedMediaBoxMM() const;
|
||||
QRectF getRotatedCropBox() const;
|
||||
|
||||
inline const std::vector<PDFObjectReference>& 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<PDFObjectReference> m_annots;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
@ -224,10 +224,8 @@ PDFPageContentProcessor::~PDFPageContentProcessor()
|
||||
PDFExecutionPolicy::endProcessingContentStream();
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFPageContentProcessor::processContents()
|
||||
void PDFPageContentProcessor::initializeProcessor()
|
||||
{
|
||||
const PDFObject& contents = m_page->getContents();
|
||||
|
||||
// Clear the old errors
|
||||
m_errorList.clear();
|
||||
|
||||
@ -254,6 +252,14 @@ QList<PDFRenderError> PDFPageContentProcessor::processContents()
|
||||
m_graphicState.setFillColor(m_deviceGrayColorSpace->getDefaultColor(m_CMS, m_graphicState.getRenderingIntent(), this));
|
||||
m_graphicState.setStateFlags(PDFPageContentProcessorState::StateAll);
|
||||
updateGraphicState();
|
||||
}
|
||||
|
||||
QList<PDFRenderError> PDFPageContentProcessor::processContents()
|
||||
{
|
||||
const PDFObject& contents = m_page->getContents();
|
||||
|
||||
// Initialize stream processor
|
||||
initializeProcessor();
|
||||
|
||||
if (contents.isArray())
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
#include <functional>
|
||||
|
||||
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<T(void)>& 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()
|
||||
|
@ -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);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "pdfwidgettool.h"
|
||||
#include "pdfrecentfilemanager.h"
|
||||
#include "pdftexttospeech.h"
|
||||
#include "pdfannotation.h"
|
||||
|
||||
#include <QFuture>
|
||||
#include <QTreeView>
|
||||
@ -179,6 +180,7 @@ private:
|
||||
bool m_isChangingProgressStep;
|
||||
|
||||
pdf::PDFToolManager* m_toolManager;
|
||||
pdf::PDFAnnotationManager* m_annotationManager;
|
||||
PDFTextToSpeech* m_textToSpeech;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user