]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/Link.cc
c87f6ce7b5e1f2a70353cd10a1f88c26291faef8
[evince.git] / pdf / xpdf / Link.cc
1 //========================================================================
2 //
3 // Link.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stddef.h>
16 #include <string.h>
17 #include "gmem.h"
18 #include "GString.h"
19 #include "Error.h"
20 #include "Object.h"
21 #include "Array.h"
22 #include "Dict.h"
23 #include "Link.h"
24
25 //------------------------------------------------------------------------
26 // LinkAction
27 //------------------------------------------------------------------------
28
29 LinkAction *LinkAction::parseDest(Object *obj) {
30   LinkAction *action;
31
32   action = new LinkGoTo(obj);
33   if (!action->isOk()) {
34     delete action;
35     return NULL;
36   }
37   return action;
38 }
39
40 LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
41   LinkAction *action;
42   Object obj2, obj3, obj4;
43
44   if (!obj->isDict()) {
45       error(-1, "parseAction: Bad annotation action for URI '%s'",
46             baseURI ? baseURI->getCString() : "NULL");
47       return NULL;
48   }
49
50   obj->dictLookup("S", &obj2);
51
52   // GoTo action
53   if (obj2.isName("GoTo")) {
54     obj->dictLookup("D", &obj3);
55     action = new LinkGoTo(&obj3);
56     obj3.free();
57
58   // GoToR action
59   } else if (obj2.isName("GoToR")) {
60     obj->dictLookup("F", &obj3);
61     obj->dictLookup("D", &obj4);
62     action = new LinkGoToR(&obj3, &obj4);
63     obj3.free();
64     obj4.free();
65
66   // Launch action
67   } else if (obj2.isName("Launch")) {
68     action = new LinkLaunch(obj);
69
70   // URI action
71   } else if (obj2.isName("URI")) {
72     obj->dictLookup("URI", &obj3);
73     action = new LinkURI(&obj3, baseURI);
74     obj3.free();
75
76   // Named action
77   } else if (obj2.isName("Named")) {
78     obj->dictLookup("N", &obj3);
79     action = new LinkNamed(&obj3);
80     obj3.free();
81
82   // Movie action
83   } else if (obj2.isName("Movie")) {
84     obj->dictLookupNF("Annot", &obj3);
85     obj->dictLookup("T", &obj4);
86     action = new LinkMovie(&obj3, &obj4);
87     obj3.free();
88     obj4.free();
89
90   // unknown action
91   } else if (obj2.isName()) {
92     action = new LinkUnknown(obj2.getName());
93
94   // action is missing or wrong type
95   } else {
96     error(-1, "parseAction: Unknown annotation action object: URI = '%s'",
97           baseURI ? baseURI->getCString() : "NULL");
98     action = NULL;
99   }
100
101   obj2.free();
102
103   if (action && !action->isOk()) {
104     delete action;
105     return NULL;
106   }
107   return action;
108 }
109
110 GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
111   GString *name;
112   Object obj1;
113
114   name = NULL;
115
116   // string
117   if (fileSpecObj->isString()) {
118     name = fileSpecObj->getString()->copy();
119
120   // dictionary
121   } else if (fileSpecObj->isDict()) {
122     if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
123       obj1.free();
124       fileSpecObj->dictLookup("F", &obj1);
125     }
126     if (obj1.isString())
127       name = obj1.getString()->copy();
128     else
129       error(-1, "Illegal file spec in link");
130     obj1.free();
131
132   // error
133   } else {
134     error(-1, "Illegal file spec in link");
135   }
136
137   return name;
138 }
139
140 //------------------------------------------------------------------------
141 // LinkDest
142 //------------------------------------------------------------------------
143
144 LinkDest::LinkDest(Array *a) {
145   Object obj1, obj2;
146
147   // initialize fields
148   left = bottom = right = top = zoom = 0;
149   ok = gFalse;
150
151   // get page
152   if (a->getLength() < 2) {
153     error(-1, "Annotation destination array is too short");
154     return;
155   }
156   a->getNF(0, &obj1);
157   if (obj1.isInt()) {
158     pageNum = obj1.getInt() + 1;
159     pageIsRef = gFalse;
160   } else if (obj1.isRef()) {
161     pageRef.num = obj1.getRefNum();
162     pageRef.gen = obj1.getRefGen();
163     pageIsRef = gTrue;
164   } else {
165     error(-1, "Bad annotation destination");
166     goto err2;
167   }
168   obj1.free();
169
170   // get destination type
171   a->get(1, &obj1);
172
173   // XYZ link
174   if (obj1.isName("XYZ")) {
175     kind = destXYZ;
176     if (a->getLength() < 3) {
177       changeLeft = gFalse;
178     } else {
179       a->get(2, &obj2);
180       if (obj2.isNull()) {
181         changeLeft = gFalse;
182       } else if (obj2.isNum()) {
183         changeLeft = gTrue;
184         left = obj2.getNum();
185       } else {
186         error(-1, "Bad annotation destination position");
187         goto err1;
188       }
189       obj2.free();
190     }
191     if (a->getLength() < 4) {
192       changeTop = gFalse;
193     } else {
194       a->get(3, &obj2);
195       if (obj2.isNull()) {
196         changeTop = gFalse;
197       } else if (obj2.isNum()) {
198         changeTop = gTrue;
199         top = obj2.getNum();
200       } else {
201         error(-1, "Bad annotation destination position");
202         goto err1;
203       }
204       obj2.free();
205     }
206     if (a->getLength() < 5) {
207       changeZoom = gFalse;
208     } else {
209       a->get(4, &obj2);
210       if (obj2.isNull()) {
211         changeZoom = gFalse;
212       } else if (obj2.isNum()) {
213         changeZoom = gTrue;
214         zoom = obj2.getNum();
215       } else {
216         error(-1, "Bad annotation destination position");
217         goto err1;
218       }
219       obj2.free();
220     }
221
222   // Fit link
223   } else if (obj1.isName("Fit")) {
224     if (a->getLength() < 2) {
225       error(-1, "Annotation destination array is too short");
226       goto err2;
227     }
228     kind = destFit;
229
230   // FitH link
231   } else if (obj1.isName("FitH")) {
232     if (a->getLength() < 3) {
233       error(-1, "Annotation destination array is too short");
234       goto err2;
235     }
236     kind = destFitH;
237     if (!a->get(2, &obj2)->isNum()) {
238       error(-1, "Bad annotation destination position");
239       goto err1;
240     }
241     top = obj2.getNum();
242     obj2.free();
243
244   // FitV link
245   } else if (obj1.isName("FitV")) {
246     if (a->getLength() < 3) {
247       error(-1, "Annotation destination array is too short");
248       goto err2;
249     }
250     kind = destFitV;
251     if (!a->get(2, &obj2)->isNum()) {
252       error(-1, "Bad annotation destination position");
253       goto err1;
254     }
255     left = obj2.getNum();
256     obj2.free();
257
258   // FitR link
259   } else if (obj1.isName("FitR")) {
260     if (a->getLength() < 6) {
261       error(-1, "Annotation destination array is too short");
262       goto err2;
263     }
264     kind = destFitR;
265     if (!a->get(2, &obj2)->isNum()) {
266       error(-1, "Bad annotation destination position");
267       goto err1;
268     }
269     left = obj2.getNum();
270     obj2.free();
271     if (!a->get(3, &obj2)->isNum()) {
272       error(-1, "Bad annotation destination position");
273       goto err1;
274     }
275     bottom = obj2.getNum();
276     obj2.free();
277     if (!a->get(4, &obj2)->isNum()) {
278       error(-1, "Bad annotation destination position");
279       goto err1;
280     }
281     right = obj2.getNum();
282     obj2.free();
283     if (!a->get(5, &obj2)->isNum()) {
284       error(-1, "Bad annotation destination position");
285       goto err1;
286     }
287     top = obj2.getNum();
288     obj2.free();
289
290   // FitB link
291   } else if (obj1.isName("FitB")) {
292     if (a->getLength() < 2) {
293       error(-1, "Annotation destination array is too short");
294       goto err2;
295     }
296     kind = destFitB;
297
298   // FitBH link
299   } else if (obj1.isName("FitBH")) {
300     if (a->getLength() < 3) {
301       error(-1, "Annotation destination array is too short");
302       goto err2;
303     }
304     kind = destFitBH;
305     if (!a->get(2, &obj2)->isNum()) {
306       error(-1, "Bad annotation destination position");
307       goto err1;
308     }
309     top = obj2.getNum();
310     obj2.free();
311
312   // FitBV link
313   } else if (obj1.isName("FitBV")) {
314     if (a->getLength() < 3) {
315       error(-1, "Annotation destination array is too short");
316       goto err2;
317     }
318     kind = destFitBV;
319     if (!a->get(2, &obj2)->isNum()) {
320       error(-1, "Bad annotation destination position");
321       goto err1;
322     }
323     left = obj2.getNum();
324     obj2.free();
325
326   // unknown link kind
327   } else {
328     error(-1, "Unknown annotation destination type");
329     goto err2;
330   }
331
332   obj1.free();
333   ok = gTrue;
334   return;
335
336  err1:
337   obj2.free();
338  err2:
339   obj1.free();
340 }
341
342 LinkDest::LinkDest(LinkDest *dest) {
343   kind = dest->kind;
344   pageIsRef = dest->pageIsRef;
345   if (pageIsRef)
346     pageRef = dest->pageRef;
347   else
348     pageNum = dest->pageNum;
349   left = dest->left;
350   bottom = dest->bottom;
351   right = dest->right;
352   top = dest->top;
353   zoom = dest->zoom;
354   changeLeft = dest->changeLeft;
355   changeTop = dest->changeTop;
356   changeZoom = dest->changeZoom;
357   ok = gTrue;
358 }
359
360 //------------------------------------------------------------------------
361 // LinkGoTo
362 //------------------------------------------------------------------------
363
364 LinkGoTo::LinkGoTo(Object *destObj) {
365   dest = NULL;
366   namedDest = NULL;
367
368   // named destination
369   if (destObj->isName()) {
370     namedDest = new GString(destObj->getName());
371   } else if (destObj->isString()) {
372     namedDest = destObj->getString()->copy();
373
374   // destination dictionary
375   } else if (destObj->isArray()) {
376     dest = new LinkDest(destObj->getArray());
377     if (!dest->isOk()) {
378       delete dest;
379       dest = NULL;
380     }
381
382   // error
383   } else {
384     error(-1, "Illegal annotation destination");
385   }
386 }
387
388 LinkGoTo::~LinkGoTo() {
389   if (dest)
390     delete dest;
391   if (namedDest)
392     delete namedDest;
393 }
394
395 //------------------------------------------------------------------------
396 // LinkGoToR
397 //------------------------------------------------------------------------
398
399 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
400   dest = NULL;
401   namedDest = NULL;
402
403   // get file name
404   fileName = getFileSpecName(fileSpecObj);
405
406   // named destination
407   if (destObj->isName()) {
408     namedDest = new GString(destObj->getName());
409   } else if (destObj->isString()) {
410     namedDest = destObj->getString()->copy();
411
412   // destination dictionary
413   } else if (destObj->isArray()) {
414     dest = new LinkDest(destObj->getArray());
415     if (!dest->isOk()) {
416       delete dest;
417       dest = NULL;
418     }
419
420   // error
421   } else {
422     error(-1, "Illegal annotation destination");
423   }
424 }
425
426 LinkGoToR::~LinkGoToR() {
427   if (fileName)
428     delete fileName;
429   if (dest)
430     delete dest;
431   if (namedDest)
432     delete namedDest;
433 }
434
435
436 //------------------------------------------------------------------------
437 // LinkLaunch
438 //------------------------------------------------------------------------
439
440 LinkLaunch::LinkLaunch(Object *actionObj) {
441   Object obj1, obj2;
442
443   fileName = NULL;
444   params = NULL;
445
446   if (actionObj->isDict()) {
447     if (!actionObj->dictLookup("F", &obj1)->isNull()) {
448       fileName = getFileSpecName(&obj1);
449     } else {
450       obj1.free();
451 #ifdef WIN32
452       if (actionObj->dictLookup("Win", &obj1)->isDict()) {
453         obj1.dictLookup("F", &obj2);
454         fileName = getFileSpecName(&obj2);
455         obj2.free();
456         if (obj1.dictLookup("P", &obj2)->isString()) {
457           params = obj2.getString()->copy();
458         }
459         obj2.free();
460       } else {
461         error(-1, "Bad launch-type link action");
462       }
463 #else
464       //~ This hasn't been defined by Adobe yet, so assume it looks
465       //~ just like the Win dictionary until they say otherwise.
466       if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
467         obj1.dictLookup("F", &obj2);
468         fileName = getFileSpecName(&obj2);
469         obj2.free();
470         if (obj1.dictLookup("P", &obj2)->isString()) {
471           params = obj2.getString()->copy();
472         }
473         obj2.free();
474       } else {
475         error(-1, "Bad launch-type link action");
476       }
477 #endif
478     }
479     obj1.free();
480   }
481 }
482
483 LinkLaunch::~LinkLaunch() {
484   if (fileName)
485     delete fileName;
486   if (params)
487     delete params;
488 }
489
490 //------------------------------------------------------------------------
491 // LinkURI
492 //------------------------------------------------------------------------
493
494 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
495   GString *uri2;
496   int n;
497   char c;
498
499   uri = NULL;
500   if (uriObj->isString()) {
501     uri2 = uriObj->getString()->copy();
502     if (baseURI) {
503       n = strcspn(uri2->getCString(), "/:");
504       if (n == uri2->getLength() || uri2->getChar(n) == '/') {
505         uri = baseURI->copy();
506         c = uri->getChar(uri->getLength() - 1);
507         if (c == '/' || c == '?') {
508           if (uri2->getChar(0) == '/') {
509             uri2->del(0);
510           }
511         } else {
512           if (uri2->getChar(0) != '/') {
513             uri->append('/');
514           }
515         }
516         uri->append(uri2);
517         delete uri2;
518       } else {
519         uri = uri2;
520       }
521     } else {
522       uri = uri2;
523     }
524   } else {
525     error(-1, "Illegal URI-type link");
526   }
527 }
528
529 LinkURI::~LinkURI() {
530   if (uri)
531     delete uri;
532 }
533
534 //------------------------------------------------------------------------
535 // LinkNamed
536 //------------------------------------------------------------------------
537
538 LinkNamed::LinkNamed(Object *nameObj) {
539   name = NULL;
540   if (nameObj->isName()) {
541     name = new GString(nameObj->getName());
542   }
543 }
544
545 LinkNamed::~LinkNamed() {
546   if (name) {
547     delete name;
548   }
549 }
550
551 //------------------------------------------------------------------------
552 // LinkMovie
553 //------------------------------------------------------------------------
554
555 LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
556   annotRef.num = -1;
557   title = NULL;
558   if (annotObj->isRef()) {
559     annotRef = annotObj->getRef();
560   } else if (titleObj->isString()) {
561     title = titleObj->getString()->copy();
562   } else {
563     error(-1, "Movie action is missing both the Annot and T keys");
564   }
565 }
566
567 LinkMovie::~LinkMovie() {
568   if (title) {
569     delete title;
570   }
571 }
572
573 //------------------------------------------------------------------------
574 // LinkUnknown
575 //------------------------------------------------------------------------
576
577 LinkUnknown::LinkUnknown(char *actionA) {
578   action = new GString(actionA);
579 }
580
581 LinkUnknown::~LinkUnknown() {
582   delete action;
583 }
584
585 //------------------------------------------------------------------------
586 // Link
587 //------------------------------------------------------------------------
588
589 Link::Link(Dict *dict, GString *baseURI) {
590   Object obj1, obj2;
591   double t;
592
593   action = NULL;
594   ok = gFalse;
595
596   // get rectangle
597   if (!dict->lookup("Rect", &obj1)->isArray()) {
598     error(-1, "Annotation rectangle is wrong type");
599     goto err2;
600   }
601   if (!obj1.arrayGet(0, &obj2)->isNum()) {
602     error(-1, "Bad annotation rectangle");
603     goto err1;
604   }
605   x1 = obj2.getNum();
606   obj2.free();
607   if (!obj1.arrayGet(1, &obj2)->isNum()) {
608     error(-1, "Bad annotation rectangle");
609     goto err1;
610   }
611   y1 = obj2.getNum();
612   obj2.free();
613   if (!obj1.arrayGet(2, &obj2)->isNum()) {
614     error(-1, "Bad annotation rectangle");
615     goto err1;
616   }
617   x2 = obj2.getNum();
618   obj2.free();
619   if (!obj1.arrayGet(3, &obj2)->isNum()) {
620     error(-1, "Bad annotation rectangle");
621     goto err1;
622   }
623   y2 = obj2.getNum();
624   obj2.free();
625   obj1.free();
626   if (x1 > x2) {
627     t = x1;
628     x1 = x2;
629     x2 = t;
630   }
631   if (y1 > y2) {
632     t = y1;
633     y1 = y2;
634     y2 = t;
635   }
636
637   // get border
638   borderW = 1;
639   if (!dict->lookup("Border", &obj1)->isNull()) {
640     if (obj1.isArray() && obj1.arrayGetLength() >= 3) {
641       if (obj1.arrayGet(2, &obj2)->isNum()) {
642         borderW = obj2.getNum();
643       } else {
644         error(-1, "Bad annotation border");
645       }
646       obj2.free();
647     }
648   }
649   obj1.free();
650
651   // look for destination
652   if (!dict->lookup("Dest", &obj1)->isNull()) {
653     action = LinkAction::parseDest(&obj1);
654
655   // look for action
656   } else {
657     obj1.free();
658     if (dict->lookup("A", &obj1)->isDict()) {
659       action = LinkAction::parseAction(&obj1, baseURI);
660     }
661   }
662   obj1.free();
663
664   // check for bad action
665   if (action) {
666     ok = gTrue;
667   }
668
669   return;
670
671  err1:
672   obj2.free();
673  err2:
674   obj1.free();
675 }
676
677 Link::~Link() {
678   if (action)
679     delete action;
680 }
681
682 //------------------------------------------------------------------------
683 // Links
684 //------------------------------------------------------------------------
685
686 Links::Links(Object *annots, GString *baseURI) {
687   Link *link;
688   Object obj1, obj2;
689   int size;
690   int i;
691
692   links = NULL;
693   size = 0;
694   numLinks = 0;
695
696   if (annots->isArray()) {
697     for (i = 0; i < annots->arrayGetLength(); ++i) {
698       if (annots->arrayGet(i, &obj1)->isDict()) {
699         if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
700           link = new Link(obj1.getDict(), baseURI);
701           if (link->isOk()) {
702             if (numLinks >= size) {
703               size += 16;
704               links = (Link **)grealloc(links, size * sizeof(Link *));
705             }
706             links[numLinks++] = link;
707           } else {
708             delete link;
709           }
710         }
711         obj2.free();
712       }
713       obj1.free();
714     }
715   }
716 }
717
718 Links::~Links() {
719   int i;
720
721   for (i = 0; i < numLinks; ++i)
722     delete links[i];
723   gfree(links);
724 }
725
726 LinkAction *Links::find(double x, double y) const {
727   int i;
728
729   for (i = numLinks - 1; i >= 0; --i) {
730     if (links[i]->inRect(x, y)) {
731       return links[i]->getAction();
732     }
733   }
734   return NULL;
735 }
736
737 GBool Links::onLink(double x, double y) const {
738   int i;
739
740   for (i = 0; i < numLinks; ++i) {
741     if (links[i]->inRect(x, y))
742       return gTrue;
743   }
744   return gFalse;
745 }