upgrade to xpdf 3.00.
[swftools.git] / pdf2swf / xpdf / Link.cc
blob2d146b5736056d1c80864ca562e02561a68a167a
1 //========================================================================
2 //
3 // Link.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 #include <aconf.h>
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
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"
25 //------------------------------------------------------------------------
26 // LinkAction
27 //------------------------------------------------------------------------
29 LinkAction *LinkAction::parseDest(Object *obj) {
30 LinkAction *action;
32 action = new LinkGoTo(obj);
33 if (!action->isOk()) {
34 delete action;
35 return NULL;
37 return action;
40 LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
41 LinkAction *action;
42 Object obj2, obj3, obj4;
44 if (!obj->isDict()) {
45 error(-1, "Bad annotation action");
46 return NULL;
49 obj->dictLookup("S", &obj2);
51 // GoTo action
52 if (obj2.isName("GoTo")) {
53 obj->dictLookup("D", &obj3);
54 action = new LinkGoTo(&obj3);
55 obj3.free();
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();
65 // Launch action
66 } else if (obj2.isName("Launch")) {
67 action = new LinkLaunch(obj);
69 // URI action
70 } else if (obj2.isName("URI")) {
71 obj->dictLookup("URI", &obj3);
72 action = new LinkURI(&obj3, baseURI);
73 obj3.free();
75 // Named action
76 } else if (obj2.isName("Named")) {
77 obj->dictLookup("N", &obj3);
78 action = new LinkNamed(&obj3);
79 obj3.free();
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();
89 // unknown action
90 } else if (obj2.isName()) {
91 action = new LinkUnknown(obj2.getName());
93 // action is missing or wrong type
94 } else {
95 error(-1, "Bad annotation action");
96 action = NULL;
99 obj2.free();
101 if (action && !action->isOk()) {
102 delete action;
103 return NULL;
105 return action;
108 GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
109 GString *name;
110 Object obj1;
112 name = NULL;
114 // string
115 if (fileSpecObj->isString()) {
116 name = fileSpecObj->getString()->copy();
118 // dictionary
119 } else if (fileSpecObj->isDict()) {
120 if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
121 obj1.free();
122 fileSpecObj->dictLookup("F", &obj1);
124 if (obj1.isString())
125 name = obj1.getString()->copy();
126 else
127 error(-1, "Illegal file spec in link");
128 obj1.free();
130 // error
131 } else {
132 error(-1, "Illegal file spec in link");
135 return name;
138 //------------------------------------------------------------------------
139 // LinkDest
140 //------------------------------------------------------------------------
142 LinkDest::LinkDest(Array *a) {
143 Object obj1, obj2;
145 // initialize fields
146 left = bottom = right = top = zoom = 0;
147 ok = gFalse;
149 // get page
150 if (a->getLength() < 2) {
151 error(-1, "Annotation destination array is too short");
152 return;
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;
166 obj1.free();
168 // get destination type
169 a->get(1, &obj1);
171 // XYZ link
172 if (obj1.isName("XYZ")) {
173 kind = destXYZ;
174 if (a->getLength() < 3) {
175 changeLeft = gFalse;
176 } else {
177 a->get(2, &obj2);
178 if (obj2.isNull()) {
179 changeLeft = gFalse;
180 } else if (obj2.isNum()) {
181 changeLeft = gTrue;
182 left = obj2.getNum();
183 } else {
184 error(-1, "Bad annotation destination position");
185 goto err1;
187 obj2.free();
189 if (a->getLength() < 4) {
190 changeTop = gFalse;
191 } else {
192 a->get(3, &obj2);
193 if (obj2.isNull()) {
194 changeTop = gFalse;
195 } else if (obj2.isNum()) {
196 changeTop = gTrue;
197 top = obj2.getNum();
198 } else {
199 error(-1, "Bad annotation destination position");
200 goto err1;
202 obj2.free();
204 if (a->getLength() < 5) {
205 changeZoom = gFalse;
206 } else {
207 a->get(4, &obj2);
208 if (obj2.isNull()) {
209 changeZoom = gFalse;
210 } else if (obj2.isNum()) {
211 changeZoom = gTrue;
212 zoom = obj2.getNum();
213 } else {
214 error(-1, "Bad annotation destination position");
215 goto err1;
217 obj2.free();
220 // Fit link
221 } else if (obj1.isName("Fit")) {
222 if (a->getLength() < 2) {
223 error(-1, "Annotation destination array is too short");
224 goto err2;
226 kind = destFit;
228 // FitH link
229 } else if (obj1.isName("FitH")) {
230 if (a->getLength() < 3) {
231 error(-1, "Annotation destination array is too short");
232 goto err2;
234 kind = destFitH;
235 if (!a->get(2, &obj2)->isNum()) {
236 error(-1, "Bad annotation destination position");
237 goto err1;
239 top = obj2.getNum();
240 obj2.free();
242 // FitV link
243 } else if (obj1.isName("FitV")) {
244 if (a->getLength() < 3) {
245 error(-1, "Annotation destination array is too short");
246 goto err2;
248 kind = destFitV;
249 if (!a->get(2, &obj2)->isNum()) {
250 error(-1, "Bad annotation destination position");
251 goto err1;
253 left = obj2.getNum();
254 obj2.free();
256 // FitR link
257 } else if (obj1.isName("FitR")) {
258 if (a->getLength() < 6) {
259 error(-1, "Annotation destination array is too short");
260 goto err2;
262 kind = destFitR;
263 if (!a->get(2, &obj2)->isNum()) {
264 error(-1, "Bad annotation destination position");
265 goto err1;
267 left = obj2.getNum();
268 obj2.free();
269 if (!a->get(3, &obj2)->isNum()) {
270 error(-1, "Bad annotation destination position");
271 goto err1;
273 bottom = obj2.getNum();
274 obj2.free();
275 if (!a->get(4, &obj2)->isNum()) {
276 error(-1, "Bad annotation destination position");
277 goto err1;
279 right = obj2.getNum();
280 obj2.free();
281 if (!a->get(5, &obj2)->isNum()) {
282 error(-1, "Bad annotation destination position");
283 goto err1;
285 top = obj2.getNum();
286 obj2.free();
288 // FitB link
289 } else if (obj1.isName("FitB")) {
290 if (a->getLength() < 2) {
291 error(-1, "Annotation destination array is too short");
292 goto err2;
294 kind = destFitB;
296 // FitBH link
297 } else if (obj1.isName("FitBH")) {
298 if (a->getLength() < 3) {
299 error(-1, "Annotation destination array is too short");
300 goto err2;
302 kind = destFitBH;
303 if (!a->get(2, &obj2)->isNum()) {
304 error(-1, "Bad annotation destination position");
305 goto err1;
307 top = obj2.getNum();
308 obj2.free();
310 // FitBV link
311 } else if (obj1.isName("FitBV")) {
312 if (a->getLength() < 3) {
313 error(-1, "Annotation destination array is too short");
314 goto err2;
316 kind = destFitBV;
317 if (!a->get(2, &obj2)->isNum()) {
318 error(-1, "Bad annotation destination position");
319 goto err1;
321 left = obj2.getNum();
322 obj2.free();
324 // unknown link kind
325 } else {
326 error(-1, "Unknown annotation destination type");
327 goto err2;
330 obj1.free();
331 ok = gTrue;
332 return;
334 err1:
335 obj2.free();
336 err2:
337 obj1.free();
340 LinkDest::LinkDest(LinkDest *dest) {
341 kind = dest->kind;
342 pageIsRef = dest->pageIsRef;
343 if (pageIsRef)
344 pageRef = dest->pageRef;
345 else
346 pageNum = dest->pageNum;
347 left = dest->left;
348 bottom = dest->bottom;
349 right = dest->right;
350 top = dest->top;
351 zoom = dest->zoom;
352 changeLeft = dest->changeLeft;
353 changeTop = dest->changeTop;
354 changeZoom = dest->changeZoom;
355 ok = gTrue;
358 //------------------------------------------------------------------------
359 // LinkGoTo
360 //------------------------------------------------------------------------
362 LinkGoTo::LinkGoTo(Object *destObj) {
363 dest = NULL;
364 namedDest = NULL;
366 // named destination
367 if (destObj->isName()) {
368 namedDest = new GString(destObj->getName());
369 } else if (destObj->isString()) {
370 namedDest = destObj->getString()->copy();
372 // destination dictionary
373 } else if (destObj->isArray()) {
374 dest = new LinkDest(destObj->getArray());
375 if (!dest->isOk()) {
376 delete dest;
377 dest = NULL;
380 // error
381 } else {
382 error(-1, "Illegal annotation destination");
386 LinkGoTo::~LinkGoTo() {
387 if (dest)
388 delete dest;
389 if (namedDest)
390 delete namedDest;
393 //------------------------------------------------------------------------
394 // LinkGoToR
395 //------------------------------------------------------------------------
397 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
398 dest = NULL;
399 namedDest = NULL;
401 // get file name
402 fileName = getFileSpecName(fileSpecObj);
404 // named destination
405 if (destObj->isName()) {
406 namedDest = new GString(destObj->getName());
407 } else if (destObj->isString()) {
408 namedDest = destObj->getString()->copy();
410 // destination dictionary
411 } else if (destObj->isArray()) {
412 dest = new LinkDest(destObj->getArray());
413 if (!dest->isOk()) {
414 delete dest;
415 dest = NULL;
418 // error
419 } else {
420 error(-1, "Illegal annotation destination");
424 LinkGoToR::~LinkGoToR() {
425 if (fileName)
426 delete fileName;
427 if (dest)
428 delete dest;
429 if (namedDest)
430 delete namedDest;
434 //------------------------------------------------------------------------
435 // LinkLaunch
436 //------------------------------------------------------------------------
438 LinkLaunch::LinkLaunch(Object *actionObj) {
439 Object obj1, obj2;
441 fileName = NULL;
442 params = NULL;
444 if (actionObj->isDict()) {
445 if (!actionObj->dictLookup("F", &obj1)->isNull()) {
446 fileName = getFileSpecName(&obj1);
447 } else {
448 obj1.free();
449 #ifdef WIN32
450 if (actionObj->dictLookup("Win", &obj1)->isDict()) {
451 obj1.dictLookup("F", &obj2);
452 fileName = getFileSpecName(&obj2);
453 obj2.free();
454 if (obj1.dictLookup("P", &obj2)->isString()) {
455 params = obj2.getString()->copy();
457 obj2.free();
458 } else {
459 error(-1, "Bad launch-type link action");
461 #else
462 //~ This hasn't been defined by Adobe yet, so assume it looks
463 //~ just like the Win dictionary until they say otherwise.
464 if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
465 obj1.dictLookup("F", &obj2);
466 fileName = getFileSpecName(&obj2);
467 obj2.free();
468 if (obj1.dictLookup("P", &obj2)->isString()) {
469 params = obj2.getString()->copy();
471 obj2.free();
472 } else {
473 error(-1, "Bad launch-type link action");
475 #endif
477 obj1.free();
481 LinkLaunch::~LinkLaunch() {
482 if (fileName)
483 delete fileName;
484 if (params)
485 delete params;
488 //------------------------------------------------------------------------
489 // LinkURI
490 //------------------------------------------------------------------------
492 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
493 GString *uri2;
494 int n;
495 char c;
497 uri = NULL;
498 if (uriObj->isString()) {
499 uri2 = uriObj->getString()->copy();
500 if (baseURI) {
501 n = strcspn(uri2->getCString(), "/:");
502 if (n == uri2->getLength() || uri2->getChar(n) == '/') {
503 uri = baseURI->copy();
504 c = uri->getChar(uri->getLength() - 1);
505 if (c == '/' || c == '?') {
506 if (uri2->getChar(0) == '/') {
507 uri2->del(0);
509 } else {
510 if (uri2->getChar(0) != '/') {
511 uri->append('/');
514 uri->append(uri2);
515 delete uri2;
516 } else {
517 uri = uri2;
519 } else {
520 uri = uri2;
522 } else {
523 error(-1, "Illegal URI-type link");
527 LinkURI::~LinkURI() {
528 if (uri)
529 delete uri;
532 //------------------------------------------------------------------------
533 // LinkNamed
534 //------------------------------------------------------------------------
536 LinkNamed::LinkNamed(Object *nameObj) {
537 name = NULL;
538 if (nameObj->isName()) {
539 name = new GString(nameObj->getName());
543 LinkNamed::~LinkNamed() {
544 if (name) {
545 delete name;
549 //------------------------------------------------------------------------
550 // LinkMovie
551 //------------------------------------------------------------------------
553 LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
554 annotRef.num = -1;
555 title = NULL;
556 if (annotObj->isRef()) {
557 annotRef = annotObj->getRef();
558 } else if (titleObj->isString()) {
559 title = titleObj->getString()->copy();
560 } else {
561 error(-1, "Movie action is missing both the Annot and T keys");
565 LinkMovie::~LinkMovie() {
566 if (title) {
567 delete title;
571 //------------------------------------------------------------------------
572 // LinkUnknown
573 //------------------------------------------------------------------------
575 LinkUnknown::LinkUnknown(char *actionA) {
576 action = new GString(actionA);
579 LinkUnknown::~LinkUnknown() {
580 delete action;
583 //------------------------------------------------------------------------
584 // LinkBorderStyle
585 //------------------------------------------------------------------------
587 LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
588 double *dashA, int dashLengthA,
589 double rA, double gA, double bA) {
590 type = typeA;
591 width = widthA;
592 dash = dashA;
593 dashLength = dashLengthA;
594 r = rA;
595 g = gA;
596 b = bA;
599 LinkBorderStyle::~LinkBorderStyle() {
600 if (dash) {
601 gfree(dash);
605 //------------------------------------------------------------------------
606 // Link
607 //------------------------------------------------------------------------
609 Link::Link(Dict *dict, GString *baseURI) {
610 Object obj1, obj2, obj3;
611 LinkBorderType borderType;
612 double borderWidth;
613 double *borderDash;
614 int borderDashLength;
615 double borderR, borderG, borderB;
616 double t;
617 int i;
619 borderStyle = NULL;
620 action = NULL;
621 ok = gFalse;
623 // get rectangle
624 if (!dict->lookup("Rect", &obj1)->isArray()) {
625 error(-1, "Annotation rectangle is wrong type");
626 goto err2;
628 if (!obj1.arrayGet(0, &obj2)->isNum()) {
629 error(-1, "Bad annotation rectangle");
630 goto err1;
632 x1 = obj2.getNum();
633 obj2.free();
634 if (!obj1.arrayGet(1, &obj2)->isNum()) {
635 error(-1, "Bad annotation rectangle");
636 goto err1;
638 y1 = obj2.getNum();
639 obj2.free();
640 if (!obj1.arrayGet(2, &obj2)->isNum()) {
641 error(-1, "Bad annotation rectangle");
642 goto err1;
644 x2 = obj2.getNum();
645 obj2.free();
646 if (!obj1.arrayGet(3, &obj2)->isNum()) {
647 error(-1, "Bad annotation rectangle");
648 goto err1;
650 y2 = obj2.getNum();
651 obj2.free();
652 obj1.free();
653 if (x1 > x2) {
654 t = x1;
655 x1 = x2;
656 x2 = t;
658 if (y1 > y2) {
659 t = y1;
660 y1 = y2;
661 y2 = t;
664 // get the border style info
665 borderType = linkBorderSolid;
666 borderWidth = 1;
667 borderDash = NULL;
668 borderDashLength = 0;
669 borderR = 0;
670 borderG = 0;
671 borderB = 1;
672 if (dict->lookup("BS", &obj1)->isDict()) {
673 if (obj1.dictLookup("S", &obj2)->isName()) {
674 if (obj2.isName("S")) {
675 borderType = linkBorderSolid;
676 } else if (obj2.isName("D")) {
677 borderType = linkBorderDashed;
678 } else if (obj2.isName("B")) {
679 borderType = linkBorderEmbossed;
680 } else if (obj2.isName("I")) {
681 borderType = linkBorderEngraved;
682 } else if (obj2.isName("U")) {
683 borderType = linkBorderUnderlined;
686 obj2.free();
687 if (obj1.dictLookup("W", &obj2)->isNum()) {
688 borderWidth = obj2.getNum();
690 obj2.free();
691 if (obj1.dictLookup("D", &obj2)->isArray()) {
692 borderDashLength = obj2.arrayGetLength();
693 borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
694 for (i = 0; i < borderDashLength; ++i) {
695 if (obj2.arrayGet(i, &obj3)->isNum()) {
696 borderDash[i] = obj3.getNum();
697 } else {
698 borderDash[i] = 1;
700 obj3.free();
703 obj2.free();
704 } else {
705 obj1.free();
706 if (dict->lookup("Border", &obj1)->isArray()) {
707 if (obj1.arrayGetLength() >= 3) {
708 if (obj1.arrayGet(2, &obj2)->isNum()) {
709 borderWidth = obj2.getNum();
711 obj2.free();
712 if (obj1.arrayGetLength() >= 4) {
713 if (obj1.arrayGet(3, &obj2)->isArray()) {
714 borderType = linkBorderDashed;
715 borderDashLength = obj2.arrayGetLength();
716 borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
717 for (i = 0; i < borderDashLength; ++i) {
718 if (obj2.arrayGet(i, &obj3)->isNum()) {
719 borderDash[i] = obj3.getNum();
720 } else {
721 borderDash[i] = 1;
723 obj3.free();
726 obj2.free();
731 obj1.free();
732 if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
733 if (obj1.arrayGet(0, &obj2)->isNum()) {
734 borderR = obj2.getNum();
736 obj1.free();
737 if (obj1.arrayGet(1, &obj2)->isNum()) {
738 borderG = obj2.getNum();
740 obj1.free();
741 if (obj1.arrayGet(2, &obj2)->isNum()) {
742 borderB = obj2.getNum();
744 obj1.free();
746 obj1.free();
747 borderStyle = new LinkBorderStyle(borderType, borderWidth,
748 borderDash, borderDashLength,
749 borderR, borderG, borderB);
751 // look for destination
752 if (!dict->lookup("Dest", &obj1)->isNull()) {
753 action = LinkAction::parseDest(&obj1);
755 // look for action
756 } else {
757 obj1.free();
758 if (dict->lookup("A", &obj1)->isDict()) {
759 action = LinkAction::parseAction(&obj1, baseURI);
762 obj1.free();
764 // check for bad action
765 if (action) {
766 ok = gTrue;
769 return;
771 err1:
772 obj2.free();
773 err2:
774 obj1.free();
777 Link::~Link() {
778 if (borderStyle) {
779 delete borderStyle;
781 if (action) {
782 delete action;
786 //------------------------------------------------------------------------
787 // Links
788 //------------------------------------------------------------------------
790 Links::Links(Object *annots, GString *baseURI) {
791 Link *link;
792 Object obj1, obj2;
793 int size;
794 int i;
796 links = NULL;
797 size = 0;
798 numLinks = 0;
800 if (annots->isArray()) {
801 for (i = 0; i < annots->arrayGetLength(); ++i) {
802 if (annots->arrayGet(i, &obj1)->isDict()) {
803 if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
804 link = new Link(obj1.getDict(), baseURI);
805 if (link->isOk()) {
806 if (numLinks >= size) {
807 size += 16;
808 links = (Link **)grealloc(links, size * sizeof(Link *));
810 links[numLinks++] = link;
811 } else {
812 delete link;
815 obj2.free();
817 obj1.free();
822 Links::~Links() {
823 int i;
825 for (i = 0; i < numLinks; ++i)
826 delete links[i];
827 gfree(links);
830 LinkAction *Links::find(double x, double y) {
831 int i;
833 for (i = numLinks - 1; i >= 0; --i) {
834 if (links[i]->inRect(x, y)) {
835 return links[i]->getAction();
838 return NULL;
841 GBool Links::onLink(double x, double y) {
842 int i;
844 for (i = 0; i < numLinks; ++i) {
845 if (links[i]->inRect(x, y))
846 return gTrue;
848 return gFalse;