From ce20a5536050718bf310fc2f4bc856f1f192abdc Mon Sep 17 00:00:00 2001
From: Adam Reichold <adamreichold@myopera.com>
Date: Wed, 21 Mar 2012 10:17:17 +0100
Subject: [PATCH 1/2] added a simple TOC interface to the qt4 frontend

---
 qt4/src/poppler-document.cc |   17 +++++++
 qt4/src/poppler-private.cc  |  106 +++++++++++++++++++++++++++++++++++++++++++
 qt4/src/poppler-private.h   |    2 +
 qt4/src/poppler-qt4.h       |   39 ++++++++++++++++
 4 files changed, 164 insertions(+)

diff --git a/qt4/src/poppler-document.cc b/qt4/src/poppler-document.cc
index 550e706..97726a9 100644
--- a/qt4/src/poppler-document.cc
+++ b/qt4/src/poppler-document.cc
@@ -435,6 +435,23 @@ namespace Poppler {
 
         return toc;
     }
+    
+    Document::SimpleTocNode *Document::simpleToc() const
+    {
+        Outline * outline = m_doc->doc->getOutline();
+        if ( !outline )
+            return NULL;
+
+        GooList * items = outline->getItems();
+        if ( !items || items->getLength() < 1 )
+            return NULL;
+
+	    SimpleTocNode *toc = new SimpleTocNode();
+        if ( items->getLength() > 0 )
+           m_doc->addSimpleTocChildren( toc, items );
+
+        return toc;
+    }
 
     LinkDestination *Document::linkDestination( const QString &name )
     {
diff --git a/qt4/src/poppler-private.cc b/qt4/src/poppler-private.cc
index ffb5b92..f462b1d 100644
--- a/qt4/src/poppler-private.cc
+++ b/qt4/src/poppler-private.cc
@@ -221,6 +221,71 @@ namespace Debug {
         }
     }
     
+    void linkActionToSimpleTocNode( ::LinkAction * a, DocumentData * doc, Document::SimpleTocNode * node )
+    {
+        if ( !a || !node )
+            return;
+
+        switch ( a->getKind() )
+        {
+            case actionGoTo:
+            {
+                // page number is contained/referenced in a LinkGoTo
+                LinkGoTo * g = static_cast< LinkGoTo * >( a );
+                LinkDest * destination = g->getDest();
+                if ( !destination && g->getNamedDest() )
+                {
+                    // no 'destination' but an internal 'named reference'. we could
+                    // get the destination for the page now, but it's VERY time consuming,
+                    // so better storing the reference and provide the viewport on demand
+                    GooString *s = g->getNamedDest();
+                    QChar *charArray = new QChar[s->getLength()];
+                    for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->getCString()[i]);
+                    QString aux(charArray, s->getLength());
+                    node->destinationName = aux;
+                    delete[] charArray;
+                }
+                else if ( destination && destination->isOk() )
+                {
+                    LinkDestinationData ldd(destination, NULL, doc, false);
+                    node->destination = LinkDestination(ldd).toString();
+                }
+                break;
+            }
+            case actionGoToR:
+            {
+                // page number is contained/referenced in a LinkGoToR
+                LinkGoToR * g = static_cast< LinkGoToR * >( a );
+                LinkDest * destination = g->getDest();
+                if ( !destination && g->getNamedDest() )
+                {
+                    // no 'destination' but an internal 'named reference'. we could
+                    // get the destination for the page now, but it's VERY time consuming,
+                    // so better storing the reference and provide the viewport on demand
+                    GooString *s = g->getNamedDest();
+                    QChar *charArray = new QChar[s->getLength()];
+                    for (int i = 0; i < s->getLength(); ++i) charArray[i] = QChar(s->getCString()[i]);
+                    QString aux(charArray, s->getLength());
+                    node->destinationName = aux;
+                    delete[] charArray;
+                }
+                else if ( destination && destination->isOk() )
+                {
+                    LinkDestinationData ldd(destination, NULL, doc, g->getFileName() != 0);
+                    node->destination = LinkDestination(ldd).toString();
+                }
+                node->externalFileName = g->getFileName()->getCString();
+                break;
+            }
+            case actionURI:
+            {
+                LinkURI * u = static_cast< LinkURI * >( a );
+                node->destinationURI = u->getURI()->getCString();
+            }
+            default: ;
+        }
+    }
+    
     DocumentData::~DocumentData()
     {
         qDeleteAll(m_embeddedFiles);
@@ -288,5 +353,46 @@ namespace Debug {
                 addTocChildren( docSyn, &item, children );
         }
     }
+    
+    void DocumentData::addSimpleTocChildren( Document::SimpleTocNode *node, GooList * items )
+    {
+        Document::SimpleTocNode *lastNode = node;
+        
+        int numItems = items->getLength();
+        for ( int i = 0; i < numItems; ++i )
+        {
+            // iterate over every object in 'items'
+            OutlineItem * outlineItem = (OutlineItem *)items->get( i );
+
+            // 1. create element using outlineItem's title as tagName
+            QString name;
+            Unicode * uniChar = outlineItem->getTitle();
+            int titleLength = outlineItem->getTitleLength();
+            name = unicodeToQString(uniChar, titleLength);
+            if ( name.isEmpty() )
+                continue;
+            
+            lastNode->name = name;
+            lastNode->isOpen = (bool)outlineItem->isOpen();
+            
+            // 2. find the page the link refers to
+            ::LinkAction * a = outlineItem->getAction();
+            linkActionToSimpleTocNode( a, this, lastNode );
+            
+            // 3. recursively descend over children
+            outlineItem->open();
+            GooList * children = outlineItem->getKids();
+            if ( children ) {
+                lastNode->child = new Document::SimpleTocNode();
+                addSimpleTocChildren( lastNode->child, children );
+            }
+            
+            // 4. prepare the next node
+            if( i+1 < numItems ) {
+                lastNode->sibling = new Document::SimpleTocNode();
+                lastNode = lastNode->sibling;
+            }
+        }
+    }
 
 }
diff --git a/qt4/src/poppler-private.h b/qt4/src/poppler-private.h
index 6d2ef2a..0ea8389 100644
--- a/qt4/src/poppler-private.h
+++ b/qt4/src/poppler-private.h
@@ -143,6 +143,8 @@ namespace Poppler {
 	
 	void addTocChildren( QDomDocument * docSyn, QDomNode * parent, GooList * items );
 	
+	void addSimpleTocChildren( Document::SimpleTocNode * node, GooList * items );
+	
 	void setPaperColor(const QColor &color)
 	{
 		if (color == paperColor)
diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h
index f721f92..9da92f8 100644
--- a/qt4/src/poppler-qt4.h
+++ b/qt4/src/poppler-qt4.h
@@ -1188,6 +1188,45 @@ QString subject = m_doc->info("Subject");
 	*/
 	QDomDocument *toc() const;
 	
+	struct SimpleTocNode {
+		SimpleTocNode() : name(), isOpen(false), destination(), destinationName(), externalFileName(), sibling(0), child(0) {}
+
+		QString name;
+		bool isOpen;
+
+		QString destination;
+		QString destinationName;
+		QString externalFileName;
+		QString destinationURI;
+
+		SimpleTocNode *sibling;
+		SimpleTocNode *child;
+	};
+
+	/**
+	  Gets the table of contents (TOC) of the Document.
+	
+	  The caller is responsable for the returned object.
+	
+	  The tree is return as linked SimpleTocNodes. A node can have
+	  a destination described by the fields:
+	  - Destination: A string description of the referred destination
+	  - DestinationName: A 'named reference' to the viewport
+	  - ExternalFileName: A link to a external filename
+	
+	  Resolving the final destination for each item can be done in the following way:
+	  - first, checking for 'Destination': if not empty, then a LinkDestination
+	    can be constructed straight with it
+	  - as second step, if the 'DestinationName' is not empty, then the destination
+	    can be resolved using linkDestination()
+	
+	  Note also that if 'ExternalFileName' is not emtpy, then the destination refers
+	  to that document (and not to the current one).
+	
+	  \returns the TOC, or NULL if the Document does not have one
+	*/
+	SimpleTocNode *simpleToc() const;
+	
 	/**
 	   Tries to resolve the named destination \p name.
 	
-- 
1.7.9.4


From 9b36a955748400eeb820b66bff62e92e5b4e5763 Mon Sep 17 00:00:00 2001
From: Adam Reichold <adamreichold@myopera.com>
Date: Wed, 21 Mar 2012 10:19:59 +0100
Subject: [PATCH 2/2] modified the qt4 demo to be able to test the simple TOC
 interface

---
 qt4/demos/toc.cpp |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/qt4/demos/toc.cpp b/qt4/demos/toc.cpp
index bf3e5cb..5a78f53 100644
--- a/qt4/demos/toc.cpp
+++ b/qt4/demos/toc.cpp
@@ -23,6 +23,35 @@
 #include <QtGui/QHeaderView>
 #include <QtGui/QTreeWidget>
 
+// uncomment this to try out the simple toc
+// #define USE_SIMPLE_TOC
+
+#ifdef USE_SIMPLE_TOC
+
+static void fillToc(const Poppler::Document::SimpleTocNode *node, QTreeWidget *tree, QTreeWidgetItem *parentItem)
+{
+    QTreeWidgetItem *newitem = 0;
+    for (const Poppler::Document::SimpleTocNode *lastNode = node; lastNode != NULL; lastNode = lastNode->sibling) {
+        if (!parentItem) {
+            newitem = new QTreeWidgetItem(tree, newitem);
+        } else {
+            newitem = new QTreeWidgetItem(parentItem, newitem);
+        }
+
+        newitem->setText(0, lastNode->name);
+        
+        if (lastNode->isOpen) {
+            tree->expandItem(newitem);
+        }
+
+        if (lastNode->child) {
+            fillToc(lastNode->child, tree, newitem);
+        }
+    }
+}
+
+#else
+
 static void fillToc(const QDomNode &parent, QTreeWidget *tree, QTreeWidgetItem *parentItem)
 {
     QTreeWidgetItem *newitem = 0;
@@ -50,6 +79,8 @@ static void fillToc(const QDomNode &parent, QTreeWidget *tree, QTreeWidgetItem *
     }
 }
 
+#endif
+
 
 TocDock::TocDock(QWidget *parent)
     : AbstractInfoDock(parent)
@@ -68,6 +99,21 @@ TocDock::~TocDock()
 
 void TocDock::fillInfo()
 {
+
+#ifdef USE_SIMPLE_TOC
+
+    const Poppler::Document::SimpleTocNode *toc = document()->simpleToc();
+    if(toc) {
+        fillToc(toc, m_tree, 0);
+    } else {
+        QTreeWidgetItem *item = new QTreeWidgetItem();
+        item->setText(0, tr("No TOC"));
+        item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
+        m_tree->addTopLevelItem(item);
+    }
+
+#else
+
     const QDomDocument *toc = document()->toc();
     if (toc) {
         fillToc(*toc, m_tree, 0);
@@ -77,6 +123,9 @@ void TocDock::fillInfo()
         item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
         m_tree->addTopLevelItem(item);
     }
+    
+#endif
+
 }
 
 void TocDock::documentClosed()
-- 
1.7.9.4

