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