diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 91e678f..95a7f65 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -20,6 +20,7 @@ // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011 Adrian Johnson +// Copyright (C) 2012 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -349,6 +350,9 @@ public: void setFreeTypeHinting(GBool enable, GBool enableSlightHinting); +protected: + void doUpdateFont(GfxState *state); + private: GBool univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax); @@ -362,7 +366,6 @@ private: int overprintMode, GfxColor *singleColor, GBool grayIndexed = gFalse); SplashPath *convertPath(GfxState *state, GfxPath *path, GBool dropEmptySubpaths); - void doUpdateFont(GfxState *state); void drawType3Glyph(GfxState *state, T3FontCache *t3Font, T3FontCacheTag *tag, Guchar *data); static GBool imageMaskSrc(void *data, SplashColorPtr line); diff --git a/qt4/src/CMakeLists.txt b/qt4/src/CMakeLists.txt index 99dce58..5fe8314 100644 --- a/qt4/src/CMakeLists.txt +++ b/qt4/src/CMakeLists.txt @@ -15,6 +15,7 @@ set(poppler_qt4_SRCS poppler-link.cc poppler-link-extractor.cc poppler-movie.cc + poppler-objectreference.cc poppler-optcontent.cc poppler-page.cc poppler-base-converter.cc @@ -25,6 +26,8 @@ set(poppler_qt4_SRCS poppler-sound.cc poppler-textbox.cc poppler-page-transition.cc + poppler-streamdevice.cc + poppler-media.cc ${CMAKE_SOURCE_DIR}/poppler/ArthurOutputDev.cc ) qt4_automoc(${poppler_qt4_SRCS}) @@ -44,5 +47,6 @@ install(FILES poppler-optcontent.h poppler-export.h poppler-page-transition.h + poppler-media.h DESTINATION include/poppler/qt4) diff --git a/qt4/src/poppler-annotation-private.h b/qt4/src/poppler-annotation-private.h index b428347..5040da0 100644 --- a/qt4/src/poppler-annotation-private.h +++ b/qt4/src/poppler-annotation-private.h @@ -1,5 +1,6 @@ /* poppler-link-extractor-private.h: qt interface to poppler * Copyright (C) 2007, Pino Toscano + * Copyright (C) 2012, Tobias Koenig * * 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 @@ -21,6 +22,8 @@ #include "poppler-annotation.h" +#include "poppler-objectreference_p.h" + namespace Poppler { @@ -42,6 +45,7 @@ class AnnotationPrivate QRectF boundary; QLinkedList< Annotation::Revision > revisions; + ObjectReference pdfObjectReference; }; } diff --git a/qt4/src/poppler-annotation.h b/qt4/src/poppler-annotation.h index ae90d71..4124c74 100644 --- a/qt4/src/poppler-annotation.h +++ b/qt4/src/poppler-annotation.h @@ -3,6 +3,7 @@ * Copyright (C) 2006, 2008 Pino Toscano * Copyright (C) 2007, Brad Hards * Copyright (C) 2010, Philip Lorenz + * Copyright (C) 2012, Tobias Koenig * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -93,6 +94,9 @@ class POPPLER_QT4_EXPORT AnnotationUtils */ class POPPLER_QT4_EXPORT Annotation { + friend class LinkMovie; + friend class Page; + public: // enum definitions // WARNING!!! oKular uses that very same values so if you change them notify the author! diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc index 68c607a..f4cc2af 100644 --- a/qt4/src/poppler-link.cc +++ b/qt4/src/poppler-link.cc @@ -2,6 +2,7 @@ * Copyright (C) 2006-2007, Albert Astals Cid * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2010 Hib Eris + * Copyright (C) 2012, Tobias Koenig * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -25,6 +26,9 @@ #include +#include "poppler-annotation-private.h" +#include "poppler-objectreference_p.h" + #include "Link.h" namespace Poppler { @@ -154,6 +158,20 @@ class LinkSoundPrivate : public LinkPrivate delete sound; } +class LinkRenditionPrivate : public LinkPrivate +{ + public: + LinkRenditionPrivate( const QRectF &area ); + + MediaRendition *rendition; +}; + + LinkRenditionPrivate::LinkRenditionPrivate( const QRectF &area ) + : LinkPrivate( area ) + , rendition( 0 ) + { + } + class LinkJavaScriptPrivate : public LinkPrivate { public: @@ -167,18 +185,20 @@ class LinkJavaScriptPrivate : public LinkPrivate { } -#if 0 class LinkMoviePrivate : public LinkPrivate { public: - LinkMoviePrivate( const QRectF &area ); + LinkMoviePrivate( const QRectF &area, LinkMovie::Operation operation, const QString &title, const ObjectReference &reference ); + + LinkMovie::Operation operation; + QString annotationTitle; + ObjectReference annotationReference; }; - LinkMoviePrivate::LinkMoviePrivate( const QRectF &area ) - : LinkPrivate( area ) + LinkMoviePrivate::LinkMoviePrivate( const QRectF &area, LinkMovie::Operation _operation, const QString &title, const ObjectReference &reference ) + : LinkPrivate( area ), operation( _operation ), annotationTitle( title ), annotationReference( reference ) { } -#endif static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd) { double ctm[6]; @@ -544,6 +564,29 @@ class LinkMoviePrivate : public LinkPrivate return d->sound; } + // LinkRendition + LinkRendition::LinkRendition( const QRectF &linkArea, MediaRendition *rendition ) + : Link( *new LinkRenditionPrivate( linkArea ) ) + { + Q_D( LinkRendition ); + d->rendition = rendition; + } + + LinkRendition::~LinkRendition() + { + } + + Link::LinkType LinkRendition::linkType() const + { + return Rendition; + } + + MediaRendition * LinkRendition::rendition() const + { + Q_D( const LinkRendition ); + return d->rendition; + } + // LinkJavaScript LinkJavaScript::LinkJavaScript( const QRectF &linkArea, const QString &js ) : Link( *new LinkJavaScriptPrivate( linkArea ) ) @@ -567,10 +610,9 @@ class LinkMoviePrivate : public LinkPrivate return d->js; } -#if 0 // LinkMovie - LinkMovie::LinkMovie( const QRectF &linkArea ) - : Link( *new LinkMoviePrivate( linkArea ) ) + LinkMovie::LinkMovie( const QRectF &linkArea, Operation operation, const QString &annotationTitle, const ObjectReference &annotationReference ) + : Link( *new LinkMoviePrivate( linkArea, operation, annotationTitle, annotationReference ) ) { } @@ -582,6 +624,25 @@ class LinkMoviePrivate : public LinkPrivate { return Movie; } -#endif + + LinkMovie::Operation LinkMovie::operation() const + { + Q_D( const LinkMovie ); + return d->operation; + } + + bool LinkMovie::isReferencedAnnotation( const MovieAnnotation *annotation ) const + { + Q_D( const LinkMovie ); + if ( d->annotationReference.isValid() && d->annotationReference == annotation->d_ptr->pdfObjectReference ) + { + return true; + } + else if ( !d->annotationTitle.isNull() ) + { + return ( annotation->movieTitle() == d->annotationTitle ); + } + return false; + } } diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h index 1aa6d78..946aeed 100644 --- a/qt4/src/poppler-link.h +++ b/qt4/src/poppler-link.h @@ -2,6 +2,7 @@ * Copyright (C) 2006, Albert Astals Cid * Copyright (C) 2007-2008, 2010, Pino Toscano * Copyright (C) 2010, Guillermo Amaral + * Copyright (C) 2012, Tobias Koenig * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -40,7 +41,10 @@ class LinkJavaScriptPrivate; class LinkMoviePrivate; class LinkDestinationData; class LinkDestinationPrivate; +class LinkRenditionPrivate; +class ObjectReference; class SoundObject; +class MediaRendition; /** * \short A destination. @@ -183,6 +187,7 @@ class POPPLER_QT4_EXPORT Link Action, ///< A "standard" action to be executed in the viewer Sound, ///< A link representing a sound to be played Movie, ///< An action to be executed on a movie + Rendition, ///< A rendition link JavaScript ///< A JavaScript code to be interpreted \since 0.10 }; @@ -440,6 +445,36 @@ class POPPLER_QT4_EXPORT LinkSound : public Link }; /** + * Rendition: Rendition link. + */ +class POPPLER_QT4_EXPORT LinkRendition : public Link +{ + public: + /** + * Create a new media rendition link. + * + * \param linkArea the active area of the link + * \param rendition + */ + LinkRendition( const QRectF &linkArea, MediaRendition *rendition ); + /** + * Destructor. + */ + virtual ~LinkRendition(); + + LinkType linkType() const; + + /** + * + */ + MediaRendition *rendition() const; + + private: + Q_DECLARE_PRIVATE( LinkRendition ) + Q_DISABLE_COPY( LinkRendition ) +}; + +/** * JavaScript: a JavaScript code to be interpreted. * * \since 0.10 @@ -471,21 +506,52 @@ class POPPLER_QT4_EXPORT LinkJavaScript : public Link Q_DISABLE_COPY( LinkJavaScript ) }; -#if 0 -/** Movie: Not yet defined -> think renaming to 'Media' link **/ +/** + * Movie: a movie to be played. + * + * \since 0.20 + */ class POPPLER_QT4_EXPORT LinkMovie : public Link -// TODO this (Movie link) { public: - LinkMovie( const QRectF &linkArea ); + /** + * Describes the operation to be performed on the movie. + */ + enum Operation { Play, + Stop, + Pause, + Resume + }; + + /** + * Create a new Movie link. + * + * \param linkArea the active area of the link + * \param operation the operation to be performed on the movie + * \param annotationTitle the title of the movie annotation identifying the movie to be played + * \param annotationReference the object reference of the movie annotation identifying the movie to be played + * + * Note: This constructor is supposed to be used by Poppler::Page only. + */ + LinkMovie( const QRectF &linkArea, Operation operation, const QString &annotationTitle, const ObjectReference &annotationReference ); + /** + * Destructor. + */ ~LinkMovie(); LinkType linkType() const; + /** + * Returns the operation to be performed on the movie. + */ + Operation operation() const; + /** + * Returns whether the given @p annotation is the referenced movie annotation for this movie @p link. + */ + bool isReferencedAnnotation( const MovieAnnotation *annotation ) const; private: Q_DECLARE_PRIVATE( LinkMovie ) Q_DISABLE_COPY( LinkMovie ) }; -#endif } diff --git a/qt4/src/poppler-media.cc b/qt4/src/poppler-media.cc new file mode 100644 index 0000000..e5a5676 --- /dev/null +++ b/qt4/src/poppler-media.cc @@ -0,0 +1,123 @@ +/* poppler-media.h: qt interface to poppler + * Copyright (C) 2012 Guillermo A. Amaral B. + * + * 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-media.h" + +#include "Rendition.h" + +#include "poppler-private.h" +#include "poppler-streamdevice-private.h" + +namespace Poppler +{ + +MediaRendition::MediaRendition(::MediaRendition *rendition) + : m_rendition(rendition) + , m_device(0) +{ + if (m_rendition) + m_device = new StreamDevice(m_rendition->getEmbbededStream()); +} + +MediaRendition::~MediaRendition() +{ + delete m_device; +} + +bool +MediaRendition::isValid() +{ + return m_rendition && m_rendition->isOk(); +} + +MediaParameters* +MediaRendition::getMHParameters() +{ + Q_ASSERT(isValid() && "Invalid media rendition."); + return m_rendition->getMHParameters(); +} + +MediaParameters* +MediaRendition::getBEParameters() +{ + Q_ASSERT(isValid() && "Invalid media rendition."); + return m_rendition->getBEParameters(); +} + +QString +MediaRendition::contentType() +{ + Q_ASSERT(isValid() && "Invalid media rendition."); + return UnicodeParsedString(m_rendition->getContentType()); +} + +QString +MediaRendition::fileName() +{ + Q_ASSERT(isValid() && "Invalid media rendition."); + return UnicodeParsedString(m_rendition->getFileName()); +} + +bool +MediaRendition::isEmbedded() +{ + Q_ASSERT(isValid() && "Invalid media rendition."); + return m_rendition->getIsEmbedded(); +} + +QIODevice * +MediaRendition::streamDevice() const +{ + return m_device; +} + +bool +MediaRendition::autoPlay() const +{ + if (m_rendition->getBEParameters()) { + return m_rendition->getBEParameters()->autoPlay; + } else if (m_rendition->getMHParameters()) { + return m_rendition->getMHParameters()->autoPlay; + } else qDebug("No BE or MH paremeters to reference!"); + return false; +} + +bool +MediaRendition::showControls() const +{ + if (m_rendition->getBEParameters()) { + return m_rendition->getBEParameters()->showControls; + } else if (m_rendition->getMHParameters()) { + return m_rendition->getMHParameters()->showControls; + } else qDebug("No BE or MH paremeters to reference!"); + return false; +} + +float +MediaRendition::repeatCount() const +{ + if (m_rendition->getBEParameters()) { + return m_rendition->getBEParameters()->repeatCount; + } else if (m_rendition->getMHParameters()) { + return m_rendition->getMHParameters()->repeatCount; + } else qDebug("No BE or MH paremeters to reference!"); + return 1.f; +} + +} + diff --git a/qt4/src/poppler-media.h b/qt4/src/poppler-media.h new file mode 100644 index 0000000..b9843ec --- /dev/null +++ b/qt4/src/poppler-media.h @@ -0,0 +1,58 @@ +/* poppler-media-private.h: qt interface to poppler + * Copyright (C) 2012 Guillermo A. Amaral B. + * + * 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. + */ + +#ifndef __POPPLER_MEDIARENDITION_H__ +#define __POPPLER_MEDIARENDITION_H__ + +#include + +class MediaRendition; +class QIODevice; + +struct MediaParameters; + +namespace Poppler +{ + class MediaRendition { + public: + MediaRendition(::MediaRendition *rendition); + ~MediaRendition(); + + bool isValid(); + + MediaParameters* getMHParameters(); + MediaParameters* getBEParameters(); + + QString contentType(); + QString fileName(); + + bool isEmbedded(); + QIODevice *streamDevice() const; + + /* helper function */ + bool autoPlay() const; + bool showControls() const; + float repeatCount() const; + + private: + ::MediaRendition *m_rendition; + QIODevice *m_device; + }; +} + +#endif /* __POPPLER_MEDIARENDITION_H__ */ diff --git a/qt4/src/poppler-objectreference.cc b/qt4/src/poppler-objectreference.cc new file mode 100644 index 0000000..cb32314 --- /dev/null +++ b/qt4/src/poppler-objectreference.cc @@ -0,0 +1,89 @@ +/* poppler-objectreference.h: qt interface to poppler + * + * Copyright (C) 2012, Tobias Koenig + * + * 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-objectreference_p.h" + +namespace Poppler { + + class ObjectReferencePrivate + { + public: + ObjectReferencePrivate() + : m_number( -1 ), m_generation( -1 ) + { + } + + ObjectReferencePrivate( int number, int generation ) + : m_number( number ), m_generation( generation ) + { + } + + int m_number; + int m_generation; + }; + +} + +using namespace Poppler; + +ObjectReference::ObjectReference() + : d( new ObjectReferencePrivate() ) +{ +} + +ObjectReference::ObjectReference( int number, int generation ) + : d( new ObjectReferencePrivate( number, generation ) ) +{ +} + +ObjectReference::ObjectReference( const ObjectReference &other ) + : d( new ObjectReferencePrivate( other.d->m_number, other.d->m_generation ) ) +{ +} + +ObjectReference::~ObjectReference() +{ + delete d; +} + +ObjectReference& ObjectReference::operator=( const ObjectReference &other ) +{ + if ( this != &other ) + { + d->m_number = other.d->m_number; + d->m_generation = other.d->m_generation; + } + + return *this; +} + +bool ObjectReference::isValid() const +{ + return ( d->m_number != -1 ); +} + +bool ObjectReference::operator==( const ObjectReference &other ) const +{ + return ( ( d->m_number == other.d->m_number ) && ( d->m_generation == other.d->m_generation ) ); +} + +bool ObjectReference::operator!=( const ObjectReference &other ) const +{ + return !operator==( other ); +} diff --git a/qt4/src/poppler-objectreference_p.h b/qt4/src/poppler-objectreference_p.h new file mode 100644 index 0000000..52af958 --- /dev/null +++ b/qt4/src/poppler-objectreference_p.h @@ -0,0 +1,77 @@ +/* poppler-objectreference.h: qt interface to poppler + * + * Copyright (C) 2012, Tobias Koenig + * + * 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. + */ + +#ifndef POPPLER_OBJECTREFERENCE_H +#define POPPLER_OBJECTREFERENCE_H + +#include "poppler-export.h" + +namespace Poppler +{ + class ObjectReferencePrivate; + + /** + * \brief Reference of a PDF object + * + * ObjectReference is a class that encapsulates a reference to a PDF object. + * It is needed to store (and later on resolve) references between various PDF entities. + */ + class POPPLER_QT4_EXPORT ObjectReference + { + public: + /** + * Creates a new, invalid ObjectReference. + */ + ObjectReference(); + + /** + * Creates a new ObjectReference. + * + * @param number The number of the PDF object. + * @param generation The generation of the PDF object. + */ + ObjectReference( int number, int generation ); + + /** + * Creates a new ObjectReference from an @p other one. + */ + ObjectReference( const ObjectReference &other ); + + /** + * Destroys the ObjectReference. + */ + ~ObjectReference(); + + ObjectReference& operator=( const ObjectReference &other ); + + /** + * Returns whether the object reference is valid. + */ + bool isValid() const; + + bool operator==( const ObjectReference &other ) const; + + bool operator!=( const ObjectReference &other ) const; + + private: + ObjectReferencePrivate * const d; + }; +} + +#endif diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc index 398a69b..8fb6481 100644 --- a/qt4/src/poppler-page.cc +++ b/qt4/src/poppler-page.cc @@ -10,6 +10,7 @@ * Copyright (C) 2010 Suzuki Toshiya * Copyright (C) 2010 Matthias Fauconneau * Copyright (C) 2010 Hib Eris + * Copyright (C) 2012 Tobias Koenig * * 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 @@ -44,6 +45,7 @@ #include #include #include +#include #if defined(HAVE_SPLASH) #include #include @@ -56,6 +58,8 @@ #include "poppler-annotation-helper.h" #include "poppler-annotation-private.h" #include "poppler-form.h" +#include "poppler-media.h" +#include "poppler-streamdevice-private.h" namespace Poppler { @@ -171,15 +175,40 @@ Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDo 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; + { + ::LinkMovie *lm = (::LinkMovie *)a; + + const QString title = ( lm->hasAnnotTitle() ? UnicodeParsedString( lm->getAnnotTitle() ) : QString() ); + + const ObjectReference reference = ( lm->hasAnnotRef() ? ObjectReference( lm->getAnnotRef()->num, lm->getAnnotRef()->gen ) : ObjectReference() ); + + LinkMovie::Operation operation = LinkMovie::Play; + switch ( lm->getOperation() ) + { + case ::LinkMovie::operationTypePlay: + operation = LinkMovie::Play; + break; + case ::LinkMovie::operationTypePause: + operation = LinkMovie::Pause; + break; + case ::LinkMovie::operationTypeResume: + operation = LinkMovie::Resume; + break; + case ::LinkMovie::operationTypeStop: + operation = LinkMovie::Stop; + break; + }; + + popplerLink = new LinkMovie( linkArea, operation, title, reference ); + } + break; + + case actionRendition: + { + ::LinkRendition *lrn = (::LinkRendition *)a; + popplerLink = new LinkRendition( linkArea, new MediaRendition(lrn->getMedia()) ); + } + break; case actionUnknown: break; @@ -981,6 +1010,21 @@ QList Page::annotations() const // handled as forms or some other way case Annot::typeWidget: continue; + case Annot::typeScreen: + { + AnnotScreen * screenann = static_cast< AnnotScreen * >( ann ); + + const GooString *title = screenann->getTitle(); + if (title) + qDebug("tokoe: title: %s", title->getCString()); + + if (screenann->getAction()) { + Link * popplerLink = m_page->convertLinkActionToLink( screenann->getAction(), QRectF() ); + } + + qDebug("appear=%p action=%p additionalAction=%p", screenann->getAppearCharacs(), screenann->getAction(), screenann->getAdditionActions()); + } + continue; default: { #define CASE_FOR_TYPE( thetype ) \ @@ -1029,6 +1073,8 @@ QList Page::annotations() const //annotation->rUnscaledHeight = (r[3] > r[1]) ? r[3] - r[1] : r[1] - r[3]; } annotation->setBoundary( boundaryRect ); + // -> PDF object reference + annotation->d_ptr->pdfObjectReference = ObjectReference( ann->getRef().num, ann->getRef().gen ); // -> contents annotation->setContents( UnicodeParsedString( ann->getContents() ) ); // -> uniqueName diff --git a/qt4/src/poppler-streamdevice-private.h b/qt4/src/poppler-streamdevice-private.h new file mode 100644 index 0000000..0c2adc5 --- /dev/null +++ b/qt4/src/poppler-streamdevice-private.h @@ -0,0 +1,48 @@ +/* poppler-streamdevice-private.h: Qt4 interface to poppler + * Copyright (C) 2012, Guiillermo A. Amaral B. + * + * 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. + */ + +#ifndef POPPLER_STREAMDEVICE_PRIVATE_H +#define POPPLER_STREAMDEVICE_PRIVATE_H + +#include + +class Stream; + +namespace Poppler { + +class StreamDevice : public QIODevice +{ + public: + StreamDevice(Stream *stream, QObject *parent = 0); + virtual ~StreamDevice(); + + virtual bool isSequential() const + { return true; } + + protected: + virtual qint64 readData(char *data, qint64 maxSize); + inline virtual qint64 writeData(const char *, qint64) + { return 0; } + + private: + Stream *m_stream; +}; + +} + +#endif diff --git a/qt4/src/poppler-streamdevice.cc b/qt4/src/poppler-streamdevice.cc new file mode 100644 index 0000000..69da072 --- /dev/null +++ b/qt4/src/poppler-streamdevice.cc @@ -0,0 +1,49 @@ +/* poppler-streamdevice.cc: Qt4 interface to poppler + * Copyright (C) 2012, Guiillermo A. Amaral B. + * + * 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-streamdevice-private.h" + +#include "Object.h" +#include "Stream.h" + +namespace Poppler { + +StreamDevice::StreamDevice(Stream *stream, QObject *parent) + : QIODevice(parent) + , m_stream(stream) +{ + Q_ASSERT(m_stream && "Invalid stream assigned."); + m_stream->incRef(); + m_stream->reset(); + setOpenMode(QIODevice::ReadOnly); +} + +StreamDevice::~StreamDevice() +{ + m_stream->decRef(); + m_stream = 0; +} + +qint64 +StreamDevice::readData(char *data, qint64 maxSize) +{ + return m_stream->doGetChars(maxSize, reinterpret_cast(data)); +} + +} + diff --git a/regtest/Bisect.py b/regtest/Bisect.py new file mode 100644 index 0000000..715424f --- /dev/null +++ b/regtest/Bisect.py @@ -0,0 +1,113 @@ +# Bisect.py +# +# Copyright (C) 2012 Carlos Garcia Campos +# +# 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 of the License, 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 + +from builder import get_builder +from TestRun import TestRun +from Config import Config +import os +import subprocess +import sys + +class GitBisect: + def __init__(self, srcdir): + self.srcdir = srcdir + + def __run_cmd(self, cmd): + p = subprocess.Popen(cmd, cwd=self.srcdir, stdout=subprocess.PIPE) + stdout, stderr = p.communicate() + if stdout: + sys.stdout.write(stdout) + + status = p.returncode + print status + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) + + return stdout + + def __finished(self, output): + if not output: + return True + + return "is the first bad commit" in output + + def start(self, bad=None, good=None): + cmd = ['git', 'bisect', 'start'] + if bad is not None: + cmd.append(bad) + if good is not None: + cmd.append(good) + self.__run_cmd(cmd) + + def good(self): + cmd = ['git', 'bisect', 'good'] + output = self.__run_cmd(cmd) + return self.__finished(output) + + def bad(self): + cmd = ['git', 'bisect', 'bad'] + output = self.__run_cmd(cmd) + return self.__finished(output) + + def reset(self): + cmd = ['git', 'bisect', 'reset'] + self.__run_cmd(cmd) + +class Bisect: + + def __init__(self, test, refsdir, outdir): + self._test = test + self._refsdir = refsdir + self._outdir = outdir + self.config = Config() + self._builder = get_builder(self.config.builder) + + # Add run-tests options to config + self.config.keep_results = False + self.config.create_diffs = False + self.config.update_refs = False + + def __get_current_revision(self): + p = subprocess.Popen(['git', 'rev-parse', 'HEAD'], cwd=self.config.src_dir, stdout=subprocess.PIPE) + return p.communicate()[0] + + def run(self): + bisect = GitBisect(self.config.src_dir) + # TODO: save revision in .md5 files and get the good + # revision from refs when not provided by command line + try: + bisect.start(self.config.bad, self.config.good) + except: + print("Couldn't start git bisect") + return + finished = False + while not finished: + test_runner = TestRun(os.path.dirname(self._test), self._refsdir, self._outdir) + try: + self._builder.build() + except: + print("Impossible to find regression, build is broken in revision: %s" % (self.__get_current_revision())) + break + test_runner.run_test(os.path.basename(self._test)) + if test_runner._n_passed == 0: + finished = bisect.bad() + else: + finished = bisect.good() + + bisect.reset() + diff --git a/regtest/TestRun.py b/regtest/TestRun.py index 80a4d33..cffd00b 100644 --- a/regtest/TestRun.py +++ b/regtest/TestRun.py @@ -29,7 +29,7 @@ class TestRun: self._docsdir = docsdir self._refsdir = refsdir self._outdir = outdir - self._skipped = get_skipped_tests(docsdir) + self._skip = get_skipped_tests(docsdir) self.config = Config() # Results @@ -39,6 +39,7 @@ class TestRun: self._crashed = [] self._failed_status_error = [] self._stderr = [] + self._skipped = [] try: os.makedirs(self._outdir); @@ -54,6 +55,7 @@ class TestRun: ref_is_crashed = backend.is_crashed(refs_path) ref_is_failed = backend.is_failed(refs_path) if not ref_has_md5 and not ref_is_crashed and not ref_is_failed: + self._skipped.append("%s (%s)" % (doc_path, backend.get_name())) print("Reference files not found, skipping '%s' for %s backend" % (doc_path, backend.get_name())) return @@ -106,8 +108,10 @@ class TestRun: return def run_test(self, filename, n_doc = 1, total_docs = 1): - if filename in self._skipped: - print("Skipping test '%s' (%d/%d)" % (os.path.join(self._docsdir, filename), n_doc, total_docs)) + if filename in self._skip: + doc_path = os.path.join(self._docsdir, filename) + self._skipped.append("%s" % (doc_path)) + print("Skipping test '%s' (%d/%d)" % (doc_path, n_doc, total_docs)) return out_path = os.path.join(self._outdir, filename) @@ -122,6 +126,7 @@ class TestRun: refs_path = os.path.join(self._refsdir, filename) if not os.path.isdir(refs_path): + self._skipped.append("%s" % (doc_path)) print("Reference dir not found for %s, skipping (%d/%d)" % (doc_path, n_doc, total_docs)) return @@ -156,5 +161,6 @@ class TestRun: report_tests(self._crashed, "crashed") report_tests(self._failed_status_error, "failed to run") report_tests(self._stderr, "have stderr output") + report_tests(self._skipped, "skipped") diff --git a/regtest/builder/__init__.py b/regtest/builder/__init__.py new file mode 100644 index 0000000..649c68d --- /dev/null +++ b/regtest/builder/__init__.py @@ -0,0 +1,86 @@ +# builder +# +# Copyright (C) 2012 Carlos Garcia Campos +# +# 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 of the License, 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 + +from Config import Config +import os +import sys +import subprocess + +__all__ = [ 'register_builder', + 'get_builder', + 'UnknownBuilderError', + 'Builder' ] + +class UnknownBuilderError(Exception): + '''Unknown builder''' + +class Builder: + + def __init__(self): + self.config = Config() + self._srcdir = self.config.src_dir + self._prefix = self.config.prefix + + def number_of_cpus(self): + if not sys.platform.startswith("linux"): + # TODO + return 0 + + n_cpus = subprocess.Popen(['nproc'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + if n_cpus: + return int(n_cpus) + + n_cpus = 0 + f = open('/proc/cpuinfo', 'r') + for line in f.readlines(): + if 'processor' in line: + n_cpus += 1 + f.close() + + return n_cpus + + def _configure(self): + raise NotImplementedError + + def _build(self): + raise NotImplementedError + + def build(self): + self._configure() + self._build() + + +_builders = {} +def register_builder(builder_name, builder_class): + _builders[builder_name] = builder_class + +def _get_builder(builder_name): + if builder_name not in _builders: + try: + __import__('builder.%s' % builder_name) + except ImportError: + pass + + if builder_name not in _builders: + raise UnknownBuilderError('Invalid %s builder' % builder_name) + + return _builders[builder_name] + +def get_builder(builder_name): + builder_class = _get_builder(builder_name) + return builder_class() diff --git a/regtest/builder/autotools.py b/regtest/builder/autotools.py new file mode 100644 index 0000000..4dd0565 --- /dev/null +++ b/regtest/builder/autotools.py @@ -0,0 +1,63 @@ +# autotools.py +# +# Copyright (C) 2012 Carlos Garcia Campos +# +# 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 of the License, 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 + +from builder import Builder, register_builder +import os +import subprocess + +class Autotools(Builder): + + def _configure(self): + autogen = os.path.join(self._srcdir, 'autogen.sh') + cmd = [autogen, '--prefix=%s' % (self._prefix), '--enable-utils'] + + # Disable frontends and tests + cmd.extend(['--disable-poppler-glib', + '--disable-poppler-qt4', + '--disable-poppler-cpp', + '--disable-gtk-test']) + + backends = self.config.backends + if backends: + # Disable backends. Text and ps can't be disabled. + if 'cairo' not in backends: + cmd.append('--disable-cairo-output') + if 'splash' not in backends: + cmd.append('--disable-splash-output') + else: + cmd.extend(['--enable-cairo-output', + '--enable-splash-output']) + + p = subprocess.Popen(cmd, cwd=self._srcdir) + status = p.wait() + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) + + def _build(self): + make = os.environ.get('MAKE', 'make') + cmd = [make] + n_cpus = self.number_of_cpus() + if n_cpus: + cmd.append('-j%d' % (n_cpus)) + + p = subprocess.Popen(cmd, cwd=self._srcdir) + status = p.wait() + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) + +register_builder('autotools', Autotools) diff --git a/regtest/commands/find-regression.py b/regtest/commands/find-regression.py new file mode 100644 index 0000000..1a46eee --- /dev/null +++ b/regtest/commands/find-regression.py @@ -0,0 +1,77 @@ +# find-regression.py +# +# Copyright (C) 2012 Carlos Garcia Campos +# +# 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 of the License, 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 + +from commands import Command, register_command +from Bisect import Bisect +from Timer import Timer +from Config import Config +import os +import tempfile + +class FindRegression(Command): + + name = 'find-regression' + usage_args = '[ options ... ] test ' + description = 'Find revision that introduced a regression for the given test' + + def __init__(self): + Command.__init__(self) + parser = self._get_args_parser() + parser.add_argument('--refs-dir', + action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), + help = 'Directory containing the references') + parser.add_argument('-o', '--out-dir', + action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), + help = 'Directory containing the results') + parser.add_argument('--src-dir', + action = 'store', dest = 'src_dir', default = os.path.abspath("../"), + help = 'Directory of poppler sources') + parser.add_argument('--builder', + action = 'store', dest = 'builder', + choices=['autotools'], default = 'autotools', + help = 'Build system to use') + parser.add_argument('--prefix', + action = 'store', dest = 'prefix', default = '/usr/local', + help = 'Build prefix') + parser.add_argument('--good', + action = 'store', dest = 'good', metavar = 'REVISION', + help = 'Good revision') + parser.add_argument('--bad', + action = 'store', dest = 'bad', default = 'HEAD', metavar = 'REVISION', + help = 'Bad revision') + parser.add_argument('test') + + def run(self, options): + config = Config() + config.src_dir = options['src_dir'] + config.builder = options['builder'] + config.prefix = options['prefix'] + config.good = options['good'] + config.bad = options['bad'] + + doc = options['test'] + if not os.path.isfile(doc): + print("Invalid test %s: not a regulat file" % (doc)) + return + + t = Timer() + bisect = Bisect(options['test'], options['refs_dir'], options['out_dir']) + bisect.run() + print("Tests run in %s" % (t.elapsed_str())) + +register_command('find-regression', FindRegression)