]> git.ipfire.org Git - thirdparty/cups.git/blob - pdftops/Link.cxx
Merge changes from 1.1.x into 1.2 devel.
[thirdparty/cups.git] / pdftops / Link.cxx
1 //========================================================================
2 //
3 // Link.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stddef.h>
14 #include <string.h>
15 #include "gmem.h"
16 #include "GString.h"
17 #include "Error.h"
18 #include "Object.h"
19 #include "Array.h"
20 #include "Dict.h"
21 #include "Link.h"
22
23 //------------------------------------------------------------------------
24
25 static GString *getFileSpecName(Object *fileSpecObj);
26
27 //------------------------------------------------------------------------
28 // LinkDest
29 //------------------------------------------------------------------------
30
31 LinkDest::LinkDest(Array *a, GBool pageIsRefA) {
32 Object obj1, obj2;
33
34 // initialize fields
35 pageIsRef = pageIsRefA;
36 left = bottom = right = top = zoom = 0;
37 ok = gFalse;
38
39 // get page
40 if (pageIsRef) {
41 if (!a->getNF(0, &obj1)->isRef()) {
42 error(-1, "Bad annotation destination");
43 goto err2;
44 }
45 pageRef.num = obj1.getRefNum();
46 pageRef.gen = obj1.getRefGen();
47 obj1.free();
48 } else {
49 if (!a->get(0, &obj1)->isInt()) {
50 error(-1, "Bad annotation destination");
51 goto err2;
52 }
53 pageNum = obj1.getInt() + 1;
54 obj1.free();
55 }
56
57 // get destination type
58 a->get(1, &obj1);
59
60 // XYZ link
61 if (obj1.isName("XYZ")) {
62 kind = destXYZ;
63 a->get(2, &obj2);
64 if (obj2.isNull()) {
65 changeLeft = gFalse;
66 } else if (obj2.isNum()) {
67 changeLeft = gTrue;
68 left = obj2.getNum();
69 } else {
70 error(-1, "Bad annotation destination position");
71 goto err1;
72 }
73 obj2.free();
74 a->get(3, &obj2);
75 if (obj2.isNull()) {
76 changeTop = gFalse;
77 } else if (obj2.isNum()) {
78 changeTop = gTrue;
79 top = obj2.getNum();
80 } else {
81 error(-1, "Bad annotation destination position");
82 goto err1;
83 }
84 obj2.free();
85 a->get(4, &obj2);
86 if (obj2.isNull()) {
87 changeZoom = gFalse;
88 } else if (obj2.isNum()) {
89 changeZoom = gTrue;
90 zoom = obj2.getNum();
91 } else {
92 error(-1, "Bad annotation destination position");
93 goto err1;
94 }
95 obj2.free();
96
97 // Fit link
98 } else if (obj1.isName("Fit")) {
99 kind = destFit;
100
101 // FitH link
102 } else if (obj1.isName("FitH")) {
103 kind = destFitH;
104 if (!a->get(2, &obj2)->isNum()) {
105 error(-1, "Bad annotation destination position");
106 goto err1;
107 }
108 top = obj2.getNum();
109 obj2.free();
110
111 // FitV link
112 } else if (obj1.isName("FitV")) {
113 kind = destFitV;
114 if (!a->get(2, &obj2)->isNum()) {
115 error(-1, "Bad annotation destination position");
116 goto err1;
117 }
118 left = obj2.getNum();
119 obj2.free();
120
121 // FitR link
122 } else if (obj1.isName("FitR")) {
123 kind = destFitR;
124 if (!a->get(2, &obj2)->isNum()) {
125 error(-1, "Bad annotation destination position");
126 goto err1;
127 }
128 left = obj2.getNum();
129 obj2.free();
130 if (!a->get(3, &obj2)->isNum()) {
131 error(-1, "Bad annotation destination position");
132 goto err1;
133 }
134 bottom = obj2.getNum();
135 obj2.free();
136 if (!a->get(4, &obj2)->isNum()) {
137 error(-1, "Bad annotation destination position");
138 goto err1;
139 }
140 right = obj2.getNum();
141 obj2.free();
142 if (!a->get(5, &obj2)->isNum()) {
143 error(-1, "Bad annotation destination position");
144 goto err1;
145 }
146 top = obj2.getNum();
147 obj2.free();
148
149 // FitB link
150 } else if (obj1.isName("FitB")) {
151 kind = destFitB;
152
153 // FitBH link
154 } else if (obj1.isName("FitBH")) {
155 kind = destFitBH;
156 if (!a->get(2, &obj2)->isNum()) {
157 error(-1, "Bad annotation destination position");
158 goto err1;
159 }
160 top = obj2.getNum();
161 obj2.free();
162
163 // FitBV link
164 } else if (obj1.isName("FitBV")) {
165 kind = destFitBV;
166 if (!a->get(2, &obj2)->isNum()) {
167 error(-1, "Bad annotation destination position");
168 goto err1;
169 }
170 left = obj2.getNum();
171 obj2.free();
172
173 // unknown link kind
174 } else {
175 error(-1, "Unknown annotation destination type");
176 goto err2;
177 }
178
179 obj1.free();
180 ok = gTrue;
181 return;
182
183 err1:
184 obj2.free();
185 err2:
186 obj1.free();
187 }
188
189 LinkDest::LinkDest(LinkDest *dest) {
190 kind = dest->kind;
191 pageIsRef = dest->pageIsRef;
192 if (pageIsRef)
193 pageRef = dest->pageRef;
194 else
195 pageNum = dest->pageNum;
196 left = dest->left;
197 bottom = dest->bottom;
198 right = dest->right;
199 top = dest->top;
200 zoom = dest->zoom;
201 changeLeft = dest->changeLeft;
202 changeTop = dest->changeTop;
203 changeZoom = dest->changeZoom;
204 ok = gTrue;
205 }
206
207 //------------------------------------------------------------------------
208 // LinkGoTo
209 //------------------------------------------------------------------------
210
211 LinkGoTo::LinkGoTo(Object *destObj) {
212 dest = NULL;
213 namedDest = NULL;
214
215 // named destination
216 if (destObj->isName()) {
217 namedDest = new GString(destObj->getName());
218 } else if (destObj->isString()) {
219 namedDest = destObj->getString()->copy();
220
221 // destination dictionary
222 } else if (destObj->isArray()) {
223 dest = new LinkDest(destObj->getArray(), gTrue);
224 if (!dest->isOk()) {
225 delete dest;
226 dest = NULL;
227 }
228
229 // error
230 } else {
231 error(-1, "Illegal annotation destination");
232 }
233 }
234
235 LinkGoTo::~LinkGoTo() {
236 if (dest)
237 delete dest;
238 if (namedDest)
239 delete namedDest;
240 }
241
242 //------------------------------------------------------------------------
243 // LinkGoToR
244 //------------------------------------------------------------------------
245
246 LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
247 dest = NULL;
248 namedDest = NULL;
249
250 // get file name
251 fileName = getFileSpecName(fileSpecObj);
252
253 // named destination
254 if (destObj->isName()) {
255 namedDest = new GString(destObj->getName());
256 } else if (destObj->isString()) {
257 namedDest = destObj->getString()->copy();
258
259 // destination dictionary
260 } else if (destObj->isArray()) {
261 dest = new LinkDest(destObj->getArray(), gFalse);
262 if (!dest->isOk()) {
263 delete dest;
264 dest = NULL;
265 }
266
267 // error
268 } else {
269 error(-1, "Illegal annotation destination");
270 }
271 }
272
273 LinkGoToR::~LinkGoToR() {
274 if (fileName)
275 delete fileName;
276 if (dest)
277 delete dest;
278 if (namedDest)
279 delete namedDest;
280 }
281
282
283 //------------------------------------------------------------------------
284 // LinkLaunch
285 //------------------------------------------------------------------------
286
287 LinkLaunch::LinkLaunch(Object *actionObj) {
288 Object obj1, obj2;
289
290 fileName = NULL;
291 params = NULL;
292
293 if (actionObj->isDict()) {
294 if (!actionObj->dictLookup("F", &obj1)->isNull()) {
295 fileName = getFileSpecName(&obj1);
296 } else {
297 obj1.free();
298 //~ This hasn't been defined by Adobe yet, so assume it looks
299 //~ just like the Win dictionary until they say otherwise.
300 if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
301 obj1.dictLookup("F", &obj2);
302 fileName = getFileSpecName(&obj2);
303 obj2.free();
304 if (obj1.dictLookup("P", &obj2)->isString())
305 params = obj2.getString()->copy();
306 obj2.free();
307 } else {
308 error(-1, "Bad launch-type link action");
309 }
310 }
311 obj1.free();
312 }
313 }
314
315 LinkLaunch::~LinkLaunch() {
316 if (fileName)
317 delete fileName;
318 if (params)
319 delete params;
320 }
321
322 //------------------------------------------------------------------------
323 // LinkURI
324 //------------------------------------------------------------------------
325
326 LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
327 GString *uri2;
328 int n;
329 char c;
330
331 uri = NULL;
332 if (uriObj->isString()) {
333 uri2 = uriObj->getString()->copy();
334 if (baseURI) {
335 n = strcspn(uri2->getCString(), "/:");
336 if (n == uri2->getLength() || uri2->getChar(n) == '/') {
337 uri = baseURI->copy();
338 c = uri->getChar(uri->getLength() - 1);
339 if (c == '/' || c == '?') {
340 if (uri2->getChar(0) == '/') {
341 uri2->del(0);
342 }
343 } else {
344 if (uri2->getChar(0) != '/') {
345 uri->append('/');
346 }
347 }
348 uri->append(uri2);
349 delete uri2;
350 } else {
351 uri = uri2;
352 }
353 } else {
354 uri = uri2;
355 }
356 } else {
357 error(-1, "Illegal URI-type link");
358 }
359 }
360
361 LinkURI::~LinkURI() {
362 if (uri)
363 delete uri;
364 }
365
366 //------------------------------------------------------------------------
367 // LinkNamed
368 //------------------------------------------------------------------------
369
370 LinkNamed::LinkNamed(Object *nameObj) {
371 name = NULL;
372 if (nameObj->isName()) {
373 name = new GString(nameObj->getName());
374 }
375 }
376
377 LinkNamed::~LinkNamed() {
378 if (name) {
379 delete name;
380 }
381 }
382
383 //------------------------------------------------------------------------
384 // LinkUnknown
385 //------------------------------------------------------------------------
386
387 LinkUnknown::LinkUnknown(char *actionA) {
388 action = new GString(actionA);
389 }
390
391 LinkUnknown::~LinkUnknown() {
392 delete action;
393 }
394
395 //------------------------------------------------------------------------
396 // Link
397 //------------------------------------------------------------------------
398
399 Link::Link(Dict *dict, GString *baseURI) {
400 Object obj1, obj2, obj3, obj4;
401 double t;
402
403 action = NULL;
404 ok = gFalse;
405
406 // get rectangle
407 if (!dict->lookup("Rect", &obj1)->isArray()) {
408 error(-1, "Annotation rectangle is wrong type");
409 goto err2;
410 }
411 if (!obj1.arrayGet(0, &obj2)->isNum()) {
412 error(-1, "Bad annotation rectangle");
413 goto err1;
414 }
415 x1 = obj2.getNum();
416 obj2.free();
417 if (!obj1.arrayGet(1, &obj2)->isNum()) {
418 error(-1, "Bad annotation rectangle");
419 goto err1;
420 }
421 y1 = obj2.getNum();
422 obj2.free();
423 if (!obj1.arrayGet(2, &obj2)->isNum()) {
424 error(-1, "Bad annotation rectangle");
425 goto err1;
426 }
427 x2 = obj2.getNum();
428 obj2.free();
429 if (!obj1.arrayGet(3, &obj2)->isNum()) {
430 error(-1, "Bad annotation rectangle");
431 goto err1;
432 }
433 y2 = obj2.getNum();
434 obj2.free();
435 obj1.free();
436 if (x1 > x2) {
437 t = x1;
438 x1 = x2;
439 x2 = t;
440 }
441 if (y1 > y2) {
442 t = y1;
443 y1 = y2;
444 y2 = t;
445 }
446
447 // get border
448 borderW = 0;
449 if (!dict->lookup("Border", &obj1)->isNull()) {
450 if (obj1.isArray() && obj1.arrayGetLength() >= 3) {
451 if (obj1.arrayGet(2, &obj2)->isNum()) {
452 borderW = obj2.getNum();
453 } else {
454 error(-1, "Bad annotation border");
455 }
456 obj2.free();
457 }
458 }
459 obj1.free();
460
461 // look for destination
462 if (!dict->lookup("Dest", &obj1)->isNull()) {
463 action = new LinkGoTo(&obj1);
464
465 // look for action
466 } else {
467 obj1.free();
468 if (dict->lookup("A", &obj1)->isDict()) {
469 obj1.dictLookup("S", &obj2);
470
471 // GoTo action
472 if (obj2.isName("GoTo")) {
473 obj1.dictLookup("D", &obj3);
474 action = new LinkGoTo(&obj3);
475 obj3.free();
476
477 // GoToR action
478 } else if (obj2.isName("GoToR")) {
479 obj1.dictLookup("F", &obj3);
480 obj1.dictLookup("D", &obj4);
481 action = new LinkGoToR(&obj3, &obj4);
482 obj3.free();
483 obj4.free();
484
485 // Launch action
486 } else if (obj2.isName("Launch")) {
487 action = new LinkLaunch(&obj1);
488
489 // URI action
490 } else if (obj2.isName("URI")) {
491 obj1.dictLookup("URI", &obj3);
492 action = new LinkURI(&obj3, baseURI);
493 obj3.free();
494
495 // Named action
496 } else if (obj2.isName("Named")) {
497 obj1.dictLookup("N", &obj3);
498 action = new LinkNamed(&obj3);
499 obj3.free();
500
501 // unknown action
502 } else if (obj2.isName()) {
503 action = new LinkUnknown(obj2.getName());
504
505 // action is missing or wrong type
506 } else {
507 error(-1, "Bad annotation action");
508 action = NULL;
509 }
510
511 obj2.free();
512
513 } else {
514 error(-1, "Missing annotation destination/action");
515 action = NULL;
516 }
517 }
518 obj1.free();
519
520 // check for bad action
521 if (action && action->isOk())
522 ok = gTrue;
523
524 return;
525
526 err1:
527 obj2.free();
528 err2:
529 obj1.free();
530 }
531
532 Link::~Link() {
533 if (action)
534 delete action;
535 }
536
537 //------------------------------------------------------------------------
538 // Links
539 //------------------------------------------------------------------------
540
541 Links::Links(Object *annots, GString *baseURI) {
542 Link *link;
543 Object obj1, obj2;
544 int size;
545 int i;
546
547 links = NULL;
548 size = 0;
549 numLinks = 0;
550
551 if (annots->isArray()) {
552 for (i = 0; i < annots->arrayGetLength(); ++i) {
553 if (annots->arrayGet(i, &obj1)->isDict()) {
554 if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
555 link = new Link(obj1.getDict(), baseURI);
556 if (link->isOk()) {
557 if (numLinks >= size) {
558 size += 16;
559 links = (Link **)grealloc(links, size * sizeof(Link *));
560 }
561 links[numLinks++] = link;
562 } else {
563 delete link;
564 }
565 }
566 obj2.free();
567 }
568 obj1.free();
569 }
570 }
571 }
572
573 Links::~Links() {
574 int i;
575
576 for (i = 0; i < numLinks; ++i)
577 delete links[i];
578 gfree(links);
579 }
580
581 LinkAction *Links::find(double x, double y) {
582 int i;
583
584 for (i = 0; i < numLinks; ++i) {
585 if (links[i]->inRect(x, y)) {
586 return links[i]->getAction();
587 }
588 }
589 return NULL;
590 }
591
592 GBool Links::onLink(double x, double y) {
593 int i;
594
595 for (i = 0; i < numLinks; ++i) {
596 if (links[i]->inRect(x, y))
597 return gTrue;
598 }
599 return gFalse;
600 }
601
602 //------------------------------------------------------------------------
603
604 // Extract a file name from a file specification (string or dictionary).
605 static GString *getFileSpecName(Object *fileSpecObj) {
606 GString *name;
607 Object obj1;
608
609 name = NULL;
610
611 // string
612 if (fileSpecObj->isString()) {
613 name = fileSpecObj->getString()->copy();
614
615 // dictionary
616 } else if (fileSpecObj->isDict()) {
617 if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
618 obj1.free();
619 fileSpecObj->dictLookup("F", &obj1);
620 }
621 if (obj1.isString())
622 name = obj1.getString()->copy();
623 else
624 error(-1, "Illegal file spec in link");
625 obj1.free();
626
627 // error
628 } else {
629 error(-1, "Illegal file spec in link");
630 }
631
632 return name;
633 }