| /* poppler-annotation.cc: qt interface to poppler |
| * Copyright (C) 2006, 2009, 2012-2015, 2018, 2019 Albert Astals Cid <aacid@kde.org> |
| * Copyright (C) 2006, 2008, 2010 Pino Toscano <pino@kde.org> |
| * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org> |
| * Copyright (C) 2012-2014 Fabio D'Urso <fabiodurso@hotmail.it> |
| * Copyright (C) 2012, 2015, Tobias Koenig <tobias.koenig@kdab.com> |
| * Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
| * Copyright (C) 2018 Intevation GmbH <intevation@intevation.de> |
| * Copyright (C) 2018 Dileep Sankhla <sankhla.dileep96@gmail.com> |
| * Copyright (C) 2018, 2019 Tobias Deiminger <haxtibal@posteo.de> |
| * Copyright (C) 2018 Carlos Garcia Campos <carlosgc@gnome.org> |
| * Adapting code from |
| * Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program 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 General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| // qt/kde includes |
| #include <QtCore/QRegExp> |
| #include <QtCore/QtAlgorithms> |
| #include <QtXml/QDomElement> |
| #include <QtGui/QColor> |
| #include <QtGui/QTransform> |
| |
| // local includes |
| #include "poppler-annotation.h" |
| #include "poppler-link.h" |
| #include "poppler-qt5.h" |
| #include "poppler-annotation-helper.h" |
| #include "poppler-annotation-private.h" |
| #include "poppler-page-private.h" |
| #include "poppler-private.h" |
| |
| // poppler includes |
| #include <Page.h> |
| #include <Annot.h> |
| #include <Gfx.h> |
| #include <Error.h> |
| #include <FileSpec.h> |
| #include <Link.h> |
| |
| /* Almost all getters directly query the underlying poppler annotation, with |
| * the exceptions of link, file attachment, sound, movie and screen annotations, |
| * Whose data retrieval logic has not been moved yet. Their getters return |
| * static data set at creation time by findAnnotations |
| */ |
| |
| namespace Poppler { |
| |
| //BEGIN AnnotationUtils implementation |
| Annotation * AnnotationUtils::createAnnotation( const QDomElement & annElement ) |
| { |
| // safety check on annotation element |
| if ( !annElement.hasAttribute( QStringLiteral("type") ) ) |
| return nullptr; |
| |
| // build annotation of given type |
| Annotation * annotation = nullptr; |
| int typeNumber = annElement.attribute( QStringLiteral("type") ).toInt(); |
| switch ( typeNumber ) |
| { |
| case Annotation::AText: |
| annotation = new TextAnnotation( annElement ); |
| break; |
| case Annotation::ALine: |
| annotation = new LineAnnotation( annElement ); |
| break; |
| case Annotation::AGeom: |
| annotation = new GeomAnnotation( annElement ); |
| break; |
| case Annotation::AHighlight: |
| annotation = new HighlightAnnotation( annElement ); |
| break; |
| case Annotation::AStamp: |
| annotation = new StampAnnotation( annElement ); |
| break; |
| case Annotation::AInk: |
| annotation = new InkAnnotation( annElement ); |
| break; |
| case Annotation::ACaret: |
| annotation = new CaretAnnotation( annElement ); |
| break; |
| } |
| |
| // return created annotation |
| return annotation; |
| } |
| |
| void AnnotationUtils::storeAnnotation( const Annotation * ann, QDomElement & annElement, |
| QDomDocument & document ) |
| { |
| // save annotation's type as element's attribute |
| annElement.setAttribute( QStringLiteral("type"), (uint)ann->subType() ); |
| |
| // append all annotation data as children of this node |
| ann->store( annElement, document ); |
| } |
| |
| QDomElement AnnotationUtils::findChildElement( const QDomNode & parentNode, |
| const QString & name ) |
| { |
| // loop through the whole children and return a 'name' named element |
| QDomNode subNode = parentNode.firstChild(); |
| while( subNode.isElement() ) |
| { |
| QDomElement element = subNode.toElement(); |
| if ( element.tagName() == name ) |
| return element; |
| subNode = subNode.nextSibling(); |
| } |
| // if the name can't be found, return a dummy null element |
| return QDomElement(); |
| } |
| //END AnnotationUtils implementation |
| |
| |
| //BEGIN Annotation implementation |
| AnnotationPrivate::AnnotationPrivate() |
| : flags( 0 ), revisionScope ( Annotation::Root ), |
| revisionType ( Annotation::None ), pdfAnnot ( nullptr ), pdfPage ( nullptr ), |
| parentDoc ( nullptr ) |
| { |
| } |
| |
| void AnnotationPrivate::addRevision( Annotation *ann, Annotation::RevScope scope, Annotation::RevType type ) |
| { |
| /* Since ownership stays with the caller, create an alias of ann */ |
| revisions.append( ann->d_ptr->makeAlias() ); |
| |
| /* Set revision properties */ |
| revisionScope = scope; |
| revisionType = type; |
| } |
| |
| AnnotationPrivate::~AnnotationPrivate() |
| { |
| // Delete all children revisions |
| qDeleteAll( revisions ); |
| |
| // Release Annot object |
| if (pdfAnnot) |
| pdfAnnot->decRefCnt(); |
| } |
| |
| void AnnotationPrivate::tieToNativeAnnot(Annot *ann, ::Page *page, Poppler::DocumentData * doc) |
| { |
| if (pdfAnnot) |
| { |
| error(errIO, -1, "Annotation is already tied"); |
| return; |
| } |
| |
| pdfAnnot = ann; |
| pdfPage = page; |
| parentDoc = doc; |
| |
| pdfAnnot->incRefCnt(); |
| } |
| |
| /* This method is called when a new annotation is created, after pdfAnnot and |
| * pdfPage have been set */ |
| void AnnotationPrivate::flushBaseAnnotationProperties() |
| { |
| Q_ASSERT ( pdfPage ); |
| |
| Annotation *q = makeAlias(); // Setters are defined in the public class |
| |
| // Since pdfAnnot has been set, this calls will write in the Annot object |
| q->setAuthor(author); |
| q->setContents(contents); |
| q->setUniqueName(uniqueName); |
| q->setModificationDate(modDate); |
| q->setCreationDate(creationDate); |
| q->setFlags(flags); |
| //q->setBoundary(boundary); -- already set by subclass-specific code |
| q->setStyle(style); |
| q->setPopup(popup); |
| |
| // Flush revisions |
| foreach (Annotation *r, revisions) |
| { |
| // TODO: Flush revision |
| delete r; // Object is no longer needed |
| } |
| |
| delete q; |
| |
| // Clear some members to save memory |
| author.clear(); |
| contents.clear(); |
| uniqueName.clear(); |
| revisions.clear(); |
| } |
| |
| // Returns matrix to convert from user space coords (oriented according to the |
| // specified rotation) to normalized coords |
| void AnnotationPrivate::fillNormalizationMTX(double MTX[6], int pageRotation) const |
| { |
| Q_ASSERT ( pdfPage ); |
| |
| // build a normalized transform matrix for this page at 100% scale |
| GfxState * gfxState = new GfxState( 72.0, 72.0, pdfPage->getCropBox(), pageRotation, true ); |
| const double * gfxCTM = gfxState->getCTM(); |
| |
| double w = pdfPage->getCropWidth(); |
| double h = pdfPage->getCropHeight(); |
| |
| // Swap width and height if the page is rotated landscape or seascape |
| if ( pageRotation == 90 || pageRotation == 270 ) |
| { |
| double t = w; |
| w = h; |
| h = t; |
| } |
| |
| for ( int i = 0; i < 6; i+=2 ) |
| { |
| MTX[i] = gfxCTM[i] / w; |
| MTX[i+1] = gfxCTM[i+1] / h; |
| } |
| delete gfxState; |
| } |
| |
| // Returns matrix to convert from user space coords (i.e. those that are stored |
| // in the PDF file) to normalized coords (i.e. those that we expose to clients). |
| // This method also applies a rotation around the top-left corner if the |
| // FixedRotation flag is set. |
| void AnnotationPrivate::fillTransformationMTX(double MTX[6]) const |
| { |
| Q_ASSERT ( pdfPage ); |
| Q_ASSERT ( pdfAnnot ); |
| |
| const int pageRotate = pdfPage->getRotate(); |
| |
| if ( pageRotate == 0 || ( pdfAnnot->getFlags() & Annot::flagNoRotate ) == 0 ) |
| { |
| // Use the normalization matrix for this page's rotation |
| fillNormalizationMTX( MTX, pageRotate ); |
| } |
| else |
| { |
| // Clients expect coordinates relative to this page's rotation, but |
| // FixedRotation annotations internally use unrotated coordinates: |
| // construct matrix to both normalize and rotate coordinates using the |
| // top-left corner as rotation pivot |
| |
| double MTXnorm[6]; |
| fillNormalizationMTX( MTXnorm, pageRotate ); |
| |
| QTransform transform( MTXnorm[0], MTXnorm[1], MTXnorm[2], |
| MTXnorm[3], MTXnorm[4], MTXnorm[5] ); |
| transform.translate( +pdfAnnot->getXMin(), +pdfAnnot->getYMax() ); |
| transform.rotate( pageRotate ); |
| transform.translate( -pdfAnnot->getXMin(), -pdfAnnot->getYMax() ); |
| |
| MTX[0] = transform.m11(); |
| MTX[1] = transform.m12(); |
| MTX[2] = transform.m21(); |
| MTX[3] = transform.m22(); |
| MTX[4] = transform.dx(); |
| MTX[5] = transform.dy(); |
| } |
| } |
| |
| QRectF AnnotationPrivate::fromPdfRectangle(const PDFRectangle &r) const |
| { |
| double swp, MTX[6]; |
| fillTransformationMTX(MTX); |
| |
| QPointF p1, p2; |
| XPDFReader::transform( MTX, r.x1, r.y1, p1 ); |
| XPDFReader::transform( MTX, r.x2, r.y2, p2 ); |
| |
| double tl_x = p1.x(); |
| double tl_y = p1.y(); |
| double br_x = p2.x(); |
| double br_y = p2.y(); |
| |
| if (tl_x > br_x) |
| { |
| swp = tl_x; |
| tl_x = br_x; |
| br_x = swp; |
| } |
| |
| if (tl_y > br_y) |
| { |
| swp = tl_y; |
| tl_y = br_y; |
| br_y = swp; |
| } |
| |
| return QRectF( QPointF(tl_x,tl_y) , QPointF(br_x,br_y) ); |
| } |
| |
| // This function converts a boundary QRectF in normalized coords to a |
| // PDFRectangle in user coords. If the FixedRotation flag is set, this function |
| // also applies a rotation around the top-left corner: it's the inverse of |
| // the transformation produced by fillTransformationMTX, but we can't use |
| // fillTransformationMTX here because it relies on the native annotation |
| // object's boundary rect to be already set up. |
| PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFlags) const |
| { |
| Q_ASSERT ( pdfPage ); |
| |
| const int pageRotate = pdfPage->getRotate(); |
| |
| double MTX[6]; |
| fillNormalizationMTX( MTX, pageRotate ); |
| |
| double tl_x, tl_y, br_x, br_y, swp; |
| XPDFReader::invTransform( MTX, r.topLeft(), tl_x, tl_y ); |
| XPDFReader::invTransform( MTX, r.bottomRight(), br_x, br_y ); |
| |
| if (tl_x > br_x) |
| { |
| swp = tl_x; |
| tl_x = br_x; |
| br_x = swp; |
| } |
| |
| if (tl_y > br_y) |
| { |
| swp = tl_y; |
| tl_y = br_y; |
| br_y = swp; |
| } |
| |
| const int rotationFixUp = ( rFlags & Annotation::FixedRotation ) ? pageRotate : 0; |
| const double width = br_x - tl_x; |
| const double height = br_y - tl_y; |
| |
| if ( rotationFixUp == 0 ) |
| return PDFRectangle(tl_x, tl_y, br_x, br_y); |
| else if ( rotationFixUp == 90 ) |
| return PDFRectangle(tl_x, tl_y - width, tl_x + height, tl_y); |
| else if ( rotationFixUp == 180 ) |
| return PDFRectangle(br_x, tl_y - height, br_x + width, tl_y); |
| else // rotationFixUp == 270 |
| return PDFRectangle(br_x, br_y - width, br_x + height, br_y); |
| } |
| |
| AnnotPath * AnnotationPrivate::toAnnotPath(const QLinkedList<QPointF> &list) const |
| { |
| const int count = list.size(); |
| std::vector<AnnotCoord> ac; |
| ac.reserve(count); |
| |
| double MTX[6]; |
| fillTransformationMTX(MTX); |
| |
| foreach (const QPointF &p, list) |
| { |
| double x, y; |
| XPDFReader::invTransform( MTX, p, x, y ); |
| ac.emplace_back(x, y); |
| } |
| |
| return new AnnotPath(std::move(ac)); |
| } |
| |
| QList<Annotation*> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet<Annotation::SubType> &subtypes, int parentID) |
| { |
| Annots* annots = pdfPage->getAnnots(); |
| const uint numAnnotations = annots->getNumAnnots(); |
| if ( numAnnotations == 0 ) |
| { |
| return QList<Annotation*>(); |
| } |
| |
| const bool wantTextAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AText); |
| const bool wantLineAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALine); |
| const bool wantGeomAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AGeom); |
| const bool wantHighlightAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AHighlight); |
| const bool wantStampAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AStamp); |
| const bool wantInkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AInk); |
| const bool wantLinkAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ALink); |
| const bool wantCaretAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ACaret); |
| const bool wantFileAttachmentAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AFileAttachment); |
| const bool wantSoundAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::ASound); |
| const bool wantMovieAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AMovie); |
| const bool wantScreenAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AScreen); |
| const bool wantWidgetAnnotations = subtypes.isEmpty() || subtypes.contains(Annotation::AWidget); |
| |
| // Create Annotation objects and tie to their native Annot |
| QList<Annotation*> res; |
| for ( uint k = 0; k < numAnnotations; k++ ) |
| { |
| // get the j-th annotation |
| Annot * ann = annots->getAnnot( k ); |
| if ( !ann ) |
| { |
| error(errInternal, -1, "Annot {0:ud} is null", k); |
| continue; |
| } |
| |
| // Check parent annotation |
| AnnotMarkup * markupann = dynamic_cast< AnnotMarkup * >( ann ); |
| if (!markupann) |
| { |
| // Assume it's a root annotation, and skip if user didn't request it |
| if (parentID != -1) |
| continue; |
| } |
| else if (markupann->getInReplyToID() != parentID) |
| continue; |
| |
| /* Create Annotation of the right subclass */ |
| Annotation * annotation = nullptr; |
| Annot::AnnotSubtype subType = ann->getType(); |
| |
| switch ( subType ) |
| { |
| case Annot::typeText: |
| if (!wantTextAnnotations) |
| continue; |
| annotation = new TextAnnotation(TextAnnotation::Linked); |
| break; |
| case Annot::typeFreeText: |
| if (!wantTextAnnotations) |
| continue; |
| annotation = new TextAnnotation(TextAnnotation::InPlace); |
| break; |
| case Annot::typeLine: |
| if (!wantLineAnnotations) |
| continue; |
| annotation = new LineAnnotation(LineAnnotation::StraightLine); |
| break; |
| case Annot::typePolygon: |
| case Annot::typePolyLine: |
| if (!wantLineAnnotations) |
| continue; |
| annotation = new LineAnnotation(LineAnnotation::Polyline); |
| break; |
| case Annot::typeSquare: |
| case Annot::typeCircle: |
| if (!wantGeomAnnotations) |
| continue; |
| annotation = new GeomAnnotation(); |
| break; |
| case Annot::typeHighlight: |
| case Annot::typeUnderline: |
| case Annot::typeSquiggly: |
| case Annot::typeStrikeOut: |
| if (!wantHighlightAnnotations) |
| continue; |
| annotation = new HighlightAnnotation(); |
| break; |
| case Annot::typeStamp: |
| if (!wantStampAnnotations) |
| continue; |
| annotation = new StampAnnotation(); |
| break; |
| case Annot::typeInk: |
| if (!wantInkAnnotations) |
| continue; |
| annotation = new InkAnnotation(); |
| break; |
| case Annot::typeLink: /* TODO: Move logic to getters */ |
| { |
| if (!wantLinkAnnotations) |
| continue; |
| // parse Link params |
| AnnotLink * linkann = static_cast< AnnotLink * >( ann ); |
| LinkAnnotation * l = new LinkAnnotation(); |
| annotation = l; |
| |
| // -> hlMode |
| l->setLinkHighlightMode( (LinkAnnotation::HighlightMode)linkann->getLinkEffect() ); |
| |
| // -> link region |
| // TODO |
| |
| // reading link action |
| if ( linkann->getAction() ) |
| { |
| Link * popplerLink = PageData::convertLinkActionToLink( linkann->getAction(), doc, QRectF() ); |
| if ( popplerLink ) |
| { |
| l->setLinkDestination( popplerLink ); |
| } |
| } |
| break; |
| } |
| case Annot::typeCaret: |
| if (!wantCaretAnnotations) |
| continue; |
| annotation = new CaretAnnotation(); |
| break; |
| case Annot::typeFileAttachment: /* TODO: Move logic to getters */ |
| { |
| if (!wantFileAttachmentAnnotations) |
| continue; |
| AnnotFileAttachment * attachann = static_cast< AnnotFileAttachment * >( ann ); |
| FileAttachmentAnnotation * f = new FileAttachmentAnnotation(); |
| annotation = f; |
| // -> fileIcon |
| f->setFileIconName( QString::fromLatin1( attachann->getName()->c_str() ) ); |
| // -> embeddedFile |
| FileSpec *filespec = new FileSpec( attachann->getFile() ); |
| f->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( filespec ) ) ); |
| break; |
| } |
| case Annot::typeSound: /* TODO: Move logic to getters */ |
| { |
| if (!wantSoundAnnotations) |
| continue; |
| AnnotSound * soundann = static_cast< AnnotSound * >( ann ); |
| SoundAnnotation * s = new SoundAnnotation(); |
| annotation = s; |
| |
| // -> soundIcon |
| s->setSoundIconName( QString::fromLatin1( soundann->getName()->c_str() ) ); |
| // -> sound |
| s->setSound( new SoundObject( soundann->getSound() ) ); |
| break; |
| } |
| case Annot::typeMovie: /* TODO: Move logic to getters */ |
| { |
| if (!wantMovieAnnotations) |
| continue; |
| AnnotMovie * movieann = static_cast< AnnotMovie * >( ann ); |
| MovieAnnotation * m = new MovieAnnotation(); |
| annotation = m; |
| |
| // -> movie |
| MovieObject *movie = new MovieObject( movieann ); |
| m->setMovie( movie ); |
| // -> movieTitle |
| const GooString * movietitle = movieann->getTitle(); |
| if ( movietitle ) |
| m->setMovieTitle( QString::fromLatin1( movietitle->c_str() ) ); |
| break; |
| } |
| case Annot::typeScreen: |
| { |
| if (!wantScreenAnnotations) |
| continue; |
| AnnotScreen * screenann = static_cast< AnnotScreen * >( ann ); |
| // TODO Support other link types than Link::Rendition in ScreenAnnotation |
| if (!screenann->getAction() || screenann->getAction()->getKind() != actionRendition) |
| continue; |
| ScreenAnnotation * s = new ScreenAnnotation(); |
| annotation = s; |
| |
| // -> screen |
| Link * popplerLink = PageData::convertLinkActionToLink( screenann->getAction(), doc, QRectF() ); |
| s->setAction( static_cast<Poppler::LinkRendition *>(popplerLink) ); |
| |
| // -> screenTitle |
| const GooString * screentitle = screenann->getTitle(); |
| if ( screentitle ) |
| s->setScreenTitle( UnicodeParsedString( screentitle ) ); |
| break; |
| } |
| case Annot::typePopup: |
| continue; // popups are parsed by Annotation's window() getter |
| case Annot::typeUnknown: |
| continue; // special case for ignoring unknown annotations |
| case Annot::typeWidget: |
| if (!wantWidgetAnnotations) |
| continue; |
| annotation = new WidgetAnnotation(); |
| break; |
| case Annot::typeRichMedia: |
| { |
| const AnnotRichMedia * annotRichMedia = static_cast< AnnotRichMedia * >( ann ); |
| |
| RichMediaAnnotation *richMediaAnnotation = new RichMediaAnnotation; |
| |
| const AnnotRichMedia::Settings *annotSettings = annotRichMedia->getSettings(); |
| if ( annotSettings ) { |
| RichMediaAnnotation::Settings *settings = new RichMediaAnnotation::Settings; |
| |
| if ( annotSettings->getActivation() ) { |
| RichMediaAnnotation::Activation *activation = new RichMediaAnnotation::Activation; |
| |
| switch ( annotSettings->getActivation()->getCondition() ) |
| { |
| case AnnotRichMedia::Activation::conditionPageOpened: |
| activation->setCondition( RichMediaAnnotation::Activation::PageOpened ); |
| break; |
| case AnnotRichMedia::Activation::conditionPageVisible: |
| activation->setCondition( RichMediaAnnotation::Activation::PageVisible ); |
| break; |
| case AnnotRichMedia::Activation::conditionUserAction: |
| activation->setCondition( RichMediaAnnotation::Activation::UserAction ); |
| break; |
| } |
| |
| settings->setActivation( activation ); |
| } |
| |
| if ( annotSettings->getDeactivation() ) { |
| RichMediaAnnotation::Deactivation *deactivation = new RichMediaAnnotation::Deactivation; |
| |
| switch ( annotSettings->getDeactivation()->getCondition() ) |
| { |
| case AnnotRichMedia::Deactivation::conditionPageClosed: |
| deactivation->setCondition( RichMediaAnnotation::Deactivation::PageClosed ); |
| break; |
| case AnnotRichMedia::Deactivation::conditionPageInvisible: |
| deactivation->setCondition( RichMediaAnnotation::Deactivation::PageInvisible ); |
| break; |
| case AnnotRichMedia::Deactivation::conditionUserAction: |
| deactivation->setCondition( RichMediaAnnotation::Deactivation::UserAction ); |
| break; |
| } |
| |
| settings->setDeactivation( deactivation ); |
| } |
| |
| richMediaAnnotation->setSettings( settings ); |
| } |
| |
| const AnnotRichMedia::Content *annotContent = annotRichMedia->getContent(); |
| if ( annotContent ) { |
| RichMediaAnnotation::Content *content = new RichMediaAnnotation::Content; |
| |
| const int configurationsCount = annotContent->getConfigurationsCount(); |
| if ( configurationsCount > 0 ) { |
| QList< RichMediaAnnotation::Configuration* > configurations; |
| |
| for ( int i = 0; i < configurationsCount; ++i ) { |
| const AnnotRichMedia::Configuration *annotConfiguration = annotContent->getConfiguration( i ); |
| if ( !annotConfiguration ) |
| continue; |
| |
| RichMediaAnnotation::Configuration *configuration = new RichMediaAnnotation::Configuration; |
| |
| if ( annotConfiguration->getName() ) |
| configuration->setName( UnicodeParsedString( annotConfiguration->getName() ) ); |
| |
| switch ( annotConfiguration->getType() ) |
| { |
| case AnnotRichMedia::Configuration::type3D: |
| configuration->setType( RichMediaAnnotation::Configuration::Type3D ); |
| break; |
| case AnnotRichMedia::Configuration::typeFlash: |
| configuration->setType( RichMediaAnnotation::Configuration::TypeFlash ); |
| break; |
| case AnnotRichMedia::Configuration::typeSound: |
| configuration->setType( RichMediaAnnotation::Configuration::TypeSound ); |
| break; |
| case AnnotRichMedia::Configuration::typeVideo: |
| configuration->setType( RichMediaAnnotation::Configuration::TypeVideo ); |
| break; |
| } |
| |
| const int instancesCount = annotConfiguration->getInstancesCount(); |
| if ( instancesCount > 0 ) { |
| QList< RichMediaAnnotation::Instance* > instances; |
| |
| for ( int j = 0; j < instancesCount; ++j ) { |
| const AnnotRichMedia::Instance *annotInstance = annotConfiguration->getInstance( j ); |
| if ( !annotInstance ) |
| continue; |
| |
| RichMediaAnnotation::Instance *instance = new RichMediaAnnotation::Instance; |
| |
| switch ( annotInstance->getType() ) |
| { |
| case AnnotRichMedia::Instance::type3D: |
| instance->setType( RichMediaAnnotation::Instance::Type3D ); |
| break; |
| case AnnotRichMedia::Instance::typeFlash: |
| instance->setType( RichMediaAnnotation::Instance::TypeFlash ); |
| break; |
| case AnnotRichMedia::Instance::typeSound: |
| instance->setType( RichMediaAnnotation::Instance::TypeSound ); |
| break; |
| case AnnotRichMedia::Instance::typeVideo: |
| instance->setType( RichMediaAnnotation::Instance::TypeVideo ); |
| break; |
| } |
| |
| const AnnotRichMedia::Params *annotParams = annotInstance->getParams(); |
| if ( annotParams ) { |
| RichMediaAnnotation::Params *params = new RichMediaAnnotation::Params; |
| |
| if ( annotParams->getFlashVars() ) |
| params->setFlashVars( UnicodeParsedString( annotParams->getFlashVars() ) ); |
| |
| instance->setParams( params ); |
| } |
| |
| instances.append( instance ); |
| } |
| |
| configuration->setInstances( instances ); |
| } |
| |
| configurations.append( configuration ); |
| } |
| |
| content->setConfigurations( configurations ); |
| } |
| |
| const int assetsCount = annotContent->getAssetsCount(); |
| if ( assetsCount > 0 ) { |
| QList< RichMediaAnnotation::Asset* > assets; |
| |
| for ( int i = 0; i < assetsCount; ++i ) { |
| const AnnotRichMedia::Asset *annotAsset = annotContent->getAsset( i ); |
| if ( !annotAsset ) |
| continue; |
| |
| RichMediaAnnotation::Asset *asset = new RichMediaAnnotation::Asset; |
| |
| if ( annotAsset->getName() ) |
| asset->setName( UnicodeParsedString( annotAsset->getName() ) ); |
| |
| FileSpec *fileSpec = new FileSpec( annotAsset->getFileSpec() ); |
| asset->setEmbeddedFile( new EmbeddedFile( *new EmbeddedFileData( fileSpec ) ) ); |
| |
| assets.append( asset ); |
| } |
| |
| content->setAssets( assets ); |
| } |
| |
| richMediaAnnotation->setContent( content ); |
| } |
| |
| annotation = richMediaAnnotation; |
| |
| break; |
| } |
| default: |
| { |
| #define CASE_FOR_TYPE( thetype ) \ |
| case Annot::type ## thetype: \ |
| error(errUnimplemented, -1, "Annotation " #thetype " not supported"); \ |
| break; |
| switch ( subType ) |
| { |
| CASE_FOR_TYPE( PrinterMark ) |
| CASE_FOR_TYPE( TrapNet ) |
| CASE_FOR_TYPE( Watermark ) |
| CASE_FOR_TYPE( 3D ) |
| default: error(errUnimplemented, -1, "Annotation {0:d} not supported", subType); |
| } |
| continue; |
| #undef CASE_FOR_TYPE |
| } |
| } |
| |
| annotation->d_ptr->tieToNativeAnnot(ann, pdfPage, doc); |
| res.append(annotation); |
| } |
| |
| return res; |
| } |
| |
| Ref AnnotationPrivate::pdfObjectReference() const |
| { |
| if (pdfAnnot == nullptr) |
| { |
| return Ref::INVALID(); |
| } |
| |
| return pdfAnnot->getRef(); |
| } |
| |
| Link* AnnotationPrivate::additionalAction( Annotation::AdditionalActionType type ) const |
| { |
| if ( pdfAnnot->getType() != Annot::typeScreen && pdfAnnot->getType() != Annot::typeWidget ) |
| return nullptr; |
| |
| const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); |
| |
| ::LinkAction *linkAction = nullptr; |
| if ( pdfAnnot->getType() == Annot::typeScreen ) |
| linkAction = static_cast<AnnotScreen*>( pdfAnnot )->getAdditionalAction( actionType ); |
| else |
| linkAction = static_cast<AnnotWidget*>( pdfAnnot )->getAdditionalAction( actionType ); |
| |
| Link *link = nullptr; |
| |
| if ( linkAction ) |
| link = PageData::convertLinkActionToLink( linkAction, parentDoc, QRectF() ); |
| |
| return link; |
| } |
| |
| void AnnotationPrivate::addAnnotationToPage(::Page *pdfPage, DocumentData *doc, const Annotation * ann) |
| { |
| if (ann->d_ptr->pdfAnnot != nullptr) |
| { |
| error(errIO, -1, "Annotation is already tied"); |
| return; |
| } |
| |
| // Unimplemented annotations can't be created by the user because their ctor |
| // is private. Therefore, createNativeAnnot will never return 0 |
| Annot *nativeAnnot = ann->d_ptr->createNativeAnnot(pdfPage, doc); |
| Q_ASSERT(nativeAnnot); |
| pdfPage->addAnnot(nativeAnnot); |
| } |
| |
| void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotation * ann) |
| { |
| if (ann->d_ptr->pdfAnnot == nullptr) |
| { |
| error(errIO, -1, "Annotation is not tied"); |
| return; |
| } |
| |
| if (ann->d_ptr->pdfPage != pdfPage) |
| { |
| error(errIO, -1, "Annotation doesn't belong to the specified page"); |
| return; |
| } |
| |
| // Remove annotation |
| pdfPage->removeAnnot(ann->d_ptr->pdfAnnot); |
| |
| // Destroy object |
| delete ann; |
| } |
| |
| class Annotation::Style::Private : public QSharedData |
| { |
| public: |
| Private() |
| : opacity( 1.0 ), width( 1.0 ), lineStyle( Solid ), xCorners( 0.0 ), |
| yCorners( 0.0 ), lineEffect( NoEffect ), effectIntensity( 1.0 ) |
| { |
| dashArray.resize(1); |
| dashArray[0] = 3; |
| } |
| |
| QColor color; |
| double opacity; |
| double width; |
| Annotation::LineStyle lineStyle; |
| double xCorners; |
| double yCorners; |
| QVector<double> dashArray; |
| Annotation::LineEffect lineEffect; |
| double effectIntensity; |
| }; |
| |
| Annotation::Style::Style() |
| : d ( new Private ) |
| { |
| } |
| |
| Annotation::Style::Style( const Style &other ) |
| : d( other.d ) |
| { |
| } |
| |
| Annotation::Style& Annotation::Style::operator=( const Style &other ) |
| { |
| if ( this != &other ) |
| d = other.d; |
| |
| return *this; |
| } |
| |
| Annotation::Style::~Style() |
| { |
| } |
| |
| QColor Annotation::Style::color() const |
| { |
| return d->color; |
| } |
| |
| void Annotation::Style::setColor(const QColor &color) |
| { |
| d->color = color; |
| } |
| |
| double Annotation::Style::opacity() const |
| { |
| return d->opacity; |
| } |
| |
| void Annotation::Style::setOpacity(double opacity) |
| { |
| d->opacity = opacity; |
| } |
| |
| double Annotation::Style::width() const |
| { |
| return d->width; |
| } |
| |
| void Annotation::Style::setWidth(double width) |
| { |
| d->width = width; |
| } |
| |
| Annotation::LineStyle Annotation::Style::lineStyle() const |
| { |
| return d->lineStyle; |
| } |
| |
| void Annotation::Style::setLineStyle(Annotation::LineStyle style) |
| { |
| d->lineStyle = style; |
| } |
| |
| double Annotation::Style::xCorners() const |
| { |
| return d->xCorners; |
| } |
| |
| void Annotation::Style::setXCorners(double radius) |
| { |
| d->xCorners = radius; |
| } |
| |
| double Annotation::Style::yCorners() const |
| { |
| return d->yCorners; |
| } |
| |
| void Annotation::Style::setYCorners(double radius) |
| { |
| d->yCorners = radius; |
| } |
| |
| const QVector<double>& Annotation::Style::dashArray() const |
| { |
| return d->dashArray; |
| } |
| |
| void Annotation::Style::setDashArray(const QVector<double> &array) |
| { |
| d->dashArray = array; |
| } |
| |
| Annotation::LineEffect Annotation::Style::lineEffect() const |
| { |
| return d->lineEffect; |
| } |
| |
| void Annotation::Style::setLineEffect(Annotation::LineEffect effect) |
| { |
| d->lineEffect = effect; |
| } |
| |
| double Annotation::Style::effectIntensity() const |
| { |
| return d->effectIntensity; |
| } |
| |
| void Annotation::Style::setEffectIntensity(double intens) |
| { |
| d->effectIntensity = intens; |
| } |
| |
| class Annotation::Popup::Private : public QSharedData |
| { |
| public: |
| Private() |
| : flags( -1 ) {} |
| |
| int flags; |
| QRectF geometry; |
| QString title; |
| QString summary; |
| QString text; |
| }; |
| |
| Annotation::Popup::Popup() |
| : d ( new Private ) |
| { |
| } |
| |
| Annotation::Popup::Popup( const Popup &other ) |
| : d( other.d ) |
| { |
| } |
| |
| Annotation::Popup& Annotation::Popup::operator=( const Popup &other ) |
| { |
| if ( this != &other ) |
| d = other.d; |
| |
| return *this; |
| } |
| |
| Annotation::Popup::~Popup() |
| { |
| } |
| |
| int Annotation::Popup::flags() const |
| { |
| return d->flags; |
| } |
| |
| void Annotation::Popup::setFlags( int flags ) |
| { |
| d->flags = flags; |
| } |
| |
| QRectF Annotation::Popup::geometry() const |
| { |
| return d->geometry; |
| } |
| |
| void Annotation::Popup::setGeometry( const QRectF &geom ) |
| { |
| d->geometry = geom; |
| } |
| |
| QString Annotation::Popup::title() const |
| { |
| return d->title; |
| } |
| |
| void Annotation::Popup::setTitle( const QString &title ) |
| { |
| d->title = title; |
| } |
| |
| QString Annotation::Popup::summary() const |
| { |
| return d->summary; |
| } |
| |
| void Annotation::Popup::setSummary( const QString &summary ) |
| { |
| d->summary = summary; |
| } |
| |
| QString Annotation::Popup::text() const |
| { |
| return d->text; |
| } |
| |
| void Annotation::Popup::setText( const QString &text ) |
| { |
| d->text = text; |
| } |
| |
| Annotation::Annotation( AnnotationPrivate &dd ) |
| : d_ptr( &dd ) |
| { |
| } |
| |
| Annotation::~Annotation() |
| { |
| } |
| |
| Annotation::Annotation( AnnotationPrivate &dd, const QDomNode &annNode ) |
| : d_ptr( &dd ) |
| { |
| Q_D( Annotation ); |
| |
| // get the [base] element of the annotation node |
| QDomElement e = AnnotationUtils::findChildElement( annNode, QStringLiteral("base") ); |
| if ( e.isNull() ) |
| return; |
| |
| Style s; |
| Popup w; |
| |
| // parse -contents- attributes |
| if ( e.hasAttribute( QStringLiteral("author") ) ) |
| setAuthor(e.attribute( QStringLiteral("author") )); |
| if ( e.hasAttribute( QStringLiteral("contents") ) ) |
| setContents(e.attribute( QStringLiteral("contents") )); |
| if ( e.hasAttribute( QStringLiteral("uniqueName") ) ) |
| setUniqueName(e.attribute( QStringLiteral("uniqueName") )); |
| if ( e.hasAttribute( QStringLiteral("modifyDate") ) ) |
| setModificationDate(QDateTime::fromString( e.attribute( QStringLiteral("modifyDate") ) )); |
| if ( e.hasAttribute( QStringLiteral("creationDate") ) ) |
| setCreationDate(QDateTime::fromString( e.attribute( QStringLiteral("creationDate") ) )); |
| |
| // parse -other- attributes |
| if ( e.hasAttribute( QStringLiteral("flags") ) ) |
| setFlags(e.attribute( QStringLiteral("flags") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("color") ) ) |
| s.setColor(QColor( e.attribute( QStringLiteral("color") ) )); |
| if ( e.hasAttribute( QStringLiteral("opacity") ) ) |
| s.setOpacity(e.attribute( QStringLiteral("opacity") ).toDouble()); |
| |
| // parse -the-subnodes- (describing Style, Window, Revision(s) structures) |
| // Note: all subnodes if present must be 'attributes complete' |
| QDomNode eSubNode = e.firstChild(); |
| while ( eSubNode.isElement() ) |
| { |
| QDomElement ee = eSubNode.toElement(); |
| eSubNode = eSubNode.nextSibling(); |
| |
| // parse boundary |
| if ( ee.tagName() == QLatin1String("boundary") ) |
| { |
| QRectF brect; |
| brect.setLeft(ee.attribute( QStringLiteral("l") ).toDouble()); |
| brect.setTop(ee.attribute( QStringLiteral("t") ).toDouble()); |
| brect.setRight(ee.attribute( QStringLiteral("r") ).toDouble()); |
| brect.setBottom(ee.attribute( QStringLiteral("b") ).toDouble()); |
| setBoundary(brect); |
| } |
| // parse penStyle if not default |
| else if ( ee.tagName() == QLatin1String("penStyle") ) |
| { |
| s.setWidth(ee.attribute( QStringLiteral("width") ).toDouble()); |
| s.setLineStyle((LineStyle)ee.attribute( QStringLiteral("style") ).toInt()); |
| s.setXCorners(ee.attribute( QStringLiteral("xcr") ).toDouble()); |
| s.setYCorners(ee.attribute( QStringLiteral("ycr") ).toDouble()); |
| |
| // Try to parse dash array (new format) |
| QVector<double> dashArray; |
| |
| QDomNode eeSubNode = ee.firstChild(); |
| while ( eeSubNode.isElement() ) |
| { |
| QDomElement eee = eeSubNode.toElement(); |
| eeSubNode = eeSubNode.nextSibling(); |
| |
| if ( eee.tagName() != QLatin1String("dashsegm") ) |
| continue; |
| |
| dashArray.append(eee.attribute( QStringLiteral("len") ).toDouble()); |
| } |
| |
| // If no segments were found use marks/spaces (old format) |
| if ( dashArray.size() == 0 ) |
| { |
| dashArray.append(ee.attribute( QStringLiteral("marks") ).toDouble()); |
| dashArray.append(ee.attribute( QStringLiteral("spaces") ).toDouble()); |
| } |
| |
| s.setDashArray(dashArray); |
| } |
| // parse effectStyle if not default |
| else if ( ee.tagName() == QLatin1String("penEffect") ) |
| { |
| s.setLineEffect((LineEffect)ee.attribute( QStringLiteral("effect") ).toInt()); |
| s.setEffectIntensity(ee.attribute( QStringLiteral("intensity") ).toDouble()); |
| } |
| // parse window if present |
| else if ( ee.tagName() == QLatin1String("window") ) |
| { |
| QRectF geom; |
| geom.setX(ee.attribute( QStringLiteral("top") ).toDouble()); |
| geom.setY(ee.attribute( QStringLiteral("left") ).toDouble()); |
| |
| if (ee.hasAttribute(QStringLiteral("widthDouble"))) |
| geom.setWidth(ee.attribute( QStringLiteral("widthDouble") ).toDouble()); |
| else |
| geom.setWidth(ee.attribute( QStringLiteral("width") ).toDouble()); |
| |
| if (ee.hasAttribute(QStringLiteral("widthDouble"))) |
| geom.setHeight(ee.attribute( QStringLiteral("heightDouble") ).toDouble()); |
| else |
| geom.setHeight(ee.attribute( QStringLiteral("height") ).toDouble()); |
| |
| w.setGeometry(geom); |
| |
| w.setFlags(ee.attribute( QStringLiteral("flags") ).toInt()); |
| w.setTitle(ee.attribute( QStringLiteral("title") )); |
| w.setSummary(ee.attribute( QStringLiteral("summary") )); |
| // parse window subnodes |
| QDomNode winNode = ee.firstChild(); |
| for ( ; winNode.isElement(); winNode = winNode.nextSibling() ) |
| { |
| QDomElement winElement = winNode.toElement(); |
| if ( winElement.tagName() == QLatin1String("text") ) |
| w.setText(winElement.firstChild().toCDATASection().data()); |
| } |
| } |
| } |
| |
| setStyle(s); // assign parsed style |
| setPopup(w); // assign parsed window |
| |
| // get the [revisions] element of the annotation node |
| QDomNode revNode = annNode.firstChild(); |
| for ( ; revNode.isElement(); revNode = revNode.nextSibling() ) |
| { |
| QDomElement revElement = revNode.toElement(); |
| if ( revElement.tagName() != QLatin1String("revision") ) |
| continue; |
| |
| Annotation *reply = AnnotationUtils::createAnnotation( revElement ); |
| |
| if (reply) // if annotation is valid, add as a revision of this annotation |
| { |
| RevScope scope = (RevScope)revElement.attribute( QStringLiteral("revScope") ).toInt(); |
| RevType type = (RevType)revElement.attribute( QStringLiteral("revType") ).toInt(); |
| d->addRevision(reply, scope, type); |
| delete reply; |
| } |
| } |
| } |
| |
| void Annotation::storeBaseAnnotationProperties( QDomNode & annNode, QDomDocument & document ) const |
| { |
| // create [base] element of the annotation node |
| QDomElement e = document.createElement( QStringLiteral("base") ); |
| annNode.appendChild( e ); |
| |
| const Style s = style(); |
| const Popup w = popup(); |
| |
| // store -contents- attributes |
| if ( !author().isEmpty() ) |
| e.setAttribute( QStringLiteral("author"), author() ); |
| if ( !contents().isEmpty() ) |
| e.setAttribute( QStringLiteral("contents"), contents() ); |
| if ( !uniqueName().isEmpty() ) |
| e.setAttribute( QStringLiteral("uniqueName"), uniqueName() ); |
| if ( modificationDate().isValid() ) |
| e.setAttribute( QStringLiteral("modifyDate"), modificationDate().toString() ); |
| if ( creationDate().isValid() ) |
| e.setAttribute( QStringLiteral("creationDate"), creationDate().toString() ); |
| |
| // store -other- attributes |
| if ( flags() ) |
| e.setAttribute( QStringLiteral("flags"), flags() ); |
| if ( s.color().isValid() ) |
| e.setAttribute( QStringLiteral("color"), s.color().name() ); |
| if ( s.opacity() != 1.0 ) |
| e.setAttribute( QStringLiteral("opacity"), QString::number( s.opacity() ) ); |
| |
| // Sub-Node-1 - boundary |
| const QRectF brect = boundary(); |
| QDomElement bE = document.createElement( QStringLiteral("boundary") ); |
| e.appendChild( bE ); |
| bE.setAttribute( QStringLiteral("l"), QString::number( (double)brect.left() ) ); |
| bE.setAttribute( QStringLiteral("t"), QString::number( (double)brect.top() ) ); |
| bE.setAttribute( QStringLiteral("r"), QString::number( (double)brect.right() ) ); |
| bE.setAttribute( QStringLiteral("b"), QString::number( (double)brect.bottom() ) ); |
| |
| // Sub-Node-2 - penStyle |
| const QVector<double> &dashArray = s.dashArray(); |
| if ( s.width() != 1 || s.lineStyle() != Solid || s.xCorners() != 0 || |
| s.yCorners() != 0.0 || dashArray.size() != 1 || dashArray[0] != 3 ) |
| { |
| QDomElement psE = document.createElement( QStringLiteral("penStyle") ); |
| e.appendChild( psE ); |
| psE.setAttribute( QStringLiteral("width"), QString::number( s.width() ) ); |
| psE.setAttribute( QStringLiteral("style"), (int)s.lineStyle() ); |
| psE.setAttribute( QStringLiteral("xcr"), QString::number( s.xCorners() ) ); |
| psE.setAttribute( QStringLiteral("ycr"), QString::number( s.yCorners() ) ); |
| |
| int marks = 3, spaces = 0; // Do not break code relying on marks/spaces |
| if (dashArray.size() != 0) |
| marks = (int)dashArray[0]; |
| if (dashArray.size() > 1) |
| spaces = (int)dashArray[1]; |
| |
| psE.setAttribute( QStringLiteral("marks"), marks ); |
| psE.setAttribute( QStringLiteral("spaces"), spaces ); |
| |
| foreach (double segm, dashArray) |
| { |
| QDomElement pattE = document.createElement( QStringLiteral("dashsegm") ); |
| pattE.setAttribute( QStringLiteral("len"), QString::number( segm ) ); |
| psE.appendChild(pattE); |
| } |
| } |
| |
| // Sub-Node-3 - penEffect |
| if ( s.lineEffect() != NoEffect || s.effectIntensity() != 1.0 ) |
| { |
| QDomElement peE = document.createElement( QStringLiteral("penEffect") ); |
| e.appendChild( peE ); |
| peE.setAttribute( QStringLiteral("effect"), (int)s.lineEffect() ); |
| peE.setAttribute( QStringLiteral("intensity"), QString::number( s.effectIntensity() ) ); |
| } |
| |
| // Sub-Node-4 - window |
| if ( w.flags() != -1 || !w.title().isEmpty() || !w.summary().isEmpty() || |
| !w.text().isEmpty() ) |
| { |
| QDomElement wE = document.createElement( QStringLiteral("window") ); |
| const QRectF geom = w.geometry(); |
| e.appendChild( wE ); |
| wE.setAttribute( QStringLiteral("flags"), w.flags() ); |
| wE.setAttribute( QStringLiteral("top"), QString::number( geom.x() ) ); |
| wE.setAttribute( QStringLiteral("left"), QString::number( geom.y() ) ); |
| wE.setAttribute( QStringLiteral("width"), (int)geom.width() ); |
| wE.setAttribute( QStringLiteral("height"), (int)geom.height() ); |
| wE.setAttribute( QStringLiteral("widthDouble"), QString::number( geom.width() ) ); |
| wE.setAttribute( QStringLiteral("heightDouble"), QString::number( geom.height() ) ); |
| wE.setAttribute( QStringLiteral("title"), w.title() ); |
| wE.setAttribute( QStringLiteral("summary"), w.summary() ); |
| // store window.text as a subnode, because we need escaped data |
| if ( !w.text().isEmpty() ) |
| { |
| QDomElement escapedText = document.createElement( QStringLiteral("text") ); |
| wE.appendChild( escapedText ); |
| QDomCDATASection textCData = document.createCDATASection( w.text() ); |
| escapedText.appendChild( textCData ); |
| } |
| } |
| |
| const QList<Annotation*> revs = revisions(); |
| |
| // create [revision] element of the annotation node (if any) |
| if ( revs.isEmpty() ) |
| return; |
| |
| // add all revisions as children of revisions element |
| foreach (const Annotation *rev, revs) |
| { |
| QDomElement r = document.createElement( QStringLiteral("revision") ); |
| annNode.appendChild( r ); |
| // set element attributes |
| r.setAttribute( QStringLiteral("revScope"), (int)rev->revisionScope() ); |
| r.setAttribute( QStringLiteral("revType"), (int)rev->revisionType() ); |
| // use revision as the annotation element, so fill it up |
| AnnotationUtils::storeAnnotation( rev, r, document ); |
| delete rev; |
| } |
| } |
| |
| QString Annotation::author() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->author; |
| |
| const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(d->pdfAnnot); |
| return markupann ? UnicodeParsedString( markupann->getLabel() ) : QString(); |
| } |
| |
| void Annotation::setAuthor( const QString &author ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->author = author; |
| return; |
| } |
| |
| AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot); |
| if (markupann) |
| { |
| GooString *s = QStringToUnicodeGooString(author); |
| markupann->setLabel(s); |
| delete s; |
| } |
| } |
| |
| QString Annotation::contents() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->contents; |
| |
| return UnicodeParsedString( d->pdfAnnot->getContents() ); |
| } |
| |
| void Annotation::setContents( const QString &contents ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->contents = contents; |
| return; |
| } |
| |
| GooString *s = QStringToUnicodeGooString(contents); |
| d->pdfAnnot->setContents(s); |
| delete s; |
| } |
| |
| QString Annotation::uniqueName() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->uniqueName; |
| |
| return UnicodeParsedString( d->pdfAnnot->getName() ); |
| } |
| |
| void Annotation::setUniqueName( const QString &uniqueName ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->uniqueName = uniqueName; |
| return; |
| } |
| |
| QByteArray ascii = uniqueName.toLatin1(); |
| GooString s(ascii.constData()); |
| d->pdfAnnot->setName(&s); |
| } |
| |
| QDateTime Annotation::modificationDate() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->modDate; |
| |
| if ( d->pdfAnnot->getModified() ) |
| return convertDate( d->pdfAnnot->getModified()->c_str() ); |
| else |
| return QDateTime(); |
| } |
| |
| void Annotation::setModificationDate( const QDateTime &date ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->modDate = date; |
| return; |
| } |
| |
| #if 0 // TODO: Conversion routine is broken |
| if (d->pdfAnnot) |
| { |
| time_t t = date.toTime_t(); |
| GooString *s = timeToDateString(&t); |
| d->pdfAnnot->setModified(s); |
| delete s; |
| } |
| #endif |
| } |
| |
| QDateTime Annotation::creationDate() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->creationDate; |
| |
| const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(d->pdfAnnot); |
| |
| if (markupann && markupann->getDate()) |
| return convertDate( markupann->getDate()->c_str() ); |
| |
| return modificationDate(); |
| } |
| |
| void Annotation::setCreationDate( const QDateTime &date ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->creationDate = date; |
| return; |
| } |
| |
| #if 0 // TODO: Conversion routine is broken |
| AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot); |
| if (markupann) |
| { |
| time_t t = date.toTime_t(); |
| GooString *s = timeToDateString(&t); |
| markupann->setDate(s); |
| delete s; |
| } |
| #endif |
| } |
| |
| static int fromPdfFlags(int flags) |
| { |
| int qtflags = 0; |
| |
| if ( flags & Annot::flagHidden ) |
| qtflags |= Annotation::Hidden; |
| if ( flags & Annot::flagNoZoom ) |
| qtflags |= Annotation::FixedSize; |
| if ( flags & Annot::flagNoRotate ) |
| qtflags |= Annotation::FixedRotation; |
| if ( !( flags & Annot::flagPrint ) ) |
| qtflags |= Annotation::DenyPrint; |
| if ( flags & Annot::flagReadOnly ) |
| qtflags |= (Annotation::DenyWrite | Annotation::DenyDelete); |
| if ( flags & Annot::flagLocked ) |
| qtflags |= Annotation::DenyDelete; |
| if ( flags & Annot::flagToggleNoView ) |
| qtflags |= Annotation::ToggleHidingOnMouse; |
| |
| return qtflags; |
| } |
| |
| static int toPdfFlags(int qtflags) |
| { |
| int flags = 0; |
| |
| if ( qtflags & Annotation::Hidden ) |
| flags |= Annot::flagHidden; |
| if ( qtflags & Annotation::FixedSize ) |
| flags |= Annot::flagNoZoom; |
| if ( qtflags & Annotation::FixedRotation ) |
| flags |= Annot::flagNoRotate; |
| if ( !( qtflags & Annotation::DenyPrint ) ) |
| flags |= Annot::flagPrint; |
| if ( qtflags & Annotation::DenyWrite ) |
| flags |= Annot::flagReadOnly; |
| if ( qtflags & Annotation::DenyDelete ) |
| flags |= Annot::flagLocked; |
| if ( qtflags & Annotation::ToggleHidingOnMouse ) |
| flags |= Annot::flagToggleNoView; |
| |
| return flags; |
| } |
| |
| int Annotation::flags() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->flags; |
| |
| return fromPdfFlags( d->pdfAnnot->getFlags() ); |
| } |
| |
| void Annotation::setFlags( int flags ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->flags = flags; |
| return; |
| } |
| |
| d->pdfAnnot->setFlags(toPdfFlags( flags )); |
| } |
| |
| QRectF Annotation::boundary() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->boundary; |
| |
| const PDFRectangle * rect = d->pdfAnnot->getRect(); |
| return d->fromPdfRectangle( *rect ); |
| } |
| |
| void Annotation::setBoundary( const QRectF &boundary ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->boundary = boundary; |
| return; |
| } |
| |
| PDFRectangle rect = d->boundaryToPdfRectangle( boundary, flags() ); |
| d->pdfAnnot->setRect(&rect); |
| } |
| |
| Annotation::Style Annotation::style() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->style; |
| |
| Style s; |
| s.setColor(convertAnnotColor( d->pdfAnnot->getColor() )); |
| |
| const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(d->pdfAnnot); |
| if (markupann) |
| s.setOpacity( markupann->getOpacity() ); |
| |
| const AnnotBorder *border = d->pdfAnnot->getBorder(); |
| if (border) |
| { |
| if ( border->getType() == AnnotBorder::typeArray ) |
| { |
| const AnnotBorderArray *border_array = static_cast<const AnnotBorderArray*>(border); |
| s.setXCorners( border_array->getHorizontalCorner() ); |
| s.setYCorners( border_array->getVerticalCorner() ); |
| } |
| |
| s.setWidth( border->getWidth() ); |
| s.setLineStyle((Annotation::LineStyle)( 1 << border->getStyle() )); |
| |
| const int dashArrLen = border->getDashLength(); |
| const double* dashArrData = border->getDash(); |
| QVector<double> dashArrVect( dashArrLen ); |
| for (int i = 0; i < dashArrLen; ++i) |
| dashArrVect[i] = dashArrData[i]; |
| s.setDashArray(dashArrVect); |
| } |
| |
| AnnotBorderEffect *border_effect; |
| switch (d->pdfAnnot->getType()) |
| { |
| case Annot::typeFreeText: |
| border_effect = static_cast<AnnotFreeText*>(d->pdfAnnot)->getBorderEffect(); |
| break; |
| case Annot::typeSquare: |
| case Annot::typeCircle: |
| border_effect = static_cast<AnnotGeometry*>(d->pdfAnnot)->getBorderEffect(); |
| break; |
| default: |
| border_effect = nullptr; |
| } |
| if (border_effect) |
| { |
| s.setLineEffect( (Annotation::LineEffect)border_effect->getEffectType() ); |
| s.setEffectIntensity( border_effect->getIntensity() ); |
| } |
| |
| return s; |
| } |
| |
| void Annotation::setStyle( const Annotation::Style& style ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->style = style; |
| return; |
| } |
| |
| d->pdfAnnot->setColor(convertQColor( style.color() )); |
| |
| AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot); |
| if (markupann) |
| markupann->setOpacity( style.opacity() ); |
| |
| auto border = std::make_unique<AnnotBorderArray>(); |
| border->setWidth( style.width() ); |
| border->setHorizontalCorner( style.xCorners() ); |
| border->setVerticalCorner( style.yCorners() ); |
| d->pdfAnnot->setBorder(std::move(border)); |
| } |
| |
| Annotation::Popup Annotation::popup() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->popup; |
| |
| Popup w; |
| AnnotPopup *popup = nullptr; |
| int flags = -1; // Not initialized |
| |
| const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(d->pdfAnnot); |
| if (markupann) |
| { |
| popup = markupann->getPopup(); |
| w.setSummary(UnicodeParsedString( markupann->getSubject() )); |
| } |
| |
| if (popup) |
| { |
| flags = fromPdfFlags( popup->getFlags() ) & ( Annotation::Hidden | |
| Annotation::FixedSize | Annotation::FixedRotation ); |
| |
| if (!popup->getOpen()) |
| flags |= Annotation::Hidden; |
| |
| const PDFRectangle * rect = popup->getRect(); |
| w.setGeometry( d->fromPdfRectangle( *rect ) ); |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeText) |
| { |
| const AnnotText * textann = static_cast<const AnnotText*>(d->pdfAnnot); |
| |
| // Text annotations default to same rect as annotation |
| if (flags == -1) |
| { |
| flags = 0; |
| w.setGeometry( boundary() ); |
| } |
| |
| // If text is not 'opened', force window hiding. if the window |
| // was parsed from popup, the flag should already be set |
| if ( !textann->getOpen() && flags != -1 ) |
| flags |= Annotation::Hidden; |
| } |
| |
| w.setFlags(flags); |
| |
| return w; |
| } |
| |
| void Annotation::setPopup( const Annotation::Popup& popup ) |
| { |
| Q_D( Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->popup = popup; |
| return; |
| } |
| |
| #if 0 /* TODO: Remove old popup and add AnnotPopup to page */ |
| AnnotMarkup *markupann = dynamic_cast<AnnotMarkup*>(d->pdfAnnot); |
| if (!markupann) |
| return; |
| |
| // Create a new AnnotPopup and assign it to pdfAnnot |
| PDFRectangle rect = d->toPdfRectangle( popup.geometry() ); |
| AnnotPopup * p = new AnnotPopup( d->pdfPage->getDoc(), &rect ); |
| p->setOpen( !(popup.flags() & Annotation::Hidden) ); |
| if (!popup.summary().isEmpty()) |
| { |
| GooString *s = QStringToUnicodeGooString(popup.summary()); |
| markupann->setLabel(s); |
| delete s; |
| } |
| markupann->setPopup(p); |
| #endif |
| } |
| |
| Annotation::RevScope Annotation::revisionScope() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->revisionScope; |
| |
| const AnnotMarkup *markupann = dynamic_cast<const AnnotMarkup*>(d->pdfAnnot); |
| |
| if (markupann && markupann->isInReplyTo()) |
| { |
| switch (markupann->getReplyTo()) |
| { |
| case AnnotMarkup::replyTypeR: |
| return Annotation::Reply; |
| case AnnotMarkup::replyTypeGroup: |
| return Annotation::Group; |
| } |
| } |
| |
| return Annotation::Root; // It's not a revision |
| } |
| |
| Annotation::RevType Annotation::revisionType() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| return d->revisionType; |
| |
| const AnnotText *textann = dynamic_cast<const AnnotText*>(d->pdfAnnot); |
| |
| if (textann && textann->isInReplyTo()) |
| { |
| switch (textann->getState()) |
| { |
| case AnnotText::stateMarked: |
| return Annotation::Marked; |
| case AnnotText::stateUnmarked: |
| return Annotation::Unmarked; |
| case AnnotText::stateAccepted: |
| return Annotation::Accepted; |
| case AnnotText::stateRejected: |
| return Annotation::Rejected; |
| case AnnotText::stateCancelled: |
| return Annotation::Cancelled; |
| case AnnotText::stateCompleted: |
| return Annotation::Completed; |
| default: |
| break; |
| } |
| } |
| |
| return Annotation::None; |
| } |
| |
| QList<Annotation*> Annotation::revisions() const |
| { |
| Q_D( const Annotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| /* Return aliases, whose ownership goes to the caller */ |
| QList<Annotation*> res; |
| foreach (Annotation *rev, d->revisions) |
| res.append( rev->d_ptr->makeAlias() ); |
| return res; |
| } |
| |
| /* If the annotation doesn't live in a object on its own (eg bug51361), it |
| * has no ref, therefore it can't have revisions */ |
| if ( !d->pdfAnnot->getHasRef() ) |
| return QList<Annotation*>(); |
| |
| return AnnotationPrivate::findAnnotations( d->pdfPage, d->parentDoc, QSet<Annotation::SubType>(), d->pdfAnnot->getId() ); |
| } |
| |
| //END Annotation implementation |
| |
| |
| /** TextAnnotation [Annotation] */ |
| class TextAnnotationPrivate : public AnnotationPrivate |
| { |
| public: |
| TextAnnotationPrivate(); |
| Annotation * makeAlias() override; |
| Annot* createNativeAnnot(::Page *destPage, DocumentData *doc) override; |
| void setDefaultAppearanceToNative(); |
| std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const; |
| |
| // data fields |
| TextAnnotation::TextType textType; |
| QString textIcon; |
| QFont textFont; |
| QColor textColor; |
| int inplaceAlign; // 0:left, 1:center, 2:right |
| QVector<QPointF> inplaceCallout; |
| TextAnnotation::InplaceIntent inplaceIntent; |
| }; |
| |
| TextAnnotationPrivate::TextAnnotationPrivate() |
| : AnnotationPrivate(), textType( TextAnnotation::Linked ), |
| textIcon( QStringLiteral("Note") ), inplaceAlign( 0 ), |
| inplaceIntent( TextAnnotation::Unknown ) |
| { |
| } |
| |
| Annotation * TextAnnotationPrivate::makeAlias() |
| { |
| return new TextAnnotation(*this); |
| } |
| |
| Annot* TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) |
| { |
| // Setters are defined in the public class |
| TextAnnotation *q = static_cast<TextAnnotation*>( makeAlias() ); |
| |
| // Set page and contents |
| pdfPage = destPage; |
| parentDoc = doc; |
| |
| // Set pdfAnnot |
| PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); |
| if (textType == TextAnnotation::Linked) |
| { |
| pdfAnnot = new AnnotText{ destPage->getDoc(), &rect }; |
| } |
| else |
| { |
| DefaultAppearance da{ { objName, "Invalid_font" }, static_cast<double>( textFont.pointSize() ), std::unique_ptr<AnnotColor>{ convertQColor( textColor ) } }; |
| pdfAnnot = new AnnotFreeText{ destPage->getDoc(), &rect, da }; |
| } |
| |
| // Set properties |
| flushBaseAnnotationProperties(); |
| q->setTextIcon(textIcon); |
| q->setInplaceAlign(inplaceAlign); |
| q->setCalloutPoints(inplaceCallout); |
| q->setInplaceIntent(inplaceIntent); |
| |
| delete q; |
| |
| inplaceCallout.clear(); // Free up memory |
| |
| return pdfAnnot; |
| } |
| |
| void TextAnnotationPrivate::setDefaultAppearanceToNative() |
| { |
| if ( pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText ) |
| { |
| AnnotFreeText * ftextann = static_cast<AnnotFreeText*>( pdfAnnot ); |
| DefaultAppearance da{ { objName, "Invalid_font" }, static_cast<double>( textFont.pointSize() ), std::unique_ptr<AnnotColor>{ convertQColor( textColor ) } }; |
| ftextann->setDefaultAppearance( da ); |
| } |
| } |
| |
| std::unique_ptr<DefaultAppearance> TextAnnotationPrivate::getDefaultAppearanceFromNative() const |
| { |
| if ( pdfAnnot && pdfAnnot->getType() == Annot::typeFreeText ) |
| { |
| AnnotFreeText * ftextann = static_cast<AnnotFreeText*>( pdfAnnot ); |
| return ftextann->getDefaultAppearance(); |
| } |
| else |
| { |
| return {}; |
| } |
| } |
| |
| TextAnnotation::TextAnnotation( TextAnnotation::TextType type ) |
| : Annotation( *new TextAnnotationPrivate() ) |
| { |
| setTextType( type ); |
| } |
| |
| TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) |
| : Annotation( dd ) |
| {} |
| |
| TextAnnotation::TextAnnotation( const QDomNode & node ) |
| : Annotation( *new TextAnnotationPrivate, node ) |
| { |
| // loop through the whole children looking for a 'text' element |
| QDomNode subNode = node.firstChild(); |
| while( subNode.isElement() ) |
| { |
| QDomElement e = subNode.toElement(); |
| subNode = subNode.nextSibling(); |
| if ( e.tagName() != QLatin1String("text") ) |
| continue; |
| |
| // parse the attributes |
| if ( e.hasAttribute( QStringLiteral("type") ) ) |
| setTextType((TextAnnotation::TextType)e.attribute( QStringLiteral("type") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("icon") ) ) |
| setTextIcon(e.attribute( QStringLiteral("icon") )); |
| if ( e.hasAttribute( QStringLiteral("font") ) ) |
| { |
| QFont font; |
| font.fromString( e.attribute( QStringLiteral("font") ) ); |
| setTextFont(font); |
| if ( e.hasAttribute( QStringLiteral("fontColor") ) ) |
| { |
| const QColor color = QColor(e.attribute( QStringLiteral("fontColor") ) ); |
| setTextColor(color); |
| } |
| } |
| if ( e.hasAttribute( QStringLiteral("align") ) ) |
| setInplaceAlign(e.attribute( QStringLiteral("align") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("intent") ) ) |
| setInplaceIntent((TextAnnotation::InplaceIntent)e.attribute( QStringLiteral("intent") ).toInt()); |
| |
| // parse the subnodes |
| QDomNode eSubNode = e.firstChild(); |
| while ( eSubNode.isElement() ) |
| { |
| QDomElement ee = eSubNode.toElement(); |
| eSubNode = eSubNode.nextSibling(); |
| |
| if ( ee.tagName() == QLatin1String("escapedText") ) |
| { |
| setContents(ee.firstChild().toCDATASection().data()); |
| } |
| else if ( ee.tagName() == QLatin1String("callout") ) |
| { |
| QVector<QPointF> points(3); |
| points[0] = QPointF(ee.attribute( QStringLiteral("ax") ).toDouble(), |
| ee.attribute( QStringLiteral("ay") ).toDouble()); |
| points[1] = QPointF(ee.attribute( QStringLiteral("bx") ).toDouble(), |
| ee.attribute( QStringLiteral("by") ).toDouble()); |
| points[2] = QPointF(ee.attribute( QStringLiteral("cx") ).toDouble(), |
| ee.attribute( QStringLiteral("cy") ).toDouble()); |
| setCalloutPoints(points); |
| } |
| } |
| |
| // loading complete |
| break; |
| } |
| } |
| |
| TextAnnotation::~TextAnnotation() |
| { |
| } |
| |
| void TextAnnotation::store( QDomNode & node, QDomDocument & document ) const |
| { |
| // store base annotation properties |
| storeBaseAnnotationProperties( node, document ); |
| |
| // create [text] element |
| QDomElement textElement = document.createElement( QStringLiteral("text") ); |
| node.appendChild( textElement ); |
| |
| // store the optional attributes |
| if ( textType() != Linked ) |
| textElement.setAttribute( QStringLiteral("type"), (int)textType() ); |
| if ( textIcon() != QLatin1String("Note") ) |
| textElement.setAttribute( QStringLiteral("icon"), textIcon() ); |
| if ( inplaceAlign() ) |
| textElement.setAttribute( QStringLiteral("align"), inplaceAlign() ); |
| if ( inplaceIntent() != Unknown ) |
| textElement.setAttribute( QStringLiteral("intent"), (int)inplaceIntent() ); |
| |
| textElement.setAttribute( QStringLiteral("font"), textFont().toString() ); |
| textElement.setAttribute( QStringLiteral("fontColor"), textColor().name() ); |
| |
| // Sub-Node-1 - escapedText |
| if ( !contents().isEmpty() ) |
| { |
| QDomElement escapedText = document.createElement( QStringLiteral("escapedText") ); |
| textElement.appendChild( escapedText ); |
| QDomCDATASection textCData = document.createCDATASection( contents() ); |
| escapedText.appendChild( textCData ); |
| } |
| |
| // Sub-Node-2 - callout |
| if ( calloutPoint(0).x() != 0.0 ) |
| { |
| QDomElement calloutElement = document.createElement( QStringLiteral("callout") ); |
| textElement.appendChild( calloutElement ); |
| calloutElement.setAttribute( QStringLiteral("ax"), QString::number( calloutPoint(0).x() ) ); |
| calloutElement.setAttribute( QStringLiteral("ay"), QString::number( calloutPoint(0).y() ) ); |
| calloutElement.setAttribute( QStringLiteral("bx"), QString::number( calloutPoint(1).x() ) ); |
| calloutElement.setAttribute( QStringLiteral("by"), QString::number( calloutPoint(1).y() ) ); |
| calloutElement.setAttribute( QStringLiteral("cx"), QString::number( calloutPoint(2).x() ) ); |
| calloutElement.setAttribute( QStringLiteral("cy"), QString::number( calloutPoint(2).y() ) ); |
| } |
| } |
| |
| Annotation::SubType TextAnnotation::subType() const |
| { |
| return AText; |
| } |
| |
| TextAnnotation::TextType TextAnnotation::textType() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->textType; |
| |
| return d->pdfAnnot->getType() == Annot::typeText ? |
| TextAnnotation::Linked : TextAnnotation::InPlace; |
| } |
| |
| void TextAnnotation::setTextType( TextAnnotation::TextType type ) |
| { |
| Q_D( TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->textType = type; |
| return; |
| } |
| |
| // Type cannot be changed if annotation is already tied |
| } |
| |
| QString TextAnnotation::textIcon() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->textIcon; |
| |
| if (d->pdfAnnot->getType() == Annot::typeText) |
| { |
| const AnnotText * textann = static_cast<const AnnotText*>(d->pdfAnnot); |
| return QString::fromLatin1( textann->getIcon()->c_str() ); |
| } |
| |
| return QString(); |
| } |
| |
| void TextAnnotation::setTextIcon( const QString &icon ) |
| { |
| Q_D( TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->textIcon = icon; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeText) |
| { |
| AnnotText * textann = static_cast<AnnotText*>(d->pdfAnnot); |
| QByteArray encoded = icon.toLatin1(); |
| GooString s(encoded.constData()); |
| textann->setIcon(&s); |
| } |
| } |
| |
| QFont TextAnnotation::textFont() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if ( !d->pdfAnnot ) |
| return d->textFont; |
| |
| double fontSize { AnnotFreeText::undefinedFontPtSize }; |
| if ( d->pdfAnnot->getType() == Annot::typeFreeText ) |
| { |
| std::unique_ptr<DefaultAppearance> da{ d->getDefaultAppearanceFromNative() }; |
| if ( da && da->getFontPtSize() > 0 ) |
| { |
| fontSize = da->getFontPtSize(); |
| } |
| } |
| |
| QFont font; |
| font.setPointSizeF( fontSize ); |
| return font; |
| } |
| |
| void TextAnnotation::setTextFont( const QFont &font ) |
| { |
| Q_D( TextAnnotation ); |
| d->textFont = font; |
| d->textColor = Qt::black; |
| |
| d->setDefaultAppearanceToNative(); |
| } |
| |
| QColor TextAnnotation::textColor() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if ( !d->pdfAnnot ) |
| return d->textColor; |
| |
| if ( std::unique_ptr<DefaultAppearance> da{ d->getDefaultAppearanceFromNative() } ) |
| { |
| return convertAnnotColor( da->getFontColor() ); |
| } |
| |
| return {}; |
| } |
| |
| void TextAnnotation::setTextColor( const QColor &color ) |
| { |
| Q_D( TextAnnotation ); |
| d->textColor = color; |
| |
| d->setDefaultAppearanceToNative(); |
| } |
| |
| int TextAnnotation::inplaceAlign() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->inplaceAlign; |
| |
| if (d->pdfAnnot->getType() == Annot::typeFreeText) |
| { |
| const AnnotFreeText * ftextann = static_cast<const AnnotFreeText*>(d->pdfAnnot); |
| return ftextann->getQuadding(); |
| } |
| |
| return 0; |
| } |
| |
| void TextAnnotation::setInplaceAlign( int align ) |
| { |
| Q_D( TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->inplaceAlign = align; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeFreeText) |
| { |
| AnnotFreeText * ftextann = static_cast<AnnotFreeText*>(d->pdfAnnot); |
| ftextann->setQuadding((AnnotFreeText::AnnotFreeTextQuadding)align); |
| } |
| } |
| |
| QPointF TextAnnotation::calloutPoint( int id ) const |
| { |
| const QVector<QPointF> points = calloutPoints(); |
| if ( id < 0 || id >= points.size() ) |
| return QPointF(); |
| else |
| return points[id]; |
| } |
| |
| QVector<QPointF> TextAnnotation::calloutPoints() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->inplaceCallout; |
| |
| if (d->pdfAnnot->getType() == Annot::typeText) |
| return QVector<QPointF>(); |
| |
| const AnnotFreeText * ftextann = static_cast<const AnnotFreeText*>(d->pdfAnnot); |
| const AnnotCalloutLine *callout = ftextann->getCalloutLine(); |
| |
| if (!callout) |
| return QVector<QPointF>(); |
| |
| double MTX[6]; |
| d->fillTransformationMTX(MTX); |
| |
| const AnnotCalloutMultiLine * callout_v6 = dynamic_cast<const AnnotCalloutMultiLine*>(callout); |
| QVector<QPointF> res(callout_v6 ? 3 : 2); |
| XPDFReader::transform(MTX, callout->getX1(), callout->getY1(), res[0]); |
| XPDFReader::transform(MTX, callout->getX2(), callout->getY2(), res[1]); |
| if (callout_v6) |
| XPDFReader::transform(MTX, callout_v6->getX3(), callout_v6->getY3(), res[2]); |
| return res; |
| } |
| |
| void TextAnnotation::setCalloutPoints( const QVector<QPointF> &points ) |
| { |
| Q_D( TextAnnotation ); |
| if (!d->pdfAnnot) |
| { |
| d->inplaceCallout = points; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() != Annot::typeFreeText) |
| return; |
| |
| AnnotFreeText * ftextann = static_cast<AnnotFreeText*>(d->pdfAnnot); |
| const int count = points.size(); |
| |
| if (count == 0) |
| { |
| ftextann->setCalloutLine(nullptr); |
| return; |
| } |
| |
| if (count != 2 && count != 3) |
| { |
| error(errSyntaxError, -1, "Expected zero, two or three points for callout"); |
| return; |
| } |
| |
| AnnotCalloutLine *callout; |
| double x1, y1, x2, y2; |
| double MTX[6]; |
| d->fillTransformationMTX(MTX); |
| |
| XPDFReader::invTransform( MTX, points[0], x1, y1 ); |
| XPDFReader::invTransform( MTX, points[1], x2, y2 ); |
| if (count == 3) |
| { |
| double x3, y3; |
| XPDFReader::invTransform( MTX, points[2], x3, y3 ); |
| callout = new AnnotCalloutMultiLine(x1, y1, x2, y2, x3, y3); |
| } |
| else |
| { |
| callout = new AnnotCalloutLine(x1, y1, x2, y2); |
| } |
| |
| ftextann->setCalloutLine(callout); |
| delete callout; |
| } |
| |
| TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const |
| { |
| Q_D( const TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->inplaceIntent; |
| |
| if (d->pdfAnnot->getType() == Annot::typeFreeText) |
| { |
| const AnnotFreeText * ftextann = static_cast<const AnnotFreeText*>(d->pdfAnnot); |
| return (TextAnnotation::InplaceIntent)ftextann->getIntent(); |
| } |
| |
| return TextAnnotation::Unknown; |
| } |
| |
| void TextAnnotation::setInplaceIntent( TextAnnotation::InplaceIntent intent ) |
| { |
| Q_D( TextAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->inplaceIntent = intent; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeFreeText) |
| { |
| AnnotFreeText * ftextann = static_cast<AnnotFreeText*>(d->pdfAnnot); |
| ftextann->setIntent((AnnotFreeText::AnnotFreeTextIntent)intent); |
| } |
| } |
| |
| |
| /** LineAnnotation [Annotation] */ |
| class LineAnnotationPrivate : public AnnotationPrivate |
| { |
| public: |
| LineAnnotationPrivate(); |
| Annotation * makeAlias() override; |
| Annot* createNativeAnnot(::Page *destPage, DocumentData *doc) override; |
| |
| // data fields (note uses border for rendering style) |
| QLinkedList<QPointF> linePoints; |
| LineAnnotation::TermStyle lineStartStyle; |
| LineAnnotation::TermStyle lineEndStyle; |
| bool lineClosed : 1; // (if true draw close shape) |
| bool lineShowCaption : 1; |
| LineAnnotation::LineType lineType; |
| QColor lineInnerColor; |
| double lineLeadingFwdPt; |
| double lineLeadingBackPt; |
| LineAnnotation::LineIntent lineIntent; |
| }; |
| |
| LineAnnotationPrivate::LineAnnotationPrivate() |
| : AnnotationPrivate(), lineStartStyle( LineAnnotation::None ), |
| lineEndStyle( LineAnnotation::None ), lineClosed( false ), |
| lineShowCaption( false ), lineLeadingFwdPt( 0 ), |
| lineLeadingBackPt( 0 ), lineIntent( LineAnnotation::Unknown ) |
| { |
| } |
| |
| Annotation * LineAnnotationPrivate::makeAlias() |
| { |
| return new LineAnnotation(*this); |
| } |
| |
| Annot* LineAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) |
| { |
| // Setters are defined in the public class |
| LineAnnotation *q = static_cast<LineAnnotation*>( makeAlias() ); |
| |
| // Set page and document |
| pdfPage = destPage; |
| parentDoc = doc; |
| |
| // Set pdfAnnot |
| PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); |
| if (lineType == LineAnnotation::StraightLine) |
| { |
| pdfAnnot = new AnnotLine(doc->doc, &rect); |
| } |
| else |
| { |
| pdfAnnot = new AnnotPolygon(doc->doc, &rect, |
| lineClosed ? Annot::typePolygon : Annot::typePolyLine ); |
| } |
| |
| // Set properties |
| flushBaseAnnotationProperties(); |
| q->setLinePoints(linePoints); |
| q->setLineStartStyle(lineStartStyle); |
| q->setLineEndStyle(lineEndStyle); |
| q->setLineInnerColor(lineInnerColor); |
| q->setLineLeadingForwardPoint(lineLeadingFwdPt); |
| q->setLineLeadingBackPoint(lineLeadingBackPt); |
| q->setLineShowCaption(lineShowCaption); |
| q->setLineIntent(lineIntent); |
| |
| delete q; |
| |
| linePoints.clear(); // Free up memory |
| |
| return pdfAnnot; |
| } |
| |
| LineAnnotation::LineAnnotation( LineAnnotation::LineType type ) |
| : Annotation( *new LineAnnotationPrivate() ) |
| { |
| setLineType(type); |
| } |
| |
| LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) |
| : Annotation( dd ) |
| {} |
| |
| LineAnnotation::LineAnnotation( const QDomNode & node ) |
| : Annotation( *new LineAnnotationPrivate(), node ) |
| { |
| // loop through the whole children looking for a 'line' element |
| QDomNode subNode = node.firstChild(); |
| while( subNode.isElement() ) |
| { |
| QDomElement e = subNode.toElement(); |
| subNode = subNode.nextSibling(); |
| if ( e.tagName() != QLatin1String("line") ) |
| continue; |
| |
| // parse the attributes |
| if ( e.hasAttribute( QStringLiteral("startStyle") ) ) |
| setLineStartStyle((LineAnnotation::TermStyle)e.attribute( QStringLiteral("startStyle") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("endStyle") ) ) |
| setLineEndStyle((LineAnnotation::TermStyle)e.attribute( QStringLiteral("endStyle") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("closed") ) ) |
| setLineClosed(e.attribute( QStringLiteral("closed") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("innerColor") ) ) |
| setLineInnerColor(QColor( e.attribute( QStringLiteral("innerColor") ) )); |
| if ( e.hasAttribute( QStringLiteral("leadFwd") ) ) |
| setLineLeadingForwardPoint(e.attribute( QStringLiteral("leadFwd") ).toDouble()); |
| if ( e.hasAttribute( QStringLiteral("leadBack") ) ) |
| setLineLeadingBackPoint(e.attribute( QStringLiteral("leadBack") ).toDouble()); |
| if ( e.hasAttribute( QStringLiteral("showCaption") ) ) |
| setLineShowCaption(e.attribute( QStringLiteral("showCaption") ).toInt()); |
| if ( e.hasAttribute( QStringLiteral("intent") ) ) |
| setLineIntent((LineAnnotation::LineIntent)e.attribute( QStringLiteral("intent") ).toInt()); |
| |
| // parse all 'point' subnodes |
| QLinkedList<QPointF> points; |
| QDomNode pointNode = e.firstChild(); |
| while ( pointNode.isElement() ) |
| { |
| QDomElement pe = pointNode.toElement(); |
| pointNode = pointNode.nextSibling(); |
| |
| if ( pe.tagName() != QLatin1String("point") ) |
| continue; |
| |
| QPointF p(pe.attribute( QStringLiteral("x"), QStringLiteral("0.0") ).toDouble(), pe.attribute( QStringLiteral("y"), QStringLiteral("0.0") ).toDouble()); |
| points.append( p ); |
| } |
| setLinePoints(points); |
| setLineType(points.size() == 2 ? StraightLine : Polyline); |
| |
| // loading complete |
| break; |
| } |
| } |
| |
| LineAnnotation::~LineAnnotation() |
| { |
| } |
| |
| void LineAnnotation::store( QDomNode & node, QDomDocument & document ) const |
| { |
| // store base annotation properties |
| storeBaseAnnotationProperties( node, document ); |
| |
| // create [line] element |
| QDomElement lineElement = document.createElement( QStringLiteral("line") ); |
| node.appendChild( lineElement ); |
| |
| // store the attributes |
| if ( lineStartStyle() != None ) |
| lineElement.setAttribute( QStringLiteral("startStyle"), (int)lineStartStyle() ); |
| if ( lineEndStyle() != None ) |
| lineElement.setAttribute( QStringLiteral("endStyle"), (int)lineEndStyle() ); |
| if ( isLineClosed() ) |
| lineElement.setAttribute( QStringLiteral("closed"), isLineClosed() ); |
| if ( lineInnerColor().isValid() ) |
| lineElement.setAttribute( QStringLiteral("innerColor"), lineInnerColor().name() ); |
| if ( lineLeadingForwardPoint() != 0.0 ) |
| lineElement.setAttribute( QStringLiteral("leadFwd"), QString::number( lineLeadingForwardPoint() ) ); |
| if ( lineLeadingBackPoint() != 0.0 ) |
| lineElement.setAttribute( QStringLiteral("leadBack"), QString::number( lineLeadingBackPoint() ) ); |
| if ( lineShowCaption() ) |
| lineElement.setAttribute( QStringLiteral("showCaption"), lineShowCaption() ); |
| if ( lineIntent() != Unknown ) |
| lineElement.setAttribute( QStringLiteral("intent"), lineIntent() ); |
| |
| // append the list of points |
| const QLinkedList<QPointF> points = linePoints(); |
| if ( points.count() > 1 ) |
| { |
| QLinkedList<QPointF>::const_iterator it = points.begin(), end = points.end(); |
| while ( it != end ) |
| { |
| const QPointF & p = *it; |
| QDomElement pElement = document.createElement( QStringLiteral("point") ); |
| lineElement.appendChild( pElement ); |
| pElement.setAttribute( QStringLiteral("x"), QString::number( p.x() ) ); |
| pElement.setAttribute( QStringLiteral("y"), QString::number( p.y() ) ); |
| ++it; |
| } |
| } |
| } |
| |
| Annotation::SubType LineAnnotation::subType() const |
| { |
| return ALine; |
| } |
| |
| LineAnnotation::LineType LineAnnotation::lineType() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineType; |
| |
| return (d->pdfAnnot->getType() == Annot::typeLine) ? |
| LineAnnotation::StraightLine : LineAnnotation::Polyline; |
| } |
| |
| void LineAnnotation::setLineType( LineAnnotation::LineType type ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineType = type; |
| return; |
| } |
| |
| // Type cannot be changed if annotation is already tied |
| } |
| |
| QLinkedList<QPointF> LineAnnotation::linePoints() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->linePoints; |
| |
| double MTX[6]; |
| d->fillTransformationMTX(MTX); |
| |
| QLinkedList<QPointF> res; |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| const AnnotLine * lineann = static_cast<const AnnotLine*>(d->pdfAnnot); |
| QPointF p; |
| XPDFReader::transform(MTX, lineann->getX1(), lineann->getY1(), p); |
| res.append(p); |
| XPDFReader::transform(MTX, lineann->getX2(), lineann->getY2(), p); |
| res.append(p); |
| } |
| else |
| { |
| const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(d->pdfAnnot); |
| const AnnotPath * vertices = polyann->getVertices(); |
| |
| for (int i = 0; i < vertices->getCoordsLength(); ++i) |
| { |
| QPointF p; |
| XPDFReader::transform(MTX, vertices->getX(i), vertices->getY(i), p); |
| res.append(p); |
| } |
| } |
| |
| return res; |
| } |
| |
| void LineAnnotation::setLinePoints( const QLinkedList<QPointF> &points ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->linePoints = points; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot); |
| if (points.size() != 2) |
| { |
| error(errSyntaxError, -1, "Expected two points for a straight line"); |
| return; |
| } |
| double x1, y1, x2, y2; |
| double MTX[6]; |
| d->fillTransformationMTX(MTX); |
| XPDFReader::invTransform( MTX, points.first(), x1, y1 ); |
| XPDFReader::invTransform( MTX, points.last(), x2, y2 ); |
| lineann->setVertices(x1, y1, x2, y2); |
| } |
| else |
| { |
| AnnotPolygon *polyann = static_cast<AnnotPolygon*>(d->pdfAnnot); |
| AnnotPath * p = d->toAnnotPath(points); |
| polyann->setVertices(p); |
| delete p; |
| } |
| } |
| |
| LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineStartStyle; |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| const AnnotLine * lineann = static_cast<const AnnotLine*>(d->pdfAnnot); |
| return (LineAnnotation::TermStyle)lineann->getStartStyle(); |
| } |
| else |
| { |
| const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(d->pdfAnnot); |
| return (LineAnnotation::TermStyle)polyann->getStartStyle(); |
| } |
| } |
| |
| void LineAnnotation::setLineStartStyle( LineAnnotation::TermStyle style ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineStartStyle = style; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot); |
| lineann->setStartEndStyle((AnnotLineEndingStyle)style, lineann->getEndStyle()); |
| } |
| else |
| { |
| AnnotPolygon *polyann = static_cast<AnnotPolygon*>(d->pdfAnnot); |
| polyann->setStartEndStyle((AnnotLineEndingStyle)style, polyann->getEndStyle()); |
| } |
| } |
| |
| LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineEndStyle; |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| const AnnotLine * lineann = static_cast<const AnnotLine*>(d->pdfAnnot); |
| return (LineAnnotation::TermStyle)lineann->getEndStyle(); |
| } |
| else |
| { |
| const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(d->pdfAnnot); |
| return (LineAnnotation::TermStyle)polyann->getEndStyle(); |
| } |
| } |
| |
| void LineAnnotation::setLineEndStyle( LineAnnotation::TermStyle style ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineEndStyle = style; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot); |
| lineann->setStartEndStyle(lineann->getStartStyle(), (AnnotLineEndingStyle)style); |
| } |
| else |
| { |
| AnnotPolygon *polyann = static_cast<AnnotPolygon*>(d->pdfAnnot); |
| polyann->setStartEndStyle(polyann->getStartStyle(), (AnnotLineEndingStyle)style); |
| } |
| } |
| |
| bool LineAnnotation::isLineClosed() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineClosed; |
| |
| return d->pdfAnnot->getType() == Annot::typePolygon; |
| } |
| |
| void LineAnnotation::setLineClosed( bool closed ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineClosed = closed; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() != Annot::typeLine) |
| { |
| AnnotPolygon *polyann = static_cast<AnnotPolygon*>(d->pdfAnnot); |
| |
| // Set new subtype and switch intent if necessary |
| if (closed) |
| { |
| polyann->setType(Annot::typePolygon); |
| if (polyann->getIntent() == AnnotPolygon::polylineDimension) |
| polyann->setIntent( AnnotPolygon::polygonDimension ); |
| } |
| else |
| { |
| polyann->setType(Annot::typePolyLine); |
| if (polyann->getIntent() == AnnotPolygon::polygonDimension) |
| polyann->setIntent( AnnotPolygon::polylineDimension ); |
| } |
| } |
| } |
| |
| QColor LineAnnotation::lineInnerColor() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineInnerColor; |
| |
| AnnotColor * c; |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| const AnnotLine * lineann = static_cast<const AnnotLine*>(d->pdfAnnot); |
| c = lineann->getInteriorColor(); |
| } |
| else |
| { |
| const AnnotPolygon * polyann = static_cast<const AnnotPolygon*>(d->pdfAnnot); |
| c = polyann->getInteriorColor(); |
| } |
| |
| return convertAnnotColor(c); |
| } |
| |
| void LineAnnotation::setLineInnerColor( const QColor &color ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineInnerColor = color; |
| return; |
| } |
| |
| auto c = convertQColor(color); |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot); |
| lineann->setInteriorColor(std::move(c)); |
| } |
| else |
| { |
| AnnotPolygon *polyann = static_cast<AnnotPolygon*>(d->pdfAnnot); |
| polyann->setInteriorColor(std::move(c)); |
| } |
| } |
| |
| double LineAnnotation::lineLeadingForwardPoint() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineLeadingFwdPt; |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| const AnnotLine * lineann = static_cast<const AnnotLine*>(d->pdfAnnot); |
| return lineann->getLeaderLineLength(); |
| } |
| |
| return 0; |
| } |
| |
| void LineAnnotation::setLineLeadingForwardPoint( double point ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineLeadingFwdPt = point; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot); |
| lineann->setLeaderLineLength(point); |
| } |
| } |
| |
| double LineAnnotation::lineLeadingBackPoint() const |
| { |
| Q_D( const LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| return d->lineLeadingBackPt; |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| const AnnotLine * lineann = static_cast<const AnnotLine*>(d->pdfAnnot); |
| return lineann->getLeaderLineExtension(); |
| } |
| |
| return 0; |
| } |
| |
| void LineAnnotation::setLineLeadingBackPoint( double point ) |
| { |
| Q_D( LineAnnotation ); |
| |
| if (!d->pdfAnnot) |
| { |
| d->lineLeadingBackPt = point; |
| return; |
| } |
| |
| if (d->pdfAnnot->getType() == Annot::typeLine) |
| { |
| AnnotLine *lineann = static_cast<AnnotLine*>(d->pdfAnnot); |
| lineann->setLeaderLineExtension(<
|