Coverage Report

Created: 2026-08-25 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}