mirror of
https://invent.kde.org/graphics/okular
synced 2024-11-05 18:34:53 +00:00
6efedc7bea
See patch 0006 in http://lists.freedesktop.org/archives/poppler/2013-April/010214.html CCBUG: 313177
424 lines
16 KiB
C++
424 lines
16 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2008 by Pino Toscano <pino@kde.org> *
|
|
* Copyright (C) 2012 by Guillermo A. Amaral B. <gamaral@kde.org> *
|
|
* *
|
|
* 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. *
|
|
***************************************************************************/
|
|
|
|
#include <poppler-annotation.h>
|
|
|
|
// qt/kde includes
|
|
#include <qvariant.h>
|
|
|
|
#include <core/annotations.h>
|
|
#include <core/area.h>
|
|
|
|
#include "annots.h"
|
|
#include "generator_pdf.h"
|
|
#include "popplerembeddedfile.h"
|
|
#include "config-okular-poppler.h"
|
|
|
|
Q_DECLARE_METATYPE( Poppler::Annotation* )
|
|
|
|
extern Okular::Sound* createSoundFromPopplerSound( const Poppler::SoundObject *popplerSound );
|
|
extern Okular::Movie* createMovieFromPopplerMovie( const Poppler::MovieObject *popplerMovie );
|
|
#ifdef HAVE_POPPLER_0_20
|
|
extern Okular::Movie* createMovieFromPopplerScreen( const Poppler::LinkRendition *popplerScreen );
|
|
#endif
|
|
|
|
|
|
static void disposeAnnotation( const Okular::Annotation *ann )
|
|
{
|
|
Poppler::Annotation *popplerAnn = qvariant_cast< Poppler::Annotation * >( ann->nativeId() );
|
|
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) );
|
|
}
|
|
|
|
// Poppler and Okular share the same flag values, but we don't want to export internal flags
|
|
static int maskExportedFlags(int flags)
|
|
{
|
|
return flags & ( Okular::Annotation::Hidden |
|
|
Okular::Annotation::FixedSize |
|
|
Okular::Annotation::FixedRotation |
|
|
Okular::Annotation::DenyPrint |
|
|
Okular::Annotation::DenyWrite |
|
|
Okular::Annotation::DenyDelete |
|
|
Okular::Annotation::ToggleHidingOnMouse );
|
|
}
|
|
|
|
//BEGIN PopplerAnnotationProxy implementation
|
|
PopplerAnnotationProxy::PopplerAnnotationProxy( Poppler::Document *doc, QMutex *userMutex )
|
|
: ppl_doc ( doc ), mutex ( userMutex )
|
|
{
|
|
}
|
|
|
|
PopplerAnnotationProxy::~PopplerAnnotationProxy()
|
|
{
|
|
}
|
|
|
|
bool PopplerAnnotationProxy::supports( Capability cap ) const
|
|
{
|
|
switch ( cap )
|
|
{
|
|
#ifdef HAVE_POPPLER_0_20
|
|
case Addition:
|
|
case Modification:
|
|
case Removal:
|
|
return true;
|
|
#endif
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void PopplerAnnotationProxy::notifyAddition( Okular::Annotation *okl_ann, int page )
|
|
{
|
|
#ifdef HAVE_POPPLER_0_20
|
|
// Export annotation to DOM
|
|
QDomDocument doc;
|
|
QDomElement dom_ann = doc.createElement( "root" );
|
|
Okular::AnnotationUtils::storeAnnotation( okl_ann, dom_ann, doc );
|
|
|
|
QMutexLocker ml(mutex);
|
|
|
|
// 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<Poppler::HighlightAnnotation*>( ppl_ann );
|
|
QList<Poppler::HighlightAnnotation::Quad> quads = hlann->highlightQuads();
|
|
QMutableListIterator<Poppler::HighlightAnnotation::Quad> 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();
|
|
#endif
|
|
}
|
|
|
|
void PopplerAnnotationProxy::notifyModification( const Okular::Annotation *okl_ann, int page, bool appearanceChanged )
|
|
{
|
|
#ifdef HAVE_POPPLER_0_20
|
|
Q_UNUSED( page );
|
|
Q_UNUSED( appearanceChanged );
|
|
|
|
Poppler::Annotation *ppl_ann = qvariant_cast<Poppler::Annotation*>( okl_ann->nativeId() );
|
|
|
|
if ( !ppl_ann ) // Ignore non-native annotations
|
|
return;
|
|
|
|
QMutexLocker ml(mutex);
|
|
|
|
if ( okl_ann->flags() & Okular::Annotation::BeingMoved )
|
|
{
|
|
// Okular ui already renders the annotation on its own
|
|
ppl_ann->setFlags( Poppler::Annotation::Hidden );
|
|
return;
|
|
}
|
|
|
|
// Set basic properties
|
|
// Note: flags and boundary must be set first in order to correctly handle
|
|
// FixedRotation annotations.
|
|
ppl_ann->setFlags(maskExportedFlags( okl_ann->flags() ));
|
|
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<const Okular::TextAnnotation*>(okl_ann);
|
|
Poppler::TextAnnotation * ppl_txtann = static_cast<Poppler::TextAnnotation*>(ppl_ann);
|
|
ppl_txtann->setTextIcon( okl_txtann->textIcon() );
|
|
ppl_txtann->setTextFont( okl_txtann->textFont() );
|
|
ppl_txtann->setInplaceAlign( okl_txtann->inplaceAlignment() );
|
|
ppl_txtann->setCalloutPoints( QVector<QPointF>() );
|
|
ppl_txtann->setInplaceIntent( (Poppler::TextAnnotation::InplaceIntent)okl_txtann->inplaceIntent() );
|
|
break;
|
|
}
|
|
case Poppler::Annotation::ALine:
|
|
{
|
|
const Okular::LineAnnotation * okl_lineann = static_cast<const Okular::LineAnnotation*>(okl_ann);
|
|
Poppler::LineAnnotation * ppl_lineann = static_cast<Poppler::LineAnnotation*>(ppl_ann);
|
|
QLinkedList<QPointF> 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<const Okular::GeomAnnotation*>(okl_ann);
|
|
Poppler::GeomAnnotation * ppl_geomann = static_cast<Poppler::GeomAnnotation*>(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<const Okular::HighlightAnnotation*>(okl_ann);
|
|
Poppler::HighlightAnnotation * ppl_hlann = static_cast<Poppler::HighlightAnnotation*>(ppl_ann);
|
|
ppl_hlann->setHighlightType( (Poppler::HighlightAnnotation::HighlightType)okl_hlann->highlightType() );
|
|
break;
|
|
}
|
|
case Poppler::Annotation::AStamp:
|
|
{
|
|
const Okular::StampAnnotation * okl_stampann = static_cast<const Okular::StampAnnotation*>(okl_ann);
|
|
Poppler::StampAnnotation * ppl_stampann = static_cast<Poppler::StampAnnotation*>(ppl_ann);
|
|
ppl_stampann->setStampIconName( okl_stampann->stampIconName() );
|
|
break;
|
|
}
|
|
case Poppler::Annotation::AInk:
|
|
{
|
|
const Okular::InkAnnotation * okl_inkann = static_cast<const Okular::InkAnnotation*>(okl_ann);
|
|
Poppler::InkAnnotation * ppl_inkann = static_cast<Poppler::InkAnnotation*>(ppl_ann);
|
|
QList< QLinkedList<QPointF> > paths;
|
|
foreach ( const QLinkedList<Okular::NormalizedPoint> &path, okl_inkann->inkPaths() )
|
|
{
|
|
QLinkedList<QPointF> 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();
|
|
#endif
|
|
}
|
|
|
|
void PopplerAnnotationProxy::notifyRemoval( Okular::Annotation *okl_ann, int page )
|
|
{
|
|
#ifdef HAVE_POPPLER_0_20
|
|
Poppler::Annotation *ppl_ann = qvariant_cast<Poppler::Annotation*>( okl_ann->nativeId() );
|
|
|
|
if ( !ppl_ann ) // Ignore non-native annotations
|
|
return;
|
|
|
|
QMutexLocker ml(mutex);
|
|
|
|
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();
|
|
#endif
|
|
}
|
|
//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:
|
|
{
|
|
Poppler::FileAttachmentAnnotation * attachann = static_cast< Poppler::FileAttachmentAnnotation * >( ann );
|
|
Okular::FileAttachmentAnnotation * f = new Okular::FileAttachmentAnnotation();
|
|
annotation = f;
|
|
tieToOkularAnn = true;
|
|
*doDelete = false;
|
|
|
|
f->setFileIconName( attachann->fileIconName() );
|
|
f->setEmbeddedFile( new PDFEmbeddedFile( attachann->embeddedFile() ) );
|
|
|
|
break;
|
|
}
|
|
case Poppler::Annotation::ASound:
|
|
{
|
|
Poppler::SoundAnnotation * soundann = static_cast< Poppler::SoundAnnotation * >( ann );
|
|
Okular::SoundAnnotation * s = new Okular::SoundAnnotation();
|
|
annotation = s;
|
|
|
|
s->setSoundIconName( soundann->soundIconName() );
|
|
s->setSound( createSoundFromPopplerSound( soundann->sound() ) );
|
|
|
|
break;
|
|
}
|
|
case Poppler::Annotation::AMovie:
|
|
{
|
|
Poppler::MovieAnnotation * movieann = static_cast< Poppler::MovieAnnotation * >( ann );
|
|
Okular::MovieAnnotation * m = new Okular::MovieAnnotation();
|
|
annotation = m;
|
|
tieToOkularAnn = true;
|
|
*doDelete = false;
|
|
|
|
m->setMovie( createMovieFromPopplerMovie( movieann->movie() ) );
|
|
|
|
break;
|
|
}
|
|
#ifdef HAVE_POPPLER_0_22
|
|
case Poppler::Annotation::AWidget:
|
|
{
|
|
annotation = new Okular::WidgetAnnotation();
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_POPPLER_0_20
|
|
case Poppler::Annotation::AScreen:
|
|
{
|
|
#ifdef HAVE_POPPLER_0_22
|
|
Okular::ScreenAnnotation * m = new Okular::ScreenAnnotation();
|
|
annotation = m;
|
|
tieToOkularAnn = true;
|
|
*doDelete = false;
|
|
#else
|
|
Poppler::ScreenAnnotation * screenann = static_cast< Poppler::ScreenAnnotation * >( ann );
|
|
Okular::MovieAnnotation * m = new Okular::MovieAnnotation();
|
|
annotation = m;
|
|
|
|
m->setMovie( createMovieFromPopplerScreen( screenann->action() ) );
|
|
#endif
|
|
break;
|
|
}
|
|
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 */
|
|
}
|
|
#endif
|
|
default:
|
|
{
|
|
// this is uber ugly but i don't know a better way to do it without introducing a poppler::annotation dependency on core
|
|
QDomDocument doc;
|
|
QDomElement root = doc.createElement( "root" );
|
|
doc.appendChild( root );
|
|
Poppler::AnnotationUtils::storeAnnotation( ann, root, doc );
|
|
annotation = Okular::AnnotationUtils::createAnnotation( root );
|
|
break;
|
|
}
|
|
}
|
|
if ( annotation )
|
|
{
|
|
// the Contents field might have lines separated by \r
|
|
QString contents = ann->contents();
|
|
contents.replace( QLatin1Char( '\r' ), QLatin1Char( '\n' ) );
|
|
|
|
annotation->setAuthor( ann->author() );
|
|
annotation->setContents( contents );
|
|
annotation->setUniqueName( ann->uniqueName() );
|
|
annotation->setModificationDate( ann->modificationDate() );
|
|
annotation->setCreationDate( ann->creationDate() );
|
|
annotation->setFlags( ann->flags() | Okular::Annotation::External );
|
|
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<Okular::HighlightAnnotation*>( annotation );
|
|
QList<Okular::HighlightAnnotation::Quad> &quads = hlann->highlightQuads();
|
|
for (QList<Okular::HighlightAnnotation::Quad>::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 );
|
|
}
|
|
}
|
|
|
|
if ( annotation->subType() == Okular::Annotation::AText )
|
|
{
|
|
Okular::TextAnnotation * txtann = static_cast<Okular::TextAnnotation*>( annotation );
|
|
|
|
if ( txtann->textType() == Okular::TextAnnotation::Linked )
|
|
{
|
|
Poppler::TextAnnotation * ppl_txtann = static_cast<Poppler::TextAnnotation*>( ann );
|
|
|
|
// Poppler and Okular assume a different default icon name in XML
|
|
// We re-read it via getter, which always tells the right one
|
|
txtann->setTextIcon( ppl_txtann->textIcon() );
|
|
}
|
|
}
|
|
|
|
// TODO clone style
|
|
// TODO clone window
|
|
// TODO clone revisions
|
|
if ( tieToOkularAnn )
|
|
{
|
|
annotation->setNativeId( qVariantFromValue( ann ) );
|
|
annotation->setDisposeDataFunction( disposeAnnotation );
|
|
}
|
|
}
|
|
return annotation;
|
|
}
|