001/* 002 * $Id: PdfAction.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.IOException; 047import java.net.URL; 048import java.util.ArrayList; 049 050import com.itextpdf.text.error_messages.MessageLocalization; 051import com.itextpdf.text.pdf.collection.PdfTargetDictionary; 052 053/** 054 * A <CODE>PdfAction</CODE> defines an action that can be triggered from a PDF file. 055 * 056 * @see PdfDictionary 057 */ 058 059public class PdfAction extends PdfDictionary { 060 061 /** A named action to go to the first page. 062 */ 063 public static final int FIRSTPAGE = 1; 064 /** A named action to go to the previous page. 065 */ 066 public static final int PREVPAGE = 2; 067 /** A named action to go to the next page. 068 */ 069 public static final int NEXTPAGE = 3; 070 /** A named action to go to the last page. 071 */ 072 public static final int LASTPAGE = 4; 073 074 /** A named action to open a print dialog. 075 */ 076 public static final int PRINTDIALOG = 5; 077 078 /** a possible submitvalue */ 079 public static final int SUBMIT_EXCLUDE = 1; 080 /** a possible submitvalue */ 081 public static final int SUBMIT_INCLUDE_NO_VALUE_FIELDS = 2; 082 /** a possible submitvalue */ 083 public static final int SUBMIT_HTML_FORMAT = 4; 084 /** a possible submitvalue */ 085 public static final int SUBMIT_HTML_GET = 8; 086 /** a possible submitvalue */ 087 public static final int SUBMIT_COORDINATES = 16; 088 /** a possible submitvalue */ 089 public static final int SUBMIT_XFDF = 32; 090 /** a possible submitvalue */ 091 public static final int SUBMIT_INCLUDE_APPEND_SAVES = 64; 092 /** a possible submitvalue */ 093 public static final int SUBMIT_INCLUDE_ANNOTATIONS = 128; 094 /** a possible submitvalue */ 095 public static final int SUBMIT_PDF = 256; 096 /** a possible submitvalue */ 097 public static final int SUBMIT_CANONICAL_FORMAT = 512; 098 /** a possible submitvalue */ 099 public static final int SUBMIT_EXCL_NON_USER_ANNOTS = 1024; 100 /** a possible submitvalue */ 101 public static final int SUBMIT_EXCL_F_KEY = 2048; 102 /** a possible submitvalue */ 103 public static final int SUBMIT_EMBED_FORM = 8196; 104 /** a possible submitvalue */ 105 public static final int RESET_EXCLUDE = 1; 106 107 // constructors 108 109 /** Create an empty action. 110 */ 111 public PdfAction() { 112 } 113 114 /** 115 * Constructs a new <CODE>PdfAction</CODE> of Subtype URI. 116 * 117 * @param url the Url to go to 118 */ 119 120 public PdfAction(URL url) { 121 this(url.toExternalForm()); 122 } 123 124 /** 125 * Construct a new <CODE>PdfAction</CODE> of Subtype URI that accepts the x and y coordinate of the position that was clicked. 126 * @param url 127 * @param isMap 128 */ 129 public PdfAction(URL url, boolean isMap) { 130 this(url.toExternalForm(), isMap); 131 } 132 133 /** 134 * Constructs a new <CODE>PdfAction</CODE> of Subtype URI. 135 * 136 * @param url the url to go to 137 */ 138 139 public PdfAction(String url) { 140 this(url, false); 141 } 142 143 /** 144 * Construct a new <CODE>PdfAction</CODE> of Subtype URI that accepts the x and y coordinate of the position that was clicked. 145 * @param url 146 * @param isMap 147 */ 148 149 public PdfAction(String url, boolean isMap) { 150 put(PdfName.S, PdfName.URI); 151 put(PdfName.URI, new PdfString(url)); 152 if (isMap) 153 put(PdfName.ISMAP, PdfBoolean.PDFTRUE); 154 } 155 156 /** 157 * Constructs a new <CODE>PdfAction</CODE> of Subtype GoTo. 158 * @param destination the destination to go to 159 */ 160 161 PdfAction(PdfIndirectReference destination) { 162 put(PdfName.S, PdfName.GOTO); 163 put(PdfName.D, destination); 164 } 165 166 /** 167 * Constructs a new <CODE>PdfAction</CODE> of Subtype GoToR. 168 * @param filename the file name to go to 169 * @param name the named destination to go to 170 */ 171 172 public PdfAction(String filename, String name) { 173 put(PdfName.S, PdfName.GOTOR); 174 put(PdfName.F, new PdfString(filename)); 175 put(PdfName.D, new PdfString(name)); 176 } 177 178 /** 179 * Constructs a new <CODE>PdfAction</CODE> of Subtype GoToR. 180 * @param filename the file name to go to 181 * @param page the page destination to go to 182 */ 183 184 public PdfAction(String filename, int page) { 185 put(PdfName.S, PdfName.GOTOR); 186 put(PdfName.F, new PdfString(filename)); 187 put(PdfName.D, new PdfLiteral("[" + (page - 1) + " /FitH 10000]")); 188 } 189 190 /** Implements name actions. The action can be FIRSTPAGE, LASTPAGE, 191 * NEXTPAGE, PREVPAGE and PRINTDIALOG. 192 * @param named the named action 193 */ 194 public PdfAction(int named) { 195 put(PdfName.S, PdfName.NAMED); 196 switch (named) { 197 case FIRSTPAGE: 198 put(PdfName.N, PdfName.FIRSTPAGE); 199 break; 200 case LASTPAGE: 201 put(PdfName.N, PdfName.LASTPAGE); 202 break; 203 case NEXTPAGE: 204 put(PdfName.N, PdfName.NEXTPAGE); 205 break; 206 case PREVPAGE: 207 put(PdfName.N, PdfName.PREVPAGE); 208 break; 209 case PRINTDIALOG: 210 put(PdfName.S, PdfName.JAVASCRIPT); 211 put(PdfName.JS, new PdfString("this.print(true);\r")); 212 break; 213 default: 214 throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.named.action")); 215 } 216 } 217 218 /** Launches an application or a document. 219 * @param application the application to be launched or the document to be opened or printed. 220 * @param parameters (Windows-specific) A parameter string to be passed to the application. 221 * It can be <CODE>null</CODE>. 222 * @param operation (Windows-specific) the operation to perform: "open" - Open a document, 223 * "print" - Print a document. 224 * It can be <CODE>null</CODE>. 225 * @param defaultDir (Windows-specific) the default directory in standard DOS syntax. 226 * It can be <CODE>null</CODE>. 227 */ 228 public PdfAction(String application, String parameters, String operation, String defaultDir) { 229 put(PdfName.S, PdfName.LAUNCH); 230 if (parameters == null && operation == null && defaultDir == null) 231 put(PdfName.F, new PdfString(application)); 232 else { 233 PdfDictionary dic = new PdfDictionary(); 234 dic.put(PdfName.F, new PdfString(application)); 235 if (parameters != null) 236 dic.put(PdfName.P, new PdfString(parameters)); 237 if (operation != null) 238 dic.put(PdfName.O, new PdfString(operation)); 239 if (defaultDir != null) 240 dic.put(PdfName.D, new PdfString(defaultDir)); 241 put(PdfName.WIN, dic); 242 } 243 } 244 245 /** Launches an application or a document. 246 * @param application the application to be launched or the document to be opened or printed. 247 * @param parameters (Windows-specific) A parameter string to be passed to the application. 248 * It can be <CODE>null</CODE>. 249 * @param operation (Windows-specific) the operation to perform: "open" - Open a document, 250 * "print" - Print a document. 251 * It can be <CODE>null</CODE>. 252 * @param defaultDir (Windows-specific) the default directory in standard DOS syntax. 253 * It can be <CODE>null</CODE>. 254 * @return a Launch action 255 */ 256 public static PdfAction createLaunch(String application, String parameters, String operation, String defaultDir) { 257 return new PdfAction(application, parameters, operation, defaultDir); 258 } 259 260 /**Creates a Rendition action 261 * @param file 262 * @param fs 263 * @param mimeType 264 * @param ref 265 * @return a Media Clip action 266 * @throws IOException 267 */ 268 public static PdfAction rendition(String file, PdfFileSpecification fs, String mimeType, PdfIndirectReference ref) throws IOException { 269 PdfAction js = new PdfAction(); 270 js.put(PdfName.S, PdfName.RENDITION); 271 js.put(PdfName.R, new PdfRendition(file, fs, mimeType)); 272 js.put(new PdfName("OP"), new PdfNumber(0)); 273 js.put(new PdfName("AN"), ref); 274 return js; 275 } 276 277 /** Creates a JavaScript action. If the JavaScript is smaller than 278 * 50 characters it will be placed as a string, otherwise it will 279 * be placed as a compressed stream. 280 * @param code the JavaScript code 281 * @param writer the writer for this action 282 * @param unicode select JavaScript unicode. Note that the internal 283 * Acrobat JavaScript engine does not support unicode, 284 * so this may or may not work for you 285 * @return the JavaScript action 286 */ 287 public static PdfAction javaScript(String code, PdfWriter writer, boolean unicode) { 288 PdfAction js = new PdfAction(); 289 js.put(PdfName.S, PdfName.JAVASCRIPT); 290 if (unicode && code.length() < 50) { 291 js.put(PdfName.JS, new PdfString(code, PdfObject.TEXT_UNICODE)); 292 } 293 else if (!unicode && code.length() < 100) { 294 js.put(PdfName.JS, new PdfString(code)); 295 } 296 else { 297 try { 298 byte b[] = PdfEncodings.convertToBytes(code, unicode ? PdfObject.TEXT_UNICODE : PdfObject.TEXT_PDFDOCENCODING); 299 PdfStream stream = new PdfStream(b); 300 stream.flateCompress(writer.getCompressionLevel()); 301 js.put(PdfName.JS, writer.addToBody(stream).getIndirectReference()); 302 } 303 catch (Exception e) { 304 js.put(PdfName.JS, new PdfString(code)); 305 } 306 } 307 return js; 308 } 309 310 /** Creates a JavaScript action. If the JavaScript is smaller than 311 * 50 characters it will be place as a string, otherwise it will 312 * be placed as a compressed stream. 313 * @param code the JavaScript code 314 * @param writer the writer for this action 315 * @return the JavaScript action 316 */ 317 public static PdfAction javaScript(String code, PdfWriter writer) { 318 return javaScript(code, writer, false); 319 } 320 321 /** 322 * A Hide action hides or shows an object. 323 * @param obj object to hide or show 324 * @param hide true is hide, false is show 325 * @return a Hide Action 326 */ 327 static PdfAction createHide(PdfObject obj, boolean hide) { 328 PdfAction action = new PdfAction(); 329 action.put(PdfName.S, PdfName.HIDE); 330 action.put(PdfName.T, obj); 331 if (!hide) 332 action.put(PdfName.H, PdfBoolean.PDFFALSE); 333 return action; 334 } 335 336 /** 337 * A Hide action hides or shows an annotation. 338 * @param annot 339 * @param hide 340 * @return A Hide Action 341 */ 342 public static PdfAction createHide(PdfAnnotation annot, boolean hide) { 343 return createHide(annot.getIndirectReference(), hide); 344 } 345 346 /** 347 * A Hide action hides or shows an annotation. 348 * @param name 349 * @param hide 350 * @return A Hide Action 351 */ 352 public static PdfAction createHide(String name, boolean hide) { 353 return createHide(new PdfString(name), hide); 354 } 355 356 static PdfArray buildArray(Object names[]) { 357 PdfArray array = new PdfArray(); 358 for (int k = 0; k < names.length; ++k) { 359 Object obj = names[k]; 360 if (obj instanceof String) 361 array.add(new PdfString((String)obj)); 362 else if (obj instanceof PdfAnnotation) 363 array.add(((PdfAnnotation)obj).getIndirectReference()); 364 else 365 throw new RuntimeException(MessageLocalization.getComposedMessage("the.array.must.contain.string.or.pdfannotation")); 366 } 367 return array; 368 } 369 370 /** 371 * A Hide action hides or shows objects. 372 * @param names 373 * @param hide 374 * @return A Hide Action 375 */ 376 public static PdfAction createHide(Object names[], boolean hide) { 377 return createHide(buildArray(names), hide); 378 } 379 380 /** 381 * Creates a submit form. 382 * @param file the URI to submit the form to 383 * @param names the objects to submit 384 * @param flags submit properties 385 * @return A PdfAction 386 */ 387 public static PdfAction createSubmitForm(String file, Object names[], int flags) { 388 PdfAction action = new PdfAction(); 389 action.put(PdfName.S, PdfName.SUBMITFORM); 390 PdfDictionary dic = new PdfDictionary(); 391 dic.put(PdfName.F, new PdfString(file)); 392 dic.put(PdfName.FS, PdfName.URL); 393 action.put(PdfName.F, dic); 394 if (names != null) 395 action.put(PdfName.FIELDS, buildArray(names)); 396 action.put(PdfName.FLAGS, new PdfNumber(flags)); 397 return action; 398 } 399 400 /** 401 * Creates a resetform. 402 * @param names the objects to reset 403 * @param flags submit properties 404 * @return A PdfAction 405 */ 406 public static PdfAction createResetForm(Object names[], int flags) { 407 PdfAction action = new PdfAction(); 408 action.put(PdfName.S, PdfName.RESETFORM); 409 if (names != null) 410 action.put(PdfName.FIELDS, buildArray(names)); 411 action.put(PdfName.FLAGS, new PdfNumber(flags)); 412 return action; 413 } 414 415 /** 416 * Creates an Import field. 417 * @param file 418 * @return A PdfAction 419 */ 420 public static PdfAction createImportData(String file) { 421 PdfAction action = new PdfAction(); 422 action.put(PdfName.S, PdfName.IMPORTDATA); 423 action.put(PdfName.F, new PdfString(file)); 424 return action; 425 } 426 427 /** Add a chained action. 428 * @param na the next action 429 */ 430 public void next(PdfAction na) { 431 PdfObject nextAction = get(PdfName.NEXT); 432 if (nextAction == null) 433 put(PdfName.NEXT, na); 434 else if (nextAction.isDictionary()) { 435 PdfArray array = new PdfArray(nextAction); 436 array.add(na); 437 put(PdfName.NEXT, array); 438 } 439 else { 440 ((PdfArray)nextAction).add(na); 441 } 442 } 443 444 /** Creates a GoTo action to an internal page. 445 * @param page the page to go. First page is 1 446 * @param dest the destination for the page 447 * @param writer the writer for this action 448 * @return a GoTo action 449 */ 450 public static PdfAction gotoLocalPage(int page, PdfDestination dest, PdfWriter writer) { 451 PdfIndirectReference ref = writer.getPageReference(page); 452 dest.addPage(ref); 453 PdfAction action = new PdfAction(); 454 action.put(PdfName.S, PdfName.GOTO); 455 action.put(PdfName.D, dest); 456 return action; 457 } 458 459 /** 460 * Creates a GoTo action to a named destination. 461 * @param dest the named destination 462 * @param isName if true sets the destination as a name, if false sets it as a String 463 * @return a GoTo action 464 */ 465 public static PdfAction gotoLocalPage(String dest, boolean isName) { 466 PdfAction action = new PdfAction(); 467 action.put(PdfName.S, PdfName.GOTO); 468 if (isName) 469 action.put(PdfName.D, new PdfName(dest)); 470 else 471 action.put(PdfName.D, new PdfString(dest, null)); 472 return action; 473 } 474 475 /** 476 * Creates a GoToR action to a named destination. 477 * @param filename the file name to go to 478 * @param dest the destination name 479 * @param isName if true sets the destination as a name, if false sets it as a String 480 * @param newWindow open the document in a new window if <CODE>true</CODE>, if false the current document is replaced by the new document. 481 * @return a GoToR action 482 */ 483 public static PdfAction gotoRemotePage(String filename, String dest, boolean isName, boolean newWindow) { 484 PdfAction action = new PdfAction(); 485 action.put(PdfName.F, new PdfString(filename)); 486 action.put(PdfName.S, PdfName.GOTOR); 487 if (isName) 488 action.put(PdfName.D, new PdfName(dest)); 489 else 490 action.put(PdfName.D, new PdfString(dest, null)); 491 if (newWindow) 492 action.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE); 493 return action; 494 } 495 496 /** 497 * Creates a GoToE action to an embedded file. 498 * @param filename the root document of the target (null if the target is in the same document) 499 * @param dest the named destination 500 * @param isName if true sets the destination as a name, if false sets it as a String 501 * @return a GoToE action 502 */ 503 public static PdfAction gotoEmbedded(String filename, PdfTargetDictionary target, String dest, boolean isName, boolean newWindow) { 504 if (isName) 505 return gotoEmbedded(filename, target, new PdfName(dest), newWindow); 506 else 507 return gotoEmbedded(filename, target, new PdfString(dest, null), newWindow); 508 } 509 510 /** 511 * Creates a GoToE action to an embedded file. 512 * @param filename the root document of the target (null if the target is in the same document) 513 * @param target a path to the target document of this action 514 * @param dest the destination inside the target document, can be of type PdfDestination, PdfName, or PdfString 515 * @param newWindow if true, the destination document should be opened in a new window 516 * @return a GoToE action 517 */ 518 public static PdfAction gotoEmbedded(String filename, PdfTargetDictionary target, PdfObject dest, boolean newWindow) { 519 PdfAction action = new PdfAction(); 520 action.put(PdfName.S, PdfName.GOTOE); 521 action.put(PdfName.T, target); 522 action.put(PdfName.D, dest); 523 action.put(PdfName.NEWWINDOW, new PdfBoolean(newWindow)); 524 if (filename != null) { 525 action.put(PdfName.F, new PdfString(filename)); 526 } 527 return action; 528 } 529 530 /** 531 * A set-OCG-state action (PDF 1.5) sets the state of one or more optional content 532 * groups. 533 * @param state an array consisting of any number of sequences beginning with a <CODE>PdfName</CODE> 534 * or <CODE>String</CODE> (ON, OFF, or Toggle) followed by one or more optional content group dictionaries 535 * <CODE>PdfLayer</CODE> or a <CODE>PdfIndirectReference</CODE> to a <CODE>PdfLayer</CODE>.<br> 536 * The array elements are processed from left to right; each name is applied 537 * to the subsequent groups until the next name is encountered: 538 * <ul> 539 * <li>ON sets the state of subsequent groups to ON</li> 540 * <li>OFF sets the state of subsequent groups to OFF</li> 541 * <li>Toggle reverses the state of subsequent groups</li> 542 * </ul> 543 * @param preserveRB if <CODE>true</CODE>, indicates that radio-button state relationships between optional 544 * content groups (as specified by the RBGroups entry in the current configuration 545 * dictionary) should be preserved when the states in the 546 * <CODE>state</CODE> array are applied. That is, if a group is set to ON (either by ON or Toggle) during 547 * processing of the <CODE>state</CODE> array, any other groups belong to the same radio-button 548 * group are turned OFF. If a group is set to OFF, there is no effect on other groups.<br> 549 * If <CODE>false</CODE>, radio-button state relationships, if any, are ignored 550 * @return the action 551 */ 552 public static PdfAction setOCGstate(ArrayList<Object> state, boolean preserveRB) { 553 PdfAction action = new PdfAction(); 554 action.put(PdfName.S, PdfName.SETOCGSTATE); 555 PdfArray a = new PdfArray(); 556 for (int k = 0; k < state.size(); ++k) { 557 Object o = state.get(k); 558 if (o == null) 559 continue; 560 if (o instanceof PdfIndirectReference) 561 a.add((PdfIndirectReference)o); 562 else if (o instanceof PdfLayer) 563 a.add(((PdfLayer)o).getRef()); 564 else if (o instanceof PdfName) 565 a.add((PdfName)o); 566 else if (o instanceof String) { 567 PdfName name = null; 568 String s = (String)o; 569 if (s.equalsIgnoreCase("on")) 570 name = PdfName.ON; 571 else if (s.equalsIgnoreCase("off")) 572 name = PdfName.OFF; 573 else if (s.equalsIgnoreCase("toggle")) 574 name = PdfName.TOGGLE; 575 else 576 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("a.string.1.was.passed.in.state.only.on.off.and.toggle.are.allowed", s)); 577 a.add(name); 578 } 579 else 580 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("invalid.type.was.passed.in.state.1", o.getClass().getName())); 581 } 582 action.put(PdfName.STATE, a); 583 if (!preserveRB) 584 action.put(PdfName.PRESERVERB, PdfBoolean.PDFFALSE); 585 return action; 586 } 587}