1 //========================================================================
5 // Copyright 2000-2005 Emmanuel Lesueur
7 //========================================================================
10 #pragma implementation
26 #include "Annotations.h"
29 //------------------------------------------------------------------------
31 GString
*parseFileSpecName(Object
*fileSpecObj
);
34 //------------------------------------------------------------------------
36 //------------------------------------------------------------------------
38 Destination::Destination(Object
*obj
, GBool pageIsRef1
) { DEBUG_INFO
42 pageIsRef
= pageIsRef1
;
43 left
= bottom
= right
= top
= zoom
= 0;
50 name
= new GString(obj
->getName());
53 } else if (obj
->isString()) {
56 name
= obj
->getString()->copy();
59 } else if (obj
->isArray()) {
60 Array
*a
= obj
->getArray();
64 if (!a
->getNF(0, &obj1
)->isRef()) {
65 error(-1, "Bad annotation destination");
68 pageRef
.num
= obj1
.getRefNum();
69 pageRef
.gen
= obj1
.getRefGen();
72 if (!a
->get(0, &obj1
)->isInt()) {
73 error(-1, "Bad annotation destination");
76 pageNum
= obj1
.getInt() + 1;
80 // get destination type
84 if (obj1
.isName("XYZ")) {
89 } else if (obj2
.isNum()) {
93 error(-1, "Bad annotation destination position");
100 } else if (obj2
.isNum()) {
104 error(-1, "Bad annotation destination position");
111 } else if (obj2
.isNum()) {
113 zoom
= obj2
.getNum();
115 error(-1, "Bad annotation destination position");
121 } else if (obj1
.isName("Fit")) {
125 } else if (obj1
.isName("FitH")) {
127 if (!a
->get(2, &obj2
)->isNum()) {
128 error(-1, "Bad annotation destination position");
135 } else if (obj1
.isName("FitV")) {
137 if (!a
->get(2, &obj2
)->isNum()) {
138 error(-1, "Bad annotation destination position");
141 left
= obj2
.getNum();
145 } else if (obj1
.isName("FitR")) {
147 if (!a
->get(2, &obj2
)->isNum()) {
148 error(-1, "Bad annotation destination position");
151 left
= obj2
.getNum();
153 if (!a
->get(3, &obj2
)->isNum()) {
154 error(-1, "Bad annotation destination position");
157 bottom
= obj2
.getNum();
159 if (!a
->get(4, &obj2
)->isNum()) {
160 error(-1, "Bad annotation destination position");
163 right
= obj2
.getNum();
165 if (!a
->get(5, &obj2
)->isNum()) {
166 error(-1, "Bad annotation destination position");
173 } else if (obj1
.isName("FitB")) {
177 } else if (obj1
.isName("FitBH")) {
179 if (!a
->get(2, &obj2
)->isNum()) {
180 error(-1, "Bad annotation destination position");
187 } else if (obj1
.isName("FitBV")) {
189 if (!a
->get(2, &obj2
)->isNum()) {
190 error(-1, "Bad annotation destination position");
193 left
= obj2
.getNum();
198 error(-1, "Unknown annotation destination type");
211 error(-1, "Bad link destination");
214 Destination::Destination(Destination
*dest
) { DEBUG_INFO
216 if (kind
== destNamed
)
217 name
= dest
->name
->copy();
219 pageIsRef
= dest
->pageIsRef
;
221 pageRef
= dest
->pageRef
;
223 pageNum
= dest
->pageNum
;
225 bottom
= dest
->bottom
;
229 changeLeft
= dest
->changeLeft
;
230 changeTop
= dest
->changeTop
;
231 changeZoom
= dest
->changeZoom
;
236 Destination::~Destination() {
237 if (kind
== destNamed
)
242 //------------------------------------------------------------------------
244 //------------------------------------------------------------------------
246 ActionContext::~ActionContext() {
251 GString
*ActionContext::getBaseURI(){
252 return doc
->getCatalog()->getBaseURI();
255 Annot
*ActionContext::findAnnot(GString
*name
) {
256 AcroForm
*form
= getDoc()->getCatalog()->getAcroForm();
258 Field
*field
= form
->findField(name
);
260 return field
->getAnnot();
265 void ActionContext::push(Action
*action
) {
266 if (curAction
= maxAction
) {
268 actions
= (Action
**)grealloc(actions
, maxAction
* sizeof(*actions
));
270 actions
[curAction
++] = action
;
273 Action
*ActionContext::pop() {
275 return actions
[--curAction
];
281 //------------------------------------------------------------------------
283 //------------------------------------------------------------------------
285 Action::Action(Dict
*dict
) { DEBUG_INFO
292 if (!dict
->lookup("Next", &obj
)->isNull())
293 next
= makeAction(&obj
);
302 void Action::add(Action
*action
) {
303 Action
*action1
= this;
304 while (action1
->next
)
305 action1
= action1
->next
;
306 action1
->next
= action
;
309 Action
*Action::execute(ActionContext
*context
) {
310 Action
*action2
= doExecute(context
);
313 return action2
? action2
: (next
? next
: context
->pop());
316 Action
*Action::makeAction(Object
*obj1
) { DEBUG_INFO
317 Object obj2
, obj3
, obj4
, obj5
;
318 Action
*action
= NULL
;
320 if (!obj1
->isDict()) {
321 error(-1, "Invalid action");
325 obj1
->dictLookup("S", &obj2
);
328 if (obj2
.isName("GoTo")) {
329 action
= new ActionGoTo(obj1
->getDict());
332 } else if (obj2
.isName("GoToR")) {
333 action
= new ActionGoToR(obj1
->getDict());
336 } else if (obj2
.isName("Launch")) {
337 action
= new ActionLaunch(obj1
->getDict());
340 } else if (obj2
.isName("URI")) {
341 action
= new ActionURI(obj1
->getDict());
344 } else if (obj2
.isName("SetState")) {
345 action
= new ActionSetState(obj1
->getDict());
348 } else if (obj2
.isName("Hide")) {
349 action
= new ActionHide(obj1
->getDict());
352 } else if (obj2
.isName("Named")) {
353 action
= new ActionNamed(obj1
->getDict());
356 } else if (obj2
.isName("SubmitForm")) {
357 action
= new ActionSubmitForm(obj1
->getDict());
360 } else if (obj2
.isName("ResetForm")) {
361 action
= new ActionResetForm(obj1
->getDict());
364 } else if (obj2
.isName("ImportData")) {
365 action
= new ActionImportData(obj1
->getDict());
368 } else if (obj2
.isName("JavaScript")) {
369 error(-1, "JavaScript not supported.");
373 } else if (obj2
.isName()) {
374 error(-1, "Unknown action type: %s.", obj2
.getName());
376 //action = new ActionUnknown(obj2.getName());
378 // action is missing or wrong type
380 error(-1, "Bad action");
384 if (action
&& !action
->isOk()) {
394 //------------------------------------------------------------------------
396 //------------------------------------------------------------------------
398 ActionGoTo::ActionGoTo(Dict
*dict
) : Action(dict
) { DEBUG_INFO
402 if (dict
->lookup("D", &obj
))
403 dest
= new Destination(&obj
);
407 ActionGoTo::~ActionGoTo() {
411 Action
*ActionGoTo::doExecute(ActionContext
*context
) { DEBUG_INFO
413 struct PDFRectangle rect
;
416 Destination
*dest1
= dest
;
418 if (dest1
->getKind() == destNamed
) {
419 //printf("named dest: %s\n",dest1->getName()->getCString());
420 dest1
= context
->getDoc()->findDest(dest1
->getName());
422 error(-1, "Invalid destination");
426 //printf("dest kind=%d\n",dest1->getKind());
428 if (dest1
->isPageRef()) {
429 Ref ref
= dest1
->getPageRef();
430 num
= context
->getDoc()->findPage(ref
.num
, ref
.gen
);
432 num
= dest1
->getPageNum();
433 page
= context
->getDoc()->getCatalog()->getPage(num
);
435 switch (dest1
->getKind()) {
438 rect
.x1
= dest1
->getChangeLeft() ? dest1
->getLeft() : -10000;
439 rect
.y1
= dest1
->getChangeTop() ? dest1
->getTop() : -10000;
445 rect
= *page
->getMediaBox();
449 rect
.y1
= dest1
->getTop();
450 rect
.x1
= page
->getMediaBox()->x1
;
451 rect
.x2
= page
->getMediaBox()->x2
;
456 rect
.x1
= dest1
->getLeft();
457 rect
.y1
= page
->getMediaBox()->y1
;
458 rect
.y2
= page
->getMediaBox()->y2
;
463 rect
.x1
= dest1
->getLeft();
464 rect
.y1
= dest1
->getBottom();
465 rect
.x2
= dest1
->getRight();
466 rect
.y2
= dest1
->getTop();
470 rect
= *page
->getCropBox();
474 rect
.y1
= dest1
->getTop();
475 rect
.x1
= page
->getCropBox()->x1
;
476 rect
.x2
= page
->getCropBox()->x2
;
481 rect
.x1
= dest1
->getLeft();
482 rect
.y1
= page
->getCropBox()->y1
;
483 rect
.y2
= page
->getCropBox()->y2
;
494 context
->goToPage(num
, &rect
, mode
);
499 //------------------------------------------------------------------------
501 //------------------------------------------------------------------------
503 ActionGoToR::ActionGoToR(Dict
*dict
) : Action(dict
) { DEBUG_INFO
510 if (dict
->lookup("D", &obj
))
511 goTo
= new ActionGoTo(new Destination(&obj
, gFalse
));
514 if (dict
->lookup("F", &obj
))
515 fileName
= parseFileSpecName(&obj
);
518 if (dict
->lookup("NewWindow", &obj
)->isBool())
519 newWindow
= obj
.getBool();
523 ActionGoToR::~ActionGoToR() {
528 Action
*ActionGoToR::doExecute(ActionContext
*context
) {
529 context
->openDoc(fileName
, newWindow
);
534 //------------------------------------------------------------------------
536 //------------------------------------------------------------------------
538 ActionLaunch::ActionLaunch(Dict
*dict
) : Action(dict
) { DEBUG_INFO
544 if (dict
->lookup("F", &obj
))
545 file
= parseFileSpecName(&obj
);
548 if (dict
->lookup("NewWindow", &obj
)->isBool())
549 newWindow
= obj
.getBool();
553 ActionLaunch::~ActionLaunch() {
557 Action
*ActionLaunch::doExecute(ActionContext
*context
) {
558 context
->launch(file
, newWindow
);
563 //------------------------------------------------------------------------
565 //------------------------------------------------------------------------
567 ActionURI::ActionURI(Dict
*dict
) : Action(dict
) { DEBUG_INFO
573 if (dict
->lookup("URI", &obj
)->isString())
574 uri
= obj
.getString()->copy();
577 if (dict
->lookup("IsMap", &obj
)->isBool())
578 isMap
= obj
.getBool();
582 ActionURI::~ActionURI() {
586 Action
*ActionURI::doExecute(ActionContext
*context
) {
587 context
->URI(uri
, isMap
);
592 //------------------------------------------------------------------------
594 //------------------------------------------------------------------------
596 ActionSetState::ActionSetState(Dict
*dict
) : Action(dict
) { DEBUG_INFO
602 if (dict
->lookup("T", &obj
)->isString())
603 target
= obj
.getString()->copy();
606 if (dict
->lookup("AS", &obj
)->isName())
607 state
= new GString(obj
.getName());
611 ActionSetState::~ActionSetState() {
616 Action
*ActionSetState::doExecute(ActionContext
*context
) {
619 if (annot
= context
->findAnnot(target
)) {
620 annot
->setAppearanceState(state
->getCString());
621 context
->refresh(annot
);
628 //------------------------------------------------------------------------
630 //------------------------------------------------------------------------
632 ActionHide::ActionHide(Dict
*dict
) : Action(dict
) { DEBUG_INFO
638 if (dict
->lookup("T", &obj
)->isString())
639 target
= obj
.getString()->copy();
642 if (dict
->lookup("H", &obj
)->isBool())
643 hide
= obj
.getBool();
647 ActionHide::~ActionHide() {
651 Action
*ActionHide::doExecute(ActionContext
*context
) {
654 if (annot
= context
->findAnnot(target
)) {
656 context
->refresh(annot
);
657 context
->changeAnnotState(annot
);
664 //------------------------------------------------------------------------
666 //------------------------------------------------------------------------
668 ActionNamed::ActionNamed(Dict
*dict
) : Action(dict
) { DEBUG_INFO
669 dict
->lookup("N", &name
);
672 ActionNamed::~ActionNamed() {
676 Action
*ActionNamed::doExecute(ActionContext
*context
) {
677 context
->namedAction(name
.getName());
682 //------------------------------------------------------------------------
684 //------------------------------------------------------------------------
686 ActionSubmitForm::ActionSubmitForm(Dict
*dict
) : Action(dict
) { DEBUG_INFO
692 if (dict
->lookup("F", &obj
))
693 url
= parseFileSpecName(&obj
);
696 dict
->lookup("Fields", &fields
);
698 if (dict
->lookup("Flags", &obj
)->isInt())
699 flags
= obj
.getInt();
703 ActionSubmitForm::~ActionSubmitForm() {
708 Action
*ActionSubmitForm::doExecute(ActionContext
*context
) { DEBUG_INFO
709 AcroForm
*form
= context
->getDoc()->getCatalog()->getAcroForm();
710 SubmitContext
*submitContext
;
713 if (fields
.isArray()) {
714 Array
*array
= fields
.getArray();
718 if (flags
& submitExclude
&& !(flags
& submitIncludeNoValue
)) {
719 for (k
= 0; k
< form
->getNumRootFields(); ++k
)
720 form
->getRootField(k
)->setExportFlags(flags
& submitIncludeNoValue
);
722 GBool flag
= (flags
& submitExclude
) != 0;
723 for (k
= 0; k
< form
->getNumFields(); ++k
)
724 form
->getField(k
)->setExportFlag(flag
);
727 for (k
= 0; k
<array
->getLength(); ++k
) {
728 array
->getNF(k
, &obj
);
730 field
= form
->findField(obj
.getRefNum(), obj
.getRefGen());
731 else if (obj
.isString())
732 field
= form
->findField(obj
.getString());
735 error(-1, "Invalid fields array");
738 if (flags
& submitExclude
) {
739 field
->setExportFlag(gFalse
);
742 field
->setExportFlags(flags
& submitIncludeNoValue
);
749 for (k
= 0; k
< form
->getNumRootFields(); ++k
)
750 form
->getRootField(k
)->setExportFlags((flags
& submitIncludeNoValue
) != 0);
753 if (flags
& submitExportHTML
) {
754 if (flags
& submitGetMethod
) {
755 submitContext
= new SubmitHTMLGetContext
;
757 submitContext
= new SubmitHTMLPostContext("t:tmp.html"); //~
760 submitContext
= new SubmitFDFContext("t:tmp.fdf"); //~
763 for (k
= 0; k
< form
->getNumRootFields(); ++k
)
764 form
->getRootField(k
)->submit(submitContext
);
766 if (flags
& submitExportHTML
) {
768 if (flags
& submitGetMethod
) {
769 GString
*str
= url
->copy();
771 str
->append(((SubmitHTMLGetContext
*)submitContext
)->getContents());
772 context
->URI(str
, gFalse
);
775 context
->post(url
, "t:tmp.html");
778 context
->post(url
, "t:tmp.fdf");
781 delete submitContext
;
787 //------------------------------------------------------------------------
789 //------------------------------------------------------------------------
791 ActionResetForm::ActionResetForm(Dict
*dict
) : Action(dict
) { DEBUG_INFO
796 dict
->lookup("Fields", &fields
);
798 if (dict
->lookup("Flags", &obj
)->isInt())
799 flags
= obj
.getInt();
803 ActionResetForm::~ActionResetForm() {
807 Action
*ActionResetForm::doExecute(ActionContext
*context
) { DEBUG_INFO
808 AcroForm
*form
= context
->getDoc()->getCatalog()->getAcroForm();
810 if (fields
.isArray()) {
811 Array
*array
= fields
.getArray();
818 fields
= new Field
* [form
->getNumFields()];
819 for (k
= 0; k
< form
->getNumFields(); ++k
)
820 fields
[k
] = form
->getField(k
);
823 for (k
= 0; k
<array
->getLength(); ++k
) {
824 array
->getNF(k
, &obj
);
826 field
= form
->findField(obj
.getRefNum(), obj
.getRefGen());
827 else if (obj
.isString())
828 field
= form
->findField(obj
.getString());
831 error(-1, "Invalid fields array");
835 // reset all fields except those in the array.
836 for (l
= 0; l
< form
->getNumFields(); ++l
)
837 if (fields
[l
] == field
) {
842 // reset fields designated by the array, including kids.
843 field
->resetFieldsTree();
850 for (l
= 0; l
< form
->getNumFields(); ++l
)
859 for (k
= 0; k
< form
->getNumFields(); ++k
) {
860 form
->getField(k
)->reset();
868 //------------------------------------------------------------------------
870 //------------------------------------------------------------------------
872 ActionImportData::ActionImportData(Dict
*dict
) : Action(dict
) { DEBUG_INFO
877 if (dict
->lookup("F", &obj
))
878 file
= parseFileSpecName(&obj
);
882 ActionImportData::~ActionImportData() {
886 Action
*ActionImportData::doExecute(ActionContext
*context
) { DEBUG_INFO
887 AcroForm
*form
= context
->getDoc()->getCatalog()->getAcroForm();
889 if (form
&& form
->importFDF(file
->copy())) {
897 //------------------------------------------------------------------------
899 //------------------------------------------------------------------------
901 AdditionalAction::AdditionalAction(Dict
*dict
) { DEBUG_INFO
904 for (k
= 0; k
< maxTrig
; ++k
)
907 for (k
= 0; k
< maxTrig
; ++k
) {
911 key
[0] = "EXDUOCFVK" [k
];
914 if (dict
->lookup(key
, &obj
)->isDict())
915 actions
[k
] = Action::makeAction(&obj
);
921 AdditionalAction::~AdditionalAction() {
923 for (k
= 0; k
< maxTrig
; ++k
)
927 void AdditionalAction::setAction(ActionTrigger t
, Action
*action
) {
928 action
->add(actions
[t
]);
933 //------------------------------------------------------------------------
935 // Extract a file name from a file specification (string or dictionary).
936 GString
*parseFileSpecName(Object
*fileSpecObj
) { DEBUG_INFO
943 if (fileSpecObj
->isString()) {
944 name
= fileSpecObj
->getString()->copy();
947 } else if (fileSpecObj
->isDict()) {
948 if (!fileSpecObj
->dictLookup("Unix", &obj1
)->isString()) {
950 fileSpecObj
->dictLookup("F", &obj1
);
953 name
= obj1
.getString()->copy();
955 error(-1, "Illegal file spec in link");
960 error(-1, "Illegal file spec in link");