[poppler] 4 commits - poppler/Catalog.cc poppler/Catalog.h poppler/Link.cc poppler/Link.h qt4/src

Pino Toscano pino at kemper.freedesktop.org
Wed Mar 26 13:00:09 PDT 2008


 poppler/Catalog.cc           |   39 +
 poppler/Catalog.h            |    7 
 poppler/Link.cc              |   33 +
 poppler/Link.h               |   23 
 qt4/src/poppler-document.cc  |   15 
 qt4/src/poppler-link.cc      |   36 +
 qt4/src/poppler-link.h       |   32 +
 qt4/src/poppler-page.cc      |    7 
 qt4/src/poppler-page.cc.orig | 1317 +++++++++++++++++++++++++++++++++++++++++++
 qt4/src/poppler-qt4.h        |    8 
 10 files changed, 1516 insertions(+), 1 deletion(-)

New commits:
commit 7a47ff3b54678a3de6964d25050e02186484f39a
Author: Pino Toscano <pino at kde.org>
Date:   Wed Mar 26 21:00:32 2008 +0100

    [Qt4] Add support for JavaScript links, and create them when present.

diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc
index 3d94363..fb9df21 100644
--- a/qt4/src/poppler-link.cc
+++ b/qt4/src/poppler-link.cc
@@ -151,6 +151,19 @@ class LinkSoundPrivate : public LinkPrivate
 		delete sound;
 	}
 
+class LinkJavaScriptPrivate : public LinkPrivate
+{
+	public:
+		LinkJavaScriptPrivate( const QRectF &area );
+
+		QString js;
+};
+
+	LinkJavaScriptPrivate::LinkJavaScriptPrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
 #if 0
 class LinkMoviePrivate : public LinkPrivate
 {
@@ -515,6 +528,29 @@ class LinkMoviePrivate : public LinkPrivate
 		return d->sound;
 	}
 
+	// LinkJavaScript
+	LinkJavaScript::LinkJavaScript( const QRectF &linkArea, const QString &js )
+		: Link( *new LinkJavaScriptPrivate( linkArea ) )
+	{
+		Q_D( LinkJavaScript );
+		d->js = js;
+	}
+	
+	LinkJavaScript::~LinkJavaScript()
+	{
+	}
+	
+	Link::LinkType LinkJavaScript::linkType() const
+	{
+		return JavaScript;
+	}
+	
+	QString LinkJavaScript::script() const
+	{
+		Q_D( const LinkJavaScript );
+		return d->js;
+	}
+
 #if 0
 	// LinkMovie
 	LinkMovie::LinkMovie( const QRectF &linkArea )
diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h
index 204ffe1..5102d06 100644
--- a/qt4/src/poppler-link.h
+++ b/qt4/src/poppler-link.h
@@ -34,6 +34,7 @@ class LinkExecutePrivate;
 class LinkBrowsePrivate;
 class LinkActionPrivate;
 class LinkSoundPrivate;
+class LinkJavaScriptPrivate;
 class LinkMoviePrivate;
 class LinkDestinationData;
 class LinkDestinationPrivate;
@@ -124,7 +125,8 @@ class POPPLER_QT4_EXPORT Link
 		    Browse,
 		    Action,
 		    Sound,    ///< A link representing a sound to be played
-		    Movie
+		    Movie,
+		    JavaScript    ///< A JavaScript code to be interpreted
 		};
 
 		/**
@@ -342,6 +344,34 @@ class POPPLER_QT4_EXPORT LinkSound : public Link
 		Q_DISABLE_COPY( LinkSound )
 };
 
+/** JavaScript: a JavaScript code to be interpreted. **/
+class POPPLER_QT4_EXPORT LinkJavaScript : public Link
+{
+	public:
+		/**
+		 * Create a new JavaScript link.
+		 *
+		 * \param linkArea the active area of the link
+		 * \param js the JS code to be interpreted
+		 */
+		LinkJavaScript( const QRectF &linkArea, const QString &js );
+		/**
+		 * Destructor.
+		 */
+		virtual ~LinkJavaScript();
+
+		LinkType linkType() const;
+
+		/**
+		 * The JS code
+		 */
+		QString script() const;
+
+	private:
+		Q_DECLARE_PRIVATE( LinkJavaScript )
+		Q_DISABLE_COPY( LinkJavaScript )
+};	
+
 #if 0
 /** Movie: Not yet defined -> think renaming to 'Media' link **/
 class POPPLER_QT4_EXPORT LinkMovie : public Link
diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc
index 478845d..630010a 100644
--- a/qt4/src/poppler-page.cc
+++ b/qt4/src/poppler-page.cc
@@ -140,6 +140,13 @@ Link* PageData::convertLinkActionToLink(::LinkAction * a, const QRectF &linkArea
     }
     break;
 
+    case actionJavaScript:
+    {
+      ::LinkJavaScript *ljs = (::LinkJavaScript *)a;
+      popplerLink = new LinkJavaScript( linkArea, UnicodeParsedString(ljs->getScript()) );
+    }
+    break;
+
     case actionMovie:
 /*      TODO this (Movie link)
           m_type = Movie;
diff --git a/qt4/src/poppler-page.cc.orig b/qt4/src/poppler-page.cc.orig
new file mode 100644
index 0000000..478845d
--- /dev/null
+++ b/qt4/src/poppler-page.cc.orig
@@ -0,0 +1,1317 @@
+/* poppler-page.cc: qt interface to poppler
+ * Copyright (C) 2005, Net Integration Technologies, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <poppler-qt4.h>
+
+#include <QtCore/QMap>
+#include <QtCore/QVarLengthArray>
+#include <QtGui/QImage>
+#include <QtGui/QPainter>
+
+#include <config.h>
+#include <PDFDoc.h>
+#include <Catalog.h>
+#include <Form.h>
+#include <ErrorCodes.h>
+#include <TextOutputDev.h>
+#if defined(HAVE_SPLASH)
+#include <SplashOutputDev.h>
+#include <splash/SplashBitmap.h>
+#include <ArthurOutputDev.h>
+#endif
+
+#include "poppler-private.h"
+#include "poppler-page-transition-private.h"
+#include "poppler-page-private.h"
+#include "poppler-link-extractor-private.h"
+#include "poppler-annotation-helper.h"
+#include "poppler-annotation-private.h"
+#include "poppler-form.h"
+
+namespace Poppler {
+
+class DummyAnnotation : public Annotation
+{
+    public:
+        DummyAnnotation()
+            : Annotation( *new AnnotationPrivate() )
+        {
+        }
+
+        virtual SubType subType() const { return A_BASE; }
+};
+
+Link* PageData::convertLinkActionToLink(::LinkAction * a, const QRectF &linkArea)
+{
+  if ( !a )
+    return NULL;
+
+  Link * popplerLink = NULL;
+  switch ( a->getKind() )
+  {
+    case actionGoTo:
+    {
+      LinkGoTo * g = (LinkGoTo *) a;
+      // create link: no ext file, namedDest, object pointer
+      popplerLink = new LinkGoto( linkArea, QString::null, LinkDestination( LinkDestinationData(g->getDest(), g->getNamedDest(), parentDoc ) ) );
+    }
+    break;
+
+    case actionGoToR:
+    {
+      LinkGoToR * g = (LinkGoToR *) a;
+      // copy link file
+      const char * fileName = g->getFileName()->getCString();
+      // ceate link: fileName, namedDest, object pointer
+      popplerLink = new LinkGoto( linkArea, (QString)fileName, LinkDestination( LinkDestinationData(g->getDest(), g->getNamedDest(), parentDoc ) ) );
+    }
+    break;
+
+    case actionLaunch:
+    {
+      LinkLaunch * e = (LinkLaunch *)a;
+      GooString * p = e->getParams();
+      popplerLink = new LinkExecute( linkArea, e->getFileName()->getCString(), p ? p->getCString() : 0 );
+    }
+    break;
+
+    case actionNamed:
+    {
+      const char * name = ((LinkNamed *)a)->getName()->getCString();
+      if ( !strcmp( name, "NextPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PageNext );
+      else if ( !strcmp( name, "PrevPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PagePrev );
+      else if ( !strcmp( name, "FirstPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PageFirst );
+      else if ( !strcmp( name, "LastPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::PageLast );
+      else if ( !strcmp( name, "GoBack" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::HistoryBack );
+      else if ( !strcmp( name, "GoForward" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::HistoryForward );
+      else if ( !strcmp( name, "Quit" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Quit );
+      else if ( !strcmp( name, "GoToPage" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::GoToPage );
+      else if ( !strcmp( name, "Find" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Find );
+      else if ( !strcmp( name, "FullScreen" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Presentation );
+      else if ( !strcmp( name, "Close" ) )
+      {
+        // acroread closes the document always, doesnt care whether 
+        // its presentation mode or not
+        // popplerLink = new LinkAction( linkArea, LinkAction::EndPresentation );
+        popplerLink = new LinkAction( linkArea, LinkAction::Close );
+      }
+      else
+      {
+        // TODO
+      }
+    }
+    break;
+
+    case actionURI:
+    {
+      popplerLink = new LinkBrowse( linkArea, ((LinkURI *)a)->getURI()->getCString() );
+    }
+    break;
+
+    case actionSound:
+    {
+      ::LinkSound *ls = (::LinkSound *)a;
+      popplerLink = new LinkSound( linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject( ls->getSound() ) );
+    }
+    break;
+
+    case actionMovie:
+/*      TODO this (Movie link)
+          m_type = Movie;
+          LinkMovie * m = (LinkMovie *) a;
+          // copy Movie parameters (2 IDs and a const char *)
+          Ref * r = m->getAnnotRef();
+          m_refNum = r->num;
+          m_refGen = r->gen;
+          copyString( m_uri, m->getTitle()->getCString() );
+*/  break;
+
+    case actionUnknown:
+    break;
+  }
+
+  return popplerLink;
+}
+
+
+Page::Page(DocumentData *doc, int index) {
+  m_page = new PageData();
+  m_page->index = index;
+  m_page->parentDoc = doc;
+  m_page->page = doc->doc->getCatalog()->getPage(m_page->index + 1);
+  m_page->transition = 0;
+}
+
+Page::~Page()
+{
+  delete m_page->transition;
+  delete m_page;
+}
+
+QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, Rotation rotate) const
+{
+  int rotation = (int)rotate * 90;
+  QImage img;
+  switch(m_page->parentDoc->m_backend)
+  {
+    case Poppler::Document::SplashBackend:
+    {
+#if defined(HAVE_SPLASH)
+      SplashOutputDev *splash_output = static_cast<SplashOutputDev *>(m_page->parentDoc->getOutputDev());
+
+      m_page->parentDoc->doc->displayPageSlice(splash_output, m_page->index + 1, xres, yres,
+						 rotation, false, true, false, x, y, w, h);
+
+      SplashBitmap *bitmap = splash_output->getBitmap();
+      int bw = bitmap->getWidth();
+      int bh = bitmap->getHeight();
+
+      SplashColorPtr dataPtr = splash_output->getBitmap()->getDataPtr();
+
+      if (QSysInfo::BigEndian == QSysInfo::ByteOrder)
+      {
+        uchar c;
+        int count = bw * bh * 4;
+        for (int k = 0; k < count; k += 4)
+        {
+          c = dataPtr[k];
+          dataPtr[k] = dataPtr[k+3];
+          dataPtr[k+3] = c;
+
+          c = dataPtr[k+1];
+          dataPtr[k+1] = dataPtr[k+2];
+          dataPtr[k+2] = c;
+        }
+      }
+
+      // construct a qimage SHARING the raw bitmap data in memory
+      QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 );
+      img = tmpimg.copy();
+      // unload underlying xpdf bitmap
+      splash_output->startPage( 0, NULL );
+#endif
+      break;
+    }
+    case Poppler::Document::ArthurBackend:
+    {
+#if defined(HAVE_SPLASH)
+      QSize size = pageSize();
+      QImage tmpimg(w == -1 ? qRound( size.width() * xres / 72.0 ) : w, h == -1 ? qRound( size.height() * yres / 72.0 ) : h, QImage::Format_ARGB32);
+
+      QPainter painter(&tmpimg);
+      if (m_page->parentDoc->m_hints & Document::Antialiasing)
+          painter.setRenderHint(QPainter::Antialiasing);
+      if (m_page->parentDoc->m_hints & Document::TextAntialiasing)
+          painter.setRenderHint(QPainter::TextAntialiasing);
+      painter.save();
+      painter.translate(x == -1 ? 0 : -x, y == -1 ? 0 : -y);
+      ArthurOutputDev arthur_output(&painter);
+      arthur_output.startDoc(m_page->parentDoc->doc->getXRef());
+      m_page->parentDoc->doc->displayPageSlice(&arthur_output,
+						 m_page->index + 1,
+						 xres,
+						 yres,
+						 rotation,
+						 false,
+						 true,
+						 false,
+						 x,
+						 y,
+						 w,
+						 h);
+      painter.restore();
+      painter.end();
+      img = tmpimg;
+#endif
+      break;
+    }
+  }
+
+  return img;
+}
+
+QString Page::text(const QRectF &r) const
+{
+  TextOutputDev *output_dev;
+  GooString *s;
+  PDFRectangle *rect;
+  QString result;
+  
+  output_dev = new TextOutputDev(0, gFalse, gFalse, gFalse);
+  m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72,
+      0, false, true, false, -1, -1, -1, -1);
+  if (r.isNull())
+  {
+    rect = m_page->page->getCropBox();
+    s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2);
+  }
+  else
+  {
+    double height, y1, y2;
+    height = m_page->page->getCropHeight();
+    y1 = height - r.top();
+    y2 = height - r.bottom();
+    s = output_dev->getText(r.left(), y1, r.right(), y2);
+  }
+
+  result = QString::fromUtf8(s->getCString());
+
+  delete output_dev;
+  delete s;
+  return result;
+}
+
+bool Page::search(const QString &text, QRectF &rect, SearchDirection direction, SearchMode caseSensitive, Rotation rotate) const
+{
+  const QChar * str = text.unicode();
+  int len = text.length();
+  QVector<Unicode> u(len);
+  for (int i = 0; i < len; ++i) u[i] = str[i].unicode();
+
+  GBool sCase;
+  if (caseSensitive == CaseSensitive) sCase = gTrue;
+  else sCase = gFalse;
+
+  bool found = false;
+  double sLeft, sTop, sRight, sBottom;
+  sLeft = rect.left();
+  sTop = rect.top();
+  sRight = rect.right();
+  sBottom = rect.bottom();
+
+  int rotation = (int)rotate * 90;
+
+  // fetch ourselves a textpage
+  TextOutputDev td(NULL, gTrue, gFalse, gFalse);
+  m_page->parentDoc->doc->displayPage( &td, m_page->index + 1, 72, 72, rotation, false, true, false );
+  TextPage *textPage=td.takeText();
+
+  if (direction == FromTop)
+    found = textPage->findText( u.data(), len, 
+            gTrue, gTrue, gFalse, gFalse, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom );
+  else if ( direction == NextResult )
+    found = textPage->findText( u.data(), len, 
+            gFalse, gTrue, gTrue, gFalse, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom );
+  else if ( direction == PreviousResult )
+    found = textPage->findText( u.data(), len, 
+            gTrue, gFalse, gFalse, gTrue, sCase, gFalse, &sLeft, &sTop, &sRight, &sBottom );
+
+  delete textPage;
+
+  rect.setLeft( sLeft );
+  rect.setTop( sTop );
+  rect.setRight( sRight );
+  rect.setBottom( sBottom );
+
+  return found;
+}
+
+QList<TextBox*> Page::textList(Rotation rotate) const
+{
+  TextOutputDev *output_dev;
+  
+  QList<TextBox*> output_list;
+  
+  output_dev = new TextOutputDev(0, gFalse, gFalse, gFalse);
+  
+  int rotation = (int)rotate * 90;
+
+  m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72,
+      rotation, false, false, false, -1, -1, -1, -1);
+
+  TextWordList *word_list = output_dev->makeWordList();
+  
+  if (!word_list) {
+    delete output_dev;
+    return output_list;
+  }
+  
+  QMap<TextWord *, TextBox*> wordBoxMap;
+  
+  for (int i = 0; i < word_list->getLength(); i++) {
+    TextWord *word = word_list->get(i);
+    GooString *gooWord = word->getText();
+    QString string = QString::fromUtf8(gooWord->getCString());
+    delete gooWord;
+    double xMin, yMin, xMax, yMax;
+    word->getBBox(&xMin, &yMin, &xMax, &yMax);
+    
+    TextBox* text_box = new TextBox(string, QRectF(xMin, yMin, xMax-xMin, yMax-yMin));
+    text_box->m_data->hasSpaceAfter = word->hasSpaceAfter() == gTrue;
+    text_box->m_data->charBBoxes.reserve(word->getLength());
+    for (int j = 0; j < word->getLength(); ++j)
+    {
+        word->getCharBBox(j, &xMin, &yMin, &xMax, &yMax);
+        text_box->m_data->charBBoxes.append(QRectF(xMin, yMin, xMax-xMin, yMax-yMin));
+    }
+    
+    wordBoxMap.insert(word, text_box);
+    
+    output_list.append(text_box);
+  }
+  
+  for (int i = 0; i < word_list->getLength(); i++) {
+    TextWord *word = word_list->get(i);
+    TextBox* text_box = wordBoxMap.value(word);
+    text_box->m_data->nextWord = wordBoxMap.value(word->nextWord());
+  }
+  
+  delete word_list;
+  delete output_dev;
+  
+  return output_list;
+}
+
+PageTransition *Page::transition() const
+{
+  if (!m_page->transition) {
+    Object o;
+    PageTransitionParams params;
+    params.dictObj = m_page->page->getTrans(&o);
+    if (params.dictObj->isDict()) m_page->transition = new PageTransition(params);
+    o.free();
+  }
+  return m_page->transition;
+}
+
+Link *Page::action( PageAction act ) const
+{
+  if ( act == Page::Opening || act == Page::Closing )
+  {
+    Object o;
+    m_page->page->getActions(&o);
+    if (!o.isDict())
+    {
+      o.free();
+      return 0;
+    }
+    Dict *dict = o.getDict();
+    Object o2;
+    const char *key = act == Page::Opening ? "O" : "C";
+    dict->lookup((char*)key, &o2);
+    ::LinkAction *act = ::LinkAction::parseAction(&o2, m_page->parentDoc->doc->getCatalog()->getBaseURI() );
+    o2.free();
+    o.free();
+    Link *popplerLink = NULL;
+    if (act != NULL)
+    {
+      popplerLink = m_page->convertLinkActionToLink(act, QRectF());
+      delete act;
+    }
+    return popplerLink;
+  }
+  return 0;
+}
+
+QSizeF Page::pageSizeF() const
+{
+  Page::Orientation orient = orientation();
+  if ( ( Page::Landscape == orient ) || (Page::Seascape == orient ) ) {
+      return QSizeF( m_page->page->getCropHeight(), m_page->page->getCropWidth() );
+  } else {
+    return QSizeF( m_page->page->getCropWidth(), m_page->page->getCropHeight() );
+  }
+}
+
+QSize Page::pageSize() const
+{
+  return pageSizeF().toSize();
+}
+
+Page::Orientation Page::orientation() const
+{
+  const int rotation = m_page->page->getRotate();
+  switch (rotation) {
+  case 90:
+    return Page::Landscape;
+    break;
+  case 180:
+    return Page::UpsideDown;
+    break;
+  case 270:
+    return Page::Seascape;
+    break;
+  default:
+    return Page::Portrait;
+  }
+}
+
+void Page::defaultCTM(double *CTM, double dpiX, double dpiY, int rotate, bool upsideDown)
+{
+  m_page->page->getDefaultCTM(CTM, dpiX, dpiY, rotate, gFalse, upsideDown);
+}
+
+QList<Link*> Page::links() const
+{
+  LinkExtractorOutputDev link_dev(m_page);
+  m_page->parentDoc->doc->processLinks(&link_dev, m_page->index + 1);
+  QList<Link*> popplerLinks = link_dev.links();
+
+  return popplerLinks;
+}
+
+QList<Annotation*> Page::annotations() const
+{
+    Object annotArray;
+    ::Page *pdfPage = m_page->page;
+    pdfPage->getAnnots( &annotArray );
+    if ( !annotArray.isArray() || annotArray.arrayGetLength() < 1 )
+    {
+        annotArray.free();
+        return QList<Annotation*>();
+    }
+
+    // ID to Annotation/PopupWindow maps
+    QMap< int, Annotation * > annotationsMap;
+    QMap< int, PopupWindow * > popupsMap;
+    // lists of Windows and Revisions that needs resolution
+    QLinkedList< ResolveRevision > resolveRevList;
+    QLinkedList< ResolveWindow > resolvePopList;
+    QLinkedList< PostProcessText > ppTextList;
+
+    // build a normalized transform matrix for this page at 100% scale
+    GfxState * gfxState = new GfxState( 72.0, 72.0, pdfPage->getMediaBox(), pdfPage->getRotate(), gTrue );
+    double * gfxCTM = gfxState->getCTM();
+    double MTX[6];
+    for ( int i = 0; i < 6; i+=2 )
+    {
+        MTX[i] = gfxCTM[i] / pdfPage->getCropWidth();
+        MTX[i+1] = gfxCTM[i+1] / pdfPage->getCropHeight();
+    }
+    delete gfxState;
+
+    /** 1 - PARSE ALL ANNOTATIONS AND POPUPS FROM THE PAGE */
+    Object annot;
+    Object annotRef;    // no need to free this (impl. dependent!)
+    uint numAnnotations = annotArray.arrayGetLength();
+    for ( uint j = 0; j < numAnnotations; j++ )
+    {
+        // get the j-th annotation
+        annotArray.arrayGet( j, &annot );
+        if ( !annot.isDict() )
+        {
+            qDebug() << "PDFGenerator: annot not dictionary.";
+            annot.free();
+            continue;
+        }
+
+        Annotation * annotation = 0;
+        Dict * annotDict = annot.getDict();
+        int annotID = annotArray.arrayGetNF( j, &annotRef )->getRefNum();
+        bool parseMarkup = true,    // nearly all supported annots are markup
+             addToPage = true;      // Popup annots are added to custom queue
+
+        /** 1.1. GET Subtype */
+        QString subType;
+        XPDFReader::lookupName( annotDict, "Subtype", subType );
+        if ( subType.isEmpty() )
+        {
+            qDebug() << "Annot has no Subtype";
+            annot.free();
+            continue;
+        }
+
+        /** 1.2. CREATE Annotation / PopupWindow and PARSE specific params */
+        if ( subType == "Text" || subType == "FreeText" )
+        {
+            // parse TextAnnotation params
+            TextAnnotation * t = new TextAnnotation();
+            annotation = t;
+
+            if ( subType == "Text" )
+            {
+                // -> textType
+                t->setTextType( TextAnnotation::Linked );
+                // -> textIcon
+                QString tmpstring;
+                XPDFReader::lookupName( annotDict, "Name", tmpstring );
+                if ( !tmpstring.isEmpty() )
+                {
+                    tmpstring = tmpstring.toLower();
+                    tmpstring.remove( ' ' );
+                    t->setTextIcon( tmpstring );
+                }
+                // request for postprocessing window geometry
+                PostProcessText request;
+                request.textAnnotation = t;
+                request.opened = false;
+                XPDFReader::lookupBool( annotDict, "Open", request.opened );
+                ppTextList.append( request );
+            }
+            else
+            {
+                // NOTE: please provide testcases for FreeText (don't have any) - Enrico
+                // -> textType
+                t->setTextType( TextAnnotation::InPlace );
+                // -> textFont
+                QString textFormat;
+                XPDFReader::lookupString( annotDict, "DA", textFormat );
+                // TODO, fill t->textFont using textFormat if not empty
+                // -> inplaceAlign
+                int tmpint = 0;
+                XPDFReader::lookupInt( annotDict, "Q", tmpint );
+                t->setInplaceAlign( tmpint );
+                // -> inplaceText (simple)
+                QString tmpstring;
+                XPDFReader::lookupString( annotDict, "DS", tmpstring );
+                // -> inplaceText (complex override)
+                XPDFReader::lookupString( annotDict, "RC", tmpstring );
+                t->setInplaceText( tmpstring );
+                // -> inplaceCallout
+                double c[6];
+                int n = XPDFReader::lookupNumArray( annotDict, "CL", c, 6 );
+                if ( n >= 4 )
+                {
+                    QPointF tmppoint;
+                    XPDFReader::transform( MTX, c[0], c[1], tmppoint );
+                    t->setCalloutPoint( 0, tmppoint );
+                    XPDFReader::transform( MTX, c[2], c[3], tmppoint );
+                    t->setCalloutPoint( 1, tmppoint );
+                    if ( n == 6 )
+                    {
+                        XPDFReader::transform( MTX, c[4], c[5], tmppoint );
+                        t->setCalloutPoint( 2, tmppoint );
+                    }
+                }
+                // -> inplaceIntent
+                QString intentName;
+                XPDFReader::lookupString( annotDict, "IT", intentName );
+                if ( intentName == "FreeTextCallout" )
+                    t->setInplaceIntent( TextAnnotation::Callout );
+                else if ( intentName == "FreeTextTypeWriter" )
+                    t->setInplaceIntent( TextAnnotation::TypeWriter );
+            }
+        }
+        else if ( subType == "Line" || subType == "Polygon" || subType == "PolyLine" )
+        {
+            // parse LineAnnotation params
+            LineAnnotation * l = new LineAnnotation();
+            annotation = l;
+
+            // -> linePoints
+            double c[100];
+            int num = XPDFReader::lookupNumArray( annotDict, (char*)((subType == "Line") ? "L" : "Vertices"), c, 100 );
+            if ( num < 4 || (num % 2) != 0 )
+            {
+                qDebug() << "L/Vertices wrong fol Line/Poly.";
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            QLinkedList<QPointF> linePoints;
+            for ( int i = 0; i < num; i += 2 )
+            {
+                QPointF p;
+                XPDFReader::transform( MTX, c[i], c[i+1], p );
+                linePoints.push_back( p );
+            }
+            l->setLinePoints( linePoints );
+            // -> lineStartStyle, lineEndStyle
+            Object leArray;
+            annotDict->lookup( "LE", &leArray );
+            if ( leArray.isArray() && leArray.arrayGetLength() == 2 )
+            {
+                // -> lineStartStyle
+                Object styleObj;
+                leArray.arrayGet( 0, &styleObj );
+                if ( styleObj.isName() )
+                {
+                    const char * name = styleObj.getName();
+                    if ( !strcmp( name, "Square" ) )
+                        l->setLineStartStyle( LineAnnotation::Square );
+                    else if ( !strcmp( name, "Circle" ) )
+                        l->setLineStartStyle( LineAnnotation::Circle );
+                    else if ( !strcmp( name, "Diamond" ) )
+                        l->setLineStartStyle( LineAnnotation::Diamond );
+                    else if ( !strcmp( name, "OpenArrow" ) )
+                        l->setLineStartStyle( LineAnnotation::OpenArrow );
+                    else if ( !strcmp( name, "ClosedArrow" ) )
+                        l->setLineStartStyle( LineAnnotation::ClosedArrow );
+                    else if ( !strcmp( name, "None" ) )
+                        l->setLineStartStyle( LineAnnotation::None );
+                    else if ( !strcmp( name, "Butt" ) )
+                        l->setLineStartStyle( LineAnnotation::Butt );
+                    else if ( !strcmp( name, "ROpenArrow" ) )
+                        l->setLineStartStyle( LineAnnotation::ROpenArrow );
+                    else if ( !strcmp( name, "RClosedArrow" ) )
+                        l->setLineStartStyle( LineAnnotation::RClosedArrow );
+                    else if ( !strcmp( name, "Slash" ) )
+                        l->setLineStartStyle( LineAnnotation::Slash );
+                }
+                styleObj.free();
+                // -> lineEndStyle
+                leArray.arrayGet( 1, &styleObj );
+                if ( styleObj.isName() )
+                {
+                    const char * name = styleObj.getName();
+                    if ( !strcmp( name, "Square" ) )
+                        l->setLineEndStyle( LineAnnotation::Square );
+                    else if ( !strcmp( name, "Circle" ) )
+                        l->setLineEndStyle( LineAnnotation::Circle );
+                    else if ( !strcmp( name, "Diamond" ) )
+                        l->setLineEndStyle( LineAnnotation::Diamond );
+                    else if ( !strcmp( name, "OpenArrow" ) )
+                        l->setLineEndStyle( LineAnnotation::OpenArrow );
+                    else if ( !strcmp( name, "ClosedArrow" ) )
+                        l->setLineEndStyle( LineAnnotation::ClosedArrow );
+                    else if ( !strcmp( name, "None" ) )
+                        l->setLineEndStyle( LineAnnotation::None );
+                    else if ( !strcmp( name, "Butt" ) )
+                        l->setLineEndStyle( LineAnnotation::Butt );
+                    else if ( !strcmp( name, "ROpenArrow" ) )
+                        l->setLineEndStyle( LineAnnotation::ROpenArrow );
+                    else if ( !strcmp( name, "RClosedArrow" ) )
+                        l->setLineEndStyle( LineAnnotation::RClosedArrow );
+                    else if ( !strcmp( name, "Slash" ) )
+                        l->setLineEndStyle( LineAnnotation::Slash );
+                }
+                styleObj.free();
+            }
+            leArray.free();
+            // -> lineClosed
+            l->setLineClosed( subType == "Polygon" );
+            // -> lineInnerColor
+            QColor tmpcolor = l->lineInnerColor();
+            XPDFReader::lookupColor( annotDict, "IC", tmpcolor );
+            l->setLineInnerColor( tmpcolor );
+            // -> lineLeadingFwdPt
+            double tmpdouble = l->lineLeadingForwardPoint();
+            XPDFReader::lookupNum( annotDict, "LL", tmpdouble );
+            l->setLineLeadingForwardPoint( tmpdouble );
+            // -> lineLeadingBackPt
+            tmpdouble = l->lineLeadingBackPoint();
+            XPDFReader::lookupNum( annotDict, "LLE", tmpdouble );
+            l->setLineLeadingBackPoint( tmpdouble );
+            // -> lineShowCaption
+            bool tmpbool = l->lineShowCaption();
+            XPDFReader::lookupBool( annotDict, "Cap", tmpbool );
+            l->setLineShowCaption( tmpbool );
+            // -> lineIntent
+            QString intentName;
+            XPDFReader::lookupString( annotDict, "IT", intentName );
+            if ( intentName == "LineArrow" )
+                l->setLineIntent( LineAnnotation::Arrow );
+            else if ( intentName == "LineDimension" )
+                l->setLineIntent( LineAnnotation::Dimension );
+            else if ( intentName == "PolygonCloud" )
+                l->setLineIntent( LineAnnotation::PolygonCloud );
+        }
+        else if ( subType == "Square" || subType == "Circle" )
+        {
+            // parse GeomAnnotation params
+            GeomAnnotation * g = new GeomAnnotation();
+            annotation = g;
+
+            // -> geomType
+            if ( subType == "Square" )
+                g->setGeomType( GeomAnnotation::InscribedSquare );
+            else
+                g->setGeomType( GeomAnnotation::InscribedCircle );
+            // -> geomInnerColor
+            QColor tmpcolor = g->geomInnerColor();
+            XPDFReader::lookupColor( annotDict, "IC", tmpcolor );
+            g->setGeomInnerColor( tmpcolor );
+            // TODO RD
+        }
+        else if ( subType == "Highlight" || subType == "Underline" ||
+                  subType == "Squiggly" || subType == "StrikeOut" )
+        {
+            // parse HighlightAnnotation params
+            HighlightAnnotation * h = new HighlightAnnotation();
+            annotation = h;
+
+            // -> highlightType
+            if ( subType == "Highlight" )
+                h->setHighlightType( HighlightAnnotation::Highlight );
+            else if ( subType == "Underline" )
+                h->setHighlightType( HighlightAnnotation::Underline );
+            else if ( subType == "Squiggly" )
+                h->setHighlightType( HighlightAnnotation::Squiggly );
+            else if ( subType == "StrikeOut" )
+                h->setHighlightType( HighlightAnnotation::StrikeOut );
+
+            // -> highlightQuads
+            double c[80];
+            int num = XPDFReader::lookupNumArray( annotDict, "QuadPoints", c, 80 );
+            if ( num < 8 || (num % 8) != 0 )
+            {
+                qDebug() << "Wrong QuadPoints for a Highlight annotation.";
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            QList< HighlightAnnotation::Quad > quads;
+            for ( int q = 0; q < num; q += 8 )
+            {
+                HighlightAnnotation::Quad quad;
+                for ( int p = 0; p < 4; p++ )
+                    XPDFReader::transform( MTX, c[ q + p*2 ], c[ q + p*2 + 1 ], quad.points[ p ] );
+                // ### PDF1.6 specs says that point are in ccw order, but in fact
+                // points 3 and 4 are swapped in every PDF around!
+                QPointF tmpPoint = quad.points[ 2 ];
+                quad.points[ 2 ] = quad.points[ 3 ];
+                quad.points[ 3 ] = tmpPoint;
+                // initialize other oroperties and append quad
+                quad.capStart = true;       // unlinked quads are always capped
+                quad.capEnd = true;         // unlinked quads are always capped
+                quad.feather = 0.1;         // default feather
+                quads.append( quad );
+            }
+            h->setHighlightQuads( quads );
+        }
+        else if ( subType == "Stamp" )
+        {
+            // parse StampAnnotation params
+            StampAnnotation * s = new StampAnnotation();
+            annotation = s;
+
+            // -> stampIconName
+            QString tmpstring = s->stampIconName();
+            XPDFReader::lookupName( annotDict, "Name", tmpstring );
+            s->setStampIconName( tmpstring );
+        }
+        else if ( subType == "Ink" )
+        {
+            // parse InkAnnotation params
+            InkAnnotation * k = new InkAnnotation();
+            annotation = k;
+
+            // -> inkPaths
+            Object pathsArray;
+            annotDict->lookup( "InkList", &pathsArray );
+            if ( !pathsArray.isArray() || pathsArray.arrayGetLength() < 1 )
+            {
+                qDebug() << "InkList not present for ink annot";
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            int pathsNumber = pathsArray.arrayGetLength();
+            QList< QLinkedList<QPointF> > inkPaths;
+            for ( int m = 0; m < pathsNumber; m++ )
+            {
+                // transform each path in a list of normalized points ..
+                QLinkedList<QPointF> localList;
+                Object pointsArray;
+                pathsArray.arrayGet( m, &pointsArray );
+                if ( pointsArray.isArray() )
+                {
+                    int pointsNumber = pointsArray.arrayGetLength();
+                    for ( int n = 0; n < pointsNumber; n+=2 )
+                    {
+                        // get the x,y numbers for current point
+                        Object numObj;
+                        double x = pointsArray.arrayGet( n, &numObj )->getNum();
+                        numObj.free();
+                        double y = pointsArray.arrayGet( n+1, &numObj )->getNum();
+                        numObj.free();
+                        // add normalized point to localList
+                        QPointF np;
+                        XPDFReader::transform( MTX, x, y, np );
+                        localList.push_back( np );
+                    }
+                }
+                pointsArray.free();
+                // ..and add it to the annotation
+                inkPaths.push_back( localList );
+            }
+            k->setInkPaths( inkPaths );
+            pathsArray.free();
+        }
+        else if ( subType == "Popup" )
+        {
+            // create PopupWindow and add it to the popupsMap
+            PopupWindow * popup = new PopupWindow();
+            popupsMap[ annotID ] = popup;
+            parseMarkup = false;
+            addToPage = false;
+
+            // get window specific properties if any
+            popup->shown = false;
+            XPDFReader::lookupBool( annotDict, "Open", popup->shown );
+            // no need to parse parent annotation id
+            //XPDFReader::lookupIntRef( annotDict, "Parent", popup->... );
+
+            // use the 'dummy annotation' for getting other parameters
+            popup->dummyAnnotation = new DummyAnnotation();
+            annotation = popup->dummyAnnotation;
+        }
+        else if ( subType == "Link" )
+        {
+            // parse Link params
+            LinkAnnotation * l = new LinkAnnotation();
+            annotation = l;
+
+            // -> hlMode
+            QString hlModeString;
+            XPDFReader::lookupName( annotDict, "H", hlModeString );
+            if ( hlModeString == "N" )
+                l->setLinkHighlightMode( LinkAnnotation::None );
+            else if ( hlModeString == "I" )
+                l->setLinkHighlightMode( LinkAnnotation::Invert );
+            else if ( hlModeString == "O" )
+                l->setLinkHighlightMode( LinkAnnotation::Outline );
+            else if ( hlModeString == "P" )
+                l->setLinkHighlightMode( LinkAnnotation::Push );
+
+            // -> link region
+            double c[8];
+            int num = XPDFReader::lookupNumArray( annotDict, "QuadPoints", c, 8 );
+            if ( num > 0 && num != 8 )
+            {
+                qDebug() << "Wrong QuadPoints for a Link annotation.";
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            if ( num == 8 )
+            {
+                QPointF tmppoint;
+                XPDFReader::transform( MTX, c[ 0 ], c[ 1 ], tmppoint );
+                l->setLinkRegionPoint( 0, tmppoint );
+                XPDFReader::transform( MTX, c[ 2 ], c[ 3 ], tmppoint );
+                l->setLinkRegionPoint( 1, tmppoint );
+                XPDFReader::transform( MTX, c[ 4 ], c[ 5 ], tmppoint );
+                l->setLinkRegionPoint( 2, tmppoint );
+                XPDFReader::transform( MTX, c[ 6 ], c[ 7 ], tmppoint );
+                l->setLinkRegionPoint( 3, tmppoint );
+            }
+
+            // reading link action
+            Object objPA;
+            annotDict->lookup( "PA", &objPA );
+            if (!objPA.isNull())
+            {
+                ::LinkAction * a = ::LinkAction::parseAction( &objPA, m_page->parentDoc->doc->getCatalog()->getBaseURI() );
+                Link * popplerLink = m_page->convertLinkActionToLink( a, QRectF() );
+                if ( popplerLink )
+                {
+                     l->setLinkDestination( popplerLink );
+                }
+                objPA.free();
+            }
+        }
+        else
+        {
+            // MISSING: Caret, FileAttachment, Sound, Movie, Widget,
+            //          Screen, PrinterMark, TrapNet, Watermark, 3D
+            qDebug() << "Annotation" << subType << "not supported";
+            annot.free();
+            continue;
+        }
+
+        /** 1.3. PARSE common parameters */
+        // -> boundary
+        double r[4];
+        if ( XPDFReader::lookupNumArray( annotDict, "Rect", r, 4 ) != 4 )
+        {
+            qDebug() << "Rect is missing for annotation.";
+            annot.free();
+            continue;
+        }
+        // transform annotation rect to uniform coords
+        QPointF topLeft, bottomRight;
+        XPDFReader::transform( MTX, r[0], r[1], topLeft );
+        XPDFReader::transform( MTX, r[2], r[3], bottomRight );
+        QRectF boundaryRect;
+        boundaryRect.setTopLeft(topLeft);
+        boundaryRect.setBottomRight(bottomRight);
+        if ( boundaryRect.left() > boundaryRect.right() )
+        {
+            double aux = boundaryRect.left();
+            boundaryRect.setLeft( boundaryRect.right() );
+            boundaryRect.setRight(aux);
+        }
+        if ( boundaryRect.top() > boundaryRect.bottom() )
+        {
+            double aux = boundaryRect.top();
+            boundaryRect.setTop( boundaryRect.bottom() );
+            boundaryRect.setBottom(aux);
+           //annotation->rUnscaledWidth = (r[2] > r[0]) ? r[2] - r[0] : r[0] - r[2];
+           //annotation->rUnscaledHeight = (r[3] > r[1]) ? r[3] - r[1] : r[1] - r[3];
+        }
+        annotation->setBoundary( boundaryRect );
+        // -> contents
+        QString tmpstring = annotation->contents();
+        XPDFReader::lookupString( annotDict, "Contents", tmpstring );
+        annotation->setContents( tmpstring );
+        // -> uniqueName
+        tmpstring = annotation->uniqueName();
+        XPDFReader::lookupString( annotDict, "NM", tmpstring );
+        annotation->setUniqueName( tmpstring );
+        // -> modifyDate (and -> creationDate)
+        QDateTime tmpdatetime = annotation->modificationDate();
+        XPDFReader::lookupDate( annotDict, "M", tmpdatetime );
+        annotation->setModificationDate( tmpdatetime );
+        if ( annotation->creationDate().isNull() && !annotation->modificationDate().isNull() )
+            annotation->setCreationDate( annotation->modificationDate() );
+        // -> flags: set the external attribute since it's embedded on file
+        int annoflags = 0;
+        annoflags |= Annotation::External;
+        // -> flags
+        int flags = 0;
+        XPDFReader::lookupInt( annotDict, "F", flags );
+        if ( flags & 0x2 )
+            annoflags |= Annotation::Hidden;
+        if ( flags & 0x8 )
+            annoflags |= Annotation::FixedSize;
+        if ( flags & 0x10 )
+            annoflags |= Annotation::FixedRotation;
+        if ( !(flags & 0x4) )
+            annoflags |= Annotation::DenyPrint;
+        if ( flags & 0x40 )
+            annoflags |= (Annotation::DenyWrite | Annotation::DenyDelete);
+        if ( flags & 0x80 )
+            annoflags |= Annotation::DenyDelete;
+        if ( flags & 0x100 )
+            annoflags |= Annotation::ToggleHidingOnMouse;
+        annotation->setFlags( annoflags );
+        // -> style (Border(old spec), BS, BE)
+        double border[3];
+        int bn = XPDFReader::lookupNumArray( annotDict, "Border", border, 3 );
+        if ( bn == 3 )
+        {
+            // -> style.xCorners
+            annotation->style.xCorners = border[0];
+            // -> style.yCorners
+            annotation->style.yCorners = border[1];
+            // -> style.width
+            annotation->style.width = border[2];
+        }
+        Object bsObj;
+        annotDict->lookup( "BS", &bsObj );
+        if ( bsObj.isDict() )
+        {
+            // -> style.width
+            XPDFReader::lookupNum( bsObj.getDict(), "W", annotation->style.width );
+            // -> style.style
+            QString styleName;
+            XPDFReader::lookupName( bsObj.getDict(), "S", styleName );
+            if ( styleName == "S" )
+                annotation->style.style = Annotation::Solid;
+            else if ( styleName == "D" )
+                annotation->style.style = Annotation::Dashed;
+            else if ( styleName == "B" )
+                annotation->style.style = Annotation::Beveled;
+            else if ( styleName == "I" )
+                annotation->style.style = Annotation::Inset;
+            else if ( styleName == "U" )
+                annotation->style.style = Annotation::Underline;
+            // -> style.marks and style.spaces
+            Object dashArray;
+            bsObj.getDict()->lookup( "D", &dashArray );
+            if ( dashArray.isArray() )
+            {
+                int dashMarks = 3;
+                int dashSpaces = 0;
+                Object intObj;
+                dashArray.arrayGet( 0, &intObj );
+                if ( intObj.isInt() )
+                    dashMarks = intObj.getInt();
+                intObj.free();
+                dashArray.arrayGet( 1, &intObj );
+                if ( intObj.isInt() )
+                    dashSpaces = intObj.getInt();
+                intObj.free();
+                annotation->style.marks = dashMarks;
+                annotation->style.spaces = dashSpaces;
+            }
+            dashArray.free();
+        }
+        bsObj.free();
+        Object beObj;
+        annotDict->lookup( "BE", &beObj );
+        if ( beObj.isDict() )
+        {
+            // -> style.effect
+            QString effectName;
+            XPDFReader::lookupName( beObj.getDict(), "S", effectName );
+            if ( effectName == "C" )
+                annotation->style.effect = Annotation::Cloudy;
+            // -> style.effectIntensity
+            int intensityInt = -1;
+            XPDFReader::lookupInt( beObj.getDict(), "I", intensityInt );
+            if ( intensityInt != -1 )
+                annotation->style.effectIntensity = (double)intensityInt;
+        }
+        beObj.free();
+        // -> style.color
+        XPDFReader::lookupColor( annotDict, "C", annotation->style.color );
+
+        /** 1.4. PARSE markup { common, Popup, Revision } parameters */
+        if ( parseMarkup )
+        {
+            // -> creationDate
+            tmpdatetime = annotation->creationDate();
+            XPDFReader::lookupDate( annotDict, "CreationDate", tmpdatetime );
+            annotation->setCreationDate( tmpdatetime );
+            // -> style.opacity
+            XPDFReader::lookupNum( annotDict, "CA", annotation->style.opacity );
+            // -> window.title and author
+            XPDFReader::lookupString( annotDict, "T", annotation->window.title );
+            annotation->setAuthor( annotation->window.title );
+            // -> window.summary
+            XPDFReader::lookupString( annotDict, "Subj", annotation->window.summary );
+            // -> window.text
+            XPDFReader::lookupString( annotDict, "RC", annotation->window.text );
+
+            // if a popup is referenced, schedule for resolving it later
+            int popupID = 0;
+            XPDFReader::lookupIntRef( annotDict, "Popup", popupID );
+            if ( popupID )
+            {
+                ResolveWindow request;
+                request.popupWindowID = popupID;
+                request.annotation = annotation;
+                resolvePopList.append( request );
+            }
+
+            // if an older version is referenced, schedule for reparenting
+            int parentID = 0;
+            XPDFReader::lookupIntRef( annotDict, "IRT", parentID );
+            if ( parentID )
+            {
+                ResolveRevision request;
+                request.nextAnnotation = annotation;
+                request.nextAnnotationID = annotID;
+                request.prevAnnotationID = parentID;
+
+                // -> request.nextScope
+                request.nextScope = Annotation::Reply;
+                Object revObj;
+                annotDict->lookup( "RT", &revObj );
+                if ( revObj.isName() )
+                {
+                    const char * name = revObj.getName();
+                    if ( !strcmp( name, "R" ) )
+                        request.nextScope = Annotation::Reply;
+                    else if ( !strcmp( name, "Group" ) )
+                        request.nextScope = Annotation::Group;
+                }
+                revObj.free();
+
+                // -> revision.type (StateModel is deduced from type, not parsed)
+                request.nextType = Annotation::None;
+                annotDict->lookup( "State", &revObj );
+                if ( revObj.isString() )
+                {
+                    const char * name = revObj.getString()->getCString();
+                    if ( !strcmp( name, "Marked" ) )
+                        request.nextType = Annotation::Marked;
+                    else if ( !strcmp( name, "Unmarked" ) )
+                        request.nextType = Annotation::Unmarked;
+                    else if ( !strcmp( name, "Accepted" ) )
+                        request.nextType = Annotation::Accepted;
+                    else if ( !strcmp( name, "Rejected" ) )
+                        request.nextType = Annotation::Rejected;
+                    else if ( !strcmp( name, "Cancelled" ) )
+                        request.nextType = Annotation::Cancelled;
+                    else if ( !strcmp( name, "Completed" ) )
+                        request.nextType = Annotation::Completed;
+                    else if ( !strcmp( name, "None" ) )
+                        request.nextType = Annotation::None;
+                }
+                revObj.free();
+
+                // schedule for later reparenting
+                resolveRevList.append( request );
+            }
+        }
+        // free annot object
+        annot.free();
+
+        /** 1.5. ADD ANNOTATION to the annotationsMap  */
+        if ( addToPage )
+        {
+            if ( annotationsMap.contains( annotID ) )
+                qDebug() << "Clash for annotations with ID:" << annotID;
+            annotationsMap[ annotID ] = annotation;
+        }
+    } // end Annotation/PopupWindow parsing loop
+
+    /** 2 - RESOLVE POPUPS (popup.* -> annotation.window) */
+    if ( !resolvePopList.isEmpty() && !popupsMap.isEmpty() )
+    {
+        QLinkedList< ResolveWindow >::iterator it = resolvePopList.begin(),
+                                              end = resolvePopList.end();
+        for ( ; it != end; ++it )
+        {
+            const ResolveWindow & request = *it;
+            if ( !popupsMap.contains( request.popupWindowID ) )
+                // warn aboud problems in popup resolving logic
+                qDebug() << "Cannot resolve popup"
+                          << request.popupWindowID << ".";
+            else
+            {
+                // set annotation's window properties taking ones from popup
+                PopupWindow * pop = popupsMap[ request.popupWindowID ];
+                Annotation * pa = pop->dummyAnnotation;
+                Annotation::Window & w = request.annotation->window;
+
+                // transfer properties to Annotation's window
+                w.flags = pa->flags() & (Annotation::Hidden |
+                    Annotation::FixedSize | Annotation::FixedRotation);
+                if ( !pop->shown )
+                    w.flags |= Annotation::Hidden;
+                w.topLeft.setX(pa->boundary().left());
+                w.topLeft.setY(pa->boundary().top());
+                w.width = (int)( pa->boundary().right() - pa->boundary().left() );
+                w.height = (int)( pa->boundary().bottom() - pa->boundary().top() );
+            }
+        }
+
+        // clear data
+        QMap< int, PopupWindow * >::Iterator dIt = popupsMap.begin(), dEnd = popupsMap.end();
+        for ( ; dIt != dEnd; ++dIt )
+        {
+            PopupWindow * p = dIt.value();
+            delete p->dummyAnnotation;
+            delete p;
+        }
+    }
+
+    /** 3 - RESOLVE REVISIONS (parent.revisions.append( children )) */
+    if ( !resolveRevList.isEmpty() )
+    {
+        // append children to parents
+        QVarLengthArray< int > excludeIDs( resolveRevList.count() );   // can't even reach this size
+        int excludeIndex = 0;                       // index in excludeIDs array
+        QLinkedList< ResolveRevision >::iterator it = resolveRevList.begin(), end = resolveRevList.end();
+        for ( ; it != end; ++it )
+        {
+            const ResolveRevision & request = *it;
+            int parentID = request.prevAnnotationID;
+            if ( !annotationsMap.contains( parentID ) )
+                // warn about problems in reparenting logic
+                qDebug() << "Cannot reparent annotation to"
+                          << parentID << ".";
+            else
+            {
+                // compile and add a Revision structure to the parent annotation
+                Annotation::Revision childRevision;
+                childRevision.annotation = request.nextAnnotation;
+                childRevision.scope = request.nextScope;
+                childRevision.type = request.nextType;
+                annotationsMap[ parentID ]->revisions().append( childRevision );
+                // exclude child annotation from being rooted in page
+                excludeIDs[ excludeIndex++ ] = request.nextAnnotationID;
+            }
+        }
+
+        // prevent children from being attached to page as roots
+        for ( int i = 0; i < excludeIndex; i++ )
+            annotationsMap.remove( excludeIDs[ i ] );
+    }
+
+    /** 4 - POSTPROCESS TextAnnotations (when window geom is embedded) */
+    if ( !ppTextList.isEmpty() )
+    {
+        QLinkedList< PostProcessText >::const_iterator it = ppTextList.begin(), end = ppTextList.end();
+        for ( ; it != end; ++it )
+        {
+            const PostProcessText & request = *it;
+            Annotation::Window & window = request.textAnnotation->window;
+            // if not present, 'create' the window in-place over the annotation
+            if ( window.flags == -1 )
+            {
+                window.flags = 0;
+                QRectF geom = request.textAnnotation->boundary();
+                // initialize window geometry to annotation's one
+                window.width = (int)( geom.right() - geom.left() );
+                window.height = (int)( geom.bottom() - geom.top() );
+                window.topLeft.setX( geom.left() > 0.0 ? geom.left() : 0.0 );
+                window.topLeft.setY( geom.top() > 0.0 ? geom.top() : 0.0 );
+            }
+            // (pdf) if text is not 'opened', force window hiding. if the window
+            // was parsed from popup, the flag should already be set
+            if ( !request.opened && window.flags != -1 )
+                window.flags |= Annotation::Hidden;
+        }
+    }
+
+    annotArray.free();
+    /** 5 - finally RETURN ANNOTATIONS */
+    return annotationsMap.values();
+}
+
+QList<FormField*> Page::formFields() const
+{
+  QList<FormField*> fields;
+  ::Page *p = m_page->page;
+  ::FormPageWidgets * form = p->getPageWidgets();
+  int formcount = form->getNumWidgets();
+  for (int i = 0; i < formcount; ++i)
+  {
+    ::FormWidget *fm = form->getWidget(i);
+    FormField * ff = NULL;
+    switch (fm->getType())
+    {
+      case formButton:
+      {
+        ff = new FormFieldButton(m_page->parentDoc, p, static_cast<FormWidgetButton*>(fm));
+      }
+      break;
+
+      case formText:
+      {
+        ff = new FormFieldText(m_page->parentDoc, p, static_cast<FormWidgetText*>(fm));
+      }
+      break;
+
+      case formChoice:
+      {
+        ff = new FormFieldChoice(m_page->parentDoc, p, static_cast<FormWidgetChoice*>(fm));
+      }
+      break;
+
+      default: ;
+    }
+
+    if (ff)
+      fields.append(ff);
+  }
+
+  return fields;
+}
+
+double Page::duration() const
+{
+  return m_page->page->getDuration();
+}
+
+QString Page::label() const
+{
+  GooString goo;
+  if (!m_page->parentDoc->doc->getCatalog()->indexToLabel(m_page->index, &goo))
+    return QString();
+
+  return QString(goo.getCString());
+}
+
+
+}
commit a6f2c10ee01ee62ae945b50f6b6eae380377fe03
Author: Pino Toscano <pino at kde.org>
Date:   Wed Mar 26 20:59:21 2008 +0100

    [Qt4] Read the document-level JavaScript scripts.

diff --git a/qt4/src/poppler-document.cc b/qt4/src/poppler-document.cc
index e18ea8b..ceffeca 100644
--- a/qt4/src/poppler-document.cc
+++ b/qt4/src/poppler-document.cc
@@ -478,6 +478,21 @@ namespace Poppler {
         return (OptContentModel *)m_doc->m_optContentModel;
     }
 
+    QStringList Document::scripts() const
+    {
+        Catalog *catalog = m_doc->doc->getCatalog();
+        const int numScripts = catalog->numJS();
+        QStringList scripts;
+        for (int i = 0; i < numScripts; ++i) {
+            GooString *s = catalog->getJS(i);
+            if (s) {
+                scripts.append(UnicodeParsedString(s));
+                delete s;
+            }
+        }
+        return scripts;
+    }
+
     QDateTime convertDate( char *dateString )
     {
         int year;
diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h
index 5deedd8..ac106ae 100644
--- a/qt4/src/poppler-qt4.h
+++ b/qt4/src/poppler-qt4.h
@@ -906,6 +906,14 @@ QString subject = m_doc->info("Subject");
 	OptContentModel *optionalContentModel();
 
 	/**
+	   Document JavaScript scripts.
+
+	   Returns the list of document level JavaScript scripts to be always
+	   executed before any other script.
+	*/
+	QStringList scripts() const;
+
+	/**
 	   Destructor.
 	*/
 	~Document();
commit b8a471e55b998836c09c65ff736afdef8ac55189
Author: Pino Toscano <pino at kde.org>
Date:   Wed Mar 26 20:56:01 2008 +0100

    Add support for JavaScript actions, and read them when found.

diff --git a/poppler/Link.cc b/poppler/Link.cc
index 0eb6822..fa19a7c 100644
--- a/poppler/Link.cc
+++ b/poppler/Link.cc
@@ -93,6 +93,12 @@ LinkAction *LinkAction::parseAction(Object *obj, GooString *baseURI) {
   } else if (obj2.isName("Sound")) {
     action = new LinkSound(obj);
 
+  // JavaScript action
+  } else if (obj2.isName("JavaScript")) {
+    obj->dictLookup("JS", &obj3);
+    action = new LinkJavaScript(&obj3);
+    obj3.free();
+
   // unknown action
   } else if (obj2.isName()) {
     action = new LinkUnknown(obj2.getName());
@@ -778,6 +784,33 @@ LinkRendition::~LinkRendition() {
 
 
 //------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+LinkJavaScript::LinkJavaScript(Object *jsObj) {
+  js = NULL;
+
+  if (jsObj->isString()) {
+    js = new GooString(jsObj->getString());
+  }
+  else if (jsObj->isStream()) {
+    Stream *stream = jsObj->getStream();
+    js = new GooString();
+    stream->reset();
+    int i;
+    while ((i = stream->getChar()) != EOF) {
+      js->append((char)i);
+    }
+  }
+}
+
+LinkJavaScript::~LinkJavaScript() {
+  if (js) {
+    delete js;
+  }
+}
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
diff --git a/poppler/Link.h b/poppler/Link.h
index 64e8e8e..98e1c91 100644
--- a/poppler/Link.h
+++ b/poppler/Link.h
@@ -34,6 +34,7 @@ enum LinkActionKind {
   actionMovie,			// movie action
   actionRendition,
   actionSound,			// sound action
+  actionJavaScript,		// JavaScript action
   actionUnknown			// anything else
 };
 
@@ -359,6 +360,28 @@ private:
 };
 
 //------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+class LinkJavaScript: public LinkAction {
+public:
+
+  // Build a LinkJavaScript given the action name.
+  LinkJavaScript(Object *jsObj);
+
+  virtual ~LinkJavaScript();
+
+  virtual GBool isOk() { return js != NULL; }
+
+  virtual LinkActionKind getKind() { return actionJavaScript; }
+  GooString *getScript() { return js; }
+
+private:
+
+  GooString *js;
+};
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
commit 2fd85dc1b8b2ababadfc60e285c08a844737e4bb
Author: Pino Toscano <pino at kde.org>
Date:   Wed Mar 26 20:53:42 2008 +0100

    Read the JavaScript codes in the NameTree of the Catalog.

diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index ea786c5..7858247 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -114,6 +114,9 @@ Catalog::Catalog(XRef *xrefA) {
     obj.dictLookup("EmbeddedFiles", &obj2);
     embeddedFileNameTree.init(xref, &obj2);
     obj2.free();
+    obj.dictLookup("JavaScript", &obj2);
+    jsNameTree.init(xref, &obj2);
+    obj2.free();
   }
   obj.free();
 
@@ -211,6 +214,7 @@ Catalog::~Catalog() {
   dests.free();
   destNameTree.free();
   embeddedFileNameTree.free();
+  jsNameTree.free();
   if (baseURI) {
     delete baseURI;
   }
@@ -484,6 +488,41 @@ EmbFile *Catalog::embeddedFile(int i)
     return embeddedFile;
 }
 
+GooString *Catalog::getJS(int i)
+{
+  Object obj = jsNameTree.getValue(i);
+  if (obj.isRef()) {
+    Ref r = obj.getRef();
+    obj.free();
+    xref->fetch(r.num, r.gen, &obj);
+  }
+
+  if (!obj.isDict()) {
+    obj.free();
+    return 0;
+  }
+  Object obj2;
+  if (!obj.dictLookup("S", &obj2)->isName()) {
+    obj2.free();
+    obj.free();
+    return 0;
+  }
+  if (strcmp(obj2.getName(), "JavaScript")) {
+    obj2.free();
+    obj.free();
+    return 0;
+  }
+  if (!obj.dictLookup("JS", &obj2)->isString()) {
+    obj2.free();
+    obj.free();
+    return 0;
+  }
+  GooString *js = new GooString(obj2.getString());
+  obj2.free();
+  obj.free();
+  return js;
+}
+
 NameTree::NameTree()
 {
   size = 0;
diff --git a/poppler/Catalog.h b/poppler/Catalog.h
index 97fa08d..b06b13d 100644
--- a/poppler/Catalog.h
+++ b/poppler/Catalog.h
@@ -160,6 +160,12 @@ public:
   // Get the i'th file embedded (at the Document level) in the document
   EmbFile *embeddedFile(int i);
 
+  // Get the number of javascript scripts
+  int numJS() { return jsNameTree.numEntries(); }
+
+  // Get the i'th JavaScript script (at the Document level) in the document
+  GooString *getJS(int i);
+
   // Convert between page indices and page labels.
   GBool labelToIndex(GooString *label, int *index);
   GBool indexToLabel(int index, GooString *label);
@@ -205,6 +211,7 @@ private:
   Object dests;			// named destination dictionary
   NameTree destNameTree;	// named destination name-tree
   NameTree embeddedFileNameTree;  // embedded file name-tree
+  NameTree jsNameTree;		// Java Script name-tree
   GooString *baseURI;		// base URI for URI-type links
   Object metadata;		// metadata stream
   Object structTreeRoot;	// structure tree root dictionary


More information about the poppler mailing list