| //======================================================================== |
| // |
| // Link.cc |
| // |
| // Copyright 1996-2003 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2006, 2008 Pino Toscano <pino@kde.org> |
| // Copyright (C) 2007, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com> |
| // Copyright (C) 2008-2010, 2012-2014, 2016-2019 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net> |
| // Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com> |
| // Copyright (C) 2012 Tobias Koening <tobias.koenig@kdab.com> |
| // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich |
| // Copyright (C) 2018 Intevation GmbH <intevation@intevation.de> |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <stddef.h> |
| #include <string.h> |
| #include "goo/gmem.h" |
| #include "goo/GooString.h" |
| #include "Error.h" |
| #include "Object.h" |
| #include "Array.h" |
| #include "Dict.h" |
| #include "Link.h" |
| #include "Sound.h" |
| #include "FileSpec.h" |
| #include "Rendition.h" |
| #include "Annot.h" |
| |
| //------------------------------------------------------------------------ |
| // LinkAction |
| //------------------------------------------------------------------------ |
| LinkAction::LinkAction() : nextActionList(nullptr) { |
| } |
| |
| LinkAction::~LinkAction() { |
| if (nextActionList) { |
| for (auto entry : *nextActionList) { |
| delete entry; |
| } |
| delete nextActionList; |
| } |
| } |
| |
| LinkAction *LinkAction::parseDest(const Object *obj) { |
| LinkAction *action; |
| |
| action = new LinkGoTo(obj); |
| if (!action->isOk()) { |
| delete action; |
| return nullptr; |
| } |
| return action; |
| } |
| |
| LinkAction *LinkAction::parseAction(const Object *obj, const GooString *baseURI) |
| { |
| std::set<int> seenNextActions; |
| return parseAction(obj, baseURI, &seenNextActions); |
| } |
| |
| LinkAction *LinkAction::parseAction(const Object *obj, const GooString *baseURI, |
| std::set<int> *seenNextActions) { |
| LinkAction *action; |
| |
| if (!obj->isDict()) { |
| error(errSyntaxWarning, -1, "parseAction: Bad annotation action for URI '{0:s}'", |
| baseURI ? baseURI->c_str() : "NULL"); |
| return nullptr; |
| } |
| |
| Object obj2 = obj->dictLookup("S"); |
| |
| // GoTo action |
| if (obj2.isName("GoTo")) { |
| Object obj3 = obj->dictLookup("D"); |
| action = new LinkGoTo(&obj3); |
| |
| // GoToR action |
| } else if (obj2.isName("GoToR")) { |
| Object obj3 = obj->dictLookup("F"); |
| Object obj4 = obj->dictLookup("D"); |
| action = new LinkGoToR(&obj3, &obj4); |
| |
| // Launch action |
| } else if (obj2.isName("Launch")) { |
| action = new LinkLaunch(obj); |
| |
| // URI action |
| } else if (obj2.isName("URI")) { |
| Object obj3 = obj->dictLookup("URI"); |
| action = new LinkURI(&obj3, baseURI); |
| |
| // Named action |
| } else if (obj2.isName("Named")) { |
| Object obj3 = obj->dictLookup("N"); |
| action = new LinkNamed(&obj3); |
| |
| // Movie action |
| } else if (obj2.isName("Movie")) { |
| action = new LinkMovie(obj); |
| |
| // Rendition action |
| } else if (obj2.isName("Rendition")) { |
| action = new LinkRendition(obj); |
| |
| // Sound action |
| } else if (obj2.isName("Sound")) { |
| action = new LinkSound(obj); |
| |
| // JavaScript action |
| } else if (obj2.isName("JavaScript")) { |
| Object obj3 = obj->dictLookup("JS"); |
| action = new LinkJavaScript(&obj3); |
| |
| // Set-OCG-State action |
| } else if (obj2.isName("SetOCGState")) { |
| action = new LinkOCGState(obj); |
| |
| // Hide action |
| } else if (obj2.isName("Hide")) { |
| action = new LinkHide(obj); |
| |
| // unknown action |
| } else if (obj2.isName()) { |
| action = new LinkUnknown(obj2.getName()); |
| |
| // action is missing or wrong type |
| } else { |
| error(errSyntaxWarning, -1, "parseAction: Unknown annotation action object: URI = '{0:s}'", |
| baseURI ? baseURI->c_str() : "NULL"); |
| action = nullptr; |
| } |
| |
| if (action && !action->isOk()) { |
| delete action; |
| return nullptr; |
| } |
| |
| if (!action) { |
| return nullptr; |
| } |
| |
| // parse the next actions |
| const Object nextObj = obj->dictLookup("Next"); |
| std::vector<LinkAction*> *actionList = nullptr; |
| if (nextObj.isDict()) { |
| |
| // Prevent circles in the tree by checking the ref against used refs in |
| // our current tree branch. |
| const Object &nextRefObj = obj->dictLookupNF("Next"); |
| if (nextRefObj.isRef()) { |
| const Ref ref = nextRefObj.getRef(); |
| if (!seenNextActions->insert(ref.num).second) { |
| error(errSyntaxWarning, -1, "parseAction: Circular next actions detected."); |
| return action; |
| } |
| } |
| |
| actionList = new std::vector<LinkAction*>(); |
| actionList->reserve(1); |
| actionList->push_back(parseAction(&nextObj, nullptr, seenNextActions)); |
| } else if (nextObj.isArray()) { |
| const Array *a = nextObj.getArray(); |
| const int n = a->getLength(); |
| actionList = new std::vector<LinkAction*>(); |
| actionList->reserve(n); |
| for (int i = 0; i < n; ++i) { |
| const Object obj3 = a->get(i); |
| if (!obj3.isDict()) { |
| error(errSyntaxWarning, -1, "parseAction: Next array does not contain only dicts"); |
| continue; |
| } |
| |
| // Similar circle check as above. |
| const Object &obj3Ref = a->getNF(i); |
| if (obj3Ref.isRef()) { |
| const Ref ref = obj3Ref.getRef(); |
| if (!seenNextActions->insert(ref.num).second) { |
| error(errSyntaxWarning, -1, "parseAction: Circular next actions detected in array."); |
| delete actionList; |
| return action; |
| } |
| } |
| |
| actionList->push_back(parseAction(&obj3, nullptr, seenNextActions)); |
| } |
| } |
| |
| action->setNextActions(actionList); |
| |
| return action; |
| } |
| |
| const std::vector<LinkAction*> *LinkAction::nextActions() const { |
| return nextActionList; |
| } |
| |
| void LinkAction::setNextActions(std::vector<LinkAction*> *actions) { |
| delete nextActionList; |
| nextActionList = actions; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkDest |
| //------------------------------------------------------------------------ |
| |
| LinkDest::LinkDest(const Array *a) { |
| // initialize fields |
| left = bottom = right = top = zoom = 0; |
| changeLeft = changeTop = changeZoom = false; |
| ok = false; |
| |
| // get page |
| if (a->getLength() < 2) { |
| error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
| return; |
| } |
| const Object &obj0 = a->getNF(0); |
| if (obj0.isInt()) { |
| pageNum = obj0.getInt() + 1; |
| pageIsRef = false; |
| } else if (obj0.isRef()) { |
| pageRef = obj0.getRef(); |
| pageIsRef = true; |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination"); |
| return; |
| } |
| |
| // get destination type |
| Object obj1 = a->get(1); |
| |
| // XYZ link |
| if (obj1.isName("XYZ")) { |
| kind = destXYZ; |
| if (a->getLength() < 3) { |
| changeLeft = false; |
| } else { |
| Object obj2 = a->get(2); |
| if (obj2.isNull()) { |
| changeLeft = false; |
| } else if (obj2.isNum()) { |
| changeLeft = true; |
| left = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| return; |
| } |
| } |
| if (a->getLength() < 4) { |
| changeTop = false; |
| } else { |
| Object obj2 = a->get(3); |
| if (obj2.isNull()) { |
| changeTop = false; |
| } else if (obj2.isNum()) { |
| changeTop = true; |
| top = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| return; |
| } |
| } |
| if (a->getLength() < 5) { |
| changeZoom = false; |
| } else { |
| Object obj2 = a->get(4); |
| if (obj2.isNull()) { |
| changeZoom = false; |
| } else if (obj2.isNum()) { |
| zoom = obj2.getNum(); |
| changeZoom = (zoom == 0) ? false : true; |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| return; |
| } |
| } |
| |
| // Fit link |
| } else if (obj1.isName("Fit")) { |
| kind = destFit; |
| |
| // FitH link |
| } else if (obj1.isName("FitH")) { |
| kind = destFitH; |
| if (a->getLength() < 3) { |
| changeTop = false; |
| } else { |
| Object obj2 = a->get(2); |
| if (obj2.isNull()) { |
| changeTop = false; |
| } else if (obj2.isNum()) { |
| changeTop = true; |
| top = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| } |
| |
| // FitV link |
| } else if (obj1.isName("FitV")) { |
| if (a->getLength() < 3) { |
| error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
| return; |
| } |
| kind = destFitV; |
| Object obj2 = a->get(2); |
| if (obj2.isNull()) { |
| changeLeft = false; |
| } else if (obj2.isNum()) { |
| changeLeft = true; |
| left = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| |
| // FitR link |
| } else if (obj1.isName("FitR")) { |
| if (a->getLength() < 6) { |
| error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
| return; |
| } |
| kind = destFitR; |
| Object obj2 = a->get(2); |
| if (obj2.isNum()) { |
| left = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| obj2 = a->get(3); |
| if (obj2.isNum()) { |
| bottom = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| obj2 = a->get(4); |
| if (obj2.isNum()) { |
| right = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| obj2 = a->get(5); |
| if (obj2.isNum()) { |
| top = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| |
| // FitB link |
| } else if (obj1.isName("FitB")) { |
| kind = destFitB; |
| |
| // FitBH link |
| } else if (obj1.isName("FitBH")) { |
| if (a->getLength() < 3) { |
| error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
| return; |
| } |
| kind = destFitBH; |
| Object obj2 = a->get(2); |
| if (obj2.isNull()) { |
| changeTop = false; |
| } else if (obj2.isNum()) { |
| changeTop = true; |
| top = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| |
| // FitBV link |
| } else if (obj1.isName("FitBV")) { |
| if (a->getLength() < 3) { |
| error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
| return; |
| } |
| kind = destFitBV; |
| Object obj2 = a->get(2); |
| if (obj2.isNull()) { |
| changeLeft = false; |
| } else if (obj2.isNum()) { |
| changeLeft = true; |
| left = obj2.getNum(); |
| } else { |
| error(errSyntaxWarning, -1, "Bad annotation destination position"); |
| kind = destFit; |
| } |
| |
| // unknown link kind |
| } else { |
| error(errSyntaxWarning, -1, "Unknown annotation destination type"); |
| } |
| |
| ok = true; |
| return; |
| } |
| |
| LinkDest::LinkDest(const LinkDest *dest) { |
| kind = dest->kind; |
| pageIsRef = dest->pageIsRef; |
| if (pageIsRef) |
| pageRef = dest->pageRef; |
| else |
| pageNum = dest->pageNum; |
| left = dest->left; |
| bottom = dest->bottom; |
| right = dest->right; |
| top = dest->top; |
| zoom = dest->zoom; |
| changeLeft = dest->changeLeft; |
| changeTop = dest->changeTop; |
| changeZoom = dest->changeZoom; |
| ok = true; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkGoTo |
| //------------------------------------------------------------------------ |
| |
| LinkGoTo::LinkGoTo(const Object *destObj) { |
| dest = nullptr; |
| namedDest = nullptr; |
| |
| // named destination |
| if (destObj->isName()) { |
| namedDest = new GooString(destObj->getName()); |
| } else if (destObj->isString()) { |
| namedDest = destObj->getString()->copy(); |
| |
| // destination dictionary |
| } else if (destObj->isArray()) { |
| dest = new LinkDest(destObj->getArray()); |
| if (!dest->isOk()) { |
| delete dest; |
| dest = nullptr; |
| } |
| |
| // error |
| } else { |
| error(errSyntaxWarning, -1, "Illegal annotation destination"); |
| } |
| } |
| |
| LinkGoTo::~LinkGoTo() { |
| if (dest) |
| delete dest; |
| if (namedDest) |
| delete namedDest; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkGoToR |
| //------------------------------------------------------------------------ |
| |
| LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) { |
| fileName = nullptr; |
| dest = nullptr; |
| namedDest = nullptr; |
| |
| // get file name |
| Object obj1 = getFileSpecNameForPlatform (fileSpecObj); |
| if (obj1.isString()) { |
| fileName = obj1.getString()->copy(); |
| } |
| |
| // named destination |
| if (destObj->isName()) { |
| namedDest = new GooString(destObj->getName()); |
| } else if (destObj->isString()) { |
| namedDest = destObj->getString()->copy(); |
| |
| // destination dictionary |
| } else if (destObj->isArray()) { |
| dest = new LinkDest(destObj->getArray()); |
| if (!dest->isOk()) { |
| delete dest; |
| dest = nullptr; |
| } |
| |
| // error |
| } else { |
| error(errSyntaxWarning, -1, "Illegal annotation destination"); |
| } |
| } |
| |
| LinkGoToR::~LinkGoToR() { |
| if (fileName) |
| delete fileName; |
| if (dest) |
| delete dest; |
| if (namedDest) |
| delete namedDest; |
| } |
| |
| |
| //------------------------------------------------------------------------ |
| // LinkLaunch |
| //------------------------------------------------------------------------ |
| |
| LinkLaunch::LinkLaunch(const Object *actionObj) { |
| |
| fileName = nullptr; |
| params = nullptr; |
| |
| if (actionObj->isDict()) { |
| Object obj1 = actionObj->dictLookup("F"); |
| if (!obj1.isNull()) { |
| Object obj3 = getFileSpecNameForPlatform (&obj1); |
| if (obj3.isString()) { |
| fileName = obj3.getString()->copy(); |
| } |
| } else { |
| #ifdef _WIN32 |
| obj1 = actionObj->dictLookup("Win"); |
| #else |
| //~ This hasn't been defined by Adobe yet, so assume it looks |
| //~ just like the Win dictionary until they say otherwise. |
| obj1 = actionObj->dictLookup("Unix"); |
| #endif |
| if (obj1.isDict()) { |
| Object obj2 = obj1.dictLookup("F"); |
| Object obj3 = getFileSpecNameForPlatform (&obj2); |
| if (obj3.isString()) { |
| fileName = obj3.getString()->copy(); |
| } |
| obj2 = obj1.dictLookup("P"); |
| if (obj2.isString()) { |
| params = obj2.getString()->copy(); |
| } |
| } else { |
| error(errSyntaxWarning, -1, "Bad launch-type link action"); |
| } |
| } |
| } |
| } |
| |
| LinkLaunch::~LinkLaunch() { |
| if (fileName) |
| delete fileName; |
| if (params) |
| delete params; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkURI |
| //------------------------------------------------------------------------ |
| |
| LinkURI::LinkURI(const Object *uriObj, const GooString *baseURI) { |
| const GooString *uri2; |
| int n; |
| char c; |
| |
| uri = nullptr; |
| if (uriObj->isString()) { |
| uri2 = uriObj->getString(); |
| n = (int)strcspn(uri2->c_str(), "/:"); |
| if (n < uri2->getLength() && uri2->getChar(n) == ':') { |
| // "http:..." etc. |
| uri = uri2->copy(); |
| } else if (!uri2->cmpN("www.", 4)) { |
| // "www.[...]" without the leading "http://" |
| uri = new GooString("http://"); |
| uri->append(uri2); |
| } else { |
| // relative URI |
| if (baseURI) { |
| uri = baseURI->copy(); |
| if (uri->getLength() > 0) { |
| c = uri->getChar(uri->getLength() - 1); |
| if (c != '/' && c != '?') { |
| uri->append('/'); |
| } |
| } |
| if (uri2->getChar(0) == '/') { |
| uri->append(uri2->c_str() + 1, uri2->getLength() - 1); |
| } else { |
| uri->append(uri2); |
| } |
| } else { |
| uri = uri2->copy(); |
| } |
| } |
| } else { |
| error(errSyntaxWarning, -1, "Illegal URI-type link"); |
| } |
| } |
| |
| LinkURI::~LinkURI() { |
| if (uri) |
| delete uri; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkNamed |
| //------------------------------------------------------------------------ |
| |
| LinkNamed::LinkNamed(const Object *nameObj) { |
| name = nullptr; |
| if (nameObj->isName()) { |
| name = new GooString(nameObj->getName()); |
| } |
| } |
| |
| LinkNamed::~LinkNamed() { |
| if (name) { |
| delete name; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkMovie |
| //------------------------------------------------------------------------ |
| |
| LinkMovie::LinkMovie(const Object *obj) { |
| annotRef = Ref::INVALID(); |
| annotTitle = nullptr; |
| |
| const Object &annotationObj = obj->dictLookupNF("Annotation"); |
| if (annotationObj.isRef()) { |
| annotRef = annotationObj.getRef(); |
| } |
| |
| Object tmp = obj->dictLookup("T"); |
| if (tmp.isString()) { |
| annotTitle = tmp.getString()->copy(); |
| } |
| |
| if ((annotTitle == nullptr) && (annotRef == Ref::INVALID())) { |
| error(errSyntaxError, -1, |
| "Movie action is missing both the Annot and T keys"); |
| } |
| |
| tmp = obj->dictLookup("Operation"); |
| if (tmp.isName()) { |
| const char *name = tmp.getName(); |
| |
| if (!strcmp(name, "Play")) { |
| operation = operationTypePlay; |
| } |
| else if (!strcmp(name, "Stop")) { |
| operation = operationTypeStop; |
| } |
| else if (!strcmp(name, "Pause")) { |
| operation = operationTypePause; |
| } |
| else if (!strcmp(name, "Resume")) { |
| operation = operationTypeResume; |
| } |
| } |
| } |
| |
| LinkMovie::~LinkMovie() { |
| if (annotTitle) { |
| delete annotTitle; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkSound |
| //------------------------------------------------------------------------ |
| |
| LinkSound::LinkSound(const Object *soundObj) { |
| volume = 1.0; |
| sync = false; |
| repeat = false; |
| mix = false; |
| sound = nullptr; |
| if (soundObj->isDict()) |
| { |
| // volume |
| Object tmp = soundObj->dictLookup("Volume"); |
| if (tmp.isNum()) { |
| volume = tmp.getNum(); |
| } |
| // sync |
| tmp = soundObj->dictLookup("Synchronous"); |
| if (tmp.isBool()) { |
| sync = tmp.getBool(); |
| } |
| // repeat |
| tmp = soundObj->dictLookup("Repeat"); |
| if (tmp.isBool()) { |
| repeat = tmp.getBool(); |
| } |
| // mix |
| tmp = soundObj->dictLookup("Mix"); |
| if (tmp.isBool()) { |
| mix = tmp.getBool(); |
| } |
| // 'Sound' object |
| tmp = soundObj->dictLookup("Sound"); |
| sound = Sound::parseSound(&tmp); |
| } |
| } |
| |
| LinkSound::~LinkSound() { |
| delete sound; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkRendition |
| //------------------------------------------------------------------------ |
| |
| LinkRendition::LinkRendition(const Object *obj) { |
| operation = NoRendition; |
| media = nullptr; |
| js = nullptr; |
| int operationCode = -1; |
| |
| screenRef = Ref::INVALID(); |
| |
| if (obj->isDict()) { |
| Object tmp = obj->dictLookup("JS"); |
| if (!tmp.isNull()) { |
| if (tmp.isString()) { |
| js = new GooString(tmp.getString()); |
| } else if (tmp.isStream()) { |
| Stream *stream = tmp.getStream(); |
| js = new GooString(); |
| stream->fillGooString(js); |
| } else { |
| error(errSyntaxWarning, -1, "Invalid Rendition Action: JS not string or stream"); |
| } |
| } |
| |
| tmp = obj->dictLookup("OP"); |
| if (tmp.isInt()) { |
| operationCode = tmp.getInt(); |
| if (!js && (operationCode < 0 || operationCode > 4)) { |
| error(errSyntaxWarning, -1, "Invalid Rendition Action: unrecognized operation valued: {0:d}", operationCode); |
| } else { |
| // retrieve rendition object |
| renditionObj = obj->dictLookup("R"); |
| if (renditionObj.isDict()) { |
| media = new MediaRendition(&renditionObj); |
| } else if (operationCode == 0 || operationCode == 4) { |
| error(errSyntaxWarning, -1, "Invalid Rendition Action: no R field with op = {0:d}", operationCode); |
| renditionObj.setToNull(); |
| } |
| |
| const Object &anObj = obj->dictLookupNF("AN"); |
| if (anObj.isRef()) { |
| screenRef = anObj.getRef(); |
| } else if (operation >= 0 && operation <= 4) { |
| error(errSyntaxWarning, -1, "Invalid Rendition Action: no AN field with op = {0:d}", operationCode); |
| } |
| } |
| |
| switch (operationCode) { |
| case 0: |
| operation = PlayRendition; |
| break; |
| case 1: |
| operation = StopRendition; |
| break; |
| case 2: |
| operation = PauseRendition; |
| break; |
| case 3: |
| operation = ResumeRendition; |
| break; |
| case 4: |
| operation = PlayRendition; |
| break; |
| } |
| } else if (!js) { |
| error(errSyntaxWarning, -1, "Invalid Rendition action: no OP or JS field defined"); |
| } |
| } |
| } |
| |
| LinkRendition::~LinkRendition() { |
| delete js; |
| delete media; |
| } |
| |
| |
| //------------------------------------------------------------------------ |
| // LinkJavaScript |
| //------------------------------------------------------------------------ |
| |
| LinkJavaScript::LinkJavaScript(Object *jsObj) { |
| js = nullptr; |
| |
| if (jsObj->isString()) { |
| js = new GooString(jsObj->getString()); |
| } |
| else if (jsObj->isStream()) { |
| Stream *stream = jsObj->getStream(); |
| js = new GooString(); |
| stream->fillGooString(js); |
| } |
| } |
| |
| LinkJavaScript::~LinkJavaScript() { |
| if (js) { |
| delete js; |
| } |
| } |
| |
| Object LinkJavaScript::createObject(XRef *xref, const GooString &js) |
| { |
| Dict *linkDict = new Dict(xref); |
| linkDict->add("S", Object(objName, "JavaScript")); |
| linkDict->add("JS", Object(js.copy())); |
| |
| return Object(linkDict); |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkOCGState |
| //------------------------------------------------------------------------ |
| LinkOCGState::LinkOCGState(const Object *obj) { |
| stateList = new std::vector<StateList*>(); |
| preserveRB = true; |
| |
| Object obj1 = obj->dictLookup("State"); |
| if (obj1.isArray()) { |
| StateList *stList = nullptr; |
| |
| for (int i = 0; i < obj1.arrayGetLength(); ++i) { |
| const Object &obj2 = obj1.arrayGetNF(i); |
| if (obj2.isName()) { |
| if (stList) |
| stateList->push_back(stList); |
| |
| const char *name = obj2.getName(); |
| stList = new StateList(); |
| stList->list = new std::vector<Ref*>(); |
| if (!strcmp (name, "ON")) { |
| stList->st = On; |
| } else if (!strcmp (name, "OFF")) { |
| stList->st = Off; |
| } else if (!strcmp (name, "Toggle")) { |
| stList->st = Toggle; |
| } else { |
| error(errSyntaxWarning, -1, "Invalid name '{0:s}' in OCG Action state array", name); |
| delete stList; |
| stList = nullptr; |
| } |
| } else if (obj2.isRef()) { |
| if (stList) { |
| Ref ocgRef = obj2.getRef(); |
| Ref *item = new Ref(); |
| *item = ocgRef; |
| stList->list->push_back(item); |
| } else { |
| error(errSyntaxWarning, -1, "Invalid OCG Action State array, expected name instead of ref"); |
| } |
| } else { |
| error(errSyntaxWarning, -1, "Invalid item in OCG Action State array"); |
| } |
| } |
| // Add the last group |
| if (stList) |
| stateList->push_back(stList); |
| } else { |
| error(errSyntaxWarning, -1, "Invalid OCGState action"); |
| delete stateList; |
| stateList = nullptr; |
| } |
| |
| obj1 = obj->dictLookup("PreserveRB"); |
| if (obj1.isBool()) { |
| preserveRB = obj1.getBool(); |
| } |
| } |
| |
| LinkOCGState::~LinkOCGState() { |
| if (stateList) { |
| for (auto entry : *stateList) { |
| delete entry; |
| } |
| delete stateList; |
| } |
| } |
| |
| LinkOCGState::StateList::~StateList() { |
| if (list) { |
| for (auto entry : *list) { |
| delete entry; |
| } |
| delete list; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkHide |
| //------------------------------------------------------------------------ |
| |
| LinkHide::LinkHide(const Object *hideObj) { |
| targetName = nullptr; |
| show = false; // Default |
| |
| if (hideObj->isDict()) { |
| const Object targetObj = hideObj->dictLookup("T"); |
| if (targetObj.isString()) { |
| targetName = targetObj.getString()->copy(); |
| } |
| const Object shouldHide = hideObj->dictLookup("H"); |
| if (shouldHide.isBool()) { |
| show = !shouldHide.getBool(); |
| } |
| } |
| } |
| |
| LinkHide::~LinkHide() { |
| delete targetName; |
| } |
| |
| //------------------------------------------------------------------------ |
| // LinkUnknown |
| //------------------------------------------------------------------------ |
| |
| LinkUnknown::LinkUnknown(const char *actionA) { |
| action = new GooString(actionA); |
| } |
| |
| LinkUnknown::~LinkUnknown() { |
| delete action; |
| } |
| |
| //------------------------------------------------------------------------ |
| // Links |
| //------------------------------------------------------------------------ |
| |
| Links::Links(Annots *annots) { |
| int size; |
| int i; |
| |
| links = nullptr; |
| size = 0; |
| numLinks = 0; |
| |
| if (!annots) |
| return; |
| |
| for (i = 0; i < annots->getNumAnnots(); ++i) { |
| Annot *annot = annots->getAnnot(i); |
| |
| if (annot->getType() != Annot::typeLink) |
| continue; |
| |
| if (numLinks >= size) { |
| size += 16; |
| links = (AnnotLink **)greallocn(links, size, sizeof(AnnotLink *)); |
| } |
| annot->incRefCnt(); |
| links[numLinks++] = static_cast<AnnotLink *>(annot); |
| } |
| } |
| |
| Links::~Links() { |
| int i; |
| |
| for (i = 0; i < numLinks; ++i) |
| links[i]->decRefCnt(); |
| |
| gfree(links); |
| } |
| |
| LinkAction *Links::find(double x, double y) const { |
| int i; |
| |
| for (i = numLinks - 1; i >= 0; --i) { |
| if (links[i]->inRect(x, y)) { |
| return links[i]->getAction(); |
| } |
| } |
| return nullptr; |
| } |
| |
| bool Links::onLink(double x, double y) const { |
| int i; |
| |
| for (i = 0; i < numLinks; ++i) { |
| if (links[i]->inRect(x, y)) |
| return true; |
| } |
| return false; |
| } |