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