1 /*
2  * $Id: PdfAction.java 3912 2009-04-26 08:38:15Z blowagie $
3  *
4  * Copyright 2000 by Bruno Lowagie.
5  *
6  * The contents of this file are subject to the Mozilla Public License Version 1.1
7  * (the "License"); you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the License.
13  *
14  * The Original Code is 'iText, a free JAVA-PDF library'.
15  *
16  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18  * All Rights Reserved.
19  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21  *
22  * Contributor(s): all the names of the contributors are added in the source code
23  * where applicable.
24  *
25  * Alternatively, the contents of this file may be used under the terms of the
26  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27  * provisions of LGPL are applicable instead of those above.  If you wish to
28  * allow use of your version of this file only under the terms of the LGPL
29  * License and not to allow others to use your version of this file under
30  * the MPL, indicate your decision by deleting the provisions above and
31  * replace them with the notice and other provisions required by the LGPL.
32  * If you do not delete the provisions above, a recipient may use your version
33  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the MPL as stated above or under the terms of the GNU
37  * Library General Public License as published by the Free Software Foundation;
38  * either version 2 of the License, or any later version.
39  *
40  * This library is distributed in the hope that it will be useful, but WITHOUT
41  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43  * details.
44  *
45  * If you didn't download this code from the following link, you should check if
46  * you aren't using an obsolete version:
47  * http://www.lowagie.com/iText/
48  */

49
50 package com.lowagie.text.pdf;
51
52 import java.io.IOException;
53 import java.net.URL;
54 import java.util.ArrayList;
55
56 import com.lowagie.text.pdf.collection.PdfTargetDictionary;
57
58 /**
59  * A <CODE>PdfAction</CODE> defines an action that can be triggered from a PDF file.
60  *
61  * @see        PdfDictionary
62  */

63
64 public class PdfAction extends PdfDictionary {
65     
66     /** A named action to go to the first page.
67      */

68     public static final int FIRSTPAGE = 1;
69     /** A named action to go to the previous page.
70      */

71     public static final int PREVPAGE = 2;
72     /** A named action to go to the next page.
73      */

74     public static final int NEXTPAGE = 3;
75     /** A named action to go to the last page.
76      */

77     public static final int LASTPAGE = 4;
78
79     /** A named action to open a print dialog.
80      */

81     public static final int PRINTDIALOG = 5;
82
83     /** a possible submitvalue */
84     public static final int SUBMIT_EXCLUDE = 1;
85     /** a possible submitvalue */
86     public static final int SUBMIT_INCLUDE_NO_VALUE_FIELDS = 2;
87     /** a possible submitvalue */
88     public static final int SUBMIT_HTML_FORMAT = 4;
89     /** a possible submitvalue */
90     public static final int SUBMIT_HTML_GET = 8;
91     /** a possible submitvalue */
92     public static final int SUBMIT_COORDINATES = 16;
93     /** a possible submitvalue */
94     public static final int SUBMIT_XFDF = 32;
95     /** a possible submitvalue */
96     public static final int SUBMIT_INCLUDE_APPEND_SAVES = 64;
97     /** a possible submitvalue */
98     public static final int SUBMIT_INCLUDE_ANNOTATIONS = 128;
99     /** a possible submitvalue */
100     public static final int SUBMIT_PDF = 256;
101     /** a possible submitvalue */
102     public static final int SUBMIT_CANONICAL_FORMAT = 512;
103     /** a possible submitvalue */
104     public static final int SUBMIT_EXCL_NON_USER_ANNOTS = 1024;
105     /** a possible submitvalue */
106     public static final int SUBMIT_EXCL_F_KEY = 2048;
107     /** a possible submitvalue */
108     public static final int SUBMIT_EMBED_FORM = 8196;
109     /** a possible submitvalue */
110     public static final int RESET_EXCLUDE = 1;
111
112     // constructors
113     
114     /** Create an empty action.
115      */
    
116     public PdfAction() {
117     }
118     
119     /**
120      * Constructs a new <CODE>PdfAction</CODE> of Subtype URI.
121      *
122      * @param url the Url to go to
123      */

124     
125     public PdfAction(URL url) {
126         this(url.toExternalForm());
127     }
128     
129     /**
130      * Construct a new <CODE>PdfAction</CODE> of Subtype URI that accepts the x and y coordinate of the position that was clicked.
131      * @param url
132      * @param isMap
133      */

134     public PdfAction(URL url, boolean isMap) {
135         this(url.toExternalForm(), isMap);
136     }
137     
138     /**
139      * Constructs a new <CODE>PdfAction</CODE> of Subtype URI.
140      *
141      * @param url the url to go to
142      */

143     
144     public PdfAction(String url) {
145         this(url, false);
146     }
147     
148     /**
149      * Construct a new <CODE>PdfAction</CODE> of Subtype URI that accepts the x and y coordinate of the position that was clicked.
150      * @param url
151      * @param isMap
152      */

153     
154     public PdfAction(String url, boolean isMap) {
155         put(PdfName.S, PdfName.URI);
156         put(PdfName.URI, new PdfString(url));
157         if (isMap)
158             put(PdfName.ISMAP, PdfBoolean.PDFTRUE);
159     }
160     
161     /**
162      * Constructs a new <CODE>PdfAction</CODE> of Subtype GoTo.
163      * @param destination the destination to go to
164      */

165     
166     PdfAction(PdfIndirectReference destination) {
167         put(PdfName.S, PdfName.GOTO);
168         put(PdfName.D, destination);
169     }
170     
171     /**
172      * Constructs a new <CODE>PdfAction</CODE> of Subtype GoToR.
173      * @param filename the file name to go to
174      * @param name the named destination to go to
175      */

176     
177     public PdfAction(String filename, String name) {
178         put(PdfName.S, PdfName.GOTOR);
179         put(PdfName.F, new PdfString(filename));
180         put(PdfName.D, new PdfString(name));
181     }
182     
183     /**
184      * Constructs a new <CODE>PdfAction</CODE> of Subtype GoToR.
185      * @param filename the file name to go to
186      * @param page the page destination to go to
187      */

188     
189     public PdfAction(String filename, int page) {
190         put(PdfName.S, PdfName.GOTOR);
191         put(PdfName.F, new PdfString(filename));
192         put(PdfName.D, new PdfLiteral("[" + (page - 1) + " /FitH 10000]"));
193     }
194     
195     /** Implements name actions. The action can be FIRSTPAGE, LASTPAGE,
196      * NEXTPAGE, PREVPAGE and PRINTDIALOG.
197      * @param named the named action
198      */

199     public PdfAction(int named) {
200         put(PdfName.S, PdfName.NAMED);
201         switch (named) {
202             case FIRSTPAGE:
203                 put(PdfName.N, PdfName.FIRSTPAGE);
204                 break;
205             case LASTPAGE:
206                 put(PdfName.N, PdfName.LASTPAGE);
207                 break;
208             case NEXTPAGE:
209                 put(PdfName.N, PdfName.NEXTPAGE);
210                 break;
211             case PREVPAGE:
212                 put(PdfName.N, PdfName.PREVPAGE);
213                 break;
214             case PRINTDIALOG:
215                 put(PdfName.S, PdfName.JAVASCRIPT);
216                 put(PdfName.JS, new PdfString("this.print(true);\r"));
217                 break;
218             default:
219                 throw new RuntimeException("Invalid named action.");
220         }
221     }
222     
223     /** Launches an application or a document.
224      * @param application the application to be launched or the document to be opened or printed.
225      * @param parameters (Windows-specific) A parameter string to be passed to the application.
226      * It can be <CODE>null</CODE>.
227      * @param operation (Windows-specific) the operation to perform: "open" - Open a document,
228      * "print" - Print a document.
229      * It can be <CODE>null</CODE>.
230      * @param defaultDir (Windows-specific) the default directory in standard DOS syntax.
231      * It can be <CODE>null</CODE>.
232      */

233     public PdfAction(String application, String parameters, String operation, String defaultDir) {
234         put(PdfName.S, PdfName.LAUNCH);
235         if (parameters == null && operation == null && defaultDir == null)
236             put(PdfName.F, new PdfString(application));
237         else {
238             PdfDictionary dic = new PdfDictionary();
239             dic.put(PdfName.F, new PdfString(application));
240             if (parameters != null)
241                 dic.put(PdfName.P, new PdfString(parameters));
242             if (operation != null)
243                 dic.put(PdfName.O, new PdfString(operation));
244             if (defaultDir != null)
245                 dic.put(PdfName.D, new PdfString(defaultDir));
246             put(PdfName.WIN, dic);
247         }
248     }
249     
250     /** Launches an application or a document.
251      * @param application the application to be launched or the document to be opened or printed.
252      * @param parameters (Windows-specific) A parameter string to be passed to the application.
253      * It can be <CODE>null</CODE>.
254      * @param operation (Windows-specific) the operation to perform: "open" - Open a document,
255      * "print" - Print a document.
256      * It can be <CODE>null</CODE>.
257      * @param defaultDir (Windows-specific) the default directory in standard DOS syntax.
258      * It can be <CODE>null</CODE>.
259      * @return a Launch action
260      */

261     public static PdfAction createLaunch(String application, String parameters, String operation, String defaultDir) {
262         return new PdfAction(application, parameters, operation, defaultDir);
263     }
264     
265      /**Creates a Rendition action
266      * @param file
267      * @param fs
268      * @param mimeType
269      * @param ref
270      * @return a Media Clip action
271      * @throws IOException
272      */

273     public static PdfAction rendition(String file, PdfFileSpecification fs, String mimeType, PdfIndirectReference ref) throws IOException {
274         PdfAction js = new PdfAction();
275         js.put(PdfName.S, PdfName.RENDITION);
276         js.put(PdfName.R, new PdfRendition(file, fs, mimeType));
277         js.put(new PdfName("OP"), new PdfNumber(0));
278         js.put(new PdfName("AN"), ref);
279         return js;
280      }
281   
282     /** Creates a JavaScript action. If the JavaScript is smaller than
283      * 50 characters it will be placed as a string, otherwise it will
284      * be placed as a compressed stream.
285      * @param code the JavaScript code
286      * @param writer the writer for this action
287      * @param unicode select JavaScript unicode. Note that the internal
288      * Acrobat JavaScript engine does not support unicode,
289      * so this may or may not work for you
290      * @return the JavaScript action
291      */
    
292     public static PdfAction javaScript(String code, PdfWriter writer, boolean unicode) {
293         PdfAction js = new PdfAction();
294         js.put(PdfName.S, PdfName.JAVASCRIPT);
295         if (unicode && code.length() < 50) {
296                 js.put(PdfName.JS, new PdfString(code, PdfObject.TEXT_UNICODE));
297         }
298         else if (!unicode && code.length() < 100) {
299                 js.put(PdfName.JS, new PdfString(code));
300         }
301         else {
302             try {
303                 byte b[] = PdfEncodings.convertToBytes(code, unicode ? PdfObject.TEXT_UNICODE : PdfObject.TEXT_PDFDOCENCODING);
304                 PdfStream stream = new PdfStream(b);
305                 stream.flateCompress(writer.getCompressionLevel());
306                 js.put(PdfName.JS, writer.addToBody(stream).getIndirectReference());
307             }
308             catch (Exception e) {
309                 js.put(PdfName.JS, new PdfString(code));
310             }
311         }
312         return js;
313     }
314
315     /** Creates a JavaScript action. If the JavaScript is smaller than
316      * 50 characters it will be place as a string, otherwise it will
317      * be placed as a compressed stream.
318      * @param code the JavaScript code
319      * @param writer the writer for this action
320      * @return the JavaScript action
321      */
    
322     public static PdfAction javaScript(String code, PdfWriter writer) {
323         return javaScript(code, writer, false);
324     }
325     
326     /**
327      * A Hide action hides or shows an object.
328      * @param obj object to hide or show
329      * @param hide true is hide, false is show
330      * @return a Hide Action
331      */

332     static PdfAction createHide(PdfObject obj, boolean hide) {
333         PdfAction action = new PdfAction();
334         action.put(PdfName.S, PdfName.HIDE);
335         action.put(PdfName.T, obj);
336         if (!hide)
337             action.put(PdfName.H, PdfBoolean.PDFFALSE);
338         return action;
339     }
340     
341     /**
342      * A Hide action hides or shows an annotation.
343      * @param annot
344      * @param hide
345      * @return A Hide Action
346      */

347     public static PdfAction createHide(PdfAnnotation annot, boolean hide) {
348         return createHide(annot.getIndirectReference(), hide);
349     }
350     
351     /**
352      * A Hide action hides or shows an annotation.
353      * @param name
354      * @param hide
355      * @return A Hide Action
356      */

357     public static PdfAction createHide(String name, boolean hide) {
358         return createHide(new PdfString(name), hide);
359     }
360     
361     static PdfArray buildArray(Object names[]) {
362         PdfArray array = new PdfArray();
363         for (int k = 0; k < names.length; ++k) {
364             Object obj = names[k];
365             if (obj instanceof String)
366                 array.add(new PdfString((String)obj));
367             else if (obj instanceof PdfAnnotation)
368                 array.add(((PdfAnnotation)obj).getIndirectReference());
369             else
370                 throw new RuntimeException("The array must contain String or PdfAnnotation.");
371         }
372         return array;
373     }
374     
375     /**
376      * A Hide action hides or shows objects.
377      * @param names
378      * @param hide
379      * @return A Hide Action
380      */

381     public static PdfAction createHide(Object names[], boolean hide) {
382         return createHide(buildArray(names), hide);
383     }
384     
385     /**
386      * Creates a submit form.
387      * @param file    the URI to submit the form to
388      * @param names    the objects to submit
389      * @param flags    submit properties
390      * @return A PdfAction
391      */

392     public static PdfAction createSubmitForm(String file, Object names[], int flags) {
393         PdfAction action = new PdfAction();
394         action.put(PdfName.S, PdfName.SUBMITFORM);
395         PdfDictionary dic = new PdfDictionary();
396         dic.put(PdfName.F, new PdfString(file));
397         dic.put(PdfName.FS, PdfName.URL);
398         action.put(PdfName.F, dic);
399         if (names != null)
400             action.put(PdfName.FIELDS, buildArray(names));
401         action.put(PdfName.FLAGS, new PdfNumber(flags));
402         return action;
403     }
404     
405     /**
406      * Creates a resetform.
407      * @param names    the objects to reset
408      * @param flags    submit properties
409      * @return A PdfAction
410      */

411     public static PdfAction createResetForm(Object names[], int flags) {
412         PdfAction action = new PdfAction();
413         action.put(PdfName.S, PdfName.RESETFORM);
414         if (names != null)
415             action.put(PdfName.FIELDS, buildArray(names));
416         action.put(PdfName.FLAGS, new PdfNumber(flags));
417         return action;
418     }
419     
420     /**
421      * Creates an Import field.
422      * @param file
423      * @return A PdfAction
424      */

425     public static PdfAction createImportData(String file) {
426         PdfAction action = new PdfAction();
427         action.put(PdfName.S, PdfName.IMPORTDATA);
428         action.put(PdfName.F, new PdfString(file));
429         return action;
430     }
431     
432     /** Add a chained action.
433      * @param na the next action
434      */
    
435     public void next(PdfAction na) {
436         PdfObject nextAction = get(PdfName.NEXT);
437         if (nextAction == null)
438             put(PdfName.NEXT, na);
439         else if (nextAction.isDictionary()) {
440             PdfArray array = new PdfArray(nextAction);
441             array.add(na);
442             put(PdfName.NEXT, array);
443         }
444         else {
445             ((PdfArray)nextAction).add(na);
446         }
447     }
448     
449     /** Creates a GoTo action to an internal page.
450      * @param page the page to go. First page is 1
451      * @param dest the destination for the page
452      * @param writer the writer for this action
453      * @return a GoTo action
454      */
    
455     public static PdfAction gotoLocalPage(int page, PdfDestination dest, PdfWriter writer) {
456         PdfIndirectReference ref = writer.getPageReference(page);
457         dest.addPage(ref);
458         PdfAction action = new PdfAction();
459         action.put(PdfName.S, PdfName.GOTO);
460         action.put(PdfName.D, dest);
461         return action;
462     }
463
464     /**
465      * Creates a GoTo action to a named destination.
466      * @param dest the named destination
467      * @param isName if true sets the destination as a name, if false sets it as a String
468      * @return a GoTo action
469      */

470     public static PdfAction gotoLocalPage(String dest, boolean isName) {
471         PdfAction action = new PdfAction();
472         action.put(PdfName.S, PdfName.GOTO);
473         if (isName)
474             action.put(PdfName.D, new PdfName(dest));
475         else
476             action.put(PdfName.D, new PdfString(dest, null));
477         return action;
478     }
479
480     /**
481      * Creates a GoToR action to a named destination.
482      * @param filename the file name to go to
483      * @param dest the destination name
484      * @param isName if true sets the destination as a name, if false sets it as a String
485      * @param newWindow open the document in a new window if <CODE>true</CODE>, if false the current document is replaced by the new document.
486      * @return a GoToR action
487      */

488     public static PdfAction gotoRemotePage(String filename, String dest, boolean isName, boolean newWindow) {
489         PdfAction action = new PdfAction();
490         action.put(PdfName.F, new PdfString(filename));
491         action.put(PdfName.S, PdfName.GOTOR);
492         if (isName)
493             action.put(PdfName.D, new PdfName(dest));
494         else
495             action.put(PdfName.D, new PdfString(dest, null));
496         if (newWindow)
497             action.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);
498         return action;
499     }
500
501     /**
502      * Creates a GoToE action to an embedded file.
503      * @param filename    the root document of the target (null if the target is in the same document)
504      * @param dest the named destination
505      * @param isName if true sets the destination as a name, if false sets it as a String
506      * @return a GoToE action
507      */

508     public static PdfAction gotoEmbedded(String filename, PdfTargetDictionary target, String dest, boolean isName, boolean newWindow) {
509         if (isName)
510             return gotoEmbedded(filename, target, new PdfName(dest), newWindow);
511         else
512             return gotoEmbedded(filename, target, new PdfString(dest, null), newWindow);
513     }
514
515     /**
516      * Creates a GoToE action to an embedded file.
517      * @param filename    the root document of the target (null if the target is in the same document)
518      * @param target    a path to the target document of this action
519      * @param dest        the destination inside the target document, can be of type PdfDestination, PdfName, or PdfString
520      * @param newWindow    if true, the destination document should be opened in a new window
521      * @return a GoToE action
522      */

523     public static PdfAction gotoEmbedded(String filename, PdfTargetDictionary target, PdfObject dest, boolean newWindow) {
524         PdfAction action = new PdfAction();
525         action.put(PdfName.S, PdfName.GOTOE);
526         action.put(PdfName.T, target);
527         action.put(PdfName.D, dest);
528         action.put(PdfName.NEWWINDOW, new PdfBoolean(newWindow));
529         if (filename != null) {
530             action.put(PdfName.F, new PdfString(filename));
531         }
532         return action;
533     }
534
535     /**
536      * A set-OCG-state action (PDF 1.5) sets the state of one or more optional content
537      * groups.
538      * @param state an array consisting of any number of sequences beginning with a <CODE>PdfName</CODE>
539      * or <CODE>String</CODE> (ON, OFF, or Toggle) followed by one or more optional content group dictionaries
540      * <CODE>PdfLayer</CODE> or a <CODE>PdfIndirectReference</CODE> to a <CODE>PdfLayer</CODE>.<br>
541      * The array elements are processed from left to right; each name is applied
542      * to the subsequent groups until the next name is encountered:
543      * <ul>
544      * <li>ON sets the state of subsequent groups to ON</li>
545      * <li>OFF sets the state of subsequent groups to OFF</li>
546      * <li>Toggle reverses the state of subsequent groups</li>
547      * </ul>
548      * @param preserveRB if <CODE>true</CODE>, indicates that radio-button state relationships between optional
549      * content groups (as specified by the RBGroups entry in the current configuration
550      * dictionary) should be preserved when the states in the
551      * <CODE>state</CODE> array are applied. That is, if a group is set to ON (either by ON or Toggle) during
552      * processing of the <CODE>state</CODE> array, any other groups belong to the same radio-button
553      * group are turned OFF. If a group is set to OFF, there is no effect on other groups.<br>
554      * If <CODE>false</CODE>, radio-button state relationships, if any, are ignored
555      * @return the action
556      */
    
557     public static PdfAction setOCGstate(ArrayList state, boolean preserveRB) {
558         PdfAction action = new PdfAction();
559         action.put(PdfName.S, PdfName.SETOCGSTATE);
560         PdfArray a = new PdfArray();
561         for (int k = 0; k < state.size(); ++k) {
562             Object o = state.get(k);
563             if (o == null)
564                 continue;
565             if (o instanceof PdfIndirectReference)
566                 a.add((PdfIndirectReference)o);
567             else if (o instanceof PdfLayer)
568                 a.add(((PdfLayer)o).getRef());
569             else if (o instanceof PdfName)
570                 a.add((PdfName)o);
571             else if (o instanceof String) {
572                 PdfName name = null;
573                 String s = (String)o;
574                 if (s.equalsIgnoreCase("on"))
575                     name = PdfName.ON;
576                 else if (s.equalsIgnoreCase("off"))
577                     name = PdfName.OFF;
578                 else if (s.equalsIgnoreCase("toggle"))
579                     name = PdfName.TOGGLE;
580                 else
581                     throw new IllegalArgumentException("A string '" + s + " was passed in state. Only 'ON', 'OFF' and 'Toggle' are allowed.");
582                 a.add(name);
583             }
584             else
585                 throw new IllegalArgumentException("Invalid type was passed in state: " + o.getClass().getName());
586         }
587         action.put(PdfName.STATE, a);
588         if (!preserveRB)
589             action.put(PdfName.PRESERVERB, PdfBoolean.PDFFALSE);
590         return action;
591     }
592 }
593