X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=pdf2swf%2Fxpdf%2FLink.cc;h=898ca20c355fc5b4bcc9d9ffc9dd97277139f32d;hb=85c46a8011c7fd5e4bda282266006c972ea7606b;hp=154a5772a1f713d195d287047b44b8da98e56d63;hpb=7922a4e8c418c48ffeeec0f95be24359fc817267;p=swftools.git diff --git a/pdf2swf/xpdf/Link.cc b/pdf2swf/xpdf/Link.cc index 154a577..898ca20 100644 --- a/pdf2swf/xpdf/Link.cc +++ b/pdf2swf/xpdf/Link.cc @@ -2,11 +2,13 @@ // // Link.cc // -// Copyright 1996 Derek B. Noonburg +// Copyright 1996-2003 Glyph & Cog, LLC // //======================================================================== -#ifdef __GNUC__ +#include + +#ifdef USE_GCC_PRAGMAS #pragma implementation #endif @@ -21,39 +23,201 @@ #include "Link.h" //------------------------------------------------------------------------ +// LinkAction +//------------------------------------------------------------------------ + +LinkAction *LinkAction::parseDest(Object *obj) { + LinkAction *action; + + action = new LinkGoTo(obj); + if (!action->isOk()) { + delete action; + return NULL; + } + return action; +} + +LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) { + LinkAction *action; + Object obj2, obj3, obj4; + + if (!obj->isDict()) { + error(-1, "Bad annotation action"); + return NULL; + } + + obj->dictLookup("S", &obj2); + + // GoTo action + if (obj2.isName("GoTo")) { + obj->dictLookup("D", &obj3); + action = new LinkGoTo(&obj3); + obj3.free(); + + // GoToR action + } else if (obj2.isName("GoToR")) { + obj->dictLookup("F", &obj3); + obj->dictLookup("D", &obj4); + action = new LinkGoToR(&obj3, &obj4); + obj3.free(); + obj4.free(); + + // Launch action + } else if (obj2.isName("Launch")) { + action = new LinkLaunch(obj); + + // URI action + } else if (obj2.isName("URI")) { + obj->dictLookup("URI", &obj3); + action = new LinkURI(&obj3, baseURI); + obj3.free(); + + // Named action + } else if (obj2.isName("Named")) { + obj->dictLookup("N", &obj3); + action = new LinkNamed(&obj3); + obj3.free(); + + // Movie action + } else if (obj2.isName("Movie")) { + obj->dictLookupNF("Annot", &obj3); + obj->dictLookup("T", &obj4); + action = new LinkMovie(&obj3, &obj4); + obj3.free(); + obj4.free(); + + // unknown action + } else if (obj2.isName()) { + action = new LinkUnknown(obj2.getName()); + + // action is missing or wrong type + } else { + error(-1, "Bad annotation action"); + action = NULL; + } + + obj2.free(); + + if (action && !action->isOk()) { + delete action; + return NULL; + } + return action; +} + +GString *LinkAction::getFileSpecName(Object *fileSpecObj) { + GString *name; + Object obj1; + + name = NULL; + + // string + if (fileSpecObj->isString()) { + name = fileSpecObj->getString()->copy(); + + // dictionary + } else if (fileSpecObj->isDict()) { +#ifdef WIN32 + if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) { +#else + if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) { +#endif + obj1.free(); + fileSpecObj->dictLookup("F", &obj1); + } + if (obj1.isString()) { + name = obj1.getString()->copy(); + } else { + error(-1, "Illegal file spec in link"); + } + obj1.free(); + + // error + } else { + error(-1, "Illegal file spec in link"); + } + + // system-dependent path manipulation + if (name) { +#ifdef WIN32 + int i, j; + + // "//...." --> "\...." + // "/x/...." --> "x:\...." + // "/server/share/...." --> "\\server\share\...." + // convert escaped slashes to slashes and unescaped slashes to backslashes + i = 0; + if (name->getChar(0) == '/') { + if (name->getLength() >= 2 && name->getChar(1) == '/') { + name->del(0); + i = 0; + } else if (name->getLength() >= 2 && + ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') || + (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) && + (name->getLength() == 2 || name->getChar(2) == '/')) { + name->setChar(0, name->getChar(1)); + name->setChar(1, ':'); + i = 2; + } else { + for (j = 2; j < name->getLength(); ++j) { + if (name->getChar(j-1) != '\\' && + name->getChar(j) == '/') { + break; + } + } + if (j < name->getLength()) { + name->setChar(0, '\\'); + name->insert(0, '\\'); + i = 2; + } + } + } + for (; i < name->getLength(); ++i) { + if (name->getChar(i) == '/') { + name->setChar(i, '\\'); + } else if (name->getChar(i) == '\\' && + i+1 < name->getLength() && + name->getChar(i+1) == '/') { + name->del(i); + } + } +#else + // no manipulation needed for Unix +#endif + } -static GString *getFileSpecName(Object *fileSpecObj); + return name; +} //------------------------------------------------------------------------ // LinkDest //------------------------------------------------------------------------ -LinkDest::LinkDest(Array *a, GBool pageIsRef1) { +LinkDest::LinkDest(Array *a) { Object obj1, obj2; // initialize fields - pageIsRef = pageIsRef1; left = bottom = right = top = zoom = 0; ok = gFalse; // get page - if (pageIsRef) { - if (!a->getNF(0, &obj1)->isRef()) { - if(obj1.getType()!=objInt) - error(-1, "Bad annotation destination (1) type=%d", obj1.getType()); - goto err2; - } + if (a->getLength() < 2) { + error(-1, "Annotation destination array is too short"); + return; + } + a->getNF(0, &obj1); + if (obj1.isInt()) { + pageNum = obj1.getInt() + 1; + pageIsRef = gFalse; + } else if (obj1.isRef()) { pageRef.num = obj1.getRefNum(); pageRef.gen = obj1.getRefGen(); - obj1.free(); + pageIsRef = gTrue; } else { - if (!a->get(0, &obj1)->isInt()) { - error(-1, "Bad annotation destination (2)"); - goto err2; - } - pageNum = obj1.getInt() + 1; - obj1.free(); + error(-1, "Bad annotation destination"); + goto err2; } + obj1.free(); // get destination type a->get(1, &obj1); @@ -61,46 +225,66 @@ LinkDest::LinkDest(Array *a, GBool pageIsRef1) { // XYZ link if (obj1.isName("XYZ")) { kind = destXYZ; - a->get(2, &obj2); - if (obj2.isNull()) { + if (a->getLength() < 3) { changeLeft = gFalse; - } else if (obj2.isNum()) { - changeLeft = gTrue; - left = obj2.getNum(); } else { - error(-1, "Bad annotation destination position"); - goto err1; + a->get(2, &obj2); + if (obj2.isNull()) { + changeLeft = gFalse; + } else if (obj2.isNum()) { + changeLeft = gTrue; + left = obj2.getNum(); + } else { + error(-1, "Bad annotation destination position"); + goto err1; + } + obj2.free(); } - obj2.free(); - a->get(3, &obj2); - if (obj2.isNull()) { + if (a->getLength() < 4) { changeTop = gFalse; - } else if (obj2.isNum()) { - changeTop = gTrue; - top = obj2.getNum(); } else { - error(-1, "Bad annotation destination position"); - goto err1; + a->get(3, &obj2); + if (obj2.isNull()) { + changeTop = gFalse; + } else if (obj2.isNum()) { + changeTop = gTrue; + top = obj2.getNum(); + } else { + error(-1, "Bad annotation destination position"); + goto err1; + } + obj2.free(); } - obj2.free(); - a->get(4, &obj2); - if (obj2.isNull()) { + if (a->getLength() < 5) { changeZoom = gFalse; - } else if (obj2.isNum()) { - changeZoom = gTrue; - zoom = obj2.getNum(); } else { - error(-1, "Bad annotation destination position"); - goto err1; + a->get(4, &obj2); + if (obj2.isNull()) { + changeZoom = gFalse; + } else if (obj2.isNum()) { + changeZoom = gTrue; + zoom = obj2.getNum(); + } else { + error(-1, "Bad annotation destination position"); + goto err1; + } + obj2.free(); } - obj2.free(); // Fit link } else if (obj1.isName("Fit")) { + if (a->getLength() < 2) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFit; // FitH link } else if (obj1.isName("FitH")) { + if (a->getLength() < 3) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFitH; if (!a->get(2, &obj2)->isNum()) { error(-1, "Bad annotation destination position"); @@ -111,6 +295,10 @@ LinkDest::LinkDest(Array *a, GBool pageIsRef1) { // FitV link } else if (obj1.isName("FitV")) { + if (a->getLength() < 3) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFitV; if (!a->get(2, &obj2)->isNum()) { error(-1, "Bad annotation destination position"); @@ -121,6 +309,10 @@ LinkDest::LinkDest(Array *a, GBool pageIsRef1) { // FitR link } else if (obj1.isName("FitR")) { + if (a->getLength() < 6) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFitR; if (!a->get(2, &obj2)->isNum()) { error(-1, "Bad annotation destination position"); @@ -149,10 +341,18 @@ LinkDest::LinkDest(Array *a, GBool pageIsRef1) { // FitB link } else if (obj1.isName("FitB")) { + if (a->getLength() < 2) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFitB; // FitBH link } else if (obj1.isName("FitBH")) { + if (a->getLength() < 3) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFitBH; if (!a->get(2, &obj2)->isNum()) { error(-1, "Bad annotation destination position"); @@ -163,6 +363,10 @@ LinkDest::LinkDest(Array *a, GBool pageIsRef1) { // FitBV link } else if (obj1.isName("FitBV")) { + if (a->getLength() < 3) { + error(-1, "Annotation destination array is too short"); + goto err2; + } kind = destFitBV; if (!a->get(2, &obj2)->isNum()) { error(-1, "Bad annotation destination position"); @@ -221,7 +425,7 @@ LinkGoTo::LinkGoTo(Object *destObj) { // destination dictionary } else if (destObj->isArray()) { - dest = new LinkDest(destObj->getArray(), gTrue); + dest = new LinkDest(destObj->getArray()); if (!dest->isOk()) { delete dest; dest = NULL; @@ -259,7 +463,7 @@ LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) { // destination dictionary } else if (destObj->isArray()) { - dest = new LinkDest(destObj->getArray(), gFalse); + dest = new LinkDest(destObj->getArray()); if (!dest->isOk()) { delete dest; dest = NULL; @@ -296,18 +500,33 @@ LinkLaunch::LinkLaunch(Object *actionObj) { fileName = getFileSpecName(&obj1); } else { obj1.free(); +#ifdef WIN32 + if (actionObj->dictLookup("Win", &obj1)->isDict()) { + obj1.dictLookup("F", &obj2); + fileName = getFileSpecName(&obj2); + obj2.free(); + if (obj1.dictLookup("P", &obj2)->isString()) { + params = obj2.getString()->copy(); + } + obj2.free(); + } else { + error(-1, "Bad launch-type link action"); + } +#else //~ This hasn't been defined by Adobe yet, so assume it looks //~ just like the Win dictionary until they say otherwise. if (actionObj->dictLookup("Unix", &obj1)->isDict()) { obj1.dictLookup("F", &obj2); fileName = getFileSpecName(&obj2); obj2.free(); - if (obj1.dictLookup("P", &obj2)->isString()) + if (obj1.dictLookup("P", &obj2)->isString()) { params = obj2.getString()->copy(); + } obj2.free(); } else { error(-1, "Bad launch-type link action"); } +#endif } obj1.free(); } @@ -332,7 +551,7 @@ LinkURI::LinkURI(Object *uriObj, GString *baseURI) { uri = NULL; if (uriObj->isString()) { uri2 = uriObj->getString()->copy(); - if (baseURI) { + if (baseURI && baseURI->getLength() > 0) { n = strcspn(uri2->getCString(), "/:"); if (n == uri2->getLength() || uri2->getChar(n) == '/') { uri = baseURI->copy(); @@ -382,11 +601,33 @@ LinkNamed::~LinkNamed() { } //------------------------------------------------------------------------ +// LinkMovie +//------------------------------------------------------------------------ + +LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) { + annotRef.num = -1; + title = NULL; + if (annotObj->isRef()) { + annotRef = annotObj->getRef(); + } else if (titleObj->isString()) { + title = titleObj->getString()->copy(); + } else { + error(-1, "Movie action is missing both the Annot and T keys"); + } +} + +LinkMovie::~LinkMovie() { + if (title) { + delete title; + } +} + +//------------------------------------------------------------------------ // LinkUnknown //------------------------------------------------------------------------ -LinkUnknown::LinkUnknown(char *action1) { - action = new GString(action1); +LinkUnknown::LinkUnknown(char *actionA) { + action = new GString(actionA); } LinkUnknown::~LinkUnknown() { @@ -394,13 +635,42 @@ LinkUnknown::~LinkUnknown() { } //------------------------------------------------------------------------ +// LinkBorderStyle +//------------------------------------------------------------------------ + +LinkBorderStyle::LinkBorderStyle(LinkBorderType typeA, double widthA, + double *dashA, int dashLengthA, + double rA, double gA, double bA) { + type = typeA; + width = widthA; + dash = dashA; + dashLength = dashLengthA; + r = rA; + g = gA; + b = bA; +} + +LinkBorderStyle::~LinkBorderStyle() { + if (dash) { + gfree(dash); + } +} + +//------------------------------------------------------------------------ // Link //------------------------------------------------------------------------ Link::Link(Dict *dict, GString *baseURI) { - Object obj1, obj2, obj3, obj4; + Object obj1, obj2, obj3; + LinkBorderType borderType; + double borderWidth; + double *borderDash; + int borderDashLength; + double borderR, borderG, borderB; double t; + int i; + borderStyle = NULL; action = NULL; ok = gFalse; @@ -445,79 +715,114 @@ Link::Link(Dict *dict, GString *baseURI) { y2 = t; } - // get border - borderW = 0; - if (!dict->lookup("Border", &obj1)->isNull()) { - if (obj1.isArray() && obj1.arrayGet(2, &obj2)->isNum()) - borderW = obj2.getNum(); - else - error(-1, "Bad annotation border"); + // get the border style info + borderType = linkBorderSolid; + borderWidth = 1; + borderDash = NULL; + borderDashLength = 0; + borderR = 0; + borderG = 0; + borderB = 1; + if (dict->lookup("BS", &obj1)->isDict()) { + if (obj1.dictLookup("S", &obj2)->isName()) { + if (obj2.isName("S")) { + borderType = linkBorderSolid; + } else if (obj2.isName("D")) { + borderType = linkBorderDashed; + } else if (obj2.isName("B")) { + borderType = linkBorderEmbossed; + } else if (obj2.isName("I")) { + borderType = linkBorderEngraved; + } else if (obj2.isName("U")) { + borderType = linkBorderUnderlined; + } + } + obj2.free(); + if (obj1.dictLookup("W", &obj2)->isNum()) { + borderWidth = obj2.getNum(); + } obj2.free(); + if (obj1.dictLookup("D", &obj2)->isArray()) { + borderDashLength = obj2.arrayGetLength(); + borderDash = (double *)gmallocn(borderDashLength, sizeof(double)); + for (i = 0; i < borderDashLength; ++i) { + if (obj2.arrayGet(i, &obj3)->isNum()) { + borderDash[i] = obj3.getNum(); + } else { + borderDash[i] = 1; + } + obj3.free(); + } + } + obj2.free(); + } else { + obj1.free(); + if (dict->lookup("Border", &obj1)->isArray()) { + if (obj1.arrayGetLength() >= 3) { + if (obj1.arrayGet(2, &obj2)->isNum()) { + borderWidth = obj2.getNum(); + } + obj2.free(); + if (obj1.arrayGetLength() >= 4) { + if (obj1.arrayGet(3, &obj2)->isArray()) { + borderType = linkBorderDashed; + borderDashLength = obj2.arrayGetLength(); + borderDash = (double *)gmallocn(borderDashLength, sizeof(double)); + for (i = 0; i < borderDashLength; ++i) { + if (obj2.arrayGet(i, &obj3)->isNum()) { + borderDash[i] = obj3.getNum(); + } else { + borderDash[i] = 1; + } + obj3.free(); + } + } else { + // Adobe draws no border at all if the last element is of + // the wrong type. + borderWidth = 0; + } + obj2.free(); + } + } + } } obj1.free(); + if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) { + if (obj1.arrayGet(0, &obj2)->isNum()) { + borderR = obj2.getNum(); + } + obj1.free(); + if (obj1.arrayGet(1, &obj2)->isNum()) { + borderG = obj2.getNum(); + } + obj1.free(); + if (obj1.arrayGet(2, &obj2)->isNum()) { + borderB = obj2.getNum(); + } + obj1.free(); + } + obj1.free(); + borderStyle = new LinkBorderStyle(borderType, borderWidth, + borderDash, borderDashLength, + borderR, borderG, borderB); // look for destination if (!dict->lookup("Dest", &obj1)->isNull()) { - action = new LinkGoTo(&obj1); + action = LinkAction::parseDest(&obj1); // look for action } else { obj1.free(); if (dict->lookup("A", &obj1)->isDict()) { - obj1.dictLookup("S", &obj2); - - // GoTo action - if (obj2.isName("GoTo")) { - obj1.dictLookup("D", &obj3); - action = new LinkGoTo(&obj3); - obj3.free(); - - // GoToR action - } else if (obj2.isName("GoToR")) { - obj1.dictLookup("F", &obj3); - obj1.dictLookup("D", &obj4); - action = new LinkGoToR(&obj3, &obj4); - obj3.free(); - obj4.free(); - - // Launch action - } else if (obj2.isName("Launch")) { - action = new LinkLaunch(&obj1); - - // URI action - } else if (obj2.isName("URI")) { - obj1.dictLookup("URI", &obj3); - action = new LinkURI(&obj3, baseURI); - obj3.free(); - - // Named action - } else if (obj2.isName("Named")) { - obj1.dictLookup("N", &obj3); - action = new LinkNamed(&obj3); - obj3.free(); - - // unknown action - } else if (obj2.isName()) { - action = new LinkUnknown(obj2.getName()); - - // action is missing or wrong type - } else { - error(-1, "Bad annotation action"); - action = NULL; - } - - obj2.free(); - - } else { - error(-1, "Missing annotation destination/action"); - action = NULL; + action = LinkAction::parseAction(&obj1, baseURI); } } obj1.free(); // check for bad action - if (action && action->isOk()) + if (action) { ok = gTrue; + } return; @@ -528,8 +833,12 @@ Link::Link(Dict *dict, GString *baseURI) { } Link::~Link() { - if (action) + if (borderStyle) { + delete borderStyle; + } + if (action) { delete action; + } } //------------------------------------------------------------------------ @@ -554,7 +863,7 @@ Links::Links(Object *annots, GString *baseURI) { if (link->isOk()) { if (numLinks >= size) { size += 16; - links = (Link **)grealloc(links, size * sizeof(Link *)); + links = (Link **)greallocn(links, size, sizeof(Link *)); } links[numLinks++] = link; } else { @@ -579,7 +888,7 @@ Links::~Links() { LinkAction *Links::find(double x, double y) { int i; - for (i = 0; i < numLinks; ++i) { + for (i = numLinks - 1; i >= 0; --i) { if (links[i]->inRect(x, y)) { return links[i]->getAction(); } @@ -596,36 +905,3 @@ GBool Links::onLink(double x, double y) { } return gFalse; } - -//------------------------------------------------------------------------ - -// Extract a file name from a file specification (string or dictionary). -static GString *getFileSpecName(Object *fileSpecObj) { - GString *name; - Object obj1; - - name = NULL; - - // string - if (fileSpecObj->isString()) { - name = fileSpecObj->getString()->copy(); - - // dictionary - } else if (fileSpecObj->isDict()) { - if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) { - obj1.free(); - fileSpecObj->dictLookup("F", &obj1); - } - if (obj1.isString()) - name = obj1.getString()->copy(); - else - error(-1, "Illegal file spec in link"); - obj1.free(); - - // error - } else { - error(-1, "Illegal file spec in link"); - } - - return name; -}