1//========================================================================
2//
3// Link.cc
4//
5// Copyright 1996-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2006, 2008 Pino Toscano <pino@kde.org>
17// Copyright (C) 2007, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
18// Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
19// Copyright (C) 2008-2010, 2012-2014, 2016-2023 Albert Astals Cid <aacid@kde.org>
20// Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
21// Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
22// Copyright (C) 2012 Tobias Koening <tobias.koenig@kdab.com>
23// 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
24// Copyright (C) 2018 Intevation GmbH <intevation@intevation.de>
25// Copyright (C) 2018, 2020 Adam Reichold <adam.reichold@t-online.de>
26// Copyright (C) 2019, 2020 Oliver Sander <oliver.sander@tu-dresden.de>
27// Copyright (C) 2020 Marek Kasik <mkasik@redhat.com>
28//
29// To see a description of the changes please see the Changelog file that
30// came with your tarball or type make ChangeLog if you are building from git
31//
32//========================================================================
33
34#include <config.h>
35
36#include <cstddef>
37#include <cstring>
38#include "goo/gmem.h"
39#include "goo/GooString.h"
40#include "Error.h"
41#include "Object.h"
42#include "Array.h"
43#include "Dict.h"
44#include "Link.h"
45#include "Sound.h"
46#include "FileSpec.h"
47#include "Rendition.h"
48#include "Annot.h"
49
50//------------------------------------------------------------------------
51// LinkAction
52//------------------------------------------------------------------------
53LinkAction::LinkAction() = default;
54
55LinkAction::~LinkAction() = default;
56
57std::unique_ptr<LinkAction> LinkAction::parseDest(const Object *obj)
58{
59 auto action = std::unique_ptr<LinkAction>(new LinkGoTo(obj));
60 if (!action->isOk()) {
61 action.reset();
62 }
63 return action;
64}
65
66std::unique_ptr<LinkAction> LinkAction::parseAction(const Object *obj, const std::optional<std::string> &baseURI)
67{
68 std::set<int> seenNextActions;
69 return parseAction(obj, baseURI, seenNextActions: &seenNextActions);
70}
71
72std::unique_ptr<LinkAction> LinkAction::parseAction(const Object *obj, const std::optional<std::string> &baseURI, std::set<int> *seenNextActions)
73{
74
75 if (!obj->isDict()) {
76 error(category: errSyntaxWarning, pos: -1, msg: "parseAction: Bad annotation action for URI '{0:s}'", baseURI ? baseURI->c_str() : "NULL");
77 return nullptr;
78 }
79
80 std::unique_ptr<LinkAction> action;
81 Object obj2 = obj->dictLookup(key: "S");
82
83 // GoTo action
84 if (obj2.isName(nameA: "GoTo")) {
85 Object obj3 = obj->dictLookup(key: "D");
86 action = std::make_unique<LinkGoTo>(args: &obj3);
87
88 // GoToR action
89 } else if (obj2.isName(nameA: "GoToR")) {
90 Object obj3 = obj->dictLookup(key: "F");
91 Object obj4 = obj->dictLookup(key: "D");
92 action = std::make_unique<LinkGoToR>(args: &obj3, args: &obj4);
93
94 // Launch action
95 } else if (obj2.isName(nameA: "Launch")) {
96 action = std::make_unique<LinkLaunch>(args&: obj);
97
98 // URI action
99 } else if (obj2.isName(nameA: "URI")) {
100 Object obj3 = obj->dictLookup(key: "URI");
101 action = std::make_unique<LinkURI>(args: &obj3, args: baseURI);
102
103 // Named action
104 } else if (obj2.isName(nameA: "Named")) {
105 Object obj3 = obj->dictLookup(key: "N");
106 action = std::make_unique<LinkNamed>(args: &obj3);
107
108 // Movie action
109 } else if (obj2.isName(nameA: "Movie")) {
110 action = std::make_unique<LinkMovie>(args&: obj);
111
112 // Rendition action
113 } else if (obj2.isName(nameA: "Rendition")) {
114 action = std::make_unique<LinkRendition>(args&: obj);
115
116 // Sound action
117 } else if (obj2.isName(nameA: "Sound")) {
118 action = std::make_unique<LinkSound>(args&: obj);
119
120 // JavaScript action
121 } else if (obj2.isName(nameA: "JavaScript")) {
122 Object obj3 = obj->dictLookup(key: "JS");
123 action = std::make_unique<LinkJavaScript>(args: &obj3);
124
125 // Set-OCG-State action
126 } else if (obj2.isName(nameA: "SetOCGState")) {
127 action = std::make_unique<LinkOCGState>(args&: obj);
128
129 // Hide action
130 } else if (obj2.isName(nameA: "Hide")) {
131 action = std::make_unique<LinkHide>(args&: obj);
132
133 // ResetForm action
134 } else if (obj2.isName(nameA: "ResetForm")) {
135 action = std::make_unique<LinkResetForm>(args&: obj);
136
137 // unknown action
138 } else if (obj2.isName()) {
139 action = std::make_unique<LinkUnknown>(args: obj2.getName());
140
141 // action is missing or wrong type
142 } else {
143 error(category: errSyntaxWarning, pos: -1, msg: "parseAction: Unknown annotation action object: URI = '{0:s}'", baseURI ? baseURI->c_str() : "NULL");
144 action = nullptr;
145 }
146
147 if (action && !action->isOk()) {
148 action.reset();
149 return nullptr;
150 }
151
152 if (!action) {
153 return nullptr;
154 }
155
156 // parse the next actions
157 const Object nextObj = obj->dictLookup(key: "Next");
158 std::vector<std::unique_ptr<LinkAction>> actionList;
159 if (nextObj.isDict()) {
160
161 // Prevent circles in the tree by checking the ref against used refs in
162 // our current tree branch.
163 const Object &nextRefObj = obj->dictLookupNF(key: "Next");
164 if (nextRefObj.isRef()) {
165 const Ref ref = nextRefObj.getRef();
166 if (!seenNextActions->insert(x: ref.num).second) {
167 error(category: errSyntaxWarning, pos: -1, msg: "parseAction: Circular next actions detected.");
168 return action;
169 }
170 }
171
172 actionList.reserve(n: 1);
173 actionList.push_back(x: parseAction(obj: &nextObj, baseURI: {}, seenNextActions));
174 } else if (nextObj.isArray()) {
175 const Array *a = nextObj.getArray();
176 const int n = a->getLength();
177 actionList.reserve(n: n);
178 for (int i = 0; i < n; ++i) {
179 const Object obj3 = a->get(i);
180 if (!obj3.isDict()) {
181 error(category: errSyntaxWarning, pos: -1, msg: "parseAction: Next array does not contain only dicts");
182 continue;
183 }
184
185 // Similar circle check as above.
186 const Object &obj3Ref = a->getNF(i);
187 if (obj3Ref.isRef()) {
188 const Ref ref = obj3Ref.getRef();
189 if (!seenNextActions->insert(x: ref.num).second) {
190 error(category: errSyntaxWarning, pos: -1, msg: "parseAction: Circular next actions detected in array.");
191 return action;
192 }
193 }
194
195 actionList.push_back(x: parseAction(obj: &obj3, baseURI: {}, seenNextActions));
196 }
197 }
198
199 action->nextActionList = std::move(actionList);
200
201 return action;
202}
203
204const std::vector<std::unique_ptr<LinkAction>> &LinkAction::nextActions() const
205{
206 return nextActionList;
207}
208
209//------------------------------------------------------------------------
210// LinkDest
211//------------------------------------------------------------------------
212
213LinkDest::LinkDest(const Array *a)
214{
215 // initialize fields
216 left = bottom = right = top = zoom = 0;
217 changeLeft = changeTop = changeZoom = false;
218 ok = false;
219
220 // get page
221 if (a->getLength() < 2) {
222 error(category: errSyntaxWarning, pos: -1, msg: "Annotation destination array is too short");
223 return;
224 }
225 const Object &obj0 = a->getNF(i: 0);
226 if (obj0.isInt()) {
227 pageNum = obj0.getInt() + 1;
228 pageIsRef = false;
229 } else if (obj0.isRef()) {
230 pageRef = obj0.getRef();
231 pageIsRef = true;
232 } else {
233 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination");
234 return;
235 }
236
237 // get destination type
238 Object obj1 = a->get(i: 1);
239
240 // XYZ link
241 if (obj1.isName(nameA: "XYZ")) {
242 kind = destXYZ;
243 if (a->getLength() < 3) {
244 changeLeft = false;
245 } else {
246 Object obj2 = a->get(i: 2);
247 if (obj2.isNull()) {
248 changeLeft = false;
249 } else if (obj2.isNum()) {
250 changeLeft = true;
251 left = obj2.getNum();
252 } else {
253 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
254 return;
255 }
256 }
257 if (a->getLength() < 4) {
258 changeTop = false;
259 } else {
260 Object obj2 = a->get(i: 3);
261 if (obj2.isNull()) {
262 changeTop = false;
263 } else if (obj2.isNum()) {
264 changeTop = true;
265 top = obj2.getNum();
266 } else {
267 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
268 return;
269 }
270 }
271 if (a->getLength() < 5) {
272 changeZoom = false;
273 } else {
274 Object obj2 = a->get(i: 4);
275 if (obj2.isNull()) {
276 changeZoom = false;
277 } else if (obj2.isNum()) {
278 zoom = obj2.getNum();
279 changeZoom = (zoom == 0) ? false : true;
280 } else {
281 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
282 return;
283 }
284 }
285
286 // Fit link
287 } else if (obj1.isName(nameA: "Fit")) {
288 kind = destFit;
289
290 // FitH link
291 } else if (obj1.isName(nameA: "FitH")) {
292 kind = destFitH;
293 if (a->getLength() < 3) {
294 changeTop = false;
295 } else {
296 Object obj2 = a->get(i: 2);
297 if (obj2.isNull()) {
298 changeTop = false;
299 } else if (obj2.isNum()) {
300 changeTop = true;
301 top = obj2.getNum();
302 } else {
303 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
304 kind = destFit;
305 }
306 }
307
308 // FitV link
309 } else if (obj1.isName(nameA: "FitV")) {
310 if (a->getLength() < 3) {
311 error(category: errSyntaxWarning, pos: -1, msg: "Annotation destination array is too short");
312 return;
313 }
314 kind = destFitV;
315 Object obj2 = a->get(i: 2);
316 if (obj2.isNull()) {
317 changeLeft = false;
318 } else if (obj2.isNum()) {
319 changeLeft = true;
320 left = obj2.getNum();
321 } else {
322 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
323 kind = destFit;
324 }
325
326 // FitR link
327 } else if (obj1.isName(nameA: "FitR")) {
328 if (a->getLength() < 6) {
329 error(category: errSyntaxWarning, pos: -1, msg: "Annotation destination array is too short");
330 return;
331 }
332 kind = destFitR;
333 Object obj2 = a->get(i: 2);
334 if (obj2.isNum()) {
335 left = obj2.getNum();
336 } else {
337 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
338 kind = destFit;
339 }
340 obj2 = a->get(i: 3);
341 if (obj2.isNum()) {
342 bottom = obj2.getNum();
343 } else {
344 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
345 kind = destFit;
346 }
347 obj2 = a->get(i: 4);
348 if (obj2.isNum()) {
349 right = obj2.getNum();
350 } else {
351 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
352 kind = destFit;
353 }
354 obj2 = a->get(i: 5);
355 if (obj2.isNum()) {
356 top = obj2.getNum();
357 } else {
358 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
359 kind = destFit;
360 }
361
362 // FitB link
363 } else if (obj1.isName(nameA: "FitB")) {
364 kind = destFitB;
365
366 // FitBH link
367 } else if (obj1.isName(nameA: "FitBH")) {
368 if (a->getLength() < 3) {
369 error(category: errSyntaxWarning, pos: -1, msg: "Annotation destination array is too short");
370 return;
371 }
372 kind = destFitBH;
373 Object obj2 = a->get(i: 2);
374 if (obj2.isNull()) {
375 changeTop = false;
376 } else if (obj2.isNum()) {
377 changeTop = true;
378 top = obj2.getNum();
379 } else {
380 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
381 kind = destFit;
382 }
383
384 // FitBV link
385 } else if (obj1.isName(nameA: "FitBV")) {
386 if (a->getLength() < 3) {
387 error(category: errSyntaxWarning, pos: -1, msg: "Annotation destination array is too short");
388 return;
389 }
390 kind = destFitBV;
391 Object obj2 = a->get(i: 2);
392 if (obj2.isNull()) {
393 changeLeft = false;
394 } else if (obj2.isNum()) {
395 changeLeft = true;
396 left = obj2.getNum();
397 } else {
398 error(category: errSyntaxWarning, pos: -1, msg: "Bad annotation destination position");
399 kind = destFit;
400 }
401
402 // unknown link kind
403 } else {
404 error(category: errSyntaxWarning, pos: -1, msg: "Unknown annotation destination type");
405 return;
406 }
407
408 ok = true;
409}
410
411//------------------------------------------------------------------------
412// LinkGoTo
413//------------------------------------------------------------------------
414
415LinkGoTo::LinkGoTo(const Object *destObj)
416{
417 // named destination
418 if (destObj->isName()) {
419 namedDest = std::make_unique<GooString>(args: destObj->getName());
420 } else if (destObj->isString()) {
421 namedDest = std::unique_ptr<GooString>(destObj->getString()->copy());
422
423 // destination dictionary
424 } else if (destObj->isArray()) {
425 dest = std::make_unique<LinkDest>(args: destObj->getArray());
426 if (!dest->isOk()) {
427 dest.reset();
428 }
429
430 // error
431 } else {
432 error(category: errSyntaxWarning, pos: -1, msg: "Illegal annotation destination");
433 }
434}
435
436LinkGoTo::~LinkGoTo() = default;
437
438//------------------------------------------------------------------------
439// LinkGoToR
440//------------------------------------------------------------------------
441
442LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj)
443{
444 // get file name
445 Object obj1 = getFileSpecNameForPlatform(fileSpec: fileSpecObj);
446 if (obj1.isString()) {
447 fileName = std::unique_ptr<GooString>(obj1.getString()->copy());
448 }
449
450 // named destination
451 if (destObj->isName()) {
452 namedDest = std::make_unique<GooString>(args: destObj->getName());
453 } else if (destObj->isString()) {
454 namedDest = std::unique_ptr<GooString>(destObj->getString()->copy());
455
456 // destination dictionary
457 } else if (destObj->isArray()) {
458 dest = std::make_unique<LinkDest>(args: destObj->getArray());
459 if (!dest->isOk()) {
460 dest.reset();
461 }
462
463 // error
464 } else {
465 error(category: errSyntaxWarning, pos: -1, msg: "Illegal annotation destination");
466 }
467}
468
469LinkGoToR::~LinkGoToR() = default;
470
471//------------------------------------------------------------------------
472// LinkLaunch
473//------------------------------------------------------------------------
474
475LinkLaunch::LinkLaunch(const Object *actionObj)
476{
477
478 if (actionObj->isDict()) {
479 Object obj1 = actionObj->dictLookup(key: "F");
480 if (!obj1.isNull()) {
481 Object obj3 = getFileSpecNameForPlatform(fileSpec: &obj1);
482 if (obj3.isString()) {
483 fileName = std::unique_ptr<GooString>(obj3.getString()->copy());
484 }
485 } else {
486#ifdef _WIN32
487 obj1 = actionObj->dictLookup("Win");
488#else
489 //~ This hasn't been defined by Adobe yet, so assume it looks
490 //~ just like the Win dictionary until they say otherwise.
491 obj1 = actionObj->dictLookup(key: "Unix");
492#endif
493 if (obj1.isDict()) {
494 Object obj2 = obj1.dictLookup(key: "F");
495 Object obj3 = getFileSpecNameForPlatform(fileSpec: &obj2);
496 if (obj3.isString()) {
497 fileName = std::unique_ptr<GooString>(obj3.getString()->copy());
498 }
499 obj2 = obj1.dictLookup(key: "P");
500 if (obj2.isString()) {
501 params = std::unique_ptr<GooString>(obj2.getString()->copy());
502 }
503 } else {
504 error(category: errSyntaxWarning, pos: -1, msg: "Bad launch-type link action");
505 }
506 }
507 }
508}
509
510LinkLaunch::~LinkLaunch() = default;
511
512//------------------------------------------------------------------------
513// LinkURI
514//------------------------------------------------------------------------
515
516LinkURI::LinkURI(const Object *uriObj, const std::optional<std::string> &baseURI)
517{
518 hasURIFlag = false;
519 if (uriObj->isString()) {
520 hasURIFlag = true;
521 const std::string &uri2 = uriObj->getString()->toStr();
522 size_t n = strcspn(s: uri2.c_str(), reject: "/:");
523 if (n < uri2.size() && uri2[n] == ':') {
524 // "http:..." etc.
525 uri = uri2;
526 } else if (!uri2.compare(pos: 0, n1: 4, s: "www.")) {
527 // "www.[...]" without the leading "http://"
528 uri = "http://" + uri2;
529 } else {
530 // relative URI
531 if (baseURI) {
532 uri = *baseURI;
533 if (uri.size() > 0) {
534 char c = uri.back();
535 if (c != '/' && c != '?') {
536 uri += '/';
537 }
538 }
539 if (uri2[0] == '/') {
540 uri.append(s: uri2.c_str() + 1, n: uri2.size() - 1);
541 } else {
542 uri += uri2;
543 }
544 } else {
545 uri = uri2;
546 }
547 }
548 } else {
549 error(category: errSyntaxWarning, pos: -1, msg: "Illegal URI-type link");
550 }
551}
552
553LinkURI::~LinkURI() = default;
554
555//------------------------------------------------------------------------
556// LinkNamed
557//------------------------------------------------------------------------
558
559LinkNamed::LinkNamed(const Object *nameObj)
560{
561 hasNameFlag = false;
562 if (nameObj->isName()) {
563 name = (nameObj->getName()) ? nameObj->getName() : "";
564 hasNameFlag = true;
565 }
566}
567
568LinkNamed::~LinkNamed() = default;
569
570//------------------------------------------------------------------------
571// LinkMovie
572//------------------------------------------------------------------------
573
574LinkMovie::LinkMovie(const Object *obj)
575{
576 annotRef = Ref::INVALID();
577 hasAnnotTitleFlag = false;
578
579 const Object &annotationObj = obj->dictLookupNF(key: "Annotation");
580 if (annotationObj.isRef()) {
581 annotRef = annotationObj.getRef();
582 }
583
584 Object tmp = obj->dictLookup(key: "T");
585 if (tmp.isString()) {
586 annotTitle = tmp.getString()->toStr();
587 hasAnnotTitleFlag = true;
588 }
589
590 if ((!hasAnnotTitleFlag) && (annotRef == Ref::INVALID())) {
591 error(category: errSyntaxError, pos: -1, msg: "Movie action is missing both the Annot and T keys");
592 }
593
594 tmp = obj->dictLookup(key: "Operation");
595 if (tmp.isName()) {
596 const char *name = tmp.getName();
597
598 if (!strcmp(s1: name, s2: "Play")) {
599 operation = operationTypePlay;
600 } else if (!strcmp(s1: name, s2: "Stop")) {
601 operation = operationTypeStop;
602 } else if (!strcmp(s1: name, s2: "Pause")) {
603 operation = operationTypePause;
604 } else if (!strcmp(s1: name, s2: "Resume")) {
605 operation = operationTypeResume;
606 }
607 }
608}
609
610LinkMovie::~LinkMovie() = default;
611
612//------------------------------------------------------------------------
613// LinkSound
614//------------------------------------------------------------------------
615
616LinkSound::LinkSound(const Object *soundObj)
617{
618 volume = 1.0;
619 sync = false;
620 repeat = false;
621 mix = false;
622 sound = nullptr;
623 if (soundObj->isDict()) {
624 // volume
625 Object tmp = soundObj->dictLookup(key: "Volume");
626 if (tmp.isNum()) {
627 volume = tmp.getNum();
628 }
629 // sync
630 tmp = soundObj->dictLookup(key: "Synchronous");
631 if (tmp.isBool()) {
632 sync = tmp.getBool();
633 }
634 // repeat
635 tmp = soundObj->dictLookup(key: "Repeat");
636 if (tmp.isBool()) {
637 repeat = tmp.getBool();
638 }
639 // mix
640 tmp = soundObj->dictLookup(key: "Mix");
641 if (tmp.isBool()) {
642 mix = tmp.getBool();
643 }
644 // 'Sound' object
645 tmp = soundObj->dictLookup(key: "Sound");
646 sound = Sound::parseSound(obj: &tmp);
647 }
648}
649
650LinkSound::~LinkSound() = default;
651
652//------------------------------------------------------------------------
653// LinkRendition
654//------------------------------------------------------------------------
655
656LinkRendition::LinkRendition(const Object *obj)
657{
658 operation = NoRendition;
659 media = nullptr;
660 int operationCode = -1;
661
662 screenRef = Ref::INVALID();
663
664 if (obj->isDict()) {
665 Object tmp = obj->dictLookup(key: "JS");
666 if (!tmp.isNull()) {
667 if (tmp.isString()) {
668 js = tmp.getString()->toStr();
669 } else if (tmp.isStream()) {
670 Stream *stream = tmp.getStream();
671 stream->fillString(s&: js);
672 } else {
673 error(category: errSyntaxWarning, pos: -1, msg: "Invalid Rendition Action: JS not string or stream");
674 }
675 }
676
677 tmp = obj->dictLookup(key: "OP");
678 if (tmp.isInt()) {
679 operationCode = tmp.getInt();
680 if (js.empty() && (operationCode < 0 || operationCode > 4)) {
681 error(category: errSyntaxWarning, pos: -1, msg: "Invalid Rendition Action: unrecognized operation valued: {0:d}", operationCode);
682 } else {
683 // retrieve rendition object
684 Object renditionObj = obj->dictLookup(key: "R");
685 if (renditionObj.isDict()) {
686 media = new MediaRendition(&renditionObj);
687 } else if (operationCode == 0 || operationCode == 4) {
688 error(category: errSyntaxWarning, pos: -1, msg: "Invalid Rendition Action: no R field with op = {0:d}", operationCode);
689 renditionObj.setToNull();
690 }
691
692 const Object &anObj = obj->dictLookupNF(key: "AN");
693 if (anObj.isRef()) {
694 screenRef = anObj.getRef();
695 } else if (operation >= 0 && operation <= 4) {
696 error(category: errSyntaxWarning, pos: -1, msg: "Invalid Rendition Action: no AN field with op = {0:d}", operationCode);
697 }
698 }
699
700 switch (operationCode) {
701 case 0:
702 operation = PlayRendition;
703 break;
704 case 1:
705 operation = StopRendition;
706 break;
707 case 2:
708 operation = PauseRendition;
709 break;
710 case 3:
711 operation = ResumeRendition;
712 break;
713 case 4:
714 operation = PlayRendition;
715 break;
716 }
717 } else if (js == "") {
718 error(category: errSyntaxWarning, pos: -1, msg: "Invalid Rendition action: no OP or JS field defined");
719 }
720 }
721}
722
723LinkRendition::~LinkRendition()
724{
725 delete media;
726}
727
728//------------------------------------------------------------------------
729// LinkJavaScript
730//------------------------------------------------------------------------
731
732LinkJavaScript::LinkJavaScript(Object *jsObj)
733{
734 isValid = false;
735
736 if (jsObj->isString()) {
737 js = jsObj->getString()->toStr();
738 isValid = true;
739 } else if (jsObj->isStream()) {
740 Stream *stream = jsObj->getStream();
741 stream->fillString(s&: js);
742 isValid = true;
743 }
744}
745
746LinkJavaScript::~LinkJavaScript() = default;
747
748Object LinkJavaScript::createObject(XRef *xref, const std::string &js)
749{
750 Dict *linkDict = new Dict(xref);
751 linkDict->add(key: "S", val: Object(objName, "JavaScript"));
752 linkDict->add(key: "JS", val: Object(new GooString(js)));
753
754 return Object(linkDict);
755}
756
757//------------------------------------------------------------------------
758// LinkOCGState
759//------------------------------------------------------------------------
760LinkOCGState::LinkOCGState(const Object *obj) : isValid(true)
761{
762 Object obj1 = obj->dictLookup(key: "State");
763 if (obj1.isArray()) {
764 StateList stList;
765
766 for (int i = 0; i < obj1.arrayGetLength(); ++i) {
767 const Object &obj2 = obj1.arrayGetNF(i);
768 if (obj2.isName()) {
769 if (!stList.list.empty()) {
770 stateList.push_back(x: stList);
771 }
772
773 const char *name = obj2.getName();
774 stList.list.clear();
775 if (!strcmp(s1: name, s2: "ON")) {
776 stList.st = On;
777 } else if (!strcmp(s1: name, s2: "OFF")) {
778 stList.st = Off;
779 } else if (!strcmp(s1: name, s2: "Toggle")) {
780 stList.st = Toggle;
781 } else {
782 error(category: errSyntaxWarning, pos: -1, msg: "Invalid name '{0:s}' in OCG Action state array", name);
783 isValid = false;
784 }
785 } else if (obj2.isRef()) {
786 stList.list.push_back(x: obj2.getRef());
787 } else {
788 error(category: errSyntaxWarning, pos: -1, msg: "Invalid item in OCG Action State array");
789 isValid = false;
790 }
791 }
792 // Add the last group
793 if (!stList.list.empty()) {
794 stateList.push_back(x: stList);
795 }
796 } else {
797 error(category: errSyntaxWarning, pos: -1, msg: "Invalid OCGState action");
798 isValid = false;
799 }
800
801 preserveRB = obj->dictLookup(key: "PreserveRB").getBoolWithDefaultValue(defaultValue: true);
802}
803
804LinkOCGState::~LinkOCGState() = default;
805
806//------------------------------------------------------------------------
807// LinkHide
808//------------------------------------------------------------------------
809
810LinkHide::LinkHide(const Object *hideObj)
811{
812 hasTargetNameFlag = false;
813 show = false; // Default
814
815 if (hideObj->isDict()) {
816 const Object targetObj = hideObj->dictLookup(key: "T");
817 if (targetObj.isString()) {
818 targetName = targetObj.getString()->toStr();
819 hasTargetNameFlag = true;
820 }
821 const Object shouldHide = hideObj->dictLookup(key: "H");
822 if (shouldHide.isBool()) {
823 show = !shouldHide.getBool();
824 }
825 }
826}
827
828LinkHide::~LinkHide() = default;
829
830//------------------------------------------------------------------------
831// LinkResetForm
832//------------------------------------------------------------------------
833
834LinkResetForm::LinkResetForm(const Object *obj)
835{
836 Object obj1;
837
838 exclude = false;
839
840 obj1 = obj->dictLookup(key: "Fields");
841 if (obj1.isArray()) {
842 fields.resize(new_size: obj1.arrayGetLength());
843 for (int i = 0; i < obj1.arrayGetLength(); ++i) {
844 const Object &obj2 = obj1.arrayGetNF(i);
845 if (obj2.isName()) {
846 fields[i] = std::string(obj2.getName());
847 } else if (obj2.isString()) {
848 fields[i] = obj2.getString()->toStr();
849 } else if (obj2.isRef()) {
850 fields[i] = std::to_string(val: obj2.getRef().num);
851 fields[i].append(s: " ");
852 fields[i].append(str: std::to_string(val: obj2.getRef().gen));
853 fields[i].append(s: " R");
854 } else {
855 error(category: errSyntaxWarning, pos: -1, msg: "LinkResetForm: unexpected Field type");
856 }
857 }
858 }
859
860 obj1 = obj->dictLookup(key: "Flags");
861 if (obj1.isInt()) {
862 int flags = obj1.getInt();
863
864 if (flags & 0x1) {
865 exclude = true;
866 }
867 }
868}
869
870LinkResetForm::~LinkResetForm() = default;
871
872//------------------------------------------------------------------------
873// LinkUnknown
874//------------------------------------------------------------------------
875
876LinkUnknown::LinkUnknown(const char *actionA)
877{
878 action = std::string(actionA ? actionA : "");
879}
880
881LinkUnknown::~LinkUnknown() = default;
882
883//------------------------------------------------------------------------
884// Links
885//------------------------------------------------------------------------
886
887Links::Links(Annots *annots)
888{
889 if (!annots) {
890 return;
891 }
892
893 for (Annot *annot : annots->getAnnots()) {
894
895 if (annot->getType() != Annot::typeLink) {
896 continue;
897 }
898
899 annot->incRefCnt();
900 links.push_back(x: static_cast<AnnotLink *>(annot));
901 }
902}
903
904Links::~Links()
905{
906 for (AnnotLink *link : links) {
907 link->decRefCnt();
908 }
909}
910

source code of poppler/poppler/Link.cc