1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.pdfbox.pdmodel.interactive.annotation; 18 19 import java.io.IOException; 20 21 import org.apache.pdfbox.cos.COSArray; 22 import org.apache.pdfbox.cos.COSDictionary; 23 import org.apache.pdfbox.cos.COSName; 24 25 import org.apache.pdfbox.pdmodel.PDPage; 26 import org.apache.pdfbox.pdmodel.common.COSObjectable; 27 import org.apache.pdfbox.pdmodel.common.PDRectangle; 28 import org.apache.pdfbox.pdmodel.graphics.color.PDGamma; 29 import org.apache.pdfbox.util.BitFlagHelper; 30 import org.apache.pdfbox.cos.COSBase; 31 32 /** 33 * This class represents a PDF annotation. 34 * 35 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 36 * @version $Revision: 1.16 $ 37 */ 38 public abstract class PDAnnotation implements COSObjectable 39 { 40 /** 41 * An annotation flag. 42 */ 43 public static final int FLAG_INVISIBLE = 1 << 0; 44 /** 45 * An annotation flag. 46 */ 47 public static final int FLAG_HIDDEN = 1 << 1; 48 /** 49 * An annotation flag. 50 */ 51 public static final int FLAG_PRINTED = 1 << 2; 52 /** 53 * An annotation flag. 54 */ 55 public static final int FLAG_NO_ZOOM = 1 << 3; 56 /** 57 * An annotation flag. 58 */ 59 public static final int FLAG_NO_ROTATE = 1 << 4; 60 /** 61 * An annotation flag. 62 */ 63 public static final int FLAG_NO_VIEW = 1 << 5; 64 /** 65 * An annotation flag. 66 */ 67 public static final int FLAG_READ_ONLY = 1 << 6; 68 /** 69 * An annotation flag. 70 */ 71 public static final int FLAG_LOCKED = 1 << 7; 72 /** 73 * An annotation flag. 74 */ 75 public static final int FLAG_TOGGLE_NO_VIEW = 1 << 8; 76 77 78 79 private COSDictionary dictionary; 80 81 /** 82 * Create the correct annotation from the base COS object. 83 * 84 * @param base The COS object that is the annotation. 85 * @return The correctly typed annotation object. 86 * @throws IOException If there is an error while creating the annotation. 87 */ 88 // TODO not yet implemented: FreeText, Polygon, PolyLine, Caret, Ink, Sound, 89 // Movie, Screen, PrinterMark, TrapNet, Watermark, 3D, Redact 90 public static PDAnnotation createAnnotation( COSBase base ) throws IOException 91 { 92 PDAnnotation annot = null; 93 if( base instanceof COSDictionary ) 94 { 95 COSDictionary annotDic = (COSDictionary)base; 96 String subtype = annotDic.getNameAsString( COSName.SUBTYPE ); 97 if( subtype.equals( PDAnnotationFileAttachment.SUB_TYPE ) ) 98 { 99 annot = new PDAnnotationFileAttachment( annotDic ); 100 } 101 else if( subtype.equals( PDAnnotationLine.SUB_TYPE ) ) 102 { 103 annot = new PDAnnotationLine( annotDic ); 104 } 105 else if( subtype.equals( PDAnnotationLink.SUB_TYPE ) ) 106 { 107 annot = new PDAnnotationLink(annotDic); 108 } 109 else if( subtype.equals( PDAnnotationPopup.SUB_TYPE ) ) 110 { 111 annot = new PDAnnotationPopup(annotDic); 112 } 113 else if( subtype.equals( PDAnnotationRubberStamp.SUB_TYPE ) ) 114 { 115 annot = new PDAnnotationRubberStamp(annotDic); 116 } 117 else if( subtype.equals( PDAnnotationSquareCircle.SUB_TYPE_SQUARE ) || 118 subtype.equals( PDAnnotationSquareCircle.SUB_TYPE_CIRCLE ) ) 119 { 120 annot = new PDAnnotationSquareCircle( annotDic ); 121 } 122 else if( subtype.equals( PDAnnotationText.SUB_TYPE ) ) 123 { 124 annot = new PDAnnotationText( annotDic); 125 } 126 else if( subtype.equals( PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT ) || 127 subtype.equals( PDAnnotationTextMarkup.SUB_TYPE_UNDERLINE ) || 128 subtype.equals( PDAnnotationTextMarkup.SUB_TYPE_SQUIGGLY ) || 129 subtype.equals( PDAnnotationTextMarkup.SUB_TYPE_STRIKEOUT )) 130 { 131 annot = new PDAnnotationTextMarkup( annotDic ); 132 } 133 else if( subtype.equals( PDAnnotationLink.SUB_TYPE ) ) 134 { 135 annot = new PDAnnotationLink( annotDic ); 136 } 137 else if( subtype.equals( PDAnnotationWidget.SUB_TYPE ) ) 138 { 139 annot = new PDAnnotationWidget( annotDic ); 140 } 141 else 142 { 143 annot = new PDAnnotationUnknown( annotDic ); 144 } 145 } 146 else 147 { 148 throw new IOException( "Error: Unknown annotation type " + base ); 149 } 150 151 return annot; 152 } 153 154 /** 155 * Constructor. 156 */ 157 public PDAnnotation() 158 { 159 dictionary = new COSDictionary(); 160 dictionary.setItem( COSName.TYPE, COSName.getPDFName( "Annot" ) ); 161 } 162 163 /** 164 * Constructor. 165 * 166 * @param dict The annotations dictionary. 167 */ 168 public PDAnnotation( COSDictionary dict ) 169 { 170 dictionary = dict; 171 } 172 173 /** 174 * returns the dictionary. 175 * @return the dictionary 176 */ 177 public COSDictionary getDictionary() 178 { 179 return dictionary; 180 } 181 182 /** 183 * The annotation rectangle, defining the location of the annotation 184 * on the page in default user space units. This is usually required and should 185 * not return null on valid PDF documents. But where this is a parent form field 186 * with children, such as radio button collections then the rectangle will be null. 187 * 188 * @return The Rect value of this annotation. 189 */ 190 public PDRectangle getRectangle() 191 { 192 COSArray rectArray = (COSArray)dictionary.getDictionaryObject( COSName.getPDFName( "Rect" ) ); 193 PDRectangle rectangle = null; 194 if( rectArray != null ) 195 { 196 rectangle = new PDRectangle( rectArray ); 197 } 198 return rectangle; 199 } 200 201 /** 202 * This will set the rectangle for this annotation. 203 * 204 * @param rectangle The new rectangle values. 205 */ 206 public void setRectangle( PDRectangle rectangle ) 207 { 208 dictionary.setItem( COSName.getPDFName( "Rect" ), rectangle.getCOSArray() ); 209 } 210 211 /** 212 * This will get the flags for this field. 213 * 214 * @return flags The set of flags. 215 */ 216 public int getAnnotationFlags() 217 { 218 return getDictionary().getInt( "F", 0 ); 219 } 220 221 /** 222 * This will set the flags for this field. 223 * 224 * @param flags The new flags. 225 */ 226 public void setAnnotationFlags( int flags ) 227 { 228 getDictionary().setInt( "F", flags ); 229 } 230 231 /** 232 * Interface method for COSObjectable. 233 * 234 * @return This object as a standard COS object. 235 */ 236 public COSBase getCOSObject() 237 { 238 return getDictionary(); 239 } 240 241 /** 242 * This will get the name of the current appearance stream if any. 243 * 244 * @return The name of the appearance stream. 245 */ 246 public String getAppearanceStream() 247 { 248 String retval = null; 249 COSName name = (COSName)getDictionary().getDictionaryObject( COSName.getPDFName( "AS" ) ); 250 if( name != null ) 251 { 252 retval = name.getName(); 253 } 254 return retval; 255 } 256 257 /** 258 * This will set the annotations appearance stream name. 259 * 260 * @param as The name of the appearance stream. 261 */ 262 public void setAppearanceStream( String as ) 263 { 264 if( as == null ) 265 { 266 getDictionary().removeItem( COSName.getPDFName( "AS" ) ); 267 } 268 else 269 { 270 getDictionary().setItem( COSName.getPDFName( "AS" ), COSName.getPDFName( as ) ); 271 } 272 } 273 274 /** 275 * This will get the appearance dictionary associated with this annotation. 276 * This may return null. 277 * 278 * @return This annotations appearance. 279 */ 280 public PDAppearanceDictionary getAppearance() 281 { 282 PDAppearanceDictionary ap = null; 283 COSDictionary apDic = (COSDictionary)dictionary.getDictionaryObject( COSName.getPDFName( "AP" ) ); 284 if( apDic != null ) 285 { 286 ap = new PDAppearanceDictionary( apDic ); 287 } 288 return ap; 289 } 290 291 /** 292 * This will set the appearance associated with this annotation. 293 * 294 * @param appearance The appearance dictionary for this annotation. 295 */ 296 public void setAppearance( PDAppearanceDictionary appearance ) 297 { 298 COSDictionary ap = null; 299 if( appearance != null ) 300 { 301 ap = appearance.getDictionary(); 302 } 303 dictionary.setItem( COSName.getPDFName( "AP" ), ap ); 304 } 305 306 /** 307 * Get the invisible flag. 308 * 309 * @return The invisible flag. 310 */ 311 public boolean isInvisible() 312 { 313 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_INVISIBLE ); 314 } 315 316 /** 317 * Set the invisible flag. 318 * 319 * @param invisible The new invisible flag. 320 */ 321 public void setInvisible( boolean invisible ) 322 { 323 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_INVISIBLE, invisible ); 324 } 325 326 /** 327 * Get the hidden flag. 328 * 329 * @return The hidden flag. 330 */ 331 public boolean isHidden() 332 { 333 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_HIDDEN ); 334 } 335 336 /** 337 * Set the hidden flag. 338 * 339 * @param hidden The new hidden flag. 340 */ 341 public void setHidden( boolean hidden ) 342 { 343 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_HIDDEN, hidden ); 344 } 345 346 /** 347 * Get the printed flag. 348 * 349 * @return The printed flag. 350 */ 351 public boolean isPrinted() 352 { 353 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_PRINTED ); 354 } 355 356 /** 357 * Set the printed flag. 358 * 359 * @param printed The new printed flag. 360 */ 361 public void setPrinted( boolean printed ) 362 { 363 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_PRINTED, printed ); 364 } 365 366 /** 367 * Get the noZoom flag. 368 * 369 * @return The noZoom flag. 370 */ 371 public boolean isNoZoom() 372 { 373 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_NO_ZOOM ); 374 } 375 376 /** 377 * Set the noZoom flag. 378 * 379 * @param noZoom The new noZoom flag. 380 */ 381 public void setNoZoom( boolean noZoom ) 382 { 383 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_NO_ZOOM, noZoom ); 384 } 385 386 /** 387 * Get the noRotate flag. 388 * 389 * @return The noRotate flag. 390 */ 391 public boolean isNoRotate() 392 { 393 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_NO_ROTATE ); 394 } 395 396 /** 397 * Set the noRotate flag. 398 * 399 * @param noRotate The new noRotate flag. 400 */ 401 public void setNoRotate( boolean noRotate ) 402 { 403 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_NO_ROTATE, noRotate ); 404 } 405 406 /** 407 * Get the noView flag. 408 * 409 * @return The noView flag. 410 */ 411 public boolean isNoView() 412 { 413 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_NO_VIEW ); 414 } 415 416 /** 417 * Set the noView flag. 418 * 419 * @param noView The new noView flag. 420 */ 421 public void setNoView( boolean noView ) 422 { 423 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_NO_VIEW, noView ); 424 } 425 426 /** 427 * Get the readOnly flag. 428 * 429 * @return The readOnly flag. 430 */ 431 public boolean isReadOnly() 432 { 433 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_READ_ONLY ); 434 } 435 436 /** 437 * Set the readOnly flag. 438 * 439 * @param readOnly The new readOnly flag. 440 */ 441 public void setReadOnly( boolean readOnly ) 442 { 443 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_READ_ONLY, readOnly ); 444 } 445 446 /** 447 * Get the locked flag. 448 * 449 * @return The locked flag. 450 */ 451 public boolean isLocked() 452 { 453 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_LOCKED ); 454 } 455 456 /** 457 * Set the locked flag. 458 * 459 * @param locked The new locked flag. 460 */ 461 public void setLocked( boolean locked ) 462 { 463 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_LOCKED, locked ); 464 } 465 466 /** 467 * Get the toggleNoView flag. 468 * 469 * @return The toggleNoView flag. 470 */ 471 public boolean isToggleNoView() 472 { 473 return BitFlagHelper.getFlag( getDictionary(), "F", FLAG_TOGGLE_NO_VIEW ); 474 } 475 476 /** 477 * Set the toggleNoView flag. 478 * 479 * @param toggleNoView The new toggleNoView flag. 480 */ 481 public void setToggleNoView( boolean toggleNoView ) 482 { 483 BitFlagHelper.setFlag( getDictionary(), "F", FLAG_TOGGLE_NO_VIEW, toggleNoView ); 484 } 485 486 /** 487 * Get the "contents" of the field. 488 * 489 * @return the value of the contents. 490 */ 491 public String getContents() 492 { 493 return dictionary.getString(COSName.CONTENTS); 494 } 495 496 /** 497 * Set the "contents" of the field. 498 * 499 * @param value the value of the contents. 500 */ 501 public void setContents( String value) 502 { 503 dictionary.setString(COSName.CONTENTS, value); 504 } 505 506 /** 507 * This will retrieve the date and time the annotation was modified. 508 * 509 * @return the modified date/time (often in date format, but can be an arbitary string). 510 */ 511 public String getModifiedDate() 512 { 513 return getDictionary().getString( "M" ); 514 } 515 516 /** 517 * This will set the the date and time the annotation was modified. 518 * 519 * @param m 520 * the date and time the annotation was created. 521 */ 522 public void setModifiedDate( String m ) 523 { 524 getDictionary().setString( "M", m ); 525 } 526 527 /** 528 * This will get the name, a string intended to uniquely identify each annotation 529 * within a page. Not to be confused with some annotations Name entry which 530 * impact the default image drawn for them. 531 * 532 * @return The identifying name for the Annotation. 533 */ 534 public String getAnnotationName() 535 { 536 return getDictionary().getString( "NM" ); 537 } 538 539 /** 540 * This will set the name, a string intended to uniquely identify each annotation 541 * within a page. Not to be confused with some annotations Name entry which 542 * impact the default image drawn for them. 543 * 544 * @param nm The identifying name for the annotation. 545 */ 546 public void setAnnotationName( String nm ) 547 { 548 getDictionary().setString( "NM", nm ); 549 } 550 551 /** 552 * This will set the colour used in drawing various elements. 553 * As of PDF 1.6 these are : Background of icon when closed 554 * Title bar of popup window 555 * Border of a link annotation 556 * 557 * Colour is in DeviceRGB colourspace 558 * 559 * @param c 560 * colour in the DeviceRGB colourspace 561 * 562 */ 563 public void setColour( PDGamma c ) 564 { 565 getDictionary().setItem( "C", c ); 566 } 567 568 /** 569 * This will retrieve the colour used in drawing various elements. 570 * As of PDF 1.6 these are : Background of icon when closed 571 * Title bar of popup window 572 * Border of a link annotation 573 * 574 * Colour is in DeviceRGB colourspace 575 * 576 * @return PDGamma object representing the colour 577 * 578 */ 579 public PDGamma getColour() 580 { 581 COSArray c = (COSArray) getDictionary().getItem(COSName.getPDFName( "C" ) ); 582 if (c != null) 583 { 584 return new PDGamma( c ); 585 } 586 else 587 { 588 return null; 589 } 590 } 591 592 /** 593 * This will retrieve the subtype of the annotation. 594 * 595 * @return the subtype 596 */ 597 public String getSubtype() 598 { 599 return this.getDictionary().getNameAsString(COSName.SUBTYPE); 600 } 601 602 /** 603 * This will retrieve the corresponding page of this annotation. 604 * 605 * @return the corresponding page 606 */ 607 public PDPage getPage() 608 { 609 COSDictionary p = (COSDictionary) this.getDictionary().getDictionaryObject(COSName.P); 610 if (p != null) 611 { 612 return new PDPage(p); 613 } 614 return null; 615 } 616 617 }