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 Carlos Garcia Campos <carlosgc@gnome.org>
18 // Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
19 // Copyright (C) 2008-2010 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 //
23 // To see a description of the changes please see the Changelog file that
24 // came with your tarball or type make ChangeLog if you are building from git
25 //
26 //========================================================================
27
28 #include <config.h>
29
30 #ifdef USE_GCC_PRAGMAS
31 #pragma implementation
32 #endif
33
34 #include <stddef.h>
35 #include <string.h>
36 #include "goo/gmem.h"
37 #include "goo/GooString.h"
38 #include "goo/GooList.h"
39 #include "Error.h"
40 #include "Object.h"
41 #include "Array.h"
42 #include "Dict.h"
43 #include "Link.h"
44 #include "Sound.h"
45 #include "FileSpec.h"
46 #include "Rendition.h"
47
48 //------------------------------------------------------------------------
49 // LinkAction
50 //------------------------------------------------------------------------
51
parseDest(Object * obj)52 LinkAction *LinkAction::parseDest(Object *obj) {
53 LinkAction *action;
54
55 action = new LinkGoTo(obj);
56 if (!action->isOk()) {
57 delete action;
58 return NULL;
59 }
60 return action;
61 }
62
parseAction(Object * obj,GooString * baseURI)63 LinkAction *LinkAction::parseAction(Object *obj, GooString *baseURI) {
64 LinkAction *action;
65 Object obj2, obj3, obj4;
66
67 if (!obj->isDict()) {
68 error(-1, "parseAction: Bad annotation action for URI '%s'",
69 baseURI ? baseURI->getCString() : "NULL");
70 return NULL;
71 }
72
73 obj->dictLookup("S", &obj2);
74
75 // GoTo action
76 if (obj2.isName("GoTo")) {
77 obj->dictLookup("D", &obj3);
78 action = new LinkGoTo(&obj3);
79 obj3.free();
80
81 // GoToR action
82 } else if (obj2.isName("GoToR")) {
83 obj->dictLookup("F", &obj3);
84 obj->dictLookup("D", &obj4);
85 action = new LinkGoToR(&obj3, &obj4);
86 obj3.free();
87 obj4.free();
88
89 // Launch action
90 } else if (obj2.isName("Launch")) {
91 action = new LinkLaunch(obj);
92
93 // URI action
94 } else if (obj2.isName("URI")) {
95 obj->dictLookup("URI", &obj3);
96 action = new LinkURI(&obj3, baseURI);
97 obj3.free();
98
99 // Named action
100 } else if (obj2.isName("Named")) {
101 obj->dictLookup("N", &obj3);
102 action = new LinkNamed(&obj3);
103 obj3.free();
104
105 // Movie action
106 } else if (obj2.isName("Movie")) {
107 action = new LinkMovie(obj);
108
109 // Rendition action
110 } else if (obj2.isName("Rendition")) {
111 action = new LinkRendition(obj);
112
113 // Sound action
114 } else if (obj2.isName("Sound")) {
115 action = new LinkSound(obj);
116
117 // JavaScript action
118 } else if (obj2.isName("JavaScript")) {
119 obj->dictLookup("JS", &obj3);
120 action = new LinkJavaScript(&obj3);
121 obj3.free();
122
123 // Set-OCG-State action
124 } else if (obj2.isName("SetOCGState")) {
125 action = new LinkOCGState(obj);
126
127 // unknown action
128 } else if (obj2.isName()) {
129 action = new LinkUnknown(obj2.getName());
130
131 // action is missing or wrong type
132 } else {
133 error(-1, "parseAction: Unknown annotation action object: URI = '%s'",
134 baseURI ? baseURI->getCString() : "NULL");
135 action = NULL;
136 }
137
138 obj2.free();
139
140 if (action && !action->isOk()) {
141 delete action;
142 return NULL;
143 }
144 return action;
145 }
146
147 //------------------------------------------------------------------------
148 // LinkDest
149 //------------------------------------------------------------------------
150
LinkDest(Array * a)151 LinkDest::LinkDest(Array *a) {
152 Object obj1, obj2;
153
154 // initialize fields
155 left = bottom = right = top = zoom = 0;
156 changeLeft = changeTop = changeZoom = gFalse;
157 ok = gFalse;
158
159 // get page
160 if (a->getLength() < 2) {
161 error(-1, "Annotation destination array is too short");
162 return;
163 }
164 a->getNF(0, &obj1);
165 if (obj1.isInt()) {
166 pageNum = obj1.getInt() + 1;
167 pageIsRef = gFalse;
168 } else if (obj1.isRef()) {
169 pageRef.num = obj1.getRefNum();
170 pageRef.gen = obj1.getRefGen();
171 pageIsRef = gTrue;
172 } else {
173 error(-1, "Bad annotation destination");
174 goto err2;
175 }
176 obj1.free();
177
178 // get destination type
179 a->get(1, &obj1);
180
181 // XYZ link
182 if (obj1.isName("XYZ")) {
183 kind = destXYZ;
184 if (a->getLength() < 3) {
185 changeLeft = gFalse;
186 } else {
187 a->get(2, &obj2);
188 if (obj2.isNull()) {
189 changeLeft = gFalse;
190 } else if (obj2.isNum()) {
191 changeLeft = gTrue;
192 left = obj2.getNum();
193 } else {
194 error(-1, "Bad annotation destination position");
195 goto err1;
196 }
197 obj2.free();
198 }
199 if (a->getLength() < 4) {
200 changeTop = gFalse;
201 } else {
202 a->get(3, &obj2);
203 if (obj2.isNull()) {
204 changeTop = gFalse;
205 } else if (obj2.isNum()) {
206 changeTop = gTrue;
207 top = obj2.getNum();
208 } else {
209 error(-1, "Bad annotation destination position");
210 goto err1;
211 }
212 obj2.free();
213 }
214 if (a->getLength() < 5) {
215 changeZoom = gFalse;
216 } else {
217 a->get(4, &obj2);
218 if (obj2.isNull()) {
219 changeZoom = gFalse;
220 } else if (obj2.isNum()) {
221 changeZoom = gTrue;
222 zoom = obj2.getNum();
223 } else {
224 error(-1, "Bad annotation destination position");
225 goto err1;
226 }
227 obj2.free();
228 }
229
230 // Fit link
231 } else if (obj1.isName("Fit")) {
232 if (a->getLength() < 2) {
233 error(-1, "Annotation destination array is too short");
234 goto err2;
235 }
236 kind = destFit;
237
238 // FitH link
239 } else if (obj1.isName("FitH")) {
240 if (a->getLength() < 3) {
241 error(-1, "Annotation destination array is too short");
242 goto err2;
243 }
244 kind = destFitH;
245 a->get(2, &obj2);
246 if (obj2.isNull()) {
247 changeTop = gFalse;
248 } else if (obj2.isNum()) {
249 changeTop = gTrue;
250 top = obj2.getNum();
251 } else {
252 error(-1, "Bad annotation destination position");
253 kind = destFit;
254 }
255 obj2.free();
256
257 // FitV link
258 } else if (obj1.isName("FitV")) {
259 if (a->getLength() < 3) {
260 error(-1, "Annotation destination array is too short");
261 goto err2;
262 }
263 kind = destFitV;
264 a->get(2, &obj2);
265 if (obj2.isNull()) {
266 changeLeft = gFalse;
267 } else if (obj2.isNum()) {
268 changeLeft = gTrue;
269 left = obj2.getNum();
270 } else {
271 error(-1, "Bad annotation destination position");
272 kind = destFit;
273 }
274 obj2.free();
275
276 // FitR link
277 } else if (obj1.isName("FitR")) {
278 if (a->getLength() < 6) {
279 error(-1, "Annotation destination array is too short");
280 goto err2;
281 }
282 kind = destFitR;
283 if (!a->get(2, &obj2)->isNum()) {
284 error(-1, "Bad annotation destination position");
285 kind = destFit;
286 }
287 left = obj2.getNum();
288 obj2.free();
289 if (!a->get(3, &obj2)->isNum()) {
290 error(-1, "Bad annotation destination position");
291 kind = destFit;
292 }
293 bottom = obj2.getNum();
294 obj2.free();
295 if (!a->get(4, &obj2)->isNum()) {
296 error(-1, "Bad annotation destination position");
297 kind = destFit;
298 }
299 right = obj2.getNum();
300 obj2.free();
301 if (!a->get(5, &obj2)->isNum()) {
302 error(-1, "Bad annotation destination position");
303 kind = destFit;
304 }
305 top = obj2.getNum();
306 obj2.free();
307
308 // FitB link
309 } else if (obj1.isName("FitB")) {
310 if (a->getLength() < 2) {
311 error(-1, "Annotation destination array is too short");
312 goto err2;
313 }
314 kind = destFitB;
315
316 // FitBH link
317 } else if (obj1.isName("FitBH")) {
318 if (a->getLength() < 3) {
319 error(-1, "Annotation destination array is too short");
320 goto err2;
321 }
322 kind = destFitBH;
323 a->get(2, &obj2);
324 if (obj2.isNull()) {
325 changeTop = gFalse;
326 } else if (obj2.isNum()) {
327 changeTop = gTrue;
328 top = obj2.getNum();
329 } else {
330 error(-1, "Bad annotation destination position");
331 kind = destFit;
332 }
333 obj2.free();
334
335 // FitBV link
336 } else if (obj1.isName("FitBV")) {
337 if (a->getLength() < 3) {
338 error(-1, "Annotation destination array is too short");
339 goto err2;
340 }
341 kind = destFitBV;
342 a->get(2, &obj2);
343 if (obj2.isNull()) {
344 changeLeft = gFalse;
345 } else if (obj2.isNum()) {
346 changeLeft = gTrue;
347 left = obj2.getNum();
348 } else {
349 error(-1, "Bad annotation destination position");
350 kind = destFit;
351 }
352 obj2.free();
353
354 // unknown link kind
355 } else {
356 error(-1, "Unknown annotation destination type");
357 goto err2;
358 }
359
360 obj1.free();
361 ok = gTrue;
362 return;
363
364 err1:
365 obj2.free();
366 err2:
367 obj1.free();
368 }
369
LinkDest(LinkDest * dest)370 LinkDest::LinkDest(LinkDest *dest) {
371 kind = dest->kind;
372 pageIsRef = dest->pageIsRef;
373 if (pageIsRef)
374 pageRef = dest->pageRef;
375 else
376 pageNum = dest->pageNum;
377 left = dest->left;
378 bottom = dest->bottom;
379 right = dest->right;
380 top = dest->top;
381 zoom = dest->zoom;
382 changeLeft = dest->changeLeft;
383 changeTop = dest->changeTop;
384 changeZoom = dest->changeZoom;
385 ok = gTrue;
386 }
387
388 //------------------------------------------------------------------------
389 // LinkGoTo
390 //------------------------------------------------------------------------
391
LinkGoTo(Object * destObj)392 LinkGoTo::LinkGoTo(Object *destObj) {
393 dest = NULL;
394 namedDest = NULL;
395
396 // named destination
397 if (destObj->isName()) {
398 namedDest = new GooString(destObj->getName());
399 } else if (destObj->isString()) {
400 namedDest = destObj->getString()->copy();
401
402 // destination dictionary
403 } else if (destObj->isArray()) {
404 dest = new LinkDest(destObj->getArray());
405 if (!dest->isOk()) {
406 delete dest;
407 dest = NULL;
408 }
409
410 // error
411 } else {
412 error(-1, "Illegal annotation destination");
413 }
414 }
415
~LinkGoTo()416 LinkGoTo::~LinkGoTo() {
417 if (dest)
418 delete dest;
419 if (namedDest)
420 delete namedDest;
421 }
422
423 //------------------------------------------------------------------------
424 // LinkGoToR
425 //------------------------------------------------------------------------
426
LinkGoToR(Object * fileSpecObj,Object * destObj)427 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
428 fileName = NULL;
429 dest = NULL;
430 namedDest = NULL;
431
432 // get file name
433 Object obj1;
434 if (getFileSpecNameForPlatform (fileSpecObj, &obj1)) {
435 fileName = obj1.getString()->copy();
436 obj1.free();
437 }
438
439 // named destination
440 if (destObj->isName()) {
441 namedDest = new GooString(destObj->getName());
442 } else if (destObj->isString()) {
443 namedDest = destObj->getString()->copy();
444
445 // destination dictionary
446 } else if (destObj->isArray()) {
447 dest = new LinkDest(destObj->getArray());
448 if (!dest->isOk()) {
449 delete dest;
450 dest = NULL;
451 }
452
453 // error
454 } else {
455 error(-1, "Illegal annotation destination");
456 }
457 }
458
~LinkGoToR()459 LinkGoToR::~LinkGoToR() {
460 if (fileName)
461 delete fileName;
462 if (dest)
463 delete dest;
464 if (namedDest)
465 delete namedDest;
466 }
467
468
469 //------------------------------------------------------------------------
470 // LinkLaunch
471 //------------------------------------------------------------------------
472
LinkLaunch(Object * actionObj)473 LinkLaunch::LinkLaunch(Object *actionObj) {
474 Object obj1, obj2, obj3;
475
476 fileName = NULL;
477 params = NULL;
478
479 if (actionObj->isDict()) {
480 if (!actionObj->dictLookup("F", &obj1)->isNull()) {
481 if (getFileSpecNameForPlatform (&obj1, &obj3)) {
482 fileName = obj3.getString()->copy();
483 obj3.free();
484 }
485 } else {
486 obj1.free();
487 #ifdef _WIN32
488 if (actionObj->dictLookup("Win", &obj1)->isDict()) {
489 obj1.dictLookup("F", &obj2);
490 if (getFileSpecNameForPlatform (&obj2, &obj3)) {
491 fileName = obj3.getString()->copy();
492 obj3.free();
493 }
494 obj2.free();
495 if (obj1.dictLookup("P", &obj2)->isString()) {
496 params = obj2.getString()->copy();
497 }
498 obj2.free();
499 } else {
500 error(-1, "Bad launch-type link action");
501 }
502 #else
503 //~ This hasn't been defined by Adobe yet, so assume it looks
504 //~ just like the Win dictionary until they say otherwise.
505 if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
506 obj1.dictLookup("F", &obj2);
507 if (getFileSpecNameForPlatform (&obj2, &obj3)) {
508 fileName = obj3.getString()->copy();
509 obj3.free();
510 }
511 obj2.free();
512 if (obj1.dictLookup("P", &obj2)->isString()) {
513 params = obj2.getString()->copy();
514 }
515 obj2.free();
516 } else {
517 error(-1, "Bad launch-type link action");
518 }
519 #endif
520 }
521 obj1.free();
522 }
523 }
524
~LinkLaunch()525 LinkLaunch::~LinkLaunch() {
526 if (fileName)
527 delete fileName;
528 if (params)
529 delete params;
530 }
531
532 //------------------------------------------------------------------------
533 // LinkURI
534 //------------------------------------------------------------------------
535
LinkURI(Object * uriObj,GooString * baseURI)536 LinkURI::LinkURI(Object *uriObj, GooString *baseURI) {
537 GooString *uri2;
538 int n;
539 char c;
540
541 uri = NULL;
542 if (uriObj->isString()) {
543 uri2 = uriObj->getString()->copy();
544 if (baseURI && baseURI->getLength() > 0) {
545 n = strcspn(uri2->getCString(), "/:");
546 if (n == uri2->getLength() || uri2->getChar(n) == '/') {
547 uri = baseURI->copy();
548 c = uri->getChar(uri->getLength() - 1);
549 if (c == '/' || c == '?') {
550 if (uri2->getChar(0) == '/') {
551 uri2->del(0);
552 }
553 } else {
554 if (uri2->getChar(0) != '/') {
555 uri->append('/');
556 }
557 }
558 uri->append(uri2);
559 delete uri2;
560 } else {
561 uri = uri2;
562 }
563 } else {
564 uri = uri2;
565 }
566 } else {
567 error(-1, "Illegal URI-type link");
568 }
569 }
570
~LinkURI()571 LinkURI::~LinkURI() {
572 if (uri)
573 delete uri;
574 }
575
576 //------------------------------------------------------------------------
577 // LinkNamed
578 //------------------------------------------------------------------------
579
LinkNamed(Object * nameObj)580 LinkNamed::LinkNamed(Object *nameObj) {
581 name = NULL;
582 if (nameObj->isName()) {
583 name = new GooString(nameObj->getName());
584 }
585 }
586
~LinkNamed()587 LinkNamed::~LinkNamed() {
588 if (name) {
589 delete name;
590 }
591 }
592
593 //------------------------------------------------------------------------
594 // LinkMovie
595 //------------------------------------------------------------------------
596
LinkMovie(Object * obj)597 LinkMovie::LinkMovie(Object *obj) {
598 annotRef.num = -1;
599 annotTitle = NULL;
600
601 Object tmp;
602 if (obj->dictLookupNF("Annotation", &tmp)->isRef()) {
603 annotRef = tmp.getRef();
604 }
605 tmp.free();
606
607 if (obj->dictLookup("T", &tmp)->isString()) {
608 annotTitle = tmp.getString()->copy();
609 }
610 tmp.free();
611
612 if ((annotTitle == NULL) && (annotRef.num == -1)) {
613 error(-1, "Movie action is missing both the Annot and T keys");
614 }
615
616 if (obj->dictLookup("Operation", &tmp)->isName()) {
617 char *name = tmp.getName();
618
619 if (!strcmp(name, "Play")) {
620 operation = operationTypePlay;
621 }
622 else if (!strcmp(name, "Stop")) {
623 operation = operationTypeStop;
624 }
625 else if (!strcmp(name, "Pause")) {
626 operation = operationTypePause;
627 }
628 else if (!strcmp(name, "Resume")) {
629 operation = operationTypeResume;
630 }
631 }
632 tmp.free();
633 }
634
~LinkMovie()635 LinkMovie::~LinkMovie() {
636 if (annotTitle) {
637 delete annotTitle;
638 }
639 }
640
641 //------------------------------------------------------------------------
642 // LinkSound
643 //------------------------------------------------------------------------
644
LinkSound(Object * soundObj)645 LinkSound::LinkSound(Object *soundObj) {
646 volume = 1.0;
647 sync = gFalse;
648 repeat = gFalse;
649 mix = gFalse;
650 sound = NULL;
651 if (soundObj->isDict())
652 {
653 Object tmp;
654 // volume
655 soundObj->dictLookup("Volume", &tmp);
656 if (tmp.isNum()) {
657 volume = tmp.getNum();
658 }
659 tmp.free();
660 // sync
661 soundObj->dictLookup("Synchronous", &tmp);
662 if (tmp.isBool()) {
663 sync = tmp.getBool();
664 }
665 tmp.free();
666 // repeat
667 soundObj->dictLookup("Repeat", &tmp);
668 if (tmp.isBool()) {
669 repeat = tmp.getBool();
670 }
671 tmp.free();
672 // mix
673 soundObj->dictLookup("Mix", &tmp);
674 if (tmp.isBool()) {
675 mix = tmp.getBool();
676 }
677 tmp.free();
678 // 'Sound' object
679 soundObj->dictLookup("Sound", &tmp);
680 sound = Sound::parseSound(&tmp);
681 tmp.free();
682 }
683 }
684
~LinkSound()685 LinkSound::~LinkSound() {
686 delete sound;
687 }
688
689 //------------------------------------------------------------------------
690 // LinkRendition
691 //------------------------------------------------------------------------
692
LinkRendition(Object * obj)693 LinkRendition::LinkRendition(Object *obj) {
694 operation = -1;
695 media = NULL;
696 js = NULL;
697
698 if (obj->isDict()) {
699 Object tmp;
700
701 if (!obj->dictLookup("JS", &tmp)->isNull()) {
702 if (tmp.isString()) {
703 js = new GooString(tmp.getString());
704 } else if (tmp.isStream()) {
705 Stream *stream = tmp.getStream();
706 js = new GooString();
707 stream->fillGooString(js);
708 } else {
709 error(-1, "Invalid Rendition Action: JS not string or stream");
710 }
711 }
712 tmp.free();
713
714 if (obj->dictLookup("OP", &tmp)->isInt()) {
715 operation = tmp.getInt();
716 if (!js && (operation < 0 || operation > 4)) {
717 error (-1, "Invalid Rendition Action: unrecognized operation valued: %d", operation);
718 } else {
719 Object obj1;
720
721 // retrieve rendition object
722 if (obj->dictLookup("R", &renditionObj)->isDict()) {
723 media = new MediaRendition(&renditionObj);
724 } else if (operation == 0 || operation == 4) {
725 error (-1, "Invalid Rendition Action: no R field with op = %d", operation);
726 renditionObj.free();
727 }
728
729 if (!obj->dictLookupNF("AN", &screenRef)->isRef() && operation >= 0 && operation <= 4) {
730 error (-1, "Invalid Rendition Action: no AN field with op = %d", operation);
731 screenRef.free();
732 }
733 }
734 } else if (!js) {
735 error(-1, "Invalid Rendition action: no OP or JS field defined");
736 }
737 tmp.free();
738 }
739 }
740
~LinkRendition()741 LinkRendition::~LinkRendition() {
742 renditionObj.free();
743 screenRef.free();
744
745 if (js)
746 delete js;
747 if (media)
748 delete media;
749 }
750
751
752 //------------------------------------------------------------------------
753 // LinkJavaScript
754 //------------------------------------------------------------------------
755
LinkJavaScript(Object * jsObj)756 LinkJavaScript::LinkJavaScript(Object *jsObj) {
757 js = NULL;
758
759 if (jsObj->isString()) {
760 js = new GooString(jsObj->getString());
761 }
762 else if (jsObj->isStream()) {
763 Stream *stream = jsObj->getStream();
764 js = new GooString();
765 stream->fillGooString(js);
766 }
767 }
768
~LinkJavaScript()769 LinkJavaScript::~LinkJavaScript() {
770 if (js) {
771 delete js;
772 }
773 }
774
775 //------------------------------------------------------------------------
776 // LinkOCGState
777 //------------------------------------------------------------------------
LinkOCGState(Object * obj)778 LinkOCGState::LinkOCGState(Object *obj) {
779 Object obj1;
780
781 stateList = new GooList();
782 preserveRB = gTrue;
783
784 if (obj->dictLookup("State", &obj1)->isArray()) {
785 StateList *stList = NULL;
786
787 for (int i = 0; i < obj1.arrayGetLength(); ++i) {
788 Object obj2;
789
790 obj1.arrayGetNF(i, &obj2);
791 if (obj2.isName()) {
792 if (stList)
793 stateList->append(stList);
794
795 char *name = obj2.getName();
796 stList = new StateList();
797 stList->list = new GooList();
798 if (!strcmp (name, "ON")) {
799 stList->st = On;
800 } else if (!strcmp (name, "OFF")) {
801 stList->st = Off;
802 } else if (!strcmp (name, "Toggle")) {
803 stList->st = Toggle;
804 } else {
805 error (-1, "Invalid name '%s' in OCG Action state array", name);
806 delete stList;
807 stList = NULL;
808 }
809 } else if (obj2.isRef()) {
810 if (stList) {
811 Ref ocgRef = obj2.getRef();
812 Ref *item = new Ref();
813 item->num = ocgRef.num;
814 item->gen = ocgRef.gen;
815 stList->list->append(item);
816 } else {
817 error (-1, "Invalid OCG Action State array, expected name instead of ref");
818 }
819 } else {
820 error (-1, "Invalid item in OCG Action State array");
821 }
822 obj2.free();
823 }
824 // Add the last group
825 if (stList)
826 stateList->append(stList);
827 } else {
828 error (-1, "Invalid OCGState action");
829 delete stateList;
830 stateList = NULL;
831 }
832 obj1.free();
833
834 if (obj->dictLookup("PreserveRB", &obj1)->isBool()) {
835 preserveRB = obj1.getBool();
836 }
837 obj1.free();
838 }
839
~LinkOCGState()840 LinkOCGState::~LinkOCGState() {
841 if (stateList)
842 deleteGooList(stateList, StateList);
843 }
844
~StateList()845 LinkOCGState::StateList::~StateList() {
846 if (list)
847 deleteGooList(list, Ref);
848 }
849
850 //------------------------------------------------------------------------
851 // LinkUnknown
852 //------------------------------------------------------------------------
853
LinkUnknown(char * actionA)854 LinkUnknown::LinkUnknown(char *actionA) {
855 action = new GooString(actionA);
856 }
857
~LinkUnknown()858 LinkUnknown::~LinkUnknown() {
859 delete action;
860 }
861
862 //------------------------------------------------------------------------
863 // Link
864 //------------------------------------------------------------------------
865
Link(Dict * dict,GooString * baseURI)866 Link::Link(Dict *dict, GooString *baseURI) {
867 Object obj1, obj2;
868 double t;
869
870 action = NULL;
871 ok = gFalse;
872
873 // get rectangle
874 if (!dict->lookup("Rect", &obj1)->isArray()) {
875 error(-1, "Annotation rectangle is wrong type");
876 goto err2;
877 }
878 if (!obj1.arrayGet(0, &obj2)->isNum()) {
879 error(-1, "Bad annotation rectangle");
880 goto err1;
881 }
882 x1 = obj2.getNum();
883 obj2.free();
884 if (!obj1.arrayGet(1, &obj2)->isNum()) {
885 error(-1, "Bad annotation rectangle");
886 goto err1;
887 }
888 y1 = obj2.getNum();
889 obj2.free();
890 if (!obj1.arrayGet(2, &obj2)->isNum()) {
891 error(-1, "Bad annotation rectangle");
892 goto err1;
893 }
894 x2 = obj2.getNum();
895 obj2.free();
896 if (!obj1.arrayGet(3, &obj2)->isNum()) {
897 error(-1, "Bad annotation rectangle");
898 goto err1;
899 }
900 y2 = obj2.getNum();
901 obj2.free();
902 obj1.free();
903 if (x1 > x2) {
904 t = x1;
905 x1 = x2;
906 x2 = t;
907 }
908 if (y1 > y2) {
909 t = y1;
910 y1 = y2;
911 y2 = t;
912 }
913
914 // look for destination
915 if (!dict->lookup("Dest", &obj1)->isNull()) {
916 action = LinkAction::parseDest(&obj1);
917
918 // look for action
919 } else {
920 obj1.free();
921 if (dict->lookup("A", &obj1)->isDict()) {
922 action = LinkAction::parseAction(&obj1, baseURI);
923 }
924 }
925 obj1.free();
926
927 // check for bad action
928 if (action) {
929 ok = gTrue;
930 }
931
932 return;
933
934 err1:
935 obj2.free();
936 err2:
937 obj1.free();
938 }
939
~Link()940 Link::~Link() {
941 if (action) {
942 delete action;
943 }
944 }
945
946 //------------------------------------------------------------------------
947 // Links
948 //------------------------------------------------------------------------
949
Links(Object * annots,GooString * baseURI)950 Links::Links(Object *annots, GooString *baseURI) {
951 Link *link;
952 Object obj1, obj2;
953 int size;
954 int i;
955
956 links = NULL;
957 size = 0;
958 numLinks = 0;
959
960 if (annots->isArray()) {
961 for (i = 0; i < annots->arrayGetLength(); ++i) {
962 if (annots->arrayGet(i, &obj1)->isDict()) {
963 if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
964 link = new Link(obj1.getDict(), baseURI);
965 if (link->isOk()) {
966 if (numLinks >= size) {
967 size += 16;
968 links = (Link **)greallocn(links, size, sizeof(Link *));
969 }
970 links[numLinks++] = link;
971 } else {
972 delete link;
973 }
974 }
975 obj2.free();
976 }
977 obj1.free();
978 }
979 }
980 }
981
~Links()982 Links::~Links() {
983 int i;
984
985 for (i = 0; i < numLinks; ++i)
986 delete links[i];
987 gfree(links);
988 }
989
find(double x,double y) const990 LinkAction *Links::find(double x, double y) const {
991 int i;
992
993 for (i = numLinks - 1; i >= 0; --i) {
994 if (links[i]->inRect(x, y)) {
995 return links[i]->getAction();
996 }
997 }
998 return NULL;
999 }
1000
onLink(double x,double y) const1001 GBool Links::onLink(double x, double y) const {
1002 int i;
1003
1004 for (i = 0; i < numLinks; ++i) {
1005 if (links[i]->inRect(x, y))
1006 return gTrue;
1007 }
1008 return gFalse;
1009 }
1010