You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
16 KiB
428 lines
16 KiB
/*************************************************************************** |
|
* 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 |
|
ppl_ann->setBoundary(normRectToRectF( okl_ann->boundingRectangle() )); |
|
ppl_ann->setAuthor( okl_ann->author() ); |
|
ppl_ann->setContents( okl_ann->contents() ); |
|
ppl_ann->setFlags(maskExportedFlags( okl_ann->flags() )); |
|
|
|
// 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::InPlace ) |
|
{ |
|
#ifndef HAVE_POPPLER_0_20 |
|
// Poppler before 0.20 returns the inplaceText in contents |
|
txtann->setInplaceText( txtann->contents() ); |
|
#endif |
|
} |
|
else 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; |
|
}
|
|
|