half link stuff foreported from head. Links are intercepted by our oDev and

stored as KPDFLink inside KPDFPage(s) :-). What we have now: links are
detected when hovering them on a page eve in multiple-pages-per-view mode.

svn path=/branches/kpdf_experiments/kdegraphics/kpdf/; revision=351190
This commit is contained in:
Enrico Ros 2004-10-02 14:30:30 +00:00
parent 5b3dbe60c4
commit 2695164b6e
8 changed files with 265 additions and 60 deletions

View file

@ -20,7 +20,10 @@
#endif
#include <kdebug.h>
#include <qpixmap.h>
#include <qimage.h>
#include "page.h"
#include "SplashBitmap.h"
#include "TextOutputDev.h"
#include "QOutputDev.h"
@ -36,11 +39,14 @@ KPDFOutputDev::KPDFOutputDev(SplashColor paperColor)
KPDFOutputDev::~KPDFOutputDev()
{
QValueList< KPDFLink * >::iterator it = m_links.begin(), end = m_links.end();
for ( ; it != end; ++it )
delete *it;
delete m_pixmap;
delete m_text;
}
void KPDFOutputDev::setParams( int width, int height, bool generateText )
void KPDFOutputDev::setParams( int width, int height, bool genText, bool /*genLinks*/ )
{
m_pixmapWidth = width;
m_pixmapHeight = height;
@ -51,7 +57,12 @@ void KPDFOutputDev::setParams( int width, int height, bool generateText )
}
delete m_text;
m_text = generateText ? new TextPage( gFalse ) : 0;
m_text = genText ? new TextPage( gFalse ) : 0;
QValueList< KPDFLink * >::iterator it = m_links.begin(), end = m_links.end();
for ( ; it != end; ++it )
delete *it;
m_links.clear();
}
QPixmap * KPDFOutputDev::takePixmap()
@ -68,6 +79,13 @@ TextPage * KPDFOutputDev::takeTextPage()
return text;
}
QValueList< KPDFLink * > KPDFOutputDev::takeLinks()
{
QValueList< KPDFLink * > linksCopy( m_links );
m_links.clear();
return linksCopy;
}
void KPDFOutputDev::startPage(int pageNum, GfxState *state)
{
m_pageNum = pageNum;
@ -102,14 +120,58 @@ void KPDFOutputDev::endPage()
SplashOutputDev::startPage(0, NULL);
}
void KPDFOutputDev::drawLink(Link * /*l*/, Catalog */*catalog*/)
void KPDFOutputDev::drawLink(Link * link, Catalog */*catalog*/)
{
/* double x1,y1, x2,y2;
l->getRect( &x1,&y1, &x2,&y2 );
LinkAction * a = l->getAction();
pri NOWARN ntf("LINK %x ok:%d t:%d rect:[%f,%f,%f,%f] \n", (uint)l, (int)l->isOk(),
(int)a->getKind(), x1,y2, x2-x1, y2-y1 );
*/}
if ( !link->isOk() )
return;
// create the new KPDFLink using transformed link coordinates
double x1, y1, x2, y2;
link->getRect( &x1, &y1, &x2, &y2 );
int left, top, right, bottom;
cvtUserToDev( x1, y1, &left, &top );
cvtUserToDev( x2, y2, &right, &bottom );
KPDFLink * l = new KPDFLink( left, top, right, bottom );
// add the link to the vector container
m_links.push_back( l );
// set link action params processing (XPDF)LinkAction
LinkAction * a = link->getAction();
switch ( a->getKind() )
{
case actionGoTo: {
LinkGoTo * g = (LinkGoTo *) a;
GString * nd = g->getNamedDest();
LinkDest * d = g->getDest();
l->setLinkGoto( d ? d->copy() : 0, nd ? nd->getCString() : 0 );
} break;
case actionGoToR: {
LinkGoToR * g = (LinkGoToR *) a;
GString * nd = g->getNamedDest();
LinkDest * d = g->getDest();
l->setLinkGoto( d ? d->copy() : 0, nd ? nd->getCString() : 0, g->getFileName()->getCString() );
} break;
case actionLaunch:
l->setLinkExecute( ((LinkLaunch *)a)->getFileName()->getCString(),
((LinkLaunch *)a)->getParams()->getCString() );
break;
case actionURI:
l->setLinkURI( ((LinkURI *)a)->getURI()->getCString() );
break;
case actionNamed:
l->setLinkNamed( ((LinkNamed *)a)->getName()->getCString() );
break;
case actionMovie: {
LinkMovie * m = (LinkMovie *) a;
Ref * r = m->getAnnotRef();
l->setLinkMovie( r->num, r->gen, m->getTitle()->getCString() );
} break;
case actionUnknown:
// TODO Warn or not???
break;
}
}
void KPDFOutputDev::updateFont(GfxState *state)
{

View file

@ -19,13 +19,11 @@
#pragma interface
#endif
#include <qimage.h>
#include "SplashOutputDev.h"
#include "Link.h"
class TextPage;
class KPDFPage;
class KPDFLink;
/**
* @short A SplashOutputDev renderer that grabs text and links.
@ -33,7 +31,7 @@ class KPDFPage;
* This output device:
* - renders the page using SplashOutputDev (its parent)
* - harvests text into a textPage (for searching text)
* - harvests links and set them to a KPDFPage
* - harvests links and collect them
*/
class KPDFOutputDev : public SplashOutputDev
{
@ -42,11 +40,12 @@ public:
virtual ~KPDFOutputDev();
// to be called before PDFDoc->displayPage( thisclass, .. )
void setParams( int pixmapWidth, int pixmapHeight, bool generateText );
void setParams( int pixmapWidth, int pixmapHeight, bool generateTextpage, bool generateLinks );
// takes pointers out of the class (so deletion it's up to others)
QPixmap * takePixmap();
TextPage * takeTextPage();
QValueList< KPDFLink * > takeLinks();
/** inherited from OutputDev */
// Start a page.
@ -70,6 +69,9 @@ private:
// text page generated on demand
TextPage * m_text;
// links generated on demand
QValueList< KPDFLink * > m_links;
};
@ -78,7 +80,8 @@ private:
*
* This is the simplest OutputDev. It harvests text from currently
* rendered page and provides a method for getting the TextPage.
* Xpdf's textOutputDev can't return a textpage, unfortunately.
* Xpdf's textOutputDev can't return a textpage, unfortunately, and
* KPDFOutputDev is too heavy for sucha a simple task.
*/
class KPDFTextDev : public OutputDev
{

View file

@ -2,23 +2,28 @@ Personal Albert's list
-> make links functional (done in HEAD)
More items
-> new icons (contest at kde-look that will end in 2004-Oct-01)
-> new icons (contest at kde-look that will end in 2004-Oct-XX)
-> screen editing (annotations): framework (BR67300,BR62793)
-> screen editing (annotations): tools (BR67300)
-> export all text in plain_text/html
-> extract(export?) images
-> implement history (mainly for actionNamed)
-> history as a toolbox child (collecting DOs's setPage calls)
-> zoom: fit text (with configurable margin)
-> automatic online dictionaries / translators (BR80338)
-> session support: restoring page location (BR82589)
-> merge head copyright headers (by albert)
-> merge head xpdf changes for --enable-final (by adrian de groot/albert)
-> merge head support for show menubar in rmb (by albert)
-> wrong zoom buttons order (BR74248) (check consistancy with kdvi/kghostview/.. (not konq))
Porting / In progress on the branch (first item comes first):
-> use a kconfigxt settings framework
-> fix keys/mouse in single/continous modes
-> porting Albert's link following
-> minimize PageView's reLayout and requestPixmaps on changes
-> implementing async document generator using Albert's thread as the generation thread
-> reading aids (accessibility): mode: normal, invert, contrast, recolor bg/text
-> reading aids (accessibility): mode: normal, invert, contrast, recolor bg/text (enhance links too)
Done (sorted by inv.time)
-> remake single page mode

View file

@ -250,15 +250,19 @@ void KPDFDocument::requestPixmap( int id, uint page, int width, int height, bool
// setup kpdf output device: text page is generated only if we are at 72dpi.
// since we can pre-generate the TextPage at the right res.. why not?
bool genTextPage = !kp->hasSearchPage() && (width == kp->width()) && (height == kp->height());
d->kpdfOutputDev->setParams( width, height, genTextPage );
// generate links if rendering pages on pageview
bool genLinks = id == PAGEVIEW_ID;
d->kpdfOutputDev->setParams( width, height, genTextPage, genLinks );
d->docLock.lock();
d->pdfdoc->displayPage( d->kpdfOutputDev, page + 1, fakeDpiX, fakeDpiY, 0, true, false/*dolinks*/ );
d->pdfdoc->displayPage( d->kpdfOutputDev, page + 1, fakeDpiX, fakeDpiY, 0, true, genLinks );
d->docLock.unlock();
kp->setPixmap( id, d->kpdfOutputDev->takePixmap() );
if ( genTextPage )
kp->setSearchPage( d->kpdfOutputDev->takeTextPage() );
if ( genLinks )
kp->setLinks( d->kpdfOutputDev->takeLinks() );
d->observers[id]->notifyPixmapChanged( page );
}
@ -294,7 +298,7 @@ void KPDFDocument::slotSetFilter( const QString & pattern, bool keepCase )
void KPDFDocument::slotBookmarkPage( int page, bool on )
{
KPDFPage * p = ( page < d->pages.count() ) ? d->pages[page] : 0;
KPDFPage * p = ( page < (int)d->pages.count() ) ? d->pages[page] : 0;
if ( p )
{
p->bookmark( on );

View file

@ -17,8 +17,8 @@
<Action name="zoom_fit_width"/>
<Action name="zoom_fit_page"/>
<Separator/>
<Action name="view_continous"/>
<Action name="view_twopages"/>
<Action name="view_continous"/>
</Menu>
<Menu name="go"><text>&amp;Go</text>
<Action name="first_page"/>
@ -46,8 +46,8 @@
<!--Action name="zoom_fit_page"/-->
<Merge/>
<Separator/>
<Action name="view_continous"/>
<Action name="view_twopages"/>
<Action name="view_continous"/>
<Separator/>
<Action name="mouse_drag"/>
<Action name="mouse_select"/>

View file

@ -15,7 +15,11 @@
#include <qpainter.h>
#include <qmap.h>
// system includes
#include <string.h>
// local includes
#include "Link.h"
#include "TextOutputDev.h"
#include "page.h"
@ -34,6 +38,9 @@ KPDFPage::~KPDFPage()
QMap<int,QPixmap *>::iterator it = m_pixmaps.begin(), end = m_pixmaps.end();
for ( ; it != end; ++it )
delete *it;
QValueList< KPDFLink * >::iterator lIt = m_links.begin(), lEnd = m_links.end();
for ( ; lIt != lEnd; ++lIt )
delete *lIt;
delete m_text;
}
@ -53,9 +60,13 @@ bool KPDFPage::hasSearchPage() const
bool KPDFPage::hasLink( int mouseX, int mouseY ) const
{
//TODO this.
//Sample implementation using a small rect as 'active' link zone
return QRect( 20,20, 100,50 ).contains( mouseX, mouseY );
if ( m_links.count() < 0 )
return false;
QValueList< KPDFLink * >::const_iterator it = m_links.begin(), end = m_links.end();
for ( ; it != end; ++it )
if ( (*it)->contains( mouseX, mouseY ) )
return true;
return false;
}
// BEGIN commands (paint / search)
@ -169,12 +180,96 @@ void KPDFPage::setSearchPage( TextPage * tp )
m_text = tp;
}
/*
void KPDFPage::setLinks( ..SomeStruct.. )
{ //TODO this
void KPDFPage::setLinks( const QValueList<KPDFLink *> links )
{
QValueList< KPDFLink * >::iterator it = m_links.begin(), end = m_links.end();
for ( ; it != end; ++it )
delete *it;
m_links = links;
}
/*
void KPDFPage::setPixmapOverlayNotations( ..DOMdescription.. )
{ //TODO this
}
*/
KPDFLink::KPDFLink( int l, int t, int r, int b )
: m_type( Unknown ), m_dest( 0 ), m_destNamed( 0 ),
m_fileName( 0 ), m_parameters( 0 ), m_uri( 0 )
{
// assign coordinates swapping them if negative width or height
x_min = r > l ? l : r;
x_max = r > l ? r : l;
y_min = b > t ? t : b;
y_max = b > t ? b : t;
}
KPDFLink::~KPDFLink()
{
delete m_dest;
delete [] m_destNamed;
delete [] m_fileName;
delete [] m_parameters;
delete [] m_uri;
}
void KPDFLink::setLinkGoto( LinkDest * d, const char * n, const char * file )
{
m_type = Goto;
delete m_dest;
m_dest = d;
copyString( m_destNamed, n );
copyString( m_fileName, file );
}
void KPDFLink::setLinkExecute( const char * file, const char * par )
{
m_type = Execute;
copyString( m_fileName, file );
copyString( m_parameters, par );
}
void KPDFLink::setLinkNamed( const char * name )
{
m_type = Action;
copyString( m_uri, name );
}
void KPDFLink::setLinkURI( const char * uri )
{
m_type = URI;
copyString( m_uri, uri );
}
void KPDFLink::setLinkMovie( int ref_num, int ref_gen, const char * title )
{
m_type = Movie;
m_refNum = ref_num;
m_refGen = ref_gen;
copyString( m_uri, title );
}
KPDFLink::LinkType KPDFLink::type() const
{
return m_type;
}
bool KPDFLink::contains( int x, int y ) const
{
return (x > x_min) && (x < x_max) && (y > y_min) && (y < y_max);
}
void KPDFLink::copyString( char * dest, const char * src )
{
if ( dest )
delete [] dest;
dest = 0;
if ( src )
{
dest = new char[ strlen(src) + 1 ];
strcpy( dest, src );
}
}

View file

@ -10,11 +10,14 @@
#ifndef _KPDF_PAGE_H_
#define _KPDF_PAGE_H_
#include <qmap.h>
#include <qvaluelist.h>
class QPainter;
class QPixmap;
//class QString;
//class QRect;
class TextPage;
class LinkDest;
class KPDFLink;
/**
* @short Collector for all the data belonging to a page.
@ -25,15 +28,13 @@ class TextPage;
*
* Note: All objects passed to this class will be destoryed on class deletion.
*/
// ### HACK : this structure is under big changes ###
class KPDFPage
{
public:
KPDFPage( int number, float width, float height, int rotation );
~KPDFPage();
// query properties (const read-only methods)
// query properties and draw (const read-only methods)
uint number() const { return m_number; }
float width() const { return m_width; }
float height() const { return m_height; }
@ -44,17 +45,17 @@ public:
bool hasPixmap( int id, int width, int height ) const;
bool hasSearchPage() const;
bool hasLink( int mouseX, int mouseY ) const;
// commands
void drawPixmap( int id, QPainter * p, const QRect & rect, int width, int height ) const;
// commands (not const methods caled by KPDFDocument)
bool hasText( const QString & text, bool strictCase, bool fromTop );
void hilightLastSearch( bool enabled );
void bookmark( bool enabled );
// set page contents
// set page contents (not const methods caled by KPDFDocument)
void setPixmap( int id, QPixmap * pixmap );
void setSearchPage( TextPage * text );
/*void setLinks( ..SomeStruct.. ); or (better): */
void setLinks( const QValueList<KPDFLink *> links );
/*void setPixmapOverlayNotations( ..DOMdescription.. );*/
private:
@ -63,26 +64,56 @@ private:
bool m_hilighting, m_bookmarking;
double m_sLeft, m_sTop, m_sRight, m_sBottom;
QMap<int,QPixmap *> m_pixmaps;
QMap< int, QPixmap * > m_pixmaps;
TextPage * m_text;
QValueList< KPDFLink * > m_links;
};
/*
/**
* @short Encapsulates data that describes a link.
*
* There are many types of PDF links, here we provide accessors to set the
* link to be of the given type. Other functions are for asking if a point
* is inside the link rect (in displayed page coordinates).
* KPDFLinks are created by the KPDFOutputDevice then stored and deleted
* inside the referring KPDFPage.
* Note: this structure is similar to XPDF LinkAction and its hieracy, but
* is needed for storing data inside pages, since XPDF's PDFDoc deletes
* Links objects when changing page (and we need persistant storage).
*/
class KPDFLink
{
public:
enum LinkType { Goto, Execute, Action, URI, Movie };
KPDFLink( int left, int top, int right, int bottom );
~KPDFLink();
KPDFLink( LinkType type ) : m_type( type ) {};
// action types setup
enum LinkType { Goto, Execute, Action, URI, Movie, Unknown };
void setLinkGoto( LinkDest * dest, const char * namedDest, const char * fileName = 0 );
void setLinkExecute( const char * file, const char * parameters );
void setLinkNamed( const char * name );
void setLinkURI( const char * uri );
void setLinkMovie( int ref_num, int ref_gen, const char * title );
void setType( LinkType type ) { m_type = type; }
LinkType type() { return m_type; }
// query
LinkType type() const;
bool contains( int x, int y ) const;
private:
LinkType m_type;
float x_min, x_max, y_min, y_max;
// [Goto] type
// general
LinkType m_type;
float x_min, x_max, y_min, y_max;
// []
// actions related
LinkDest * m_dest;
char * m_destNamed;
char * m_fileName;
char * m_parameters;
char * m_uri;
int m_refNum, m_refGen;
void copyString( char * dest, const char * src );
};
*/
#endif

View file

@ -87,9 +87,10 @@ PageView::PageView( QWidget *parent, KPDFDocument *document )
d = new PageViewPrivate();
d->document = document;
d->page = 0;
d->vectorIndex = -1;
d->viewColumns = 1;
d->viewContinous = false;
d->zoomMode = ZoomFitWidth;
d->zoomMode = ZoomFixed;
d->zoomFactor = 1.0;
d->mouseMode = MouseNormal;
d->mouseOnLink = false;
@ -308,17 +309,21 @@ void PageView::contentsMouseReleaseEvent( QMouseEvent * e )
// check if it was a click, in that case select the page
if ( e->globalPos() == d->mouseStartPos && page )
d->document->slotSetCurrentPage( page->pageNumber() );
// check if we release the mouse over the same link if so play it
/* TODO Albert
PageLink * link = *PAGE* ->findLink(e->x()/m_ppp, e->y()/m_ppp);
if ( link == d->pressedLink )
//go to link, use:
document->slotSetCurrentPagePosition( (int)link->page(), (float)link->position() );
//and all the views will update and display the right page at the right position
d->pressedLink = 0;
*/
{
if ( d->mouseOnLink )
{
/* TODO Enrico: port links here
PageLink * link = *PAGE* ->findLink(e->x()/m_ppp, e->y()/m_ppp);
if ( link == d->pressedLink )
//go to link, use:
document->slotSetCurrentPagePosition( (int)link->page(), (float)link->position() );
//and all the views will update and display the right page at the right position
d->pressedLink = 0;
*/
}
else
d->document->slotSetCurrentPage( page->pageNumber() );
}
}
else if ( e->button() == Qt::RightButton && page )
{