/src/xpdf-4.04/xpdf/Link.cc
Line | Count | Source (jump to first uncovered line) |
1 | | //======================================================================== |
2 | | // |
3 | | // Link.cc |
4 | | // |
5 | | // Copyright 1996-2003 Glyph & Cog, LLC |
6 | | // |
7 | | //======================================================================== |
8 | | |
9 | | #include <aconf.h> |
10 | | |
11 | | #ifdef USE_GCC_PRAGMAS |
12 | | #pragma implementation |
13 | | #endif |
14 | | |
15 | | #include <stddef.h> |
16 | | #include <string.h> |
17 | | #include "gmem.h" |
18 | | #include "gmempp.h" |
19 | | #include "GString.h" |
20 | | #include "Error.h" |
21 | | #include "Object.h" |
22 | | #include "Array.h" |
23 | | #include "Dict.h" |
24 | | #include "Link.h" |
25 | | |
26 | | //------------------------------------------------------------------------ |
27 | | // LinkAction |
28 | | //------------------------------------------------------------------------ |
29 | | |
30 | 21 | LinkAction *LinkAction::parseDest(Object *obj) { |
31 | 21 | LinkAction *action; |
32 | | |
33 | 21 | action = new LinkGoTo(obj); |
34 | 21 | if (!action->isOk()) { |
35 | 21 | delete action; |
36 | 21 | return NULL; |
37 | 21 | } |
38 | 0 | return action; |
39 | 21 | } |
40 | | |
41 | 48 | LinkAction *LinkAction::parseAction(Object *obj, GString *baseURI) { |
42 | 48 | LinkAction *action; |
43 | 48 | Object obj2, obj3, obj4, obj5; |
44 | | |
45 | 48 | if (!obj->isDict()) { |
46 | 0 | error(errSyntaxWarning, -1, "Bad annotation action"); |
47 | 0 | return NULL; |
48 | 0 | } |
49 | | |
50 | 48 | obj->dictLookup("S", &obj2); |
51 | | |
52 | | // GoTo action |
53 | 48 | if (obj2.isName("GoTo")) { |
54 | 26 | obj->dictLookup("D", &obj3); |
55 | 26 | action = new LinkGoTo(&obj3); |
56 | 26 | obj3.free(); |
57 | | |
58 | | // GoToR action |
59 | 26 | } else if (obj2.isName("GoToR")) { |
60 | 6 | obj->dictLookup("F", &obj3); |
61 | 6 | obj->dictLookup("D", &obj4); |
62 | 6 | action = new LinkGoToR(&obj3, &obj4); |
63 | 6 | obj3.free(); |
64 | 6 | obj4.free(); |
65 | | |
66 | | // Launch action |
67 | 16 | } else if (obj2.isName("Launch")) { |
68 | 0 | action = new LinkLaunch(obj); |
69 | | |
70 | | // URI action |
71 | 16 | } else if (obj2.isName("URI")) { |
72 | 0 | obj->dictLookup("URI", &obj3); |
73 | 0 | action = new LinkURI(&obj3, baseURI); |
74 | 0 | obj3.free(); |
75 | | |
76 | | // Named action |
77 | 16 | } else if (obj2.isName("Named")) { |
78 | 0 | obj->dictLookup("N", &obj3); |
79 | 0 | action = new LinkNamed(&obj3); |
80 | 0 | obj3.free(); |
81 | | |
82 | | // Movie action |
83 | 16 | } else if (obj2.isName("Movie")) { |
84 | 0 | obj->dictLookupNF("Annot", &obj3); |
85 | 0 | obj->dictLookup("T", &obj4); |
86 | 0 | action = new LinkMovie(&obj3, &obj4); |
87 | 0 | obj3.free(); |
88 | 0 | obj4.free(); |
89 | | |
90 | | // JavaScript action |
91 | 16 | } else if (obj2.isName("JavaScript")) { |
92 | 0 | obj->dictLookup("JS", &obj3); |
93 | 0 | action = new LinkJavaScript(&obj3); |
94 | 0 | obj3.free(); |
95 | | |
96 | | // SubmitForm action |
97 | 16 | } else if (obj2.isName("SubmitForm")) { |
98 | 0 | obj->dictLookup("F", &obj3); |
99 | 0 | obj->dictLookup("Fields", &obj4); |
100 | 0 | obj->dictLookup("Flags", &obj5); |
101 | 0 | action = new LinkSubmitForm(&obj3, &obj4, &obj5); |
102 | 0 | obj3.free(); |
103 | 0 | obj4.free(); |
104 | 0 | obj5.free(); |
105 | | |
106 | | // Hide action |
107 | 16 | } else if (obj2.isName("Hide")) { |
108 | 0 | obj->dictLookupNF("T", &obj3); |
109 | 0 | obj->dictLookup("H", &obj4); |
110 | 0 | action = new LinkHide(&obj3, &obj4); |
111 | 0 | obj3.free(); |
112 | 0 | obj4.free(); |
113 | | |
114 | | // unknown action |
115 | 16 | } else if (obj2.isName()) { |
116 | 6 | action = new LinkUnknown(obj2.getName()); |
117 | | |
118 | | // action is missing or wrong type |
119 | 10 | } else { |
120 | 10 | error(errSyntaxWarning, -1, "Bad annotation action"); |
121 | 10 | action = NULL; |
122 | 10 | } |
123 | | |
124 | 48 | obj2.free(); |
125 | | |
126 | 48 | if (action && !action->isOk()) { |
127 | 22 | delete action; |
128 | 22 | return NULL; |
129 | 22 | } |
130 | 26 | return action; |
131 | 48 | } |
132 | | |
133 | 6 | GString *LinkAction::getFileSpecName(Object *fileSpecObj) { |
134 | 6 | GString *name; |
135 | 6 | Object obj1; |
136 | | |
137 | 6 | name = NULL; |
138 | | |
139 | | // string |
140 | 6 | if (fileSpecObj->isString()) { |
141 | 1 | name = fileSpecObj->getString()->copy(); |
142 | | |
143 | | // dictionary |
144 | 5 | } else if (fileSpecObj->isDict()) { |
145 | | #ifdef _WIN32 |
146 | | if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) { |
147 | | #else |
148 | 0 | if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) { |
149 | 0 | #endif |
150 | 0 | obj1.free(); |
151 | 0 | fileSpecObj->dictLookup("F", &obj1); |
152 | 0 | } |
153 | 0 | if (obj1.isString()) { |
154 | 0 | name = obj1.getString()->copy(); |
155 | 0 | } else { |
156 | 0 | error(errSyntaxWarning, -1, "Illegal file spec in link"); |
157 | 0 | } |
158 | 0 | obj1.free(); |
159 | | |
160 | | // error |
161 | 5 | } else { |
162 | 5 | error(errSyntaxWarning, -1, "Illegal file spec in link"); |
163 | 5 | } |
164 | | |
165 | | // system-dependent path manipulation |
166 | 6 | if (name) { |
167 | | #ifdef _WIN32 |
168 | | int i, j; |
169 | | |
170 | | // "//...." --> "\...." |
171 | | // "/x/...." --> "x:\...." |
172 | | // "/server/share/...." --> "\\server\share\...." |
173 | | // convert escaped slashes to slashes and unescaped slashes to backslashes |
174 | | i = 0; |
175 | | if (name->getChar(0) == '/') { |
176 | | if (name->getLength() >= 2 && name->getChar(1) == '/') { |
177 | | name->del(0); |
178 | | i = 0; |
179 | | } else if (name->getLength() >= 2 && |
180 | | ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') || |
181 | | (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) && |
182 | | (name->getLength() == 2 || name->getChar(2) == '/')) { |
183 | | name->setChar(0, name->getChar(1)); |
184 | | name->setChar(1, ':'); |
185 | | i = 2; |
186 | | } else { |
187 | | for (j = 2; j < name->getLength(); ++j) { |
188 | | if (name->getChar(j-1) != '\\' && |
189 | | name->getChar(j) == '/') { |
190 | | break; |
191 | | } |
192 | | } |
193 | | if (j < name->getLength()) { |
194 | | name->setChar(0, '\\'); |
195 | | name->insert(0, '\\'); |
196 | | i = 2; |
197 | | } |
198 | | } |
199 | | } |
200 | | for (; i < name->getLength(); ++i) { |
201 | | if (name->getChar(i) == '/') { |
202 | | name->setChar(i, '\\'); |
203 | | } else if (name->getChar(i) == '\\' && |
204 | | i+1 < name->getLength() && |
205 | | name->getChar(i+1) == '/') { |
206 | | name->del(i); |
207 | | } |
208 | | } |
209 | | #else |
210 | | // no manipulation needed for Unix |
211 | 1 | #endif |
212 | 1 | } |
213 | | |
214 | 6 | return name; |
215 | 6 | } |
216 | | |
217 | | //------------------------------------------------------------------------ |
218 | | // LinkDest |
219 | | //------------------------------------------------------------------------ |
220 | | |
221 | 42 | LinkDest::LinkDest(Array *a) { |
222 | 42 | Object obj1, obj2; |
223 | | |
224 | | // initialize fields |
225 | 42 | left = bottom = right = top = zoom = 0; |
226 | 42 | ok = gFalse; |
227 | | |
228 | | // get page |
229 | 42 | if (a->getLength() < 2) { |
230 | 13 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
231 | 13 | return; |
232 | 13 | } |
233 | 29 | a->getNF(0, &obj1); |
234 | 29 | if (obj1.isInt()) { |
235 | 0 | pageNum = obj1.getInt() + 1; |
236 | 0 | pageIsRef = gFalse; |
237 | 29 | } else if (obj1.isRef()) { |
238 | 25 | pageRef.num = obj1.getRefNum(); |
239 | 25 | pageRef.gen = obj1.getRefGen(); |
240 | 25 | pageIsRef = gTrue; |
241 | 25 | } else { |
242 | 4 | error(errSyntaxWarning, -1, "Bad annotation destination"); |
243 | 4 | goto err2; |
244 | 4 | } |
245 | 25 | obj1.free(); |
246 | | |
247 | | // get destination type |
248 | 25 | a->get(1, &obj1); |
249 | | |
250 | | // XYZ link |
251 | 25 | if (obj1.isName("XYZ")) { |
252 | 9 | kind = destXYZ; |
253 | 9 | if (a->getLength() < 3) { |
254 | 0 | changeLeft = gFalse; |
255 | 9 | } else { |
256 | 9 | a->get(2, &obj2); |
257 | 9 | if (obj2.isNull()) { |
258 | 0 | changeLeft = gFalse; |
259 | 9 | } else if (obj2.isNum()) { |
260 | 4 | changeLeft = gTrue; |
261 | 4 | left = obj2.getNum(); |
262 | 5 | } else { |
263 | 5 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
264 | 5 | goto err1; |
265 | 5 | } |
266 | 4 | obj2.free(); |
267 | 4 | } |
268 | 4 | if (a->getLength() < 4) { |
269 | 0 | changeTop = gFalse; |
270 | 4 | } else { |
271 | 4 | a->get(3, &obj2); |
272 | 4 | if (obj2.isNull()) { |
273 | 0 | changeTop = gFalse; |
274 | 4 | } else if (obj2.isNum()) { |
275 | 4 | changeTop = gTrue; |
276 | 4 | top = obj2.getNum(); |
277 | 4 | } else { |
278 | 0 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
279 | 0 | goto err1; |
280 | 0 | } |
281 | 4 | obj2.free(); |
282 | 4 | } |
283 | 4 | if (a->getLength() < 5) { |
284 | 0 | changeZoom = gFalse; |
285 | 4 | } else { |
286 | 4 | a->get(4, &obj2); |
287 | 4 | if (obj2.isNull()) { |
288 | 3 | changeZoom = gFalse; |
289 | 3 | } else if (obj2.isNum()) { |
290 | 1 | changeZoom = gTrue; |
291 | 1 | zoom = obj2.getNum(); |
292 | 1 | } else { |
293 | 0 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
294 | 0 | goto err1; |
295 | 0 | } |
296 | 4 | obj2.free(); |
297 | 4 | } |
298 | | |
299 | | // Fit link |
300 | 16 | } else if (obj1.isName("Fit")) { |
301 | 0 | if (a->getLength() < 2) { |
302 | 0 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
303 | 0 | goto err2; |
304 | 0 | } |
305 | 0 | kind = destFit; |
306 | | |
307 | | // FitH link |
308 | 16 | } else if (obj1.isName("FitH")) { |
309 | 5 | if (a->getLength() < 3) { |
310 | 3 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
311 | 3 | goto err2; |
312 | 3 | } |
313 | 2 | kind = destFitH; |
314 | 2 | if (a->get(2, &obj2)->isNum()) { |
315 | 0 | top = obj2.getNum(); |
316 | 0 | changeTop = gTrue; |
317 | 2 | } else if (obj2.isNull()) { |
318 | 0 | changeTop = gFalse; |
319 | 2 | } else { |
320 | 2 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
321 | 2 | kind = destFit; |
322 | 2 | } |
323 | 2 | obj2.free(); |
324 | | |
325 | | // FitV link |
326 | 11 | } else if (obj1.isName("FitV")) { |
327 | 2 | if (a->getLength() < 3) { |
328 | 0 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
329 | 0 | goto err2; |
330 | 0 | } |
331 | 2 | kind = destFitV; |
332 | 2 | if (a->get(2, &obj2)->isNum()) { |
333 | 0 | left = obj2.getNum(); |
334 | 0 | changeLeft = gTrue; |
335 | 2 | } else if (obj2.isNull()) { |
336 | 0 | changeLeft = gFalse; |
337 | 2 | } else { |
338 | 2 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
339 | 2 | kind = destFit; |
340 | 2 | } |
341 | 2 | obj2.free(); |
342 | | |
343 | | // FitR link |
344 | 9 | } else if (obj1.isName("FitR")) { |
345 | 2 | if (a->getLength() < 6) { |
346 | 0 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
347 | 0 | goto err2; |
348 | 0 | } |
349 | 2 | kind = destFitR; |
350 | 2 | if (a->get(2, &obj2)->isNum()) { |
351 | 0 | left = obj2.getNum(); |
352 | 2 | } else { |
353 | 2 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
354 | 2 | kind = destFit; |
355 | 2 | } |
356 | 2 | obj2.free(); |
357 | 2 | if (!a->get(3, &obj2)->isNum()) { |
358 | 0 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
359 | 0 | kind = destFit; |
360 | 0 | } |
361 | 2 | bottom = obj2.getNum(); |
362 | 2 | obj2.free(); |
363 | 2 | if (!a->get(4, &obj2)->isNum()) { |
364 | 0 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
365 | 0 | kind = destFit; |
366 | 0 | } |
367 | 2 | right = obj2.getNum(); |
368 | 2 | obj2.free(); |
369 | 2 | if (!a->get(5, &obj2)->isNum()) { |
370 | 2 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
371 | 2 | kind = destFit; |
372 | 2 | } |
373 | 2 | top = obj2.getNum(); |
374 | 2 | obj2.free(); |
375 | | |
376 | | // FitB link |
377 | 7 | } else if (obj1.isName("FitB")) { |
378 | 0 | if (a->getLength() < 2) { |
379 | 0 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
380 | 0 | goto err2; |
381 | 0 | } |
382 | 0 | kind = destFitB; |
383 | | |
384 | | // FitBH link |
385 | 7 | } else if (obj1.isName("FitBH")) { |
386 | 0 | if (a->getLength() < 3) { |
387 | 0 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
388 | 0 | goto err2; |
389 | 0 | } |
390 | 0 | kind = destFitBH; |
391 | 0 | if (a->get(2, &obj2)->isNum()) { |
392 | 0 | top = obj2.getNum(); |
393 | 0 | changeTop = gTrue; |
394 | 0 | } else if (obj2.isNull()) { |
395 | 0 | changeTop = gFalse; |
396 | 0 | } else { |
397 | 0 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
398 | 0 | kind = destFit; |
399 | 0 | } |
400 | 0 | obj2.free(); |
401 | | |
402 | | // FitBV link |
403 | 7 | } else if (obj1.isName("FitBV")) { |
404 | 0 | if (a->getLength() < 3) { |
405 | 0 | error(errSyntaxWarning, -1, "Annotation destination array is too short"); |
406 | 0 | goto err2; |
407 | 0 | } |
408 | 0 | kind = destFitBV; |
409 | 0 | if (a->get(2, &obj2)->isNum()) { |
410 | 0 | left = obj2.getNum(); |
411 | 0 | changeLeft = gTrue; |
412 | 0 | } else if (obj2.isNull()) { |
413 | 0 | changeLeft = gFalse; |
414 | 0 | } else { |
415 | 0 | error(errSyntaxWarning, -1, "Bad annotation destination position"); |
416 | 0 | kind = destFit; |
417 | 0 | } |
418 | 0 | obj2.free(); |
419 | | |
420 | | // unknown link kind |
421 | 7 | } else { |
422 | 7 | error(errSyntaxWarning, -1, "Unknown annotation destination type"); |
423 | 7 | goto err2; |
424 | 7 | } |
425 | | |
426 | 10 | obj1.free(); |
427 | 10 | ok = gTrue; |
428 | 10 | return; |
429 | | |
430 | 5 | err1: |
431 | 5 | obj2.free(); |
432 | 19 | err2: |
433 | 19 | obj1.free(); |
434 | 19 | } |
435 | | |
436 | 0 | LinkDest::LinkDest(LinkDest *dest) { |
437 | 0 | kind = dest->kind; |
438 | 0 | pageIsRef = dest->pageIsRef; |
439 | 0 | if (pageIsRef) |
440 | 0 | pageRef = dest->pageRef; |
441 | 0 | else |
442 | 0 | pageNum = dest->pageNum; |
443 | 0 | left = dest->left; |
444 | 0 | bottom = dest->bottom; |
445 | 0 | right = dest->right; |
446 | 0 | top = dest->top; |
447 | 0 | zoom = dest->zoom; |
448 | 0 | changeLeft = dest->changeLeft; |
449 | 0 | changeTop = dest->changeTop; |
450 | 0 | changeZoom = dest->changeZoom; |
451 | 0 | ok = gTrue; |
452 | 0 | } |
453 | | |
454 | | //------------------------------------------------------------------------ |
455 | | // LinkGoTo |
456 | | //------------------------------------------------------------------------ |
457 | | |
458 | 47 | LinkGoTo::LinkGoTo(Object *destObj) { |
459 | 47 | dest = NULL; |
460 | 47 | namedDest = NULL; |
461 | | |
462 | | // named destination |
463 | 47 | if (destObj->isName()) { |
464 | 0 | namedDest = new GString(destObj->getName()); |
465 | 47 | } else if (destObj->isString()) { |
466 | 0 | namedDest = destObj->getString()->copy(); |
467 | | |
468 | | // destination dictionary |
469 | 47 | } else if (destObj->isArray()) { |
470 | 41 | dest = new LinkDest(destObj->getArray()); |
471 | 41 | if (!dest->isOk()) { |
472 | 31 | delete dest; |
473 | 31 | dest = NULL; |
474 | 31 | } |
475 | | |
476 | | // error |
477 | 41 | } else { |
478 | 6 | error(errSyntaxWarning, -1, "Illegal annotation destination"); |
479 | 6 | } |
480 | 47 | } |
481 | | |
482 | 47 | LinkGoTo::~LinkGoTo() { |
483 | 47 | if (dest) |
484 | 10 | delete dest; |
485 | 47 | if (namedDest) |
486 | 0 | delete namedDest; |
487 | 47 | } |
488 | | |
489 | | //------------------------------------------------------------------------ |
490 | | // LinkGoToR |
491 | | //------------------------------------------------------------------------ |
492 | | |
493 | 6 | LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) { |
494 | 6 | dest = NULL; |
495 | 6 | namedDest = NULL; |
496 | | |
497 | | // get file name |
498 | 6 | fileName = getFileSpecName(fileSpecObj); |
499 | | |
500 | | // named destination |
501 | 6 | if (destObj->isName()) { |
502 | 0 | namedDest = new GString(destObj->getName()); |
503 | 6 | } else if (destObj->isString()) { |
504 | 0 | namedDest = destObj->getString()->copy(); |
505 | | |
506 | | // destination dictionary |
507 | 6 | } else if (destObj->isArray()) { |
508 | 1 | dest = new LinkDest(destObj->getArray()); |
509 | 1 | if (!dest->isOk()) { |
510 | 1 | delete dest; |
511 | 1 | dest = NULL; |
512 | 1 | } |
513 | | |
514 | | // error |
515 | 5 | } else { |
516 | 5 | error(errSyntaxWarning, -1, "Illegal annotation destination"); |
517 | 5 | } |
518 | 6 | } |
519 | | |
520 | 6 | LinkGoToR::~LinkGoToR() { |
521 | 6 | if (fileName) |
522 | 1 | delete fileName; |
523 | 6 | if (dest) |
524 | 0 | delete dest; |
525 | 6 | if (namedDest) |
526 | 0 | delete namedDest; |
527 | 6 | } |
528 | | |
529 | | |
530 | | //------------------------------------------------------------------------ |
531 | | // LinkLaunch |
532 | | //------------------------------------------------------------------------ |
533 | | |
534 | 0 | LinkLaunch::LinkLaunch(Object *actionObj) { |
535 | 0 | Object obj1, obj2; |
536 | |
|
537 | 0 | fileName = NULL; |
538 | 0 | params = NULL; |
539 | |
|
540 | 0 | if (actionObj->isDict()) { |
541 | 0 | if (!actionObj->dictLookup("F", &obj1)->isNull()) { |
542 | 0 | fileName = getFileSpecName(&obj1); |
543 | 0 | } else { |
544 | 0 | obj1.free(); |
545 | | #ifdef _WIN32 |
546 | | if (actionObj->dictLookup("Win", &obj1)->isDict()) { |
547 | | obj1.dictLookup("F", &obj2); |
548 | | fileName = getFileSpecName(&obj2); |
549 | | obj2.free(); |
550 | | if (obj1.dictLookup("P", &obj2)->isString()) { |
551 | | params = obj2.getString()->copy(); |
552 | | } |
553 | | obj2.free(); |
554 | | } else { |
555 | | error(errSyntaxWarning, -1, "Bad launch-type link action"); |
556 | | } |
557 | | #else |
558 | | //~ This hasn't been defined by Adobe yet, so assume it looks |
559 | | //~ just like the Win dictionary until they say otherwise. |
560 | 0 | if (actionObj->dictLookup("Unix", &obj1)->isDict()) { |
561 | 0 | obj1.dictLookup("F", &obj2); |
562 | 0 | fileName = getFileSpecName(&obj2); |
563 | 0 | obj2.free(); |
564 | 0 | if (obj1.dictLookup("P", &obj2)->isString()) { |
565 | 0 | params = obj2.getString()->copy(); |
566 | 0 | } |
567 | 0 | obj2.free(); |
568 | 0 | } else { |
569 | 0 | error(errSyntaxWarning, -1, "Bad launch-type link action"); |
570 | 0 | } |
571 | 0 | #endif |
572 | 0 | } |
573 | 0 | obj1.free(); |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | 0 | LinkLaunch::~LinkLaunch() { |
578 | 0 | if (fileName) |
579 | 0 | delete fileName; |
580 | 0 | if (params) |
581 | 0 | delete params; |
582 | 0 | } |
583 | | |
584 | | //------------------------------------------------------------------------ |
585 | | // LinkURI |
586 | | //------------------------------------------------------------------------ |
587 | | |
588 | 0 | LinkURI::LinkURI(Object *uriObj, GString *baseURI) { |
589 | 0 | GString *uri2; |
590 | 0 | int n; |
591 | 0 | char c; |
592 | |
|
593 | 0 | uri = NULL; |
594 | 0 | if (uriObj->isString()) { |
595 | 0 | uri2 = uriObj->getString(); |
596 | 0 | n = (int)strcspn(uri2->getCString(), "/:"); |
597 | 0 | if (n < uri2->getLength() && uri2->getChar(n) == ':') { |
598 | | // "http:..." etc. |
599 | 0 | uri = uri2->copy(); |
600 | 0 | } else if (!uri2->cmpN("www.", 4)) { |
601 | | // "www.[...]" without the leading "http://" |
602 | 0 | uri = new GString("http://"); |
603 | 0 | uri->append(uri2); |
604 | 0 | } else { |
605 | | // relative URI |
606 | 0 | if (baseURI) { |
607 | 0 | uri = baseURI->copy(); |
608 | 0 | c = uri->getChar(uri->getLength() - 1); |
609 | 0 | if (c != '/' && c != '?') { |
610 | 0 | uri->append('/'); |
611 | 0 | } |
612 | 0 | if (uri2->getChar(0) == '/') { |
613 | 0 | uri->append(uri2->getCString() + 1, uri2->getLength() - 1); |
614 | 0 | } else { |
615 | 0 | uri->append(uri2); |
616 | 0 | } |
617 | 0 | } else { |
618 | 0 | uri = uri2->copy(); |
619 | 0 | } |
620 | 0 | } |
621 | 0 | } else { |
622 | 0 | error(errSyntaxWarning, -1, "Illegal URI-type link"); |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | 0 | LinkURI::~LinkURI() { |
627 | 0 | if (uri) |
628 | 0 | delete uri; |
629 | 0 | } |
630 | | |
631 | | //------------------------------------------------------------------------ |
632 | | // LinkNamed |
633 | | //------------------------------------------------------------------------ |
634 | | |
635 | 0 | LinkNamed::LinkNamed(Object *nameObj) { |
636 | 0 | name = NULL; |
637 | 0 | if (nameObj->isName()) { |
638 | 0 | name = new GString(nameObj->getName()); |
639 | 0 | } |
640 | 0 | } |
641 | | |
642 | 0 | LinkNamed::~LinkNamed() { |
643 | 0 | if (name) { |
644 | 0 | delete name; |
645 | 0 | } |
646 | 0 | } |
647 | | |
648 | | //------------------------------------------------------------------------ |
649 | | // LinkMovie |
650 | | //------------------------------------------------------------------------ |
651 | | |
652 | 0 | LinkMovie::LinkMovie(Object *annotObj, Object *titleObj) { |
653 | 0 | annotRef.num = -1; |
654 | 0 | title = NULL; |
655 | 0 | if (annotObj->isRef()) { |
656 | 0 | annotRef = annotObj->getRef(); |
657 | 0 | } else if (titleObj->isString()) { |
658 | 0 | title = titleObj->getString()->copy(); |
659 | 0 | } else { |
660 | 0 | error(errSyntaxError, -1, |
661 | 0 | "Movie action is missing both the Annot and T keys"); |
662 | 0 | } |
663 | 0 | } |
664 | | |
665 | 0 | LinkMovie::~LinkMovie() { |
666 | 0 | if (title) { |
667 | 0 | delete title; |
668 | 0 | } |
669 | 0 | } |
670 | | |
671 | | //------------------------------------------------------------------------ |
672 | | // LinkJavaScript |
673 | | //------------------------------------------------------------------------ |
674 | | |
675 | 0 | LinkJavaScript::LinkJavaScript(Object *jsObj) { |
676 | 0 | char buf[4096]; |
677 | 0 | int n; |
678 | |
|
679 | 0 | if (jsObj->isString()) { |
680 | 0 | js = jsObj->getString()->copy(); |
681 | 0 | } else if (jsObj->isStream()) { |
682 | 0 | js = new GString(); |
683 | 0 | jsObj->streamReset(); |
684 | 0 | while ((n = jsObj->getStream()->getBlock(buf, sizeof(buf))) > 0) { |
685 | 0 | js->append(buf, n); |
686 | 0 | } |
687 | 0 | jsObj->streamClose(); |
688 | 0 | } else { |
689 | 0 | error(errSyntaxError, -1, "JavaScript action JS key is wrong type"); |
690 | 0 | js = NULL; |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | 0 | LinkJavaScript::~LinkJavaScript() { |
695 | 0 | if (js) { |
696 | 0 | delete js; |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | //------------------------------------------------------------------------ |
701 | | // LinkSubmitForm |
702 | | //------------------------------------------------------------------------ |
703 | | |
704 | | LinkSubmitForm::LinkSubmitForm(Object *urlObj, Object *fieldsObj, |
705 | 0 | Object *flagsObj) { |
706 | 0 | if (urlObj->isString()) { |
707 | 0 | url = urlObj->getString()->copy(); |
708 | 0 | } else { |
709 | 0 | error(errSyntaxError, -1, "SubmitForm action URL is wrong type"); |
710 | 0 | url = NULL; |
711 | 0 | } |
712 | |
|
713 | 0 | if (fieldsObj->isArray()) { |
714 | 0 | fieldsObj->copy(&fields); |
715 | 0 | } else { |
716 | 0 | if (!fieldsObj->isNull()) { |
717 | 0 | error(errSyntaxError, -1, "SubmitForm action Fields value is wrong type"); |
718 | 0 | } |
719 | 0 | fields.initNull(); |
720 | 0 | } |
721 | |
|
722 | 0 | if (flagsObj->isInt()) { |
723 | 0 | flags = flagsObj->getInt(); |
724 | 0 | } else { |
725 | 0 | if (!flagsObj->isNull()) { |
726 | 0 | error(errSyntaxError, -1, "SubmitForm action Flags value is wrong type"); |
727 | 0 | } |
728 | 0 | flags = 0; |
729 | 0 | } |
730 | 0 | } |
731 | | |
732 | 0 | LinkSubmitForm::~LinkSubmitForm() { |
733 | 0 | if (url) { |
734 | 0 | delete url; |
735 | 0 | } |
736 | 0 | fields.free(); |
737 | 0 | } |
738 | | |
739 | | //------------------------------------------------------------------------ |
740 | | // LinkHide |
741 | | //------------------------------------------------------------------------ |
742 | | |
743 | 0 | LinkHide::LinkHide(Object *fieldsObj, Object *hideFlagObj) { |
744 | 0 | if (fieldsObj->isRef() || fieldsObj->isString() || fieldsObj->isArray()) { |
745 | 0 | fieldsObj->copy(&fields); |
746 | 0 | } else { |
747 | 0 | error(errSyntaxError, -1, "Hide action T value is wrong type"); |
748 | 0 | fields.initNull(); |
749 | 0 | } |
750 | |
|
751 | 0 | if (hideFlagObj->isBool()) { |
752 | 0 | hideFlag = hideFlagObj->getBool(); |
753 | 0 | } else { |
754 | 0 | error(errSyntaxError, -1, "Hide action H value is wrong type"); |
755 | 0 | hideFlag = gFalse; |
756 | 0 | } |
757 | 0 | } |
758 | | |
759 | 0 | LinkHide::~LinkHide() { |
760 | 0 | fields.free(); |
761 | 0 | } |
762 | | |
763 | | //------------------------------------------------------------------------ |
764 | | // LinkUnknown |
765 | | //------------------------------------------------------------------------ |
766 | | |
767 | 6 | LinkUnknown::LinkUnknown(char *actionA) { |
768 | 6 | action = new GString(actionA); |
769 | 6 | } |
770 | | |
771 | 6 | LinkUnknown::~LinkUnknown() { |
772 | 6 | delete action; |
773 | 6 | } |
774 | | |
775 | | //------------------------------------------------------------------------ |
776 | | // Link |
777 | | //------------------------------------------------------------------------ |
778 | | |
779 | 0 | Link::Link(Dict *dict, GString *baseURI) { |
780 | 0 | Object obj1, obj2; |
781 | 0 | double t; |
782 | |
|
783 | 0 | action = NULL; |
784 | 0 | ok = gFalse; |
785 | | |
786 | | // get rectangle |
787 | 0 | if (!dict->lookup("Rect", &obj1)->isArray()) { |
788 | 0 | error(errSyntaxError, -1, "Annotation rectangle is wrong type"); |
789 | 0 | goto err2; |
790 | 0 | } |
791 | 0 | if (!obj1.arrayGet(0, &obj2)->isNum()) { |
792 | 0 | error(errSyntaxError, -1, "Bad annotation rectangle"); |
793 | 0 | goto err1; |
794 | 0 | } |
795 | 0 | x1 = obj2.getNum(); |
796 | 0 | obj2.free(); |
797 | 0 | if (!obj1.arrayGet(1, &obj2)->isNum()) { |
798 | 0 | error(errSyntaxError, -1, "Bad annotation rectangle"); |
799 | 0 | goto err1; |
800 | 0 | } |
801 | 0 | y1 = obj2.getNum(); |
802 | 0 | obj2.free(); |
803 | 0 | if (!obj1.arrayGet(2, &obj2)->isNum()) { |
804 | 0 | error(errSyntaxError, -1, "Bad annotation rectangle"); |
805 | 0 | goto err1; |
806 | 0 | } |
807 | 0 | x2 = obj2.getNum(); |
808 | 0 | obj2.free(); |
809 | 0 | if (!obj1.arrayGet(3, &obj2)->isNum()) { |
810 | 0 | error(errSyntaxError, -1, "Bad annotation rectangle"); |
811 | 0 | goto err1; |
812 | 0 | } |
813 | 0 | y2 = obj2.getNum(); |
814 | 0 | obj2.free(); |
815 | 0 | obj1.free(); |
816 | 0 | if (x1 > x2) { |
817 | 0 | t = x1; |
818 | 0 | x1 = x2; |
819 | 0 | x2 = t; |
820 | 0 | } |
821 | 0 | if (y1 > y2) { |
822 | 0 | t = y1; |
823 | 0 | y1 = y2; |
824 | 0 | y2 = t; |
825 | 0 | } |
826 | | |
827 | | // look for destination |
828 | 0 | if (!dict->lookup("Dest", &obj1)->isNull()) { |
829 | 0 | action = LinkAction::parseDest(&obj1); |
830 | | |
831 | | // look for action |
832 | 0 | } else { |
833 | 0 | obj1.free(); |
834 | 0 | if (dict->lookup("A", &obj1)->isDict()) { |
835 | 0 | action = LinkAction::parseAction(&obj1, baseURI); |
836 | 0 | } |
837 | 0 | } |
838 | 0 | obj1.free(); |
839 | | |
840 | | // check for bad action |
841 | 0 | if (action) { |
842 | 0 | ok = gTrue; |
843 | 0 | } |
844 | |
|
845 | 0 | return; |
846 | | |
847 | 0 | err1: |
848 | 0 | obj2.free(); |
849 | 0 | err2: |
850 | 0 | obj1.free(); |
851 | 0 | } |
852 | | |
853 | 0 | Link::~Link() { |
854 | 0 | if (action) { |
855 | 0 | delete action; |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | | //------------------------------------------------------------------------ |
860 | | // Links |
861 | | //------------------------------------------------------------------------ |
862 | | |
863 | 0 | Links::Links(Object *annots, GString *baseURI) { |
864 | 0 | Link *link; |
865 | 0 | Object obj1, obj2, obj3; |
866 | 0 | int size; |
867 | 0 | int i; |
868 | |
|
869 | 0 | links = NULL; |
870 | 0 | size = 0; |
871 | 0 | numLinks = 0; |
872 | |
|
873 | 0 | if (annots->isArray()) { |
874 | 0 | for (i = 0; i < annots->arrayGetLength(); ++i) { |
875 | 0 | if (annots->arrayGet(i, &obj1)->isDict()) { |
876 | 0 | obj1.dictLookup("Subtype", &obj2); |
877 | 0 | obj1.dictLookup("FT", &obj3); |
878 | 0 | if (obj2.isName("Link") || |
879 | 0 | (obj2.isName("Widget") && (obj3.isName("Btn") || obj3.isNull()))) { |
880 | 0 | link = new Link(obj1.getDict(), baseURI); |
881 | 0 | if (link->isOk()) { |
882 | 0 | if (numLinks >= size) { |
883 | 0 | size += 16; |
884 | 0 | links = (Link **)greallocn(links, size, sizeof(Link *)); |
885 | 0 | } |
886 | 0 | links[numLinks++] = link; |
887 | 0 | } else { |
888 | 0 | delete link; |
889 | 0 | } |
890 | 0 | } |
891 | 0 | obj3.free(); |
892 | 0 | obj2.free(); |
893 | 0 | } |
894 | 0 | obj1.free(); |
895 | 0 | } |
896 | 0 | } |
897 | 0 | } |
898 | | |
899 | 0 | Links::~Links() { |
900 | 0 | int i; |
901 | |
|
902 | 0 | for (i = 0; i < numLinks; ++i) |
903 | 0 | delete links[i]; |
904 | 0 | gfree(links); |
905 | 0 | } |
906 | | |
907 | 0 | LinkAction *Links::find(double x, double y) { |
908 | 0 | int i; |
909 | |
|
910 | 0 | for (i = numLinks - 1; i >= 0; --i) { |
911 | 0 | if (links[i]->inRect(x, y)) { |
912 | 0 | return links[i]->getAction(); |
913 | 0 | } |
914 | 0 | } |
915 | 0 | return NULL; |
916 | 0 | } |
917 | | |
918 | 0 | GBool Links::onLink(double x, double y) { |
919 | 0 | int i; |
920 | |
|
921 | 0 | for (i = 0; i < numLinks; ++i) { |
922 | 0 | if (links[i]->inRect(x, y)) |
923 | 0 | return gTrue; |
924 | 0 | } |
925 | 0 | return gFalse; |
926 | 0 | } |