diff options
author | Nelson Benítez León <nbenitezl@gmail.com> | 2020-06-18 15:31:23 -0400 |
---|---|---|
committer | Albert Astals Cid <tsdgeos@yahoo.es> | 2020-06-30 18:21:56 +0000 |
commit | 4b9a643e7308852f1bc6e5932287c313e14416a5 (patch) | |
tree | b8b056a20e55ef03d516b07ef7da94b81ef47286 /poppler | |
parent | 69794176c8be5ebfa2a60d1261c8532695d18681 (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.cc | 268 | ||||
-rw-r--r-- | poppler/JSInfo.h | 66 | ||||
-rw-r--r-- | poppler/PDFDoc.cc | 7 | ||||
-rw-r--r-- | poppler/PDFDoc.h | 2 |
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 |