Commit 2a3f35ac authored by Enrico Ros's avatar Enrico Ros

Fixed broken link following (if not in external document).

 Normalized [0..1] every object attached to page.
Fixed find-ahead (didn't loop after last page).

svn path=/trunk/kdegraphics/kpdf/; revision=387447
parent bcf32aaf
......@@ -81,7 +81,7 @@ struct RunningSearch
{
// store search properties
int continueOnPage;
HighlightRect continueOnMatch;
NormalizedRect continueOnMatch;
QValueList< int > highlightedPages;
// fields related to previous searches (used for 'continueSearch')
......@@ -619,21 +619,19 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar
// loop on a page adding highlights for all found items
bool addedHighlights = false;
HighlightRect * lastMatch = 0;
NormalizedRect * lastMatch = 0;
while ( 1 )
{
if ( lastMatch )
lastMatch = page->searchText( text, caseSensitive, lastMatch );
lastMatch = page->findText( text, caseSensitive, lastMatch );
else
lastMatch = page->searchText( text, caseSensitive );
lastMatch = page->findText( text, caseSensitive );
if ( !lastMatch )
break;
// add highligh rect to the page
lastMatch->id = searchID;
lastMatch->color = color;
page->setHighlight( lastMatch );
page->setHighlight( searchID, lastMatch, color );
addedHighlights = true;
}
......@@ -663,45 +661,38 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar
KPDFPage * lastPage = fromStart ? 0 : pages_vector[ currentPage ];
// continue checking last SearchPage first (if it is the current page)
HighlightRect * r = 0;
NormalizedRect * match = 0;
if ( lastPage && lastPage->number() == s->continueOnPage )
{
if ( newText )
r = lastPage->searchText( text, caseSensitive );
else
r = lastPage->searchText( text, caseSensitive, &s->continueOnMatch );
if ( r )
{
s->continueOnMatch = *r;
s->highlightedPages.append( currentPage );
}
match = lastPage->findText( text, caseSensitive );
else
match = lastPage->findText( text, caseSensitive, &s->continueOnMatch );
if ( !match )
currentPage++;
}
// if no match found, loop through the whole doc, starting from currentPage
if ( !r )
if ( !match )
{
const int pageCount = pages_vector.count();
for ( int i = 0; i < pageCount; i++ )
{
if ( currentPage >= pageCount )
{
if ( !noDialogs && KMessageBox::questionYesNo(0, i18n("End of document reached.\nContinue from the beginning?")) == KMessageBox::Yes )
if ( noDialogs || KMessageBox::questionYesNo(0, i18n("End of document reached.\nContinue from the beginning?")) == KMessageBox::Yes )
currentPage = 0;
else
break;
}
// get page
KPDFPage * page = pages_vector[ currentPage ];
// request search page if needed
if ( !page->hasSearchPage() )
requestTextPage( page->number() );
if ( (r = page->searchText( text, caseSensitive )) )
{
s->continueOnPage = currentPage;
s->continueOnMatch = *r;
s->highlightedPages.append( currentPage );
// if found a match on the current page, end the loop
if ( (match = page->findText( text, caseSensitive )) )
break;
}
currentPage++;
}
}
......@@ -710,27 +701,27 @@ bool KPDFDocument::searchText( int searchID, const QString & text, bool fromStar
QApplication::restoreOverrideCursor();
// if a match has been found..
if ( r )
if ( match )
{
// update the RunningSearch structure adding this match..
foundAMatch = true;
s->continueOnPage = currentPage;
s->continueOnMatch = *match;
s->highlightedPages.append( currentPage );
// ..add highlight to the page..
int pageNumber = currentPage;
KPDFPage * foundPage = pages_vector[ currentPage ];
r->id = searchID;
r->color = color;
foundPage->setHighlight( r );
pages_vector[ currentPage ]->setHighlight( searchID, match, color );
// ..queue page for notifying changes..
if ( !pagesToNotify.contains( pageNumber ) )
pagesToNotify.append( pageNumber );
if ( !pagesToNotify.contains( currentPage ) )
pagesToNotify.append( currentPage );
// ..move the viewport to show the searched word centered
if ( moveViewport )
{
DocumentViewport searchViewport( pageNumber );
DocumentViewport searchViewport( currentPage );
searchViewport.reCenter.enabled = true;
searchViewport.reCenter.normalizedCenterX = (r->left + r->right) / 2.0;
searchViewport.reCenter.normalizedCenterY = (r->top + r->bottom) / 2.0;
searchViewport.reCenter.normalizedCenterX = (match->left + match->right) / 2.0;
searchViewport.reCenter.normalizedCenterY = (match->top + match->bottom) / 2.0;
setViewport( searchViewport, -1, true );
}
}
......@@ -831,6 +822,11 @@ void KPDFDocument::processLink( const KPDFLink * link )
kdWarning() << "Link: Error opening '" << go->fileName() << "'." << endl;
return;
}
else
{
setViewport( d->nextDocumentViewport );
d->nextDocumentViewport = DocumentViewport();
}
} break;
......
......@@ -164,7 +164,12 @@ void KPDFOutputDev::drawLink( Link * link, Catalog * catalog )
int left, top, right, bottom;
cvtUserToDev( x1, y1, &left, &top );
cvtUserToDev( x2, y2, &right, &bottom );
KPDFPageRect * rect = new KPDFPageRect( left, top, right, bottom );
double nl = (double)left / (double)m_pixmapWidth,
nt = (double)top / (double)m_pixmapHeight,
nr = (double)right / (double)m_pixmapWidth,
nb = (double)bottom / (double)m_pixmapHeight;
// create the rect using normalized coords
KPDFPageRect * rect = new KPDFPageRect( nl, nt, nr, nb );
// attach the link object to the rect and add it to the vector container
rect->setPointer( l, KPDFPageRect::Link );
m_rects.push_back( rect );
......@@ -220,7 +225,12 @@ void KPDFOutputDev::drawImage( GfxState *state, Object *ref, Stream *str,
if ( width > 10 && height > 10 )
{
// build a descriptor for the image rect
KPDFPageRect * r = new KPDFPageRect( left, top, left + width, top + height );
double nl = (double)left / (double)m_pixmapWidth,
nt = (double)top / (double)m_pixmapHeight,
nr = (double)(left + width) / (double)m_pixmapWidth,
nb = (double)(top + height) / (double)m_pixmapHeight;
// create the rect using normalized coords
KPDFPageRect * r = new KPDFPageRect( nl, nt, nr, nb );
r->setPointer( 0, KPDFPageRect::Image );
// add the rect to the vector container
m_rects.push_back( r );
......
......@@ -49,22 +49,6 @@ KPDFPage::~KPDFPage()
}
bool KPDFPage::hasHighlights( int id ) const
{
// simple case: have no highlights
if ( m_highlights.isEmpty() )
return false;
// simple case: we have highlights and no id to match
if ( 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)->id == id )
return true;
return false;
}
bool KPDFPage::hasPixmap( int id, int width, int height ) const
{
if ( !m_pixmaps.contains( id ) )
......@@ -80,54 +64,45 @@ bool KPDFPage::hasSearchPage() const
return m_text != 0;
}
bool KPDFPage::hasRect( int mouseX, int mouseY ) const
bool KPDFPage::hasBookmark() const
{
return m_bookmarked;
}
bool KPDFPage::hasRect( double x, double y ) const
{
if ( m_rects.count() < 1 )
return false;
QValueList< KPDFPageRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end; ++it )
if ( (*it)->contains( mouseX, mouseY ) )
if ( (*it)->contains( x, y ) )
return true;
return false;
}
bool KPDFPage::hasLink( int mouseX, int mouseY ) const
{
const KPDFPageRect *r;
r = getRect( mouseX, mouseY);
return r && r->pointerType() == KPDFPageRect::Link;
}
const KPDFPageRect * KPDFPage::getRect( int mouseX, int mouseY ) const
bool KPDFPage::hasHighlights( int s_id ) const
{
QValueList< KPDFPageRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
// 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)->contains( mouseX, mouseY ) )
return *it;
return 0;
if ( (*it)->s_id == s_id )
return true;
return false;
}
const KPDFPageTransition * KPDFPage::getTransition() const
bool KPDFPage::hasTransition() const
{
return m_transition;
}
const QString KPDFPage::getTextInRect( const QRect & rect, double zoom ) const
{
if ( !m_text )
return QString::null;
int left = (int)((double)rect.left() / zoom),
top = (int)((double)rect.top() / zoom),
right = (int)((double)rect.right() / zoom),
bottom = (int)((double)rect.bottom() / zoom);
GString * text = m_text->getText( left, top, right, bottom );
QString result = QString::fromUtf8( text->getCString() );
delete text;
return result;
return m_transition != 0;
}
HighlightRect * KPDFPage::searchText( const QString & text, bool strictCase, HighlightRect * lastRect )
NormalizedRect * KPDFPage::findText( const QString & text, bool strictCase, NormalizedRect * lastRect ) const
{
if ( text.isEmpty() )
return 0;
......@@ -178,12 +153,41 @@ HighlightRect * KPDFPage::searchText( const QString & text, bool strictCase, Hig
}
}
// if the page was found, return a new normalized HighlightRect
// if the page was found, return a new normalizedRect
if ( found )
return new HighlightRect( sLeft / m_width, sTop / m_height, sRight / m_width, sBottom / m_height );
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 KPDFPageRect * KPDFPage::getRect( double x, double y ) const
{
QValueList< KPDFPageRect * >::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 ) )
......@@ -197,6 +201,11 @@ void KPDFPage::setSearchPage( TextPage * tp )
m_text = tp;
}
void KPDFPage::setBookmark( bool state )
{
m_bookmarked = state;
}
void KPDFPage::setRects( const QValueList< KPDFPageRect * > rects )
{
QValueList< KPDFPageRect * >::iterator it = m_rects.begin(), end = m_rects.end();
......@@ -205,11 +214,21 @@ void KPDFPage::setRects( const QValueList< KPDFPageRect * > rects )
m_rects = rects;
}
void KPDFPage::setHighlight( HighlightRect * hr, bool add )
void KPDFPage::setHighlight( int s_id, NormalizedRect * &rect, const QColor & color )
{
if ( !add )
deleteHighlights( hr->id );
// 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::setTransition( const KPDFPageTransition * transition )
......@@ -241,14 +260,14 @@ void KPDFPage::deletePixmapsAndRects()
m_rects.clear();
}
void KPDFPage::deleteHighlights( int id )
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 ( id == -1 || highlight->id == id )
if ( s_id == -1 || highlight->s_id == s_id )
{
it = m_highlights.remove( it );
delete highlight;
......@@ -259,31 +278,56 @@ void KPDFPage::deleteHighlights( int id )
}
/** class KPDFPageRect **/
/** 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 ) {}
KPDFPageRect::KPDFPageRect( int l, int t, int r, int b )
: m_pointerType( NoPointer ), m_pointer( 0 )
bool NormalizedRect::contains( double x, double y ) const
{
// assign coordinates swapping them if negative width or height
m_xMin = r > l ? l : r;
m_xMax = r > l ? r : l;
m_yMin = b > t ? t : b;
m_yMax = b > t ? b : t;
return x >= left && x <= right && y >= top && y <= bottom;
}
KPDFPageRect::~KPDFPageRect()
bool NormalizedRect::intersects( const NormalizedRect & r ) const
{
deletePointer();
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);
}
bool KPDFPageRect::contains( int x, int y ) const
/** class KPDFPageRect **/
KPDFPageRect::KPDFPageRect( double l, double t, double r, double b )
// 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_pointerType( NoPointer ), m_pointer( 0 )
{
return (x > m_xMin) && (x < m_xMax) && (y > m_yMin) && (y < m_yMax);
}
QRect KPDFPageRect::geometry() const
KPDFPageRect::~KPDFPageRect()
{
return QRect( m_xMin, m_yMin, m_xMax - m_xMin, m_yMax - m_yMin );
deletePointer();
}
QRect KPDFPageRect::geometry( int width, int height ) const
{
int l = (int)( left * width ),
t = (int)( top * height ),
r = (int)( right * width ),
b = (int)( bottom * height );
return QRect( l, t, r - l, b - t );
}
void KPDFPageRect::setPointer( void * object, enum PointerType pType )
......@@ -314,16 +358,3 @@ void KPDFPageRect::deletePointer()
kdDebug() << "Object deletion not implemented for type '"
<< m_pointerType << "' ." << endl;
}
/** class HighlightRect **/
HighlightRect::HighlightRect()
: id( -1 ), left( 0.0 ), top( 0.0 ), right( 0.0 ), bottom( 0.0 )
{
}
HighlightRect::HighlightRect( double l, double t, double r, double b )
: id( -1 ), left( l ), top( t ), right( r ), bottom( b )
{
}
......@@ -18,6 +18,7 @@ class QRect;
class TextPage;
class KPDFPageRect;
class KPDFPageTransition;
class NormalizedRect;
class HighlightRect;
/**
......@@ -27,7 +28,10 @@ class HighlightRect;
* a search page (a class used internally for retrieving text), rect classes
* (that describe links or other active areas in the current page) and more.
*
* Note: All objects are reparented to this class.
* All coordinates are normalized to the page, so {x,y} are valid in [0,1]
* range as long as NormalizedRect components.
*
* Note: The class takes ownership of all objects.
*/
class KPDFPage
{
......@@ -35,38 +39,34 @@ class KPDFPage
KPDFPage( uint number, float width, float height, int rotation );
~KPDFPage();
enum KPDFPageAttributes { Bookmark = 1, Highlights = 2 };
// query properties (const read-only methods)
inline int number() const { return m_number; }
inline int rotation() const { return m_rotation; }
inline float width() const { return m_width; }
inline float height() const { return m_height; }
inline float ratio() const { return m_height / m_width; }
inline bool hasBookmark() const { return m_bookmarked; }
bool hasHighlights( int id = -1 ) const;
bool hasPixmap( int id, int width = -1, int height = -1 ) const;
bool hasPixmap( int p_id, int width = -1, int height = -1 ) const;
bool hasSearchPage() const;
bool hasRect( int mouseX, int mouseY ) const;
bool hasLink( int mouseX, int mouseY ) const;
const KPDFPageRect * getRect( int mouseX, int mouseY ) const;
bool hasBookmark() const;
bool hasRect( double x, double y ) const;
bool hasHighlights( int s_id = -1 ) const;
bool hasTransition() const;
NormalizedRect * findText( const QString & text, bool keepCase, NormalizedRect * last = 0 ) const;
const QString getText( const NormalizedRect & rect ) const;
const KPDFPageRect * getRect( double x, double y ) const;
const KPDFPageTransition * getTransition() const;
const QString getTextInRect( const QRect & rect, double zoom /*= 1.0*/ ) const;
// operations (by KPDFDocument)
inline void setBookmark( bool state ) { m_bookmarked = state; }
HighlightRect * searchText( const QString & text,
bool strictCase, HighlightRect * lastPosition = 0 );
// oprations: set/delete contents (by KPDFDocument)
void setPixmap( int id, QPixmap * pixmap );
void setPixmap( int p_id, QPixmap * pixmap );
void setSearchPage( TextPage * text );
void setBookmark( bool state );
void setRects( const QValueList< KPDFPageRect * > rects );
void setHighlight( HighlightRect * hr, bool add = true );
void setHighlight( int s_id, NormalizedRect * &r, const QColor & color );
void setTransition( const KPDFPageTransition * transition );
void deletePixmap( int id );
void deletePixmap( int p_id );
void deletePixmapsAndRects();
void deleteHighlights( int id = -1 );
void deleteHighlights( int s_id = -1 );
private:
friend class PagePainter;
......@@ -82,6 +82,23 @@ class KPDFPage
};
/**
* @short A rect in normalized [0,1] coordinates.
*/
class NormalizedRect
{
public:
double left, top, right, bottom;
NormalizedRect();
NormalizedRect( double l, double t, double r, double b );
NormalizedRect( const QRect & r, double xScale, double yScale );
bool contains( double x, double y ) const;
bool intersects( const NormalizedRect & normRect ) const;
bool intersects( double l, double t, double r, double b ) const;
};
/**
* @short A rect on the page that may contain an object.
*
......@@ -97,17 +114,15 @@ class KPDFPage
* - NoPointer : '' : no object is stored
* - Link : class KPDFLink : description of a link
* - Image : class KPDFImage : description of an image
*
*/
class KPDFPageRect
class KPDFPageRect : public NormalizedRect
{
public:
KPDFPageRect( int left, int top, int right, int bottom );
KPDFPageRect( double left, double top, double right, double bottom );
~KPDFPageRect();
// query geometric properties
bool contains( int x, int y ) const;
QRect geometry() const;
// expand NormalizedRect to given width and height
QRect geometry( int width, int height ) const;
// set a pointer to data associated to this rect
enum PointerType { NoPointer, Link, Image };
......@@ -119,24 +134,18 @@ class KPDFPageRect
private:
void deletePointer();
int m_xMin, m_xMax, m_yMin, m_yMax;
PointerType m_pointerType;
void * m_pointer;
};
/**
* @short A normalized and colored highlight on the page.
* Internal Storage: normalized colored highlight owned by id
*/
struct HighlightRect
struct HighlightRect : public NormalizedRect
{
// publicly accessed fields
int id;
double left, top, right, bottom;
// more publicly accessed fields
int s_id;
QColor color;
// initialize default values
HighlightRect();
HighlightRect( double l, double t, double r, double b );
};
#endif
......@@ -82,9 +82,7 @@ void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
QValueList< HighlightRect * >::const_iterator hIt = page->m_highlights.begin(), hEnd = page->m_highlights.end();
for ( ; hIt != hEnd; ++hIt )
{
HighlightRect * r = *hIt;
// intersection: A) highlightRect, B) normalized limits
if ( (r->left < nXMax) && (r->right > nXMin) && (r->top < nYMax) && (r->bottom > nYMin) )
if ( (*hIt)->intersects( nXMin, nYMin, nXMax, nYMax ) )
{
paintHighlights = true;
break;
......@@ -216,7 +214,7 @@ void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
if ( (enhanceLinks && rect->pointerType() == KPDFPageRect::Link) ||
(enhanceImages && rect->pointerType() == KPDFPageRect::Image) )
{
QRect rectGeometry = rect->geometry();