Commit 0c5cd4e0 authored by Enrico Ros's avatar Enrico Ros
Browse files

Fixed a GeneratorPDF <-> KPDFOutputDev bug.

Abstracted xpdf's Outline to a Dom Tree. Fixed MERGE tags through the code.
Added comments (documentation) to KPDFDocument class and other classes in
document.h. Changed a little Generator interface.
Moved code from toc.h/.cpp to GeneratorPDF and shrinked a lot.

TODO (now): Convert TOC (widget) to use the Dom tree as data source.

svn path=/branches/kpdf_experiments/kdegraphics/kpdf/; revision=369914
parent 87244030
......@@ -35,9 +35,9 @@
//NOTE: XPDF/Splash *implementation dependant* code is marked with '###'
//BEGIN KPDFOutputDev
KPDFOutputDev::KPDFOutputDev( SplashColor paperColor )
KPDFOutputDev::KPDFOutputDev( GeneratorPDF * parent, SplashColor paperColor )
: SplashOutputDev( splashModeRGB8, false, paperColor ), m_pixmap( 0 ),
m_generator( 0 ), m_text( 0 )
m_generator( parent ), m_text( 0 )
{
}
......@@ -46,7 +46,7 @@ KPDFOutputDev::~KPDFOutputDev()
clear();
}
void KPDFOutputDev::setParams( int width, int height, bool genT, bool genL, bool genI, GeneratorPDF * parent )
void KPDFOutputDev::setParams( int width, int height, bool genT, bool genL, bool genI )
{
clear();
......@@ -57,8 +57,6 @@ void KPDFOutputDev::setParams( int width, int height, bool genT, bool genL, bool
m_generateLinks = genL;
m_generateImages = genI;
m_generator = parent;
if ( m_generateText )
m_text = new TextPage( gFalse );
}
......@@ -66,8 +64,8 @@ void KPDFOutputDev::setParams( int width, int height, bool genT, bool genL, bool
KPDFLink * KPDFOutputDev::generateLink( LinkAction * a )
{
KPDFLink * link = 0;
switch ( a->getKind() )
KPDFLink * link = NULL;
if ( a ) switch ( a->getKind() )
{
case actionGoTo:
{
......
......@@ -42,12 +42,12 @@ class KPDFPageRect;
class KPDFOutputDev : public SplashOutputDev
{
public:
KPDFOutputDev( SplashColor paperColor );
KPDFOutputDev( GeneratorPDF * parent, SplashColor paperColor );
virtual ~KPDFOutputDev();
// to be called before PDFDoc->displayPage( thisclass, .. )
void setParams( int pixmapWidth, int pixmapHeight, bool generateTextpage,
bool decodeLinks, bool decodeImages, GeneratorPDF * parent );
bool decodeLinks, bool decodeImages );
// generate a valid KPDFLink subclass (or null) from a xpdf's LinkAction
KPDFLink * generateLink( LinkAction * );
......
......@@ -7,9 +7,9 @@ Legend:
(*) - Some parts of this item are already done
In progress on the branch (first item comes first):
-> Abstract contents generation [70% missing TextPage, Outline]
-> memory manager with different profiles (mem/cpu tradeoff: {memory saving, normal, memory aggressive}) [0%]
-> ADD: viewport changes the right way when clicking links (also suggested by Mikolaj Machowski) [60% done]
-> Abstract contents generation [70% missing TextPage only!]
-> memory manager with different profiles (mem/cpu tradeoff: {memory saving, normal, memory aggressive}) [20%]
-> ADD: viewport changes the right way when clicking links (also suggested by Mikolaj Machowski) [65% done]
Things to do in order to merge in HEAD (first item has highest priority):
-> take care of naming on merge, too differences (remove some kpdf_* prefixes
......@@ -56,6 +56,7 @@ More items (first items will enter 'In progress list' first):
-> rotate the whole document / individual pages
-> fullscreen pdf view (presentations-like) with some gfx tools
-> incremental zoom with fast-refresh (tested but flickering!) or contour tracing
-> investigate 'Splash' lack of smoothness at low resolutions (see lines in thumbnails)
Done (newest feature comes firts):
-> FIX: Dynamic zoom repaints the page while rescaling.
......
......@@ -45,7 +45,6 @@ class KPDFDocumentPrivate
// cached stuff
int currentPage;
DocumentInfo noDocumentInfo;
// observers related (note: won't delete oservers)
QMap< int, KPDFDocumentObserver* > observers;
......@@ -61,7 +60,6 @@ KPDFDocument::KPDFDocument()
{
d->currentPage = -1;
d->searchPage = -1;
d->noDocumentInfo.title = i18n( "No document opened!" );
}
KPDFDocument::~KPDFDocument()
......@@ -145,11 +143,14 @@ void KPDFDocument::reparseConfig()
}
const DocumentInfo & KPDFDocument::documentInfo() const
const DocumentInfo * KPDFDocument::documentInfo() const
{
if ( generator )
return generator->documentInfo();
return d->noDocumentInfo;
return generator ? generator->documentInfo() : NULL;
}
const DocumentSynopsis * KPDFDocument::documentSynopsis() const
{
return generator ? generator->documentSynopsis() : NULL;
}
const KPDFPage * KPDFDocument::page( uint n ) const
......@@ -172,11 +173,6 @@ bool KPDFDocument::okToPrint() const
return generator ? generator->allowed( Generator::Print ) : false;
}
Outline * KPDFDocument::outline() const
{
return generator ? generator->synopsis().outline : 0;
}
void KPDFDocument::requestPixmap( int id, uint page, int width, int height, bool syn )
{
......@@ -308,7 +304,7 @@ void KPDFDocument::processLink( const KPDFLink * link )
}
// note: if external file is opened, 'link' doesn't exist anymore!
setCurrentPage( destVp.page ); //TODO implement and use viewport
setCurrentPage( destVp.page ); //TODO implement and use Viewport
} break;
case KPDFLink::Execute: {
......
......@@ -13,19 +13,20 @@
#include <qvaluevector.h>
#include <qstring.h>
#include <qdom.h>
class KPrinter;
class KPDFPage;
class KPDFLink;
class Generator;
class DocumentInfo;
class Outline; // FIXME: ABSTRACT-REDO
class DocumentSynopsis;
/**
* @short Base class for objects being notified when something changes.
*
* Inherit this class and call KPDFDocument->addObserver( obsClass ) to get notified
* of asyncronous events (a new pixmap has arrived, changed, etc... ).
* Inherit this class and call KPDFDocument->addObserver( yourClass ) to get
* notified of asyncronous events (new pixmap generated, or changed, etc..).
*/
class KPDFDocumentObserver
{
......@@ -48,9 +49,21 @@ class KPDFDocumentObserver
#define TOC_ID 4
/**
* @short The Document. Actions (like open,find) take place here.
* @short The Document. Heart of everything. Actions take place here.
*
* ### MERGE: comment
* The Document is the main object in KPDF. All views query the Document to
* get data/properties or even for accessing pages (in a 'const' way).
*
* It is designed to keep it detached from the document type (pdf, ps, you
* name it..) so whenever you want to get some data, it asks its internals
* generator to do the job and return results in a format-indepedent way.
*
* Apart from the generator (the currently running one) the document stores
* all the Pages ('KPDFPage' class) of the current document in a vector and
* notifies all the registered DocumentObservers when some content changes.
*
* For a better understanding of hieracies @see README.internals.png
* @see KPDFDocumentObserver, KPDFPage
*/
class KPDFDocument
{
......@@ -67,12 +80,12 @@ class KPDFDocument
void reparseConfig();
// query methods (const ones)
const DocumentInfo & documentInfo() const;
const DocumentInfo * documentInfo() const;
const DocumentSynopsis * documentSynopsis() const;
const KPDFPage * page( uint page ) const;
uint currentPage() const;
uint pages() const;
bool okToPrint() const;
Outline * outline() const;
// perform actions on document / pages
void requestPixmap( int id, uint page, int width, int height, bool syncronous = false );
......@@ -96,9 +109,13 @@ class KPDFDocument
class KPDFDocumentPrivate * d;
};
/**
* ### MERGE: comment
* @short Metadata that describes the document.
*
* The Info structure can be filled in by generators to display metadata
* about the currently opened file.
* FUTURE: use a Dom tree so every generator can have different fields for
* its metadata and renew the display widget to use the dynamic format.
*/
struct DocumentInfo
{
......@@ -117,13 +134,25 @@ struct DocumentInfo
};
/**
* ### TEMP IMPLEMENTATION. ABSTRACT OutLine !!
* ### NOTE: this IMPL is for PDF only. Need to better abstract this.
* @short A Dom tree that describes the Table of Contents.
*
* The Synopsis (TOC or Table Of Contents for friends) is represented via
* a dom tree where each nod has an internal name (displayed in the listview)
* and one or more attributes.
*
* To fill in a valid synopsis tree just add domElements where the tag name
* is the screen name of the entry.
*
* The following attributes are valid [more may be added in future]:
* - Page: The page to which the node refers.
* - Position: The position inside the page, where 0 means top and 100 is
* the bottom.
*/
class DocumentSynopsis
class DocumentSynopsis : public QDomDocument
{
public:
Outline * outline;
// void implementation, only subclassed for naming!
DocumentSynopsis() : QDomDocument() {};
};
#endif
......@@ -35,8 +35,9 @@ class Generator
// load a document and fill up the pagesVector
virtual bool loadDocument( const QString & fileName, QValueVector< KPDFPage* > & pagesVector ) = 0;
// Document description
virtual const DocumentInfo & documentInfo() = 0;
// Document description and Table of contents
virtual const DocumentInfo * documentInfo() { return 0L; }
virtual const DocumentSynopsis * documentSynopsis() { return 0L; }
// DRM handling
enum Permissions { Modify = 1, Copy = 2, Print = 4, AddNotes = 8 };
......@@ -46,7 +47,6 @@ class Generator
virtual bool print( KPrinter& printer ) = 0;
virtual bool requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous = false ) = 0;
virtual void requestTextPage( KPDFPage * page ) = 0;
virtual DocumentSynopsis& synopsis() = 0;
// check configuration and return if something changed
virtual bool reparseConfig() { return false; }
......
......@@ -22,6 +22,10 @@
#include "xpdf/Link.h"
#include "xpdf/ErrorCodes.h"
#include "xpdf/UnicodeMap.h"
#include "xpdf/Outline.h"
#include "goo/GList.h"
//#include "xpdf/PDFDoc.h"
//#include "xpdf/GlobalParams.h"
// local includes
#include "generator_pdf.h"
......@@ -32,11 +36,11 @@
GeneratorPDF::GeneratorPDF()
: pdfdoc( 0 ), kpdfOutputDev( 0 ), docInfoDirty( true )
: pdfdoc( 0 ), kpdfOutputDev( 0 ),
docInfoDirty( true ), docSynopsisDirty( true )
{
// generate kpdfOutputDev and cache page color
reparseConfig();
syn.outline = 0;
}
GeneratorPDF::~GeneratorPDF()
......@@ -118,7 +122,7 @@ bool GeneratorPDF::loadDocument( const QString & fileName, QValueVector<KPDFPage
}
const DocumentInfo & GeneratorPDF::documentInfo()
const DocumentInfo * GeneratorPDF::documentInfo()
{
if ( docInfoDirty )
{
......@@ -149,7 +153,70 @@ const DocumentInfo & GeneratorPDF::documentInfo()
if ( pdfdoc )
docInfoDirty = false;
}
return docInfo;
return &docInfo;
}
const DocumentSynopsis * GeneratorPDF::documentSynopsis()
{
if ( !docSynopsisDirty )
return &docSyn;
if ( !pdfdoc )
return NULL;
Outline * outline = pdfdoc->getOutline();
if ( !outline )
return NULL;
GList * items = outline->getItems();
if ( !items || items->getLength() < 1 )
return NULL;
docSyn = DocumentSynopsis();
if ( items->getLength() > 0 )
addDomChildren( &docSyn, items );
docSynopsisDirty = false;
return &docSyn;
}
void GeneratorPDF::addDomChildren( QDomNode * parent, GList * items )
{
int numItems = items->getLength();
for ( int i = 0; i < numItems; ++i )
{
// iterate over every object in 'items'
OutlineItem * outlineItem = (OutlineItem *)items->get( i );
// create element using outlineItem's title as tagName
QString name;
Unicode * uniChar = outlineItem->getTitle();
for ( int i = 0; i < outlineItem->getTitleLength(); ++i )
name += uniChar[ i ];
if ( name.isEmpty() )
continue;
QDomElement item = docSyn.createElement( name );
parent->appendChild( item );
// set element's attributes
if ( kpdfOutputDev )
{
KPDFLink * link = kpdfOutputDev->generateLink( outlineItem->getAction() );
if ( link && link->linkType() == KPDFLink::Goto )
{
KPDFLinkGoto * linkGoto = static_cast< KPDFLinkGoto * >( link );
item.setAttribute( "Page", linkGoto->destViewport().page );
//TODO item.setAttribute( "Position", 0 );
}
delete link;
}
// recursively descend over children
outlineItem->open();
GList * children = outlineItem->getKids();
if ( children )
addDomChildren( &item, children );
}
}
......@@ -208,7 +275,7 @@ bool GeneratorPDF::requestPixmap( int id, KPDFPage * page, int width, int height
bool genTextPage = !page->hasSearchPage() && (width == page->width()) && (height == page->height());
// generate links and image rects if rendering pages on pageview
bool genRects = id == PAGEVIEW_ID;
kpdfOutputDev->setParams( width, height, genTextPage, genRects, genRects, this );
kpdfOutputDev->setParams( width, height, genTextPage, genRects, genRects );
docLock.lock();
pdfdoc->displayPage( kpdfOutputDev, page->number() + 1, fakeDpiX, fakeDpiY, 0, true, genRects );
......@@ -245,12 +312,6 @@ void GeneratorPDF::requestTextPage( KPDFPage * page )
page->setSearchPage( td.takeTextPage() );
}
DocumentSynopsis& GeneratorPDF::synopsis()
{
syn.outline = pdfdoc->getOutline();
return syn;
}
bool GeneratorPDF::reparseConfig()
{
// load paper color from Settings or use the white default color
......@@ -267,7 +328,7 @@ bool GeneratorPDF::reparseConfig()
// rebuild the output device using the new paper color
docLock.lock();
delete kpdfOutputDev;
kpdfOutputDev = new KPDFOutputDev( splashCol );
kpdfOutputDev = new KPDFOutputDev( this, splashCol );
if ( pdfdoc )
kpdfOutputDev->startDoc( pdfdoc->getXRef() );
docLock.unlock();
......
......@@ -19,6 +19,7 @@
#include "link.h"
class PDFDoc;
class GList;
class KPDFOutputDev;
/**
......@@ -36,13 +37,13 @@ class GeneratorPDF : public Generator
bool loadDocument( const QString & fileName, QValueVector<KPDFPage*> & pagesVector );
// [INHERITED] document informations
const DocumentInfo & documentInfo();
const DocumentInfo * documentInfo();
const DocumentSynopsis * documentSynopsis();
// [INHERITED] perform actions on document / pages
bool print( KPrinter& printer );
bool requestPixmap( int id, KPDFPage * page, int width, int height, bool syncronous = false );
void requestTextPage( KPDFPage * page );
DocumentSynopsis& synopsis();
// [INHERITED] reparse configuration
bool reparseConfig();
......@@ -54,6 +55,8 @@ class GeneratorPDF : public Generator
// private functions for accessing document informations via PDFDoc
QString getDocumentInfo( const QString & data ) const;
QString getDocumentDate( const QString & data ) const;
// private function for creating the document synopsis hieracy
void addDomChildren( QDomNode * parent, GList * items );
// private classes
QMutex docLock;
......@@ -62,7 +65,8 @@ class GeneratorPDF : public Generator
QColor paperColor;
bool docInfoDirty;
DocumentInfo docInfo;
DocumentSynopsis syn;
bool docSynopsisDirty;
DocumentSynopsis docSyn;
};
/*
......
......@@ -19,17 +19,22 @@ propertiesDialog::propertiesDialog(QWidget *parent, KPDFDocument *doc) : KDialog
{
properties *p = new properties(this);
setMainWidget(p);
const DocumentInfo & info = doc->documentInfo();
p->pagesValue->setText(QString::number(doc->pages()));
p->authorValue->setText( info.author );
p->titleValue->setText( info.title );
p->subjectValue->setText( info.subject );
p->keywordsValue->setText( info.keywords );
p->producerValue->setText( info.producer );
p->creatorValue->setText( info.creator );
p->optimizedValue->setText( info.optimization );
p->securityValue->setText( info.encryption );
p->versionValue->setText( info.format + " v." + info.formatVersion );
p->createdValue->setText( info.creationDate );
p->modifiedValue->setText( info.modificationDate );
const DocumentInfo * info = doc->documentInfo();
if ( !info )
{
p->titleValue->setText( i18n( "No document opened!" ) );
return;
}
p->pagesValue->setText( QString::number( doc->pages() ) );
p->authorValue->setText( info->author );
p->titleValue->setText( info->title );
p->subjectValue->setText( info->subject );
p->keywordsValue->setText( info->keywords );
p->producerValue->setText( info->producer );
p->creatorValue->setText( info->creator );
p->optimizedValue->setText( info->optimization );
p->securityValue->setText( info->encryption );
p->versionValue->setText( info->format + " v." + info->formatVersion );
p->createdValue->setText( info->creationDate );
p->modifiedValue->setText( info->modificationDate );
}
......@@ -9,136 +9,65 @@
#include <qheader.h>
#include "goo/GList.h"
#include "xpdf/GlobalParams.h"
#include "xpdf/PDFDoc.h"
#include "xpdf/Outline.h"
#include "xpdf/UnicodeMap.h"
#include "toc.h"
#include "page.h"
#include "document.h"
class TOCItem : public KListViewItem
{
public:
TOCItem(KListView *parent, TOCItem *after, QString name, LinkAction *action) :
KListViewItem(parent, after, name), m_action(action)
TOCItem(KListView *parent, TOCItem *after, QString name, const QDomElement & e) :
KListViewItem(parent, after, name), m_element(e)
{
}
TOCItem(KListViewItem *parent, TOCItem *after, QString name, LinkAction *action) :
KListViewItem(parent, after, name), m_action(action)
TOCItem(KListViewItem *parent, TOCItem *after, QString name, const QDomElement & e) :
KListViewItem(parent, after, name), m_element(e)
{
}
LinkAction *getAction() const
const QDomElement & element() const
{
return m_action;
return m_element;
}
private:
LinkAction *m_action;
QDomElement m_element;
};
TOC::TOC(QWidget *parent, KPDFDocument *document) : KListView(parent), m_document(document)
{
addColumn("");
header() -> hide();
setSorting(-1);
setRootIsDecorated(true);
setResizeMode(AllColumns);
connect(this, SIGNAL(executed(QListViewItem *)), this, SLOT(slotExecuted(QListViewItem *)));
addColumn("");
header() -> hide();
setSorting(-1);
setRootIsDecorated(true);
setResizeMode(AllColumns);
connect(this, SIGNAL(executed(QListViewItem *)), this, SLOT(slotExecuted(QListViewItem *)));
}
uint TOC::observerId() const
{
return TOC_ID;
return TOC_ID;
}
void TOC::pageSetup( const QValueVector<KPDFPage*> & /*pages*/, bool documentChanged)
void TOC::pageSetup( const QValueVector<KPDFPage*> & pages, bool documentChanged)
{
if (documentChanged)
{
GList *items, *kids;
OutlineItem *item;
UnicodeMap *uMap;
GString *enc;
TOCItem *last;
clear();
last = 0;
Outline *out = m_document->outline();
if (out) items = out->getItems();
else items = 0;
if (items && items->getLength() > 0)
{
enc = new GString("Latin1");
uMap = globalParams->getUnicodeMap(enc);
delete enc;
for (int i = 0; i < items->getLength(); ++i)
{
item = (OutlineItem *)items->get(i);
last = new TOCItem(this, last, getTitle(item->getTitle(), item->getTitleLength(), uMap), item->getAction());
item->open();
if ((kids = item->getKids()))
{
addKids(last, kids, uMap);
}
}
emit hasTOC(true);
}
else emit hasTOC(false);
}
}
if ( !documentChanged || pages.size() < 1 )
return;
void TOC::addKids(KListViewItem *parent, GList *items, UnicodeMap *uMap)
{
GList *kids;
OutlineItem *item;
TOCItem *last;
last = 0;
if (items && items->getLength() > 0)
{
for (int i = 0; i < items->getLength(); ++i)
{
item = (OutlineItem *)items->get(i);
last = new TOCItem(parent, last, getTitle(item->getTitle(), item->getTitleLength(), uMap), item->getAction());
item->open();
if ((kids = item->getKids()))
{
addKids(last, kids, uMap);
}
}
}
}
QString TOC::getTitle(Unicode *u, int length, UnicodeMap *uMap) const
{
GString *title;
QString s;
int n;
char buf[8];
clear();
const DocumentSynopsis * syn = m_document->documentSynopsis();
if ( syn )
{
}
title = new GString();
for (int j = 0; j < length; ++j)
{
n = uMap->mapUnicode(u[j], buf, sizeof(buf));
title->append(buf, n);
}
s = title->getCString();
delete title;
return s;
emit hasTOC( syn );