]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/Link.cc
Initial revision
[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) {
327   uri = NULL;
328   if (uriObj->isString())
329     uri = uriObj->getString()->copy();
330   else
331     error(-1, "Illegal URI-type link");
332 }
333
334 LinkURI::~LinkURI() {
335   if (uri)
336     delete uri;
337 }
338
339 //------------------------------------------------------------------------
340 // LinkUnknown
341 //------------------------------------------------------------------------
342
343 LinkUnknown::LinkUnknown(char *action1) {
344   action = new GString(action1);
345 }
346
347 LinkUnknown::~LinkUnknown() {
348   delete action;
349 }
350
351 //------------------------------------------------------------------------
352 // Link
353 //------------------------------------------------------------------------
354
355 Link::Link(Dict *dict) {
356   Object obj1, obj2, obj3, obj4;
357   double t;
358
359   action = NULL;
360   ok = gFalse;
361
362   // get rectangle
363   if (!dict->lookup("Rect", &obj1)->isArray()) {
364     error(-1, "Annotation rectangle is wrong type");
365     goto err2;
366   }
367   if (!obj1.arrayGet(0, &obj2)->isNum()) {
368     error(-1, "Bad annotation rectangle");
369     goto err1;
370   }
371   x1 = obj2.getNum();
372   obj2.free();
373   if (!obj1.arrayGet(1, &obj2)->isNum()) {
374     error(-1, "Bad annotation rectangle");
375     goto err1;
376   }
377   y1 = obj2.getNum();
378   obj2.free();
379   if (!obj1.arrayGet(2, &obj2)->isNum()) {
380     error(-1, "Bad annotation rectangle");
381     goto err1;
382   }
383   x2 = obj2.getNum();
384   obj2.free();
385   if (!obj1.arrayGet(3, &obj2)->isNum()) {
386     error(-1, "Bad annotation rectangle");
387     goto err1;
388   }
389   y2 = obj2.getNum();
390   obj2.free();
391   obj1.free();
392   if (x1 > x2) {
393     t = x1;
394     x1 = x2;
395     x2 = t;
396   }
397   if (y1 > y2) {
398     t = y1;
399     y1 = y2;
400     y2 = t;
401   }
402
403   // get border
404   borderW = 0;
405   if (!dict->lookup("Border", &obj1)->isNull()) {
406     if (obj1.isArray() && obj1.arrayGet(2, &obj2)->isNum())
407       borderW = obj2.getNum();
408     else
409       error(-1, "Bad annotation border");
410     obj2.free();
411   }
412   obj1.free();
413
414   // look for destination
415   if (!dict->lookup("Dest", &obj1)->isNull()) {
416     action = new LinkGoTo(&obj1);
417
418   // look for action
419   } else {
420     obj1.free();
421     if (dict->lookup("A", &obj1)->isDict()) {
422       obj1.dictLookup("S", &obj2);
423
424       // GoTo action
425       if (obj2.isName("GoTo")) {
426         obj1.dictLookup("D", &obj3);
427         action = new LinkGoTo(&obj3);
428         obj3.free();
429
430       // GoToR action
431       } else if (obj2.isName("GoToR")) {
432         obj1.dictLookup("F", &obj3);
433         obj1.dictLookup("D", &obj4);
434         action = new LinkGoToR(&obj3, &obj4);
435         obj3.free();
436         obj4.free();
437
438       // Launch action
439       } else if (obj2.isName("Launch")) {
440         action = new LinkLaunch(&obj1);
441
442       // URI action
443       } else if (obj2.isName("URI")) {
444         obj1.dictLookup("URI", &obj3);
445         action = new LinkURI(&obj3);
446         obj3.free();
447
448       // unknown action
449       } else if (obj2.isName()) {
450         action = new LinkUnknown(obj2.getName());
451
452       // action is missing or wrong type
453       } else {
454         error(-1, "Bad annotation action");
455         action = NULL;
456       }
457
458       obj2.free();
459
460     } else {
461       error(-1, "Missing annotation destination/action");
462       action = NULL;
463     }
464   }
465   obj1.free();
466
467   // check for bad action
468   if (action && action->isOk())
469     ok = gTrue;
470
471   return;
472
473  err1:
474   obj2.free();
475  err2:
476   obj1.free();
477 }
478
479 Link::~Link() {
480   if (action)
481     delete action;
482 }
483
484 //------------------------------------------------------------------------
485 // Links
486 //------------------------------------------------------------------------
487
488 Links::Links(Object *annots) {
489   Link *link;
490   Object obj1, obj2;
491   int size;
492   int i;
493
494   links = NULL;
495   size = 0;
496   numLinks = 0;
497
498   if (annots->isArray()) {
499     for (i = 0; i < annots->arrayGetLength(); ++i) {
500       if (annots->arrayGet(i, &obj1)->isDict()) {
501         if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
502           link = new Link(obj1.getDict());
503           if (link->isOk()) {
504             if (numLinks >= size) {
505               size += 16;
506               links = (Link **)grealloc(links, size * sizeof(Link *));
507             }
508             links[numLinks++] = link;
509           } else {
510             delete link;
511           }
512         }
513         obj2.free();
514       }
515       obj1.free();
516     }
517   }
518 }
519
520 Links::~Links() {
521   int i;
522
523   for (i = 0; i < numLinks; ++i)
524     delete links[i];
525   gfree(links);
526 }
527
528 LinkAction *Links::find(double x, double y) {
529   int i;
530
531   for (i = 0; i < numLinks; ++i) {
532     if (links[i]->inRect(x, y)) {
533       if (links[i]->getAction())
534         return links[i]->getAction();
535       return NULL;
536     }
537   }
538   return NULL;
539 }
540
541 GBool Links::onLink(double x, double y) {
542   int i;
543
544   for (i = 0; i < numLinks; ++i) {
545     if (links[i]->inRect(x, y))
546       return gTrue;
547   }
548   return gFalse;
549 }
550
551 //------------------------------------------------------------------------
552
553 // Extract a file name from a file specification (string or dictionary).
554 static GString *getFileSpecName(Object *fileSpecObj) {
555   GString *name;
556   Object obj1;
557
558   name = NULL;
559
560   // string
561   if (fileSpecObj->isString()) {
562     name = fileSpecObj->getString()->copy();
563
564   // dictionary
565   } else if (fileSpecObj->isDict()) {
566     if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
567       obj1.free();
568       fileSpecObj->dictLookup("F", &obj1);
569     }
570     if (obj1.isString())
571       name = obj1.getString()->copy();
572     else
573       error(-1, "Illegal file spec in link");
574
575   // error
576   } else {
577     error(-1, "Illegal file spec in link");
578   }
579
580   return name;
581 }