From 65d59f2a3e47aa61467adb54d2edb5c8d41a1e92 Mon Sep 17 00:00:00 2001 From: Fabio D'Urso Date: Fri, 13 Apr 2012 21:33:25 +0200 Subject: [PATCH] poppler gen: AnnotationProxy implementation --- generators/poppler/annots.cpp | 239 ++++++++++++++++++++++++++- generators/poppler/annots.h | 34 ++++ generators/poppler/generator_pdf.cpp | 12 +- generators/poppler/generator_pdf.h | 2 + 4 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 generators/poppler/annots.h diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp index f99a316f8..9be6242b5 100644 --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -14,7 +14,10 @@ #include #include +#include +#include "annots.h" +#include "generator_pdf.h" #include "popplerembeddedfile.h" #include "config-okular-poppler.h" @@ -32,11 +35,209 @@ static void disposeAnnotation( const Okular::Annotation *ann ) delete popplerAnn; } +static QPointF normPointToPointF( const Okular::NormalizedPoint& pt ) +{ + return QPointF(pt.x, pt.y); +} + +static QRectF normRectToRectF( const Okular::NormalizedRect& rect ) +{ + return QRectF( QPointF(rect.left, rect.top), QPointF(rect.right, rect.bottom) ); +} + +//BEGIN PopplerAnnotationProxy implementation +PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc ) + : ppl_doc ( doc ) +{ +} + +PopplerAnnotationProxy::~PopplerAnnotationProxy() +{ +} + +bool PopplerAnnotationProxy::supports( Capability cap ) const +{ + switch ( cap ) + { + case Addition: + case Modification: + case Removal: + return true; + default: + return false; + } +} + +void PopplerAnnotationProxy::notifyAddition( Okular::Annotation *okl_ann, int page ) +{ + // Export annotation to DOM + QDomDocument doc; + QDomElement dom_ann = doc.createElement( "root" ); + Okular::AnnotationUtils::storeAnnotation( okl_ann, dom_ann, doc ); + + // Create poppler annotation + Poppler::Annotation *ppl_ann = Poppler::AnnotationUtils::createAnnotation( dom_ann ); + + // Poppler doesn't render StampAnnotations yet + if ( ppl_ann->subType() != Poppler::Annotation::AStamp ) + okl_ann->setFlags( okl_ann->flags() | Okular::Annotation::ExternallyDrawn ); + + // Poppler stores highlight points in swapped order + if ( ppl_ann->subType() == Poppler::Annotation::AHighlight ) + { + Poppler::HighlightAnnotation * hlann = static_cast( ppl_ann ); + QList quads = hlann->highlightQuads(); + QMutableListIterator it( quads ); + while ( it.hasNext() ) + { + Poppler::HighlightAnnotation::Quad &q = it.next(); + QPointF t; + t = q.points[3]; + q.points[3] = q.points[0]; + q.points[0] = t; + t = q.points[2]; + q.points[2] = q.points[1]; + q.points[1] = t; + } + hlann->setHighlightQuads( quads ); + } + + // Bind poppler object to page + Poppler::Page *ppl_page = ppl_doc->page( page ); + ppl_page->addAnnotation( ppl_ann ); + delete ppl_page; + + // Set pointer to poppler annotation as native Id + okl_ann->setNativeId( qVariantFromValue( ppl_ann ) ); + okl_ann->setDisposeDataFunction( disposeAnnotation ); + + kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +} + +void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_ann, int page, bool appearanceChanged ) +{ + Q_UNUSED( page ); + Q_UNUSED( appearanceChanged ); + + Poppler::Annotation *ppl_ann = qvariant_cast( okl_ann->nativeId() ); + + if ( !ppl_ann ) // Ignore non-native annotations + return; + + // Set basic properties + ppl_ann->setBoundary(normRectToRectF( okl_ann->boundingRectangle() )); + ppl_ann->setAuthor( okl_ann->author() ); + ppl_ann->setContents( okl_ann->contents() ); + + // Set style + Poppler::Annotation::Style s; + s.setColor( okl_ann->style().color() ); + s.setWidth( okl_ann->style().width() ); + s.setOpacity( okl_ann->style().opacity() ); + ppl_ann->setStyle( s ); + + // Set type-specific properties (if any) + switch ( ppl_ann->subType() ) + { + case Poppler::Annotation::AText: + { + const Okular::TextAnnotation * okl_txtann = static_cast(okl_ann); + Poppler::TextAnnotation * ppl_txtann = static_cast(ppl_ann); + ppl_txtann->setTextIcon( okl_txtann->textIcon() ); + ppl_txtann->setTextFont( okl_txtann->textFont() ); + ppl_txtann->setInplaceAlign( okl_txtann->inplaceAlignment() ); + if ( okl_txtann->textType() == Okular::TextAnnotation::InPlace ) + ppl_txtann->setContents( okl_txtann->inplaceText() ); // overrides contents + ppl_txtann->setCalloutPoints( QVector() ); + ppl_txtann->setInplaceIntent( (Poppler::TextAnnotation::InplaceIntent)okl_txtann->inplaceIntent() ); + break; + } + case Poppler::Annotation::ALine: + { + const Okular::LineAnnotation * okl_lineann = static_cast(okl_ann); + Poppler::LineAnnotation * ppl_lineann = static_cast(ppl_ann); + QLinkedList points; + foreach ( const Okular::NormalizedPoint &p, okl_lineann->linePoints() ) + points.append(normPointToPointF( p )); + ppl_lineann->setLinePoints( points ); + ppl_lineann->setLineStartStyle( (Poppler::LineAnnotation::TermStyle)okl_lineann->lineStartStyle() ); + ppl_lineann->setLineEndStyle( (Poppler::LineAnnotation::TermStyle)okl_lineann->lineEndStyle() ); + ppl_lineann->setLineClosed( okl_lineann->lineClosed() ); + ppl_lineann->setLineInnerColor( okl_lineann->lineInnerColor() ); + ppl_lineann->setLineLeadingForwardPoint( okl_lineann->lineLeadingForwardPoint() ); + ppl_lineann->setLineLeadingBackPoint( okl_lineann->lineLeadingBackwardPoint() ); + ppl_lineann->setLineShowCaption( okl_lineann->showCaption() ); + ppl_lineann->setLineIntent( (Poppler::LineAnnotation::LineIntent)okl_lineann->lineIntent() ); + break; + } + case Poppler::Annotation::AGeom: + { + const Okular::GeomAnnotation * okl_geomann = static_cast(okl_ann); + Poppler::GeomAnnotation * ppl_geomann = static_cast(ppl_ann); + ppl_geomann->setGeomType( (Poppler::GeomAnnotation::GeomType)okl_geomann->geometricalType() ); + ppl_geomann->setGeomInnerColor( okl_geomann->geometricalInnerColor() ); + break; + } + case Poppler::Annotation::AHighlight: + { + const Okular::HighlightAnnotation * okl_hlann = static_cast(okl_ann); + Poppler::HighlightAnnotation * ppl_hlann = static_cast(ppl_ann); + ppl_hlann->setHighlightType( (Poppler::HighlightAnnotation::HighlightType)okl_hlann->highlightType() ); + break; + } + case Poppler::Annotation::AStamp: + { + const Okular::StampAnnotation * okl_stampann = static_cast(okl_ann); + Poppler::StampAnnotation * ppl_stampann = static_cast(ppl_ann); + ppl_stampann->setStampIconName( okl_stampann->stampIconName() ); + break; + } + case Poppler::Annotation::AInk: + { + const Okular::InkAnnotation * okl_inkann = static_cast(okl_ann); + Poppler::InkAnnotation * ppl_inkann = static_cast(ppl_ann); + QList< QLinkedList > paths; + foreach ( const QLinkedList &path, okl_inkann->inkPaths() ) + { + QLinkedList points; + foreach ( const Okular::NormalizedPoint &p, path ) + points.append(normPointToPointF( p )); + paths.append( points ); + } + ppl_inkann->setInkPaths( paths ); + break; + } + default: + kDebug() << "Type-specific property modification is not implemented for this annotation type"; + break; + } + + kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +} + +void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int page ) +{ + Poppler::Annotation *ppl_ann = qvariant_cast( okl_ann->nativeId() ); + + if ( !ppl_ann ) // Ignore non-native annotations + return; + + Poppler::Page *ppl_page = ppl_doc->page( page ); + ppl_page->removeAnnotation( ppl_ann ); // Also destroys ppl_ann + delete ppl_page; + + okl_ann->setNativeId( qVariantFromValue(0) ); // So that we don't double-free in disposeAnnotation + + kDebug(PDFGenerator::PDFDebug) << okl_ann->uniqueName(); +} +//END PopplerAnnotationProxy implementation + Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool *doDelete ) { Okular::Annotation *annotation = 0; *doDelete = true; bool tieToOkularAnn = false; + bool externallyDrawn = false; switch ( ann->subType() ) { case Poppler::Annotation::AFileAttachment: @@ -87,6 +288,21 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * break; } #endif + case Poppler::Annotation::AText: + case Poppler::Annotation::ALine: + case Poppler::Annotation::AGeom: + case Poppler::Annotation::AHighlight: + case Poppler::Annotation::AInk: + { + externallyDrawn = true; + /* fallback */ + } + case Poppler::Annotation::AStamp: + { + tieToOkularAnn = true; + *doDelete = false; + /* fallback */ + } default: { // this is uber ugly but i don't know a better way to do it without introducing a poppler::annotation dependency on core @@ -95,7 +311,7 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * doc.appendChild( root ); Poppler::AnnotationUtils::storeAnnotation( ann, root, doc ); annotation = Okular::AnnotationUtils::createAnnotation( root ); - return annotation; + break; } } if ( annotation ) @@ -107,6 +323,27 @@ Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation * annotation->setCreationDate( ann->creationDate() ); annotation->setFlags( ann->flags() ); annotation->setBoundingRectangle( Okular::NormalizedRect::fromQRectF( ann->boundary() ) ); + + if (externallyDrawn) + annotation->setFlags( annotation->flags() | Okular::Annotation::ExternallyDrawn ); + + // Poppler stores highlight points in swapped order + if ( annotation->subType() == Okular::Annotation::AHighlight ) + { + Okular::HighlightAnnotation * hlann = static_cast( annotation ); + QList &quads = hlann->highlightQuads(); + for (QList::iterator it = quads.begin(); it != quads.end(); ++it) + { + Okular::NormalizedPoint t; + t = it->point( 3 ); + it->setPoint( it->point(0), 3 ); + it->setPoint( t, 0 ); + t = it->point( 2 ); + it->setPoint( it->point(1), 2 ); + it->setPoint( t, 1 ); + } + } + // TODO clone style // TODO clone window // TODO clone revisions diff --git a/generators/poppler/annots.h b/generators/poppler/annots.h new file mode 100644 index 000000000..d71e7e537 --- /dev/null +++ b/generators/poppler/annots.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright (C) 2012 by Fabio D'Urso * + * * + * 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 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef _OKULAR_GENERATOR_PDF_ANNOTS_H_ +#define _OKULAR_GENERATOR_PDF_ANNOTS_H_ + +#include +#include + +#include "core/annotations.h" + +extern Okular::Annotation* createAnnotationFromPopplerAnnotation( Poppler::Annotation *ann, bool * doDelete ); + +class PopplerAnnotationProxy : public Okular::AnnotationProxy +{ + public: + PopplerAnnotationProxy( Poppler::Document *doc ); + ~PopplerAnnotationProxy(); + + bool supports( Capability capability ) const; + void notifyAddition( Okular::Annotation *annotation, int page ); + void notifyModification( const Okular::Annotation *annotation, int page, bool appearanceChanged ); + void notifyRemoval( Okular::Annotation *annotation, int page ); + private: + Poppler::Document *ppl_doc; +}; + +#endif diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index 84855edbd..b89bfb34d 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -52,6 +52,7 @@ # include #endif +#include "annots.h" #include "formfields.h" #include "popplerembeddedfile.h" @@ -333,8 +334,6 @@ static QLinkedList generateLinks( const QList & pagesVector, const QString &wal // update the configuration reparseConfig(); + // create annotation proxy + annotProxy = new PopplerAnnotationProxy( pdfdoc ); + // the file has been loaded correctly return true; } @@ -537,6 +539,8 @@ bool PDFGenerator::doCloseDocument() { // remove internal objects userMutex()->lock(); + delete annotProxy; + annotProxy = 0; delete pdfdoc; pdfdoc = 0; userMutex()->unlock(); @@ -1787,7 +1791,7 @@ bool PDFGenerator::save( const QString &fileName, SaveOptions options, QString * Okular::AnnotationProxy* PDFGenerator::annotationProxy() const { - return 0; // Not supported + return annotProxy; } #include "generator_pdf.moc" diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h index b75be74e6..bea65d9ee 100644 --- a/generators/poppler/generator_pdf.h +++ b/generators/poppler/generator_pdf.h @@ -32,6 +32,7 @@ class SourceReference; } class PDFOptionsPage; +class PopplerAnnotationProxy; /** * @short A generator that builds contents from a PDF document. @@ -145,6 +146,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p int nextFontPage; double dpiX; double dpiY; + PopplerAnnotationProxy *annotProxy; QHash annotationsHash; QBitArray rectsGenerated;