Back to index

tetex-bin  3.0
Link.cc
Go to the documentation of this file.
00001 //========================================================================
00002 //
00003 // Link.cc
00004 //
00005 // Copyright 1996-2003 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <stddef.h>
00016 #include <string.h>
00017 #include "gmem.h"
00018 #include "GString.h"
00019 #include "Error.h"
00020 #include "Object.h"
00021 #include "Array.h"
00022 #include "Dict.h"
00023 #include "Link.h"
00024 
00025 //------------------------------------------------------------------------
00026 // LinkAction
00027 //------------------------------------------------------------------------
00028 
00029 LinkAction *LinkAction::parseDest(Object *obj) {
00030   LinkAction *action;
00031 
00032   action = new LinkGoTo(obj);
00033   if (!action->isOk()) {
00034     delete action;
00035     return NULL;
00036   }
00037   return action;
00038 }
00039 
00040 LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) {
00041   LinkAction *action;
00042   Object obj2, obj3, obj4;
00043 
00044   if (!obj->isDict()) {
00045     error(-1, "Bad annotation action");
00046     return NULL;
00047   }
00048 
00049   obj->dictLookup("S", &obj2);
00050 
00051   // GoTo action
00052   if (obj2.isName("GoTo")) {
00053     obj->dictLookup("D", &obj3);
00054     action = new LinkGoTo(&obj3);
00055     obj3.free();
00056 
00057   // GoToR action
00058   } else if (obj2.isName("GoToR")) {
00059     obj->dictLookup("F", &obj3);
00060     obj->dictLookup("D", &obj4);
00061     action = new LinkGoToR(&obj3, &obj4);
00062     obj3.free();
00063     obj4.free();
00064 
00065   // Launch action
00066   } else if (obj2.isName("Launch")) {
00067     action = new LinkLaunch(obj);
00068 
00069   // URI action
00070   } else if (obj2.isName("URI")) {
00071     obj->dictLookup("URI", &obj3);
00072     action = new LinkURI(&obj3, baseURI);
00073     obj3.free();
00074 
00075   // Named action
00076   } else if (obj2.isName("Named")) {
00077     obj->dictLookup("N", &obj3);
00078     action = new LinkNamed(&obj3);
00079     obj3.free();
00080 
00081   // Movie action
00082   } else if (obj2.isName("Movie")) {
00083     obj->dictLookupNF("Annot", &obj3);
00084     obj->dictLookup("T", &obj4);
00085     action = new LinkMovie(&obj3, &obj4);
00086     obj3.free();
00087     obj4.free();
00088 
00089   // unknown action
00090   } else if (obj2.isName()) {
00091     action = new LinkUnknown(obj2.getName());
00092 
00093   // action is missing or wrong type
00094   } else {
00095     error(-1, "Bad annotation action");
00096     action = NULL;
00097   }
00098 
00099   obj2.free();
00100 
00101   if (action && !action->isOk()) {
00102     delete action;
00103     return NULL;
00104   }
00105   return action;
00106 }
00107 
00108 GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
00109   GString *name;
00110   Object obj1;
00111 
00112   name = NULL;
00113 
00114   // string
00115   if (fileSpecObj->isString()) {
00116     name = fileSpecObj->getString()->copy();
00117 
00118   // dictionary
00119   } else if (fileSpecObj->isDict()) {
00120     if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
00121       obj1.free();
00122       fileSpecObj->dictLookup("F", &obj1);
00123     }
00124     if (obj1.isString())
00125       name = obj1.getString()->copy();
00126     else
00127       error(-1, "Illegal file spec in link");
00128     obj1.free();
00129 
00130   // error
00131   } else {
00132     error(-1, "Illegal file spec in link");
00133   }
00134 
00135   return name;
00136 }
00137 
00138 //------------------------------------------------------------------------
00139 // LinkDest
00140 //------------------------------------------------------------------------
00141 
00142 LinkDest::LinkDest(Array *a) {
00143   Object obj1, obj2;
00144 
00145   // initialize fields
00146   left = bottom = right = top = zoom = 0;
00147   ok = gFalse;
00148 
00149   // get page
00150   if (a->getLength() < 2) {
00151     error(-1, "Annotation destination array is too short");
00152     return;
00153   }
00154   a->getNF(0, &obj1);
00155   if (obj1.isInt()) {
00156     pageNum = obj1.getInt() + 1;
00157     pageIsRef = gFalse;
00158   } else if (obj1.isRef()) {
00159     pageRef.num = obj1.getRefNum();
00160     pageRef.gen = obj1.getRefGen();
00161     pageIsRef = gTrue;
00162   } else {
00163     error(-1, "Bad annotation destination");
00164     goto err2;
00165   }
00166   obj1.free();
00167 
00168   // get destination type
00169   a->get(1, &obj1);
00170 
00171   // XYZ link
00172   if (obj1.isName("XYZ")) {
00173     kind = destXYZ;
00174     if (a->getLength() < 3) {
00175       changeLeft = gFalse;
00176     } else {
00177       a->get(2, &obj2);
00178       if (obj2.isNull()) {
00179        changeLeft = gFalse;
00180       } else if (obj2.isNum()) {
00181        changeLeft = gTrue;
00182        left = obj2.getNum();
00183       } else {
00184        error(-1, "Bad annotation destination position");
00185        goto err1;
00186       }
00187       obj2.free();
00188     }
00189     if (a->getLength() < 4) {
00190       changeTop = gFalse;
00191     } else {
00192       a->get(3, &obj2);
00193       if (obj2.isNull()) {
00194        changeTop = gFalse;
00195       } else if (obj2.isNum()) {
00196        changeTop = gTrue;
00197        top = obj2.getNum();
00198       } else {
00199        error(-1, "Bad annotation destination position");
00200        goto err1;
00201       }
00202       obj2.free();
00203     }
00204     if (a->getLength() < 5) {
00205       changeZoom = gFalse;
00206     } else {
00207       a->get(4, &obj2);
00208       if (obj2.isNull()) {
00209        changeZoom = gFalse;
00210       } else if (obj2.isNum()) {
00211        changeZoom = gTrue;
00212        zoom = obj2.getNum();
00213       } else {
00214        error(-1, "Bad annotation destination position");
00215        goto err1;
00216       }
00217       obj2.free();
00218     }
00219 
00220   // Fit link
00221   } else if (obj1.isName("Fit")) {
00222     if (a->getLength() < 2) {
00223       error(-1, "Annotation destination array is too short");
00224       goto err2;
00225     }
00226     kind = destFit;
00227 
00228   // FitH link
00229   } else if (obj1.isName("FitH")) {
00230     if (a->getLength() < 3) {
00231       error(-1, "Annotation destination array is too short");
00232       goto err2;
00233     }
00234     kind = destFitH;
00235     if (!a->get(2, &obj2)->isNum()) {
00236       error(-1, "Bad annotation destination position");
00237       goto err1;
00238     }
00239     top = obj2.getNum();
00240     obj2.free();
00241 
00242   // FitV link
00243   } else if (obj1.isName("FitV")) {
00244     if (a->getLength() < 3) {
00245       error(-1, "Annotation destination array is too short");
00246       goto err2;
00247     }
00248     kind = destFitV;
00249     if (!a->get(2, &obj2)->isNum()) {
00250       error(-1, "Bad annotation destination position");
00251       goto err1;
00252     }
00253     left = obj2.getNum();
00254     obj2.free();
00255 
00256   // FitR link
00257   } else if (obj1.isName("FitR")) {
00258     if (a->getLength() < 6) {
00259       error(-1, "Annotation destination array is too short");
00260       goto err2;
00261     }
00262     kind = destFitR;
00263     if (!a->get(2, &obj2)->isNum()) {
00264       error(-1, "Bad annotation destination position");
00265       goto err1;
00266     }
00267     left = obj2.getNum();
00268     obj2.free();
00269     if (!a->get(3, &obj2)->isNum()) {
00270       error(-1, "Bad annotation destination position");
00271       goto err1;
00272     }
00273     bottom = obj2.getNum();
00274     obj2.free();
00275     if (!a->get(4, &obj2)->isNum()) {
00276       error(-1, "Bad annotation destination position");
00277       goto err1;
00278     }
00279     right = obj2.getNum();
00280     obj2.free();
00281     if (!a->get(5, &obj2)->isNum()) {
00282       error(-1, "Bad annotation destination position");
00283       goto err1;
00284     }
00285     top = obj2.getNum();
00286     obj2.free();
00287 
00288   // FitB link
00289   } else if (obj1.isName("FitB")) {
00290     if (a->getLength() < 2) {
00291       error(-1, "Annotation destination array is too short");
00292       goto err2;
00293     }
00294     kind = destFitB;
00295 
00296   // FitBH link
00297   } else if (obj1.isName("FitBH")) {
00298     if (a->getLength() < 3) {
00299       error(-1, "Annotation destination array is too short");
00300       goto err2;
00301     }
00302     kind = destFitBH;
00303     if (!a->get(2, &obj2)->isNum()) {
00304       error(-1, "Bad annotation destination position");
00305       goto err1;
00306     }
00307     top = obj2.getNum();
00308     obj2.free();
00309 
00310   // FitBV link
00311   } else if (obj1.isName("FitBV")) {
00312     if (a->getLength() < 3) {
00313       error(-1, "Annotation destination array is too short");
00314       goto err2;
00315     }
00316     kind = destFitBV;
00317     if (!a->get(2, &obj2)->isNum()) {
00318       error(-1, "Bad annotation destination position");
00319       goto err1;
00320     }
00321     left = obj2.getNum();
00322     obj2.free();
00323 
00324   // unknown link kind
00325   } else {
00326     error(-1, "Unknown annotation destination type");
00327     goto err2;
00328   }
00329 
00330   obj1.free();
00331   ok = gTrue;
00332   return;
00333 
00334  err1:
00335   obj2.free();
00336  err2:
00337   obj1.free();
00338 }
00339 
00340 LinkDest::LinkDest(LinkDest *dest) {
00341   kind = dest->kind;
00342   pageIsRef = dest->pageIsRef;
00343   if (pageIsRef)
00344     pageRef = dest->pageRef;
00345   else
00346     pageNum = dest->pageNum;
00347   left = dest->left;
00348   bottom = dest->bottom;
00349   right = dest->right;
00350   top = dest->top;
00351   zoom = dest->zoom;
00352   changeLeft = dest->changeLeft;
00353   changeTop = dest->changeTop;
00354   changeZoom = dest->changeZoom;
00355   ok = gTrue;
00356 }
00357 
00358 //------------------------------------------------------------------------
00359 // LinkGoTo
00360 //------------------------------------------------------------------------
00361 
00362 LinkGoTo::LinkGoTo(Object *destObj) {
00363   dest = NULL;
00364   namedDest = NULL;
00365 
00366   // named destination
00367   if (destObj->isName()) {
00368     namedDest = new GString(destObj->getName());
00369   } else if (destObj->isString()) {
00370     namedDest = destObj->getString()->copy();
00371 
00372   // destination dictionary
00373   } else if (destObj->isArray()) {
00374     dest = new LinkDest(destObj->getArray());
00375     if (!dest->isOk()) {
00376       delete dest;
00377       dest = NULL;
00378     }
00379 
00380   // error
00381   } else {
00382     error(-1, "Illegal annotation destination");
00383   }
00384 }
00385 
00386 LinkGoTo::~LinkGoTo() {
00387   if (dest)
00388     delete dest;
00389   if (namedDest)
00390     delete namedDest;
00391 }
00392 
00393 //------------------------------------------------------------------------
00394 // LinkGoToR
00395 //------------------------------------------------------------------------
00396 
00397 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
00398   dest = NULL;
00399   namedDest = NULL;
00400 
00401   // get file name
00402   fileName = getFileSpecName(fileSpecObj);
00403 
00404   // named destination
00405   if (destObj->isName()) {
00406     namedDest = new GString(destObj->getName());
00407   } else if (destObj->isString()) {
00408     namedDest = destObj->getString()->copy();
00409 
00410   // destination dictionary
00411   } else if (destObj->isArray()) {
00412     dest = new LinkDest(destObj->getArray());
00413     if (!dest->isOk()) {
00414       delete dest;
00415       dest = NULL;
00416     }
00417 
00418   // error
00419   } else {
00420     error(-1, "Illegal annotation destination");
00421   }
00422 }
00423 
00424 LinkGoToR::~LinkGoToR() {
00425   if (fileName)
00426     delete fileName;
00427   if (dest)
00428     delete dest;
00429   if (namedDest)
00430     delete namedDest;
00431 }
00432 
00433 
00434 //------------------------------------------------------------------------
00435 // LinkLaunch
00436 //------------------------------------------------------------------------
00437 
00438 LinkLaunch::LinkLaunch(Object *actionObj) {
00439   Object obj1, obj2;
00440 
00441   fileName = NULL;
00442   params = NULL;
00443 
00444   if (actionObj->isDict()) {
00445     if (!actionObj->dictLookup("F", &obj1)->isNull()) {
00446       fileName = getFileSpecName(&obj1);
00447     } else {
00448       obj1.free();
00449 #ifdef WIN32
00450       if (actionObj->dictLookup("Win", &obj1)->isDict()) {
00451        obj1.dictLookup("F", &obj2);
00452        fileName = getFileSpecName(&obj2);
00453        obj2.free();
00454        if (obj1.dictLookup("P", &obj2)->isString()) {
00455          params = obj2.getString()->copy();
00456        }
00457        obj2.free();
00458       } else {
00459        error(-1, "Bad launch-type link action");
00460       }
00461 #else
00462       //~ This hasn't been defined by Adobe yet, so assume it looks
00463       //~ just like the Win dictionary until they say otherwise.
00464       if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
00465        obj1.dictLookup("F", &obj2);
00466        fileName = getFileSpecName(&obj2);
00467        obj2.free();
00468        if (obj1.dictLookup("P", &obj2)->isString()) {
00469          params = obj2.getString()->copy();
00470        }
00471        obj2.free();
00472       } else {
00473        error(-1, "Bad launch-type link action");
00474       }
00475 #endif
00476     }
00477     obj1.free();
00478   }
00479 }
00480 
00481 LinkLaunch::~LinkLaunch() {
00482   if (fileName)
00483     delete fileName;
00484   if (params)
00485     delete params;
00486 }
00487 
00488 //------------------------------------------------------------------------
00489 // LinkURI
00490 //------------------------------------------------------------------------
00491 
00492 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
00493   GString *uri2;
00494   int n;
00495   char c;
00496 
00497   uri = NULL;
00498   if (uriObj->isString()) {
00499     uri2 = uriObj->getString()->copy();
00500     if (baseURI) {
00501       n = strcspn(uri2->getCString(), "/:");
00502       if (n == uri2->getLength() || uri2->getChar(n) == '/') {
00503        uri = baseURI->copy();
00504        c = uri->getChar(uri->getLength() - 1);
00505        if (c == '/' || c == '?') {
00506          if (uri2->getChar(0) == '/') {
00507            uri2->del(0);
00508          }
00509        } else {
00510          if (uri2->getChar(0) != '/') {
00511            uri->append('/');
00512          }
00513        }
00514        uri->append(uri2);
00515        delete uri2;
00516       } else {
00517        uri = uri2;
00518       }
00519     } else {
00520       uri = uri2;
00521     }
00522   } else {
00523     error(-1, "Illegal URI-type link");
00524   }
00525 }
00526 
00527 LinkURI::~LinkURI() {
00528   if (uri)
00529     delete uri;
00530 }
00531 
00532 //------------------------------------------------------------------------
00533 // LinkNamed
00534 //------------------------------------------------------------------------
00535 
00536 LinkNamed::LinkNamed(Object *nameObj) {
00537   name = NULL;
00538   if (nameObj->isName()) {
00539     name = new GString(nameObj->getName());
00540   }
00541 }
00542 
00543 LinkNamed::~LinkNamed() {
00544   if (name) {
00545     delete name;
00546   }
00547 }
00548 
00549 //------------------------------------------------------------------------
00550 // LinkMovie
00551 //------------------------------------------------------------------------
00552 
00553 LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) {
00554   annotRef.num = -1;
00555   title = NULL;
00556   if (annotObj->isRef()) {
00557     annotRef = annotObj->getRef();
00558   } else if (titleObj->isString()) {
00559     title = titleObj->getString()->copy();
00560   } else {
00561     error(-1, "Movie action is missing both the Annot and T keys");
00562   }
00563 }
00564 
00565 LinkMovie::~LinkMovie() {
00566   if (title) {
00567     delete title;
00568   }
00569 }
00570 
00571 //------------------------------------------------------------------------
00572 // LinkUnknown
00573 //------------------------------------------------------------------------
00574 
00575 LinkUnknown::LinkUnknown(char *actionA) {
00576   action = new GString(actionA);
00577 }
00578 
00579 LinkUnknown::~LinkUnknown() {
00580   delete action;
00581 }
00582 
00583 //------------------------------------------------------------------------
00584 // LinkBorderStyle
00585 //------------------------------------------------------------------------
00586 
00587 LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA,
00588                              double *dashA, int dashLengthA,
00589                              double rA, double gA, double bA) {
00590   type = typeA;
00591   width = widthA;
00592   dash = dashA;
00593   dashLength = dashLengthA;
00594   r = rA;
00595   g = gA;
00596   b = bA;
00597 }
00598 
00599 LinkBorderStyle::~LinkBorderStyle() {
00600   if (dash) {
00601     gfree(dash);
00602   }
00603 }
00604 
00605 //------------------------------------------------------------------------
00606 // Link
00607 //------------------------------------------------------------------------
00608 
00609 Link::Link(Dict *dict, GString *baseURI) {
00610   Object obj1, obj2, obj3;
00611   LinkBorderType borderType;
00612   double borderWidth;
00613   double *borderDash;
00614   int borderDashLength;
00615   double borderR, borderG, borderB;
00616   double t;
00617   int i;
00618 
00619   borderStyle = NULL;
00620   action = NULL;
00621   ok = gFalse;
00622 
00623   // get rectangle
00624   if (!dict->lookup("Rect", &obj1)->isArray()) {
00625     error(-1, "Annotation rectangle is wrong type");
00626     goto err2;
00627   }
00628   if (!obj1.arrayGet(0, &obj2)->isNum()) {
00629     error(-1, "Bad annotation rectangle");
00630     goto err1;
00631   }
00632   x1 = obj2.getNum();
00633   obj2.free();
00634   if (!obj1.arrayGet(1, &obj2)->isNum()) {
00635     error(-1, "Bad annotation rectangle");
00636     goto err1;
00637   }
00638   y1 = obj2.getNum();
00639   obj2.free();
00640   if (!obj1.arrayGet(2, &obj2)->isNum()) {
00641     error(-1, "Bad annotation rectangle");
00642     goto err1;
00643   }
00644   x2 = obj2.getNum();
00645   obj2.free();
00646   if (!obj1.arrayGet(3, &obj2)->isNum()) {
00647     error(-1, "Bad annotation rectangle");
00648     goto err1;
00649   }
00650   y2 = obj2.getNum();
00651   obj2.free();
00652   obj1.free();
00653   if (x1 > x2) {
00654     t = x1;
00655     x1 = x2;
00656     x2 = t;
00657   }
00658   if (y1 > y2) {
00659     t = y1;
00660     y1 = y2;
00661     y2 = t;
00662   }
00663 
00664   // get the border style info
00665   borderType = linkBorderSolid;
00666   borderWidth = 1;
00667   borderDash = NULL;
00668   borderDashLength = 0;
00669   borderR = 0;
00670   borderG = 0;
00671   borderB = 1;
00672   if (dict->lookup("BS", &obj1)->isDict()) {
00673     if (obj1.dictLookup("S", &obj2)->isName()) {
00674       if (obj2.isName("S")) {
00675        borderType = linkBorderSolid;
00676       } else if (obj2.isName("D")) {
00677        borderType = linkBorderDashed;
00678       } else if (obj2.isName("B")) {
00679        borderType = linkBorderEmbossed;
00680       } else if (obj2.isName("I")) {
00681        borderType = linkBorderEngraved;
00682       } else if (obj2.isName("U")) {
00683        borderType = linkBorderUnderlined;
00684       }
00685     }
00686     obj2.free();
00687     if (obj1.dictLookup("W", &obj2)->isNum()) {
00688       borderWidth = obj2.getNum();
00689     }
00690     obj2.free();
00691     if (obj1.dictLookup("D", &obj2)->isArray()) {
00692       borderDashLength = obj2.arrayGetLength();
00693       borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
00694       for (i = 0; i < borderDashLength; ++i) {
00695        if (obj2.arrayGet(i, &obj3)->isNum()) {
00696          borderDash[i] = obj3.getNum();
00697        } else {
00698          borderDash[i] = 1;
00699        }
00700        obj3.free();
00701       }
00702     }
00703     obj2.free();
00704   } else {
00705     obj1.free();
00706     if (dict->lookup("Border", &obj1)->isArray()) {
00707       if (obj1.arrayGetLength() >= 3) {
00708        if (obj1.arrayGet(2, &obj2)->isNum()) {
00709          borderWidth = obj2.getNum();
00710        }
00711        obj2.free();
00712        if (obj1.arrayGetLength() >= 4) {
00713          if (obj1.arrayGet(3, &obj2)->isArray()) {
00714            borderType = linkBorderDashed;
00715            borderDashLength = obj2.arrayGetLength();
00716            borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
00717            for (i = 0; i < borderDashLength; ++i) {
00718              if (obj2.arrayGet(i, &obj3)->isNum()) {
00719               borderDash[i] = obj3.getNum();
00720              } else {
00721               borderDash[i] = 1;
00722              }
00723              obj3.free();
00724            }
00725          }
00726          obj2.free();
00727        }
00728       }
00729     }
00730   }
00731   obj1.free();
00732   if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {
00733     if (obj1.arrayGet(0, &obj2)->isNum()) {
00734       borderR = obj2.getNum();
00735     }
00736     obj1.free();
00737     if (obj1.arrayGet(1, &obj2)->isNum()) {
00738       borderG = obj2.getNum();
00739     }
00740     obj1.free();
00741     if (obj1.arrayGet(2, &obj2)->isNum()) {
00742       borderB = obj2.getNum();
00743     }
00744     obj1.free();
00745   }
00746   obj1.free();
00747   borderStyle = new LinkBorderStyle(borderType, borderWidth,
00748                                 borderDash, borderDashLength,
00749                                 borderR, borderG, borderB);
00750 
00751   // look for destination
00752   if (!dict->lookup("Dest", &obj1)->isNull()) {
00753     action = LinkAction::parseDest(&obj1);
00754 
00755   // look for action
00756   } else {
00757     obj1.free();
00758     if (dict->lookup("A", &obj1)->isDict()) {
00759       action = LinkAction::parseAction(&obj1, baseURI);
00760     }
00761   }
00762   obj1.free();
00763 
00764   // check for bad action
00765   if (action) {
00766     ok = gTrue;
00767   }
00768 
00769   return;
00770 
00771  err1:
00772   obj2.free();
00773  err2:
00774   obj1.free();
00775 }
00776 
00777 Link::~Link() {
00778   if (borderStyle) {
00779     delete borderStyle;
00780   }
00781   if (action) {
00782     delete action;
00783   }
00784 }
00785 
00786 //------------------------------------------------------------------------
00787 // Links
00788 //------------------------------------------------------------------------
00789 
00790 Links::Links(Object *annots, GString *baseURI) {
00791   Link *link;
00792   Object obj1, obj2;
00793   int size;
00794   int i;
00795 
00796   links = NULL;
00797   size = 0;
00798   numLinks = 0;
00799 
00800   if (annots->isArray()) {
00801     for (i = 0; i < annots->arrayGetLength(); ++i) {
00802       if (annots->arrayGet(i, &obj1)->isDict()) {
00803        if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
00804          link = new Link(obj1.getDict(), baseURI);
00805          if (link->isOk()) {
00806            if (numLinks >= size) {
00807              size += 16;
00808              links = (Link **)grealloc(links, size * sizeof(Link *));
00809            }
00810            links[numLinks++] = link;
00811          } else {
00812            delete link;
00813          }
00814        }
00815        obj2.free();
00816       }
00817       obj1.free();
00818     }
00819   }
00820 }
00821 
00822 Links::~Links() {
00823   int i;
00824 
00825   for (i = 0; i < numLinks; ++i)
00826     delete links[i];
00827   gfree(links);
00828 }
00829 
00830 LinkAction *Links::find(double x, double y) {
00831   int i;
00832 
00833   for (i = numLinks - 1; i >= 0; --i) {
00834     if (links[i]->inRect(x, y)) {
00835       return links[i]->getAction();
00836     }
00837   }
00838   return NULL;
00839 }
00840 
00841 GBool Links::onLink(double x, double y) {
00842   int i;
00843 
00844   for (i = 0; i < numLinks; ++i) {
00845     if (links[i]->inRect(x, y))
00846       return gTrue;
00847   }
00848   return gFalse;
00849 }