1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
25 //------------------------------------------------------------------------
27 //------------------------------------------------------------------------
29 LinkAction *LinkAction::parseDest(Object *obj) {
32 action = new LinkGoTo(obj);
33 if (!action->isOk()) {
40 LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
42 Object obj2, obj3, obj4;
45 error(-1, "Bad annotation action");
49 obj->dictLookup("S", &obj2);
52 if (obj2.isName("GoTo")) {
53 obj->dictLookup("D", &obj3);
54 action = new LinkGoTo(&obj3);
58 } else if (obj2.isName("GoToR")) {
59 obj->dictLookup("F", &obj3);
60 obj->dictLookup("D", &obj4);
61 action = new LinkGoToR(&obj3, &obj4);
66 } else if (obj2.isName("Launch")) {
67 action = new LinkLaunch(obj);
70 } else if (obj2.isName("URI")) {
71 obj->dictLookup("URI", &obj3);
72 action = new LinkURI(&obj3, baseURI);
76 } else if (obj2.isName("Named")) {
77 obj->dictLookup("N", &obj3);
78 action = new LinkNamed(&obj3);
82 } else if (obj2.isName("Movie")) {
83 obj->dictLookupNF("Annot", &obj3);
84 obj->dictLookup("T", &obj4);
85 action = new LinkMovie(&obj3, &obj4);
90 } else if (obj2.isName()) {
91 action = new LinkUnknown(obj2.getName());
93 // action is missing or wrong type
95 error(-1, "Bad annotation action");
101 if (action && !action->isOk()) {
108 GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
115 if (fileSpecObj->isString()) {
116 name = fileSpecObj->getString()->copy();
119 } else if (fileSpecObj->isDict()) {
121 if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
123 if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
126 fileSpecObj->dictLookup("F", &obj1);
128 if (obj1.isString()) {
129 name = obj1.getString()->copy();
131 error(-1, "Illegal file spec in link");
137 error(-1, "Illegal file spec in link");
140 // system-dependent path manipulation
145 // "//...." --> "\...."
146 // "/x/...." --> "x:\...."
147 // "/server/share/...." --> "\\server\share\...."
148 // convert escaped slashes to slashes and unescaped slashes to backslashes
150 if (name->getChar(0) == '/') {
151 if (name->getLength() >= 2 && name->getChar(1) == '/') {
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, ':');
162 for (j = 2; j < name->getLength(); ++j) {
163 if (name->getChar(j-1) != '\\' &&
164 name->getChar(j) == '/') {
168 if (j < name->getLength()) {
169 name->setChar(0, '\\');
170 name->insert(0, '\\');
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) == '/') {
185 // no manipulation needed for Unix
192 //------------------------------------------------------------------------
194 //------------------------------------------------------------------------
196 LinkDest::LinkDest(Array *a) {
200 left = bottom = right = top = zoom = 0;
204 if (a->getLength() < 2) {
205 error(-1, "Annotation destination array is too short");
210 pageNum = obj1.getInt() + 1;
212 } else if (obj1.isRef()) {
213 pageRef.num = obj1.getRefNum();
214 pageRef.gen = obj1.getRefGen();
217 error(-1, "Bad annotation destination");
222 // get destination type
226 if (obj1.isName("XYZ")) {
228 if (a->getLength() < 3) {
234 } else if (obj2.isNum()) {
236 left = obj2.getNum();
238 error(-1, "Bad annotation destination position");
243 if (a->getLength() < 4) {
249 } else if (obj2.isNum()) {
253 error(-1, "Bad annotation destination position");
258 if (a->getLength() < 5) {
264 } else if (obj2.isNum()) {
266 zoom = obj2.getNum();
268 error(-1, "Bad annotation destination position");
275 } else if (obj1.isName("Fit")) {
276 if (a->getLength() < 2) {
277 error(-1, "Annotation destination array is too short");
283 } else if (obj1.isName("FitH")) {
284 if (a->getLength() < 3) {
285 error(-1, "Annotation destination array is too short");
289 if (!a->get(2, &obj2)->isNum()) {
290 error(-1, "Bad annotation destination position");
297 } else if (obj1.isName("FitV")) {
298 if (a->getLength() < 3) {
299 error(-1, "Annotation destination array is too short");
303 if (!a->get(2, &obj2)->isNum()) {
304 error(-1, "Bad annotation destination position");
307 left = obj2.getNum();
311 } else if (obj1.isName("FitR")) {
312 if (a->getLength() < 6) {
313 error(-1, "Annotation destination array is too short");
317 if (!a->get(2, &obj2)->isNum()) {
318 error(-1, "Bad annotation destination position");
321 left = obj2.getNum();
323 if (!a->get(3, &obj2)->isNum()) {
324 error(-1, "Bad annotation destination position");
327 bottom = obj2.getNum();
329 if (!a->get(4, &obj2)->isNum()) {
330 error(-1, "Bad annotation destination position");
333 right = obj2.getNum();
335 if (!a->get(5, &obj2)->isNum()) {
336 error(-1, "Bad annotation destination position");
343 } else if (obj1.isName("FitB")) {
344 if (a->getLength() < 2) {
345 error(-1, "Annotation destination array is too short");
351 } else if (obj1.isName("FitBH")) {
352 if (a->getLength() < 3) {
353 error(-1, "Annotation destination array is too short");
357 if (!a->get(2, &obj2)->isNum()) {
358 error(-1, "Bad annotation destination position");
365 } else if (obj1.isName("FitBV")) {
366 if (a->getLength() < 3) {
367 error(-1, "Annotation destination array is too short");
371 if (!a->get(2, &obj2)->isNum()) {
372 error(-1, "Bad annotation destination position");
375 left = obj2.getNum();
380 error(-1, "Unknown annotation destination type");
394 LinkDest::LinkDest(LinkDest *dest) {
396 pageIsRef = dest->pageIsRef;
398 pageRef = dest->pageRef;
400 pageNum = dest->pageNum;
402 bottom = dest->bottom;
406 changeLeft = dest->changeLeft;
407 changeTop = dest->changeTop;
408 changeZoom = dest->changeZoom;
412 //------------------------------------------------------------------------
414 //------------------------------------------------------------------------
416 LinkGoTo::LinkGoTo(Object *destObj) {
421 if (destObj->isName()) {
422 namedDest = new GString(destObj->getName());
423 } else if (destObj->isString()) {
424 namedDest = destObj->getString()->copy();
426 // destination dictionary
427 } else if (destObj->isArray()) {
428 dest = new LinkDest(destObj->getArray());
435 error(-1, "Illegal annotation destination %d", destObj->getType());
439 LinkGoTo::~LinkGoTo() {
446 //------------------------------------------------------------------------
448 //------------------------------------------------------------------------
450 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
455 fileName = getFileSpecName(fileSpecObj);
458 if (destObj->isName()) {
459 namedDest = new GString(destObj->getName());
460 } else if (destObj->isString()) {
461 namedDest = destObj->getString()->copy();
463 // destination dictionary
464 } else if (destObj->isArray()) {
465 dest = new LinkDest(destObj->getArray());
472 error(-1, "Illegal annotation destination %d", destObj->getType());
476 LinkGoToR::~LinkGoToR() {
486 //------------------------------------------------------------------------
488 //------------------------------------------------------------------------
490 LinkLaunch::LinkLaunch(Object *actionObj) {
496 if (actionObj->isDict()) {
497 if (!actionObj->dictLookup("F", &obj1)->isNull()) {
498 fileName = getFileSpecName(&obj1);
502 if (actionObj->dictLookup("Win", &obj1)->isDict()) {
503 obj1.dictLookup("F", &obj2);
504 fileName = getFileSpecName(&obj2);
506 if (obj1.dictLookup("P", &obj2)->isString()) {
507 params = obj2.getString()->copy();
511 error(-1, "Bad launch-type link action");
514 //~ This hasn't been defined by Adobe yet, so assume it looks
515 //~ just like the Win dictionary until they say otherwise.
516 if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
517 obj1.dictLookup("F", &obj2);
518 fileName = getFileSpecName(&obj2);
520 if (obj1.dictLookup("P", &obj2)->isString()) {
521 params = obj2.getString()->copy();
525 error(-1, "Bad launch-type link action");
533 LinkLaunch::~LinkLaunch() {
540 //------------------------------------------------------------------------
542 //------------------------------------------------------------------------
544 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
550 if (uriObj->isString()) {
551 uri2 = uriObj->getString()->copy();
552 if (baseURI && baseURI->getLength() > 0) {
553 n = strcspn(uri2->getCString(), "/:");
554 if (n == uri2->getLength() || uri2->getChar(n) == '/') {
555 uri = baseURI->copy();
556 c = uri->getChar(uri->getLength() - 1);
557 if (c == '/' || c == '?') {
558 if (uri2->getChar(0) == '/') {
562 if (uri2->getChar(0) != '/') {
575 error(-1, "Illegal URI-type link");
579 LinkURI::~LinkURI() {
584 //------------------------------------------------------------------------
586 //------------------------------------------------------------------------
588 LinkNamed::LinkNamed(Object *nameObj) {
590 if (nameObj->isName()) {
591 name = new GString(nameObj->getName());
595 LinkNamed::~LinkNamed() {
601 //------------------------------------------------------------------------
603 //------------------------------------------------------------------------
605 LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
608 if (annotObj->isRef()) {
609 annotRef = annotObj->getRef();
610 } else if (titleObj->isString()) {
611 title = titleObj->getString()->copy();
613 error(-1, "Movie action is missing both the Annot and T keys");
617 LinkMovie::~LinkMovie() {
623 //------------------------------------------------------------------------
625 //------------------------------------------------------------------------
627 LinkUnknown::LinkUnknown(char *actionA) {
628 action = new GString(actionA);
631 LinkUnknown::~LinkUnknown() {
635 //------------------------------------------------------------------------
637 //------------------------------------------------------------------------
639 LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
640 double *dashA, int dashLengthA,
641 double rA, double gA, double bA) {
645 dashLength = dashLengthA;
651 LinkBorderStyle::~LinkBorderStyle() {
657 //------------------------------------------------------------------------
659 //------------------------------------------------------------------------
661 Link::Link(Dict *dict, GString *baseURI) {
662 Object obj1, obj2, obj3;
663 LinkBorderType borderType;
666 int borderDashLength;
667 double borderR, borderG, borderB;
676 if (!dict->lookup("Rect", &obj1)->isArray()) {
677 error(-1, "Annotation rectangle is wrong type");
680 if (!obj1.arrayGet(0, &obj2)->isNum()) {
681 error(-1, "Bad annotation rectangle");
686 if (!obj1.arrayGet(1, &obj2)->isNum()) {
687 error(-1, "Bad annotation rectangle");
692 if (!obj1.arrayGet(2, &obj2)->isNum()) {
693 error(-1, "Bad annotation rectangle");
698 if (!obj1.arrayGet(3, &obj2)->isNum()) {
699 error(-1, "Bad annotation rectangle");
716 // get the border style info
717 borderType = linkBorderSolid;
720 borderDashLength = 0;
724 if (dict->lookup("BS", &obj1)->isDict()) {
725 if (obj1.dictLookup("S", &obj2)->isName()) {
726 if (obj2.isName("S")) {
727 borderType = linkBorderSolid;
728 } else if (obj2.isName("D")) {
729 borderType = linkBorderDashed;
730 } else if (obj2.isName("B")) {
731 borderType = linkBorderEmbossed;
732 } else if (obj2.isName("I")) {
733 borderType = linkBorderEngraved;
734 } else if (obj2.isName("U")) {
735 borderType = linkBorderUnderlined;
739 if (obj1.dictLookup("W", &obj2)->isNum()) {
740 borderWidth = obj2.getNum();
743 if (obj1.dictLookup("D", &obj2)->isArray()) {
744 borderDashLength = obj2.arrayGetLength();
745 borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
746 for (i = 0; i < borderDashLength; ++i) {
747 if (obj2.arrayGet(i, &obj3)->isNum()) {
748 borderDash[i] = obj3.getNum();
758 if (dict->lookup("Border", &obj1)->isArray()) {
759 if (obj1.arrayGetLength() >= 3) {
760 if (obj1.arrayGet(2, &obj2)->isNum()) {
761 borderWidth = obj2.getNum();
764 if (obj1.arrayGetLength() >= 4) {
765 if (obj1.arrayGet(3, &obj2)->isArray()) {
766 borderType = linkBorderDashed;
767 borderDashLength = obj2.arrayGetLength();
768 borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
769 for (i = 0; i < borderDashLength; ++i) {
770 if (obj2.arrayGet(i, &obj3)->isNum()) {
771 borderDash[i] = obj3.getNum();
778 // Adobe draws no border at all if the last element is of
788 if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
789 if (obj1.arrayGet(0, &obj2)->isNum()) {
790 borderR = obj2.getNum();
793 if (obj1.arrayGet(1, &obj2)->isNum()) {
794 borderG = obj2.getNum();
797 if (obj1.arrayGet(2, &obj2)->isNum()) {
798 borderB = obj2.getNum();
803 borderStyle = new LinkBorderStyle(borderType, borderWidth,
804 borderDash, borderDashLength,
805 borderR, borderG, borderB);
807 // look for destination
808 if (!dict->lookup("Dest", &obj1)->isNull()) {
809 action = LinkAction::parseDest(&obj1);
814 if (dict->lookup("A", &obj1)->isDict()) {
815 action = LinkAction::parseAction(&obj1, baseURI);
820 // check for bad action
842 //------------------------------------------------------------------------
844 //------------------------------------------------------------------------
846 Links::Links(Object *annots, GString *baseURI) {
856 if (annots->isArray()) {
857 for (i = 0; i < annots->arrayGetLength(); ++i) {
858 if (annots->arrayGet(i, &obj1)->isDict()) {
859 if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
860 link = new Link(obj1.getDict(), baseURI);
862 if (numLinks >= size) {
864 links = (Link **)greallocn(links, size, sizeof(Link *));
866 links[numLinks++] = link;
881 for (i = 0; i < numLinks; ++i)
886 LinkAction *Links::find(double x, double y) {
889 for (i = numLinks - 1; i >= 0; --i) {
890 if (links[i]->inRect(x, y)) {
891 return links[i]->getAction();
897 GBool Links::onLink(double x, double y) {
900 for (i = 0; i < numLinks; ++i) {
901 if (links[i]->inRect(x, y))