Hi,

I know, this would be post-0.8 material, but I'm posting it now to review it.

The following two patches start adding bits for make poppler able to read the 
JavaScript stuff in PDFs.

- poppler-jsnametree.diff
This patch adds the support in Catalog for reading the JS name tree, as 
defined in section 3.6.3 (table 3.28) of the PDF 1.7 specs.

- poppler-linkjavascript.diff
This patch adds the LinkJavaScript link type and make it built, as defined in 
section 6.8.4 of the PDF 1.7 specs.

- poppler-qt4-js.diff
This patch applies both the improvements of the former two patches to the Qt4 
frontend. (Can be splitted, if needed.)

These patches do not complete the support for JavaScript (reading) in the 
core, but they should cover a good percentage of the use cases (especially 
the Link one).

Comments? Doubts? Ideas?

-- 
Pino Toscano
diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index ea786c5..7858247 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -114,6 +114,9 @@ Catalog::Catalog(XRef *xrefA) {
     obj.dictLookup("EmbeddedFiles", &obj2);
     embeddedFileNameTree.init(xref, &obj2);
     obj2.free();
+    obj.dictLookup("JavaScript", &obj2);
+    jsNameTree.init(xref, &obj2);
+    obj2.free();
   }
   obj.free();
 
@@ -211,6 +214,7 @@ Catalog::~Catalog() {
   dests.free();
   destNameTree.free();
   embeddedFileNameTree.free();
+  jsNameTree.free();
   if (baseURI) {
     delete baseURI;
   }
@@ -484,6 +488,41 @@ EmbFile *Catalog::embeddedFile(int i)
     return embeddedFile;
 }
 
+GooString *Catalog::getJS(int i)
+{
+  Object obj = jsNameTree.getValue(i);
+  if (obj.isRef()) {
+    Ref r = obj.getRef();
+    obj.free();
+    xref->fetch(r.num, r.gen, &obj);
+  }
+
+  if (!obj.isDict()) {
+    obj.free();
+    return 0;
+  }
+  Object obj2;
+  if (!obj.dictLookup("S", &obj2)->isName()) {
+    obj2.free();
+    obj.free();
+    return 0;
+  }
+  if (strcmp(obj2.getName(), "JavaScript")) {
+    obj2.free();
+    obj.free();
+    return 0;
+  }
+  if (!obj.dictLookup("JS", &obj2)->isString()) {
+    obj2.free();
+    obj.free();
+    return 0;
+  }
+  GooString *js = new GooString(obj2.getString());
+  obj2.free();
+  obj.free();
+  return js;
+}
+
 NameTree::NameTree()
 {
   size = 0;
diff --git a/poppler/Catalog.h b/poppler/Catalog.h
index 97fa08d..b06b13d 100644
--- a/poppler/Catalog.h
+++ b/poppler/Catalog.h
@@ -160,6 +160,12 @@ public:
   // Get the i'th file embedded (at the Document level) in the document
   EmbFile *embeddedFile(int i);
 
+  // Get the number of javascript scripts
+  int numJS() { return jsNameTree.numEntries(); }
+
+  // Get the i'th JavaScript script (at the Document level) in the document
+  GooString *getJS(int i);
+
   // Convert between page indices and page labels.
   GBool labelToIndex(GooString *label, int *index);
   GBool indexToLabel(int index, GooString *label);
@@ -205,6 +211,7 @@ private:
   Object dests;			// named destination dictionary
   NameTree destNameTree;	// named destination name-tree
   NameTree embeddedFileNameTree;  // embedded file name-tree
+  NameTree jsNameTree;		// Java Script name-tree
   GooString *baseURI;		// base URI for URI-type links
   Object metadata;		// metadata stream
   Object structTreeRoot;	// structure tree root dictionary
diff --git a/poppler/Link.cc b/poppler/Link.cc
index 0eb6822..fa19a7c 100644
--- a/poppler/Link.cc
+++ b/poppler/Link.cc
@@ -93,6 +93,12 @@ LinkAction *LinkAction::parseAction(Object *obj, GooString *baseURI) {
   } else if (obj2.isName("Sound")) {
     action = new LinkSound(obj);
 
+  // JavaScript action
+  } else if (obj2.isName("JavaScript")) {
+    obj->dictLookup("JS", &obj3);
+    action = new LinkJavaScript(&obj3);
+    obj3.free();
+
   // unknown action
   } else if (obj2.isName()) {
     action = new LinkUnknown(obj2.getName());
@@ -778,6 +784,33 @@ LinkRendition::~LinkRendition() {
 
 
 //------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+LinkJavaScript::LinkJavaScript(Object *jsObj) {
+  js = NULL;
+
+  if (jsObj->isString()) {
+    js = new GooString(jsObj->getString());
+  }
+  else if (jsObj->isStream()) {
+    Stream *stream = jsObj->getStream();
+    js = new GooString();
+    stream->reset();
+    int i;
+    while ((i = stream->getChar()) != EOF) {
+      js->append((char)i);
+    }
+  }
+}
+
+LinkJavaScript::~LinkJavaScript() {
+  if (js) {
+    delete js;
+  }
+}
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
diff --git a/poppler/Link.h b/poppler/Link.h
index 64e8e8e..98e1c91 100644
--- a/poppler/Link.h
+++ b/poppler/Link.h
@@ -34,6 +34,7 @@ enum LinkActionKind {
   actionMovie,			// movie action
   actionRendition,
   actionSound,			// sound action
+  actionJavaScript,		// JavaScript action
   actionUnknown			// anything else
 };
 
@@ -359,6 +360,28 @@ private:
 };
 
 //------------------------------------------------------------------------
+// LinkJavaScript
+//------------------------------------------------------------------------
+
+class LinkJavaScript: public LinkAction {
+public:
+
+  // Build a LinkJavaScript given the action name.
+  LinkJavaScript(Object *jsObj);
+
+  virtual ~LinkJavaScript();
+
+  virtual GBool isOk() { return js != NULL; }
+
+  virtual LinkActionKind getKind() { return actionJavaScript; }
+  GooString *getScript() { return js; }
+
+private:
+
+  GooString *js;
+};
+
+//------------------------------------------------------------------------
 // LinkUnknown
 //------------------------------------------------------------------------
 
diff --git a/qt4/src/poppler-document.cc b/qt4/src/poppler-document.cc
index e18ea8b..ceffeca 100644
--- a/qt4/src/poppler-document.cc
+++ b/qt4/src/poppler-document.cc
@@ -478,6 +478,21 @@ namespace Poppler {
         return (OptContentModel *)m_doc->m_optContentModel;
     }
 
+    QStringList Document::scripts() const
+    {
+        Catalog *catalog = m_doc->doc->getCatalog();
+        const int numScripts = catalog->numJS();
+        QStringList scripts;
+        for (int i = 0; i < numScripts; ++i) {
+            GooString *s = catalog->getJS(i);
+            if (s) {
+                scripts.append(UnicodeParsedString(s));
+                delete s;
+            }
+        }
+        return scripts;
+    }
+
     QDateTime convertDate( char *dateString )
     {
         int year;
diff --git a/qt4/src/poppler-link.cc b/qt4/src/poppler-link.cc
index 3d94363..fb9df21 100644
--- a/qt4/src/poppler-link.cc
+++ b/qt4/src/poppler-link.cc
@@ -151,6 +151,19 @@ class LinkSoundPrivate : public LinkPrivate
 		delete sound;
 	}
 
+class LinkJavaScriptPrivate : public LinkPrivate
+{
+	public:
+		LinkJavaScriptPrivate( const QRectF &area );
+
+		QString js;
+};
+
+	LinkJavaScriptPrivate::LinkJavaScriptPrivate( const QRectF &area )
+		: LinkPrivate( area )
+	{
+	}
+
 #if 0
 class LinkMoviePrivate : public LinkPrivate
 {
@@ -515,6 +528,29 @@ class LinkMoviePrivate : public LinkPrivate
 		return d->sound;
 	}
 
+	// LinkJavaScript
+	LinkJavaScript::LinkJavaScript( const QRectF &linkArea, const QString &js )
+		: Link( *new LinkJavaScriptPrivate( linkArea ) )
+	{
+		Q_D( LinkJavaScript );
+		d->js = js;
+	}
+	
+	LinkJavaScript::~LinkJavaScript()
+	{
+	}
+	
+	Link::LinkType LinkJavaScript::linkType() const
+	{
+		return JavaScript;
+	}
+	
+	QString LinkJavaScript::script() const
+	{
+		Q_D( const LinkJavaScript );
+		return d->js;
+	}
+
 #if 0
 	// LinkMovie
 	LinkMovie::LinkMovie( const QRectF &linkArea )
diff --git a/qt4/src/poppler-link.h b/qt4/src/poppler-link.h
index 204ffe1..e5a0057 100644
--- a/qt4/src/poppler-link.h
+++ b/qt4/src/poppler-link.h
@@ -34,6 +34,7 @@ class LinkExecutePrivate;
 class LinkBrowsePrivate;
 class LinkActionPrivate;
 class LinkSoundPrivate;
+class LinkJavaScriptPrivate;
 class LinkMoviePrivate;
 class LinkDestinationData;
 class LinkDestinationPrivate;
@@ -124,7 +125,8 @@ class POPPLER_QT4_EXPORT Link
 		    Browse,
 		    Action,
 		    Sound,    ///< A link representing a sound to be played
-		    Movie
+		    Movie,
+		    JavaScript
 		};
 
 		/**
@@ -342,6 +344,34 @@ class POPPLER_QT4_EXPORT LinkSound : public Link
 		Q_DISABLE_COPY( LinkSound )
 };
 
+/** JavaScript: a JavaScript script to be interpreted. **/
+class POPPLER_QT4_EXPORT LinkJavaScript : public Link
+{
+	public:
+		/**
+		 * Create a new JS link.
+		 *
+		 * \param linkArea the active area of the link
+		 * \param js the script to be interpreted
+		 */
+		LinkJavaScript( const QRectF &linkArea, const QString &js );
+		/**
+		 * Destructor.
+		 */
+		virtual ~LinkJavaScript();
+
+		LinkType linkType() const;
+
+		/**
+		 * The JS script
+		 */
+		QString script() const;
+
+	private:
+		Q_DECLARE_PRIVATE( LinkJavaScript )
+		Q_DISABLE_COPY( LinkJavaScript )
+};	
+
 #if 0
 /** Movie: Not yet defined -> think renaming to 'Media' link **/
 class POPPLER_QT4_EXPORT LinkMovie : public Link
diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc
index 8ae6ccd..c285a70 100644
--- a/qt4/src/poppler-page.cc
+++ b/qt4/src/poppler-page.cc
@@ -139,6 +139,13 @@ Link* PageData::convertLinkActionToLink(::LinkAction * a, const QRectF &linkArea
       popplerLink = new LinkSound( linkArea, ls->getVolume(), ls->getSynchronous(), ls->getRepeat(), ls->getMix(), new SoundObject( ls->getSound() ) );
     }
 
+    case actionJavaScript:
+    {
+      ::LinkJavaScript *ljs = (::LinkJavaScript *)a;
+      popplerLink = new LinkJavaScript( linkArea, UnicodeParsedString(ljs->getScript()) );
+    }
+    break;
+
     case actionMovie:
 /*      TODO this (Movie link)
           m_type = Movie;
diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h
index 5deedd8..ac106ae 100644
--- a/qt4/src/poppler-qt4.h
+++ b/qt4/src/poppler-qt4.h
@@ -906,6 +906,14 @@ QString subject = m_doc->info("Subject");
 	OptContentModel *optionalContentModel();
 
 	/**
+	   Document JavaScript scripts.
+
+	   Returns the list of document level JavaScript scripts to be always
+	   executed before any other script.
+	*/
+	QStringList scripts() const;
+
+	/**
 	   Destructor.
 	*/
 	~Document();

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
poppler mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/poppler

Reply via email to