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