[Okular-devel] Add Form-Reset and Print (button) functionality

Guillermo Amaral gamaral at kdab.com
Sat Jun 12 06:41:18 CEST 2010


Hi Guys,

I wanted to submit these patches, they make the print and form-reset buttons
work on PDF forms. :)

You will also need the poppler side of the patches for this to work, I have
sent them to the poppler mailing list already, but I will also add them here
for good measure (since I know some of you work in poppler too).

Any questions, look me up in IRC or make sure you RE to this email address.

Cheers,
GA

-- 
Guillermo Amaral | guillermo.amaral at kdab.com | Software Desperado
Klarälvdalens Datakonsult AB, a KDAB Group company
Tel. Sweden (HQ) +46-563-540090, USA +1-866-777-KDAB(5322)
KDAB - Qt Experts - Platform-independent software solutions

-------------- next part --------------
diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h
index 6d42cbe..48b558d 100644
--- a/qt4/src/poppler-link.h
+++ b/qt4/src/poppler-link.h
@@ -357,7 +357,8 @@ class POPPLER_QT4_EXPORT LinkAction : public Link
 		                  EndPresentation = 9,
 		                  Find = 10,
 		                  GoToPage = 11,
-		                  Close = 12 };
+		                  Close = 12,
+		                  Print = 13 };
 
 		/**
 		 * The action of the current LinkAction
diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc
index 6dbf50f..8aea6ce 100644
--- a/qt4/src/poppler-page.cc
+++ b/qt4/src/poppler-page.cc
@@ -129,6 +129,8 @@ Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDo
         popplerLink = new LinkAction( linkArea, LinkAction::Find );
       else if ( !strcmp( name, "FullScreen" ) )
         popplerLink = new LinkAction( linkArea, LinkAction::Presentation );
+      else if ( !strcmp( name, "Print" ) )
+        popplerLink = new LinkAction( linkArea, LinkAction::Print );
       else if ( !strcmp( name, "Close" ) )
       {
         // acroread closes the document always, doesnt care whether 
-------------- next part --------------
diff --git a/okular/core/action.cpp b/okular/core/action.cpp
index b047ddf..5b99ca1 100644
--- a/okular/core/action.cpp
+++ b/okular/core/action.cpp
@@ -267,6 +267,8 @@ QString DocumentAction::actionTip() const
             return i18n( "Find..." );
         case GoToPage:
             return i18n( "Go To Page..." );
+        case Print:
+            return i18n( "Print" );
         case Close:
         default: ;
     }
diff --git a/okular/core/action.h b/okular/core/action.h
index c45a13d..6b3dd20 100644
--- a/okular/core/action.h
+++ b/okular/core/action.h
@@ -256,7 +256,8 @@ class OKULAR_EXPORT DocumentAction : public Action
             EndPresentation = 9,  ///< End presentation
             Find = 10,            ///< Open find dialog
             GoToPage = 11,        ///< Goto page
-            Close = 12            ///< Close document
+            Close = 12,           ///< Close document
+            Print = 13            ///< Print document
         };
 
         /**
diff --git a/okular/core/document.cpp b/okular/core/document.cpp
index e5faa0e..5fd8e56 100644
--- a/okular/core/document.cpp
+++ b/okular/core/document.cpp
@@ -2905,6 +2905,9 @@ void Document::processAction( const Action * action )
                 case DocumentAction::GoToPage:
                     emit linkGoToPage();
                     break;
+                case DocumentAction::Print:
+                    emit linkPrint();
+                    break;
                 case DocumentAction::Close:
                     emit close();
                     break;
diff --git a/okular/core/document.h b/okular/core/document.h
index 14db2ee..e27c331 100644
--- a/okular/core/document.h
+++ b/okular/core/document.h
@@ -662,6 +662,12 @@ class OKULAR_EXPORT Document : public QObject
         void linkEndPresentation();
 
         /**
+         * This signal is emitted whenever an action requests a
+         * print operation.
+         */
+        void linkPrint();
+
+        /**
          * This signal is emitted whenever an action requests an
          * open url operation for the given document @p url.
          */
diff --git a/okular/part.cpp b/okular/part.cpp
index f031a7f..7a5d085 100644
--- a/okular/part.cpp
+++ b/okular/part.cpp
@@ -267,6 +267,7 @@ m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args
     connect( m_document, SIGNAL( linkGoToPage() ), this, SLOT( slotGoToPage() ) );
     connect( m_document, SIGNAL( linkPresentation() ), this, SLOT( slotShowPresentation() ) );
     connect( m_document, SIGNAL( linkEndPresentation() ), this, SLOT( slotHidePresentation() ) );
+    connect( m_document, SIGNAL( linkPrint() ), this, SLOT( slotPrint() ) );
     connect( m_document, SIGNAL( openUrl(const KUrl &) ), this, SLOT( openUrlFromDocument(const KUrl &) ) );
     connect( m_document->bookmarkManager(), SIGNAL( openUrl(const KUrl &) ), this, SLOT( openUrlFromBookmarks(const KUrl &) ) );
     connect( m_document, SIGNAL( close() ), this, SLOT( close() ) );
-------------- next part --------------
diff --git a/poppler/Link.cc b/poppler/Link.cc
index 5d7b779..9c51f27 100644
--- a/poppler/Link.cc
+++ b/poppler/Link.cc
@@ -124,6 +124,10 @@ LinkAction *LinkAction::parseAction(Object *obj, GooString *baseURI) {
   } else if (obj2.isName("SetOCGState")) {
     action = new LinkOCGState(obj);
 
+  // ResetForm action
+  } else if (obj2.isName("ResetForm")) {
+    action = new LinkResetForm(obj2.getName());
+
   // unknown action
   } else if (obj2.isName()) {
     action = new LinkUnknown(obj2.getName());
@@ -856,6 +860,18 @@ LinkOCGState::StateList::~StateList() {
 }
 
 //------------------------------------------------------------------------
+// LinkFormClear
+//------------------------------------------------------------------------
+
+LinkResetForm::LinkResetForm(char *actionA) {
+  action = new GooString(actionA);
+}
+
+LinkResetForm::~LinkResetForm() {
+  delete action;
+}
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
diff --git a/poppler/Link.h b/poppler/Link.h
index ea10375..962d596 100644
--- a/poppler/Link.h
+++ b/poppler/Link.h
@@ -53,6 +53,7 @@ enum LinkActionKind {
   actionSound,			// sound action
   actionJavaScript,		// JavaScript action
   actionOCGState,               // Set-OCG-State action
+  actionResetForm,              // Reset form action
   actionUnknown			// anything else
 };
 
@@ -429,6 +430,31 @@ private:
 };
 
 //------------------------------------------------------------------------
+// LinkResetForm
+//------------------------------------------------------------------------
+
+class LinkResetForm: public LinkAction {
+public:
+
+  // Build a LinkResetForm with the specified action type.
+  LinkResetForm(char *actionA);
+
+  // Destructor.
+  virtual ~LinkResetForm();
+
+  // Was the LinkResetForm create successfully?
+  virtual GBool isOk() { return action != NULL; }
+
+  // Accessors.
+  virtual LinkActionKind getKind() { return actionResetForm; }
+  GooString *getAction() { return action; }
+
+private:
+
+  GooString *action;		// action subtype
+};
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
diff --git a/qt4/src/poppler-form.cc b/qt4/src/poppler-form.cc
index 6224b68..4ce4465 100644
--- a/qt4/src/poppler-form.cc
+++ b/qt4/src/poppler-form.cc
@@ -139,14 +139,24 @@ Link* FormField::activationAction() const
   Object tmp;
   Object *obj = m_formData->fm->getObj();
   Link* action = 0;
+  ::LinkAction *act = 0;
+
   if (obj->dictLookup("A", &tmp)->isDict())
   {
-    ::LinkAction *act = ::LinkAction::parseAction(&tmp, m_formData->doc->doc->getCatalog()->getBaseURI());
-    if (act)
-    {
-      action = PageData::convertLinkActionToLink(act, m_formData->doc, QRectF());
-      delete act;
-    }
+    act = ::LinkAction::parseAction(&tmp, m_formData->doc->doc->getCatalog()->getBaseURI());
+  }
+  else if (obj->dictLookup("AA", &tmp)->isDict())
+  {
+    Object tmp1;
+    if (tmp.dictLookup("D", &tmp1)->isDict())
+      act = ::LinkAction::parseAction(&tmp1, m_formData->doc->doc->getCatalog()->getBaseURI());
+    tmp1.free();
+  }
+
+  if (act)
+  {
+    action = PageData::convertLinkActionToLink(act, m_formData->doc, QRectF());
+    delete act;
   }
   tmp.free();
   return action;
diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc
index de06242..80c390c 100644
--- a/qt4/src/poppler-link.cc
+++ b/qt4/src/poppler-link.cc
@@ -179,6 +179,19 @@ class LinkMoviePrivate : public LinkPrivate
 	}
 #endif
 
+class LinkFormActionPrivate : public LinkPrivate
+{
+	public:
+		LinkFormActionPrivate( const QRectF &area );
+
+		LinkFormAction::ActionType type;
+};
+
+	LinkFormActionPrivate::LinkFormActionPrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
 	static void cvtUserToDev(::Page *page, double xu, double yu, int *xd, int *yd) {
 		double ctm[6];
 		
@@ -581,4 +594,27 @@ class LinkMoviePrivate : public LinkPrivate
 	}
 #endif
 
+	// LinkFormAction
+	LinkFormAction::LinkFormAction( const QRectF &linkArea, ActionType actionType )
+		: Link( *new LinkFormActionPrivate( linkArea ) )
+	{
+		Q_D( LinkFormAction );
+		d->type = actionType;
+	}
+		
+	LinkFormAction::~LinkFormAction()
+	{
+	}
+	
+	LinkFormAction::ActionType LinkFormAction::actionType() const
+	{
+		Q_D( const LinkFormAction );
+		return d->type;
+	}
+
+	Link::LinkType LinkFormAction::linkType() const
+	{
+		return FormAction;
+	}
+
 }
diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h
index 6d42cbe..f037642 100644
--- a/qt4/src/poppler-link.h
+++ b/qt4/src/poppler-link.h
@@ -39,6 +39,7 @@ class LinkJavaScriptPrivate;
 class LinkMoviePrivate;
 class LinkDestinationData;
 class LinkDestinationPrivate;
+class LinkFormActionPrivate;
 class SoundObject;
 
 /**
@@ -182,7 +183,8 @@ 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
-		    JavaScript    ///< A JavaScript code to be interpreted \since 0.10
+		    JavaScript,   ///< A JavaScript code to be interpreted \since 0.10
+		    FormAction    ///< A "form" action to be executed in the viewer
 		};
 
 		/**
@@ -484,6 +486,46 @@ class POPPLER_QT4_EXPORT LinkMovie : public Link
 };
 #endif
 
+/**
+ * \brief "Form" action request.
+ *
+ * The LinkFormAction class represents a link that request a "form" action
+ * to be performed by the viewer on the displayed document.
+ */
+class POPPLER_QT4_EXPORT LinkFormAction : public Link
+{
+	public:
+		/**
+		 * The possible types of actions
+		 */
+		enum ActionType { Submit = 1,
+		                  Reset = 2,
+		                  ImportData = 3 };
+
+		/**
+		 * The action of the current LinkFormAction
+		 */
+		ActionType actionType() const;
+
+		/**
+		 * Create a new Action link, that executes a specified action
+		 * on the document.
+		 *
+		 * \param linkArea the active area of the link
+		 * \param actionType which action should be executed
+		 */
+		LinkFormAction( const QRectF &linkArea, ActionType actionType );
+		/**
+		 * Destructor.
+		 */
+		~LinkFormAction();
+		LinkType linkType() const;
+
+	private:
+		Q_DECLARE_PRIVATE( LinkFormAction )
+		Q_DISABLE_COPY( LinkFormAction )
+};
+
 }
 
 #endif
diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc
index 6dbf50f..b0b2871 100644
--- a/qt4/src/poppler-page.cc
+++ b/qt4/src/poppler-page.cc
@@ -174,6 +174,10 @@ Link* PageData::convertLinkActionToLink(::LinkAction * a, DocumentData *parentDo
           copyString( m_uri, m->getTitle()->getCString() );
 */  break;
 
+    case actionResetForm:
+        popplerLink = new LinkFormAction( linkArea, LinkFormAction::Reset );
+    break;
+
     case actionUnknown:
     break;
   }
-------------- next part --------------
diff --git a/okular/core/action.cpp b/okular/core/action.cpp
index b047ddf..7a489ed 100644
--- a/okular/core/action.cpp
+++ b/okular/core/action.cpp
@@ -429,3 +429,54 @@ QString MovieAction::actionTip() const
     return i18n( "Play movie..." );
 }
 #endif
+
+// FormAction
+
+class Okular::FormActionPrivate : public Okular::ActionPrivate
+{
+    public:
+        FormActionPrivate( enum FormAction::FormActionType formActionType )
+            : ActionPrivate(), m_type( formActionType )
+        {
+        }
+
+        FormAction::FormActionType m_type;
+};
+
+FormAction::FormAction( enum FormActionType formActionType )
+    : Action( *new FormActionPrivate( formActionType ) )
+{
+}
+
+FormAction::~FormAction()
+{
+}
+
+FormAction::FormActionType FormAction::formActionType() const
+{
+    Q_D( const Okular::FormAction );
+    return d->m_type;
+}
+
+Action::ActionType FormAction::actionType() const
+{
+    return FrmAction;
+}
+
+QString FormAction::actionTip() const
+{
+    Q_D( const Okular::FormAction );
+    switch ( d->m_type )
+    {
+        case Submit:
+            return i18n( "Submit" );
+        case Reset:
+            return i18n( "Reset" );
+        case ImportData:
+            return i18n( "Import Data" );
+        default: ;
+    }
+
+    return QString();
+}
+
diff --git a/okular/core/action.h b/okular/core/action.h
index c45a13d..b9054d9 100644
--- a/okular/core/action.h
+++ b/okular/core/action.h
@@ -27,6 +27,7 @@ class ScriptActionPrivate;
 class MovieActionPrivate;
 class Sound;
 class DocumentViewport;
+class FormActionPrivate;
 
 /**
  * @short Encapsulates data that describes an action.
@@ -48,7 +49,8 @@ class OKULAR_EXPORT Action
             DocAction,  ///< Start a custom action
             Sound,      ///< Play a sound
             Movie,      ///< Play a movie
-            Script      ///< Executes a Script code
+            Script,     ///< Executes a Script code
+            FrmAction   ///< Start a form action
         };
 
         /**
@@ -431,6 +433,56 @@ class MovieAction : public Action
 };
 #endif
 
+/**
+ * The FormAction action contains an action that is performed on
+ * the current document.
+ */
+class OKULAR_EXPORT FormAction : public Action
+{
+    public:
+        /**
+         * Describes the possible action types.
+         *
+         * WARNING KEEP IN SYNC WITH POPPLER!
+         */
+        enum FormActionType {
+            Submit = 1,        ///< Submit form data
+            Reset = 2,         ///< Reset form data
+            ImportData = 3     ///< Import data into form
+        };
+
+        /**
+         * Creates a new document action.
+         *
+         * @param documentActionType The type of document action.
+         */
+        explicit FormAction( enum FormActionType formActionType );
+
+        /**
+         * Destroys the document action.
+         */
+        virtual ~FormAction();
+
+        /**
+         * Returns the action type.
+         */
+        ActionType actionType() const;
+
+        /**
+         * Returns the action tip.
+         */
+        QString actionTip() const;
+
+        /**
+         * Returns the type of action.
+         */
+        FormActionType formActionType() const;
+
+    private:
+        Q_DECLARE_PRIVATE( FormAction )
+        Q_DISABLE_COPY( FormAction )
+};
+
 }
 
 #endif
diff --git a/okular/core/document.cpp b/okular/core/document.cpp
index e5faa0e..8f2ef04 100644
--- a/okular/core/document.cpp
+++ b/okular/core/document.cpp
@@ -73,6 +73,8 @@
 #include "utils_p.h"
 #include "view.h"
 #include "view_p.h"
+#include "form.h"
+#include "form_p.h"
 
 #include <memory>
 
@@ -2963,6 +2965,22 @@ void Document::processAction( const Action * action )
             //const MovieAction * movie = static_cast< const MovieAction * >( action );
             // TODO this (Movie action)
             break;
+
+        case Action::FrmAction: {
+            const FormAction * frmaction = static_cast< const FormAction * >( action );
+            switch( frmaction->formActionType() )
+            {
+                case FormAction::Submit:
+		    emit linkSubmitForm();
+                    break;
+                case FormAction::Reset:
+		    emit linkResetForm();
+		    break;
+                case FormAction::ImportData:
+		    emit linkImportFormData();
+                    break;
+            }
+            } break;
     }
 }
 
diff --git a/okular/core/document.h b/okular/core/document.h
index 14db2ee..3e48827 100644
--- a/okular/core/document.h
+++ b/okular/core/document.h
@@ -662,6 +662,24 @@ class OKULAR_EXPORT Document : public QObject
         void linkEndPresentation();
 
         /**
+         * This signal is emitted whenever an form-action requests a
+         * submit operation.
+         */
+        void linkSubmitForm();
+
+        /**
+         * This signal is emitted whenever an form-action requests a
+         * reset operation.
+         */
+        void linkResetForm();
+
+        /**
+         * This signal is emitted whenever an form-action requests a
+         * import data operation.
+         */
+        void linkImportFormData();
+
+        /**
          * This signal is emitted whenever an action requests an
          * open url operation for the given document @p url.
          */
diff --git a/okular/core/form.cpp b/okular/core/form.cpp
index b56071b..f13b866 100644
--- a/okular/core/form.cpp
+++ b/okular/core/form.cpp
@@ -50,6 +50,12 @@ FormField::FieldType FormField::type() const
     return d->m_type;
 }
 
+QString FormField::defaultValue() const
+{
+    Q_D( const FormField );
+    return d->m_default;
+}
+
 bool FormField::isReadOnly() const
 {
     return false;
diff --git a/okular/core/form.h b/okular/core/form.h
index 24e1901..df50905 100644
--- a/okular/core/form.h
+++ b/okular/core/form.h
@@ -60,6 +60,11 @@ class OKULAR_EXPORT FormField
         FieldType type() const;
 
         /**
+         * The fields default value.
+         */
+        QString defaultValue() const;
+
+        /**
          * The bouding rect of the field, in normalized coordinates.
          */
         virtual NormalizedRect rect() const = 0;
diff --git a/okular/generators/poppler/generator_pdf.cpp b/okular/generators/poppler/generator_pdf.cpp
index 0406bb1..4fd8d91 100644
--- a/okular/generators/poppler/generator_pdf.cpp
+++ b/okular/generators/poppler/generator_pdf.cpp
@@ -163,6 +163,7 @@ Okular::Action* createLinkFromPopplerLink(const Poppler::Link *popplerLink)
 #ifdef HAVE_POPPLER_0_9
 	const Poppler::LinkJavaScript *popplerLinkJS;
 #endif
+	const Poppler::LinkFormAction *popplerLinkFormAction;
 	Okular::DocumentViewport viewport;
 	
 	switch(popplerLink->linkType())
@@ -224,10 +225,14 @@ Okular::Action* createLinkFromPopplerLink(const Poppler::Link *popplerLink)
 		}
 		break;
 #endif
-		
 		case Poppler::Link::Movie:
 			// not implemented
 		break;
+
+		case Poppler::Link::FormAction:
+			popplerLinkFormAction = static_cast<const Poppler::LinkFormAction *>(popplerLink);
+			link = new Okular::FormAction( (Okular::FormAction::FormActionType)popplerLinkFormAction->actionType() );
+		break;
 	}
 	
 	return link;
diff --git a/okular/part.cpp b/okular/part.cpp
index f031a7f..abdd813 100644
--- a/okular/part.cpp
+++ b/okular/part.cpp
@@ -353,6 +353,7 @@ m_cliPresentation(false), m_embedMode(detectEmbedMode(parentWidget, parent, args
     connect( m_document, SIGNAL( error( const QString&, int ) ), m_pageView, SLOT( errorMessage( const QString&, int ) ) );
     connect( m_document, SIGNAL( warning( const QString&, int ) ), m_pageView, SLOT( warningMessage( const QString&, int ) ) );
     connect( m_document, SIGNAL( notice( const QString&, int ) ), m_pageView, SLOT( noticeMessage( const QString&, int ) ) );
+    connect( m_document, SIGNAL( linkResetForm() ), m_pageView, SLOT( slotResetFormWidgets() ) );
     rightLayout->addWidget( m_pageView );
     m_findBar = new FindBar( m_document, rightContainer );
     rightLayout->addWidget( m_findBar );
diff --git a/okular/ui/pageview.cpp b/okular/ui/pageview.cpp
index 48f899a..78c09b8 100644
--- a/okular/ui/pageview.cpp
+++ b/okular/ui/pageview.cpp
@@ -3543,6 +3543,44 @@ void PageView::slotAction( Okular::Action *action )
 {
     d->document->processAction( action );
 }
+
+void PageView::slotResetFormWidgets()
+{
+    QVector< PageViewItem * >::const_iterator dIt = d->items.constBegin(), dEnd = d->items.constEnd();
+    for ( ; dIt != dEnd; ++dIt )
+    {
+	QHash<int, FormWidgetIface*>& widgets = (*dIt)->formWidgets();
+	foreach( FormWidgetIface* fw, widgets.values() ) {
+	    Okular::FormField* ff = fw->formField();
+	    switch (ff->type()) {
+	    case Okular::FormField::FormText: {
+		Okular::FormFieldText* fft = static_cast<Okular::FormFieldText *>(ff);
+		FormLineEdit *le = dynamic_cast<FormLineEdit *>(fw);
+		TextAreaEdit *te = dynamic_cast<TextAreaEdit *>(fw);
+
+		if (!fft->isReadOnly()) {
+		    fft->setText( fft->defaultValue() );
+		    if (le) le->setText( fft->text() );
+		    if (te) te->setText( fft->text() );
+		}
+		} break;
+	    case Okular::FormField::FormButton: {
+		Okular::FormFieldButton* ffb = static_cast<Okular::FormFieldButton *>(ff);
+
+		if (!ffb->isReadOnly()) {
+		    ffb->setState( "true" == ffb->defaultValue() );
+		    QAbstractButton *be = dynamic_cast<QAbstractButton *>(fw);
+		    if (be) be->setChecked( ffb->state() );
+		}
+		} break;
+	    default:
+		kDebug() << "Unhandled form field: " << ff->name() << ff->defaultValue();
+		break;
+	    }
+	}
+    }
+}
+
 //END private SLOTS
 
 #include "pageview.moc"
diff --git a/okular/ui/pageview.h b/okular/ui/pageview.h
index cabd776..836c356 100644
--- a/okular/ui/pageview.h
+++ b/okular/ui/pageview.h
@@ -221,6 +221,7 @@ Q_OBJECT
         void slotSpeakCurrentPage();
         void slotStopSpeaks();
         void slotAction( Okular::Action *action );
+        void slotResetFormWidgets();
 };
 
 #endif


More information about the Okular-devel mailing list