mirror of
https://invent.kde.org/graphics/okular
synced 2024-11-05 18:34:53 +00:00
fbc7d45083
implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
353 lines
10 KiB
C++
353 lines
10 KiB
C++
/***************************************************************************
|
|
* 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 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
|
|
// qt/kde includes
|
|
#include <qpixmap.h>
|
|
#include <qstring.h>
|
|
#include <qmap.h>
|
|
#include <kdebug.h>
|
|
|
|
// local includes
|
|
#include "page.h"
|
|
#include "pagetransition.h"
|
|
#include "link.h"
|
|
#include "annotations.h"
|
|
#include "conf/settings.h"
|
|
#include "xpdf/TextOutputDev.h"
|
|
|
|
|
|
/** class KPDFPage **/
|
|
|
|
KPDFPage::KPDFPage( uint page, float w, float h, int r )
|
|
: m_number( page ), m_rotation( r ), m_width( w ), m_height( h ),
|
|
m_bookmarked( false ), m_text( 0 ), m_transition( 0 )
|
|
{
|
|
// if landscape swap width <-> height (rotate 90deg CCW)
|
|
if ( r == 90 || r == 270 )
|
|
{
|
|
m_width = h;
|
|
m_height = w;
|
|
}
|
|
// avoid Division-By-Zero problems in the program
|
|
if ( m_width <= 0 )
|
|
m_width = 1;
|
|
if ( m_height <= 0 )
|
|
m_height = 1;
|
|
}
|
|
|
|
KPDFPage::~KPDFPage()
|
|
{
|
|
deletePixmapsAndRects();
|
|
deleteHighlights();
|
|
deleteAnnotations();
|
|
delete m_text;
|
|
delete m_transition;
|
|
}
|
|
|
|
|
|
bool KPDFPage::hasPixmap( int id, int width, int height ) const
|
|
{
|
|
if ( !m_pixmaps.contains( id ) )
|
|
return false;
|
|
if ( width == -1 || height == -1 )
|
|
return true;
|
|
QPixmap * p = m_pixmaps[ id ];
|
|
return p ? ( p->width() == width && p->height() == height ) : false;
|
|
}
|
|
|
|
bool KPDFPage::hasSearchPage() const
|
|
{
|
|
return m_text != 0;
|
|
}
|
|
|
|
bool KPDFPage::hasBookmark() const
|
|
{
|
|
return m_bookmarked;
|
|
}
|
|
|
|
bool KPDFPage::hasObjectRect( double x, double y ) const
|
|
{
|
|
if ( m_rects.count() < 1 )
|
|
return false;
|
|
QValueList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
|
|
for ( ; it != end; ++it )
|
|
if ( (*it)->contains( x, y ) )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool KPDFPage::hasHighlights( int s_id ) const
|
|
{
|
|
// simple case: have no highlights
|
|
if ( m_highlights.isEmpty() )
|
|
return false;
|
|
// simple case: we have highlights and no id to match
|
|
if ( s_id == -1 )
|
|
return true;
|
|
// iterate on the highlights list to find an entry by id
|
|
QValueList< HighlightRect * >::const_iterator it = m_highlights.begin(), end = m_highlights.end();
|
|
for ( ; it != end; ++it )
|
|
if ( (*it)->s_id == s_id )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool KPDFPage::hasTransition() const
|
|
{
|
|
return m_transition != 0;
|
|
}
|
|
|
|
|
|
NormalizedRect * KPDFPage::findText( const QString & text, bool strictCase, NormalizedRect * lastRect ) const
|
|
{
|
|
if ( text.isEmpty() )
|
|
return 0;
|
|
|
|
// create a xpf's Unicode (unsigned int) array for the given text
|
|
const QChar * str = text.unicode();
|
|
int len = text.length();
|
|
QMemArray<Unicode> u(len);
|
|
for (int i = 0; i < len; ++i)
|
|
u[i] = str[i].unicode();
|
|
|
|
// find out the direction of search
|
|
enum SearchDir { FromTop, NextMatch, PrevMatch } dir = lastRect ? NextMatch : FromTop;
|
|
double sLeft, sTop, sRight, sBottom;
|
|
if ( dir == NextMatch )
|
|
{
|
|
sLeft = lastRect->left * m_width;
|
|
sTop = lastRect->top * m_height;
|
|
sRight = lastRect->right * m_width;
|
|
sBottom = lastRect->bottom * m_height;
|
|
}
|
|
|
|
// this loop is only for 'bad case' matches
|
|
bool found = false;
|
|
while ( !found )
|
|
{
|
|
if ( dir == FromTop )
|
|
found = m_text->findText( const_cast<Unicode*>(static_cast<const Unicode*>(u)), len, gTrue, gTrue, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom );
|
|
else if ( dir == NextMatch )
|
|
found = m_text->findText( const_cast<Unicode*>(static_cast<const Unicode*>(u)), len, gFalse, gTrue, gTrue, gFalse, &sLeft, &sTop, &sRight, &sBottom );
|
|
else if ( dir == PrevMatch )
|
|
// FIXME: this doesn't work as expected (luckily backward search isn't yet used)
|
|
found = m_text->findText( const_cast<Unicode*>(static_cast<const Unicode*>(u)), len, gTrue, gFalse, gFalse, gTrue, &sLeft, &sTop, &sRight, &sBottom );
|
|
|
|
// if not found (even in case unsensitive search), terminate
|
|
if ( !found )
|
|
break;
|
|
|
|
// check for case sensitivity
|
|
if ( strictCase )
|
|
{
|
|
// since we're in 'Case sensitive' mode, check if words are identical
|
|
GString * realText = m_text->getText( sLeft, sTop, sRight, sBottom );
|
|
found = QString::fromUtf8( realText->getCString() ) == text;
|
|
if ( !found && dir == FromTop )
|
|
dir = NextMatch;
|
|
delete realText;
|
|
}
|
|
}
|
|
|
|
// if the page was found, return a new normalizedRect
|
|
if ( found )
|
|
return new NormalizedRect( sLeft / m_width, sTop / m_height, sRight / m_width, sBottom / m_height );
|
|
return 0;
|
|
}
|
|
|
|
const QString KPDFPage::getText( const NormalizedRect & rect ) const
|
|
{
|
|
if ( !m_text )
|
|
return QString::null;
|
|
int left = (int)( rect.left * m_width ),
|
|
top = (int)( rect.top * m_height ),
|
|
right = (int)( rect.right * m_width ),
|
|
bottom = (int)( rect.bottom * m_height );
|
|
GString * text = m_text->getText( left, top, right, bottom );
|
|
QString result = QString::fromUtf8( text->getCString() );
|
|
delete text;
|
|
return result;
|
|
}
|
|
|
|
const ObjectRect * KPDFPage::getObjectRect( double x, double y ) const
|
|
{
|
|
QValueList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
|
|
for ( ; it != end; ++it )
|
|
if ( (*it)->contains( x, y ) )
|
|
return *it;
|
|
return 0;
|
|
}
|
|
|
|
const KPDFPageTransition * KPDFPage::getTransition() const
|
|
{
|
|
return m_transition;
|
|
}
|
|
|
|
|
|
void KPDFPage::setPixmap( int id, QPixmap * pixmap )
|
|
{
|
|
if ( m_pixmaps.contains( id ) )
|
|
delete m_pixmaps[id];
|
|
m_pixmaps[id] = pixmap;
|
|
}
|
|
|
|
void KPDFPage::setSearchPage( TextPage * tp )
|
|
{
|
|
delete m_text;
|
|
m_text = tp;
|
|
}
|
|
|
|
void KPDFPage::setBookmark( bool state )
|
|
{
|
|
m_bookmarked = state;
|
|
}
|
|
|
|
void KPDFPage::setObjectRects( const QValueList< ObjectRect * > rects )
|
|
{
|
|
QValueList< ObjectRect * >::iterator it = m_rects.begin(), end = m_rects.end();
|
|
for ( ; it != end; ++it )
|
|
delete *it;
|
|
m_rects = rects;
|
|
}
|
|
|
|
void KPDFPage::setHighlight( int s_id, NormalizedRect * &rect, const QColor & color )
|
|
{
|
|
// create a HighlightRect descriptor taking values from params
|
|
HighlightRect * hr = new HighlightRect();
|
|
hr->s_id = s_id;
|
|
hr->color = color;
|
|
hr->left = rect->left;
|
|
hr->top = rect->top;
|
|
hr->right = rect->right;
|
|
hr->bottom = rect->bottom;
|
|
// append the HighlightRect to the list
|
|
m_highlights.append( hr );
|
|
// delete old object and change reference
|
|
delete rect;
|
|
rect = hr;
|
|
}
|
|
|
|
void KPDFPage::addAnnotation( Annotation * annotation )
|
|
{
|
|
m_annotations.append( annotation );
|
|
}
|
|
|
|
void KPDFPage::setTransition( KPDFPageTransition * transition )
|
|
{
|
|
delete m_transition;
|
|
m_transition = transition;
|
|
}
|
|
|
|
void KPDFPage::deletePixmap( int id )
|
|
{
|
|
if ( m_pixmaps.contains( id ) )
|
|
{
|
|
delete m_pixmaps[ id ];
|
|
m_pixmaps.remove( id );
|
|
}
|
|
}
|
|
|
|
void KPDFPage::deletePixmapsAndRects()
|
|
{
|
|
// delete all stored pixmaps
|
|
QMap<int,QPixmap *>::iterator it = m_pixmaps.begin(), end = m_pixmaps.end();
|
|
for ( ; it != end; ++it )
|
|
delete *it;
|
|
m_pixmaps.clear();
|
|
// delete ObjectRects
|
|
QValueList< ObjectRect * >::iterator rIt = m_rects.begin(), rEnd = m_rects.end();
|
|
for ( ; rIt != rEnd; ++rIt )
|
|
delete *rIt;
|
|
m_rects.clear();
|
|
}
|
|
|
|
void KPDFPage::deleteHighlights( int s_id )
|
|
{
|
|
// delete highlights by ID
|
|
QValueList< HighlightRect * >::iterator it = m_highlights.begin(), end = m_highlights.end();
|
|
while ( it != end )
|
|
{
|
|
HighlightRect * highlight = *it;
|
|
if ( s_id == -1 || highlight->s_id == s_id )
|
|
{
|
|
it = m_highlights.remove( it );
|
|
delete highlight;
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void KPDFPage::deleteAnnotations()
|
|
{
|
|
// delete all stored annotations
|
|
QValueList< Annotation * >::iterator aIt = m_annotations.begin(), aEnd = m_annotations.end();
|
|
for ( ; aIt != aEnd; ++aIt )
|
|
delete *aIt;
|
|
m_annotations.clear();
|
|
}
|
|
|
|
|
|
/** class NormalizedRect **/
|
|
|
|
NormalizedRect::NormalizedRect()
|
|
: left( 0.0 ), top( 0.0 ), right( 0.0 ), bottom( 0.0 ) {}
|
|
|
|
NormalizedRect::NormalizedRect( double l, double t, double r, double b )
|
|
// note: check for swapping coords?
|
|
: left( l ), top( t ), right( r ), bottom( b ) {}
|
|
|
|
NormalizedRect::NormalizedRect( const QRect & r, double xScale, double yScale )
|
|
: left( (double)r.left() / xScale ), top( (double)r.top() / yScale ),
|
|
right( (double)r.right() / xScale ), bottom( (double)r.bottom() / yScale ) {}
|
|
|
|
bool NormalizedRect::contains( double x, double y ) const
|
|
{
|
|
return x >= left && x <= right && y >= top && y <= bottom;
|
|
}
|
|
|
|
bool NormalizedRect::intersects( const NormalizedRect & r ) const
|
|
{
|
|
return (r.left < right) && (r.right > left) && (r.top < bottom) && (r.bottom > top);
|
|
}
|
|
|
|
bool NormalizedRect::intersects( double l, double t, double r, double b ) const
|
|
{
|
|
return (l < right) && (r > left) && (t < bottom) && (b > top);
|
|
}
|
|
|
|
QRect NormalizedRect::geometry( int xScale, int yScale ) const
|
|
{
|
|
int l = (int)( left * xScale ),
|
|
t = (int)( top * yScale ),
|
|
r = (int)( right * xScale ),
|
|
b = (int)( bottom * yScale );
|
|
return QRect( l, t, r - l + 1, b - t + 1 );
|
|
}
|
|
|
|
|
|
/** class ObjectRect **/
|
|
|
|
ObjectRect::ObjectRect( double l, double t, double r, double b, ObjectType type, void * pnt )
|
|
// assign coordinates swapping them if negative width or height
|
|
: NormalizedRect( r > l ? l : r, b > t ? t : b, r > l ? r : l, b > t ? b : t ),
|
|
m_objectType( type ), m_pointer( pnt )
|
|
{
|
|
}
|
|
|
|
ObjectRect::~ObjectRect()
|
|
{
|
|
if ( !m_pointer )
|
|
return;
|
|
|
|
if ( m_objectType == Link )
|
|
delete static_cast<KPDFLink*>( m_pointer );
|
|
else
|
|
kdDebug() << "Object deletion not implemented for type '" << m_objectType << "' ." << endl;
|
|
}
|