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, "parseAction: Bad annotation action for URI '%s'",
46 baseURI ? baseURI->getCString() : "NULL");
50 obj->dictLookup("S", &obj2);
53 if (obj2.isName("GoTo")) {
54 obj->dictLookup("D", &obj3);
55 action = new LinkGoTo(&obj3);
59 } else if (obj2.isName("GoToR")) {
60 obj->dictLookup("F", &obj3);
61 obj->dictLookup("D", &obj4);
62 action = new LinkGoToR(&obj3, &obj4);
67 } else if (obj2.isName("Launch")) {
68 action = new LinkLaunch(obj);
71 } else if (obj2.isName("URI")) {
72 obj->dictLookup("URI", &obj3);
73 action = new LinkURI(&obj3, baseURI);
77 } else if (obj2.isName("Named")) {
78 obj->dictLookup("N", &obj3);
79 action = new LinkNamed(&obj3);
83 } else if (obj2.isName("Movie")) {
84 obj->dictLookupNF("Annot", &obj3);
85 obj->dictLookup("T", &obj4);
86 action = new LinkMovie(&obj3, &obj4);
91 } else if (obj2.isName()) {
92 action = new LinkUnknown(obj2.getName());
94 // action is missing or wrong type
96 error(-1, "parseAction: Unknown annotation action object: URI = '%s'",
97 baseURI ? baseURI->getCString() : "NULL");
103 if (action && !action->isOk()) {
110 GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
117 if (fileSpecObj->isString()) {
118 name = fileSpecObj->getString()->copy();
121 } else if (fileSpecObj->isDict()) {
122 if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
124 fileSpecObj->dictLookup("F", &obj1);
127 name = obj1.getString()->copy();
129 error(-1, "Illegal file spec in link");
134 error(-1, "Illegal file spec in link");
140 //------------------------------------------------------------------------
142 //------------------------------------------------------------------------
144 LinkDest::LinkDest(Array *a) {
148 left = bottom = right = top = zoom = 0;
152 if (a->getLength() < 2) {
153 error(-1, "Annotation destination array is too short");
158 pageNum = obj1.getInt() + 1;
160 } else if (obj1.isRef()) {
161 pageRef.num = obj1.getRefNum();
162 pageRef.gen = obj1.getRefGen();
165 error(-1, "Bad annotation destination");
170 // get destination type
174 if (obj1.isName("XYZ")) {
176 if (a->getLength() < 3) {
182 } else if (obj2.isNum()) {
184 left = obj2.getNum();
186 error(-1, "Bad annotation destination position");
191 if (a->getLength() < 4) {
197 } else if (obj2.isNum()) {
201 error(-1, "Bad annotation destination position");
206 if (a->getLength() < 5) {
212 } else if (obj2.isNum()) {
214 zoom = obj2.getNum();
216 error(-1, "Bad annotation destination position");
223 } else if (obj1.isName("Fit")) {
224 if (a->getLength() < 2) {
225 error(-1, "Annotation destination array is too short");
231 } else if (obj1.isName("FitH")) {
232 if (a->getLength() < 3) {
233 error(-1, "Annotation destination array is too short");
237 if (!a->get(2, &obj2)->isNum()) {
238 error(-1, "Bad annotation destination position");
245 } else if (obj1.isName("FitV")) {
246 if (a->getLength() < 3) {
247 error(-1, "Annotation destination array is too short");
251 if (!a->get(2, &obj2)->isNum()) {
252 error(-1, "Bad annotation destination position");
255 left = obj2.getNum();
259 } else if (obj1.isName("FitR")) {
260 if (a->getLength() < 6) {
261 error(-1, "Annotation destination array is too short");
265 if (!a->get(2, &obj2)->isNum()) {
266 error(-1, "Bad annotation destination position");
269 left = obj2.getNum();
271 if (!a->get(3, &obj2)->isNum()) {
272 error(-1, "Bad annotation destination position");
275 bottom = obj2.getNum();
277 if (!a->get(4, &obj2)->isNum()) {
278 error(-1, "Bad annotation destination position");
281 right = obj2.getNum();
283 if (!a->get(5, &obj2)->isNum()) {
284 error(-1, "Bad annotation destination position");
291 } else if (obj1.isName("FitB")) {
292 if (a->getLength() < 2) {
293 error(-1, "Annotation destination array is too short");
299 } else if (obj1.isName("FitBH")) {
300 if (a->getLength() < 3) {
301 error(-1, "Annotation destination array is too short");
305 if (!a->get(2, &obj2)->isNum()) {
306 error(-1, "Bad annotation destination position");
313 } else if (obj1.isName("FitBV")) {
314 if (a->getLength() < 3) {
315 error(-1, "Annotation destination array is too short");
319 if (!a->get(2, &obj2)->isNum()) {
320 error(-1, "Bad annotation destination position");
323 left = obj2.getNum();
328 error(-1, "Unknown annotation destination type");
342 LinkDest::LinkDest(LinkDest *dest) {
344 pageIsRef = dest->pageIsRef;
346 pageRef = dest->pageRef;
348 pageNum = dest->pageNum;
350 bottom = dest->bottom;
354 changeLeft = dest->changeLeft;
355 changeTop = dest->changeTop;
356 changeZoom = dest->changeZoom;
360 //------------------------------------------------------------------------
362 //------------------------------------------------------------------------
364 LinkGoTo::LinkGoTo(Object *destObj) {
369 if (destObj->isName()) {
370 namedDest = new GString(destObj->getName());
371 } else if (destObj->isString()) {
372 namedDest = destObj->getString()->copy();
374 // destination dictionary
375 } else if (destObj->isArray()) {
376 dest = new LinkDest(destObj->getArray());
384 error(-1, "Illegal annotation destination");
388 LinkGoTo::~LinkGoTo() {
395 //------------------------------------------------------------------------
397 //------------------------------------------------------------------------
399 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
404 fileName = getFileSpecName(fileSpecObj);
407 if (destObj->isName()) {
408 namedDest = new GString(destObj->getName());
409 } else if (destObj->isString()) {
410 namedDest = destObj->getString()->copy();
412 // destination dictionary
413 } else if (destObj->isArray()) {
414 dest = new LinkDest(destObj->getArray());
422 error(-1, "Illegal annotation destination");
426 LinkGoToR::~LinkGoToR() {
436 //------------------------------------------------------------------------
438 //------------------------------------------------------------------------
440 LinkLaunch::LinkLaunch(Object *actionObj) {
446 if (actionObj->isDict()) {
447 if (!actionObj->dictLookup("F", &obj1)->isNull()) {
448 fileName = getFileSpecName(&obj1);
452 if (actionObj->dictLookup("Win", &obj1)->isDict()) {
453 obj1.dictLookup("F", &obj2);
454 fileName = getFileSpecName(&obj2);
456 if (obj1.dictLookup("P", &obj2)->isString()) {
457 params = obj2.getString()->copy();
461 error(-1, "Bad launch-type link action");
464 //~ This hasn't been defined by Adobe yet, so assume it looks
465 //~ just like the Win dictionary until they say otherwise.
466 if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
467 obj1.dictLookup("F", &obj2);
468 fileName = getFileSpecName(&obj2);
470 if (obj1.dictLookup("P", &obj2)->isString()) {
471 params = obj2.getString()->copy();
475 error(-1, "Bad launch-type link action");
483 LinkLaunch::~LinkLaunch() {
490 //------------------------------------------------------------------------
492 //------------------------------------------------------------------------
494 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
500 if (uriObj->isString()) {
501 uri2 = uriObj->getString()->copy();
503 n = strcspn(uri2->getCString(), "/:");
504 if (n == uri2->getLength() || uri2->getChar(n) == '/') {
505 uri = baseURI->copy();
506 c = uri->getChar(uri->getLength() - 1);
507 if (c == '/' || c == '?') {
508 if (uri2->getChar(0) == '/') {
512 if (uri2->getChar(0) != '/') {
525 error(-1, "Illegal URI-type link");
529 LinkURI::~LinkURI() {
534 //------------------------------------------------------------------------
536 //------------------------------------------------------------------------
538 LinkNamed::LinkNamed(Object *nameObj) {
540 if (nameObj->isName()) {
541 name = new GString(nameObj->getName());
545 LinkNamed::~LinkNamed() {
551 //------------------------------------------------------------------------
553 //------------------------------------------------------------------------
555 LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
558 if (annotObj->isRef()) {
559 annotRef = annotObj->getRef();
560 } else if (titleObj->isString()) {
561 title = titleObj->getString()->copy();
563 error(-1, "Movie action is missing both the Annot and T keys");
567 LinkMovie::~LinkMovie() {
573 //------------------------------------------------------------------------
575 //------------------------------------------------------------------------
577 LinkUnknown::LinkUnknown(char *actionA) {
578 action = new GString(actionA);
581 LinkUnknown::~LinkUnknown() {
585 //------------------------------------------------------------------------
587 //------------------------------------------------------------------------
589 LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
590 double *dashA, int dashLengthA,
591 double rA, double gA, double bA) {
595 dashLength = dashLengthA;
601 LinkBorderStyle::~LinkBorderStyle() {
607 //------------------------------------------------------------------------
609 //------------------------------------------------------------------------
611 Link::Link(Dict *dict, GString *baseURI) {
612 Object obj1, obj2, obj3;
613 LinkBorderType borderType;
616 int borderDashLength;
617 double borderR, borderG, borderB;
626 if (!dict->lookup("Rect", &obj1)->isArray()) {
627 error(-1, "Annotation rectangle is wrong type");
630 if (!obj1.arrayGet(0, &obj2)->isNum()) {
631 error(-1, "Bad annotation rectangle");
636 if (!obj1.arrayGet(1, &obj2)->isNum()) {
637 error(-1, "Bad annotation rectangle");
642 if (!obj1.arrayGet(2, &obj2)->isNum()) {
643 error(-1, "Bad annotation rectangle");
648 if (!obj1.arrayGet(3, &obj2)->isNum()) {
649 error(-1, "Bad annotation rectangle");
666 // get the border style info
667 borderType = linkBorderSolid;
670 borderDashLength = 0;
674 if (dict->lookup("BS", &obj1)->isDict()) {
675 if (obj1.dictLookup("S", &obj2)->isName()) {
676 if (obj2.isName("S")) {
677 borderType = linkBorderSolid;
678 } else if (obj2.isName("D")) {
679 borderType = linkBorderDashed;
680 } else if (obj2.isName("B")) {
681 borderType = linkBorderEmbossed;
682 } else if (obj2.isName("I")) {
683 borderType = linkBorderEngraved;
684 } else if (obj2.isName("U")) {
685 borderType = linkBorderUnderlined;
689 if (obj1.dictLookup("W", &obj2)->isNum()) {
690 borderWidth = obj2.getNum();
693 if (obj1.dictLookup("D", &obj2)->isArray()) {
694 borderDashLength = obj2.arrayGetLength();
695 borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
696 for (i = 0; i < borderDashLength; ++i) {
697 if (obj2.arrayGet(i, &obj3)->isNum()) {
698 borderDash[i] = obj3.getNum();
708 if (dict->lookup("Border", &obj1)->isArray()) {
709 if (obj1.arrayGetLength() >= 3) {
710 if (obj1.arrayGet(2, &obj2)->isNum()) {
711 borderWidth = obj2.getNum();
714 if (obj1.arrayGetLength() >= 4) {
715 if (obj1.arrayGet(3, &obj2)->isArray()) {
716 borderType = linkBorderDashed;
717 borderDashLength = obj2.arrayGetLength();
718 borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
719 for (i = 0; i < borderDashLength; ++i) {
720 if (obj2.arrayGet(i, &obj3)->isNum()) {
721 borderDash[i] = obj3.getNum();
734 if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
735 if (obj1.arrayGet(0, &obj2)->isNum()) {
736 borderR = obj2.getNum();
739 if (obj1.arrayGet(1, &obj2)->isNum()) {
740 borderG = obj2.getNum();
743 if (obj1.arrayGet(2, &obj2)->isNum()) {
744 borderB = obj2.getNum();
749 borderStyle = new LinkBorderStyle(borderType, borderWidth,
750 borderDash, borderDashLength,
751 borderR, borderG, borderB);
753 // look for destination
754 if (!dict->lookup("Dest", &obj1)->isNull()) {
755 action = LinkAction::parseDest(&obj1);
760 if (dict->lookup("A", &obj1)->isDict()) {
761 action = LinkAction::parseAction(&obj1, baseURI);
766 // check for bad action
788 //------------------------------------------------------------------------
790 //------------------------------------------------------------------------
792 Links::Links(Object *annots, GString *baseURI) {
802 if (annots->isArray()) {
803 for (i = 0; i < annots->arrayGetLength(); ++i) {
804 if (annots->arrayGet(i, &obj1)->isDict()) {
805 if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
806 link = new Link(obj1.getDict(), baseURI);
808 if (numLinks >= size) {
810 links = (Link **)grealloc(links, size * sizeof(Link *));
812 links[numLinks++] = link;
827 for (i = 0; i < numLinks; ++i)
832 LinkAction *Links::find(double x, double y) const {
835 for (i = numLinks - 1; i >= 0; --i) {
836 if (links[i]->inRect(x, y)) {
837 return links[i]->getAction();
843 GBool Links::onLink(double x, double y) const {
846 for (i = 0; i < numLinks; ++i) {
847 if (links[i]->inRect(x, y))