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