[poppler] poppler/qt4/src: Makefile.am, 1.10, 1.11 poppler-annotation-helper.h, NONE, 1.1 poppler-annotation.cc, NONE, 1.1 poppler-annotation.h, NONE, 1.1 poppler-link.cc, 1.2, 1.3 poppler-link.h, 1.1, 1.2 poppler-page.cc, 1.20, 1.21 poppler-qt4.h, 1.31, 1.32

Albert Astals Cid aacid at kemper.freedesktop.org
Fri May 12 13:40:07 PDT 2006


Update of /cvs/poppler/poppler/qt4/src
In directory kemper:/tmp/cvs-serv24273/qt4/src

Modified Files:
	Makefile.am poppler-link.cc poppler-link.h poppler-page.cc 
	poppler-qt4.h 
Added Files:
	poppler-annotation-helper.h poppler-annotation.cc 
	poppler-annotation.h 
Log Message:
        * qt4/src/Makefile.am
        * qt4/src/poppler-annotation-helper.h
        * qt4/src/poppler-annotation.cc
        * qt4/src/poppler-annotation.h
        * qt4/src/poppler-link.cc
        * qt4/src/poppler-link.h
        * qt4/src/poppler-page.cc
        * qt4/src/poppler-qt4.h: Code for annotations stripped from oKular,
        it's all based on Enrico's work, so ask him for details, the problem
        is that he left KDE development a while ago.



Index: Makefile.am
===================================================================
RCS file: /cvs/poppler/poppler/qt4/src/Makefile.am,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- Makefile.am	9 May 2006 20:07:06 -0000	1.10
+++ Makefile.am	12 May 2006 20:40:05 -0000	1.11
@@ -10,6 +10,7 @@
 poppler_include_HEADERS =			\
 	poppler-qt4.h				\
 	poppler-link.h				\
+	poppler-annotation.h			\
 	../../qt/poppler-page-transition.h
 
 lib_LTLIBRARIES = libpoppler-qt4.la
@@ -21,6 +22,7 @@
 	poppler-embeddedfile.cc			\
 	poppler-textbox.cc			\
 	poppler-link.cc				\
+	poppler-annotation.cc			\
 	../../qt/poppler-page-transition.cc	\
 	poppler-private.h
 

--- NEW FILE: poppler-annotation-helper.h ---
/* poppler-annotation-helper.h: qt interface to poppler
 * Copyright (C) 2006, Albert Astals Cid <aacid at kde.org>
 * Adapting code from
 *   Copyright (C) 2004 by Enrico Ros <eros.kde at email.it>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <QtCore/QDebug>

namespace Poppler {

class XPDFReader
{
    public:
        // find named symbol and parse it
        static void lookupName( Dict *, const char *, QString & dest );
        static void lookupString( Dict *, const char *, QString & dest );
        static void lookupBool( Dict *, const char *, bool & dest );
        static void lookupInt( Dict *, const char *, int & dest );
        static void lookupNum( Dict *, const char *, double & dest );
        static int lookupNumArray( Dict *, const char *, double * dest, int len );
        static void lookupColor( Dict *, const char *, QColor & color );
        static void lookupIntRef( Dict *, const char *, int & dest );
        static void lookupDate( Dict *, const char *, QDateTime & dest );
        // transform from user coords to normalized ones using the matrix M
        static inline void transform( double * M, double x, double y, QPointF &res );
};

void XPDFReader::lookupName( Dict * dict, const char * type, QString & dest )
{
    Object nameObj;
    dict->lookup( type, &nameObj );
    if ( nameObj.isNull() )
        return;
    if ( nameObj.isName() )
        dest = nameObj.getName();
    else
        qDebug() << type << " is not Name." << endl;
    nameObj.free();
}

void XPDFReader::lookupString( Dict * dict, const char * type, QString & dest )
{
    Object stringObj;
    dict->lookup( type, &stringObj );
    if ( stringObj.isNull() )
        return;
    if ( stringObj.isString() )
        dest = stringObj.getString()->getCString();
    else
        qDebug() << type << " is not String." << endl;
    stringObj.free();
}

void XPDFReader::lookupBool( Dict * dict, const char * type, bool & dest )
{
    Object boolObj;
    dict->lookup( type, &boolObj );
    if ( boolObj.isNull() )
        return;
    if ( boolObj.isBool() )
        dest = boolObj.getBool() == gTrue;
    else
        qDebug() << type << " is not Bool." << endl;
    boolObj.free();
}

void XPDFReader::lookupInt( Dict * dict, const char * type, int & dest )
{
    Object intObj;
    dict->lookup( type, &intObj );
    if ( intObj.isNull() )
        return;
    if ( intObj.isInt() )
        dest = intObj.getInt();
    else
        qDebug() << type << " is not Int." << endl;
    intObj.free();
}

void XPDFReader::lookupNum( Dict * dict, const char * type, double & dest )
{
    Object numObj;
    dict->lookup( type, &numObj );
    if ( numObj.isNull() )
        return;
    if ( numObj.isNum() )
        dest = numObj.getNum();
    else
        qDebug() << type << " is not Num." << endl;
    numObj.free();
}

int XPDFReader::lookupNumArray( Dict * dict, const char * type, double * dest, int len )
{
    Object arrObj;
    dict->lookup( type, &arrObj );
    if ( arrObj.isNull() )
        return 0;
    Object numObj;
    if ( arrObj.isArray() )
    {
        len = qMin( len, arrObj.arrayGetLength() );
        for ( int i = 0; i < len; i++ )
        {
            dest[i] = arrObj.arrayGet( i, &numObj )->getNum();
            numObj.free();
        }
    }
    else
    {
        len = 0;
        qDebug() << type << "is not Array." << endl;
    }
    arrObj.free();
    return len;
}

void XPDFReader::lookupColor( Dict * dict, const char * type, QColor & dest )
{
    double c[3];
    if ( XPDFReader::lookupNumArray( dict, type, c, 3 ) == 3 )
        dest = QColor( (int)(c[0]*255.0), (int)(c[1]*255.0), (int)(c[2]*255.0));
}

void XPDFReader::lookupIntRef( Dict * dict, const char * type, int & dest )
{
    Object refObj;
    dict->lookupNF( type, &refObj );
    if ( refObj.isNull() )
        return;
    if ( refObj.isRef() )
        dest = refObj.getRefNum();
    else
        qDebug() << type << " is not Ref." << endl;
    refObj.free();
}

void XPDFReader::lookupDate( Dict * dict, const char * type, QDateTime & dest )
{
    Object dateObj;
    dict->lookup( type, &dateObj );
    if ( dateObj.isNull() )
        return;
    if ( dateObj.isString() )
    {
        const char * s = dateObj.getString()->getCString();
        if ( s[0] == 'D' && s[1] == ':' )
            s += 2;
        int year, mon, day, hour, min, sec;
        if ( sscanf( s, "%4d%2d%2d%2d%2d%2d", &year, &mon, &day, &hour, &min, &sec ) == 6 )
        {
            QDate d( year, mon, day );
            QTime t( hour, min, sec );
            if ( d.isValid() && t.isValid() )
                dest = QDateTime(d, t);
        }
        else
            qDebug() << "Wrong Date format '" << s << "' for '" << type << "'." << endl;
    }
    else
        qDebug() << type << " is not Date" << endl;
    dateObj.free();
}

void XPDFReader::transform( double * M, double x, double y, QPointF &res )
{
    res.setX( M[0] * x + M[2] * y + M[4] );
    res.setY( M[1] * x + M[3] * y + M[5] );
}

/** @short Helper classes for CROSSDEPS resolving and DS conversion. */
struct ResolveRevision
{
    int           prevAnnotationID; // ID of the annotation to be reparended
    int           nextAnnotationID; // (only needed for speeding up resolving)
    Annotation *  nextAnnotation;   // annotation that will act as parent
    Annotation::RevScope nextScope; // scope of revision (Reply)
    Annotation::RevType  nextType;  // type of revision (None)
};

struct ResolveWindow
{
    int           popupWindowID;    // ID of the (maybe shared) window
    Annotation *  annotation;       // annotation having the popup window
};

struct PostProcessText              // this handles a special pdf case conversion
{
    Annotation *  textAnnotation;   // a popup text annotation (not FreeText)
    bool          opened;           // pdf property to convert to window flags
};

struct PopupWindow
{
    Annotation *  dummyAnnotation;  // window properties (in pdf as Annotation)
    bool          shown;            // converted to Annotation::Hidden flag
};

}

--- NEW FILE: poppler-annotation.cc ---
/* poppler-annotation.cc: qt interface to poppler
 * Copyright (C) 2006, Albert Astals Cid
 * Adapting code from
 *   Copyright (C) 2004 by Enrico Ros <eros.kde at email.it>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

// qt/kde includes
#include <QtXml/QDomElement>
#include <QtGui/QColor>

// local includes
#include "poppler-annotation.h"

namespace Poppler {

//BEGIN AnnotationUtils implementation
Annotation * AnnotationUtils::createAnnotation( const QDomElement & annElement )
{
    // safety check on annotation element
    if ( !annElement.hasAttribute( "type" ) )
        return 0;

    // build annotation of given type
    Annotation * annotation = 0;
    int typeNumber = annElement.attribute( "type" ).toInt();
    switch ( typeNumber )
    {
        case Annotation::AText:
            annotation = new TextAnnotation( annElement );
            break;
        case Annotation::ALine:
            annotation = new LineAnnotation( annElement );
            break;
        case Annotation::AGeom:
            annotation = new GeomAnnotation( annElement );
            break;
        case Annotation::AHighlight:
            annotation = new HighlightAnnotation( annElement );
            break;
        case Annotation::AStamp:
            annotation = new StampAnnotation( annElement );
            break;
        case Annotation::AInk:
            annotation = new InkAnnotation( annElement );
            break;
    }

    // return created annotation
    return annotation;
}

void AnnotationUtils::storeAnnotation( const Annotation * ann, QDomElement & annElement,
    QDomDocument & document )
{
    // save annotation's type as element's attribute
    annElement.setAttribute( "type", (uint)ann->subType() );

    // append all annotation data as children of this node
    ann->store( annElement, document );
}

QDomElement AnnotationUtils::findChildElement( const QDomNode & parentNode,
    const QString & name )
{
    // loop through the whole children and return a 'name' named element
    QDomNode subNode = parentNode.firstChild();
    while( subNode.isElement() )
    {
        QDomElement element = subNode.toElement();
        if ( element.tagName() == name )
            return element;
        subNode = subNode.nextSibling();
    }
    // if the name can't be found, return a dummy null element
    return QDomElement();
}
//END AnnotationUtils implementation


//BEGIN Annotation implementation
Annotation::Style::Style()
    : opacity( 1.0 ), width( 1.0 ), style( Solid ), xCorners( 0.0 ),
    yCorners( 0.0 ), marks( 3 ), spaces( 0 ), effect( NoEffect ),
    effectIntensity( 1.0 ) {}

Annotation::Window::Window()
    : flags( -1 ), width( 0 ), height( 0 ) {}

Annotation::Revision::Revision()
    : annotation( 0 ), scope( Reply ), type( None ) {}

Annotation::Annotation()
    : flags( 0 ) {}

Annotation::~Annotation()
{
    // delete all children revisions
    if ( revisions.isEmpty() )
        return;
    QLinkedList< Revision >::iterator it = revisions.begin(), end = revisions.end();
    for ( ; it != end; ++it )
        delete (*it).annotation;
}

Annotation::Annotation( const QDomNode & annNode )
    : flags( 0 )
{
    // get the [base] element of the annotation node
    QDomElement e = AnnotationUtils::findChildElement( annNode, "base" );
    if ( e.isNull() )
        return;

    // parse -contents- attributes
    if ( e.hasAttribute( "author" ) )
        author = e.attribute( "author" );
    if ( e.hasAttribute( "contents" ) )
        contents = e.attribute( "contents" );
    if ( e.hasAttribute( "uniqueName" ) )
        uniqueName = e.attribute( "uniqueName" );
    if ( e.hasAttribute( "modifyDate" ) )
        modifyDate = QDateTime::fromString( e.attribute( "modifyDate" ) );
    if ( e.hasAttribute( "creationDate" ) )
        creationDate = QDateTime::fromString( e.attribute( "creationDate" ) );

    // parse -other- attributes
    if ( e.hasAttribute( "flags" ) )
        flags = e.attribute( "flags" ).toInt();
    if ( e.hasAttribute( "color" ) )
        style.color = QColor( e.attribute( "color" ) );
    if ( e.hasAttribute( "opacity" ) )
        style.opacity = e.attribute( "opacity" ).toDouble();

    // parse -the-subnodes- (describing Style, Window, Revision(s) structures)
    // Note: all subnodes if present must be 'attributes complete'
    QDomNode eSubNode = e.firstChild();
    while ( eSubNode.isElement() )
    {
        QDomElement ee = eSubNode.toElement();
        eSubNode = eSubNode.nextSibling();

        // parse boundary
        if ( ee.tagName() == "boundary" )
        {
            boundary.setLeft(ee.attribute( "l" ).toDouble());
            boundary.setTop(ee.attribute( "t" ).toDouble());
            boundary.setRight(ee.attribute( "r" ).toDouble());
            boundary.setBottom(ee.attribute( "b" ).toDouble());
        }
        // parse penStyle if not default
        else if ( ee.tagName() == "penStyle" )
        {
            style.width = ee.attribute( "width" ).toDouble();
            style.style = (LineStyle)ee.attribute( "style" ).toInt();
            style.xCorners = ee.attribute( "xcr" ).toDouble();
            style.yCorners = ee.attribute( "ycr" ).toDouble();
            style.marks = ee.attribute( "marks" ).toInt();
            style.spaces = ee.attribute( "spaces" ).toInt();
        }
        // parse effectStyle if not default
        else if ( ee.tagName() == "penEffect" )
        {
            style.effect = (LineEffect)ee.attribute( "effect" ).toInt();
            style.effectIntensity = ee.attribute( "intensity" ).toDouble();
        }
        // parse window if present
        else if ( ee.tagName() == "window" )
        {
            window.flags = ee.attribute( "flags" ).toInt();
            window.topLeft.setX(ee.attribute( "top" ).toDouble());
            window.topLeft.setY(ee.attribute( "left" ).toDouble());
            window.width = ee.attribute( "width" ).toInt();
            window.height = ee.attribute( "height" ).toInt();
            window.title = ee.attribute( "title" );
            window.summary = ee.attribute( "summary" );
            // parse window subnodes
            QDomNode winNode = ee.firstChild();
            for ( ; winNode.isElement(); winNode = winNode.nextSibling() )
            {
                QDomElement winElement = winNode.toElement();
                if ( winElement.tagName() == "text" )
                    window.text = winElement.firstChild().toCDATASection().data();
            }
        }
    }

    // get the [revisions] element of the annotation node
    QDomNode revNode = annNode.firstChild();
    for ( ; revNode.isElement(); revNode = revNode.nextSibling() )
    {
        QDomElement revElement = revNode.toElement();
        if ( revElement.tagName() != "revision" )
            continue;

        // compile the Revision structure crating annotation
        Revision rev;
        rev.scope = (RevScope)revElement.attribute( "revScope" ).toInt();
        rev.type = (RevType)revElement.attribute( "revType" ).toInt();
        rev.annotation = AnnotationUtils::createAnnotation( revElement );

        // if annotation is valid, add revision to internal list
        if ( rev.annotation );
            revisions.append( rev );
    }
}

void Annotation::store( QDomNode & annNode, QDomDocument & document ) const
{
    // create [base] element of the annotation node
    QDomElement e = document.createElement( "base" );
    annNode.appendChild( e );

    // store -contents- attributes
    if ( !author.isEmpty() )
        e.setAttribute( "author", author );
    if ( !contents.isEmpty() )
        e.setAttribute( "contents", contents );
    if ( !uniqueName.isEmpty() )
        e.setAttribute( "uniqueName", uniqueName );
    if ( modifyDate.isValid() )
        e.setAttribute( "modifyDate", modifyDate.toString() );
    if ( creationDate.isValid() )
        e.setAttribute( "creationDate", creationDate.toString() );

    // store -other- attributes
    if ( flags )
        e.setAttribute( "flags", flags );
    if ( style.color.isValid() && style.color != Qt::black )
        e.setAttribute( "color", style.color.name() );
    if ( style.opacity != 1.0 )
        e.setAttribute( "opacity", style.opacity );

    // Sub-Node-1 - boundary
    QDomElement bE = document.createElement( "boundary" );
    e.appendChild( bE );
    bE.setAttribute( "l", (double)boundary.left() );
    bE.setAttribute( "t", (double)boundary.top() );
    bE.setAttribute( "r", (double)boundary.right() );
    bE.setAttribute( "b", (double)boundary.bottom() );

    // Sub-Node-2 - penStyle
    if ( style.width != 1 || style.style != Solid || style.xCorners != 0 ||
         style.yCorners != 0.0 || style.marks != 3 || style.spaces != 0 )
    {
        QDomElement psE = document.createElement( "penStyle" );
        e.appendChild( psE );
        psE.setAttribute( "width", style.width );
        psE.setAttribute( "style", (int)style.style );
        psE.setAttribute( "xcr", style.xCorners );
        psE.setAttribute( "ycr", style.yCorners );
        psE.setAttribute( "marks", style.marks );
        psE.setAttribute( "spaces", style.spaces );
    }

    // Sub-Node-3 - penEffect
    if ( style.effect != NoEffect || style.effectIntensity != 1.0 )
    {
        QDomElement peE = document.createElement( "penEffect" );
        e.appendChild( peE );
        peE.setAttribute( "effect", (int)style.effect );
        peE.setAttribute( "intensity", style.effectIntensity );
    }

    // Sub-Node-4 - window
    if ( window.flags != -1 || !window.title.isEmpty() ||
         !window.summary.isEmpty() || !window.text.isEmpty() )
    {
        QDomElement wE = document.createElement( "window" );
        e.appendChild( wE );
        wE.setAttribute( "flags", window.flags );
        wE.setAttribute( "top", window.topLeft.x() );
        wE.setAttribute( "left", window.topLeft.y() );
        wE.setAttribute( "width", window.width );
        wE.setAttribute( "height", window.height );
        wE.setAttribute( "title", window.title );
        wE.setAttribute( "summary", window.summary );
        // store window.text as a subnode, because we need escaped data
        if ( !window.text.isEmpty() )
        {
            QDomElement escapedText = document.createElement( "text" );
            wE.appendChild( escapedText );
            QDomCDATASection textCData = document.createCDATASection( window.text );
            escapedText.appendChild( textCData );
        }
    }

    // create [revision] element of the annotation node (if any)
    if ( revisions.isEmpty() )
        return;

    // add all revisions as children of revisions element
    QLinkedList< Revision >::const_iterator it = revisions.begin(), end = revisions.end();
    for ( ; it != end; ++it )
    {
        // create revision element
        const Revision & revision = *it;
        QDomElement r = document.createElement( "revision" );
        annNode.appendChild( r );
        // set element attributes
        r.setAttribute( "revScope", (int)revision.scope );
        r.setAttribute( "revType", (int)revision.type );
        // use revision as the annotation element, so fill it up
        AnnotationUtils::storeAnnotation( revision.annotation, r, document );
    }
}
//END AnnotationUtils implementation


/** TextAnnotation [Annotation] */

TextAnnotation::TextAnnotation()
    : Annotation(), textType( Linked ), textIcon( "Comment" ),
    inplaceAlign( 0 ), inplaceIntent( Unknown )
{}

TextAnnotation::TextAnnotation( const QDomNode & node )
    : Annotation( node ), textType( Linked ), textIcon( "Comment" ),
    inplaceAlign( 0 ), inplaceIntent( Unknown )
{
    // loop through the whole children looking for a 'text' element
    QDomNode subNode = node.firstChild();
    while( subNode.isElement() )
    {
        QDomElement e = subNode.toElement();
        subNode = subNode.nextSibling();
        if ( e.tagName() != "text" )
            continue;

        // parse the attributes
        if ( e.hasAttribute( "type" ) )
            textType = (TextAnnotation::TextType)e.attribute( "type" ).toInt();
        if ( e.hasAttribute( "icon" ) )
            textIcon = e.attribute( "icon" );
        if ( e.hasAttribute( "font" ) )
            textFont.fromString( e.attribute( "font" ) );
        if ( e.hasAttribute( "align" ) )
            inplaceAlign = e.attribute( "align" ).toInt();
        if ( e.hasAttribute( "intent" ) )
            inplaceIntent = (TextAnnotation::InplaceIntent)e.attribute( "intent" ).toInt();

        // parse the subnodes
        QDomNode eSubNode = e.firstChild();
        while ( eSubNode.isElement() )
        {
            QDomElement ee = eSubNode.toElement();
            eSubNode = eSubNode.nextSibling();

            if ( ee.tagName() == "escapedText" )
            {
                inplaceText = ee.firstChild().toCDATASection().data();
            }
            else if ( ee.tagName() == "callout" )
            {
                inplaceCallout[0].setX(ee.attribute( "ax" ).toDouble());
                inplaceCallout[0].setY(ee.attribute( "ay" ).toDouble());
                inplaceCallout[1].setX(ee.attribute( "bx" ).toDouble());
                inplaceCallout[1].setY(ee.attribute( "by" ).toDouble());
                inplaceCallout[2].setX(ee.attribute( "cx" ).toDouble());
                inplaceCallout[2].setY(ee.attribute( "cy" ).toDouble());
            }
        }

        // loading complete
        break;
    }
}

void TextAnnotation::store( QDomNode & node, QDomDocument & document ) const
{
    // recurse to parent objects storing properties
    Annotation::store( node, document );

    // create [text] element
    QDomElement textElement = document.createElement( "text" );
    node.appendChild( textElement );

    // store the optional attributes
    if ( textType != Linked )
        textElement.setAttribute( "type", (int)textType );
    if ( textIcon != "Comment" )
        textElement.setAttribute( "icon", textIcon );
    if ( inplaceAlign )
        textElement.setAttribute( "align", inplaceAlign );
    if ( inplaceIntent != Unknown )
        textElement.setAttribute( "intent", (int)inplaceIntent );

    textElement.setAttribute( "font", textFont.toString() );

    // Sub-Node-1 - escapedText
    if ( !inplaceText.isEmpty() )
    {
        QDomElement escapedText = document.createElement( "escapedText" );
        textElement.appendChild( escapedText );
        QDomCDATASection textCData = document.createCDATASection( inplaceText );
        escapedText.appendChild( textCData );
    }

    // Sub-Node-2 - callout
    if ( inplaceCallout[0].x() != 0.0 )
    {
        QDomElement calloutElement = document.createElement( "callout" );
        textElement.appendChild( calloutElement );
        calloutElement.setAttribute( "ax", inplaceCallout[0].x() );
        calloutElement.setAttribute( "ay", inplaceCallout[0].y() );
        calloutElement.setAttribute( "bx", inplaceCallout[1].x() );
        calloutElement.setAttribute( "by", inplaceCallout[1].y() );
        calloutElement.setAttribute( "cx", inplaceCallout[2].x() );
        calloutElement.setAttribute( "cy", inplaceCallout[2].y() );
    }
}


/** LineAnnotation [Annotation] */

LineAnnotation::LineAnnotation()
    : Annotation(), lineStartStyle( None ), lineEndStyle( None ),
    lineClosed( false ), lineLeadingFwdPt( 0 ), lineLeadingBackPt( 0 ),
    lineShowCaption( false ), lineIntent( Unknown )
{}

LineAnnotation::LineAnnotation( const QDomNode & node )
    : Annotation( node ), lineStartStyle( None ), lineEndStyle( None ),
    lineClosed( false ), lineLeadingFwdPt( 0 ), lineLeadingBackPt( 0 ),
    lineShowCaption( false ), lineIntent( Unknown )
{
    // loop through the whole children looking for a 'line' element
    QDomNode subNode = node.firstChild();
    while( subNode.isElement() )
    {
        QDomElement e = subNode.toElement();
        subNode = subNode.nextSibling();
        if ( e.tagName() != "line" )
            continue;

        // parse the attributes
        if ( e.hasAttribute( "startStyle" ) )
            lineStartStyle = (LineAnnotation::TermStyle)e.attribute( "startStyle" ).toInt();
        if ( e.hasAttribute( "endStyle" ) )
            lineEndStyle = (LineAnnotation::TermStyle)e.attribute( "endStyle" ).toInt();
        if ( e.hasAttribute( "closed" ) )
            lineClosed = e.attribute( "closed" ).toInt();
        if ( e.hasAttribute( "innerColor" ) )
            lineInnerColor = QColor( e.attribute( "innerColor" ) );
        if ( e.hasAttribute( "leadFwd" ) )
            lineLeadingFwdPt = e.attribute( "leadFwd" ).toDouble();
        if ( e.hasAttribute( "leadBack" ) )
            lineLeadingBackPt = e.attribute( "leadBack" ).toDouble();
        if ( e.hasAttribute( "showCaption" ) )
            lineShowCaption = e.attribute( "showCaption" ).toInt();
        if ( e.hasAttribute( "intent" ) )
            lineIntent = (LineAnnotation::LineIntent)e.attribute( "intent" ).toInt();

        // parse all 'point' subnodes
        QDomNode pointNode = e.firstChild();
        while ( pointNode.isElement() )
        {
            QDomElement pe = pointNode.toElement();
            pointNode = pointNode.nextSibling();

            if ( pe.tagName() != "point" )
                continue;

            QPointF p(pe.attribute( "x", "0.0" ).toDouble(), pe.attribute( "y", "0.0" ).toDouble());
            linePoints.append( p );
        }

        // loading complete
        break;
    }
}

void LineAnnotation::store( QDomNode & node, QDomDocument & document ) const
{
    // recurse to parent objects storing properties
    Annotation::store( node, document );

    // create [line] element
    QDomElement lineElement = document.createElement( "line" );
    node.appendChild( lineElement );

    // store the attributes
    if ( lineStartStyle != None )
        lineElement.setAttribute( "startStyle", (int)lineStartStyle );
    if ( lineEndStyle != None )
        lineElement.setAttribute( "endStyle", (int)lineEndStyle );
    if ( lineClosed )
        lineElement.setAttribute( "closed", lineClosed );
    if ( lineInnerColor.isValid() )
        lineElement.setAttribute( "innerColor", lineInnerColor.name() );
    if ( lineLeadingFwdPt != 0.0 )
        lineElement.setAttribute( "leadFwd", lineLeadingFwdPt );
    if ( lineLeadingBackPt != 0.0 )
        lineElement.setAttribute( "leadBack", lineLeadingBackPt );
    if ( lineShowCaption )
        lineElement.setAttribute( "showCaption", lineShowCaption );
    if ( lineIntent != Unknown )
        lineElement.setAttribute( "intent", lineIntent );

    // append the list of points
    int points = linePoints.count();
    if ( points > 1 )
    {
        QLinkedList<QPointF>::const_iterator it = linePoints.begin(), end = linePoints.end();
        while ( it != end )
        {
            const QPointF & p = *it;
            QDomElement pElement = document.createElement( "point" );
            lineElement.appendChild( pElement );
            pElement.setAttribute( "x", p.x() );
            pElement.setAttribute( "y", p.y() );
        }
    }
}


/** GeomAnnotation [Annotation] */

GeomAnnotation::GeomAnnotation()
    : Annotation(), geomType( InscribedSquare ), geomWidthPt( 18 )
{}

GeomAnnotation::GeomAnnotation( const QDomNode & node )
    : Annotation( node ), geomType( InscribedSquare ), geomWidthPt( 18 )
{
    // loop through the whole children looking for a 'geom' element
    QDomNode subNode = node.firstChild();
    while( subNode.isElement() )
    {
        QDomElement e = subNode.toElement();
        subNode = subNode.nextSibling();
        if ( e.tagName() != "geom" )
            continue;

        // parse the attributes
        if ( e.hasAttribute( "type" ) )
            geomType = (GeomAnnotation::GeomType)e.attribute( "type" ).toInt();
        if ( e.hasAttribute( "color" ) )
            geomInnerColor = QColor( e.attribute( "color" ) );
        if ( e.hasAttribute( "width" ) )
            geomWidthPt = e.attribute( "width" ).toInt();

        // loading complete
        break;
    }
}

void GeomAnnotation::store( QDomNode & node, QDomDocument & document ) const
{
    // recurse to parent objects storing properties
    Annotation::store( node, document );

    // create [geom] element
    QDomElement geomElement = document.createElement( "geom" );
    node.appendChild( geomElement );

    // append the optional attributes
    if ( geomType != InscribedSquare )
        geomElement.setAttribute( "type", (int)geomType );
    if ( geomInnerColor.isValid() )
        geomElement.setAttribute( "color", geomInnerColor.name() );
    if ( geomWidthPt != 18 )
        geomElement.setAttribute( "width", geomWidthPt );
}


/** HighlightAnnotation [Annotation] */

HighlightAnnotation::HighlightAnnotation()
    : Annotation(), highlightType( Highlight )
{}

HighlightAnnotation::HighlightAnnotation( const QDomNode & node )
    : Annotation( node ), highlightType( Highlight )
{
    // loop through the whole children looking for a 'hl' element
    QDomNode subNode = node.firstChild();
    while( subNode.isElement() )
    {
        QDomElement e = subNode.toElement();
        subNode = subNode.nextSibling();
        if ( e.tagName() != "hl" )
            continue;

        // parse the attributes
        if ( e.hasAttribute( "type" ) )
            highlightType = (HighlightAnnotation::HighlightType)e.attribute( "type" ).toInt();

        // parse all 'quad' subnodes
        QDomNode quadNode = e.firstChild();
        for ( ; quadNode.isElement(); quadNode = quadNode.nextSibling() )
        {
            QDomElement qe = quadNode.toElement();
            if ( qe.tagName() != "quad" )
                continue;

            Quad q;
            q.points[0].setX(qe.attribute( "ax", "0.0" ).toDouble());
            q.points[0].setY(qe.attribute( "ay", "0.0" ).toDouble());
            q.points[1].setX(qe.attribute( "bx", "0.0" ).toDouble());
            q.points[1].setY(qe.attribute( "by", "0.0" ).toDouble());
            q.points[2].setX(qe.attribute( "cx", "0.0" ).toDouble());
            q.points[2].setY(qe.attribute( "cy", "0.0" ).toDouble());
            q.points[3].setX(qe.attribute( "dx", "0.0" ).toDouble());
            q.points[3].setY(qe.attribute( "dy", "0.0" ).toDouble());
            q.capStart = qe.hasAttribute( "start" );
            q.capEnd = qe.hasAttribute( "end" );
            q.feather = qe.attribute( "feather", "0.1" ).toDouble();
            highlightQuads.append( q );
        }

        // loading complete
        break;
    }
}

void HighlightAnnotation::store( QDomNode & node, QDomDocument & document ) const
{
    // recurse to parent objects storing properties
    Annotation::store( node, document );

    // create [hl] element
    QDomElement hlElement = document.createElement( "hl" );
    node.appendChild( hlElement );

    // append the optional attributes
    if ( highlightType != Highlight )
        hlElement.setAttribute( "type", (int)highlightType );
    if ( highlightQuads.count() < 1 )
        return;
    // append highlight quads, all children describe quads
    QList< Quad >::const_iterator it = highlightQuads.begin(), end = highlightQuads.end();
    for ( ; it != end; ++it )
    {
        QDomElement quadElement = document.createElement( "quad" );
        hlElement.appendChild( quadElement );
        const Quad & q = *it;
        quadElement.setAttribute( "ax", q.points[0].x() );
        quadElement.setAttribute( "ay", q.points[0].y() );
        quadElement.setAttribute( "bx", q.points[1].x() );
        quadElement.setAttribute( "by", q.points[1].y() );
        quadElement.setAttribute( "cx", q.points[2].x() );
        quadElement.setAttribute( "cy", q.points[2].y() );
        quadElement.setAttribute( "dx", q.points[3].x() );
        quadElement.setAttribute( "dy", q.points[3].y() );
        if ( q.capStart )
            quadElement.setAttribute( "start", 1 );
        if ( q.capEnd )
            quadElement.setAttribute( "end", 1 );
        quadElement.setAttribute( "feather", q.feather );
    }
}


/** StampAnnotation [Annotation] */

StampAnnotation::StampAnnotation()
    : Annotation(), stampIconName( "oKular" )
{}

StampAnnotation::StampAnnotation( const QDomNode & node )
    : Annotation( node ), stampIconName( "oKular" )
{
    // loop through the whole children looking for a 'stamp' element
    QDomNode subNode = node.firstChild();
    while( subNode.isElement() )
    {
        QDomElement e = subNode.toElement();
        subNode = subNode.nextSibling();
        if ( e.tagName() != "stamp" )
            continue;

        // parse the attributes
        if ( e.hasAttribute( "icon" ) )
            stampIconName = e.attribute( "icon" );

        // loading complete
        break;
    }
}

void StampAnnotation::store( QDomNode & node, QDomDocument & document ) const
{
    // recurse to parent objects storing properties
    Annotation::store( node, document );

    // create [stamp] element
    QDomElement stampElement = document.createElement( "stamp" );
    node.appendChild( stampElement );

    // append the optional attributes
    if ( stampIconName != "oKular" )
        stampElement.setAttribute( "icon", stampIconName );
}


/** InkAnnotation [Annotation] */

InkAnnotation::InkAnnotation()
    : Annotation()
{}

InkAnnotation::InkAnnotation( const QDomNode & node )
    : Annotation( node )
{
    // loop through the whole children looking for a 'ink' element
    QDomNode subNode = node.firstChild();
    while( subNode.isElement() )
    {
        QDomElement e = subNode.toElement();
        subNode = subNode.nextSibling();
        if ( e.tagName() != "ink" )
            continue;

        // parse the 'path' subnodes
        QDomNode pathNode = e.firstChild();
        while ( pathNode.isElement() )
        {
            QDomElement pathElement = pathNode.toElement();
            pathNode = pathNode.nextSibling();

            if ( pathElement.tagName() != "path" )
                continue;

            // build each path parsing 'point' subnodes
            QLinkedList<QPointF> path;
            QDomNode pointNode = pathElement.firstChild();
            while ( pointNode.isElement() )
            {
                QDomElement pointElement = pointNode.toElement();
                pointNode = pointNode.nextSibling();

                if ( pointElement.tagName() != "point" )
                    continue;

                QPointF p(pointElement.attribute( "x", "0.0" ).toDouble(), pointElement.attribute( "y", "0.0" ).toDouble());
                path.append( p );
            }

            // add the path to the path list if it contains at least 2 nodes
            if ( path.count() >= 2 )
                inkPaths.append( path );
        }

        // loading complete
        break;
    }
}

void InkAnnotation::store( QDomNode & node, QDomDocument & document ) const
{
    // recurse to parent objects storing properties
    Annotation::store( node, document );

    // create [ink] element
    QDomElement inkElement = document.createElement( "ink" );
    node.appendChild( inkElement );

    // append the optional attributes
    if ( inkPaths.count() < 1 )
        return;
    QList< QLinkedList<QPointF> >::const_iterator pIt = inkPaths.begin(), pEnd = inkPaths.end();
    for ( ; pIt != pEnd; ++pIt )
    {
        QDomElement pathElement = document.createElement( "path" );
        inkElement.appendChild( pathElement );
        const QLinkedList<QPointF> & path = *pIt;
        QLinkedList<QPointF>::const_iterator iIt = path.begin(), iEnd = path.end();
        for ( ; iIt != iEnd; ++iIt )
        {
            const QPointF & point = *iIt;
            QDomElement pointElement = document.createElement( "point" );
            pathElement.appendChild( pointElement );
            pointElement.setAttribute( "x", point.x() );
            pointElement.setAttribute( "y", point.y() );
        }
    }
}

}

--- NEW FILE: poppler-annotation.h ---
/* poppler-annotation.h: qt interface to poppler
 * Copyright (C) 2006, Albert Astals Cid
 * Adapting code from
 *   Copyright (C) 2004 by Enrico Ros <eros.kde at email.it>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifndef _POPPLER_ANNOTATION_H_
#define _POPPLER_ANNOTATION_H_

#include <QtCore/QDateTime>
#include <QtCore/QLinkedList>
#include <QtCore/QPointF>
#include <QtCore/QRectF>
#include <QtGui/QFont>

namespace Poppler {

class Annotation;

/**
 * @short Helper class for (recoursive) annotation retrieval/storage.
 *
 */
class AnnotationUtils
{
    public:
        // restore an annotation (with revisions if needed) from a dom
        // element. returns a pointer to the complete annotation or 0 if
        // element is invalid.
        static Annotation * createAnnotation( const QDomElement & annElement );

        // save the 'ann' annotations as a child of parentElement taking
        // care of saving all revisions if 'ann' has any.
        static void storeAnnotation( const Annotation * ann,
            QDomElement & annElement, QDomDocument & document );

        // return an element called 'name' from the direct children of
        // parentNode or a null element if not found
        static QDomElement findChildElement( const QDomNode & parentNode,
            const QString & name );

        //static inline QRect annotationGeometry( const Annotation * ann,
        //    int pageWidth, int pageHeight, int scaledWidth, int scaledHeight ) const;
};


/**
 * @short Annotation struct holds properties shared by all annotations.
 *
 * An Annotation is an object (text note, highlight, sound, popup window, ..)
 * contained by a Page in the document.
 *
 */
struct Annotation
{
    // enum definitions
    // WARNING!!! oKular uses that very same values so if you change them notify the author!
    enum SubType { AText = 1, ALine = 2, AGeom = 3, AHighlight = 4, AStamp = 5,
                   AInk = 6, A_BASE = 0 };
    enum Flag { Hidden = 1, FixedSize = 2, FixedRotation = 4, DenyPrint = 8,
                DenyWrite = 16, DenyDelete = 32, ToggleHidingOnMouse = 64, External = 128 };
    enum LineStyle { Solid = 1, Dashed = 2, Beveled = 4, Inset = 8, Underline = 16 };
    enum LineEffect { NoEffect = 1, Cloudy = 2};
    enum RevScope { Reply = 1, Group = 2, Delete = 4 };
    enum RevType { None = 1,  Marked = 2, Unmarked = 4,  Accepted = 8, Rejected = 16, Cancelled = 32, Completed = 64 };


    /** properties: contents related */
    QString         author;                 // ''
    QString         contents;               // ''
    QString         uniqueName;             // '#NUMBER#'
    QDateTime       modifyDate;             // before or equal to currentDateTime()
    QDateTime       creationDate;           // before or equal to modifyDate

    /** properties: look/interaction related */
    int             flags;                  // 0
    QRectF          boundary;               // valid or isNull()
    struct Style
    {
        // appearance properties
        QColor          color;              // black
        double          opacity;            // 1.0
        // pen properties
        double          width;              // 1.0
        LineStyle       style;              // LineStyle::Solid
        double          xCorners;           // 0.0
        double          yCorners;           // 0.0
        int             marks;              // 3
        int             spaces;             // 0
        // pen effects
        LineEffect      effect;             // LineEffect::NoEffect
        double          effectIntensity;    // 1.0
        // default initializer
        Style();
    }               style;

    /** properties: popup window */
    struct Window
    {
        // window state (Hidden, FixedRotation, Deny* flags allowed)
        int             flags;              // -1 (never initialized) -> 0 (if inited and shown)
        // geometric properties
        QPointF         topLeft;            // no default, inited to boundary.topLeft
        int             width;              // no default
        int             height;             // no default
        // window contens/override properties
        QString         title;              // '' text in the titlebar (overrides author)
        QString         summary;            // '' short description (displayed if not empty)
        QString         text;               // '' text for the window (overrides annot->contents)
        // default initializer
        Window();
    }               window;

    /** properties: versioning */
    struct Revision
    {
        // child revision
        Annotation *    annotation;         // not null
        // scope and type of revision
        RevScope        scope;              // Reply
        RevType         type;               // None
        // default initializer
        Revision();
    };
    QLinkedList< Revision > revisions;       // empty by default

    // methods: query annotation's type for runtime type identification
    virtual SubType subType() const { return A_BASE; }
    //QRect geometry( int scaledWidth, int scaledHeight, KPDFPage * page );

    // methods: storage/retrieval from xml nodes
    Annotation( const QDomNode & node );
    virtual void store( QDomNode & parentNode, QDomDocument & document ) const;

    // methods: default constructor / virtual destructor
    Annotation();
    virtual ~Annotation();
};


// a helper used to shorten the code presented below
#define AN_COMMONDECL( className, rttiType )\
    className();\
    className( const class QDomNode & node );\
    void store( QDomNode & parentNode, QDomDocument & document ) const;\
    SubType subType() const { return rttiType; }

struct TextAnnotation : public Annotation
{
    // common stuff for Annotation derived classes
    AN_COMMONDECL( TextAnnotation, AText );

    // local enums
    enum TextType { Linked, InPlace };
    enum InplaceIntent { Unknown, Callout, TypeWriter };

    // data fields
    TextType        textType;               // Linked
    QString         textIcon;               // 'Comment'
    QFont           textFont;               // app def font
    int             inplaceAlign;           // 0:left, 1:center, 2:right
    QString         inplaceText;            // '' overrides contents
    QPointF         inplaceCallout[3];      //
    InplaceIntent   inplaceIntent;          // Unknown
};

struct LineAnnotation : public Annotation
{
    // common stuff for Annotation derived classes
    AN_COMMONDECL( LineAnnotation, ALine )

    // local enums
    enum TermStyle { Square, Circle, Diamond, OpenArrow, ClosedArrow, None,
                     Butt, ROpenArrow, RClosedArrow, Slash };
    enum LineIntent { Unknown, Arrow, Dimension, PolygonCloud };

    // data fields (note uses border for rendering style)
    QLinkedList<QPointF> linePoints;
    TermStyle       lineStartStyle;         // None
    TermStyle       lineEndStyle;           // None
    bool            lineClosed;             // false (if true draw close shape)
    QColor          lineInnerColor;         //
    double          lineLeadingFwdPt;       // 0.0
    double          lineLeadingBackPt;      // 0.0
    bool            lineShowCaption;        // false
    LineIntent      lineIntent;             // Unknown
};

struct GeomAnnotation : public Annotation
{
    // common stuff for Annotation derived classes
    AN_COMMONDECL( GeomAnnotation, AGeom )

    // common enums
    enum GeomType { InscribedSquare, InscribedCircle };

    // data fields (note uses border for rendering style)
    GeomType        geomType;               // InscribedSquare
    QColor          geomInnerColor;         //
    int             geomWidthPt;            // 18
};

struct HighlightAnnotation : public Annotation
{
    // common stuff for Annotation derived classes
    AN_COMMONDECL( HighlightAnnotation, AHighlight )

    // local enums
    enum HighlightType { Highlight, Squiggly, Underline, StrikeOut };

    // data fields
    HighlightType   highlightType;          // Highlight
    struct Quad
    {
        QPointF         points[4];          // 8 valid coords
        bool            capStart;           // false (vtx 1-4) [K]
        bool            capEnd;             // false (vtx 2-3) [K]
        double          feather;            // 0.1 (in range 0..1) [K]
    };
    QList< Quad >  highlightQuads;     // not empty
};

struct StampAnnotation : public Annotation
{
    // common stuff for Annotation derived classes
    AN_COMMONDECL( StampAnnotation, AStamp )

    // data fields
    QString         stampIconName;          // 'kpdf'
};

struct InkAnnotation : public Annotation
{
    // common stuff for Annotation derived classes
    AN_COMMONDECL( InkAnnotation, AInk )

    // data fields
    QList< QLinkedList<QPointF> > inkPaths;
};

}

#endif

Index: poppler-link.cc
===================================================================
RCS file: /cvs/poppler/poppler/qt4/src/poppler-link.cc,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- poppler-link.cc	9 May 2006 20:07:06 -0000	1.2
+++ poppler-link.cc	12 May 2006 20:40:05 -0000	1.3
@@ -1,5 +1,7 @@
-/* poppler-page.cc: qt interface to poppler
+/* poppler-link.cc: qt interface to poppler
  * Copyright (C) 2006, Albert Astals Cid
+ * Adapting code from
+ *   Copyright (C) 2004 by Enrico Ros <eros.kde at email.it>
  *
  * 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

Index: poppler-link.h
===================================================================
RCS file: /cvs/poppler/poppler/qt4/src/poppler-link.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- poppler-link.h	9 May 2006 20:07:06 -0000	1.1
+++ poppler-link.h	12 May 2006 20:40:05 -0000	1.2
@@ -1,4 +1,4 @@
-/* poppler-link.cc: qt interface to poppler
+/* poppler-link.h: qt interface to poppler
  * Copyright (C) 2006, Albert Astals Cid <aacid at kde.org>
  * Adapting code from
  *   Copyright (C) 2004 by Enrico Ros <eros.kde at email.it>

Index: poppler-page.cc
===================================================================
RCS file: /cvs/poppler/poppler/qt4/src/poppler-page.cc,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -d -r1.20 -r1.21
--- poppler-page.cc	9 May 2006 20:07:06 -0000	1.20
+++ poppler-page.cc	12 May 2006 20:40:05 -0000	1.21
@@ -33,6 +33,7 @@
 
 #include "poppler-private.h"
 #include "poppler-page-transition-private.h"
+#include "poppler-annotation-helper.h"
 
 namespace Poppler {
 
@@ -405,4 +406,694 @@
   return popplerLinks;
 }
 
+QList<Annotation*> Page::annotations() const
+{
+    Object annotArray;
+    ::Page *pdfPage = m_page->parentDoc->m_doc->doc.getCatalog()->getPage(m_page->index + 1);
+    pdfPage->getAnnots( &annotArray );
+    if ( !annotArray.isArray() || annotArray.arrayGetLength() < 1 )
+        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." << endl;
+            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" << endl;
+            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->textType = TextAnnotation::Linked;
+                // -> textIcon
+                XPDFReader::lookupName( annotDict, "Name", t->textIcon );
+                if ( !t->textIcon.isEmpty() )
+                {
+                    t->textIcon = t->textIcon.toLower();
+                    t->textIcon.remove( ' ' );
+                }
+                // 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->textType = TextAnnotation::InPlace;
+                // -> textFont
+                QString textFormat;
+                XPDFReader::lookupString( annotDict, "DA", textFormat );
+                // TODO, fill t->textFont using textFormat if not empty
+                // -> inplaceAlign
+                XPDFReader::lookupInt( annotDict, "Q", t->inplaceAlign );
+                // -> inplaceText (simple)
+                XPDFReader::lookupString( annotDict, "DS", t->inplaceText );
+                // -> inplaceText (complex override)
+                XPDFReader::lookupString( annotDict, "RC", t->inplaceText );
+                // -> inplaceCallout
+                double c[6];
+                int n = XPDFReader::lookupNumArray( annotDict, "CL", c, 6 );
+                if ( n >= 4 )
+                {
+                    XPDFReader::transform( MTX, c[0], c[1], t->inplaceCallout[0] );
+                    XPDFReader::transform( MTX, c[2], c[3], t->inplaceCallout[1] );
+                    if ( n == 6 )
+                        XPDFReader::transform( MTX, c[4], c[5], t->inplaceCallout[2] );
+                }
+                // -> inplaceIntent
+                QString intentName;
+                XPDFReader::lookupString( annotDict, "IT", intentName );
+                if ( intentName == "FreeTextCallout" )
+                    t->inplaceIntent = TextAnnotation::Callout;
+                else if ( intentName == "FreeTextTypeWriter" )
+                    t->inplaceIntent = 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, (subType == "Line") ? "L" : "Vertices", c, 100 );
+            if ( num < 4 || (num % 2) != 0 )
+            {
+                qDebug() << "L/Vertices wrong fol Line/Poly." << endl;
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            for ( int i = 0; i < num; i += 2 )
+            {
+                QPointF p;
+                XPDFReader::transform( MTX, c[i], c[i+1], p );
+                l->linePoints.push_back( p );
+            }
+            // -> 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->lineStartStyle = LineAnnotation::Square;
+                    else if ( !strcmp( name, "Circle" ) )
+                        l->lineStartStyle = LineAnnotation::Circle;
+                    else if ( !strcmp( name, "Diamond" ) )
+                        l->lineStartStyle = LineAnnotation::Diamond;
+                    else if ( !strcmp( name, "OpenArrow" ) )
+                        l->lineStartStyle = LineAnnotation::OpenArrow;
+                    else if ( !strcmp( name, "ClosedArrow" ) )
+                        l->lineStartStyle = LineAnnotation::ClosedArrow;
+                    else if ( !strcmp( name, "None" ) )
+                        l->lineStartStyle = LineAnnotation::None;
+                    else if ( !strcmp( name, "Butt" ) )
+                        l->lineStartStyle = LineAnnotation::Butt;
+                    else if ( !strcmp( name, "ROpenArrow" ) )
+                        l->lineStartStyle = LineAnnotation::ROpenArrow;
+                    else if ( !strcmp( name, "RClosedArrow" ) )
+                        l->lineStartStyle = LineAnnotation::RClosedArrow;
+                    else if ( !strcmp( name, "Slash" ) )
+                        l->lineStartStyle = LineAnnotation::Slash;
+                }
+                styleObj.free();
+                // -> lineEndStyle
+                leArray.arrayGet( 1, &styleObj );
+                if ( styleObj.isName() )
+                {
+                    const char * name = styleObj.getName();
+                    if ( !strcmp( name, "Square" ) )
+                        l->lineEndStyle = LineAnnotation::Square;
+                    else if ( !strcmp( name, "Circle" ) )
+                        l->lineEndStyle = LineAnnotation::Circle;
+                    else if ( !strcmp( name, "Diamond" ) )
+                        l->lineEndStyle = LineAnnotation::Diamond;
+                    else if ( !strcmp( name, "OpenArrow" ) )
+                        l->lineEndStyle = LineAnnotation::OpenArrow;
+                    else if ( !strcmp( name, "ClosedArrow" ) )
+                        l->lineEndStyle = LineAnnotation::ClosedArrow;
+                    else if ( !strcmp( name, "None" ) )
+                        l->lineEndStyle = LineAnnotation::None;
+                    else if ( !strcmp( name, "Butt" ) )
+                        l->lineEndStyle = LineAnnotation::Butt;
+                    else if ( !strcmp( name, "ROpenArrow" ) )
+                        l->lineEndStyle = LineAnnotation::ROpenArrow;
+                    else if ( !strcmp( name, "RClosedArrow" ) )
+                        l->lineEndStyle = LineAnnotation::RClosedArrow;
+                    else if ( !strcmp( name, "Slash" ) )
+                        l->lineEndStyle = LineAnnotation::Slash;
+                }
+                styleObj.free();
+            }
+            leArray.free();
+            // -> lineClosed
+            l->lineClosed = subType == "Polygon";
+            // -> lineInnerColor
+            XPDFReader::lookupColor( annotDict, "IC", l->lineInnerColor );
+            // -> lineLeadingFwdPt
+            XPDFReader::lookupNum( annotDict, "LL", l->lineLeadingFwdPt );
+            // -> lineLeadingBackPt
+            XPDFReader::lookupNum( annotDict, "LLE", l->lineLeadingBackPt );
+            // -> lineShowCaption
+            XPDFReader::lookupBool( annotDict, "Cap", l->lineShowCaption );
+            // -> lineIntent
+            QString intentName;
+            XPDFReader::lookupString( annotDict, "IT", intentName );
+            if ( intentName == "LineArrow" )
+                l->lineIntent = LineAnnotation::Arrow;
+            else if ( intentName == "LineDimension" )
+                l->lineIntent = LineAnnotation::Dimension;
+            else if ( intentName == "PolygonCloud" )
+                l->lineIntent = LineAnnotation::PolygonCloud;
+        }
+        else if ( subType == "Square" || subType == "Circle" )
+        {
+            // parse GeomAnnotation params
+            GeomAnnotation * g = new GeomAnnotation();
+            annotation = g;
+
+            // -> geomType
+            if ( subType == "Square" )
+                g->geomType = GeomAnnotation::InscribedSquare;
+            else
+                g->geomType = GeomAnnotation::InscribedCircle;
+            // -> geomInnerColor
+            XPDFReader::lookupColor( annotDict, "IC", g->geomInnerColor );
+            // 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->highlightType = HighlightAnnotation::Highlight;
+            else if ( subType == "Underline" )
+                h->highlightType = HighlightAnnotation::Underline;
+            else if ( subType == "Squiggly" )
+                h->highlightType = HighlightAnnotation::Squiggly;
+            else if ( subType == "StrikeOut" )
+                h->highlightType = 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." << endl;
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            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
+                h->highlightQuads.append( quad );
+            }
+        }
+        else if ( subType == "Stamp" )
+        {
+            // parse StampAnnotation params
+            StampAnnotation * s = new StampAnnotation();
+            annotation = s;
+
+            // -> stampIconName
+            XPDFReader::lookupName( annotDict, "Name", s->stampIconName );
+        }
+        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" << endl;
+                delete annotation;
+                annot.free();
+                continue;
+            }
+            int pathsNumber = pathsArray.arrayGetLength();
+            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
+                k->inkPaths.push_back( localList );
+            }
+            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 Annotation();
+            annotation = popup->dummyAnnotation;
+        }
+        else if ( subType == "Link" )
+        {
+            // ignore links (this may change in future)
+            annot.free();
+            continue;
+        }
+        else
+        {
+            // MISSING: Caret, FileAttachment, Sound, Movie, Widget,
+            //          Screen, PrinterMark, TrapNet, Watermark, 3D
+            qDebug() << "annotation '" << subType << "' not supported" << endl;
+            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." << endl;
+            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 );
+        annotation->boundary.setTopLeft(topLeft);
+        annotation->boundary.setBottomRight(bottomRight);
+        if ( annotation->boundary.left() > annotation->boundary.right() )
+        {
+            double aux = annotation->boundary.left();
+            annotation->boundary.setLeft(annotation->boundary.right());
+            annotation->boundary.setRight(aux);
+        }
+        if ( annotation->boundary.top() > annotation->boundary.bottom() )
+        {
+            double aux = annotation->boundary.top();
+            annotation->boundary.setTop(annotation->boundary.bottom());
+            annotation->boundary.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];
+        }
+        // -> contents
+        XPDFReader::lookupString( annotDict, "Contents", annotation->contents );
+        // -> uniqueName
+        XPDFReader::lookupString( annotDict, "NM", annotation->uniqueName );
+        // -> modifyDate (and -> creationDate)
+        XPDFReader::lookupDate( annotDict, "M", annotation->modifyDate );
+        if ( annotation->creationDate.isNull() && !annotation->modifyDate.isNull() )
+            annotation->creationDate = annotation->modifyDate;
+        // -> flags: set the external attribute since it's embedded on file
+        annotation->flags |= Annotation::External;
+        // -> flags
+        int flags = 0;
+        XPDFReader::lookupInt( annotDict, "F", flags );
+        if ( flags & 0x2 )
+            annotation->flags |= Annotation::Hidden;
+        if ( flags & 0x8 )
+            annotation->flags |= Annotation::FixedSize;
+        if ( flags & 0x10 )
+            annotation->flags |= Annotation::FixedRotation;
+        if ( !(flags & 0x4) )
+            annotation->flags |= Annotation::DenyPrint;
+        if ( flags & 0x40 )
+            annotation->flags |= (Annotation::DenyWrite | Annotation::DenyDelete);
+        if ( flags & 0x80 )
+            annotation->flags |= Annotation::DenyDelete;
+        if ( flags & 0x100 )
+            annotation->flags |= Annotation::ToggleHidingOnMouse;
+        // -> 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
+            XPDFReader::lookupDate( annotDict, "CreationDate", annotation->creationDate );
+            // -> style.opacity
+            XPDFReader::lookupNum( annotDict, "CA", annotation->style.opacity );
+            // -> window.title and author
+            XPDFReader::lookupString( annotDict, "T", annotation->window.title );
+            annotation->author = 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() << "PDFGenerator: clash for annotations with ID:" << annotID << endl;
+            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() << "PDFGenerator: can't resolve popup "
+                          << request.popupWindowID << "." << endl;
+            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
+        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() << "PDFGenerator: can't reparent annotation to "
+                          << parentID << "." << endl;
+            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;
+        }
+    }
+
+    /** 5 - finally RETURN ANNOTATIONS */
+    return annotationsMap.values();
+}
+
+
 }

Index: poppler-qt4.h
===================================================================
RCS file: /cvs/poppler/poppler/qt4/src/poppler-qt4.h,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -d -r1.31 -r1.32
--- poppler-qt4.h	9 May 2006 20:07:06 -0000	1.31
+++ poppler-qt4.h	12 May 2006 20:40:05 -0000	1.32
@@ -28,8 +28,9 @@
 #include <QtGui/QPixmap>
 #include <QtXml/QDomDocument>
 
-#include <poppler-page-transition.h>
+#include <poppler-annotation.h>
 #include <poppler-link.h>
+#include <poppler-page-transition.h>
 
 class EmbFile;
 
@@ -395,6 +396,11 @@
 	*/
 	QList<Link*> links() const;
 	
+	/**
+	 Returns the annotations of the page
+	*/
+	QList<Annotation*> annotations () const;
+	
     private:
 	Page(const Document *doc, int index);
 	PageData *m_page;



More information about the poppler mailing list