From 2695164b6ecc9221a5a15b73845522c032aecfb9 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Sat, 2 Oct 2004 14:30:30 +0000 Subject: [PATCH] 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 --- kpdf/QOutputDev.cpp | 80 +++++++++++++++++++++++++++++---- kpdf/QOutputDev.h | 15 ++++--- kpdf/TODO | 9 +++- kpdf/document.cpp | 10 +++-- kpdf/kpdf_part.rc | 4 +- kpdf/page.cpp | 107 +++++++++++++++++++++++++++++++++++++++++--- kpdf/page.h | 71 ++++++++++++++++++++--------- kpdf/pageview.cpp | 29 +++++++----- 8 files changed, 265 insertions(+), 60 deletions(-) diff --git a/kpdf/QOutputDev.cpp b/kpdf/QOutputDev.cpp index b8887cd65..5b7e09f4e 100644 --- a/kpdf/QOutputDev.cpp +++ b/kpdf/QOutputDev.cpp @@ -20,7 +20,10 @@ #endif #include +#include +#include +#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) { diff --git a/kpdf/QOutputDev.h b/kpdf/QOutputDev.h index 77b126ace..20d81e4f9 100644 --- a/kpdf/QOutputDev.h +++ b/kpdf/QOutputDev.h @@ -19,13 +19,11 @@ #pragma interface #endif -#include - #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 { diff --git a/kpdf/TODO b/kpdf/TODO index d9363f899..c9216d95a 100644 --- a/kpdf/TODO +++ b/kpdf/TODO @@ -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 diff --git a/kpdf/document.cpp b/kpdf/document.cpp index 1ea8f6408..946cdc840 100644 --- a/kpdf/document.cpp +++ b/kpdf/document.cpp @@ -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 ); diff --git a/kpdf/kpdf_part.rc b/kpdf/kpdf_part.rc index 9ec21ef0e..f0838e954 100644 --- a/kpdf/kpdf_part.rc +++ b/kpdf/kpdf_part.rc @@ -17,8 +17,8 @@ - + &Go @@ -46,8 +46,8 @@ - + diff --git a/kpdf/page.cpp b/kpdf/page.cpp index 94228189d..13bf431fa 100644 --- a/kpdf/page.cpp +++ b/kpdf/page.cpp @@ -15,7 +15,11 @@ #include #include +// system includes +#include + // local includes +#include "Link.h" #include "TextOutputDev.h" #include "page.h" @@ -34,6 +38,9 @@ KPDFPage::~KPDFPage() QMap::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 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 ); + } +} diff --git a/kpdf/page.h b/kpdf/page.h index 2da92ddf7..7127165ea 100644 --- a/kpdf/page.h +++ b/kpdf/page.h @@ -10,11 +10,14 @@ #ifndef _KPDF_PAGE_H_ #define _KPDF_PAGE_H_ +#include +#include + 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 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 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 diff --git a/kpdf/pageview.cpp b/kpdf/pageview.cpp index 01b13899b..4e630776a 100644 --- a/kpdf/pageview.cpp +++ b/kpdf/pageview.cpp @@ -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 ) {