1 //========================================================================
2 //
3 // Link.cc
4 //
5 // Copyright 1996-2003 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
parseDest(Object * obj)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
parseAction(Object * obj,GString * baseURI)40 LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
41 LinkAction *action;
42 Object obj2, obj3, obj4, obj5;
43
44 if (!obj->isDict()) {
45 error(errSyntaxWarning, -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 // JavaScript action
90 } else if (obj2.isName("JavaScript")) {
91 obj->dictLookup("JS", &obj3);
92 action = new LinkJavaScript(&obj3);
93 obj3.free();
94
95 // SubmitForm action
96 } else if (obj2.isName("SubmitForm")) {
97 obj->dictLookup("F", &obj3);
98 obj->dictLookup("Fields", &obj4);
99 obj->dictLookup("Flags", &obj5);
100 action = new LinkSubmitForm(&obj3, &obj4, &obj5);
101 obj3.free();
102 obj4.free();
103 obj5.free();
104
105 // Hide action
106 } else if (obj2.isName("Hide")) {
107 obj->dictLookupNF("T", &obj3);
108 obj->dictLookup("H", &obj4);
109 action = new LinkHide(&obj3, &obj4);
110 obj3.free();
111 obj4.free();
112
113 // unknown action
114 } else if (obj2.isName()) {
115 action = new LinkUnknown(obj2.getName());
116
117 // action is missing or wrong type
118 } else {
119 error(errSyntaxWarning, -1, "Bad annotation action");
120 action = NULL;
121 }
122
123 obj2.free();
124
125 if (action && !action->isOk()) {
126 delete action;
127 return NULL;
128 }
129 return action;
130 }
131
getFileSpecName(Object * fileSpecObj)132 GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
133 GString *name;
134 Object obj1;
135
136 name = NULL;
137
138 // string
139 if (fileSpecObj->isString()) {
140 name = fileSpecObj->getString()->copy();
141
142 // dictionary
143 } else if (fileSpecObj->isDict()) {
144 #ifdef _WIN32
145 if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
146 #else
147 if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
148 #endif
149 obj1.free();
150 fileSpecObj->dictLookup("F", &obj1);
151 }
152 if (obj1.isString()) {
153 name = obj1.getString()->copy();
154 } else {
155 error(errSyntaxWarning, -1, "Illegal file spec in link");
156 }
157 obj1.free();
158
159 // error
160 } else {
161 error(errSyntaxWarning, -1, "Illegal file spec in link");
162 }
163
164 // system-dependent path manipulation
165 if (name) {
166 #ifdef _WIN32
167 int i, j;
168
169 // "//...." --> "\...."
170 // "/x/...." --> "x:\...."
171 // "/server/share/...." --> "\\server\share\...."
172 // convert escaped slashes to slashes and unescaped slashes to backslashes
173 i = 0;
174 if (name->getChar(0) == '/') {
175 if (name->getLength() >= 2 && name->getChar(1) == '/') {
176 name->del(0);
177 i = 0;
178 } else if (name->getLength() >= 2 &&
179 ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
180 (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
181 (name->getLength() == 2 || name->getChar(2) == '/')) {
182 name->setChar(0, name->getChar(1));
183 name->setChar(1, ':');
184 i = 2;
185 } else {
186 for (j = 2; j < name->getLength(); ++j) {
187 if (name->getChar(j-1) != '\\' &&
188 name->getChar(j) == '/') {
189 break;
190 }
191 }
192 if (j < name->getLength()) {
193 name->setChar(0, '\\');
194 name->insert(0, '\\');
195 i = 2;
196 }
197 }
198 }
199 for (; i < name->getLength(); ++i) {
200 if (name->getChar(i) == '/') {
201 name->setChar(i, '\\');
202 } else if (name->getChar(i) == '\\' &&
203 i+1 < name->getLength() &&
204 name->getChar(i+1) == '/') {
205 name->del(i);
206 }
207 }
208 #else
209 // no manipulation needed for Unix
210 #endif
211 }
212
213 return name;
214 }
215
216 //------------------------------------------------------------------------
217 // LinkDest
218 //------------------------------------------------------------------------
219
220 LinkDest::LinkDest(Array *a) {
221 Object obj1, obj2;
222
223 // initialize fields
224 left = bottom = right = top = zoom = 0;
225 ok = gFalse;
226
227 // get page
228 if (a->getLength() < 2) {
229 error(errSyntaxWarning, -1, "Annotation destination array is too short");
230 return;
231 }
232 a->getNF(0, &obj1);
233 if (obj1.isInt()) {
234 pageNum = obj1.getInt() + 1;
235 pageIsRef = gFalse;
236 } else if (obj1.isRef()) {
237 pageRef.num = obj1.getRefNum();
238 pageRef.gen = obj1.getRefGen();
239 pageIsRef = gTrue;
240 } else {
241 error(errSyntaxWarning, -1, "Bad annotation destination");
242 goto err2;
243 }
244 obj1.free();
245
246 // get destination type
247 a->get(1, &obj1);
248
249 // XYZ link
250 if (obj1.isName("XYZ")) {
251 kind = destXYZ;
252 if (a->getLength() < 3) {
253 changeLeft = gFalse;
254 } else {
255 a->get(2, &obj2);
256 if (obj2.isNull()) {
257 changeLeft = gFalse;
258 } else if (obj2.isNum()) {
259 changeLeft = gTrue;
260 left = obj2.getNum();
261 } else {
262 error(errSyntaxWarning, -1, "Bad annotation destination position");
263 goto err1;
264 }
265 obj2.free();
266 }
267 if (a->getLength() < 4) {
268 changeTop = gFalse;
269 } else {
270 a->get(3, &obj2);
271 if (obj2.isNull()) {
272 changeTop = gFalse;
273 } else if (obj2.isNum()) {
274 changeTop = gTrue;
275 top = obj2.getNum();
276 } else {
277 error(errSyntaxWarning, -1, "Bad annotation destination position");
278 goto err1;
279 }
280 obj2.free();
281 }
282 if (a->getLength() < 5) {
283 changeZoom = gFalse;
284 } else {
285 a->get(4, &obj2);
286 if (obj2.isNull()) {
287 changeZoom = gFalse;
288 } else if (obj2.isNum()) {
289 changeZoom = gTrue;
290 zoom = obj2.getNum();
291 } else {
292 error(errSyntaxWarning, -1, "Bad annotation destination position");
293 goto err1;
294 }
295 obj2.free();
296 }
297
298 // Fit link
299 } else if (obj1.isName("Fit")) {
300 if (a->getLength() < 2) {
301 error(errSyntaxWarning, -1, "Annotation destination array is too short");
302 goto err2;
303 }
304 kind = destFit;
305
306 // FitH link
307 } else if (obj1.isName("FitH")) {
308 if (a->getLength() < 3) {
309 error(errSyntaxWarning, -1, "Annotation destination array is too short");
310 goto err2;
311 }
312 kind = destFitH;
313 if (a->get(2, &obj2)->isNum()) {
314 top = obj2.getNum();
315 changeTop = gTrue;
316 } else if (obj2.isNull()) {
317 changeTop = gFalse;
318 } else {
319 error(errSyntaxWarning, -1, "Bad annotation destination position");
320 kind = destFit;
321 }
322 obj2.free();
323
324 // FitV link
325 } else if (obj1.isName("FitV")) {
326 if (a->getLength() < 3) {
327 error(errSyntaxWarning, -1, "Annotation destination array is too short");
328 goto err2;
329 }
330 kind = destFitV;
331 if (a->get(2, &obj2)->isNum()) {
332 left = obj2.getNum();
333 changeLeft = gTrue;
334 } else if (obj2.isNull()) {
335 changeLeft = gFalse;
336 } else {
337 error(errSyntaxWarning, -1, "Bad annotation destination position");
338 kind = destFit;
339 }
340 obj2.free();
341
342 // FitR link
343 } else if (obj1.isName("FitR")) {
344 if (a->getLength() < 6) {
345 error(errSyntaxWarning, -1, "Annotation destination array is too short");
346 goto err2;
347 }
348 kind = destFitR;
349 if (a->get(2, &obj2)->isNum()) {
350 left = obj2.getNum();
351 } else {
352 error(errSyntaxWarning, -1, "Bad annotation destination position");
353 kind = destFit;
354 }
355 obj2.free();
356 if (!a->get(3, &obj2)->isNum()) {
357 error(errSyntaxWarning, -1, "Bad annotation destination position");
358 kind = destFit;
359 }
360 bottom = obj2.getNum();
361 obj2.free();
362 if (!a->get(4, &obj2)->isNum()) {
363 error(errSyntaxWarning, -1, "Bad annotation destination position");
364 kind = destFit;
365 }
366 right = obj2.getNum();
367 obj2.free();
368 if (!a->get(5, &obj2)->isNum()) {
369 error(errSyntaxWarning, -1, "Bad annotation destination position");
370 kind = destFit;
371 }
372 top = obj2.getNum();
373 obj2.free();
374
375 // FitB link
376 } else if (obj1.isName("FitB")) {
377 if (a->getLength() < 2) {
378 error(errSyntaxWarning, -1, "Annotation destination array is too short");
379 goto err2;
380 }
381 kind = destFitB;
382
383 // FitBH link
384 } else if (obj1.isName("FitBH")) {
385 if (a->getLength() < 3) {
386 error(errSyntaxWarning, -1, "Annotation destination array is too short");
387 goto err2;
388 }
389 kind = destFitBH;
390 if (a->get(2, &obj2)->isNum()) {
391 top = obj2.getNum();
392 changeTop = gTrue;
393 } else if (obj2.isNull()) {
394 changeTop = gFalse;
395 } else {
396 error(errSyntaxWarning, -1, "Bad annotation destination position");
397 kind = destFit;
398 }
399 obj2.free();
400
401 // FitBV link
402 } else if (obj1.isName("FitBV")) {
403 if (a->getLength() < 3) {
404 error(errSyntaxWarning, -1, "Annotation destination array is too short");
405 goto err2;
406 }
407 kind = destFitBV;
408 if (a->get(2, &obj2)->isNum()) {
409 left = obj2.getNum();
410 changeLeft = gTrue;
411 } else if (obj2.isNull()) {
412 changeLeft = gFalse;
413 } else {
414 error(errSyntaxWarning, -1, "Bad annotation destination position");
415 kind = destFit;
416 }
417 obj2.free();
418
419 // unknown link kind
420 } else {
421 error(errSyntaxWarning, -1, "Unknown annotation destination type");
422 goto err2;
423 }
424
425 obj1.free();
426 ok = gTrue;
427 return;
428
429 err1:
430 obj2.free();
431 err2:
432 obj1.free();
433 }
434
435 LinkDest::LinkDest(LinkDest *dest) {
436 kind = dest->kind;
437 pageIsRef = dest->pageIsRef;
438 if (pageIsRef)
439 pageRef = dest->pageRef;
440 else
441 pageNum = dest->pageNum;
442 left = dest->left;
443 bottom = dest->bottom;
444 right = dest->right;
445 top = dest->top;
446 zoom = dest->zoom;
447 changeLeft = dest->changeLeft;
448 changeTop = dest->changeTop;
449 changeZoom = dest->changeZoom;
450 ok = gTrue;
451 }
452
453 //------------------------------------------------------------------------
454 // LinkGoTo
455 //------------------------------------------------------------------------
456
457 LinkGoTo::LinkGoTo(Object *destObj) {
458 dest = NULL;
459 namedDest = NULL;
460
461 // named destination
462 if (destObj->isName()) {
463 namedDest = new GString(destObj->getName());
464 } else if (destObj->isString()) {
465 namedDest = destObj->getString()->copy();
466
467 // destination dictionary
468 } else if (destObj->isArray()) {
469 dest = new LinkDest(destObj->getArray());
470 if (!dest->isOk()) {
471 delete dest;
472 dest = NULL;
473 }
474
475 // error
476 } else {
477 error(errSyntaxWarning, -1, "Illegal annotation destination");
478 }
479 }
480
481 LinkGoTo::~LinkGoTo() {
482 if (dest)
483 delete dest;
484 if (namedDest)
485 delete namedDest;
486 }
487
488 //------------------------------------------------------------------------
489 // LinkGoToR
490 //------------------------------------------------------------------------
491
492 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
493 dest = NULL;
494 namedDest = NULL;
495
496 // get file name
497 fileName = getFileSpecName(fileSpecObj);
498
499 // named destination
500 if (destObj->isName()) {
501 namedDest = new GString(destObj->getName());
502 } else if (destObj->isString()) {
503 namedDest = destObj->getString()->copy();
504
505 // destination dictionary
506 } else if (destObj->isArray()) {
507 dest = new LinkDest(destObj->getArray());
508 if (!dest->isOk()) {
509 delete dest;
510 dest = NULL;
511 }
512
513 // error
514 } else {
515 error(errSyntaxWarning, -1, "Illegal annotation destination");
516 }
517 }
518
519 LinkGoToR::~LinkGoToR() {
520 if (fileName)
521 delete fileName;
522 if (dest)
523 delete dest;
524 if (namedDest)
525 delete namedDest;
526 }
527
528
529 //------------------------------------------------------------------------
530 // LinkLaunch
531 //------------------------------------------------------------------------
532
533 LinkLaunch::LinkLaunch(Object *actionObj) {
534 Object obj1, obj2;
535
536 fileName = NULL;
537 params = NULL;
538
539 if (actionObj->isDict()) {
540 if (!actionObj->dictLookup("F", &obj1)->isNull()) {
541 fileName = getFileSpecName(&obj1);
542 } else {
543 obj1.free();
544 #ifdef _WIN32
545 if (actionObj->dictLookup("Win", &obj1)->isDict()) {
546 obj1.dictLookup("F", &obj2);
547 fileName = getFileSpecName(&obj2);
548 obj2.free();
549 if (obj1.dictLookup("P", &obj2)->isString()) {
550 params = obj2.getString()->copy();
551 }
552 obj2.free();
553 } else {
554 error(errSyntaxWarning, -1, "Bad launch-type link action");
555 }
556 #else
557 //~ This hasn't been defined by Adobe yet, so assume it looks
558 //~ just like the Win dictionary until they say otherwise.
559 if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
560 obj1.dictLookup("F", &obj2);
561 fileName = getFileSpecName(&obj2);
562 obj2.free();
563 if (obj1.dictLookup("P", &obj2)->isString()) {
564 params = obj2.getString()->copy();
565 }
566 obj2.free();
567 } else {
568 error(errSyntaxWarning, -1, "Bad launch-type link action");
569 }
570 #endif
571 }
572 obj1.free();
573 }
574 }
575
576 LinkLaunch::~LinkLaunch() {
577 if (fileName)
578 delete fileName;
579 if (params)
580 delete params;
581 }
582
583 //------------------------------------------------------------------------
584 // LinkURI
585 //------------------------------------------------------------------------
586
587 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
588 GString *uri2;
589 int n;
590 char c;
591
592 uri = NULL;
593 if (uriObj->isString()) {
594 uri2 = uriObj->getString();
595 n = (int)strcspn(uri2->getCString(), "/:");
596 if (n < uri2->getLength() && uri2->getChar(n) == ':') {
597 // "http:..." etc.
598 uri = uri2->copy();
599 } else if (!uri2->cmpN("www.", 4)) {
600 // "www.[...]" without the leading "http://"
601 uri = new GString("http://");
602 uri->append(uri2);
603 } else {
604 // relative URI
605 if (baseURI) {
606 uri = baseURI->copy();
607 c = uri->getChar(uri->getLength() - 1);
608 if (c != '/' && c != '?') {
609 uri->append('/');
610 }
611 if (uri2->getChar(0) == '/') {
612 uri->append(uri2->getCString() + 1, uri2->getLength() - 1);
613 } else {
614 uri->append(uri2);
615 }
616 } else {
617 uri = uri2->copy();
618 }
619 }
620 } else {
621 error(errSyntaxWarning, -1, "Illegal URI-type link");
622 }
623 }
624
625 LinkURI::~LinkURI() {
626 if (uri)
627 delete uri;
628 }
629
630 //------------------------------------------------------------------------
631 // LinkNamed
632 //------------------------------------------------------------------------
633
634 LinkNamed::LinkNamed(Object *nameObj) {
635 name = NULL;
636 if (nameObj->isName()) {
637 name = new GString(nameObj->getName());
638 }
639 }
640
641 LinkNamed::~LinkNamed() {
642 if (name) {
643 delete name;
644 }
645 }
646
647 //------------------------------------------------------------------------
648 // LinkMovie
649 //------------------------------------------------------------------------
650
651 LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
652 annotRef.num = -1;
653 title = NULL;
654 if (annotObj->isRef()) {
655 annotRef = annotObj->getRef();
656 } else if (titleObj->isString()) {
657 title = titleObj->getString()->copy();
658 } else {
659 error(errSyntaxError, -1,
660 "Movie action is missing both the Annot and T keys");
661 }
662 }
663
664 LinkMovie::~LinkMovie() {
665 if (title) {
666 delete title;
667 }
668 }
669
670 //------------------------------------------------------------------------
671 // LinkJavaScript
672 //------------------------------------------------------------------------
673
674 LinkJavaScript::LinkJavaScript(Object *jsObj) {
675 char buf[4096];
676 int n;
677
678 if (jsObj->isString()) {
679 js = jsObj->getString()->copy();
680 } else if (jsObj->isStream()) {
681 js = new GString();
682 jsObj->streamReset();
683 while ((n = jsObj->getStream()->getBlock(buf, sizeof(buf))) > 0) {
684 js->append(buf, n);
685 }
686 jsObj->streamClose();
687 } else {
688 error(errSyntaxError, -1, "JavaScript action JS key is wrong type");
689 js = NULL;
690 }
691 }
692
693 LinkJavaScript::~LinkJavaScript() {
694 if (js) {
695 delete js;
696 }
697 }
698
699 //------------------------------------------------------------------------
700 // LinkSubmitForm
701 //------------------------------------------------------------------------
702
703 LinkSubmitForm::LinkSubmitForm(Object *urlObj, Object *fieldsObj,
704 Object *flagsObj) {
705 if (urlObj->isString()) {
706 url = urlObj->getString()->copy();
707 } else {
708 error(errSyntaxError, -1, "SubmitForm action URL is wrong type");
709 url = NULL;
710 }
711
712 if (fieldsObj->isArray()) {
713 fieldsObj->copy(&fields);
714 } else {
715 if (!fieldsObj->isNull()) {
716 error(errSyntaxError, -1, "SubmitForm action Fields value is wrong type");
717 }
718 fields.initNull();
719 }
720
721 if (flagsObj->isInt()) {
722 flags = flagsObj->getInt();
723 } else {
724 if (!flagsObj->isNull()) {
725 error(errSyntaxError, -1, "SubmitForm action Flags value is wrong type");
726 }
727 flags = 0;
728 }
729 }
730
731 LinkSubmitForm::~LinkSubmitForm() {
732 if (url) {
733 delete url;
734 }
735 fields.free();
736 }
737
738 //------------------------------------------------------------------------
739 // LinkHide
740 //------------------------------------------------------------------------
741
742 LinkHide::LinkHide(Object *fieldsObj, Object *hideFlagObj) {
743 if (fieldsObj->isRef() || fieldsObj->isString() || fieldsObj->isArray()) {
744 fieldsObj->copy(&fields);
745 } else {
746 error(errSyntaxError, -1, "Hide action T value is wrong type");
747 fields.initNull();
748 }
749
750 if (hideFlagObj->isBool()) {
751 hideFlag = hideFlagObj->getBool();
752 } else {
753 error(errSyntaxError, -1, "Hide action H value is wrong type");
754 hideFlag = gFalse;
755 }
756 }
757
758 LinkHide::~LinkHide() {
759 fields.free();
760 }
761
762 //------------------------------------------------------------------------
763 // LinkUnknown
764 //------------------------------------------------------------------------
765
766 LinkUnknown::LinkUnknown(char *actionA) {
767 action = new GString(actionA);
768 }
769
770 LinkUnknown::~LinkUnknown() {
771 delete action;
772 }
773
774 //------------------------------------------------------------------------
775 // Link
776 //------------------------------------------------------------------------
777
778 Link::Link(Dict *dict, GString *baseURI) {
779 Object obj1, obj2;
780 double t;
781
782 action = NULL;
783 ok = gFalse;
784
785 // get rectangle
786 if (!dict->lookup("Rect", &obj1)->isArray()) {
787 error(errSyntaxError, -1, "Annotation rectangle is wrong type");
788 goto err2;
789 }
790 if (!obj1.arrayGet(0, &obj2)->isNum()) {
791 error(errSyntaxError, -1, "Bad annotation rectangle");
792 goto err1;
793 }
794 x1 = obj2.getNum();
795 obj2.free();
796 if (!obj1.arrayGet(1, &obj2)->isNum()) {
797 error(errSyntaxError, -1, "Bad annotation rectangle");
798 goto err1;
799 }
800 y1 = obj2.getNum();
801 obj2.free();
802 if (!obj1.arrayGet(2, &obj2)->isNum()) {
803 error(errSyntaxError, -1, "Bad annotation rectangle");
804 goto err1;
805 }
806 x2 = obj2.getNum();
807 obj2.free();
808 if (!obj1.arrayGet(3, &obj2)->isNum()) {
809 error(errSyntaxError, -1, "Bad annotation rectangle");
810 goto err1;
811 }
812 y2 = obj2.getNum();
813 obj2.free();
814 obj1.free();
815 if (x1 > x2) {
816 t = x1;
817 x1 = x2;
818 x2 = t;
819 }
820 if (y1 > y2) {
821 t = y1;
822 y1 = y2;
823 y2 = t;
824 }
825
826 // look for destination
827 if (!dict->lookup("Dest", &obj1)->isNull()) {
828 action = LinkAction::parseDest(&obj1);
829
830 // look for action
831 } else {
832 obj1.free();
833 if (dict->lookup("A", &obj1)->isDict()) {
834 action = LinkAction::parseAction(&obj1, baseURI);
835 }
836 }
837 obj1.free();
838
839 // check for bad action
840 if (action) {
841 ok = gTrue;
842 }
843
844 return;
845
846 err1:
847 obj2.free();
848 err2:
849 obj1.free();
850 }
851
852 Link::~Link() {
853 if (action) {
854 delete action;
855 }
856 }
857
858 //------------------------------------------------------------------------
859 // Links
860 //------------------------------------------------------------------------
861
862 Links::Links(Object *annots, GString *baseURI) {
863 Link *link;
864 Object obj1, obj2, obj3;
865 int size;
866 int i;
867
868 links = NULL;
869 size = 0;
870 numLinks = 0;
871
872 if (annots->isArray()) {
873 for (i = 0; i < annots->arrayGetLength(); ++i) {
874 if (annots->arrayGet(i, &obj1)->isDict()) {
875 obj1.dictLookup("Subtype", &obj2);
876 obj1.dictLookup("FT", &obj3);
877 if (obj2.isName("Link") ||
878 (obj2.isName("Widget") && (obj3.isName("Btn") || obj3.isNull()))) {
879 link = new Link(obj1.getDict(), baseURI);
880 if (link->isOk()) {
881 if (numLinks >= size) {
882 size += 16;
883 links = (Link **)greallocn(links, size, sizeof(Link *));
884 }
885 links[numLinks++] = link;
886 } else {
887 delete link;
888 }
889 }
890 obj3.free();
891 obj2.free();
892 }
893 obj1.free();
894 }
895 }
896 }
897
898 Links::~Links() {
899 int i;
900
901 for (i = 0; i < numLinks; ++i)
902 delete links[i];
903 gfree(links);
904 }
905
906 LinkAction *Links::find(double x, double y) {
907 int i;
908
909 for (i = numLinks - 1; i >= 0; --i) {
910 if (links[i]->inRect(x, y)) {
911 return links[i]->getAction();
912 }
913 }
914 return NULL;
915 }
916
917 GBool Links::onLink(double x, double y) {
918 int i;
919
920 for (i = 0; i < numLinks; ++i) {
921 if (links[i]->inRect(x, y))
922 return gTrue;
923 }
924 return gFalse;
925 }
926