[poppler] 2 commits - poppler/Annot.cc poppler/Annot.h poppler/Catalog.cc poppler/Catalog.h poppler/Form.cc poppler/Form.h poppler/Page.cc poppler/Page.h utils/CMakeLists.txt utils/JSInfo.cc utils/JSInfo.h utils/Makefile.am utils/pdfinfo.1 utils/pdfinfo.cc

Adrian Johnson ajohnson at kemper.freedesktop.org
Fri Aug 23 22:35:20 PDT 2013


 poppler/Annot.cc     |   27 +++++
 poppler/Annot.h      |    8 +
 poppler/Catalog.cc   |   26 +++++
 poppler/Catalog.h    |   13 ++
 poppler/Form.cc      |    4 
 poppler/Form.h       |    2 
 poppler/Page.cc      |   20 ++++
 poppler/Page.h       |   10 +-
 utils/CMakeLists.txt |    4 
 utils/JSInfo.cc      |  233 +++++++++++++++++++++++++++++++++++++++++++++++++++
 utils/JSInfo.h       |   60 +++++++++++++
 utils/Makefile.am    |    4 
 utils/pdfinfo.1      |    6 +
 utils/pdfinfo.cc     |   18 +++
 14 files changed, 434 insertions(+), 1 deletion(-)

New commits:
commit a47b7f853174d6101f2b882a2db1a7dc95b33293
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Aug 3 10:28:20 2013 +0930

    Add pdfinfo option to print out javascript

diff --git a/poppler/Catalog.h b/poppler/Catalog.h
index 0486bf0..eb1dd29 100644
--- a/poppler/Catalog.h
+++ b/poppler/Catalog.h
@@ -153,6 +153,7 @@ public:
 
   // Get the number of javascript scripts
   int numJS() { return getJSNameTree()->numEntries(); }
+  GooString *getJSName(int i) { return getJSNameTree()->getName(i); }
 
   // Get the i'th JavaScript script (at the Document level) in the document
   GooString *getJS(int i);
diff --git a/utils/JSInfo.cc b/utils/JSInfo.cc
index 6b66888..e3205c4 100644
--- a/utils/JSInfo.cc
+++ b/utils/JSInfo.cc
@@ -13,6 +13,7 @@
 
 
 #include "config.h"
+#include <stdio.h>
 #include "Object.h"
 #include "Dict.h"
 #include "Annot.h"
@@ -20,6 +21,8 @@
 #include "JSInfo.h"
 #include "Link.h"
 #include "Form.h"
+#include "UnicodeMap.h"
+#include "UTF.h"
 
 JSInfo::JSInfo(PDFDoc *docA, int firstPage) {
   doc = docA;
@@ -29,23 +32,68 @@ JSInfo::JSInfo(PDFDoc *docA, int firstPage) {
 JSInfo::~JSInfo() {
 }
 
+void JSInfo::printJS(GooString *js) {
+  Unicode *u;
+  char buf[8];
+  int i, n, len;
 
-void JSInfo::scanLinkAction(LinkAction *link) {
+  if (!js || !js->getCString())
+    return;
+
+  len = TextStringToUCS4(js, &u);
+  for (i = 0; i < len; i++) {
+    n = uniMap->mapUnicode(u[i], buf, sizeof(buf));
+    fwrite(buf, 1, n, file);
+  }
+}
+
+void JSInfo::scanLinkAction(LinkAction *link, const char *action) {
   if (!link)
     return;
 
   if (link->getKind() == actionJavaScript) {
     hasJS = gTrue;
+    if (print) {
+      LinkJavaScript *linkjs = static_cast<LinkJavaScript *>(link);
+      GooString *s = linkjs->getScript();
+      if (s && s->getCString()) {
+	fprintf(file, "%s:\n", action);
+	printJS(s);
+	fputs("\n\n", file);
+      }
+    }
   }
 
   if (link->getKind() == actionRendition) {
     LinkRendition *linkr = static_cast<LinkRendition *>(link);
-    if (linkr->getScript())
+    if (linkr->getScript()) {
       hasJS = gTrue;
+      if (print) {
+        GooString *s = linkr->getScript();
+        if (s && s->getCString()) {
+          fprintf(file, "%s (Rendition):\n", action);
+          printJS(s);
+          fputs("\n\n", file);
+        }
+      }
+    }
   }
 }
 
 void JSInfo::scanJS(int nPages) {
+  print = gFalse;
+  file = NULL;
+  scan(nPages);
+}
+
+void JSInfo::scanJS(int nPages, FILE *fout, UnicodeMap *uMap) {
+  print = gTrue;
+  file = fout;
+  uniMap = uMap;
+  scan(nPages);
+}
+
+void JSInfo::scan(int nPages) {
   Page *page;
   Annots *annots;
   Object obj1, obj2;
@@ -54,16 +102,29 @@ void JSInfo::scanJS(int nPages) {
   hasJS = gFalse;
 
   // Names
-  if (doc->getCatalog()->numJS() > 0) {
+  int numNames = doc->getCatalog()->numJS();
+  if (numNames > 0) {
     hasJS = gTrue;
+    if (print) {
+      for (int i = 0; i < numNames; i++) {
+	fprintf(file, "Name Dictionary \"%s\":\n", doc->getCatalog()->getJSName(i)->getCString());
+	printJS(doc->getCatalog()->getJS(i));
+	fputs("\n\n", file);
+      }
+    }
   }
 
   // document actions
-  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument));
-  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart));
-  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish));
-  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart));
-  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish));
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument),
+                 "Before Close Document");
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart),
+                 "Before Save Document");
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish),
+                 "After Save Document");
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart),
+                 "Before Print Document");
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish),
+                 "After Print Document");
 
   // form field actions
   if (doc->getCatalog()->getFormType() == Catalog::AcroForm) {
@@ -72,11 +133,16 @@ void JSInfo::scanJS(int nPages) {
       FormField *field = form->getRootField(i);
       for (int j = 0; j < field->getNumWidgets(); j++) {
 	FormWidget *widget = field->getWidget(j);
-	scanLinkAction(widget->getActivationAction());
-	scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified));
-	scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField));
-	scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField));
-	scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField));
+	scanLinkAction(widget->getActivationAction(),
+                       "Field Activated");
+	scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified),
+                       "Field Modified");
+	scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField),
+                       "Format Field");
+	scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField),
+                       "Validate Field");
+	scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField),
+                       "Calculate Field");
       }
     }
   }
@@ -97,42 +163,64 @@ void JSInfo::scanJS(int nPages) {
     if (!page) continue;
 
     // page actions (open, close)
-    scanLinkAction(page->getAdditionalAction(Page::actionOpenPage));
-    scanLinkAction(page->getAdditionalAction(Page::actionClosePage));
+    scanLinkAction(page->getAdditionalAction(Page::actionOpenPage), "Page Open");
+    scanLinkAction(page->getAdditionalAction(Page::actionClosePage), "Page Close");
 
     // annotation actions (links, screen, widget)
     annots = page->getAnnots();
     for (int i = 0; i < annots->getNumAnnots(); ++i) {
       if (annots->getAnnot(i)->getType() == Annot::typeLink) {
 	AnnotLink *annot = static_cast<AnnotLink *>(annots->getAnnot(i));
-	scanLinkAction(annot->getAction());
+	scanLinkAction(annot->getAction(), "Link Annotation Activated");
       } else if (annots->getAnnot(i)->getType() == Annot::typeScreen) {
 	AnnotScreen *annot = static_cast<AnnotScreen *>(annots->getAnnot(i));
-	scanLinkAction(annot->getAction());
-	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
+	scanLinkAction(annot->getAction(),
+                       "Screen Annotation Activated");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering),
+                       "Screen Annotation Cursor Enter");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving),
+                       "Screen Annotation Cursor Leave");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed),
+                       "Screen Annotation Mouse Pressed");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased),
+                       "Screen Annotation Mouse Released");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn),
+                       "Screen Annotation Focus In");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut),
+                       "Screen Annotation Focus Out");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening),
+                       "Screen Annotation Page Open");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing),
+                       "Screen Annotation Page Close");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible),
+                       "Screen Annotation Page Visible");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible),
+                       "Screen Annotation Page Invisible");
 
       } else if (annots->getAnnot(i)->getType() == Annot::typeWidget) {
 	AnnotWidget *annot = static_cast<AnnotWidget *>(annots->getAnnot(i));
-	scanLinkAction(annot->getAction());
-	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
-	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
+	scanLinkAction(annot->getAction(),
+                       "Widget Annotation Activated");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering),
+                       "Widget Annotation Cursor Enter");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving),
+                       "Widget Annotation Cursor Leave");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed),
+                       "Widget Annotation Mouse Pressed");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased),
+                       "Widget Annotation Mouse Released");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn),
+                       "Widget Annotation Focus In");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut),
+                       "Widget Annotation Focus Out");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening),
+                       "Widget Annotation Page Open");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing),
+                       "Widget Annotation Page Close");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible),
+                       "Widget Annotation Page Visible");
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible),
+                       "Widget Annotation Page Invisible");
       }
     }
   }
diff --git a/utils/JSInfo.h b/utils/JSInfo.h
index fed3f68..19b786f 100644
--- a/utils/JSInfo.h
+++ b/utils/JSInfo.h
@@ -14,11 +14,13 @@
 #ifndef JS_INFO_H
 #define JS_INFO_H
 
+#include <stdio.h>
 #include "Object.h"
 #include "PDFDoc.h"
 #include "goo/gtypes.h"
 
 #include "Link.h"
+#include "UnicodeMap.h"
 
 class PDFDoc;
 
@@ -34,6 +36,9 @@ public:
   // scan for JS in the PDF
   void scanJS(int nPages);
 
+  // scan and print JS in the PDF
+  void scanJS(int nPages, FILE *fout, UnicodeMap *uMap);
+
   // return true if PDF contains JavaScript
   GBool containsJS();
 
@@ -42,8 +47,13 @@ private:
   PDFDoc *doc;
   int currentPage;
   GBool hasJS;
+  GBool print;
+  FILE *file;
+  UnicodeMap *uniMap;
 
-  void scanLinkAction(LinkAction *link);
+  void scan(int nPages);
+  void scanLinkAction(LinkAction *link, const char *action);
+  void printJS(GooString *js);
 
 };
 
diff --git a/utils/pdfinfo.1 b/utils/pdfinfo.1
index 134bd3f..1dd7466 100644
--- a/utils/pdfinfo.1
+++ b/utils/pdfinfo.1
@@ -93,6 +93,9 @@ TrimBox, and ArtBox.
 Prints document-level metadata.  (This is the "Metadata" stream from
 the PDF file's Catalog object.)
 .TP
++.B \-js
++Prints all JavaScript in the PDF.
++.TP
 .B \-rawdates
 Prints the raw (undecoded) date strings, directly from the PDF file.
 .TP
diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc
index 902200f..0927855 100644
--- a/utils/pdfinfo.cc
+++ b/utils/pdfinfo.cc
@@ -64,6 +64,7 @@ static int firstPage = 1;
 static int lastPage = 0;
 static GBool printBoxes = gFalse;
 static GBool printMetadata = gFalse;
+static GBool printJS = gFalse;
 static GBool rawDates = gFalse;
 static char textEncName[128] = "";
 static char ownerPassword[33] = "\001";
@@ -81,6 +82,8 @@ static const ArgDesc argDesc[] = {
    "print the page bounding boxes"},
   {"-meta",   argFlag,     &printMetadata,    0,
    "print the document metadata (XML)"},
+  {"-js",     argFlag,     &printJS,          0,
+   "print all JavaScript in the PDF"},
   {"-rawdates", argFlag,   &rawDates,         0,
    "print the undecoded date strings directly from the PDF file"},
   {"-enc",    argString,   textEncName,    sizeof(textEncName),
@@ -383,6 +386,13 @@ int main(int argc, char *argv[]) {
     delete metadata;
   }
 
+  // print javascript
+  if (printJS) {
+    JSInfo jsInfo(doc, firstPage - 1);
+    fputs("\n", stdout);
+    jsInfo.scanJS(lastPage - firstPage + 1, stdout, uMap);
+  }
+
   exitCode = 0;
 
   // clean up
commit 8f7155e7e3180bb1966a5e7df6af6acdd479939b
Author: Adrian Johnson <ajohnson at redneon.com>
Date:   Sat Aug 3 09:05:21 2013 +0930

    pdfinfo: indicate if pdf contains javascript

diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 00291f8..bbe1e36 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -234,6 +234,28 @@ static LinkAction* getAdditionalAction(Annot::AdditionalActionsType type, Object
   return linkAction;
 }
 
+static LinkAction* getFormAdditionalAction(Annot::FormAdditionalActionsType type, Object *additionalActions, PDFDoc *doc) {
+  Object additionalActionsObject;
+  LinkAction *linkAction = NULL;
+
+  if (additionalActions->fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
+    const char *key = (type == Annot::actionFieldModified ?  "K" :
+                       type == Annot::actionFormatField ?    "F" :
+                       type == Annot::actionValidateField ?  "V" :
+                       type == Annot::actionCalculateField ? "C" : NULL);
+
+    Object actionObject;
+
+    if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
+      linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
+    actionObject.free();
+  }
+
+  additionalActionsObject.free();
+
+  return linkAction;
+}
+
 //------------------------------------------------------------------------
 // AnnotBorderEffect
 //------------------------------------------------------------------------
@@ -3911,6 +3933,11 @@ LinkAction* AnnotWidget::getAdditionalAction(AdditionalActionsType type)
   return ::getAdditionalAction(type, &additionalActions, doc);
 }
 
+LinkAction* AnnotWidget::getFormAdditionalAction(FormAdditionalActionsType type)
+{
+  return ::getFormAdditionalAction(type, &additionalActions, doc);
+}
+
 // Grand unified handler for preparing text strings to be drawn into form
 // fields.  Takes as input a text string (in PDFDocEncoding or UTF-16).
 // Converts some or all of this string to the appropriate encoding for the
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 2865d23..ef2b1d0 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -536,6 +536,13 @@ public:
     actionPageInvisible   ///< Performed when the page containing the annotation becomes invisible
   };
 
+  enum FormAdditionalActionsType {
+    actionFieldModified,   ///< Performed when the when the user modifies the field
+    actionFormatField,     ///< Performed before the field is formatted to display its value
+    actionValidateField,   ///< Performed when the field value changes
+    actionCalculateField,  ///< Performed when the field needs to be recalculated
+  };
+
   Annot(PDFDoc *docA, PDFRectangle *rectA);
   Annot(PDFDoc *docA, Dict *dict);
   Annot(PDFDoc *docA, Dict *dict, Object *obj);
@@ -1305,6 +1312,7 @@ public:
   AnnotAppearanceCharacs *getAppearCharacs() { return appearCharacs; }
   LinkAction *getAction() { return action; }
   LinkAction *getAdditionalAction(AdditionalActionsType type);
+  LinkAction *getFormAdditionalAction(FormAdditionalActionsType type);
   Dict *getParent() { return parent; }
 
 private:
diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index 25a8997..beb74c3 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -126,6 +126,9 @@ Catalog::Catalog(PDFDoc *docA) {
   }
   optContentProps.free();
 
+  // actions
+  catDict.dictLookupNF("AA", &additionalActions);
+
   // get the ViewerPreferences dictionary
   catDict.dictLookup("ViewerPreferences", &viewerPreferences);
   catDict.free();
@@ -181,6 +184,7 @@ Catalog::~Catalog() {
   outline.free();
   acroForm.free();
   viewerPreferences.free();
+  additionalActions.free();
 #if MULTITHREADED
   gDestroyMutex(&mutex);
 #endif
@@ -1062,3 +1066,25 @@ NameTree *Catalog::getJSNameTree()
   return jsNameTree;
 }
 
+LinkAction* Catalog::getAdditionalAction(DocumentAdditionalActionsType type) {
+  Object additionalActionsObject;
+  LinkAction *linkAction = NULL;
+
+  if (additionalActions.fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
+    const char *key = (type == actionCloseDocument ?       "WC" :
+                       type == actionSaveDocumentStart ?   "WS" :
+                       type == actionSaveDocumentFinish ?  "DS" :
+                       type == actionPrintDocumentStart ?  "WP" :
+                       type == actionPrintDocumentFinish ? "DP" : NULL);
+
+    Object actionObject;
+
+    if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
+      linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
+    actionObject.free();
+  }
+
+  additionalActionsObject.free();
+
+  return linkAction;
+}
diff --git a/poppler/Catalog.h b/poppler/Catalog.h
index a89d9aa..0486bf0 100644
--- a/poppler/Catalog.h
+++ b/poppler/Catalog.h
@@ -48,6 +48,7 @@ class Page;
 class PageAttrs;
 struct Ref;
 class LinkDest;
+class LinkAction;
 class PageLabelInfo;
 class Form;
 class OCGs;
@@ -202,6 +203,16 @@ public:
   PageMode getPageMode();
   PageLayout getPageLayout();
 
+  enum DocumentAdditionalActionsType {
+    actionCloseDocument,        ///< Performed before closing the document
+    actionSaveDocumentStart,    ///< Performed before saving the document
+    actionSaveDocumentFinish,   ///< Performed after saving the document
+    actionPrintDocumentStart,   ///< Performed before printing the document
+    actionPrintDocumentFinish,  ///< Performed after printing the document
+  };
+
+  LinkAction *getAdditionalAction(DocumentAdditionalActionsType type);
+
 private:
 
   // Get page label info.
@@ -237,6 +248,7 @@ private:
   PageLabelInfo *pageLabelInfo; // info about page labels
   PageMode pageMode;		// page mode
   PageLayout pageLayout;	// page layout
+  Object additionalActions;     // page additional actions
 
   GBool cachePageTree(int page); // Cache first <page> pages.
   Object *findDestInTree(Object *tree, GooString *name, Object *obj);
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 78c25e3..3070927 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -156,6 +156,10 @@ LinkAction *FormWidget::getActivationAction() {
   return widget ? widget->getAction() : NULL;
 }
 
+LinkAction *FormWidget::getAdditionalAction(Annot::FormAdditionalActionsType type) {
+  return widget ? widget->getFormAdditionalAction(type) : NULL;
+}
+
 FormWidgetButton::FormWidgetButton (PDFDoc *docA, Object *aobj, unsigned num, Ref ref, FormField *p) :
 	FormWidget(docA, aobj, num, ref, p)
 {
diff --git a/poppler/Form.h b/poppler/Form.h
index ef67748..6cc54a9 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -21,6 +21,7 @@
 #endif
 
 #include "Object.h"
+#include "Annot.h"
 
 #include <set>
 
@@ -101,6 +102,7 @@ public:
   bool isReadOnly() const;
 
   LinkAction *getActivationAction();
+  LinkAction *getAdditionalAction(Annot::FormAdditionalActionsType type);
 
   // return the unique ID corresponding to pageNum/fieldNum
   static int encodeID (unsigned pageNum, unsigned fieldNum);
diff --git a/poppler/Page.cc b/poppler/Page.cc
index e0a3b29..7825f80 100644
--- a/poppler/Page.cc
+++ b/poppler/Page.cc
@@ -823,3 +823,23 @@ void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,
   }
  delete state;
 }
+
+LinkAction* Page::getAdditionalAction(PageAdditionalActionsType type) {
+  Object additionalActionsObject;
+  LinkAction *linkAction = NULL;
+
+  if (actions.fetch(doc->getXRef(), &additionalActionsObject)->isDict()) {
+    const char *key = (type == actionOpenPage ?  "O" :
+                       type == actionClosePage ? "C" : NULL);
+
+    Object actionObject;
+
+    if (additionalActionsObject.dictLookup(key, &actionObject)->isDict())
+      linkAction = LinkAction::parseAction(&actionObject, doc->getCatalog()->getBaseURI());
+    actionObject.free();
+  }
+
+  additionalActionsObject.free();
+
+  return linkAction;
+}
diff --git a/poppler/Page.h b/poppler/Page.h
index 1c9d0a9..95adf3a 100644
--- a/poppler/Page.h
+++ b/poppler/Page.h
@@ -44,6 +44,7 @@ class PDFDoc;
 class XRef;
 class OutputDev;
 class Links;
+class LinkAction;
 class Annots;
 class Annot;
 class Gfx;
@@ -211,6 +212,13 @@ public:
   // Get actions
   Object *getActions(Object *obj) { return actions.fetch(xref, obj); }
 
+  enum PageAdditionalActionsType {
+    actionOpenPage,     ///< Performed when opening the page
+    actionClosePage,    ///< Performed when closing the page
+  };
+
+  LinkAction *getAdditionalAction(PageAdditionalActionsType type);
+
   Gfx *createGfx(OutputDev *out, double hDPI, double vDPI,
 		 int rotate, GBool useMediaBox, GBool crop,
 		 int sliceX, int sliceY, int sliceW, int sliceH,
@@ -267,7 +275,7 @@ private:
   Object contents;		// page contents
   Object thumb;			// page thumbnail
   Object trans;			// page transition
-  Object actions;		// page addiction actions
+  Object actions;		// page additional actions
   double duration;              // page duration
   GBool ok;			// true if page is valid
 #if MULTITHREADED
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index f82cfa4..2f04b39 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -69,6 +69,8 @@ set(pdfimages_SOURCES ${common_srcs}
   pdfimages.cc
   ImageOutputDev.cc
   ImageOutputDev.h
+  JSInfo.cc
+  JSInfo.h
 )
 add_executable(pdfimages ${pdfimages_SOURCES})
 target_link_libraries(pdfimages ${common_libs})
@@ -78,6 +80,8 @@ install(FILES pdfimages.1 DESTINATION share/man/man1)
 # pdfinfo
 set(pdfinfo_SOURCES ${common_srcs}
   pdfinfo.cc printencodings.cc
+  JSInfo.cc
+  JSInfo.h
 )
 add_executable(pdfinfo ${pdfinfo_SOURCES})
 target_link_libraries(pdfinfo ${common_libs})
diff --git a/utils/JSInfo.cc b/utils/JSInfo.cc
new file mode 100644
index 0000000..6b66888
--- /dev/null
+++ b/utils/JSInfo.cc
@@ -0,0 +1,145 @@
+//========================================================================
+//
+// JSInfo.cc
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright (C) 2013 Adrian Johnson <ajohnson at redneon.com>
+//
+// 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
+//
+//========================================================================
+
+
+#include "config.h"
+#include "Object.h"
+#include "Dict.h"
+#include "Annot.h"
+#include "PDFDoc.h"
+#include "JSInfo.h"
+#include "Link.h"
+#include "Form.h"
+
+JSInfo::JSInfo(PDFDoc *docA, int firstPage) {
+  doc = docA;
+  currentPage = firstPage + 1;
+}
+
+JSInfo::~JSInfo() {
+}
+
+
+void JSInfo::scanLinkAction(LinkAction *link) {
+  if (!link)
+    return;
+
+  if (link->getKind() == actionJavaScript) {
+    hasJS = gTrue;
+  }
+
+  if (link->getKind() == actionRendition) {
+    LinkRendition *linkr = static_cast<LinkRendition *>(link);
+    if (linkr->getScript())
+      hasJS = gTrue;
+  }
+}
+
+void JSInfo::scanJS(int nPages) {
+  Page *page;
+  Annots *annots;
+  Object obj1, obj2;
+  int lastPage;
+
+  hasJS = gFalse;
+
+  // Names
+  if (doc->getCatalog()->numJS() > 0) {
+    hasJS = gTrue;
+  }
+
+  // document actions
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument));
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart));
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish));
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart));
+  scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish));
+
+  // form field actions
+  if (doc->getCatalog()->getFormType() == Catalog::AcroForm) {
+    Form *form = doc->getCatalog()->getForm();
+    for (int i = 0; i < form->getNumFields(); i++) {
+      FormField *field = form->getRootField(i);
+      for (int j = 0; j < field->getNumWidgets(); j++) {
+	FormWidget *widget = field->getWidget(j);
+	scanLinkAction(widget->getActivationAction());
+	scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified));
+	scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField));
+	scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField));
+	scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField));
+      }
+    }
+  }
+
+  // scan pages
+
+  if (currentPage > doc->getNumPages()) {
+    return;
+  }
+
+  lastPage = currentPage + nPages;
+  if (lastPage > doc->getNumPages() + 1) {
+    lastPage = doc->getNumPages() + 1;
+  }
+
+  for (int pg = currentPage; pg < lastPage; ++pg) {
+    page = doc->getPage(pg);
+    if (!page) continue;
+
+    // page actions (open, close)
+    scanLinkAction(page->getAdditionalAction(Page::actionOpenPage));
+    scanLinkAction(page->getAdditionalAction(Page::actionClosePage));
+
+    // annotation actions (links, screen, widget)
+    annots = page->getAnnots();
+    for (int i = 0; i < annots->getNumAnnots(); ++i) {
+      if (annots->getAnnot(i)->getType() == Annot::typeLink) {
+	AnnotLink *annot = static_cast<AnnotLink *>(annots->getAnnot(i));
+	scanLinkAction(annot->getAction());
+      } else if (annots->getAnnot(i)->getType() == Annot::typeScreen) {
+	AnnotScreen *annot = static_cast<AnnotScreen *>(annots->getAnnot(i));
+	scanLinkAction(annot->getAction());
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
+
+      } else if (annots->getAnnot(i)->getType() == Annot::typeWidget) {
+	AnnotWidget *annot = static_cast<AnnotWidget *>(annots->getAnnot(i));
+	scanLinkAction(annot->getAction());
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
+	scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible));
+      }
+    }
+  }
+
+  currentPage = lastPage;
+}
+
+GBool JSInfo::containsJS() {
+  return hasJS;
+};
diff --git a/utils/JSInfo.h b/utils/JSInfo.h
new file mode 100644
index 0000000..fed3f68
--- /dev/null
+++ b/utils/JSInfo.h
@@ -0,0 +1,50 @@
+//========================================================================
+//
+// JSInfo.h
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright (C) 2013 Adrian Johnson <ajohnson at redneon.com>
+//
+// 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
+//
+//========================================================================
+
+#ifndef JS_INFO_H
+#define JS_INFO_H
+
+#include "Object.h"
+#include "PDFDoc.h"
+#include "goo/gtypes.h"
+
+#include "Link.h"
+
+class PDFDoc;
+
+class JSInfo {
+public:
+
+  // Constructor.
+  JSInfo(PDFDoc *doc, int firstPage = 0);
+
+  // Destructor.
+  ~JSInfo();
+
+  // scan for JS in the PDF
+  void scanJS(int nPages);
+
+  // return true if PDF contains JavaScript
+  GBool containsJS();
+
+private:
+
+  PDFDoc *doc;
+  int currentPage;
+  GBool hasJS;
+
+  void scanLinkAction(LinkAction *link);
+
+};
+
+#endif
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 0c95441..1dd9a12 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -88,12 +88,16 @@ pdfimages_SOURCES =				\
 	pdfimages.cc				\
 	ImageOutputDev.cc			\
 	ImageOutputDev.h			\
+	JSInfo.cc				\
+	JSInfo.h				\
 	$(common)
 
 pdfinfo_SOURCES =				\
 	pdfinfo.cc				\
 	printencodings.cc			\
 	printencodings.h			\
+	JSInfo.cc				\
+	JSInfo.h				\
 	$(common)
 
 pdftops_SOURCES =				\
diff --git a/utils/pdfinfo.1 b/utils/pdfinfo.1
index a3ad1c3..134bd3f 100644
--- a/utils/pdfinfo.1
+++ b/utils/pdfinfo.1
@@ -48,6 +48,9 @@ tagged (yes/no)
 form (AcroForm / XFA / none)
 .RE
 .RS
+javascript (yes/no)
+.RE
+.RS
 page count
 .RE
 .RS
diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc
index f297614..902200f 100644
--- a/utils/pdfinfo.cc
+++ b/utils/pdfinfo.cc
@@ -53,6 +53,7 @@
 #include "UTF.h"
 #include "Error.h"
 #include "DateInfo.h"
+#include "JSInfo.h"
 
 static void printInfoString(Dict *infoDict, const char *key, const char *text,
 			    UnicodeMap *uMap);
@@ -246,6 +247,13 @@ int main(int argc, char *argv[]) {
       break;
   }
 
+  // print javascript info
+  {
+    JSInfo jsInfo(doc, firstPage - 1);
+    jsInfo.scanJS(lastPage - firstPage + 1);
+    printf("JavaScript:     %s\n", jsInfo.containsJS() ? "yes" : "no");
+  }
+
   // print page count
   printf("Pages:          %d\n", doc->getNumPages());
 


More information about the poppler mailing list