1//========================================================================
2//
3// JSInfo.cc
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com>
8// Copyright (C) 2017, 2020, 2021 Albert Astals Cid <aacid@kde.org>
9// 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
10// Copyright (C) 2020 Oliver Sander <oliver.sander@tu-dresden.de>
11// Copyright (C) 2020 Nelson Benítez León <nbenitezl@gmail.com>
12// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
13//
14// To see a description of the changes please see the Changelog file that
15// came with your tarball or type make ChangeLog if you are building from git
16//
17//========================================================================
18
19#include "config.h"
20#include <cstdio>
21#include "Object.h"
22#include "Dict.h"
23#include "Annot.h"
24#include "PDFDoc.h"
25#include "JSInfo.h"
26#include "Link.h"
27#include "Form.h"
28#include "UnicodeMap.h"
29#include "UTF.h"
30// #include "Win32Console.h"
31
32JSInfo::JSInfo(PDFDoc *docA, int firstPage)
33{
34 doc = docA;
35 currentPage = firstPage + 1;
36}
37
38JSInfo::~JSInfo() { }
39
40void JSInfo::printJS(const GooString *js)
41{
42 char buf[8];
43
44 if (!js || !js->c_str()) {
45 return;
46 }
47
48 std::vector<Unicode> u = TextStringToUCS4(textStr: js->toStr());
49 for (auto &c : u) {
50 int n = uniMap->mapUnicode(u: c, buf, bufSize: sizeof(buf));
51 fwrite(ptr: buf, size: 1, n: n, s: file);
52 }
53}
54
55void JSInfo::scanLinkAction(LinkAction *link, const char *action)
56{
57 if (!link) {
58 return;
59 }
60
61 if (link->getKind() == actionJavaScript) {
62 hasJS = true;
63 if (print) {
64 LinkJavaScript *linkjs = static_cast<LinkJavaScript *>(link);
65 if (linkjs->isOk()) {
66 const std::string &s = linkjs->getScript();
67 fprintf(stream: file, format: "%s:\n", action);
68 GooString gooS = GooString(s);
69 printJS(js: &gooS);
70 fputs(s: "\n\n", stream: file);
71 }
72 }
73 }
74
75 if (link->getKind() == actionRendition) {
76 LinkRendition *linkr = static_cast<LinkRendition *>(link);
77 if (!linkr->getScript().empty()) {
78 hasJS = true;
79 if (print) {
80 fprintf(stream: file, format: "%s (Rendition):\n", action);
81 const GooString s(linkr->getScript());
82 printJS(js: &s);
83 fputs(s: "\n\n", stream: file);
84 }
85 }
86 }
87}
88
89void JSInfo::scanJS(int nPages)
90{
91 print = false;
92 file = nullptr;
93 onlyFirstJS = false;
94 scan(nPages);
95}
96
97void JSInfo::scanJS(int nPages, FILE *fout, const UnicodeMap *uMap)
98{
99 print = true;
100 file = fout;
101 uniMap = uMap;
102 onlyFirstJS = false;
103 scan(nPages);
104}
105
106void JSInfo::scanJS(int nPages, bool stopOnFirstJS)
107{
108 print = false;
109 file = nullptr;
110 onlyFirstJS = stopOnFirstJS;
111 scan(nPages);
112}
113
114void JSInfo::scan(int nPages)
115{
116 Page *page;
117 Annots *annots;
118 int lastPage;
119
120 hasJS = false;
121
122 // Names
123 int numNames = doc->getCatalog()->numJS();
124 if (numNames > 0) {
125 hasJS = true;
126 if (onlyFirstJS) {
127 return;
128 }
129 if (print) {
130 for (int i = 0; i < numNames; i++) {
131 fprintf(stream: file, format: "Name Dictionary \"%s\":\n", doc->getCatalog()->getJSName(i)->c_str());
132 GooString *js = doc->getCatalog()->getJS(i);
133 printJS(js);
134 delete js;
135 fputs(s: "\n\n", stream: file);
136 }
137 }
138 }
139
140 // document actions
141 scanLinkAction(link: doc->getCatalog()->getAdditionalAction(type: Catalog::actionCloseDocument).get(), action: "Before Close Document");
142 scanLinkAction(link: doc->getCatalog()->getAdditionalAction(type: Catalog::actionSaveDocumentStart).get(), action: "Before Save Document");
143 scanLinkAction(link: doc->getCatalog()->getAdditionalAction(type: Catalog::actionSaveDocumentFinish).get(), action: "After Save Document");
144 scanLinkAction(link: doc->getCatalog()->getAdditionalAction(type: Catalog::actionPrintDocumentStart).get(), action: "Before Print Document");
145 scanLinkAction(link: doc->getCatalog()->getAdditionalAction(type: Catalog::actionPrintDocumentFinish).get(), action: "After Print Document");
146
147 if (onlyFirstJS && hasJS) {
148 return;
149 }
150 // form field actions
151 if (doc->getCatalog()->getFormType() == Catalog::AcroForm) {
152 Form *form = doc->getCatalog()->getForm();
153 for (int i = 0; i < form->getNumFields(); i++) {
154 FormField *field = form->getRootField(i);
155 for (int j = 0; j < field->getNumWidgets(); j++) {
156 FormWidget *widget = field->getWidget(i: j);
157 scanLinkAction(link: widget->getActivationAction(), action: "Field Activated");
158 scanLinkAction(link: widget->getAdditionalAction(type: Annot::actionFieldModified).get(), action: "Field Modified");
159 scanLinkAction(link: widget->getAdditionalAction(type: Annot::actionFormatField).get(), action: "Format Field");
160 scanLinkAction(link: widget->getAdditionalAction(type: Annot::actionValidateField).get(), action: "Validate Field");
161 scanLinkAction(link: widget->getAdditionalAction(type: Annot::actionCalculateField).get(), action: "Calculate Field");
162 if (onlyFirstJS && hasJS) {
163 return;
164 }
165 }
166 }
167 }
168
169 // scan pages
170
171 if (currentPage > doc->getNumPages()) {
172 return;
173 }
174
175 lastPage = currentPage + nPages;
176 if (lastPage > doc->getNumPages() + 1) {
177 lastPage = doc->getNumPages() + 1;
178 }
179
180 for (int pg = currentPage; pg < lastPage; ++pg) {
181 page = doc->getPage(page: pg);
182 if (!page) {
183 continue;
184 }
185
186 // page actions (open, close)
187 scanLinkAction(link: page->getAdditionalAction(type: Page::actionOpenPage).get(), action: "Page Open");
188 scanLinkAction(link: page->getAdditionalAction(type: Page::actionClosePage).get(), action: "Page Close");
189
190 if (onlyFirstJS && hasJS) {
191 return;
192 }
193 // annotation actions (links, screen, widget)
194 annots = page->getAnnots();
195 for (Annot *a : annots->getAnnots()) {
196 if (a->getType() == Annot::typeLink) {
197 AnnotLink *annot = static_cast<AnnotLink *>(a);
198 scanLinkAction(link: annot->getAction(), action: "Link Annotation Activated");
199 if (onlyFirstJS && hasJS) {
200 return;
201 }
202 } else if (a->getType() == Annot::typeScreen) {
203 AnnotScreen *annot = static_cast<AnnotScreen *>(a);
204 scanLinkAction(link: annot->getAction(), action: "Screen Annotation Activated");
205 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionCursorEntering).get(), action: "Screen Annotation Cursor Enter");
206 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionCursorLeaving).get(), action: "Screen Annotation Cursor Leave");
207 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionMousePressed).get(), action: "Screen Annotation Mouse Pressed");
208 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionMouseReleased).get(), action: "Screen Annotation Mouse Released");
209 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionFocusIn).get(), action: "Screen Annotation Focus In");
210 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionFocusOut).get(), action: "Screen Annotation Focus Out");
211 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageOpening).get(), action: "Screen Annotation Page Open");
212 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageClosing).get(), action: "Screen Annotation Page Close");
213 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageVisible).get(), action: "Screen Annotation Page Visible");
214 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageInvisible).get(), action: "Screen Annotation Page Invisible");
215
216 if (onlyFirstJS && hasJS) {
217 return;
218 }
219 } else if (a->getType() == Annot::typeWidget) {
220 AnnotWidget *annot = static_cast<AnnotWidget *>(a);
221 scanLinkAction(link: annot->getAction(), action: "Widget Annotation Activated");
222 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionCursorEntering).get(), action: "Widget Annotation Cursor Enter");
223 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionCursorLeaving).get(), action: "Widget Annotation Cursor Leave");
224 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionMousePressed).get(), action: "Widget Annotation Mouse Pressed");
225 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionMouseReleased).get(), action: "Widget Annotation Mouse Released");
226 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionFocusIn).get(), action: "Widget Annotation Focus In");
227 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionFocusOut).get(), action: "Widget Annotation Focus Out");
228 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageOpening).get(), action: "Widget Annotation Page Open");
229 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageClosing).get(), action: "Widget Annotation Page Close");
230 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageVisible).get(), action: "Widget Annotation Page Visible");
231 scanLinkAction(link: annot->getAdditionalAction(type: Annot::actionPageInvisible).get(), action: "Widget Annotation Page Invisible");
232 if (onlyFirstJS && hasJS) {
233 return;
234 }
235 }
236 }
237 }
238
239 currentPage = lastPage;
240}
241
242bool JSInfo::containsJS()
243{
244 return hasJS;
245}
246

source code of poppler/poppler/JSInfo.cc