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