blob: f3383dbb6e48ddda6f23b5c9c63e692d8d8c4473 [file] [log] [blame]
//========================================================================
//
// JSInfo.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2017, 2020, 2021, 2024 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>
// Copyright (C) 2020 Nelson Benítez León <nbenitezl@gmail.com>
// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
//
// 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)
{
char buf[8];
if (!js || !js->c_str()) {
return;
}
std::vector<Unicode> u = TextStringToUCS4(js->toStr());
for (auto &c : u) {
int n = uniMap->mapUnicode(c, buf, sizeof(buf));
fwrite(buf, 1, n, file);
}
}
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()->getOpenAction().get(), "Open Document Action");
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 (Annot *a : annots->getAnnots()) {
if (a->getType() == Annot::typeLink) {
AnnotLink *annot = static_cast<AnnotLink *>(a);
scanLinkAction(annot->getAction(), "Link Annotation Activated");
if (onlyFirstJS && hasJS) {
return;
}
} else if (a->getType() == Annot::typeScreen) {
AnnotScreen *annot = static_cast<AnnotScreen *>(a);
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 (a->getType() == Annot::typeWidget) {
AnnotWidget *annot = static_cast<AnnotWidget *>(a);
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;
}