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