filters

Link.cpp
1//========================================================================
2//
3// Link.cpp
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
29LinkAction *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
40LinkAction *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
108GString *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
142LinkDest::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
332LinkDest::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
354LinkGoTo::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
378LinkGoTo::~LinkGoTo() {
379 if (dest)
380 delete dest;
381 if (namedDest)
382 delete namedDest;
383}
384
385//------------------------------------------------------------------------
386// LinkGoToR
387//------------------------------------------------------------------------
388
389LinkGoToR::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
416LinkGoToR::~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
430LinkLaunch::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
473LinkLaunch::~LinkLaunch() {
474 if (fileName)
475 delete fileName;
476 if (params)
477 delete params;
478}
479
480//------------------------------------------------------------------------
481// LinkURI
482//------------------------------------------------------------------------
483
484LinkURI::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
519LinkURI::~LinkURI() {
520 if (uri)
521 delete uri;
522}
523
524//------------------------------------------------------------------------
525// LinkNamed
526//------------------------------------------------------------------------
527
528LinkNamed::LinkNamed(Object *nameObj) {
529 name = NULL;
530 if (nameObj->isName()) {
531 name = new GString(nameObj->getName());
532 }
533}
534
535LinkNamed::~LinkNamed() {
536 if (name) {
537 delete name;
538 }
539}
540
541//------------------------------------------------------------------------
542// LinkMovie
543//------------------------------------------------------------------------
544
545LinkMovie::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
557LinkMovie::~LinkMovie() {
558 if (title) {
559 delete title;
560 }
561}
562
563//------------------------------------------------------------------------
564// LinkUnknown
565//------------------------------------------------------------------------
566
567LinkUnknown::LinkUnknown(char *actionA) {
568 action = new GString(actionA);
569}
570
571LinkUnknown::~LinkUnknown() {
572 delete action;
573}
574
575//------------------------------------------------------------------------
576// Link
577//------------------------------------------------------------------------
578
579Link::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
667Link::~Link() {
668 if (action)
669 delete action;
670}
671
672//------------------------------------------------------------------------
673// Links
674//------------------------------------------------------------------------
675
676Links::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
708Links::~Links() {
709 int i;
710
711 for (i = 0; i < numLinks; ++i)
712 delete links[i];
713 gfree(links);
714}
715
716LinkAction *Links::find(double x, double y) {
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
727GBool Links::onLink(double x, double y) {
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}