/src/poppler/poppler/JSInfo.cc
Line | Count | Source |
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, 2024-2026 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, 2025 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 "Annot.h" |
22 | | #include "PDFDoc.h" |
23 | | #include "JSInfo.h" |
24 | | #include "Link.h" |
25 | | #include "Form.h" |
26 | | #include "UnicodeMap.h" |
27 | | #include "UTF.h" |
28 | | // #include "Win32Console.h" |
29 | | |
30 | | JSInfo::JSInfo(PDFDoc *docA, int firstPage) |
31 | 0 | { |
32 | 0 | doc = docA; |
33 | 0 | currentPage = firstPage + 1; |
34 | 0 | } |
35 | | |
36 | | void JSInfo::printJS(std::string_view js) |
37 | 0 | { |
38 | 0 | char buf[8]; |
39 | |
|
40 | 0 | if (js.empty()) { |
41 | 0 | return; |
42 | 0 | } |
43 | | |
44 | 0 | std::vector<Unicode> u = TextStringToUCS4(js); |
45 | 0 | for (auto &c : u) { |
46 | 0 | int n = uniMap->mapUnicode(c, buf, sizeof(buf)); |
47 | 0 | fwrite(buf, 1, n, file); |
48 | 0 | } |
49 | 0 | } |
50 | | |
51 | | void JSInfo::scanLinkAction(const LinkAction *link, const char *action) |
52 | 0 | { |
53 | 0 | if (!link) { |
54 | 0 | return; |
55 | 0 | } |
56 | | |
57 | 0 | if (link->getKind() == actionJavaScript) { |
58 | 0 | hasJS = true; |
59 | 0 | if (print) { |
60 | 0 | const auto *linkjs = static_cast<const LinkJavaScript *>(link); |
61 | 0 | if (linkjs->isOk()) { |
62 | 0 | const std::string &s = linkjs->getScript(); |
63 | 0 | fprintf(file, "%s:\n", action); |
64 | 0 | printJS(s); |
65 | 0 | fputs("\n\n", file); |
66 | 0 | } |
67 | 0 | } |
68 | 0 | } |
69 | |
|
70 | 0 | if (link->getKind() == actionRendition) { |
71 | 0 | const auto *linkr = static_cast<const LinkRendition *>(link); |
72 | 0 | if (!linkr->getScript().empty()) { |
73 | 0 | hasJS = true; |
74 | 0 | if (print) { |
75 | 0 | fprintf(file, "%s (Rendition):\n", action); |
76 | 0 | const std::string &s(linkr->getScript()); |
77 | 0 | printJS(s); |
78 | 0 | fputs("\n\n", file); |
79 | 0 | } |
80 | 0 | } |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | void JSInfo::scanJS(int nPages) |
85 | 0 | { |
86 | 0 | print = false; |
87 | 0 | file = nullptr; |
88 | 0 | onlyFirstJS = false; |
89 | 0 | scan(nPages); |
90 | 0 | } |
91 | | |
92 | | void JSInfo::scanJS(int nPages, FILE *fout, const UnicodeMap *uMap) |
93 | 0 | { |
94 | 0 | print = true; |
95 | 0 | file = fout; |
96 | 0 | uniMap = uMap; |
97 | 0 | onlyFirstJS = false; |
98 | 0 | scan(nPages); |
99 | 0 | } |
100 | | |
101 | | void JSInfo::scanJS(int nPages, bool stopOnFirstJS) |
102 | 0 | { |
103 | 0 | print = false; |
104 | 0 | file = nullptr; |
105 | 0 | onlyFirstJS = stopOnFirstJS; |
106 | 0 | scan(nPages); |
107 | 0 | } |
108 | | |
109 | | void JSInfo::scan(int nPages) |
110 | 0 | { |
111 | 0 | Page *page; |
112 | 0 | Annots *annots; |
113 | 0 | int lastPage; |
114 | |
|
115 | 0 | hasJS = false; |
116 | | |
117 | | // Names |
118 | 0 | int numNames = doc->getCatalog()->numJS(); |
119 | 0 | if (numNames > 0) { |
120 | 0 | hasJS = true; |
121 | 0 | if (onlyFirstJS) { |
122 | 0 | return; |
123 | 0 | } |
124 | 0 | if (print) { |
125 | 0 | for (int i = 0; i < numNames; i++) { |
126 | 0 | fprintf(file, "Name Dictionary \"%s\":\n", doc->getCatalog()->getJSName(i)->c_str()); |
127 | 0 | std::string js = doc->getCatalog()->getJS(i); |
128 | 0 | printJS(js); |
129 | 0 | fputs("\n\n", file); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | | // document actions |
135 | 0 | scanLinkAction(doc->getCatalog()->getOpenAction().get(), "Open Document Action"); |
136 | 0 | scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument).get(), "Before Close Document"); |
137 | 0 | scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart).get(), "Before Save Document"); |
138 | 0 | scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish).get(), "After Save Document"); |
139 | 0 | scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentStart).get(), "Before Print Document"); |
140 | 0 | scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionPrintDocumentFinish).get(), "After Print Document"); |
141 | |
|
142 | 0 | if (onlyFirstJS && hasJS) { |
143 | 0 | return; |
144 | 0 | } |
145 | | // form field actions |
146 | 0 | if (doc->getCatalog()->getFormType() == Catalog::AcroForm) { |
147 | 0 | Form *form = doc->getCatalog()->getForm(); |
148 | 0 | for (int i = 0; i < form->getNumFields(); i++) { |
149 | 0 | FormField *field = form->getRootField(i); |
150 | 0 | for (int j = 0; j < field->getNumWidgets(); j++) { |
151 | 0 | FormWidget *widget = field->getWidget(j); |
152 | 0 | scanLinkAction(widget->getActivationAction(), "Field Activated"); |
153 | 0 | scanLinkAction(widget->getAdditionalAction(Annot::actionFieldModified).get(), "Field Modified"); |
154 | 0 | scanLinkAction(widget->getAdditionalAction(Annot::actionFormatField).get(), "Format Field"); |
155 | 0 | scanLinkAction(widget->getAdditionalAction(Annot::actionValidateField).get(), "Validate Field"); |
156 | 0 | scanLinkAction(widget->getAdditionalAction(Annot::actionCalculateField).get(), "Calculate Field"); |
157 | 0 | if (onlyFirstJS && hasJS) { |
158 | 0 | return; |
159 | 0 | } |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | // scan pages |
165 | | |
166 | 0 | if (currentPage > doc->getNumPages()) { |
167 | 0 | return; |
168 | 0 | } |
169 | | |
170 | 0 | lastPage = currentPage + nPages; |
171 | 0 | if (lastPage > doc->getNumPages() + 1) { |
172 | 0 | lastPage = doc->getNumPages() + 1; |
173 | 0 | } |
174 | |
|
175 | 0 | for (int pg = currentPage; pg < lastPage; ++pg) { |
176 | 0 | page = doc->getPage(pg); |
177 | 0 | if (!page) { |
178 | 0 | continue; |
179 | 0 | } |
180 | | |
181 | | // page actions (open, close) |
182 | 0 | scanLinkAction(page->getAdditionalAction(Page::actionOpenPage).get(), "Page Open"); |
183 | 0 | scanLinkAction(page->getAdditionalAction(Page::actionClosePage).get(), "Page Close"); |
184 | |
|
185 | 0 | if (onlyFirstJS && hasJS) { |
186 | 0 | return; |
187 | 0 | } |
188 | | // annotation actions (links, screen, widget) |
189 | 0 | annots = page->getAnnots(); |
190 | 0 | for (const std::shared_ptr<Annot> &a : annots->getAnnots()) { |
191 | 0 | if (a->getType() == Annot::typeLink) { |
192 | 0 | auto *annot = static_cast<AnnotLink *>(a.get()); |
193 | 0 | scanLinkAction(annot->getAction(), "Link Annotation Activated"); |
194 | 0 | if (onlyFirstJS && hasJS) { |
195 | 0 | return; |
196 | 0 | } |
197 | 0 | } else if (a->getType() == Annot::typeScreen) { |
198 | 0 | auto *annot = static_cast<AnnotScreen *>(a.get()); |
199 | 0 | scanLinkAction(annot->getAction(), "Screen Annotation Activated"); |
200 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(), "Screen Annotation Cursor Enter"); |
201 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(), "Screen Annotation Cursor Leave"); |
202 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(), "Screen Annotation Mouse Pressed"); |
203 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(), "Screen Annotation Mouse Released"); |
204 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(), "Screen Annotation Focus In"); |
205 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(), "Screen Annotation Focus Out"); |
206 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(), "Screen Annotation Page Open"); |
207 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(), "Screen Annotation Page Close"); |
208 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(), "Screen Annotation Page Visible"); |
209 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(), "Screen Annotation Page Invisible"); |
210 | |
|
211 | 0 | if (onlyFirstJS && hasJS) { |
212 | 0 | return; |
213 | 0 | } |
214 | 0 | } else if (a->getType() == Annot::typeWidget) { |
215 | 0 | auto *annot = static_cast<AnnotWidget *>(a.get()); |
216 | 0 | scanLinkAction(annot->getAction(), "Widget Annotation Activated"); |
217 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionCursorEntering).get(), "Widget Annotation Cursor Enter"); |
218 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionCursorLeaving).get(), "Widget Annotation Cursor Leave"); |
219 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionMousePressed).get(), "Widget Annotation Mouse Pressed"); |
220 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionMouseReleased).get(), "Widget Annotation Mouse Released"); |
221 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionFocusIn).get(), "Widget Annotation Focus In"); |
222 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionFocusOut).get(), "Widget Annotation Focus Out"); |
223 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageOpening).get(), "Widget Annotation Page Open"); |
224 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageClosing).get(), "Widget Annotation Page Close"); |
225 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageVisible).get(), "Widget Annotation Page Visible"); |
226 | 0 | scanLinkAction(annot->getAdditionalAction(Annot::actionPageInvisible).get(), "Widget Annotation Page Invisible"); |
227 | 0 | if (onlyFirstJS && hasJS) { |
228 | 0 | return; |
229 | 0 | } |
230 | 0 | } |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | 0 | currentPage = lastPage; |
235 | 0 | } |
236 | | |
237 | | bool JSInfo::containsJS() const |
238 | 0 | { |
239 | 0 | return hasJS; |
240 | 0 | } |