Link.cc 24.5 KB
Newer Older
Kristian Høgsberg's avatar
Kristian Høgsberg committed
1 2 3 4 5 6 7 8
//========================================================================
//
// Link.cc
//
// Copyright 1996-2003 Glyph & Cog, LLC
//
//========================================================================

9 10 11 12
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
13 14 15
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
16
// Copyright (C) 2006, 2008 Pino Toscano <pino@kde.org>
Albert Astals Cid's avatar
Albert Astals Cid committed
17
// Copyright (C) 2007, 2010, 2011 Carlos Garcia Campos <carlosgc@gnome.org>
18
// Copyright (C) 2008 Hugo Mercier <hmercier31@gmail.com>
19
// Copyright (C) 2008-2010, 2012-2014, 2016-2019 Albert Astals Cid <aacid@kde.org>
Kovid Goyal's avatar
Kovid Goyal committed
20
// Copyright (C) 2009 Kovid Goyal <kovid@kovidgoyal.net>
Ilya Gorenbein's avatar
Ilya Gorenbein committed
21
// Copyright (C) 2009 Ilya Gorenbein <igorenbein@finjan.com>
22
// Copyright (C) 2012 Tobias Koening <tobias.koenig@kdab.com>
23
// 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
Andre Heinecke's avatar
Andre Heinecke committed
24
// Copyright (C) 2018 Intevation GmbH <intevation@intevation.de>
Albert Astals Cid's avatar
Albert Astals Cid committed
25
// Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de>
26 27 28 29 30 31
//
// 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
//
//========================================================================

Kristian Høgsberg's avatar
Kristian Høgsberg committed
32 33 34 35 36 37
#include <config.h>

#include <stddef.h>
#include <string.h>
#include "goo/gmem.h"
#include "goo/GooString.h"
38
#include "goo/GooList.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
39 40 41 42 43
#include "Error.h"
#include "Object.h"
#include "Array.h"
#include "Dict.h"
#include "Link.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
44
#include "Sound.h"
45
#include "FileSpec.h"
46
#include "Rendition.h"
47
#include "Annot.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
48 49 50 51

//------------------------------------------------------------------------
// LinkAction
//------------------------------------------------------------------------
52 53 54 55
LinkAction::LinkAction() : nextActionList(nullptr) {
}

LinkAction::~LinkAction() {
56
  if (nextActionList)
57
    deleteGooList<LinkAction>(nextActionList);
58
}
Kristian Høgsberg's avatar
Kristian Høgsberg committed
59

Albert Astals Cid's avatar
Albert Astals Cid committed
60
LinkAction *LinkAction::parseDest(const Object *obj) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
61 62 63 64 65
  LinkAction *action;

  action = new LinkGoTo(obj);
  if (!action->isOk()) {
    delete action;
66
    return nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
67 68 69 70
  }
  return action;
}

71 72 73 74 75 76
LinkAction *LinkAction::parseAction(const Object *obj, const GooString *baseURI)
{
    std::set<int> seenNextActions;
    return parseAction(obj, baseURI, &seenNextActions);
}

77
LinkAction *LinkAction::parseAction(const Object *obj, const GooString *baseURI,
78
                                    std::set<int> *seenNextActions) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
79 80 81
  LinkAction *action;

  if (!obj->isDict()) {
82
      error(errSyntaxWarning, -1, "parseAction: Bad annotation action for URI '{0:s}'",
83
            baseURI ? baseURI->c_str() : "NULL");
84
      return nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
85 86
  }

Albert Astals Cid's avatar
Albert Astals Cid committed
87
  Object obj2 = obj->dictLookup("S");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
88 89 90

  // GoTo action
  if (obj2.isName("GoTo")) {
Albert Astals Cid's avatar
Albert Astals Cid committed
91
    Object obj3 = obj->dictLookup("D");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
92 93 94 95
    action = new LinkGoTo(&obj3);

  // GoToR action
  } else if (obj2.isName("GoToR")) {
Albert Astals Cid's avatar
Albert Astals Cid committed
96 97
    Object obj3 = obj->dictLookup("F");
    Object obj4 = obj->dictLookup("D");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
98 99 100 101 102 103 104 105
    action = new LinkGoToR(&obj3, &obj4);

  // Launch action
  } else if (obj2.isName("Launch")) {
    action = new LinkLaunch(obj);

  // URI action
  } else if (obj2.isName("URI")) {
Albert Astals Cid's avatar
Albert Astals Cid committed
106
    Object obj3 = obj->dictLookup("URI");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
107 108 109 110
    action = new LinkURI(&obj3, baseURI);

  // Named action
  } else if (obj2.isName("Named")) {
Albert Astals Cid's avatar
Albert Astals Cid committed
111
    Object obj3 = obj->dictLookup("N");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
112 113 114 115
    action = new LinkNamed(&obj3);

  // Movie action
  } else if (obj2.isName("Movie")) {
Hugo Mercier's avatar
Hugo Mercier committed
116 117 118 119 120
    action = new LinkMovie(obj);

  // Rendition action
  } else if (obj2.isName("Rendition")) {
    action = new LinkRendition(obj);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
121

Albert Astals Cid's avatar
Albert Astals Cid committed
122 123 124 125
  // Sound action
  } else if (obj2.isName("Sound")) {
    action = new LinkSound(obj);

126 127
  // JavaScript action
  } else if (obj2.isName("JavaScript")) {
Albert Astals Cid's avatar
Albert Astals Cid committed
128
    Object obj3 = obj->dictLookup("JS");
129 130
    action = new LinkJavaScript(&obj3);

131 132 133 134
  // Set-OCG-State action
  } else if (obj2.isName("SetOCGState")) {
    action = new LinkOCGState(obj);

Andre Heinecke's avatar
Andre Heinecke committed
135 136 137 138
  // Hide action
  } else if (obj2.isName("Hide")) {
    action = new LinkHide(obj);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
139 140 141 142 143 144
  // unknown action
  } else if (obj2.isName()) {
    action = new LinkUnknown(obj2.getName());

  // action is missing or wrong type
  } else {
145
    error(errSyntaxWarning, -1, "parseAction: Unknown annotation action object: URI = '{0:s}'",
146
          baseURI ? baseURI->c_str() : "NULL");
147
    action = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
148 149 150 151
  }

  if (action && !action->isOk()) {
    delete action;
152
    return nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
153
  }
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

  if (!action) {
    return nullptr;
  }

  // parse the next actions
  const Object nextObj = obj->dictLookup("Next");
  GooList *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();
169
        if (!seenNextActions->insert(ref.num).second) {
170 171 172 173 174
            error(errSyntaxWarning, -1, "parseAction: Circular next actions detected.");
            return action;
        }
    }

175 176
    actionList = new GooList();
    actionList->reserve(1);
177
    actionList->push_back(parseAction(&nextObj, nullptr, seenNextActions));
178 179 180
  } else if (nextObj.isArray()) {
    const Array *a = nextObj.getArray();
    const int n = a->getLength();
181 182
    actionList = new GooList();
    actionList->reserve(n);
183 184 185 186 187 188 189 190
    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.
191
      const Object &obj3Ref = a->getNF(i);
192 193
      if (obj3Ref.isRef()) {
          const Ref ref = obj3Ref.getRef();
194
          if (!seenNextActions->insert(ref.num).second) {
195 196 197 198 199
              error(errSyntaxWarning, -1, "parseAction: Circular next actions detected in array.");
              return action;
          }
      }

200
      actionList->push_back(parseAction(&obj3, nullptr, seenNextActions));
201 202 203 204 205
    }
  }

  action->setNextActions(actionList);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
206 207 208
  return action;
}

209 210 211 212 213 214 215 216 217
const GooList *LinkAction::nextActions() const {
  return nextActionList;
}

void LinkAction::setNextActions(GooList *actions) {
  delete nextActionList;
  nextActionList = actions;
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
218 219 220 221
//------------------------------------------------------------------------
// LinkDest
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
222
LinkDest::LinkDest(const Array *a) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
223 224
  // initialize fields
  left = bottom = right = top = zoom = 0;
225 226
  changeLeft = changeTop = changeZoom = false;
  ok = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
227 228 229

  // get page
  if (a->getLength() < 2) {
230
    error(errSyntaxWarning, -1, "Annotation destination array is too short");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
231 232
    return;
  }
233 234 235
  const Object &obj0 = a->getNF(0);
  if (obj0.isInt()) {
    pageNum = obj0.getInt() + 1;
236
    pageIsRef = false;
237 238 239
  } else if (obj0.isRef()) {
    pageRef.num = obj0.getRefNum();
    pageRef.gen = obj0.getRefGen();
240
    pageIsRef = true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
241
  } else {
242
    error(errSyntaxWarning, -1, "Bad annotation destination");
Albert Astals Cid's avatar
Albert Astals Cid committed
243
    return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
244 245 246
  }

  // get destination type
247
  Object obj1 = a->get(1);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
248 249 250 251 252

  // XYZ link
  if (obj1.isName("XYZ")) {
    kind = destXYZ;
    if (a->getLength() < 3) {
253
      changeLeft = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
254
    } else {
Albert Astals Cid's avatar
Albert Astals Cid committed
255
      Object obj2 = a->get(2);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
256
      if (obj2.isNull()) {
257
	changeLeft = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
258
      } else if (obj2.isNum()) {
259
	changeLeft = true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
260 261
	left = obj2.getNum();
      } else {
262
	error(errSyntaxWarning, -1, "Bad annotation destination position");
Albert Astals Cid's avatar
Albert Astals Cid committed
263
	return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
264 265 266
      }
    }
    if (a->getLength() < 4) {
267
      changeTop = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
268
    } else {
Albert Astals Cid's avatar
Albert Astals Cid committed
269
      Object obj2 = a->get(3);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
270
      if (obj2.isNull()) {
271
	changeTop = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
272
      } else if (obj2.isNum()) {
273
	changeTop = true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
274 275
	top = obj2.getNum();
      } else {
276
	error(errSyntaxWarning, -1, "Bad annotation destination position");
Albert Astals Cid's avatar
Albert Astals Cid committed
277
	return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
278 279 280
      }
    }
    if (a->getLength() < 5) {
281
      changeZoom = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
282
    } else {
Albert Astals Cid's avatar
Albert Astals Cid committed
283
      Object obj2 = a->get(4);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
284
      if (obj2.isNull()) {
285
	changeZoom = false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
286 287
      } else if (obj2.isNum()) {
	zoom = obj2.getNum();
288
	changeZoom = (zoom == 0) ? false : true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
289
      } else {
290
	error(errSyntaxWarning, -1, "Bad annotation destination position");
Albert Astals Cid's avatar
Albert Astals Cid committed
291
	return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
292 293 294 295 296 297 298 299 300 301
      }
    }

  // Fit link
  } else if (obj1.isName("Fit")) {
    kind = destFit;

  // FitH link
  } else if (obj1.isName("FitH")) {
    kind = destFitH;
302
    if (a->getLength() < 3) {
303
      changeTop = false;
304
    } else {
Albert Astals Cid's avatar
Albert Astals Cid committed
305
      Object obj2 = a->get(2);
306
      if (obj2.isNull()) {
307
	changeTop = false;
308
      } else if (obj2.isNum()) {
309
	changeTop = true;
310 311 312 313 314
	top = obj2.getNum();
      } else {
	error(errSyntaxWarning, -1, "Bad annotation destination position");
	kind = destFit;
      }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
315 316 317 318 319
    }

  // FitV link
  } else if (obj1.isName("FitV")) {
    if (a->getLength() < 3) {
320
      error(errSyntaxWarning, -1, "Annotation destination array is too short");
Albert Astals Cid's avatar
Albert Astals Cid committed
321
      return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
322 323
    }
    kind = destFitV;
Albert Astals Cid's avatar
Albert Astals Cid committed
324
    Object obj2 = a->get(2);
325
    if (obj2.isNull()) {
326
      changeLeft = false;
327
    } else if (obj2.isNum()) {
328
      changeLeft = true;
329 330
      left = obj2.getNum();
    } else {
331
      error(errSyntaxWarning, -1, "Bad annotation destination position");
332
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
333 334 335 336 337
    }

  // FitR link
  } else if (obj1.isName("FitR")) {
    if (a->getLength() < 6) {
338
      error(errSyntaxWarning, -1, "Annotation destination array is too short");
Albert Astals Cid's avatar
Albert Astals Cid committed
339
      return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
340 341
    }
    kind = destFitR;
Albert Astals Cid's avatar
Albert Astals Cid committed
342 343
    Object obj2 = a->get(2);
    if (obj2.isNum()) {
344 345
      left = obj2.getNum();
    } else {
346
      error(errSyntaxWarning, -1, "Bad annotation destination position");
347
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
348
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
349 350
    obj2 = a->get(3);
    if (obj2.isNum()) {
351 352
      bottom = obj2.getNum();
    } else {
353
      error(errSyntaxWarning, -1, "Bad annotation destination position");
354
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
355
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
356 357
    obj2 = a->get(4);
    if (obj2.isNum()) {
358 359
      right = obj2.getNum();
    } else {
360
      error(errSyntaxWarning, -1, "Bad annotation destination position");
361
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
362
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
363 364
    obj2 = a->get(5);
    if (obj2.isNum()) {
365 366
      top = obj2.getNum();
    } else {
367
      error(errSyntaxWarning, -1, "Bad annotation destination position");
368
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
369 370 371 372 373 374 375 376 377
    }

  // FitB link
  } else if (obj1.isName("FitB")) {
    kind = destFitB;

  // FitBH link
  } else if (obj1.isName("FitBH")) {
    if (a->getLength() < 3) {
378
      error(errSyntaxWarning, -1, "Annotation destination array is too short");
Albert Astals Cid's avatar
Albert Astals Cid committed
379
      return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
380 381
    }
    kind = destFitBH;
Albert Astals Cid's avatar
Albert Astals Cid committed
382
    Object obj2 = a->get(2);
383
    if (obj2.isNull()) {
384
      changeTop = false;
385
    } else if (obj2.isNum()) {
386
      changeTop = true;
387 388
      top = obj2.getNum();
    } else {
389
      error(errSyntaxWarning, -1, "Bad annotation destination position");
390
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
391 392 393 394 395
    }

  // FitBV link
  } else if (obj1.isName("FitBV")) {
    if (a->getLength() < 3) {
396
      error(errSyntaxWarning, -1, "Annotation destination array is too short");
Albert Astals Cid's avatar
Albert Astals Cid committed
397
      return;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
398 399
    }
    kind = destFitBV;
Albert Astals Cid's avatar
Albert Astals Cid committed
400
    Object obj2 = a->get(2);
401
    if (obj2.isNull()) {
402
      changeLeft = false;
403
    } else if (obj2.isNum()) {
404
      changeLeft = true;
405 406
      left = obj2.getNum();
    } else {
407
      error(errSyntaxWarning, -1, "Bad annotation destination position");
408
      kind = destFit;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
409 410 411 412
    }

  // unknown link kind
  } else {
413
    error(errSyntaxWarning, -1, "Unknown annotation destination type");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
414 415
  }

416
  ok = true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
417 418 419
  return;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
420
LinkDest::LinkDest(const LinkDest *dest) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
421 422 423 424 425 426 427 428 429 430 431 432 433 434
  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;
435
  ok = true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
436 437 438 439 440 441
}

//------------------------------------------------------------------------
// LinkGoTo
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
442
LinkGoTo::LinkGoTo(const Object *destObj) {
443 444
  dest = nullptr;
  namedDest = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
445 446 447

  // named destination
  if (destObj->isName()) {
448
    namedDest = new GooString(destObj->getName());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
449
  } else if (destObj->isString()) {
450
    namedDest = destObj->getString()->copy();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
451 452 453 454 455 456

  // destination dictionary
  } else if (destObj->isArray()) {
    dest = new LinkDest(destObj->getArray());
    if (!dest->isOk()) {
      delete dest;
457
      dest = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
458 459 460 461
    }

  // error
  } else {
462
    error(errSyntaxWarning, -1, "Illegal annotation destination");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
  }
}

LinkGoTo::~LinkGoTo() {
  if (dest)
    delete dest;
  if (namedDest)
    delete namedDest;
}

//------------------------------------------------------------------------
// LinkGoToR
//------------------------------------------------------------------------

LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
478 479 480
  fileName = nullptr;
  dest = nullptr;
  namedDest = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
481 482

  // get file name
Albert Astals Cid's avatar
Albert Astals Cid committed
483 484
  Object obj1 = getFileSpecNameForPlatform (fileSpecObj);
  if (obj1.isString()) {
485 486
    fileName = obj1.getString()->copy();
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
487 488 489

  // named destination
  if (destObj->isName()) {
490
    namedDest = new GooString(destObj->getName());
Kristian Høgsberg's avatar
Kristian Høgsberg committed
491
  } else if (destObj->isString()) {
492
    namedDest = destObj->getString()->copy();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
493 494 495 496 497 498

  // destination dictionary
  } else if (destObj->isArray()) {
    dest = new LinkDest(destObj->getArray());
    if (!dest->isOk()) {
      delete dest;
499
      dest = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
500 501 502 503
    }

  // error
  } else {
504
    error(errSyntaxWarning, -1, "Illegal annotation destination");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
  }
}

LinkGoToR::~LinkGoToR() {
  if (fileName)
    delete fileName;
  if (dest)
    delete dest;
  if (namedDest)
    delete namedDest;
}


//------------------------------------------------------------------------
// LinkLaunch
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
522
LinkLaunch::LinkLaunch(const Object *actionObj) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
523

524 525
  fileName = nullptr;
  params = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
526 527

  if (actionObj->isDict()) {
Albert Astals Cid's avatar
Albert Astals Cid committed
528 529 530 531
    Object obj1 = actionObj->dictLookup("F");
    if (!obj1.isNull()) {
      Object obj3 = getFileSpecNameForPlatform (&obj1);
      if (obj3.isString()) {
532 533
	fileName = obj3.getString()->copy();
      }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
534
    } else {
Kovid Goyal's avatar
Kovid Goyal committed
535
#ifdef _WIN32
Albert Astals Cid's avatar
Albert Astals Cid committed
536
      obj1 = actionObj->dictLookup("Win");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
537 538 539
#else
      //~ This hasn't been defined by Adobe yet, so assume it looks
      //~ just like the Win dictionary until they say otherwise.
Albert Astals Cid's avatar
Albert Astals Cid committed
540
      obj1 = actionObj->dictLookup("Unix");
Albert Astals Cid's avatar
Albert Astals Cid committed
541
#endif
Albert Astals Cid's avatar
Albert Astals Cid committed
542 543 544 545
      if (obj1.isDict()) {
	Object obj2 = obj1.dictLookup("F");
	Object obj3 = getFileSpecNameForPlatform (&obj2);
	if (obj3.isString()) {
546 547
	  fileName = obj3.getString()->copy();
	}
Albert Astals Cid's avatar
Albert Astals Cid committed
548 549
	obj2 = obj1.dictLookup("P");
	if (obj2.isString()) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
550 551 552
	  params = obj2.getString()->copy();
	}
      } else {
553
	error(errSyntaxWarning, -1, "Bad launch-type link action");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
      }
    }
  }
}

LinkLaunch::~LinkLaunch() {
  if (fileName)
    delete fileName;
  if (params)
    delete params;
}

//------------------------------------------------------------------------
// LinkURI
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
570
LinkURI::LinkURI(const Object *uriObj, const GooString *baseURI) {
571
  const GooString *uri2;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
572 573 574
  int n;
  char c;

575
  uri = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
576
  if (uriObj->isString()) {
577
    uri2 = uriObj->getString();
578
    n = (int)strcspn(uri2->c_str(), "/:");
579 580 581 582 583 584 585 586 587 588
    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) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
589
	uri = baseURI->copy();
590 591 592 593 594
	if (uri->getLength() > 0) {
	  c = uri->getChar(uri->getLength() - 1);
	  if (c != '/' && c != '?') {
	    uri->append('/');
	  }
595 596
	}
	if (uri2->getChar(0) == '/') {
597
	  uri->append(uri2->c_str() + 1, uri2->getLength() - 1);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
598
	} else {
599
	  uri->append(uri2);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
600 601
	}
      } else {
602
	uri = uri2->copy();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
603 604 605
      }
    }
  } else {
606
    error(errSyntaxWarning, -1, "Illegal URI-type link");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
607 608 609 610 611 612 613 614 615 616 617 618
  }
}

LinkURI::~LinkURI() {
  if (uri)
    delete uri;
}

//------------------------------------------------------------------------
// LinkNamed
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
619
LinkNamed::LinkNamed(const Object *nameObj) {
620
  name = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
  if (nameObj->isName()) {
    name = new GooString(nameObj->getName());
  }
}

LinkNamed::~LinkNamed() {
  if (name) {
    delete name;
  }
}

//------------------------------------------------------------------------
// LinkMovie
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
636
LinkMovie::LinkMovie(const Object *obj) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
637
  annotRef.num = -1;
638
  annotTitle = nullptr;
Hugo Mercier's avatar
Hugo Mercier committed
639

Albert Astals Cid's avatar
Albert Astals Cid committed
640 641
  Object tmp = obj->dictLookupNF("Annotation");
  if (tmp.isRef()) {
Hugo Mercier's avatar
Hugo Mercier committed
642 643 644
    annotRef = tmp.getRef();
  }

Albert Astals Cid's avatar
Albert Astals Cid committed
645 646
  tmp = obj->dictLookup("T");
  if (tmp.isString()) {
Hugo Mercier's avatar
Hugo Mercier committed
647 648 649
    annotTitle = tmp.getString()->copy();
  }

650
  if ((annotTitle == nullptr) && (annotRef.num == -1)) {
651 652
    error(errSyntaxError, -1,
	  "Movie action is missing both the Annot and T keys");
Kristian Høgsberg's avatar
Kristian Høgsberg committed
653
  }
Hugo Mercier's avatar
Hugo Mercier committed
654

Albert Astals Cid's avatar
Albert Astals Cid committed
655 656
  tmp = obj->dictLookup("Operation");
  if (tmp.isName()) {
657
    const char *name = tmp.getName();
Hugo Mercier's avatar
Hugo Mercier committed
658 659 660 661 662 663 664 665 666 667 668 669 670 671
    
    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;
    }
  }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
672 673 674
}

LinkMovie::~LinkMovie() {
Hugo Mercier's avatar
Hugo Mercier committed
675 676
  if (annotTitle) {
    delete annotTitle;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
677 678 679
  }
}

Albert Astals Cid's avatar
Albert Astals Cid committed
680 681 682 683
//------------------------------------------------------------------------
// LinkSound
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
684
LinkSound::LinkSound(const Object *soundObj) {
Albert Astals Cid's avatar
Albert Astals Cid committed
685
  volume = 1.0;
686 687 688
  sync = false;
  repeat = false;
  mix = false;
689
  sound = nullptr;
Albert Astals Cid's avatar
Albert Astals Cid committed
690 691 692
  if (soundObj->isDict())
  {
    // volume
Albert Astals Cid's avatar
Albert Astals Cid committed
693
    Object tmp = soundObj->dictLookup("Volume");
Albert Astals Cid's avatar
Albert Astals Cid committed
694 695 696 697
    if (tmp.isNum()) {
      volume = tmp.getNum();
    }
    // sync
Albert Astals Cid's avatar
Albert Astals Cid committed
698
    tmp = soundObj->dictLookup("Synchronous");
Albert Astals Cid's avatar
Albert Astals Cid committed
699 700 701 702
    if (tmp.isBool()) {
      sync = tmp.getBool();
    }
    // repeat
Albert Astals Cid's avatar
Albert Astals Cid committed
703
    tmp = soundObj->dictLookup("Repeat");
Albert Astals Cid's avatar
Albert Astals Cid committed
704 705 706 707
    if (tmp.isBool()) {
      repeat = tmp.getBool();
    }
    // mix
Albert Astals Cid's avatar
Albert Astals Cid committed
708
    tmp = soundObj->dictLookup("Mix");
Albert Astals Cid's avatar
Albert Astals Cid committed
709 710 711 712
    if (tmp.isBool()) {
      mix = tmp.getBool();
    }
    // 'Sound' object
Albert Astals Cid's avatar
Albert Astals Cid committed
713
    tmp = soundObj->dictLookup("Sound");
Albert Astals Cid's avatar
Albert Astals Cid committed
714 715 716 717 718 719 720 721
    sound = Sound::parseSound(&tmp);
  }
}

LinkSound::~LinkSound() {
  delete sound;
}

Hugo Mercier's avatar
Hugo Mercier committed
722 723 724 725
//------------------------------------------------------------------------
// LinkRendition
//------------------------------------------------------------------------

Albert Astals Cid's avatar
Albert Astals Cid committed
726
LinkRendition::LinkRendition(const Object *obj) {
727
  operation = NoRendition;
728 729
  media = nullptr;
  js = nullptr;
730
  int operationCode = -1;
Hugo Mercier's avatar
Hugo Mercier committed
731

732
  if (obj->isDict()) {
Albert Astals Cid's avatar
Albert Astals Cid committed
733 734
    Object tmp = obj->dictLookup("JS");
    if (!tmp.isNull()) {
735 736 737 738 739
      if (tmp.isString()) {
        js = new GooString(tmp.getString());
      } else if (tmp.isStream()) {
        Stream *stream = tmp.getStream();
	js = new GooString();
740
	stream->fillGooString(js);
741
      } else {
742
        error(errSyntaxWarning, -1, "Invalid Rendition Action: JS not string or stream");
Hugo Mercier's avatar
Hugo Mercier committed
743
      }
744
    }
Hugo Mercier's avatar
Hugo Mercier committed
745

Albert Astals Cid's avatar
Albert Astals Cid committed
746 747
    tmp = obj->dictLookup("OP");
    if (tmp.isInt()) {
748 749 750
      operationCode = tmp.getInt();
      if (!js && (operationCode < 0 || operationCode > 4)) {
        error(errSyntaxWarning, -1, "Invalid Rendition Action: unrecognized operation valued: {0:d}", operationCode);
751 752
      } else {
        // retrieve rendition object
Albert Astals Cid's avatar
Albert Astals Cid committed
753 754
        renditionObj = obj->dictLookup("R");
        if (renditionObj.isDict()) {
755
          media = new MediaRendition(&renditionObj);
756 757
	} else if (operationCode == 0 || operationCode == 4) {
          error(errSyntaxWarning, -1, "Invalid Rendition Action: no R field with op = {0:d}", operationCode);
Albert Astals Cid's avatar
Albert Astals Cid committed
758
	  renditionObj.setToNull();
Hugo Mercier's avatar
Hugo Mercier committed
759 760
	}

Albert Astals Cid's avatar
Albert Astals Cid committed
761 762
	screenRef = obj->dictLookupNF("AN");
	if (!screenRef.isRef() && operation >= 0 && operation <= 4) {
763
	  error(errSyntaxWarning, -1, "Invalid Rendition Action: no AN field with op = {0:d}", operationCode);
Albert Astals Cid's avatar
Albert Astals Cid committed
764
	  screenRef.setToNull();
765
	}
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
      }

      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;
784 785
      }
    } else if (!js) {
786
      error(errSyntaxWarning, -1, "Invalid Rendition action: no OP or JS field defined");
Hugo Mercier's avatar
Hugo Mercier committed
787 788 789 790 791
    }
  }
}

LinkRendition::~LinkRendition() {
Albert Astals Cid's avatar
Albert Astals Cid committed
792 793
  delete js;
  delete media;
Hugo Mercier's avatar
Hugo Mercier committed
794 795 796
}


797 798 799 800 801
//------------------------------------------------------------------------
// LinkJavaScript
//------------------------------------------------------------------------

LinkJavaScript::LinkJavaScript(Object *jsObj) {
802
  js = nullptr;
803 804 805 806 807 808 809

  if (jsObj->isString()) {
    js = new GooString(jsObj->getString());
  }
  else if (jsObj->isStream()) {
    Stream *stream = jsObj->getStream();
    js = new GooString();
810
    stream->fillGooString(js);
811 812 813 814 815 816 817 818 819
  }
}

LinkJavaScript::~LinkJavaScript() {
  if (js) {
    delete js;
  }
}

820 821 822
//------------------------------------------------------------------------
// LinkOCGState
//------------------------------------------------------------------------
Albert Astals Cid's avatar
Albert Astals Cid committed
823
LinkOCGState::LinkOCGState(const Object *obj) {
824
  stateList = new GooList();
825
  preserveRB = true;
826

Albert Astals Cid's avatar
Albert Astals Cid committed
827 828
  Object obj1 = obj->dictLookup("State");
  if (obj1.isArray()) {
829
    StateList *stList = nullptr;
830 831

    for (int i = 0; i < obj1.arrayGetLength(); ++i) {
832
      const Object &obj2 = obj1.arrayGetNF(i);
833 834
      if (obj2.isName()) {
        if (stList)
835
	  stateList->push_back(stList);
836

837
	const char *name = obj2.getName();
838 839 840 841 842 843 844 845 846
	stList = new StateList();
	stList->list = new GooList();
	if (!strcmp (name, "ON")) {
	  stList->st = On;
	} else if (!strcmp (name, "OFF")) {
	  stList->st = Off;
	} else if (!strcmp (name, "Toggle")) {
	  stList->st = Toggle;
	} else {
847
	  error(errSyntaxWarning, -1, "Invalid name '{0:s}' in OCG Action state array", name);
848
	  delete stList;
849
	  stList = nullptr;
850 851 852 853 854 855 856
	}
      } else if (obj2.isRef()) {
        if (stList) {
	  Ref ocgRef = obj2.getRef();
	  Ref *item = new Ref();
	  item->num = ocgRef.num;
	  item->gen = ocgRef.gen;
857
	  stList->list->push_back(item);
858
	} else {
859
	  error(errSyntaxWarning, -1, "Invalid OCG Action State array, expected name instead of ref");
860 861
	}
      } else {
862
        error(errSyntaxWarning, -1, "Invalid item in OCG Action State array");
863 864 865 866
      }
    }
    // Add the last group
    if (stList)
867
      stateList->push_back(stList);
868
  } else {
869
    error(errSyntaxWarning, -1, "Invalid OCGState action");
870
    delete stateList;
871
    stateList = nullptr;
872 873
  }

Albert Astals Cid's avatar
Albert Astals Cid committed
874 875
  obj1 = obj->dictLookup("PreserveRB");
  if (obj1.isBool()) {
876 877 878 879 880 881
    preserveRB = obj1.getBool();
  }
}

LinkOCGState::~LinkOCGState() {
  if (stateList)
882
    deleteGooList<StateList>(stateList);
883 884 885 886
}

LinkOCGState::StateList::~StateList() {
  if (list)
887
    deleteGooList<Ref>(list);
888 889
}

Andre Heinecke's avatar
Andre Heinecke committed
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
//------------------------------------------------------------------------
// 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;
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
914 915 916 917
//------------------------------------------------------------------------
// LinkUnknown
//------------------------------------------------------------------------

918
LinkUnknown::LinkUnknown(const char *actionA) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
919 920 921 922 923 924 925 926 927 928 929
  action = new GooString(actionA);
}

LinkUnknown::~LinkUnknown() {
  delete action;
}

//------------------------------------------------------------------------
// Links
//------------------------------------------------------------------------

930
Links::Links(Annots *annots) {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
931 932 933
  int size;
  int i;

934
  links = nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
935 936 937
  size = 0;
  numLinks = 0;

938 939 940 941 942 943 944 945 946 947 948 949
  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 *));
Kristian Høgsberg's avatar
Kristian Høgsberg committed
950
    }
951 952
    annot->incRefCnt();
    links[numLinks++] = static_cast<AnnotLink *>(annot);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
953 954 955 956 957 958 959
  }
}

Links::~Links() {
  int i;

  for (i = 0; i < numLinks; ++i)
960 961
    links[i]->decRefCnt();

Kristian Høgsberg's avatar
Kristian Høgsberg committed
962 963 964 965 966 967 968 969 970 971 972
  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();
    }
  }
973
  return nullptr;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
974 975
}

976
bool Links::onLink(double x, double y) const {
Kristian Høgsberg's avatar
Kristian Høgsberg committed
977 978 979 980
  int i;

  for (i = 0; i < numLinks; ++i) {
    if (links[i]->inRect(x, y))
981
      return true;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
982
  }
983
  return false;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
984
}