summaryrefslogtreecommitdiff
path: root/poppler
diff options
context:
space:
mode:
authorNelson Benítez León <nbenitezl@gmail.com>2020-06-18 15:31:23 -0400
committerAlbert Astals Cid <tsdgeos@yahoo.es>2020-06-30 18:21:56 +0000
commit4b9a643e7308852f1bc6e5932287c313e14416a5 (patch)
treeb8b056a20e55ef03d516b07ef7da94b81ef47286 /poppler
parent69794176c8be5ebfa2a60d1261c8532695d18681 (diff)
Move utils/JSInfo.cc utils/JSInfo.h to core poppler
and add new JSInfo::scanJS() variant that returns immediately after finding JS. This variant is used by newly added PDFDoc::hasJavascript() method which is in turn used by newly added poppler-glib function poppler_document_has_javascript()
Diffstat (limited to 'poppler')
-rw-r--r--poppler/JSInfo.cc268
-rw-r--r--poppler/JSInfo.h66
-rw-r--r--poppler/PDFDoc.cc7
-rw-r--r--poppler/PDFDoc.h2
4 files changed, 343 insertions, 0 deletions
diff --git a/poppler/JSInfo.cc b/poppler/JSInfo.cc
new file mode 100644
index 00000000..4d3f5919
--- /dev/null
+++ b/poppler/JSInfo.cc
@@ -0,0 +1,268 @@
+//========================================================================
+//
+// JSInfo.cc
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
+// Copyright (C) 2017, 2020 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
+// Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
+//
+// 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 <cstdio>
+#include "Object.h"
+#include "Dict.h"
+#include "Annot.h"
+#include "PDFDoc.h"
+#include "JSInfo.h"
+#include "Link.h"
+#include "Form.h"
+#include "UnicodeMap.h"
+#include "UTF.h"
+// #include "Win32Console.h"
+
+JSInfo::JSInfo(PDFDoc *docA, int firstPage) {
+ doc = docA;
+ currentPage = firstPage + 1;
+}
+
+JSInfo::~JSInfo() {
+}
+
+void JSInfo::printJS(const GooString *js) {
+ Unicode *u = nullptr;
+ char buf[8];
+ int i, n, len;
+
+ if (!js || !js->c_str())
+ return;
+
+ len = TextStringToUCS4(js, &u);
+ for (i = 0; i < len; i++) {
+ n = uniMap->mapUnicode(u[i], buf, sizeof(buf));
+ fwrite(buf, 1, n, file);
+ }
+ gfree(u);
+}
+
+void JSInfo::scanLinkAction(LinkAction *link, const char *action) {
+ if (!link)
+ return;
+
+ if (link->getKind() == actionJavaScript) {
+ hasJS = true;
+ if (print) {
+ LinkJavaScript *linkjs = static_cast<LinkJavaScript *>(link);
+ if (linkjs->isOk()) {
+ const std::string& s = linkjs->getScript();
+ fprintf(file, "%s:\n", action);
+ GooString gooS = GooString(s);
+ printJS(&gooS);
+ fputs("\n\n", file);
+ }
+ }
+ }
+
+ if (link->getKind() == actionRendition) {
+ LinkRendition *linkr = static_cast<LinkRendition *>(link);
+ if (!linkr->getScript().empty()) {
+ hasJS = true;
+ if (print) {
+ fprintf(file, "%s (Rendition):\n", action);
+ const GooString s(linkr->getScript());
+ printJS(&s);
+ fputs("\n\n", file);
+ }
+ }
+ }
+}
+
+void JSInfo::scanJS(int nPages) {
+ print = false;
+ file = nullptr;
+ onlyFirstJS = false;
+ scan(nPages);
+}
+
+void JSInfo::scanJS(int nPages, FILE *fout, const UnicodeMap *uMap) {
+ print = true;
+ file = fout;
+ uniMap = uMap;
+ onlyFirstJS = false;
+ scan(nPages);
+}
+
+void JSInfo::scanJS(int nPages, bool stopOnFirstJS) {
+ print = false;
+ file = nullptr;
+ onlyFirstJS = stopOnFirstJS;
+ scan(nPages);
+}
+
+void JSInfo::scan(int nPages) {
+ Page *page;
+ Annots *annots;
+ int lastPage;
+
+ hasJS = false;
+
+ // Names
+ int numNames = doc->getCatalog()->numJS();
+ if (numNames > 0) {
+ hasJS = true;
+ if (onlyFirstJS) {
+ return;
+ }
+ if (print) {
+ for (int i = 0; i < numNames; i++) {
+ fprintf(file, "Name Dictionary \"%s\":\n", doc->getCatalog()->getJSName(i)->c_str());
+ GooString *js = doc->getCatalog()->getJS(i);
+ printJS(js);
+ delete js;
+ fputs("\n\n", file);
+ }
+ }
+ }
+
+ // document actions
+ scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument).get(),
+ "Before Close Document");
+ scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart).get(),
+ "Before Save Document");
+ scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish).get(),
+ "After Save Document");
+ scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart).get(),
+ "Before Print Document");
+ scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish).get(),
+ "After Print Document");
+
+ if (onlyFirstJS && hasJS) {
+ return;
+ }
+ // 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(),
+ "Field Activated");
+ scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified).get(),
+ "Field Modified");
+ scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField).get(),
+ "Format Field");
+ scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField).get(),
+ "Validate Field");
+ scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField).get(),
+ "Calculate Field");
+ if (onlyFirstJS && hasJS) {
+ return;
+ }
+ }
+ }
+ }
+
+ // 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).get(), "Page Open");
+ scanLinkAction(page->getAdditionalAction(Page::actionClosePage).get(), "Page Close");
+
+ if (onlyFirstJS && hasJS) {
+ return;
+ }
+ // 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(), "Link Annotation Activated");
+ if (onlyFirstJS && hasJS) {
+ return;
+ }
+ } else if (annots->getAnnot(i)->getType() == Annot::typeScreen) {
+ AnnotScreen *annot = static_cast<AnnotScreen *>(annots->getAnnot(i));
+ scanLinkAction(annot->getAction(),
+ "Screen Annotation Activated");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(),
+ "Screen Annotation Cursor Enter");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(),
+ "Screen Annotation Cursor Leave");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(),
+ "Screen Annotation Mouse Pressed");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(),
+ "Screen Annotation Mouse Released");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(),
+ "Screen Annotation Focus In");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(),
+ "Screen Annotation Focus Out");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(),
+ "Screen Annotation Page Open");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(),
+ "Screen Annotation Page Close");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(),
+ "Screen Annotation Page Visible");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(),
+ "Screen Annotation Page Invisible");
+
+ if (onlyFirstJS && hasJS) {
+ return;
+ }
+ } else if (annots->getAnnot(i)->getType() == Annot::typeWidget) {
+ AnnotWidget *annot = static_cast<AnnotWidget *>(annots->getAnnot(i));
+ scanLinkAction(annot->getAction(),
+ "Widget Annotation Activated");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(),
+ "Widget Annotation Cursor Enter");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(),
+ "Widget Annotation Cursor Leave");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(),
+ "Widget Annotation Mouse Pressed");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(),
+ "Widget Annotation Mouse Released");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(),
+ "Widget Annotation Focus In");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(),
+ "Widget Annotation Focus Out");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(),
+ "Widget Annotation Page Open");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(),
+ "Widget Annotation Page Close");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(),
+ "Widget Annotation Page Visible");
+ scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(),
+ "Widget Annotation Page Invisible");
+ if (onlyFirstJS && hasJS) {
+ return;
+ }
+ }
+ }
+ }
+
+ currentPage = lastPage;
+}
+
+bool JSInfo::containsJS() {
+ return hasJS;
+}
diff --git a/poppler/JSInfo.h b/poppler/JSInfo.h
new file mode 100644
index 00000000..89e0a3d0
--- /dev/null
+++ b/poppler/JSInfo.h
@@ -0,0 +1,66 @@
+//========================================================================
+//
+// JSInfo.h
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
+// Copyright (C) 2020 Albert Astals Cid <aacid@kde.org>
+// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
+// Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
+//
+// 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 <cstdio>
+#include "Object.h"
+#include "PDFDoc.h"
+
+#include "Link.h"
+#include "UnicodeMap.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);
+
+ // scan and print JS in the PDF
+ void scanJS(int nPages, FILE *fout, const UnicodeMap *uMap);
+
+ // scan but exit after finding first JS in the PDF
+ void scanJS(int nPages, bool stopOnFirstJS);
+
+ // return true if PDF contains JavaScript
+ bool containsJS();
+
+private:
+
+ PDFDoc *doc;
+ int currentPage;
+ bool hasJS;
+ bool print;
+ FILE *file;
+ const UnicodeMap *uniMap;
+ bool onlyFirstJS; /* stop scanning after finding first JS */
+
+ void scan(int nPages);
+ void scanLinkAction(LinkAction *link, const char *action);
+ void printJS(const GooString *js);
+
+};
+
+#endif
diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc
index 4b5621fc..6ad884cf 100644
--- a/poppler/PDFDoc.cc
+++ b/poppler/PDFDoc.cc
@@ -86,6 +86,7 @@
#include "PDFDoc.h"
#include "Hints.h"
#include "UTF.h"
+#include "JSInfo.h"
//------------------------------------------------------------------------
@@ -2152,3 +2153,9 @@ Page *PDFDoc::getPage(int page)
return catalog->getPage(page);
}
+
+bool PDFDoc::hasJavascript() {
+ JSInfo jsInfo(this);
+ jsInfo.scanJS (getNumPages(), true);
+ return jsInfo.containsJS();
+}
diff --git a/poppler/PDFDoc.h b/poppler/PDFDoc.h
index 7f4fb167..ca8bf6b6 100644
--- a/poppler/PDFDoc.h
+++ b/poppler/PDFDoc.h
@@ -349,6 +349,8 @@ public:
Goffset uxrefOffset, OutStream* outStr, XRef *xRef);
static void writeXRefStreamTrailer (Object &&trailerDict, XRef *uxref, Ref *uxrefStreamRef,
Goffset uxrefOffset, OutStream* outStr, XRef *xRef);
+ // scans the PDF and returns whether it contains any javascript
+ bool hasJavascript();
private:
// insert referenced objects in XRef