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