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());
436 error(-1, "Illegal annotation destination");
440 LinkGoTo::~LinkGoTo() {
447 //------------------------------------------------------------------------
449 //------------------------------------------------------------------------
451 LinkGoToR::LinkGoToR(Object
*fileSpecObj
, Object
*destObj
) {
456 fileName
= getFileSpecName(fileSpecObj
);
459 if (destObj
->isName()) {
460 namedDest
= new GString(destObj
->getName());
461 } else if (destObj
->isString()) {
462 namedDest
= destObj
->getString()->copy();
464 // destination dictionary
465 } else if (destObj
->isArray()) {
466 dest
= new LinkDest(destObj
->getArray());
474 error(-1, "Illegal annotation destination");
478 LinkGoToR::~LinkGoToR() {
488 //------------------------------------------------------------------------
490 //------------------------------------------------------------------------
492 LinkLaunch::LinkLaunch(Object
*actionObj
) {
498 if (actionObj
->isDict()) {
499 if (!actionObj
->dictLookup("F", &obj1
)->isNull()) {
500 fileName
= getFileSpecName(&obj1
);
504 if (actionObj
->dictLookup("Win", &obj1
)->isDict()) {
505 obj1
.dictLookup("F", &obj2
);
506 fileName
= getFileSpecName(&obj2
);
508 if (obj1
.dictLookup("P", &obj2
)->isString()) {
509 params
= obj2
.getString()->copy();
513 error(-1, "Bad launch-type link action");
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
);
522 if (obj1
.dictLookup("P", &obj2
)->isString()) {
523 params
= obj2
.getString()->copy();
527 error(-1, "Bad launch-type link action");
535 LinkLaunch::~LinkLaunch() {
542 //------------------------------------------------------------------------
544 //------------------------------------------------------------------------
546 LinkURI::LinkURI(Object
*uriObj
, GString
*baseURI
) {
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) == '/') {
564 if (uri2
->getChar(0) != '/') {
577 error(-1, "Illegal URI-type link");
581 LinkURI::~LinkURI() {
586 //------------------------------------------------------------------------
588 //------------------------------------------------------------------------
590 LinkNamed::LinkNamed(Object
*nameObj
) {
592 if (nameObj
->isName()) {
593 name
= new GString(nameObj
->getName());
597 LinkNamed::~LinkNamed() {
603 //------------------------------------------------------------------------
605 //------------------------------------------------------------------------
607 LinkMovie::LinkMovie(Object
*annotObj
, Object
*titleObj
) {
610 if (annotObj
->isRef()) {
611 annotRef
= annotObj
->getRef();
612 } else if (titleObj
->isString()) {
613 title
= titleObj
->getString()->copy();
615 error(-1, "Movie action is missing both the Annot and T keys");
619 LinkMovie::~LinkMovie() {
625 //------------------------------------------------------------------------
627 //------------------------------------------------------------------------
629 LinkUnknown::LinkUnknown(char *actionA
) {
630 action
= new GString(actionA
);
633 LinkUnknown::~LinkUnknown() {
637 //------------------------------------------------------------------------
639 //------------------------------------------------------------------------
641 LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA
, double widthA
,
642 double *dashA
, int dashLengthA
,
643 double rA
, double gA
, double bA
) {
647 dashLength
= dashLengthA
;
653 LinkBorderStyle::~LinkBorderStyle() {
659 //------------------------------------------------------------------------
661 //------------------------------------------------------------------------
663 Link::Link(Dict
*dict
, GString
*baseURI
) {
664 Object obj1
, obj2
, obj3
;
665 LinkBorderType borderType
;
668 int borderDashLength
;
669 double borderR
, borderG
, borderB
;
678 if (!dict
->lookup("Rect", &obj1
)->isArray()) {
679 error(-1, "Annotation rectangle is wrong type");
682 if (!obj1
.arrayGet(0, &obj2
)->isNum()) {
683 error(-1, "Bad annotation rectangle");
688 if (!obj1
.arrayGet(1, &obj2
)->isNum()) {
689 error(-1, "Bad annotation rectangle");
694 if (!obj1
.arrayGet(2, &obj2
)->isNum()) {
695 error(-1, "Bad annotation rectangle");
700 if (!obj1
.arrayGet(3, &obj2
)->isNum()) {
701 error(-1, "Bad annotation rectangle");
718 // get the border style info
719 borderType
= linkBorderSolid
;
722 borderDashLength
= 0;
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
;
741 if (obj1
.dictLookup("W", &obj2
)->isNum()) {
742 borderWidth
= obj2
.getNum();
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();
760 if (dict
->lookup("Border", &obj1
)->isArray()) {
761 if (obj1
.arrayGetLength() >= 3) {
762 if (obj1
.arrayGet(2, &obj2
)->isNum()) {
763 borderWidth
= obj2
.getNum();
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();
780 // Adobe draws no border at all if the last element is of
790 if (dict
->lookup("C", &obj1
)->isArray() && obj1
.arrayGetLength() == 3) {
791 if (obj1
.arrayGet(0, &obj2
)->isNum()) {
792 borderR
= obj2
.getNum();
795 if (obj1
.arrayGet(1, &obj2
)->isNum()) {
796 borderG
= obj2
.getNum();
799 if (obj1
.arrayGet(2, &obj2
)->isNum()) {
800 borderB
= obj2
.getNum();
805 borderStyle
= new LinkBorderStyle(borderType
, borderWidth
,
806 borderDash
, borderDashLength
,
807 borderR
, borderG
, borderB
);
809 // look for destination
810 if (!dict
->lookup("Dest", &obj1
)->isNull()) {
811 action
= LinkAction::parseDest(&obj1
);
816 if (dict
->lookup("A", &obj1
)->isDict()) {
817 action
= LinkAction::parseAction(&obj1
, baseURI
);
822 // check for bad action
844 //------------------------------------------------------------------------
846 //------------------------------------------------------------------------
848 Links::Links(Object
*annots
, GString
*baseURI
) {
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
);
864 if (numLinks
>= size
) {
866 links
= (Link
**)greallocn(links
, size
, sizeof(Link
*));
868 links
[numLinks
++] = link
;
883 for (i
= 0; i
< numLinks
; ++i
)
888 LinkAction
*Links::find(double x
, double y
) {
891 for (i
= numLinks
- 1; i
>= 0; --i
) {
892 if (links
[i
]->inRect(x
, y
)) {
893 return links
[i
]->getAction();
899 GBool
Links::onLink(double x
, double y
) {
902 for (i
= 0; i
< numLinks
; ++i
) {
903 if (links
[i
]->inRect(x
, y
))